-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a config file.
+The [player options page for this game](../player-options) contains all the options you need to configure and export a config file.
`Kingdom Hearts II Final Mix` from the [Epic Games Store](https://store.epicgames.com/en-US/discover/kingdom-hearts)
diff --git a/worlds/ladx/LADXR/utils.py b/worlds/ladx/LADXR/utils.py
index fcf1d2bb56e7..5f8b2685550d 100644
--- a/worlds/ladx/LADXR/utils.py
+++ b/worlds/ladx/LADXR/utils.py
@@ -146,7 +146,7 @@ def setReplacementName(key: str, value: str) -> None:
def formatText(instr: str, *, center: bool = False, ask: Optional[str] = None) -> bytes:
instr = instr.format(**_NAMES)
- s = instr.encode("ascii")
+ s = instr.encode("ascii", errors="replace")
s = s.replace(b"'", b"^")
def padLine(line: bytes) -> bytes:
@@ -169,7 +169,7 @@ def padLine(line: bytes) -> bytes:
if result_line:
result += padLine(result_line)
if ask is not None:
- askbytes = ask.encode("ascii")
+ askbytes = ask.encode("ascii", errors="replace")
result = result.rstrip()
while len(result) % 32 != 16:
result += b' '
diff --git a/worlds/ladx/Options.py b/worlds/ladx/Options.py
index 691891c0b350..ec4570640788 100644
--- a/worlds/ladx/Options.py
+++ b/worlds/ladx/Options.py
@@ -179,6 +179,22 @@ class ShuffleStoneBeaks(DungeonItemShuffle):
display_name = "Shuffle Stone Beaks"
ladxr_item = "STONE_BEAK"
+class ShuffleInstruments(DungeonItemShuffle):
+ """
+ Shuffle Instruments
+ [Original Dungeon] The item will be within its original dungeon
+ [Own Dungeons] The item will be within a dungeon in your world
+ [Own World] The item will be somewhere in your world
+ [Any World] The item could be anywhere
+ [Different World] The item will be somewhere in another world
+ [Vanilla] The item will be in its vanilla location in your world
+ """
+ display_name = "Shuffle Instruments"
+ ladxr_item = "INSTRUMENT"
+ default = 100
+ option_vanilla = 100
+ alias_false = 100
+
class Goal(Choice, LADXROption):
"""
The Goal of the game
@@ -399,6 +415,26 @@ class Palette(Choice):
option_pink = 4
option_inverted = 5
+class Music(Choice, LADXROption):
+ """
+ [Vanilla] Regular Music
+ [Shuffled] Shuffled Music
+ [Off] No music
+ """
+ ladxr_name = "music"
+ option_vanilla = 0
+ option_shuffled = 1
+ option_off = 2
+
+
+ def to_ladxr_option(self, all_options):
+ s = ""
+ if self.value == self.option_shuffled:
+ s = "random"
+ elif self.value == self.option_off:
+ s = "off"
+ return self.ladxr_name, s
+
class WarpImprovements(DefaultOffToggle):
"""
[On] Adds remake style warp screen to the game. Choose your warp destination on the map after jumping in a portal and press B to select.
@@ -444,6 +480,8 @@ class AdditionalWarpPoints(DefaultOffToggle):
'shuffle_maps': ShuffleMaps,
'shuffle_compasses': ShuffleCompasses,
'shuffle_stone_beaks': ShuffleStoneBeaks,
+ 'music': Music,
+ 'shuffle_instruments': ShuffleInstruments,
'music_change_condition': MusicChangeCondition,
'nag_messages': NagMessages,
'ap_title_screen': APTitleScreen,
diff --git a/worlds/ladx/__init__.py b/worlds/ladx/__init__.py
index 181cc053222d..d662b526bb61 100644
--- a/worlds/ladx/__init__.py
+++ b/worlds/ladx/__init__.py
@@ -23,7 +23,7 @@
from .LADXR.worldSetup import WorldSetup as LADXRWorldSetup
from .Locations import (LinksAwakeningLocation, LinksAwakeningRegion,
create_regions_from_ladxr, get_locations_to_id)
-from .Options import DungeonItemShuffle, links_awakening_options
+from .Options import DungeonItemShuffle, links_awakening_options, ShuffleInstruments
from .Rom import LADXDeltaPatch
DEVELOPER_MODE = False
@@ -184,19 +184,22 @@ def create_items(self) -> None:
self.pre_fill_items = []
# For any and different world, set item rule instead
- for option in ["maps", "compasses", "small_keys", "nightmare_keys", "stone_beaks"]:
- option = "shuffle_" + option
+ for dungeon_item_type in ["maps", "compasses", "small_keys", "nightmare_keys", "stone_beaks", "instruments"]:
+ option = "shuffle_" + dungeon_item_type
option = self.player_options[option]
dungeon_item_types[option.ladxr_item] = option.value
+ # The color dungeon does not contain an instrument
+ num_items = 8 if dungeon_item_type == "instruments" else 9
+
if option.value == DungeonItemShuffle.option_own_world:
self.multiworld.local_items[self.player].value |= {
- ladxr_item_to_la_item_name[f"{option.ladxr_item}{i}"] for i in range(1, 10)
+ ladxr_item_to_la_item_name[f"{option.ladxr_item}{i}"] for i in range(1, num_items + 1)
}
elif option.value == DungeonItemShuffle.option_different_world:
self.multiworld.non_local_items[self.player].value |= {
- ladxr_item_to_la_item_name[f"{option.ladxr_item}{i}"] for i in range(1, 10)
+ ladxr_item_to_la_item_name[f"{option.ladxr_item}{i}"] for i in range(1, num_items + 1)
}
# option_original_dungeon = 0
# option_own_dungeons = 1
@@ -224,7 +227,10 @@ def create_items(self) -> None:
continue
if isinstance(item.item_data, DungeonItemData):
- if item.item_data.dungeon_item_type == DungeonItemType.INSTRUMENT:
+ item_type = item.item_data.ladxr_id[:-1]
+ shuffle_type = dungeon_item_types[item_type]
+
+ if item.item_data.dungeon_item_type == DungeonItemType.INSTRUMENT and shuffle_type == ShuffleInstruments.option_vanilla:
# Find instrument, lock
# TODO: we should be able to pinpoint the region we want, save a lookup table please
found = False
@@ -240,10 +246,8 @@ def create_items(self) -> None:
found = True
break
if found:
- break
+ break
else:
- item_type = item.item_data.ladxr_id[:-1]
- shuffle_type = dungeon_item_types[item_type]
if shuffle_type == DungeonItemShuffle.option_original_dungeon:
self.prefill_original_dungeon[item.item_data.dungeon_index - 1].append(item)
self.pre_fill_items.append(item)
@@ -276,6 +280,11 @@ def create_items(self) -> None:
# Properly fill locations within dungeon
location.dungeon = r.dungeon_index
+ # For now, special case first item
+ FORCE_START_ITEM = True
+ if FORCE_START_ITEM:
+ self.force_start_item()
+
def force_start_item(self):
start_loc = self.multiworld.get_location("Tarin's Gift (Mabe Village)", self.player)
if not start_loc.item:
@@ -287,17 +296,12 @@ def force_start_item(self):
start_item = self.multiworld.itempool.pop(index)
start_loc.place_locked_item(start_item)
-
def get_pre_fill_items(self):
return self.pre_fill_items
def pre_fill(self) -> None:
allowed_locations_by_item = {}
- # For now, special case first item
- FORCE_START_ITEM = True
- if FORCE_START_ITEM:
- self.force_start_item()
# Set up filter rules
diff --git a/worlds/ladx/docs/en_Links Awakening DX.md b/worlds/ladx/docs/en_Links Awakening DX.md
index bceda4bc89c1..91a34107c169 100644
--- a/worlds/ladx/docs/en_Links Awakening DX.md
+++ b/worlds/ladx/docs/en_Links Awakening DX.md
@@ -1,8 +1,8 @@
# Links Awakening DX
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
@@ -85,7 +85,7 @@ Title screen graphics by toomanyteeth✨ (https://instagram.com/toomanyyyteeth)
The walrus is moved a bit, so that you can access the desert without taking Marin on a date.
Logic
-
Depending on your settings, you can only steal after you find the sword, always, or never.
+
Depending on your options, you can only steal after you find the sword, always, or never.
Do not forget that there are two items in the rafting ride. You can access this with just Hookshot or Flippers.
Killing enemies with bombs is in normal logic. You can switch to casual logic if you do not want this.
D7 confuses some people, but by dropping down pits on the 2nd floor you can access almost all of this dungeon, even without feather and power bracelet.
diff --git a/worlds/ladx/docs/setup_en.md b/worlds/ladx/docs/setup_en.md
index aad077d73037..d12f9b8b3b84 100644
--- a/worlds/ladx/docs/setup_en.md
+++ b/worlds/ladx/docs/setup_en.md
@@ -35,8 +35,8 @@ options.
### Where do I get a config file?
-The [Player Settings](/games/Links%20Awakening%20DX/player-settings) page on the website allows you to configure
-your personal settings and export a config file from them.
+The [Player Options](/games/Links%20Awakening%20DX/player-options) page on the website allows you to configure
+your personal options and export a config file from them.
### Verifying your config file
@@ -45,7 +45,7 @@ If you would like to validate your config file to make sure it works, you may do
## Generating a Single-Player Game
-1. Navigate to the [Player Settings](/games/Links%20Awakening%20DX/player-settings) page, configure your options,
+1. Navigate to the [Player Options](/games/Links%20Awakening%20DX/player-options) page, configure your options,
and click the "Generate Game" button.
2. You will be presented with a "Seed Info" page.
3. Click the "Create New Room" link.
diff --git a/worlds/landstalker/docs/en_Landstalker - The Treasures of King Nole.md b/worlds/landstalker/docs/en_Landstalker - The Treasures of King Nole.md
index 90a79f8bd986..9239f741b436 100644
--- a/worlds/landstalker/docs/en_Landstalker - The Treasures of King Nole.md
+++ b/worlds/landstalker/docs/en_Landstalker - The Treasures of King Nole.md
@@ -1,8 +1,8 @@
# Landstalker: The Treasures of King Nole
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains most of the options you need to
+The [player options page for this game](../player-options) contains most of the options you need to
configure and export a config file.
## What does randomization do to this game?
@@ -35,7 +35,7 @@ All key doors are gone, except three of them :
The secondary shop of Mercator requiring to do the traders sidequest in the original game is now unlocked by having
**Buyer Card** in your inventory.
-You will need as many **jewels** as specified in the settings to use the teleporter to go to Kazalt and the final dungeon.
+You will need as many **jewels** as specified in the options to use the teleporter to go to Kazalt and the final dungeon.
If you find and use the **Lithograph**, it will tell you in which world are each one of your jewels.
Each seed, there is a random dungeon which is chosen to be the "dark dungeon" where you won't see anything unless you
@@ -54,7 +54,7 @@ be significantly harder, both combat-wise and logic-wise.
Having fully open & shuffled teleportation trees is an interesting way to play, but is discouraged for beginners
as well since it can force you to go in late-game zones with few Life Stocks.
-Overall, the default settings are good for a beginner-friendly seed, and if you don't feel too confident, you can also
+Overall, the default options are good for a beginner-friendly seed, and if you don't feel too confident, you can also
lower the combat difficulty to make it more forgiving.
*Have fun on your adventure!*
diff --git a/worlds/landstalker/docs/landstalker_setup_en.md b/worlds/landstalker/docs/landstalker_setup_en.md
index 9f453c146de3..30f85dd8f19b 100644
--- a/worlds/landstalker/docs/landstalker_setup_en.md
+++ b/worlds/landstalker/docs/landstalker_setup_en.md
@@ -30,8 +30,10 @@ guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
### Where do I get a config file?
-The [Player Settings Page](../player-settings) on the website allows you to easily configure your personal settings
-and export a config file from them.
+
+The [Player Options Page](/games/Landstalker%20-%20The%20Treasures%20of%20King%20Nole/player-options) on the website allows
+you to easily configure your personal options.
+
## How-to-play
diff --git a/worlds/lingo/__init__.py b/worlds/lingo/__init__.py
index 088967445007..b749418368d1 100644
--- a/worlds/lingo/__init__.py
+++ b/worlds/lingo/__init__.py
@@ -5,12 +5,12 @@
from BaseClasses import Item, ItemClassification, Tutorial
from worlds.AutoWorld import WebWorld, World
-from .items import ALL_ITEM_TABLE, LingoItem
-from .locations import ALL_LOCATION_TABLE
+from .datatypes import Room, RoomEntrance
+from .items import ALL_ITEM_TABLE, ITEMS_BY_GROUP, TRAP_ITEMS, LingoItem
+from .locations import ALL_LOCATION_TABLE, LOCATIONS_BY_GROUP
from .options import LingoOptions
from .player_logic import LingoPlayerLogic
from .regions import create_regions
-from .static_logic import Room, RoomEntrance
class LingoWebWorld(WebWorld):
@@ -46,6 +46,8 @@ class LingoWorld(World):
location_name_to_id = {
name: data.code for name, data in ALL_LOCATION_TABLE.items()
}
+ item_name_groups = ITEMS_BY_GROUP
+ location_name_groups = LOCATIONS_BY_GROUP
player_logic: LingoPlayerLogic
@@ -82,18 +84,30 @@ def create_items(self):
skips = int(non_traps * skip_percentage / 100.0)
non_skips = non_traps - skips
- filler_list = [":)", "The Feeling of Being Lost", "Wanderlust", "Empty White Hallways"]
for i in range(0, non_skips):
- pool.append(self.create_item(filler_list[i % len(filler_list)]))
+ pool.append(self.create_item(self.get_filler_item_name()))
for i in range(0, skips):
pool.append(self.create_item("Puzzle Skip"))
if traps:
- traps_list = ["Slowness Trap", "Iceland Trap", "Atbash Trap"]
+ total_weight = sum(self.options.trap_weights.values())
- for i in range(0, traps):
- pool.append(self.create_item(traps_list[i % len(traps_list)]))
+ if total_weight == 0:
+ raise Exception("Sum of trap weights must be at least one.")
+
+ trap_counts = {name: int(weight * traps / total_weight)
+ for name, weight in self.options.trap_weights.items()}
+
+ trap_difference = traps - sum(trap_counts.values())
+ if trap_difference > 0:
+ allowed_traps = [name for name in TRAP_ITEMS if self.options.trap_weights[name] > 0]
+ for i in range(0, trap_difference):
+ trap_counts[allowed_traps[i % len(allowed_traps)]] += 1
+
+ for name, count in trap_counts.items():
+ for i in range(0, count):
+ pool.append(self.create_item(name))
self.multiworld.itempool += pool
@@ -101,9 +115,9 @@ def create_item(self, name: str) -> Item:
item = ALL_ITEM_TABLE[name]
classification = item.classification
- if hasattr(self, "options") and self.options.shuffle_paintings and len(item.painting_ids) > 0\
- and len(item.door_ids) == 0 and all(painting_id not in self.player_logic.painting_mapping
- for painting_id in item.painting_ids)\
+ if hasattr(self, "options") and self.options.shuffle_paintings and len(item.painting_ids) > 0 \
+ and not item.has_doors and all(painting_id not in self.player_logic.painting_mapping
+ for painting_id in item.painting_ids) \
and "pilgrim_painting2" not in item.painting_ids:
# If this is a "door" that just moves one or more paintings, and painting shuffle is on and those paintings
# go nowhere, then this item should not be progression. The Pilgrim Room painting is special and needs to be
@@ -130,3 +144,7 @@ def fill_slot_data(self):
slot_data["painting_entrance_to_exit"] = self.player_logic.painting_mapping
return slot_data
+
+ def get_filler_item_name(self) -> str:
+ filler_list = [":)", "The Feeling of Being Lost", "Wanderlust", "Empty White Hallways"]
+ return self.random.choice(filler_list)
diff --git a/worlds/lingo/data/LL1.yaml b/worlds/lingo/data/LL1.yaml
index ea5886fea00e..f2d2a9ff5448 100644
--- a/worlds/lingo/data/LL1.yaml
+++ b/worlds/lingo/data/LL1.yaml
@@ -63,12 +63,13 @@
# - item_name: Overrides the name of the item generated for this door.
# If not specified, the item name will be generated from
# the room name and the door name.
+ # - item_group: If set, this item will be in the specified item group.
# - location_name: Overrides the name of the location generated for this
# door. If not specified, the location name will be
# generated using the names of the panels.
# - skip_location: If true, no location is generated for this door.
# - skip_item: If true, no item is generated for this door.
- # - group: When simple doors is used, all doors with the same group
+ # - door_group: When simple doors is used, all doors with the same group
# will be covered by a single item.
# - include_reduce: Door checks are assumed to be EXCLUDED when reduce checks
# is on. This option includes the check anyway.
@@ -112,6 +113,7 @@
HI:
id: Entry Room/Panel_hi_hi
tag: midwhite
+ check: True
HIDDEN:
id: Entry Room/Panel_hidden_hidden
tag: midwhite
@@ -143,7 +145,7 @@
- Palindrome Room Area Doors/Door_racecar_racecar_2
- Palindrome Room Area Doors/Door_solos_solos_2
skip_location: True
- group: Rhyme Room Doors
+ door_group: Rhyme Room Doors
panels:
- room: The Tenacious
panel: LEVEL (Black)
@@ -230,7 +232,7 @@
Dead End Door:
id: Appendix Room Area Doors/Door_rat_tar_2
skip_location: true
- group: Dead End Area Access
+ door_group: Dead End Area Access
panels:
- room: Hub Room
panel: RAT
@@ -243,6 +245,7 @@
Seeker Entrance:
id: Entry Room Area Doors/Door_entrance_entrance
item_name: The Seeker - Entrance
+ item_group: Achievement Room Entrances
panels:
- OPEN
Rhyme Room Entrance:
@@ -250,7 +253,7 @@
- Appendix Room Area Doors/Door_rat_tar_3
- Double Room Area Doors/Door_room_entry_stairs
skip_location: True
- group: Rhyme Room Doors
+ door_group: Rhyme Room Doors
panels:
- room: The Tenacious
panel: LEVEL (Black)
@@ -278,10 +281,10 @@
tag: forbid
check: True
achievement: The Seeker
- BEAR:
+ BEAR (1):
id: Heteronym Room/Panel_bear_bear
tag: midwhite
- MINE:
+ MINE (1):
id: Heteronym Room/Panel_mine_mine
tag: double midwhite
subtag: left
@@ -297,7 +300,7 @@
DOES:
id: Heteronym Room/Panel_does_does
tag: midwhite
- MOBILE:
+ MOBILE (1):
id: Heteronym Room/Panel_mobile_mobile
tag: double midwhite
subtag: left
@@ -399,8 +402,7 @@
door: Crossroads Entrance
The Tenacious:
door: Tenacious Entrance
- Warts Straw Area:
- door: Symmetry Door
+ Near Far Area: True
Hedge Maze:
door: Shortcut to Hedge Maze
Orange Tower First Floor:
@@ -427,14 +429,6 @@
id: Palindrome Room/Panel_slaughter_laughter
colors: red
tag: midred
- NEAR:
- id: Symmetry Room/Panel_near_far
- colors: black
- tag: botblack
- FAR:
- id: Symmetry Room/Panel_far_near
- colors: black
- tag: botblack
TRACE:
id: Maze Room/Panel_trace_trace
tag: midwhite
@@ -458,6 +452,7 @@
id: Shuffle Room/Panel_lost_found
colors: black
tag: botblack
+ check: True
FORWARD:
id: Entry Room/Panel_forward_forward
tag: midwhite
@@ -469,45 +464,39 @@
tag: midwhite
doors:
Crossroads Entrance:
- id: Shuffle Room Area Doors/Door_chaos
+ id:
+ - Shuffle Room Area Doors/Door_chaos
+ - Shuffle Room Area Doors/Door_swap
+ - Shuffle Room Area Doors/Door_swap2
+ - Shuffle Room Area Doors/Door_swap3
+ - Shuffle Room Area Doors/Door_swap4
panels:
- ORDER
Tenacious Entrance:
id: Palindrome Room Area Doors/Door_slaughter_laughter
- group: Entrances to The Tenacious
+ door_group: Entrances to The Tenacious
+ item_group: Achievement Room Entrances
panels:
- SLAUGHTER
- Symmetry Door:
- id:
- - Symmetry Room Area Doors/Door_near_far
- - Symmetry Room Area Doors/Door_far_near
- group: Symmetry Doors
- panels:
- - NEAR
- - FAR
Shortcut to Hedge Maze:
id: Maze Area Doors/Door_trace_trace
- group: Hedge Maze Doors
+ door_group: Hedge Maze Doors
panels:
- TRACE
Near RAT Door:
id: Appendix Room Area Doors/Door_deadend_deadened
skip_location: True
- group: Dead End Area Access
+ door_group: Dead End Area Access
panels:
- room: Hidden Room
panel: DEAD END
Traveled Entrance:
id: Appendix Room Area Doors/Door_open_open
item_name: The Traveled - Entrance
- group: Entrance to The Traveled
+ door_group: Entrance to The Traveled
+ item_group: Achievement Room Entrances
panels:
- OPEN
- Lost Door:
- id: Shuffle Room Area Doors/Door_lost_found
- junk_item: True
- panels:
- - LOST
paintings:
- id: maze_painting
orientation: west
@@ -548,7 +537,7 @@
id: Lingo Room/Panel_shortcut
colors: yellow
tag: midyellow
- PILGRIMAGE:
+ PILGRIM:
id: Lingo Room/Panel_pilgrim
colors: blue
tag: midblue
@@ -562,6 +551,7 @@
doors:
Sun Painting:
item_name: Pilgrim Room - Sun Painting
+ item_group: Paintings
location_name: Pilgrim Room - HOT CRUST
painting_id: pilgrim_painting2
panels:
@@ -569,7 +559,7 @@
Exit:
event: True
panels:
- - PILGRIMAGE
+ - PILGRIM
Pilgrim Room:
entrances:
The Seeker:
@@ -710,6 +700,8 @@
door: Hollow Hallway
tag: midwhite
SWAP:
+ # In vanilla doors, solving this panel will open the way to Hub Room. This does not impact logic at all because
+ # Hub Room is always sphere 1 in vanilla doors.
id: Shuffle Room/Panel_swap_wasp
colors: yellow
tag: midyellow
@@ -731,12 +723,14 @@
doors:
Tenacious Entrance:
id: Palindrome Room Area Doors/Door_decay_day
- group: Entrances to The Tenacious
+ door_group: Entrances to The Tenacious
+ item_group: Achievement Room Entrances
panels:
- DECAY
Discerning Entrance:
id: Shuffle Room Area Doors/Door_nope_open
item_name: The Discerning - Entrance
+ item_group: Achievement Room Entrances
panels:
- NOPE
Tower Entrance:
@@ -745,35 +739,35 @@
- Shuffle Room Area Doors/Door_tower2
- Shuffle Room Area Doors/Door_tower3
- Shuffle Room Area Doors/Door_tower4
- group: Crossroads - Tower Entrances
+ door_group: Crossroads - Tower Entrances
panels:
- WE ROT
Tower Back Entrance:
id: Shuffle Room Area Doors/Door_runt
location_name: Crossroads - TURN/RUNT
- group: Crossroads - Tower Entrances
+ door_group: Crossroads - Tower Entrances
panels:
- TURN
- room: Orange Tower Fourth Floor
- panel: RUNT
+ panel: RUNT (1)
Words Sword Door:
id:
- Shuffle Room Area Doors/Door_words_shuffle_3
- Shuffle Room Area Doors/Door_words_shuffle_4
- group: Crossroads Doors
+ door_group: Crossroads Doors
panels:
- WORDS
- SWORD
Eye Wall:
id: Shuffle Room Area Doors/Door_behind
junk_item: True
- group: Crossroads Doors
+ door_group: Crossroads Doors
panels:
- BEND HI
Hollow Hallway:
id: Shuffle Room Area Doors/Door_crossroads6
skip_location: True
- group: Crossroads Doors
+ door_group: Crossroads Doors
panels:
- BEND HI
Roof Access:
@@ -950,7 +944,7 @@
- Palindrome Room Area Doors/Door_racecar_racecar_1
- Palindrome Room Area Doors/Door_solos_solos_1
location_name: The Tenacious - Palindromes
- group: Entrances to The Tenacious
+ door_group: Entrances to The Tenacious
panels:
- LEVEL (Black)
- RACECAR (Black)
@@ -962,11 +956,36 @@
- LEVEL (White)
- RACECAR (White)
- SOLOS (White)
+ Near Far Area:
+ entrances:
+ Hub Room: True
+ Warts Straw Area:
+ door: Door
+ panels:
+ NEAR:
+ id: Symmetry Room/Panel_near_far
+ colors: black
+ tag: botblack
+ FAR:
+ id: Symmetry Room/Panel_far_near
+ colors: black
+ tag: botblack
+ doors:
+ Door:
+ id:
+ - Symmetry Room Area Doors/Door_near_far
+ - Symmetry Room Area Doors/Door_far_near
+ door_group: Symmetry Doors
+ item_name: Symmetry Room - Near Far Door
+ location_name: Symmetry Room - NEAR, FAR
+ panels:
+ - NEAR
+ - FAR
Warts Straw Area:
entrances:
- Hub Room:
- room: Hub Room
- door: Symmetry Door
+ Near Far Area:
+ room: Near Far Area
+ door: Door
Leaf Feel Area:
door: Door
panels:
@@ -983,7 +1002,9 @@
id:
- Symmetry Room Area Doors/Door_warts_straw
- Symmetry Room Area Doors/Door_straw_warts
- group: Symmetry Doors
+ door_group: Symmetry Doors
+ item_name: Symmetry Room - Warts Straw Door
+ location_name: Symmetry Room - WARTS, STRAW
panels:
- WARTS
- STRAW
@@ -1008,7 +1029,9 @@
id:
- Symmetry Room Area Doors/Door_leaf_feel
- Symmetry Room Area Doors/Door_feel_leaf
- group: Symmetry Doors
+ door_group: Symmetry Doors
+ item_name: Symmetry Room - Leaf Feel Door
+ location_name: Symmetry Room - LEAF, FEEL
panels:
- LEAF
- FEEL
@@ -1118,7 +1141,13 @@
id: Cross Room/Panel_north_missing
colors: green
tag: forbid
- required_room: Outside The Bold
+ required_panel:
+ - room: Outside The Bold
+ panel: SOUND
+ - room: Outside The Bold
+ panel: YEAST
+ - room: Outside The Bold
+ panel: WET
DIAMONDS:
id: Cross Room/Panel_diamonds_missing
colors: green
@@ -1137,34 +1166,37 @@
doors:
Tenacious Entrance:
id: Palindrome Room Area Doors/Door_massacred_sacred
- group: Entrances to The Tenacious
+ door_group: Entrances to The Tenacious
+ item_group: Achievement Room Entrances
panels:
- MASSACRED
Black Door:
id: Symmetry Room Area Doors/Door_black_white
- group: Entrances to The Tenacious
+ door_group: Entrances to The Tenacious
panels:
- BLACK
Agreeable Entrance:
id: Symmetry Room Area Doors/Door_close_open
item_name: The Agreeable - Entrance
+ item_group: Achievement Room Entrances
panels:
- CLOSE
Painting Shortcut:
item_name: Starting Room - Street Painting
+ item_group: Paintings
painting_id: eyes_yellow_painting2
panels:
- RIGHT
Purple Barrier:
id: Color Arrow Room Doors/Door_purple_3
- group: Color Hunt Barriers
+ door_group: Color Hunt Barriers
skip_location: True
panels:
- - room: Champion's Rest
+ - room: Color Hunt
panel: PURPLE
Hallway Door:
id: Red Blue Purple Room Area Doors/Door_room_2
- group: Hallway Room Doors
+ door_group: Hallway Room Doors
location_name: Hallway Room - First Room
panels:
- WALL
@@ -1210,7 +1242,8 @@
doors:
Tenacious Entrance:
id: Palindrome Room Area Doors/Door_dread_dead
- group: Entrances to The Tenacious
+ door_group: Entrances to The Tenacious
+ item_group: Achievement Room Entrances
panels:
- DREAD
The Agreeable:
@@ -1268,7 +1301,7 @@
id: Antonym Room/Panel_star_rats
colors: black
tag: midblack
- TAME:
+ TUBE:
id: Antonym Room/Panel_tame_mate
colors: black
tag: topblack
@@ -1279,7 +1312,8 @@
doors:
Shortcut to Hedge Maze:
id: Symmetry Room Area Doors/Door_bye_hi
- group: Hedge Maze Doors
+ item_group: Achievement Room Entrances
+ door_group: Hedge Maze Doors
panels:
- BYE
Hedge Maze:
@@ -1372,12 +1406,14 @@
Perceptive Entrance:
id: Maze Area Doors/Door_maze_maze
item_name: The Perceptive - Entrance
- group: Hedge Maze Doors
+ door_group: Hedge Maze Doors
+ item_group: Achievement Room Entrances
panels:
- DOWN
Painting Shortcut:
painting_id: garden_painting_tower2
item_name: Starting Room - Hedge Maze Painting
+ item_group: Paintings
skip_location: True
panels:
- DOWN
@@ -1388,7 +1424,8 @@
- Maze Area Doors/Door_look_room_3
skip_location: True
item_name: The Observant - Entrance
- group: Observant Doors
+ door_group: Observant Doors
+ item_group: Achievement Room Entrances
panels:
- room: The Perceptive
panel: GAZE
@@ -1430,7 +1467,7 @@
entrances:
The Perceptive: True
panels:
- NAPS:
+ SPAN:
id: Naps Room/Panel_naps_span
colors: black
tag: midblack
@@ -1454,9 +1491,9 @@
Second Floor:
id: Naps Room Doors/Door_hider_5
location_name: The Fearless - First Floor Puzzles
- group: Fearless Doors
+ door_group: Fearless Doors
panels:
- - NAPS
+ - SPAN
- TEAM
- TEEM
- IMPATIENT
@@ -1506,7 +1543,7 @@
- Naps Room Doors/Door_hider_1b2
- Naps Room Doors/Door_hider_new1
location_name: The Fearless - Second Floor Puzzles
- group: Fearless Doors
+ door_group: Fearless Doors
panels:
- NONE
- SUM
@@ -1558,11 +1595,11 @@
required_door:
door: Stairs
achievement: The Observant
- BACK:
+ FOUR (1):
id: Look Room/Panel_four_back
colors: green
tag: forbid
- SIDE:
+ FOUR (2):
id: Look Room/Panel_four_side
colors: green
tag: forbid
@@ -1572,87 +1609,87 @@
hunt: True
required_door:
door: Backside Door
- STAIRS:
+ SIX:
id: Look Room/Panel_six_stairs
colors: green
tag: forbid
- WAYS:
+ FOUR (3):
id: Look Room/Panel_four_ways
colors: green
tag: forbid
- "ON":
+ TWO (1):
id: Look Room/Panel_two_on
colors: green
tag: forbid
- UP:
+ TWO (2):
id: Look Room/Panel_two_up
colors: green
tag: forbid
- SWIMS:
+ FIVE:
id: Look Room/Panel_five_swims
colors: green
tag: forbid
- UPSTAIRS:
+ BELOW (1):
id: Look Room/Panel_eight_upstairs
colors: green
tag: forbid
required_door:
door: Stairs
- TOIL:
+ BLUE:
id: Look Room/Panel_blue_toil
colors: green
tag: forbid
required_door:
door: Stairs
- STOP:
+ BELOW (2):
id: Look Room/Panel_four_stop
colors: green
tag: forbid
required_door:
door: Stairs
- TOP:
+ MINT (1):
id: Look Room/Panel_aqua_top
colors: green
tag: forbid
required_door:
door: Stairs
- HI:
+ ESACREWOL:
id: Look Room/Panel_blue_hi
colors: green
tag: forbid
required_door:
door: Stairs
- HI (2):
+ EULB:
id: Look Room/Panel_blue_hi2
colors: green
tag: forbid
required_door:
door: Stairs
- "31":
+ NUMBERS (1):
id: Look Room/Panel_numbers_31
colors: green
tag: forbid
required_door:
door: Stairs
- "52":
+ NUMBERS (2):
id: Look Room/Panel_numbers_52
colors: green
tag: forbid
required_door:
door: Stairs
- OIL:
+ MINT (2):
id: Look Room/Panel_aqua_oil
colors: green
tag: forbid
required_door:
door: Stairs
- BACKSIDE (GREEN):
+ GREEN (1):
id: Look Room/Panel_eight_backside
colors: green
tag: forbid
required_door:
door: Stairs
- SIDEWAYS:
+ GREEN (2):
id: Look Room/Panel_eight_sideways
colors: green
tag: forbid
@@ -1661,15 +1698,15 @@
doors:
Backside Door:
id: Maze Area Doors/Door_backside
- group: Backside Doors
+ door_group: Backside Doors
panels:
- - BACK
- - SIDE
+ - FOUR (1)
+ - FOUR (2)
Stairs:
id: Maze Area Doors/Door_stairs
- group: Observant Doors
+ door_group: Observant Doors
panels:
- - STAIRS
+ - SIX
The Incomparable:
entrances:
The Observant: True # Assuming that access to The Observant includes access to the right entrance
@@ -1745,7 +1782,7 @@
Eight Door:
id: Red Blue Purple Room Area Doors/Door_a_strands
location_name: Giant Sevens
- group: Observant Doors
+ door_group: Observant Doors
panels:
- I (Seven)
- room: Courtyard
@@ -1896,13 +1933,13 @@
doors:
Shortcut to Hub Room:
id: Shuffle Room Area Doors/Door_secret_secret
- group: Orange Tower First Floor - Shortcuts
+ door_group: Orange Tower First Floor - Shortcuts
panels:
- SECRET
Salt Pepper Door:
id: Count Up Room Area Doors/Door_salt_pepper
location_name: Orange Tower First Floor - Salt Pepper Door
- group: Orange Tower First Floor - Shortcuts
+ door_group: Orange Tower First Floor - Shortcuts
panels:
- SALT
- room: Directional Gallery
@@ -1933,6 +1970,7 @@
door: Shortcut to Tower
Rhyme Room (Smiley):
door: Rhyme Room Entrance
+ Art Gallery: True # mark this as a warp in the sunwarps branch
panels:
RED:
id: Color Arrow Room/Panel_red_afar
@@ -1948,15 +1986,15 @@
doors:
Red Barrier:
id: Color Arrow Room Doors/Door_red_6
- group: Color Hunt Barriers
+ door_group: Color Hunt Barriers
skip_location: True
panels:
- - room: Champion's Rest
+ - room: Color Hunt
panel: RED
Rhyme Room Entrance:
id: Double Room Area Doors/Door_room_entry_stairs2
skip_location: True
- group: Rhyme Room Doors
+ door_group: Rhyme Room Doors
panels:
- room: The Tenacious
panel: LEVEL (Black)
@@ -1969,9 +2007,9 @@
- Color Arrow Room Doors/Door_orange_hider_1
- Color Arrow Room Doors/Door_orange_hider_2
- Color Arrow Room Doors/Door_orange_hider_3
- location_name: Color Hunt - RED and YELLOW
- group: Champion's Rest - Color Barriers
- item_name: Champion's Rest - Orange Barrier
+ location_name: Color Barriers - RED and YELLOW
+ door_group: Color Hunt Barriers
+ item_name: Color Hunt - Orange Barrier
panels:
- RED
- room: Directional Gallery
@@ -1999,7 +2037,7 @@
Courtyard: True
Roof: True # through the sunwarp
panels:
- RUNT:
+ RUNT (1):
id: Shuffle Room/Panel_turn_runt2
colors: yellow
tag: midyellow
@@ -2131,7 +2169,7 @@
doors:
Welcome Back:
id: Entry Room Area Doors/Door_sizes
- group: Welcome Back Doors
+ door_group: Welcome Back Doors
panels:
- SIZE (Small)
- SIZE (Big)
@@ -2213,6 +2251,7 @@
- Master Room Doors/Door_master_down
- Master Room Doors/Door_master_down2
skip_location: True
+ item_name: Mastery
panels:
- THE MASTER
Mastery Panels:
@@ -2229,25 +2268,25 @@
panel: MASTERY
- room: Hedge Maze
panel: MASTERY (1)
- - room: Roof
- panel: MASTERY (1)
- - room: Roof
- panel: MASTERY (2)
+ - room: Behind A Smile
+ panel: MASTERY
+ - room: Sixteen Colorful Squares
+ panel: MASTERY
- MASTERY
- room: Hedge Maze
panel: MASTERY (2)
- - room: Roof
- panel: MASTERY (3)
- - room: Roof
- panel: MASTERY (4)
- - room: Roof
- panel: MASTERY (5)
+ - room: Among Treetops
+ panel: MASTERY
+ - room: Horizon's Edge
+ panel: MASTERY
+ - room: Beneath The Lookout
+ panel: MASTERY
- room: Elements Area
panel: MASTERY
- room: Pilgrim Antechamber
panel: MASTERY
- - room: Roof
- panel: MASTERY (6)
+ - room: Rooftop Staircase
+ panel: MASTERY
paintings:
- id: map_painting2
orientation: north
@@ -2259,52 +2298,75 @@
Crossroads:
room: Crossroads
door: Roof Access
+ Behind A Smile:
+ entrances:
+ Roof: True
panels:
- MASTERY (1):
+ MASTERY:
id: Master Room/Panel_mastery_mastery6
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
- MASTERY (2):
+ STAIRCASE:
+ id: Open Areas/Panel_staircase
+ tag: midwhite
+ Sixteen Colorful Squares:
+ entrances:
+ Roof: True
+ panels:
+ MASTERY:
id: Master Room/Panel_mastery_mastery7
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
- MASTERY (3):
+ Among Treetops:
+ entrances:
+ Roof: True
+ panels:
+ MASTERY:
id: Master Room/Panel_mastery_mastery10
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
- MASTERY (4):
+ Horizon's Edge:
+ entrances:
+ Roof: True
+ panels:
+ MASTERY:
id: Master Room/Panel_mastery_mastery11
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
- MASTERY (5):
+ Beneath The Lookout:
+ entrances:
+ Roof: True
+ panels:
+ MASTERY:
id: Master Room/Panel_mastery_mastery12
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
- MASTERY (6):
+ Rooftop Staircase:
+ entrances:
+ Roof: True
+ panels:
+ MASTERY:
id: Master Room/Panel_mastery_mastery15
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
- STAIRCASE:
- id: Open Areas/Panel_staircase
- tag: midwhite
Orange Tower Basement:
entrances:
Orange Tower Sixth Floor:
@@ -2361,6 +2423,7 @@
Painting Shortcut:
painting_id: flower_painting_8
item_name: Starting Room - Flower Painting
+ item_group: Paintings
skip_location: True
panels:
- room: First Second Third Fourth
@@ -2373,10 +2436,10 @@
panel: FOURTH
Green Barrier:
id: Color Arrow Room Doors/Door_green_5
- group: Color Hunt Barriers
+ door_group: Color Hunt Barriers
skip_location: True
panels:
- - room: Champion's Rest
+ - room: Color Hunt
panel: GREEN
paintings:
- id: flower_painting_7
@@ -2427,7 +2490,7 @@
doors:
Backside Door:
id: Count Up Room Area Doors/Door_yellow_backside
- group: Backside Doors
+ door_group: Backside Doors
location_name: Courtyard - FIRST, SECOND, THIRD, FOURTH
item_name: Courtyard - Backside Door
panels:
@@ -2448,7 +2511,7 @@
Progress Door:
id: Doorway Room Doors/Door_white
item_name: The Colorful - White Door
- group: Colorful Doors
+ door_group: Colorful Doors
location_name: The Colorful - White
panels:
- BEGIN
@@ -2469,7 +2532,7 @@
id: Doorway Room Doors/Door_black
item_name: The Colorful - Black Door
location_name: The Colorful - Black
- group: Colorful Doors
+ door_group: Colorful Doors
panels:
- FOUND
The Colorful (Red):
@@ -2489,7 +2552,7 @@
id: Doorway Room Doors/Door_red
item_name: The Colorful - Red Door
location_name: The Colorful - Red
- group: Colorful Doors
+ door_group: Colorful Doors
panels:
- LOAF
The Colorful (Yellow):
@@ -2509,7 +2572,7 @@
id: Doorway Room Doors/Door_yellow
item_name: The Colorful - Yellow Door
location_name: The Colorful - Yellow
- group: Colorful Doors
+ door_group: Colorful Doors
panels:
- CREAM
The Colorful (Blue):
@@ -2529,7 +2592,7 @@
id: Doorway Room Doors/Door_blue
item_name: The Colorful - Blue Door
location_name: The Colorful - Blue
- group: Colorful Doors
+ door_group: Colorful Doors
panels:
- SUN
The Colorful (Purple):
@@ -2549,7 +2612,7 @@
id: Doorway Room Doors/Door_purple
item_name: The Colorful - Purple Door
location_name: The Colorful - Purple
- group: Colorful Doors
+ door_group: Colorful Doors
panels:
- SPOON
The Colorful (Orange):
@@ -2569,7 +2632,7 @@
id: Doorway Room Doors/Door_orange
item_name: The Colorful - Orange Door
location_name: The Colorful - Orange
- group: Colorful Doors
+ door_group: Colorful Doors
panels:
- LETTERS
The Colorful (Green):
@@ -2589,7 +2652,7 @@
id: Doorway Room Doors/Door_green
item_name: The Colorful - Green Door
location_name: The Colorful - Green
- group: Colorful Doors
+ door_group: Colorful Doors
panels:
- WALLS
The Colorful (Brown):
@@ -2609,7 +2672,7 @@
id: Doorway Room Doors/Door_brown
item_name: The Colorful - Brown Door
location_name: The Colorful - Brown
- group: Colorful Doors
+ door_group: Colorful Doors
panels:
- IRON
The Colorful (Gray):
@@ -2626,21 +2689,13 @@
tag: forbid
doors:
Progress Door:
- id:
- - Doorway Room Doors/Door_gray
- - Doorway Room Doors/Door_gray2 # See comment below
+ id: Doorway Room Doors/Door_gray
item_name: The Colorful - Gray Door
location_name: The Colorful - Gray
- group: Colorful Doors
+ door_group: Colorful Doors
panels:
- OBSTACLE
The Colorful:
- # The set of required_doors in the achievement panel should prevent
- # generation from asking you to solve The Colorful before opening all of the
- # doors. Access from the roof is included so that the painting here could be
- # an entrance. The client will have to be hardcoded to not open the door to
- # the achievement until all of the doors are open, whether by solving the
- # panels or through receiving items.
entrances:
The Colorful (Gray):
room: The Colorful (Gray)
@@ -2651,31 +2706,53 @@
id: Countdown Panels/Panel_colorful_colorful
check: True
tag: forbid
- required_door:
+ required_panel:
- room: The Colorful (White)
- door: Progress Door
+ panel: BEGIN
- room: The Colorful (Black)
- door: Progress Door
+ panel: FOUND
- room: The Colorful (Red)
- door: Progress Door
+ panel: LOAF
- room: The Colorful (Yellow)
- door: Progress Door
+ panel: CREAM
- room: The Colorful (Blue)
- door: Progress Door
+ panel: SUN
- room: The Colorful (Purple)
- door: Progress Door
+ panel: SPOON
- room: The Colorful (Orange)
- door: Progress Door
+ panel: LETTERS
- room: The Colorful (Green)
- door: Progress Door
+ panel: WALLS
- room: The Colorful (Brown)
- door: Progress Door
+ panel: IRON
- room: The Colorful (Gray)
- door: Progress Door
+ panel: OBSTACLE
achievement: The Colorful
paintings:
- id: arrows_painting_12
orientation: north
+ progression:
+ Progressive Colorful:
+ - room: The Colorful (White)
+ door: Progress Door
+ - room: The Colorful (Black)
+ door: Progress Door
+ - room: The Colorful (Red)
+ door: Progress Door
+ - room: The Colorful (Yellow)
+ door: Progress Door
+ - room: The Colorful (Blue)
+ door: Progress Door
+ - room: The Colorful (Purple)
+ door: Progress Door
+ - room: The Colorful (Orange)
+ door: Progress Door
+ - room: The Colorful (Green)
+ door: Progress Door
+ - room: The Colorful (Brown)
+ door: Progress Door
+ - room: The Colorful (Gray)
+ door: Progress Door
Welcome Back Area:
entrances:
Starting Room:
@@ -2711,7 +2788,7 @@
doors:
Shortcut to Starting Room:
id: Entry Room Area Doors/Door_return_return
- group: Welcome Back Doors
+ door_group: Welcome Back Doors
include_reduce: True
panels:
- WELCOME BACK
@@ -2736,7 +2813,7 @@
doors:
Shortcut to Hedge Maze:
id: Maze Area Doors/Door_strays_maze
- group: Hedge Maze Doors
+ door_group: Hedge Maze Doors
panels:
- STRAYS
paintings:
@@ -2762,6 +2839,7 @@
door: Exit
Eight Alcove:
door: Eight Door
+ The Optimistic: True
panels:
SEVEN (1):
id: Backside Room/Panel_seven_seven_5
@@ -2811,21 +2889,11 @@
id: Rhyme Room/Panel_locked_knocked
colors: purple
tag: midpurp
- BACKSIDE:
- id: Backside Room/Panel_backside_1
- tag: midwhite
- The Optimistic:
- id: Countdown Panels/Panel_optimistic_optimistic
- check: True
- tag: forbid
- required_door:
- door: Backsides
- achievement: The Optimistic
- PAST:
+ PAST (1):
id: Shuffle Room/Panel_past_present
colors: brown
tag: botbrown
- FUTURE:
+ FUTURE (1):
id: Shuffle Room/Panel_future_present
colors:
- brown
@@ -2868,21 +2936,22 @@
- UNCOVER
Blue Barrier:
id: Color Arrow Room Doors/Door_blue_3
- group: Color Hunt Barriers
+ door_group: Color Hunt Barriers
skip_location: True
panels:
- - room: Champion's Rest
+ - room: Color Hunt
panel: BLUE
Orange Barrier:
id: Color Arrow Room Doors/Door_orange_3
- group: Color Hunt Barriers
+ door_group: Color Hunt Barriers
skip_location: True
panels:
- - room: Champion's Rest
+ - room: Color Hunt
panel: ORANGE
Initiated Entrance:
id: Red Blue Purple Room Area Doors/Door_locked_knocked
item_name: The Initiated - Entrance
+ item_group: Achievement Room Entrances
panels:
- OXEN
# These would be more appropriate in Champion's Rest, but as currently
@@ -2890,9 +2959,9 @@
# containing region.
Green Barrier:
id: Color Arrow Room Doors/Door_green_hider_1
- location_name: Color Hunt - BLUE and YELLOW
- item_name: Champion's Rest - Green Barrier
- group: Champion's Rest - Color Barriers
+ location_name: Color Barriers - BLUE and YELLOW
+ item_name: Color Hunt - Green Barrier
+ door_group: Color Hunt Barriers
panels:
- BLUE
- room: Directional Gallery
@@ -2902,9 +2971,9 @@
- Color Arrow Room Doors/Door_purple_hider_1
- Color Arrow Room Doors/Door_purple_hider_2
- Color Arrow Room Doors/Door_purple_hider_3
- location_name: Color Hunt - RED and BLUE
- item_name: Champion's Rest - Purple Barrier
- group: Champion's Rest - Color Barriers
+ location_name: Color Barriers - RED and BLUE
+ item_name: Color Hunt - Purple Barrier
+ door_group: Color Hunt Barriers
panels:
- BLUE
- room: Orange Tower Third Floor
@@ -2914,7 +2983,7 @@
- Color Arrow Room Doors/Door_all_hider_1
- Color Arrow Room Doors/Door_all_hider_2
- Color Arrow Room Doors/Door_all_hider_3
- location_name: Color Hunt - GREEN, ORANGE and PURPLE
+ location_name: Color Barriers - GREEN, ORANGE and PURPLE
item_name: Champion's Rest - Entrance
panels:
- ORANGE
@@ -2922,19 +2991,9 @@
panel: GREEN
- room: Outside The Agreeable
panel: PURPLE
- Backsides:
- event: True
- panels:
- - room: The Observant
- panel: BACKSIDE
- - room: Yellow Backside Area
- panel: BACKSIDE
- - room: Directional Gallery
- panel: BACKSIDE
- - room: The Bearer
- panel: BACKSIDE
Eight Door:
id: Red Blue Purple Room Area Doors/Door_a_strands2
+ item_group: Achievement Room Entrances
skip_location: True
panels:
- room: The Incomparable
@@ -3042,6 +3101,28 @@
id: Rhyme Room/Panel_bed_dead
colors: purple
tag: toppurp
+ The Optimistic:
+ entrances:
+ Outside The Initiated: True
+ panels:
+ BACKSIDE:
+ id: Backside Room/Panel_backside_1
+ tag: midwhite
+ Achievement:
+ id: Countdown Panels/Panel_optimistic_optimistic
+ check: True
+ tag: forbid
+ required_panel:
+ - panel: BACKSIDE
+ - room: The Observant
+ panel: BACKSIDE
+ - room: Yellow Backside Area
+ panel: BACKSIDE
+ - room: Directional Gallery
+ panel: BACKSIDE
+ - room: The Bearer
+ panel: BACKSIDE
+ achievement: The Optimistic
The Traveled:
entrances:
Hub Room:
@@ -3130,7 +3211,8 @@
doors:
Color Hallways Entrance:
id: Appendix Room Area Doors/Door_hello_hi
- group: Entrance to The Traveled
+ door_group: Entrance to The Traveled
+ item_group: Achievement Room Entrances
panels:
- HELLO
Color Hallways:
@@ -3142,7 +3224,7 @@
Outside The Undeterred: True
Crossroads: True
Hedge Maze: True
- Outside The Initiated: True # backside
+ The Optimistic: True # backside
Directional Gallery: True # backside
Yellow Backside Area: True
The Bearer:
@@ -3154,12 +3236,12 @@
Outside The Bold:
entrances:
Color Hallways: True
- Champion's Rest:
- room: Champion's Rest
+ Color Hunt:
+ room: Color Hunt
door: Shortcut to The Steady
The Bearer:
room: The Bearer
- door: Shortcut to The Bold
+ door: Entrance
Directional Gallery:
# There is a painting warp here from the Directional Gallery, but it
# only appears when the sixes are revealed. It could be its own item if
@@ -3230,7 +3312,7 @@
tag: midwhite
required_door:
door: Stargazer Door
- MOUTH:
+ SOUND:
id: Cross Room/Panel_mouth_south
colors: purple
tag: midpurp
@@ -3246,17 +3328,20 @@
Bold Entrance:
id: Red Blue Purple Room Area Doors/Door_unopened_open
item_name: The Bold - Entrance
+ item_group: Achievement Room Entrances
panels:
- UNOPEN
Painting Shortcut:
painting_id: pencil_painting6
skip_location: True
item_name: Starting Room - Pencil Painting
+ item_group: Paintings
panels:
- UNOPEN
Steady Entrance:
id: Rock Room Doors/Door_2
item_name: The Steady - Entrance
+ item_group: Achievement Room Entrances
panels:
- BEGIN
Lilac Entrance:
@@ -3477,6 +3562,7 @@
Undeterred Entrance:
id: Red Blue Purple Room Area Doors/Door_pen_open
item_name: The Undeterred - Entrance
+ item_group: Achievement Room Entrances
panels:
- PEN
Painting Shortcut:
@@ -3485,11 +3571,13 @@
- arrows_painting3
skip_location: True
item_name: Starting Room - Blue Painting
+ item_group: Paintings
panels:
- PEN
Green Painting:
painting_id: maze_painting_3
skip_location: True
+ item_group: Paintings
panels:
- FOUR
Twos:
@@ -3497,6 +3585,7 @@
- Count Up Room Area Doors/Door_two_hider
- Count Up Room Area Doors/Door_two_hider_2
include_reduce: True
+ item_group: Numbers
panels:
- ONE
Threes:
@@ -3506,6 +3595,7 @@
- Count Up Room Area Doors/Door_three_hider_3
location_name: Twos
include_reduce: True
+ item_group: Numbers
panels:
- TWO (1)
- TWO (2)
@@ -3524,6 +3614,7 @@
- Count Up Room Area Doors/Door_four_hider_3
- Count Up Room Area Doors/Door_four_hider_4
skip_location: True
+ item_group: Numbers
panels:
- THREE (1)
- THREE (2)
@@ -3535,6 +3626,7 @@
- Count Up Room Area Doors/Door_five_hider_5
location_name: Fours
item_name: Number Hunt - Fives
+ item_group: Numbers
include_reduce: True
panels:
- FOUR
@@ -3547,6 +3639,7 @@
Challenge Entrance:
id: Count Up Room Area Doors/Door_zero_unlocked
item_name: Number Hunt - Challenge Entrance
+ item_group: Achievement Room Entrances
panels:
- ZERO
paintings:
@@ -3574,7 +3667,7 @@
id: Blue Room/Panel_bone_skeleton
colors: blue
tag: botblue
- EYE:
+ EYE (1):
id: Blue Room/Panel_mouth_face
colors: blue
tag: double botblue
@@ -3693,7 +3786,7 @@
doors:
Door to Directional Gallery:
id: Count Up Room Area Doors/Door_five_unlocked
- group: Directional Gallery Doors
+ door_group: Directional Gallery Doors
skip_location: True
panels:
- FIVE
@@ -3707,6 +3800,7 @@
- Count Up Room Area Doors/Door_six_hider_6
painting_id: pencil_painting3 # See note in Outside The Bold
location_name: Fives
+ item_group: Numbers
include_reduce: True
panels:
- FIVE
@@ -3729,6 +3823,7 @@
- Count Up Room Area Doors/Door_seven_hider_6
- Count Up Room Area Doors/Door_seven_hider_7
location_name: Sixes
+ item_group: Numbers
include_reduce: True
panels:
- SIX
@@ -3754,6 +3849,7 @@
- Count Up Room Area Doors/Door_eight_hider_7
- Count Up Room Area Doors/Door_eight_hider_8
location_name: Sevens
+ item_group: Numbers
include_reduce: True
panels:
- SEVEN
@@ -3781,6 +3877,7 @@
- Count Up Room Area Doors/Door_nine_hider_8
- Count Up Room Area Doors/Door_nine_hider_9
location_name: Eights
+ item_group: Numbers
include_reduce: True
panels:
- EIGHT
@@ -3803,6 +3900,7 @@
id: Count Up Room Area Doors/Door_zero_hider_2
location_name: Nines
item_name: Outside The Undeterred - Zero Door
+ item_group: Numbers
include_reduce: True
panels:
- NINE
@@ -3971,16 +4069,16 @@
doors:
Shortcut to The Undeterred:
id: Count Up Room Area Doors/Door_return_double
- group: Directional Gallery Doors
+ door_group: Directional Gallery Doors
panels:
- TURN
- LEARN
Yellow Barrier:
id: Color Arrow Room Doors/Door_yellow_4
- group: Color Hunt Barriers
+ door_group: Color Hunt Barriers
skip_location: True
panels:
- - room: Champion's Rest
+ - room: Color Hunt
panel: YELLOW
paintings:
- id: smile_painting_7
@@ -3998,12 +4096,15 @@
orientation: south
- id: cherry_painting
orientation: east
- Champion's Rest:
+ Color Hunt:
entrances:
Outside The Bold:
door: Shortcut to The Steady
Orange Tower Fourth Floor: True # sunwarp
Roof: True # through ceiling of sunwarp
+ Champion's Rest:
+ room: Outside The Initiated
+ door: Entrance
panels:
EXIT:
id: Rock Room/Panel_red_red
@@ -4044,11 +4145,28 @@
required_door:
room: Orange Tower Third Floor
door: Orange Barrier
- YOU:
- id: Color Arrow Room/Panel_you
+ doors:
+ Shortcut to The Steady:
+ id: Rock Room Doors/Door_hint
+ panels:
+ - EXIT
+ paintings:
+ - id: arrows_painting_7
+ orientation: east
+ - id: fruitbowl_painting3
+ orientation: west
+ enter_only: True
required_door:
room: Outside The Initiated
door: Entrance
+ Champion's Rest:
+ entrances:
+ Color Hunt:
+ room: Outside The Initiated
+ door: Entrance
+ panels:
+ YOU:
+ id: Color Arrow Room/Panel_you
check: True
colors: gray
tag: forbid
@@ -4056,53 +4174,24 @@
id: Color Arrow Room/Panel_me
colors: gray
tag: forbid
- required_door:
- room: Outside The Initiated
- door: Entrance
SECRET BLUE:
# Pretend this and the other two are white, because they are snipes.
# TODO: Extract them and randomize them?
id: Color Arrow Room/Panel_secret_blue
tag: forbid
- required_door:
- room: Outside The Initiated
- door: Entrance
SECRET YELLOW:
id: Color Arrow Room/Panel_secret_yellow
tag: forbid
- required_door:
- room: Outside The Initiated
- door: Entrance
SECRET RED:
id: Color Arrow Room/Panel_secret_red
tag: forbid
- required_door:
- room: Outside The Initiated
- door: Entrance
- doors:
- Shortcut to The Steady:
- id: Rock Room Doors/Door_hint
- panels:
- - EXIT
paintings:
- - id: arrows_painting_7
- orientation: east
- - id: fruitbowl_painting3
- orientation: west
- enter_only: True
- required_door:
- room: Outside The Initiated
- door: Entrance
- id: colors_painting
orientation: south
- enter_only: True
- required_door:
- room: Outside The Initiated
- door: Entrance
The Bearer:
entrances:
Outside The Bold:
- door: Shortcut to The Bold
+ door: Entrance
Orange Tower Fifth Floor:
room: Art Gallery
door: Exit
@@ -4179,13 +4268,14 @@
- yellow
tag: mid red yellow
doors:
- Shortcut to The Bold:
+ Entrance:
id: Red Blue Purple Room Area Doors/Door_middle_middle
+ item_group: Achievement Room Entrances
panels:
- MIDDLE
Backside Door:
id: Red Blue Purple Room Area Doors/Door_locked_knocked2 # yeah...
- group: Backside Doors
+ door_group: Backside Doors
panels:
- FARTHER
East Entrance:
@@ -4202,9 +4292,6 @@
SIX:
id: Backside Room/Panel_six_six_5
tag: midwhite
- colors:
- - red
- - yellow
hunt: True
required_door:
room: Number Hunt
@@ -4280,9 +4367,6 @@
SIX:
id: Backside Room/Panel_six_six_6
tag: midwhite
- colors:
- - red
- - yellow
hunt: True
required_door:
room: Number Hunt
@@ -4314,21 +4398,21 @@
door: Side Area Shortcut
Roof: True
panels:
- SNOW:
+ SMILE:
id: Cross Room/Panel_smile_lime
colors:
- red
- yellow
tag: mid yellow red
- SMILE:
+ required_panel:
+ room: The Bearer (North)
+ panel: WARTS
+ SNOW:
id: Cross Room/Panel_snow_won
colors:
- red
- yellow
tag: mid red yellow
- required_panel:
- room: The Bearer (North)
- panel: WARTS
doors:
Side Area Shortcut:
event: True
@@ -4404,9 +4488,14 @@
colors: blue
tag: forbid
required_panel:
- room: The Bearer (West)
- panel: SMILE
- required_room: Outside The Bold
+ - room: The Bearer (West)
+ panel: SMILE
+ - room: Outside The Bold
+ panel: SOUND
+ - room: Outside The Bold
+ panel: YEAST
+ - room: Outside The Bold
+ panel: WET
Cross Tower (South):
entrances: # No roof access
The Bearer (North):
@@ -4903,7 +4992,7 @@
colors:
- red
- blue
- tag: mid red blue
+ tag: chain mid red blue
required_panel:
- room: Knight Night (Right Lower Segment)
panel: ADJUST
@@ -5174,7 +5263,7 @@
- Ceiling Room Doors/Door_blue
- Ceiling Room Doors/Door_blue2
location_name: The Artistic - Smiley and Panda
- group: Artistic Doors
+ door_group: Artistic Doors
panels:
- FINE
- BLADE
@@ -5284,7 +5373,7 @@
- Ceiling Room Doors/Door_red
- Ceiling Room Doors/Door_red2
location_name: The Artistic - Panda and Lattice
- group: Artistic Doors
+ door_group: Artistic Doors
panels:
- EYE (Top)
- EYE (Bottom)
@@ -5395,7 +5484,7 @@
- Ceiling Room Doors/Door_black
- Ceiling Room Doors/Door_black2
location_name: The Artistic - Lattice and Apple
- group: Artistic Doors
+ door_group: Artistic Doors
panels:
- POSH
- MALL
@@ -5508,7 +5597,7 @@
- Ceiling Room Doors/Door_yellow
- Ceiling Room Doors/Door_yellow2
location_name: The Artistic - Apple and Smiley
- group: Artistic Doors
+ door_group: Artistic Doors
panels:
- SPRIG
- RELEASES
@@ -5672,7 +5761,7 @@
doors:
Exit:
id: Count Up Room Area Doors/Door_near_near
- group: Crossroads Doors
+ door_group: Crossroads Doors
panels:
- NEAR
paintings:
@@ -5713,6 +5802,7 @@
Wondrous Entrance:
id: Red Blue Purple Room Area Doors/Door_wonderland
item_name: The Wondrous - Entrance
+ item_group: Achievement Room Entrances
panels:
- SHRINK
The Wondrous (Doorknob):
@@ -5733,6 +5823,7 @@
- arrows_painting2
skip_location: True
item_name: Starting Room - Symmetry Painting
+ item_group: Paintings
panels:
- room: Outside The Wondrous
panel: SHRINK
@@ -5837,6 +5928,7 @@
doors:
Exit:
id: Red Blue Purple Room Area Doors/Door_wonderland_exit
+ item_group: Paintings
painting_id: arrows_painting_9
include_reduce: True
panels:
@@ -5906,7 +5998,7 @@
Exit:
id: Red Blue Purple Room Area Doors/Door_room_3
location_name: Hallway Room - Second Room
- group: Hallway Room Doors
+ door_group: Hallway Room Doors
panels:
- WISE
- CLOCK
@@ -5943,7 +6035,7 @@
Exit:
id: Red Blue Purple Room Area Doors/Door_room_4
location_name: Hallway Room - Third Room
- group: Hallway Room Doors
+ door_group: Hallway Room Doors
panels:
- TRANCE
- FORM
@@ -5965,7 +6057,7 @@
id:
- Red Blue Purple Room Area Doors/Door_room_5
- Red Blue Purple Room Area Doors/Door_room_6 # this is the connection to The Artistic
- group: Hallway Room Doors
+ door_group: Hallway Room Doors
location_name: Hallway Room - Fourth Room
panels:
- WHEEL
@@ -6033,6 +6125,7 @@
Wanderer Entrance:
id: Tower Room Area Doors/Door_wanderer_entrance
item_name: The Wanderer - Entrance
+ item_group: Achievement Room Entrances
panels:
- WANDERLUST
Tower Entrance:
@@ -6117,7 +6210,7 @@
id: Painting Room/Panel_our_four
colors: blue
tag: midblue
- ONE ROAD MANY TURNS:
+ ORDER:
id: Painting Room/Panel_order_onepathmanyturns
tag: forbid
colors:
@@ -6172,8 +6265,10 @@
Exit:
id: Tower Room Area Doors/Door_painting_exit
include_reduce: True
+ item_name: Orange Tower Fifth Floor - Quadruple Intersection
+ item_group: Achievement Room Entrances
panels:
- - ONE ROAD MANY TURNS
+ - ORDER
paintings:
- id: smile_painting_3
orientation: west
@@ -6191,7 +6286,6 @@
- Third Floor
- Fourth Floor
- Fifth Floor
- - Exit
Art Gallery (Second Floor):
entrances:
Art Gallery:
@@ -6368,7 +6462,7 @@
- Double Room Area Doors/Door_room_3a
- Double Room Area Doors/Door_room_3bc
skip_location: True
- group: Rhyme Room Doors
+ door_group: Rhyme Room Doors
panels:
- SCHEME
- FANTASY
@@ -6469,7 +6563,7 @@
Exit:
id: Double Room Area Doors/Door_room_exit
location_name: Rhyme Room (Cross) - Exit Puzzles
- group: Rhyme Room Doors
+ door_group: Rhyme Room Doors
panels:
- PLUMP
- BOUNCE
@@ -6532,7 +6626,7 @@
- Double Room Area Doors/Door_room_2b
- Double Room Area Doors/Door_room_3b
location_name: Rhyme Room - Circle/Smiley Wall
- group: Rhyme Room Doors
+ door_group: Rhyme Room Doors
panels:
- BIRD
- LETTER
@@ -6615,7 +6709,7 @@
- Double Room Area Doors/Door_room_2a
- Double Room Area Doors/Door_room_1c
location_name: Rhyme Room - Circle/Looped Square Wall
- group: Rhyme Room Doors
+ door_group: Rhyme Room Doors
panels:
- WALKED
- OBSTRUCTED
@@ -6634,7 +6728,7 @@
- Double Room Area Doors/Door_room_1a
- Double Room Area Doors/Door_room_5a
location_name: Rhyme Room - Cross/Looped Square Wall
- group: Rhyme Room Doors
+ door_group: Rhyme Room Doors
panels:
- SKIES
- SWELL
@@ -6653,7 +6747,7 @@
- Double Room Area Doors/Door_room_1b
- Double Room Area Doors/Door_room_4b
location_name: Rhyme Room - Target/Looped Square Wall
- group: Rhyme Room Doors
+ door_group: Rhyme Room Doors
panels:
- PENNED
- CLIMB
@@ -6666,7 +6760,7 @@
- room: Rhyme Room (Target)
panel: PISTOL
- room: Rhyme Room (Target)
- panel: QUARTZ
+ panel: GEM
Rhyme Room (Target):
entrances:
Rhyme Room (Smiley): # one-way
@@ -6694,7 +6788,7 @@
tag: syn rhyme
subtag: top
link: rhyme CRYSTAL
- QUARTZ:
+ GEM:
id: Double Room/Panel_crystal_syn
colors: purple
tag: syn rhyme
@@ -6716,10 +6810,10 @@
Door to Cross:
id: Double Room Area Doors/Door_room_4a
location_name: Rhyme Room (Target) - Puzzles Toward Cross
- group: Rhyme Room Doors
+ door_group: Rhyme Room Doors
panels:
- PISTOL
- - QUARTZ
+ - GEM
- INNOVATIVE (Top)
- INNOVATIVE (Bottom)
paintings:
@@ -6777,22 +6871,18 @@
id: Panel Room/Panel_room_floor_5
colors: gray
tag: forbid
- FLOOR (7):
+ FLOOR (6):
id: Panel Room/Panel_room_floor_7
colors: gray
tag: forbid
- FLOOR (8):
+ FLOOR (7):
id: Panel Room/Panel_room_floor_8
colors: gray
tag: forbid
- FLOOR (9):
+ FLOOR (8):
id: Panel Room/Panel_room_floor_9
colors: gray
tag: forbid
- FLOOR (10):
- id: Panel Room/Panel_room_floor_10
- colors: gray
- tag: forbid
CEILING (1):
id: Panel Room/Panel_room_ceiling_1
colors: gray
@@ -6813,6 +6903,10 @@
id: Panel Room/Panel_room_ceiling_5
colors: gray
tag: forbid
+ CEILING (6):
+ id: Panel Room/Panel_room_floor_10
+ colors: gray
+ tag: forbid
WALL (1):
id: Panel Room/Panel_room_wall_1
colors: gray
@@ -6918,10 +7012,12 @@
MASTERY:
id: Master Room/Panel_mastery_mastery
tag: midwhite
- colors: gray
required_door:
room: Orange Tower Seventh Floor
door: Mastery
+ required_panel:
+ room: Room Room
+ panel: WALL (2)
doors:
Excavation:
event: True
@@ -6965,6 +7061,7 @@
Wise Entrance:
id: Clock Room Area Doors/Door_time_start
item_name: The Wise - Entrance
+ item_group: Achievement Room Entrances
panels:
- KITTEN
- CAT
@@ -7071,7 +7168,7 @@
id: Hangry Room/Panel_red_top_3
colors: red
tag: topred
- FLUMMOXED:
+ FLUSTERED:
id: Hangry Room/Panel_red_top_4
colors: red
tag: topred
@@ -7218,6 +7315,7 @@
Scientific Entrance:
id: Red Blue Purple Room Area Doors/Door_chemistry_lab
item_name: The Scientific - Entrance
+ item_group: Achievement Room Entrances
panels:
- OPEN
The Scientific:
@@ -7574,7 +7672,7 @@
- black
- blue
tag: chain mid black blue
- BREAD:
+ CHEESE:
id: Challenge Room/Panel_bread_mold
colors: brown
tag: double botbrown
@@ -7621,7 +7719,7 @@
id: Challenge Room/Panel_double_anagram_5
colors: yellow
tag: midyellow
- FACTS:
+ FACTS (Chain):
id: Challenge Room/Panel_facts
colors:
- red
@@ -7631,18 +7729,18 @@
id: Challenge Room/Panel_facts2
colors: red
tag: forbid
- FACTS (3):
+ FACTS (2):
id: Challenge Room/Panel_facts3
tag: forbid
- FACTS (4):
+ FACTS (3):
id: Challenge Room/Panel_facts4
colors: blue
tag: forbid
- FACTS (5):
+ FACTS (4):
id: Challenge Room/Panel_facts5
colors: blue
tag: forbid
- FACTS (6):
+ FACTS (5):
id: Challenge Room/Panel_facts6
colors: blue
tag: forbid
@@ -7653,5 +7751,6 @@
doors:
Welcome Door:
id: Entry Room Area Doors/Door_challenge_challenge
+ item_group: Achievement Room Entrances
panels:
- WELCOME
diff --git a/worlds/lingo/data/README.md b/worlds/lingo/data/README.md
new file mode 100644
index 000000000000..fe834cef0d47
--- /dev/null
+++ b/worlds/lingo/data/README.md
@@ -0,0 +1,5 @@
+# lingo data
+
+The source of truth for the Lingo randomizer is (currently) the LL1.yaml and ids.yaml files located here. These files are used by the generator, the game client, and the tracker, in order to have logic that is consistent across them all.
+
+The generator does not actually read in the yaml files. Instead, a compiled datafile called generated.dat is also located in this directory. If you update LL1.yaml and/or ids.yaml, you must also regenerate the datafile using `python worlds/lingo/utils/pickle_static_data.py`. A unit test will fail if you don't.
diff --git a/worlds/lingo/data/generated.dat b/worlds/lingo/data/generated.dat
new file mode 100644
index 000000000000..c957e5d51c89
Binary files /dev/null and b/worlds/lingo/data/generated.dat differ
diff --git a/worlds/lingo/data/ids.yaml b/worlds/lingo/data/ids.yaml
index 3239f21854c4..d3307deaa300 100644
--- a/worlds/lingo/data/ids.yaml
+++ b/worlds/lingo/data/ids.yaml
@@ -31,12 +31,12 @@ panels:
LIES: 444408
The Seeker:
Achievement: 444409
- BEAR: 444410
- MINE: 444411
+ BEAR (1): 444410
+ MINE (1): 444411
MINE (2): 444412
BOW: 444413
DOES: 444414
- MOBILE: 444415
+ MOBILE (1): 444415
MOBILE (2): 444416
DESERT: 444417
DESSERT: 444418
@@ -57,8 +57,6 @@ panels:
Hub Room:
ORDER: 444432
SLAUGHTER: 444433
- NEAR: 444434
- FAR: 444435
TRACE: 444436
RAT: 444437
OPEN: 444438
@@ -72,7 +70,7 @@ panels:
EIGHT: 444445
Pilgrim Antechamber:
HOT CRUST: 444446
- PILGRIMAGE: 444447
+ PILGRIM: 444447
MASTERY: 444448
Pilgrim Room:
THIS: 444449
@@ -123,6 +121,9 @@ panels:
RACECAR (White): 444489
SOLOS (White): 444490
Achievement: 444491
+ Near Far Area:
+ NEAR: 444434
+ FAR: 444435
Warts Straw Area:
WARTS: 444492
STRAW: 444493
@@ -164,7 +165,7 @@ panels:
THAT: 444525
STRESSED: 444526
STAR: 444527
- TAME: 444528
+ TUBE: 444528
CAT: 444529
Hedge Maze:
DOWN: 444530
@@ -187,7 +188,7 @@ panels:
Achievement: 444546
GAZE: 444547
The Fearless (First Floor):
- NAPS: 444548
+ SPAN: 444548
TEAM: 444549
TEEM: 444550
IMPATIENT: 444551
@@ -208,25 +209,25 @@ panels:
EVEN: 444564
The Observant:
Achievement: 444565
- BACK: 444566
- SIDE: 444567
+ FOUR (1): 444566
+ FOUR (2): 444567
BACKSIDE: 444568
- STAIRS: 444569
- WAYS: 444570
- 'ON': 444571
- UP: 444572
- SWIMS: 444573
- UPSTAIRS: 444574
- TOIL: 444575
- STOP: 444576
- TOP: 444577
- HI: 444578
- HI (2): 444579
- '31': 444580
- '52': 444581
- OIL: 444582
- BACKSIDE (GREEN): 444583
- SIDEWAYS: 444584
+ SIX: 444569
+ FOUR (3): 444570
+ TWO (1): 444571
+ TWO (2): 444572
+ FIVE: 444573
+ BELOW (1): 444574
+ BLUE: 444575
+ BELOW (2): 444576
+ MINT (1): 444577
+ ESACREWOL: 444578
+ EULB: 444579
+ NUMBERS (1): 444580
+ NUMBERS (2): 444581
+ MINT (2): 444582
+ GREEN (1): 444583
+ GREEN (2): 444584
The Incomparable:
Achievement: 444585
A (One): 444586
@@ -254,7 +255,7 @@ panels:
RED: 444605
DEER + WREN: 444606
Orange Tower Fourth Floor:
- RUNT: 444607
+ RUNT (1): 444607
RUNT (2): 444608
LEARNS + UNSEW: 444609
HOT CRUSTS: 444610
@@ -279,14 +280,19 @@ panels:
THE END: 444620
THE MASTER: 444621
MASTERY: 444622
- Roof:
- MASTERY (1): 444623
- MASTERY (2): 444624
- MASTERY (3): 444625
- MASTERY (4): 444626
- MASTERY (5): 444627
- MASTERY (6): 444628
+ Behind A Smile:
+ MASTERY: 444623
STAIRCASE: 444629
+ Sixteen Colorful Squares:
+ MASTERY: 444624
+ Among Treetops:
+ MASTERY: 444625
+ Horizon's Edge:
+ MASTERY: 444626
+ Beneath The Lookout:
+ MASTERY: 444627
+ Rooftop Staircase:
+ MASTERY: 444628
Orange Tower Basement:
MASTERY: 444630
THE LIBRARY: 444631
@@ -341,16 +347,17 @@ panels:
ORANGE: 444663
UNCOVER: 444664
OXEN: 444665
- BACKSIDE: 444666
- The Optimistic: 444667
- PAST: 444668
- FUTURE: 444669
+ PAST (1): 444668
+ FUTURE (1): 444669
FUTURE (2): 444670
PAST (2): 444671
PRESENT: 444672
SMILE: 444673
ANGERED: 444674
VOTE: 444675
+ The Optimistic:
+ BACKSIDE: 444666
+ Achievement: 444667
The Initiated:
Achievement: 444676
DAUGHTER: 444677
@@ -400,7 +407,7 @@ panels:
ZEN: 444719
SON: 444720
STARGAZER: 444721
- MOUTH: 444722
+ SOUND: 444722
YEAST: 444723
WET: 444724
The Bold:
@@ -442,7 +449,7 @@ panels:
The Undeterred:
Achievement: 444759
BONE: 444760
- EYE: 444761
+ EYE (1): 444761
MOUTH: 444762
IRIS: 444763
EYE (2): 444764
@@ -489,7 +496,7 @@ panels:
WINDWARD: 444803
LIGHT: 444804
REWIND: 444805
- Champion's Rest:
+ Color Hunt:
EXIT: 444806
HUES: 444807
RED: 444808
@@ -498,6 +505,7 @@ panels:
GREEN: 444811
PURPLE: 444812
ORANGE: 444813
+ Champion's Rest:
YOU: 444814
ME: 444815
SECRET BLUE: 444816
@@ -523,8 +531,8 @@ panels:
TENT: 444832
BOWL: 444833
The Bearer (West):
- SNOW: 444834
- SMILE: 444835
+ SMILE: 444834
+ SNOW: 444835
Bearer Side Area:
SHORTCUT: 444836
POTS: 444837
@@ -719,7 +727,7 @@ panels:
TRUSTWORTHY: 444978
FREE: 444979
OUR: 444980
- ONE ROAD MANY TURNS: 444981
+ ORDER: 444981
Art Gallery (Second Floor):
HOUSE: 444982
PATH: 444983
@@ -777,7 +785,7 @@ panels:
WILD: 445028
KID: 445029
PISTOL: 445030
- QUARTZ: 445031
+ GEM: 445031
INNOVATIVE (Top): 445032
INNOVATIVE (Bottom): 445033
Room Room:
@@ -791,15 +799,15 @@ panels:
FLOOR (3): 445041
FLOOR (4): 445042
FLOOR (5): 445043
- FLOOR (7): 445044
- FLOOR (8): 445045
- FLOOR (9): 445046
- FLOOR (10): 445047
+ FLOOR (6): 445044
+ FLOOR (7): 445045
+ FLOOR (8): 445046
CEILING (1): 445048
CEILING (2): 445049
CEILING (3): 445050
CEILING (4): 445051
CEILING (5): 445052
+ CEILING (6): 445047
WALL (1): 445053
WALL (2): 445054
WALL (3): 445055
@@ -847,7 +855,7 @@ panels:
PANDEMIC (1): 445100
TRINITY: 445101
CHEMISTRY: 445102
- FLUMMOXED: 445103
+ FLUSTERED: 445103
PANDEMIC (2): 445104
COUNTERCLOCKWISE: 445105
FEARLESS: 445106
@@ -933,7 +941,7 @@ panels:
CORNER: 445182
STRAWBERRIES: 445183
GRUB: 445184
- BREAD: 445185
+ CHEESE: 445185
COLOR: 445186
WRITER: 445187
'02759': 445188
@@ -944,12 +952,12 @@ panels:
DUCK LOGO: 445193
AVIAN GREEN: 445194
FEVER TEAR: 445195
- FACTS: 445196
+ FACTS (Chain): 445196
FACTS (1): 445197
- FACTS (3): 445198
- FACTS (4): 445199
- FACTS (5): 445200
- FACTS (6): 445201
+ FACTS (2): 445198
+ FACTS (3): 445199
+ FACTS (4): 445200
+ FACTS (5): 445201
LAPEL SHEEP: 445202
doors:
Starting Room:
@@ -979,9 +987,6 @@ doors:
Tenacious Entrance:
item: 444426
location: 444433
- Symmetry Door:
- item: 444428
- location: 445204
Shortcut to Hedge Maze:
item: 444430
location: 444436
@@ -990,9 +995,6 @@ doors:
Traveled Entrance:
item: 444433
location: 444438
- Lost Door:
- item: 444435
- location: 444440
Pilgrim Antechamber:
Sun Painting:
item: 444436
@@ -1038,6 +1040,10 @@ doors:
location: 445210
White Palindromes:
location: 445211
+ Near Far Area:
+ Door:
+ item: 444428
+ location: 445204
Warts Straw Area:
Door:
item: 444451
@@ -1286,12 +1292,12 @@ doors:
location: 445246
Yellow Barrier:
item: 444538
- Champion's Rest:
+ Color Hunt:
Shortcut to The Steady:
item: 444539
location: 444806
The Bearer:
- Shortcut to The Bold:
+ Entrance:
item: 444540
location: 444820
Backside Door:
@@ -1442,7 +1448,6 @@ door_groups:
Fearless Doors: 444469
Backside Doors: 444473
Orange Tower First Floor - Shortcuts: 444484
- Champion's Rest - Color Barriers: 444489
Welcome Back Doors: 444492
Colorful Doors: 444498
Directional Gallery Doors: 444531
@@ -1452,3 +1457,4 @@ progression:
Progressive Fearless: 444470
Progressive Orange Tower: 444482
Progressive Art Gallery: 444563
+ Progressive Colorful: 444580
diff --git a/worlds/lingo/datatypes.py b/worlds/lingo/datatypes.py
new file mode 100644
index 000000000000..e9bf0a378039
--- /dev/null
+++ b/worlds/lingo/datatypes.py
@@ -0,0 +1,68 @@
+from typing import List, NamedTuple, Optional
+
+
+class RoomAndDoor(NamedTuple):
+ room: Optional[str]
+ door: str
+
+
+class RoomAndPanel(NamedTuple):
+ room: Optional[str]
+ panel: str
+
+
+class RoomEntrance(NamedTuple):
+ room: str # source room
+ door: Optional[RoomAndDoor]
+ painting: bool
+
+
+class Room(NamedTuple):
+ name: str
+ entrances: List[RoomEntrance]
+
+
+class Door(NamedTuple):
+ name: str
+ item_name: str
+ location_name: Optional[str]
+ panels: Optional[List[RoomAndPanel]]
+ skip_location: bool
+ skip_item: bool
+ has_doors: bool
+ painting_ids: List[str]
+ event: bool
+ door_group: Optional[str]
+ include_reduce: bool
+ junk_item: bool
+ item_group: Optional[str]
+
+
+class Panel(NamedTuple):
+ required_rooms: List[str]
+ required_doors: List[RoomAndDoor]
+ required_panels: List[RoomAndPanel]
+ colors: List[str]
+ check: bool
+ event: bool
+ exclude_reduce: bool
+ achievement: bool
+ non_counting: bool
+
+
+class Painting(NamedTuple):
+ id: str
+ room: str
+ enter_only: bool
+ exit_only: bool
+ required: bool
+ required_when_no_doors: bool
+ required_door: Optional[RoomAndDoor]
+ disable: bool
+ req_blocked: bool
+ req_blocked_when_no_doors: bool
+
+
+class Progression(NamedTuple):
+ item_name: str
+ index: int
diff --git a/worlds/lingo/docs/en_Lingo.md b/worlds/lingo/docs/en_Lingo.md
index cff0581d9b2f..c7e1bfc8192e 100644
--- a/worlds/lingo/docs/en_Lingo.md
+++ b/worlds/lingo/docs/en_Lingo.md
@@ -1,8 +1,8 @@
# Lingo
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
diff --git a/worlds/lingo/items.py b/worlds/lingo/items.py
index af24570f278e..7c7928cbab68 100644
--- a/worlds/lingo/items.py
+++ b/worlds/lingo/items.py
@@ -1,13 +1,9 @@
from typing import Dict, List, NamedTuple, Optional, TYPE_CHECKING
from BaseClasses import Item, ItemClassification
-from .options import ShuffleDoors
from .static_logic import DOORS_BY_ROOM, PROGRESSION_BY_ROOM, PROGRESSIVE_ITEMS, get_door_group_item_id, \
get_door_item_id, get_progressive_item_id, get_special_item_id
-if TYPE_CHECKING:
- from . import LingoWorld
-
class ItemData(NamedTuple):
"""
@@ -16,27 +12,9 @@ class ItemData(NamedTuple):
code: int
classification: ItemClassification
mode: Optional[str]
- door_ids: List[str]
+ has_doors: bool
painting_ids: List[str]
- def should_include(self, world: "LingoWorld") -> bool:
- if self.mode == "colors":
- return world.options.shuffle_colors > 0
- elif self.mode == "doors":
- return world.options.shuffle_doors != ShuffleDoors.option_none
- elif self.mode == "orange tower":
- # door shuffle is on and tower isn't progressive
- return world.options.shuffle_doors != ShuffleDoors.option_none \
- and not world.options.progressive_orange_tower
- elif self.mode == "complex door":
- return world.options.shuffle_doors == ShuffleDoors.option_complex
- elif self.mode == "door group":
- return world.options.shuffle_doors == ShuffleDoors.option_simple
- elif self.mode == "special":
- return False
- else:
- return True
-
class LingoItem(Item):
"""
@@ -46,14 +24,18 @@ class LingoItem(Item):
ALL_ITEM_TABLE: Dict[str, ItemData] = {}
+ITEMS_BY_GROUP: Dict[str, List[str]] = {}
+
+TRAP_ITEMS: List[str] = ["Slowness Trap", "Iceland Trap", "Atbash Trap"]
def load_item_data():
- global ALL_ITEM_TABLE
+ global ALL_ITEM_TABLE, ITEMS_BY_GROUP
for color in ["Black", "Red", "Blue", "Yellow", "Green", "Orange", "Gray", "Brown", "Purple"]:
ALL_ITEM_TABLE[color] = ItemData(get_special_item_id(color), ItemClassification.progression,
"colors", [], [])
+ ITEMS_BY_GROUP.setdefault("Colors", []).append(color)
door_groups: Dict[str, List[str]] = {}
for room_name, doors in DOORS_BY_ROOM.items():
@@ -61,45 +43,50 @@ def load_item_data():
if door.skip_item is True or door.event is True:
continue
- if door.group is None:
+ if door.door_group is None:
door_mode = "doors"
else:
door_mode = "complex door"
- door_groups.setdefault(door.group, []).extend(door.door_ids)
+ door_groups.setdefault(door.door_group, [])
if room_name in PROGRESSION_BY_ROOM and door_name in PROGRESSION_BY_ROOM[room_name]:
- if room_name == "Orange Tower":
- door_mode = "orange tower"
- else:
- door_mode = "special"
+ door_mode = "special"
ALL_ITEM_TABLE[door.item_name] = \
ItemData(get_door_item_id(room_name, door_name),
ItemClassification.filler if door.junk_item else ItemClassification.progression, door_mode,
- door.door_ids, door.painting_ids)
+ door.has_doors, door.painting_ids)
+ ITEMS_BY_GROUP.setdefault("Doors", []).append(door.item_name)
+
+ if door.item_group is not None:
+ ITEMS_BY_GROUP.setdefault(door.item_group, []).append(door.item_name)
for group, group_door_ids in door_groups.items():
ALL_ITEM_TABLE[group] = ItemData(get_door_group_item_id(group),
- ItemClassification.progression, "door group", group_door_ids, [])
+ ItemClassification.progression, "door group", True, [])
+ ITEMS_BY_GROUP.setdefault("Doors", []).append(group)
special_items: Dict[str, ItemClassification] = {
":)": ItemClassification.filler,
"The Feeling of Being Lost": ItemClassification.filler,
"Wanderlust": ItemClassification.filler,
"Empty White Hallways": ItemClassification.filler,
- "Slowness Trap": ItemClassification.trap,
- "Iceland Trap": ItemClassification.trap,
- "Atbash Trap": ItemClassification.trap,
+ **{trap_name: ItemClassification.trap for trap_name in TRAP_ITEMS},
"Puzzle Skip": ItemClassification.useful,
}
for item_name, classification in special_items.items():
ALL_ITEM_TABLE[item_name] = ItemData(get_special_item_id(item_name), classification,
- "special", [], [])
+ "special", False, [])
+
+ if classification == ItemClassification.filler:
+ ITEMS_BY_GROUP.setdefault("Junk", []).append(item_name)
+ elif classification == ItemClassification.trap:
+ ITEMS_BY_GROUP.setdefault("Traps", []).append(item_name)
for item_name in PROGRESSIVE_ITEMS:
ALL_ITEM_TABLE[item_name] = ItemData(get_progressive_item_id(item_name),
- ItemClassification.progression, "special", [], [])
+ ItemClassification.progression, "special", False, [])
# Initialize the item data at module scope.
diff --git a/worlds/lingo/locations.py b/worlds/lingo/locations.py
index 5903d603ec4f..92ee309487a5 100644
--- a/worlds/lingo/locations.py
+++ b/worlds/lingo/locations.py
@@ -2,7 +2,8 @@
from typing import Dict, List, NamedTuple
from BaseClasses import Location
-from .static_logic import DOORS_BY_ROOM, PANELS_BY_ROOM, RoomAndPanel, get_door_location_id, get_panel_location_id
+from .datatypes import RoomAndPanel
+from .static_logic import DOORS_BY_ROOM, PANELS_BY_ROOM, get_door_location_id, get_panel_location_id
class LocationClassification(Flag):
@@ -20,14 +21,6 @@ class LocationData(NamedTuple):
panels: List[RoomAndPanel]
classification: LocationClassification
- def panel_ids(self):
- ids = set()
- for panel in self.panels:
- effective_room = self.room if panel.room is None else panel.room
- panel_data = PANELS_BY_ROOM[effective_room][panel.panel]
- ids = ids | set(panel_data.internal_ids)
- return ids
-
class LingoLocation(Location):
"""
@@ -37,10 +30,11 @@ class LingoLocation(Location):
ALL_LOCATION_TABLE: Dict[str, LocationData] = {}
+LOCATIONS_BY_GROUP: Dict[str, List[str]] = {}
def load_location_data():
- global ALL_LOCATION_TABLE
+ global ALL_LOCATION_TABLE, LOCATIONS_BY_GROUP
for room_name, panels in PANELS_BY_ROOM.items():
for panel_name, panel in panels.items():
@@ -57,6 +51,9 @@ def load_location_data():
LocationData(get_panel_location_id(room_name, panel_name), room_name,
[RoomAndPanel(None, panel_name)], classification)
+ if panel.achievement:
+ LOCATIONS_BY_GROUP.setdefault("Achievements", []).append(location_name)
+
for room_name, doors in DOORS_BY_ROOM.items():
for door_name, door in doors.items():
if door.skip_location or door.event or door.panels is None:
diff --git a/worlds/lingo/options.py b/worlds/lingo/options.py
index c00208621f9e..293992ab91d6 100644
--- a/worlds/lingo/options.py
+++ b/worlds/lingo/options.py
@@ -1,6 +1,9 @@
from dataclasses import dataclass
-from Options import Toggle, Choice, DefaultOnToggle, Range, PerGameCommonOptions
+from schema import And, Schema
+
+from Options import Toggle, Choice, DefaultOnToggle, Range, PerGameCommonOptions, StartInventoryPool, OptionDict
+from worlds.lingo.items import TRAP_ITEMS
class ShuffleDoors(Choice):
@@ -21,6 +24,13 @@ class ProgressiveOrangeTower(DefaultOnToggle):
display_name = "Progressive Orange Tower"
+class ProgressiveColorful(DefaultOnToggle):
+ """When "Shuffle Doors" is on "complex", this setting governs the manner in which The Colorful opens up.
+ If off, there is an item for each room of The Colorful, meaning that random rooms in the middle of the sequence can open up without giving you access to them.
+ If on, there are ten progressive items, which open up the sequence from White forward."""
+ display_name = "Progressive Colorful"
+
+
class LocationChecks(Choice):
"""On "normal", there will be a location check for each panel set that would ordinarily open a door, as well as for
achievement panels and a small handful of other panels.
@@ -100,6 +110,14 @@ class TrapPercentage(Range):
default = 20
+class TrapWeights(OptionDict):
+ """Specify the distribution of traps that should be placed into the pool.
+ If you don't want a specific type of trap, set the weight to zero."""
+ display_name = "Trap Weights"
+ schema = Schema({trap_name: And(int, lambda n: n >= 0) for trap_name in TRAP_ITEMS})
+ default = {trap_name: 1 for trap_name in TRAP_ITEMS}
+
+
class PuzzleSkipPercentage(Range):
"""Replaces junk items with puzzle skips, at the specified rate."""
display_name = "Puzzle Skip Percentage"
@@ -117,6 +135,7 @@ class DeathLink(Toggle):
class LingoOptions(PerGameCommonOptions):
shuffle_doors: ShuffleDoors
progressive_orange_tower: ProgressiveOrangeTower
+ progressive_colorful: ProgressiveColorful
location_checks: LocationChecks
shuffle_colors: ShuffleColors
shuffle_panels: ShufflePanels
@@ -126,5 +145,7 @@ class LingoOptions(PerGameCommonOptions):
level_2_requirement: Level2Requirement
early_color_hallways: EarlyColorHallways
trap_percentage: TrapPercentage
+ trap_weights: TrapWeights
puzzle_skip_percentage: PuzzleSkipPercentage
death_link: DeathLink
+ start_inventory_from_pool: StartInventoryPool
diff --git a/worlds/lingo/player_logic.py b/worlds/lingo/player_logic.py
index fa497c59bd45..966f5a163762 100644
--- a/worlds/lingo/player_logic.py
+++ b/worlds/lingo/player_logic.py
@@ -1,11 +1,12 @@
+from enum import Enum
from typing import Dict, List, NamedTuple, Optional, Set, Tuple, TYPE_CHECKING
-from .items import ALL_ITEM_TABLE
+from .datatypes import Door, RoomAndDoor, RoomAndPanel
+from .items import ALL_ITEM_TABLE, ItemData
from .locations import ALL_LOCATION_TABLE, LocationClassification
from .options import LocationChecks, ShuffleDoors, VictoryCondition
-from .static_logic import DOORS_BY_ROOM, Door, PAINTINGS, PAINTINGS_BY_ROOM, PAINTING_ENTRANCES, PAINTING_EXITS, \
- PANELS_BY_ROOM, PROGRESSION_BY_ROOM, REQUIRED_PAINTING_ROOMS, REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS, RoomAndDoor, \
- RoomAndPanel
+from .static_logic import DOORS_BY_ROOM, PAINTINGS, PAINTING_ENTRANCES, PAINTING_EXITS, \
+ PANELS_BY_ROOM, PROGRESSION_BY_ROOM, REQUIRED_PAINTING_ROOMS, REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS
if TYPE_CHECKING:
from . import LingoWorld
@@ -36,6 +37,42 @@ class PlayerLocation(NamedTuple):
access: AccessRequirements
+class ProgressiveItemBehavior(Enum):
+ DISABLE = 1
+ SPLIT = 2
+ PROGRESSIVE = 3
+
+
+def should_split_progression(progression_name: str, world: "LingoWorld") -> ProgressiveItemBehavior:
+ if progression_name == "Progressive Orange Tower":
+ if world.options.progressive_orange_tower:
+ return ProgressiveItemBehavior.PROGRESSIVE
+ else:
+ return ProgressiveItemBehavior.SPLIT
+ elif progression_name == "Progressive Colorful":
+ if world.options.progressive_colorful:
+ return ProgressiveItemBehavior.PROGRESSIVE
+ else:
+ return ProgressiveItemBehavior.SPLIT
+
+ return ProgressiveItemBehavior.PROGRESSIVE
+
+
+def should_include_item(item: ItemData, world: "LingoWorld") -> bool:
+ if item.mode == "colors":
+ return world.options.shuffle_colors > 0
+ elif item.mode == "doors":
+ return world.options.shuffle_doors != ShuffleDoors.option_none
+ elif item.mode == "complex door":
+ return world.options.shuffle_doors == ShuffleDoors.option_complex
+ elif item.mode == "door group":
+ return world.options.shuffle_doors == ShuffleDoors.option_simple
+ elif item.mode == "special":
+ return False
+ else:
+ return True
+
+
class LingoPlayerLogic:
"""
Defines logic after a player's options have been applied
@@ -83,9 +120,13 @@ def set_door_item(self, room: str, door: str, item: str):
def handle_non_grouped_door(self, room_name: str, door_data: Door, world: "LingoWorld"):
if room_name in PROGRESSION_BY_ROOM and door_data.name in PROGRESSION_BY_ROOM[room_name]:
- if room_name == "Orange Tower" and not world.options.progressive_orange_tower:
+ progression_name = PROGRESSION_BY_ROOM[room_name][door_data.name].item_name
+ progression_handling = should_split_progression(progression_name, world)
+
+ if progression_handling == ProgressiveItemBehavior.SPLIT:
self.set_door_item(room_name, door_data.name, door_data.item_name)
- else:
+ self.real_items.append(door_data.item_name)
+ elif progression_handling == ProgressiveItemBehavior.PROGRESSIVE:
progressive_item_name = PROGRESSION_BY_ROOM[room_name][door_data.name].item_name
self.set_door_item(room_name, door_data.name, progressive_item_name)
self.real_items.append(progressive_item_name)
@@ -124,9 +165,9 @@ def __init__(self, world: "LingoWorld"):
for room_name, room_data in DOORS_BY_ROOM.items():
for door_name, door_data in room_data.items():
if door_data.skip_item is False and door_data.event is False:
- if door_data.group is not None and door_shuffle == ShuffleDoors.option_simple:
+ if door_data.door_group is not None and door_shuffle == ShuffleDoors.option_simple:
# Grouped doors are handled differently if shuffle doors is on simple.
- self.set_door_item(room_name, door_name, door_data.group)
+ self.set_door_item(room_name, door_name, door_data.door_group)
else:
self.handle_non_grouped_door(room_name, door_data, world)
@@ -186,7 +227,7 @@ def __init__(self, world: "LingoWorld"):
# Instantiate all real items.
for name, item in ALL_ITEM_TABLE.items():
- if item.should_include(world):
+ if should_include_item(item, world):
self.real_items.append(name)
# Calculate the requirements for the fake pilgrimage.
@@ -195,9 +236,8 @@ def __init__(self, world: "LingoWorld"):
["Orange Tower Fourth Floor", "Hot Crusts Door"], ["Outside The Initiated", "Shortcut to Hub Room"],
["Orange Tower First Floor", "Shortcut to Hub Room"], ["Directional Gallery", "Shortcut to The Undeterred"],
["Orange Tower First Floor", "Salt Pepper Door"], ["Hub Room", "Crossroads Entrance"],
- ["Champion's Rest", "Shortcut to The Steady"], ["The Bearer", "Shortcut to The Bold"],
- ["Art Gallery", "Exit"], ["The Tenacious", "Shortcut to Hub Room"],
- ["Outside The Agreeable", "Tenacious Entrance"]
+ ["Color Hunt", "Shortcut to The Steady"], ["The Bearer", "Entrance"], ["Art Gallery", "Exit"],
+ ["The Tenacious", "Shortcut to Hub Room"], ["Outside The Agreeable", "Tenacious Entrance"]
]
pilgrimage_reqs = AccessRequirements()
for door in fake_pilgrimage:
@@ -223,30 +263,45 @@ def __init__(self, world: "LingoWorld"):
"kind of logic error.")
if door_shuffle != ShuffleDoors.option_none and location_classification != LocationClassification.insanity \
- and not early_color_hallways is False:
- # If shuffle doors is on, force a useful item onto the HI panel. This may not necessarily get you out of BK,
- # but the goal is to allow you to reach at least one more check. The non-painting ones are hardcoded right
- # now. We only allow the entrance to the Pilgrim Room if color shuffle is off, because otherwise there are
- # no extra checks in there. We only include the entrance to the Rhyme Room when color shuffle is off and
- # door shuffle is on simple, because otherwise there are no extra checks in there.
+ and not early_color_hallways and world.multiworld.players > 1:
+ # Under the combination of door shuffle, normal location checks, and no early color hallways, sphere 1 is
+ # only three checks. In a multiplayer situation, this can be frustrating for the player because they are
+ # more likely to be stuck in the starting room for a long time. To remedy this, we will force a useful item
+ # onto the GOOD LUCK check under these circumstances. The goal is to expand sphere 1 to at least four
+ # checks (and likely more than that).
+ #
+ # Note: A very low LEVEL 2 requirement would naturally expand sphere 1 to four checks, but this is a very
+ # uncommon configuration, so we will ignore it and force a good item anyway.
+
+ # Starting Room - Back Right Door gives access to OPEN and DEAD END.
+ # Starting Room - Exit Door gives access to OPEN and TRACE.
good_item_options: List[str] = ["Starting Room - Back Right Door", "Second Room - Exit Door"]
if not color_shuffle:
+ # HOT CRUST and THIS.
good_item_options.append("Pilgrim Room - Sun Painting")
- if door_shuffle == ShuffleDoors.option_simple:
- good_item_options += ["Welcome Back Doors"]
-
- if not color_shuffle:
- good_item_options.append("Rhyme Room Doors")
- else:
- good_item_options += ["Welcome Back Area - Shortcut to Starting Room"]
+ if door_shuffle == ShuffleDoors.option_simple:
+ # WELCOME BACK, CLOCKWISE, and DRAWL + RUNS.
+ good_item_options.append("Welcome Back Doors")
+ else:
+ # WELCOME BACK and CLOCKWISE.
+ good_item_options.append("Welcome Back Area - Shortcut to Starting Room")
- for painting_obj in PAINTINGS_BY_ROOM["Starting Room"]:
- if not painting_obj.enter_only or painting_obj.required_door is None:
+ if door_shuffle == ShuffleDoors.option_simple:
+ # Color hallways access (NOTE: reconsider when sunwarp shuffling exists).
+ good_item_options.append("Rhyme Room Doors")
+
+ # When painting shuffle is off, most Starting Room paintings give color hallways access. The Wondrous's
+ # painting does not, but it gives access to SHRINK and WELCOME BACK.
+ for painting_obj in PAINTINGS.values():
+ if not painting_obj.enter_only or painting_obj.required_door is None\
+ or painting_obj.room != "Starting Room":
continue
# If painting shuffle is on, we only want to consider paintings that actually go somewhere.
+ #
+ # NOTE: This does not guarantee that there will be any checks on the other side.
if painting_shuffle and painting_obj.id not in self.painting_mapping.keys():
continue
diff --git a/worlds/lingo/regions.py b/worlds/lingo/regions.py
index bdc42f42f5ec..464e9a149a2f 100644
--- a/worlds/lingo/regions.py
+++ b/worlds/lingo/regions.py
@@ -1,11 +1,12 @@
from typing import Dict, Optional, TYPE_CHECKING
from BaseClasses import Entrance, ItemClassification, Region
+from .datatypes import Room, RoomAndDoor
from .items import LingoItem
from .locations import LingoLocation
from .player_logic import LingoPlayerLogic
from .rules import lingo_can_use_entrance, make_location_lambda
-from .static_logic import ALL_ROOMS, PAINTINGS, Room, RoomAndDoor
+from .static_logic import ALL_ROOMS, PAINTINGS
if TYPE_CHECKING:
from . import LingoWorld
diff --git a/worlds/lingo/rules.py b/worlds/lingo/rules.py
index 481fab18b5a1..054c330c450f 100644
--- a/worlds/lingo/rules.py
+++ b/worlds/lingo/rules.py
@@ -1,8 +1,9 @@
from typing import TYPE_CHECKING
from BaseClasses import CollectionState
+from .datatypes import RoomAndDoor
from .player_logic import AccessRequirements, LingoPlayerLogic, PlayerLocation
-from .static_logic import PROGRESSION_BY_ROOM, PROGRESSIVE_ITEMS, RoomAndDoor
+from .static_logic import PROGRESSION_BY_ROOM, PROGRESSIVE_ITEMS
if TYPE_CHECKING:
from . import LingoWorld
diff --git a/worlds/lingo/static_logic.py b/worlds/lingo/static_logic.py
index e9f82fb751ca..1da265df7d70 100644
--- a/worlds/lingo/static_logic.py
+++ b/worlds/lingo/static_logic.py
@@ -1,86 +1,16 @@
-from typing import Dict, List, NamedTuple, Optional, Set
+import os
+import pkgutil
+from io import BytesIO
+from typing import Dict, List, Set
-import Utils
+import pickle
-
-class RoomAndDoor(NamedTuple):
- room: Optional[str]
- door: str
-
-
-class RoomAndPanel(NamedTuple):
- room: Optional[str]
- panel: str
-
-
-class RoomEntrance(NamedTuple):
- room: str # source room
- door: Optional[RoomAndDoor]
- painting: bool
-
-
-class Room(NamedTuple):
- name: str
- entrances: List[RoomEntrance]
-
-
-class Door(NamedTuple):
- name: str
- item_name: str
- location_name: Optional[str]
- panels: Optional[List[RoomAndPanel]]
- skip_location: bool
- skip_item: bool
- door_ids: List[str]
- painting_ids: List[str]
- event: bool
- group: Optional[str]
- include_reduce: bool
- junk_item: bool
-
-
-class Panel(NamedTuple):
- required_rooms: List[str]
- required_doors: List[RoomAndDoor]
- required_panels: List[RoomAndPanel]
- colors: List[str]
- check: bool
- event: bool
- internal_ids: List[str]
- exclude_reduce: bool
- achievement: bool
- non_counting: bool
-
-
-class Painting(NamedTuple):
- id: str
- room: str
- enter_only: bool
- exit_only: bool
- orientation: str
- required: bool
- required_when_no_doors: bool
- required_door: Optional[RoomAndDoor]
- disable: bool
- move: bool
- req_blocked: bool
- req_blocked_when_no_doors: bool
-
-
-class Progression(NamedTuple):
- item_name: str
- index: int
-
-
-ROOMS: Dict[str, Room] = {}
-PANELS: Dict[str, Panel] = {}
-DOORS: Dict[str, Door] = {}
-PAINTINGS: Dict[str, Painting] = {}
+from .datatypes import Door, Painting, Panel, Progression, Room
ALL_ROOMS: List[Room] = []
DOORS_BY_ROOM: Dict[str, Dict[str, Door]] = {}
PANELS_BY_ROOM: Dict[str, Dict[str, Panel]] = {}
-PAINTINGS_BY_ROOM: Dict[str, List[Painting]] = {}
+PAINTINGS: Dict[str, Painting] = {}
PROGRESSIVE_ITEMS: List[str] = []
PROGRESSION_BY_ROOM: Dict[str, Dict[str, Progression]] = {}
@@ -98,61 +28,7 @@ class Progression(NamedTuple):
DOOR_GROUP_ITEM_IDS: Dict[str, int] = {}
PROGRESSIVE_ITEM_IDS: Dict[str, int] = {}
-
-def load_static_data():
- global PAINTING_EXITS, SPECIAL_ITEM_IDS, PANEL_LOCATION_IDS, DOOR_LOCATION_IDS, DOOR_ITEM_IDS, \
- DOOR_GROUP_ITEM_IDS, PROGRESSIVE_ITEM_IDS
-
- try:
- from importlib.resources import files
- except ImportError:
- from importlib_resources import files
-
- from . import data
-
- # Load in all item and location IDs. These are broken up into groups based on the type of item/location.
- with files(data).joinpath("ids.yaml").open() as file:
- config = Utils.parse_yaml(file)
-
- if "special_items" in config:
- for item_name, item_id in config["special_items"].items():
- SPECIAL_ITEM_IDS[item_name] = item_id
-
- if "panels" in config:
- for room_name in config["panels"].keys():
- PANEL_LOCATION_IDS[room_name] = {}
-
- for panel_name, location_id in config["panels"][room_name].items():
- PANEL_LOCATION_IDS[room_name][panel_name] = location_id
-
- if "doors" in config:
- for room_name in config["doors"].keys():
- DOOR_LOCATION_IDS[room_name] = {}
- DOOR_ITEM_IDS[room_name] = {}
-
- for door_name, door_data in config["doors"][room_name].items():
- if "location" in door_data:
- DOOR_LOCATION_IDS[room_name][door_name] = door_data["location"]
-
- if "item" in door_data:
- DOOR_ITEM_IDS[room_name][door_name] = door_data["item"]
-
- if "door_groups" in config:
- for item_name, item_id in config["door_groups"].items():
- DOOR_GROUP_ITEM_IDS[item_name] = item_id
-
- if "progression" in config:
- for item_name, item_id in config["progression"].items():
- PROGRESSIVE_ITEM_IDS[item_name] = item_id
-
- # Process the main world file.
- with files(data).joinpath("LL1.yaml").open() as file:
- config = Utils.parse_yaml(file)
-
- for room_name, room_data in config.items():
- process_room(room_name, room_data)
-
- PAINTING_EXITS = len(PAINTING_EXIT_ROOMS)
+HASHES: Dict[str, str] = {}
def get_special_item_id(name: str):
@@ -197,363 +73,39 @@ def get_progressive_item_id(name: str):
return PROGRESSIVE_ITEM_IDS[name]
-def process_entrance(source_room, doors, room_obj):
- global PAINTING_ENTRANCES, PAINTING_EXIT_ROOMS
-
- # If the value of an entrance is just True, that means that the entrance is always accessible.
- if doors is True:
- room_obj.entrances.append(RoomEntrance(source_room, None, False))
- elif isinstance(doors, dict):
- # If the value of an entrance is a dictionary, that means the entrance requires a door to be accessible, is a
- # painting-based entrance, or both.
- if "painting" in doors and "door" not in doors:
- PAINTING_EXIT_ROOMS.add(room_obj.name)
- PAINTING_ENTRANCES += 1
-
- room_obj.entrances.append(RoomEntrance(source_room, None, True))
- else:
- if "painting" in doors and doors["painting"]:
- PAINTING_EXIT_ROOMS.add(room_obj.name)
- PAINTING_ENTRANCES += 1
-
- room_obj.entrances.append(RoomEntrance(source_room, RoomAndDoor(
- doors["room"] if "room" in doors else None,
- doors["door"]
- ), doors["painting"] if "painting" in doors else False))
- else:
- # If the value of an entrance is a list, then there are multiple possible doors that can give access to the
- # entrance.
- for door in doors:
- if "painting" in door and door["painting"]:
- PAINTING_EXIT_ROOMS.add(room_obj.name)
- PAINTING_ENTRANCES += 1
-
- room_obj.entrances.append(RoomEntrance(source_room, RoomAndDoor(
- door["room"] if "room" in door else None,
- door["door"]
- ), door["painting"] if "painting" in door else False))
-
-
-def process_panel(room_name, panel_name, panel_data):
- global PANELS, PANELS_BY_ROOM
-
- full_name = f"{room_name} - {panel_name}"
-
- # required_room can either be a single room or a list of rooms.
- if "required_room" in panel_data:
- if isinstance(panel_data["required_room"], list):
- required_rooms = panel_data["required_room"]
- else:
- required_rooms = [panel_data["required_room"]]
- else:
- required_rooms = []
-
- # required_door can either be a single door or a list of doors. For convenience, the room key for each door does not
- # need to be specified if the door is in this room.
- required_doors = list()
- if "required_door" in panel_data:
- if isinstance(panel_data["required_door"], dict):
- door = panel_data["required_door"]
- required_doors.append(RoomAndDoor(
- door["room"] if "room" in door else None,
- door["door"]
- ))
- else:
- for door in panel_data["required_door"]:
- required_doors.append(RoomAndDoor(
- door["room"] if "room" in door else None,
- door["door"]
- ))
-
- # required_panel can either be a single panel or a list of panels. For convenience, the room key for each panel does
- # not need to be specified if the panel is in this room.
- required_panels = list()
- if "required_panel" in panel_data:
- if isinstance(panel_data["required_panel"], dict):
- other_panel = panel_data["required_panel"]
- required_panels.append(RoomAndPanel(
- other_panel["room"] if "room" in other_panel else None,
- other_panel["panel"]
- ))
- else:
- for other_panel in panel_data["required_panel"]:
- required_panels.append(RoomAndPanel(
- other_panel["room"] if "room" in other_panel else None,
- other_panel["panel"]
- ))
-
- # colors can either be a single color or a list of colors.
- if "colors" in panel_data:
- if isinstance(panel_data["colors"], list):
- colors = panel_data["colors"]
- else:
- colors = [panel_data["colors"]]
- else:
- colors = []
-
- if "check" in panel_data:
- check = panel_data["check"]
- else:
- check = False
-
- if "event" in panel_data:
- event = panel_data["event"]
- else:
- event = False
-
- if "achievement" in panel_data:
- achievement = True
- else:
- achievement = False
-
- if "exclude_reduce" in panel_data:
- exclude_reduce = panel_data["exclude_reduce"]
- else:
- exclude_reduce = False
-
- if "non_counting" in panel_data:
- non_counting = panel_data["non_counting"]
- else:
- non_counting = False
-
- if "id" in panel_data:
- if isinstance(panel_data["id"], list):
- internal_ids = panel_data["id"]
- else:
- internal_ids = [panel_data["id"]]
- else:
- internal_ids = []
-
- panel_obj = Panel(required_rooms, required_doors, required_panels, colors, check, event, internal_ids,
- exclude_reduce, achievement, non_counting)
- PANELS[full_name] = panel_obj
- PANELS_BY_ROOM[room_name][panel_name] = panel_obj
-
-
-def process_door(room_name, door_name, door_data):
- global DOORS, DOORS_BY_ROOM
-
- # The item name associated with a door can be explicitly specified in the configuration. If it is not, it is
- # generated from the room and door name.
- if "item_name" in door_data:
- item_name = door_data["item_name"]
- else:
- item_name = f"{room_name} - {door_name}"
-
- if "skip_location" in door_data:
- skip_location = door_data["skip_location"]
- else:
- skip_location = False
-
- if "skip_item" in door_data:
- skip_item = door_data["skip_item"]
- else:
- skip_item = False
-
- if "event" in door_data:
- event = door_data["event"]
- else:
- event = False
-
- if "include_reduce" in door_data:
- include_reduce = door_data["include_reduce"]
- else:
- include_reduce = False
-
- if "junk_item" in door_data:
- junk_item = door_data["junk_item"]
- else:
- junk_item = False
-
- if "group" in door_data:
- group = door_data["group"]
- else:
- group = None
-
- # panels is a list of panels. Each panel can either be a simple string (the name of a panel in the current room) or
- # a dictionary specifying a panel in a different room.
- if "panels" in door_data:
- panels = list()
- for panel in door_data["panels"]:
- if isinstance(panel, dict):
- panels.append(RoomAndPanel(panel["room"], panel["panel"]))
- else:
- panels.append(RoomAndPanel(None, panel))
- else:
- skip_location = True
- panels = None
-
- # The location name associated with a door can be explicitly specified in the configuration. If it is not, then the
- # name is generated using a combination of all of the panels that would ordinarily open the door. This can get quite
- # messy if there are a lot of panels, especially if panels from multiple rooms are involved, so in these cases it
- # would be better to specify a name.
- if "location_name" in door_data:
- location_name = door_data["location_name"]
- elif skip_location is False:
- panel_per_room = dict()
- for panel in panels:
- panel_room_name = room_name if panel.room is None else panel.room
- panel_per_room.setdefault(panel_room_name, []).append(panel.panel)
-
- room_strs = list()
- for door_room_str, door_panels_str in panel_per_room.items():
- room_strs.append(door_room_str + " - " + ", ".join(door_panels_str))
-
- location_name = " and ".join(room_strs)
- else:
- location_name = None
-
- # The id field can be a single item, or a list of door IDs, in the event that the item for this logical door should
- # open more than one actual in-game door.
- if "id" in door_data:
- if isinstance(door_data["id"], list):
- door_ids = door_data["id"]
- else:
- door_ids = [door_data["id"]]
- else:
- door_ids = []
-
- # The painting_id field can be a single item, or a list of painting IDs, in the event that the item for this logical
- # door should move more than one actual in-game painting.
- if "painting_id" in door_data:
- if isinstance(door_data["painting_id"], list):
- painting_ids = door_data["painting_id"]
- else:
- painting_ids = [door_data["painting_id"]]
- else:
- painting_ids = []
-
- door_obj = Door(door_name, item_name, location_name, panels, skip_location, skip_item, door_ids,
- painting_ids, event, group, include_reduce, junk_item)
-
- DOORS[door_obj.item_name] = door_obj
- DOORS_BY_ROOM[room_name][door_name] = door_obj
-
-
-def process_painting(room_name, painting_data):
- global PAINTINGS, PAINTINGS_BY_ROOM, REQUIRED_PAINTING_ROOMS, REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS
-
- # Read in information about this painting and store it in an object.
- painting_id = painting_data["id"]
-
- if "orientation" in painting_data:
- orientation = painting_data["orientation"]
- else:
- orientation = ""
-
- if "disable" in painting_data:
- disable_painting = painting_data["disable"]
- else:
- disable_painting = False
-
- if "required" in painting_data:
- required_painting = painting_data["required"]
- if required_painting:
- REQUIRED_PAINTING_ROOMS.append(room_name)
- else:
- required_painting = False
-
- if "move" in painting_data:
- move_painting = painting_data["move"]
- else:
- move_painting = False
-
- if "required_when_no_doors" in painting_data:
- rwnd = painting_data["required_when_no_doors"]
- if rwnd:
- REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS.append(room_name)
- else:
- rwnd = False
-
- if "exit_only" in painting_data:
- exit_only = painting_data["exit_only"]
- else:
- exit_only = False
-
- if "enter_only" in painting_data:
- enter_only = painting_data["enter_only"]
- else:
- enter_only = False
-
- if "req_blocked" in painting_data:
- req_blocked = painting_data["req_blocked"]
- else:
- req_blocked = False
-
- if "req_blocked_when_no_doors" in painting_data:
- req_blocked_when_no_doors = painting_data["req_blocked_when_no_doors"]
- else:
- req_blocked_when_no_doors = False
-
- required_door = None
- if "required_door" in painting_data:
- door = painting_data["required_door"]
- required_door = RoomAndDoor(
- door["room"] if "room" in door else room_name,
- door["door"]
- )
-
- painting_obj = Painting(painting_id, room_name, enter_only, exit_only, orientation,
- required_painting, rwnd, required_door, disable_painting, move_painting, req_blocked,
- req_blocked_when_no_doors)
- PAINTINGS[painting_id] = painting_obj
- PAINTINGS_BY_ROOM[room_name].append(painting_obj)
-
-
-def process_progression(room_name, progression_name, progression_doors):
- global PROGRESSIVE_ITEMS, PROGRESSION_BY_ROOM
-
- # Progressive items are configured as a list of doors.
- PROGRESSIVE_ITEMS.append(progression_name)
-
- progression_index = 1
- for door in progression_doors:
- if isinstance(door, Dict):
- door_room = door["room"]
- door_door = door["door"]
- else:
- door_room = room_name
- door_door = door
-
- room_progressions = PROGRESSION_BY_ROOM.setdefault(door_room, {})
- room_progressions[door_door] = Progression(progression_name, progression_index)
- progression_index += 1
-
-
-def process_room(room_name, room_data):
- global ROOMS, ALL_ROOMS
-
- room_obj = Room(room_name, [])
-
- if "entrances" in room_data:
- for source_room, doors in room_data["entrances"].items():
- process_entrance(source_room, doors, room_obj)
-
- if "panels" in room_data:
- PANELS_BY_ROOM[room_name] = dict()
-
- for panel_name, panel_data in room_data["panels"].items():
- process_panel(room_name, panel_name, panel_data)
-
- if "doors" in room_data:
- DOORS_BY_ROOM[room_name] = dict()
-
- for door_name, door_data in room_data["doors"].items():
- process_door(room_name, door_name, door_data)
-
- if "paintings" in room_data:
- PAINTINGS_BY_ROOM[room_name] = []
-
- for painting_data in room_data["paintings"]:
- process_painting(room_name, painting_data)
-
- if "progression" in room_data:
- for progression_name, progression_doors in room_data["progression"].items():
- process_progression(room_name, progression_name, progression_doors)
-
- ROOMS[room_name] = room_obj
- ALL_ROOMS.append(room_obj)
+def load_static_data_from_file():
+ global PAINTING_ENTRANCES, PAINTING_EXITS
+
+ class RenameUnpickler(pickle.Unpickler):
+ def find_class(self, module, name):
+ renamed_module = module
+ if module == "datatypes":
+ renamed_module = "worlds.lingo.datatypes"
+
+ return super(RenameUnpickler, self).find_class(renamed_module, name)
+
+ file = pkgutil.get_data(__name__, os.path.join("data", "generated.dat"))
+ pickdata = RenameUnpickler(BytesIO(file)).load()
+
+ HASHES.update(pickdata["HASHES"])
+ PAINTINGS.update(pickdata["PAINTINGS"])
+ ALL_ROOMS.extend(pickdata["ALL_ROOMS"])
+ DOORS_BY_ROOM.update(pickdata["DOORS_BY_ROOM"])
+ PANELS_BY_ROOM.update(pickdata["PANELS_BY_ROOM"])
+ PROGRESSIVE_ITEMS.extend(pickdata["PROGRESSIVE_ITEMS"])
+ PROGRESSION_BY_ROOM.update(pickdata["PROGRESSION_BY_ROOM"])
+ PAINTING_ENTRANCES = pickdata["PAINTING_ENTRANCES"]
+ PAINTING_EXIT_ROOMS.update(pickdata["PAINTING_EXIT_ROOMS"])
+ PAINTING_EXITS = pickdata["PAINTING_EXITS"]
+ REQUIRED_PAINTING_ROOMS.extend(pickdata["REQUIRED_PAINTING_ROOMS"])
+ REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS.extend(pickdata["REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS"])
+ SPECIAL_ITEM_IDS.update(pickdata["SPECIAL_ITEM_IDS"])
+ PANEL_LOCATION_IDS.update(pickdata["PANEL_LOCATION_IDS"])
+ DOOR_LOCATION_IDS.update(pickdata["DOOR_LOCATION_IDS"])
+ DOOR_ITEM_IDS.update(pickdata["DOOR_ITEM_IDS"])
+ DOOR_GROUP_ITEM_IDS.update(pickdata["DOOR_GROUP_ITEM_IDS"])
+ PROGRESSIVE_ITEM_IDS.update(pickdata["PROGRESSIVE_ITEM_IDS"])
# Initialize the static data at module scope.
-load_static_data()
+load_static_data_from_file()
diff --git a/worlds/lingo/test/TestDatafile.py b/worlds/lingo/test/TestDatafile.py
new file mode 100644
index 000000000000..9f4e9da05f65
--- /dev/null
+++ b/worlds/lingo/test/TestDatafile.py
@@ -0,0 +1,16 @@
+import os
+import unittest
+
+from worlds.lingo.static_logic import HASHES
+from worlds.lingo.utils.pickle_static_data import hash_file
+
+
+class TestDatafile(unittest.TestCase):
+ def test_check_hashes(self) -> None:
+ ll1_file_hash = hash_file(os.path.join(os.path.dirname(__file__), "..", "data", "LL1.yaml"))
+ ids_file_hash = hash_file(os.path.join(os.path.dirname(__file__), "..", "data", "ids.yaml"))
+
+ self.assertEqual(ll1_file_hash, HASHES["LL1.yaml"],
+ "LL1.yaml hash does not match generated.dat. Please regenerate using 'python worlds/lingo/utils/pickle_static_data.py'")
+ self.assertEqual(ids_file_hash, HASHES["ids.yaml"],
+ "ids.yaml hash does not match generated.dat. Please regenerate using 'python worlds/lingo/utils/pickle_static_data.py'")
diff --git a/worlds/lingo/test/TestDoors.py b/worlds/lingo/test/TestDoors.py
index 49a0f9c49010..f496c5f5785a 100644
--- a/worlds/lingo/test/TestDoors.py
+++ b/worlds/lingo/test/TestDoors.py
@@ -8,8 +8,6 @@ class TestRequiredRoomLogic(LingoTestBase):
}
def test_pilgrim_first(self) -> None:
- self.remove_forced_good_item()
-
self.assertFalse(self.multiworld.state.can_reach("The Seeker", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Pilgrim Antechamber", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Pilgrim Room", "Region", self.player))
@@ -30,8 +28,6 @@ def test_pilgrim_first(self) -> None:
self.assertTrue(self.can_reach_location("The Seeker - Achievement"))
def test_hidden_first(self) -> None:
- self.remove_forced_good_item()
-
self.assertFalse(self.multiworld.state.can_reach("The Seeker", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Pilgrim Room", "Region", self.player))
self.assertFalse(self.can_reach_location("The Seeker - Achievement"))
@@ -59,8 +55,6 @@ class TestRequiredDoorLogic(LingoTestBase):
}
def test_through_rhyme(self) -> None:
- self.remove_forced_good_item()
-
self.assertFalse(self.can_reach_location("Rhyme Room - Circle/Looped Square Wall"))
self.collect_by_name("Starting Room - Rhyme Room Entrance")
@@ -70,8 +64,6 @@ def test_through_rhyme(self) -> None:
self.assertTrue(self.can_reach_location("Rhyme Room - Circle/Looped Square Wall"))
def test_through_hidden(self) -> None:
- self.remove_forced_good_item()
-
self.assertFalse(self.can_reach_location("Rhyme Room - Circle/Looped Square Wall"))
self.collect_by_name("Starting Room - Rhyme Room Entrance")
@@ -91,8 +83,6 @@ class TestSimpleDoors(LingoTestBase):
}
def test_requirement(self):
- self.remove_forced_good_item()
-
self.assertFalse(self.multiworld.state.can_reach("Outside The Wanderer", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Third Floor", "Region", self.player))
diff --git a/worlds/lingo/test/TestOrangeTower.py b/worlds/lingo/test/TestOrangeTower.py
index 9170de108ad0..7b0c3bb52518 100644
--- a/worlds/lingo/test/TestOrangeTower.py
+++ b/worlds/lingo/test/TestOrangeTower.py
@@ -8,8 +8,6 @@ class TestProgressiveOrangeTower(LingoTestBase):
}
def test_from_welcome_back(self) -> None:
- self.remove_forced_good_item()
-
self.assertFalse(self.multiworld.state.can_reach("Orange Tower First Floor", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Second Floor", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Third Floor", "Region", self.player))
@@ -85,8 +83,6 @@ def test_from_welcome_back(self) -> None:
self.assertTrue(self.multiworld.state.can_reach("Orange Tower Seventh Floor", "Region", self.player))
def test_from_hub_room(self) -> None:
- self.remove_forced_good_item()
-
self.assertFalse(self.multiworld.state.can_reach("Orange Tower First Floor", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Second Floor", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Third Floor", "Region", self.player))
diff --git a/worlds/lingo/test/TestProgressive.py b/worlds/lingo/test/TestProgressive.py
index 8edc7ce6ccef..e79fd6bc9087 100644
--- a/worlds/lingo/test/TestProgressive.py
+++ b/worlds/lingo/test/TestProgressive.py
@@ -7,8 +7,6 @@ class TestComplexProgressiveHallwayRoom(LingoTestBase):
}
def test_item(self):
- self.remove_forced_good_item()
-
self.assertFalse(self.multiworld.state.can_reach("Outside The Agreeable", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Hallway Room (2)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Hallway Room (3)", "Region", self.player))
@@ -60,8 +58,6 @@ class TestSimpleHallwayRoom(LingoTestBase):
}
def test_item(self):
- self.remove_forced_good_item()
-
self.assertFalse(self.multiworld.state.can_reach("Outside The Agreeable", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Hallway Room (2)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Hallway Room (3)", "Region", self.player))
@@ -90,13 +86,11 @@ class TestProgressiveArtGallery(LingoTestBase):
}
def test_item(self):
- self.remove_forced_good_item()
-
self.assertFalse(self.multiworld.state.can_reach("Art Gallery", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
- self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
+ self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect_by_name(["Second Room - Exit Door", "Crossroads - Tower Entrance",
@@ -105,7 +99,7 @@ def test_item(self):
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
- self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
+ self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
progressive_gallery_room = self.get_items_by_name("Progressive Art Gallery")
@@ -115,7 +109,7 @@ def test_item(self):
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
- self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
+ self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect(progressive_gallery_room[1])
@@ -123,7 +117,7 @@ def test_item(self):
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
- self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
+ self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect(progressive_gallery_room[2])
@@ -131,7 +125,7 @@ def test_item(self):
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
- self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
+ self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect(progressive_gallery_room[3])
@@ -139,15 +133,15 @@ def test_item(self):
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
- self.assertTrue(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
+ self.assertTrue(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
- self.collect(progressive_gallery_room[4])
+ self.collect_by_name("Orange Tower Fifth Floor - Quadruple Intersection")
self.assertTrue(self.multiworld.state.can_reach("Art Gallery", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
- self.assertTrue(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
+ self.assertTrue(self.can_reach_location("Art Gallery - ORDER"))
self.assertTrue(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
@@ -162,7 +156,7 @@ def test_item(self):
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
- self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
+ self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect_by_name("Yellow")
@@ -170,7 +164,7 @@ def test_item(self):
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
- self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
+ self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect_by_name("Brown")
@@ -178,7 +172,7 @@ def test_item(self):
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
- self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
+ self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect_by_name("Blue")
@@ -186,7 +180,7 @@ def test_item(self):
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
- self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
+ self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect_by_name(["Orange", "Gray"])
@@ -194,5 +188,5 @@ def test_item(self):
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
- self.assertTrue(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
+ self.assertTrue(self.can_reach_location("Art Gallery - ORDER"))
self.assertTrue(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
diff --git a/worlds/lingo/test/__init__.py b/worlds/lingo/test/__init__.py
index 7ff456d8fcc3..a4196de110db 100644
--- a/worlds/lingo/test/__init__.py
+++ b/worlds/lingo/test/__init__.py
@@ -6,12 +6,3 @@
class LingoTestBase(WorldTestBase):
game = "Lingo"
player: ClassVar[int] = 1
-
- def world_setup(self, *args, **kwargs):
- super().world_setup(*args, **kwargs)
-
- def remove_forced_good_item(self):
- location = self.multiworld.get_location("Second Room - Good Luck", self.player)
- self.remove(location.item)
- self.multiworld.itempool.append(location.item)
- self.multiworld.state.events.add(location)
diff --git a/worlds/lingo/utils/__init__.py b/worlds/lingo/utils/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/worlds/lingo/utils/pickle_static_data.py b/worlds/lingo/utils/pickle_static_data.py
new file mode 100644
index 000000000000..5d6fa1e68328
--- /dev/null
+++ b/worlds/lingo/utils/pickle_static_data.py
@@ -0,0 +1,480 @@
+from typing import Dict, List, Set
+
+import os
+import sys
+
+sys.path.append(os.path.join("worlds", "lingo"))
+sys.path.append(".")
+sys.path.append("..")
+from datatypes import Door, Painting, Panel, Progression, Room, RoomAndDoor, RoomAndPanel, RoomEntrance
+
+import hashlib
+import pickle
+import sys
+import Utils
+
+
+ALL_ROOMS: List[Room] = []
+DOORS_BY_ROOM: Dict[str, Dict[str, Door]] = {}
+PANELS_BY_ROOM: Dict[str, Dict[str, Panel]] = {}
+PAINTINGS: Dict[str, Painting] = {}
+
+PROGRESSIVE_ITEMS: List[str] = []
+PROGRESSION_BY_ROOM: Dict[str, Dict[str, Progression]] = {}
+
+PAINTING_ENTRANCES: int = 0
+PAINTING_EXIT_ROOMS: Set[str] = set()
+PAINTING_EXITS: int = 0
+REQUIRED_PAINTING_ROOMS: List[str] = []
+REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS: List[str] = []
+
+SPECIAL_ITEM_IDS: Dict[str, int] = {}
+PANEL_LOCATION_IDS: Dict[str, Dict[str, int]] = {}
+DOOR_LOCATION_IDS: Dict[str, Dict[str, int]] = {}
+DOOR_ITEM_IDS: Dict[str, Dict[str, int]] = {}
+DOOR_GROUP_ITEM_IDS: Dict[str, int] = {}
+PROGRESSIVE_ITEM_IDS: Dict[str, int] = {}
+
+
+def hash_file(path):
+ md5 = hashlib.md5()
+
+ with open(path, 'rb') as f:
+ content = f.read()
+ content = content.replace(b'\r\n', b'\n')
+ md5.update(content)
+
+ return md5.hexdigest()
+
+
+def load_static_data(ll1_path, ids_path):
+ global PAINTING_EXITS, SPECIAL_ITEM_IDS, PANEL_LOCATION_IDS, DOOR_LOCATION_IDS, DOOR_ITEM_IDS, \
+ DOOR_GROUP_ITEM_IDS, PROGRESSIVE_ITEM_IDS
+
+ # Load in all item and location IDs. These are broken up into groups based on the type of item/location.
+ with open(ids_path, "r") as file:
+ config = Utils.parse_yaml(file)
+
+ if "special_items" in config:
+ for item_name, item_id in config["special_items"].items():
+ SPECIAL_ITEM_IDS[item_name] = item_id
+
+ if "panels" in config:
+ for room_name in config["panels"].keys():
+ PANEL_LOCATION_IDS[room_name] = {}
+
+ for panel_name, location_id in config["panels"][room_name].items():
+ PANEL_LOCATION_IDS[room_name][panel_name] = location_id
+
+ if "doors" in config:
+ for room_name in config["doors"].keys():
+ DOOR_LOCATION_IDS[room_name] = {}
+ DOOR_ITEM_IDS[room_name] = {}
+
+ for door_name, door_data in config["doors"][room_name].items():
+ if "location" in door_data:
+ DOOR_LOCATION_IDS[room_name][door_name] = door_data["location"]
+
+ if "item" in door_data:
+ DOOR_ITEM_IDS[room_name][door_name] = door_data["item"]
+
+ if "door_groups" in config:
+ for item_name, item_id in config["door_groups"].items():
+ DOOR_GROUP_ITEM_IDS[item_name] = item_id
+
+ if "progression" in config:
+ for item_name, item_id in config["progression"].items():
+ PROGRESSIVE_ITEM_IDS[item_name] = item_id
+
+ # Process the main world file.
+ with open(ll1_path, "r") as file:
+ config = Utils.parse_yaml(file)
+
+ for room_name, room_data in config.items():
+ process_room(room_name, room_data)
+
+ PAINTING_EXITS = len(PAINTING_EXIT_ROOMS)
+
+
+def process_entrance(source_room, doors, room_obj):
+ global PAINTING_ENTRANCES, PAINTING_EXIT_ROOMS
+
+ # If the value of an entrance is just True, that means that the entrance is always accessible.
+ if doors is True:
+ room_obj.entrances.append(RoomEntrance(source_room, None, False))
+ elif isinstance(doors, dict):
+ # If the value of an entrance is a dictionary, that means the entrance requires a door to be accessible, is a
+ # painting-based entrance, or both.
+ if "painting" in doors and "door" not in doors:
+ PAINTING_EXIT_ROOMS.add(room_obj.name)
+ PAINTING_ENTRANCES += 1
+
+ room_obj.entrances.append(RoomEntrance(source_room, None, True))
+ else:
+ if "painting" in doors and doors["painting"]:
+ PAINTING_EXIT_ROOMS.add(room_obj.name)
+ PAINTING_ENTRANCES += 1
+
+ room_obj.entrances.append(RoomEntrance(source_room, RoomAndDoor(
+ doors["room"] if "room" in doors else None,
+ doors["door"]
+ ), doors["painting"] if "painting" in doors else False))
+ else:
+ # If the value of an entrance is a list, then there are multiple possible doors that can give access to the
+ # entrance.
+ for door in doors:
+ if "painting" in door and door["painting"]:
+ PAINTING_EXIT_ROOMS.add(room_obj.name)
+ PAINTING_ENTRANCES += 1
+
+ room_obj.entrances.append(RoomEntrance(source_room, RoomAndDoor(
+ door["room"] if "room" in door else None,
+ door["door"]
+ ), door["painting"] if "painting" in door else False))
+
+
+def process_panel(room_name, panel_name, panel_data):
+ global PANELS_BY_ROOM
+
+ full_name = f"{room_name} - {panel_name}"
+
+ # required_room can either be a single room or a list of rooms.
+ if "required_room" in panel_data:
+ if isinstance(panel_data["required_room"], list):
+ required_rooms = panel_data["required_room"]
+ else:
+ required_rooms = [panel_data["required_room"]]
+ else:
+ required_rooms = []
+
+ # required_door can either be a single door or a list of doors. For convenience, the room key for each door does not
+ # need to be specified if the door is in this room.
+ required_doors = list()
+ if "required_door" in panel_data:
+ if isinstance(panel_data["required_door"], dict):
+ door = panel_data["required_door"]
+ required_doors.append(RoomAndDoor(
+ door["room"] if "room" in door else None,
+ door["door"]
+ ))
+ else:
+ for door in panel_data["required_door"]:
+ required_doors.append(RoomAndDoor(
+ door["room"] if "room" in door else None,
+ door["door"]
+ ))
+
+ # required_panel can either be a single panel or a list of panels. For convenience, the room key for each panel does
+ # not need to be specified if the panel is in this room.
+ required_panels = list()
+ if "required_panel" in panel_data:
+ if isinstance(panel_data["required_panel"], dict):
+ other_panel = panel_data["required_panel"]
+ required_panels.append(RoomAndPanel(
+ other_panel["room"] if "room" in other_panel else None,
+ other_panel["panel"]
+ ))
+ else:
+ for other_panel in panel_data["required_panel"]:
+ required_panels.append(RoomAndPanel(
+ other_panel["room"] if "room" in other_panel else None,
+ other_panel["panel"]
+ ))
+
+ # colors can either be a single color or a list of colors.
+ if "colors" in panel_data:
+ if isinstance(panel_data["colors"], list):
+ colors = panel_data["colors"]
+ else:
+ colors = [panel_data["colors"]]
+ else:
+ colors = []
+
+ if "check" in panel_data:
+ check = panel_data["check"]
+ else:
+ check = False
+
+ if "event" in panel_data:
+ event = panel_data["event"]
+ else:
+ event = False
+
+ if "achievement" in panel_data:
+ achievement = True
+ else:
+ achievement = False
+
+ if "exclude_reduce" in panel_data:
+ exclude_reduce = panel_data["exclude_reduce"]
+ else:
+ exclude_reduce = False
+
+ if "non_counting" in panel_data:
+ non_counting = panel_data["non_counting"]
+ else:
+ non_counting = False
+
+ panel_obj = Panel(required_rooms, required_doors, required_panels, colors, check, event, exclude_reduce,
+ achievement, non_counting)
+ PANELS_BY_ROOM[room_name][panel_name] = panel_obj
+
+
+def process_door(room_name, door_name, door_data):
+ global DOORS_BY_ROOM
+
+ # The item name associated with a door can be explicitly specified in the configuration. If it is not, it is
+ # generated from the room and door name.
+ if "item_name" in door_data:
+ item_name = door_data["item_name"]
+ else:
+ item_name = f"{room_name} - {door_name}"
+
+ if "skip_location" in door_data:
+ skip_location = door_data["skip_location"]
+ else:
+ skip_location = False
+
+ if "skip_item" in door_data:
+ skip_item = door_data["skip_item"]
+ else:
+ skip_item = False
+
+ if "event" in door_data:
+ event = door_data["event"]
+ else:
+ event = False
+
+ if "include_reduce" in door_data:
+ include_reduce = door_data["include_reduce"]
+ else:
+ include_reduce = False
+
+ if "junk_item" in door_data:
+ junk_item = door_data["junk_item"]
+ else:
+ junk_item = False
+
+ if "door_group" in door_data:
+ door_group = door_data["door_group"]
+ else:
+ door_group = None
+
+ if "item_group" in door_data:
+ item_group = door_data["item_group"]
+ else:
+ item_group = None
+
+ # panels is a list of panels. Each panel can either be a simple string (the name of a panel in the current room) or
+ # a dictionary specifying a panel in a different room.
+ if "panels" in door_data:
+ panels = list()
+ for panel in door_data["panels"]:
+ if isinstance(panel, dict):
+ panels.append(RoomAndPanel(panel["room"], panel["panel"]))
+ else:
+ panels.append(RoomAndPanel(None, panel))
+ else:
+ skip_location = True
+ panels = None
+
+ # The location name associated with a door can be explicitly specified in the configuration. If it is not, then the
+ # name is generated using a combination of all of the panels that would ordinarily open the door. This can get quite
+ # messy if there are a lot of panels, especially if panels from multiple rooms are involved, so in these cases it
+ # would be better to specify a name.
+ if "location_name" in door_data:
+ location_name = door_data["location_name"]
+ elif skip_location is False:
+ panel_per_room = dict()
+ for panel in panels:
+ panel_room_name = room_name if panel.room is None else panel.room
+ panel_per_room.setdefault(panel_room_name, []).append(panel.panel)
+
+ room_strs = list()
+ for door_room_str, door_panels_str in panel_per_room.items():
+ room_strs.append(door_room_str + " - " + ", ".join(door_panels_str))
+
+ location_name = " and ".join(room_strs)
+ else:
+ location_name = None
+
+ # The id field can be a single item, or a list of door IDs, in the event that the item for this logical door should
+ # open more than one actual in-game door.
+ has_doors = "id" in door_data
+
+ # The painting_id field can be a single item, or a list of painting IDs, in the event that the item for this logical
+ # door should move more than one actual in-game painting.
+ if "painting_id" in door_data:
+ if isinstance(door_data["painting_id"], list):
+ painting_ids = door_data["painting_id"]
+ else:
+ painting_ids = [door_data["painting_id"]]
+ else:
+ painting_ids = []
+
+ door_obj = Door(door_name, item_name, location_name, panels, skip_location, skip_item, has_doors,
+ painting_ids, event, door_group, include_reduce, junk_item, item_group)
+
+ DOORS_BY_ROOM[room_name][door_name] = door_obj
+
+
+def process_painting(room_name, painting_data):
+ global PAINTINGS, REQUIRED_PAINTING_ROOMS, REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS
+
+ # Read in information about this painting and store it in an object.
+ painting_id = painting_data["id"]
+
+ if "disable" in painting_data:
+ disable_painting = painting_data["disable"]
+ else:
+ disable_painting = False
+
+ if "required" in painting_data:
+ required_painting = painting_data["required"]
+ if required_painting:
+ REQUIRED_PAINTING_ROOMS.append(room_name)
+ else:
+ required_painting = False
+
+ if "required_when_no_doors" in painting_data:
+ rwnd = painting_data["required_when_no_doors"]
+ if rwnd:
+ REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS.append(room_name)
+ else:
+ rwnd = False
+
+ if "exit_only" in painting_data:
+ exit_only = painting_data["exit_only"]
+ else:
+ exit_only = False
+
+ if "enter_only" in painting_data:
+ enter_only = painting_data["enter_only"]
+ else:
+ enter_only = False
+
+ if "req_blocked" in painting_data:
+ req_blocked = painting_data["req_blocked"]
+ else:
+ req_blocked = False
+
+ if "req_blocked_when_no_doors" in painting_data:
+ req_blocked_when_no_doors = painting_data["req_blocked_when_no_doors"]
+ else:
+ req_blocked_when_no_doors = False
+
+ required_door = None
+ if "required_door" in painting_data:
+ door = painting_data["required_door"]
+ required_door = RoomAndDoor(
+ door["room"] if "room" in door else room_name,
+ door["door"]
+ )
+
+ painting_obj = Painting(painting_id, room_name, enter_only, exit_only,
+ required_painting, rwnd, required_door, disable_painting, req_blocked,
+ req_blocked_when_no_doors)
+ PAINTINGS[painting_id] = painting_obj
+
+
+def process_progression(room_name, progression_name, progression_doors):
+ global PROGRESSIVE_ITEMS, PROGRESSION_BY_ROOM
+
+ # Progressive items are configured as a list of doors.
+ PROGRESSIVE_ITEMS.append(progression_name)
+
+ progression_index = 1
+ for door in progression_doors:
+ if isinstance(door, Dict):
+ door_room = door["room"]
+ door_door = door["door"]
+ else:
+ door_room = room_name
+ door_door = door
+
+ room_progressions = PROGRESSION_BY_ROOM.setdefault(door_room, {})
+ room_progressions[door_door] = Progression(progression_name, progression_index)
+ progression_index += 1
+
+
+def process_room(room_name, room_data):
+ global ALL_ROOMS
+
+ room_obj = Room(room_name, [])
+
+ if "entrances" in room_data:
+ for source_room, doors in room_data["entrances"].items():
+ process_entrance(source_room, doors, room_obj)
+
+ if "panels" in room_data:
+ PANELS_BY_ROOM[room_name] = dict()
+
+ for panel_name, panel_data in room_data["panels"].items():
+ process_panel(room_name, panel_name, panel_data)
+
+ if "doors" in room_data:
+ DOORS_BY_ROOM[room_name] = dict()
+
+ for door_name, door_data in room_data["doors"].items():
+ process_door(room_name, door_name, door_data)
+
+ if "paintings" in room_data:
+ for painting_data in room_data["paintings"]:
+ process_painting(room_name, painting_data)
+
+ if "progression" in room_data:
+ for progression_name, progression_doors in room_data["progression"].items():
+ process_progression(room_name, progression_name, progression_doors)
+
+ ALL_ROOMS.append(room_obj)
+
+
+if __name__ == '__main__':
+ if len(sys.argv) == 1:
+ ll1_path = os.path.join("worlds", "lingo", "data", "LL1.yaml")
+ ids_path = os.path.join("worlds", "lingo", "data", "ids.yaml")
+ output_path = os.path.join("worlds", "lingo", "data", "generated.dat")
+ elif len(sys.argv) != 4:
+ print("")
+ print("Usage: python worlds/lingo/utils/pickle_static_data.py [args]")
+ print("Arguments:")
+ print(" - Path to LL1.yaml")
+ print(" - Path to ids.yaml")
+ print(" - Path to output file")
+
+ exit()
+ else:
+ ll1_path = sys.argv[1]
+ ids_path = sys.argv[2]
+ output_path = sys.argv[3]
+
+ load_static_data(ll1_path, ids_path)
+
+ hashes = {
+ "LL1.yaml": hash_file(ll1_path),
+ "ids.yaml": hash_file(ids_path),
+ }
+
+ pickdata = {
+ "HASHES": hashes,
+ "PAINTINGS": PAINTINGS,
+ "ALL_ROOMS": ALL_ROOMS,
+ "DOORS_BY_ROOM": DOORS_BY_ROOM,
+ "PANELS_BY_ROOM": PANELS_BY_ROOM,
+ "PROGRESSIVE_ITEMS": PROGRESSIVE_ITEMS,
+ "PROGRESSION_BY_ROOM": PROGRESSION_BY_ROOM,
+ "PAINTING_ENTRANCES": PAINTING_ENTRANCES,
+ "PAINTING_EXIT_ROOMS": PAINTING_EXIT_ROOMS,
+ "PAINTING_EXITS": PAINTING_EXITS,
+ "REQUIRED_PAINTING_ROOMS": REQUIRED_PAINTING_ROOMS,
+ "REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS": REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS,
+ "SPECIAL_ITEM_IDS": SPECIAL_ITEM_IDS,
+ "PANEL_LOCATION_IDS": PANEL_LOCATION_IDS,
+ "DOOR_LOCATION_IDS": DOOR_LOCATION_IDS,
+ "DOOR_ITEM_IDS": DOOR_ITEM_IDS,
+ "DOOR_GROUP_ITEM_IDS": DOOR_GROUP_ITEM_IDS,
+ "PROGRESSIVE_ITEM_IDS": PROGRESSIVE_ITEM_IDS,
+ }
+
+ with open(output_path, "wb") as file:
+ pickle.dump(pickdata, file)
diff --git a/worlds/lingo/utils/validate_config.rb b/worlds/lingo/utils/validate_config.rb
index 3ac49dc220ce..ae0ac61cdb1b 100644
--- a/worlds/lingo/utils/validate_config.rb
+++ b/worlds/lingo/utils/validate_config.rb
@@ -8,7 +8,8 @@
require 'yaml'
configpath = ARGV[0]
-mappath = ARGV[1]
+idspath = ARGV[1]
+mappath = ARGV[2]
panels = Set["Countdown Panels/Panel_1234567890_wanderlust"]
doors = Set["Naps Room Doors/Door_hider_new1", "Tower Room Area Doors/Door_wanderer_entrance"]
@@ -41,11 +42,13 @@
directives = Set["entrances", "panels", "doors", "paintings", "progression"]
panel_directives = Set["id", "required_room", "required_door", "required_panel", "colors", "check", "exclude_reduce", "tag", "link", "subtag", "achievement", "copy_to_sign", "non_counting", "hunt"]
-door_directives = Set["id", "painting_id", "panels", "item_name", "location_name", "skip_location", "skip_item", "group", "include_reduce", "junk_item", "event"]
+door_directives = Set["id", "painting_id", "panels", "item_name", "item_group", "location_name", "skip_location", "skip_item", "door_group", "include_reduce", "junk_item", "event"]
painting_directives = Set["id", "enter_only", "exit_only", "orientation", "required_door", "required", "required_when_no_doors", "move", "req_blocked", "req_blocked_when_no_doors"]
non_counting = 0
+ids = YAML.load_file(idspath)
+
config = YAML.load_file(configpath)
config.each do |room_name, room|
configured_rooms.add(room_name)
@@ -162,6 +165,10 @@
unless bad_subdirectives.empty? then
puts "#{room_name} - #{panel_name} :::: Panel has the following invalid subdirectives: #{bad_subdirectives.join(", ")}"
end
+
+ unless ids.include?("panels") and ids["panels"].include?(room_name) and ids["panels"][room_name].include?(panel_name)
+ puts "#{room_name} - #{panel_name} :::: Panel is missing a location ID"
+ end
end
(room["doors"] || {}).each do |door_name, door|
@@ -229,6 +236,18 @@
unless bad_subdirectives.empty? then
puts "#{room_name} - #{door_name} :::: Door has the following invalid subdirectives: #{bad_subdirectives.join(", ")}"
end
+
+ unless door["skip_item"] or door["event"]
+ unless ids.include?("doors") and ids["doors"].include?(room_name) and ids["doors"][room_name].include?(door_name) and ids["doors"][room_name][door_name].include?("item")
+ puts "#{room_name} - #{door_name} :::: Door is missing an item ID"
+ end
+ end
+
+ unless door["skip_location"] or door["event"]
+ unless ids.include?("doors") and ids["doors"].include?(room_name) and ids["doors"][room_name].include?(door_name) and ids["doors"][room_name][door_name].include?("location")
+ puts "#{room_name} - #{door_name} :::: Door is missing a location ID"
+ end
+ end
end
(room["paintings"] || []).each do |painting|
@@ -281,6 +300,10 @@
mentioned_doors.add("#{room_name} - #{door}")
end
end
+
+ unless ids.include?("progression") and ids["progression"].include?(progression_name)
+ puts "#{room_name} - #{progression_name} :::: Progression is missing an item ID"
+ end
end
end
@@ -303,6 +326,10 @@
if num == 1 then
puts "Door group \"#{group}\" only has one door in it"
end
+
+ unless ids.include?("door_groups") and ids["door_groups"].include?(group)
+ puts "#{group} :::: Door group is missing an item ID"
+ end
end
slashed_rooms = configured_rooms.select do |room|
diff --git a/worlds/lufia2ac/Client.py b/worlds/lufia2ac/Client.py
index ac0de19bfdd6..9025a1137b98 100644
--- a/worlds/lufia2ac/Client.py
+++ b/worlds/lufia2ac/Client.py
@@ -30,6 +30,7 @@
class L2ACSNIClient(SNIClient):
game: str = "Lufia II Ancient Cave"
+ patch_suffix = ".apl2ac"
async def validate_rom(self, ctx: SNIContext) -> bool:
from SNIClient import snes_read
diff --git a/worlds/lufia2ac/docs/en_Lufia II Ancient Cave.md b/worlds/lufia2ac/docs/en_Lufia II Ancient Cave.md
index d24c4ef9f9af..1080a77d54f4 100644
--- a/worlds/lufia2ac/docs/en_Lufia II Ancient Cave.md
+++ b/worlds/lufia2ac/docs/en_Lufia II Ancient Cave.md
@@ -1,8 +1,8 @@
# Lufia II - Rise of the Sinistrals (Ancient Cave)
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
diff --git a/worlds/lufia2ac/docs/setup_en.md b/worlds/lufia2ac/docs/setup_en.md
index 4d4ea811abcf..d82853d4fddf 100644
--- a/worlds/lufia2ac/docs/setup_en.md
+++ b/worlds/lufia2ac/docs/setup_en.md
@@ -39,8 +39,8 @@ options.
### Where do I get a config file?
-The [Player Settings](/games/Lufia%20II%20Ancient%20Cave/player-settings) page on the website allows you to configure
-your personal settings and export a config file from them.
+The [Player Options](/games/Lufia%20II%20Ancient%20Cave/player-options) page on the website allows you to configure
+your personal options and export a config file from them.
### Verifying your config file
@@ -49,7 +49,7 @@ If you would like to validate your config file to make sure it works, you may do
## Generating a Single-Player Game
-1. Navigate to the [Player Settings](/games/Lufia%20II%20Ancient%20Cave/player-settings) page, configure your options,
+1. Navigate to the [Player Options](/games/Lufia%20II%20Ancient%20Cave/player-options) page, configure your options,
and click the "Generate Game" button.
2. You will be presented with a "Seed Info" page.
3. Click the "Create New Room" link.
diff --git a/worlds/meritous/__init__.py b/worlds/meritous/__init__.py
index 1bf1bfc0f2e6..fd12734be9db 100644
--- a/worlds/meritous/__init__.py
+++ b/worlds/meritous/__init__.py
@@ -17,7 +17,7 @@
class MeritousWeb(WebWorld):
tutorials = [Tutorial(
- "Meritous Setup Tutorial",
+ "Meritous Setup Guide",
"A guide to setting up the Archipelago Meritous software on your computer.",
"English",
"setup_en.md",
diff --git a/worlds/meritous/docs/en_Meritous.md b/worlds/meritous/docs/en_Meritous.md
index bceae7d9ae2b..d119c3634c58 100644
--- a/worlds/meritous/docs/en_Meritous.md
+++ b/worlds/meritous/docs/en_Meritous.md
@@ -1,7 +1,7 @@
# Meritous
-## Where is the settings page?
-The [player settings page for Meritous](../player-settings) contains all the options you need to configure and export a config file.
+## Where is the options page?
+The [player options page for Meritous](../player-options) contains all the options you need to configure and export a config file.
## What does randomization do to this game?
The PSI Enhancement Tiles have become general-purpose Item Caches, and all upgrades and artifacts are added to the multiworld item pool. Optionally, the progression-critical PSI Keys can also be added to the pool, as well as monster evolution traps which (in vanilla) trigger when bosses are defeated.
diff --git a/worlds/meritous/docs/setup_en.md b/worlds/meritous/docs/setup_en.md
index 63f8657b63bb..9b91f12106de 100644
--- a/worlds/meritous/docs/setup_en.md
+++ b/worlds/meritous/docs/setup_en.md
@@ -40,9 +40,9 @@ Eventually, this process will be moved to in-game menus for better ease of use.
## Finishing the Game
-Your initial goal is to find all three PSI Keys. Depending on your YAML settings, these may be located on pedestals in special rooms in the Atlas Dome, or they may be scattered across other players' worlds. These PSI Keys are then brought to their respective locations in the Dome, where you will be subjected to a boss battle. Once all three bosses are defeated, this unlocks the Cursed Seal, hidden in the farthest-away location from the Entrance. The Compass tiles can help you find your way to these locations.
+Your initial goal is to find all three PSI Keys. Depending on your YAML options, these may be located on pedestals in special rooms in the Atlas Dome, or they may be scattered across other players' worlds. These PSI Keys are then brought to their respective locations in the Dome, where you will be subjected to a boss battle. Once all three bosses are defeated, this unlocks the Cursed Seal, hidden in the farthest-away location from the Entrance. The Compass tiles can help you find your way to these locations.
-At minimum, every seed will require you to find the Cursed Seal and bring it back to the Entrance. The goal can then vary based on your `goal` YAML setting:
+At minimum, every seed will require you to find the Cursed Seal and bring it back to the Entrance. The goal can then vary based on your `goal` YAML option:
- `return_the_cursed_seal`: You will fight the final boss, but win or lose, a victory will be posted.
- `any_ending`: You must defeat the final boss.
diff --git a/worlds/messenger/__init__.py b/worlds/messenger/__init__.py
index b0d031905c92..5e1b12778638 100644
--- a/worlds/messenger/__init__.py
+++ b/worlds/messenger/__init__.py
@@ -1,14 +1,35 @@
import logging
-from typing import Any, Dict, List, Optional
+from datetime import date
+from typing import Any, ClassVar, Dict, List, Optional, TextIO
-from BaseClasses import CollectionState, Item, ItemClassification, Tutorial
+from BaseClasses import CollectionState, Entrance, Item, ItemClassification, MultiWorld, Tutorial
+from Options import Accessibility
+from Utils import output_path
+from settings import FilePath, Group
from worlds.AutoWorld import WebWorld, World
-from .constants import ALL_ITEMS, ALWAYS_LOCATIONS, BOSS_LOCATIONS, FILLER, NOTES, PHOBEKINS
-from .options import Goal, Logic, MessengerOptions, NotesNeeded, PowerSeals
-from .regions import MEGA_SHARDS, REGIONS, REGION_CONNECTIONS, SEALS
+from worlds.LauncherComponents import Component, Type, components
+from .client_setup import launch_game
+from .connections import CONNECTIONS, RANDOMIZED_CONNECTIONS, TRANSITIONS
+from .constants import ALL_ITEMS, ALWAYS_LOCATIONS, BOSS_LOCATIONS, FILLER, NOTES, PHOBEKINS, PROG_ITEMS, TRAPS, \
+ USEFUL_ITEMS
+from .options import AvailablePortals, Goal, Logic, MessengerOptions, NotesNeeded, ShuffleTransitions
+from .portals import PORTALS, add_closed_portal_reqs, disconnect_portals, shuffle_portals, validate_portals
+from .regions import LEVELS, MEGA_SHARDS, LOCATIONS, REGION_CONNECTIONS
from .rules import MessengerHardRules, MessengerOOBRules, MessengerRules
-from .shop import FIGURINES, SHOP_ITEMS, shuffle_shop_prices
-from .subclasses import MessengerItem, MessengerRegion
+from .shop import FIGURINES, PROG_SHOP_ITEMS, SHOP_ITEMS, USEFUL_SHOP_ITEMS, shuffle_shop_prices
+from .subclasses import MessengerEntrance, MessengerItem, MessengerRegion, MessengerShopLocation
+
+components.append(
+ Component("The Messenger", component_type=Type.CLIENT, func=launch_game)#, game_name="The Messenger", supports_uri=True)
+)
+
+
+class MessengerSettings(Group):
+ class GamePath(FilePath):
+ description = "The Messenger game executable"
+ is_exe = True
+
+ game_path: GamePath = GamePath("TheMessenger.exe")
class MessengerWeb(WebWorld):
@@ -17,7 +38,7 @@ class MessengerWeb(WebWorld):
bug_report_page = "https://github.com/alwaysintreble/TheMessengerRandomizerModAP/issues"
tut_en = Tutorial(
- "Multiworld Setup Tutorial",
+ "Multiworld Setup Guide",
"A guide to setting up The Messenger randomizer on your computer.",
"English",
"setup_en.md",
@@ -35,17 +56,10 @@ class MessengerWorld(World):
adventure full of thrills, surprises, and humor.
"""
game = "The Messenger"
-
- item_name_groups = {
- "Notes": set(NOTES),
- "Keys": set(NOTES),
- "Crest": {"Sun Crest", "Moon Crest"},
- "Phobe": set(PHOBEKINS),
- "Phobekin": set(PHOBEKINS),
- }
-
options_dataclass = MessengerOptions
options: MessengerOptions
+ settings_key = "messenger_settings"
+ settings: ClassVar[MessengerSettings]
base_offset = 0xADD_000
item_name_to_id = {item: item_id
@@ -54,58 +68,151 @@ class MessengerWorld(World):
for location_id, location in
enumerate([
*ALWAYS_LOCATIONS,
- *[seal for seals in SEALS.values() for seal in seals],
*[shard for shards in MEGA_SHARDS.values() for shard in shards],
*BOSS_LOCATIONS,
*[f"The Shop - {shop_loc}" for shop_loc in SHOP_ITEMS],
*FIGURINES,
"Money Wrench",
], base_offset)}
+ item_name_groups = {
+ "Notes": set(NOTES),
+ "Keys": set(NOTES),
+ "Crest": {"Sun Crest", "Moon Crest"},
+ "Phobe": set(PHOBEKINS),
+ "Phobekin": set(PHOBEKINS),
+ }
+ location_name_groups = {
+ "Notes": {
+ "Autumn Hills - Key of Hope",
+ "Searing Crags - Key of Strength",
+ "Underworld - Key of Chaos",
+ "Sunken Shrine - Key of Love",
+ "Elemental Skylands - Key of Symbiosis",
+ "Corrupted Future - Key of Courage",
+ },
+ "Keys": {
+ "Autumn Hills - Key of Hope",
+ "Searing Crags - Key of Strength",
+ "Underworld - Key of Chaos",
+ "Sunken Shrine - Key of Love",
+ "Elemental Skylands - Key of Symbiosis",
+ "Corrupted Future - Key of Courage",
+ },
+ "Phobe": {
+ "Catacombs - Necro",
+ "Bamboo Creek - Claustro",
+ "Searing Crags - Pyro",
+ "Cloud Ruins - Acro",
+ },
+ "Phobekin": {
+ "Catacombs - Necro",
+ "Bamboo Creek - Claustro",
+ "Searing Crags - Pyro",
+ "Cloud Ruins - Acro",
+ },
+ }
- required_client_version = (0, 4, 2)
+ required_client_version = (0, 4, 4)
web = MessengerWeb()
total_seals: int = 0
required_seals: int = 0
+ created_seals: int = 0
total_shards: int = 0
shop_prices: Dict[str, int]
figurine_prices: Dict[str, int]
_filler_items: List[str]
+ starting_portals: List[str]
+ plando_portals: List[str]
+ spoiler_portal_mapping: Dict[str, str]
+ portal_mapping: List[int]
+ transitions: List[Entrance]
+ reachable_locs: int = 0
+ filler: Dict[str, int]
def generate_early(self) -> None:
if self.options.goal == Goal.option_power_seal_hunt:
- self.options.shuffle_seals.value = PowerSeals.option_true
self.total_seals = self.options.total_seals.value
+ if self.options.limited_movement:
+ self.options.accessibility.value = Accessibility.option_minimal
+ if self.options.logic_level < Logic.option_hard:
+ self.options.logic_level.value = Logic.option_hard
+
+ if self.options.early_meditation:
+ self.multiworld.early_items[self.player]["Meditation"] = 1
+
self.shop_prices, self.figurine_prices = shuffle_shop_prices(self)
+ starting_portals = ["Autumn Hills", "Howling Grotto", "Glacial Peak", "Riviere Turquoise", "Sunken Shrine", "Searing Crags"]
+ self.starting_portals = [f"{portal} Portal"
+ for portal in starting_portals[:3] +
+ self.random.sample(starting_portals[3:], k=self.options.available_portals - 3)]
+
+ # super complicated method for adding searing crags to starting portals if it wasn't chosen
+ # TODO add a check for transition shuffle when that gets added back in
+ if not self.options.shuffle_portals and "Searing Crags Portal" not in self.starting_portals:
+ self.starting_portals.append("Searing Crags Portal")
+ if len(self.starting_portals) > 4:
+ portals_to_strip = [portal for portal in ["Riviere Turquoise Portal", "Sunken Shrine Portal"]
+ if portal in self.starting_portals]
+ self.starting_portals.remove(self.random.choice(portals_to_strip))
+
+ self.filler = FILLER.copy()
+ if (not hasattr(self.options, "traps") and date.today() < date(2024, 4, 2)) or self.options.traps:
+ self.filler.update(TRAPS)
+
+ self.plando_portals = []
+ self.portal_mapping = []
+ self.spoiler_portal_mapping = {}
+ self.transitions = []
+
def create_regions(self) -> None:
# MessengerRegion adds itself to the multiworld
- for region in [MessengerRegion(reg_name, self) for reg_name in REGIONS]:
- if region.name in REGION_CONNECTIONS:
- region.add_exits(REGION_CONNECTIONS[region.name])
+ # create simple regions
+ simple_regions = [MessengerRegion(level, self) for level in LEVELS]
+ # create complex regions that have sub-regions
+ complex_regions = [MessengerRegion(f"{parent} - {reg_name}", self, parent)
+ for parent, sub_region in CONNECTIONS.items()
+ for reg_name in sub_region]
+
+ for region in complex_regions:
+ region_name = region.name.replace(f"{region.parent} - ", "")
+ connection_data = CONNECTIONS[region.parent][region_name]
+ for exit_region in connection_data:
+ region.connect(self.multiworld.get_region(exit_region, self.player))
+
+ # all regions need to be created before i can do these connections so we create and connect the complex first
+ for region in [level for level in simple_regions if level.name in REGION_CONNECTIONS]:
+ region.add_exits(REGION_CONNECTIONS[region.name])
def create_items(self) -> None:
# create items that are always in the item pool
+ main_movement_items = ["Rope Dart", "Wingsuit"]
+ precollected_names = [item.name for item in self.multiworld.precollected_items[self.player]]
itempool: List[MessengerItem] = [
self.create_item(item)
for item in self.item_name_to_id
- if item not in
- {
- "Power Seal", *NOTES, *FIGURINES,
- *{collected_item.name for collected_item in self.multiworld.precollected_items[self.player]},
- } and "Time Shard" not in item
+ if item not in {
+ "Power Seal", *NOTES, *FIGURINES, *main_movement_items,
+ *precollected_names, *FILLER, *TRAPS,
+ }
]
+ if self.options.limited_movement:
+ itempool.append(self.create_item(self.random.choice(main_movement_items)))
+ else:
+ itempool += [self.create_item(move_item) for move_item in main_movement_items]
+
if self.options.goal == Goal.option_open_music_box:
# make a list of all notes except those in the player's defined starting inventory, and adjust the
# amount we need to put in the itempool and precollect based on that
- notes = [note for note in NOTES if note not in self.multiworld.precollected_items[self.player]]
+ notes = [note for note in NOTES if note not in precollected_names]
self.random.shuffle(notes)
precollected_notes_amount = NotesNeeded.range_end - \
- self.options.notes_needed - \
- (len(NOTES) - len(notes))
+ self.options.notes_needed - \
+ (len(NOTES) - len(notes))
if precollected_notes_amount:
for note in notes[:precollected_notes_amount]:
self.multiworld.push_precollected(self.create_item(note))
@@ -116,26 +223,27 @@ def create_items(self) -> None:
total_seals = min(len(self.multiworld.get_unfilled_locations(self.player)) - len(itempool),
self.options.total_seals.value)
if total_seals < self.total_seals:
- logging.warning(f"Not enough locations for total seals setting "
- f"({self.options.total_seals}). Adjusting to {total_seals}")
+ logging.warning(
+ f"Not enough locations for total seals setting "
+ f"({self.options.total_seals}). Adjusting to {total_seals}"
+ )
self.total_seals = total_seals
self.required_seals = int(self.options.percent_seals_required.value / 100 * self.total_seals)
seals = [self.create_item("Power Seal") for _ in range(self.total_seals)]
- for i in range(self.required_seals):
- seals[i].classification = ItemClassification.progression_skip_balancing
itempool += seals
+ self.multiworld.itempool += itempool
remaining_fill = len(self.multiworld.get_unfilled_locations(self.player)) - len(itempool)
if remaining_fill < 10:
self._filler_items = self.random.choices(
- list(FILLER)[2:],
- weights=list(FILLER.values())[2:],
- k=remaining_fill
+ list(self.filler)[2:],
+ weights=list(self.filler.values())[2:],
+ k=remaining_fill
)
- itempool += [self.create_filler() for _ in range(remaining_fill)]
+ filler = [self.create_filler() for _ in range(remaining_fill)]
- self.multiworld.itempool += itempool
+ self.multiworld.itempool += filler
def set_rules(self) -> None:
logic = self.options.logic_level
@@ -144,37 +252,103 @@ def set_rules(self) -> None:
elif logic == Logic.option_hard:
MessengerHardRules(self).set_messenger_rules()
else:
- MessengerOOBRules(self).set_messenger_rules()
+ raise ValueError(f"Somehow you have a logic option that's currently invalid."
+ f" {logic} for {self.multiworld.get_player_name(self.player)}")
+ # MessengerOOBRules(self).set_messenger_rules()
+
+ add_closed_portal_reqs(self)
+ # i need portal shuffle to happen after rules exist so i can validate it
+ attempts = 5
+ if self.options.shuffle_portals:
+ self.portal_mapping = []
+ self.spoiler_portal_mapping = {}
+ for _ in range(attempts):
+ disconnect_portals(self)
+ shuffle_portals(self)
+ if validate_portals(self):
+ break
+ # failsafe mostly for invalid plandoed portals with no transition shuffle
+ else:
+ raise RuntimeError("Unable to generate valid portal output.")
+
+ def write_spoiler_header(self, spoiler_handle: TextIO) -> None:
+ if self.options.available_portals < 6:
+ spoiler_handle.write(f"\nStarting Portals:\n\n")
+ for portal in self.starting_portals:
+ spoiler_handle.write(f"{portal}\n")
+
+ spoiler = self.multiworld.spoiler
+
+ if self.options.shuffle_portals:
+ # sort the portals as they appear left to right in-game
+ portal_info = sorted(
+ self.spoiler_portal_mapping.items(),
+ key=lambda portal:
+ ["Autumn Hills", "Riviere Turquoise",
+ "Howling Grotto", "Sunken Shrine",
+ "Searing Crags", "Glacial Peak"].index(portal[0]))
+ for portal, output in portal_info:
+ spoiler.set_entrance(f"{portal} Portal", output, "I can write anything I want here lmao", self.player)
def fill_slot_data(self) -> Dict[str, Any]:
- return {
+ slot_data = {
"shop": {SHOP_ITEMS[item].internal_name: price for item, price in self.shop_prices.items()},
"figures": {FIGURINES[item].internal_name: price for item, price in self.figurine_prices.items()},
"max_price": self.total_shards,
"required_seals": self.required_seals,
+ "starting_portals": self.starting_portals,
+ "portal_exits": self.portal_mapping,
+ "transitions": [[TRANSITIONS.index("Corrupted Future") if transition.name == "Artificer's Portal"
+ else TRANSITIONS.index(RANDOMIZED_CONNECTIONS[transition.parent_region.name]),
+ TRANSITIONS.index(transition.connected_region.name)]
+ for transition in self.transitions],
**self.options.as_dict("music_box", "death_link", "logic_level"),
}
+ return slot_data
def get_filler_item_name(self) -> str:
if not getattr(self, "_filler_items", None):
self._filler_items = [name for name in self.random.choices(
- list(FILLER),
- weights=list(FILLER.values()),
+ list(self.filler),
+ weights=list(self.filler.values()),
k=20
)]
return self._filler_items.pop(0)
def create_item(self, name: str) -> MessengerItem:
item_id: Optional[int] = self.item_name_to_id.get(name, None)
- override_prog = getattr(self, "multiworld") is not None and \
- name in {"Windmill Shuriken"} and \
- self.options.logic_level > Logic.option_normal
- count = 0
+ return MessengerItem(
+ name,
+ ItemClassification.progression if item_id is None else self.get_item_classification(name),
+ item_id,
+ self.player
+ )
+
+ def get_item_classification(self, name: str) -> ItemClassification:
if "Time Shard " in name:
count = int(name.strip("Time Shard ()"))
count = count if count >= 100 else 0
self.total_shards += count
- return MessengerItem(name, self.player, item_id, override_prog, count)
+ return ItemClassification.progression_skip_balancing if count else ItemClassification.filler
+
+ if name == "Windmill Shuriken" and getattr(self, "multiworld", None) is not None:
+ return ItemClassification.progression if self.options.logic_level else ItemClassification.filler
+
+ if name == "Power Seal":
+ self.created_seals += 1
+ return ItemClassification.progression_skip_balancing \
+ if self.required_seals >= self.created_seals else ItemClassification.filler
+
+ if name in {*NOTES, *PROG_ITEMS, *PHOBEKINS, *PROG_SHOP_ITEMS}:
+ return ItemClassification.progression
+
+ if name in {*USEFUL_ITEMS, *USEFUL_SHOP_ITEMS}:
+ return ItemClassification.useful
+
+ if name in TRAPS:
+ return ItemClassification.trap
+
+ return ItemClassification.filler
def collect(self, state: "CollectionState", item: "Item") -> bool:
change = super().collect(state, item)
@@ -187,3 +361,25 @@ def remove(self, state: "CollectionState", item: "Item") -> bool:
if change and "Time Shard" in item.name:
state.prog_items[self.player]["Shards"] -= int(item.name.strip("Time Shard ()"))
return change
+
+ @classmethod
+ def stage_generate_output(cls, multiworld: MultiWorld, output_directory: str) -> None:
+ # using stage_generate_output because it doesn't increase the logged player count for players without output
+ # only generate output if there's a single player
+ if multiworld.players > 1:
+ return
+ # the messenger client calls into AP with specific args, so check the out path matches what the client sends
+ out_path = output_path(multiworld.get_out_file_name_base(1) + ".aptm")
+ if "The Messenger\\Archipelago\\output" not in out_path:
+ return
+ import orjson
+ data = {
+ "name": multiworld.get_player_name(1),
+ "slot_data": multiworld.worlds[1].fill_slot_data(),
+ "loc_data": {loc.address: {loc.item.name: [loc.item.code, loc.item.flags]}
+ for loc in multiworld.get_filled_locations() if loc.address},
+ }
+
+ output = orjson.dumps(data, option=orjson.OPT_NON_STR_KEYS)
+ with open(out_path, "wb") as f:
+ f.write(output)
diff --git a/worlds/messenger/client_setup.py b/worlds/messenger/client_setup.py
new file mode 100644
index 000000000000..9fd08e52d899
--- /dev/null
+++ b/worlds/messenger/client_setup.py
@@ -0,0 +1,164 @@
+import io
+import logging
+import os.path
+import subprocess
+import urllib.request
+from shutil import which
+from tkinter.messagebox import askyesnocancel
+from typing import Any, Optional
+from zipfile import ZipFile
+from Utils import open_file
+
+import requests
+
+from Utils import is_windows, messagebox, tuplize_version
+
+
+MOD_URL = "https://api.github.com/repos/alwaysintreble/TheMessengerRandomizerModAP/releases/latest"
+
+
+def launch_game(url: Optional[str] = None) -> None:
+ """Check the game installation, then launch it"""
+ def courier_installed() -> bool:
+ """Check if Courier is installed"""
+ return os.path.exists(os.path.join(game_folder, "TheMessenger_Data", "Managed", "Assembly-CSharp.Courier.mm.dll"))
+
+ def mod_installed() -> bool:
+ """Check if the mod is installed"""
+ return os.path.exists(os.path.join(game_folder, "Mods", "TheMessengerRandomizerAP", "courier.toml"))
+
+ def request_data(request_url: str) -> Any:
+ """Fetches json response from given url"""
+ logging.info(f"requesting {request_url}")
+ response = requests.get(request_url)
+ if response.status_code == 200: # success
+ try:
+ data = response.json()
+ except requests.exceptions.JSONDecodeError:
+ raise RuntimeError(f"Unable to fetch data. (status code {response.status_code})")
+ else:
+ raise RuntimeError(f"Unable to fetch data. (status code {response.status_code})")
+ return data
+
+ def install_courier() -> None:
+ """Installs latest version of Courier"""
+ # can't use latest since courier uses pre-release tags
+ courier_url = "https://api.github.com/repos/Brokemia/Courier/releases"
+ latest_download = request_data(courier_url)[0]["assets"][-1]["browser_download_url"]
+
+ with urllib.request.urlopen(latest_download) as download:
+ with ZipFile(io.BytesIO(download.read()), "r") as zf:
+ for member in zf.infolist():
+ zf.extract(member, path=game_folder)
+
+ os.chdir(game_folder)
+ # linux and mac handling
+ if not is_windows:
+ mono_exe = which("mono")
+ if not mono_exe:
+ # steam deck support but doesn't currently work
+ messagebox("Failure", "Failed to install Courier", True)
+ raise RuntimeError("Failed to install Courier")
+ # # download and use mono kickstart
+ # # this allows steam deck support
+ # mono_kick_url = "https://github.com/flibitijibibo/MonoKickstart/archive/refs/heads/master.zip"
+ # target = os.path.join(folder, "monoKickstart")
+ # os.makedirs(target, exist_ok=True)
+ # with urllib.request.urlopen(mono_kick_url) as download:
+ # with ZipFile(io.BytesIO(download.read()), "r") as zf:
+ # for member in zf.infolist():
+ # zf.extract(member, path=target)
+ # installer = subprocess.Popen([os.path.join(target, "precompiled"),
+ # os.path.join(folder, "MiniInstaller.exe")], shell=False)
+ # os.remove(target)
+ else:
+ installer = subprocess.Popen([mono_exe, os.path.join(game_folder, "MiniInstaller.exe")], shell=False)
+ else:
+ installer = subprocess.Popen(os.path.join(game_folder, "MiniInstaller.exe"), shell=False)
+
+ failure = installer.wait()
+ if failure:
+ messagebox("Failure", "Failed to install Courier", True)
+ os.chdir(working_directory)
+ raise RuntimeError("Failed to install Courier")
+ os.chdir(working_directory)
+
+ if courier_installed():
+ messagebox("Success!", "Courier successfully installed!")
+ return
+ messagebox("Failure", "Failed to install Courier", True)
+ raise RuntimeError("Failed to install Courier")
+
+ def install_mod() -> None:
+ """Installs latest version of the mod"""
+ assets = request_data(MOD_URL)["assets"]
+ if len(assets) == 1:
+ release_url = assets[0]["browser_download_url"]
+ else:
+ for asset in assets:
+ if "TheMessengerRandomizerAP" in asset["name"]:
+ release_url = asset["browser_download_url"]
+ break
+ else:
+ messagebox("Failure", "Failed to find latest mod download", True)
+ raise RuntimeError("Failed to install Mod")
+
+ mod_folder = os.path.join(game_folder, "Mods")
+ os.makedirs(mod_folder, exist_ok=True)
+ with urllib.request.urlopen(release_url) as download:
+ with ZipFile(io.BytesIO(download.read()), "r") as zf:
+ for member in zf.infolist():
+ zf.extract(member, path=mod_folder)
+
+ messagebox("Success!", "Latest mod successfully installed!")
+
+ def available_mod_update(latest_version: str) -> bool:
+ """Check if there's an available update"""
+ latest_version = latest_version.lstrip("v")
+ toml_path = os.path.join(game_folder, "Mods", "TheMessengerRandomizerAP", "courier.toml")
+ with open(toml_path, "r") as f:
+ installed_version = f.read().splitlines()[1].strip("version = \"")
+
+ logging.info(f"Installed version: {installed_version}. Latest version: {latest_version}")
+ # one of the alpha builds
+ return "alpha" in latest_version or tuplize_version(latest_version) > tuplize_version(installed_version)
+
+ from . import MessengerWorld
+ game_folder = os.path.dirname(MessengerWorld.settings.game_path)
+ working_directory = os.getcwd()
+ if not courier_installed():
+ should_install = askyesnocancel("Install Courier",
+ "No Courier installation detected. Would you like to install now?")
+ if not should_install:
+ return
+ logging.info("Installing Courier")
+ install_courier()
+ if not mod_installed():
+ should_install = askyesnocancel("Install Mod",
+ "No randomizer mod detected. Would you like to install now?")
+ if not should_install:
+ return
+ logging.info("Installing Mod")
+ install_mod()
+ else:
+ latest = request_data(MOD_URL)["tag_name"]
+ if available_mod_update(latest):
+ should_update = askyesnocancel("Update Mod",
+ f"New mod version detected. Would you like to update to {latest} now?")
+ if should_update:
+ logging.info("Updating mod")
+ install_mod()
+ elif should_update is None:
+ return
+ if not is_windows:
+ if url:
+ open_file(f"steam://rungameid/764790//{url}/")
+ else:
+ open_file("steam://rungameid/764790")
+ else:
+ os.chdir(game_folder)
+ if url:
+ subprocess.Popen([MessengerWorld.settings.game_path, str(url)])
+ else:
+ subprocess.Popen(MessengerWorld.settings.game_path)
+ os.chdir(working_directory)
diff --git a/worlds/messenger/connections.py b/worlds/messenger/connections.py
new file mode 100644
index 000000000000..5e1871e287d2
--- /dev/null
+++ b/worlds/messenger/connections.py
@@ -0,0 +1,725 @@
+from typing import Dict, List
+
+CONNECTIONS: Dict[str, Dict[str, List[str]]] = {
+ "Ninja Village": {
+ "Right": [
+ "Autumn Hills - Left",
+ "Ninja Village - Nest",
+ ],
+ "Nest": [
+ "Ninja Village - Right",
+ ],
+ },
+ "Autumn Hills": {
+ "Left": [
+ "Ninja Village - Right",
+ "Autumn Hills - Climbing Claws Shop",
+ ],
+ "Right": [
+ "Forlorn Temple - Left",
+ "Autumn Hills - Leaf Golem Shop",
+ ],
+ "Bottom": [
+ "Catacombs - Bottom Left",
+ "Autumn Hills - Double Swing Checkpoint",
+ ],
+ "Portal": [
+ "Tower HQ",
+ "Autumn Hills - Dimension Climb Shop",
+ ],
+ "Climbing Claws Shop": [
+ "Autumn Hills - Left",
+ "Autumn Hills - Hope Path Shop",
+ "Autumn Hills - Lakeside Checkpoint",
+ "Autumn Hills - Key of Hope Checkpoint",
+ ],
+ "Hope Path Shop": [
+ "Autumn Hills - Climbing Claws Shop",
+ "Autumn Hills - Hope Latch Checkpoint",
+ "Autumn Hills - Lakeside Checkpoint",
+ ],
+ "Dimension Climb Shop": [
+ "Autumn Hills - Lakeside Checkpoint",
+ "Autumn Hills - Portal",
+ "Autumn Hills - Double Swing Checkpoint",
+ ],
+ "Leaf Golem Shop": [
+ "Autumn Hills - Spike Ball Swing Checkpoint",
+ "Autumn Hills - Right",
+ ],
+ "Hope Latch Checkpoint": [
+ "Autumn Hills - Hope Path Shop",
+ "Autumn Hills - Key of Hope Checkpoint",
+ ],
+ "Key of Hope Checkpoint": [
+ "Autumn Hills - Hope Latch Checkpoint",
+ "Autumn Hills - Lakeside Checkpoint",
+ ],
+ "Lakeside Checkpoint": [
+ "Autumn Hills - Climbing Claws Shop",
+ "Autumn Hills - Dimension Climb Shop",
+ ],
+ "Double Swing Checkpoint": [
+ "Autumn Hills - Dimension Climb Shop",
+ "Autumn Hills - Spike Ball Swing Checkpoint",
+ "Autumn Hills - Bottom",
+ ],
+ "Spike Ball Swing Checkpoint": [
+ "Autumn Hills - Double Swing Checkpoint",
+ "Autumn Hills - Leaf Golem Shop",
+ ],
+ },
+ "Forlorn Temple": {
+ "Left": [
+ "Autumn Hills - Right",
+ "Forlorn Temple - Outside Shop",
+ ],
+ "Right": [
+ "Bamboo Creek - Top Left",
+ "Forlorn Temple - Demon King Shop",
+ ],
+ "Bottom": [
+ "Catacombs - Top Left",
+ "Forlorn Temple - Outside Shop",
+ ],
+ "Outside Shop": [
+ "Forlorn Temple - Left",
+ "Forlorn Temple - Bottom",
+ "Forlorn Temple - Entrance Shop",
+ ],
+ "Entrance Shop": [
+ "Forlorn Temple - Outside Shop",
+ "Forlorn Temple - Sunny Day Checkpoint",
+ ],
+ "Climb Shop": [
+ "Forlorn Temple - Rocket Maze Checkpoint",
+ "Forlorn Temple - Rocket Sunset Shop",
+ ],
+ "Rocket Sunset Shop": [
+ "Forlorn Temple - Climb Shop",
+ "Forlorn Temple - Descent Shop",
+ ],
+ "Descent Shop": [
+ "Forlorn Temple - Rocket Sunset Shop",
+ "Forlorn Temple - Saw Gauntlet Shop",
+ ],
+ "Saw Gauntlet Shop": [
+ "Forlorn Temple - Demon King Shop",
+ ],
+ "Demon King Shop": [
+ "Forlorn Temple - Saw Gauntlet Shop",
+ "Forlorn Temple - Right",
+ ],
+ "Sunny Day Checkpoint": [
+ "Forlorn Temple - Rocket Maze Checkpoint",
+ ],
+ "Rocket Maze Checkpoint": [
+ "Forlorn Temple - Sunny Day Checkpoint",
+ "Forlorn Temple - Climb Shop",
+ ],
+ },
+ "Catacombs": {
+ "Top Left": [
+ "Forlorn Temple - Bottom",
+ "Catacombs - Triple Spike Crushers Shop",
+ ],
+ "Bottom Left": [
+ "Autumn Hills - Bottom",
+ "Catacombs - Triple Spike Crushers Shop",
+ "Catacombs - Death Trap Checkpoint",
+ ],
+ "Bottom": [
+ "Dark Cave - Right",
+ "Catacombs - Dirty Pond Checkpoint",
+ ],
+ "Right": [
+ "Bamboo Creek - Bottom Left",
+ "Catacombs - Ruxxtin Shop",
+ ],
+ "Triple Spike Crushers Shop": [
+ "Catacombs - Bottom Left",
+ "Catacombs - Death Trap Checkpoint",
+ ],
+ "Ruxxtin Shop": [
+ "Catacombs - Right",
+ "Catacombs - Dirty Pond Checkpoint",
+ ],
+ "Death Trap Checkpoint": [
+ "Catacombs - Triple Spike Crushers Shop",
+ "Catacombs - Bottom Left",
+ "Catacombs - Dirty Pond Checkpoint",
+ ],
+ "Crusher Gauntlet Checkpoint": [
+ "Catacombs - Dirty Pond Checkpoint",
+ ],
+ "Dirty Pond Checkpoint": [
+ "Catacombs - Bottom",
+ "Catacombs - Death Trap Checkpoint",
+ "Catacombs - Crusher Gauntlet Checkpoint",
+ "Catacombs - Ruxxtin Shop",
+ ],
+ },
+ "Bamboo Creek": {
+ "Bottom Left": [
+ "Catacombs - Right",
+ "Bamboo Creek - Spike Crushers Shop",
+ ],
+ "Top Left": [
+ "Bamboo Creek - Abandoned Shop",
+ "Forlorn Temple - Right",
+ ],
+ "Right": [
+ "Howling Grotto - Left",
+ "Bamboo Creek - Time Loop Shop",
+ ],
+ "Spike Crushers Shop": [
+ "Bamboo Creek - Bottom Left",
+ "Bamboo Creek - Abandoned Shop",
+ ],
+ "Abandoned Shop": [
+ "Bamboo Creek - Spike Crushers Shop",
+ "Bamboo Creek - Spike Doors Checkpoint",
+ ],
+ "Time Loop Shop": [
+ "Bamboo Creek - Right",
+ "Bamboo Creek - Spike Doors Checkpoint",
+ ],
+ "Spike Ball Pits Checkpoint": [
+ "Bamboo Creek - Spike Doors Checkpoint",
+ ],
+ "Spike Doors Checkpoint": [
+ "Bamboo Creek - Abandoned Shop",
+ "Bamboo Creek - Spike Ball Pits Checkpoint",
+ "Bamboo Creek - Time Loop Shop",
+ ],
+ },
+ "Howling Grotto": {
+ "Left": [
+ "Bamboo Creek - Right",
+ "Howling Grotto - Wingsuit Shop",
+ ],
+ "Top": [
+ "Howling Grotto - Crushing Pits Shop",
+ "Quillshroom Marsh - Bottom Left",
+ ],
+ "Right": [
+ "Howling Grotto - Emerald Golem Shop",
+ "Quillshroom Marsh - Top Left",
+ ],
+ "Bottom": [
+ "Howling Grotto - Lost Woods Checkpoint",
+ "Sunken Shrine - Left",
+ ],
+ "Portal": [
+ "Howling Grotto - Crushing Pits Shop",
+ "Tower HQ",
+ ],
+ "Wingsuit Shop": [
+ "Howling Grotto - Left",
+ "Howling Grotto - Lost Woods Checkpoint",
+ ],
+ "Crushing Pits Shop": [
+ "Howling Grotto - Lost Woods Checkpoint",
+ "Howling Grotto - Portal",
+ "Howling Grotto - Breezy Crushers Checkpoint",
+ "Howling Grotto - Top",
+ ],
+ "Emerald Golem Shop": [
+ "Howling Grotto - Breezy Crushers Checkpoint",
+ "Howling Grotto - Right",
+ ],
+ "Lost Woods Checkpoint": [
+ "Howling Grotto - Wingsuit Shop",
+ "Howling Grotto - Crushing Pits Shop",
+ "Howling Grotto - Bottom",
+ ],
+ "Breezy Crushers Checkpoint": [
+ "Howling Grotto - Crushing Pits Shop",
+ "Howling Grotto - Emerald Golem Shop",
+ ],
+ },
+ "Quillshroom Marsh": {
+ "Top Left": [
+ "Howling Grotto - Right",
+ "Quillshroom Marsh - Seashell Checkpoint",
+ "Quillshroom Marsh - Spikey Window Shop",
+ ],
+ "Bottom Left": [
+ "Howling Grotto - Top",
+ "Quillshroom Marsh - Sand Trap Shop",
+ "Quillshroom Marsh - Bottom Right",
+ ],
+ "Top Right": [
+ "Quillshroom Marsh - Queen of Quills Shop",
+ "Searing Crags - Left",
+ ],
+ "Bottom Right": [
+ "Quillshroom Marsh - Bottom Left",
+ "Quillshroom Marsh - Sand Trap Shop",
+ "Searing Crags - Bottom",
+ ],
+ "Spikey Window Shop": [
+ "Quillshroom Marsh - Top Left",
+ "Quillshroom Marsh - Seashell Checkpoint",
+ "Quillshroom Marsh - Quicksand Checkpoint",
+ ],
+ "Sand Trap Shop": [
+ "Quillshroom Marsh - Quicksand Checkpoint",
+ "Quillshroom Marsh - Bottom Left",
+ "Quillshroom Marsh - Bottom Right",
+ "Quillshroom Marsh - Spike Wave Checkpoint",
+ ],
+ "Queen of Quills Shop": [
+ "Quillshroom Marsh - Spike Wave Checkpoint",
+ "Quillshroom Marsh - Top Right",
+ ],
+ "Seashell Checkpoint": [
+ "Quillshroom Marsh - Top Left",
+ "Quillshroom Marsh - Spikey Window Shop",
+ ],
+ "Quicksand Checkpoint": [
+ "Quillshroom Marsh - Spikey Window Shop",
+ "Quillshroom Marsh - Sand Trap Shop",
+ ],
+ "Spike Wave Checkpoint": [
+ "Quillshroom Marsh - Sand Trap Shop",
+ "Quillshroom Marsh - Queen of Quills Shop",
+ ],
+ },
+ "Searing Crags": {
+ "Left": [
+ "Quillshroom Marsh - Top Right",
+ "Searing Crags - Rope Dart Shop",
+ ],
+ "Top": [
+ "Searing Crags - Colossuses Shop",
+ "Glacial Peak - Bottom",
+ ],
+ "Bottom": [
+ "Searing Crags - Portal",
+ "Quillshroom Marsh - Bottom Right",
+ ],
+ "Right": [
+ "Searing Crags - Portal",
+ "Underworld - Left",
+ ],
+ "Portal": [
+ "Searing Crags - Bottom",
+ "Searing Crags - Right",
+ "Searing Crags - Before Final Climb Shop",
+ "Searing Crags - Colossuses Shop",
+ "Tower HQ",
+ ],
+ "Rope Dart Shop": [
+ "Searing Crags - Left",
+ "Searing Crags - Triple Ball Spinner Checkpoint",
+ ],
+ "Falling Rocks Shop": [
+ "Searing Crags - Triple Ball Spinner Checkpoint",
+ "Searing Crags - Searing Mega Shard Shop",
+ ],
+ "Searing Mega Shard Shop": [
+ "Searing Crags - Falling Rocks Shop",
+ "Searing Crags - Before Final Climb Shop",
+ "Searing Crags - Key of Strength Shop",
+ ],
+ "Before Final Climb Shop": [
+ "Searing Crags - Raining Rocks Checkpoint",
+ "Searing Crags - Portal",
+ "Searing Crags - Colossuses Shop",
+ ],
+ "Colossuses Shop": [
+ "Searing Crags - Before Final Climb Shop",
+ "Searing Crags - Key of Strength Shop",
+ "Searing Crags - Portal",
+ "Searing Crags - Top",
+ ],
+ "Key of Strength Shop": [
+ "Searing Crags - Searing Mega Shard Shop",
+ ],
+ "Triple Ball Spinner Checkpoint": [
+ "Searing Crags - Rope Dart Shop",
+ "Searing Crags - Falling Rocks Shop",
+ ],
+ "Raining Rocks Checkpoint": [
+ "Searing Crags - Searing Mega Shard Shop",
+ "Searing Crags - Before Final Climb Shop",
+ ],
+ },
+ "Glacial Peak": {
+ "Bottom": [
+ "Searing Crags - Top",
+ "Glacial Peak - Ice Climbers' Shop",
+ ],
+ "Left": [
+ "Elemental Skylands - Air Shmup",
+ "Glacial Peak - Projectile Spike Pit Checkpoint",
+ "Glacial Peak - Glacial Mega Shard Shop",
+ ],
+ "Top": [
+ "Glacial Peak - Tower Entrance Shop",
+ "Cloud Ruins - Left",
+ ],
+ "Portal": [
+ "Glacial Peak - Tower Entrance Shop",
+ "Tower HQ",
+ ],
+ "Ice Climbers' Shop": [
+ "Glacial Peak - Bottom",
+ "Glacial Peak - Projectile Spike Pit Checkpoint",
+ ],
+ "Glacial Mega Shard Shop": [
+ "Glacial Peak - Left",
+ "Glacial Peak - Air Swag Checkpoint",
+ ],
+ "Tower Entrance Shop": [
+ "Glacial Peak - Top",
+ "Glacial Peak - Free Climbing Checkpoint",
+ "Glacial Peak - Portal",
+ ],
+ "Projectile Spike Pit Checkpoint": [
+ "Glacial Peak - Ice Climbers' Shop",
+ "Glacial Peak - Left",
+ ],
+ "Air Swag Checkpoint": [
+ "Glacial Peak - Glacial Mega Shard Shop",
+ "Glacial Peak - Free Climbing Checkpoint",
+ ],
+ "Free Climbing Checkpoint": [
+ "Glacial Peak - Air Swag Checkpoint",
+ "Glacial Peak - Tower Entrance Shop",
+ ],
+ },
+ "Tower of Time": {
+ "Left": [
+ "Tower of Time - Final Chance Shop",
+ ],
+ "Final Chance Shop": [
+ "Tower of Time - First Checkpoint",
+ ],
+ "Arcane Golem Shop": [
+ "Tower of Time - Sixth Checkpoint",
+ ],
+ "First Checkpoint": [
+ "Tower of Time - Second Checkpoint",
+ ],
+ "Second Checkpoint": [
+ "Tower of Time - Third Checkpoint",
+ ],
+ "Third Checkpoint": [
+ "Tower of Time - Fourth Checkpoint",
+ ],
+ "Fourth Checkpoint": [
+ "Tower of Time - Fifth Checkpoint",
+ ],
+ "Fifth Checkpoint": [
+ "Tower of Time - Sixth Checkpoint",
+ ],
+ "Sixth Checkpoint": [
+ "Tower of Time - Arcane Golem Shop",
+ ],
+ },
+ "Cloud Ruins": {
+ "Left": [
+ "Glacial Peak - Top",
+ "Cloud Ruins - Cloud Entrance Shop",
+ ],
+ "Cloud Entrance Shop": [
+ "Cloud Ruins - Left",
+ "Cloud Ruins - Spike Float Checkpoint",
+ ],
+ "Pillar Glide Shop": [
+ "Cloud Ruins - Spike Float Checkpoint",
+ "Cloud Ruins - Ghost Pit Checkpoint",
+ "Cloud Ruins - Crushers' Descent Shop",
+ ],
+ "Crushers' Descent Shop": [
+ "Cloud Ruins - Pillar Glide Shop",
+ "Cloud Ruins - Toothbrush Alley Checkpoint",
+ ],
+ "Seeing Spikes Shop": [
+ "Cloud Ruins - Toothbrush Alley Checkpoint",
+ "Cloud Ruins - Sliding Spikes Shop",
+ ],
+ "Sliding Spikes Shop": [
+ "Cloud Ruins - Seeing Spikes Shop",
+ "Cloud Ruins - Saw Pit Checkpoint",
+ ],
+ "Final Flight Shop": [
+ "Cloud Ruins - Saw Pit Checkpoint",
+ "Cloud Ruins - Manfred's Shop",
+ ],
+ "Manfred's Shop": [
+ "Cloud Ruins - Final Flight Shop",
+ ],
+ "Spike Float Checkpoint": [
+ "Cloud Ruins - Cloud Entrance Shop",
+ "Cloud Ruins - Pillar Glide Shop",
+ ],
+ "Ghost Pit Checkpoint": [
+ "Cloud Ruins - Pillar Glide Shop",
+ ],
+ "Toothbrush Alley Checkpoint": [
+ "Cloud Ruins - Crushers' Descent Shop",
+ "Cloud Ruins - Seeing Spikes Shop",
+ ],
+ "Saw Pit Checkpoint": [
+ "Cloud Ruins - Sliding Spikes Shop",
+ "Cloud Ruins - Final Flight Shop",
+ ],
+ },
+ "Underworld": {
+ "Left": [
+ "Underworld - Left Shop",
+ "Searing Crags - Right",
+ ],
+ "Left Shop": [
+ "Underworld - Left",
+ "Underworld - Hot Dip Checkpoint",
+ ],
+ "Fireball Wave Shop": [
+ "Underworld - Hot Dip Checkpoint",
+ "Underworld - Long Climb Shop",
+ ],
+ "Long Climb Shop": [
+ "Underworld - Fireball Wave Shop",
+ "Underworld - Hot Tub Checkpoint",
+ ],
+ "Barm'athaziel Shop": [
+ "Underworld - Hot Tub Checkpoint",
+ ],
+ "Key of Chaos Shop": [
+ ],
+ "Hot Dip Checkpoint": [
+ "Underworld - Left Shop",
+ "Underworld - Fireball Wave Shop",
+ "Underworld - Lava Run Checkpoint",
+ ],
+ "Hot Tub Checkpoint": [
+ "Underworld - Long Climb Shop",
+ "Underworld - Barm'athaziel Shop",
+ ],
+ "Lava Run Checkpoint": [
+ "Underworld - Hot Dip Checkpoint",
+ "Underworld - Key of Chaos Shop",
+ ],
+ },
+ "Dark Cave": {
+ "Right": [
+ "Catacombs - Bottom",
+ "Dark Cave - Left",
+ ],
+ "Left": [
+ "Riviere Turquoise - Right",
+ ],
+ },
+ "Riviere Turquoise": {
+ "Right": [
+ "Riviere Turquoise - Portal",
+ ],
+ "Portal": [
+ "Riviere Turquoise - Waterfall Shop",
+ "Tower HQ",
+ ],
+ "Waterfall Shop": [
+ "Riviere Turquoise - Portal",
+ "Riviere Turquoise - Flower Flight Checkpoint",
+ ],
+ "Launch of Faith Shop": [
+ "Riviere Turquoise - Flower Flight Checkpoint",
+ "Riviere Turquoise - Log Flume Shop",
+ ],
+ "Log Flume Shop": [
+ "Riviere Turquoise - Log Climb Shop",
+ ],
+ "Log Climb Shop": [
+ "Riviere Turquoise - Restock Shop",
+ ],
+ "Restock Shop": [
+ "Riviere Turquoise - Butterfly Matriarch Shop",
+ ],
+ "Butterfly Matriarch Shop": [
+ ],
+ "Flower Flight Checkpoint": [
+ "Riviere Turquoise - Waterfall Shop",
+ "Riviere Turquoise - Launch of Faith Shop",
+ ],
+ },
+ "Elemental Skylands": {
+ "Air Shmup": [
+ "Elemental Skylands - Air Intro Shop",
+ ],
+ "Air Intro Shop": [
+ "Elemental Skylands - Air Seal Checkpoint",
+ "Elemental Skylands - Air Generator Shop",
+ ],
+ "Air Seal Checkpoint": [
+ "Elemental Skylands - Air Intro Shop",
+ "Elemental Skylands - Air Generator Shop",
+ ],
+ "Air Generator Shop": [
+ "Elemental Skylands - Earth Shmup",
+ ],
+ "Earth Shmup": [
+ "Elemental Skylands - Earth Intro Shop",
+ ],
+ "Earth Intro Shop": [
+ "Elemental Skylands - Earth Generator Shop",
+ ],
+ "Earth Generator Shop": [
+ "Elemental Skylands - Fire Shmup",
+ ],
+ "Fire Shmup": [
+ "Elemental Skylands - Fire Intro Shop",
+ ],
+ "Fire Intro Shop": [
+ "Elemental Skylands - Fire Generator Shop",
+ ],
+ "Fire Generator Shop": [
+ "Elemental Skylands - Water Shmup",
+ ],
+ "Water Shmup": [
+ "Elemental Skylands - Water Intro Shop",
+ ],
+ "Water Intro Shop": [
+ "Elemental Skylands - Water Generator Shop",
+ ],
+ "Water Generator Shop": [
+ "Elemental Skylands - Right",
+ ],
+ "Right": [
+ "Glacial Peak - Left",
+ ],
+ },
+ "Sunken Shrine": {
+ "Left": [
+ "Howling Grotto - Bottom",
+ "Sunken Shrine - Portal",
+ ],
+ "Portal": [
+ "Sunken Shrine - Left",
+ "Sunken Shrine - Above Portal Shop",
+ "Sunken Shrine - Sun Path Shop",
+ "Sunken Shrine - Moon Path Shop",
+ "Tower HQ",
+ ],
+ "Above Portal Shop": [
+ "Sunken Shrine - Portal",
+ "Sunken Shrine - Lifeguard Shop",
+ ],
+ "Lifeguard Shop": [
+ "Sunken Shrine - Above Portal Shop",
+ "Sunken Shrine - Lightfoot Tabi Checkpoint",
+ ],
+ "Sun Path Shop": [
+ "Sunken Shrine - Portal",
+ "Sunken Shrine - Tabi Gauntlet Shop",
+ ],
+ "Tabi Gauntlet Shop": [
+ "Sunken Shrine - Sun Path Shop",
+ "Sunken Shrine - Sun Crest Checkpoint",
+ ],
+ "Moon Path Shop": [
+ "Sunken Shrine - Portal",
+ "Sunken Shrine - Waterfall Paradise Checkpoint",
+ ],
+ "Lightfoot Tabi Checkpoint": [
+ "Sunken Shrine - Portal",
+ ],
+ "Sun Crest Checkpoint": [
+ "Sunken Shrine - Tabi Gauntlet Shop",
+ "Sunken Shrine - Portal",
+ ],
+ "Waterfall Paradise Checkpoint": [
+ "Sunken Shrine - Moon Path Shop",
+ "Sunken Shrine - Moon Crest Checkpoint",
+ ],
+ "Moon Crest Checkpoint": [
+ "Sunken Shrine - Waterfall Paradise Checkpoint",
+ "Sunken Shrine - Portal",
+ ],
+ },
+}
+
+RANDOMIZED_CONNECTIONS: Dict[str, str] = {
+ "Ninja Village - Right": "Autumn Hills - Left",
+ "Autumn Hills - Left": "Ninja Village - Right",
+ "Autumn Hills - Right": "Forlorn Temple - Left",
+ "Autumn Hills - Bottom": "Catacombs - Bottom Left",
+ "Forlorn Temple - Left": "Autumn Hills - Right",
+ "Forlorn Temple - Right": "Bamboo Creek - Top Left",
+ "Forlorn Temple - Bottom": "Catacombs - Top Left",
+ "Catacombs - Top Left": "Forlorn Temple - Bottom",
+ "Catacombs - Bottom Left": "Autumn Hills - Bottom",
+ "Catacombs - Bottom": "Dark Cave - Right",
+ "Catacombs - Right": "Bamboo Creek - Bottom Left",
+ "Bamboo Creek - Bottom Left": "Catacombs - Right",
+ "Bamboo Creek - Right": "Howling Grotto - Left",
+ "Bamboo Creek - Top Left": "Forlorn Temple - Right",
+ "Howling Grotto - Left": "Bamboo Creek - Right",
+ "Howling Grotto - Top": "Quillshroom Marsh - Bottom Left",
+ "Howling Grotto - Right": "Quillshroom Marsh - Top Left",
+ "Howling Grotto - Bottom": "Sunken Shrine - Left",
+ "Quillshroom Marsh - Top Left": "Howling Grotto - Right",
+ "Quillshroom Marsh - Bottom Left": "Howling Grotto - Top",
+ "Quillshroom Marsh - Top Right": "Searing Crags - Left",
+ "Quillshroom Marsh - Bottom Right": "Searing Crags - Bottom",
+ "Searing Crags - Left": "Quillshroom Marsh - Top Right",
+ "Searing Crags - Top": "Glacial Peak - Bottom",
+ "Searing Crags - Bottom": "Quillshroom Marsh - Bottom Right",
+ "Searing Crags - Right": "Underworld - Left",
+ "Glacial Peak - Bottom": "Searing Crags - Top",
+ "Glacial Peak - Top": "Cloud Ruins - Left",
+ "Glacial Peak - Left": "Elemental Skylands - Air Shmup",
+ "Cloud Ruins - Left": "Glacial Peak - Top",
+ "Elemental Skylands - Right": "Glacial Peak - Left",
+ "Tower HQ": "Tower of Time - Left",
+ "Artificer": "Corrupted Future",
+ "Underworld - Left": "Searing Crags - Right",
+ "Dark Cave - Right": "Catacombs - Bottom",
+ "Dark Cave - Left": "Riviere Turquoise - Right",
+ "Sunken Shrine - Left": "Howling Grotto - Bottom",
+}
+
+TRANSITIONS: List[str] = [
+ "Ninja Village - Right",
+ "Autumn Hills - Left",
+ "Autumn Hills - Right",
+ "Autumn Hills - Bottom",
+ "Forlorn Temple - Left",
+ "Forlorn Temple - Bottom",
+ "Forlorn Temple - Right",
+ "Catacombs - Top Left",
+ "Catacombs - Right",
+ "Catacombs - Bottom",
+ "Catacombs - Bottom Left",
+ "Dark Cave - Right",
+ "Dark Cave - Left",
+ "Riviere Turquoise - Right",
+ "Howling Grotto - Left",
+ "Howling Grotto - Right",
+ "Howling Grotto - Top",
+ "Howling Grotto - Bottom",
+ "Sunken Shrine - Left",
+ "Bamboo Creek - Top Left",
+ "Bamboo Creek - Bottom Left",
+ "Bamboo Creek - Right",
+ "Quillshroom Marsh - Top Left",
+ "Quillshroom Marsh - Bottom Left",
+ "Quillshroom Marsh - Top Right",
+ "Quillshroom Marsh - Bottom Right",
+ "Searing Crags - Left",
+ "Searing Crags - Bottom",
+ "Searing Crags - Right",
+ "Searing Crags - Top",
+ "Glacial Peak - Bottom",
+ "Glacial Peak - Top",
+ "Glacial Peak - Left",
+ "Elemental Skylands - Air Shmup",
+ "Elemental Skylands - Right",
+ "Tower HQ",
+ "Tower of Time - Left",
+ "Corrupted Future",
+ "Cloud Ruins - Left",
+ "Underworld - Left",
+]
diff --git a/worlds/messenger/constants.py b/worlds/messenger/constants.py
index f05d276ceaf4..ea15c71068db 100644
--- a/worlds/messenger/constants.py
+++ b/worlds/messenger/constants.py
@@ -24,6 +24,8 @@
# "Astral Seed",
# "Astral Tea Leaves",
"Money Wrench",
+ "Candle",
+ "Seashell",
]
PHOBEKINS = [
@@ -46,6 +48,11 @@
"Time Shard (500)": 5,
}
+TRAPS = {
+ "Teleport Trap": 5,
+ "Prophecy Trap": 10,
+}
+
# item_name_to_id needs to be deterministic and match upstream
ALL_ITEMS = [
*NOTES,
@@ -69,6 +76,8 @@
*SHOP_ITEMS,
*FIGURINES,
"Money Wrench",
+ "Teleport Trap",
+ "Prophecy Trap",
]
# locations
@@ -103,6 +112,52 @@
"Searing Crags - Pyro",
"Bamboo Creek - Claustro",
"Cloud Ruins - Acro",
+ # seals
+ "Ninja Village Seal - Tree House",
+ "Autumn Hills Seal - Trip Saws",
+ "Autumn Hills Seal - Double Swing Saws",
+ "Autumn Hills Seal - Spike Ball Swing",
+ "Autumn Hills Seal - Spike Ball Darts",
+ "Catacombs Seal - Triple Spike Crushers",
+ "Catacombs Seal - Crusher Gauntlet",
+ "Catacombs Seal - Dirty Pond",
+ "Bamboo Creek Seal - Spike Crushers and Doors",
+ "Bamboo Creek Seal - Spike Ball Pits",
+ "Bamboo Creek Seal - Spike Crushers and Doors v2",
+ "Howling Grotto Seal - Windy Saws and Balls",
+ "Howling Grotto Seal - Crushing Pits",
+ "Howling Grotto Seal - Breezy Crushers",
+ "Quillshroom Marsh Seal - Spikey Window",
+ "Quillshroom Marsh Seal - Sand Trap",
+ "Quillshroom Marsh Seal - Do the Spike Wave",
+ "Searing Crags Seal - Triple Ball Spinner",
+ "Searing Crags Seal - Raining Rocks",
+ "Searing Crags Seal - Rhythm Rocks",
+ "Glacial Peak Seal - Ice Climbers",
+ "Glacial Peak Seal - Projectile Spike Pit",
+ "Glacial Peak Seal - Glacial Air Swag",
+ "Tower of Time Seal - Time Waster",
+ "Tower of Time Seal - Lantern Climb",
+ "Tower of Time Seal - Arcane Orbs",
+ "Cloud Ruins Seal - Ghost Pit",
+ "Cloud Ruins Seal - Toothbrush Alley",
+ "Cloud Ruins Seal - Saw Pit",
+ "Cloud Ruins Seal - Money Farm Room",
+ "Underworld Seal - Sharp and Windy Climb",
+ "Underworld Seal - Spike Wall",
+ "Underworld Seal - Fireball Wave",
+ "Underworld Seal - Rising Fanta",
+ "Forlorn Temple Seal - Rocket Maze",
+ "Forlorn Temple Seal - Rocket Sunset",
+ "Sunken Shrine Seal - Ultra Lifeguard",
+ "Sunken Shrine Seal - Waterfall Paradise",
+ "Sunken Shrine Seal - Tabi Gauntlet",
+ "Riviere Turquoise Seal - Bounces and Balls",
+ "Riviere Turquoise Seal - Launch of Faith",
+ "Riviere Turquoise Seal - Flower Power",
+ "Elemental Skylands Seal - Air",
+ "Elemental Skylands Seal - Water",
+ "Elemental Skylands Seal - Fire",
]
BOSS_LOCATIONS = [
diff --git a/worlds/messenger/docs/en_The Messenger.md b/worlds/messenger/docs/en_The Messenger.md
index 374753b487a0..8248a4755d3f 100644
--- a/worlds/messenger/docs/en_The Messenger.md
+++ b/worlds/messenger/docs/en_The Messenger.md
@@ -49,30 +49,29 @@ for it. The groups you can use for The Messenger are:
## Other changes
* The player can return to the Tower of Time HQ at any point by selecting the button from the options menu
- * This can cause issues if used at specific times. Current known:
- * During Boss fights
- * After Courage Note collection (Corrupted Future chase)
- * This is currently an expected action in logic. If you do need to teleport during this chase sequence, it
- is recommended to quit to title and reload the save
+ * This can cause issues if used at specific times. If used in any of these known problematic areas, immediately
+quit to title and reload the save. The currently known areas include:
+ * During Boss fights
+ * After Courage Note collection (Corrupted Future chase)
* After reaching ninja village a teleport option is added to the menu to reach it quickly
* Toggle Windmill Shuriken button is added to option menu once the item is received
-* The mod option menu will also have a hint item button, as well as a release and collect button that are all placed when
- the player fulfills the necessary conditions.
+* The mod option menu will also have a hint item button, as well as a release and collect button that are all placed
+when the player fulfills the necessary conditions.
* After running the game with the mod, a config file (APConfig.toml) will be generated in your game folder that can be
- used to modify certain settings such as text size and color. This can also be used to specify a player name that can't
- be entered in game.
+used to modify certain settings such as text size and color. This can also be used to specify a player name that can't
+be entered in game.
## Known issues
* Ruxxtin Coffin cutscene will sometimes not play correctly, but will still reward the item
* If you receive the Magic Firefly while in Quillshroom Marsh, The De-curse Queen cutscene will not play. You can exit
- to Searing Crags and re-enter to get it to play correctly.
-* Sometimes upon teleporting back to HQ, Ninja will run left and enter a different portal than the one entered by the
- player. This may also cause a softlock.
+to Searing Crags and re-enter to get it to play correctly.
+* Teleporting back to HQ, then returning to the same level you just left through a Portal can cause Ninja to run left
+and enter a different portal than the one entered by the player or lead to other incorrect inputs, causing a soft lock
* Text entry menus don't accept controller input
-* Opening the shop chest in power seal hunt mode from the tower of time HQ will softlock the game.
-* If you are unable to reset file slots, load into a save slot, let the game save, and close it.
+* In power seal hunt mode, the chest must be opened by entering the shop from a level. Teleporting to HQ and opening the
+chest will not work.
## What do I do if I have a problem?
-If you believe something happened that isn't intended, please get the `log.txt` from the folder of your game installation
-and send a bug report either on GitHub or the [Archipelago Discord Server](http://archipelago.gg/discord)
+If you believe something happened that isn't intended, please get the `log.txt` from the folder of your game
+installation and send a bug report either on GitHub or the [Archipelago Discord Server](http://archipelago.gg/discord)
diff --git a/worlds/messenger/docs/setup_en.md b/worlds/messenger/docs/setup_en.md
index 9617baf3e007..c1770e747442 100644
--- a/worlds/messenger/docs/setup_en.md
+++ b/worlds/messenger/docs/setup_en.md
@@ -9,16 +9,26 @@
## Installation
-1. Read the [Game Info Page](/games/The%20Messenger/info/en) for how the game works, caveats and known issues
-2. Download and install Courier Mod Loader using the instructions on the release page
+Read changes to the base game on the [Game Info Page](/games/The%20Messenger/info/en)
+
+### Automated Installation
+
+1. Download and install the latest [Archipelago release](https://github.com/ArchipelagoMW/Archipelago/releases/latest)
+2. Launch the Archipelago Launcher (ArchipelagoLauncher.exe)
+3. Click on "The Messenger"
+4. Follow the prompts
+
+These steps can also be followed to launch the game and check for mod updates after the initial setup.
+
+### Manual Installation
+
+1. Download and install Courier Mod Loader using the instructions on the release page
* [Latest release is currently 0.7.1](https://github.com/Brokemia/Courier/releases)
-3. Download and install the randomizer mod
+2. Download and install the randomizer mod
1. Download the latest TheMessengerRandomizerAP.zip from
- [The Messenger Randomizer Mod AP releases page](https://github.com/alwaysintreble/TheMessengerRandomizerModAP/releases)
+[The Messenger Randomizer Mod AP releases page](https://github.com/alwaysintreble/TheMessengerRandomizerModAP/releases)
2. Extract the zip file to `TheMessenger/Mods/` of your game's install location
- * You cannot have both the non-AP randomizer and the AP randomizer installed at the same time. The AP randomizer
- is backwards compatible, so the non-AP mod can be safely removed, and you can still play seeds generated from the
- non-AP randomizer.
+ * You cannot have both the non-AP randomizer and the AP randomizer installed at the same time
3. Optionally, Backup your save game
* On Windows
1. Press `Windows Key + R` to open run
@@ -32,19 +42,17 @@
## Joining a MultiWorld Game
1. Launch the game
-2. Navigate to `Options > Third Party Mod Options`
-3. Select `Reset Randomizer File Slots`
- * This will set up all of your save slots with new randomizer save files. You can have up to 3 randomizer files at a
- time, but must do this step again to start new runs afterward.
-4. Enter connection info using the relevant option buttons
+2. Navigate to `Options > Archipelago Options`
+3. Enter connection info using the relevant option buttons
* **The game is limited to alphanumerical characters, `.`, and `-`.**
* This defaults to `archipelago.gg` and does not need to be manually changed if connecting to a game hosted on the
- website.
+website.
* If using a name that cannot be entered in the in game menus, there is a config file (APConfig.toml) in the game
- directory. When using this, all connection information must be entered in the file.
-5. Select the `Connect to Archipelago` button
-6. Navigate to save file selection
-7. Select a new valid randomizer save
+directory. When using this, all connection information must be entered in the file.
+4. Select the `Connect to Archipelago` button
+5. Navigate to save file selection
+6. Start a new game
+ * If you're already connected, deleting an existing save will not disconnect you and is completely safe.
## Continuing a MultiWorld Game
@@ -52,6 +60,5 @@ At any point while playing, it is completely safe to quit. Returning to the titl
disconnect you from the server. To reconnect to an in progress MultiWorld, simply load the correct save file for that
MultiWorld.
-If the reconnection fails, the message on screen will state you are disconnected. If this happens, you can return to the
-main menu and connect to the server as in [Joining a Multiworld Game](#joining-a-multiworld-game), then load the correct
-save file.
+If the reconnection fails, the message on screen will state you are disconnected. If this happens, the game will attempt
+to reconnect in the background. An option will also be added to the in game menu to change the port, if necessary.
diff --git a/worlds/messenger/options.py b/worlds/messenger/options.py
index 1da544bee70c..990975a926f9 100644
--- a/worlds/messenger/options.py
+++ b/worlds/messenger/options.py
@@ -1,9 +1,10 @@
from dataclasses import dataclass
+from datetime import date
from typing import Dict
from schema import And, Optional, Or, Schema
-from Options import Accessibility, Choice, DeathLink, DefaultOnToggle, OptionDict, PerGameCommonOptions, Range, \
+from Options import Accessibility, Choice, DeathLinkMixin, DefaultOnToggle, OptionDict, PerGameCommonOptions, Range, \
StartInventoryPool, Toggle
@@ -17,29 +18,78 @@ class Logic(Choice):
"""
The level of logic to use when determining what locations in your world are accessible.
- Normal: can require damage boosts, but otherwise approachable for someone who has beaten the game.
- Hard: has leashing, normal clips, time warps and turtle boosting in logic.
- OoB: places everything with the minimum amount of rules possible. Expect to do OoB. Not guaranteed completable.
+ Normal: Can require damage boosts, but otherwise approachable for someone who has beaten the game.
+ Hard: Expects more knowledge and tighter execution. Has leashing, normal clips and much tighter d-boosting in logic.
"""
display_name = "Logic Level"
option_normal = 0
option_hard = 1
- option_oob = 2
+ alias_oob = 1
alias_challenging = 1
-class PowerSeals(DefaultOnToggle):
- """Whether power seal locations should be randomized."""
- display_name = "Shuffle Seals"
-
-
class MegaShards(Toggle):
"""Whether mega shards should be item locations."""
display_name = "Shuffle Mega Time Shards"
+class LimitedMovement(Toggle):
+ """
+ Removes either rope dart or wingsuit from the itempool. Forces logic to at least hard and accessibility to minimal.
+ """
+ display_name = "Limited Movement"
+
+
+class EarlyMed(Toggle):
+ """Guarantees meditation will be found early"""
+ display_name = "Early Meditation"
+
+
+class AvailablePortals(Range):
+ """Number of portals that are available from the start. Autumn Hills, Howling Grotto, and Glacial Peak are always available. If portal outputs are not randomized, Searing Crags will also be available."""
+ display_name = "Available Starting Portals"
+ range_start = 3
+ range_end = 6
+ default = 6
+
+
+class ShufflePortals(Choice):
+ """
+ Whether the portals lead to random places.
+ Entering a portal from its vanilla area will always lead to HQ, and will unlock it if relevant.
+ Supports plando.
+
+ None: Portals will take you where they're supposed to.
+ Shops: Portals can lead to any area except Music Box and Elemental Skylands, with each portal output guaranteed to not overlap with another portal's. Will only put you at a portal or a shop.
+ Checkpoints: Like Shops except checkpoints without shops are also valid drop points.
+ Anywhere: Like Checkpoints except it's possible for multiple portals to output to the same map.
+ """
+ display_name = "Shuffle Portal Outputs"
+ option_none = 0
+ alias_off = 0
+ option_shops = 1
+ option_checkpoints = 2
+ option_anywhere = 3
+
+
+class ShuffleTransitions(Choice):
+ """
+ Whether the transitions between the levels should be randomized.
+ Supports plando.
+
+ None: Level transitions lead where they should.
+ Coupled: Returning through a transition will take you from whence you came.
+ Decoupled: Any level transition can take you to any other level transition.
+ """
+ display_name = "Shuffle Level Transitions"
+ option_none = 0
+ alias_off = 0
+ option_coupled = 1
+ option_decoupled = 2
+
+
class Goal(Choice):
- """Requirement to finish the game. Power Seal Hunt will force power seal locations to be shuffled."""
+ """Requirement to finish the game. To win with the power seal hunt goal, you must enter the Music Box through the shop chest."""
display_name = "Goal"
option_open_music_box = 0
option_power_seal_hunt = 1
@@ -74,6 +124,11 @@ class RequiredSeals(Range):
default = range_end
+class Traps(Toggle):
+ """Whether traps should be included in the itempool."""
+ display_name = "Include Traps"
+
+
class ShopPrices(Range):
"""Percentage modifier for shuffled item prices in shops"""
display_name = "Shop Prices Modifier"
@@ -133,12 +188,16 @@ class PlannedShopPrices(OptionDict):
@dataclass
-class MessengerOptions(PerGameCommonOptions):
+class MessengerOptions(DeathLinkMixin, PerGameCommonOptions):
accessibility: MessengerAccessibility
start_inventory: StartInventoryPool
logic_level: Logic
- shuffle_seals: PowerSeals
shuffle_shards: MegaShards
+ limited_movement: LimitedMovement
+ early_meditation: EarlyMed
+ available_portals: AvailablePortals
+ shuffle_portals: ShufflePortals
+ # shuffle_transitions: ShuffleTransitions
goal: Goal
music_box: MusicBox
notes_needed: NotesNeeded
@@ -146,5 +205,6 @@ class MessengerOptions(PerGameCommonOptions):
percent_seals_required: RequiredSeals
shop_price: ShopPrices
shop_price_plan: PlannedShopPrices
- death_link: DeathLink
+ if date.today() > date(2024, 4, 1):
+ traps: Traps
diff --git a/worlds/messenger/portals.py b/worlds/messenger/portals.py
new file mode 100644
index 000000000000..51f51d7e379e
--- /dev/null
+++ b/worlds/messenger/portals.py
@@ -0,0 +1,297 @@
+from copy import deepcopy
+from typing import List, TYPE_CHECKING
+
+from BaseClasses import CollectionState, PlandoOptions
+from .options import ShufflePortals
+from ..generic import PlandoConnection
+
+if TYPE_CHECKING:
+ from . import MessengerWorld
+
+
+PORTALS = [
+ "Autumn Hills",
+ "Riviere Turquoise",
+ "Howling Grotto",
+ "Sunken Shrine",
+ "Searing Crags",
+ "Glacial Peak",
+]
+
+
+SHOP_POINTS = {
+ "Autumn Hills": [
+ "Climbing Claws",
+ "Hope Path",
+ "Dimension Climb",
+ "Leaf Golem",
+ ],
+ "Forlorn Temple": [
+ "Outside",
+ "Entrance",
+ "Climb",
+ "Rocket Sunset",
+ "Descent",
+ "Saw Gauntlet",
+ "Demon King",
+ ],
+ "Catacombs": [
+ "Triple Spike Crushers",
+ "Ruxxtin",
+ ],
+ "Bamboo Creek": [
+ "Spike Crushers",
+ "Abandoned",
+ "Time Loop",
+ ],
+ "Howling Grotto": [
+ "Wingsuit",
+ "Crushing Pits",
+ "Emerald Golem",
+ ],
+ "Quillshroom Marsh": [
+ "Spikey Window",
+ "Sand Trap",
+ "Queen of Quills",
+ ],
+ "Searing Crags": [
+ "Rope Dart",
+ "Falling Rocks",
+ "Searing Mega Shard",
+ "Before Final Climb",
+ "Colossuses",
+ "Key of Strength",
+ ],
+ "Glacial Peak": [
+ "Ice Climbers'",
+ "Glacial Mega Shard",
+ "Tower Entrance",
+ ],
+ "Tower of Time": [
+ "Final Chance",
+ "Arcane Golem",
+ ],
+ "Cloud Ruins": [
+ "Cloud Entrance",
+ "Pillar Glide",
+ "Crushers' Descent",
+ "Seeing Spikes",
+ "Final Flight",
+ "Manfred's",
+ ],
+ "Underworld": [
+ "Left",
+ "Fireball Wave",
+ "Long Climb",
+ # "Barm'athaziel", # not currently valid
+ "Key of Chaos",
+ ],
+ "Riviere Turquoise": [
+ "Waterfall",
+ "Launch of Faith",
+ "Log Flume",
+ "Log Climb",
+ "Restock",
+ "Butterfly Matriarch",
+ ],
+ "Elemental Skylands": [
+ "Air Intro",
+ "Air Generator",
+ "Earth Intro",
+ "Earth Generator",
+ "Fire Intro",
+ "Fire Generator",
+ "Water Intro",
+ "Water Generator",
+ ],
+ "Sunken Shrine": [
+ "Above Portal",
+ "Lifeguard",
+ "Sun Path",
+ "Tabi Gauntlet",
+ "Moon Path",
+ ]
+}
+
+
+CHECKPOINTS = {
+ "Autumn Hills": [
+ "Hope Latch",
+ "Key of Hope",
+ "Lakeside",
+ "Double Swing",
+ "Spike Ball Swing",
+ ],
+ "Forlorn Temple": [
+ "Sunny Day",
+ "Rocket Maze",
+ ],
+ "Catacombs": [
+ "Death Trap",
+ "Crusher Gauntlet",
+ "Dirty Pond",
+ ],
+ "Bamboo Creek": [
+ "Spike Ball Pits",
+ "Spike Doors",
+ ],
+ "Howling Grotto": [
+ "Lost Woods",
+ "Breezy Crushers",
+ ],
+ "Quillshroom Marsh": [
+ "Seashell",
+ "Quicksand",
+ "Spike Wave",
+ ],
+ "Searing Crags": [
+ "Triple Ball Spinner",
+ "Raining Rocks",
+ ],
+ "Glacial Peak": [
+ "Projectile Spike Pit",
+ "Air Swag",
+ "Free Climbing",
+ ],
+ "Tower of Time": [
+ "First",
+ "Second",
+ "Third",
+ "Fourth",
+ "Fifth",
+ "Sixth",
+ ],
+ "Cloud Ruins": [
+ "Spike Float",
+ "Ghost Pit",
+ "Toothbrush Alley",
+ "Saw Pit",
+ ],
+ "Underworld": [
+ "Hot Dip",
+ "Hot Tub",
+ "Lava Run",
+ ],
+ "Riviere Turquoise": [
+ "Flower Flight",
+ ],
+ "Elemental Skylands": [
+ "Air Seal",
+ ],
+ "Sunken Shrine": [
+ "Lightfoot Tabi",
+ "Sun Crest",
+ "Waterfall Paradise",
+ "Moon Crest",
+ ]
+}
+
+
+REGION_ORDER = [
+ "Autumn Hills",
+ "Forlorn Temple",
+ "Catacombs",
+ "Bamboo Creek",
+ "Howling Grotto",
+ "Quillshroom Marsh",
+ "Searing Crags",
+ "Glacial Peak",
+ "Tower of Time",
+ "Cloud Ruins",
+ "Underworld",
+ "Riviere Turquoise",
+ "Elemental Skylands",
+ "Sunken Shrine",
+]
+
+
+def shuffle_portals(world: "MessengerWorld") -> None:
+ """shuffles the output of the portals from the main hub"""
+ def create_mapping(in_portal: str, warp: str) -> str:
+ """assigns the chosen output to the input"""
+ parent = out_to_parent[warp]
+ exit_string = f"{parent.strip(' ')} - "
+
+ if "Portal" in warp:
+ exit_string += "Portal"
+ world.portal_mapping.append(int(f"{REGION_ORDER.index(parent)}00"))
+ elif warp in SHOP_POINTS[parent]:
+ exit_string += f"{warp} Shop"
+ world.portal_mapping.append(int(f"{REGION_ORDER.index(parent)}1{SHOP_POINTS[parent].index(warp)}"))
+ else:
+ exit_string += f"{warp} Checkpoint"
+ world.portal_mapping.append(int(f"{REGION_ORDER.index(parent)}2{CHECKPOINTS[parent].index(warp)}"))
+
+ world.spoiler_portal_mapping[in_portal] = exit_string
+ connect_portal(world, in_portal, exit_string)
+
+ return parent
+
+ def handle_planned_portals(plando_connections: List[PlandoConnection]) -> None:
+ """checks the provided plando connections for portals and connects them"""
+ for connection in plando_connections:
+ if connection.entrance not in PORTALS:
+ continue
+ # let it crash here if input is invalid
+ create_mapping(connection.entrance, connection.exit)
+ world.plando_portals.append(connection.entrance)
+
+ shuffle_type = world.options.shuffle_portals
+ shop_points = deepcopy(SHOP_POINTS)
+ for portal in PORTALS:
+ shop_points[portal].append(f"{portal} Portal")
+ if shuffle_type > ShufflePortals.option_shops:
+ for area, points in CHECKPOINTS.items():
+ shop_points[area] += points
+ out_to_parent = {checkpoint: parent for parent, checkpoints in shop_points.items() for checkpoint in checkpoints}
+ available_portals = [val for zone in shop_points.values() for val in zone]
+ world.random.shuffle(available_portals)
+
+ plando = world.multiworld.plando_connections[world.player]
+ if plando and world.multiworld.plando_options & PlandoOptions.connections:
+ handle_planned_portals(plando)
+
+ for portal in PORTALS:
+ if portal in world.plando_portals:
+ continue
+ warp_point = available_portals.pop()
+ parent = create_mapping(portal, warp_point)
+ if shuffle_type < ShufflePortals.option_anywhere:
+ available_portals = [port for port in available_portals if port not in shop_points[parent]]
+ world.random.shuffle(available_portals)
+
+
+def connect_portal(world: "MessengerWorld", portal: str, out_region: str) -> None:
+ entrance = world.multiworld.get_entrance(f"ToTHQ {portal} Portal", world.player)
+ entrance.connect(world.multiworld.get_region(out_region, world.player))
+
+
+def disconnect_portals(world: "MessengerWorld") -> None:
+ for portal in [port for port in PORTALS if port not in world.plando_portals]:
+ entrance = world.multiworld.get_entrance(f"ToTHQ {portal} Portal", world.player)
+ entrance.connected_region.entrances.remove(entrance)
+ entrance.connected_region = None
+ if portal in world.spoiler_portal_mapping:
+ del world.spoiler_portal_mapping[portal]
+ if len(world.portal_mapping) > len(world.spoiler_portal_mapping):
+ world.portal_mapping = world.portal_mapping[:len(world.spoiler_portal_mapping)]
+
+
+def validate_portals(world: "MessengerWorld") -> bool:
+ # if world.options.shuffle_transitions:
+ # return True
+ new_state = CollectionState(world.multiworld)
+ new_state.update_reachable_regions(world.player)
+ reachable_locs = 0
+ for loc in world.multiworld.get_locations(world.player):
+ reachable_locs += loc.can_reach(new_state)
+ if reachable_locs > 5:
+ return True
+ return False
+
+
+def add_closed_portal_reqs(world: "MessengerWorld") -> None:
+ closed_portals = [entrance for entrance in PORTALS if f"{entrance} Portal" not in world.starting_portals]
+ for portal in closed_portals:
+ tower_exit = world.multiworld.get_entrance(f"ToTHQ {portal} Portal", world.player)
+ tower_exit.access_rule = lambda state: state.has(portal, world.player)
diff --git a/worlds/messenger/regions.py b/worlds/messenger/regions.py
index 43de4dd1f6d0..153f8510f1bd 100644
--- a/worlds/messenger/regions.py
+++ b/worlds/messenger/regions.py
@@ -1,103 +1,446 @@
-from typing import Dict, List, Set
+from typing import Dict, List
-REGIONS: Dict[str, List[str]] = {
- "Menu": [],
- "Tower HQ": [],
- "The Shop": [],
- "The Craftsman's Corner": [],
- "Tower of Time": [],
- "Ninja Village": ["Ninja Village - Candle", "Ninja Village - Astral Seed"],
- "Autumn Hills": ["Autumn Hills - Climbing Claws", "Autumn Hills - Key of Hope", "Autumn Hills - Leaf Golem"],
- "Forlorn Temple": ["Forlorn Temple - Demon King"],
- "Catacombs": ["Catacombs - Necro", "Catacombs - Ruxxtin's Amulet", "Catacombs - Ruxxtin"],
- "Bamboo Creek": ["Bamboo Creek - Claustro"],
- "Howling Grotto": ["Howling Grotto - Wingsuit", "Howling Grotto - Emerald Golem"],
- "Quillshroom Marsh": ["Quillshroom Marsh - Seashell", "Quillshroom Marsh - Queen of Quills"],
- "Searing Crags": ["Searing Crags - Rope Dart"],
- "Searing Crags Upper": ["Searing Crags - Power Thistle", "Searing Crags - Key of Strength",
- "Searing Crags - Astral Tea Leaves"],
- "Glacial Peak": [],
- "Cloud Ruins": [],
- "Cloud Ruins Right": ["Cloud Ruins - Acro"],
- "Underworld": ["Searing Crags - Pyro", "Underworld - Key of Chaos"],
- "Dark Cave": [],
- "Riviere Turquoise Entrance": [],
- "Riviere Turquoise": ["Riviere Turquoise - Butterfly Matriarch"],
- "Sunken Shrine": ["Sunken Shrine - Lightfoot Tabi", "Sunken Shrine - Sun Crest", "Sunken Shrine - Moon Crest",
- "Sunken Shrine - Key of Love"],
- "Elemental Skylands": ["Elemental Skylands - Key of Symbiosis"],
+
+LOCATIONS: Dict[str, List[str]] = {
+ "Ninja Village - Nest": [
+ "Ninja Village - Candle",
+ "Ninja Village - Astral Seed",
+ "Ninja Village Seal - Tree House",
+ ],
+ "Autumn Hills - Climbing Claws Shop": [
+ "Autumn Hills - Climbing Claws",
+ "Autumn Hills Seal - Trip Saws",
+ ],
+ "Autumn Hills - Key of Hope Checkpoint": [
+ "Autumn Hills - Key of Hope",
+ ],
+ "Autumn Hills - Double Swing Checkpoint": [
+ "Autumn Hills Seal - Double Swing Saws",
+ ],
+ "Autumn Hills - Spike Ball Swing Checkpoint": [
+ "Autumn Hills Seal - Spike Ball Swing",
+ "Autumn Hills Seal - Spike Ball Darts",
+ ],
+ "Autumn Hills - Leaf Golem Shop": [
+ "Autumn Hills - Leaf Golem",
+ ],
+ "Forlorn Temple - Rocket Maze Checkpoint": [
+ "Forlorn Temple Seal - Rocket Maze",
+ ],
+ "Forlorn Temple - Rocket Sunset Shop": [
+ "Forlorn Temple Seal - Rocket Sunset",
+ ],
+ "Forlorn Temple - Demon King Shop": [
+ "Forlorn Temple - Demon King",
+ ],
+ "Catacombs - Top Left": [
+ "Catacombs - Necro",
+ ],
+ "Catacombs - Triple Spike Crushers Shop": [
+ "Catacombs Seal - Triple Spike Crushers",
+ ],
+ "Catacombs - Dirty Pond Checkpoint": [
+ "Catacombs Seal - Crusher Gauntlet",
+ "Catacombs Seal - Dirty Pond",
+ ],
+ "Catacombs - Ruxxtin Shop": [
+ "Catacombs - Ruxxtin's Amulet",
+ "Catacombs - Ruxxtin",
+ ],
+ "Bamboo Creek - Spike Crushers Shop": [
+ "Bamboo Creek Seal - Spike Crushers and Doors",
+ ],
+ "Bamboo Creek - Spike Ball Pits Checkpoint": [
+ "Bamboo Creek Seal - Spike Ball Pits",
+ ],
+ "Bamboo Creek - Time Loop Shop": [
+ "Bamboo Creek Seal - Spike Crushers and Doors v2",
+ "Bamboo Creek - Claustro",
+ ],
+ "Howling Grotto - Wingsuit Shop": [
+ "Howling Grotto - Wingsuit",
+ "Howling Grotto Seal - Windy Saws and Balls",
+ ],
+ "Howling Grotto - Crushing Pits Shop": [
+ "Howling Grotto Seal - Crushing Pits",
+ ],
+ "Howling Grotto - Breezy Crushers Checkpoint": [
+ "Howling Grotto Seal - Breezy Crushers",
+ ],
+ "Howling Grotto - Emerald Golem Shop": [
+ "Howling Grotto - Emerald Golem",
+ ],
+ "Quillshroom Marsh - Seashell Checkpoint": [
+ "Quillshroom Marsh - Seashell",
+ ],
+ "Quillshroom Marsh - Spikey Window Shop": [
+ "Quillshroom Marsh Seal - Spikey Window",
+ ],
+ "Quillshroom Marsh - Sand Trap Shop": [
+ "Quillshroom Marsh Seal - Sand Trap",
+ ],
+ "Quillshroom Marsh - Spike Wave Checkpoint": [
+ "Quillshroom Marsh Seal - Do the Spike Wave",
+ ],
+ "Quillshroom Marsh - Queen of Quills Shop": [
+ "Quillshroom Marsh - Queen of Quills",
+ ],
+ "Searing Crags - Rope Dart Shop": [
+ "Searing Crags - Rope Dart",
+ ],
+ "Searing Crags - Triple Ball Spinner Checkpoint": [
+ "Searing Crags Seal - Triple Ball Spinner",
+ ],
+ "Searing Crags - Raining Rocks Checkpoint": [
+ "Searing Crags Seal - Raining Rocks",
+ ],
+ "Searing Crags - Colossuses Shop": [
+ "Searing Crags Seal - Rhythm Rocks",
+ "Searing Crags - Power Thistle",
+ "Searing Crags - Astral Tea Leaves",
+ ],
+ "Searing Crags - Key of Strength Shop": [
+ "Searing Crags - Key of Strength",
+ ],
+ "Searing Crags - Portal": [
+ "Searing Crags - Pyro",
+ ],
+ "Glacial Peak - Ice Climbers' Shop": [
+ "Glacial Peak Seal - Ice Climbers",
+ ],
+ "Glacial Peak - Projectile Spike Pit Checkpoint": [
+ "Glacial Peak Seal - Projectile Spike Pit",
+ ],
+ "Glacial Peak - Air Swag Checkpoint": [
+ "Glacial Peak Seal - Glacial Air Swag",
+ ],
+ "Tower of Time - First Checkpoint": [
+ "Tower of Time Seal - Time Waster",
+ ],
+ "Tower of Time - Fourth Checkpoint": [
+ "Tower of Time Seal - Lantern Climb",
+ ],
+ "Tower of Time - Fifth Checkpoint": [
+ "Tower of Time Seal - Arcane Orbs",
+ ],
+ "Cloud Ruins - Ghost Pit Checkpoint": [
+ "Cloud Ruins Seal - Ghost Pit",
+ ],
+ "Cloud Ruins - Toothbrush Alley Checkpoint": [
+ "Cloud Ruins Seal - Toothbrush Alley",
+ ],
+ "Cloud Ruins - Saw Pit Checkpoint": [
+ "Cloud Ruins Seal - Saw Pit",
+ ],
+ "Cloud Ruins - Final Flight Shop": [
+ "Cloud Ruins - Acro",
+ ],
+ "Cloud Ruins - Manfred's Shop": [
+ "Cloud Ruins Seal - Money Farm Room",
+ ],
+ "Underworld - Left Shop": [
+ "Underworld Seal - Sharp and Windy Climb",
+ ],
+ "Underworld - Fireball Wave Shop": [
+ "Underworld Seal - Spike Wall",
+ "Underworld Seal - Fireball Wave",
+ ],
+ "Underworld - Hot Tub Checkpoint": [
+ "Underworld Seal - Rising Fanta",
+ ],
+ "Underworld - Key of Chaos Shop": [
+ "Underworld - Key of Chaos",
+ ],
+ "Riviere Turquoise - Waterfall Shop": [
+ "Riviere Turquoise Seal - Bounces and Balls",
+ ],
+ "Riviere Turquoise - Launch of Faith Shop": [
+ "Riviere Turquoise Seal - Launch of Faith",
+ ],
+ "Riviere Turquoise - Restock Shop": [
+ "Riviere Turquoise Seal - Flower Power",
+ ],
+ "Riviere Turquoise - Butterfly Matriarch Shop": [
+ "Riviere Turquoise - Butterfly Matriarch",
+ ],
+ "Sunken Shrine - Lifeguard Shop": [
+ "Sunken Shrine Seal - Ultra Lifeguard",
+ ],
+ "Sunken Shrine - Lightfoot Tabi Checkpoint": [
+ "Sunken Shrine - Lightfoot Tabi",
+ ],
+ "Sunken Shrine - Portal": [
+ "Sunken Shrine - Key of Love",
+ ],
+ "Sunken Shrine - Tabi Gauntlet Shop": [
+ "Sunken Shrine Seal - Tabi Gauntlet",
+ ],
+ "Sunken Shrine - Sun Crest Checkpoint": [
+ "Sunken Shrine - Sun Crest",
+ ],
+ "Sunken Shrine - Waterfall Paradise Checkpoint": [
+ "Sunken Shrine Seal - Waterfall Paradise",
+ ],
+ "Sunken Shrine - Moon Crest Checkpoint": [
+ "Sunken Shrine - Moon Crest",
+ ],
+ "Elemental Skylands - Air Seal Checkpoint": [
+ "Elemental Skylands Seal - Air",
+ ],
+ "Elemental Skylands - Water Intro Shop": [
+ "Elemental Skylands Seal - Water",
+ ],
+ "Elemental Skylands - Fire Intro Shop": [
+ "Elemental Skylands Seal - Fire",
+ ],
+ "Elemental Skylands - Right": [
+ "Elemental Skylands - Key of Symbiosis",
+ ],
"Corrupted Future": ["Corrupted Future - Key of Courage"],
"Music Box": ["Rescue Phantom"],
}
-SEALS: Dict[str, List[str]] = {
- "Ninja Village": ["Ninja Village Seal - Tree House"],
- "Autumn Hills": ["Autumn Hills Seal - Trip Saws", "Autumn Hills Seal - Double Swing Saws",
- "Autumn Hills Seal - Spike Ball Swing", "Autumn Hills Seal - Spike Ball Darts"],
- "Catacombs": ["Catacombs Seal - Triple Spike Crushers", "Catacombs Seal - Crusher Gauntlet",
- "Catacombs Seal - Dirty Pond"],
- "Bamboo Creek": ["Bamboo Creek Seal - Spike Crushers and Doors", "Bamboo Creek Seal - Spike Ball Pits",
- "Bamboo Creek Seal - Spike Crushers and Doors v2"],
- "Howling Grotto": ["Howling Grotto Seal - Windy Saws and Balls", "Howling Grotto Seal - Crushing Pits",
- "Howling Grotto Seal - Breezy Crushers"],
- "Quillshroom Marsh": ["Quillshroom Marsh Seal - Spikey Window", "Quillshroom Marsh Seal - Sand Trap",
- "Quillshroom Marsh Seal - Do the Spike Wave"],
- "Searing Crags": ["Searing Crags Seal - Triple Ball Spinner"],
- "Searing Crags Upper": ["Searing Crags Seal - Raining Rocks", "Searing Crags Seal - Rhythm Rocks"],
- "Glacial Peak": ["Glacial Peak Seal - Ice Climbers", "Glacial Peak Seal - Projectile Spike Pit",
- "Glacial Peak Seal - Glacial Air Swag"],
- "Tower of Time": ["Tower of Time Seal - Time Waster", "Tower of Time Seal - Lantern Climb",
- "Tower of Time Seal - Arcane Orbs"],
- "Cloud Ruins Right": ["Cloud Ruins Seal - Ghost Pit", "Cloud Ruins Seal - Toothbrush Alley",
- "Cloud Ruins Seal - Saw Pit", "Cloud Ruins Seal - Money Farm Room"],
- "Underworld": ["Underworld Seal - Sharp and Windy Climb", "Underworld Seal - Spike Wall",
- "Underworld Seal - Fireball Wave", "Underworld Seal - Rising Fanta"],
- "Forlorn Temple": ["Forlorn Temple Seal - Rocket Maze", "Forlorn Temple Seal - Rocket Sunset"],
- "Sunken Shrine": ["Sunken Shrine Seal - Ultra Lifeguard", "Sunken Shrine Seal - Waterfall Paradise",
- "Sunken Shrine Seal - Tabi Gauntlet"],
- "Riviere Turquoise Entrance": ["Riviere Turquoise Seal - Bounces and Balls"],
- "Riviere Turquoise": ["Riviere Turquoise Seal - Launch of Faith", "Riviere Turquoise Seal - Flower Power"],
- "Elemental Skylands": ["Elemental Skylands Seal - Air", "Elemental Skylands Seal - Water",
- "Elemental Skylands Seal - Fire"]
+
+SUB_REGIONS: Dict[str, List[str]] = {
+ "Ninja Village": [
+ "Right",
+ ],
+ "Autumn Hills": [
+ "Left",
+ "Right",
+ "Bottom",
+ "Portal",
+ "Climbing Claws Shop",
+ "Hope Path Shop",
+ "Dimension Climb Shop",
+ "Leaf Golem Shop",
+ "Hope Path Checkpoint",
+ "Key of Hope Checkpoint",
+ "Lakeside Checkpoint",
+ "Double Swing Checkpoint",
+ "Spike Ball Swing Checkpoint",
+ ],
+ "Forlorn Temple": [
+ "Left",
+ "Right",
+ "Bottom",
+ "Outside Shop",
+ "Entrance Shop",
+ "Climb Shop",
+ "Rocket Sunset Shop",
+ "Descent Shop",
+ "Final Fall Shop",
+ "Demon King Shop",
+ "Sunny Day Checkpoint",
+ "Rocket Maze Checkpoint",
+ ],
+ "Catacombs": [
+ "Top Left",
+ "Bottom Left",
+ "Bottom",
+ "Right",
+ "Triple Spike Crushers Shop",
+ "Ruxxtin Shop",
+ "Death Trap Checkpoint",
+ "Crusher Gauntlet Checkpoint",
+ "Dirty Pond Checkpoint",
+ ],
+ "Bamboo Creek": [
+ "Bottom Left",
+ "Top Left",
+ "Right",
+ "Spike Crushers Shop",
+ "Abandoned Shop",
+ "Time Loop Shop",
+ "Spike Ball Pits Checkpoint",
+ "Spike Doors Checkpoint",
+ ],
+ "Howling Grotto": [
+ "Left",
+ "Top",
+ "Right",
+ "Bottom",
+ "Portal",
+ "Wingsuit Shop",
+ "Crushing Pits Shop",
+ "Emerald Golem Shop",
+ "Lost Woods Checkpoint",
+ "Breezy Crushers Checkpoint",
+ ],
+ "Quillshroom Marsh": [
+ "Top Left",
+ "Bottom Left",
+ "Top Right",
+ "Bottom Right",
+ "Spikey Window Shop",
+ "Sand Trap Shop",
+ "Queen of Quills Shop",
+ "Seashell Checkpoint",
+ "Quicksand Checkpoint",
+ "Spike Wave Checkpoint",
+ ],
+ "Searing Crags": [
+ "Left",
+ "Top",
+ "Bottom",
+ "Right",
+ "Portal",
+ "Rope Dart Shop",
+ "Falling Rocks Shop",
+ "Searing Mega Shard Shop",
+ "Before Final Climb Shop",
+ "Colossuses Shop",
+ "Key of Strength Shop",
+ "Triple Ball Spinner Checkpoint",
+ "Raining Rocks Checkpoint",
+ ],
+ "Glacial Peak": [
+ "Bottom",
+ "Top",
+ "Portal",
+ "Ice Climbers' Shop",
+ "Glacial Mega Shard Shop",
+ "Tower Entrance Shop",
+ "Projectile Spike Pit Checkpoint",
+ "Air Swag Checkpoint",
+ "Free Climbing Checkpoint",
+ ],
+ "Tower of Time": [
+ "Left",
+ "Entrance Shop",
+ "Arcane Golem Shop",
+ "First Checkpoint",
+ "Second Checkpoint",
+ "Third Checkpoint",
+ "Fourth Checkpoint",
+ "Fifth Checkpoint",
+ "Sixth Checkpoint",
+ ],
+ "Cloud Ruins": [
+ "Left",
+ "Entrance Shop",
+ "Pillar Glide Shop",
+ "Crushers' Descent Shop",
+ "Seeing Spikes Shop",
+ "Sliding Spikes Shop",
+ "Final Flight Shop",
+ "Manfred's Shop",
+ "Spike Float Checkpoint",
+ "Ghost Pit Checkpoint",
+ "Toothbrush Alley Checkpoint",
+ "Saw Pit Checkpoint",
+ ],
+ "Underworld": [
+ "Left",
+ "Entrance Shop",
+ "Fireball Wave Shop",
+ "Long Climb Shop",
+ "Barm'athaziel Shop",
+ "Key of Chaos Shop",
+ "Hot Dip Checkpoint",
+ "Hot Tub Checkpoint",
+ "Lava Run Checkpoint",
+ ],
+ "Riviere Turquoise": [
+ "Right",
+ "Portal",
+ "Waterfall Shop",
+ "Launch of Faith Shop",
+ "Log Flume Shop",
+ "Log Climb Shop",
+ "Restock Shop",
+ "Butterfly Matriarch Shop",
+ "Flower Flight Checkpoint",
+ ],
+ "Elemental Skylands": [
+ "Air Shmup",
+ "Air Intro Shop",
+ "Air Seal Checkpoint",
+ "Air Generator Shop",
+ "Earth Shmup",
+ "Earth Intro Shop",
+ "Earth Generator Shop",
+ "Fire Shmup",
+ "Fire Intro Shop",
+ "Fire Generator Shop",
+ "Water Shmup",
+ "Water Intro Shop",
+ "Water Generator Shop",
+ "Right",
+ ],
+ "Sunken Shrine": [
+ "Left",
+ "Portal",
+ "Entrance Shop",
+ "Lifeguard Shop",
+ "Sun Path Shop",
+ "Tabi Gauntlet Shop",
+ "Moon Path Shop",
+ "Ninja Tabi Checkpoint",
+ "Sun Crest Checkpoint",
+ "Waterfall Paradise Checkpoint",
+ "Moon Crest Checkpoint",
+ ],
}
+
+# order is slightly funky here for back compat
MEGA_SHARDS: Dict[str, List[str]] = {
- "Autumn Hills": ["Autumn Hills Mega Shard", "Hidden Entrance Mega Shard"],
- "Catacombs": ["Catacombs Mega Shard"],
- "Bamboo Creek": ["Above Entrance Mega Shard", "Abandoned Mega Shard", "Time Loop Mega Shard"],
- "Howling Grotto": ["Bottom Left Mega Shard", "Near Portal Mega Shard", "Pie in the Sky Mega Shard"],
- "Quillshroom Marsh": ["Quillshroom Marsh Mega Shard"],
- "Searing Crags Upper": ["Searing Crags Mega Shard"],
- "Glacial Peak": ["Glacial Peak Mega Shard"],
- "Cloud Ruins": ["Cloud Entrance Mega Shard", "Time Warp Mega Shard"],
- "Cloud Ruins Right": ["Money Farm Room Mega Shard 1", "Money Farm Room Mega Shard 2"],
- "Underworld": ["Under Entrance Mega Shard", "Hot Tub Mega Shard", "Projectile Pit Mega Shard"],
- "Forlorn Temple": ["Sunny Day Mega Shard", "Down Under Mega Shard"],
- "Sunken Shrine": ["Mega Shard of the Moon", "Beginner's Mega Shard", "Mega Shard of the Stars", "Mega Shard of the Sun"],
- "Riviere Turquoise Entrance": ["Waterfall Mega Shard"],
- "Riviere Turquoise": ["Quick Restock Mega Shard 1", "Quick Restock Mega Shard 2"],
- "Elemental Skylands": ["Earth Mega Shard", "Water Mega Shard"],
+ "Autumn Hills - Lakeside Checkpoint": ["Autumn Hills Mega Shard"],
+ "Forlorn Temple - Outside Shop": ["Hidden Entrance Mega Shard"],
+ "Catacombs - Top Left": ["Catacombs Mega Shard"],
+ "Bamboo Creek - Spike Crushers Shop": ["Above Entrance Mega Shard"],
+ "Bamboo Creek - Abandoned Shop": ["Abandoned Mega Shard"],
+ "Bamboo Creek - Time Loop Shop": ["Time Loop Mega Shard"],
+ "Howling Grotto - Lost Woods Checkpoint": ["Bottom Left Mega Shard"],
+ "Howling Grotto - Breezy Crushers Checkpoint": ["Near Portal Mega Shard", "Pie in the Sky Mega Shard"],
+ "Quillshroom Marsh - Spikey Window Shop": ["Quillshroom Marsh Mega Shard"],
+ "Searing Crags - Searing Mega Shard Shop": ["Searing Crags Mega Shard"],
+ "Glacial Peak - Glacial Mega Shard Shop": ["Glacial Peak Mega Shard"],
+ "Cloud Ruins - Cloud Entrance Shop": ["Cloud Entrance Mega Shard", "Time Warp Mega Shard"],
+ "Cloud Ruins - Manfred's Shop": ["Money Farm Room Mega Shard 1", "Money Farm Room Mega Shard 2"],
+ "Underworld - Left Shop": ["Under Entrance Mega Shard"],
+ "Underworld - Hot Tub Checkpoint": ["Hot Tub Mega Shard", "Projectile Pit Mega Shard"],
+ "Forlorn Temple - Sunny Day Checkpoint": ["Sunny Day Mega Shard"],
+ "Forlorn Temple - Demon King Shop": ["Down Under Mega Shard"],
+ "Sunken Shrine - Waterfall Paradise Checkpoint": ["Mega Shard of the Moon"],
+ "Sunken Shrine - Portal": ["Beginner's Mega Shard"],
+ "Sunken Shrine - Above Portal Shop": ["Mega Shard of the Stars"],
+ "Sunken Shrine - Sun Crest Checkpoint": ["Mega Shard of the Sun"],
+ "Riviere Turquoise - Waterfall Shop": ["Waterfall Mega Shard"],
+ "Riviere Turquoise - Restock Shop": ["Quick Restock Mega Shard 1", "Quick Restock Mega Shard 2"],
+ "Elemental Skylands - Earth Intro Shop": ["Earth Mega Shard"],
+ "Elemental Skylands - Water Generator Shop": ["Water Mega Shard"],
}
-REGION_CONNECTIONS: Dict[str, Set[str]] = {
- "Menu": {"Tower HQ"},
- "Tower HQ": {"Autumn Hills", "Howling Grotto", "Searing Crags", "Glacial Peak", "Tower of Time",
- "Riviere Turquoise Entrance", "Sunken Shrine", "Corrupted Future", "The Shop",
- "The Craftsman's Corner", "Music Box"},
- "Autumn Hills": {"Ninja Village", "Forlorn Temple", "Catacombs"},
- "Forlorn Temple": {"Catacombs", "Bamboo Creek"},
- "Catacombs": {"Autumn Hills", "Bamboo Creek", "Dark Cave"},
- "Bamboo Creek": {"Catacombs", "Howling Grotto"},
- "Howling Grotto": {"Bamboo Creek", "Quillshroom Marsh", "Sunken Shrine"},
- "Quillshroom Marsh": {"Howling Grotto", "Searing Crags"},
- "Searing Crags": {"Searing Crags Upper", "Quillshroom Marsh", "Underworld"},
- "Searing Crags Upper": {"Searing Crags", "Glacial Peak"},
- "Glacial Peak": {"Searing Crags Upper", "Tower HQ", "Cloud Ruins", "Elemental Skylands"},
- "Cloud Ruins": {"Cloud Ruins Right"},
- "Cloud Ruins Right": {"Underworld"},
- "Dark Cave": {"Catacombs", "Riviere Turquoise Entrance"},
- "Riviere Turquoise Entrance": {"Riviere Turquoise"},
- "Sunken Shrine": {"Howling Grotto"},
+REGION_CONNECTIONS: Dict[str, Dict[str, str]] = {
+ "Menu": {"Tower HQ": "Start Game"},
+ "Tower HQ": {
+ "Autumn Hills - Portal": "ToTHQ Autumn Hills Portal",
+ "Howling Grotto - Portal": "ToTHQ Howling Grotto Portal",
+ "Searing Crags - Portal": "ToTHQ Searing Crags Portal",
+ "Glacial Peak - Portal": "ToTHQ Glacial Peak Portal",
+ "Tower of Time - Left": "Artificer's Challenge",
+ "Riviere Turquoise - Portal": "ToTHQ Riviere Turquoise Portal",
+ "Sunken Shrine - Portal": "ToTHQ Sunken Shrine Portal",
+ "Corrupted Future": "Artificer's Portal",
+ "The Shop": "Home",
+ "Music Box": "Shrink Down",
+ },
+ "The Shop": {
+ "The Craftsman's Corner": "Money Sink",
+ },
}
-"""Vanilla layout mapping with all Tower HQ portals open. from -> to"""
+"""Vanilla layout mapping with all Tower HQ portals open. format is source[exit_region][entrance_name]"""
+
+
+# regions that don't have sub-regions
+LEVELS: List[str] = [
+ "Menu",
+ "Tower HQ",
+ "The Shop",
+ "The Craftsman's Corner",
+ "Corrupted Future",
+ "Music Box",
+]
diff --git a/worlds/messenger/rules.py b/worlds/messenger/rules.py
index b13a453f7f59..ff1b75d70f27 100644
--- a/worlds/messenger/rules.py
+++ b/worlds/messenger/rules.py
@@ -1,7 +1,7 @@
from typing import Dict, TYPE_CHECKING
from BaseClasses import CollectionState
-from worlds.generic.Rules import add_rule, allow_self_locking_items, CollectionRule
+from worlds.generic.Rules import CollectionRule, add_rule, allow_self_locking_items
from .constants import NOTES, PHOBEKINS
from .options import MessengerAccessibility
@@ -12,6 +12,7 @@
class MessengerRules:
player: int
world: "MessengerWorld"
+ connection_rules: Dict[str, CollectionRule]
region_rules: Dict[str, CollectionRule]
location_rules: Dict[str, CollectionRule]
maximum_price: int
@@ -27,83 +28,286 @@ def __init__(self, world: "MessengerWorld") -> None:
self.maximum_price = min(maximum_price, world.total_shards)
self.required_seals = max(1, world.required_seals)
- self.region_rules = {
- "Ninja Village": self.has_wingsuit,
- "Autumn Hills": self.has_wingsuit,
- "Catacombs": self.has_wingsuit,
- "Bamboo Creek": self.has_wingsuit,
- "Searing Crags Upper": self.has_vertical,
- "Cloud Ruins": lambda state: self.has_vertical(state) and state.has("Ruxxtin's Amulet", self.player),
- "Cloud Ruins Right": lambda state: self.has_wingsuit(state) and
- (self.has_dart(state) or self.can_dboost(state)),
- "Underworld": self.has_tabi,
- "Riviere Turquoise": lambda state: self.has_dart(state) or
- (self.has_wingsuit(state) and self.can_destroy_projectiles(state)),
- "Forlorn Temple": lambda state: state.has_all({"Wingsuit", *PHOBEKINS}, self.player) and self.can_dboost(state),
- "Glacial Peak": self.has_vertical,
- "Elemental Skylands": lambda state: state.has("Magic Firefly", self.player) and self.has_wingsuit(state),
- "Music Box": lambda state: (state.has_all(NOTES, self.player)
- or self.has_enough_seals(state)) and self.has_dart(state),
- "The Craftsman's Corner": lambda state: state.has("Money Wrench", self.player) and self.can_shop(state),
+ # dict of connection names and requirements to traverse the exit
+ self.connection_rules = {
+ # from ToTHQ
+ "Artificer's Portal":
+ lambda state: state.has_all({"Demon King Crown", "Magic Firefly"}, self.player),
+ "Shrink Down":
+ lambda state: state.has_all(NOTES, self.player) or self.has_enough_seals(state),
+ # the shop
+ "Money Sink":
+ lambda state: state.has("Money Wrench", self.player) and self.can_shop(state),
+ # Autumn Hills
+ "Autumn Hills - Portal -> Autumn Hills - Dimension Climb Shop":
+ lambda state: self.has_wingsuit(state) and self.has_dart(state),
+ "Autumn Hills - Dimension Climb Shop -> Autumn Hills - Portal":
+ self.has_vertical,
+ "Autumn Hills - Climbing Claws Shop -> Autumn Hills - Hope Path Shop":
+ self.has_dart,
+ "Autumn Hills - Climbing Claws Shop -> Autumn Hills - Key of Hope Checkpoint":
+ self.false, # hard logic only
+ "Autumn Hills - Hope Path Shop -> Autumn Hills - Hope Latch Checkpoint":
+ self.has_dart,
+ "Autumn Hills - Hope Path Shop -> Autumn Hills - Climbing Claws Shop":
+ lambda state: self.has_dart(state) and self.can_dboost(state),
+ "Autumn Hills - Hope Path Shop -> Autumn Hills - Lakeside Checkpoint":
+ lambda state: self.has_dart(state) and self.can_dboost(state),
+ "Autumn Hills - Hope Latch Checkpoint -> Autumn Hills - Hope Path Shop":
+ self.can_dboost,
+ "Autumn Hills - Hope Latch Checkpoint -> Autumn Hills - Key of Hope Checkpoint":
+ lambda state: self.has_dart(state) and self.has_wingsuit(state),
+ # Forlorn Temple
+ "Forlorn Temple - Outside Shop -> Forlorn Temple - Entrance Shop":
+ lambda state: state.has_all(PHOBEKINS, self.player),
+ "Forlorn Temple - Entrance Shop -> Forlorn Temple - Outside Shop":
+ lambda state: state.has_all(PHOBEKINS, self.player),
+ "Forlorn Temple - Entrance Shop -> Forlorn Temple - Sunny Day Checkpoint":
+ lambda state: self.has_vertical(state) and self.can_dboost(state),
+ "Forlorn Temple - Sunny Day Checkpoint -> Forlorn Temple - Rocket Maze Checkpoint":
+ self.has_vertical,
+ "Forlorn Temple - Rocket Sunset Shop -> Forlorn Temple - Descent Shop":
+ lambda state: self.has_dart(state) and (self.can_dboost(state) or self.has_wingsuit(state)),
+ "Forlorn Temple - Saw Gauntlet Shop -> Forlorn Temple - Demon King Shop":
+ self.has_vertical,
+ "Forlorn Temple - Demon King Shop -> Forlorn Temple - Saw Gauntlet Shop":
+ self.has_vertical,
+ # Howling Grotto
+ "Howling Grotto - Portal -> Howling Grotto - Crushing Pits Shop":
+ self.has_wingsuit,
+ "Howling Grotto - Wingsuit Shop -> Howling Grotto - Left":
+ self.has_wingsuit,
+ "Howling Grotto - Wingsuit Shop -> Howling Grotto - Lost Woods Checkpoint":
+ self.has_wingsuit,
+ "Howling Grotto - Lost Woods Checkpoint -> Howling Grotto - Bottom":
+ lambda state: state.has("Seashell", self.player),
+ "Howling Grotto - Crushing Pits Shop -> Howling Grotto - Portal":
+ lambda state: self.has_wingsuit(state) or self.can_dboost(state),
+ "Howling Grotto - Breezy Crushers Checkpoint -> Howling Grotto - Emerald Golem Shop":
+ self.has_wingsuit,
+ "Howling Grotto - Breezy Crushers Checkpoint -> Howling Grotto - Crushing Pits Shop":
+ lambda state: (self.has_wingsuit(state) or self.can_dboost(
+ state
+ ) or self.can_destroy_projectiles(state))
+ and state.multiworld.get_region(
+ "Howling Grotto - Emerald Golem Shop", self.player
+ ).can_reach(state),
+ "Howling Grotto - Emerald Golem Shop -> Howling Grotto - Right":
+ self.has_wingsuit,
+ # Searing Crags
+ "Searing Crags - Rope Dart Shop -> Searing Crags - Triple Ball Spinner Checkpoint":
+ self.has_vertical,
+ "Searing Crags - Portal -> Searing Crags - Right":
+ self.has_tabi,
+ "Searing Crags - Portal -> Searing Crags - Before Final Climb Shop":
+ self.has_wingsuit,
+ "Searing Crags - Portal -> Searing Crags - Colossuses Shop":
+ self.has_wingsuit,
+ "Searing Crags - Bottom -> Searing Crags - Portal":
+ self.has_wingsuit,
+ "Searing Crags - Right -> Searing Crags - Portal":
+ lambda state: self.has_tabi(state) and self.has_wingsuit(state),
+ "Searing Crags - Colossuses Shop -> Searing Crags - Key of Strength Shop":
+ lambda state: state.has("Power Thistle", self.player)
+ and (self.has_dart(state)
+ or (self.has_wingsuit(state)
+ and self.can_destroy_projectiles(state))),
+ "Searing Crags - Falling Rocks Shop -> Searing Crags - Searing Mega Shard Shop":
+ self.has_dart,
+ "Searing Crags - Searing Mega Shard Shop -> Searing Crags - Before Final Climb Shop":
+ lambda state: self.has_dart(state) or self.can_destroy_projectiles(state),
+ "Searing Crags - Searing Mega Shard Shop -> Searing Crags - Falling Rocks Shop":
+ self.has_dart,
+ "Searing Crags - Searing Mega Shard Shop -> Searing Crags - Key of Strength Shop":
+ self.false,
+ "Searing Crags - Before Final Climb Shop -> Searing Crags - Colossuses Shop":
+ self.has_dart,
+ # Glacial Peak
+ "Glacial Peak - Portal -> Glacial Peak - Tower Entrance Shop":
+ self.has_vertical,
+ "Glacial Peak - Left -> Elemental Skylands - Air Shmup":
+ lambda state: state.has("Magic Firefly", self.player)
+ and state.multiworld.get_location("Quillshroom Marsh - Queen of Quills", self.player)
+ .can_reach(state),
+ "Glacial Peak - Tower Entrance Shop -> Glacial Peak - Top":
+ lambda state: state.has("Ruxxtin's Amulet", self.player),
+ "Glacial Peak - Projectile Spike Pit Checkpoint -> Glacial Peak - Left":
+ lambda state: self.has_dart(state) or (self.can_dboost(state) and self.has_wingsuit(state)),
+ # Tower of Time
+ "Tower of Time - Left -> Tower of Time - Final Chance Shop":
+ self.has_dart,
+ "Tower of Time - Second Checkpoint -> Tower of Time - Third Checkpoint":
+ lambda state: self.has_wingsuit(state) and (self.has_dart(state) or self.can_dboost(state)),
+ "Tower of Time - Third Checkpoint -> Tower of Time - Fourth Checkpoint":
+ lambda state: self.has_wingsuit(state) or self.can_dboost(state),
+ "Tower of Time - Fourth Checkpoint -> Tower of Time - Fifth Checkpoint":
+ lambda state: self.has_wingsuit(state) and self.has_dart(state),
+ "Tower of Time - Fifth Checkpoint -> Tower of Time - Sixth Checkpoint":
+ self.has_wingsuit,
+ # Cloud Ruins
+ "Cloud Ruins - Cloud Entrance Shop -> Cloud Ruins - Spike Float Checkpoint":
+ self.has_wingsuit,
+ "Cloud Ruins - Spike Float Checkpoint -> Cloud Ruins - Cloud Entrance Shop":
+ lambda state: self.has_vertical(state) or self.can_dboost(state),
+ "Cloud Ruins - Spike Float Checkpoint -> Cloud Ruins - Pillar Glide Shop":
+ lambda state: self.has_vertical(state) or self.can_dboost(state),
+ "Cloud Ruins - Pillar Glide Shop -> Cloud Ruins - Spike Float Checkpoint":
+ lambda state: self.has_vertical(state) and self.can_double_dboost(state),
+ "Cloud Ruins - Pillar Glide Shop -> Cloud Ruins - Ghost Pit Checkpoint":
+ lambda state: self.has_dart(state) and self.has_wingsuit(state),
+ "Cloud Ruins - Pillar Glide Shop -> Cloud Ruins - Crushers' Descent Shop":
+ lambda state: self.has_wingsuit(state) and (self.has_dart(state) or self.can_dboost(state)),
+ "Cloud Ruins - Toothbrush Alley Checkpoint -> Cloud Ruins - Seeing Spikes Shop":
+ self.has_vertical,
+ "Cloud Ruins - Seeing Spikes Shop -> Cloud Ruins - Sliding Spikes Shop":
+ self.has_wingsuit,
+ "Cloud Ruins - Sliding Spikes Shop -> Cloud Ruins - Seeing Spikes Shop":
+ self.has_wingsuit,
+ "Cloud Ruins - Sliding Spikes Shop -> Cloud Ruins - Saw Pit Checkpoint":
+ self.has_vertical,
+ "Cloud Ruins - Final Flight Shop -> Cloud Ruins - Manfred's Shop":
+ lambda state: self.has_wingsuit(state) and self.has_dart(state),
+ "Cloud Ruins - Manfred's Shop -> Cloud Ruins - Final Flight Shop":
+ lambda state: self.has_wingsuit(state) and self.can_dboost(state),
+ # Underworld
+ "Underworld - Left -> Underworld - Left Shop":
+ self.has_tabi,
+ "Underworld - Left Shop -> Underworld - Left":
+ self.has_tabi,
+ "Underworld - Hot Dip Checkpoint -> Underworld - Lava Run Checkpoint":
+ self.has_tabi,
+ "Underworld - Fireball Wave Shop -> Underworld - Long Climb Shop":
+ lambda state: self.can_destroy_projectiles(state) or self.has_tabi(state) or self.has_vertical(state),
+ "Underworld - Long Climb Shop -> Underworld - Hot Tub Checkpoint":
+ lambda state: self.has_tabi(state)
+ and (self.can_destroy_projectiles(state)
+ or self.has_wingsuit(state))
+ or (self.has_wingsuit(state)
+ and (self.has_dart(state)
+ or self.can_dboost(state)
+ or self.can_destroy_projectiles(state))),
+ "Underworld - Hot Tub Checkpoint -> Underworld - Long Climb Shop":
+ lambda state: self.has_tabi(state)
+ or self.can_destroy_projectiles(state)
+ or (self.has_dart(state) and self.has_wingsuit(state)),
+ # Dark Cave
+ "Dark Cave - Right -> Dark Cave - Left":
+ lambda state: state.has("Candle", self.player) and self.has_dart(state),
+ # Riviere Turquoise
+ "Riviere Turquoise - Waterfall Shop -> Riviere Turquoise - Flower Flight Checkpoint":
+ lambda state: self.has_dart(state) or (
+ self.has_wingsuit(state) and self.can_destroy_projectiles(state)),
+ "Riviere Turquoise - Launch of Faith Shop -> Riviere Turquoise - Flower Flight Checkpoint":
+ lambda state: self.has_dart(state) and self.can_dboost(state),
+ "Riviere Turquoise - Flower Flight Checkpoint -> Riviere Turquoise - Waterfall Shop":
+ lambda state: False,
+ # Elemental Skylands
+ "Elemental Skylands - Air Intro Shop -> Elemental Skylands - Air Seal Checkpoint":
+ self.has_wingsuit,
+ "Elemental Skylands - Air Intro Shop -> Elemental Skylands - Air Generator Shop":
+ self.has_wingsuit,
+ # Sunken Shrine
+ "Sunken Shrine - Portal -> Sunken Shrine - Sun Path Shop":
+ self.has_tabi,
+ "Sunken Shrine - Portal -> Sunken Shrine - Moon Path Shop":
+ self.has_tabi,
+ "Sunken Shrine - Moon Path Shop -> Sunken Shrine - Waterfall Paradise Checkpoint":
+ self.has_tabi,
+ "Sunken Shrine - Waterfall Paradise Checkpoint -> Sunken Shrine - Moon Path Shop":
+ self.has_tabi,
+ "Sunken Shrine - Tabi Gauntlet Shop -> Sunken Shrine - Sun Path Shop":
+ lambda state: self.can_dboost(state) or self.has_dart(state),
}
self.location_rules = {
# ninja village
- "Ninja Village Seal - Tree House": self.has_dart,
+ "Ninja Village Seal - Tree House":
+ self.has_dart,
+ "Ninja Village - Candle":
+ lambda state: state.multiworld.get_location("Searing Crags - Astral Tea Leaves", self.player).can_reach(
+ state),
# autumn hills
- "Autumn Hills - Key of Hope": self.has_dart,
- "Autumn Hills Seal - Spike Ball Darts": self.is_aerobatic,
+ "Autumn Hills Seal - Spike Ball Darts":
+ self.is_aerobatic,
+ "Autumn Hills Seal - Trip Saws":
+ self.has_wingsuit,
+ # forlorn temple
+ "Forlorn Temple Seal - Rocket Maze":
+ self.has_vertical,
# bamboo creek
- "Bamboo Creek - Claustro": lambda state: self.has_dart(state) or self.can_dboost(state),
+ "Bamboo Creek - Claustro":
+ lambda state: self.has_wingsuit(state) and (self.has_dart(state) or self.can_dboost(state)),
+ "Above Entrance Mega Shard":
+ lambda state: self.has_dart(state) or self.can_dboost(state),
+ "Bamboo Creek Seal - Spike Ball Pits":
+ self.has_wingsuit,
# howling grotto
- "Howling Grotto Seal - Windy Saws and Balls": self.has_wingsuit,
- "Howling Grotto Seal - Crushing Pits": lambda state: self.has_wingsuit(state) and self.has_dart(state),
- "Howling Grotto - Emerald Golem": self.has_wingsuit,
+ "Howling Grotto Seal - Windy Saws and Balls":
+ self.has_wingsuit,
+ "Howling Grotto Seal - Crushing Pits":
+ lambda state: self.has_wingsuit(state) and self.has_dart(state),
+ "Howling Grotto - Emerald Golem":
+ self.has_wingsuit,
# searing crags
- "Searing Crags Seal - Triple Ball Spinner": self.has_vertical,
"Searing Crags - Astral Tea Leaves":
- lambda state: state.can_reach("Ninja Village - Astral Seed", "Location", self.player),
- "Searing Crags - Key of Strength": lambda state: state.has("Power Thistle", self.player)
- and (self.has_dart(state)
- or (self.has_wingsuit(state)
- and self.can_destroy_projectiles(state))),
+ lambda state: state.multiworld.get_location("Ninja Village - Astral Seed", self.player).can_reach(state),
+ "Searing Crags Seal - Triple Ball Spinner":
+ self.can_dboost,
+ "Searing Crags - Pyro":
+ self.has_tabi,
# glacial peak
- "Glacial Peak Seal - Ice Climbers": self.has_dart,
- "Glacial Peak Seal - Projectile Spike Pit": self.can_destroy_projectiles,
- # cloud ruins
- "Cloud Ruins Seal - Ghost Pit": self.has_dart,
+ "Glacial Peak Seal - Ice Climbers":
+ self.has_dart,
+ "Glacial Peak Seal - Projectile Spike Pit":
+ self.can_destroy_projectiles,
# tower of time
- "Tower of Time Seal - Time Waster": self.has_dart,
- "Tower of Time Seal - Lantern Climb": lambda state: self.has_wingsuit(state) and self.has_dart(state),
- "Tower of Time Seal - Arcane Orbs": lambda state: self.has_wingsuit(state) and self.has_dart(state),
+ "Tower of Time Seal - Time Waster":
+ self.has_dart,
+ # cloud ruins
+ "Time Warp Mega Shard":
+ lambda state: self.has_vertical(state) or self.can_dboost(state),
+ "Cloud Ruins Seal - Ghost Pit":
+ self.has_vertical,
+ "Cloud Ruins Seal - Toothbrush Alley":
+ self.has_dart,
+ "Cloud Ruins Seal - Saw Pit":
+ self.has_vertical,
# underworld
- "Underworld Seal - Sharp and Windy Climb": self.has_wingsuit,
- "Underworld Seal - Fireball Wave": self.is_aerobatic,
- "Underworld Seal - Rising Fanta": self.has_dart,
+ "Underworld Seal - Sharp and Windy Climb":
+ self.has_wingsuit,
+ "Underworld Seal - Fireball Wave":
+ self.is_aerobatic,
+ "Underworld Seal - Rising Fanta":
+ self.has_dart,
+ "Hot Tub Mega Shard":
+ lambda state: self.has_tabi(state) or self.has_dart(state),
# sunken shrine
- "Sunken Shrine - Sun Crest": self.has_tabi,
- "Sunken Shrine - Moon Crest": self.has_tabi,
- "Sunken Shrine - Key of Love": lambda state: state.has_all({"Sun Crest", "Moon Crest"}, self.player),
- "Sunken Shrine Seal - Waterfall Paradise": self.has_tabi,
- "Sunken Shrine Seal - Tabi Gauntlet": self.has_tabi,
- "Mega Shard of the Moon": self.has_tabi,
- "Mega Shard of the Sun": self.has_tabi,
+ "Sunken Shrine - Key of Love":
+ lambda state: state.has_all({"Sun Crest", "Moon Crest"}, self.player),
+ "Sunken Shrine Seal - Waterfall Paradise":
+ self.has_tabi,
+ "Sunken Shrine Seal - Tabi Gauntlet":
+ self.has_tabi,
+ "Mega Shard of the Sun":
+ self.has_tabi,
# riviere turquoise
- "Riviere Turquoise Seal - Bounces and Balls": self.can_dboost,
- "Riviere Turquoise Seal - Launch of Faith": lambda state: self.can_dboost(state) or self.has_dart(state),
+ "Riviere Turquoise Seal - Bounces and Balls":
+ self.can_dboost,
+ "Riviere Turquoise Seal - Launch of Faith":
+ lambda state: self.has_vertical(state),
# elemental skylands
- "Elemental Skylands - Key of Symbiosis": self.has_dart,
- "Elemental Skylands Seal - Air": self.has_wingsuit,
- "Elemental Skylands Seal - Water": lambda state: self.has_dart(state) and
- state.has("Currents Master", self.player),
- "Elemental Skylands Seal - Fire": lambda state: self.has_dart(state) and self.can_destroy_projectiles(state),
- "Earth Mega Shard": self.has_dart,
- "Water Mega Shard": self.has_dart,
- # corrupted future
- "Corrupted Future - Key of Courage": lambda state: state.has_all({"Demon King Crown", "Magic Firefly"},
- self.player),
- # tower hq
- "Money Wrench": self.can_shop,
+ "Elemental Skylands - Key of Symbiosis":
+ self.has_dart,
+ "Elemental Skylands Seal - Air":
+ self.has_wingsuit,
+ "Elemental Skylands Seal - Water":
+ lambda state: self.has_dart(state) and state.has("Currents Master", self.player),
+ "Elemental Skylands Seal - Fire":
+ lambda state: self.has_dart(state) and self.can_destroy_projectiles(state) and self.is_aerobatic(state),
+ "Earth Mega Shard":
+ self.has_dart,
+ "Water Mega Shard":
+ self.has_dart,
}
def has_wingsuit(self, state: CollectionState) -> bool:
@@ -128,6 +332,9 @@ def can_dboost(self, state: CollectionState) -> bool:
return state.has_any({"Path of Resilience", "Meditation"}, self.player) and \
state.has("Second Wind", self.player)
+ def can_double_dboost(self, state: CollectionState) -> bool:
+ return state.has_all({"Path of Resilience", "Meditation", "Second Wind"}, self.player)
+
def is_aerobatic(self, state: CollectionState) -> bool:
return self.has_wingsuit(state) and state.has("Aerobatics Warrior", self.player)
@@ -135,87 +342,147 @@ def true(self, state: CollectionState) -> bool:
"""I know this is stupid, but it's easier to read in the dicts."""
return True
+ def false(self, state: CollectionState) -> bool:
+ """It's a bit easier to just always create the connections that are only possible in hard or higher logic."""
+ return False
+
def can_shop(self, state: CollectionState) -> bool:
return state.has("Shards", self.player, self.maximum_price)
def set_messenger_rules(self) -> None:
multiworld = self.world.multiworld
- for region in multiworld.get_regions(self.player):
- if region.name in self.region_rules:
- for entrance in region.entrances:
- entrance.access_rule = self.region_rules[region.name]
- for loc in region.locations:
- if loc.name in self.location_rules:
- loc.access_rule = self.location_rules[loc.name]
-
- multiworld.completion_condition[self.player] = lambda state: state.has("Rescue Phantom", self.player)
- if multiworld.accessibility[self.player]: # not locations accessibility
+ for entrance_name, rule in self.connection_rules.items():
+ entrance = multiworld.get_entrance(entrance_name, self.player)
+ entrance.access_rule = rule
+ for loc in multiworld.get_locations(self.player):
+ if loc.name in self.location_rules:
+ loc.access_rule = self.location_rules[loc.name]
+
+ if self.world.options.music_box and not self.world.options.limited_movement:
+ add_rule(multiworld.get_entrance("Shrink Down", self.player), self.has_dart)
+ multiworld.completion_condition[self.player] = lambda state: state.has("Do the Thing!", self.player)
+ if self.world.options.accessibility: # not locations accessibility
set_self_locking_items(self.world, self.player)
class MessengerHardRules(MessengerRules):
- extra_rules: Dict[str, CollectionRule]
-
def __init__(self, world: "MessengerWorld") -> None:
super().__init__(world)
- self.region_rules.update({
- "Ninja Village": self.has_vertical,
- "Autumn Hills": self.has_vertical,
- "Catacombs": self.has_vertical,
- "Bamboo Creek": self.has_vertical,
- "Riviere Turquoise": self.true,
- "Forlorn Temple": lambda state: self.has_vertical(state) and state.has_all(PHOBEKINS, self.player),
- "Searing Crags Upper": lambda state: self.can_destroy_projectiles(state) or self.has_windmill(state)
- or self.has_vertical(state),
- "Glacial Peak": lambda state: self.can_destroy_projectiles(state) or self.has_windmill(state)
- or self.has_vertical(state),
- "Elemental Skylands": lambda state: state.has("Magic Firefly", self.player) or
- self.has_windmill(state) or
- self.has_dart(state),
- })
-
- self.location_rules.update({
- "Howling Grotto Seal - Windy Saws and Balls": self.true,
- "Searing Crags Seal - Triple Ball Spinner": self.true,
- "Searing Crags Seal - Raining Rocks": lambda state: self.has_vertical(state) or self.can_destroy_projectiles(state),
- "Searing Crags Seal - Rhythm Rocks": lambda state: self.has_vertical(state) or self.can_destroy_projectiles(state),
- "Searing Crags - Power Thistle": lambda state: self.has_vertical(state) or self.can_destroy_projectiles(state),
- "Glacial Peak Seal - Ice Climbers": lambda state: self.has_vertical(state) or self.can_dboost(state),
- "Glacial Peak Seal - Projectile Spike Pit": self.true,
- "Glacial Peak Seal - Glacial Air Swag": lambda state: self.has_windmill(state) or self.has_vertical(state),
- "Glacial Peak Mega Shard": lambda state: self.has_windmill(state) or self.has_vertical(state),
- "Cloud Ruins Seal - Ghost Pit": self.true,
- "Bamboo Creek - Claustro": self.has_wingsuit,
- "Tower of Time Seal - Lantern Climb": self.has_wingsuit,
- "Elemental Skylands Seal - Water": lambda state: self.has_dart(state) or self.can_dboost(state)
- or self.has_windmill(state),
- "Elemental Skylands Seal - Fire": lambda state: (self.has_dart(state) or self.can_dboost(state)
- or self.has_windmill(state)) and
- self.can_destroy_projectiles(state),
- "Earth Mega Shard": lambda state: self.has_dart(state) or self.can_dboost(state) or self.has_windmill(state),
- "Water Mega Shard": lambda state: self.has_dart(state) or self.can_dboost(state) or self.has_windmill(state),
- })
-
- self.extra_rules = {
- "Searing Crags - Key of Strength": lambda state: self.has_dart(state) or self.has_windmill(state),
- "Elemental Skylands - Key of Symbiosis": lambda state: self.has_windmill(state) or self.can_dboost(state),
- "Autumn Hills Seal - Spike Ball Darts": lambda state: self.has_dart(state) or self.has_windmill(state),
- "Underworld Seal - Fireball Wave": self.has_windmill,
- }
+ self.connection_rules.update(
+ {
+ # Autumn Hills
+ "Autumn Hills - Portal -> Autumn Hills - Dimension Climb Shop":
+ self.has_dart,
+ "Autumn Hills - Climbing Claws Shop -> Autumn Hills - Key of Hope Checkpoint":
+ self.true, # super easy normal clip - also possible with moderately difficult cloud stepping
+ # Howling Grotto
+ "Howling Grotto - Portal -> Howling Grotto - Crushing Pits Shop":
+ self.true,
+ "Howling Grotto - Lost Woods Checkpoint -> Howling Grotto - Bottom":
+ self.true, # just memorize the pattern :)
+ "Howling Grotto - Crushing Pits Shop -> Howling Grotto - Portal":
+ self.true,
+ "Howling Grotto - Breezy Crushers Checkpoint -> Howling Grotto - Emerald Golem Shop":
+ lambda state: self.has_wingsuit(state) or # there's a very easy normal clip here but it's 16-bit only
+ "Howling Grotto - Breezy Crushers Checkpoint" in self.world.spoiler_portal_mapping.values(),
+ # Searing Crags
+ "Searing Crags - Rope Dart Shop -> Searing Crags - Triple Ball Spinner Checkpoint":
+ lambda state: self.has_vertical(state) or self.can_destroy_projectiles(state),
+ # it's doable without anything but one jump is pretty hard and time warping is no longer reliable
+ "Searing Crags - Falling Rocks Shop -> Searing Crags - Searing Mega Shard Shop":
+ lambda state: self.has_vertical(state) or self.can_destroy_projectiles(state),
+ "Searing Crags - Searing Mega Shard Shop -> Searing Crags - Falling Rocks Shop":
+ lambda state: self.has_dart(state) or
+ (self.can_destroy_projectiles(state) and
+ (self.has_wingsuit(state) or self.can_dboost(state))),
+ "Searing Crags - Searing Mega Shard Shop -> Searing Crags - Key of Strength Shop":
+ lambda state: self.can_leash(state) or self.has_windmill(state),
+ "Searing Crags - Before Final Climb Shop -> Searing Crags - Colossuses Shop":
+ self.true,
+ # Glacial Peak
+ "Glacial Peak - Left -> Elemental Skylands - Air Shmup":
+ lambda state: self.has_windmill(state) or
+ (state.has("Magic Firefly", self.player) and
+ state.multiworld.get_location(
+ "Quillshroom Marsh - Queen of Quills", self.player).can_reach(state)) or
+ (self.has_dart(state) and self.can_dboost(state)),
+ "Glacial Peak - Projectile Spike Pit Checkpoint -> Glacial Peak - Left":
+ lambda state: self.has_vertical(state) or self.has_windmill(state),
+ # Cloud Ruins
+ "Cloud Ruins - Sliding Spikes Shop -> Cloud Ruins - Saw Pit Checkpoint":
+ self.true,
+ # Elemental Skylands
+ "Elemental Skylands - Air Intro Shop -> Elemental Skylands - Air Generator Shop":
+ self.true,
+ # Riviere Turquoise
+ "Riviere Turquoise - Waterfall Shop -> Riviere Turquoise - Flower Flight Checkpoint":
+ self.true,
+ "Riviere Turquoise - Launch of Faith Shop -> Riviere Turquoise - Flower Flight Checkpoint":
+ self.can_dboost,
+ "Riviere Turquoise - Flower Flight Checkpoint -> Riviere Turquoise - Waterfall Shop":
+ self.can_double_dboost,
+ }
+ )
+
+ self.location_rules.update(
+ {
+ "Autumn Hills Seal - Spike Ball Darts":
+ lambda state: self.has_vertical(state) and self.has_windmill(state) or self.is_aerobatic(state),
+ "Bamboo Creek - Claustro":
+ self.has_wingsuit,
+ "Bamboo Creek Seal - Spike Ball Pits":
+ self.true,
+ "Howling Grotto Seal - Windy Saws and Balls":
+ self.true,
+ "Searing Crags Seal - Triple Ball Spinner":
+ self.true,
+ "Glacial Peak Seal - Ice Climbers":
+ lambda state: self.has_vertical(state) or self.can_dboost(state),
+ "Glacial Peak Seal - Projectile Spike Pit":
+ lambda state: self.can_dboost(state) or self.can_destroy_projectiles(state),
+ "Glacial Peak Seal - Glacial Air Swag":
+ lambda state: self.has_windmill(state) or self.has_vertical(state),
+ "Glacial Peak Mega Shard":
+ lambda state: self.has_windmill(state) or self.has_vertical(state),
+ "Cloud Ruins Seal - Ghost Pit":
+ self.true,
+ "Cloud Ruins Seal - Toothbrush Alley":
+ self.true,
+ "Cloud Ruins Seal - Saw Pit":
+ self.true,
+ "Underworld Seal - Fireball Wave":
+ lambda state: self.is_aerobatic(state) or self.has_windmill(state),
+ "Riviere Turquoise Seal - Bounces and Balls":
+ self.true,
+ "Riviere Turquoise Seal - Launch of Faith":
+ lambda state: self.can_dboost(state) or self.has_vertical(state),
+ "Elemental Skylands - Key of Symbiosis":
+ lambda state: self.has_dart(state) or self.can_dboost(state) or self.has_windmill(state),
+ "Elemental Skylands Seal - Water":
+ lambda state: self.has_dart(state) or self.can_dboost(state) or self.has_windmill(state),
+ "Elemental Skylands Seal - Fire":
+ lambda state: (self.has_dart(state) or self.can_dboost(state) or self.has_windmill(state))
+ and self.can_destroy_projectiles(state),
+ "Earth Mega Shard":
+ lambda state: self.has_dart(state) or self.can_dboost(state) or self.has_windmill(state),
+ "Water Mega Shard":
+ lambda state: self.has_dart(state) or self.can_dboost(state) or self.has_windmill(state),
+ }
+ )
def has_windmill(self, state: CollectionState) -> bool:
return state.has("Windmill Shuriken", self.player)
- def set_messenger_rules(self) -> None:
- super().set_messenger_rules()
- for loc, rule in self.extra_rules.items():
- if not self.world.options.shuffle_seals and "Seal" in loc:
- continue
- if not self.world.options.shuffle_shards and "Shard" in loc:
- continue
- add_rule(self.world.multiworld.get_location(loc, self.player), rule, "or")
+ def can_dboost(self, state: CollectionState) -> bool:
+ return state.has("Second Wind", self.player) # who really needs meditation
+
+ def can_destroy_projectiles(self, state: CollectionState) -> bool:
+ return super().can_destroy_projectiles(state) or self.has_windmill(state)
+
+ def can_leash(self, state: CollectionState) -> bool:
+ return self.has_dart(state) and self.can_dboost(state)
class MessengerOOBRules(MessengerRules):
@@ -226,7 +493,9 @@ def __init__(self, world: "MessengerWorld") -> None:
self.required_seals = max(1, world.required_seals)
self.region_rules = {
"Elemental Skylands":
- lambda state: state.has_any({"Windmill Shuriken", "Wingsuit", "Rope Dart", "Magic Firefly"}, self.player),
+ lambda state: state.has_any(
+ {"Windmill Shuriken", "Wingsuit", "Rope Dart", "Magic Firefly"}, self.player
+ ),
"Music Box": lambda state: state.has_all(set(NOTES), self.player) or self.has_enough_seals(state),
}
@@ -240,8 +509,10 @@ def __init__(self, world: "MessengerWorld") -> None:
lambda state: state.has_all({"Demon King Crown", "Magic Firefly"}, self.player),
"Autumn Hills Seal - Spike Ball Darts": self.has_dart,
"Ninja Village Seal - Tree House": self.has_dart,
- "Underworld Seal - Fireball Wave": lambda state: state.has_any({"Wingsuit", "Windmill Shuriken"},
- self.player),
+ "Underworld Seal - Fireball Wave": lambda state: state.has_any(
+ {"Wingsuit", "Windmill Shuriken"},
+ self.player
+ ),
"Tower of Time Seal - Time Waster": self.has_dart,
}
@@ -251,18 +522,8 @@ def set_messenger_rules(self) -> None:
def set_self_locking_items(world: "MessengerWorld", player: int) -> None:
- multiworld = world.multiworld
-
- # do the ones for seal shuffle on and off first
- allow_self_locking_items(multiworld.get_location("Searing Crags - Key of Strength", player), "Power Thistle")
- allow_self_locking_items(multiworld.get_location("Sunken Shrine - Key of Love", player), "Sun Crest", "Moon Crest")
- allow_self_locking_items(multiworld.get_location("Corrupted Future - Key of Courage", player), "Demon King Crown")
-
- # add these locations when seals are shuffled
- if world.options.shuffle_seals:
- allow_self_locking_items(multiworld.get_location("Elemental Skylands Seal - Water", player), "Currents Master")
- # add these locations when seals and shards aren't shuffled
- elif not world.options.shuffle_shards:
- for entrance in multiworld.get_region("Cloud Ruins", player).entrances:
- entrance.access_rule = lambda state: state.has("Wingsuit", player) or state.has("Rope Dart", player)
- allow_self_locking_items(multiworld.get_region("Forlorn Temple", player), *PHOBEKINS)
+ # locations where these placements are always valid
+ allow_self_locking_items(world.get_location("Searing Crags - Key of Strength").parent_region, "Power Thistle")
+ allow_self_locking_items(world.get_location("Sunken Shrine - Key of Love"), "Sun Crest", "Moon Crest")
+ allow_self_locking_items(world.get_location("Corrupted Future - Key of Courage").parent_region, "Demon King Crown")
+ allow_self_locking_items(world.get_location("Elemental Skylands Seal - Water"), "Currents Master")
diff --git a/worlds/messenger/subclasses.py b/worlds/messenger/subclasses.py
index b6a0b80b21a6..b60aeb179feb 100644
--- a/worlds/messenger/subclasses.py
+++ b/worlds/messenger/subclasses.py
@@ -1,36 +1,48 @@
from functools import cached_property
-from typing import Optional, TYPE_CHECKING, cast
+from typing import Optional, TYPE_CHECKING
-from BaseClasses import CollectionState, Item, ItemClassification, Location, Region
-from .constants import NOTES, PHOBEKINS, PROG_ITEMS, USEFUL_ITEMS
-from .regions import MEGA_SHARDS, REGIONS, SEALS
-from .shop import FIGURINES, PROG_SHOP_ITEMS, SHOP_ITEMS, USEFUL_SHOP_ITEMS
+from BaseClasses import CollectionState, Entrance, Item, ItemClassification, Location, Region
+from .regions import LOCATIONS, MEGA_SHARDS
+from .shop import FIGURINES, SHOP_ITEMS
if TYPE_CHECKING:
from . import MessengerWorld
+class MessengerEntrance(Entrance):
+ world: Optional["MessengerWorld"] = None
+
+
class MessengerRegion(Region):
-
- def __init__(self, name: str, world: "MessengerWorld") -> None:
+ parent: str
+ entrance_type = MessengerEntrance
+
+ def __init__(self, name: str, world: "MessengerWorld", parent: Optional[str] = None) -> None:
super().__init__(name, world.player, world.multiworld)
- locations = [loc for loc in REGIONS[self.name]]
- if self.name == "The Shop":
+ self.parent = parent
+ locations = []
+ if name in LOCATIONS:
+ locations = [loc for loc in LOCATIONS[name]]
+ # portal event locations since portals can be opened from their exit regions
+ if name.endswith("Portal"):
+ locations.append(name.replace(" -", ""))
+
+ if name == "The Shop":
shop_locations = {f"The Shop - {shop_loc}": world.location_name_to_id[f"The Shop - {shop_loc}"]
for shop_loc in SHOP_ITEMS}
self.add_locations(shop_locations, MessengerShopLocation)
- elif self.name == "The Craftsman's Corner":
+ elif name == "The Craftsman's Corner":
self.add_locations({figurine: world.location_name_to_id[figurine] for figurine in FIGURINES},
MessengerLocation)
- elif self.name == "Tower HQ":
+ elif name == "Tower HQ":
locations.append("Money Wrench")
- if world.options.shuffle_seals and self.name in SEALS:
- locations += [seal_loc for seal_loc in SEALS[self.name]]
- if world.options.shuffle_shards and self.name in MEGA_SHARDS:
- locations += [shard for shard in MEGA_SHARDS[self.name]]
+
+ if world.options.shuffle_shards and name in MEGA_SHARDS:
+ locations += MEGA_SHARDS[name]
loc_dict = {loc: world.location_name_to_id.get(loc, None) for loc in locations}
self.add_locations(loc_dict, MessengerLocation)
- world.multiworld.regions.append(self)
+
+ self.multiworld.regions.append(self)
class MessengerLocation(Location):
@@ -39,46 +51,36 @@ class MessengerLocation(Location):
def __init__(self, player: int, name: str, loc_id: Optional[int], parent: MessengerRegion) -> None:
super().__init__(player, name, loc_id, parent)
if loc_id is None:
- self.place_locked_item(MessengerItem(name, parent.player, None))
+ if name == "Rescue Phantom":
+ name = "Do the Thing!"
+ self.place_locked_item(MessengerItem(name, ItemClassification.progression, None, parent.player))
class MessengerShopLocation(MessengerLocation):
@cached_property
def cost(self) -> int:
name = self.name.replace("The Shop - ", "") # TODO use `remove_prefix` when 3.8 finally gets dropped
- world = cast("MessengerWorld", self.parent_region.multiworld.worlds[self.player])
+ world = self.parent_region.multiworld.worlds[self.player]
shop_data = SHOP_ITEMS[name]
if shop_data.prerequisite:
prereq_cost = 0
if isinstance(shop_data.prerequisite, set):
for prereq in shop_data.prerequisite:
- prereq_cost +=\
- cast(MessengerShopLocation,
- world.multiworld.get_location(prereq, self.player)).cost
+ loc = world.multiworld.get_location(prereq, self.player)
+ assert isinstance(loc, MessengerShopLocation)
+ prereq_cost += loc.cost
else:
- prereq_cost +=\
- cast(MessengerShopLocation,
- world.multiworld.get_location(shop_data.prerequisite, self.player)).cost
+ loc = world.multiworld.get_location(shop_data.prerequisite, self.player)
+ assert isinstance(loc, MessengerShopLocation)
+ prereq_cost += loc.cost
return world.shop_prices[name] + prereq_cost
return world.shop_prices[name]
def access_rule(self, state: CollectionState) -> bool:
- world = cast("MessengerWorld", state.multiworld.worlds[self.player])
+ world = state.multiworld.worlds[self.player]
can_afford = state.has("Shards", self.player, min(self.cost, world.total_shards))
return can_afford
class MessengerItem(Item):
game = "The Messenger"
-
- def __init__(self, name: str, player: int, item_id: Optional[int] = None, override_progression: bool = False,
- count: int = 0) -> None:
- if count:
- item_class = ItemClassification.progression_skip_balancing
- elif item_id is None or override_progression or name in {*NOTES, *PROG_ITEMS, *PHOBEKINS, *PROG_SHOP_ITEMS}:
- item_class = ItemClassification.progression
- elif name in {*USEFUL_ITEMS, *USEFUL_SHOP_ITEMS}:
- item_class = ItemClassification.useful
- else:
- item_class = ItemClassification.filler
- super().__init__(name, item_class, item_id, player)
diff --git a/worlds/messenger/test/__init__.py b/worlds/messenger/test/__init__.py
index 7ab1e11781da..83bb248d6483 100644
--- a/worlds/messenger/test/__init__.py
+++ b/worlds/messenger/test/__init__.py
@@ -1,6 +1,7 @@
-from test.TestBase import WorldTestBase
+from test.bases import WorldTestBase
+from .. import MessengerWorld
class MessengerTestBase(WorldTestBase):
game = "The Messenger"
- player: int = 1
+ world: MessengerWorld
diff --git a/worlds/messenger/test/test_access.py b/worlds/messenger/test/test_access.py
index 7a77a9b06695..ad2265ffa0a6 100644
--- a/worlds/messenger/test/test_access.py
+++ b/worlds/messenger/test/test_access.py
@@ -1,3 +1,5 @@
+import typing
+
from . import MessengerTestBase
from ..constants import NOTES, PHOBEKINS
@@ -22,11 +24,27 @@ def test_tabi(self) -> None:
def test_dart(self) -> None:
"""locations that hard require the Rope Dart"""
locations = [
- "Ninja Village Seal - Tree House", "Autumn Hills - Key of Hope", "Howling Grotto Seal - Crushing Pits",
- "Glacial Peak Seal - Ice Climbers", "Tower of Time Seal - Time Waster", "Tower of Time Seal - Lantern Climb",
- "Tower of Time Seal - Arcane Orbs", "Cloud Ruins Seal - Ghost Pit", "Underworld Seal - Rising Fanta",
- "Elemental Skylands - Key of Symbiosis", "Elemental Skylands Seal - Water",
- "Elemental Skylands Seal - Fire", "Earth Mega Shard", "Water Mega Shard", "Rescue Phantom",
+ "Ninja Village Seal - Tree House",
+ "Autumn Hills - Key of Hope",
+ "Forlorn Temple - Demon King",
+ "Down Under Mega Shard",
+ "Howling Grotto Seal - Crushing Pits",
+ "Glacial Peak Seal - Ice Climbers",
+ "Tower of Time Seal - Time Waster",
+ "Tower of Time Seal - Lantern Climb",
+ "Tower of Time Seal - Arcane Orbs",
+ "Cloud Ruins Seal - Ghost Pit",
+ "Cloud Ruins Seal - Money Farm Room",
+ "Cloud Ruins Seal - Toothbrush Alley",
+ "Money Farm Room Mega Shard 1",
+ "Money Farm Room Mega Shard 2",
+ "Underworld Seal - Rising Fanta",
+ "Elemental Skylands - Key of Symbiosis",
+ "Elemental Skylands Seal - Water",
+ "Elemental Skylands Seal - Fire",
+ "Earth Mega Shard",
+ "Water Mega Shard",
+ "Rescue Phantom",
]
items = [["Rope Dart"]]
self.assertAccessDependency(locations, items)
@@ -136,11 +154,37 @@ def test_crown(self) -> None:
items = [["Demon King Crown"]]
self.assertAccessDependency(locations, items)
+ def test_dboost(self) -> None:
+ """
+ short for damage boosting, d-boosting is a technique in video games where the player intentionally or
+ unintentionally takes damage and uses the several following frames of invincibility to defeat or get past an
+ enemy or obstacle, most commonly used in platformers such as the Super Mario games
+ """
+ locations = [
+ "Riviere Turquoise Seal - Bounces and Balls", "Searing Crags Seal - Triple Ball Spinner",
+ "Forlorn Temple - Demon King", "Forlorn Temple Seal - Rocket Maze", "Forlorn Temple Seal - Rocket Sunset",
+ "Sunny Day Mega Shard", "Down Under Mega Shard",
+ ]
+ items = [["Path of Resilience", "Meditation", "Second Wind"]]
+ self.assertAccessDependency(locations, items)
+
+ def test_currents(self) -> None:
+ """there's one of these but oh man look at it go"""
+ self.assertAccessDependency(["Elemental Skylands Seal - Water"], [["Currents Master"]])
+
+ def test_strike(self) -> None:
+ """strike is pretty cool but it doesn't block much"""
+ locations = [
+ "Glacial Peak Seal - Projectile Spike Pit", "Elemental Skylands Seal - Fire",
+ ]
+ items = [["Strike of the Ninja"]]
+ self.assertAccessDependency(locations, items)
+
def test_goal(self) -> None:
"""Test some different states to verify goal requires the correct items"""
- self.collect_all_but([*NOTES, "Rescue Phantom"])
+ self.collect_all_but([*NOTES, "Do the Thing!"])
self.assertEqual(self.can_reach_location("Rescue Phantom"), False)
- self.collect_all_but(["Key of Love", "Rescue Phantom"])
+ self.collect_all_but(["Key of Love", "Do the Thing!"])
self.assertBeatable(False)
self.collect_by_name(["Key of Love"])
self.assertEqual(self.can_reach_location("Rescue Phantom"), True)
@@ -159,14 +203,15 @@ def test_self_locking_items(self) -> None:
"Searing Crags - Key of Strength": ["Power Thistle"],
"Sunken Shrine - Key of Love": ["Sun Crest", "Moon Crest"],
"Corrupted Future - Key of Courage": ["Demon King Crown"],
- "Cloud Ruins - Acro": ["Ruxxtin's Amulet"],
- "Forlorn Temple - Demon King": PHOBEKINS
}
- self.multiworld.state = self.multiworld.get_all_state(True)
- self.remove_by_name(location_lock_pairs.values())
+ self.collect_all_but([item for items in location_lock_pairs.values() for item in items])
for loc in location_lock_pairs:
for item_name in location_lock_pairs[loc]:
item = self.get_item_by_name(item_name)
with self.subTest("Fulfills Accessibility", location=loc, item=item_name):
- self.assertTrue(self.multiworld.get_location(loc, self.player).can_fill(self.multiworld.state, item, True))
+ location = self.multiworld.get_location(loc, self.player)
+ self.assertTrue(location.can_fill(self.multiworld.state, item, True))
+ location.item = item
+ self.multiworld.state.update_reachable_regions(self.player)
+ self.assertTrue(self.can_reach_location(loc))
diff --git a/worlds/messenger/test/test_locations.py b/worlds/messenger/test/test_locations.py
index 0c330be4bd3a..627d58c29061 100644
--- a/worlds/messenger/test/test_locations.py
+++ b/worlds/messenger/test/test_locations.py
@@ -12,5 +12,5 @@ def run_default_tests(self) -> bool:
return False
def test_locations_exist(self) -> None:
- for location in self.multiworld.worlds[1].location_name_to_id:
+ for location in self.world.location_name_to_id:
self.assertIsInstance(self.multiworld.get_location(location, self.player), MessengerLocation)
diff --git a/worlds/messenger/test/test_logic.py b/worlds/messenger/test/test_logic.py
index 15df89b92097..c13bd5c5a008 100644
--- a/worlds/messenger/test/test_logic.py
+++ b/worlds/messenger/test/test_logic.py
@@ -41,7 +41,7 @@ def test_vertical(self) -> None:
# cloud ruins
"Cloud Ruins - Acro", "Cloud Ruins Seal - Ghost Pit",
"Cloud Ruins Seal - Toothbrush Alley", "Cloud Ruins Seal - Saw Pit", "Cloud Ruins Seal - Money Farm Room",
- "Cloud Entrance Mega Shard", "Time Warp Mega Shard", "Money Farm Room Mega Shard 1", "Money Farm Room Mega Shard 2",
+ "Money Farm Room Mega Shard 1", "Money Farm Room Mega Shard 2",
# underworld
"Underworld Seal - Rising Fanta", "Underworld Seal - Sharp and Windy Climb",
# elemental skylands
@@ -80,18 +80,6 @@ def test_windmill(self) -> None:
self.collect(item)
self.assertTrue(self.can_reach_location(special_loc))
- def test_glacial(self) -> None:
- """Test Glacial Peak locations."""
- self.assertAccessDependency(["Glacial Peak Seal - Ice Climbers"],
- [["Second Wind", "Meditation"], ["Rope Dart"], ["Wingsuit"]],
- True)
- self.assertAccessDependency(["Glacial Peak Seal - Projectile Spike Pit"],
- [["Strike of the Ninja"], ["Windmill Shuriken"], ["Rope Dart"], ["Wingsuit"]],
- True)
- self.assertAccessDependency(["Glacial Peak Seal - Glacial Air Swag", "Glacial Peak Mega Shard"],
- [["Windmill Shuriken"], ["Wingsuit"], ["Rope Dart"]],
- True)
-
class NoLogicTest(MessengerTestBase):
options = {
diff --git a/worlds/messenger/test/test_notes.py b/worlds/messenger/test/test_notes.py
index 46cec5f3c819..fdb1cef1dfbe 100644
--- a/worlds/messenger/test/test_notes.py
+++ b/worlds/messenger/test/test_notes.py
@@ -2,29 +2,19 @@
from ..constants import NOTES
-class TwoNoteGoalTest(MessengerTestBase):
- options = {
- "notes_needed": 2,
- }
-
- def test_precollected_notes(self) -> None:
- self.assertEqual(self.multiworld.state.count_group("Notes", self.player), 4)
-
-
-class FourNoteGoalTest(MessengerTestBase):
- options = {
- "notes_needed": 4,
- }
-
- def test_precollected_notes(self) -> None:
- self.assertEqual(self.multiworld.state.count_group("Notes", self.player), 2)
+class PrecollectedNotesTestBase(MessengerTestBase):
+ starting_notes: int = 0
+ @property
+ def run_default_tests(self) -> bool:
+ return False
-class DefaultGoalTest(MessengerTestBase):
def test_precollected_notes(self) -> None:
- self.assertEqual(self.multiworld.state.count_group("Notes", self.player), 0)
+ self.assertEqual(self.multiworld.state.count_group("Notes", self.player), self.starting_notes)
def test_goal(self) -> None:
+ if self.__class__ is not PrecollectedNotesTestBase:
+ return
self.assertBeatable(False)
self.collect_by_name(NOTES)
rope_dart = self.get_item_by_name("Rope Dart")
@@ -33,3 +23,17 @@ def test_goal(self) -> None:
self.remove(rope_dart)
self.collect_by_name("Wingsuit")
self.assertBeatable(True)
+
+
+class TwoNoteGoalTest(PrecollectedNotesTestBase):
+ options = {
+ "notes_needed": 2,
+ }
+ starting_notes = 4
+
+
+class FourNoteGoalTest(PrecollectedNotesTestBase):
+ options = {
+ "notes_needed": 4,
+ }
+ starting_notes = 2
diff --git a/worlds/messenger/test/test_options.py b/worlds/messenger/test/test_options.py
new file mode 100644
index 000000000000..ea84af80388f
--- /dev/null
+++ b/worlds/messenger/test/test_options.py
@@ -0,0 +1,35 @@
+from BaseClasses import CollectionState
+from Fill import distribute_items_restrictive
+from . import MessengerTestBase
+from .. import MessengerWorld
+from ..options import Logic
+
+
+class LimitedMovementTest(MessengerTestBase):
+ options = {
+ "limited_movement": "true",
+ "shuffle_shards": "true",
+ }
+
+ @property
+ def run_default_tests(self) -> bool:
+ # This test base fails reachability tests. Not sure if the core tests should change to support that
+ return False
+
+ def test_options(self) -> None:
+ """Tests that options were correctly changed."""
+ assert isinstance(self.multiworld.worlds[self.player], MessengerWorld)
+ self.assertEqual(Logic.option_hard, self.world.options.logic_level)
+
+
+class EarlyMeditationTest(MessengerTestBase):
+ options = {
+ "early_meditation": "true",
+ }
+
+ def test_option(self) -> None:
+ """Checks that Meditation gets placed early"""
+ distribute_items_restrictive(self.multiworld)
+ sphere1 = self.multiworld.get_reachable_locations(CollectionState(self.multiworld))
+ items = [loc.item.name for loc in sphere1]
+ self.assertIn("Meditation", items)
diff --git a/worlds/messenger/test/test_portals.py b/worlds/messenger/test/test_portals.py
new file mode 100644
index 000000000000..6ebb18381331
--- /dev/null
+++ b/worlds/messenger/test/test_portals.py
@@ -0,0 +1,33 @@
+from BaseClasses import CollectionState
+from . import MessengerTestBase
+from ..portals import PORTALS
+
+
+class PortalTestBase(MessengerTestBase):
+ def test_portal_reqs(self) -> None:
+ """tests the paths to open a portal if only that portal is closed with vanilla connections."""
+ # portal and requirements to reach it if it's the only closed portal
+ portal_requirements = {
+ "Autumn Hills Portal": [["Wingsuit"]], # grotto -> bamboo -> catacombs -> hills
+ "Riviere Turquoise Portal": [["Candle", "Wingsuit", "Rope Dart"]], # hills -> catacombs -> dark cave -> riviere
+ "Howling Grotto Portal": [["Wingsuit"], ["Meditation", "Second Wind"]], # crags -> quillshroom -> grotto
+ "Sunken Shrine Portal": [["Seashell"]], # crags -> quillshroom -> grotto -> shrine
+ "Searing Crags Portal": [["Wingsuit"], ["Rope Dart"]], # grotto -> quillshroom -> crags there's two separate paths
+ "Glacial Peak Portal": [["Wingsuit", "Second Wind", "Meditation"], ["Rope Dart"]], # grotto -> quillshroom -> crags -> peak or crags -> peak
+ }
+
+ for portal in PORTALS:
+ name = f"{portal} Portal"
+ entrance_name = f"ToTHQ {name}"
+ with self.subTest(portal=name, entrance_name=entrance_name):
+ entrance = self.multiworld.get_entrance(entrance_name, self.player)
+ # this emulates the portal being initially closed
+ entrance.access_rule = lambda state: state.has(name, self.player)
+ for grouping in portal_requirements[name]:
+ test_state = CollectionState(self.multiworld)
+ self.assertFalse(entrance.can_reach(test_state), "reachable with nothing")
+ items = self.get_items_by_name(grouping)
+ for item in items:
+ test_state.collect(item)
+ self.assertTrue(entrance.can_reach(test_state), grouping)
+ entrance.access_rule = lambda state: True
diff --git a/worlds/messenger/test/test_shop.py b/worlds/messenger/test/test_shop.py
index afb1b32b88e3..971ff1763b47 100644
--- a/worlds/messenger/test/test_shop.py
+++ b/worlds/messenger/test/test_shop.py
@@ -17,32 +17,13 @@ def test_shop_rules(self) -> None:
self.assertFalse(self.can_reach_location(loc))
def test_shop_prices(self) -> None:
- prices: Dict[str, int] = self.multiworld.worlds[self.player].shop_prices
+ prices: Dict[str, int] = self.world.shop_prices
for loc, price in prices.items():
with self.subTest("prices", loc=loc):
self.assertLessEqual(price, self.multiworld.get_location(f"The Shop - {loc}", self.player).cost)
self.assertTrue(loc in SHOP_ITEMS)
self.assertEqual(len(prices), len(SHOP_ITEMS))
- def test_dboost(self) -> None:
- locations = [
- "Riviere Turquoise Seal - Bounces and Balls",
- "Forlorn Temple - Demon King", "Forlorn Temple Seal - Rocket Maze", "Forlorn Temple Seal - Rocket Sunset",
- "Sunny Day Mega Shard", "Down Under Mega Shard",
- ]
- items = [["Path of Resilience", "Meditation", "Second Wind"]]
- self.assertAccessDependency(locations, items)
-
- def test_currents(self) -> None:
- self.assertAccessDependency(["Elemental Skylands Seal - Water"], [["Currents Master"]])
-
- def test_strike(self) -> None:
- locations = [
- "Glacial Peak Seal - Projectile Spike Pit", "Elemental Skylands Seal - Fire",
- ]
- items = [["Strike of the Ninja"]]
- self.assertAccessDependency(locations, items)
-
class ShopCostMinTest(ShopCostTest):
options = {
@@ -51,7 +32,7 @@ class ShopCostMinTest(ShopCostTest):
}
def test_shop_rules(self) -> None:
- if self.multiworld.worlds[self.player].total_shards:
+ if self.world.total_shards:
super().test_shop_rules()
else:
for loc in SHOP_ITEMS:
@@ -85,7 +66,7 @@ def test_costs(self) -> None:
with self.subTest("has cost", loc=loc):
self.assertFalse(self.can_reach_location(loc))
- prices = self.multiworld.worlds[self.player].shop_prices
+ prices = self.world.shop_prices
for loc, price in prices.items():
with self.subTest("prices", loc=loc):
if loc == "Karuta Plates":
@@ -98,7 +79,7 @@ def test_costs(self) -> None:
self.assertTrue(loc.replace("The Shop - ", "") in SHOP_ITEMS)
self.assertEqual(len(prices), len(SHOP_ITEMS))
- figures = self.multiworld.worlds[self.player].figurine_prices
+ figures = self.world.figurine_prices
for loc, price in figures.items():
with self.subTest("figure prices", loc=loc):
if loc == "Barmath'azel Figurine":
diff --git a/worlds/messenger/test/test_shop_chest.py b/worlds/messenger/test/test_shop_chest.py
index a34fa0fb96c0..2ac306972614 100644
--- a/worlds/messenger/test/test_shop_chest.py
+++ b/worlds/messenger/test/test_shop_chest.py
@@ -4,19 +4,14 @@
class AllSealsRequired(MessengerTestBase):
options = {
- "shuffle_seals": "false",
"goal": "power_seal_hunt",
}
- def test_seals_shuffled(self) -> None:
- """Shuffle seals should be forced on when shop chest is the goal so test it."""
- self.assertTrue(self.multiworld.shuffle_seals[self.player])
-
def test_chest_access(self) -> None:
"""Defaults to a total of 45 power seals in the pool and required."""
with self.subTest("Access Dependency"):
self.assertEqual(len([seal for seal in self.multiworld.itempool if seal.name == "Power Seal"]),
- self.multiworld.total_seals[self.player])
+ self.world.options.total_seals)
locations = ["Rescue Phantom"]
items = [["Power Seal"]]
self.assertAccessDependency(locations, items)
@@ -24,7 +19,7 @@ def test_chest_access(self) -> None:
self.assertEqual(self.can_reach_location("Rescue Phantom"), False)
self.assertBeatable(False)
- self.collect_all_but(["Power Seal", "Rescue Phantom"])
+ self.collect_all_but(["Power Seal", "Do the Thing!"])
self.assertEqual(self.can_reach_location("Rescue Phantom"), False)
self.assertBeatable(False)
self.collect_by_name("Power Seal")
@@ -40,9 +35,9 @@ class HalfSealsRequired(MessengerTestBase):
def test_seals_amount(self) -> None:
"""Should have 45 power seals in the item pool and half that required"""
- self.assertEqual(self.multiworld.total_seals[self.player], 45)
- self.assertEqual(self.multiworld.worlds[self.player].total_seals, 45)
- self.assertEqual(self.multiworld.worlds[self.player].required_seals, 22)
+ self.assertEqual(self.world.options.total_seals, 45)
+ self.assertEqual(self.world.total_seals, 45)
+ self.assertEqual(self.world.required_seals, 22)
total_seals = [seal for seal in self.multiworld.itempool if seal.name == "Power Seal"]
required_seals = [seal for seal in total_seals
if seal.classification == ItemClassification.progression_skip_balancing]
@@ -59,9 +54,9 @@ class ThirtyThirtySeals(MessengerTestBase):
def test_seals_amount(self) -> None:
"""Should have 30 power seals in the pool and 33 percent of that required."""
- self.assertEqual(self.multiworld.total_seals[self.player], 30)
- self.assertEqual(self.multiworld.worlds[self.player].total_seals, 30)
- self.assertEqual(self.multiworld.worlds[self.player].required_seals, 10)
+ self.assertEqual(self.world.options.total_seals, 30)
+ self.assertEqual(self.world.total_seals, 30)
+ self.assertEqual(self.world.required_seals, 10)
total_seals = [seal for seal in self.multiworld.itempool if seal.name == "Power Seal"]
required_seals = [seal for seal in total_seals
if seal.classification == ItemClassification.progression_skip_balancing]
@@ -77,8 +72,8 @@ class MaxSealsNoShards(MessengerTestBase):
def test_seals_amount(self) -> None:
"""Should set total seals to 70 since shards aren't shuffled."""
- self.assertEqual(self.multiworld.total_seals[self.player], 85)
- self.assertEqual(self.multiworld.worlds[self.player].total_seals, 70)
+ self.assertEqual(self.world.options.total_seals, 85)
+ self.assertEqual(self.world.total_seals, 70)
class MaxSealsWithShards(MessengerTestBase):
@@ -90,9 +85,9 @@ class MaxSealsWithShards(MessengerTestBase):
def test_seals_amount(self) -> None:
"""Should have 85 seals in the pool with all required and be a valid seed."""
- self.assertEqual(self.multiworld.total_seals[self.player], 85)
- self.assertEqual(self.multiworld.worlds[self.player].total_seals, 85)
- self.assertEqual(self.multiworld.worlds[self.player].required_seals, 85)
+ self.assertEqual(self.world.options.total_seals, 85)
+ self.assertEqual(self.world.total_seals, 85)
+ self.assertEqual(self.world.required_seals, 85)
total_seals = [seal for seal in self.multiworld.itempool if seal.name == "Power Seal"]
required_seals = [seal for seal in total_seals
if seal.classification == ItemClassification.progression_skip_balancing]
diff --git a/worlds/minecraft/__init__.py b/worlds/minecraft/__init__.py
index 187f1fdf196a..343b9bad19a9 100644
--- a/worlds/minecraft/__init__.py
+++ b/worlds/minecraft/__init__.py
@@ -37,7 +37,7 @@ class MinecraftWebWorld(WebWorld):
bug_report_page = "https://github.com/KonoTyran/Minecraft_AP_Randomizer/issues/new?assignees=&labels=bug&template=bug_report.yaml&title=%5BBug%5D%3A+Brief+Description+of+bug+here"
setup = Tutorial(
- "Multiworld Setup Tutorial",
+ "Multiworld Setup Guide",
"A guide to setting up the Archipelago Minecraft software on your computer. This guide covers"
"single-player, multiworld, and related software.",
"English",
diff --git a/worlds/minecraft/docs/en_Minecraft.md b/worlds/minecraft/docs/en_Minecraft.md
index 1ef347983bc4..3a69a7f59a22 100644
--- a/worlds/minecraft/docs/en_Minecraft.md
+++ b/worlds/minecraft/docs/en_Minecraft.md
@@ -1,8 +1,8 @@
# Minecraft
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
@@ -11,9 +11,9 @@ Some recipes are locked from being able to be crafted and shuffled into the item
structures appear in each dimension. Crafting recipes are re-learned when they are received from other players as item
checks, and occasionally when completing your own achievements. See below for which recipes are shuffled.
-## What is considered a location check in minecraft?
+## What is considered a location check in Minecraft?
-Location checks in are completed when the player completes various Minecraft achievements. Opening the advancements menu
+Location checks are completed when the player completes various Minecraft achievements. Opening the advancements menu
in-game by pressing "L" will display outstanding achievements.
## When the player receives an item, what happens?
@@ -24,7 +24,7 @@ inventory directly.
## What is the victory condition?
Victory is achieved when the player kills the Ender Dragon, enters the portal in The End, and completes the credits
-sequence either by skipping it or watching hit play out.
+sequence either by skipping it or watching it play out.
## Which recipes are locked?
@@ -64,12 +64,15 @@ sequence either by skipping it or watching hit play out.
* Diamond Axe
* Progessive Tools
* Tier I
+ * Stone Pickaxe
* Stone Shovel
* Stone Hoe
* Tier II
+ * Iron Pickaxe
* Iron Shovel
* Iron Hoe
* Tier III
+ * Diamond Pickaxe
* Diamond Shovel
* Diamond Hoe
* Netherite Ingot
diff --git a/worlds/minecraft/docs/minecraft_en.md b/worlds/minecraft/docs/minecraft_en.md
index b71ed930a5d8..e0b5ae3b98b5 100644
--- a/worlds/minecraft/docs/minecraft_en.md
+++ b/worlds/minecraft/docs/minecraft_en.md
@@ -15,7 +15,7 @@ guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
### Where do I get a YAML file?
-You can customize your settings by visiting the [Minecraft Player Settings Page](/games/Minecraft/player-settings)
+You can customize your options by visiting the [Minecraft Player Options Page](/games/Minecraft/player-options)
## Joining a MultiWorld Game
diff --git a/worlds/mmbn3/__init__.py b/worlds/mmbn3/__init__.py
index acf258a730c6..762bfd11ae4a 100644
--- a/worlds/mmbn3/__init__.py
+++ b/worlds/mmbn3/__init__.py
@@ -100,9 +100,7 @@ def create_regions(self) -> None:
for region_info in regions:
region = name_to_region[region_info.name]
for connection in region_info.connections:
- connection_region = name_to_region[connection]
- entrance = Entrance(self.player, connection, region)
- entrance.connect(connection_region)
+ entrance = region.connect(name_to_region[connection])
# ACDC Pending with Start Randomizer
# if connection == RegionName.ACDC_Overworld:
@@ -141,7 +139,6 @@ def create_regions(self) -> None:
if connection == RegionName.WWW_Island:
entrance.access_rule = lambda state:\
state.has(ItemName.Progressive_Undernet_Rank, self.player, 8)
- region.exits.append(entrance)
def create_items(self) -> None:
# First add in all progression and useful items
diff --git a/worlds/mmbn3/docs/en_MegaMan Battle Network 3.md b/worlds/mmbn3/docs/en_MegaMan Battle Network 3.md
index 7ffa4665fd2a..bb9d2c15af2c 100644
--- a/worlds/mmbn3/docs/en_MegaMan Battle Network 3.md
+++ b/worlds/mmbn3/docs/en_MegaMan Battle Network 3.md
@@ -1,8 +1,8 @@
# MegaMan Battle Network 3
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and
+The [player options page for this game](../player-options) contains all the options you need to configure and
export a config file.
## What does randomization do to this game?
diff --git a/worlds/mmbn3/docs/setup_en.md b/worlds/mmbn3/docs/setup_en.md
index e9181ea54881..44a6b9c14448 100644
--- a/worlds/mmbn3/docs/setup_en.md
+++ b/worlds/mmbn3/docs/setup_en.md
@@ -53,8 +53,8 @@ an experience customized for their taste, and different players in the same mult
### Where do I get a YAML file?
-You can customize your settings by visiting the
-[MegaMan Battle Network 3 Player Settings Page](/games/MegaMan%20Battle%20Network%203/player-settings)
+You can customize your options by visiting the
+[MegaMan Battle Network 3 Player Options Page](/games/MegaMan%20Battle%20Network%203/player-options)
## Joining a MultiWorld Game
diff --git a/worlds/musedash/MuseDashCollection.py b/worlds/musedash/MuseDashCollection.py
index 55523542d7df..20bb8decebcc 100644
--- a/worlds/musedash/MuseDashCollection.py
+++ b/worlds/musedash/MuseDashCollection.py
@@ -26,7 +26,8 @@ class MuseDashCollections:
# MUSE_PLUS_DLC, # To be included when OptionSets are rendered as part of basic settings.
# "maimai DX Limited-time Suite", # Part of Muse Plus. Goes away 31st Jan 2026.
"Miku in Museland", # Paid DLC not included in Muse Plus
- "MSR Anthology", # Part of Muse Plus. Goes away 20th Jan 2024.
+ "Rin Len's Mirrorland", # Paid DLC not included in Muse Plus
+ "MSR Anthology", # Now no longer available.
]
DIFF_OVERRIDES: List[str] = [
@@ -34,6 +35,13 @@ class MuseDashCollections:
"Rush-Hour",
"Find this Month's Featured Playlist",
"PeroPero in the Universe",
+ "umpopoff"
+ ]
+
+ REMOVED_SONGS = [
+ "CHAOS Glitch",
+ "FM 17314 SUGAR RADIO",
+ "Yume Ou Mono Yo Secret"
]
album_items: Dict[str, AlbumData] = {}
@@ -81,11 +89,22 @@ def __init__(self) -> None:
steamer_mode = sections[3] == "True"
if song_name in self.DIFF_OVERRIDES:
- # Note: These difficulties may not actually be representative of these songs.
- # The game does not provide these difficulties so they have to be filled in.
- diff_of_easy = 4
- diff_of_hard = 7
- diff_of_master = 10
+ # These songs use non-standard difficulty values. Which are being overriden with standard values.
+ # But also avoid filling any missing difficulties (i.e. 0s) with a difficulty value.
+ if sections[4] != '0':
+ diff_of_easy = 4
+ else:
+ diff_of_easy = None
+
+ if sections[5] != '0':
+ diff_of_hard = 7
+ else:
+ diff_of_hard = None
+
+ if sections[6] != '0':
+ diff_of_master = 10
+ else:
+ diff_of_master = None
else:
diff_of_easy = self.parse_song_difficulty(sections[4])
diff_of_hard = self.parse_song_difficulty(sections[5])
@@ -117,6 +136,9 @@ def get_songs_with_settings(self, dlc_songs: Set[str], streamer_mode_active: boo
for songKey, songData in self.song_items.items():
if not self.song_matches_dlc_filter(songData, dlc_songs):
continue
+
+ if songKey in self.REMOVED_SONGS:
+ continue
if streamer_mode_active and not songData.streamer_mode:
continue
diff --git a/worlds/musedash/MuseDashData.txt b/worlds/musedash/MuseDashData.txt
index 54a0124474c6..620c1968bda8 100644
--- a/worlds/musedash/MuseDashData.txt
+++ b/worlds/musedash/MuseDashData.txt
@@ -119,7 +119,7 @@ Prestige and Vestige|56-4|Give Up TREATMENT Vol.11|True|6|8|11|
Tiny Fate|56-5|Give Up TREATMENT Vol.11|False|7|9|11|
Tsuki ni Murakumo Hana ni Kaze|55-0|Touhou Mugakudan -2-|False|3|5|7|
Patchouli's - Best Hit GSK|55-1|Touhou Mugakudan -2-|False|3|5|8|
-Monosugoi Space Shuttle de Koishi ga Monosugoi uta|55-2|Touhou Mugakudan -2-|False|3|5|7|
+Monosugoi Space Shuttle de Koishi ga Monosugoi uta|55-2|Touhou Mugakudan -2-|False|3|5|7|11
Kakoinaki Yo wa Ichigo no Tsukikage|55-3|Touhou Mugakudan -2-|False|3|6|8|
Psychedelic Kizakura Doumei|55-4|Touhou Mugakudan -2-|False|4|7|10|
Mischievous Sensation|55-5|Touhou Mugakudan -2-|False|5|7|9|
@@ -484,7 +484,7 @@ Hand in Hand|66-1|Miku in Museland|False|1|3|6|
Cynical Night Plan|66-2|Miku in Museland|False|4|6|8|
God-ish|66-3|Miku in Museland|False|4|7|10|
Darling Dance|66-4|Miku in Museland|False|4|7|9|
-Hatsune Creation Myth|66-5|Miku in Museland|False|6|8|10|
+Hatsune Creation Myth|66-5|Miku in Museland|False|6|8|10|11
The Vampire|66-6|Miku in Museland|False|4|6|9|
Future Eve|66-7|Miku in Museland|False|4|8|11|
Unknown Mother Goose|66-8|Miku in Museland|False|4|8|10|
@@ -501,4 +501,40 @@ slic.hertz|68-1|Gambler's Tricks|True|5|7|9|
Fuzzy-Navel|68-2|Gambler's Tricks|True|6|8|10|11
Swing Edge|68-3|Gambler's Tricks|True|4|8|10|
Twisted Escape|68-4|Gambler's Tricks|True|5|8|10|11
-Swing Sweet Twee Dance|68-5|Gambler's Tricks|False|4|7|10|
\ No newline at end of file
+Swing Sweet Twee Dance|68-5|Gambler's Tricks|False|4|7|10|
+Sanyousei SAY YA!!!|43-42|MD Plus Project|False|4|6|8|
+YUKEMURI TAMAONSEN II|43-43|MD Plus Project|False|3|6|9|
+Samayoi no mei Amatsu|69-0|Touhou Mugakudan -3-|False|4|6|9|
+INTERNET SURVIVOR|69-1|Touhou Mugakudan -3-|False|5|8|10|
+Shuki*RaiRai|69-2|Touhou Mugakudan -3-|False|5|7|9|
+HELLOHELL|69-3|Touhou Mugakudan -3-|False|4|7|10|
+Calamity Fortune|69-4|Touhou Mugakudan -3-|True|6|8|10|11
+Tsurupettan|69-5|Touhou Mugakudan -3-|True|2|5|8|
+Twilight Poems|43-44|MD Plus Project|True|3|6|8|
+All My Friends feat. RANASOL|43-45|MD Plus Project|True|4|7|9|
+Heartache|43-46|MD Plus Project|True|5|7|10|
+Blue Lemonade|43-47|MD Plus Project|True|3|6|8|
+Haunted Dance|43-48|MD Plus Project|False|6|9|11|
+Hey Vincent.|43-49|MD Plus Project|True|6|8|10|
+Meteor feat. TEA|43-50|MD Plus Project|True|3|6|9|
+Narcissism Angel|43-51|MD Plus Project|True|1|3|6|
+AlterLuna|43-52|MD Plus Project|True|6|8|11|
+Niki Tousen|43-53|MD Plus Project|True|6|8|10|11
+Rettou Joutou|70-0|Rin Len's Mirrorland|False|4|7|9|
+Telecaster B-Boy|70-1|Rin Len's Mirrorland|False|5|7|10|
+Iya Iya Iya|70-2|Rin Len's Mirrorland|False|2|4|7|
+Nee Nee Nee|70-3|Rin Len's Mirrorland|False|4|6|8|
+Chaotic Love Revolution|70-4|Rin Len's Mirrorland|False|4|6|8|
+Dance of the Corpses|70-5|Rin Len's Mirrorland|False|2|5|8|
+Bitter Choco Decoration|70-6|Rin Len's Mirrorland|False|3|6|9|
+Dance Robot Dance|70-7|Rin Len's Mirrorland|False|4|7|10|
+Sweet Devil|70-8|Rin Len's Mirrorland|False|5|7|9|
+Someday'z Coming|70-9|Rin Len's Mirrorland|False|5|7|9|
+Yume Ou Mono Yo Secret|0-53|Default Music|True|6|8|10|
+Yume Ou Mono Yo|0-54|Default Music|True|1|4|0|
+Sweet Dream VIVINOS|71-0|Valentine Stage|False|1|4|7|
+Ruler Of My Heart VIVINOS|71-1|Valentine Stage|False|2|4|6|
+Reality Show|71-2|Valentine Stage|False|5|7|10|
+SIG feat.Tobokegao|71-3|Valentine Stage|True|3|6|8|
+Rose Love|71-4|Valentine Stage|True|2|4|7|
+Euphoria|71-5|Valentine Stage|True|1|3|6|
\ No newline at end of file
diff --git a/worlds/musedash/Options.py b/worlds/musedash/Options.py
index 3fe28187fae6..26ad5ff5d967 100644
--- a/worlds/musedash/Options.py
+++ b/worlds/musedash/Options.py
@@ -36,7 +36,7 @@ class AdditionalSongs(Range):
- The final song count may be lower due to other settings.
"""
range_start = 15
- range_end = 500 # Note will probably not reach this high if any other settings are done.
+ range_end = 528 # Note will probably not reach this high if any other settings are done.
default = 40
display_name = "Additional Song Count"
diff --git a/worlds/musedash/__init__.py b/worlds/musedash/__init__.py
index a68fd2853def..af2d4cc207da 100644
--- a/worlds/musedash/__init__.py
+++ b/worlds/musedash/__init__.py
@@ -328,5 +328,6 @@ def fill_slot_data(self):
"victoryLocation": self.victory_song_name,
"deathLink": self.options.death_link.value,
"musicSheetWinCount": self.get_music_sheet_win_count(),
- "gradeNeeded": self.options.grade_needed.value
+ "gradeNeeded": self.options.grade_needed.value,
+ "hasFiller": True,
}
diff --git a/worlds/musedash/docs/en_Muse Dash.md b/worlds/musedash/docs/en_Muse Dash.md
index 008fd4d2df0c..29d1465ed098 100644
--- a/worlds/musedash/docs/en_Muse Dash.md
+++ b/worlds/musedash/docs/en_Muse Dash.md
@@ -2,10 +2,10 @@
## Quick Links
- [Setup Guide](../../../tutorial/Muse%20Dash/setup/en)
-- [Settings Page](../player-settings)
+- [Options Page](../player-options)
## What Does Randomization do to this Game?
-- You will be given a number of starting songs. The number of which depends on your settings.
+- You will be given a number of starting songs. The number of which depends on your options.
- Completing any song will give you 1 or 2 rewards.
- The rewards for completing songs will range from songs to traps and **Music Sheets**.
diff --git a/worlds/musedash/docs/setup_en.md b/worlds/musedash/docs/setup_en.md
index ebf165c7dd78..312cdbd1958f 100644
--- a/worlds/musedash/docs/setup_en.md
+++ b/worlds/musedash/docs/setup_en.md
@@ -2,7 +2,7 @@
## Quick Links
- [Main Page](../../../../games/Muse%20Dash/info/en)
-- [Settings Page](../../../../games/Muse%20Dash/player-settings)
+- [Options Page](../../../../games/Muse%20Dash/player-options)
## Required Software
@@ -27,7 +27,7 @@
If you've successfully installed everything, a button will appear in the bottom right which will allow you to log into an Archipelago server.
## Generating a MultiWorld Game
-1. Visit the [Player Settings](/games/Muse%20Dash/player-settings) page and configure the game-specific settings to your taste.
+1. Visit the [Player Options](/games/Muse%20Dash/player-options) page and configure the game-specific options to your taste.
2. Export your yaml file and use it to generate a new randomized game
- (For instructions on how to generate an Archipelago game, refer to the [Archipelago Web Guide](/tutorial/Archipelago/setup/en))
diff --git a/worlds/musedash/test/TestCollection.py b/worlds/musedash/test/TestCollection.py
index f9422388ae1e..48cb69e403ad 100644
--- a/worlds/musedash/test/TestCollection.py
+++ b/worlds/musedash/test/TestCollection.py
@@ -3,11 +3,6 @@
class CollectionsTest(unittest.TestCase):
- REMOVED_SONGS = [
- "CHAOS Glitch",
- "FM 17314 SUGAR RADIO",
- ]
-
def test_all_names_are_ascii(self) -> None:
bad_names = list()
collection = MuseDashCollections()
@@ -58,5 +53,5 @@ def test_remove_songs_are_not_generated(self) -> None:
collection = MuseDashCollections()
songs = collection.get_songs_with_settings({x for x in collection.DLC}, False, 0, 12)
- for song_name in self.REMOVED_SONGS:
+ for song_name in collection.REMOVED_SONGS:
self.assertNotIn(song_name, songs, f"Song '{song_name}' wasn't removed correctly.")
diff --git a/worlds/musedash/test/TestDifficultyRanges.py b/worlds/musedash/test/TestDifficultyRanges.py
index 01420347af15..af3469aa080f 100644
--- a/worlds/musedash/test/TestDifficultyRanges.py
+++ b/worlds/musedash/test/TestDifficultyRanges.py
@@ -66,5 +66,11 @@ def test_songs_have_difficulty(self) -> None:
for song_name in muse_dash_world.md_collection.DIFF_OVERRIDES:
song = muse_dash_world.md_collection.song_items[song_name]
- self.assertTrue(song.easy is not None and song.hard is not None and song.master is not None,
+ # umpopoff is a one time weird song. Its currently the only song in the game
+ # with non-standard difficulties and also doesn't have 3 or more difficulties.
+ if song_name == 'umpopoff':
+ self.assertTrue(song.easy is None and song.hard is not None and song.master is None,
+ f"Song '{song_name}' difficulty not set when it should be.")
+ else:
+ self.assertTrue(song.easy is not None and song.hard is not None and song.master is not None,
f"Song '{song_name}' difficulty not set when it should be.")
diff --git a/worlds/noita/Events.py b/worlds/noita/Events.py
deleted file mode 100644
index e759d38c6c7a..000000000000
--- a/worlds/noita/Events.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from typing import Dict
-
-from BaseClasses import Item, ItemClassification, Location, MultiWorld, Region
-from . import Items, Locations
-
-
-def create_event(player: int, name: str) -> Item:
- return Items.NoitaItem(name, ItemClassification.progression, None, player)
-
-
-def create_location(player: int, name: str, region: Region) -> Location:
- return Locations.NoitaLocation(player, name, None, region)
-
-
-def create_locked_location_event(multiworld: MultiWorld, player: int, region_name: str, item: str) -> Location:
- region = multiworld.get_region(region_name, player)
-
- new_location = create_location(player, item, region)
- new_location.place_locked_item(create_event(player, item))
-
- region.locations.append(new_location)
- return new_location
-
-
-def create_all_events(multiworld: MultiWorld, player: int) -> None:
- for region, event in event_locks.items():
- create_locked_location_event(multiworld, player, region, event)
-
- multiworld.completion_condition[player] = lambda state: state.has("Victory", player)
-
-
-# Maps region names to event names
-event_locks: Dict[str, str] = {
- "The Work": "Victory",
- "Mines": "Portal to Holy Mountain 1",
- "Coal Pits": "Portal to Holy Mountain 2",
- "Snowy Depths": "Portal to Holy Mountain 3",
- "Hiisi Base": "Portal to Holy Mountain 4",
- "Underground Jungle": "Portal to Holy Mountain 5",
- "The Vault": "Portal to Holy Mountain 6",
- "Temple of the Art": "Portal to Holy Mountain 7",
-}
diff --git a/worlds/noita/Rules.py b/worlds/noita/Rules.py
deleted file mode 100644
index 8190b80dc710..000000000000
--- a/worlds/noita/Rules.py
+++ /dev/null
@@ -1,166 +0,0 @@
-from typing import List, NamedTuple, Set
-
-from BaseClasses import CollectionState, MultiWorld
-from . import Items, Locations
-from .Options import BossesAsChecks, VictoryCondition
-from worlds.generic import Rules as GenericRules
-
-
-class EntranceLock(NamedTuple):
- source: str
- destination: str
- event: str
- items_needed: int
-
-
-entrance_locks: List[EntranceLock] = [
- EntranceLock("Mines", "Coal Pits Holy Mountain", "Portal to Holy Mountain 1", 1),
- EntranceLock("Coal Pits", "Snowy Depths Holy Mountain", "Portal to Holy Mountain 2", 2),
- EntranceLock("Snowy Depths", "Hiisi Base Holy Mountain", "Portal to Holy Mountain 3", 3),
- EntranceLock("Hiisi Base", "Underground Jungle Holy Mountain", "Portal to Holy Mountain 4", 4),
- EntranceLock("Underground Jungle", "Vault Holy Mountain", "Portal to Holy Mountain 5", 5),
- EntranceLock("The Vault", "Temple of the Art Holy Mountain", "Portal to Holy Mountain 6", 6),
- EntranceLock("Temple of the Art", "Laboratory Holy Mountain", "Portal to Holy Mountain 7", 7),
-]
-
-
-holy_mountain_regions: List[str] = [
- "Coal Pits Holy Mountain",
- "Snowy Depths Holy Mountain",
- "Hiisi Base Holy Mountain",
- "Underground Jungle Holy Mountain",
- "Vault Holy Mountain",
- "Temple of the Art Holy Mountain",
- "Laboratory Holy Mountain",
-]
-
-
-wand_tiers: List[str] = [
- "Wand (Tier 1)", # Coal Pits
- "Wand (Tier 2)", # Snowy Depths
- "Wand (Tier 3)", # Hiisi Base
- "Wand (Tier 4)", # Underground Jungle
- "Wand (Tier 5)", # The Vault
- "Wand (Tier 6)", # Temple of the Art
-]
-
-items_hidden_from_shops: List[str] = ["Gold (200)", "Gold (1000)", "Potion", "Random Potion", "Secret Potion",
- "Chaos Die", "Greed Die", "Kammi", "Refreshing Gourd", "Sädekivi", "Broken Wand",
- "Powder Pouch"]
-
-perk_list: List[str] = list(filter(Items.item_is_perk, Items.item_table.keys()))
-
-
-# ----------------
-# Helper Functions
-# ----------------
-
-
-def has_perk_count(state: CollectionState, player: int, amount: int) -> bool:
- return sum(state.count(perk, player) for perk in perk_list) >= amount
-
-
-def has_orb_count(state: CollectionState, player: int, amount: int) -> bool:
- return state.count("Orb", player) >= amount
-
-
-def forbid_items_at_location(multiworld: MultiWorld, location_name: str, items: Set[str], player: int):
- location = multiworld.get_location(location_name, player)
- GenericRules.forbid_items_for_player(location, items, player)
-
-
-# ----------------
-# Rule Functions
-# ----------------
-
-
-# Prevent gold and potions from appearing as purchasable items in shops (because physics will destroy them)
-def ban_items_from_shops(multiworld: MultiWorld, player: int) -> None:
- for location_name in Locations.location_name_to_id.keys():
- if "Shop Item" in location_name:
- forbid_items_at_location(multiworld, location_name, items_hidden_from_shops, player)
-
-
-# Prevent high tier wands from appearing in early Holy Mountain shops
-def ban_early_high_tier_wands(multiworld: MultiWorld, player: int) -> None:
- for i, region_name in enumerate(holy_mountain_regions):
- wands_to_forbid = wand_tiers[i+1:]
-
- locations_in_region = Locations.location_region_mapping[region_name].keys()
- for location_name in locations_in_region:
- forbid_items_at_location(multiworld, location_name, wands_to_forbid, player)
-
- # Prevent high tier wands from appearing in the Secret shop
- wands_to_forbid = wand_tiers[3:]
- locations_in_region = Locations.location_region_mapping["Secret Shop"].keys()
- for location_name in locations_in_region:
- forbid_items_at_location(multiworld, location_name, wands_to_forbid, player)
-
-
-def lock_holy_mountains_into_spheres(multiworld: MultiWorld, player: int) -> None:
- for lock in entrance_locks:
- location = multiworld.get_entrance(f"From {lock.source} To {lock.destination}", player)
- GenericRules.set_rule(location, lambda state, evt=lock.event: state.has(evt, player))
-
-
-def holy_mountain_unlock_conditions(multiworld: MultiWorld, player: int) -> None:
- victory_condition = multiworld.victory_condition[player].value
- for lock in entrance_locks:
- location = multiworld.get_location(lock.event, player)
-
- if victory_condition == VictoryCondition.option_greed_ending:
- location.access_rule = lambda state, items_needed=lock.items_needed: (
- has_perk_count(state, player, items_needed//2)
- )
- elif victory_condition == VictoryCondition.option_pure_ending:
- location.access_rule = lambda state, items_needed=lock.items_needed: (
- has_perk_count(state, player, items_needed//2) and
- has_orb_count(state, player, items_needed)
- )
- elif victory_condition == VictoryCondition.option_peaceful_ending:
- location.access_rule = lambda state, items_needed=lock.items_needed: (
- has_perk_count(state, player, items_needed//2) and
- has_orb_count(state, player, items_needed * 3)
- )
-
-
-def biome_unlock_conditions(multiworld: MultiWorld, player: int):
- lukki_entrances = multiworld.get_region("Lukki Lair", player).entrances
- magical_entrances = multiworld.get_region("Magical Temple", player).entrances
- wizard_entrances = multiworld.get_region("Wizards' Den", player).entrances
- for entrance in lukki_entrances:
- entrance.access_rule = lambda state: state.has("Melee Immunity Perk", player) and\
- state.has("All-Seeing Eye Perk", player)
- for entrance in magical_entrances:
- entrance.access_rule = lambda state: state.has("All-Seeing Eye Perk", player)
- for entrance in wizard_entrances:
- entrance.access_rule = lambda state: state.has("All-Seeing Eye Perk", player)
-
-
-def victory_unlock_conditions(multiworld: MultiWorld, player: int) -> None:
- victory_condition = multiworld.victory_condition[player].value
- victory_location = multiworld.get_location("Victory", player)
-
- if victory_condition == VictoryCondition.option_pure_ending:
- victory_location.access_rule = lambda state: has_orb_count(state, player, 11)
- elif victory_condition == VictoryCondition.option_peaceful_ending:
- victory_location.access_rule = lambda state: has_orb_count(state, player, 33)
-
-
-# ----------------
-# Main Function
-# ----------------
-
-
-def create_all_rules(multiworld: MultiWorld, player: int) -> None:
- if multiworld.players > 1:
- ban_items_from_shops(multiworld, player)
- ban_early_high_tier_wands(multiworld, player)
- lock_holy_mountains_into_spheres(multiworld, player)
- holy_mountain_unlock_conditions(multiworld, player)
- biome_unlock_conditions(multiworld, player)
- victory_unlock_conditions(multiworld, player)
-
- # Prevent the Map perk (used to find Toveri) from being on Toveri (boss)
- if multiworld.bosses_as_checks[player].value >= BossesAsChecks.option_all_bosses:
- forbid_items_at_location(multiworld, "Toveri", {"Spatial Awareness Perk"}, player)
diff --git a/worlds/noita/__init__.py b/worlds/noita/__init__.py
index 792b90e3f551..b8f8e4ae8346 100644
--- a/worlds/noita/__init__.py
+++ b/worlds/noita/__init__.py
@@ -1,6 +1,8 @@
from BaseClasses import Item, Tutorial
from worlds.AutoWorld import WebWorld, World
-from . import Events, Items, Locations, Options, Regions, Rules
+from typing import Dict, Any
+from . import events, items, locations, regions, rules
+from .options import NoitaOptions
class NoitaWeb(WebWorld):
@@ -24,13 +26,14 @@ class NoitaWorld(World):
"""
game = "Noita"
- option_definitions = Options.noita_options
+ options: NoitaOptions
+ options_dataclass = NoitaOptions
- item_name_to_id = Items.item_name_to_id
- location_name_to_id = Locations.location_name_to_id
+ item_name_to_id = items.item_name_to_id
+ location_name_to_id = locations.location_name_to_id
- item_name_groups = Items.item_name_groups
- location_name_groups = Locations.location_name_groups
+ item_name_groups = items.item_name_groups
+ location_name_groups = locations.location_name_groups
data_version = 2
web = NoitaWeb()
@@ -40,21 +43,21 @@ def generate_early(self):
raise Exception("Noita yaml's slot name has invalid character(s).")
# Returned items will be sent over to the client
- def fill_slot_data(self):
- return {name: getattr(self.multiworld, name)[self.player].value for name in self.option_definitions}
+ def fill_slot_data(self) -> Dict[str, Any]:
+ return self.options.as_dict("death_link", "victory_condition", "path_option", "hidden_chests",
+ "pedestal_checks", "orbs_as_checks", "bosses_as_checks", "extra_orbs", "shop_price")
def create_regions(self) -> None:
- Regions.create_all_regions_and_connections(self.multiworld, self.player)
- Events.create_all_events(self.multiworld, self.player)
+ regions.create_all_regions_and_connections(self)
def create_item(self, name: str) -> Item:
- return Items.create_item(self.player, name)
+ return items.create_item(self.player, name)
def create_items(self) -> None:
- Items.create_all_items(self.multiworld, self.player)
+ items.create_all_items(self)
def set_rules(self) -> None:
- Rules.create_all_rules(self.multiworld, self.player)
+ rules.create_all_rules(self)
def get_filler_item_name(self) -> str:
- return self.multiworld.random.choice(Items.filler_items)
+ return self.random.choice(items.filler_items)
diff --git a/worlds/noita/docs/en_Noita.md b/worlds/noita/docs/en_Noita.md
index b1480068e96c..1e560cfcb748 100644
--- a/worlds/noita/docs/en_Noita.md
+++ b/worlds/noita/docs/en_Noita.md
@@ -1,15 +1,15 @@
# Noita
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
Noita is a procedurally generated action roguelike. During runs in Noita you will find potions, wands, spells, perks,
chests, etc. Shop items, chests/hearts hidden in the environment, and pedestal items will be replaced with location
-checks. Orbs and boss drops can give location checks as well, if they are enabled in the settings.
+checks. Orbs and boss drops can give location checks as well, if their respective options are enabled.
Noita items that can be found in other players' games include specific perks, orbs (optional), wands, hearts, gold,
potions, and other items. If traps are enabled, some randomized negative effects can affect your game when found.
@@ -50,9 +50,9 @@ Traps consist of all "Bad" and "Awful" events from Noita's native stream integra
## How many location checks are there?
-When using the default settings, there are 109 location checks. The number of checks in the game is dependent on the settings that you choose.
-Please check the information boxes next to the settings when setting up your YAML to see how many checks the individual options add.
-There are always 42 Holy Mountain checks and 4 Secret Shop checks in the pool which are not affected by your YAML settings.
+When using the default options, there are 109 location checks. The number of checks in the game is dependent on the options that you choose.
+Please check the information boxes next to the options when setting up your YAML to see how many checks the individual options add.
+There are always 42 Holy Mountain checks and 4 Secret Shop checks in the pool which are not affected by your YAML options.
## What does another world's item look like in Noita?
diff --git a/worlds/noita/docs/setup_en.md b/worlds/noita/docs/setup_en.md
index b67e840bb94c..25c6cbc948bc 100644
--- a/worlds/noita/docs/setup_en.md
+++ b/worlds/noita/docs/setup_en.md
@@ -44,7 +44,7 @@ Please note that Noita only allows you to type certain characters for your slot
These characters are: `` !#$%&'()+,-.0123456789;=@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{}~<>|\/``
### Where do I get a YAML?
-You can use the [game settings page for Noita](/games/Noita/player-settings) here on the Archipelago website to
+You can use the [game options page for Noita](/games/Noita/player-options) here on the Archipelago website to
generate a YAML using a graphical interface.
## Poptracker Pack
diff --git a/worlds/noita/events.py b/worlds/noita/events.py
new file mode 100644
index 000000000000..4ec04e98b457
--- /dev/null
+++ b/worlds/noita/events.py
@@ -0,0 +1,43 @@
+from typing import Dict, TYPE_CHECKING
+from BaseClasses import Item, ItemClassification, Location, Region
+from . import items, locations
+
+if TYPE_CHECKING:
+ from . import NoitaWorld
+
+
+def create_event(player: int, name: str) -> Item:
+ return items.NoitaItem(name, ItemClassification.progression, None, player)
+
+
+def create_location(player: int, name: str, region: Region) -> Location:
+ return locations.NoitaLocation(player, name, None, region)
+
+
+def create_locked_location_event(player: int, region: Region, item: str) -> Location:
+ new_location = create_location(player, item, region)
+ new_location.place_locked_item(create_event(player, item))
+
+ region.locations.append(new_location)
+ return new_location
+
+
+def create_all_events(world: "NoitaWorld", created_regions: Dict[str, Region]) -> None:
+ for region_name, event in event_locks.items():
+ region = created_regions[region_name]
+ create_locked_location_event(world.player, region, event)
+
+ world.multiworld.completion_condition[world.player] = lambda state: state.has("Victory", world.player)
+
+
+# Maps region names to event names
+event_locks: Dict[str, str] = {
+ "The Work": "Victory",
+ "Mines": "Portal to Holy Mountain 1",
+ "Coal Pits": "Portal to Holy Mountain 2",
+ "Snowy Depths": "Portal to Holy Mountain 3",
+ "Hiisi Base": "Portal to Holy Mountain 4",
+ "Underground Jungle": "Portal to Holy Mountain 5",
+ "The Vault": "Portal to Holy Mountain 6",
+ "Temple of the Art": "Portal to Holy Mountain 7",
+}
diff --git a/worlds/noita/Items.py b/worlds/noita/items.py
similarity index 82%
rename from worlds/noita/Items.py
rename to worlds/noita/items.py
index c859a8039494..6b662fbee692 100644
--- a/worlds/noita/Items.py
+++ b/worlds/noita/items.py
@@ -1,9 +1,14 @@
import itertools
from collections import Counter
-from typing import Dict, List, NamedTuple, Set
+from typing import Dict, List, NamedTuple, Set, TYPE_CHECKING
-from BaseClasses import Item, ItemClassification, MultiWorld
-from .Options import BossesAsChecks, VictoryCondition, ExtraOrbs
+from BaseClasses import Item, ItemClassification
+from .options import BossesAsChecks, VictoryCondition, ExtraOrbs
+
+if TYPE_CHECKING:
+ from . import NoitaWorld
+else:
+ NoitaWorld = object
class ItemData(NamedTuple):
@@ -44,39 +49,40 @@ def create_kantele(victory_condition: VictoryCondition) -> List[str]:
return ["Kantele"] if victory_condition.value >= VictoryCondition.option_pure_ending else []
-def create_random_items(multiworld: MultiWorld, player: int, weights: Dict[str, int], count: int) -> List[str]:
+def create_random_items(world: NoitaWorld, weights: Dict[str, int], count: int) -> List[str]:
filler_pool = weights.copy()
- if multiworld.bad_effects[player].value == 0:
+ if not world.options.bad_effects:
del filler_pool["Trap"]
- return multiworld.random.choices(population=list(filler_pool.keys()),
- weights=list(filler_pool.values()),
- k=count)
+ return world.random.choices(population=list(filler_pool.keys()),
+ weights=list(filler_pool.values()),
+ k=count)
-def create_all_items(multiworld: MultiWorld, player: int) -> None:
- locations_to_fill = len(multiworld.get_unfilled_locations(player))
+def create_all_items(world: NoitaWorld) -> None:
+ player = world.player
+ locations_to_fill = len(world.multiworld.get_unfilled_locations(player))
itempool = (
create_fixed_item_pool()
- + create_orb_items(multiworld.victory_condition[player], multiworld.extra_orbs[player])
- + create_spatial_awareness_item(multiworld.bosses_as_checks[player])
- + create_kantele(multiworld.victory_condition[player])
+ + create_orb_items(world.options.victory_condition, world.options.extra_orbs)
+ + create_spatial_awareness_item(world.options.bosses_as_checks)
+ + create_kantele(world.options.victory_condition)
)
# if there's not enough shop-allowed items in the pool, we can encounter gen issues
# 39 is the number of shop-valid items we need to guarantee
if len(itempool) < 39:
- itempool += create_random_items(multiworld, player, shop_only_filler_weights, 39 - len(itempool))
+ itempool += create_random_items(world, shop_only_filler_weights, 39 - len(itempool))
# this is so that it passes tests and gens if you have minimal locations and only one player
- if multiworld.players == 1:
- for location in multiworld.get_unfilled_locations(player):
+ if world.multiworld.players == 1:
+ for location in world.multiworld.get_unfilled_locations(player):
if "Shop Item" in location.name:
location.item = create_item(player, itempool.pop())
- locations_to_fill = len(multiworld.get_unfilled_locations(player))
+ locations_to_fill = len(world.multiworld.get_unfilled_locations(player))
- itempool += create_random_items(multiworld, player, filler_weights, locations_to_fill - len(itempool))
- multiworld.itempool += [create_item(player, name) for name in itempool]
+ itempool += create_random_items(world, filler_weights, locations_to_fill - len(itempool))
+ world.multiworld.itempool += [create_item(player, name) for name in itempool]
# 110000 - 110032
diff --git a/worlds/noita/Locations.py b/worlds/noita/locations.py
similarity index 94%
rename from worlds/noita/Locations.py
rename to worlds/noita/locations.py
index 7c27d699ccba..afe16c54e4b2 100644
--- a/worlds/noita/Locations.py
+++ b/worlds/noita/locations.py
@@ -201,11 +201,10 @@ class LocationFlag(IntEnum):
}
-# Iterating the hidden chest and pedestal locations here to avoid clutter above
-def generate_location_entries(locname: str, locinfo: LocationData) -> Dict[str, int]:
- if locinfo.ltype in ["chest", "pedestal"]:
- return {f"{locname} {i + 1}": locinfo.id + i for i in range(20)}
- return {locname: locinfo.id}
+def make_location_range(location_name: str, base_id: int, amt: int) -> Dict[str, int]:
+ if amt == 1:
+ return {location_name: base_id}
+ return {f"{location_name} {i+1}": base_id + i for i in range(amt)}
location_name_groups: Dict[str, Set[str]] = {"shop": set(), "orb": set(), "boss": set(), "chest": set(),
@@ -215,9 +214,11 @@ def generate_location_entries(locname: str, locinfo: LocationData) -> Dict[str,
for location_group in location_region_mapping.values():
for locname, locinfo in location_group.items():
- location_name_to_id.update(generate_location_entries(locname, locinfo))
- if locinfo.ltype in ["chest", "pedestal"]:
- for i in range(20):
- location_name_groups[locinfo.ltype].add(f"{locname} {i + 1}")
- else:
- location_name_groups[locinfo.ltype].add(locname)
+ # Iterating the hidden chest and pedestal locations here to avoid clutter above
+ amount = 20 if locinfo.ltype in ["chest", "pedestal"] else 1
+ entries = make_location_range(locname, locinfo.id, amount)
+
+ location_name_to_id.update(entries)
+ location_name_groups[locinfo.ltype].update(entries.keys())
+
+shop_locations = {name for name in location_name_to_id.keys() if "Shop Item" in name}
diff --git a/worlds/noita/Options.py b/worlds/noita/options.py
similarity index 85%
rename from worlds/noita/Options.py
rename to worlds/noita/options.py
index 0b54597f364d..2c99e9dd2f38 100644
--- a/worlds/noita/Options.py
+++ b/worlds/noita/options.py
@@ -1,5 +1,5 @@
-from typing import Dict
-from Options import AssembleOptions, Choice, DeathLink, DefaultOnToggle, Range, StartInventoryPool
+from Options import Choice, DeathLink, DefaultOnToggle, Range, StartInventoryPool, PerGameCommonOptions
+from dataclasses import dataclass
class PathOption(Choice):
@@ -78,8 +78,7 @@ class VictoryCondition(Choice):
class ExtraOrbs(Range):
- """Add extra orbs to your item pool, to prevent you from needing to wait as long
- for the last orb you need for your victory condition.
+ """Add extra orbs to your item pool, to prevent you from needing to wait as long for the last orb you need for your victory condition.
Extra orbs received past your victory condition's amount will be received as hearts instead.
Can be turned on for the Greed Ending goal, but will only really make it harder."""
display_name = "Extra Orbs"
@@ -99,16 +98,16 @@ class ShopPrice(Choice):
default = 100
-noita_options: Dict[str, AssembleOptions] = {
- "start_inventory_from_pool": StartInventoryPool,
- "death_link": DeathLink,
- "bad_effects": Traps,
- "victory_condition": VictoryCondition,
- "path_option": PathOption,
- "hidden_chests": HiddenChests,
- "pedestal_checks": PedestalChecks,
- "orbs_as_checks": OrbsAsChecks,
- "bosses_as_checks": BossesAsChecks,
- "extra_orbs": ExtraOrbs,
- "shop_price": ShopPrice,
-}
+@dataclass
+class NoitaOptions(PerGameCommonOptions):
+ start_inventory_from_pool: StartInventoryPool
+ death_link: DeathLink
+ bad_effects: Traps
+ victory_condition: VictoryCondition
+ path_option: PathOption
+ hidden_chests: HiddenChests
+ pedestal_checks: PedestalChecks
+ orbs_as_checks: OrbsAsChecks
+ bosses_as_checks: BossesAsChecks
+ extra_orbs: ExtraOrbs
+ shop_price: ShopPrice
diff --git a/worlds/noita/Regions.py b/worlds/noita/regions.py
similarity index 61%
rename from worlds/noita/Regions.py
rename to worlds/noita/regions.py
index 561d483b4865..6a9c86772381 100644
--- a/worlds/noita/Regions.py
+++ b/worlds/noita/regions.py
@@ -1,48 +1,43 @@
# Regions are areas in your game that you travel to.
-from typing import Dict, Set, List
+from typing import Dict, List, TYPE_CHECKING
-from BaseClasses import Entrance, MultiWorld, Region
-from . import Locations
+from BaseClasses import Entrance, Region
+from . import locations
+from .events import create_all_events
+if TYPE_CHECKING:
+ from . import NoitaWorld
-def add_location(player: int, loc_name: str, id: int, region: Region) -> None:
- location = Locations.NoitaLocation(player, loc_name, id, region)
- region.locations.append(location)
-
-def add_locations(multiworld: MultiWorld, player: int, region: Region) -> None:
- locations = Locations.location_region_mapping.get(region.name, {})
- for location_name, location_data in locations.items():
+def create_locations(world: "NoitaWorld", region: Region) -> None:
+ locs = locations.location_region_mapping.get(region.name, {})
+ for location_name, location_data in locs.items():
location_type = location_data.ltype
flag = location_data.flag
- opt_orbs = multiworld.orbs_as_checks[player].value
- opt_bosses = multiworld.bosses_as_checks[player].value
- opt_paths = multiworld.path_option[player].value
- opt_num_chests = multiworld.hidden_chests[player].value
- opt_num_pedestals = multiworld.pedestal_checks[player].value
+ is_orb_allowed = location_type == "orb" and flag <= world.options.orbs_as_checks
+ is_boss_allowed = location_type == "boss" and flag <= world.options.bosses_as_checks
+ amount = 0
+ if flag == locations.LocationFlag.none or is_orb_allowed or is_boss_allowed:
+ amount = 1
+ elif location_type == "chest" and flag <= world.options.path_option:
+ amount = world.options.hidden_chests.value
+ elif location_type == "pedestal" and flag <= world.options.path_option:
+ amount = world.options.pedestal_checks.value
- is_orb_allowed = location_type == "orb" and flag <= opt_orbs
- is_boss_allowed = location_type == "boss" and flag <= opt_bosses
- if flag == Locations.LocationFlag.none or is_orb_allowed or is_boss_allowed:
- add_location(player, location_name, location_data.id, region)
- elif location_type == "chest" and flag <= opt_paths:
- for i in range(opt_num_chests):
- add_location(player, f"{location_name} {i+1}", location_data.id + i, region)
- elif location_type == "pedestal" and flag <= opt_paths:
- for i in range(opt_num_pedestals):
- add_location(player, f"{location_name} {i+1}", location_data.id + i, region)
+ region.add_locations(locations.make_location_range(location_name, location_data.id, amount),
+ locations.NoitaLocation)
# Creates a new Region with the locations found in `location_region_mapping` and adds them to the world.
-def create_region(multiworld: MultiWorld, player: int, region_name: str) -> Region:
- new_region = Region(region_name, player, multiworld)
- add_locations(multiworld, player, new_region)
+def create_region(world: "NoitaWorld", region_name: str) -> Region:
+ new_region = Region(region_name, world.player, world.multiworld)
+ create_locations(world, new_region)
return new_region
-def create_regions(multiworld: MultiWorld, player: int) -> Dict[str, Region]:
- return {name: create_region(multiworld, player, name) for name in noita_regions}
+def create_regions(world: "NoitaWorld") -> Dict[str, Region]:
+ return {name: create_region(world, name) for name in noita_regions}
# An "Entrance" is really just a connection between two regions
@@ -60,11 +55,12 @@ def create_connections(player: int, regions: Dict[str, Region]) -> None:
# Creates all regions and connections. Called from NoitaWorld.
-def create_all_regions_and_connections(multiworld: MultiWorld, player: int) -> None:
- created_regions = create_regions(multiworld, player)
- create_connections(player, created_regions)
+def create_all_regions_and_connections(world: "NoitaWorld") -> None:
+ created_regions = create_regions(world)
+ create_connections(world.player, created_regions)
+ create_all_events(world, created_regions)
- multiworld.regions += created_regions.values()
+ world.multiworld.regions += created_regions.values()
# Oh, what a tangled web we weave
diff --git a/worlds/noita/rules.py b/worlds/noita/rules.py
new file mode 100644
index 000000000000..95039bee4635
--- /dev/null
+++ b/worlds/noita/rules.py
@@ -0,0 +1,172 @@
+from typing import List, NamedTuple, Set, TYPE_CHECKING
+
+from BaseClasses import CollectionState
+from . import items, locations
+from .options import BossesAsChecks, VictoryCondition
+from worlds.generic import Rules as GenericRules
+
+if TYPE_CHECKING:
+ from . import NoitaWorld
+
+
+class EntranceLock(NamedTuple):
+ source: str
+ destination: str
+ event: str
+ items_needed: int
+
+
+entrance_locks: List[EntranceLock] = [
+ EntranceLock("Mines", "Coal Pits Holy Mountain", "Portal to Holy Mountain 1", 1),
+ EntranceLock("Coal Pits", "Snowy Depths Holy Mountain", "Portal to Holy Mountain 2", 2),
+ EntranceLock("Snowy Depths", "Hiisi Base Holy Mountain", "Portal to Holy Mountain 3", 3),
+ EntranceLock("Hiisi Base", "Underground Jungle Holy Mountain", "Portal to Holy Mountain 4", 4),
+ EntranceLock("Underground Jungle", "Vault Holy Mountain", "Portal to Holy Mountain 5", 5),
+ EntranceLock("The Vault", "Temple of the Art Holy Mountain", "Portal to Holy Mountain 6", 6),
+ EntranceLock("Temple of the Art", "Laboratory Holy Mountain", "Portal to Holy Mountain 7", 7),
+]
+
+
+holy_mountain_regions: List[str] = [
+ "Coal Pits Holy Mountain",
+ "Snowy Depths Holy Mountain",
+ "Hiisi Base Holy Mountain",
+ "Underground Jungle Holy Mountain",
+ "Vault Holy Mountain",
+ "Temple of the Art Holy Mountain",
+ "Laboratory Holy Mountain",
+]
+
+
+wand_tiers: List[str] = [
+ "Wand (Tier 1)", # Coal Pits
+ "Wand (Tier 2)", # Snowy Depths
+ "Wand (Tier 3)", # Hiisi Base
+ "Wand (Tier 4)", # Underground Jungle
+ "Wand (Tier 5)", # The Vault
+ "Wand (Tier 6)", # Temple of the Art
+]
+
+
+items_hidden_from_shops: Set[str] = {"Gold (200)", "Gold (1000)", "Potion", "Random Potion", "Secret Potion",
+ "Chaos Die", "Greed Die", "Kammi", "Refreshing Gourd", "Sädekivi", "Broken Wand",
+ "Powder Pouch"}
+
+perk_list: List[str] = list(filter(items.item_is_perk, items.item_table.keys()))
+
+
+# ----------------
+# Helper Functions
+# ----------------
+
+
+def has_perk_count(state: CollectionState, player: int, amount: int) -> bool:
+ return sum(state.count(perk, player) for perk in perk_list) >= amount
+
+
+def has_orb_count(state: CollectionState, player: int, amount: int) -> bool:
+ return state.count("Orb", player) >= amount
+
+
+def forbid_items_at_locations(world: "NoitaWorld", shop_locations: Set[str], forbidden_items: Set[str]):
+ for shop_location in shop_locations:
+ location = world.multiworld.get_location(shop_location, world.player)
+ GenericRules.forbid_items_for_player(location, forbidden_items, world.player)
+
+
+# ----------------
+# Rule Functions
+# ----------------
+
+
+# Prevent gold and potions from appearing as purchasable items in shops (because physics will destroy them)
+# def ban_items_from_shops(world: "NoitaWorld") -> None:
+# for location_name in Locations.location_name_to_id.keys():
+# if "Shop Item" in location_name:
+# forbid_items_at_location(world, location_name, items_hidden_from_shops)
+def ban_items_from_shops(world: "NoitaWorld") -> None:
+ forbid_items_at_locations(world, locations.shop_locations, items_hidden_from_shops)
+
+
+# Prevent high tier wands from appearing in early Holy Mountain shops
+def ban_early_high_tier_wands(world: "NoitaWorld") -> None:
+ for i, region_name in enumerate(holy_mountain_regions):
+ wands_to_forbid = set(wand_tiers[i+1:])
+
+ locations_in_region = set(locations.location_region_mapping[region_name].keys())
+ forbid_items_at_locations(world, locations_in_region, wands_to_forbid)
+
+ # Prevent high tier wands from appearing in the Secret shop
+ wands_to_forbid = set(wand_tiers[3:])
+ locations_in_region = set(locations.location_region_mapping["Secret Shop"].keys())
+ forbid_items_at_locations(world, locations_in_region, wands_to_forbid)
+
+
+def lock_holy_mountains_into_spheres(world: "NoitaWorld") -> None:
+ for lock in entrance_locks:
+ location = world.multiworld.get_entrance(f"From {lock.source} To {lock.destination}", world.player)
+ GenericRules.set_rule(location, lambda state, evt=lock.event: state.has(evt, world.player))
+
+
+def holy_mountain_unlock_conditions(world: "NoitaWorld") -> None:
+ victory_condition = world.options.victory_condition.value
+ for lock in entrance_locks:
+ location = world.multiworld.get_location(lock.event, world.player)
+
+ if victory_condition == VictoryCondition.option_greed_ending:
+ location.access_rule = lambda state, items_needed=lock.items_needed: (
+ has_perk_count(state, world.player, items_needed//2)
+ )
+ elif victory_condition == VictoryCondition.option_pure_ending:
+ location.access_rule = lambda state, items_needed=lock.items_needed: (
+ has_perk_count(state, world.player, items_needed//2) and
+ has_orb_count(state, world.player, items_needed)
+ )
+ elif victory_condition == VictoryCondition.option_peaceful_ending:
+ location.access_rule = lambda state, items_needed=lock.items_needed: (
+ has_perk_count(state, world.player, items_needed//2) and
+ has_orb_count(state, world.player, items_needed * 3)
+ )
+
+
+def biome_unlock_conditions(world: "NoitaWorld"):
+ lukki_entrances = world.multiworld.get_region("Lukki Lair", world.player).entrances
+ magical_entrances = world.multiworld.get_region("Magical Temple", world.player).entrances
+ wizard_entrances = world.multiworld.get_region("Wizards' Den", world.player).entrances
+ for entrance in lukki_entrances:
+ entrance.access_rule = lambda state: state.has("Melee Immunity Perk", world.player) and\
+ state.has("All-Seeing Eye Perk", world.player)
+ for entrance in magical_entrances:
+ entrance.access_rule = lambda state: state.has("All-Seeing Eye Perk", world.player)
+ for entrance in wizard_entrances:
+ entrance.access_rule = lambda state: state.has("All-Seeing Eye Perk", world.player)
+
+
+def victory_unlock_conditions(world: "NoitaWorld") -> None:
+ victory_condition = world.options.victory_condition.value
+ victory_location = world.multiworld.get_location("Victory", world.player)
+
+ if victory_condition == VictoryCondition.option_pure_ending:
+ victory_location.access_rule = lambda state: has_orb_count(state, world.player, 11)
+ elif victory_condition == VictoryCondition.option_peaceful_ending:
+ victory_location.access_rule = lambda state: has_orb_count(state, world.player, 33)
+
+
+# ----------------
+# Main Function
+# ----------------
+
+
+def create_all_rules(world: "NoitaWorld") -> None:
+ if world.multiworld.players > 1:
+ ban_items_from_shops(world)
+ ban_early_high_tier_wands(world)
+ lock_holy_mountains_into_spheres(world)
+ holy_mountain_unlock_conditions(world)
+ biome_unlock_conditions(world)
+ victory_unlock_conditions(world)
+
+ # Prevent the Map perk (used to find Toveri) from being on Toveri (boss)
+ if world.options.bosses_as_checks.value >= BossesAsChecks.option_all_bosses:
+ toveri = world.multiworld.get_location("Toveri", world.player)
+ GenericRules.forbid_items_for_player(toveri, {"Spatial Awareness Perk"}, world.player)
diff --git a/worlds/oot/Options.py b/worlds/oot/Options.py
index 120027e29dfa..2543cdc715c7 100644
--- a/worlds/oot/Options.py
+++ b/worlds/oot/Options.py
@@ -1271,7 +1271,7 @@ class LogicTricks(OptionList):
https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/oot/LogicTricks.py
"""
display_name = "Logic Tricks"
- valid_keys = frozenset(normalized_name_tricks)
+ valid_keys = tuple(normalized_name_tricks.keys())
valid_keys_casefold = True
diff --git a/worlds/oot/Patches.py b/worlds/oot/Patches.py
index 0f1d3f4dcb8c..2219d7bb95a8 100644
--- a/worlds/oot/Patches.py
+++ b/worlds/oot/Patches.py
@@ -29,14 +29,14 @@
from .texture_util import ci4_rgba16patch_to_ci8, rgba16_patch
from .Utils import __version__
-from worlds.Files import APContainer
+from worlds.Files import APPatch
from Utils import __version__ as ap_version
AP_PROGRESSION = 0xD4
AP_JUNK = 0xD5
-class OoTContainer(APContainer):
+class OoTContainer(APPatch):
game: str = 'Ocarina of Time'
def __init__(self, patch_data: bytes, base_path: str, output_directory: str,
diff --git a/worlds/oot/__init__.py b/worlds/oot/__init__.py
index e9c889d6f653..303529c945f6 100644
--- a/worlds/oot/__init__.py
+++ b/worlds/oot/__init__.py
@@ -92,7 +92,7 @@ class RomStart(str):
class OOTWeb(WebWorld):
setup = Tutorial(
- "Multiworld Setup Tutorial",
+ "Multiworld Setup Guide",
"A guide to setting up the Archipelago Ocarina of Time software on your computer.",
"English",
"setup_en.md",
@@ -118,7 +118,16 @@ class OOTWeb(WebWorld):
["TheLynk"]
)
- tutorials = [setup, setup_es, setup_fr]
+ setup_de = Tutorial(
+ setup.tutorial_name,
+ setup.description,
+ "Deutsch",
+ "setup_de.md",
+ "setup/de",
+ ["Held_der_Zeit"]
+ )
+
+ tutorials = [setup, setup_es, setup_fr, setup_de]
class OOTWorld(World):
@@ -1025,6 +1034,31 @@ def prefill_state(base_state):
def generate_output(self, output_directory: str):
+
+ # Write entrances to spoiler log
+ all_entrances = self.get_shuffled_entrances()
+ all_entrances.sort(reverse=True, key=lambda x: (x.type, x.name))
+ if not self.decouple_entrances:
+ while all_entrances:
+ loadzone = all_entrances.pop()
+ if loadzone.type != 'Overworld':
+ if loadzone.primary:
+ entrance = loadzone
+ else:
+ entrance = loadzone.reverse
+ if entrance.reverse is not None:
+ self.multiworld.spoiler.set_entrance(entrance, entrance.replaces.reverse, 'both', self.player)
+ else:
+ self.multiworld.spoiler.set_entrance(entrance, entrance.replaces, 'entrance', self.player)
+ else:
+ reverse = loadzone.replaces.reverse
+ if reverse in all_entrances:
+ all_entrances.remove(reverse)
+ self.multiworld.spoiler.set_entrance(loadzone, reverse, 'both', self.player)
+ else:
+ for entrance in all_entrances:
+ self.multiworld.spoiler.set_entrance(entrance, entrance.replaces, 'entrance', self.player)
+
if self.hints != 'none':
self.hint_data_available.wait()
@@ -1220,6 +1254,8 @@ def get_entrance_to_region(region):
def write_spoiler(self, spoiler_handle: typing.TextIO) -> None:
required_trials_str = ", ".join(t for t in self.skipped_trials if not self.skipped_trials[t])
+ if required_trials_str == "":
+ required_trials_str = "None"
spoiler_handle.write(f"\n\nTrials ({self.multiworld.get_player_name(self.player)}): {required_trials_str}\n")
if self.shopsanity != 'off':
@@ -1227,31 +1263,6 @@ def write_spoiler(self, spoiler_handle: typing.TextIO) -> None:
for k, v in self.shop_prices.items():
spoiler_handle.write(f"{k}: {v} Rupees\n")
- # Write entrances to spoiler log
- all_entrances = self.get_shuffled_entrances()
- all_entrances.sort(reverse=True, key=lambda x: x.name)
- all_entrances.sort(reverse=True, key=lambda x: x.type)
- if not self.decouple_entrances:
- while all_entrances:
- loadzone = all_entrances.pop()
- if loadzone.type != 'Overworld':
- if loadzone.primary:
- entrance = loadzone
- else:
- entrance = loadzone.reverse
- if entrance.reverse is not None:
- self.multiworld.spoiler.set_entrance(entrance, entrance.replaces.reverse, 'both', self.player)
- else:
- self.multiworld.spoiler.set_entrance(entrance, entrance.replaces, 'entrance', self.player)
- else:
- reverse = loadzone.replaces.reverse
- if reverse in all_entrances:
- all_entrances.remove(reverse)
- self.multiworld.spoiler.set_entrance(loadzone, reverse, 'both', self.player)
- else:
- for entrance in all_entrances:
- self.multiworld.spoiler.set_entrance(entrance, entrance.replaces, 'entrance', self.player)
-
# Key ring handling:
# Key rings are multiple items glued together into one, so we need to give
diff --git a/worlds/oot/docs/MultiWorld-Room_oot.png b/worlds/oot/docs/MultiWorld-Room_oot.png
new file mode 100644
index 000000000000..f0f224e5e1af
Binary files /dev/null and b/worlds/oot/docs/MultiWorld-Room_oot.png differ
diff --git a/worlds/oot/docs/de_Ocarina of Time.md b/worlds/oot/docs/de_Ocarina of Time.md
new file mode 100644
index 000000000000..4d9fd2ea14bd
--- /dev/null
+++ b/worlds/oot/docs/de_Ocarina of Time.md
@@ -0,0 +1,41 @@
+# The Legend of Zelda: Ocarina of Time
+
+## Wo ist die Seite für die Einstellungen?
+
+Die [Seite für die Spielereinstellungen dieses Spiels](../player-options) enthält alle Optionen die man benötigt um
+eine YAML-Datei zu konfigurieren und zu exportieren.
+
+## Was macht der Randomizer in diesem Spiel?
+
+Items, welche der Spieler für gewöhnlich im Verlauf des Spiels erhalten würde, wurden umhergemischt. Die Logik bleit
+bestehen, damit ist das Spiel immer durchspielbar. Doch weil die Items durch das ganze Spiel gemischt wurden, müssen
+ manche Bereiche früher bescuht werden, als man es in Vanilla tun würde.
+Eine Liste von implementierter Logik, die unoffensichtlich erscheinen kann, kann
+[hier (Englisch)](https://wiki.ootrandomizer.com/index.php?title=Logic) gefunden werden.
+
+## Welche Items und Bereiche werden gemischt?
+
+Alle ausrüstbare und sammelbare Gegenstände, sowie Munition können gemischt werden. Und alle Bereiche, die einen
+dieser Items enthalten könnten, haben (sehr wahrscheinlich) ihren Inhalt verändert. Goldene Skulltulas können ebenfalls
+dazugezählt werden, je nach Wunsch des Spielers.
+
+## Welche Items können in sich in der Welt eines anderen Spielers befinden?
+
+Jedes dieser Items, die gemicht werden können, können in einer Multiworld auch in der Welt eines anderen Spielers
+fallen. Es ist jedoch möglich ausgewählte Items auf deine eigene Welt zu beschränken.
+
+## Wie sieht ein Item einer anderen Welt in OoT aus?
+
+Items, die zu einer anderen Welt gehören, werden repräsentiert durch Zelda's Brief.
+
+## Was passiert, wenn der Spieler ein Item erhält?
+
+Sobald der Spieler ein Item erhält, wird Link das Item über seinen Kopf halten und der ganzen Welt präsentieren.
+Gut für's Geschäft!
+
+## Einzigartige Lokale Befehle
+
+Die folgenden Befehle stehen nur im OoTClient, um mit Archipelago zu spielen, zur Verfügung:
+
+- `/n64` Überprüffe den Verbindungsstatus deiner N64
+- `/deathlink` Schalte den "Deathlink" des Clients um. Überschreibt die zuvor konfigurierten Einstellungen.
diff --git a/worlds/oot/docs/en_Ocarina of Time.md b/worlds/oot/docs/en_Ocarina of Time.md
index fa8e148957c7..5a480d864124 100644
--- a/worlds/oot/docs/en_Ocarina of Time.md
+++ b/worlds/oot/docs/en_Ocarina of Time.md
@@ -1,8 +1,8 @@
# Ocarina of Time
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
@@ -37,4 +37,4 @@ business!
The following commands are only available when using the OoTClient to play with Archipelago.
- `/n64` Check N64 Connection State
-- `/deathlink` Toggle deathlink from client. Overrides default setting.
+- `/deathlink` Toggle deathlink from client. Overrides default option.
diff --git a/worlds/oot/docs/setup_de.md b/worlds/oot/docs/setup_de.md
new file mode 100644
index 000000000000..92c3150a7d2f
--- /dev/null
+++ b/worlds/oot/docs/setup_de.md
@@ -0,0 +1,108 @@
+# Setup Anleitung für Ocarina of Time: Archipelago Edition
+
+## WICHTIG
+
+Da wir BizHawk benutzen, gilt diese Anleitung nur für Windows und Linux.
+
+## Benötigte Software
+
+- BizHawk: [BizHawk Veröffentlichungen von TASVideos](https://tasvideos.org/BizHawk/ReleaseHistory)
+ - Version 2.3.1 und später werden unterstützt. Version 2.9 ist empfohlen.
+ - Detailierte Installtionsanweisungen für BizHawk können über den obrigen Link gefunden werden.
+ - Windows-Benutzer müssen die Prerequisiten installiert haben. Diese können ebenfalls über
+ den obrigen Link gefunden werden.
+- Der integrierte Archipelago-Client, welcher [hier](https://github.com/ArchipelagoMW/Archipelago/releases) installiert
+ werden kann.
+- Eine `Ocarina of Time v1.0 US(?) ROM`. (Nicht aus Europa und keine Master-Quest oder Debug-Rom!)
+
+## Konfigurieren von BizHawk
+
+Sobald Bizhawk einmal installiert wurde, öffne **EmuHawk** und ändere die folgenen Einsteluungen:
+
+- (≤ 2.8) Gehe zu `Config > Customize`. Wechlse zu dem `Advanced`-Reiter, wechsle dann den `Lua Core` von "NLua+KopiLua" zu
+ `"Lua+LuaInterface"`. Starte danach EmuHawk neu. Dies ist zwingend notwendig, damit die Lua-Scripts, mit denen man sich mit dem Client verbindet, ordnungsgemäß funktionieren.
+ **ANMERKUNG: Selbst wenn "Lua+LuaInterface" bereits ausgewählt ist, wechsle zwischen den beiden Optionen umher und**
+ **wähle es erneut aus. Neue Installationen oder Versionen von EmuHawk neigen dazu "Lua+LuaInterface" als die**
+ **Standard-Option anzuzeigen, aber laden dennoch "NLua+KopiLua", bis dieser Schritt getan ist.**
+- Unter `Config > Customize > Advanced`, gehe sicher dass der Haken bei `AutoSaveRAM` ausgeählt ist, und klicke dann
+ den 5s-Knopf. Dies verringert die Wahrscheinlichkeit den Speicherfrotschritt zu verlieren, sollte der Emulator mal
+ abstürzen.
+- **(Optional)** Unter `Config > Customize` kannst du die Haken in den "Run in background"
+ (Laufe weiter im Hintergrund) und "Accept background input" (akzeptiere Tastendruck im Hintergrund) Kästchen setzen.
+ Dies erlaubt dir das Spiel im Hintergrund weiter zu spielen, selbst wenn ein anderes Fenster aktiv ist. (Nützlich bei
+ mehreren oder eher großen Bildschrimen/Monitoren.)
+- Unter `Config > Hotkeys` sind viele Hotkeys, die mit oft genuten Tasten belegt worden sind. Es wird empfohlen die
+ meisten (oder alle) Hotkeys zu deaktivieren. Dies kann schnell mit `Esc` erledigt werden.
+- Wird mit einem Kontroller gespielt, bei der Tastenbelegung (bei einem Laufendem Spiel, unter
+ `Config > Controllers...`), deaktiviere "P1 A Up", "P1 A Down", "P1 A Left", and "P1 A Right" und gehe stattdessen in
+ den Reiter `Analog Controls` um den Stick zu belegen, da sonst Probleme beim Zielen auftreten (mit dem Bogen oder
+ ähnliches). Y-Axis ist für Oben und Unten, und die X-Axis ist für Links und Rechts.
+- Unter `N64` setze einen Haken bei "Use Expansion Slot" (Benutze Erweiterungs-Slot). Dies wird benötigt damit
+ savestates/schnellspeichern funktioniert. (Das N64-Menü taucht nur **nach** dem laden einer N64-ROM auf.)
+
+Es wird sehr empfohlen N64 Rom-Erweiterungen (\*.n64, \*.z64) mit dem Emuhawk - welcher zuvor installiert wurde - zu
+verknüpfen.
+Um dies zu tun, muss eine beliebige N64 Rom aufgefunden werden, welche in deinem Besitz ist, diese Rechtsklicken und
+dann auf "Öffnen mit..." gehen. Gehe dann auf "Andere App auswählen" und suche nach deinen BizHawk-Ordner, in der
+sich der Emulator befindet, und wähle dann `EmuHawk.exe` **(NICHT "DiscoHawk.exe"!)** aus.
+
+Eine Alternative BizHawk Setup Anleitung (auf Englisch), sowie weitere Hilfe bei Problemen kann
+[hier](https://wiki.ootrandomizer.com/index.php?title=Bizhawk) gefunden werden.
+
+## Erstelle eine YAML-Datei
+
+### Was ist eine YAML-Datei und Warum brauch ich eine?
+
+Eine YAML-Datie enthält einen Satz an einstellbaren Optionen, die dem Generator mitteilen, wie
+dein Spiel generiert werden soll. In einer Multiworld stellt jeder Spieler eine eigene YAML-Datei zur Verfügung. Dies
+erlaubt jeden Spieler eine personalisierte Erfahrung nach derem Geschmack. Damit kann auch jeder Spieler in einer
+Multiworld (des gleichen Spiels) völlig unterschiedliche Einstellungen haben.
+
+Für weitere Informationen, besuche die allgemeine Anleitung zum Erstellen einer
+YAML-Datei: [Archipelago Setup Anleitung](/tutorial/Archipelago/setup/en)
+
+### Woher bekomme ich eine YAML-Datei?
+
+Die Seite für die Spielereinstellungen auf dieser Website erlaubt es dir deine persönlichen Einstellungen nach
+vorlieben zu konfigurieren und eine YAML-Datei zu exportieren.
+Seite für die Spielereinstellungen:
+[Seite für die Spielereinstellungen von Ocarina of Time](/games/Ocarina%20of%20Time/player-options)
+
+### Überprüfen deiner YAML-Datei
+
+Wenn du deine YAML-Datei überprüfen möchtest, um sicher zu gehen, dass sie funktioniert, kannst du dies auf der
+YAML-Überprüfungsseite tun.
+YAML-Überprüfungsseite: [YAML-Überprüfungsseite](/check)
+
+## Beitreten einer Multiworld
+
+### Erhalte deinen OoT-Patch
+
+(Der folgende Prozess ist bei den meisten ROM-basierenden Spielen sehr ähnlich.)
+
+Wenn du einer Multiworld beitrittst, wirst du gefordert eine YAML-Datei bei dem Host abzugeben. Ist dies getan,
+erhälst du (in der Regel) einen Link vom Host der Multiworld. Dieser führt dich zu einem Raum, in dem alle
+teilnehmenden Spieler (bzw. Welten) aufgelistet sind. Du solltest dich dann auf **deine** Welt konzentrieren
+und klicke dann auf `Download APZ5 File...`.
+![Screenshot of a Multiworld Room with an Ocarina of Time Player](/static/generated/docs/Ocarina%20of%20Time/MultiWorld-room_oot.png)
+
+Führe die `.apz5`-Datei mit einem Doppelklick aus, um deinen Ocarina Of Time-Client zu starten, sowie das patchen
+deiner ROM. Ist dieser Prozess fertig (kann etwas dauern), startet sich der Client und der Emulator automatisch
+(sofern das "Öffnen mit..." ausgewählt wurde).
+
+### Verbinde zum Multiserver
+
+Sind einmal der Client und der Emulator gestartet, müssen sie nur noch miteinander verbunden werden. Gehe dazu in
+deinen Archipelago-Ordner, dann zu `data/lua`, und füge das `connector_oot.lua` Script per Drag&Drop (ziehen und
+fallen lassen) auf das EmuHawk-Fenster. (Alternativ kannst du die Lua-Konsole manuell öffnen, gehe dazu auf
+`Script > Open Script` und durchsuche die Ordner nach `data/lua/connector_oot.lua`.)
+
+Um den Client mit dem Multiserver zu verbinden, füge einfach `:` in das Textfeld ganz oben im
+Client ein und drücke Enter oder "Connect" (verbinden). Wird ein Passwort benötigt, musst du es danach unten in das
+Textfeld (für den Chat und Befehle) eingeben.
+Alternativ kannst du auch in dem unterem Textfeld den folgenden Befehl schreiben:
+`/connect : [Passwort]` (wie die Adresse und der Port lautet steht in dem Raum, oder wird von deinem
+Host an dich weitergegeben.)
+Beispiel: `/connect archipelago.gg:12345 Passw123`
+
+Du bist nun bereit für dein Zeitreise-Abenteuer in Hyrule.
diff --git a/worlds/oot/docs/setup_en.md b/worlds/oot/docs/setup_en.md
index 4d27019fa771..553f1820c3ea 100644
--- a/worlds/oot/docs/setup_en.md
+++ b/worlds/oot/docs/setup_en.md
@@ -50,8 +50,8 @@ guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
### Where do I get a config file?
-The Player Settings page on the website allows you to configure your personal settings and export a config file from
-them. Player settings page: [Ocarina of Time Player Settings Page](/games/Ocarina%20of%20Time/player-settings)
+The Player Options page on the website allows you to configure your personal options and export a config file from
+them. Player options page: [Ocarina of Time Player Options Page](/games/Ocarina%20of%20Time/player-options)
### Verifying your config file
diff --git a/worlds/overcooked2/__init__.py b/worlds/overcooked2/__init__.py
index 0451f32bdd49..da0e1890894a 100644
--- a/worlds/overcooked2/__init__.py
+++ b/worlds/overcooked2/__init__.py
@@ -16,7 +16,7 @@ class Overcooked2Web(WebWorld):
bug_report_page = "https://github.com/toasterparty/oc2-modding/issues"
setup_en = Tutorial(
- "Multiworld Setup Tutorial",
+ "Multiworld Setup Guide",
"A guide to setting up the Overcooked! 2 randomizer on your computer.",
"English",
"setup_en.md",
@@ -90,13 +90,7 @@ def add_region(self, region_name: str):
def connect_regions(self, source: str, target: str, rule: Optional[Callable[[CollectionState], bool]] = None):
sourceRegion = self.multiworld.get_region(source, self.player)
targetRegion = self.multiworld.get_region(target, self.player)
-
- connection = Entrance(self.player, '', sourceRegion)
- if rule:
- connection.access_rule = rule
-
- sourceRegion.exits.append(connection)
- connection.connect(targetRegion)
+ sourceRegion.connect(targetRegion, rule=rule)
def add_level_location(
self,
diff --git a/worlds/overcooked2/docs/en_Overcooked! 2.md b/worlds/overcooked2/docs/en_Overcooked! 2.md
index 298c33683ce7..d4cb6fba1f9a 100644
--- a/worlds/overcooked2/docs/en_Overcooked! 2.md
+++ b/worlds/overcooked2/docs/en_Overcooked! 2.md
@@ -2,7 +2,7 @@
## Quick Links
- [Setup Guide](../../../../tutorial/Overcooked!%202/setup/en)
-- [Settings Page](../../../../games/Overcooked!%202/player-settings)
+- [Options Page](../../../../games/Overcooked!%202/player-options)
- [OC2-Modding GitHub](https://github.com/toasterparty/oc2-modding)
## How Does Randomizer Work in the Kitchen?
@@ -55,7 +55,7 @@ The following items were invented for Randomizer:
- Ramp Buttons (x7)
- Bonus Star (Filler Item*)
-**Note: Bonus star count varies with settings*
+**Note: Bonus star count varies with options*
## Other Game Modifications
diff --git a/worlds/overcooked2/docs/setup_en.md b/worlds/overcooked2/docs/setup_en.md
index 1b21642cfe03..9f9eae5fc1ae 100644
--- a/worlds/overcooked2/docs/setup_en.md
+++ b/worlds/overcooked2/docs/setup_en.md
@@ -2,7 +2,7 @@
## Quick Links
- [Main Page](../../../../games/Overcooked!%202/info/en)
-- [Settings Page](../../../../games/Overcooked!%202/player-settings)
+- [Options Page](../../../../games/Overcooked!%202/player-options)
- [OC2-Modding GitHub](https://github.com/toasterparty/oc2-modding)
## Required Software
@@ -49,9 +49,9 @@ To completely remove *OC2-Modding*, navigate to your game's installation folder
## Generate a MultiWorld Game
-1. Visit the [Player Settings](../../../../games/Overcooked!%202/player-settings) page and configure the game-specific settings to taste
+1. Visit the [Player Options](../../../../games/Overcooked!%202/player-options) page and configure the game-specific options to taste
-*By default, these settings will only use levels from the base game and the "Seasonal" free DLC updates. If you own any of the paid DLC, you may select individual DLC packs to include/exclude on the [Weighted Settings](../../../../weighted-settings) page*
+*By default, these options will only use levels from the base game and the "Seasonal" free DLC updates. If you own any of the paid DLC, you may select individual DLC packs to include/exclude on the [Weighted Options](../../../../weighted-options) page*
2. Export your yaml file and use it to generate a new randomized game
@@ -84,11 +84,11 @@ To completely remove *OC2-Modding*, navigate to your game's installation folder
Since the goal of randomizer isn't necessarily to achieve new personal high scores, players may find themselves waiting for a level timer to expire once they've met their objective. A new feature called *Auto-Complete* has been added to automatically complete levels once a target star count has been achieved.
-To enable *Auto-Complete*, press the **Show** button near the top of your screen to expand the modding controls. Then, repeatedly press the **Auto-Complete** button until it shows the desired setting.
+To enable *Auto-Complete*, press the **Show** button near the top of your screen to expand the modding controls. Then, repeatedly press the **Auto-Complete** button until it shows the desired option.
## Overworld Sequence Breaking
-In the world's settings, there is an option called "Overworld Tricks" which allows the generator to make games which require doing tricks with the food truck to complete. This includes:
+In the world's options, there is an option called "Overworld Tricks" which allows the generator to make games which require doing tricks with the food truck to complete. This includes:
- Dashing across gaps
diff --git a/worlds/pokemon_emerald/CHANGELOG.md b/worlds/pokemon_emerald/CHANGELOG.md
new file mode 100644
index 000000000000..dbc1123b7719
--- /dev/null
+++ b/worlds/pokemon_emerald/CHANGELOG.md
@@ -0,0 +1,186 @@
+# 2.0.0
+
+### Features
+- Picking up items for other players will display the actual item name and receiving player in-game instead of
+"ARCHIPELAGO ITEM". (This does have a limit, but you're unlikely to reach it in all but the largest multiworlds.)
+- New goal `legendary_hunt`. Your goal is to catch/defeat some number of legendary encounters. That is, the static
+encounters themselves, whatever species they may be. Legendary species found in the wild don't count.
+ - You can force the goal to require captures with `legendary_hunt_catch`. If you accidentally faint a legendary, you
+ can respawn it by beating the champion.
+ - The number of legendaries needed is controlled by the `legendary_hunt_count` option.
+ - The caves containing Kyogre and Groudon are fixed to one location per seed. You need to go to the weather
+ institute to trigger a permanent weather event at the corresponding locations. Only one weather event can be active
+ at a time.
+ - The move tutor for the move Sleep Talk has been changed to Dig and is unlimited use (for Sealed Chamber).
+ - Relicanth and Wailord are guaranteed to be reachable in the wild (for Sealed Chamber). Interacting with the Sealed
+ Chamber wall will give you dex info for Wailord and Relicanth.
+ - Event legendaries are included for this goal (see below for new ferry behavior and event tickets).
+ - The roamer is included in this count. It will _always_ be Latios no matter what your options are. Otherwise you
+ might not have any way of knowing which species is roaming to be able to track it. In legendary hunt, Latios will
+ never appear as a wild pokemon to make tracking it easier. The television broadcast that creates the roamer will
+ give you dex info for Latios.
+ - You can set which encounters are considered for this goal with the `allowed_legendary_hunt_encounters` option.
+- New option `dexsanity`. Adds pokedex entries as locations.
+ - Added locations contribute either a Poke Ball, Great Ball, or Ultra Ball to the item pool, based on the evolution
+ stage.
+ - Logic uses only wild encounters for now.
+ - Defeating a gym leader awards "seen" info on 1/8th of the pokedex.
+- New option `trainersanity`. Defeating a trainer awards a random item.
+ - Trainers no longer award money upon defeat. Instead they add a sellable item to the item pool.
+ - Missable trainers are prevented from disappearing when this is enabled.
+ - Gym trainers remain active after their leader is defeated.
+ - Does not include trainers in the Trick House.
+- New option `berry_trees`. Adds berry trees as locations.
+ - All soil patches start with a fully grown berry tree that gives one item.
+ - There are 88 berry trees.
+ - Berries cannot be planted in soil with this option enabled.
+ - Soil that doesn't start with a tree on a fresh save contributes a Sitrus Berry to the item pool.
+- New option `death_link`. Forgive me, Figment.
+- Added Artisan Cave locations
+ - Requires Wailmer Pail and the ability to Surf to access.
+- Added Trick House locations. The Trick Master is finally here!
+ - He will make new layouts only if you have the corresponding badge (or beat the game) and have completed the
+ previous layout (all vanilla behavior).
+ - If you neglect to pick up an item in a puzzle before completing it, the Trick Master will give the item to you
+ alongside the prize.
+ - Locations are enabled or disabled with their broader categories (npc gifts, overworld items, etc...)
+- Added daily berry gift locations. There are a half dozen or so NPCs that give you one or two berries per day.
+ - All these locations are considered NPC gifts.
+ - The NPCs have been reworked to give this gift once permanently so they can be added as locations.
+- New option `remote_items`. All randomized items are sent from the server instead of being patched into your game
+(except for start inventory, which remains in the PC)
+ - As a side effect, when you pick up your own item, there will be a gap between the item disappearing from the
+ overworld and your game actually receiving it. It also causes gifts from NPCs which contain your own items to not
+ show up until after their text box closes. It can feel odd, but there should be no danger to it.
+ - If the seed is in race mode, this is forcibly enabled.
+ - Benefits include:
+ - Two players can play the same slot and both receive items that slot picks up for itself (as long as it was
+ randomized)
+ - You receive items you picked up for yourself if you lose progress on your save
+ - Competitive integrity; the patch file no longer has any knowledge of item placement
+- New option `match_trainer_levels`. This is a sort of pseudo level cap for a randomizer context.
+ - When you start a trainer fight, all your pokemon have their levels temporarily set to the highest level in the
+ opponent's party.
+ - During the battle, all earned exp is set to 0 (EVs are still gained during battle as normal). When the outcome of
+ the battle is decided, your pokemon have their levels reset to what they were before the fight and exp is awarded as
+ it would have been without this option. Think of it as holding earned exp in reserve and awarding it at the end
+ instead, even giving it to fainted pokemon if they earned any before fainting.
+ - Exp gain is based on _your_ party's average level to moderate exp over the course of a seed. Wild battles are
+ entirely unchanged by this option.
+- New option `match_trainer_levels_bonus`. A flat bonus to apply to your party's levels when using
+`match_trainer_levels`. In case you want to give yourself a nerf or buff while still approximately matching your
+opponent.
+- New option `force_fully_evolved`. Define a level at which trainers will stop using pokemon that have further evolution
+stages.
+- New option `move_blacklist`. Define a list of moves that should not be given randomly to learnsets or TMs. Move names
+are accurate to Gen 3 except for capitalization.
+- New option `extra_bumpy_slope`. Adds a "bumpy slope" to Route 115 that lets you hop up the ledge with the Acro Bike.
+- New option `modify_118`. Changes Route 118 so that it must be crossed with the Acro Bike, and cannot be crossed by
+surfing.
+- Changed `require_flash` option to a choice between none, only granite cave, only victory road, or both caves.
+- Removed `static_encounters` option.
+- New option `legendary_encounters`. Replaces `static_encounters`, but only concerns legendaries.
+- New option `misc_pokemon`. Replaces `static_encounters`, but only concerns non-legendaries.
+- Removed `fly_without_badge` option. (Don't worry)
+- New option `hm_requirements`. Will eventually be able to give you more control over the badge requirements for all
+HMs. For now, only includes the presets `vanilla` and `fly_without_badge`.
+- Removed `allow_wild_legendaries`, `allow_starter_legendaries`, and `allow_trainer_legendaries` options.
+- New options `wild_encounter_blacklist`, `starter_blacklist`, and `trainer_party_blacklist`.
+ - These take lists of species and prevent them from randomizing into the corresponding categories
+ - If adhering to your blacklist would make it impossible to choose a random species, your blacklist is ignored in
+ that case
+ - All three include a shorthand for excluding legendaries
+- Removed `enable_ferry` option.
+ - The ferry is now always present.
+ - The S.S. Ticket item/location is now part of `key_items`.
+- Added event tickets and islands.
+ - All event tickets are given to the player by Norman after defeating the Champion alongside the S.S. Ticket.
+ - As in vanilla, these tickets are only usable from Lilycove. Not Slateport or the Battle Frontier.
+- New option `event_tickets`. Randomizes the above-mentioned tickets into the item pool.
+- New option `enable_wonder_trading`. You can participate in Wonder Trading by interacting with the center receptionist
+on the second floor of Pokemon Centers.
+ - Why is this an option instead of just being enabled? You might want to disable wonder trading in a meta yaml to
+ make sure certain rules can't be broken. Or you may want to turn it off for yourself to definitively prevent being
+ asked for help if you prefer to keep certain walls up between your game and others. Trades _do_ include items and
+ known moves, which means there is potential for an extra level of cooperation and even ways to go out of logic. But
+ that's not a boundary everyone wants broken down all the time. Please be respectful of someone's choice to not
+ participate if that's their preference.
+ - A lot of time was spent trying to make this all work without having to touch your client. Hopefully it goes
+ smoothly, but there's room for jank. Anything you decide to give to her you should consider gone forever, whether
+ because it was traded away or because something "went wrong in transit" and the pokemon's data got lost after being
+ removed from the server.
+ - Wonder Trading is _not_ resistant to save scumming in either direction. You _could_ abuse it to dupe pokemon,
+ because there's not realistically a way for me to prevent it, but I'd urge you to stick to the spirit of the design
+ unless everyone involved doesn't mind.
+ - The wonder trades you receive are stored in your save data even before you pick them up, so if you save after the
+ client tells you that you received a wonder trade, it's safe. You don't need to retrieve it from a poke center for
+ it to persist. However, if you reset your game to a point in time before your client popped the "Wonder trade
+ received" message, that pokemon is lost forever.
+- New `easter_egg` passphrase system.
+ - All valid easter egg passphrases will be a phrase that it's possible to submit as a trendy phrase in Dewford Town.
+ Changing the trendy phrase does ***not*** trigger easter eggs. Only the phrase you put in your YAML can trigger an
+ easter egg.
+ - There may be other ways to learn more information.
+ - Phrases are case insensitive. Here are a couple examples of possible phrases: `"GET FOE"`,
+ `"HERE GOES GRANDMOTHER"`, `"late eh?"` (None of those do anything, but I'd love to hear what you think they would.)
+- Added three new easter egg effects.
+- Changed the original easter egg phrase to use the new system.
+- Renamed `tm_moves` to `tm_tutor_moves`. Move tutors are also affected by this option (except the new Dig tutor).
+- Renamed `tm_compatibility` to `tm_tutor_compatibility`. Move tutors are also affected by this option.
+- Changed `tm_tutor_compatibility` to be a percent chance instead of a choice. Use `-1` for vanilla.
+- Changed `hm_compatibility` to be a percent chance instead of a choice. Use `-1` for vanilla.
+- New option `music`. Shuffles all looping music. Includes FRLG tracks and possibly some unused stuff.
+- New option `fanfares`. Shuffles all fanfares. Includes FRLG tracks. When this is enabled, pressing B will interrupt
+most fanfares.
+- New option `purge_spinners`. Trainers that change which direction they face will do so predictably, and will no longer
+turn to face you when you run.
+- New option `normalize_encounter_rates`. Sets every encounter slot to (almost) equal probability. Does NOT make every
+species equally likely to appear, but makes rare encounters less rare.
+- Added `Trick House` location group.
+- Removed `Postgame Locations` location group.
+
+### QoL
+
+- Can teach moves over HM moves.
+- Fishing is much less random; pokemon will always bite if there's an encounter there.
+- Mirage Island is now always present.
+- Waking Rayquaza is no longer required. After releasing Kyogre, going to Sootopolis will immediately trigger the
+Rayquaza cutscene.
+- Renamed some locations to be more accurate.
+- Most trainers will no longer ask to be registered in your Pokegear after battle. Also removed most step-based match
+calls.
+- Removed a ledge on Route 123. With careful routing, it's now possible to check every location without having to save
+scum or go back around.
+- Added "GO HOME" button on the start menu where "EXIT" used to be. Will teleport you to Littleroot.
+- Some locations which are directly blocked by completing your goal are automatically excluded.
+ - For example, the S.S. Ticket and a Champion goal, or the Sludge Bomb TM and the Norman goal.
+ - Your particular options might still result in locations that can't be reached until after your goal. For example,
+ setting a Norman goal and setting your E4 requirement to 8 gyms means that post-Champion locations will not be
+ reachable before defeating Norman, but they are NOT excluded by this modification. That's one of the simpler
+ examples. It is extremely tedious to try to detect these sorts of situations, so I'll instead leave it to you to be
+ aware of your own options.
+- Species in the pokedex are searchable by type even if you haven't caught that species yet
+
+### Fixes
+
+- Mt. Pyre summit state no longer changes when you finish the Sootopolis events, which would lock you out of one or two
+locations.
+- Whiting out under certain conditions no longer softlocks you by moving Mr. Briney to an inaccessible area.
+- It's no longer possible to join a room using the wrong patch file, even if the slot names match.
+- NPCs now stop moving while you're receiving an item.
+- Creating a secret base no longer triggers sending the Secret Power TM location.
+- Hopefully fix bug where receiving an item while walking over a trigger can skip that trigger (the Moving
+Truck/Petalburg wrong warp)
+
+## Easter Eggs
+
+There are plenty among you who are capable of ~~cheating~~ finding information about the easter egg phrases by reading
+source code, writing brute force scripts, and inspecting memory for clues and answers. By all means, go ahead, that can
+be your version of this puzzle and I don't intend to stand in your way. **However**, I would ask that any information
+you come up with by doing this, you keep entirely to yourself until the community as a whole has figured out what you
+know. There was not previously a way to reasonably learn about or make guesses at the easter egg, but that has changed.
+There are mechanisms by which solutions can be found or guessed over the course of multiple games by multiple people,
+and I'd rather the fun not get spoiled immediately.
+
+Once a solution has been found I'd _still_ prefer discussion about hints and effects remain behind spoiler tags just in
+case there are people who want to do the hunt on their own. Thank you all, and good luck.
diff --git a/worlds/pokemon_emerald/README.md b/worlds/pokemon_emerald/README.md
index 2c1e9e356046..8441afc56ae6 100644
--- a/worlds/pokemon_emerald/README.md
+++ b/worlds/pokemon_emerald/README.md
@@ -1,58 +1,3 @@
# Pokemon Emerald
-Version 1.2.1
-
-This README contains general info useful for understanding the world. Pretty much all the long lists of locations,
-regions, and items are stored in `data/` and (mostly) loaded in by `data.py`. Access rules are in `rules.py`. Check
-[data/README.md](data/README.md) for more detailed information on the JSON files holding most of the data.
-
-## Warps
-
-Quick note to start, you should not be defining or modifying encoded warps from this repository. They're encoded in the
-source code repository for the mod, and then assigned to regions in `data/regions/`. All warps in the game already exist
-within `extracted_data.json`, and all relevant warps are already placed in `data/regions/` (unless they were deleted
-accidentally).
-
-Many warps are actually two or three events acting as one logical warp. Doorways, for example, are often 2 tiles wide
-indoors but only 1 tile wide outdoors. Both indoor warps point to the outdoor warp, and the outdoor warp points to only
-one of the indoor warps. We want to describe warps logically in a way that retains information about individual warp
-events. That way a 2-tile-wide doorway doesnt look like a one-way warp next to an unrelated two-way warp, but if we want
-to randomize the destinations of those warps, we can still get back each individual id of the multi-tile warp.
-
-This is how warps are encoded:
-
-`{source_map}:{source_warp_ids}/{dest_map}:{dest_warp_ids}[!]`
-
-- `source_map`: The map the warp events are located in
-- `source_warp_ids`: The ids of all adjacent warp events in source_map which lead to the same destination (these must be
-in ascending order)
-- `dest_map`: The map of the warp event to which this one is connected
-- `dest_warp_ids`: The ids of the warp events in dest_map
-- `[!]`: If the warp expects to lead to a destination which doesnot lead back to it, add a ! to the end
-
-Example: `MAP_LAVARIDGE_TOWN_HOUSE:0,1/MAP_LAVARIDGE_TOWN:4`
-
-Example 2: `MAP_AQUA_HIDEOUT_B1F:14/MAP_AQUA_HIDEOUT_B1F:12!`
-
-Note: A warp must have its destination set to another warp event. However, that does not guarantee that the destination
-warp event will warp back to the source.
-
-Note 2: Some warps _only_ act as destinations and cannot actually be interacted with by the player as sources. These are
-usually places you fall from a hole above. At the time of writing, these are actually not accounted for, but there are
-no instances where it changes logical access.
-
-Note 3: Some warp destinations go to the map `MAP_DYNAMIC` and have a special warp id. These edge cases are:
-
-- The Moving Truck
-- Terra Cave
-- Marine Cave
-- The Department Store Elevator
-- Secret Bases
-- The Trade Center
-- The Union Room
-- The Record Corner
-- 2P/4P Battle Colosseum
-
-Note 4: The trick house on Route 110 changes the warp destinations of its entrance and ending room as you progress
-through the puzzles, but the source code only sets the trick house up for the first puzzle, and I assume the destination
-gets overwritten at run time when certain flags are set.
+Version 2.0.0
diff --git a/worlds/pokemon_emerald/__init__.py b/worlds/pokemon_emerald/__init__.py
index b7730fbdf785..c7f060a72969 100644
--- a/worlds/pokemon_emerald/__init__.py
+++ b/worlds/pokemon_emerald/__init__.py
@@ -5,30 +5,27 @@
import copy
import logging
import os
-from typing import Any, Set, List, Dict, Optional, Tuple, ClassVar
+from typing import Any, Set, List, Dict, Optional, Tuple, ClassVar, TextIO, Union
-from BaseClasses import ItemClassification, MultiWorld, Tutorial
+from BaseClasses import ItemClassification, MultiWorld, Tutorial, LocationProgressType
from Fill import FillError, fill_restrictive
from Options import Toggle
import settings
from worlds.AutoWorld import WebWorld, World
from .client import PokemonEmeraldClient # Unused, but required to register with BizHawkClient
-from .data import (SpeciesData, MapData, EncounterTableData, LearnsetMove, TrainerPokemonData, StaticEncounterData,
- TrainerData, data as emerald_data)
+from .data import LEGENDARY_POKEMON, MapData, SpeciesData, TrainerData, data as emerald_data
from .items import (ITEM_GROUPS, PokemonEmeraldItem, create_item_label_to_code_map, get_item_classification,
offset_item_value)
from .locations import (LOCATION_GROUPS, PokemonEmeraldLocation, create_location_label_to_id_map,
- create_locations_with_tags)
-from .options import (ItemPoolType, RandomizeWildPokemon, RandomizeBadges, RandomizeTrainerParties, RandomizeHms,
- RandomizeStarters, LevelUpMoves, RandomizeAbilities, RandomizeTypes, TmCompatibility,
- HmCompatibility, RandomizeStaticEncounters, NormanRequirement, PokemonEmeraldOptions)
-from .pokemon import get_random_species, get_random_move, get_random_damaging_move, get_random_type
-from .regions import create_regions
-from .rom import PokemonEmeraldDeltaPatch, generate_output, location_visited_event_to_id_map
-from .rules import set_rules
-from .sanity_check import validate_regions
-from .util import int_to_bool_array, bool_array_to_int
+ create_locations_with_tags, set_free_fly, set_legendary_cave_entrances)
+from .opponents import randomize_opponent_parties
+from .options import (Goal, DarkCavesRequireFlash, HmRequirements, ItemPoolType, PokemonEmeraldOptions,
+ RandomizeWildPokemon, RandomizeBadges, RandomizeHms, NormanRequirement)
+from .pokemon import (get_random_move, get_species_id_by_label, randomize_abilities, randomize_learnsets,
+ randomize_legendary_encounters, randomize_misc_pokemon, randomize_starters,
+ randomize_tm_hm_compatibility,randomize_types, randomize_wild_encounters)
+from .rom import PokemonEmeraldDeltaPatch, create_patch
class PokemonEmeraldWebWorld(WebWorld):
@@ -36,6 +33,7 @@ class PokemonEmeraldWebWorld(WebWorld):
Webhost info for Pokemon Emerald
"""
theme = "ocean"
+
setup_en = Tutorial(
"Multiworld Setup Guide",
"A guide to playing Pokémon Emerald with Archipelago.",
@@ -45,7 +43,16 @@ class PokemonEmeraldWebWorld(WebWorld):
["Zunawe"]
)
- tutorials = [setup_en]
+ setup_es = Tutorial(
+ "Guía de configuración para Multiworld",
+ "Una guía para jugar Pokémon Emerald en Archipelago",
+ "Español",
+ "setup_es.md",
+ "setup/es",
+ ["nachocua"]
+ )
+
+ tutorials = [setup_en, setup_es]
class PokemonEmeraldSettings(settings.Group):
@@ -79,22 +86,46 @@ class PokemonEmeraldWorld(World):
item_name_groups = ITEM_GROUPS
location_name_groups = LOCATION_GROUPS
- data_version = 1
- required_client_version = (0, 4, 3)
-
- badge_shuffle_info: Optional[List[Tuple[PokemonEmeraldLocation, PokemonEmeraldItem]]] = None
- hm_shuffle_info: Optional[List[Tuple[PokemonEmeraldLocation, PokemonEmeraldItem]]] = None
- free_fly_location_id: int = 0
-
- modified_species: List[Optional[SpeciesData]]
- modified_maps: List[MapData]
+ data_version = 2
+ required_client_version = (0, 4, 5)
+
+ badge_shuffle_info: Optional[List[Tuple[PokemonEmeraldLocation, PokemonEmeraldItem]]]
+ hm_shuffle_info: Optional[List[Tuple[PokemonEmeraldLocation, PokemonEmeraldItem]]]
+ free_fly_location_id: int
+ blacklisted_moves: Set[int]
+ blacklisted_wilds: Set[int]
+ blacklisted_starters: Set[int]
+ blacklisted_opponent_pokemon: Set[int]
+ hm_requirements: Dict[str, Union[int, List[str]]]
+ auth: bytes
+
+ modified_species: Dict[int, SpeciesData]
+ modified_maps: Dict[str, MapData]
modified_tmhm_moves: List[int]
- modified_static_encounters: List[int]
+ modified_legendary_encounters: List[int]
modified_starters: Tuple[int, int, int]
modified_trainers: List[TrainerData]
+ def __init__(self, multiworld, player):
+ super(PokemonEmeraldWorld, self).__init__(multiworld, player)
+ self.badge_shuffle_info = None
+ self.hm_shuffle_info = None
+ self.free_fly_location_id = 0
+ self.blacklisted_moves = set()
+ self.blacklisted_wilds = set()
+ self.blacklisted_starters = set()
+ self.blacklisted_opponent_pokemon = set()
+ self.modified_maps = copy.deepcopy(emerald_data.maps)
+ self.modified_species = copy.deepcopy(emerald_data.species)
+ self.modified_tmhm_moves = []
+ self.modified_starters = emerald_data.starters
+ self.modified_trainers = []
+ self.modified_legendary_encounters = []
+
@classmethod
def stage_assert_generate(cls, multiworld: MultiWorld) -> None:
+ from .sanity_check import validate_regions
+
if not os.path.exists(cls.settings.rom_file):
raise FileNotFoundError(cls.settings.rom_file)
@@ -104,15 +135,83 @@ def get_filler_item_name(self) -> str:
return "Great Ball"
def generate_early(self) -> None:
- # If badges or HMs are vanilla, Norman locks you from using Surf, which means you're not guaranteed to be
- # able to reach Fortree Gym, Mossdeep Gym, or Sootopolis Gym. So we can't require reaching those gyms to
- # challenge Norman or it creates a circular dependency.
- # This is never a problem for completely random badges/hms because the algo will not place Surf/Balance Badge
- # on Norman on its own. It's never a problem for shuffled badges/hms because there is no scenario where Cut or
- # the Stone Badge can be a lynchpin for access to any gyms, so they can always be put on Norman in a worst case
- # scenario.
- # This will also be a problem in warp rando if direct access to Norman's room requires Surf or if access
- # any gym leader in general requires Surf. We will probably have to force this to 0 in that case.
+ self.hm_requirements = {
+ "HM01 Cut": ["Stone Badge"],
+ "HM02 Fly": ["Feather Badge"],
+ "HM03 Surf": ["Balance Badge"],
+ "HM04 Strength": ["Heat Badge"],
+ "HM05 Flash": ["Knuckle Badge"],
+ "HM06 Rock Smash": ["Dynamo Badge"],
+ "HM07 Waterfall": ["Rain Badge"],
+ "HM08 Dive": ["Mind Badge"],
+ }
+ if self.options.hm_requirements == HmRequirements.option_fly_without_badge:
+ self.hm_requirements["HM02 Fly"] = 0
+
+ self.blacklisted_moves = {emerald_data.move_labels[label] for label in self.options.move_blacklist.value}
+
+ self.blacklisted_wilds = {
+ get_species_id_by_label(species_name)
+ for species_name in self.options.wild_encounter_blacklist.value
+ if species_name != "_Legendaries"
+ }
+ if "_Legendaries" in self.options.wild_encounter_blacklist.value:
+ self.blacklisted_wilds |= LEGENDARY_POKEMON
+
+ self.blacklisted_starters = {
+ get_species_id_by_label(species_name)
+ for species_name in self.options.starter_blacklist.value
+ if species_name != "_Legendaries"
+ }
+ if "_Legendaries" in self.options.starter_blacklist.value:
+ self.blacklisted_starters |= LEGENDARY_POKEMON
+
+ self.blacklisted_opponent_pokemon = {
+ get_species_id_by_label(species_name)
+ for species_name in self.options.trainer_party_blacklist.value
+ if species_name != "_Legendaries"
+ }
+ if "_Legendaries" in self.options.starter_blacklist.value:
+ self.blacklisted_opponent_pokemon |= LEGENDARY_POKEMON
+
+ # In race mode we don't patch any item location information into the ROM
+ if self.multiworld.is_race and not self.options.remote_items:
+ logging.warning("Pokemon Emerald: Forcing Player %s (%s) to use remote items due to race mode.",
+ self.player, self.multiworld.player_name[self.player])
+ self.options.remote_items.value = Toggle.option_true
+
+ if self.options.goal == Goal.option_legendary_hunt:
+ # Prevent turning off all legendary encounters
+ if len(self.options.allowed_legendary_hunt_encounters.value) == 0:
+ raise ValueError(f"Pokemon Emerald: Player {self.player} ({self.multiworld.player_name[self.player]}) "
+ "needs to allow at least one legendary encounter when goal is legendary hunt.")
+
+ # Prevent setting the number of required legendaries higher than the number of enabled legendaries
+ if self.options.legendary_hunt_count.value > len(self.options.allowed_legendary_hunt_encounters.value):
+ logging.warning("Pokemon Emerald: Legendary hunt count for Player %s (%s) higher than number of allowed "
+ "legendary encounters. Reducing to number of allowed encounters.", self.player,
+ self.multiworld.player_name[self.player])
+ self.options.legendary_hunt_count.value = len(self.options.allowed_legendary_hunt_encounters.value)
+
+ # Require random wild encounters if dexsanity is enabled
+ if self.options.dexsanity and self.options.wild_pokemon == RandomizeWildPokemon.option_vanilla:
+ raise ValueError(f"Pokemon Emerald: Player {self.player} ({self.multiworld.player_name[self.player]}) must "
+ "not leave wild encounters vanilla if enabling dexsanity.")
+
+ # If badges or HMs are vanilla, Norman locks you from using Surf,
+ # which means you're not guaranteed to be able to reach Fortree Gym,
+ # Mossdeep Gym, or Sootopolis Gym. So we can't require reaching those
+ # gyms to challenge Norman or it creates a circular dependency.
+ #
+ # This is never a problem for completely random badges/hms because the
+ # algo will not place Surf/Balance Badge on Norman on its own. It's
+ # never a problem for shuffled badges/hms because there is no scenario
+ # where Cut or the Stone Badge can be a lynchpin for access to any gyms,
+ # so they can always be put on Norman in a worst case scenario.
+ #
+ # This will also be a problem in warp rando if direct access to Norman's
+ # room requires Surf or if access any gym leader in general requires
+ # Surf. We will probably have to force this to 0 in that case.
max_norman_count = 7
if self.options.badges == RandomizeBadges.option_vanilla:
@@ -131,21 +230,85 @@ def generate_early(self) -> None:
self.options.norman_count.value = max_norman_count
def create_regions(self) -> None:
+ from .regions import create_regions
regions = create_regions(self)
- tags = {"Badge", "HM", "KeyItem", "Rod", "Bike"}
+ tags = {"Badge", "HM", "KeyItem", "Rod", "Bike", "EventTicket"} # Tags with progression items always included
if self.options.overworld_items:
tags.add("OverworldItem")
if self.options.hidden_items:
tags.add("HiddenItem")
if self.options.npc_gifts:
tags.add("NpcGift")
- if self.options.enable_ferry:
- tags.add("Ferry")
+ if self.options.berry_trees:
+ tags.add("BerryTree")
+ if self.options.dexsanity:
+ tags.add("Pokedex")
+ if self.options.trainersanity:
+ tags.add("Trainer")
create_locations_with_tags(self, regions, tags)
self.multiworld.regions.extend(regions.values())
+ # Exclude locations which are always locked behind the player's goal
+ def exclude_locations(location_names: List[str]):
+ for location_name in location_names:
+ try:
+ self.multiworld.get_location(location_name,
+ self.player).progress_type = LocationProgressType.EXCLUDED
+ except KeyError:
+ continue # Location not in multiworld
+
+ if self.options.goal == Goal.option_champion:
+ # Always required to beat champion before receiving these
+ exclude_locations([
+ "Littleroot Town - S.S. Ticket from Norman",
+ "Littleroot Town - Aurora Ticket from Norman",
+ "Littleroot Town - Eon Ticket from Norman",
+ "Littleroot Town - Mystic Ticket from Norman",
+ "Littleroot Town - Old Sea Map from Norman",
+ "Ever Grande City - Champion Wallace",
+ "Meteor Falls 1F - Rival Steven",
+ "Trick House Puzzle 8 - Item",
+ ])
+
+ # Construction workers don't move until champion is defeated
+ if "Safari Zone Construction Workers" not in self.options.remove_roadblocks.value:
+ exclude_locations([
+ "Safari Zone NE - Hidden Item North",
+ "Safari Zone NE - Hidden Item East",
+ "Safari Zone NE - Item on Ledge",
+ "Safari Zone SE - Hidden Item in South Grass 1",
+ "Safari Zone SE - Hidden Item in South Grass 2",
+ "Safari Zone SE - Item in Grass",
+ ])
+ elif self.options.goal == Goal.option_steven:
+ exclude_locations([
+ "Meteor Falls 1F - Rival Steven",
+ ])
+ elif self.options.goal == Goal.option_norman:
+ # If the player sets their options such that Surf or the Balance
+ # Badge is vanilla, a very large number of locations become
+ # "post-Norman". Similarly, access to the E4 may require you to
+ # defeat Norman as an event or to get his badge, making postgame
+ # locations inaccessible. Detecting these situations isn't trivial
+ # and excluding all locations requiring Surf would be a bad idea.
+ # So for now we just won't touch it and blame the user for
+ # constructing their options in this way. Players usually expect
+ # to only partially complete their world when playing this goal
+ # anyway.
+
+ # Locations which are directly unlocked by defeating Norman.
+ exclude_locations([
+ "Petalburg Gym - Leader Norman",
+ "Petalburg Gym - Balance Badge",
+ "Petalburg Gym - TM42 from Norman",
+ "Petalburg City - HM03 from Wally's Uncle",
+ "Dewford Town - TM36 from Sludge Bomb Man",
+ "Mauville City - Basement Key from Wattson",
+ "Mauville City - TM24 from Wattson",
+ ])
+
def create_items(self) -> None:
item_locations: List[PokemonEmeraldLocation] = [
location
@@ -153,8 +316,9 @@ def create_items(self) -> None:
if location.address is not None
]
- # Filter progression items which shouldn't be shuffled into the itempool. Their locations
- # still exist, but event items will be placed and locked at their vanilla locations instead.
+ # Filter progression items which shouldn't be shuffled into the itempool.
+ # Their locations will still exist, but event items will be placed and
+ # locked at their vanilla locations instead.
filter_tags = set()
if not self.options.key_items:
@@ -163,12 +327,17 @@ def create_items(self) -> None:
filter_tags.add("Rod")
if not self.options.bikes:
filter_tags.add("Bike")
+ if not self.options.event_tickets:
+ filter_tags.add("EventTicket")
if self.options.badges in {RandomizeBadges.option_vanilla, RandomizeBadges.option_shuffle}:
filter_tags.add("Badge")
if self.options.hms in {RandomizeHms.option_vanilla, RandomizeHms.option_shuffle}:
filter_tags.add("HM")
+ # If Badges and HMs are set to the `shuffle` option, don't add them to
+ # the normal item pool, but do create their items and save them and
+ # their locations for use in `pre_fill` later.
if self.options.badges == RandomizeBadges.option_shuffle:
self.badge_shuffle_info = [
(location, self.create_item_by_code(location.default_item_code))
@@ -180,14 +349,18 @@ def create_items(self) -> None:
for location in [l for l in item_locations if "HM" in l.tags]
]
+ # Filter down locations to actual items that will be filled and create
+ # the itempool.
item_locations = [location for location in item_locations if len(filter_tags & location.tags) == 0]
default_itempool = [self.create_item_by_code(location.default_item_code) for location in item_locations]
+ # Take the itempool as-is
if self.options.item_pool_type == ItemPoolType.option_shuffled:
self.multiworld.itempool += default_itempool
- elif self.options.item_pool_type in {ItemPoolType.option_diverse, ItemPoolType.option_diverse_balanced}:
- item_categories = ["Ball", "Heal", "Vitamin", "EvoStone", "Money", "TM", "Held", "Misc"]
+ # Recreate the itempool from random items
+ elif self.options.item_pool_type in (ItemPoolType.option_diverse, ItemPoolType.option_diverse_balanced):
+ item_categories = ["Ball", "Heal", "Candy", "Vitamin", "EvoStone", "Money", "TM", "Held", "Misc", "Berry"]
# Count occurrences of types of vanilla items in pool
item_category_counter = Counter()
@@ -225,6 +398,7 @@ def create_items(self) -> None:
def refresh_tm_choices() -> None:
fill_item_candidates_by_category["TM"] = all_tm_choices.copy()
self.random.shuffle(fill_item_candidates_by_category["TM"])
+ refresh_tm_choices()
# Create items
for item in default_itempool:
@@ -241,40 +415,26 @@ def refresh_tm_choices() -> None:
self.multiworld.itempool.append(item)
def set_rules(self) -> None:
+ from .rules import set_rules
set_rules(self)
def generate_basic(self) -> None:
- locations: List[PokemonEmeraldLocation] = self.multiworld.get_locations(self.player)
-
- # Set our free fly location
- # If not enabled, set it to Littleroot Town by default
- fly_location_name = "EVENT_VISITED_LITTLEROOT_TOWN"
- if self.options.free_fly_location:
- fly_location_name = self.random.choice([
- "EVENT_VISITED_SLATEPORT_CITY",
- "EVENT_VISITED_MAUVILLE_CITY",
- "EVENT_VISITED_VERDANTURF_TOWN",
- "EVENT_VISITED_FALLARBOR_TOWN",
- "EVENT_VISITED_LAVARIDGE_TOWN",
- "EVENT_VISITED_FORTREE_CITY",
- "EVENT_VISITED_LILYCOVE_CITY",
- "EVENT_VISITED_MOSSDEEP_CITY",
- "EVENT_VISITED_SOOTOPOLIS_CITY",
- "EVENT_VISITED_EVER_GRANDE_CITY"
- ])
-
- self.free_fly_location_id = location_visited_event_to_id_map[fly_location_name]
+ # Create auth
+ # self.auth = self.random.randbytes(16) # Requires >=3.9
+ self.auth = self.random.getrandbits(16 * 8).to_bytes(16, "little")
- free_fly_location_location = self.multiworld.get_location("FREE_FLY_LOCATION", self.player)
- free_fly_location_location.item = None
- free_fly_location_location.place_locked_item(self.create_event(fly_location_name))
+ randomize_types(self)
+ randomize_wild_encounters(self)
+ set_free_fly(self)
+ set_legendary_cave_entrances(self)
# Key items which are considered in access rules but not randomized are converted to events and placed
# in their vanilla locations so that the player can have them in their inventory for logic.
def convert_unrandomized_items_to_events(tag: str) -> None:
- for location in locations:
+ for location in self.multiworld.get_locations(self.player):
if location.tags is not None and tag in location.tags:
location.place_locked_item(self.create_event(self.item_id_to_name[location.default_item_code]))
+ location.progress_type = LocationProgressType.DEFAULT
location.address = None
if self.options.badges == RandomizeBadges.option_vanilla:
@@ -285,33 +445,50 @@ def convert_unrandomized_items_to_events(tag: str) -> None:
convert_unrandomized_items_to_events("Rod")
if not self.options.bikes:
convert_unrandomized_items_to_events("Bike")
+ if not self.options.event_tickets:
+ convert_unrandomized_items_to_events("EventTicket")
if not self.options.key_items:
convert_unrandomized_items_to_events("KeyItem")
def pre_fill(self) -> None:
- # Items which are shuffled between their own locations
+ # Badges and HMs that are set to shuffle need to be placed at
+ # their own subset of locations
if self.options.badges == RandomizeBadges.option_shuffle:
badge_locations: List[PokemonEmeraldLocation]
badge_items: List[PokemonEmeraldItem]
# Sort order makes `fill_restrictive` try to place important badges later, which
# makes it less likely to have to swap at all, and more likely for swaps to work.
- # In the case of vanilla HMs, navigating Granite Cave is required to access more than 2 gyms,
- # so Knuckle Badge deserves highest priority if Flash is logically required.
badge_locations, badge_items = [list(l) for l in zip(*self.badge_shuffle_info)]
badge_priority = {
- "Knuckle Badge": 0 if (self.options.hms == RandomizeHms.option_vanilla and self.options.require_flash) else 3,
+ "Knuckle Badge": 3,
"Balance Badge": 1,
"Dynamo Badge": 1,
"Mind Badge": 2,
"Heat Badge": 2,
"Rain Badge": 3,
"Stone Badge": 4,
- "Feather Badge": 5
+ "Feather Badge": 5,
}
+ # In the case of vanilla HMs, navigating Granite Cave is required to access more than 2 gyms,
+ # so Knuckle Badge deserves highest priority if Flash is logically required.
+ if self.options.hms == RandomizeHms.option_vanilla and \
+ self.options.require_flash in (DarkCavesRequireFlash.option_both, DarkCavesRequireFlash.option_only_granite_cave):
+ badge_priority["Knuckle Badge"] = 0
badge_items.sort(key=lambda item: badge_priority.get(item.name, 0))
+ # Un-exclude badge locations, since we need to put progression items on them
+ for location in badge_locations:
+ location.progress_type = LocationProgressType.DEFAULT \
+ if location.progress_type == LocationProgressType.EXCLUDED \
+ else location.progress_type
+
collection_state = self.multiworld.get_all_state(False)
+
+ # If HM shuffle is on, HMs are not placed and not in the pool, so
+ # `get_all_state` did not contain them. Collect them manually for
+ # this fill. We know that they will be included in all state after
+ # this stage.
if self.hm_shuffle_info is not None:
for _, item in self.hm_shuffle_info:
collection_state.collect(item)
@@ -334,27 +511,37 @@ def pre_fill(self) -> None:
logging.debug(f"Failed to shuffle badges for player {self.player}. Retrying.")
continue
+ # Badges are guaranteed to be either placed or in the multiworld's itempool now
if self.options.hms == RandomizeHms.option_shuffle:
hm_locations: List[PokemonEmeraldLocation]
hm_items: List[PokemonEmeraldItem]
# Sort order makes `fill_restrictive` try to place important HMs later, which
# makes it less likely to have to swap at all, and more likely for swaps to work.
- # In the case of vanilla badges, navigating Granite Cave is required to access more than 2 gyms,
- # so Flash deserves highest priority if it's logically required.
hm_locations, hm_items = [list(l) for l in zip(*self.hm_shuffle_info)]
hm_priority = {
- "HM05 Flash": 0 if (self.options.badges == RandomizeBadges.option_vanilla and self.options.require_flash) else 3,
+ "HM05 Flash": 3,
"HM03 Surf": 1,
"HM06 Rock Smash": 1,
"HM08 Dive": 2,
"HM04 Strength": 2,
"HM07 Waterfall": 3,
"HM01 Cut": 4,
- "HM02 Fly": 5
+ "HM02 Fly": 5,
}
+ # In the case of vanilla badges, navigating Granite Cave is required to access more than 2 gyms,
+ # so Flash deserves highest priority if it's logically required.
+ if self.options.badges == RandomizeBadges.option_vanilla and \
+ self.options.require_flash in (DarkCavesRequireFlash.option_both, DarkCavesRequireFlash.option_only_granite_cave):
+ hm_priority["HM05 Flash"] = 0
hm_items.sort(key=lambda item: hm_priority.get(item.name, 0))
+ # Un-exclude HM locations, since we need to put progression items on them
+ for location in hm_locations:
+ location.progress_type = LocationProgressType.DEFAULT \
+ if location.progress_type == LocationProgressType.EXCLUDED \
+ else location.progress_type
+
collection_state = self.multiworld.get_all_state(False)
# In specific very constrained conditions, fill_restrictive may run
@@ -376,465 +563,110 @@ def pre_fill(self) -> None:
continue
def generate_output(self, output_directory: str) -> None:
- def randomize_abilities() -> None:
- # Creating list of potential abilities
- ability_label_to_value = {ability.label.lower(): ability.ability_id for ability in emerald_data.abilities}
-
- ability_blacklist_labels = {"cacophony"}
- option_ability_blacklist = self.options.ability_blacklist.value
- if option_ability_blacklist is not None:
- ability_blacklist_labels |= {ability_label.lower() for ability_label in option_ability_blacklist}
-
- ability_blacklist = {ability_label_to_value[label] for label in ability_blacklist_labels}
- ability_whitelist = [a.ability_id for a in emerald_data.abilities if a.ability_id not in ability_blacklist]
-
- if self.options.abilities == RandomizeAbilities.option_follow_evolutions:
- already_modified: Set[int] = set()
-
- # Loops through species and only tries to modify abilities if the pokemon has no pre-evolution
- # or if the pre-evolution has already been modified. Then tries to modify all species that evolve
- # from this one which have the same abilities.
- # The outer while loop only runs three times for vanilla ordering: Once for a first pass, once for
- # Hitmonlee/Hitmonchan, and once to verify that there's nothing left to do.
- while True:
- had_clean_pass = True
- for species in self.modified_species:
- if species is None:
- continue
- if species.species_id in already_modified:
- continue
- if species.pre_evolution is not None and species.pre_evolution not in already_modified:
- continue
-
- had_clean_pass = False
-
- old_abilities = species.abilities
- new_abilities = (
- 0 if old_abilities[0] == 0 else self.random.choice(ability_whitelist),
- 0 if old_abilities[1] == 0 else self.random.choice(ability_whitelist)
- )
-
- evolutions = [species]
- while len(evolutions) > 0:
- evolution = evolutions.pop()
- if evolution.abilities == old_abilities:
- evolution.abilities = new_abilities
- already_modified.add(evolution.species_id)
- evolutions += [
- self.modified_species[evolution.species_id]
- for evolution in evolution.evolutions
- if evolution.species_id not in already_modified
- ]
-
- if had_clean_pass:
- break
- else: # Not following evolutions
- for species in self.modified_species:
- if species is None:
- continue
-
- old_abilities = species.abilities
- new_abilities = (
- 0 if old_abilities[0] == 0 else self.random.choice(ability_whitelist),
- 0 if old_abilities[1] == 0 else self.random.choice(ability_whitelist)
- )
-
- species.abilities = new_abilities
-
- def randomize_types() -> None:
- if self.options.types == RandomizeTypes.option_shuffle:
- type_map = list(range(18))
- self.random.shuffle(type_map)
-
- # We never want to map to the ??? type, so swap whatever index maps to ??? with ???
- # So ??? will always map to itself, and there are no pokemon which have the ??? type
- mystery_type_index = type_map.index(9)
- type_map[mystery_type_index], type_map[9] = type_map[9], type_map[mystery_type_index]
-
- for species in self.modified_species:
- if species is not None:
- species.types = (type_map[species.types[0]], type_map[species.types[1]])
- elif self.options.types == RandomizeTypes.option_completely_random:
- for species in self.modified_species:
- if species is not None:
- new_type_1 = get_random_type(self.random)
- new_type_2 = new_type_1
- if species.types[0] != species.types[1]:
- while new_type_1 == new_type_2:
- new_type_2 = get_random_type(self.random)
-
- species.types = (new_type_1, new_type_2)
- elif self.options.types == RandomizeTypes.option_follow_evolutions:
- already_modified: Set[int] = set()
-
- # Similar to follow evolutions for abilities, but only needs to loop through once.
- # For every pokemon without a pre-evolution, generates a random mapping from old types to new types
- # and then walks through the evolution tree applying that map. This means that evolutions that share
- # types will have those types mapped to the same new types, and evolutions with new or diverging types
- # will still have new or diverging types.
- # Consider:
- # - Charmeleon (Fire/Fire) -> Charizard (Fire/Flying)
- # - Onyx (Rock/Ground) -> Steelix (Steel/Ground)
- # - Nincada (Bug/Ground) -> Ninjask (Bug/Flying) && Shedinja (Bug/Ghost)
- # - Azurill (Normal/Normal) -> Marill (Water/Water)
- for species in self.modified_species:
- if species is None:
- continue
- if species.species_id in already_modified:
- continue
- if species.pre_evolution is not None and species.pre_evolution not in already_modified:
- continue
-
- type_map = list(range(18))
- self.random.shuffle(type_map)
-
- # We never want to map to the ??? type, so swap whatever index maps to ??? with ???
- # So ??? will always map to itself, and there are no pokemon which have the ??? type
- mystery_type_index = type_map.index(9)
- type_map[mystery_type_index], type_map[9] = type_map[9], type_map[mystery_type_index]
-
- evolutions = [species]
- while len(evolutions) > 0:
- evolution = evolutions.pop()
- evolution.types = (type_map[evolution.types[0]], type_map[evolution.types[1]])
- already_modified.add(evolution.species_id)
- evolutions += [self.modified_species[evo.species_id] for evo in evolution.evolutions]
-
- def randomize_learnsets() -> None:
- type_bias = self.options.move_match_type_bias.value
- normal_bias = self.options.move_normal_type_bias.value
-
- for species in self.modified_species:
- if species is None:
- continue
-
- old_learnset = species.learnset
- new_learnset: List[LearnsetMove] = []
-
- i = 0
- # Replace filler MOVE_NONEs at start of list
- while old_learnset[i].move_id == 0:
- if self.options.level_up_moves == LevelUpMoves.option_start_with_four_moves:
- new_move = get_random_move(self.random, {move.move_id for move in new_learnset}, type_bias,
- normal_bias, species.types)
- else:
- new_move = 0
- new_learnset.append(LearnsetMove(old_learnset[i].level, new_move))
- i += 1
-
- while i < len(old_learnset):
- # Guarantees the starter has a good damaging move
- if i == 3:
- new_move = get_random_damaging_move(self.random, {move.move_id for move in new_learnset})
- else:
- new_move = get_random_move(self.random, {move.move_id for move in new_learnset}, type_bias,
- normal_bias, species.types)
- new_learnset.append(LearnsetMove(old_learnset[i].level, new_move))
- i += 1
-
- species.learnset = new_learnset
-
- def randomize_tm_hm_compatibility() -> None:
- for species in self.modified_species:
- if species is None:
- continue
-
- combatibility_array = int_to_bool_array(species.tm_hm_compatibility)
-
- # TMs
- for i in range(0, 50):
- if self.options.tm_compatibility == TmCompatibility.option_fully_compatible:
- combatibility_array[i] = True
- elif self.options.tm_compatibility == TmCompatibility.option_completely_random:
- combatibility_array[i] = self.random.choice([True, False])
-
- # HMs
- for i in range(50, 58):
- if self.options.hm_compatibility == HmCompatibility.option_fully_compatible:
- combatibility_array[i] = True
- elif self.options.hm_compatibility == HmCompatibility.option_completely_random:
- combatibility_array[i] = self.random.choice([True, False])
+ self.modified_trainers = copy.deepcopy(emerald_data.trainers)
+ self.modified_tmhm_moves = copy.deepcopy(emerald_data.tmhm_moves)
+ self.modified_legendary_encounters = copy.deepcopy(emerald_data.legendary_encounters)
+ self.modified_misc_pokemon = copy.deepcopy(emerald_data.misc_pokemon)
+ self.modified_starters = copy.deepcopy(emerald_data.starters)
- species.tm_hm_compatibility = bool_array_to_int(combatibility_array)
+ # Modify catch rate
+ min_catch_rate = min(self.options.min_catch_rate.value, 255)
+ for species in self.modified_species.values():
+ species.catch_rate = max(species.catch_rate, min_catch_rate)
- def randomize_tm_moves() -> None:
+ # Modify TM moves
+ if self.options.tm_tutor_moves:
new_moves: Set[int] = set()
for i in range(50):
- new_move = get_random_move(self.random, new_moves)
+ new_move = get_random_move(self.random, new_moves | self.blacklisted_moves)
new_moves.add(new_move)
self.modified_tmhm_moves[i] = new_move
- def randomize_wild_encounters() -> None:
- should_match_bst = self.options.wild_pokemon in {
- RandomizeWildPokemon.option_match_base_stats,
- RandomizeWildPokemon.option_match_base_stats_and_type
- }
- should_match_type = self.options.wild_pokemon in {
- RandomizeWildPokemon.option_match_type,
- RandomizeWildPokemon.option_match_base_stats_and_type
- }
- should_allow_legendaries = self.options.allow_wild_legendaries == Toggle.option_true
-
- for map_data in self.modified_maps:
- new_encounters: List[Optional[EncounterTableData]] = [None, None, None]
- old_encounters = [map_data.land_encounters, map_data.water_encounters, map_data.fishing_encounters]
-
- for i, table in enumerate(old_encounters):
- if table is not None:
- species_old_to_new_map: Dict[int, int] = {}
- for species_id in table.slots:
- if species_id not in species_old_to_new_map:
- original_species = emerald_data.species[species_id]
- target_bst = sum(original_species.base_stats) if should_match_bst else None
- target_type = self.random.choice(original_species.types) if should_match_type else None
-
- species_old_to_new_map[species_id] = get_random_species(
- self.random,
- self.modified_species,
- target_bst,
- target_type,
- should_allow_legendaries
- ).species_id
-
- new_slots: List[int] = []
- for species_id in table.slots:
- new_slots.append(species_old_to_new_map[species_id])
-
- new_encounters[i] = EncounterTableData(new_slots, table.rom_address)
-
- map_data.land_encounters = new_encounters[0]
- map_data.water_encounters = new_encounters[1]
- map_data.fishing_encounters = new_encounters[2]
-
- def randomize_static_encounters() -> None:
- if self.options.static_encounters == RandomizeStaticEncounters.option_shuffle:
- shuffled_species = [encounter.species_id for encounter in emerald_data.static_encounters]
- self.random.shuffle(shuffled_species)
-
- self.modified_static_encounters = []
- for i, encounter in enumerate(emerald_data.static_encounters):
- self.modified_static_encounters.append(StaticEncounterData(
- shuffled_species[i],
- encounter.rom_address
- ))
- else:
- should_match_bst = self.options.static_encounters in {
- RandomizeStaticEncounters.option_match_base_stats,
- RandomizeStaticEncounters.option_match_base_stats_and_type
- }
- should_match_type = self.options.static_encounters in {
- RandomizeStaticEncounters.option_match_type,
- RandomizeStaticEncounters.option_match_base_stats_and_type
- }
-
- for encounter in emerald_data.static_encounters:
- original_species = self.modified_species[encounter.species_id]
- target_bst = sum(original_species.base_stats) if should_match_bst else None
- target_type = self.random.choice(original_species.types) if should_match_type else None
-
- self.modified_static_encounters.append(StaticEncounterData(
- get_random_species(self.random, self.modified_species, target_bst, target_type).species_id,
- encounter.rom_address
- ))
-
- def randomize_opponent_parties() -> None:
- should_match_bst = self.options.trainer_parties in {
- RandomizeTrainerParties.option_match_base_stats,
- RandomizeTrainerParties.option_match_base_stats_and_type
- }
- should_match_type = self.options.trainer_parties in {
- RandomizeTrainerParties.option_match_type,
- RandomizeTrainerParties.option_match_base_stats_and_type
+ randomize_abilities(self)
+ randomize_learnsets(self)
+ randomize_tm_hm_compatibility(self)
+ randomize_legendary_encounters(self)
+ randomize_misc_pokemon(self)
+ randomize_opponent_parties(self)
+ randomize_starters(self)
+
+ create_patch(self, output_directory)
+
+ del self.modified_trainers
+ del self.modified_tmhm_moves
+ del self.modified_legendary_encounters
+ del self.modified_misc_pokemon
+ del self.modified_starters
+ del self.modified_species
+
+ def write_spoiler(self, spoiler_handle: TextIO):
+ if self.options.dexsanity:
+ from collections import defaultdict
+
+ spoiler_handle.write(f"\n\nWild Pokemon ({self.multiworld.player_name[self.player]}):\n\n")
+
+ species_maps = defaultdict(set)
+ for map in self.modified_maps.values():
+ if map.land_encounters is not None:
+ for encounter in map.land_encounters.slots:
+ species_maps[encounter].add(map.name[4:])
+
+ if map.water_encounters is not None:
+ for encounter in map.water_encounters.slots:
+ species_maps[encounter].add(map.name[4:])
+
+ if map.fishing_encounters is not None:
+ for encounter in map.fishing_encounters.slots:
+ species_maps[encounter].add(map.name[4:])
+
+ lines = [f"{emerald_data.species[species].label}: {', '.join(maps)}\n"
+ for species, maps in species_maps.items()]
+ lines.sort()
+ for line in lines:
+ spoiler_handle.write(line)
+
+ del self.modified_maps
+
+ def extend_hint_information(self, hint_data):
+ if self.options.dexsanity:
+ from collections import defaultdict
+
+ slot_to_rod = {
+ 0: "_OLD_ROD",
+ 1: "_OLD_ROD",
+ 2: "_GOOD_ROD",
+ 3: "_GOOD_ROD",
+ 4: "_GOOD_ROD",
+ 5: "_SUPER_ROD",
+ 6: "_SUPER_ROD",
+ 7: "_SUPER_ROD",
+ 8: "_SUPER_ROD",
+ 9: "_SUPER_ROD",
}
- allow_legendaries = self.options.allow_trainer_legendaries == Toggle.option_true
-
- per_species_tmhm_moves: Dict[int, List[int]] = {}
-
- for trainer in self.modified_trainers:
- new_party = []
- for pokemon in trainer.party.pokemon:
- original_species = emerald_data.species[pokemon.species_id]
- target_bst = sum(original_species.base_stats) if should_match_bst else None
- target_type = self.random.choice(original_species.types) if should_match_type else None
-
- new_species = get_random_species(
- self.random,
- self.modified_species,
- target_bst,
- target_type,
- allow_legendaries
- )
-
- if new_species.species_id not in per_species_tmhm_moves:
- per_species_tmhm_moves[new_species.species_id] = list({
- self.modified_tmhm_moves[i]
- for i, is_compatible in enumerate(int_to_bool_array(new_species.tm_hm_compatibility))
- if is_compatible
- })
-
- tm_hm_movepool = per_species_tmhm_moves[new_species.species_id]
- level_up_movepool = list({
- move.move_id
- for move in new_species.learnset
- if move.move_id != 0 and move.level <= pokemon.level
- })
-
- new_moves = (
- self.random.choice(tm_hm_movepool if self.random.random() < 0.25 and len(tm_hm_movepool) > 0 else level_up_movepool),
- self.random.choice(tm_hm_movepool if self.random.random() < 0.25 and len(tm_hm_movepool) > 0 else level_up_movepool),
- self.random.choice(tm_hm_movepool if self.random.random() < 0.25 and len(tm_hm_movepool) > 0 else level_up_movepool),
- self.random.choice(tm_hm_movepool if self.random.random() < 0.25 and len(tm_hm_movepool) > 0 else level_up_movepool)
- )
-
- new_party.append(TrainerPokemonData(new_species.species_id, pokemon.level, new_moves))
-
- trainer.party.pokemon = new_party
-
- def randomize_starters() -> None:
- match_bst = self.options.starters in {
- RandomizeStarters.option_match_base_stats,
- RandomizeStarters.option_match_base_stats_and_type
- }
- match_type = self.options.starters in {
- RandomizeStarters.option_match_type,
- RandomizeStarters.option_match_base_stats_and_type
- }
- allow_legendaries = self.options.allow_starter_legendaries == Toggle.option_true
-
- starter_bsts = (
- sum(emerald_data.species[emerald_data.starters[0]].base_stats) if match_bst else None,
- sum(emerald_data.species[emerald_data.starters[1]].base_stats) if match_bst else None,
- sum(emerald_data.species[emerald_data.starters[2]].base_stats) if match_bst else None
- )
-
- starter_types = (
- self.random.choice(emerald_data.species[emerald_data.starters[0]].types) if match_type else None,
- self.random.choice(emerald_data.species[emerald_data.starters[1]].types) if match_type else None,
- self.random.choice(emerald_data.species[emerald_data.starters[2]].types) if match_type else None
- )
-
- new_starters = (
- get_random_species(self.random, self.modified_species,
- starter_bsts[0], starter_types[0], allow_legendaries),
- get_random_species(self.random, self.modified_species,
- starter_bsts[1], starter_types[1], allow_legendaries),
- get_random_species(self.random, self.modified_species,
- starter_bsts[2], starter_types[2], allow_legendaries)
- )
-
- egg_code = self.options.easter_egg.value
- egg_check_1 = 0
- egg_check_2 = 0
-
- for i in egg_code:
- egg_check_1 += ord(i)
- egg_check_2 += egg_check_1 * egg_check_1
-
- egg = 96 + egg_check_2 - (egg_check_1 * 0x077C)
- if egg_check_2 == 0x14E03A and egg < 411 and egg > 0 and egg not in range(252, 277):
- self.modified_starters = (egg, egg, egg)
- else:
- self.modified_starters = (
- new_starters[0].species_id,
- new_starters[1].species_id,
- new_starters[2].species_id
- )
-
- # Putting the unchosen starter onto the rival's team
- rival_teams: List[List[Tuple[str, int, bool]]] = [
- [
- ("TRAINER_BRENDAN_ROUTE_103_TREECKO", 0, False),
- ("TRAINER_BRENDAN_RUSTBORO_TREECKO", 1, False),
- ("TRAINER_BRENDAN_ROUTE_110_TREECKO", 2, True ),
- ("TRAINER_BRENDAN_ROUTE_119_TREECKO", 2, True ),
- ("TRAINER_BRENDAN_LILYCOVE_TREECKO", 3, True ),
- ("TRAINER_MAY_ROUTE_103_TREECKO", 0, False),
- ("TRAINER_MAY_RUSTBORO_TREECKO", 1, False),
- ("TRAINER_MAY_ROUTE_110_TREECKO", 2, True ),
- ("TRAINER_MAY_ROUTE_119_TREECKO", 2, True ),
- ("TRAINER_MAY_LILYCOVE_TREECKO", 3, True )
- ],
- [
- ("TRAINER_BRENDAN_ROUTE_103_TORCHIC", 0, False),
- ("TRAINER_BRENDAN_RUSTBORO_TORCHIC", 1, False),
- ("TRAINER_BRENDAN_ROUTE_110_TORCHIC", 2, True ),
- ("TRAINER_BRENDAN_ROUTE_119_TORCHIC", 2, True ),
- ("TRAINER_BRENDAN_LILYCOVE_TORCHIC", 3, True ),
- ("TRAINER_MAY_ROUTE_103_TORCHIC", 0, False),
- ("TRAINER_MAY_RUSTBORO_TORCHIC", 1, False),
- ("TRAINER_MAY_ROUTE_110_TORCHIC", 2, True ),
- ("TRAINER_MAY_ROUTE_119_TORCHIC", 2, True ),
- ("TRAINER_MAY_LILYCOVE_TORCHIC", 3, True )
- ],
- [
- ("TRAINER_BRENDAN_ROUTE_103_MUDKIP", 0, False),
- ("TRAINER_BRENDAN_RUSTBORO_MUDKIP", 1, False),
- ("TRAINER_BRENDAN_ROUTE_110_MUDKIP", 2, True ),
- ("TRAINER_BRENDAN_ROUTE_119_MUDKIP", 2, True ),
- ("TRAINER_BRENDAN_LILYCOVE_MUDKIP", 3, True ),
- ("TRAINER_MAY_ROUTE_103_MUDKIP", 0, False),
- ("TRAINER_MAY_RUSTBORO_MUDKIP", 1, False),
- ("TRAINER_MAY_ROUTE_110_MUDKIP", 2, True ),
- ("TRAINER_MAY_ROUTE_119_MUDKIP", 2, True ),
- ("TRAINER_MAY_LILYCOVE_MUDKIP", 3, True )
- ]
- ]
- for i, starter in enumerate([new_starters[1], new_starters[2], new_starters[0]]):
- potential_evolutions = [evolution.species_id for evolution in starter.evolutions]
- picked_evolution = starter.species_id
- if len(potential_evolutions) > 0:
- picked_evolution = self.random.choice(potential_evolutions)
+ species_maps = defaultdict(set)
+ for map in self.modified_maps.values():
+ if map.land_encounters is not None:
+ for encounter in map.land_encounters.slots:
+ species_maps[encounter].add(map.name[4:] + "_GRASS")
- for trainer_name, starter_position, is_evolved in rival_teams[i]:
- trainer_data = self.modified_trainers[emerald_data.constants[trainer_name]]
- trainer_data.party.pokemon[starter_position].species_id = picked_evolution if is_evolved else starter.species_id
+ if map.water_encounters is not None:
+ for encounter in map.water_encounters.slots:
+ species_maps[encounter].add(map.name[4:] + "_WATER")
- self.modified_species = copy.deepcopy(emerald_data.species)
- self.modified_trainers = copy.deepcopy(emerald_data.trainers)
- self.modified_maps = copy.deepcopy(emerald_data.maps)
- self.modified_tmhm_moves = copy.deepcopy(emerald_data.tmhm_moves)
- self.modified_static_encounters = copy.deepcopy(emerald_data.static_encounters)
- self.modified_starters = copy.deepcopy(emerald_data.starters)
-
- # Randomize species data
- if self.options.abilities != RandomizeAbilities.option_vanilla:
- randomize_abilities()
-
- if self.options.types != RandomizeTypes.option_vanilla:
- randomize_types()
-
- if self.options.level_up_moves != LevelUpMoves.option_vanilla:
- randomize_learnsets()
+ if map.fishing_encounters is not None:
+ for slot, encounter in enumerate(map.fishing_encounters.slots):
+ species_maps[encounter].add(map.name[4:] + slot_to_rod[slot])
- randomize_tm_hm_compatibility() # Options are checked within this function
-
- min_catch_rate = min(self.options.min_catch_rate.value, 255)
- for species in self.modified_species:
- if species is not None:
- species.catch_rate = max(species.catch_rate, min_catch_rate)
-
- if self.options.tm_moves:
- randomize_tm_moves()
-
- # Randomize wild encounters
- if self.options.wild_pokemon != RandomizeWildPokemon.option_vanilla:
- randomize_wild_encounters()
-
- # Randomize static encounters
- if self.options.static_encounters != RandomizeStaticEncounters.option_vanilla:
- randomize_static_encounters()
-
- # Randomize opponents
- if self.options.trainer_parties != RandomizeTrainerParties.option_vanilla:
- randomize_opponent_parties()
-
- # Randomize starters
- if self.options.starters != RandomizeStarters.option_vanilla:
- randomize_starters()
+ hint_data[self.player] = {
+ self.location_name_to_id[f"Pokedex - {emerald_data.species[species].label}"]: ", ".join(maps)
+ for species, maps in species_maps.items()
+ }
- generate_output(self, output_directory)
+ def modify_multidata(self, multidata: Dict[str, Any]):
+ import base64
+ multidata["connect_names"][base64.b64encode(self.auth).decode("ascii")] = multidata["connect_names"][self.multiworld.player_name[self.player]]
def fill_slot_data(self) -> Dict[str, Any]:
slot_data = self.options.as_dict(
@@ -843,23 +675,33 @@ def fill_slot_data(self) -> Dict[str, Any]:
"hms",
"key_items",
"bikes",
+ "event_tickets",
"rods",
"overworld_items",
"hidden_items",
"npc_gifts",
+ "berry_trees",
"require_itemfinder",
"require_flash",
- "enable_ferry",
"elite_four_requirement",
"elite_four_count",
"norman_requirement",
"norman_count",
+ "legendary_hunt_catch",
+ "legendary_hunt_count",
"extra_boulders",
"remove_roadblocks",
+ "allowed_legendary_hunt_encounters",
+ "extra_bumpy_slope",
"free_fly_location",
- "fly_without_badge",
+ "remote_items",
+ "dexsanity",
+ "trainersanity",
+ "modify_118",
+ "death_link",
)
slot_data["free_fly_location_id"] = self.free_fly_location_id
+ slot_data["hm_requirements"] = self.hm_requirements
return slot_data
def create_item(self, name: str) -> PokemonEmeraldItem:
diff --git a/worlds/pokemon_emerald/client.py b/worlds/pokemon_emerald/client.py
index d8b4b8d5878f..0dccc1fe17a0 100644
--- a/worlds/pokemon_emerald/client.py
+++ b/worlds/pokemon_emerald/client.py
@@ -1,19 +1,28 @@
-from typing import TYPE_CHECKING, Dict, Set
+import asyncio
+import copy
+import orjson
+import random
+import time
+from typing import TYPE_CHECKING, Optional, Dict, Set, Tuple
+import uuid
from NetUtils import ClientStatus
+from Options import Toggle
+import Utils
import worlds._bizhawk as bizhawk
from worlds._bizhawk.client import BizHawkClient
-from .data import BASE_OFFSET, data
-from .options import Goal
+from .data import BASE_OFFSET, POKEDEX_OFFSET, data
+from .options import Goal, RemoteItems
+from .util import pokemon_data_to_json, json_to_pokemon_data
if TYPE_CHECKING:
from worlds._bizhawk.context import BizHawkClientContext
-EXPECTED_ROM_NAME = "pokemon emerald version / AP 2"
+EXPECTED_ROM_NAME = "pokemon emerald version / AP 5"
-IS_CHAMPION_FLAG = data.constants["FLAG_IS_CHAMPION"]
+DEFEATED_WALLACE_FLAG = data.constants["TRAINER_FLAGS_START"] + data.constants["TRAINER_WALLACE"]
DEFEATED_STEVEN_FLAG = data.constants["TRAINER_FLAGS_START"] + data.constants["TRAINER_STEVEN"]
DEFEATED_NORMAN_FLAG = data.constants["TRAINER_FLAGS_START"] + data.constants["TRAINER_NORMAN_1"]
@@ -31,7 +40,7 @@
"FLAG_RECEIVED_POKENAV", # Talk to Mr. Stone
"FLAG_DELIVERED_STEVEN_LETTER",
"FLAG_DELIVERED_DEVON_GOODS",
- "FLAG_HIDE_ROUTE_119_TEAM_AQUA", # Clear Weather Institute
+ "FLAG_HIDE_ROUTE_119_TEAM_AQUA_SHELLY", # Clear Weather Institute
"FLAG_MET_ARCHIE_METEOR_FALLS", # Magma steals meteorite
"FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT", # Clear Magma Hideout
"FLAG_MET_TEAM_AQUA_HARBOR", # Aqua steals submarine
@@ -41,19 +50,19 @@
"FLAG_HIDE_SKY_PILLAR_TOP_RAYQUAZA", # Rayquaza departs for Sootopolis
"FLAG_OMIT_DIVE_FROM_STEVEN_LETTER", # Steven gives Dive HM (clears seafloor cavern grunt)
"FLAG_IS_CHAMPION",
- "FLAG_PURCHASED_HARBOR_MAIL"
+ "FLAG_PURCHASED_HARBOR_MAIL",
]
EVENT_FLAG_MAP = {data.constants[flag_name]: flag_name for flag_name in TRACKER_EVENT_FLAGS}
KEY_LOCATION_FLAGS = [
- "NPC_GIFT_RECEIVED_HM01",
- "NPC_GIFT_RECEIVED_HM02",
- "NPC_GIFT_RECEIVED_HM03",
- "NPC_GIFT_RECEIVED_HM04",
- "NPC_GIFT_RECEIVED_HM05",
- "NPC_GIFT_RECEIVED_HM06",
- "NPC_GIFT_RECEIVED_HM07",
- "NPC_GIFT_RECEIVED_HM08",
+ "NPC_GIFT_RECEIVED_HM_CUT",
+ "NPC_GIFT_RECEIVED_HM_FLY",
+ "NPC_GIFT_RECEIVED_HM_SURF",
+ "NPC_GIFT_RECEIVED_HM_STRENGTH",
+ "NPC_GIFT_RECEIVED_HM_FLASH",
+ "NPC_GIFT_RECEIVED_HM_ROCK_SMASH",
+ "NPC_GIFT_RECEIVED_HM_WATERFALL",
+ "NPC_GIFT_RECEIVED_HM_DIVE",
"NPC_GIFT_RECEIVED_ACRO_BIKE",
"NPC_GIFT_RECEIVED_WAILMER_PAIL",
"NPC_GIFT_RECEIVED_DEVON_GOODS_RUSTURF_TUNNEL",
@@ -70,7 +79,7 @@
"HIDDEN_ITEM_ABANDONED_SHIP_RM_1_KEY",
"HIDDEN_ITEM_ABANDONED_SHIP_RM_4_KEY",
"HIDDEN_ITEM_ABANDONED_SHIP_RM_6_KEY",
- "ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_4_SCANNER",
+ "ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_2_SCANNER",
"ITEM_ABANDONED_SHIP_CAPTAINS_OFFICE_STORAGE_KEY",
"NPC_GIFT_RECEIVED_OLD_ROD",
"NPC_GIFT_RECEIVED_GOOD_ROD",
@@ -78,6 +87,24 @@
]
KEY_LOCATION_FLAG_MAP = {data.locations[location_name].flag: location_name for location_name in KEY_LOCATION_FLAGS}
+LEGENDARY_NAMES = {
+ "Groudon": "GROUDON",
+ "Kyogre": "KYOGRE",
+ "Rayquaza": "RAYQUAZA",
+ "Latias": "LATIAS",
+ "Latios": "LATIOS",
+ "Regirock": "REGIROCK",
+ "Regice": "REGICE",
+ "Registeel": "REGISTEEL",
+ "Mew": "MEW",
+ "Deoxys": "DEOXYS",
+ "Ho-oh": "HO_OH",
+ "Lugia": "LUGIA",
+}
+
+DEFEATED_LEGENDARY_FLAG_MAP = {data.constants[f"FLAG_DEFEATED_{name}"]: name for name in LEGENDARY_NAMES.values()}
+CAUGHT_LEGENDARY_FLAG_MAP = {data.constants[f"FLAG_CAUGHT_{name}"]: name for name in LEGENDARY_NAMES.values()}
+
class PokemonEmeraldClient(BizHawkClient):
game = "Pokemon Emerald"
@@ -86,14 +113,31 @@ class PokemonEmeraldClient(BizHawkClient):
local_checked_locations: Set[int]
local_set_events: Dict[str, bool]
local_found_key_items: Dict[str, bool]
- goal_flag: int
+ local_defeated_legendaries: Dict[str, bool]
+ goal_flag: Optional[int]
+
+ wonder_trade_update_event: asyncio.Event
+ latest_wonder_trade_reply: dict
+ wonder_trade_cooldown: int
+ wonder_trade_cooldown_timer: int
+
+ death_counter: Optional[int]
+ previous_death_link: float
+ ignore_next_death_link: bool
def __init__(self) -> None:
super().__init__()
self.local_checked_locations = set()
self.local_set_events = {}
self.local_found_key_items = {}
- self.goal_flag = IS_CHAMPION_FLAG
+ self.local_defeated_legendaries = {}
+ self.goal_flag = None
+ self.wonder_trade_update_event = asyncio.Event()
+ self.wonder_trade_cooldown = 5000
+ self.wonder_trade_cooldown_timer = 0
+ self.death_counter = None
+ self.previous_death_link = 0
+ self.ignore_next_death_link = False
async def validate_rom(self, ctx: "BizHawkClientContext") -> bool:
from CommonClient import logger
@@ -123,88 +167,103 @@ async def validate_rom(self, ctx: "BizHawkClientContext") -> bool:
ctx.want_slot_data = True
ctx.watcher_timeout = 0.125
+ self.death_counter = None
+ self.previous_death_link = 0
+ self.ignore_next_death_link = False
+
return True
async def set_auth(self, ctx: "BizHawkClientContext") -> None:
- slot_name_bytes = (await bizhawk.read(ctx.bizhawk_ctx, [(data.rom_addresses["gArchipelagoInfo"], 64, "ROM")]))[0]
- ctx.auth = bytes([byte for byte in slot_name_bytes if byte != 0]).decode("utf-8")
+ import base64
+ auth_raw = (await bizhawk.read(ctx.bizhawk_ctx, [(data.rom_addresses["gArchipelagoInfo"], 16, "ROM")]))[0]
+ ctx.auth = base64.b64encode(auth_raw).decode("utf-8")
async def game_watcher(self, ctx: "BizHawkClientContext") -> None:
- if ctx.slot_data is not None:
- if ctx.slot_data["goal"] == Goal.option_champion:
- self.goal_flag = IS_CHAMPION_FLAG
- elif ctx.slot_data["goal"] == Goal.option_steven:
- self.goal_flag = DEFEATED_STEVEN_FLAG
- elif ctx.slot_data["goal"] == Goal.option_norman:
- self.goal_flag = DEFEATED_NORMAN_FLAG
+ if ctx.server is None or ctx.server.socket.closed or ctx.slot_data is None:
+ return
+
+ if ctx.slot_data["goal"] == Goal.option_champion:
+ self.goal_flag = DEFEATED_WALLACE_FLAG
+ elif ctx.slot_data["goal"] == Goal.option_steven:
+ self.goal_flag = DEFEATED_STEVEN_FLAG
+ elif ctx.slot_data["goal"] == Goal.option_norman:
+ self.goal_flag = DEFEATED_NORMAN_FLAG
+ elif ctx.slot_data["goal"] == Goal.option_legendary_hunt:
+ self.goal_flag = None
+
+ if ctx.slot_data["remote_items"] == RemoteItems.option_true and not ctx.items_handling & 0b010:
+ ctx.items_handling = 0b011
+ Utils.async_start(ctx.send_msgs([{
+ "cmd": "ConnectUpdate",
+ "items_handling": ctx.items_handling
+ }]))
try:
- # Checks that the player is in the overworld
- overworld_guard = (data.ram_addresses["gMain"] + 4, (data.ram_addresses["CB2_Overworld"] + 1).to_bytes(4, "little"), "System Bus")
+ guards: Dict[str, Tuple[int, bytes, str]] = {}
- # Read save block address
- read_result = await bizhawk.guarded_read(
- ctx.bizhawk_ctx,
- [(data.ram_addresses["gSaveBlock1Ptr"], 4, "System Bus")],
- [overworld_guard]
+ # Checks that the player is in the overworld
+ guards["IN OVERWORLD"] = (
+ data.ram_addresses["gMain"] + 4,
+ (data.ram_addresses["CB2_Overworld"] + 1).to_bytes(4, "little"),
+ "System Bus"
)
- if read_result is None: # Not in overworld
- return
-
- # Checks that the save block hasn't moved
- save_block_address_guard = (data.ram_addresses["gSaveBlock1Ptr"], read_result[0], "System Bus")
-
- save_block_address = int.from_bytes(read_result[0], "little")
- # Handle giving the player items
- read_result = await bizhawk.guarded_read(
+ # Read save block addresses
+ read_result = await bizhawk.read(
ctx.bizhawk_ctx,
[
- (save_block_address + 0x3778, 2, "System Bus"), # Number of received items
- (data.ram_addresses["gArchipelagoReceivedItem"] + 4, 1, "System Bus") # Received item struct full?
- ],
- [overworld_guard, save_block_address_guard]
+ (data.ram_addresses["gSaveBlock1Ptr"], 4, "System Bus"),
+ (data.ram_addresses["gSaveBlock2Ptr"], 4, "System Bus"),
+ ]
)
- if read_result is None: # Not in overworld, or save block moved
- return
- num_received_items = int.from_bytes(read_result[0], "little")
- received_item_is_empty = read_result[1][0] == 0
+ # Checks that the save data hasn't moved
+ guards["SAVE BLOCK 1"] = (data.ram_addresses["gSaveBlock1Ptr"], read_result[0], "System Bus")
+ guards["SAVE BLOCK 2"] = (data.ram_addresses["gSaveBlock2Ptr"], read_result[1], "System Bus")
- # If the game hasn't received all items yet and the received item struct doesn't contain an item, then
- # fill it with the next item
- if num_received_items < len(ctx.items_received) and received_item_is_empty:
- next_item = ctx.items_received[num_received_items]
- await bizhawk.write(ctx.bizhawk_ctx, [
- (data.ram_addresses["gArchipelagoReceivedItem"] + 0, (next_item.item - BASE_OFFSET).to_bytes(2, "little"), "System Bus"),
- (data.ram_addresses["gArchipelagoReceivedItem"] + 2, (num_received_items + 1).to_bytes(2, "little"), "System Bus"),
- (data.ram_addresses["gArchipelagoReceivedItem"] + 4, [1], "System Bus"), # Mark struct full
- (data.ram_addresses["gArchipelagoReceivedItem"] + 5, [next_item.flags & 1], "System Bus"),
- ])
+ sb1_address = int.from_bytes(guards["SAVE BLOCK 1"][1], "little")
+ sb2_address = int.from_bytes(guards["SAVE BLOCK 2"][1], "little")
+
+ await self.handle_death_link(ctx, guards)
+ await self.handle_received_items(ctx, guards)
+ await self.handle_wonder_trade(ctx, guards)
# Read flags in 2 chunks
read_result = await bizhawk.guarded_read(
ctx.bizhawk_ctx,
- [(save_block_address + 0x1450, 0x96, "System Bus")], # Flags
- [overworld_guard, save_block_address_guard]
+ [(sb1_address + 0x1450, 0x96, "System Bus")], # Flags
+ [guards["IN OVERWORLD"], guards["SAVE BLOCK 1"]]
)
if read_result is None: # Not in overworld, or save block moved
return
-
flag_bytes = read_result[0]
read_result = await bizhawk.guarded_read(
ctx.bizhawk_ctx,
- [(save_block_address + 0x14E6, 0x96, "System Bus")], # Flags
- [overworld_guard, save_block_address_guard]
+ [(sb1_address + 0x14E6, 0x96, "System Bus")], # Flags continued
+ [guards["IN OVERWORLD"], guards["SAVE BLOCK 1"]]
)
if read_result is not None:
flag_bytes += read_result[0]
+ # Read pokedex flags
+ pokedex_caught_bytes = bytes(0)
+ if ctx.slot_data["dexsanity"] == Toggle.option_true:
+ # Read pokedex flags
+ read_result = await bizhawk.guarded_read(
+ ctx.bizhawk_ctx,
+ [(sb2_address + 0x28, 0x34, "System Bus")],
+ [guards["IN OVERWORLD"], guards["SAVE BLOCK 2"]]
+ )
+ if read_result is not None:
+ pokedex_caught_bytes = read_result[0]
+
game_clear = False
local_checked_locations = set()
local_set_events = {flag_name: False for flag_name in TRACKER_EVENT_FLAGS}
local_found_key_items = {location_name: False for location_name in KEY_LOCATION_FLAGS}
+ defeated_legendaries = {legendary_name: False for legendary_name in LEGENDARY_NAMES.values()}
+ caught_legendaries = {legendary_name: False for legendary_name in LEGENDARY_NAMES.values()}
# Check set flags
for byte_i, byte in enumerate(flag_bytes):
@@ -219,12 +278,45 @@ async def game_watcher(self, ctx: "BizHawkClientContext") -> None:
if flag_id == self.goal_flag:
game_clear = True
+ if flag_id in DEFEATED_LEGENDARY_FLAG_MAP:
+ defeated_legendaries[DEFEATED_LEGENDARY_FLAG_MAP[flag_id]] = True
+
+ if flag_id in CAUGHT_LEGENDARY_FLAG_MAP:
+ caught_legendaries[CAUGHT_LEGENDARY_FLAG_MAP[flag_id]] = True
+
if flag_id in EVENT_FLAG_MAP:
local_set_events[EVENT_FLAG_MAP[flag_id]] = True
if flag_id in KEY_LOCATION_FLAG_MAP:
local_found_key_items[KEY_LOCATION_FLAG_MAP[flag_id]] = True
+ # Check pokedex
+ if ctx.slot_data["dexsanity"] == Toggle.option_true:
+ for byte_i, byte in enumerate(pokedex_caught_bytes):
+ for i in range(8):
+ if byte & (1 << i) != 0:
+ dex_number = (byte_i * 8 + i) + 1
+
+ location_id = dex_number + BASE_OFFSET + POKEDEX_OFFSET
+ if location_id in ctx.server_locations:
+ local_checked_locations.add(location_id)
+
+ # Count legendary hunt flags
+ if ctx.slot_data["goal"] == Goal.option_legendary_hunt:
+ # If legendary hunt doesn't require catching, add defeated legendaries to caught_legendaries
+ if ctx.slot_data["legendary_hunt_catch"] == Toggle.option_false:
+ for legendary, is_defeated in defeated_legendaries.items():
+ if is_defeated:
+ caught_legendaries[legendary] = True
+
+ num_caught = 0
+ for legendary, is_caught in caught_legendaries.items():
+ if is_caught and legendary in [LEGENDARY_NAMES[name] for name in ctx.slot_data["allowed_legendary_hunt_encounters"]]:
+ num_caught += 1
+
+ if num_caught >= ctx.slot_data["legendary_hunt_count"]:
+ game_clear = True
+
# Send locations
if local_checked_locations != self.local_checked_locations:
self.local_checked_locations = local_checked_locations
@@ -232,14 +324,14 @@ async def game_watcher(self, ctx: "BizHawkClientContext") -> None:
if local_checked_locations is not None:
await ctx.send_msgs([{
"cmd": "LocationChecks",
- "locations": list(local_checked_locations)
+ "locations": list(local_checked_locations),
}])
# Send game clear
if not ctx.finished_game and game_clear:
await ctx.send_msgs([{
"cmd": "StatusUpdate",
- "status": ClientStatus.CLIENT_GOAL
+ "status": ClientStatus.CLIENT_GOAL,
}])
# Send tracker event flags
@@ -254,7 +346,7 @@ async def game_watcher(self, ctx: "BizHawkClientContext") -> None:
"key": f"pokemon_emerald_events_{ctx.team}_{ctx.slot}",
"default": 0,
"want_reply": False,
- "operations": [{"operation": "or", "value": event_bitfield}]
+ "operations": [{"operation": "or", "value": event_bitfield}],
}])
self.local_set_events = local_set_events
@@ -269,9 +361,313 @@ async def game_watcher(self, ctx: "BizHawkClientContext") -> None:
"key": f"pokemon_emerald_keys_{ctx.team}_{ctx.slot}",
"default": 0,
"want_reply": False,
- "operations": [{"operation": "or", "value": key_bitfield}]
+ "operations": [{"operation": "or", "value": key_bitfield}],
}])
self.local_found_key_items = local_found_key_items
+
+ if ctx.slot_data["goal"] == Goal.option_legendary_hunt:
+ if caught_legendaries != self.local_defeated_legendaries and ctx.slot is not None:
+ legendary_bitfield = 0
+ for i, legendary_name in enumerate(LEGENDARY_NAMES.values()):
+ if caught_legendaries[legendary_name]:
+ legendary_bitfield |= 1 << i
+
+ await ctx.send_msgs([{
+ "cmd": "Set",
+ "key": f"pokemon_emerald_legendaries_{ctx.team}_{ctx.slot}",
+ "default": 0,
+ "want_reply": False,
+ "operations": [{"operation": "or", "value": legendary_bitfield}],
+ }])
+ self.local_defeated_legendaries = caught_legendaries
except bizhawk.RequestFailedError:
# Exit handler and return to main loop to reconnect
pass
+
+ async def handle_death_link(self, ctx: "BizHawkClientContext", guards: Dict[str, Tuple[int, bytes, str]]) -> None:
+ """
+ Checks whether the player has died while connected and sends a death link if so. Queues a death link in the game
+ if a new one has been received.
+ """
+ if ctx.slot_data.get("death_link", Toggle.option_false) == Toggle.option_true:
+ if "DeathLink" not in ctx.tags:
+ await ctx.update_death_link(True)
+ self.previous_death_link = ctx.last_death_link
+
+ sb1_address = int.from_bytes(guards["SAVE BLOCK 1"][1], "little")
+ sb2_address = int.from_bytes(guards["SAVE BLOCK 2"][1], "little")
+
+ read_result = await bizhawk.guarded_read(
+ ctx.bizhawk_ctx, [
+ (sb1_address + 0x177C + (52 * 4), 4, "System Bus"), # White out stat
+ (sb1_address + 0x177C + (22 * 4), 4, "System Bus"), # Canary stat
+ (sb2_address + 0xAC, 4, "System Bus"), # Encryption key
+ ],
+ [guards["SAVE BLOCK 1"], guards["SAVE BLOCK 2"]]
+ )
+ if read_result is None: # Save block moved
+ return
+
+ encryption_key = int.from_bytes(read_result[2], "little")
+ times_whited_out = int.from_bytes(read_result[0], "little") ^ encryption_key
+
+ # Canary is an unused stat that will always be 0. There is a low chance that we've done this read on
+ # a frame where the user has just entered a battle and the encryption key has been changed, but the data
+ # has not yet been encrypted with the new key. If `canary` is 0, `times_whited_out` is correct.
+ canary = int.from_bytes(read_result[1], "little") ^ encryption_key
+
+ # Skip all deathlink code if save is not yet loaded (encryption key is zero) or white out stat not yet
+ # initialized (starts at 100 as a safety for subtracting values from an unsigned int).
+ if canary == 0 and encryption_key != 0 and times_whited_out >= 100:
+ if self.previous_death_link != ctx.last_death_link:
+ self.previous_death_link = ctx.last_death_link
+ if self.ignore_next_death_link:
+ self.ignore_next_death_link = False
+ else:
+ await bizhawk.write(
+ ctx.bizhawk_ctx,
+ [(data.ram_addresses["gArchipelagoDeathLinkQueued"], [1], "System Bus")]
+ )
+
+ if self.death_counter is None:
+ self.death_counter = times_whited_out
+ elif times_whited_out > self.death_counter:
+ await ctx.send_death(f"{ctx.player_names[ctx.slot]} is out of usable POKéMON! "
+ f"{ctx.player_names[ctx.slot]} whited out!")
+ self.ignore_next_death_link = True
+ self.death_counter = times_whited_out
+
+ async def handle_received_items(self, ctx: "BizHawkClientContext", guards: Dict[str, Tuple[int, bytes, str]]) -> None:
+ """
+ Checks the index of the most recently received item and whether the item queue is full. Writes the next item
+ into the game if necessary.
+ """
+ received_item_address = data.ram_addresses["gArchipelagoReceivedItem"]
+
+ sb1_address = int.from_bytes(guards["SAVE BLOCK 1"][1], "little")
+
+ read_result = await bizhawk.guarded_read(
+ ctx.bizhawk_ctx,
+ [
+ (sb1_address + 0x3778, 2, "System Bus"), # Number of received items
+ (received_item_address + 4, 1, "System Bus") # Received item struct full?
+ ],
+ [guards["IN OVERWORLD"], guards["SAVE BLOCK 1"]]
+ )
+ if read_result is None: # Not in overworld, or save block moved
+ return
+
+ num_received_items = int.from_bytes(read_result[0], "little")
+ received_item_is_empty = read_result[1][0] == 0
+
+ # If the game hasn't received all items yet and the received item struct doesn't contain an item, then
+ # fill it with the next item
+ if num_received_items < len(ctx.items_received) and received_item_is_empty:
+ next_item = ctx.items_received[num_received_items]
+ should_display = 1 if next_item.flags & 1 or next_item.player == ctx.slot else 0
+ await bizhawk.write(ctx.bizhawk_ctx, [
+ (received_item_address + 0, (next_item.item - BASE_OFFSET).to_bytes(2, "little"), "System Bus"),
+ (received_item_address + 2, (num_received_items + 1).to_bytes(2, "little"), "System Bus"),
+ (received_item_address + 4, [1], "System Bus"),
+ (received_item_address + 5, [should_display], "System Bus"),
+ ])
+
+ async def handle_wonder_trade(self, ctx: "BizHawkClientContext", guards: Dict[str, Tuple[int, bytes, str]]) -> None:
+ """
+ Read wonder trade status from save data and either send a queued pokemon to data storage or attempt to retrieve
+ one from data storage and write it into the save.
+ """
+ from CommonClient import logger
+
+ sb1_address = int.from_bytes(guards["SAVE BLOCK 1"][1], "little")
+
+ read_result = await bizhawk.guarded_read(
+ ctx.bizhawk_ctx,
+ [
+ (sb1_address + 0x377C, 0x50, "System Bus"), # Wonder trade data
+ (sb1_address + 0x37CC, 1, "System Bus"), # Is wonder trade sent
+ ],
+ [guards["IN OVERWORLD"], guards["SAVE BLOCK 1"]]
+ )
+
+ if read_result is not None:
+ wonder_trade_pokemon_data = read_result[0]
+ trade_is_sent = read_result[1][0]
+
+ if trade_is_sent == 0 and wonder_trade_pokemon_data[19] == 2:
+ # Game has wonder trade data to send. Send it to data storage, remove it from the game's memory,
+ # and mark that the game is waiting on receiving a trade
+ Utils.async_start(self.wonder_trade_send(ctx, pokemon_data_to_json(wonder_trade_pokemon_data)))
+ await bizhawk.write(ctx.bizhawk_ctx, [
+ (sb1_address + 0x377C, bytes(0x50), "System Bus"),
+ (sb1_address + 0x37CC, [1], "System Bus"),
+ ])
+ elif trade_is_sent != 0 and wonder_trade_pokemon_data[19] != 2:
+ # Game is waiting on receiving a trade. See if there are any available trades that were not
+ # sent by this player, and if so, try to receive one.
+ if self.wonder_trade_cooldown_timer <= 0 and f"pokemon_wonder_trades_{ctx.team}" in ctx.stored_data:
+ if any(item[0] != ctx.slot
+ for key, item in ctx.stored_data.get(f"pokemon_wonder_trades_{ctx.team}", {}).items()
+ if key != "_lock" and orjson.loads(item[1])["species"] <= 386):
+ received_trade = await self.wonder_trade_receive(ctx)
+ if received_trade is None:
+ self.wonder_trade_cooldown_timer = self.wonder_trade_cooldown
+ self.wonder_trade_cooldown *= 2
+ self.wonder_trade_cooldown += random.randrange(0, 500)
+ else:
+ await bizhawk.write(ctx.bizhawk_ctx, [
+ (sb1_address + 0x377C, json_to_pokemon_data(received_trade), "System Bus"),
+ ])
+ logger.info("Wonder trade received!")
+ self.wonder_trade_cooldown = 5000
+
+ else:
+ # Very approximate "time since last loop", but extra delay is fine for this
+ self.wonder_trade_cooldown_timer -= int(ctx.watcher_timeout * 1000)
+
+ async def wonder_trade_acquire(self, ctx: "BizHawkClientContext", keep_trying: bool = False) -> Optional[dict]:
+ """
+ Acquires a lock on the `pokemon_wonder_trades_{ctx.team}` key in
+ datastorage. Locking the key means you have exclusive access
+ to modifying the value until you unlock it or the key expires (5
+ seconds).
+
+ If `keep_trying` is `True`, it will keep trying to acquire the lock
+ until successful. Otherwise it will return `None` if it fails to
+ acquire the lock.
+ """
+ while not ctx.exit_event.is_set():
+ lock = int(time.time_ns() / 1000000)
+ message_uuid = str(uuid.uuid4())
+ await ctx.send_msgs([{
+ "cmd": "Set",
+ "key": f"pokemon_wonder_trades_{ctx.team}",
+ "default": {"_lock": 0},
+ "want_reply": True,
+ "operations": [{"operation": "update", "value": {"_lock": lock}}],
+ "uuid": message_uuid,
+ }])
+
+ self.wonder_trade_update_event.clear()
+ try:
+ await asyncio.wait_for(self.wonder_trade_update_event.wait(), 5)
+ except asyncio.TimeoutError:
+ if not keep_trying:
+ return None
+ continue
+
+ reply = copy.deepcopy(self.latest_wonder_trade_reply)
+
+ # Make sure the most recently received update was triggered by our lock attempt
+ if reply.get("uuid", None) != message_uuid:
+ if not keep_trying:
+ return None
+ await asyncio.sleep(self.wonder_trade_cooldown)
+ continue
+
+ # Make sure the current value of the lock is what we set it to
+ # (I think this should theoretically never run)
+ if reply["value"]["_lock"] != lock:
+ if not keep_trying:
+ return None
+ await asyncio.sleep(self.wonder_trade_cooldown)
+ continue
+
+ # Make sure that the lock value we replaced is at least 5 seconds old
+ # If it was unlocked before our change, its value was 0 and it will look decades old
+ if lock - reply["original_value"]["_lock"] < 5000:
+ # Multiple clients trying to lock the key may get stuck in a loop of checking the lock
+ # by trying to set it, which will extend its expiration. So if we see that the lock was
+ # too new when we replaced it, we should wait for increasingly longer periods so that
+ # eventually the lock will expire and a client will acquire it.
+ self.wonder_trade_cooldown *= 2
+ self.wonder_trade_cooldown += random.randrange(0, 500)
+
+ if not keep_trying:
+ self.wonder_trade_cooldown_timer = self.wonder_trade_cooldown
+ return None
+ await asyncio.sleep(self.wonder_trade_cooldown)
+ continue
+
+ # We have the lock, reset the cooldown and return
+ self.wonder_trade_cooldown = 5000
+ return reply
+
+ async def wonder_trade_send(self, ctx: "BizHawkClientContext", data: str) -> None:
+ """
+ Sends a wonder trade pokemon to data storage
+ """
+ from CommonClient import logger
+
+ reply = await self.wonder_trade_acquire(ctx, True)
+
+ wonder_trade_slot = 0
+ while str(wonder_trade_slot) in reply["value"]:
+ wonder_trade_slot += 1
+
+ await ctx.send_msgs([{
+ "cmd": "Set",
+ "key": f"pokemon_wonder_trades_{ctx.team}",
+ "default": {"_lock": 0},
+ "operations": [{"operation": "update", "value": {
+ "_lock": 0,
+ str(wonder_trade_slot): (ctx.slot, data),
+ }}],
+ }])
+
+ logger.info("Wonder trade sent! We'll notify you here when a trade has been found.")
+
+ async def wonder_trade_receive(self, ctx: "BizHawkClientContext") -> Optional[str]:
+ """
+ Tries to pop a pokemon out of the wonder trades. Returns `None` if
+ for some reason it can't immediately remove a compatible pokemon.
+ """
+ reply = await self.wonder_trade_acquire(ctx)
+
+ if reply is None:
+ return None
+
+ candidate_slots = [
+ int(slot)
+ for slot in reply["value"]
+ if slot != "_lock" \
+ and reply["value"][slot][0] != ctx.slot \
+ and orjson.loads(reply["value"][slot][1])["species"] <= 386
+ ]
+
+ if len(candidate_slots) == 0:
+ await ctx.send_msgs([{
+ "cmd": "Set",
+ "key": f"pokemon_wonder_trades_{ctx.team}",
+ "default": {"_lock": 0},
+ "operations": [{"operation": "update", "value": {"_lock": 0}}],
+ }])
+ return None
+
+ wonder_trade_slot = max(candidate_slots)
+
+ await ctx.send_msgs([{
+ "cmd": "Set",
+ "key": f"pokemon_wonder_trades_{ctx.team}",
+ "default": {"_lock": 0},
+ "operations": [
+ {"operation": "update", "value": {"_lock": 0}},
+ {"operation": "pop", "value": str(wonder_trade_slot)},
+ ]
+ }])
+
+ return reply["value"][str(wonder_trade_slot)][1]
+
+ def on_package(self, ctx: "BizHawkClientContext", cmd: str, args: dict) -> None:
+ if cmd == "Connected":
+ Utils.async_start(ctx.send_msgs([{
+ "cmd": "SetNotify",
+ "keys": [f"pokemon_wonder_trades_{ctx.team}"],
+ }, {
+ "cmd": "Get",
+ "keys": [f"pokemon_wonder_trades_{ctx.team}"],
+ }]))
+ elif cmd == "SetReply":
+ if args.get("key", "") == f"pokemon_wonder_trades_{ctx.team}":
+ self.latest_wonder_trade_reply = args
+ self.wonder_trade_update_event.set()
diff --git a/worlds/pokemon_emerald/data.py b/worlds/pokemon_emerald/data.py
index bc51d84963c5..786740a9e48f 100644
--- a/worlds/pokemon_emerald/data.py
+++ b/worlds/pokemon_emerald/data.py
@@ -5,7 +5,6 @@
and sorting, and Warp methods.
"""
from dataclasses import dataclass
-import copy
from enum import IntEnum
import orjson
from typing import Dict, List, NamedTuple, Optional, Set, FrozenSet, Tuple, Any, Union
@@ -16,6 +15,25 @@
BASE_OFFSET = 3860000
+POKEDEX_OFFSET = 10000
+
+IGNORABLE_MAPS = {
+ "MAP_ALTERING_CAVE",
+ "MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1",
+ "MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2",
+ "MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3",
+}
+"""These maps exist but don't show up in the rando or are unused, and so should be discarded"""
+
+POSTGAME_MAPS = {
+ "MAP_DESERT_UNDERPASS",
+ "MAP_SAFARI_ZONE_NORTHEAST",
+ "MAP_SAFARI_ZONE_SOUTHEAST",
+ "MAP_METEOR_FALLS_STEVENS_CAVE",
+}
+"""These maps have encounters and are locked behind beating the champion. Those encounter slots should be ignored for logical access to a species."""
+
+NUM_REAL_SPECIES = 386
class Warp:
@@ -55,14 +73,14 @@ def encode(self) -> str:
return f"{self.source_map}:{source_ids_string}/{self.dest_map}:{dest_ids_string}{'!' if self.is_one_way else ''}"
- def connects_to(self, other: 'Warp') -> bool:
+ def connects_to(self, other: "Warp") -> bool:
"""
Returns true if this warp sends the player to `other`
"""
return self.dest_map == other.source_map and set(self.dest_ids) <= set(other.source_ids)
@staticmethod
- def decode(encoded_string: str) -> 'Warp':
+ def decode(encoded_string: str) -> "Warp":
"""
Create a Warp object from an encoded string
"""
@@ -87,6 +105,7 @@ def decode(encoded_string: str) -> 'Warp':
class ItemData(NamedTuple):
label: str
item_id: int
+ modern_id: Optional[int]
classification: ItemClassification
tags: FrozenSet[str]
@@ -96,11 +115,25 @@ class LocationData(NamedTuple):
label: str
parent_region: str
default_item: int
- rom_address: int
+ address: Union[int, List[int]]
flag: int
tags: FrozenSet[str]
+class EncounterTableData(NamedTuple):
+ slots: List[int]
+ address: int
+
+
+@dataclass
+class MapData:
+ name: str
+ header_address: int
+ land_encounters: Optional[EncounterTableData]
+ water_encounters: Optional[EncounterTableData]
+ fishing_encounters: Optional[EncounterTableData]
+
+
class EventData(NamedTuple):
name: str
parent_region: str
@@ -108,13 +141,21 @@ class EventData(NamedTuple):
class RegionData:
name: str
+ parent_map: MapData
+ has_grass: bool
+ has_water: bool
+ has_fishing: bool
exits: List[str]
warps: List[str]
locations: List[str]
events: List[EventData]
- def __init__(self, name: str):
+ def __init__(self, name: str, parent_map: MapData, has_grass: bool, has_water: bool, has_fishing: bool):
self.name = name
+ self.parent_map = parent_map
+ self.has_grass = has_grass
+ self.has_water = has_water
+ self.has_fishing = has_fishing
self.exits = []
self.warps = []
self.locations = []
@@ -181,9 +222,9 @@ class EvolutionData(NamedTuple):
species_id: int
-class StaticEncounterData(NamedTuple):
+class MiscPokemonData(NamedTuple):
species_id: int
- rom_address: int
+ address: int
@dataclass
@@ -191,16 +232,18 @@ class SpeciesData:
name: str
label: str
species_id: int
+ national_dex_number: int
base_stats: BaseStats
types: Tuple[int, int]
abilities: Tuple[int, int]
evolutions: List[EvolutionData]
pre_evolution: Optional[int]
catch_rate: int
+ friendship: int
learnset: List[LearnsetMove]
tm_hm_compatibility: int
- learnset_rom_address: int
- rom_address: int
+ learnset_address: int
+ address: int
class AbilityData(NamedTuple):
@@ -208,19 +251,6 @@ class AbilityData(NamedTuple):
label: str
-class EncounterTableData(NamedTuple):
- slots: List[int]
- rom_address: int
-
-
-@dataclass
-class MapData:
- name: str
- land_encounters: Optional[EncounterTableData]
- water_encounters: Optional[EncounterTableData]
- fishing_encounters: Optional[EncounterTableData]
-
-
class TrainerPokemonDataTypeEnum(IntEnum):
NO_ITEM_DEFAULT_MOVES = 0
ITEM_DEFAULT_MOVES = 1
@@ -250,15 +280,15 @@ class TrainerPokemonData:
class TrainerPartyData:
pokemon: List[TrainerPokemonData]
pokemon_data_type: TrainerPokemonDataTypeEnum
- rom_address: int
+ address: int
@dataclass
class TrainerData:
trainer_id: int
party: TrainerPartyData
- rom_address: int
- battle_script_rom_address: int
+ address: int
+ script_address: int
class PokemonEmeraldData:
@@ -269,11 +299,13 @@ class PokemonEmeraldData:
regions: Dict[str, RegionData]
locations: Dict[str, LocationData]
items: Dict[int, ItemData]
- species: List[Optional[SpeciesData]]
- static_encounters: List[StaticEncounterData]
+ species: Dict[int, SpeciesData]
+ legendary_encounters: List[MiscPokemonData]
+ misc_pokemon: List[MiscPokemonData]
tmhm_moves: List[int]
abilities: List[AbilityData]
- maps: List[MapData]
+ move_labels: Dict[str, int]
+ maps: Dict[str, MapData]
warps: Dict[str, Warp]
warp_map: Dict[str, Optional[str]]
trainers: List[TrainerData]
@@ -286,29 +318,20 @@ def __init__(self) -> None:
self.regions = {}
self.locations = {}
self.items = {}
- self.species = []
- self.static_encounters = []
+ self.species = {}
+ self.legendary_encounters = []
+ self.misc_pokemon = []
self.tmhm_moves = []
self.abilities = []
- self.maps = []
+ self.move_labels = {}
+ self.maps = {}
self.warps = {}
self.warp_map = {}
self.trainers = []
def load_json_data(data_name: str) -> Union[List[Any], Dict[str, Any]]:
- return orjson.loads(pkgutil.get_data(__name__, "data/" + data_name).decode('utf-8-sig'))
-
-
-data = PokemonEmeraldData()
-
-def create_data_copy() -> PokemonEmeraldData:
- new_copy = PokemonEmeraldData()
- new_copy.species = copy.deepcopy(data.species)
- new_copy.tmhm_moves = copy.deepcopy(data.tmhm_moves)
- new_copy.maps = copy.deepcopy(data.maps)
- new_copy.static_encounters = copy.deepcopy(data.static_encounters)
- new_copy.trainers = copy.deepcopy(data.trainers)
+ return orjson.loads(pkgutil.get_data(__name__, "data/" + data_name).decode("utf-8-sig"))
def _init() -> None:
@@ -319,6 +342,39 @@ def _init() -> None:
location_attributes_json = load_json_data("locations.json")
+ # Create map data
+ for map_name, map_json in extracted_data["maps"].items():
+ if map_name in IGNORABLE_MAPS:
+ continue
+
+ land_encounters = None
+ water_encounters = None
+ fishing_encounters = None
+
+ if "land_encounters" in map_json:
+ land_encounters = EncounterTableData(
+ map_json["land_encounters"]["slots"],
+ map_json["land_encounters"]["address"]
+ )
+ if "water_encounters" in map_json:
+ water_encounters = EncounterTableData(
+ map_json["water_encounters"]["slots"],
+ map_json["water_encounters"]["address"]
+ )
+ if "fishing_encounters" in map_json:
+ fishing_encounters = EncounterTableData(
+ map_json["fishing_encounters"]["slots"],
+ map_json["fishing_encounters"]["address"]
+ )
+
+ data.maps[map_name] = MapData(
+ map_name,
+ map_json["header_address"],
+ land_encounters,
+ water_encounters,
+ fishing_encounters
+ )
+
# Load/merge region json files
region_json_list = []
for file in pkg_resources.resource_listdir(__name__, "data/regions"):
@@ -338,7 +394,13 @@ def _init() -> None:
data.regions = {}
for region_name, region_json in regions_json.items():
- new_region = RegionData(region_name)
+ new_region = RegionData(
+ region_name,
+ data.maps[region_json["parent_map"]],
+ region_json["has_grass"],
+ region_json["has_water"],
+ region_json["has_fishing"]
+ )
# Locations
for location_name in region_json["locations"]:
@@ -346,15 +408,35 @@ def _init() -> None:
raise AssertionError(f"Location [{location_name}] was claimed by multiple regions")
location_json = extracted_data["locations"][location_name]
- new_location = LocationData(
- location_name,
- location_attributes_json[location_name]["label"],
- region_name,
- location_json["default_item"],
- location_json["rom_address"],
- location_json["flag"],
- frozenset(location_attributes_json[location_name]["tags"])
- )
+ if location_name.startswith("TRAINER_BRENDAN_") or location_name.startswith("TRAINER_MAY_"):
+ import re
+ locale = re.match("TRAINER_BRENDAN_([A-Z0-9_]+)_MUDKIP_REWARD", location_name).group(1)
+ alternate_rival_jsons = [extracted_data["locations"][alternate] for alternate in [
+ f"TRAINER_BRENDAN_{locale}_TORCHIC_REWARD",
+ f"TRAINER_BRENDAN_{locale}_TREECKO_REWARD",
+ f"TRAINER_MAY_{locale}_MUDKIP_REWARD",
+ f"TRAINER_MAY_{locale}_TORCHIC_REWARD",
+ f"TRAINER_MAY_{locale}_TREECKO_REWARD",
+ ]]
+ new_location = LocationData(
+ location_name,
+ location_attributes_json[location_name]["label"],
+ region_name,
+ location_json["default_item"],
+ [location_json["address"]] + [j["address"] for j in alternate_rival_jsons],
+ location_json["flag"],
+ frozenset(location_attributes_json[location_name]["tags"])
+ )
+ else:
+ new_location = LocationData(
+ location_name,
+ location_attributes_json[location_name]["label"],
+ region_name,
+ location_json["default_item"],
+ location_json["address"],
+ location_json["flag"],
+ frozenset(location_attributes_json[location_name]["tags"])
+ )
new_region.locations.append(location_name)
data.locations[location_name] = new_location
claimed_locations.add(location_name)
@@ -401,6 +483,7 @@ def _init() -> None:
data.items[data.constants[item_constant_name]] = ItemData(
attributes["label"],
data.constants[item_constant_name],
+ attributes["modern_id"],
item_classification,
frozenset(attributes["tags"])
)
@@ -408,408 +491,408 @@ def _init() -> None:
# Create species data
# Excludes extras like copies of Unown and special species values like SPECIES_EGG.
- all_species: List[Tuple[str, str]] = [
- ("SPECIES_BULBASAUR", "Bulbasaur"),
- ("SPECIES_IVYSAUR", "Ivysaur"),
- ("SPECIES_VENUSAUR", "Venusaur"),
- ("SPECIES_CHARMANDER", "Charmander"),
- ("SPECIES_CHARMELEON", "Charmeleon"),
- ("SPECIES_CHARIZARD", "Charizard"),
- ("SPECIES_SQUIRTLE", "Squirtle"),
- ("SPECIES_WARTORTLE", "Wartortle"),
- ("SPECIES_BLASTOISE", "Blastoise"),
- ("SPECIES_CATERPIE", "Caterpie"),
- ("SPECIES_METAPOD", "Metapod"),
- ("SPECIES_BUTTERFREE", "Butterfree"),
- ("SPECIES_WEEDLE", "Weedle"),
- ("SPECIES_KAKUNA", "Kakuna"),
- ("SPECIES_BEEDRILL", "Beedrill"),
- ("SPECIES_PIDGEY", "Pidgey"),
- ("SPECIES_PIDGEOTTO", "Pidgeotto"),
- ("SPECIES_PIDGEOT", "Pidgeot"),
- ("SPECIES_RATTATA", "Rattata"),
- ("SPECIES_RATICATE", "Raticate"),
- ("SPECIES_SPEAROW", "Spearow"),
- ("SPECIES_FEAROW", "Fearow"),
- ("SPECIES_EKANS", "Ekans"),
- ("SPECIES_ARBOK", "Arbok"),
- ("SPECIES_PIKACHU", "Pikachu"),
- ("SPECIES_RAICHU", "Raichu"),
- ("SPECIES_SANDSHREW", "Sandshrew"),
- ("SPECIES_SANDSLASH", "Sandslash"),
- ("SPECIES_NIDORAN_F", "Nidoran Female"),
- ("SPECIES_NIDORINA", "Nidorina"),
- ("SPECIES_NIDOQUEEN", "Nidoqueen"),
- ("SPECIES_NIDORAN_M", "Nidoran Male"),
- ("SPECIES_NIDORINO", "Nidorino"),
- ("SPECIES_NIDOKING", "Nidoking"),
- ("SPECIES_CLEFAIRY", "Clefairy"),
- ("SPECIES_CLEFABLE", "Clefable"),
- ("SPECIES_VULPIX", "Vulpix"),
- ("SPECIES_NINETALES", "Ninetales"),
- ("SPECIES_JIGGLYPUFF", "Jigglypuff"),
- ("SPECIES_WIGGLYTUFF", "Wigglytuff"),
- ("SPECIES_ZUBAT", "Zubat"),
- ("SPECIES_GOLBAT", "Golbat"),
- ("SPECIES_ODDISH", "Oddish"),
- ("SPECIES_GLOOM", "Gloom"),
- ("SPECIES_VILEPLUME", "Vileplume"),
- ("SPECIES_PARAS", "Paras"),
- ("SPECIES_PARASECT", "Parasect"),
- ("SPECIES_VENONAT", "Venonat"),
- ("SPECIES_VENOMOTH", "Venomoth"),
- ("SPECIES_DIGLETT", "Diglett"),
- ("SPECIES_DUGTRIO", "Dugtrio"),
- ("SPECIES_MEOWTH", "Meowth"),
- ("SPECIES_PERSIAN", "Persian"),
- ("SPECIES_PSYDUCK", "Psyduck"),
- ("SPECIES_GOLDUCK", "Golduck"),
- ("SPECIES_MANKEY", "Mankey"),
- ("SPECIES_PRIMEAPE", "Primeape"),
- ("SPECIES_GROWLITHE", "Growlithe"),
- ("SPECIES_ARCANINE", "Arcanine"),
- ("SPECIES_POLIWAG", "Poliwag"),
- ("SPECIES_POLIWHIRL", "Poliwhirl"),
- ("SPECIES_POLIWRATH", "Poliwrath"),
- ("SPECIES_ABRA", "Abra"),
- ("SPECIES_KADABRA", "Kadabra"),
- ("SPECIES_ALAKAZAM", "Alakazam"),
- ("SPECIES_MACHOP", "Machop"),
- ("SPECIES_MACHOKE", "Machoke"),
- ("SPECIES_MACHAMP", "Machamp"),
- ("SPECIES_BELLSPROUT", "Bellsprout"),
- ("SPECIES_WEEPINBELL", "Weepinbell"),
- ("SPECIES_VICTREEBEL", "Victreebel"),
- ("SPECIES_TENTACOOL", "Tentacool"),
- ("SPECIES_TENTACRUEL", "Tentacruel"),
- ("SPECIES_GEODUDE", "Geodude"),
- ("SPECIES_GRAVELER", "Graveler"),
- ("SPECIES_GOLEM", "Golem"),
- ("SPECIES_PONYTA", "Ponyta"),
- ("SPECIES_RAPIDASH", "Rapidash"),
- ("SPECIES_SLOWPOKE", "Slowpoke"),
- ("SPECIES_SLOWBRO", "Slowbro"),
- ("SPECIES_MAGNEMITE", "Magnemite"),
- ("SPECIES_MAGNETON", "Magneton"),
- ("SPECIES_FARFETCHD", "Farfetch'd"),
- ("SPECIES_DODUO", "Doduo"),
- ("SPECIES_DODRIO", "Dodrio"),
- ("SPECIES_SEEL", "Seel"),
- ("SPECIES_DEWGONG", "Dewgong"),
- ("SPECIES_GRIMER", "Grimer"),
- ("SPECIES_MUK", "Muk"),
- ("SPECIES_SHELLDER", "Shellder"),
- ("SPECIES_CLOYSTER", "Cloyster"),
- ("SPECIES_GASTLY", "Gastly"),
- ("SPECIES_HAUNTER", "Haunter"),
- ("SPECIES_GENGAR", "Gengar"),
- ("SPECIES_ONIX", "Onix"),
- ("SPECIES_DROWZEE", "Drowzee"),
- ("SPECIES_HYPNO", "Hypno"),
- ("SPECIES_KRABBY", "Krabby"),
- ("SPECIES_KINGLER", "Kingler"),
- ("SPECIES_VOLTORB", "Voltorb"),
- ("SPECIES_ELECTRODE", "Electrode"),
- ("SPECIES_EXEGGCUTE", "Exeggcute"),
- ("SPECIES_EXEGGUTOR", "Exeggutor"),
- ("SPECIES_CUBONE", "Cubone"),
- ("SPECIES_MAROWAK", "Marowak"),
- ("SPECIES_HITMONLEE", "Hitmonlee"),
- ("SPECIES_HITMONCHAN", "Hitmonchan"),
- ("SPECIES_LICKITUNG", "Lickitung"),
- ("SPECIES_KOFFING", "Koffing"),
- ("SPECIES_WEEZING", "Weezing"),
- ("SPECIES_RHYHORN", "Rhyhorn"),
- ("SPECIES_RHYDON", "Rhydon"),
- ("SPECIES_CHANSEY", "Chansey"),
- ("SPECIES_TANGELA", "Tangela"),
- ("SPECIES_KANGASKHAN", "Kangaskhan"),
- ("SPECIES_HORSEA", "Horsea"),
- ("SPECIES_SEADRA", "Seadra"),
- ("SPECIES_GOLDEEN", "Goldeen"),
- ("SPECIES_SEAKING", "Seaking"),
- ("SPECIES_STARYU", "Staryu"),
- ("SPECIES_STARMIE", "Starmie"),
- ("SPECIES_MR_MIME", "Mr. Mime"),
- ("SPECIES_SCYTHER", "Scyther"),
- ("SPECIES_JYNX", "Jynx"),
- ("SPECIES_ELECTABUZZ", "Electabuzz"),
- ("SPECIES_MAGMAR", "Magmar"),
- ("SPECIES_PINSIR", "Pinsir"),
- ("SPECIES_TAUROS", "Tauros"),
- ("SPECIES_MAGIKARP", "Magikarp"),
- ("SPECIES_GYARADOS", "Gyarados"),
- ("SPECIES_LAPRAS", "Lapras"),
- ("SPECIES_DITTO", "Ditto"),
- ("SPECIES_EEVEE", "Eevee"),
- ("SPECIES_VAPOREON", "Vaporeon"),
- ("SPECIES_JOLTEON", "Jolteon"),
- ("SPECIES_FLAREON", "Flareon"),
- ("SPECIES_PORYGON", "Porygon"),
- ("SPECIES_OMANYTE", "Omanyte"),
- ("SPECIES_OMASTAR", "Omastar"),
- ("SPECIES_KABUTO", "Kabuto"),
- ("SPECIES_KABUTOPS", "Kabutops"),
- ("SPECIES_AERODACTYL", "Aerodactyl"),
- ("SPECIES_SNORLAX", "Snorlax"),
- ("SPECIES_ARTICUNO", "Articuno"),
- ("SPECIES_ZAPDOS", "Zapdos"),
- ("SPECIES_MOLTRES", "Moltres"),
- ("SPECIES_DRATINI", "Dratini"),
- ("SPECIES_DRAGONAIR", "Dragonair"),
- ("SPECIES_DRAGONITE", "Dragonite"),
- ("SPECIES_MEWTWO", "Mewtwo"),
- ("SPECIES_MEW", "Mew"),
- ("SPECIES_CHIKORITA", "Chikorita"),
- ("SPECIES_BAYLEEF", "Bayleaf"),
- ("SPECIES_MEGANIUM", "Meganium"),
- ("SPECIES_CYNDAQUIL", "Cindaquil"),
- ("SPECIES_QUILAVA", "Quilava"),
- ("SPECIES_TYPHLOSION", "Typhlosion"),
- ("SPECIES_TOTODILE", "Totodile"),
- ("SPECIES_CROCONAW", "Croconaw"),
- ("SPECIES_FERALIGATR", "Feraligatr"),
- ("SPECIES_SENTRET", "Sentret"),
- ("SPECIES_FURRET", "Furret"),
- ("SPECIES_HOOTHOOT", "Hoothoot"),
- ("SPECIES_NOCTOWL", "Noctowl"),
- ("SPECIES_LEDYBA", "Ledyba"),
- ("SPECIES_LEDIAN", "Ledian"),
- ("SPECIES_SPINARAK", "Spinarak"),
- ("SPECIES_ARIADOS", "Ariados"),
- ("SPECIES_CROBAT", "Crobat"),
- ("SPECIES_CHINCHOU", "Chinchou"),
- ("SPECIES_LANTURN", "Lanturn"),
- ("SPECIES_PICHU", "Pichu"),
- ("SPECIES_CLEFFA", "Cleffa"),
- ("SPECIES_IGGLYBUFF", "Igglybuff"),
- ("SPECIES_TOGEPI", "Togepi"),
- ("SPECIES_TOGETIC", "Togetic"),
- ("SPECIES_NATU", "Natu"),
- ("SPECIES_XATU", "Xatu"),
- ("SPECIES_MAREEP", "Mareep"),
- ("SPECIES_FLAAFFY", "Flaafy"),
- ("SPECIES_AMPHAROS", "Ampharos"),
- ("SPECIES_BELLOSSOM", "Bellossom"),
- ("SPECIES_MARILL", "Marill"),
- ("SPECIES_AZUMARILL", "Azumarill"),
- ("SPECIES_SUDOWOODO", "Sudowoodo"),
- ("SPECIES_POLITOED", "Politoed"),
- ("SPECIES_HOPPIP", "Hoppip"),
- ("SPECIES_SKIPLOOM", "Skiploom"),
- ("SPECIES_JUMPLUFF", "Jumpluff"),
- ("SPECIES_AIPOM", "Aipom"),
- ("SPECIES_SUNKERN", "Sunkern"),
- ("SPECIES_SUNFLORA", "Sunflora"),
- ("SPECIES_YANMA", "Yanma"),
- ("SPECIES_WOOPER", "Wooper"),
- ("SPECIES_QUAGSIRE", "Quagsire"),
- ("SPECIES_ESPEON", "Espeon"),
- ("SPECIES_UMBREON", "Umbreon"),
- ("SPECIES_MURKROW", "Murkrow"),
- ("SPECIES_SLOWKING", "Slowking"),
- ("SPECIES_MISDREAVUS", "Misdreavus"),
- ("SPECIES_UNOWN", "Unown"),
- ("SPECIES_WOBBUFFET", "Wobbuffet"),
- ("SPECIES_GIRAFARIG", "Girafarig"),
- ("SPECIES_PINECO", "Pineco"),
- ("SPECIES_FORRETRESS", "Forretress"),
- ("SPECIES_DUNSPARCE", "Dunsparce"),
- ("SPECIES_GLIGAR", "Gligar"),
- ("SPECIES_STEELIX", "Steelix"),
- ("SPECIES_SNUBBULL", "Snubbull"),
- ("SPECIES_GRANBULL", "Granbull"),
- ("SPECIES_QWILFISH", "Qwilfish"),
- ("SPECIES_SCIZOR", "Scizor"),
- ("SPECIES_SHUCKLE", "Shuckle"),
- ("SPECIES_HERACROSS", "Heracross"),
- ("SPECIES_SNEASEL", "Sneasel"),
- ("SPECIES_TEDDIURSA", "Teddiursa"),
- ("SPECIES_URSARING", "Ursaring"),
- ("SPECIES_SLUGMA", "Slugma"),
- ("SPECIES_MAGCARGO", "Magcargo"),
- ("SPECIES_SWINUB", "Swinub"),
- ("SPECIES_PILOSWINE", "Piloswine"),
- ("SPECIES_CORSOLA", "Corsola"),
- ("SPECIES_REMORAID", "Remoraid"),
- ("SPECIES_OCTILLERY", "Octillery"),
- ("SPECIES_DELIBIRD", "Delibird"),
- ("SPECIES_MANTINE", "Mantine"),
- ("SPECIES_SKARMORY", "Skarmory"),
- ("SPECIES_HOUNDOUR", "Houndour"),
- ("SPECIES_HOUNDOOM", "Houndoom"),
- ("SPECIES_KINGDRA", "Kingdra"),
- ("SPECIES_PHANPY", "Phanpy"),
- ("SPECIES_DONPHAN", "Donphan"),
- ("SPECIES_PORYGON2", "Porygon2"),
- ("SPECIES_STANTLER", "Stantler"),
- ("SPECIES_SMEARGLE", "Smeargle"),
- ("SPECIES_TYROGUE", "Tyrogue"),
- ("SPECIES_HITMONTOP", "Hitmontop"),
- ("SPECIES_SMOOCHUM", "Smoochum"),
- ("SPECIES_ELEKID", "Elekid"),
- ("SPECIES_MAGBY", "Magby"),
- ("SPECIES_MILTANK", "Miltank"),
- ("SPECIES_BLISSEY", "Blissey"),
- ("SPECIES_RAIKOU", "Raikou"),
- ("SPECIES_ENTEI", "Entei"),
- ("SPECIES_SUICUNE", "Suicune"),
- ("SPECIES_LARVITAR", "Larvitar"),
- ("SPECIES_PUPITAR", "Pupitar"),
- ("SPECIES_TYRANITAR", "Tyranitar"),
- ("SPECIES_LUGIA", "Lugia"),
- ("SPECIES_HO_OH", "Ho-oh"),
- ("SPECIES_CELEBI", "Celebi"),
- ("SPECIES_TREECKO", "Treecko"),
- ("SPECIES_GROVYLE", "Grovyle"),
- ("SPECIES_SCEPTILE", "Sceptile"),
- ("SPECIES_TORCHIC", "Torchic"),
- ("SPECIES_COMBUSKEN", "Combusken"),
- ("SPECIES_BLAZIKEN", "Blaziken"),
- ("SPECIES_MUDKIP", "Mudkip"),
- ("SPECIES_MARSHTOMP", "Marshtomp"),
- ("SPECIES_SWAMPERT", "Swampert"),
- ("SPECIES_POOCHYENA", "Poochyena"),
- ("SPECIES_MIGHTYENA", "Mightyena"),
- ("SPECIES_ZIGZAGOON", "Zigzagoon"),
- ("SPECIES_LINOONE", "Linoon"),
- ("SPECIES_WURMPLE", "Wurmple"),
- ("SPECIES_SILCOON", "Silcoon"),
- ("SPECIES_BEAUTIFLY", "Beautifly"),
- ("SPECIES_CASCOON", "Cascoon"),
- ("SPECIES_DUSTOX", "Dustox"),
- ("SPECIES_LOTAD", "Lotad"),
- ("SPECIES_LOMBRE", "Lombre"),
- ("SPECIES_LUDICOLO", "Ludicolo"),
- ("SPECIES_SEEDOT", "Seedot"),
- ("SPECIES_NUZLEAF", "Nuzleaf"),
- ("SPECIES_SHIFTRY", "Shiftry"),
- ("SPECIES_NINCADA", "Nincada"),
- ("SPECIES_NINJASK", "Ninjask"),
- ("SPECIES_SHEDINJA", "Shedinja"),
- ("SPECIES_TAILLOW", "Taillow"),
- ("SPECIES_SWELLOW", "Swellow"),
- ("SPECIES_SHROOMISH", "Shroomish"),
- ("SPECIES_BRELOOM", "Breloom"),
- ("SPECIES_SPINDA", "Spinda"),
- ("SPECIES_WINGULL", "Wingull"),
- ("SPECIES_PELIPPER", "Pelipper"),
- ("SPECIES_SURSKIT", "Surskit"),
- ("SPECIES_MASQUERAIN", "Masquerain"),
- ("SPECIES_WAILMER", "Wailmer"),
- ("SPECIES_WAILORD", "Wailord"),
- ("SPECIES_SKITTY", "Skitty"),
- ("SPECIES_DELCATTY", "Delcatty"),
- ("SPECIES_KECLEON", "Kecleon"),
- ("SPECIES_BALTOY", "Baltoy"),
- ("SPECIES_CLAYDOL", "Claydol"),
- ("SPECIES_NOSEPASS", "Nosepass"),
- ("SPECIES_TORKOAL", "Torkoal"),
- ("SPECIES_SABLEYE", "Sableye"),
- ("SPECIES_BARBOACH", "Barboach"),
- ("SPECIES_WHISCASH", "Whiscash"),
- ("SPECIES_LUVDISC", "Luvdisc"),
- ("SPECIES_CORPHISH", "Corphish"),
- ("SPECIES_CRAWDAUNT", "Crawdaunt"),
- ("SPECIES_FEEBAS", "Feebas"),
- ("SPECIES_MILOTIC", "Milotic"),
- ("SPECIES_CARVANHA", "Carvanha"),
- ("SPECIES_SHARPEDO", "Sharpedo"),
- ("SPECIES_TRAPINCH", "Trapinch"),
- ("SPECIES_VIBRAVA", "Vibrava"),
- ("SPECIES_FLYGON", "Flygon"),
- ("SPECIES_MAKUHITA", "Makuhita"),
- ("SPECIES_HARIYAMA", "Hariyama"),
- ("SPECIES_ELECTRIKE", "Electrike"),
- ("SPECIES_MANECTRIC", "Manectric"),
- ("SPECIES_NUMEL", "Numel"),
- ("SPECIES_CAMERUPT", "Camerupt"),
- ("SPECIES_SPHEAL", "Spheal"),
- ("SPECIES_SEALEO", "Sealeo"),
- ("SPECIES_WALREIN", "Walrein"),
- ("SPECIES_CACNEA", "Cacnea"),
- ("SPECIES_CACTURNE", "Cacturne"),
- ("SPECIES_SNORUNT", "Snorunt"),
- ("SPECIES_GLALIE", "Glalie"),
- ("SPECIES_LUNATONE", "Lunatone"),
- ("SPECIES_SOLROCK", "Solrock"),
- ("SPECIES_AZURILL", "Azurill"),
- ("SPECIES_SPOINK", "Spoink"),
- ("SPECIES_GRUMPIG", "Grumpig"),
- ("SPECIES_PLUSLE", "Plusle"),
- ("SPECIES_MINUN", "Minun"),
- ("SPECIES_MAWILE", "Mawile"),
- ("SPECIES_MEDITITE", "Meditite"),
- ("SPECIES_MEDICHAM", "Medicham"),
- ("SPECIES_SWABLU", "Swablu"),
- ("SPECIES_ALTARIA", "Altaria"),
- ("SPECIES_WYNAUT", "Wynaut"),
- ("SPECIES_DUSKULL", "Duskull"),
- ("SPECIES_DUSCLOPS", "Dusclops"),
- ("SPECIES_ROSELIA", "Roselia"),
- ("SPECIES_SLAKOTH", "Slakoth"),
- ("SPECIES_VIGOROTH", "Vigoroth"),
- ("SPECIES_SLAKING", "Slaking"),
- ("SPECIES_GULPIN", "Gulpin"),
- ("SPECIES_SWALOT", "Swalot"),
- ("SPECIES_TROPIUS", "Tropius"),
- ("SPECIES_WHISMUR", "Whismur"),
- ("SPECIES_LOUDRED", "Loudred"),
- ("SPECIES_EXPLOUD", "Exploud"),
- ("SPECIES_CLAMPERL", "Clamperl"),
- ("SPECIES_HUNTAIL", "Huntail"),
- ("SPECIES_GOREBYSS", "Gorebyss"),
- ("SPECIES_ABSOL", "Absol"),
- ("SPECIES_SHUPPET", "Shuppet"),
- ("SPECIES_BANETTE", "Banette"),
- ("SPECIES_SEVIPER", "Seviper"),
- ("SPECIES_ZANGOOSE", "Zangoose"),
- ("SPECIES_RELICANTH", "Relicanth"),
- ("SPECIES_ARON", "Aron"),
- ("SPECIES_LAIRON", "Lairon"),
- ("SPECIES_AGGRON", "Aggron"),
- ("SPECIES_CASTFORM", "Castform"),
- ("SPECIES_VOLBEAT", "Volbeat"),
- ("SPECIES_ILLUMISE", "Illumise"),
- ("SPECIES_LILEEP", "Lileep"),
- ("SPECIES_CRADILY", "Cradily"),
- ("SPECIES_ANORITH", "Anorith"),
- ("SPECIES_ARMALDO", "Armaldo"),
- ("SPECIES_RALTS", "Ralts"),
- ("SPECIES_KIRLIA", "Kirlia"),
- ("SPECIES_GARDEVOIR", "Gardevoir"),
- ("SPECIES_BAGON", "Bagon"),
- ("SPECIES_SHELGON", "Shelgon"),
- ("SPECIES_SALAMENCE", "Salamence"),
- ("SPECIES_BELDUM", "Beldum"),
- ("SPECIES_METANG", "Metang"),
- ("SPECIES_METAGROSS", "Metagross"),
- ("SPECIES_REGIROCK", "Regirock"),
- ("SPECIES_REGICE", "Regice"),
- ("SPECIES_REGISTEEL", "Registeel"),
- ("SPECIES_KYOGRE", "Kyogre"),
- ("SPECIES_GROUDON", "Groudon"),
- ("SPECIES_RAYQUAZA", "Rayquaza"),
- ("SPECIES_LATIAS", "Latias"),
- ("SPECIES_LATIOS", "Latios"),
- ("SPECIES_JIRACHI", "Jirachi"),
- ("SPECIES_DEOXYS", "Deoxys"),
- ("SPECIES_CHIMECHO", "Chimecho")
+ all_species: List[Tuple[str, str, int]] = [
+ ("SPECIES_BULBASAUR", "Bulbasaur", 1),
+ ("SPECIES_IVYSAUR", "Ivysaur", 2),
+ ("SPECIES_VENUSAUR", "Venusaur", 3),
+ ("SPECIES_CHARMANDER", "Charmander", 4),
+ ("SPECIES_CHARMELEON", "Charmeleon", 5),
+ ("SPECIES_CHARIZARD", "Charizard", 6),
+ ("SPECIES_SQUIRTLE", "Squirtle", 7),
+ ("SPECIES_WARTORTLE", "Wartortle", 8),
+ ("SPECIES_BLASTOISE", "Blastoise", 9),
+ ("SPECIES_CATERPIE", "Caterpie", 10),
+ ("SPECIES_METAPOD", "Metapod", 11),
+ ("SPECIES_BUTTERFREE", "Butterfree", 12),
+ ("SPECIES_WEEDLE", "Weedle", 13),
+ ("SPECIES_KAKUNA", "Kakuna", 14),
+ ("SPECIES_BEEDRILL", "Beedrill", 15),
+ ("SPECIES_PIDGEY", "Pidgey", 16),
+ ("SPECIES_PIDGEOTTO", "Pidgeotto", 17),
+ ("SPECIES_PIDGEOT", "Pidgeot", 18),
+ ("SPECIES_RATTATA", "Rattata", 19),
+ ("SPECIES_RATICATE", "Raticate", 20),
+ ("SPECIES_SPEAROW", "Spearow", 21),
+ ("SPECIES_FEAROW", "Fearow", 22),
+ ("SPECIES_EKANS", "Ekans", 23),
+ ("SPECIES_ARBOK", "Arbok", 24),
+ ("SPECIES_PIKACHU", "Pikachu", 25),
+ ("SPECIES_RAICHU", "Raichu", 26),
+ ("SPECIES_SANDSHREW", "Sandshrew", 27),
+ ("SPECIES_SANDSLASH", "Sandslash", 28),
+ ("SPECIES_NIDORAN_F", "Nidoran Female", 29),
+ ("SPECIES_NIDORINA", "Nidorina", 30),
+ ("SPECIES_NIDOQUEEN", "Nidoqueen", 31),
+ ("SPECIES_NIDORAN_M", "Nidoran Male", 32),
+ ("SPECIES_NIDORINO", "Nidorino", 33),
+ ("SPECIES_NIDOKING", "Nidoking", 34),
+ ("SPECIES_CLEFAIRY", "Clefairy", 35),
+ ("SPECIES_CLEFABLE", "Clefable", 36),
+ ("SPECIES_VULPIX", "Vulpix", 37),
+ ("SPECIES_NINETALES", "Ninetales", 38),
+ ("SPECIES_JIGGLYPUFF", "Jigglypuff", 39),
+ ("SPECIES_WIGGLYTUFF", "Wigglytuff", 40),
+ ("SPECIES_ZUBAT", "Zubat", 41),
+ ("SPECIES_GOLBAT", "Golbat", 42),
+ ("SPECIES_ODDISH", "Oddish", 43),
+ ("SPECIES_GLOOM", "Gloom", 44),
+ ("SPECIES_VILEPLUME", "Vileplume", 45),
+ ("SPECIES_PARAS", "Paras", 46),
+ ("SPECIES_PARASECT", "Parasect", 47),
+ ("SPECIES_VENONAT", "Venonat", 48),
+ ("SPECIES_VENOMOTH", "Venomoth", 49),
+ ("SPECIES_DIGLETT", "Diglett", 50),
+ ("SPECIES_DUGTRIO", "Dugtrio", 51),
+ ("SPECIES_MEOWTH", "Meowth", 52),
+ ("SPECIES_PERSIAN", "Persian", 53),
+ ("SPECIES_PSYDUCK", "Psyduck", 54),
+ ("SPECIES_GOLDUCK", "Golduck", 55),
+ ("SPECIES_MANKEY", "Mankey", 56),
+ ("SPECIES_PRIMEAPE", "Primeape", 57),
+ ("SPECIES_GROWLITHE", "Growlithe", 58),
+ ("SPECIES_ARCANINE", "Arcanine", 59),
+ ("SPECIES_POLIWAG", "Poliwag", 60),
+ ("SPECIES_POLIWHIRL", "Poliwhirl", 61),
+ ("SPECIES_POLIWRATH", "Poliwrath", 62),
+ ("SPECIES_ABRA", "Abra", 63),
+ ("SPECIES_KADABRA", "Kadabra", 64),
+ ("SPECIES_ALAKAZAM", "Alakazam", 65),
+ ("SPECIES_MACHOP", "Machop", 66),
+ ("SPECIES_MACHOKE", "Machoke", 67),
+ ("SPECIES_MACHAMP", "Machamp", 68),
+ ("SPECIES_BELLSPROUT", "Bellsprout", 69),
+ ("SPECIES_WEEPINBELL", "Weepinbell", 70),
+ ("SPECIES_VICTREEBEL", "Victreebel", 71),
+ ("SPECIES_TENTACOOL", "Tentacool", 72),
+ ("SPECIES_TENTACRUEL", "Tentacruel", 73),
+ ("SPECIES_GEODUDE", "Geodude", 74),
+ ("SPECIES_GRAVELER", "Graveler", 75),
+ ("SPECIES_GOLEM", "Golem", 76),
+ ("SPECIES_PONYTA", "Ponyta", 77),
+ ("SPECIES_RAPIDASH", "Rapidash", 78),
+ ("SPECIES_SLOWPOKE", "Slowpoke", 79),
+ ("SPECIES_SLOWBRO", "Slowbro", 80),
+ ("SPECIES_MAGNEMITE", "Magnemite", 81),
+ ("SPECIES_MAGNETON", "Magneton", 82),
+ ("SPECIES_FARFETCHD", "Farfetch'd", 83),
+ ("SPECIES_DODUO", "Doduo", 84),
+ ("SPECIES_DODRIO", "Dodrio", 85),
+ ("SPECIES_SEEL", "Seel", 86),
+ ("SPECIES_DEWGONG", "Dewgong", 87),
+ ("SPECIES_GRIMER", "Grimer", 88),
+ ("SPECIES_MUK", "Muk", 89),
+ ("SPECIES_SHELLDER", "Shellder", 90),
+ ("SPECIES_CLOYSTER", "Cloyster", 91),
+ ("SPECIES_GASTLY", "Gastly", 92),
+ ("SPECIES_HAUNTER", "Haunter", 93),
+ ("SPECIES_GENGAR", "Gengar", 94),
+ ("SPECIES_ONIX", "Onix", 95),
+ ("SPECIES_DROWZEE", "Drowzee", 96),
+ ("SPECIES_HYPNO", "Hypno", 97),
+ ("SPECIES_KRABBY", "Krabby", 98),
+ ("SPECIES_KINGLER", "Kingler", 99),
+ ("SPECIES_VOLTORB", "Voltorb", 100),
+ ("SPECIES_ELECTRODE", "Electrode", 101),
+ ("SPECIES_EXEGGCUTE", "Exeggcute", 102),
+ ("SPECIES_EXEGGUTOR", "Exeggutor", 103),
+ ("SPECIES_CUBONE", "Cubone", 104),
+ ("SPECIES_MAROWAK", "Marowak", 105),
+ ("SPECIES_HITMONLEE", "Hitmonlee", 106),
+ ("SPECIES_HITMONCHAN", "Hitmonchan", 107),
+ ("SPECIES_LICKITUNG", "Lickitung", 108),
+ ("SPECIES_KOFFING", "Koffing", 109),
+ ("SPECIES_WEEZING", "Weezing", 110),
+ ("SPECIES_RHYHORN", "Rhyhorn", 111),
+ ("SPECIES_RHYDON", "Rhydon", 112),
+ ("SPECIES_CHANSEY", "Chansey", 113),
+ ("SPECIES_TANGELA", "Tangela", 114),
+ ("SPECIES_KANGASKHAN", "Kangaskhan", 115),
+ ("SPECIES_HORSEA", "Horsea", 116),
+ ("SPECIES_SEADRA", "Seadra", 117),
+ ("SPECIES_GOLDEEN", "Goldeen", 118),
+ ("SPECIES_SEAKING", "Seaking", 119),
+ ("SPECIES_STARYU", "Staryu", 120),
+ ("SPECIES_STARMIE", "Starmie", 121),
+ ("SPECIES_MR_MIME", "Mr. Mime", 122),
+ ("SPECIES_SCYTHER", "Scyther", 123),
+ ("SPECIES_JYNX", "Jynx", 124),
+ ("SPECIES_ELECTABUZZ", "Electabuzz", 125),
+ ("SPECIES_MAGMAR", "Magmar", 126),
+ ("SPECIES_PINSIR", "Pinsir", 127),
+ ("SPECIES_TAUROS", "Tauros", 128),
+ ("SPECIES_MAGIKARP", "Magikarp", 129),
+ ("SPECIES_GYARADOS", "Gyarados", 130),
+ ("SPECIES_LAPRAS", "Lapras", 131),
+ ("SPECIES_DITTO", "Ditto", 132),
+ ("SPECIES_EEVEE", "Eevee", 133),
+ ("SPECIES_VAPOREON", "Vaporeon", 134),
+ ("SPECIES_JOLTEON", "Jolteon", 135),
+ ("SPECIES_FLAREON", "Flareon", 136),
+ ("SPECIES_PORYGON", "Porygon", 137),
+ ("SPECIES_OMANYTE", "Omanyte", 138),
+ ("SPECIES_OMASTAR", "Omastar", 139),
+ ("SPECIES_KABUTO", "Kabuto", 140),
+ ("SPECIES_KABUTOPS", "Kabutops", 141),
+ ("SPECIES_AERODACTYL", "Aerodactyl", 142),
+ ("SPECIES_SNORLAX", "Snorlax", 143),
+ ("SPECIES_ARTICUNO", "Articuno", 144),
+ ("SPECIES_ZAPDOS", "Zapdos", 145),
+ ("SPECIES_MOLTRES", "Moltres", 146),
+ ("SPECIES_DRATINI", "Dratini", 147),
+ ("SPECIES_DRAGONAIR", "Dragonair", 148),
+ ("SPECIES_DRAGONITE", "Dragonite", 149),
+ ("SPECIES_MEWTWO", "Mewtwo", 150),
+ ("SPECIES_MEW", "Mew", 151),
+ ("SPECIES_CHIKORITA", "Chikorita", 152),
+ ("SPECIES_BAYLEEF", "Bayleef", 153),
+ ("SPECIES_MEGANIUM", "Meganium", 154),
+ ("SPECIES_CYNDAQUIL", "Cyndaquil", 155),
+ ("SPECIES_QUILAVA", "Quilava", 156),
+ ("SPECIES_TYPHLOSION", "Typhlosion", 157),
+ ("SPECIES_TOTODILE", "Totodile", 158),
+ ("SPECIES_CROCONAW", "Croconaw", 159),
+ ("SPECIES_FERALIGATR", "Feraligatr", 160),
+ ("SPECIES_SENTRET", "Sentret", 161),
+ ("SPECIES_FURRET", "Furret", 162),
+ ("SPECIES_HOOTHOOT", "Hoothoot", 163),
+ ("SPECIES_NOCTOWL", "Noctowl", 164),
+ ("SPECIES_LEDYBA", "Ledyba", 165),
+ ("SPECIES_LEDIAN", "Ledian", 166),
+ ("SPECIES_SPINARAK", "Spinarak", 167),
+ ("SPECIES_ARIADOS", "Ariados", 168),
+ ("SPECIES_CROBAT", "Crobat", 169),
+ ("SPECIES_CHINCHOU", "Chinchou", 170),
+ ("SPECIES_LANTURN", "Lanturn", 171),
+ ("SPECIES_PICHU", "Pichu", 172),
+ ("SPECIES_CLEFFA", "Cleffa", 173),
+ ("SPECIES_IGGLYBUFF", "Igglybuff", 174),
+ ("SPECIES_TOGEPI", "Togepi", 175),
+ ("SPECIES_TOGETIC", "Togetic", 176),
+ ("SPECIES_NATU", "Natu", 177),
+ ("SPECIES_XATU", "Xatu", 178),
+ ("SPECIES_MAREEP", "Mareep", 179),
+ ("SPECIES_FLAAFFY", "Flaaffy", 180),
+ ("SPECIES_AMPHAROS", "Ampharos", 181),
+ ("SPECIES_BELLOSSOM", "Bellossom", 182),
+ ("SPECIES_MARILL", "Marill", 183),
+ ("SPECIES_AZUMARILL", "Azumarill", 184),
+ ("SPECIES_SUDOWOODO", "Sudowoodo", 185),
+ ("SPECIES_POLITOED", "Politoed", 186),
+ ("SPECIES_HOPPIP", "Hoppip", 187),
+ ("SPECIES_SKIPLOOM", "Skiploom", 188),
+ ("SPECIES_JUMPLUFF", "Jumpluff", 189),
+ ("SPECIES_AIPOM", "Aipom", 190),
+ ("SPECIES_SUNKERN", "Sunkern", 191),
+ ("SPECIES_SUNFLORA", "Sunflora", 192),
+ ("SPECIES_YANMA", "Yanma", 193),
+ ("SPECIES_WOOPER", "Wooper", 194),
+ ("SPECIES_QUAGSIRE", "Quagsire", 195),
+ ("SPECIES_ESPEON", "Espeon", 196),
+ ("SPECIES_UMBREON", "Umbreon", 197),
+ ("SPECIES_MURKROW", "Murkrow", 198),
+ ("SPECIES_SLOWKING", "Slowking", 199),
+ ("SPECIES_MISDREAVUS", "Misdreavus", 200),
+ ("SPECIES_UNOWN", "Unown", 201),
+ ("SPECIES_WOBBUFFET", "Wobbuffet", 202),
+ ("SPECIES_GIRAFARIG", "Girafarig", 203),
+ ("SPECIES_PINECO", "Pineco", 204),
+ ("SPECIES_FORRETRESS", "Forretress", 205),
+ ("SPECIES_DUNSPARCE", "Dunsparce", 206),
+ ("SPECIES_GLIGAR", "Gligar", 207),
+ ("SPECIES_STEELIX", "Steelix", 208),
+ ("SPECIES_SNUBBULL", "Snubbull", 209),
+ ("SPECIES_GRANBULL", "Granbull", 210),
+ ("SPECIES_QWILFISH", "Qwilfish", 211),
+ ("SPECIES_SCIZOR", "Scizor", 212),
+ ("SPECIES_SHUCKLE", "Shuckle", 213),
+ ("SPECIES_HERACROSS", "Heracross", 214),
+ ("SPECIES_SNEASEL", "Sneasel", 215),
+ ("SPECIES_TEDDIURSA", "Teddiursa", 216),
+ ("SPECIES_URSARING", "Ursaring", 217),
+ ("SPECIES_SLUGMA", "Slugma", 218),
+ ("SPECIES_MAGCARGO", "Magcargo", 219),
+ ("SPECIES_SWINUB", "Swinub", 220),
+ ("SPECIES_PILOSWINE", "Piloswine", 221),
+ ("SPECIES_CORSOLA", "Corsola", 222),
+ ("SPECIES_REMORAID", "Remoraid", 223),
+ ("SPECIES_OCTILLERY", "Octillery", 224),
+ ("SPECIES_DELIBIRD", "Delibird", 225),
+ ("SPECIES_MANTINE", "Mantine", 226),
+ ("SPECIES_SKARMORY", "Skarmory", 227),
+ ("SPECIES_HOUNDOUR", "Houndour", 228),
+ ("SPECIES_HOUNDOOM", "Houndoom", 229),
+ ("SPECIES_KINGDRA", "Kingdra", 230),
+ ("SPECIES_PHANPY", "Phanpy", 231),
+ ("SPECIES_DONPHAN", "Donphan", 232),
+ ("SPECIES_PORYGON2", "Porygon2", 233),
+ ("SPECIES_STANTLER", "Stantler", 234),
+ ("SPECIES_SMEARGLE", "Smeargle", 235),
+ ("SPECIES_TYROGUE", "Tyrogue", 236),
+ ("SPECIES_HITMONTOP", "Hitmontop", 237),
+ ("SPECIES_SMOOCHUM", "Smoochum", 238),
+ ("SPECIES_ELEKID", "Elekid", 239),
+ ("SPECIES_MAGBY", "Magby", 240),
+ ("SPECIES_MILTANK", "Miltank", 241),
+ ("SPECIES_BLISSEY", "Blissey", 242),
+ ("SPECIES_RAIKOU", "Raikou", 243),
+ ("SPECIES_ENTEI", "Entei", 244),
+ ("SPECIES_SUICUNE", "Suicune", 245),
+ ("SPECIES_LARVITAR", "Larvitar", 246),
+ ("SPECIES_PUPITAR", "Pupitar", 247),
+ ("SPECIES_TYRANITAR", "Tyranitar", 248),
+ ("SPECIES_LUGIA", "Lugia", 249),
+ ("SPECIES_HO_OH", "Ho-oh", 250),
+ ("SPECIES_CELEBI", "Celebi", 251),
+ ("SPECIES_TREECKO", "Treecko", 252),
+ ("SPECIES_GROVYLE", "Grovyle", 253),
+ ("SPECIES_SCEPTILE", "Sceptile", 254),
+ ("SPECIES_TORCHIC", "Torchic", 255),
+ ("SPECIES_COMBUSKEN", "Combusken", 256),
+ ("SPECIES_BLAZIKEN", "Blaziken", 257),
+ ("SPECIES_MUDKIP", "Mudkip", 258),
+ ("SPECIES_MARSHTOMP", "Marshtomp", 259),
+ ("SPECIES_SWAMPERT", "Swampert", 260),
+ ("SPECIES_POOCHYENA", "Poochyena", 261),
+ ("SPECIES_MIGHTYENA", "Mightyena", 262),
+ ("SPECIES_ZIGZAGOON", "Zigzagoon", 263),
+ ("SPECIES_LINOONE", "Linoone", 264),
+ ("SPECIES_WURMPLE", "Wurmple", 265),
+ ("SPECIES_SILCOON", "Silcoon", 266),
+ ("SPECIES_BEAUTIFLY", "Beautifly", 267),
+ ("SPECIES_CASCOON", "Cascoon", 268),
+ ("SPECIES_DUSTOX", "Dustox", 269),
+ ("SPECIES_LOTAD", "Lotad", 270),
+ ("SPECIES_LOMBRE", "Lombre", 271),
+ ("SPECIES_LUDICOLO", "Ludicolo", 272),
+ ("SPECIES_SEEDOT", "Seedot", 273),
+ ("SPECIES_NUZLEAF", "Nuzleaf", 274),
+ ("SPECIES_SHIFTRY", "Shiftry", 275),
+ ("SPECIES_NINCADA", "Nincada", 290),
+ ("SPECIES_NINJASK", "Ninjask", 291),
+ ("SPECIES_SHEDINJA", "Shedinja", 292),
+ ("SPECIES_TAILLOW", "Taillow", 276),
+ ("SPECIES_SWELLOW", "Swellow", 277),
+ ("SPECIES_SHROOMISH", "Shroomish", 285),
+ ("SPECIES_BRELOOM", "Breloom", 286),
+ ("SPECIES_SPINDA", "Spinda", 327),
+ ("SPECIES_WINGULL", "Wingull", 278),
+ ("SPECIES_PELIPPER", "Pelipper", 279),
+ ("SPECIES_SURSKIT", "Surskit", 283),
+ ("SPECIES_MASQUERAIN", "Masquerain", 284),
+ ("SPECIES_WAILMER", "Wailmer", 320),
+ ("SPECIES_WAILORD", "Wailord", 321),
+ ("SPECIES_SKITTY", "Skitty", 300),
+ ("SPECIES_DELCATTY", "Delcatty", 301),
+ ("SPECIES_KECLEON", "Kecleon", 352),
+ ("SPECIES_BALTOY", "Baltoy", 343),
+ ("SPECIES_CLAYDOL", "Claydol", 344),
+ ("SPECIES_NOSEPASS", "Nosepass", 299),
+ ("SPECIES_TORKOAL", "Torkoal", 324),
+ ("SPECIES_SABLEYE", "Sableye", 302),
+ ("SPECIES_BARBOACH", "Barboach", 339),
+ ("SPECIES_WHISCASH", "Whiscash", 340),
+ ("SPECIES_LUVDISC", "Luvdisc", 370),
+ ("SPECIES_CORPHISH", "Corphish", 341),
+ ("SPECIES_CRAWDAUNT", "Crawdaunt", 342),
+ ("SPECIES_FEEBAS", "Feebas", 349),
+ ("SPECIES_MILOTIC", "Milotic", 350),
+ ("SPECIES_CARVANHA", "Carvanha", 318),
+ ("SPECIES_SHARPEDO", "Sharpedo", 319),
+ ("SPECIES_TRAPINCH", "Trapinch", 328),
+ ("SPECIES_VIBRAVA", "Vibrava", 329),
+ ("SPECIES_FLYGON", "Flygon", 330),
+ ("SPECIES_MAKUHITA", "Makuhita", 296),
+ ("SPECIES_HARIYAMA", "Hariyama", 297),
+ ("SPECIES_ELECTRIKE", "Electrike", 309),
+ ("SPECIES_MANECTRIC", "Manectric", 310),
+ ("SPECIES_NUMEL", "Numel", 322),
+ ("SPECIES_CAMERUPT", "Camerupt", 323),
+ ("SPECIES_SPHEAL", "Spheal", 363),
+ ("SPECIES_SEALEO", "Sealeo", 364),
+ ("SPECIES_WALREIN", "Walrein", 365),
+ ("SPECIES_CACNEA", "Cacnea", 331),
+ ("SPECIES_CACTURNE", "Cacturne", 332),
+ ("SPECIES_SNORUNT", "Snorunt", 361),
+ ("SPECIES_GLALIE", "Glalie", 362),
+ ("SPECIES_LUNATONE", "Lunatone", 337),
+ ("SPECIES_SOLROCK", "Solrock", 338),
+ ("SPECIES_AZURILL", "Azurill", 298),
+ ("SPECIES_SPOINK", "Spoink", 325),
+ ("SPECIES_GRUMPIG", "Grumpig", 326),
+ ("SPECIES_PLUSLE", "Plusle", 311),
+ ("SPECIES_MINUN", "Minun", 312),
+ ("SPECIES_MAWILE", "Mawile", 303),
+ ("SPECIES_MEDITITE", "Meditite", 307),
+ ("SPECIES_MEDICHAM", "Medicham", 308),
+ ("SPECIES_SWABLU", "Swablu", 333),
+ ("SPECIES_ALTARIA", "Altaria", 334),
+ ("SPECIES_WYNAUT", "Wynaut", 360),
+ ("SPECIES_DUSKULL", "Duskull", 355),
+ ("SPECIES_DUSCLOPS", "Dusclops", 356),
+ ("SPECIES_ROSELIA", "Roselia", 315),
+ ("SPECIES_SLAKOTH", "Slakoth", 287),
+ ("SPECIES_VIGOROTH", "Vigoroth", 288),
+ ("SPECIES_SLAKING", "Slaking", 289),
+ ("SPECIES_GULPIN", "Gulpin", 316),
+ ("SPECIES_SWALOT", "Swalot", 317),
+ ("SPECIES_TROPIUS", "Tropius", 357),
+ ("SPECIES_WHISMUR", "Whismur", 293),
+ ("SPECIES_LOUDRED", "Loudred", 294),
+ ("SPECIES_EXPLOUD", "Exploud", 295),
+ ("SPECIES_CLAMPERL", "Clamperl", 366),
+ ("SPECIES_HUNTAIL", "Huntail", 367),
+ ("SPECIES_GOREBYSS", "Gorebyss", 368),
+ ("SPECIES_ABSOL", "Absol", 359),
+ ("SPECIES_SHUPPET", "Shuppet", 353),
+ ("SPECIES_BANETTE", "Banette", 354),
+ ("SPECIES_SEVIPER", "Seviper", 336),
+ ("SPECIES_ZANGOOSE", "Zangoose", 335),
+ ("SPECIES_RELICANTH", "Relicanth", 369),
+ ("SPECIES_ARON", "Aron", 304),
+ ("SPECIES_LAIRON", "Lairon", 305),
+ ("SPECIES_AGGRON", "Aggron", 306),
+ ("SPECIES_CASTFORM", "Castform", 351),
+ ("SPECIES_VOLBEAT", "Volbeat", 313),
+ ("SPECIES_ILLUMISE", "Illumise", 314),
+ ("SPECIES_LILEEP", "Lileep", 345),
+ ("SPECIES_CRADILY", "Cradily", 346),
+ ("SPECIES_ANORITH", "Anorith", 347),
+ ("SPECIES_ARMALDO", "Armaldo", 348),
+ ("SPECIES_RALTS", "Ralts", 280),
+ ("SPECIES_KIRLIA", "Kirlia", 281),
+ ("SPECIES_GARDEVOIR", "Gardevoir", 282),
+ ("SPECIES_BAGON", "Bagon", 371),
+ ("SPECIES_SHELGON", "Shelgon", 372),
+ ("SPECIES_SALAMENCE", "Salamence", 373),
+ ("SPECIES_BELDUM", "Beldum", 374),
+ ("SPECIES_METANG", "Metang", 375),
+ ("SPECIES_METAGROSS", "Metagross", 376),
+ ("SPECIES_REGIROCK", "Regirock", 377),
+ ("SPECIES_REGICE", "Regice", 378),
+ ("SPECIES_REGISTEEL", "Registeel", 379),
+ ("SPECIES_KYOGRE", "Kyogre", 382),
+ ("SPECIES_GROUDON", "Groudon", 383),
+ ("SPECIES_RAYQUAZA", "Rayquaza", 384),
+ ("SPECIES_LATIAS", "Latias", 380),
+ ("SPECIES_LATIOS", "Latios", 381),
+ ("SPECIES_JIRACHI", "Jirachi", 385),
+ ("SPECIES_DEOXYS", "Deoxys", 386),
+ ("SPECIES_CHIMECHO", "Chimecho", 358),
]
- species_list: List[SpeciesData] = []
max_species_id = 0
- for species_name, species_label in all_species:
+ for species_name, species_label, species_dex_number in all_species:
species_id = data.constants[species_name]
max_species_id = max(species_id, max_species_id)
species_data = extracted_data["species"][species_id]
learnset = [LearnsetMove(item["level"], item["move_id"]) for item in species_data["learnset"]["moves"]]
- species_list.append(SpeciesData(
+ data.species[species_id] = SpeciesData(
species_name,
species_label,
species_id,
+ species_dex_number,
BaseStats(
species_data["base_stats"][0],
species_data["base_stats"][1],
@@ -827,27 +910,52 @@ def _init() -> None:
) for evolution_json in species_data["evolutions"]],
None,
species_data["catch_rate"],
+ species_data["friendship"],
learnset,
int(species_data["tmhm_learnset"], 16),
- species_data["learnset"]["rom_address"],
- species_data["rom_address"]
- ))
-
- data.species = [None for i in range(max_species_id + 1)]
+ species_data["learnset"]["address"],
+ species_data["address"]
+ )
- for species_data in species_list:
- data.species[species_data.species_id] = species_data
+ for species in data.species.values():
+ for evolution in species.evolutions:
+ data.species[evolution.species_id].pre_evolution = species.species_id
+
+ # Replace default item for dex entry locations based on evo stage of species
+ evo_stage_to_ball_map = {
+ 0: data.constants["ITEM_POKE_BALL"],
+ 1: data.constants["ITEM_GREAT_BALL"],
+ 2: data.constants["ITEM_ULTRA_BALL"],
+ }
+ for species in data.species.values():
+ evo_stage = 0
+ pre_evolution = species.pre_evolution
+ while pre_evolution is not None:
+ evo_stage += 1
+ pre_evolution = data.species[pre_evolution].pre_evolution
+
+ dex_location_name = f"POKEDEX_REWARD_{str(species.national_dex_number).zfill(3)}"
+ data.locations[dex_location_name] = LocationData(
+ data.locations[dex_location_name].name,
+ data.locations[dex_location_name].label,
+ data.locations[dex_location_name].parent_region,
+ evo_stage_to_ball_map[evo_stage],
+ data.locations[dex_location_name].address,
+ data.locations[dex_location_name].flag,
+ data.locations[dex_location_name].tags
+ )
- for species in data.species:
- if species is not None:
- for evolution in species.evolutions:
- data.species[evolution.species_id].pre_evolution = species.species_id
+ # Create legendary encounter data
+ for legendary_encounter_json in extracted_data["legendary_encounters"]:
+ data.legendary_encounters.append(MiscPokemonData(
+ legendary_encounter_json["species"],
+ legendary_encounter_json["address"]
+ ))
- # Create static encounter data
- for static_encounter_json in extracted_data["static_encounters"]:
- data.static_encounters.append(StaticEncounterData(
- static_encounter_json["species"],
- static_encounter_json["rom_address"]
+ for misc_pokemon_json in extracted_data["misc_pokemon"]:
+ data.misc_pokemon.append(MiscPokemonData(
+ misc_pokemon_json["species"],
+ misc_pokemon_json["address"]
))
# TM moves
@@ -868,7 +976,7 @@ def _init() -> None:
("ABILITY_WATER_ABSORB", "Water Absorb"),
("ABILITY_OBLIVIOUS", "Oblivious"),
("ABILITY_CLOUD_NINE", "Cloud Nine"),
- ("ABILITY_COMPOUND_EYES", "Compound Eyes"),
+ ("ABILITY_COMPOUND_EYES", "Compoundeyes"),
("ABILITY_INSOMNIA", "Insomnia"),
("ABILITY_COLOR_CHANGE", "Color Change"),
("ABILITY_IMMUNITY", "Immunity"),
@@ -885,7 +993,7 @@ def _init() -> None:
("ABILITY_SYNCHRONIZE", "Synchronize"),
("ABILITY_CLEAR_BODY", "Clear Body"),
("ABILITY_NATURAL_CURE", "Natural Cure"),
- ("ABILITY_LIGHTNING_ROD", "Lightning Rod"),
+ ("ABILITY_LIGHTNING_ROD", "Lightningrod"),
("ABILITY_SERENE_GRACE", "Serene Grace"),
("ABILITY_SWIFT_SWIM", "Swift Swim"),
("ABILITY_CHLOROPHYLL", "Chlorophyll"),
@@ -934,36 +1042,362 @@ def _init() -> None:
("ABILITY_AIR_LOCK", "Air Lock")
]]
- # Create map data
- for map_name, map_json in extracted_data["maps"].items():
- land_encounters = None
- water_encounters = None
- fishing_encounters = None
-
- if map_json["land_encounters"] is not None:
- land_encounters = EncounterTableData(
- map_json["land_encounters"]["encounter_slots"],
- map_json["land_encounters"]["rom_address"]
- )
- if map_json["water_encounters"] is not None:
- water_encounters = EncounterTableData(
- map_json["water_encounters"]["encounter_slots"],
- map_json["water_encounters"]["rom_address"]
- )
- if map_json["fishing_encounters"] is not None:
- fishing_encounters = EncounterTableData(
- map_json["fishing_encounters"]["encounter_slots"],
- map_json["fishing_encounters"]["rom_address"]
- )
-
- data.maps.append(MapData(
- map_name,
- land_encounters,
- water_encounters,
- fishing_encounters
- ))
-
- data.maps.sort(key=lambda map: map.name)
+ # Move labels
+ data.move_labels = {r: data.constants[l] for l, r in [
+ ("MOVE_POUND", "Pound"),
+ ("MOVE_KARATE_CHOP", "Karate Chop"),
+ ("MOVE_DOUBLE_SLAP", "Doubleslap"),
+ ("MOVE_COMET_PUNCH", "Comet Punch"),
+ ("MOVE_MEGA_PUNCH", "Mega Punch"),
+ ("MOVE_PAY_DAY", "Pay Day"),
+ ("MOVE_FIRE_PUNCH", "Fire Punch"),
+ ("MOVE_ICE_PUNCH", "Ice Punch"),
+ ("MOVE_THUNDER_PUNCH", "Thunderpunch"),
+ ("MOVE_SCRATCH", "Scratch"),
+ ("MOVE_VICE_GRIP", "Vicegrip"),
+ ("MOVE_GUILLOTINE", "Guillotine"),
+ ("MOVE_RAZOR_WIND", "Razor Wind"),
+ ("MOVE_SWORDS_DANCE", "Swords Dance"),
+ ("MOVE_CUT", "Cut"),
+ ("MOVE_GUST", "Gust"),
+ ("MOVE_WING_ATTACK", "Wing Attack"),
+ ("MOVE_WHIRLWIND", "Whirlwind"),
+ ("MOVE_FLY", "Fly"),
+ ("MOVE_BIND", "Bind"),
+ ("MOVE_SLAM", "Slam"),
+ ("MOVE_VINE_WHIP", "Vine Whip"),
+ ("MOVE_STOMP", "Stomp"),
+ ("MOVE_DOUBLE_KICK", "Double Kick"),
+ ("MOVE_MEGA_KICK", "Mega Kick"),
+ ("MOVE_JUMP_KICK", "Jump Kick"),
+ ("MOVE_ROLLING_KICK", "Rolling Kick"),
+ ("MOVE_SAND_ATTACK", "Sand-Attack"),
+ ("MOVE_HEADBUTT", "Headbutt"),
+ ("MOVE_HORN_ATTACK", "Horn Attack"),
+ ("MOVE_FURY_ATTACK", "Fury Attack"),
+ ("MOVE_HORN_DRILL", "Horn Drill"),
+ ("MOVE_TACKLE", "Tackle"),
+ ("MOVE_BODY_SLAM", "Body Slam"),
+ ("MOVE_WRAP", "Wrap"),
+ ("MOVE_TAKE_DOWN", "Take Down"),
+ ("MOVE_THRASH", "Thrash"),
+ ("MOVE_DOUBLE_EDGE", "Double-Edge"),
+ ("MOVE_TAIL_WHIP", "Tail Whip"),
+ ("MOVE_POISON_STING", "Poison Sting"),
+ ("MOVE_TWINEEDLE", "Twineedle"),
+ ("MOVE_PIN_MISSILE", "Pin Missile"),
+ ("MOVE_LEER", "Leer"),
+ ("MOVE_BITE", "Bite"),
+ ("MOVE_GROWL", "Growl"),
+ ("MOVE_ROAR", "Roar"),
+ ("MOVE_SING", "Sing"),
+ ("MOVE_SUPERSONIC", "Supersonic"),
+ ("MOVE_SONIC_BOOM", "Sonicboom"),
+ ("MOVE_DISABLE", "Disable"),
+ ("MOVE_ACID", "Acid"),
+ ("MOVE_EMBER", "Ember"),
+ ("MOVE_FLAMETHROWER", "Flamethrower"),
+ ("MOVE_MIST", "Mist"),
+ ("MOVE_WATER_GUN", "Water Gun"),
+ ("MOVE_HYDRO_PUMP", "Hydro Pump"),
+ ("MOVE_SURF", "Surf"),
+ ("MOVE_ICE_BEAM", "Ice Beam"),
+ ("MOVE_BLIZZARD", "Blizzard"),
+ ("MOVE_PSYBEAM", "Psybeam"),
+ ("MOVE_BUBBLE_BEAM", "Bubblebeam"),
+ ("MOVE_AURORA_BEAM", "Aurora Beam"),
+ ("MOVE_HYPER_BEAM", "Hyper Beam"),
+ ("MOVE_PECK", "Peck"),
+ ("MOVE_DRILL_PECK", "Drill Peck"),
+ ("MOVE_SUBMISSION", "Submission"),
+ ("MOVE_LOW_KICK", "Low Kick"),
+ ("MOVE_COUNTER", "Counter"),
+ ("MOVE_SEISMIC_TOSS", "Seismic Toss"),
+ ("MOVE_STRENGTH", "Strength"),
+ ("MOVE_ABSORB", "Absorb"),
+ ("MOVE_MEGA_DRAIN", "Mega Drain"),
+ ("MOVE_LEECH_SEED", "Leech Seed"),
+ ("MOVE_GROWTH", "Growth"),
+ ("MOVE_RAZOR_LEAF", "Razor Leaf"),
+ ("MOVE_SOLAR_BEAM", "Solarbeam"),
+ ("MOVE_POISON_POWDER", "Poisonpowder"),
+ ("MOVE_STUN_SPORE", "Stun Spore"),
+ ("MOVE_SLEEP_POWDER", "Sleep Powder"),
+ ("MOVE_PETAL_DANCE", "Petal Dance"),
+ ("MOVE_STRING_SHOT", "String Shot"),
+ ("MOVE_DRAGON_RAGE", "Dragon Rage"),
+ ("MOVE_FIRE_SPIN", "Fire Spin"),
+ ("MOVE_THUNDER_SHOCK", "Thundershock"),
+ ("MOVE_THUNDERBOLT", "Thunderbolt"),
+ ("MOVE_THUNDER_WAVE", "Thunder Wave"),
+ ("MOVE_THUNDER", "Thunder"),
+ ("MOVE_ROCK_THROW", "Rock Throw"),
+ ("MOVE_EARTHQUAKE", "Earthquake"),
+ ("MOVE_FISSURE", "Fissure"),
+ ("MOVE_DIG", "Dig"),
+ ("MOVE_TOXIC", "Toxic"),
+ ("MOVE_CONFUSION", "Confusion"),
+ ("MOVE_PSYCHIC", "Psychic"),
+ ("MOVE_HYPNOSIS", "Hypnosis"),
+ ("MOVE_MEDITATE", "Meditate"),
+ ("MOVE_AGILITY", "Agility"),
+ ("MOVE_QUICK_ATTACK", "Quick Attack"),
+ ("MOVE_RAGE", "Rage"),
+ ("MOVE_TELEPORT", "Teleport"),
+ ("MOVE_NIGHT_SHADE", "Night Shade"),
+ ("MOVE_MIMIC", "Mimic"),
+ ("MOVE_SCREECH", "Screech"),
+ ("MOVE_DOUBLE_TEAM", "Double Team"),
+ ("MOVE_RECOVER", "Recover"),
+ ("MOVE_HARDEN", "Harden"),
+ ("MOVE_MINIMIZE", "Minimize"),
+ ("MOVE_SMOKESCREEN", "Smokescreen"),
+ ("MOVE_CONFUSE_RAY", "Confuse Ray"),
+ ("MOVE_WITHDRAW", "Withdraw"),
+ ("MOVE_DEFENSE_CURL", "Defense Curl"),
+ ("MOVE_BARRIER", "Barrier"),
+ ("MOVE_LIGHT_SCREEN", "Light Screen"),
+ ("MOVE_HAZE", "Haze"),
+ ("MOVE_REFLECT", "Reflect"),
+ ("MOVE_FOCUS_ENERGY", "Focus Energy"),
+ ("MOVE_BIDE", "Bide"),
+ ("MOVE_METRONOME", "Metronome"),
+ ("MOVE_MIRROR_MOVE", "Mirror Move"),
+ ("MOVE_SELF_DESTRUCT", "Selfdestruct"),
+ ("MOVE_EGG_BOMB", "Egg Bomb"),
+ ("MOVE_LICK", "Lick"),
+ ("MOVE_SMOG", "Smog"),
+ ("MOVE_SLUDGE", "Sludge"),
+ ("MOVE_BONE_CLUB", "Bone Club"),
+ ("MOVE_FIRE_BLAST", "Fire Blast"),
+ ("MOVE_WATERFALL", "Waterfall"),
+ ("MOVE_CLAMP", "Clamp"),
+ ("MOVE_SWIFT", "Swift"),
+ ("MOVE_SKULL_BASH", "Skull Bash"),
+ ("MOVE_SPIKE_CANNON", "Spike Cannon"),
+ ("MOVE_CONSTRICT", "Constrict"),
+ ("MOVE_AMNESIA", "Amnesia"),
+ ("MOVE_KINESIS", "Kinesis"),
+ ("MOVE_SOFT_BOILED", "Softboiled"),
+ ("MOVE_HI_JUMP_KICK", "Hi Jump Kick"),
+ ("MOVE_GLARE", "Glare"),
+ ("MOVE_DREAM_EATER", "Dream Eater"),
+ ("MOVE_POISON_GAS", "Poison Gas"),
+ ("MOVE_BARRAGE", "Barrage"),
+ ("MOVE_LEECH_LIFE", "Leech Life"),
+ ("MOVE_LOVELY_KISS", "Lovely Kiss"),
+ ("MOVE_SKY_ATTACK", "Sky Attack"),
+ ("MOVE_TRANSFORM", "Transform"),
+ ("MOVE_BUBBLE", "Bubble"),
+ ("MOVE_DIZZY_PUNCH", "Dizzy Punch"),
+ ("MOVE_SPORE", "Spore"),
+ ("MOVE_FLASH", "Flash"),
+ ("MOVE_PSYWAVE", "Psywave"),
+ ("MOVE_SPLASH", "Splash"),
+ ("MOVE_ACID_ARMOR", "Acid Armor"),
+ ("MOVE_CRABHAMMER", "Crabhammer"),
+ ("MOVE_EXPLOSION", "Explosion"),
+ ("MOVE_FURY_SWIPES", "Fury Swipes"),
+ ("MOVE_BONEMERANG", "Bonemerang"),
+ ("MOVE_REST", "Rest"),
+ ("MOVE_ROCK_SLIDE", "Rock Slide"),
+ ("MOVE_HYPER_FANG", "Hyper Fang"),
+ ("MOVE_SHARPEN", "Sharpen"),
+ ("MOVE_CONVERSION", "Conversion"),
+ ("MOVE_TRI_ATTACK", "Tri Attack"),
+ ("MOVE_SUPER_FANG", "Super Fang"),
+ ("MOVE_SLASH", "Slash"),
+ ("MOVE_SUBSTITUTE", "Substitute"),
+ ("MOVE_SKETCH", "Sketch"),
+ ("MOVE_TRIPLE_KICK", "Triple Kick"),
+ ("MOVE_THIEF", "Thief"),
+ ("MOVE_SPIDER_WEB", "Spider Web"),
+ ("MOVE_MIND_READER", "Mind Reader"),
+ ("MOVE_NIGHTMARE", "Nightmare"),
+ ("MOVE_FLAME_WHEEL", "Flame Wheel"),
+ ("MOVE_SNORE", "Snore"),
+ ("MOVE_CURSE", "Curse"),
+ ("MOVE_FLAIL", "Flail"),
+ ("MOVE_CONVERSION_2", "Conversion 2"),
+ ("MOVE_AEROBLAST", "Aeroblast"),
+ ("MOVE_COTTON_SPORE", "Cotton Spore"),
+ ("MOVE_REVERSAL", "Reversal"),
+ ("MOVE_SPITE", "Spite"),
+ ("MOVE_POWDER_SNOW", "Powder Snow"),
+ ("MOVE_PROTECT", "Protect"),
+ ("MOVE_MACH_PUNCH", "Mach Punch"),
+ ("MOVE_SCARY_FACE", "Scary Face"),
+ ("MOVE_FAINT_ATTACK", "Faint Attack"),
+ ("MOVE_SWEET_KISS", "Sweet Kiss"),
+ ("MOVE_BELLY_DRUM", "Belly Drum"),
+ ("MOVE_SLUDGE_BOMB", "Sludge Bomb"),
+ ("MOVE_MUD_SLAP", "Mud-Slap"),
+ ("MOVE_OCTAZOOKA", "Octazooka"),
+ ("MOVE_SPIKES", "Spikes"),
+ ("MOVE_ZAP_CANNON", "Zap Cannon"),
+ ("MOVE_FORESIGHT", "Foresight"),
+ ("MOVE_DESTINY_BOND", "Destiny Bond"),
+ ("MOVE_PERISH_SONG", "Perish Song"),
+ ("MOVE_ICY_WIND", "Icy Wind"),
+ ("MOVE_DETECT", "Detect"),
+ ("MOVE_BONE_RUSH", "Bone Rush"),
+ ("MOVE_LOCK_ON", "Lock-On"),
+ ("MOVE_OUTRAGE", "Outrage"),
+ ("MOVE_SANDSTORM", "Sandstorm"),
+ ("MOVE_GIGA_DRAIN", "Giga Drain"),
+ ("MOVE_ENDURE", "Endure"),
+ ("MOVE_CHARM", "Charm"),
+ ("MOVE_ROLLOUT", "Rollout"),
+ ("MOVE_FALSE_SWIPE", "False Swipe"),
+ ("MOVE_SWAGGER", "Swagger"),
+ ("MOVE_MILK_DRINK", "Milk Drink"),
+ ("MOVE_SPARK", "Spark"),
+ ("MOVE_FURY_CUTTER", "Fury Cutter"),
+ ("MOVE_STEEL_WING", "Steel Wing"),
+ ("MOVE_MEAN_LOOK", "Mean Look"),
+ ("MOVE_ATTRACT", "Attract"),
+ ("MOVE_SLEEP_TALK", "Sleep Talk"),
+ ("MOVE_HEAL_BELL", "Heal Bell"),
+ ("MOVE_RETURN", "Return"),
+ ("MOVE_PRESENT", "Present"),
+ ("MOVE_FRUSTRATION", "Frustration"),
+ ("MOVE_SAFEGUARD", "Safeguard"),
+ ("MOVE_PAIN_SPLIT", "Pain Split"),
+ ("MOVE_SACRED_FIRE", "Sacred Fire"),
+ ("MOVE_MAGNITUDE", "Magnitude"),
+ ("MOVE_DYNAMIC_PUNCH", "Dynamicpunch"),
+ ("MOVE_MEGAHORN", "Megahorn"),
+ ("MOVE_DRAGON_BREATH", "Dragonbreath"),
+ ("MOVE_BATON_PASS", "Baton Pass"),
+ ("MOVE_ENCORE", "Encore"),
+ ("MOVE_PURSUIT", "Pursuit"),
+ ("MOVE_RAPID_SPIN", "Rapid Spin"),
+ ("MOVE_SWEET_SCENT", "Sweet Scent"),
+ ("MOVE_IRON_TAIL", "Iron Tail"),
+ ("MOVE_METAL_CLAW", "Metal Claw"),
+ ("MOVE_VITAL_THROW", "Vital Throw"),
+ ("MOVE_MORNING_SUN", "Morning Sun"),
+ ("MOVE_SYNTHESIS", "Synthesis"),
+ ("MOVE_MOONLIGHT", "Moonlight"),
+ ("MOVE_HIDDEN_POWER", "Hidden Power"),
+ ("MOVE_CROSS_CHOP", "Cross Chop"),
+ ("MOVE_TWISTER", "Twister"),
+ ("MOVE_RAIN_DANCE", "Rain Dance"),
+ ("MOVE_SUNNY_DAY", "Sunny Day"),
+ ("MOVE_CRUNCH", "Crunch"),
+ ("MOVE_MIRROR_COAT", "Mirror Coat"),
+ ("MOVE_PSYCH_UP", "Psych Up"),
+ ("MOVE_EXTREME_SPEED", "Extremespeed"),
+ ("MOVE_ANCIENT_POWER", "Ancientpower"),
+ ("MOVE_SHADOW_BALL", "Shadow Ball"),
+ ("MOVE_FUTURE_SIGHT", "Future Sight"),
+ ("MOVE_ROCK_SMASH", "Rock Smash"),
+ ("MOVE_WHIRLPOOL", "Whirlpool"),
+ ("MOVE_BEAT_UP", "Beat Up"),
+ ("MOVE_FAKE_OUT", "Fake Out"),
+ ("MOVE_UPROAR", "Uproar"),
+ ("MOVE_STOCKPILE", "Stockpile"),
+ ("MOVE_SPIT_UP", "Spit Up"),
+ ("MOVE_SWALLOW", "Swallow"),
+ ("MOVE_HEAT_WAVE", "Heat Wave"),
+ ("MOVE_HAIL", "Hail"),
+ ("MOVE_TORMENT", "Torment"),
+ ("MOVE_FLATTER", "Flatter"),
+ ("MOVE_WILL_O_WISP", "Will-O-Wisp"),
+ ("MOVE_MEMENTO", "Memento"),
+ ("MOVE_FACADE", "Facade"),
+ ("MOVE_FOCUS_PUNCH", "Focus Punch"),
+ ("MOVE_SMELLING_SALT", "Smellingsalt"),
+ ("MOVE_FOLLOW_ME", "Follow Me"),
+ ("MOVE_NATURE_POWER", "Nature Power"),
+ ("MOVE_CHARGE", "Charge"),
+ ("MOVE_TAUNT", "Taunt"),
+ ("MOVE_HELPING_HAND", "Helping Hand"),
+ ("MOVE_TRICK", "Trick"),
+ ("MOVE_ROLE_PLAY", "Role Play"),
+ ("MOVE_WISH", "Wish"),
+ ("MOVE_ASSIST", "Assist"),
+ ("MOVE_INGRAIN", "Ingrain"),
+ ("MOVE_SUPERPOWER", "Superpower"),
+ ("MOVE_MAGIC_COAT", "Magic Coat"),
+ ("MOVE_RECYCLE", "Recycle"),
+ ("MOVE_REVENGE", "Revenge"),
+ ("MOVE_BRICK_BREAK", "Brick Break"),
+ ("MOVE_YAWN", "Yawn"),
+ ("MOVE_KNOCK_OFF", "Knock Off"),
+ ("MOVE_ENDEAVOR", "Endeavor"),
+ ("MOVE_ERUPTION", "Eruption"),
+ ("MOVE_SKILL_SWAP", "Skill Swap"),
+ ("MOVE_IMPRISON", "Imprison"),
+ ("MOVE_REFRESH", "Refresh"),
+ ("MOVE_GRUDGE", "Grudge"),
+ ("MOVE_SNATCH", "Snatch"),
+ ("MOVE_SECRET_POWER", "Secret Power"),
+ ("MOVE_DIVE", "Dive"),
+ ("MOVE_ARM_THRUST", "Arm Thrust"),
+ ("MOVE_CAMOUFLAGE", "Camouflage"),
+ ("MOVE_TAIL_GLOW", "Tail Glow"),
+ ("MOVE_LUSTER_PURGE", "Luster Purge"),
+ ("MOVE_MIST_BALL", "Mist Ball"),
+ ("MOVE_FEATHER_DANCE", "Featherdance"),
+ ("MOVE_TEETER_DANCE", "Teeter Dance"),
+ ("MOVE_BLAZE_KICK", "Blaze Kick"),
+ ("MOVE_MUD_SPORT", "Mud Sport"),
+ ("MOVE_ICE_BALL", "Ice Ball"),
+ ("MOVE_NEEDLE_ARM", "Needle Arm"),
+ ("MOVE_SLACK_OFF", "Slack Off"),
+ ("MOVE_HYPER_VOICE", "Hyper Voice"),
+ ("MOVE_POISON_FANG", "Poison Fang"),
+ ("MOVE_CRUSH_CLAW", "Crush Claw"),
+ ("MOVE_BLAST_BURN", "Blast Burn"),
+ ("MOVE_HYDRO_CANNON", "Hydro Cannon"),
+ ("MOVE_METEOR_MASH", "Meteor Mash"),
+ ("MOVE_ASTONISH", "Astonish"),
+ ("MOVE_WEATHER_BALL", "Weather Ball"),
+ ("MOVE_AROMATHERAPY", "Aromatherapy"),
+ ("MOVE_FAKE_TEARS", "Fake Tears"),
+ ("MOVE_AIR_CUTTER", "Air Cutter"),
+ ("MOVE_OVERHEAT", "Overheat"),
+ ("MOVE_ODOR_SLEUTH", "Odor Sleuth"),
+ ("MOVE_ROCK_TOMB", "Rock Tomb"),
+ ("MOVE_SILVER_WIND", "Silver Wind"),
+ ("MOVE_METAL_SOUND", "Metal Sound"),
+ ("MOVE_GRASS_WHISTLE", "Grasswhistle"),
+ ("MOVE_TICKLE", "Tickle"),
+ ("MOVE_COSMIC_POWER", "Cosmic Power"),
+ ("MOVE_WATER_SPOUT", "Water Spout"),
+ ("MOVE_SIGNAL_BEAM", "Signal Beam"),
+ ("MOVE_SHADOW_PUNCH", "Shadow Punch"),
+ ("MOVE_EXTRASENSORY", "Extrasensory"),
+ ("MOVE_SKY_UPPERCUT", "Sky Uppercut"),
+ ("MOVE_SAND_TOMB", "Sand Tomb"),
+ ("MOVE_SHEER_COLD", "Sheer Cold"),
+ ("MOVE_MUDDY_WATER", "Muddy Water"),
+ ("MOVE_BULLET_SEED", "Bullet Seed"),
+ ("MOVE_AERIAL_ACE", "Aerial Ace"),
+ ("MOVE_ICICLE_SPEAR", "Icicle Spear"),
+ ("MOVE_IRON_DEFENSE", "Iron Defense"),
+ ("MOVE_BLOCK", "Block"),
+ ("MOVE_HOWL", "Howl"),
+ ("MOVE_DRAGON_CLAW", "Dragon Claw"),
+ ("MOVE_FRENZY_PLANT", "Frenzy Plant"),
+ ("MOVE_BULK_UP", "Bulk Up"),
+ ("MOVE_BOUNCE", "Bounce"),
+ ("MOVE_MUD_SHOT", "Mud Shot"),
+ ("MOVE_POISON_TAIL", "Poison Tail"),
+ ("MOVE_COVET", "Covet"),
+ ("MOVE_VOLT_TACKLE", "Volt Tackle"),
+ ("MOVE_MAGICAL_LEAF", "Magical Leaf"),
+ ("MOVE_WATER_SPORT", "Water Sport"),
+ ("MOVE_CALM_MIND", "Calm Mind"),
+ ("MOVE_LEAF_BLADE", "Leaf Blade"),
+ ("MOVE_DRAGON_DANCE", "Dragon Dance"),
+ ("MOVE_ROCK_BLAST", "Rock Blast"),
+ ("MOVE_SHOCK_WAVE", "Shock Wave"),
+ ("MOVE_WATER_PULSE", "Water Pulse"),
+ ("MOVE_DOOM_DESIRE", "Doom Desire"),
+ ("MOVE_PSYCHO_BOOST", "Psycho Boost"),
+ ]}
# Create warp map
for warp, destination in extracted_data["warps"].items():
@@ -975,21 +1409,56 @@ def _init() -> None:
# Create trainer data
for i, trainer_json in enumerate(extracted_data["trainers"]):
party_json = trainer_json["party"]
- pokemon_data_type = _str_to_pokemon_data_type(trainer_json["pokemon_data_type"])
+ pokemon_data_type = _str_to_pokemon_data_type(trainer_json["data_type"])
data.trainers.append(TrainerData(
i,
TrainerPartyData(
[TrainerPokemonData(
p["species"],
p["level"],
- (p["moves"][0], p["moves"][1], p["moves"][2], p["moves"][3])
+ (p["moves"][0], p["moves"][1], p["moves"][2], p["moves"][3]) if "moves" in p else None
) for p in party_json],
pokemon_data_type,
- trainer_json["party_rom_address"]
+ trainer_json["party_address"]
),
- trainer_json["rom_address"],
- trainer_json["battle_script_rom_address"]
+ trainer_json["address"],
+ trainer_json["script_address"]
))
+data = PokemonEmeraldData()
_init()
+
+LEGENDARY_POKEMON = frozenset([data.constants[species] for species in [
+ "SPECIES_ARTICUNO",
+ "SPECIES_ZAPDOS",
+ "SPECIES_MOLTRES",
+ "SPECIES_MEWTWO",
+ "SPECIES_MEW",
+ "SPECIES_RAIKOU",
+ "SPECIES_ENTEI",
+ "SPECIES_SUICUNE",
+ "SPECIES_LUGIA",
+ "SPECIES_HO_OH",
+ "SPECIES_CELEBI",
+ "SPECIES_REGIROCK",
+ "SPECIES_REGICE",
+ "SPECIES_REGISTEEL",
+ "SPECIES_LATIAS",
+ "SPECIES_LATIOS",
+ "SPECIES_KYOGRE",
+ "SPECIES_GROUDON",
+ "SPECIES_RAYQUAZA",
+ "SPECIES_JIRACHI",
+ "SPECIES_DEOXYS",
+]])
+"""Species IDs of legendary pokemon"""
+
+UNEVOLVED_POKEMON = frozenset({
+ species.species_id
+ for species in data.species.values()
+ if len(species.evolutions) > 0
+})
+"""Species IDs of pokemon which have further evolution stages in the vanilla game"""
+
+NATIONAL_ID_TO_SPECIES_ID = {species.national_dex_number: i for i, species in data.species.items()}
diff --git a/worlds/pokemon_emerald/data/base_patch.bsdiff4 b/worlds/pokemon_emerald/data/base_patch.bsdiff4
index c1843904a9ca..0da226f617f6 100644
Binary files a/worlds/pokemon_emerald/data/base_patch.bsdiff4 and b/worlds/pokemon_emerald/data/base_patch.bsdiff4 differ
diff --git a/worlds/pokemon_emerald/data/extracted_data.json b/worlds/pokemon_emerald/data/extracted_data.json
index 6174cd4885ee..e12620683c45 100644
--- a/worlds/pokemon_emerald/data/extracted_data.json
+++ b/worlds/pokemon_emerald/data/extracted_data.json
@@ -1 +1 @@
-{"_comment":"DO NOT MODIFY. This file was auto-generated. Your changes will likely be overwritten.","_rom_name":"pokemon emerald version / AP 2","constants":{"ABILITIES_COUNT":78,"ABILITY_AIR_LOCK":77,"ABILITY_ARENA_TRAP":71,"ABILITY_BATTLE_ARMOR":4,"ABILITY_BLAZE":66,"ABILITY_CACOPHONY":76,"ABILITY_CHLOROPHYLL":34,"ABILITY_CLEAR_BODY":29,"ABILITY_CLOUD_NINE":13,"ABILITY_COLOR_CHANGE":16,"ABILITY_COMPOUND_EYES":14,"ABILITY_CUTE_CHARM":56,"ABILITY_DAMP":6,"ABILITY_DRIZZLE":2,"ABILITY_DROUGHT":70,"ABILITY_EARLY_BIRD":48,"ABILITY_EFFECT_SPORE":27,"ABILITY_FLAME_BODY":49,"ABILITY_FLASH_FIRE":18,"ABILITY_FORECAST":59,"ABILITY_GUTS":62,"ABILITY_HUGE_POWER":37,"ABILITY_HUSTLE":55,"ABILITY_HYPER_CUTTER":52,"ABILITY_ILLUMINATE":35,"ABILITY_IMMUNITY":17,"ABILITY_INNER_FOCUS":39,"ABILITY_INSOMNIA":15,"ABILITY_INTIMIDATE":22,"ABILITY_KEEN_EYE":51,"ABILITY_LEVITATE":26,"ABILITY_LIGHTNING_ROD":31,"ABILITY_LIMBER":7,"ABILITY_LIQUID_OOZE":64,"ABILITY_MAGMA_ARMOR":40,"ABILITY_MAGNET_PULL":42,"ABILITY_MARVEL_SCALE":63,"ABILITY_MINUS":58,"ABILITY_NATURAL_CURE":30,"ABILITY_NONE":0,"ABILITY_OBLIVIOUS":12,"ABILITY_OVERGROW":65,"ABILITY_OWN_TEMPO":20,"ABILITY_PICKUP":53,"ABILITY_PLUS":57,"ABILITY_POISON_POINT":38,"ABILITY_PRESSURE":46,"ABILITY_PURE_POWER":74,"ABILITY_RAIN_DISH":44,"ABILITY_ROCK_HEAD":69,"ABILITY_ROUGH_SKIN":24,"ABILITY_RUN_AWAY":50,"ABILITY_SAND_STREAM":45,"ABILITY_SAND_VEIL":8,"ABILITY_SERENE_GRACE":32,"ABILITY_SHADOW_TAG":23,"ABILITY_SHED_SKIN":61,"ABILITY_SHELL_ARMOR":75,"ABILITY_SHIELD_DUST":19,"ABILITY_SOUNDPROOF":43,"ABILITY_SPEED_BOOST":3,"ABILITY_STATIC":9,"ABILITY_STENCH":1,"ABILITY_STICKY_HOLD":60,"ABILITY_STURDY":5,"ABILITY_SUCTION_CUPS":21,"ABILITY_SWARM":68,"ABILITY_SWIFT_SWIM":33,"ABILITY_SYNCHRONIZE":28,"ABILITY_THICK_FAT":47,"ABILITY_TORRENT":67,"ABILITY_TRACE":36,"ABILITY_TRUANT":54,"ABILITY_VITAL_SPIRIT":72,"ABILITY_VOLT_ABSORB":10,"ABILITY_WATER_ABSORB":11,"ABILITY_WATER_VEIL":41,"ABILITY_WHITE_SMOKE":73,"ABILITY_WONDER_GUARD":25,"ACRO_BIKE":1,"BAG_ITEM_CAPACITY_DIGITS":2,"BERRY_CAPACITY_DIGITS":3,"DAILY_FLAGS_END":2399,"DAILY_FLAGS_START":2336,"FIRST_BALL":1,"FIRST_BERRY_INDEX":133,"FIRST_BERRY_MASTER_BERRY":153,"FIRST_BERRY_MASTER_WIFE_BERRY":133,"FIRST_KIRI_BERRY":153,"FIRST_MAIL_INDEX":121,"FIRST_ROUTE_114_MAN_BERRY":148,"FLAGS_COUNT":2400,"FLAG_ADDED_MATCH_CALL_TO_POKENAV":304,"FLAG_ADVENTURE_STARTED":116,"FLAG_ARRIVED_AT_MARINE_CAVE_EMERGE_SPOT":2265,"FLAG_ARRIVED_AT_NAVEL_ROCK":2273,"FLAG_ARRIVED_AT_TERRA_CAVE_ENTRANCE":2266,"FLAG_ARRIVED_ON_FARAWAY_ISLAND":2264,"FLAG_BADGE01_GET":2151,"FLAG_BADGE02_GET":2152,"FLAG_BADGE03_GET":2153,"FLAG_BADGE04_GET":2154,"FLAG_BADGE05_GET":2155,"FLAG_BADGE06_GET":2156,"FLAG_BADGE07_GET":2157,"FLAG_BADGE08_GET":2158,"FLAG_BATTLED_DEOXYS":429,"FLAG_BATTLE_FRONTIER_TRADE_DONE":156,"FLAG_BEAT_MAGMA_GRUNT_JAGGED_PASS":313,"FLAG_BEAUTY_PAINTING_MADE":161,"FLAG_BETTER_SHOPS_ENABLED":483,"FLAG_BIRCH_AIDE_MET":88,"FLAG_CANCEL_BATTLE_ROOM_CHALLENGE":119,"FLAG_CAUGHT_HO_OH":146,"FLAG_CAUGHT_LATIAS_OR_LATIOS":457,"FLAG_CAUGHT_LUGIA":145,"FLAG_CAUGHT_MEW":458,"FLAG_CHOSEN_MULTI_BATTLE_NPC_PARTNER":338,"FLAG_CHOSE_CLAW_FOSSIL":336,"FLAG_CHOSE_ROOT_FOSSIL":335,"FLAG_COLLECTED_ALL_GOLD_SYMBOLS":466,"FLAG_COLLECTED_ALL_SILVER_SYMBOLS":92,"FLAG_CONTEST_SKETCH_CREATED":270,"FLAG_COOL_PAINTING_MADE":160,"FLAG_CUTE_PAINTING_MADE":162,"FLAG_DAILY_APPRENTICE_LEAVES":2356,"FLAG_DAILY_BERRY_MASTERS_WIFE":2353,"FLAG_DAILY_BERRY_MASTER_RECEIVED_BERRY":2349,"FLAG_DAILY_CONTEST_LOBBY_RECEIVED_BERRY":2337,"FLAG_DAILY_FLOWER_SHOP_RECEIVED_BERRY":2352,"FLAG_DAILY_LILYCOVE_RECEIVED_BERRY":2351,"FLAG_DAILY_PICKED_LOTO_TICKET":2346,"FLAG_DAILY_ROUTE_111_RECEIVED_BERRY":2348,"FLAG_DAILY_ROUTE_114_RECEIVED_BERRY":2347,"FLAG_DAILY_ROUTE_120_RECEIVED_BERRY":2350,"FLAG_DAILY_SECRET_BASE":2338,"FLAG_DAILY_SOOTOPOLIS_RECEIVED_BERRY":2354,"FLAG_DECLINED_BIKE":89,"FLAG_DECLINED_RIVAL_BATTLE_LILYCOVE":286,"FLAG_DECLINED_WALLY_BATTLE_MAUVILLE":284,"FLAG_DECORATION_1":174,"FLAG_DECORATION_10":183,"FLAG_DECORATION_11":184,"FLAG_DECORATION_12":185,"FLAG_DECORATION_13":186,"FLAG_DECORATION_14":187,"FLAG_DECORATION_2":175,"FLAG_DECORATION_3":176,"FLAG_DECORATION_4":177,"FLAG_DECORATION_5":178,"FLAG_DECORATION_6":179,"FLAG_DECORATION_7":180,"FLAG_DECORATION_8":181,"FLAG_DECORATION_9":182,"FLAG_DEFEATED_DEOXYS":428,"FLAG_DEFEATED_DEWFORD_GYM":1265,"FLAG_DEFEATED_ELECTRODE_1_AQUA_HIDEOUT":452,"FLAG_DEFEATED_ELECTRODE_2_AQUA_HIDEOUT":453,"FLAG_DEFEATED_ELITE_4_DRAKE":1278,"FLAG_DEFEATED_ELITE_4_GLACIA":1277,"FLAG_DEFEATED_ELITE_4_PHOEBE":1276,"FLAG_DEFEATED_ELITE_4_SIDNEY":1275,"FLAG_DEFEATED_EVIL_TEAM_MT_CHIMNEY":139,"FLAG_DEFEATED_FORTREE_GYM":1269,"FLAG_DEFEATED_GROUDON":447,"FLAG_DEFEATED_GRUNT_SPACE_CENTER_1F":191,"FLAG_DEFEATED_HO_OH":476,"FLAG_DEFEATED_KYOGRE":446,"FLAG_DEFEATED_LATIAS_OR_LATIOS":456,"FLAG_DEFEATED_LAVARIDGE_GYM":1267,"FLAG_DEFEATED_LUGIA":477,"FLAG_DEFEATED_MAGMA_SPACE_CENTER":117,"FLAG_DEFEATED_MAUVILLE_GYM":1266,"FLAG_DEFEATED_METEOR_FALLS_STEVEN":1272,"FLAG_DEFEATED_MEW":455,"FLAG_DEFEATED_MOSSDEEP_GYM":1270,"FLAG_DEFEATED_PETALBURG_GYM":1268,"FLAG_DEFEATED_RAYQUAZA":448,"FLAG_DEFEATED_REGICE":444,"FLAG_DEFEATED_REGIROCK":443,"FLAG_DEFEATED_REGISTEEL":445,"FLAG_DEFEATED_RIVAL_ROUTE103":130,"FLAG_DEFEATED_RIVAL_ROUTE_104":125,"FLAG_DEFEATED_RIVAL_RUSTBORO":211,"FLAG_DEFEATED_RUSTBORO_GYM":1264,"FLAG_DEFEATED_SEASHORE_HOUSE":141,"FLAG_DEFEATED_SOOTOPOLIS_GYM":1271,"FLAG_DEFEATED_SS_TIDAL_TRAINERS":247,"FLAG_DEFEATED_SUDOWOODO":454,"FLAG_DEFEATED_VOLTORB_1_NEW_MAUVILLE":449,"FLAG_DEFEATED_VOLTORB_2_NEW_MAUVILLE":450,"FLAG_DEFEATED_VOLTORB_3_NEW_MAUVILLE":451,"FLAG_DEFEATED_WALLY_MAUVILLE":190,"FLAG_DEFEATED_WALLY_VICTORY_ROAD":126,"FLAG_DELIVERED_DEVON_GOODS":149,"FLAG_DELIVERED_STEVEN_LETTER":189,"FLAG_DEOXYS_ROCK_COMPLETE":2260,"FLAG_DEVON_GOODS_STOLEN":142,"FLAG_DOCK_REJECTED_DEVON_GOODS":148,"FLAG_DONT_TRANSITION_MUSIC":16385,"FLAG_DUMMY_LATIAS":33,"FLAG_DUMMY_LATIOS":32,"FLAG_ENABLE_BRAWLY_MATCH_CALL":468,"FLAG_ENABLE_FIRST_WALLY_POKENAV_CALL":136,"FLAG_ENABLE_FLANNERY_MATCH_CALL":470,"FLAG_ENABLE_JUAN_MATCH_CALL":473,"FLAG_ENABLE_MOM_MATCH_CALL":216,"FLAG_ENABLE_MR_STONE_POKENAV":344,"FLAG_ENABLE_MULTI_CORRIDOR_DOOR":16386,"FLAG_ENABLE_NORMAN_MATCH_CALL":306,"FLAG_ENABLE_PROF_BIRCH_MATCH_CALL":281,"FLAG_ENABLE_RIVAL_MATCH_CALL":253,"FLAG_ENABLE_ROXANNE_FIRST_CALL":128,"FLAG_ENABLE_ROXANNE_MATCH_CALL":467,"FLAG_ENABLE_SCOTT_MATCH_CALL":215,"FLAG_ENABLE_SHIP_BIRTH_ISLAND":2261,"FLAG_ENABLE_SHIP_FARAWAY_ISLAND":2262,"FLAG_ENABLE_SHIP_NAVEL_ROCK":2272,"FLAG_ENABLE_SHIP_SOUTHERN_ISLAND":2227,"FLAG_ENABLE_TATE_AND_LIZA_MATCH_CALL":472,"FLAG_ENABLE_WALLY_MATCH_CALL":214,"FLAG_ENABLE_WATTSON_MATCH_CALL":469,"FLAG_ENABLE_WINONA_MATCH_CALL":471,"FLAG_ENCOUNTERED_LATIAS_OR_LATIOS":206,"FLAG_ENTERED_CONTEST":341,"FLAG_ENTERED_ELITE_FOUR":263,"FLAG_ENTERED_MIRAGE_TOWER":2268,"FLAG_EVIL_LEADER_PLEASE_STOP":219,"FLAG_EVIL_TEAM_ESCAPED_STERN_SPOKE":271,"FLAG_EXCHANGED_SCANNER":294,"FLAG_FAN_CLUB_STRENGTH_SHARED":210,"FLAG_FORCE_MIRAGE_TOWER_VISIBLE":157,"FLAG_FORTREE_NPC_TRADE_COMPLETED":155,"FLAG_GOOD_LUCK_SAFARI_ZONE":93,"FLAG_GOT_BASEMENT_KEY_FROM_WATTSON":208,"FLAG_GOT_TM24_FROM_WATTSON":209,"FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT":111,"FLAG_HAS_MATCH_CALL":303,"FLAG_HIDDEN_ITEMS_START":500,"FLAG_HIDDEN_ITEM_ABANDONED_SHIP_RM_1_KEY":531,"FLAG_HIDDEN_ITEM_ABANDONED_SHIP_RM_2_KEY":532,"FLAG_HIDDEN_ITEM_ABANDONED_SHIP_RM_4_KEY":533,"FLAG_HIDDEN_ITEM_ABANDONED_SHIP_RM_6_KEY":534,"FLAG_HIDDEN_ITEM_ARTISAN_CAVE_B1F_CALCIUM":601,"FLAG_HIDDEN_ITEM_ARTISAN_CAVE_B1F_IRON":604,"FLAG_HIDDEN_ITEM_ARTISAN_CAVE_B1F_PROTEIN":603,"FLAG_HIDDEN_ITEM_ARTISAN_CAVE_B1F_ZINC":602,"FLAG_HIDDEN_ITEM_FALLARBOR_TOWN_NUGGET":528,"FLAG_HIDDEN_ITEM_GRANITE_CAVE_B2F_EVERSTONE_1":548,"FLAG_HIDDEN_ITEM_GRANITE_CAVE_B2F_EVERSTONE_2":549,"FLAG_HIDDEN_ITEM_JAGGED_PASS_FULL_HEAL":577,"FLAG_HIDDEN_ITEM_JAGGED_PASS_GREAT_BALL":576,"FLAG_HIDDEN_ITEM_LAVARIDGE_TOWN_ICE_HEAL":500,"FLAG_HIDDEN_ITEM_LILYCOVE_CITY_HEART_SCALE":527,"FLAG_HIDDEN_ITEM_LILYCOVE_CITY_POKE_BALL":575,"FLAG_HIDDEN_ITEM_LILYCOVE_CITY_PP_UP":543,"FLAG_HIDDEN_ITEM_MT_PYRE_EXTERIOR_MAX_ETHER":578,"FLAG_HIDDEN_ITEM_MT_PYRE_EXTERIOR_ULTRA_BALL":529,"FLAG_HIDDEN_ITEM_MT_PYRE_SUMMIT_RARE_CANDY":580,"FLAG_HIDDEN_ITEM_MT_PYRE_SUMMIT_ZINC":579,"FLAG_HIDDEN_ITEM_NAVEL_ROCK_TOP_SACRED_ASH":609,"FLAG_HIDDEN_ITEM_PETALBURG_CITY_RARE_CANDY":595,"FLAG_HIDDEN_ITEM_PETALBURG_WOODS_POKE_BALL":561,"FLAG_HIDDEN_ITEM_PETALBURG_WOODS_POTION":558,"FLAG_HIDDEN_ITEM_PETALBURG_WOODS_TINY_MUSHROOM_1":559,"FLAG_HIDDEN_ITEM_PETALBURG_WOODS_TINY_MUSHROOM_2":560,"FLAG_HIDDEN_ITEM_ROUTE_104_ANTIDOTE":585,"FLAG_HIDDEN_ITEM_ROUTE_104_HEART_SCALE":588,"FLAG_HIDDEN_ITEM_ROUTE_104_POKE_BALL":562,"FLAG_HIDDEN_ITEM_ROUTE_104_POTION":537,"FLAG_HIDDEN_ITEM_ROUTE_104_SUPER_POTION":544,"FLAG_HIDDEN_ITEM_ROUTE_105_BIG_PEARL":611,"FLAG_HIDDEN_ITEM_ROUTE_105_HEART_SCALE":589,"FLAG_HIDDEN_ITEM_ROUTE_106_HEART_SCALE":547,"FLAG_HIDDEN_ITEM_ROUTE_106_POKE_BALL":563,"FLAG_HIDDEN_ITEM_ROUTE_106_STARDUST":546,"FLAG_HIDDEN_ITEM_ROUTE_108_RARE_CANDY":586,"FLAG_HIDDEN_ITEM_ROUTE_109_ETHER":564,"FLAG_HIDDEN_ITEM_ROUTE_109_GREAT_BALL":551,"FLAG_HIDDEN_ITEM_ROUTE_109_HEART_SCALE_1":552,"FLAG_HIDDEN_ITEM_ROUTE_109_HEART_SCALE_2":590,"FLAG_HIDDEN_ITEM_ROUTE_109_HEART_SCALE_3":591,"FLAG_HIDDEN_ITEM_ROUTE_109_REVIVE":550,"FLAG_HIDDEN_ITEM_ROUTE_110_FULL_HEAL":555,"FLAG_HIDDEN_ITEM_ROUTE_110_GREAT_BALL":553,"FLAG_HIDDEN_ITEM_ROUTE_110_POKE_BALL":565,"FLAG_HIDDEN_ITEM_ROUTE_110_REVIVE":554,"FLAG_HIDDEN_ITEM_ROUTE_111_PROTEIN":556,"FLAG_HIDDEN_ITEM_ROUTE_111_RARE_CANDY":557,"FLAG_HIDDEN_ITEM_ROUTE_111_STARDUST":502,"FLAG_HIDDEN_ITEM_ROUTE_113_ETHER":503,"FLAG_HIDDEN_ITEM_ROUTE_113_NUGGET":598,"FLAG_HIDDEN_ITEM_ROUTE_113_TM32":530,"FLAG_HIDDEN_ITEM_ROUTE_114_CARBOS":504,"FLAG_HIDDEN_ITEM_ROUTE_114_REVIVE":542,"FLAG_HIDDEN_ITEM_ROUTE_115_HEART_SCALE":597,"FLAG_HIDDEN_ITEM_ROUTE_116_BLACK_GLASSES":596,"FLAG_HIDDEN_ITEM_ROUTE_116_SUPER_POTION":545,"FLAG_HIDDEN_ITEM_ROUTE_117_REPEL":572,"FLAG_HIDDEN_ITEM_ROUTE_118_HEART_SCALE":566,"FLAG_HIDDEN_ITEM_ROUTE_118_IRON":567,"FLAG_HIDDEN_ITEM_ROUTE_119_CALCIUM":505,"FLAG_HIDDEN_ITEM_ROUTE_119_FULL_HEAL":568,"FLAG_HIDDEN_ITEM_ROUTE_119_MAX_ETHER":587,"FLAG_HIDDEN_ITEM_ROUTE_119_ULTRA_BALL":506,"FLAG_HIDDEN_ITEM_ROUTE_120_RARE_CANDY_1":571,"FLAG_HIDDEN_ITEM_ROUTE_120_RARE_CANDY_2":569,"FLAG_HIDDEN_ITEM_ROUTE_120_REVIVE":584,"FLAG_HIDDEN_ITEM_ROUTE_120_ZINC":570,"FLAG_HIDDEN_ITEM_ROUTE_121_FULL_HEAL":573,"FLAG_HIDDEN_ITEM_ROUTE_121_HP_UP":539,"FLAG_HIDDEN_ITEM_ROUTE_121_MAX_REVIVE":600,"FLAG_HIDDEN_ITEM_ROUTE_121_NUGGET":540,"FLAG_HIDDEN_ITEM_ROUTE_123_HYPER_POTION":574,"FLAG_HIDDEN_ITEM_ROUTE_123_PP_UP":599,"FLAG_HIDDEN_ITEM_ROUTE_123_RARE_CANDY":610,"FLAG_HIDDEN_ITEM_ROUTE_123_REVIVE":541,"FLAG_HIDDEN_ITEM_ROUTE_123_SUPER_REPEL":507,"FLAG_HIDDEN_ITEM_ROUTE_128_HEART_SCALE_1":592,"FLAG_HIDDEN_ITEM_ROUTE_128_HEART_SCALE_2":593,"FLAG_HIDDEN_ITEM_ROUTE_128_HEART_SCALE_3":594,"FLAG_HIDDEN_ITEM_SAFARI_ZONE_NORTH_EAST_RARE_CANDY":606,"FLAG_HIDDEN_ITEM_SAFARI_ZONE_NORTH_EAST_ZINC":607,"FLAG_HIDDEN_ITEM_SAFARI_ZONE_SOUTH_EAST_FULL_RESTORE":605,"FLAG_HIDDEN_ITEM_SAFARI_ZONE_SOUTH_EAST_PP_UP":608,"FLAG_HIDDEN_ITEM_SS_TIDAL_LOWER_DECK_LEFTOVERS":535,"FLAG_HIDDEN_ITEM_TRICK_HOUSE_NUGGET":501,"FLAG_HIDDEN_ITEM_UNDERWATER_124_BIG_PEARL":511,"FLAG_HIDDEN_ITEM_UNDERWATER_124_CALCIUM":536,"FLAG_HIDDEN_ITEM_UNDERWATER_124_CARBOS":508,"FLAG_HIDDEN_ITEM_UNDERWATER_124_GREEN_SHARD":509,"FLAG_HIDDEN_ITEM_UNDERWATER_124_HEART_SCALE_1":513,"FLAG_HIDDEN_ITEM_UNDERWATER_124_HEART_SCALE_2":538,"FLAG_HIDDEN_ITEM_UNDERWATER_124_PEARL":510,"FLAG_HIDDEN_ITEM_UNDERWATER_126_BIG_PEARL":520,"FLAG_HIDDEN_ITEM_UNDERWATER_126_BLUE_SHARD":512,"FLAG_HIDDEN_ITEM_UNDERWATER_126_HEART_SCALE":514,"FLAG_HIDDEN_ITEM_UNDERWATER_126_IRON":519,"FLAG_HIDDEN_ITEM_UNDERWATER_126_PEARL":517,"FLAG_HIDDEN_ITEM_UNDERWATER_126_STARDUST":516,"FLAG_HIDDEN_ITEM_UNDERWATER_126_ULTRA_BALL":515,"FLAG_HIDDEN_ITEM_UNDERWATER_126_YELLOW_SHARD":518,"FLAG_HIDDEN_ITEM_UNDERWATER_127_HEART_SCALE":523,"FLAG_HIDDEN_ITEM_UNDERWATER_127_HP_UP":522,"FLAG_HIDDEN_ITEM_UNDERWATER_127_RED_SHARD":524,"FLAG_HIDDEN_ITEM_UNDERWATER_127_STAR_PIECE":521,"FLAG_HIDDEN_ITEM_UNDERWATER_128_PEARL":526,"FLAG_HIDDEN_ITEM_UNDERWATER_128_PROTEIN":525,"FLAG_HIDDEN_ITEM_VICTORY_ROAD_1F_ULTRA_BALL":581,"FLAG_HIDDEN_ITEM_VICTORY_ROAD_B2F_ELIXIR":582,"FLAG_HIDDEN_ITEM_VICTORY_ROAD_B2F_MAX_REPEL":583,"FLAG_HIDE_APPRENTICE":701,"FLAG_HIDE_AQUA_HIDEOUT_1F_GRUNTS_BLOCKING_ENTRANCE":821,"FLAG_HIDE_AQUA_HIDEOUT_B1F_ELECTRODE_1":977,"FLAG_HIDE_AQUA_HIDEOUT_B1F_ELECTRODE_2":978,"FLAG_HIDE_AQUA_HIDEOUT_B2F_SUBMARINE_SHADOW":943,"FLAG_HIDE_AQUA_HIDEOUT_GRUNTS":924,"FLAG_HIDE_BATTLE_FRONTIER_RECEPTION_GATE_SCOTT":836,"FLAG_HIDE_BATTLE_FRONTIER_SUDOWOODO":842,"FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_1":711,"FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_2":712,"FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_3":713,"FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_4":714,"FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_5":715,"FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_6":716,"FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_ALT_1":864,"FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_ALT_2":865,"FLAG_HIDE_BATTLE_TOWER_OPPONENT":888,"FLAG_HIDE_BATTLE_TOWER_REPORTER":918,"FLAG_HIDE_BIRTH_ISLAND_DEOXYS_TRIANGLE":764,"FLAG_HIDE_BRINEYS_HOUSE_MR_BRINEY":739,"FLAG_HIDE_BRINEYS_HOUSE_PEEKO":881,"FLAG_HIDE_CAVE_OF_ORIGIN_B1F_WALLACE":820,"FLAG_HIDE_CHAMPIONS_ROOM_BIRCH":921,"FLAG_HIDE_CHAMPIONS_ROOM_RIVAL":920,"FLAG_HIDE_CONTEST_POKE_BALL":86,"FLAG_HIDE_DEOXYS":763,"FLAG_HIDE_DESERT_UNDERPASS_FOSSIL":874,"FLAG_HIDE_DEWFORD_HALL_SLUDGE_BOMB_MAN":940,"FLAG_HIDE_EVER_GRANDE_POKEMON_CENTER_1F_SCOTT":793,"FLAG_HIDE_FALLARBOR_AZURILL":907,"FLAG_HIDE_FALLARBOR_HOUSE_PROF_COZMO":928,"FLAG_HIDE_FALLARBOR_TOWN_BATTLE_TENT_SCOTT":767,"FLAG_HIDE_FALLORBOR_POKEMON_CENTER_LANETTE":871,"FLAG_HIDE_FANCLUB_BOY":790,"FLAG_HIDE_FANCLUB_LADY":792,"FLAG_HIDE_FANCLUB_LITTLE_BOY":791,"FLAG_HIDE_FANCLUB_OLD_LADY":789,"FLAG_HIDE_FORTREE_CITY_HOUSE_4_WINGULL":933,"FLAG_HIDE_FORTREE_CITY_KECLEON":969,"FLAG_HIDE_GRANITE_CAVE_STEVEN":833,"FLAG_HIDE_HO_OH":801,"FLAG_HIDE_JAGGED_PASS_MAGMA_GUARD":847,"FLAG_HIDE_LANETTES_HOUSE_LANETTE":870,"FLAG_HIDE_LAVARIDGE_TOWN_RIVAL":929,"FLAG_HIDE_LAVARIDGE_TOWN_RIVAL_ON_BIKE":930,"FLAG_HIDE_LEGEND_MON_CAVE_OF_ORIGIN":825,"FLAG_HIDE_LILYCOVE_CITY_AQUA_GRUNTS":852,"FLAG_HIDE_LILYCOVE_CITY_RIVAL":971,"FLAG_HIDE_LILYCOVE_CITY_WAILMER":729,"FLAG_HIDE_LILYCOVE_CONTEST_HALL_BLEND_MASTER":832,"FLAG_HIDE_LILYCOVE_CONTEST_HALL_BLEND_MASTER_REPLACEMENT":873,"FLAG_HIDE_LILYCOVE_CONTEST_HALL_CONTEST_ATTENDANT_1":774,"FLAG_HIDE_LILYCOVE_CONTEST_HALL_CONTEST_ATTENDANT_2":895,"FLAG_HIDE_LILYCOVE_CONTEST_HALL_REPORTER":802,"FLAG_HIDE_LILYCOVE_DEPARTMENT_STORE_ROOFTOP_SALE_WOMAN":962,"FLAG_HIDE_LILYCOVE_FAN_CLUB_INTERVIEWER":730,"FLAG_HIDE_LILYCOVE_HARBOR_EVENT_TICKET_TAKER":748,"FLAG_HIDE_LILYCOVE_HARBOR_FERRY_ATTENDANT":908,"FLAG_HIDE_LILYCOVE_HARBOR_FERRY_SAILOR":909,"FLAG_HIDE_LILYCOVE_HARBOR_SSTIDAL":861,"FLAG_HIDE_LILYCOVE_MOTEL_GAME_DESIGNERS":925,"FLAG_HIDE_LILYCOVE_MOTEL_SCOTT":787,"FLAG_HIDE_LILYCOVE_MUSEUM_CURATOR":775,"FLAG_HIDE_LILYCOVE_MUSEUM_PATRON_1":776,"FLAG_HIDE_LILYCOVE_MUSEUM_PATRON_2":777,"FLAG_HIDE_LILYCOVE_MUSEUM_PATRON_3":778,"FLAG_HIDE_LILYCOVE_MUSEUM_PATRON_4":779,"FLAG_HIDE_LILYCOVE_MUSEUM_TOURISTS":780,"FLAG_HIDE_LILYCOVE_POKEMON_CENTER_CONTEST_LADY_MON":993,"FLAG_HIDE_LITTLEROOT_TOWN_BIRCH":795,"FLAG_HIDE_LITTLEROOT_TOWN_BIRCHS_LAB_BIRCH":721,"FLAG_HIDE_LITTLEROOT_TOWN_BIRCHS_LAB_POKEBALL_CHIKORITA":838,"FLAG_HIDE_LITTLEROOT_TOWN_BIRCHS_LAB_POKEBALL_CYNDAQUIL":811,"FLAG_HIDE_LITTLEROOT_TOWN_BIRCHS_LAB_POKEBALL_TOTODILE":812,"FLAG_HIDE_LITTLEROOT_TOWN_BIRCHS_LAB_RIVAL":889,"FLAG_HIDE_LITTLEROOT_TOWN_BIRCHS_LAB_UNKNOWN_0x380":896,"FLAG_HIDE_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F_POKE_BALL":817,"FLAG_HIDE_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F_SWABLU_DOLL":815,"FLAG_HIDE_LITTLEROOT_TOWN_BRENDANS_HOUSE_BRENDAN":745,"FLAG_HIDE_LITTLEROOT_TOWN_BRENDANS_HOUSE_MOM":758,"FLAG_HIDE_LITTLEROOT_TOWN_BRENDANS_HOUSE_RIVAL_BEDROOM":760,"FLAG_HIDE_LITTLEROOT_TOWN_BRENDANS_HOUSE_RIVAL_MOM":784,"FLAG_HIDE_LITTLEROOT_TOWN_BRENDANS_HOUSE_RIVAL_SIBLING":735,"FLAG_HIDE_LITTLEROOT_TOWN_BRENDANS_HOUSE_TRUCK":761,"FLAG_HIDE_LITTLEROOT_TOWN_FAT_MAN":868,"FLAG_HIDE_LITTLEROOT_TOWN_MAYS_HOUSE_2F_PICHU_DOLL":849,"FLAG_HIDE_LITTLEROOT_TOWN_MAYS_HOUSE_2F_POKE_BALL":818,"FLAG_HIDE_LITTLEROOT_TOWN_MAYS_HOUSE_MAY":746,"FLAG_HIDE_LITTLEROOT_TOWN_MAYS_HOUSE_MOM":759,"FLAG_HIDE_LITTLEROOT_TOWN_MAYS_HOUSE_RIVAL_BEDROOM":722,"FLAG_HIDE_LITTLEROOT_TOWN_MAYS_HOUSE_RIVAL_MOM":785,"FLAG_HIDE_LITTLEROOT_TOWN_MAYS_HOUSE_RIVAL_SIBLING":736,"FLAG_HIDE_LITTLEROOT_TOWN_MAYS_HOUSE_TRUCK":762,"FLAG_HIDE_LITTLEROOT_TOWN_MOM_OUTSIDE":752,"FLAG_HIDE_LITTLEROOT_TOWN_PLAYERS_BEDROOM_MOM":757,"FLAG_HIDE_LITTLEROOT_TOWN_PLAYERS_HOUSE_VIGOROTH_1":754,"FLAG_HIDE_LITTLEROOT_TOWN_PLAYERS_HOUSE_VIGOROTH_2":755,"FLAG_HIDE_LITTLEROOT_TOWN_RIVAL":794,"FLAG_HIDE_LUGIA":800,"FLAG_HIDE_MAGMA_HIDEOUT_4F_GROUDON":853,"FLAG_HIDE_MAGMA_HIDEOUT_4F_GROUDON_ASLEEP":850,"FLAG_HIDE_MAGMA_HIDEOUT_GRUNTS":857,"FLAG_HIDE_MAP_NAME_POPUP":16384,"FLAG_HIDE_MARINE_CAVE_KYOGRE":782,"FLAG_HIDE_MAUVILLE_CITY_SCOTT":765,"FLAG_HIDE_MAUVILLE_CITY_WALLY":804,"FLAG_HIDE_MAUVILLE_CITY_WALLYS_UNCLE":805,"FLAG_HIDE_MAUVILLE_CITY_WATTSON":912,"FLAG_HIDE_MAUVILLE_GYM_WATTSON":913,"FLAG_HIDE_METEOR_FALLS_1F_1R_COZMO":942,"FLAG_HIDE_METEOR_FALLS_TEAM_AQUA":938,"FLAG_HIDE_METEOR_FALLS_TEAM_MAGMA":939,"FLAG_HIDE_MEW":718,"FLAG_HIDE_MIRAGE_TOWER_CLAW_FOSSIL":964,"FLAG_HIDE_MIRAGE_TOWER_ROOT_FOSSIL":963,"FLAG_HIDE_MOSSDEEP_CITY_HOUSE_2_WINGULL":934,"FLAG_HIDE_MOSSDEEP_CITY_SCOTT":788,"FLAG_HIDE_MOSSDEEP_CITY_SPACE_CENTER_1F_STEVEN":753,"FLAG_HIDE_MOSSDEEP_CITY_SPACE_CENTER_1F_TEAM_MAGMA":756,"FLAG_HIDE_MOSSDEEP_CITY_SPACE_CENTER_2F_STEVEN":863,"FLAG_HIDE_MOSSDEEP_CITY_SPACE_CENTER_2F_TEAM_MAGMA":862,"FLAG_HIDE_MOSSDEEP_CITY_SPACE_CENTER_MAGMA_NOTE":737,"FLAG_HIDE_MOSSDEEP_CITY_STEVENS_HOUSE_BELDUM_POKEBALL":968,"FLAG_HIDE_MOSSDEEP_CITY_STEVENS_HOUSE_INVISIBLE_NINJA_BOY":727,"FLAG_HIDE_MOSSDEEP_CITY_STEVENS_HOUSE_STEVEN":967,"FLAG_HIDE_MOSSDEEP_CITY_TEAM_MAGMA":823,"FLAG_HIDE_MR_BRINEY_BOAT_DEWFORD_TOWN":743,"FLAG_HIDE_MR_BRINEY_DEWFORD_TOWN":740,"FLAG_HIDE_MT_CHIMNEY_LAVA_COOKIE_LADY":994,"FLAG_HIDE_MT_CHIMNEY_TEAM_AQUA":926,"FLAG_HIDE_MT_CHIMNEY_TEAM_MAGMA":927,"FLAG_HIDE_MT_CHIMNEY_TRAINERS":877,"FLAG_HIDE_MT_PYRE_SUMMIT_ARCHIE":916,"FLAG_HIDE_MT_PYRE_SUMMIT_MAXIE":856,"FLAG_HIDE_MT_PYRE_SUMMIT_TEAM_AQUA":917,"FLAG_HIDE_NEW_MAUVILLE_VOLTORB_1":974,"FLAG_HIDE_NEW_MAUVILLE_VOLTORB_2":975,"FLAG_HIDE_NEW_MAUVILLE_VOLTORB_3":976,"FLAG_HIDE_OLDALE_TOWN_RIVAL":979,"FLAG_HIDE_PETALBURG_CITY_SCOTT":995,"FLAG_HIDE_PETALBURG_CITY_WALLY":726,"FLAG_HIDE_PETALBURG_CITY_WALLYS_DAD":830,"FLAG_HIDE_PETALBURG_CITY_WALLYS_MOM":728,"FLAG_HIDE_PETALBURG_GYM_GREETER":781,"FLAG_HIDE_PETALBURG_GYM_NORMAN":772,"FLAG_HIDE_PETALBURG_GYM_WALLY":866,"FLAG_HIDE_PETALBURG_GYM_WALLYS_DAD":824,"FLAG_HIDE_PETALBURG_WOODS_AQUA_GRUNT":725,"FLAG_HIDE_PETALBURG_WOODS_DEVON_EMPLOYEE":724,"FLAG_HIDE_PLAYERS_HOUSE_DAD":734,"FLAG_HIDE_POKEMON_CENTER_2F_MYSTERY_GIFT_MAN":702,"FLAG_HIDE_REGICE":936,"FLAG_HIDE_REGIROCK":935,"FLAG_HIDE_REGISTEEL":937,"FLAG_HIDE_ROUTE_101_BIRCH":897,"FLAG_HIDE_ROUTE_101_BIRCH_STARTERS_BAG":700,"FLAG_HIDE_ROUTE_101_BIRCH_ZIGZAGOON_BATTLE":720,"FLAG_HIDE_ROUTE_101_BOY":991,"FLAG_HIDE_ROUTE_101_ZIGZAGOON":750,"FLAG_HIDE_ROUTE_103_BIRCH":898,"FLAG_HIDE_ROUTE_103_RIVAL":723,"FLAG_HIDE_ROUTE_104_MR_BRINEY":738,"FLAG_HIDE_ROUTE_104_MR_BRINEY_BOAT":742,"FLAG_HIDE_ROUTE_104_RIVAL":719,"FLAG_HIDE_ROUTE_104_WHITE_HERB_FLORIST":906,"FLAG_HIDE_ROUTE_109_MR_BRINEY":741,"FLAG_HIDE_ROUTE_109_MR_BRINEY_BOAT":744,"FLAG_HIDE_ROUTE_110_BIRCH":837,"FLAG_HIDE_ROUTE_110_RIVAL":919,"FLAG_HIDE_ROUTE_110_RIVAL_ON_BIKE":922,"FLAG_HIDE_ROUTE_110_TEAM_AQUA":900,"FLAG_HIDE_ROUTE_111_DESERT_FOSSIL":876,"FLAG_HIDE_ROUTE_111_GABBY_AND_TY_1":796,"FLAG_HIDE_ROUTE_111_GABBY_AND_TY_2":903,"FLAG_HIDE_ROUTE_111_GABBY_AND_TY_3":799,"FLAG_HIDE_ROUTE_111_PLAYER_DESCENT":875,"FLAG_HIDE_ROUTE_111_ROCK_SMASH_TIP_GUY":843,"FLAG_HIDE_ROUTE_111_SECRET_POWER_MAN":960,"FLAG_HIDE_ROUTE_111_VICKY_WINSTRATE":771,"FLAG_HIDE_ROUTE_111_VICTORIA_WINSTRATE":769,"FLAG_HIDE_ROUTE_111_VICTOR_WINSTRATE":768,"FLAG_HIDE_ROUTE_111_VIVI_WINSTRATE":770,"FLAG_HIDE_ROUTE_112_TEAM_MAGMA":819,"FLAG_HIDE_ROUTE_115_BOULDERS":482,"FLAG_HIDE_ROUTE_116_DEVON_EMPLOYEE":947,"FLAG_HIDE_ROUTE_116_DROPPED_GLASSES_MAN":813,"FLAG_HIDE_ROUTE_116_MR_BRINEY":891,"FLAG_HIDE_ROUTE_116_WANDAS_BOYFRIEND":894,"FLAG_HIDE_ROUTE_118_GABBY_AND_TY_1":797,"FLAG_HIDE_ROUTE_118_GABBY_AND_TY_2":901,"FLAG_HIDE_ROUTE_118_GABBY_AND_TY_3":904,"FLAG_HIDE_ROUTE_118_STEVEN":966,"FLAG_HIDE_ROUTE_119_KECLEON_1":989,"FLAG_HIDE_ROUTE_119_KECLEON_2":990,"FLAG_HIDE_ROUTE_119_RIVAL":851,"FLAG_HIDE_ROUTE_119_RIVAL_ON_BIKE":923,"FLAG_HIDE_ROUTE_119_SCOTT":786,"FLAG_HIDE_ROUTE_119_TEAM_AQUA":890,"FLAG_HIDE_ROUTE_119_TEAM_AQUA_BRIDGE":822,"FLAG_HIDE_ROUTE_120_GABBY_AND_TY_1":798,"FLAG_HIDE_ROUTE_120_GABBY_AND_TY_2":902,"FLAG_HIDE_ROUTE_120_KECLEON_1":982,"FLAG_HIDE_ROUTE_120_KECLEON_2":985,"FLAG_HIDE_ROUTE_120_KECLEON_3":986,"FLAG_HIDE_ROUTE_120_KECLEON_4":987,"FLAG_HIDE_ROUTE_120_KECLEON_5":988,"FLAG_HIDE_ROUTE_120_KECLEON_BRIDGE":970,"FLAG_HIDE_ROUTE_120_KECLEON_BRIDGE_SHADOW":981,"FLAG_HIDE_ROUTE_120_STEVEN":972,"FLAG_HIDE_ROUTE_121_TEAM_AQUA_GRUNTS":914,"FLAG_HIDE_ROUTE_128_ARCHIE":944,"FLAG_HIDE_ROUTE_128_MAXIE":945,"FLAG_HIDE_ROUTE_128_STEVEN":834,"FLAG_HIDE_RUSTBORO_CITY_AQUA_GRUNT":731,"FLAG_HIDE_RUSTBORO_CITY_DEVON_CORP_3F_EMPLOYEE":949,"FLAG_HIDE_RUSTBORO_CITY_DEVON_EMPLOYEE_1":732,"FLAG_HIDE_RUSTBORO_CITY_POKEMON_SCHOOL_SCOTT":999,"FLAG_HIDE_RUSTBORO_CITY_RIVAL":814,"FLAG_HIDE_RUSTBORO_CITY_SCIENTIST":844,"FLAG_HIDE_RUSTURF_TUNNEL_AQUA_GRUNT":878,"FLAG_HIDE_RUSTURF_TUNNEL_BRINEY":879,"FLAG_HIDE_RUSTURF_TUNNEL_PEEKO":880,"FLAG_HIDE_RUSTURF_TUNNEL_ROCK_1":931,"FLAG_HIDE_RUSTURF_TUNNEL_ROCK_2":932,"FLAG_HIDE_RUSTURF_TUNNEL_WANDA":983,"FLAG_HIDE_RUSTURF_TUNNEL_WANDAS_BOYFRIEND":807,"FLAG_HIDE_SAFARI_ZONE_SOUTH_CONSTRUCTION_WORKERS":717,"FLAG_HIDE_SAFARI_ZONE_SOUTH_EAST_EXPANSION":747,"FLAG_HIDE_SEAFLOOR_CAVERN_AQUA_GRUNTS":946,"FLAG_HIDE_SEAFLOOR_CAVERN_ENTRANCE_AQUA_GRUNT":941,"FLAG_HIDE_SEAFLOOR_CAVERN_ROOM_9_ARCHIE":828,"FLAG_HIDE_SEAFLOOR_CAVERN_ROOM_9_KYOGRE":859,"FLAG_HIDE_SEAFLOOR_CAVERN_ROOM_9_KYOGRE_ASLEEP":733,"FLAG_HIDE_SEAFLOOR_CAVERN_ROOM_9_MAGMA_GRUNTS":831,"FLAG_HIDE_SEAFLOOR_CAVERN_ROOM_9_MAXIE":829,"FLAG_HIDE_SECRET_BASE_TRAINER":173,"FLAG_HIDE_SKY_PILLAR_TOP_RAYQUAZA":773,"FLAG_HIDE_SKY_PILLAR_TOP_RAYQUAZA_STILL":80,"FLAG_HIDE_SKY_PILLAR_WALLACE":855,"FLAG_HIDE_SLATEPORT_CITY_CAPTAIN_STERN":840,"FLAG_HIDE_SLATEPORT_CITY_CONTEST_REPORTER":803,"FLAG_HIDE_SLATEPORT_CITY_GABBY_AND_TY":835,"FLAG_HIDE_SLATEPORT_CITY_HARBOR_AQUA_GRUNT":845,"FLAG_HIDE_SLATEPORT_CITY_HARBOR_ARCHIE":846,"FLAG_HIDE_SLATEPORT_CITY_HARBOR_CAPTAIN_STERN":841,"FLAG_HIDE_SLATEPORT_CITY_HARBOR_PATRONS":905,"FLAG_HIDE_SLATEPORT_CITY_HARBOR_SS_TIDAL":860,"FLAG_HIDE_SLATEPORT_CITY_HARBOR_SUBMARINE_SHADOW":848,"FLAG_HIDE_SLATEPORT_CITY_OCEANIC_MUSEUM_2F_AQUA_GRUNT_1":884,"FLAG_HIDE_SLATEPORT_CITY_OCEANIC_MUSEUM_2F_AQUA_GRUNT_2":885,"FLAG_HIDE_SLATEPORT_CITY_OCEANIC_MUSEUM_2F_ARCHIE":886,"FLAG_HIDE_SLATEPORT_CITY_OCEANIC_MUSEUM_2F_CAPTAIN_STERN":887,"FLAG_HIDE_SLATEPORT_CITY_OCEANIC_MUSEUM_AQUA_GRUNTS":883,"FLAG_HIDE_SLATEPORT_CITY_OCEANIC_MUSEUM_FAMILIAR_AQUA_GRUNT":965,"FLAG_HIDE_SLATEPORT_CITY_SCOTT":749,"FLAG_HIDE_SLATEPORT_CITY_STERNS_SHIPYARD_MR_BRINEY":869,"FLAG_HIDE_SLATEPORT_CITY_TEAM_AQUA":882,"FLAG_HIDE_SLATEPORT_CITY_TM_SALESMAN":948,"FLAG_HIDE_SLATEPORT_MUSEUM_POPULATION":961,"FLAG_HIDE_SOOTOPOLIS_CITY_ARCHIE":826,"FLAG_HIDE_SOOTOPOLIS_CITY_GROUDON":998,"FLAG_HIDE_SOOTOPOLIS_CITY_KYOGRE":997,"FLAG_HIDE_SOOTOPOLIS_CITY_MAN_1":839,"FLAG_HIDE_SOOTOPOLIS_CITY_MAXIE":827,"FLAG_HIDE_SOOTOPOLIS_CITY_RAYQUAZA":996,"FLAG_HIDE_SOOTOPOLIS_CITY_RESIDENTS":854,"FLAG_HIDE_SOOTOPOLIS_CITY_STEVEN":973,"FLAG_HIDE_SOOTOPOLIS_CITY_WALLACE":816,"FLAG_HIDE_SOUTHERN_ISLAND_EON_STONE":910,"FLAG_HIDE_SOUTHERN_ISLAND_UNCHOSEN_EON_DUO_MON":911,"FLAG_HIDE_SS_TIDAL_CORRIDOR_MR_BRINEY":950,"FLAG_HIDE_SS_TIDAL_CORRIDOR_SCOTT":810,"FLAG_HIDE_SS_TIDAL_ROOMS_SNATCH_GIVER":951,"FLAG_HIDE_TERRA_CAVE_GROUDON":783,"FLAG_HIDE_TRICK_HOUSE_END_MAN":899,"FLAG_HIDE_TRICK_HOUSE_ENTRANCE_MAN":872,"FLAG_HIDE_UNDERWATER_SEA_FLOOR_CAVERN_STOLEN_SUBMARINE":980,"FLAG_HIDE_UNION_ROOM_PLAYER_1":703,"FLAG_HIDE_UNION_ROOM_PLAYER_2":704,"FLAG_HIDE_UNION_ROOM_PLAYER_3":705,"FLAG_HIDE_UNION_ROOM_PLAYER_4":706,"FLAG_HIDE_UNION_ROOM_PLAYER_5":707,"FLAG_HIDE_UNION_ROOM_PLAYER_6":708,"FLAG_HIDE_UNION_ROOM_PLAYER_7":709,"FLAG_HIDE_UNION_ROOM_PLAYER_8":710,"FLAG_HIDE_VERDANTURF_TOWN_SCOTT":766,"FLAG_HIDE_VERDANTURF_TOWN_WANDAS_HOUSE_WALLY":806,"FLAG_HIDE_VERDANTURF_TOWN_WANDAS_HOUSE_WALLYS_UNCLE":809,"FLAG_HIDE_VERDANTURF_TOWN_WANDAS_HOUSE_WANDA":984,"FLAG_HIDE_VERDANTURF_TOWN_WANDAS_HOUSE_WANDAS_BOYFRIEND":808,"FLAG_HIDE_VICTORY_ROAD_ENTRANCE_WALLY":858,"FLAG_HIDE_VICTORY_ROAD_EXIT_WALLY":751,"FLAG_HIDE_WEATHER_INSTITUTE_1F_WORKERS":892,"FLAG_HIDE_WEATHER_INSTITUTE_2F_AQUA_GRUNT_M":992,"FLAG_HIDE_WEATHER_INSTITUTE_2F_WORKERS":893,"FLAG_INTERACTED_WITH_DEVON_EMPLOYEE_GOODS_STOLEN":159,"FLAG_INTERACTED_WITH_STEVEN_SPACE_CENTER":205,"FLAG_IS_CHAMPION":2175,"FLAG_ITEM_ABANDONED_SHIP_CAPTAINS_OFFICE_STORAGE_KEY":1100,"FLAG_ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_1_TM18":1102,"FLAG_ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_3_WATER_STONE":1101,"FLAG_ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_4_SCANNER":1078,"FLAG_ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_6_LUXURY_BALL":1077,"FLAG_ITEM_ABANDONED_SHIP_ROOMS_1F_HARBOR_MAIL":1095,"FLAG_ITEM_ABANDONED_SHIP_ROOMS_2_1F_REVIVE":1099,"FLAG_ITEM_ABANDONED_SHIP_ROOMS_2_B1F_DIVE_BALL":1097,"FLAG_ITEM_ABANDONED_SHIP_ROOMS_B1F_ESCAPE_ROPE":1096,"FLAG_ITEM_ABANDONED_SHIP_ROOMS_B1F_TM13":1098,"FLAG_ITEM_AQUA_HIDEOUT_B1F_MASTER_BALL":1124,"FLAG_ITEM_AQUA_HIDEOUT_B1F_MAX_ELIXIR":1071,"FLAG_ITEM_AQUA_HIDEOUT_B1F_NUGGET":1132,"FLAG_ITEM_AQUA_HIDEOUT_B2F_NEST_BALL":1072,"FLAG_ITEM_ARTISAN_CAVE_1F_CARBOS":1163,"FLAG_ITEM_ARTISAN_CAVE_B1F_HP_UP":1162,"FLAG_ITEM_FIERY_PATH_FIRE_STONE":1111,"FLAG_ITEM_FIERY_PATH_TM06":1091,"FLAG_ITEM_GRANITE_CAVE_1F_ESCAPE_ROPE":1050,"FLAG_ITEM_GRANITE_CAVE_B1F_POKE_BALL":1051,"FLAG_ITEM_GRANITE_CAVE_B2F_RARE_CANDY":1054,"FLAG_ITEM_GRANITE_CAVE_B2F_REPEL":1053,"FLAG_ITEM_JAGGED_PASS_BURN_HEAL":1070,"FLAG_ITEM_LILYCOVE_CITY_MAX_REPEL":1042,"FLAG_ITEM_MAGMA_HIDEOUT_1F_RARE_CANDY":1151,"FLAG_ITEM_MAGMA_HIDEOUT_2F_2R_FULL_RESTORE":1165,"FLAG_ITEM_MAGMA_HIDEOUT_2F_2R_MAX_ELIXIR":1164,"FLAG_ITEM_MAGMA_HIDEOUT_3F_1R_NUGGET":1166,"FLAG_ITEM_MAGMA_HIDEOUT_3F_2R_PP_MAX":1167,"FLAG_ITEM_MAGMA_HIDEOUT_3F_3R_ECAPE_ROPE":1059,"FLAG_ITEM_MAGMA_HIDEOUT_4F_MAX_REVIVE":1168,"FLAG_ITEM_MAUVILLE_CITY_X_SPEED":1116,"FLAG_ITEM_METEOR_FALLS_1F_1R_FULL_HEAL":1045,"FLAG_ITEM_METEOR_FALLS_1F_1R_MOON_STONE":1046,"FLAG_ITEM_METEOR_FALLS_1F_1R_PP_UP":1047,"FLAG_ITEM_METEOR_FALLS_1F_1R_TM23":1044,"FLAG_ITEM_METEOR_FALLS_B1F_2R_TM02":1080,"FLAG_ITEM_MOSSDEEP_CITY_NET_BALL":1043,"FLAG_ITEM_MOSSDEEP_STEVENS_HOUSE_HM08":1133,"FLAG_ITEM_MT_PYRE_2F_ULTRA_BALL":1129,"FLAG_ITEM_MT_PYRE_3F_SUPER_REPEL":1120,"FLAG_ITEM_MT_PYRE_4F_SEA_INCENSE":1130,"FLAG_ITEM_MT_PYRE_5F_LAX_INCENSE":1052,"FLAG_ITEM_MT_PYRE_6F_TM30":1089,"FLAG_ITEM_MT_PYRE_EXTERIOR_MAX_POTION":1073,"FLAG_ITEM_MT_PYRE_EXTERIOR_TM48":1074,"FLAG_ITEM_NEW_MAUVILLE_ESCAPE_ROPE":1076,"FLAG_ITEM_NEW_MAUVILLE_FULL_HEAL":1122,"FLAG_ITEM_NEW_MAUVILLE_PARALYZE_HEAL":1123,"FLAG_ITEM_NEW_MAUVILLE_THUNDER_STONE":1110,"FLAG_ITEM_NEW_MAUVILLE_ULTRA_BALL":1075,"FLAG_ITEM_OLD_MAGMA_HIDEOUT_B1F_MASTER_BALL":1125,"FLAG_ITEM_OLD_MAGMA_HIDEOUT_B1F_MAX_ELIXIR":1126,"FLAG_ITEM_OLD_MAGMA_HIDEOUT_B2F_NEST_BALL":1127,"FLAG_ITEM_PETALBURG_CITY_ETHER":1040,"FLAG_ITEM_PETALBURG_CITY_MAX_REVIVE":1039,"FLAG_ITEM_PETALBURG_WOODS_ETHER":1058,"FLAG_ITEM_PETALBURG_WOODS_GREAT_BALL":1056,"FLAG_ITEM_PETALBURG_WOODS_PARALYZE_HEAL":1117,"FLAG_ITEM_PETALBURG_WOODS_X_ATTACK":1055,"FLAG_ITEM_ROUTE_102_POTION":1000,"FLAG_ITEM_ROUTE_103_GUARD_SPEC":1114,"FLAG_ITEM_ROUTE_103_PP_UP":1137,"FLAG_ITEM_ROUTE_104_POKE_BALL":1057,"FLAG_ITEM_ROUTE_104_POTION":1135,"FLAG_ITEM_ROUTE_104_PP_UP":1002,"FLAG_ITEM_ROUTE_104_X_ACCURACY":1115,"FLAG_ITEM_ROUTE_105_IRON":1003,"FLAG_ITEM_ROUTE_106_PROTEIN":1004,"FLAG_ITEM_ROUTE_108_STAR_PIECE":1139,"FLAG_ITEM_ROUTE_109_POTION":1140,"FLAG_ITEM_ROUTE_109_PP_UP":1005,"FLAG_ITEM_ROUTE_110_DIRE_HIT":1007,"FLAG_ITEM_ROUTE_110_ELIXIR":1141,"FLAG_ITEM_ROUTE_110_RARE_CANDY":1006,"FLAG_ITEM_ROUTE_111_ELIXIR":1142,"FLAG_ITEM_ROUTE_111_HP_UP":1010,"FLAG_ITEM_ROUTE_111_STARDUST":1009,"FLAG_ITEM_ROUTE_111_TM37":1008,"FLAG_ITEM_ROUTE_112_NUGGET":1011,"FLAG_ITEM_ROUTE_113_HYPER_POTION":1143,"FLAG_ITEM_ROUTE_113_MAX_ETHER":1012,"FLAG_ITEM_ROUTE_113_SUPER_REPEL":1013,"FLAG_ITEM_ROUTE_114_ENERGY_POWDER":1160,"FLAG_ITEM_ROUTE_114_PROTEIN":1015,"FLAG_ITEM_ROUTE_114_RARE_CANDY":1014,"FLAG_ITEM_ROUTE_115_GREAT_BALL":1118,"FLAG_ITEM_ROUTE_115_HEAL_POWDER":1144,"FLAG_ITEM_ROUTE_115_IRON":1018,"FLAG_ITEM_ROUTE_115_PP_UP":1161,"FLAG_ITEM_ROUTE_115_SUPER_POTION":1016,"FLAG_ITEM_ROUTE_115_TM01":1017,"FLAG_ITEM_ROUTE_116_ETHER":1019,"FLAG_ITEM_ROUTE_116_HP_UP":1021,"FLAG_ITEM_ROUTE_116_POTION":1146,"FLAG_ITEM_ROUTE_116_REPEL":1020,"FLAG_ITEM_ROUTE_116_X_SPECIAL":1001,"FLAG_ITEM_ROUTE_117_GREAT_BALL":1022,"FLAG_ITEM_ROUTE_117_REVIVE":1023,"FLAG_ITEM_ROUTE_118_HYPER_POTION":1121,"FLAG_ITEM_ROUTE_119_ELIXIR_1":1026,"FLAG_ITEM_ROUTE_119_ELIXIR_2":1147,"FLAG_ITEM_ROUTE_119_HYPER_POTION_1":1029,"FLAG_ITEM_ROUTE_119_HYPER_POTION_2":1106,"FLAG_ITEM_ROUTE_119_LEAF_STONE":1027,"FLAG_ITEM_ROUTE_119_NUGGET":1134,"FLAG_ITEM_ROUTE_119_RARE_CANDY":1028,"FLAG_ITEM_ROUTE_119_SUPER_REPEL":1024,"FLAG_ITEM_ROUTE_119_ZINC":1025,"FLAG_ITEM_ROUTE_120_FULL_HEAL":1031,"FLAG_ITEM_ROUTE_120_HYPER_POTION":1107,"FLAG_ITEM_ROUTE_120_NEST_BALL":1108,"FLAG_ITEM_ROUTE_120_NUGGET":1030,"FLAG_ITEM_ROUTE_120_REVIVE":1148,"FLAG_ITEM_ROUTE_121_CARBOS":1103,"FLAG_ITEM_ROUTE_121_REVIVE":1149,"FLAG_ITEM_ROUTE_121_ZINC":1150,"FLAG_ITEM_ROUTE_123_CALCIUM":1032,"FLAG_ITEM_ROUTE_123_ELIXIR":1109,"FLAG_ITEM_ROUTE_123_PP_UP":1152,"FLAG_ITEM_ROUTE_123_RARE_CANDY":1033,"FLAG_ITEM_ROUTE_123_REVIVAL_HERB":1153,"FLAG_ITEM_ROUTE_123_ULTRA_BALL":1104,"FLAG_ITEM_ROUTE_124_BLUE_SHARD":1093,"FLAG_ITEM_ROUTE_124_RED_SHARD":1092,"FLAG_ITEM_ROUTE_124_YELLOW_SHARD":1066,"FLAG_ITEM_ROUTE_125_BIG_PEARL":1154,"FLAG_ITEM_ROUTE_126_GREEN_SHARD":1105,"FLAG_ITEM_ROUTE_127_CARBOS":1035,"FLAG_ITEM_ROUTE_127_RARE_CANDY":1155,"FLAG_ITEM_ROUTE_127_ZINC":1034,"FLAG_ITEM_ROUTE_132_PROTEIN":1156,"FLAG_ITEM_ROUTE_132_RARE_CANDY":1036,"FLAG_ITEM_ROUTE_133_BIG_PEARL":1037,"FLAG_ITEM_ROUTE_133_MAX_REVIVE":1157,"FLAG_ITEM_ROUTE_133_STAR_PIECE":1038,"FLAG_ITEM_ROUTE_134_CARBOS":1158,"FLAG_ITEM_ROUTE_134_STAR_PIECE":1159,"FLAG_ITEM_RUSTBORO_CITY_X_DEFEND":1041,"FLAG_ITEM_RUSTURF_TUNNEL_MAX_ETHER":1049,"FLAG_ITEM_RUSTURF_TUNNEL_POKE_BALL":1048,"FLAG_ITEM_SAFARI_ZONE_NORTH_CALCIUM":1119,"FLAG_ITEM_SAFARI_ZONE_NORTH_EAST_NUGGET":1169,"FLAG_ITEM_SAFARI_ZONE_NORTH_WEST_TM22":1094,"FLAG_ITEM_SAFARI_ZONE_SOUTH_EAST_BIG_PEARL":1170,"FLAG_ITEM_SAFARI_ZONE_SOUTH_WEST_MAX_REVIVE":1131,"FLAG_ITEM_SCORCHED_SLAB_TM11":1079,"FLAG_ITEM_SEAFLOOR_CAVERN_ROOM_9_TM26":1090,"FLAG_ITEM_SHOAL_CAVE_ENTRANCE_BIG_PEARL":1081,"FLAG_ITEM_SHOAL_CAVE_ICE_ROOM_NEVER_MELT_ICE":1113,"FLAG_ITEM_SHOAL_CAVE_ICE_ROOM_TM07":1112,"FLAG_ITEM_SHOAL_CAVE_INNER_ROOM_RARE_CANDY":1082,"FLAG_ITEM_SHOAL_CAVE_STAIRS_ROOM_ICE_HEAL":1083,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_1_ORANGE_MAIL":1060,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_2_HARBOR_MAIL":1061,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_2_WAVE_MAIL":1062,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_3_SHADOW_MAIL":1063,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_3_WOOD_MAIL":1064,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_4_MECH_MAIL":1065,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_6_GLITTER_MAIL":1067,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_7_TROPIC_MAIL":1068,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_8_BEAD_MAIL":1069,"FLAG_ITEM_VICTORY_ROAD_1F_MAX_ELIXIR":1084,"FLAG_ITEM_VICTORY_ROAD_1F_PP_UP":1085,"FLAG_ITEM_VICTORY_ROAD_B1F_FULL_RESTORE":1087,"FLAG_ITEM_VICTORY_ROAD_B1F_TM29":1086,"FLAG_ITEM_VICTORY_ROAD_B2F_FULL_HEAL":1088,"FLAG_KECLEON_FLED_FORTREE":295,"FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN":129,"FLAG_LANDMARK_ABANDONED_SHIP":2206,"FLAG_LANDMARK_ALTERING_CAVE":2269,"FLAG_LANDMARK_ANCIENT_TOMB":2233,"FLAG_LANDMARK_ARTISAN_CAVE":2271,"FLAG_LANDMARK_BATTLE_FRONTIER":2216,"FLAG_LANDMARK_BERRY_MASTERS_HOUSE":2243,"FLAG_LANDMARK_DESERT_RUINS":2230,"FLAG_LANDMARK_DESERT_UNDERPASS":2270,"FLAG_LANDMARK_FIERY_PATH":2218,"FLAG_LANDMARK_FLOWER_SHOP":2204,"FLAG_LANDMARK_FOSSIL_MANIACS_HOUSE":2231,"FLAG_LANDMARK_GLASS_WORKSHOP":2212,"FLAG_LANDMARK_HUNTERS_HOUSE":2235,"FLAG_LANDMARK_ISLAND_CAVE":2229,"FLAG_LANDMARK_LANETTES_HOUSE":2213,"FLAG_LANDMARK_MIRAGE_TOWER":120,"FLAG_LANDMARK_MR_BRINEY_HOUSE":2205,"FLAG_LANDMARK_NEW_MAUVILLE":2208,"FLAG_LANDMARK_OLD_LADY_REST_SHOP":2209,"FLAG_LANDMARK_POKEMON_DAYCARE":2214,"FLAG_LANDMARK_POKEMON_LEAGUE":2228,"FLAG_LANDMARK_SCORCHED_SLAB":2232,"FLAG_LANDMARK_SEAFLOOR_CAVERN":2215,"FLAG_LANDMARK_SEALED_CHAMBER":2236,"FLAG_LANDMARK_SEASHORE_HOUSE":2207,"FLAG_LANDMARK_SKY_PILLAR":2238,"FLAG_LANDMARK_SOUTHERN_ISLAND":2217,"FLAG_LANDMARK_TRAINER_HILL":2274,"FLAG_LANDMARK_TRICK_HOUSE":2210,"FLAG_LANDMARK_TUNNELERS_REST_HOUSE":2234,"FLAG_LANDMARK_WINSTRATE_FAMILY":2211,"FLAG_LATIOS_OR_LATIAS_ROAMING":255,"FLAG_LEGENDARIES_IN_SOOTOPOLIS":83,"FLAG_MAP_SCRIPT_CHECKED_DEOXYS":2259,"FLAG_MATCH_CALL_REGISTERED":348,"FLAG_MAUVILLE_GYM_BARRIERS_STATE":99,"FLAG_MET_ARCHIE_METEOR_FALLS":207,"FLAG_MET_ARCHIE_SOOTOPOLIS":308,"FLAG_MET_BATTLE_FRONTIER_BREEDER":339,"FLAG_MET_BATTLE_FRONTIER_GAMBLER":343,"FLAG_MET_BATTLE_FRONTIER_MANIAC":340,"FLAG_MET_DEVON_EMPLOYEE":287,"FLAG_MET_DIVING_TREASURE_HUNTER":217,"FLAG_MET_FANCLUB_YOUNGER_BROTHER":300,"FLAG_MET_FRONTIER_BEAUTY_MOVE_TUTOR":346,"FLAG_MET_FRONTIER_SWIMMER_MOVE_TUTOR":347,"FLAG_MET_HIDDEN_POWER_GIVER":118,"FLAG_MET_MAXIE_SOOTOPOLIS":309,"FLAG_MET_PRETTY_PETAL_SHOP_OWNER":127,"FLAG_MET_PROF_COZMO":244,"FLAG_MET_RIVAL_IN_HOUSE_AFTER_LILYCOVE":293,"FLAG_MET_RIVAL_LILYCOVE":292,"FLAG_MET_RIVAL_MOM":87,"FLAG_MET_RIVAL_RUSTBORO":288,"FLAG_MET_SCOTT_AFTER_OBTAINING_STONE_BADGE":459,"FLAG_MET_SCOTT_IN_EVERGRANDE":463,"FLAG_MET_SCOTT_IN_FALLARBOR":461,"FLAG_MET_SCOTT_IN_LILYCOVE":462,"FLAG_MET_SCOTT_IN_VERDANTURF":460,"FLAG_MET_SCOTT_ON_SS_TIDAL":464,"FLAG_MET_SCOTT_RUSTBORO":310,"FLAG_MET_SLATEPORT_FANCLUB_CHAIRMAN":342,"FLAG_MET_TEAM_AQUA_HARBOR":97,"FLAG_MET_WAILMER_TRAINER":218,"FLAG_MIRAGE_TOWER_VISIBLE":334,"FLAG_MOSSDEEP_GYM_SWITCH_1":100,"FLAG_MOSSDEEP_GYM_SWITCH_2":101,"FLAG_MOSSDEEP_GYM_SWITCH_3":102,"FLAG_MOSSDEEP_GYM_SWITCH_4":103,"FLAG_MOVE_TUTOR_TAUGHT_DOUBLE_EDGE":441,"FLAG_MOVE_TUTOR_TAUGHT_DYNAMICPUNCH":440,"FLAG_MOVE_TUTOR_TAUGHT_EXPLOSION":442,"FLAG_MOVE_TUTOR_TAUGHT_FURY_CUTTER":435,"FLAG_MOVE_TUTOR_TAUGHT_METRONOME":437,"FLAG_MOVE_TUTOR_TAUGHT_MIMIC":436,"FLAG_MOVE_TUTOR_TAUGHT_ROLLOUT":434,"FLAG_MOVE_TUTOR_TAUGHT_SLEEP_TALK":438,"FLAG_MOVE_TUTOR_TAUGHT_SUBSTITUTE":439,"FLAG_MOVE_TUTOR_TAUGHT_SWAGGER":433,"FLAG_MR_BRINEY_SAILING_INTRO":147,"FLAG_MYSTERY_GIFT_1":485,"FLAG_MYSTERY_GIFT_10":494,"FLAG_MYSTERY_GIFT_11":495,"FLAG_MYSTERY_GIFT_12":496,"FLAG_MYSTERY_GIFT_13":497,"FLAG_MYSTERY_GIFT_14":498,"FLAG_MYSTERY_GIFT_15":499,"FLAG_MYSTERY_GIFT_2":486,"FLAG_MYSTERY_GIFT_3":487,"FLAG_MYSTERY_GIFT_4":488,"FLAG_MYSTERY_GIFT_5":489,"FLAG_MYSTERY_GIFT_6":490,"FLAG_MYSTERY_GIFT_7":491,"FLAG_MYSTERY_GIFT_8":492,"FLAG_MYSTERY_GIFT_9":493,"FLAG_MYSTERY_GIFT_DONE":484,"FLAG_NEVER_SET_0x0DC":220,"FLAG_NOT_READY_FOR_BATTLE_ROUTE_120":290,"FLAG_NURSE_MENTIONS_GOLD_CARD":345,"FLAG_NURSE_UNION_ROOM_REMINDER":2176,"FLAG_OCEANIC_MUSEUM_MET_REPORTER":105,"FLAG_OMIT_DIVE_FROM_STEVEN_LETTER":302,"FLAG_PACIFIDLOG_NPC_TRADE_COMPLETED":154,"FLAG_PENDING_DAYCARE_EGG":134,"FLAG_PETALBURG_MART_EXPANDED_ITEMS":296,"FLAG_POKERUS_EXPLAINED":273,"FLAG_PURCHASED_HARBOR_MAIL":104,"FLAG_RECEIVED_20_COINS":225,"FLAG_RECEIVED_6_SODA_POP":140,"FLAG_RECEIVED_ACRO_BIKE":1181,"FLAG_RECEIVED_AMULET_COIN":133,"FLAG_RECEIVED_AURORA_TICKET":314,"FLAG_RECEIVED_BADGE_1":1182,"FLAG_RECEIVED_BADGE_2":1183,"FLAG_RECEIVED_BADGE_3":1184,"FLAG_RECEIVED_BADGE_4":1185,"FLAG_RECEIVED_BADGE_5":1186,"FLAG_RECEIVED_BADGE_6":1187,"FLAG_RECEIVED_BADGE_7":1188,"FLAG_RECEIVED_BADGE_8":1189,"FLAG_RECEIVED_BELDUM":298,"FLAG_RECEIVED_BELUE_BERRY":252,"FLAG_RECEIVED_BIKE":90,"FLAG_RECEIVED_BLUE_SCARF":201,"FLAG_RECEIVED_CASTFORM":151,"FLAG_RECEIVED_CHARCOAL":254,"FLAG_RECEIVED_CHESTO_BERRY_ROUTE_104":246,"FLAG_RECEIVED_CLEANSE_TAG":282,"FLAG_RECEIVED_COIN_CASE":258,"FLAG_RECEIVED_CONTEST_PASS":150,"FLAG_RECEIVED_DEEP_SEA_SCALE":1190,"FLAG_RECEIVED_DEEP_SEA_TOOTH":1191,"FLAG_RECEIVED_DEVON_GOODS_RUSTURF_TUNNEL":1172,"FLAG_RECEIVED_DEVON_SCOPE":285,"FLAG_RECEIVED_DOLL_LANETTE":131,"FLAG_RECEIVED_DURIN_BERRY":251,"FLAG_RECEIVED_EXP_SHARE":272,"FLAG_RECEIVED_FANCLUB_TM_THIS_WEEK":299,"FLAG_RECEIVED_FOCUS_BAND":283,"FLAG_RECEIVED_GLASS_ORNAMENT":236,"FLAG_RECEIVED_GOLD_SHIELD":238,"FLAG_RECEIVED_GOOD_ROD":227,"FLAG_RECEIVED_GO_GOGGLES":221,"FLAG_RECEIVED_GREAT_BALL_PETALBURG_WOODS":1171,"FLAG_RECEIVED_GREAT_BALL_RUSTBORO_CITY":1173,"FLAG_RECEIVED_GREEN_SCARF":203,"FLAG_RECEIVED_HM01":137,"FLAG_RECEIVED_HM02":110,"FLAG_RECEIVED_HM03":122,"FLAG_RECEIVED_HM04":106,"FLAG_RECEIVED_HM05":109,"FLAG_RECEIVED_HM06":107,"FLAG_RECEIVED_HM07":312,"FLAG_RECEIVED_HM08":123,"FLAG_RECEIVED_ITEMFINDER":1176,"FLAG_RECEIVED_KINGS_ROCK":276,"FLAG_RECEIVED_LAVARIDGE_EGG":266,"FLAG_RECEIVED_LETTER":1174,"FLAG_RECEIVED_MACHO_BRACE":277,"FLAG_RECEIVED_MACH_BIKE":1180,"FLAG_RECEIVED_MAGMA_EMBLEM":1177,"FLAG_RECEIVED_MENTAL_HERB":223,"FLAG_RECEIVED_METEORITE":115,"FLAG_RECEIVED_MIRACLE_SEED":297,"FLAG_RECEIVED_MYSTIC_TICKET":315,"FLAG_RECEIVED_OLD_ROD":257,"FLAG_RECEIVED_OLD_SEA_MAP":316,"FLAG_RECEIVED_PAMTRE_BERRY":249,"FLAG_RECEIVED_PINK_SCARF":202,"FLAG_RECEIVED_POKEBLOCK_CASE":95,"FLAG_RECEIVED_POKEDEX_FROM_BIRCH":2276,"FLAG_RECEIVED_POKENAV":188,"FLAG_RECEIVED_POTION_OLDALE":132,"FLAG_RECEIVED_POWDER_JAR":337,"FLAG_RECEIVED_PREMIER_BALL_RUSTBORO":213,"FLAG_RECEIVED_QUICK_CLAW":275,"FLAG_RECEIVED_RED_OR_BLUE_ORB":212,"FLAG_RECEIVED_RED_SCARF":200,"FLAG_RECEIVED_REPEAT_BALL":256,"FLAG_RECEIVED_REVIVED_FOSSIL_MON":267,"FLAG_RECEIVED_RUNNING_SHOES":274,"FLAG_RECEIVED_SECRET_POWER":96,"FLAG_RECEIVED_SHOAL_SALT_1":952,"FLAG_RECEIVED_SHOAL_SALT_2":953,"FLAG_RECEIVED_SHOAL_SALT_3":954,"FLAG_RECEIVED_SHOAL_SALT_4":955,"FLAG_RECEIVED_SHOAL_SHELL_1":956,"FLAG_RECEIVED_SHOAL_SHELL_2":957,"FLAG_RECEIVED_SHOAL_SHELL_3":958,"FLAG_RECEIVED_SHOAL_SHELL_4":959,"FLAG_RECEIVED_SILK_SCARF":289,"FLAG_RECEIVED_SILVER_SHIELD":237,"FLAG_RECEIVED_SOFT_SAND":280,"FLAG_RECEIVED_SOOTHE_BELL":278,"FLAG_RECEIVED_SPELON_BERRY":248,"FLAG_RECEIVED_SS_TICKET":291,"FLAG_RECEIVED_STARTER_DOLL":226,"FLAG_RECEIVED_SUN_STONE_MOSSDEEP":192,"FLAG_RECEIVED_SUPER_ROD":152,"FLAG_RECEIVED_TM03":172,"FLAG_RECEIVED_TM04":171,"FLAG_RECEIVED_TM05":231,"FLAG_RECEIVED_TM08":166,"FLAG_RECEIVED_TM09":262,"FLAG_RECEIVED_TM10":264,"FLAG_RECEIVED_TM19":232,"FLAG_RECEIVED_TM21":1179,"FLAG_RECEIVED_TM27":229,"FLAG_RECEIVED_TM27_2":1178,"FLAG_RECEIVED_TM28":261,"FLAG_RECEIVED_TM31":121,"FLAG_RECEIVED_TM34":167,"FLAG_RECEIVED_TM36":230,"FLAG_RECEIVED_TM39":165,"FLAG_RECEIVED_TM40":170,"FLAG_RECEIVED_TM41":265,"FLAG_RECEIVED_TM42":169,"FLAG_RECEIVED_TM44":234,"FLAG_RECEIVED_TM45":235,"FLAG_RECEIVED_TM46":269,"FLAG_RECEIVED_TM47":1175,"FLAG_RECEIVED_TM49":260,"FLAG_RECEIVED_TM50":168,"FLAG_RECEIVED_WAILMER_DOLL":245,"FLAG_RECEIVED_WAILMER_PAIL":94,"FLAG_RECEIVED_WATMEL_BERRY":250,"FLAG_RECEIVED_WHITE_HERB":279,"FLAG_RECEIVED_YELLOW_SCARF":204,"FLAG_RECOVERED_DEVON_GOODS":143,"FLAG_REGISTERED_STEVEN_POKENAV":305,"FLAG_REGISTER_RIVAL_POKENAV":124,"FLAG_REGI_DOORS_OPENED":228,"FLAG_REMATCH_ABIGAIL":387,"FLAG_REMATCH_AMY_AND_LIV":399,"FLAG_REMATCH_ANDRES":350,"FLAG_REMATCH_ANNA_AND_MEG":378,"FLAG_REMATCH_BENJAMIN":390,"FLAG_REMATCH_BERNIE":369,"FLAG_REMATCH_BRAWLY":415,"FLAG_REMATCH_BROOKE":356,"FLAG_REMATCH_CALVIN":383,"FLAG_REMATCH_CAMERON":373,"FLAG_REMATCH_CATHERINE":406,"FLAG_REMATCH_CINDY":359,"FLAG_REMATCH_CORY":401,"FLAG_REMATCH_CRISTIN":355,"FLAG_REMATCH_CYNDY":395,"FLAG_REMATCH_DALTON":368,"FLAG_REMATCH_DIANA":398,"FLAG_REMATCH_DRAKE":424,"FLAG_REMATCH_DUSTY":351,"FLAG_REMATCH_DYLAN":388,"FLAG_REMATCH_EDWIN":402,"FLAG_REMATCH_ELLIOT":384,"FLAG_REMATCH_ERNEST":400,"FLAG_REMATCH_ETHAN":370,"FLAG_REMATCH_FERNANDO":367,"FLAG_REMATCH_FLANNERY":417,"FLAG_REMATCH_GABRIELLE":405,"FLAG_REMATCH_GLACIA":423,"FLAG_REMATCH_HALEY":408,"FLAG_REMATCH_ISAAC":404,"FLAG_REMATCH_ISABEL":379,"FLAG_REMATCH_ISAIAH":385,"FLAG_REMATCH_JACKI":374,"FLAG_REMATCH_JACKSON":407,"FLAG_REMATCH_JAMES":409,"FLAG_REMATCH_JEFFREY":372,"FLAG_REMATCH_JENNY":397,"FLAG_REMATCH_JERRY":377,"FLAG_REMATCH_JESSICA":361,"FLAG_REMATCH_JOHN_AND_JAY":371,"FLAG_REMATCH_KAREN":376,"FLAG_REMATCH_KATELYN":389,"FLAG_REMATCH_KIRA_AND_DAN":412,"FLAG_REMATCH_KOJI":366,"FLAG_REMATCH_LAO":394,"FLAG_REMATCH_LILA_AND_ROY":354,"FLAG_REMATCH_LOLA":352,"FLAG_REMATCH_LYDIA":403,"FLAG_REMATCH_MADELINE":396,"FLAG_REMATCH_MARIA":386,"FLAG_REMATCH_MIGUEL":380,"FLAG_REMATCH_NICOLAS":392,"FLAG_REMATCH_NOB":365,"FLAG_REMATCH_NORMAN":418,"FLAG_REMATCH_PABLO":391,"FLAG_REMATCH_PHOEBE":422,"FLAG_REMATCH_RICKY":353,"FLAG_REMATCH_ROBERT":393,"FLAG_REMATCH_ROSE":349,"FLAG_REMATCH_ROXANNE":414,"FLAG_REMATCH_SAWYER":411,"FLAG_REMATCH_SHELBY":382,"FLAG_REMATCH_SIDNEY":421,"FLAG_REMATCH_STEVE":363,"FLAG_REMATCH_TATE_AND_LIZA":420,"FLAG_REMATCH_THALIA":360,"FLAG_REMATCH_TIMOTHY":381,"FLAG_REMATCH_TONY":364,"FLAG_REMATCH_TRENT":410,"FLAG_REMATCH_VALERIE":358,"FLAG_REMATCH_WALLACE":425,"FLAG_REMATCH_WALLY":413,"FLAG_REMATCH_WALTER":375,"FLAG_REMATCH_WATTSON":416,"FLAG_REMATCH_WILTON":357,"FLAG_REMATCH_WINONA":419,"FLAG_REMATCH_WINSTON":362,"FLAG_RESCUED_BIRCH":82,"FLAG_RETURNED_DEVON_GOODS":144,"FLAG_RETURNED_RED_OR_BLUE_ORB":259,"FLAG_RIVAL_LEFT_FOR_ROUTE103":301,"FLAG_RUSTBORO_NPC_TRADE_COMPLETED":153,"FLAG_RUSTURF_TUNNEL_OPENED":199,"FLAG_SCOTT_CALL_BATTLE_FRONTIER":114,"FLAG_SCOTT_CALL_FORTREE_GYM":138,"FLAG_SCOTT_GIVES_BATTLE_POINTS":465,"FLAG_SECRET_BASE_REGISTRY_ENABLED":268,"FLAG_SET_WALL_CLOCK":81,"FLAG_SHOWN_AURORA_TICKET":431,"FLAG_SHOWN_BOX_WAS_FULL_MESSAGE":2263,"FLAG_SHOWN_EON_TICKET":430,"FLAG_SHOWN_MYSTIC_TICKET":475,"FLAG_SHOWN_OLD_SEA_MAP":432,"FLAG_SMART_PAINTING_MADE":163,"FLAG_SOOTOPOLIS_ARCHIE_MAXIE_LEAVE":158,"FLAG_SPECIAL_FLAG_UNUSED_0x4003":16387,"FLAG_SS_TIDAL_DISABLED":84,"FLAG_STEVEN_GUIDES_TO_CAVE_OF_ORIGIN":307,"FLAG_STORING_ITEMS_IN_PYRAMID_BAG":16388,"FLAG_SYS_ARENA_GOLD":2251,"FLAG_SYS_ARENA_SILVER":2250,"FLAG_SYS_BRAILLE_DIG":2223,"FLAG_SYS_BRAILLE_REGICE_COMPLETED":2225,"FLAG_SYS_B_DASH":2240,"FLAG_SYS_CAVE_BATTLE":2201,"FLAG_SYS_CAVE_SHIP":2199,"FLAG_SYS_CAVE_WONDER":2200,"FLAG_SYS_CHANGED_DEWFORD_TREND":2195,"FLAG_SYS_CHAT_USED":2149,"FLAG_SYS_CLOCK_SET":2197,"FLAG_SYS_CRUISE_MODE":2189,"FLAG_SYS_CTRL_OBJ_DELETE":2241,"FLAG_SYS_CYCLING_ROAD":2187,"FLAG_SYS_DOME_GOLD":2247,"FLAG_SYS_DOME_SILVER":2246,"FLAG_SYS_ENC_DOWN_ITEM":2222,"FLAG_SYS_ENC_UP_ITEM":2221,"FLAG_SYS_FACTORY_GOLD":2253,"FLAG_SYS_FACTORY_SILVER":2252,"FLAG_SYS_FRONTIER_PASS":2258,"FLAG_SYS_GAME_CLEAR":2148,"FLAG_SYS_HIPSTER_MEET":2150,"FLAG_SYS_MIX_RECORD":2196,"FLAG_SYS_MYSTERY_EVENT_ENABLE":2220,"FLAG_SYS_MYSTERY_GIFT_ENABLE":2267,"FLAG_SYS_NATIONAL_DEX":2198,"FLAG_SYS_PALACE_GOLD":2249,"FLAG_SYS_PALACE_SILVER":2248,"FLAG_SYS_PC_LANETTE":2219,"FLAG_SYS_PIKE_GOLD":2255,"FLAG_SYS_PIKE_SILVER":2254,"FLAG_SYS_POKEDEX_GET":2145,"FLAG_SYS_POKEMON_GET":2144,"FLAG_SYS_POKENAV_GET":2146,"FLAG_SYS_PYRAMID_GOLD":2257,"FLAG_SYS_PYRAMID_SILVER":2256,"FLAG_SYS_REGIROCK_PUZZLE_COMPLETED":2224,"FLAG_SYS_REGISTEEL_PUZZLE_COMPLETED":2226,"FLAG_SYS_RESET_RTC_ENABLE":2242,"FLAG_SYS_RIBBON_GET":2203,"FLAG_SYS_SAFARI_MODE":2188,"FLAG_SYS_SHOAL_ITEM":2239,"FLAG_SYS_SHOAL_TIDE":2202,"FLAG_SYS_TOWER_GOLD":2245,"FLAG_SYS_TOWER_SILVER":2244,"FLAG_SYS_TV_HOME":2192,"FLAG_SYS_TV_LATIAS_LATIOS":2237,"FLAG_SYS_TV_START":2194,"FLAG_SYS_TV_WATCH":2193,"FLAG_SYS_USE_FLASH":2184,"FLAG_SYS_USE_STRENGTH":2185,"FLAG_SYS_WEATHER_CTRL":2186,"FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE":112,"FLAG_TEMP_1":1,"FLAG_TEMP_10":16,"FLAG_TEMP_11":17,"FLAG_TEMP_12":18,"FLAG_TEMP_13":19,"FLAG_TEMP_14":20,"FLAG_TEMP_15":21,"FLAG_TEMP_16":22,"FLAG_TEMP_17":23,"FLAG_TEMP_18":24,"FLAG_TEMP_19":25,"FLAG_TEMP_1A":26,"FLAG_TEMP_1B":27,"FLAG_TEMP_1C":28,"FLAG_TEMP_1D":29,"FLAG_TEMP_1E":30,"FLAG_TEMP_1F":31,"FLAG_TEMP_2":2,"FLAG_TEMP_3":3,"FLAG_TEMP_4":4,"FLAG_TEMP_5":5,"FLAG_TEMP_6":6,"FLAG_TEMP_7":7,"FLAG_TEMP_8":8,"FLAG_TEMP_9":9,"FLAG_TEMP_A":10,"FLAG_TEMP_B":11,"FLAG_TEMP_C":12,"FLAG_TEMP_D":13,"FLAG_TEMP_E":14,"FLAG_TEMP_F":15,"FLAG_THANKED_FOR_PLAYING_WITH_WALLY":135,"FLAG_TOUGH_PAINTING_MADE":164,"FLAG_TRICK_HOUSE_PUZZLE_7_SWITCH_1":194,"FLAG_TRICK_HOUSE_PUZZLE_7_SWITCH_2":195,"FLAG_TRICK_HOUSE_PUZZLE_7_SWITCH_3":196,"FLAG_TRICK_HOUSE_PUZZLE_7_SWITCH_4":197,"FLAG_TRICK_HOUSE_PUZZLE_7_SWITCH_5":198,"FLAG_TV_EXPLAINED":98,"FLAG_UNKNOWN_0x363":867,"FLAG_UNKNOWN_0x393":915,"FLAG_UNUSED_0x022":34,"FLAG_UNUSED_0x023":35,"FLAG_UNUSED_0x024":36,"FLAG_UNUSED_0x025":37,"FLAG_UNUSED_0x026":38,"FLAG_UNUSED_0x027":39,"FLAG_UNUSED_0x028":40,"FLAG_UNUSED_0x029":41,"FLAG_UNUSED_0x02A":42,"FLAG_UNUSED_0x02B":43,"FLAG_UNUSED_0x02C":44,"FLAG_UNUSED_0x02D":45,"FLAG_UNUSED_0x02E":46,"FLAG_UNUSED_0x02F":47,"FLAG_UNUSED_0x030":48,"FLAG_UNUSED_0x031":49,"FLAG_UNUSED_0x032":50,"FLAG_UNUSED_0x033":51,"FLAG_UNUSED_0x034":52,"FLAG_UNUSED_0x035":53,"FLAG_UNUSED_0x036":54,"FLAG_UNUSED_0x037":55,"FLAG_UNUSED_0x038":56,"FLAG_UNUSED_0x039":57,"FLAG_UNUSED_0x03A":58,"FLAG_UNUSED_0x03B":59,"FLAG_UNUSED_0x03C":60,"FLAG_UNUSED_0x03D":61,"FLAG_UNUSED_0x03E":62,"FLAG_UNUSED_0x03F":63,"FLAG_UNUSED_0x040":64,"FLAG_UNUSED_0x041":65,"FLAG_UNUSED_0x042":66,"FLAG_UNUSED_0x043":67,"FLAG_UNUSED_0x044":68,"FLAG_UNUSED_0x045":69,"FLAG_UNUSED_0x046":70,"FLAG_UNUSED_0x047":71,"FLAG_UNUSED_0x048":72,"FLAG_UNUSED_0x049":73,"FLAG_UNUSED_0x04A":74,"FLAG_UNUSED_0x04B":75,"FLAG_UNUSED_0x04C":76,"FLAG_UNUSED_0x04D":77,"FLAG_UNUSED_0x04E":78,"FLAG_UNUSED_0x04F":79,"FLAG_UNUSED_0x055":85,"FLAG_UNUSED_0x0E9":233,"FLAG_UNUSED_0x1AA":426,"FLAG_UNUSED_0x1AB":427,"FLAG_UNUSED_0x1DA":474,"FLAG_UNUSED_0x1DE":478,"FLAG_UNUSED_0x1DF":479,"FLAG_UNUSED_0x1E0":480,"FLAG_UNUSED_0x1E1":481,"FLAG_UNUSED_0x264":612,"FLAG_UNUSED_0x265":613,"FLAG_UNUSED_0x266":614,"FLAG_UNUSED_0x267":615,"FLAG_UNUSED_0x268":616,"FLAG_UNUSED_0x269":617,"FLAG_UNUSED_0x26A":618,"FLAG_UNUSED_0x26B":619,"FLAG_UNUSED_0x26C":620,"FLAG_UNUSED_0x26D":621,"FLAG_UNUSED_0x26E":622,"FLAG_UNUSED_0x26F":623,"FLAG_UNUSED_0x270":624,"FLAG_UNUSED_0x271":625,"FLAG_UNUSED_0x272":626,"FLAG_UNUSED_0x273":627,"FLAG_UNUSED_0x274":628,"FLAG_UNUSED_0x275":629,"FLAG_UNUSED_0x276":630,"FLAG_UNUSED_0x277":631,"FLAG_UNUSED_0x278":632,"FLAG_UNUSED_0x279":633,"FLAG_UNUSED_0x27A":634,"FLAG_UNUSED_0x27B":635,"FLAG_UNUSED_0x27C":636,"FLAG_UNUSED_0x27D":637,"FLAG_UNUSED_0x27E":638,"FLAG_UNUSED_0x27F":639,"FLAG_UNUSED_0x280":640,"FLAG_UNUSED_0x281":641,"FLAG_UNUSED_0x282":642,"FLAG_UNUSED_0x283":643,"FLAG_UNUSED_0x284":644,"FLAG_UNUSED_0x285":645,"FLAG_UNUSED_0x286":646,"FLAG_UNUSED_0x287":647,"FLAG_UNUSED_0x288":648,"FLAG_UNUSED_0x289":649,"FLAG_UNUSED_0x28A":650,"FLAG_UNUSED_0x28B":651,"FLAG_UNUSED_0x28C":652,"FLAG_UNUSED_0x28D":653,"FLAG_UNUSED_0x28E":654,"FLAG_UNUSED_0x28F":655,"FLAG_UNUSED_0x290":656,"FLAG_UNUSED_0x291":657,"FLAG_UNUSED_0x292":658,"FLAG_UNUSED_0x293":659,"FLAG_UNUSED_0x294":660,"FLAG_UNUSED_0x295":661,"FLAG_UNUSED_0x296":662,"FLAG_UNUSED_0x297":663,"FLAG_UNUSED_0x298":664,"FLAG_UNUSED_0x299":665,"FLAG_UNUSED_0x29A":666,"FLAG_UNUSED_0x29B":667,"FLAG_UNUSED_0x29C":668,"FLAG_UNUSED_0x29D":669,"FLAG_UNUSED_0x29E":670,"FLAG_UNUSED_0x29F":671,"FLAG_UNUSED_0x2A0":672,"FLAG_UNUSED_0x2A1":673,"FLAG_UNUSED_0x2A2":674,"FLAG_UNUSED_0x2A3":675,"FLAG_UNUSED_0x2A4":676,"FLAG_UNUSED_0x2A5":677,"FLAG_UNUSED_0x2A6":678,"FLAG_UNUSED_0x2A7":679,"FLAG_UNUSED_0x2A8":680,"FLAG_UNUSED_0x2A9":681,"FLAG_UNUSED_0x2AA":682,"FLAG_UNUSED_0x2AB":683,"FLAG_UNUSED_0x2AC":684,"FLAG_UNUSED_0x2AD":685,"FLAG_UNUSED_0x2AE":686,"FLAG_UNUSED_0x2AF":687,"FLAG_UNUSED_0x2B0":688,"FLAG_UNUSED_0x2B1":689,"FLAG_UNUSED_0x2B2":690,"FLAG_UNUSED_0x2B3":691,"FLAG_UNUSED_0x2B4":692,"FLAG_UNUSED_0x2B5":693,"FLAG_UNUSED_0x2B6":694,"FLAG_UNUSED_0x2B7":695,"FLAG_UNUSED_0x2B8":696,"FLAG_UNUSED_0x2B9":697,"FLAG_UNUSED_0x2BA":698,"FLAG_UNUSED_0x2BB":699,"FLAG_UNUSED_0x468":1128,"FLAG_UNUSED_0x470":1136,"FLAG_UNUSED_0x472":1138,"FLAG_UNUSED_0x479":1145,"FLAG_UNUSED_0x4A8":1192,"FLAG_UNUSED_0x4A9":1193,"FLAG_UNUSED_0x4AA":1194,"FLAG_UNUSED_0x4AB":1195,"FLAG_UNUSED_0x4AC":1196,"FLAG_UNUSED_0x4AD":1197,"FLAG_UNUSED_0x4AE":1198,"FLAG_UNUSED_0x4AF":1199,"FLAG_UNUSED_0x4B0":1200,"FLAG_UNUSED_0x4B1":1201,"FLAG_UNUSED_0x4B2":1202,"FLAG_UNUSED_0x4B3":1203,"FLAG_UNUSED_0x4B4":1204,"FLAG_UNUSED_0x4B5":1205,"FLAG_UNUSED_0x4B6":1206,"FLAG_UNUSED_0x4B7":1207,"FLAG_UNUSED_0x4B8":1208,"FLAG_UNUSED_0x4B9":1209,"FLAG_UNUSED_0x4BA":1210,"FLAG_UNUSED_0x4BB":1211,"FLAG_UNUSED_0x4BC":1212,"FLAG_UNUSED_0x4BD":1213,"FLAG_UNUSED_0x4BE":1214,"FLAG_UNUSED_0x4BF":1215,"FLAG_UNUSED_0x4C0":1216,"FLAG_UNUSED_0x4C1":1217,"FLAG_UNUSED_0x4C2":1218,"FLAG_UNUSED_0x4C3":1219,"FLAG_UNUSED_0x4C4":1220,"FLAG_UNUSED_0x4C5":1221,"FLAG_UNUSED_0x4C6":1222,"FLAG_UNUSED_0x4C7":1223,"FLAG_UNUSED_0x4C8":1224,"FLAG_UNUSED_0x4C9":1225,"FLAG_UNUSED_0x4CA":1226,"FLAG_UNUSED_0x4CB":1227,"FLAG_UNUSED_0x4CC":1228,"FLAG_UNUSED_0x4CD":1229,"FLAG_UNUSED_0x4CE":1230,"FLAG_UNUSED_0x4CF":1231,"FLAG_UNUSED_0x4D0":1232,"FLAG_UNUSED_0x4D1":1233,"FLAG_UNUSED_0x4D2":1234,"FLAG_UNUSED_0x4D3":1235,"FLAG_UNUSED_0x4D4":1236,"FLAG_UNUSED_0x4D5":1237,"FLAG_UNUSED_0x4D6":1238,"FLAG_UNUSED_0x4D7":1239,"FLAG_UNUSED_0x4D8":1240,"FLAG_UNUSED_0x4D9":1241,"FLAG_UNUSED_0x4DA":1242,"FLAG_UNUSED_0x4DB":1243,"FLAG_UNUSED_0x4DC":1244,"FLAG_UNUSED_0x4DD":1245,"FLAG_UNUSED_0x4DE":1246,"FLAG_UNUSED_0x4DF":1247,"FLAG_UNUSED_0x4E0":1248,"FLAG_UNUSED_0x4E1":1249,"FLAG_UNUSED_0x4E2":1250,"FLAG_UNUSED_0x4E3":1251,"FLAG_UNUSED_0x4E4":1252,"FLAG_UNUSED_0x4E5":1253,"FLAG_UNUSED_0x4E6":1254,"FLAG_UNUSED_0x4E7":1255,"FLAG_UNUSED_0x4E8":1256,"FLAG_UNUSED_0x4E9":1257,"FLAG_UNUSED_0x4EA":1258,"FLAG_UNUSED_0x4EB":1259,"FLAG_UNUSED_0x4EC":1260,"FLAG_UNUSED_0x4ED":1261,"FLAG_UNUSED_0x4EE":1262,"FLAG_UNUSED_0x4EF":1263,"FLAG_UNUSED_0x4F9":1273,"FLAG_UNUSED_0x4FA":1274,"FLAG_UNUSED_0x4FF":1279,"FLAG_UNUSED_0x863":2147,"FLAG_UNUSED_0x881":2177,"FLAG_UNUSED_0x882":2178,"FLAG_UNUSED_0x883":2179,"FLAG_UNUSED_0x884":2180,"FLAG_UNUSED_0x885":2181,"FLAG_UNUSED_0x886":2182,"FLAG_UNUSED_0x887":2183,"FLAG_UNUSED_0x88E":2190,"FLAG_UNUSED_0x88F":2191,"FLAG_UNUSED_0x8E3":2275,"FLAG_UNUSED_0x8E5":2277,"FLAG_UNUSED_0x8E6":2278,"FLAG_UNUSED_0x8E7":2279,"FLAG_UNUSED_0x8E8":2280,"FLAG_UNUSED_0x8E9":2281,"FLAG_UNUSED_0x8EA":2282,"FLAG_UNUSED_0x8EB":2283,"FLAG_UNUSED_0x8EC":2284,"FLAG_UNUSED_0x8ED":2285,"FLAG_UNUSED_0x8EE":2286,"FLAG_UNUSED_0x8EF":2287,"FLAG_UNUSED_0x8F0":2288,"FLAG_UNUSED_0x8F1":2289,"FLAG_UNUSED_0x8F2":2290,"FLAG_UNUSED_0x8F3":2291,"FLAG_UNUSED_0x8F4":2292,"FLAG_UNUSED_0x8F5":2293,"FLAG_UNUSED_0x8F6":2294,"FLAG_UNUSED_0x8F7":2295,"FLAG_UNUSED_0x8F8":2296,"FLAG_UNUSED_0x8F9":2297,"FLAG_UNUSED_0x8FA":2298,"FLAG_UNUSED_0x8FB":2299,"FLAG_UNUSED_0x8FC":2300,"FLAG_UNUSED_0x8FD":2301,"FLAG_UNUSED_0x8FE":2302,"FLAG_UNUSED_0x8FF":2303,"FLAG_UNUSED_0x900":2304,"FLAG_UNUSED_0x901":2305,"FLAG_UNUSED_0x902":2306,"FLAG_UNUSED_0x903":2307,"FLAG_UNUSED_0x904":2308,"FLAG_UNUSED_0x905":2309,"FLAG_UNUSED_0x906":2310,"FLAG_UNUSED_0x907":2311,"FLAG_UNUSED_0x908":2312,"FLAG_UNUSED_0x909":2313,"FLAG_UNUSED_0x90A":2314,"FLAG_UNUSED_0x90B":2315,"FLAG_UNUSED_0x90C":2316,"FLAG_UNUSED_0x90D":2317,"FLAG_UNUSED_0x90E":2318,"FLAG_UNUSED_0x90F":2319,"FLAG_UNUSED_0x910":2320,"FLAG_UNUSED_0x911":2321,"FLAG_UNUSED_0x912":2322,"FLAG_UNUSED_0x913":2323,"FLAG_UNUSED_0x914":2324,"FLAG_UNUSED_0x915":2325,"FLAG_UNUSED_0x916":2326,"FLAG_UNUSED_0x917":2327,"FLAG_UNUSED_0x918":2328,"FLAG_UNUSED_0x919":2329,"FLAG_UNUSED_0x91A":2330,"FLAG_UNUSED_0x91B":2331,"FLAG_UNUSED_0x91C":2332,"FLAG_UNUSED_0x91D":2333,"FLAG_UNUSED_0x91E":2334,"FLAG_UNUSED_0x91F":2335,"FLAG_UNUSED_0x920":2336,"FLAG_UNUSED_0x923":2339,"FLAG_UNUSED_0x924":2340,"FLAG_UNUSED_0x925":2341,"FLAG_UNUSED_0x926":2342,"FLAG_UNUSED_0x927":2343,"FLAG_UNUSED_0x928":2344,"FLAG_UNUSED_0x929":2345,"FLAG_UNUSED_0x933":2355,"FLAG_UNUSED_0x935":2357,"FLAG_UNUSED_0x936":2358,"FLAG_UNUSED_0x937":2359,"FLAG_UNUSED_0x938":2360,"FLAG_UNUSED_0x939":2361,"FLAG_UNUSED_0x93A":2362,"FLAG_UNUSED_0x93B":2363,"FLAG_UNUSED_0x93C":2364,"FLAG_UNUSED_0x93D":2365,"FLAG_UNUSED_0x93E":2366,"FLAG_UNUSED_0x93F":2367,"FLAG_UNUSED_0x940":2368,"FLAG_UNUSED_0x941":2369,"FLAG_UNUSED_0x942":2370,"FLAG_UNUSED_0x943":2371,"FLAG_UNUSED_0x944":2372,"FLAG_UNUSED_0x945":2373,"FLAG_UNUSED_0x946":2374,"FLAG_UNUSED_0x947":2375,"FLAG_UNUSED_0x948":2376,"FLAG_UNUSED_0x949":2377,"FLAG_UNUSED_0x94A":2378,"FLAG_UNUSED_0x94B":2379,"FLAG_UNUSED_0x94C":2380,"FLAG_UNUSED_0x94D":2381,"FLAG_UNUSED_0x94E":2382,"FLAG_UNUSED_0x94F":2383,"FLAG_UNUSED_0x950":2384,"FLAG_UNUSED_0x951":2385,"FLAG_UNUSED_0x952":2386,"FLAG_UNUSED_0x953":2387,"FLAG_UNUSED_0x954":2388,"FLAG_UNUSED_0x955":2389,"FLAG_UNUSED_0x956":2390,"FLAG_UNUSED_0x957":2391,"FLAG_UNUSED_0x958":2392,"FLAG_UNUSED_0x959":2393,"FLAG_UNUSED_0x95A":2394,"FLAG_UNUSED_0x95B":2395,"FLAG_UNUSED_0x95C":2396,"FLAG_UNUSED_0x95D":2397,"FLAG_UNUSED_0x95E":2398,"FLAG_UNUSED_0x95F":2399,"FLAG_UNUSED_RS_LEGENDARY_BATTLE_DONE":113,"FLAG_USED_ROOM_1_KEY":240,"FLAG_USED_ROOM_2_KEY":241,"FLAG_USED_ROOM_4_KEY":242,"FLAG_USED_ROOM_6_KEY":243,"FLAG_USED_STORAGE_KEY":239,"FLAG_VISITED_DEWFORD_TOWN":2161,"FLAG_VISITED_EVER_GRANDE_CITY":2174,"FLAG_VISITED_FALLARBOR_TOWN":2163,"FLAG_VISITED_FORTREE_CITY":2170,"FLAG_VISITED_LAVARIDGE_TOWN":2162,"FLAG_VISITED_LILYCOVE_CITY":2171,"FLAG_VISITED_LITTLEROOT_TOWN":2159,"FLAG_VISITED_MAUVILLE_CITY":2168,"FLAG_VISITED_MOSSDEEP_CITY":2172,"FLAG_VISITED_OLDALE_TOWN":2160,"FLAG_VISITED_PACIFIDLOG_TOWN":2165,"FLAG_VISITED_PETALBURG_CITY":2166,"FLAG_VISITED_RUSTBORO_CITY":2169,"FLAG_VISITED_SLATEPORT_CITY":2167,"FLAG_VISITED_SOOTOPOLIS_CITY":2173,"FLAG_VISITED_VERDANTURF_TOWN":2164,"FLAG_WALLACE_GOES_TO_SKY_PILLAR":311,"FLAG_WALLY_SPEECH":193,"FLAG_WATTSON_REMATCH_AVAILABLE":91,"FLAG_WHITEOUT_TO_LAVARIDGE":108,"FLAG_WINGULL_DELIVERED_MAIL":224,"FLAG_WINGULL_SENT_ON_ERRAND":222,"FLAG_WONDER_CARD_UNUSED_1":317,"FLAG_WONDER_CARD_UNUSED_10":326,"FLAG_WONDER_CARD_UNUSED_11":327,"FLAG_WONDER_CARD_UNUSED_12":328,"FLAG_WONDER_CARD_UNUSED_13":329,"FLAG_WONDER_CARD_UNUSED_14":330,"FLAG_WONDER_CARD_UNUSED_15":331,"FLAG_WONDER_CARD_UNUSED_16":332,"FLAG_WONDER_CARD_UNUSED_17":333,"FLAG_WONDER_CARD_UNUSED_2":318,"FLAG_WONDER_CARD_UNUSED_3":319,"FLAG_WONDER_CARD_UNUSED_4":320,"FLAG_WONDER_CARD_UNUSED_5":321,"FLAG_WONDER_CARD_UNUSED_6":322,"FLAG_WONDER_CARD_UNUSED_7":323,"FLAG_WONDER_CARD_UNUSED_8":324,"FLAG_WONDER_CARD_UNUSED_9":325,"GOOD_ROD":1,"ITEMS_COUNT":377,"ITEM_034":52,"ITEM_035":53,"ITEM_036":54,"ITEM_037":55,"ITEM_038":56,"ITEM_039":57,"ITEM_03A":58,"ITEM_03B":59,"ITEM_03C":60,"ITEM_03D":61,"ITEM_03E":62,"ITEM_048":72,"ITEM_052":82,"ITEM_057":87,"ITEM_058":88,"ITEM_059":89,"ITEM_05A":90,"ITEM_05B":91,"ITEM_05C":92,"ITEM_063":99,"ITEM_064":100,"ITEM_065":101,"ITEM_066":102,"ITEM_069":105,"ITEM_071":113,"ITEM_072":114,"ITEM_073":115,"ITEM_074":116,"ITEM_075":117,"ITEM_076":118,"ITEM_077":119,"ITEM_078":120,"ITEM_0EA":234,"ITEM_0EB":235,"ITEM_0EC":236,"ITEM_0ED":237,"ITEM_0EE":238,"ITEM_0EF":239,"ITEM_0F0":240,"ITEM_0F1":241,"ITEM_0F2":242,"ITEM_0F3":243,"ITEM_0F4":244,"ITEM_0F5":245,"ITEM_0F6":246,"ITEM_0F7":247,"ITEM_0F8":248,"ITEM_0F9":249,"ITEM_0FA":250,"ITEM_0FB":251,"ITEM_0FC":252,"ITEM_0FD":253,"ITEM_10B":267,"ITEM_15B":347,"ITEM_15C":348,"ITEM_ACRO_BIKE":272,"ITEM_AGUAV_BERRY":146,"ITEM_AMULET_COIN":189,"ITEM_ANTIDOTE":14,"ITEM_APICOT_BERRY":172,"ITEM_ARCHIPELAGO_PROGRESSION":112,"ITEM_ASPEAR_BERRY":137,"ITEM_AURORA_TICKET":371,"ITEM_AWAKENING":17,"ITEM_BADGE_1":226,"ITEM_BADGE_2":227,"ITEM_BADGE_3":228,"ITEM_BADGE_4":229,"ITEM_BADGE_5":230,"ITEM_BADGE_6":231,"ITEM_BADGE_7":232,"ITEM_BADGE_8":233,"ITEM_BASEMENT_KEY":271,"ITEM_BEAD_MAIL":127,"ITEM_BELUE_BERRY":167,"ITEM_BERRY_JUICE":44,"ITEM_BERRY_POUCH":365,"ITEM_BICYCLE":360,"ITEM_BIG_MUSHROOM":104,"ITEM_BIG_PEARL":107,"ITEM_BIKE_VOUCHER":352,"ITEM_BLACK_BELT":207,"ITEM_BLACK_FLUTE":42,"ITEM_BLACK_GLASSES":206,"ITEM_BLUE_FLUTE":39,"ITEM_BLUE_ORB":277,"ITEM_BLUE_SCARF":255,"ITEM_BLUE_SHARD":49,"ITEM_BLUK_BERRY":149,"ITEM_BRIGHT_POWDER":179,"ITEM_BURN_HEAL":15,"ITEM_B_USE_MEDICINE":1,"ITEM_B_USE_OTHER":2,"ITEM_CALCIUM":67,"ITEM_CARBOS":66,"ITEM_CARD_KEY":355,"ITEM_CHARCOAL":215,"ITEM_CHERI_BERRY":133,"ITEM_CHESTO_BERRY":134,"ITEM_CHOICE_BAND":186,"ITEM_CLAW_FOSSIL":287,"ITEM_CLEANSE_TAG":190,"ITEM_COIN_CASE":260,"ITEM_CONTEST_PASS":266,"ITEM_CORNN_BERRY":159,"ITEM_DEEP_SEA_SCALE":193,"ITEM_DEEP_SEA_TOOTH":192,"ITEM_DEVON_GOODS":269,"ITEM_DEVON_SCOPE":288,"ITEM_DIRE_HIT":74,"ITEM_DIVE_BALL":7,"ITEM_DOME_FOSSIL":358,"ITEM_DRAGON_FANG":216,"ITEM_DRAGON_SCALE":201,"ITEM_DREAM_MAIL":130,"ITEM_DURIN_BERRY":166,"ITEM_ELIXIR":36,"ITEM_ENERGY_POWDER":30,"ITEM_ENERGY_ROOT":31,"ITEM_ENIGMA_BERRY":175,"ITEM_EON_TICKET":275,"ITEM_ESCAPE_ROPE":85,"ITEM_ETHER":34,"ITEM_EVERSTONE":195,"ITEM_EXP_SHARE":182,"ITEM_FAB_MAIL":131,"ITEM_FAME_CHECKER":363,"ITEM_FIGY_BERRY":143,"ITEM_FIRE_STONE":95,"ITEM_FLUFFY_TAIL":81,"ITEM_FOCUS_BAND":196,"ITEM_FRESH_WATER":26,"ITEM_FULL_HEAL":23,"ITEM_FULL_RESTORE":19,"ITEM_GANLON_BERRY":169,"ITEM_GLITTER_MAIL":123,"ITEM_GOLD_TEETH":353,"ITEM_GOOD_ROD":263,"ITEM_GO_GOGGLES":279,"ITEM_GREAT_BALL":3,"ITEM_GREEN_SCARF":257,"ITEM_GREEN_SHARD":51,"ITEM_GREPA_BERRY":157,"ITEM_GUARD_SPEC":73,"ITEM_HARBOR_MAIL":122,"ITEM_HARD_STONE":204,"ITEM_HEAL_POWDER":32,"ITEM_HEART_SCALE":111,"ITEM_HELIX_FOSSIL":357,"ITEM_HM01":339,"ITEM_HM01_CUT":339,"ITEM_HM02":340,"ITEM_HM02_FLY":340,"ITEM_HM03":341,"ITEM_HM03_SURF":341,"ITEM_HM04":342,"ITEM_HM04_STRENGTH":342,"ITEM_HM05":343,"ITEM_HM05_FLASH":343,"ITEM_HM06":344,"ITEM_HM06_ROCK_SMASH":344,"ITEM_HM07":345,"ITEM_HM07_WATERFALL":345,"ITEM_HM08":346,"ITEM_HM08_DIVE":346,"ITEM_HONDEW_BERRY":156,"ITEM_HP_UP":63,"ITEM_HYPER_POTION":21,"ITEM_IAPAPA_BERRY":147,"ITEM_ICE_HEAL":16,"ITEM_IRON":65,"ITEM_ITEMFINDER":261,"ITEM_KELPSY_BERRY":154,"ITEM_KINGS_ROCK":187,"ITEM_LANSAT_BERRY":173,"ITEM_LAVA_COOKIE":38,"ITEM_LAX_INCENSE":221,"ITEM_LEAF_STONE":98,"ITEM_LEFTOVERS":200,"ITEM_LEMONADE":28,"ITEM_LEPPA_BERRY":138,"ITEM_LETTER":274,"ITEM_LIECHI_BERRY":168,"ITEM_LIFT_KEY":356,"ITEM_LIGHT_BALL":202,"ITEM_LIST_END":65535,"ITEM_LUCKY_EGG":197,"ITEM_LUCKY_PUNCH":222,"ITEM_LUM_BERRY":141,"ITEM_LUXURY_BALL":11,"ITEM_MACHO_BRACE":181,"ITEM_MACH_BIKE":259,"ITEM_MAGMA_EMBLEM":375,"ITEM_MAGNET":208,"ITEM_MAGOST_BERRY":160,"ITEM_MAGO_BERRY":145,"ITEM_MASTER_BALL":1,"ITEM_MAX_ELIXIR":37,"ITEM_MAX_ETHER":35,"ITEM_MAX_POTION":20,"ITEM_MAX_REPEL":84,"ITEM_MAX_REVIVE":25,"ITEM_MECH_MAIL":124,"ITEM_MENTAL_HERB":185,"ITEM_METAL_COAT":199,"ITEM_METAL_POWDER":223,"ITEM_METEORITE":280,"ITEM_MIRACLE_SEED":205,"ITEM_MOOMOO_MILK":29,"ITEM_MOON_STONE":94,"ITEM_MYSTIC_TICKET":370,"ITEM_MYSTIC_WATER":209,"ITEM_NANAB_BERRY":150,"ITEM_NEST_BALL":8,"ITEM_NET_BALL":6,"ITEM_NEVER_MELT_ICE":212,"ITEM_NOMEL_BERRY":162,"ITEM_NONE":0,"ITEM_NUGGET":110,"ITEM_OAKS_PARCEL":349,"ITEM_OLD_AMBER":354,"ITEM_OLD_ROD":262,"ITEM_OLD_SEA_MAP":376,"ITEM_ORANGE_MAIL":121,"ITEM_ORAN_BERRY":139,"ITEM_PAMTRE_BERRY":164,"ITEM_PARALYZE_HEAL":18,"ITEM_PEARL":106,"ITEM_PECHA_BERRY":135,"ITEM_PERSIM_BERRY":140,"ITEM_PETAYA_BERRY":171,"ITEM_PINAP_BERRY":152,"ITEM_PINK_SCARF":256,"ITEM_POISON_BARB":211,"ITEM_POKEBLOCK_CASE":273,"ITEM_POKE_BALL":4,"ITEM_POKE_DOLL":80,"ITEM_POKE_FLUTE":350,"ITEM_POMEG_BERRY":153,"ITEM_POTION":13,"ITEM_POWDER_JAR":372,"ITEM_PP_MAX":71,"ITEM_PP_UP":69,"ITEM_PREMIER_BALL":12,"ITEM_PROTEIN":64,"ITEM_QUALOT_BERRY":155,"ITEM_QUICK_CLAW":183,"ITEM_RABUTA_BERRY":161,"ITEM_RAINBOW_PASS":368,"ITEM_RARE_CANDY":68,"ITEM_RAWST_BERRY":136,"ITEM_RAZZ_BERRY":148,"ITEM_RED_FLUTE":41,"ITEM_RED_ORB":276,"ITEM_RED_SCARF":254,"ITEM_RED_SHARD":48,"ITEM_REPEAT_BALL":9,"ITEM_REPEL":86,"ITEM_RETRO_MAIL":132,"ITEM_REVIVAL_HERB":33,"ITEM_REVIVE":24,"ITEM_ROOM_1_KEY":281,"ITEM_ROOM_2_KEY":282,"ITEM_ROOM_4_KEY":283,"ITEM_ROOM_6_KEY":284,"ITEM_ROOT_FOSSIL":286,"ITEM_RUBY":373,"ITEM_SACRED_ASH":45,"ITEM_SAFARI_BALL":5,"ITEM_SALAC_BERRY":170,"ITEM_SAPPHIRE":374,"ITEM_SCANNER":278,"ITEM_SCOPE_LENS":198,"ITEM_SEA_INCENSE":220,"ITEM_SECRET_KEY":351,"ITEM_SHADOW_MAIL":128,"ITEM_SHARP_BEAK":210,"ITEM_SHELL_BELL":219,"ITEM_SHOAL_SALT":46,"ITEM_SHOAL_SHELL":47,"ITEM_SILK_SCARF":217,"ITEM_SILPH_SCOPE":359,"ITEM_SILVER_POWDER":188,"ITEM_SITRUS_BERRY":142,"ITEM_SMOKE_BALL":194,"ITEM_SODA_POP":27,"ITEM_SOFT_SAND":203,"ITEM_SOOTHE_BELL":184,"ITEM_SOOT_SACK":270,"ITEM_SOUL_DEW":191,"ITEM_SPELL_TAG":213,"ITEM_SPELON_BERRY":163,"ITEM_SS_TICKET":265,"ITEM_STARDUST":108,"ITEM_STARF_BERRY":174,"ITEM_STAR_PIECE":109,"ITEM_STICK":225,"ITEM_STORAGE_KEY":285,"ITEM_SUN_STONE":93,"ITEM_SUPER_POTION":22,"ITEM_SUPER_REPEL":83,"ITEM_SUPER_ROD":264,"ITEM_TAMATO_BERRY":158,"ITEM_TEA":369,"ITEM_TEACHY_TV":366,"ITEM_THICK_CLUB":224,"ITEM_THUNDER_STONE":96,"ITEM_TIMER_BALL":10,"ITEM_TINY_MUSHROOM":103,"ITEM_TM01":289,"ITEM_TM01_FOCUS_PUNCH":289,"ITEM_TM02":290,"ITEM_TM02_DRAGON_CLAW":290,"ITEM_TM03":291,"ITEM_TM03_WATER_PULSE":291,"ITEM_TM04":292,"ITEM_TM04_CALM_MIND":292,"ITEM_TM05":293,"ITEM_TM05_ROAR":293,"ITEM_TM06":294,"ITEM_TM06_TOXIC":294,"ITEM_TM07":295,"ITEM_TM07_HAIL":295,"ITEM_TM08":296,"ITEM_TM08_BULK_UP":296,"ITEM_TM09":297,"ITEM_TM09_BULLET_SEED":297,"ITEM_TM10":298,"ITEM_TM10_HIDDEN_POWER":298,"ITEM_TM11":299,"ITEM_TM11_SUNNY_DAY":299,"ITEM_TM12":300,"ITEM_TM12_TAUNT":300,"ITEM_TM13":301,"ITEM_TM13_ICE_BEAM":301,"ITEM_TM14":302,"ITEM_TM14_BLIZZARD":302,"ITEM_TM15":303,"ITEM_TM15_HYPER_BEAM":303,"ITEM_TM16":304,"ITEM_TM16_LIGHT_SCREEN":304,"ITEM_TM17":305,"ITEM_TM17_PROTECT":305,"ITEM_TM18":306,"ITEM_TM18_RAIN_DANCE":306,"ITEM_TM19":307,"ITEM_TM19_GIGA_DRAIN":307,"ITEM_TM20":308,"ITEM_TM20_SAFEGUARD":308,"ITEM_TM21":309,"ITEM_TM21_FRUSTRATION":309,"ITEM_TM22":310,"ITEM_TM22_SOLAR_BEAM":310,"ITEM_TM23":311,"ITEM_TM23_IRON_TAIL":311,"ITEM_TM24":312,"ITEM_TM24_THUNDERBOLT":312,"ITEM_TM25":313,"ITEM_TM25_THUNDER":313,"ITEM_TM26":314,"ITEM_TM26_EARTHQUAKE":314,"ITEM_TM27":315,"ITEM_TM27_RETURN":315,"ITEM_TM28":316,"ITEM_TM28_DIG":316,"ITEM_TM29":317,"ITEM_TM29_PSYCHIC":317,"ITEM_TM30":318,"ITEM_TM30_SHADOW_BALL":318,"ITEM_TM31":319,"ITEM_TM31_BRICK_BREAK":319,"ITEM_TM32":320,"ITEM_TM32_DOUBLE_TEAM":320,"ITEM_TM33":321,"ITEM_TM33_REFLECT":321,"ITEM_TM34":322,"ITEM_TM34_SHOCK_WAVE":322,"ITEM_TM35":323,"ITEM_TM35_FLAMETHROWER":323,"ITEM_TM36":324,"ITEM_TM36_SLUDGE_BOMB":324,"ITEM_TM37":325,"ITEM_TM37_SANDSTORM":325,"ITEM_TM38":326,"ITEM_TM38_FIRE_BLAST":326,"ITEM_TM39":327,"ITEM_TM39_ROCK_TOMB":327,"ITEM_TM40":328,"ITEM_TM40_AERIAL_ACE":328,"ITEM_TM41":329,"ITEM_TM41_TORMENT":329,"ITEM_TM42":330,"ITEM_TM42_FACADE":330,"ITEM_TM43":331,"ITEM_TM43_SECRET_POWER":331,"ITEM_TM44":332,"ITEM_TM44_REST":332,"ITEM_TM45":333,"ITEM_TM45_ATTRACT":333,"ITEM_TM46":334,"ITEM_TM46_THIEF":334,"ITEM_TM47":335,"ITEM_TM47_STEEL_WING":335,"ITEM_TM48":336,"ITEM_TM48_SKILL_SWAP":336,"ITEM_TM49":337,"ITEM_TM49_SNATCH":337,"ITEM_TM50":338,"ITEM_TM50_OVERHEAT":338,"ITEM_TM_CASE":364,"ITEM_TOWN_MAP":361,"ITEM_TRI_PASS":367,"ITEM_TROPIC_MAIL":129,"ITEM_TWISTED_SPOON":214,"ITEM_ULTRA_BALL":2,"ITEM_UNUSED_BERRY_1":176,"ITEM_UNUSED_BERRY_2":177,"ITEM_UNUSED_BERRY_3":178,"ITEM_UP_GRADE":218,"ITEM_USE_BAG_MENU":4,"ITEM_USE_FIELD":2,"ITEM_USE_MAIL":0,"ITEM_USE_PARTY_MENU":1,"ITEM_USE_PBLOCK_CASE":3,"ITEM_VS_SEEKER":362,"ITEM_WAILMER_PAIL":268,"ITEM_WATER_STONE":97,"ITEM_WATMEL_BERRY":165,"ITEM_WAVE_MAIL":126,"ITEM_WEPEAR_BERRY":151,"ITEM_WHITE_FLUTE":43,"ITEM_WHITE_HERB":180,"ITEM_WIKI_BERRY":144,"ITEM_WOOD_MAIL":125,"ITEM_X_ACCURACY":78,"ITEM_X_ATTACK":75,"ITEM_X_DEFEND":76,"ITEM_X_SPECIAL":79,"ITEM_X_SPEED":77,"ITEM_YELLOW_FLUTE":40,"ITEM_YELLOW_SCARF":258,"ITEM_YELLOW_SHARD":50,"ITEM_ZINC":70,"LAST_BALL":12,"LAST_BERRY_INDEX":175,"LAST_BERRY_MASTER_BERRY":162,"LAST_BERRY_MASTER_WIFE_BERRY":142,"LAST_KIRI_BERRY":162,"LAST_ROUTE_114_MAN_BERRY":152,"MACH_BIKE":0,"MAIL_NONE":255,"MAP_ABANDONED_SHIP_CAPTAINS_OFFICE":6207,"MAP_ABANDONED_SHIP_CORRIDORS_1F":6199,"MAP_ABANDONED_SHIP_CORRIDORS_B1F":6201,"MAP_ABANDONED_SHIP_DECK":6198,"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS":6209,"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS":6210,"MAP_ABANDONED_SHIP_ROOMS2_1F":6206,"MAP_ABANDONED_SHIP_ROOMS2_B1F":6203,"MAP_ABANDONED_SHIP_ROOMS_1F":6200,"MAP_ABANDONED_SHIP_ROOMS_B1F":6202,"MAP_ABANDONED_SHIP_ROOM_B1F":6205,"MAP_ABANDONED_SHIP_UNDERWATER1":6204,"MAP_ABANDONED_SHIP_UNDERWATER2":6208,"MAP_ALTERING_CAVE":6250,"MAP_ANCIENT_TOMB":6212,"MAP_AQUA_HIDEOUT_1F":6167,"MAP_AQUA_HIDEOUT_B1F":6168,"MAP_AQUA_HIDEOUT_B2F":6169,"MAP_AQUA_HIDEOUT_UNUSED_RUBY_MAP1":6218,"MAP_AQUA_HIDEOUT_UNUSED_RUBY_MAP2":6219,"MAP_AQUA_HIDEOUT_UNUSED_RUBY_MAP3":6220,"MAP_ARTISAN_CAVE_1F":6244,"MAP_ARTISAN_CAVE_B1F":6243,"MAP_BATTLE_COLOSSEUM_2P":6424,"MAP_BATTLE_COLOSSEUM_4P":6427,"MAP_BATTLE_FRONTIER_BATTLE_ARENA_BATTLE_ROOM":6686,"MAP_BATTLE_FRONTIER_BATTLE_ARENA_CORRIDOR":6685,"MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY":6684,"MAP_BATTLE_FRONTIER_BATTLE_DOME_BATTLE_ROOM":6677,"MAP_BATTLE_FRONTIER_BATTLE_DOME_CORRIDOR":6675,"MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY":6674,"MAP_BATTLE_FRONTIER_BATTLE_DOME_PRE_BATTLE_ROOM":6676,"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_BATTLE_ROOM":6689,"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY":6687,"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_PRE_BATTLE_ROOM":6688,"MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM":6680,"MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR":6679,"MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY":6678,"MAP_BATTLE_FRONTIER_BATTLE_PIKE_CORRIDOR":6691,"MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY":6690,"MAP_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_FINAL":6694,"MAP_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_NORMAL":6693,"MAP_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS":6695,"MAP_BATTLE_FRONTIER_BATTLE_PIKE_THREE_PATH_ROOM":6692,"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR":6682,"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY":6681,"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_TOP":6683,"MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM":6664,"MAP_BATTLE_FRONTIER_BATTLE_TOWER_CORRIDOR":6663,"MAP_BATTLE_FRONTIER_BATTLE_TOWER_ELEVATOR":6662,"MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY":6661,"MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_BATTLE_ROOM":6673,"MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_CORRIDOR":6672,"MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_PARTNER_ROOM":6671,"MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER":6698,"MAP_BATTLE_FRONTIER_LOUNGE1":6697,"MAP_BATTLE_FRONTIER_LOUNGE2":6699,"MAP_BATTLE_FRONTIER_LOUNGE3":6700,"MAP_BATTLE_FRONTIER_LOUNGE4":6701,"MAP_BATTLE_FRONTIER_LOUNGE5":6703,"MAP_BATTLE_FRONTIER_LOUNGE6":6704,"MAP_BATTLE_FRONTIER_LOUNGE7":6705,"MAP_BATTLE_FRONTIER_LOUNGE8":6707,"MAP_BATTLE_FRONTIER_LOUNGE9":6708,"MAP_BATTLE_FRONTIER_MART":6711,"MAP_BATTLE_FRONTIER_OUTSIDE_EAST":6670,"MAP_BATTLE_FRONTIER_OUTSIDE_WEST":6660,"MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F":6709,"MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F":6710,"MAP_BATTLE_FRONTIER_RANKING_HALL":6696,"MAP_BATTLE_FRONTIER_RECEPTION_GATE":6706,"MAP_BATTLE_FRONTIER_SCOTTS_HOUSE":6702,"MAP_BATTLE_PYRAMID_SQUARE01":6444,"MAP_BATTLE_PYRAMID_SQUARE02":6445,"MAP_BATTLE_PYRAMID_SQUARE03":6446,"MAP_BATTLE_PYRAMID_SQUARE04":6447,"MAP_BATTLE_PYRAMID_SQUARE05":6448,"MAP_BATTLE_PYRAMID_SQUARE06":6449,"MAP_BATTLE_PYRAMID_SQUARE07":6450,"MAP_BATTLE_PYRAMID_SQUARE08":6451,"MAP_BATTLE_PYRAMID_SQUARE09":6452,"MAP_BATTLE_PYRAMID_SQUARE10":6453,"MAP_BATTLE_PYRAMID_SQUARE11":6454,"MAP_BATTLE_PYRAMID_SQUARE12":6455,"MAP_BATTLE_PYRAMID_SQUARE13":6456,"MAP_BATTLE_PYRAMID_SQUARE14":6457,"MAP_BATTLE_PYRAMID_SQUARE15":6458,"MAP_BATTLE_PYRAMID_SQUARE16":6459,"MAP_BIRTH_ISLAND_EXTERIOR":6714,"MAP_BIRTH_ISLAND_HARBOR":6715,"MAP_CAVE_OF_ORIGIN_1F":6182,"MAP_CAVE_OF_ORIGIN_B1F":6186,"MAP_CAVE_OF_ORIGIN_ENTRANCE":6181,"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1":6183,"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2":6184,"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3":6185,"MAP_CONTEST_HALL":6428,"MAP_CONTEST_HALL_BEAUTY":6435,"MAP_CONTEST_HALL_COOL":6437,"MAP_CONTEST_HALL_CUTE":6439,"MAP_CONTEST_HALL_SMART":6438,"MAP_CONTEST_HALL_TOUGH":6436,"MAP_DESERT_RUINS":6150,"MAP_DESERT_UNDERPASS":6242,"MAP_DEWFORD_TOWN":11,"MAP_DEWFORD_TOWN_GYM":771,"MAP_DEWFORD_TOWN_HALL":772,"MAP_DEWFORD_TOWN_HOUSE1":768,"MAP_DEWFORD_TOWN_HOUSE2":773,"MAP_DEWFORD_TOWN_POKEMON_CENTER_1F":769,"MAP_DEWFORD_TOWN_POKEMON_CENTER_2F":770,"MAP_EVER_GRANDE_CITY":8,"MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM":4100,"MAP_EVER_GRANDE_CITY_DRAKES_ROOM":4099,"MAP_EVER_GRANDE_CITY_GLACIAS_ROOM":4098,"MAP_EVER_GRANDE_CITY_HALL1":4101,"MAP_EVER_GRANDE_CITY_HALL2":4102,"MAP_EVER_GRANDE_CITY_HALL3":4103,"MAP_EVER_GRANDE_CITY_HALL4":4104,"MAP_EVER_GRANDE_CITY_HALL5":4105,"MAP_EVER_GRANDE_CITY_HALL_OF_FAME":4107,"MAP_EVER_GRANDE_CITY_PHOEBES_ROOM":4097,"MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F":4108,"MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F":4109,"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F":4106,"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F":4110,"MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM":4096,"MAP_FALLARBOR_TOWN":13,"MAP_FALLARBOR_TOWN_BATTLE_TENT_BATTLE_ROOM":1283,"MAP_FALLARBOR_TOWN_BATTLE_TENT_CORRIDOR":1282,"MAP_FALLARBOR_TOWN_BATTLE_TENT_LOBBY":1281,"MAP_FALLARBOR_TOWN_COZMOS_HOUSE":1286,"MAP_FALLARBOR_TOWN_MART":1280,"MAP_FALLARBOR_TOWN_MOVE_RELEARNERS_HOUSE":1287,"MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F":1284,"MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F":1285,"MAP_FARAWAY_ISLAND_ENTRANCE":6712,"MAP_FARAWAY_ISLAND_INTERIOR":6713,"MAP_FIERY_PATH":6158,"MAP_FORTREE_CITY":4,"MAP_FORTREE_CITY_DECORATION_SHOP":3081,"MAP_FORTREE_CITY_GYM":3073,"MAP_FORTREE_CITY_HOUSE1":3072,"MAP_FORTREE_CITY_HOUSE2":3077,"MAP_FORTREE_CITY_HOUSE3":3078,"MAP_FORTREE_CITY_HOUSE4":3079,"MAP_FORTREE_CITY_HOUSE5":3080,"MAP_FORTREE_CITY_MART":3076,"MAP_FORTREE_CITY_POKEMON_CENTER_1F":3074,"MAP_FORTREE_CITY_POKEMON_CENTER_2F":3075,"MAP_GRANITE_CAVE_1F":6151,"MAP_GRANITE_CAVE_B1F":6152,"MAP_GRANITE_CAVE_B2F":6153,"MAP_GRANITE_CAVE_STEVENS_ROOM":6154,"MAP_GROUPS_COUNT":34,"MAP_INSIDE_OF_TRUCK":6440,"MAP_ISLAND_CAVE":6211,"MAP_JAGGED_PASS":6157,"MAP_LAVARIDGE_TOWN":12,"MAP_LAVARIDGE_TOWN_GYM_1F":1025,"MAP_LAVARIDGE_TOWN_GYM_B1F":1026,"MAP_LAVARIDGE_TOWN_HERB_SHOP":1024,"MAP_LAVARIDGE_TOWN_HOUSE":1027,"MAP_LAVARIDGE_TOWN_MART":1028,"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F":1029,"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F":1030,"MAP_LILYCOVE_CITY":5,"MAP_LILYCOVE_CITY_CONTEST_HALL":3333,"MAP_LILYCOVE_CITY_CONTEST_LOBBY":3332,"MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F":3328,"MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_2F":3329,"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F":3344,"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F":3345,"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F":3346,"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F":3347,"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F":3348,"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR":3350,"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP":3349,"MAP_LILYCOVE_CITY_HARBOR":3338,"MAP_LILYCOVE_CITY_HOUSE1":3340,"MAP_LILYCOVE_CITY_HOUSE2":3341,"MAP_LILYCOVE_CITY_HOUSE3":3342,"MAP_LILYCOVE_CITY_HOUSE4":3343,"MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F":3330,"MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_2F":3331,"MAP_LILYCOVE_CITY_MOVE_DELETERS_HOUSE":3339,"MAP_LILYCOVE_CITY_POKEMON_CENTER_1F":3334,"MAP_LILYCOVE_CITY_POKEMON_CENTER_2F":3335,"MAP_LILYCOVE_CITY_POKEMON_TRAINER_FAN_CLUB":3337,"MAP_LILYCOVE_CITY_UNUSED_MART":3336,"MAP_LITTLEROOT_TOWN":9,"MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F":256,"MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F":257,"MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F":258,"MAP_LITTLEROOT_TOWN_MAYS_HOUSE_2F":259,"MAP_LITTLEROOT_TOWN_PROFESSOR_BIRCHS_LAB":260,"MAP_MAGMA_HIDEOUT_1F":6230,"MAP_MAGMA_HIDEOUT_2F_1R":6231,"MAP_MAGMA_HIDEOUT_2F_2R":6232,"MAP_MAGMA_HIDEOUT_2F_3R":6237,"MAP_MAGMA_HIDEOUT_3F_1R":6233,"MAP_MAGMA_HIDEOUT_3F_2R":6234,"MAP_MAGMA_HIDEOUT_3F_3R":6236,"MAP_MAGMA_HIDEOUT_4F":6235,"MAP_MARINE_CAVE_END":6247,"MAP_MARINE_CAVE_ENTRANCE":6246,"MAP_MAUVILLE_CITY":2,"MAP_MAUVILLE_CITY_BIKE_SHOP":2561,"MAP_MAUVILLE_CITY_GAME_CORNER":2563,"MAP_MAUVILLE_CITY_GYM":2560,"MAP_MAUVILLE_CITY_HOUSE1":2562,"MAP_MAUVILLE_CITY_HOUSE2":2564,"MAP_MAUVILLE_CITY_MART":2567,"MAP_MAUVILLE_CITY_POKEMON_CENTER_1F":2565,"MAP_MAUVILLE_CITY_POKEMON_CENTER_2F":2566,"MAP_METEOR_FALLS_1F_1R":6144,"MAP_METEOR_FALLS_1F_2R":6145,"MAP_METEOR_FALLS_B1F_1R":6146,"MAP_METEOR_FALLS_B1F_2R":6147,"MAP_METEOR_FALLS_STEVENS_CAVE":6251,"MAP_MIRAGE_TOWER_1F":6238,"MAP_MIRAGE_TOWER_2F":6239,"MAP_MIRAGE_TOWER_3F":6240,"MAP_MIRAGE_TOWER_4F":6241,"MAP_MOSSDEEP_CITY":6,"MAP_MOSSDEEP_CITY_GAME_CORNER_1F":3595,"MAP_MOSSDEEP_CITY_GAME_CORNER_B1F":3596,"MAP_MOSSDEEP_CITY_GYM":3584,"MAP_MOSSDEEP_CITY_HOUSE1":3585,"MAP_MOSSDEEP_CITY_HOUSE2":3586,"MAP_MOSSDEEP_CITY_HOUSE3":3590,"MAP_MOSSDEEP_CITY_HOUSE4":3592,"MAP_MOSSDEEP_CITY_MART":3589,"MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F":3587,"MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F":3588,"MAP_MOSSDEEP_CITY_SPACE_CENTER_1F":3593,"MAP_MOSSDEEP_CITY_SPACE_CENTER_2F":3594,"MAP_MOSSDEEP_CITY_STEVENS_HOUSE":3591,"MAP_MT_CHIMNEY":6156,"MAP_MT_CHIMNEY_CABLE_CAR_STATION":4865,"MAP_MT_PYRE_1F":6159,"MAP_MT_PYRE_2F":6160,"MAP_MT_PYRE_3F":6161,"MAP_MT_PYRE_4F":6162,"MAP_MT_PYRE_5F":6163,"MAP_MT_PYRE_6F":6164,"MAP_MT_PYRE_EXTERIOR":6165,"MAP_MT_PYRE_SUMMIT":6166,"MAP_NAVEL_ROCK_B1F":6725,"MAP_NAVEL_ROCK_BOTTOM":6743,"MAP_NAVEL_ROCK_DOWN01":6732,"MAP_NAVEL_ROCK_DOWN02":6733,"MAP_NAVEL_ROCK_DOWN03":6734,"MAP_NAVEL_ROCK_DOWN04":6735,"MAP_NAVEL_ROCK_DOWN05":6736,"MAP_NAVEL_ROCK_DOWN06":6737,"MAP_NAVEL_ROCK_DOWN07":6738,"MAP_NAVEL_ROCK_DOWN08":6739,"MAP_NAVEL_ROCK_DOWN09":6740,"MAP_NAVEL_ROCK_DOWN10":6741,"MAP_NAVEL_ROCK_DOWN11":6742,"MAP_NAVEL_ROCK_ENTRANCE":6724,"MAP_NAVEL_ROCK_EXTERIOR":6722,"MAP_NAVEL_ROCK_FORK":6726,"MAP_NAVEL_ROCK_HARBOR":6723,"MAP_NAVEL_ROCK_TOP":6731,"MAP_NAVEL_ROCK_UP1":6727,"MAP_NAVEL_ROCK_UP2":6728,"MAP_NAVEL_ROCK_UP3":6729,"MAP_NAVEL_ROCK_UP4":6730,"MAP_NEW_MAUVILLE_ENTRANCE":6196,"MAP_NEW_MAUVILLE_INSIDE":6197,"MAP_OLDALE_TOWN":10,"MAP_OLDALE_TOWN_HOUSE1":512,"MAP_OLDALE_TOWN_HOUSE2":513,"MAP_OLDALE_TOWN_MART":516,"MAP_OLDALE_TOWN_POKEMON_CENTER_1F":514,"MAP_OLDALE_TOWN_POKEMON_CENTER_2F":515,"MAP_PACIFIDLOG_TOWN":15,"MAP_PACIFIDLOG_TOWN_HOUSE1":1794,"MAP_PACIFIDLOG_TOWN_HOUSE2":1795,"MAP_PACIFIDLOG_TOWN_HOUSE3":1796,"MAP_PACIFIDLOG_TOWN_HOUSE4":1797,"MAP_PACIFIDLOG_TOWN_HOUSE5":1798,"MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F":1792,"MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F":1793,"MAP_PETALBURG_CITY":0,"MAP_PETALBURG_CITY_GYM":2049,"MAP_PETALBURG_CITY_HOUSE1":2050,"MAP_PETALBURG_CITY_HOUSE2":2051,"MAP_PETALBURG_CITY_MART":2054,"MAP_PETALBURG_CITY_POKEMON_CENTER_1F":2052,"MAP_PETALBURG_CITY_POKEMON_CENTER_2F":2053,"MAP_PETALBURG_CITY_WALLYS_HOUSE":2048,"MAP_PETALBURG_WOODS":6155,"MAP_RECORD_CORNER":6426,"MAP_ROUTE101":16,"MAP_ROUTE102":17,"MAP_ROUTE103":18,"MAP_ROUTE104":19,"MAP_ROUTE104_MR_BRINEYS_HOUSE":4352,"MAP_ROUTE104_PRETTY_PETAL_FLOWER_SHOP":4353,"MAP_ROUTE104_PROTOTYPE":6912,"MAP_ROUTE104_PROTOTYPE_PRETTY_PETAL_FLOWER_SHOP":6913,"MAP_ROUTE105":20,"MAP_ROUTE106":21,"MAP_ROUTE107":22,"MAP_ROUTE108":23,"MAP_ROUTE109":24,"MAP_ROUTE109_SEASHORE_HOUSE":7168,"MAP_ROUTE110":25,"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE":7435,"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE":7436,"MAP_ROUTE110_TRICK_HOUSE_CORRIDOR":7426,"MAP_ROUTE110_TRICK_HOUSE_END":7425,"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE":7424,"MAP_ROUTE110_TRICK_HOUSE_PUZZLE1":7427,"MAP_ROUTE110_TRICK_HOUSE_PUZZLE2":7428,"MAP_ROUTE110_TRICK_HOUSE_PUZZLE3":7429,"MAP_ROUTE110_TRICK_HOUSE_PUZZLE4":7430,"MAP_ROUTE110_TRICK_HOUSE_PUZZLE5":7431,"MAP_ROUTE110_TRICK_HOUSE_PUZZLE6":7432,"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7":7433,"MAP_ROUTE110_TRICK_HOUSE_PUZZLE8":7434,"MAP_ROUTE111":26,"MAP_ROUTE111_OLD_LADYS_REST_STOP":4609,"MAP_ROUTE111_WINSTRATE_FAMILYS_HOUSE":4608,"MAP_ROUTE112":27,"MAP_ROUTE112_CABLE_CAR_STATION":4864,"MAP_ROUTE113":28,"MAP_ROUTE113_GLASS_WORKSHOP":7680,"MAP_ROUTE114":29,"MAP_ROUTE114_FOSSIL_MANIACS_HOUSE":5120,"MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL":5121,"MAP_ROUTE114_LANETTES_HOUSE":5122,"MAP_ROUTE115":30,"MAP_ROUTE116":31,"MAP_ROUTE116_TUNNELERS_REST_HOUSE":5376,"MAP_ROUTE117":32,"MAP_ROUTE117_POKEMON_DAY_CARE":5632,"MAP_ROUTE118":33,"MAP_ROUTE119":34,"MAP_ROUTE119_HOUSE":8194,"MAP_ROUTE119_WEATHER_INSTITUTE_1F":8192,"MAP_ROUTE119_WEATHER_INSTITUTE_2F":8193,"MAP_ROUTE120":35,"MAP_ROUTE121":36,"MAP_ROUTE121_SAFARI_ZONE_ENTRANCE":5888,"MAP_ROUTE122":37,"MAP_ROUTE123":38,"MAP_ROUTE123_BERRY_MASTERS_HOUSE":7936,"MAP_ROUTE124":39,"MAP_ROUTE124_DIVING_TREASURE_HUNTERS_HOUSE":8448,"MAP_ROUTE125":40,"MAP_ROUTE126":41,"MAP_ROUTE127":42,"MAP_ROUTE128":43,"MAP_ROUTE129":44,"MAP_ROUTE130":45,"MAP_ROUTE131":46,"MAP_ROUTE132":47,"MAP_ROUTE133":48,"MAP_ROUTE134":49,"MAP_RUSTBORO_CITY":3,"MAP_RUSTBORO_CITY_CUTTERS_HOUSE":2827,"MAP_RUSTBORO_CITY_DEVON_CORP_1F":2816,"MAP_RUSTBORO_CITY_DEVON_CORP_2F":2817,"MAP_RUSTBORO_CITY_DEVON_CORP_3F":2818,"MAP_RUSTBORO_CITY_FLAT1_1F":2824,"MAP_RUSTBORO_CITY_FLAT1_2F":2825,"MAP_RUSTBORO_CITY_FLAT2_1F":2829,"MAP_RUSTBORO_CITY_FLAT2_2F":2830,"MAP_RUSTBORO_CITY_FLAT2_3F":2831,"MAP_RUSTBORO_CITY_GYM":2819,"MAP_RUSTBORO_CITY_HOUSE1":2826,"MAP_RUSTBORO_CITY_HOUSE2":2828,"MAP_RUSTBORO_CITY_HOUSE3":2832,"MAP_RUSTBORO_CITY_MART":2823,"MAP_RUSTBORO_CITY_POKEMON_CENTER_1F":2821,"MAP_RUSTBORO_CITY_POKEMON_CENTER_2F":2822,"MAP_RUSTBORO_CITY_POKEMON_SCHOOL":2820,"MAP_RUSTURF_TUNNEL":6148,"MAP_SAFARI_ZONE_NORTH":6657,"MAP_SAFARI_ZONE_NORTHEAST":6668,"MAP_SAFARI_ZONE_NORTHWEST":6656,"MAP_SAFARI_ZONE_REST_HOUSE":6667,"MAP_SAFARI_ZONE_SOUTH":6659,"MAP_SAFARI_ZONE_SOUTHEAST":6669,"MAP_SAFARI_ZONE_SOUTHWEST":6658,"MAP_SCORCHED_SLAB":6217,"MAP_SEAFLOOR_CAVERN_ENTRANCE":6171,"MAP_SEAFLOOR_CAVERN_ROOM1":6172,"MAP_SEAFLOOR_CAVERN_ROOM2":6173,"MAP_SEAFLOOR_CAVERN_ROOM3":6174,"MAP_SEAFLOOR_CAVERN_ROOM4":6175,"MAP_SEAFLOOR_CAVERN_ROOM5":6176,"MAP_SEAFLOOR_CAVERN_ROOM6":6177,"MAP_SEAFLOOR_CAVERN_ROOM7":6178,"MAP_SEAFLOOR_CAVERN_ROOM8":6179,"MAP_SEAFLOOR_CAVERN_ROOM9":6180,"MAP_SEALED_CHAMBER_INNER_ROOM":6216,"MAP_SEALED_CHAMBER_OUTER_ROOM":6215,"MAP_SECRET_BASE_BLUE_CAVE1":6402,"MAP_SECRET_BASE_BLUE_CAVE2":6408,"MAP_SECRET_BASE_BLUE_CAVE3":6414,"MAP_SECRET_BASE_BLUE_CAVE4":6420,"MAP_SECRET_BASE_BROWN_CAVE1":6401,"MAP_SECRET_BASE_BROWN_CAVE2":6407,"MAP_SECRET_BASE_BROWN_CAVE3":6413,"MAP_SECRET_BASE_BROWN_CAVE4":6419,"MAP_SECRET_BASE_RED_CAVE1":6400,"MAP_SECRET_BASE_RED_CAVE2":6406,"MAP_SECRET_BASE_RED_CAVE3":6412,"MAP_SECRET_BASE_RED_CAVE4":6418,"MAP_SECRET_BASE_SHRUB1":6405,"MAP_SECRET_BASE_SHRUB2":6411,"MAP_SECRET_BASE_SHRUB3":6417,"MAP_SECRET_BASE_SHRUB4":6423,"MAP_SECRET_BASE_TREE1":6404,"MAP_SECRET_BASE_TREE2":6410,"MAP_SECRET_BASE_TREE3":6416,"MAP_SECRET_BASE_TREE4":6422,"MAP_SECRET_BASE_YELLOW_CAVE1":6403,"MAP_SECRET_BASE_YELLOW_CAVE2":6409,"MAP_SECRET_BASE_YELLOW_CAVE3":6415,"MAP_SECRET_BASE_YELLOW_CAVE4":6421,"MAP_SHOAL_CAVE_HIGH_TIDE_ENTRANCE_ROOM":6194,"MAP_SHOAL_CAVE_HIGH_TIDE_INNER_ROOM":6195,"MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM":6190,"MAP_SHOAL_CAVE_LOW_TIDE_ICE_ROOM":6227,"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM":6191,"MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM":6193,"MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM":6192,"MAP_SKY_PILLAR_1F":6223,"MAP_SKY_PILLAR_2F":6224,"MAP_SKY_PILLAR_3F":6225,"MAP_SKY_PILLAR_4F":6226,"MAP_SKY_PILLAR_5F":6228,"MAP_SKY_PILLAR_ENTRANCE":6221,"MAP_SKY_PILLAR_OUTSIDE":6222,"MAP_SKY_PILLAR_TOP":6229,"MAP_SLATEPORT_CITY":1,"MAP_SLATEPORT_CITY_BATTLE_TENT_BATTLE_ROOM":2308,"MAP_SLATEPORT_CITY_BATTLE_TENT_CORRIDOR":2307,"MAP_SLATEPORT_CITY_BATTLE_TENT_LOBBY":2306,"MAP_SLATEPORT_CITY_HARBOR":2313,"MAP_SLATEPORT_CITY_HOUSE":2314,"MAP_SLATEPORT_CITY_MART":2317,"MAP_SLATEPORT_CITY_NAME_RATERS_HOUSE":2309,"MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F":2311,"MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_2F":2312,"MAP_SLATEPORT_CITY_POKEMON_CENTER_1F":2315,"MAP_SLATEPORT_CITY_POKEMON_CENTER_2F":2316,"MAP_SLATEPORT_CITY_POKEMON_FAN_CLUB":2310,"MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F":2304,"MAP_SLATEPORT_CITY_STERNS_SHIPYARD_2F":2305,"MAP_SOOTOPOLIS_CITY":7,"MAP_SOOTOPOLIS_CITY_GYM_1F":3840,"MAP_SOOTOPOLIS_CITY_GYM_B1F":3841,"MAP_SOOTOPOLIS_CITY_HOUSE1":3845,"MAP_SOOTOPOLIS_CITY_HOUSE2":3846,"MAP_SOOTOPOLIS_CITY_HOUSE3":3847,"MAP_SOOTOPOLIS_CITY_HOUSE4":3848,"MAP_SOOTOPOLIS_CITY_HOUSE5":3849,"MAP_SOOTOPOLIS_CITY_HOUSE6":3850,"MAP_SOOTOPOLIS_CITY_HOUSE7":3851,"MAP_SOOTOPOLIS_CITY_LOTAD_AND_SEEDOT_HOUSE":3852,"MAP_SOOTOPOLIS_CITY_MART":3844,"MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F":3853,"MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_B1F":3854,"MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F":3842,"MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F":3843,"MAP_SOUTHERN_ISLAND_EXTERIOR":6665,"MAP_SOUTHERN_ISLAND_INTERIOR":6666,"MAP_SS_TIDAL_CORRIDOR":6441,"MAP_SS_TIDAL_LOWER_DECK":6442,"MAP_SS_TIDAL_ROOMS":6443,"MAP_TERRA_CAVE_END":6249,"MAP_TERRA_CAVE_ENTRANCE":6248,"MAP_TRADE_CENTER":6425,"MAP_TRAINER_HILL_1F":6717,"MAP_TRAINER_HILL_2F":6718,"MAP_TRAINER_HILL_3F":6719,"MAP_TRAINER_HILL_4F":6720,"MAP_TRAINER_HILL_ELEVATOR":6744,"MAP_TRAINER_HILL_ENTRANCE":6716,"MAP_TRAINER_HILL_ROOF":6721,"MAP_UNDERWATER_MARINE_CAVE":6245,"MAP_UNDERWATER_ROUTE105":55,"MAP_UNDERWATER_ROUTE124":50,"MAP_UNDERWATER_ROUTE125":56,"MAP_UNDERWATER_ROUTE126":51,"MAP_UNDERWATER_ROUTE127":52,"MAP_UNDERWATER_ROUTE128":53,"MAP_UNDERWATER_ROUTE129":54,"MAP_UNDERWATER_ROUTE134":6213,"MAP_UNDERWATER_SEAFLOOR_CAVERN":6170,"MAP_UNDERWATER_SEALED_CHAMBER":6214,"MAP_UNDERWATER_SOOTOPOLIS_CITY":6149,"MAP_UNION_ROOM":6460,"MAP_UNUSED_CONTEST_HALL1":6429,"MAP_UNUSED_CONTEST_HALL2":6430,"MAP_UNUSED_CONTEST_HALL3":6431,"MAP_UNUSED_CONTEST_HALL4":6432,"MAP_UNUSED_CONTEST_HALL5":6433,"MAP_UNUSED_CONTEST_HALL6":6434,"MAP_VERDANTURF_TOWN":14,"MAP_VERDANTURF_TOWN_BATTLE_TENT_BATTLE_ROOM":1538,"MAP_VERDANTURF_TOWN_BATTLE_TENT_CORRIDOR":1537,"MAP_VERDANTURF_TOWN_BATTLE_TENT_LOBBY":1536,"MAP_VERDANTURF_TOWN_FRIENDSHIP_RATERS_HOUSE":1543,"MAP_VERDANTURF_TOWN_HOUSE":1544,"MAP_VERDANTURF_TOWN_MART":1539,"MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F":1540,"MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F":1541,"MAP_VERDANTURF_TOWN_WANDAS_HOUSE":1542,"MAP_VICTORY_ROAD_1F":6187,"MAP_VICTORY_ROAD_B1F":6188,"MAP_VICTORY_ROAD_B2F":6189,"MAX_BAG_ITEM_CAPACITY":99,"MAX_BERRY_CAPACITY":999,"MAX_BERRY_INDEX":178,"MAX_ITEM_DIGITS":3,"MAX_PC_ITEM_CAPACITY":999,"MAX_TRAINERS_COUNT":864,"MOVES_COUNT":355,"MOVE_ABSORB":71,"MOVE_ACID":51,"MOVE_ACID_ARMOR":151,"MOVE_AERIAL_ACE":332,"MOVE_AEROBLAST":177,"MOVE_AGILITY":97,"MOVE_AIR_CUTTER":314,"MOVE_AMNESIA":133,"MOVE_ANCIENT_POWER":246,"MOVE_ARM_THRUST":292,"MOVE_AROMATHERAPY":312,"MOVE_ASSIST":274,"MOVE_ASTONISH":310,"MOVE_ATTRACT":213,"MOVE_AURORA_BEAM":62,"MOVE_BARRAGE":140,"MOVE_BARRIER":112,"MOVE_BATON_PASS":226,"MOVE_BEAT_UP":251,"MOVE_BELLY_DRUM":187,"MOVE_BIDE":117,"MOVE_BIND":20,"MOVE_BITE":44,"MOVE_BLAST_BURN":307,"MOVE_BLAZE_KICK":299,"MOVE_BLIZZARD":59,"MOVE_BLOCK":335,"MOVE_BODY_SLAM":34,"MOVE_BONEMERANG":155,"MOVE_BONE_CLUB":125,"MOVE_BONE_RUSH":198,"MOVE_BOUNCE":340,"MOVE_BRICK_BREAK":280,"MOVE_BUBBLE":145,"MOVE_BUBBLE_BEAM":61,"MOVE_BULK_UP":339,"MOVE_BULLET_SEED":331,"MOVE_CALM_MIND":347,"MOVE_CAMOUFLAGE":293,"MOVE_CHARGE":268,"MOVE_CHARM":204,"MOVE_CLAMP":128,"MOVE_COMET_PUNCH":4,"MOVE_CONFUSE_RAY":109,"MOVE_CONFUSION":93,"MOVE_CONSTRICT":132,"MOVE_CONVERSION":160,"MOVE_CONVERSION_2":176,"MOVE_COSMIC_POWER":322,"MOVE_COTTON_SPORE":178,"MOVE_COUNTER":68,"MOVE_COVET":343,"MOVE_CRABHAMMER":152,"MOVE_CROSS_CHOP":238,"MOVE_CRUNCH":242,"MOVE_CRUSH_CLAW":306,"MOVE_CURSE":174,"MOVE_CUT":15,"MOVE_DEFENSE_CURL":111,"MOVE_DESTINY_BOND":194,"MOVE_DETECT":197,"MOVE_DIG":91,"MOVE_DISABLE":50,"MOVE_DIVE":291,"MOVE_DIZZY_PUNCH":146,"MOVE_DOOM_DESIRE":353,"MOVE_DOUBLE_EDGE":38,"MOVE_DOUBLE_KICK":24,"MOVE_DOUBLE_SLAP":3,"MOVE_DOUBLE_TEAM":104,"MOVE_DRAGON_BREATH":225,"MOVE_DRAGON_CLAW":337,"MOVE_DRAGON_DANCE":349,"MOVE_DRAGON_RAGE":82,"MOVE_DREAM_EATER":138,"MOVE_DRILL_PECK":65,"MOVE_DYNAMIC_PUNCH":223,"MOVE_EARTHQUAKE":89,"MOVE_EGG_BOMB":121,"MOVE_EMBER":52,"MOVE_ENCORE":227,"MOVE_ENDEAVOR":283,"MOVE_ENDURE":203,"MOVE_ERUPTION":284,"MOVE_EXPLOSION":153,"MOVE_EXTRASENSORY":326,"MOVE_EXTREME_SPEED":245,"MOVE_FACADE":263,"MOVE_FAINT_ATTACK":185,"MOVE_FAKE_OUT":252,"MOVE_FAKE_TEARS":313,"MOVE_FALSE_SWIPE":206,"MOVE_FEATHER_DANCE":297,"MOVE_FIRE_BLAST":126,"MOVE_FIRE_PUNCH":7,"MOVE_FIRE_SPIN":83,"MOVE_FISSURE":90,"MOVE_FLAIL":175,"MOVE_FLAMETHROWER":53,"MOVE_FLAME_WHEEL":172,"MOVE_FLASH":148,"MOVE_FLATTER":260,"MOVE_FLY":19,"MOVE_FOCUS_ENERGY":116,"MOVE_FOCUS_PUNCH":264,"MOVE_FOLLOW_ME":266,"MOVE_FORESIGHT":193,"MOVE_FRENZY_PLANT":338,"MOVE_FRUSTRATION":218,"MOVE_FURY_ATTACK":31,"MOVE_FURY_CUTTER":210,"MOVE_FURY_SWIPES":154,"MOVE_FUTURE_SIGHT":248,"MOVE_GIGA_DRAIN":202,"MOVE_GLARE":137,"MOVE_GRASS_WHISTLE":320,"MOVE_GROWL":45,"MOVE_GROWTH":74,"MOVE_GRUDGE":288,"MOVE_GUILLOTINE":12,"MOVE_GUST":16,"MOVE_HAIL":258,"MOVE_HARDEN":106,"MOVE_HAZE":114,"MOVE_HEADBUTT":29,"MOVE_HEAL_BELL":215,"MOVE_HEAT_WAVE":257,"MOVE_HELPING_HAND":270,"MOVE_HIDDEN_POWER":237,"MOVE_HI_JUMP_KICK":136,"MOVE_HORN_ATTACK":30,"MOVE_HORN_DRILL":32,"MOVE_HOWL":336,"MOVE_HYDRO_CANNON":308,"MOVE_HYDRO_PUMP":56,"MOVE_HYPER_BEAM":63,"MOVE_HYPER_FANG":158,"MOVE_HYPER_VOICE":304,"MOVE_HYPNOSIS":95,"MOVE_ICE_BALL":301,"MOVE_ICE_BEAM":58,"MOVE_ICE_PUNCH":8,"MOVE_ICICLE_SPEAR":333,"MOVE_ICY_WIND":196,"MOVE_IMPRISON":286,"MOVE_INGRAIN":275,"MOVE_IRON_DEFENSE":334,"MOVE_IRON_TAIL":231,"MOVE_JUMP_KICK":26,"MOVE_KARATE_CHOP":2,"MOVE_KINESIS":134,"MOVE_KNOCK_OFF":282,"MOVE_LEAF_BLADE":348,"MOVE_LEECH_LIFE":141,"MOVE_LEECH_SEED":73,"MOVE_LEER":43,"MOVE_LICK":122,"MOVE_LIGHT_SCREEN":113,"MOVE_LOCK_ON":199,"MOVE_LOVELY_KISS":142,"MOVE_LOW_KICK":67,"MOVE_LUSTER_PURGE":295,"MOVE_MACH_PUNCH":183,"MOVE_MAGICAL_LEAF":345,"MOVE_MAGIC_COAT":277,"MOVE_MAGNITUDE":222,"MOVE_MEAN_LOOK":212,"MOVE_MEDITATE":96,"MOVE_MEGAHORN":224,"MOVE_MEGA_DRAIN":72,"MOVE_MEGA_KICK":25,"MOVE_MEGA_PUNCH":5,"MOVE_MEMENTO":262,"MOVE_METAL_CLAW":232,"MOVE_METAL_SOUND":319,"MOVE_METEOR_MASH":309,"MOVE_METRONOME":118,"MOVE_MILK_DRINK":208,"MOVE_MIMIC":102,"MOVE_MIND_READER":170,"MOVE_MINIMIZE":107,"MOVE_MIRROR_COAT":243,"MOVE_MIRROR_MOVE":119,"MOVE_MIST":54,"MOVE_MIST_BALL":296,"MOVE_MOONLIGHT":236,"MOVE_MORNING_SUN":234,"MOVE_MUDDY_WATER":330,"MOVE_MUD_SHOT":341,"MOVE_MUD_SLAP":189,"MOVE_MUD_SPORT":300,"MOVE_NATURE_POWER":267,"MOVE_NEEDLE_ARM":302,"MOVE_NIGHTMARE":171,"MOVE_NIGHT_SHADE":101,"MOVE_NONE":0,"MOVE_OCTAZOOKA":190,"MOVE_ODOR_SLEUTH":316,"MOVE_OUTRAGE":200,"MOVE_OVERHEAT":315,"MOVE_PAIN_SPLIT":220,"MOVE_PAY_DAY":6,"MOVE_PECK":64,"MOVE_PERISH_SONG":195,"MOVE_PETAL_DANCE":80,"MOVE_PIN_MISSILE":42,"MOVE_POISON_FANG":305,"MOVE_POISON_GAS":139,"MOVE_POISON_POWDER":77,"MOVE_POISON_STING":40,"MOVE_POISON_TAIL":342,"MOVE_POUND":1,"MOVE_POWDER_SNOW":181,"MOVE_PRESENT":217,"MOVE_PROTECT":182,"MOVE_PSYBEAM":60,"MOVE_PSYCHIC":94,"MOVE_PSYCHO_BOOST":354,"MOVE_PSYCH_UP":244,"MOVE_PSYWAVE":149,"MOVE_PURSUIT":228,"MOVE_QUICK_ATTACK":98,"MOVE_RAGE":99,"MOVE_RAIN_DANCE":240,"MOVE_RAPID_SPIN":229,"MOVE_RAZOR_LEAF":75,"MOVE_RAZOR_WIND":13,"MOVE_RECOVER":105,"MOVE_RECYCLE":278,"MOVE_REFLECT":115,"MOVE_REFRESH":287,"MOVE_REST":156,"MOVE_RETURN":216,"MOVE_REVENGE":279,"MOVE_REVERSAL":179,"MOVE_ROAR":46,"MOVE_ROCK_BLAST":350,"MOVE_ROCK_SLIDE":157,"MOVE_ROCK_SMASH":249,"MOVE_ROCK_THROW":88,"MOVE_ROCK_TOMB":317,"MOVE_ROLE_PLAY":272,"MOVE_ROLLING_KICK":27,"MOVE_ROLLOUT":205,"MOVE_SACRED_FIRE":221,"MOVE_SAFEGUARD":219,"MOVE_SANDSTORM":201,"MOVE_SAND_ATTACK":28,"MOVE_SAND_TOMB":328,"MOVE_SCARY_FACE":184,"MOVE_SCRATCH":10,"MOVE_SCREECH":103,"MOVE_SECRET_POWER":290,"MOVE_SEISMIC_TOSS":69,"MOVE_SELF_DESTRUCT":120,"MOVE_SHADOW_BALL":247,"MOVE_SHADOW_PUNCH":325,"MOVE_SHARPEN":159,"MOVE_SHEER_COLD":329,"MOVE_SHOCK_WAVE":351,"MOVE_SIGNAL_BEAM":324,"MOVE_SILVER_WIND":318,"MOVE_SING":47,"MOVE_SKETCH":166,"MOVE_SKILL_SWAP":285,"MOVE_SKULL_BASH":130,"MOVE_SKY_ATTACK":143,"MOVE_SKY_UPPERCUT":327,"MOVE_SLACK_OFF":303,"MOVE_SLAM":21,"MOVE_SLASH":163,"MOVE_SLEEP_POWDER":79,"MOVE_SLEEP_TALK":214,"MOVE_SLUDGE":124,"MOVE_SLUDGE_BOMB":188,"MOVE_SMELLING_SALT":265,"MOVE_SMOG":123,"MOVE_SMOKESCREEN":108,"MOVE_SNATCH":289,"MOVE_SNORE":173,"MOVE_SOFT_BOILED":135,"MOVE_SOLAR_BEAM":76,"MOVE_SONIC_BOOM":49,"MOVE_SPARK":209,"MOVE_SPIDER_WEB":169,"MOVE_SPIKES":191,"MOVE_SPIKE_CANNON":131,"MOVE_SPITE":180,"MOVE_SPIT_UP":255,"MOVE_SPLASH":150,"MOVE_SPORE":147,"MOVE_STEEL_WING":211,"MOVE_STOCKPILE":254,"MOVE_STOMP":23,"MOVE_STRENGTH":70,"MOVE_STRING_SHOT":81,"MOVE_STRUGGLE":165,"MOVE_STUN_SPORE":78,"MOVE_SUBMISSION":66,"MOVE_SUBSTITUTE":164,"MOVE_SUNNY_DAY":241,"MOVE_SUPERPOWER":276,"MOVE_SUPERSONIC":48,"MOVE_SUPER_FANG":162,"MOVE_SURF":57,"MOVE_SWAGGER":207,"MOVE_SWALLOW":256,"MOVE_SWEET_KISS":186,"MOVE_SWEET_SCENT":230,"MOVE_SWIFT":129,"MOVE_SWORDS_DANCE":14,"MOVE_SYNTHESIS":235,"MOVE_TACKLE":33,"MOVE_TAIL_GLOW":294,"MOVE_TAIL_WHIP":39,"MOVE_TAKE_DOWN":36,"MOVE_TAUNT":269,"MOVE_TEETER_DANCE":298,"MOVE_TELEPORT":100,"MOVE_THIEF":168,"MOVE_THRASH":37,"MOVE_THUNDER":87,"MOVE_THUNDERBOLT":85,"MOVE_THUNDER_PUNCH":9,"MOVE_THUNDER_SHOCK":84,"MOVE_THUNDER_WAVE":86,"MOVE_TICKLE":321,"MOVE_TORMENT":259,"MOVE_TOXIC":92,"MOVE_TRANSFORM":144,"MOVE_TRICK":271,"MOVE_TRIPLE_KICK":167,"MOVE_TRI_ATTACK":161,"MOVE_TWINEEDLE":41,"MOVE_TWISTER":239,"MOVE_UNAVAILABLE":65535,"MOVE_UPROAR":253,"MOVE_VICE_GRIP":11,"MOVE_VINE_WHIP":22,"MOVE_VITAL_THROW":233,"MOVE_VOLT_TACKLE":344,"MOVE_WATERFALL":127,"MOVE_WATER_GUN":55,"MOVE_WATER_PULSE":352,"MOVE_WATER_SPORT":346,"MOVE_WATER_SPOUT":323,"MOVE_WEATHER_BALL":311,"MOVE_WHIRLPOOL":250,"MOVE_WHIRLWIND":18,"MOVE_WILL_O_WISP":261,"MOVE_WING_ATTACK":17,"MOVE_WISH":273,"MOVE_WITHDRAW":110,"MOVE_WRAP":35,"MOVE_YAWN":281,"MOVE_ZAP_CANNON":192,"NUM_BADGES":8,"NUM_BERRY_MASTER_BERRIES":10,"NUM_BERRY_MASTER_BERRIES_SKIPPED":20,"NUM_BERRY_MASTER_WIFE_BERRIES":10,"NUM_HIDDEN_MACHINES":8,"NUM_KIRI_BERRIES":10,"NUM_KIRI_BERRIES_SKIPPED":20,"NUM_ROUTE_114_MAN_BERRIES":5,"NUM_ROUTE_114_MAN_BERRIES_SKIPPED":15,"NUM_SPECIES":412,"NUM_TECHNICAL_MACHINES":50,"NUM_WONDER_CARD_FLAGS":20,"OLD_ROD":0,"SPECIAL_FLAGS_END":16511,"SPECIAL_FLAGS_START":16384,"SPECIES_ABRA":63,"SPECIES_ABSOL":376,"SPECIES_AERODACTYL":142,"SPECIES_AGGRON":384,"SPECIES_AIPOM":190,"SPECIES_ALAKAZAM":65,"SPECIES_ALTARIA":359,"SPECIES_AMPHAROS":181,"SPECIES_ANORITH":390,"SPECIES_ARBOK":24,"SPECIES_ARCANINE":59,"SPECIES_ARIADOS":168,"SPECIES_ARMALDO":391,"SPECIES_ARON":382,"SPECIES_ARTICUNO":144,"SPECIES_AZUMARILL":184,"SPECIES_AZURILL":350,"SPECIES_BAGON":395,"SPECIES_BALTOY":318,"SPECIES_BANETTE":378,"SPECIES_BARBOACH":323,"SPECIES_BAYLEEF":153,"SPECIES_BEAUTIFLY":292,"SPECIES_BEEDRILL":15,"SPECIES_BELDUM":398,"SPECIES_BELLOSSOM":182,"SPECIES_BELLSPROUT":69,"SPECIES_BLASTOISE":9,"SPECIES_BLAZIKEN":282,"SPECIES_BLISSEY":242,"SPECIES_BRELOOM":307,"SPECIES_BULBASAUR":1,"SPECIES_BUTTERFREE":12,"SPECIES_CACNEA":344,"SPECIES_CACTURNE":345,"SPECIES_CAMERUPT":340,"SPECIES_CARVANHA":330,"SPECIES_CASCOON":293,"SPECIES_CASTFORM":385,"SPECIES_CATERPIE":10,"SPECIES_CELEBI":251,"SPECIES_CHANSEY":113,"SPECIES_CHARIZARD":6,"SPECIES_CHARMANDER":4,"SPECIES_CHARMELEON":5,"SPECIES_CHIKORITA":152,"SPECIES_CHIMECHO":411,"SPECIES_CHINCHOU":170,"SPECIES_CLAMPERL":373,"SPECIES_CLAYDOL":319,"SPECIES_CLEFABLE":36,"SPECIES_CLEFAIRY":35,"SPECIES_CLEFFA":173,"SPECIES_CLOYSTER":91,"SPECIES_COMBUSKEN":281,"SPECIES_CORPHISH":326,"SPECIES_CORSOLA":222,"SPECIES_CRADILY":389,"SPECIES_CRAWDAUNT":327,"SPECIES_CROBAT":169,"SPECIES_CROCONAW":159,"SPECIES_CUBONE":104,"SPECIES_CYNDAQUIL":155,"SPECIES_DELCATTY":316,"SPECIES_DELIBIRD":225,"SPECIES_DEOXYS":410,"SPECIES_DEWGONG":87,"SPECIES_DIGLETT":50,"SPECIES_DITTO":132,"SPECIES_DODRIO":85,"SPECIES_DODUO":84,"SPECIES_DONPHAN":232,"SPECIES_DRAGONAIR":148,"SPECIES_DRAGONITE":149,"SPECIES_DRATINI":147,"SPECIES_DROWZEE":96,"SPECIES_DUGTRIO":51,"SPECIES_DUNSPARCE":206,"SPECIES_DUSCLOPS":362,"SPECIES_DUSKULL":361,"SPECIES_DUSTOX":294,"SPECIES_EEVEE":133,"SPECIES_EGG":412,"SPECIES_EKANS":23,"SPECIES_ELECTABUZZ":125,"SPECIES_ELECTRIKE":337,"SPECIES_ELECTRODE":101,"SPECIES_ELEKID":239,"SPECIES_ENTEI":244,"SPECIES_ESPEON":196,"SPECIES_EXEGGCUTE":102,"SPECIES_EXEGGUTOR":103,"SPECIES_EXPLOUD":372,"SPECIES_FARFETCHD":83,"SPECIES_FEAROW":22,"SPECIES_FEEBAS":328,"SPECIES_FERALIGATR":160,"SPECIES_FLAAFFY":180,"SPECIES_FLAREON":136,"SPECIES_FLYGON":334,"SPECIES_FORRETRESS":205,"SPECIES_FURRET":162,"SPECIES_GARDEVOIR":394,"SPECIES_GASTLY":92,"SPECIES_GENGAR":94,"SPECIES_GEODUDE":74,"SPECIES_GIRAFARIG":203,"SPECIES_GLALIE":347,"SPECIES_GLIGAR":207,"SPECIES_GLOOM":44,"SPECIES_GOLBAT":42,"SPECIES_GOLDEEN":118,"SPECIES_GOLDUCK":55,"SPECIES_GOLEM":76,"SPECIES_GOREBYSS":375,"SPECIES_GRANBULL":210,"SPECIES_GRAVELER":75,"SPECIES_GRIMER":88,"SPECIES_GROUDON":405,"SPECIES_GROVYLE":278,"SPECIES_GROWLITHE":58,"SPECIES_GRUMPIG":352,"SPECIES_GULPIN":367,"SPECIES_GYARADOS":130,"SPECIES_HARIYAMA":336,"SPECIES_HAUNTER":93,"SPECIES_HERACROSS":214,"SPECIES_HITMONCHAN":107,"SPECIES_HITMONLEE":106,"SPECIES_HITMONTOP":237,"SPECIES_HOOTHOOT":163,"SPECIES_HOPPIP":187,"SPECIES_HORSEA":116,"SPECIES_HOUNDOOM":229,"SPECIES_HOUNDOUR":228,"SPECIES_HO_OH":250,"SPECIES_HUNTAIL":374,"SPECIES_HYPNO":97,"SPECIES_IGGLYBUFF":174,"SPECIES_ILLUMISE":387,"SPECIES_IVYSAUR":2,"SPECIES_JIGGLYPUFF":39,"SPECIES_JIRACHI":409,"SPECIES_JOLTEON":135,"SPECIES_JUMPLUFF":189,"SPECIES_JYNX":124,"SPECIES_KABUTO":140,"SPECIES_KABUTOPS":141,"SPECIES_KADABRA":64,"SPECIES_KAKUNA":14,"SPECIES_KANGASKHAN":115,"SPECIES_KECLEON":317,"SPECIES_KINGDRA":230,"SPECIES_KINGLER":99,"SPECIES_KIRLIA":393,"SPECIES_KOFFING":109,"SPECIES_KRABBY":98,"SPECIES_KYOGRE":404,"SPECIES_LAIRON":383,"SPECIES_LANTURN":171,"SPECIES_LAPRAS":131,"SPECIES_LARVITAR":246,"SPECIES_LATIAS":407,"SPECIES_LATIOS":408,"SPECIES_LEDIAN":166,"SPECIES_LEDYBA":165,"SPECIES_LICKITUNG":108,"SPECIES_LILEEP":388,"SPECIES_LINOONE":289,"SPECIES_LOMBRE":296,"SPECIES_LOTAD":295,"SPECIES_LOUDRED":371,"SPECIES_LUDICOLO":297,"SPECIES_LUGIA":249,"SPECIES_LUNATONE":348,"SPECIES_LUVDISC":325,"SPECIES_MACHAMP":68,"SPECIES_MACHOKE":67,"SPECIES_MACHOP":66,"SPECIES_MAGBY":240,"SPECIES_MAGCARGO":219,"SPECIES_MAGIKARP":129,"SPECIES_MAGMAR":126,"SPECIES_MAGNEMITE":81,"SPECIES_MAGNETON":82,"SPECIES_MAKUHITA":335,"SPECIES_MANECTRIC":338,"SPECIES_MANKEY":56,"SPECIES_MANTINE":226,"SPECIES_MAREEP":179,"SPECIES_MARILL":183,"SPECIES_MAROWAK":105,"SPECIES_MARSHTOMP":284,"SPECIES_MASQUERAIN":312,"SPECIES_MAWILE":355,"SPECIES_MEDICHAM":357,"SPECIES_MEDITITE":356,"SPECIES_MEGANIUM":154,"SPECIES_MEOWTH":52,"SPECIES_METAGROSS":400,"SPECIES_METANG":399,"SPECIES_METAPOD":11,"SPECIES_MEW":151,"SPECIES_MEWTWO":150,"SPECIES_MIGHTYENA":287,"SPECIES_MILOTIC":329,"SPECIES_MILTANK":241,"SPECIES_MINUN":354,"SPECIES_MISDREAVUS":200,"SPECIES_MOLTRES":146,"SPECIES_MR_MIME":122,"SPECIES_MUDKIP":283,"SPECIES_MUK":89,"SPECIES_MURKROW":198,"SPECIES_NATU":177,"SPECIES_NIDOKING":34,"SPECIES_NIDOQUEEN":31,"SPECIES_NIDORAN_F":29,"SPECIES_NIDORAN_M":32,"SPECIES_NIDORINA":30,"SPECIES_NIDORINO":33,"SPECIES_NINCADA":301,"SPECIES_NINETALES":38,"SPECIES_NINJASK":302,"SPECIES_NOCTOWL":164,"SPECIES_NONE":0,"SPECIES_NOSEPASS":320,"SPECIES_NUMEL":339,"SPECIES_NUZLEAF":299,"SPECIES_OCTILLERY":224,"SPECIES_ODDISH":43,"SPECIES_OLD_UNOWN_B":252,"SPECIES_OLD_UNOWN_C":253,"SPECIES_OLD_UNOWN_D":254,"SPECIES_OLD_UNOWN_E":255,"SPECIES_OLD_UNOWN_F":256,"SPECIES_OLD_UNOWN_G":257,"SPECIES_OLD_UNOWN_H":258,"SPECIES_OLD_UNOWN_I":259,"SPECIES_OLD_UNOWN_J":260,"SPECIES_OLD_UNOWN_K":261,"SPECIES_OLD_UNOWN_L":262,"SPECIES_OLD_UNOWN_M":263,"SPECIES_OLD_UNOWN_N":264,"SPECIES_OLD_UNOWN_O":265,"SPECIES_OLD_UNOWN_P":266,"SPECIES_OLD_UNOWN_Q":267,"SPECIES_OLD_UNOWN_R":268,"SPECIES_OLD_UNOWN_S":269,"SPECIES_OLD_UNOWN_T":270,"SPECIES_OLD_UNOWN_U":271,"SPECIES_OLD_UNOWN_V":272,"SPECIES_OLD_UNOWN_W":273,"SPECIES_OLD_UNOWN_X":274,"SPECIES_OLD_UNOWN_Y":275,"SPECIES_OLD_UNOWN_Z":276,"SPECIES_OMANYTE":138,"SPECIES_OMASTAR":139,"SPECIES_ONIX":95,"SPECIES_PARAS":46,"SPECIES_PARASECT":47,"SPECIES_PELIPPER":310,"SPECIES_PERSIAN":53,"SPECIES_PHANPY":231,"SPECIES_PICHU":172,"SPECIES_PIDGEOT":18,"SPECIES_PIDGEOTTO":17,"SPECIES_PIDGEY":16,"SPECIES_PIKACHU":25,"SPECIES_PILOSWINE":221,"SPECIES_PINECO":204,"SPECIES_PINSIR":127,"SPECIES_PLUSLE":353,"SPECIES_POLITOED":186,"SPECIES_POLIWAG":60,"SPECIES_POLIWHIRL":61,"SPECIES_POLIWRATH":62,"SPECIES_PONYTA":77,"SPECIES_POOCHYENA":286,"SPECIES_PORYGON":137,"SPECIES_PORYGON2":233,"SPECIES_PRIMEAPE":57,"SPECIES_PSYDUCK":54,"SPECIES_PUPITAR":247,"SPECIES_QUAGSIRE":195,"SPECIES_QUILAVA":156,"SPECIES_QWILFISH":211,"SPECIES_RAICHU":26,"SPECIES_RAIKOU":243,"SPECIES_RALTS":392,"SPECIES_RAPIDASH":78,"SPECIES_RATICATE":20,"SPECIES_RATTATA":19,"SPECIES_RAYQUAZA":406,"SPECIES_REGICE":402,"SPECIES_REGIROCK":401,"SPECIES_REGISTEEL":403,"SPECIES_RELICANTH":381,"SPECIES_REMORAID":223,"SPECIES_RHYDON":112,"SPECIES_RHYHORN":111,"SPECIES_ROSELIA":363,"SPECIES_SABLEYE":322,"SPECIES_SALAMENCE":397,"SPECIES_SANDSHREW":27,"SPECIES_SANDSLASH":28,"SPECIES_SCEPTILE":279,"SPECIES_SCIZOR":212,"SPECIES_SCYTHER":123,"SPECIES_SEADRA":117,"SPECIES_SEAKING":119,"SPECIES_SEALEO":342,"SPECIES_SEEDOT":298,"SPECIES_SEEL":86,"SPECIES_SENTRET":161,"SPECIES_SEVIPER":379,"SPECIES_SHARPEDO":331,"SPECIES_SHEDINJA":303,"SPECIES_SHELGON":396,"SPECIES_SHELLDER":90,"SPECIES_SHIFTRY":300,"SPECIES_SHROOMISH":306,"SPECIES_SHUCKLE":213,"SPECIES_SHUPPET":377,"SPECIES_SILCOON":291,"SPECIES_SKARMORY":227,"SPECIES_SKIPLOOM":188,"SPECIES_SKITTY":315,"SPECIES_SLAKING":366,"SPECIES_SLAKOTH":364,"SPECIES_SLOWBRO":80,"SPECIES_SLOWKING":199,"SPECIES_SLOWPOKE":79,"SPECIES_SLUGMA":218,"SPECIES_SMEARGLE":235,"SPECIES_SMOOCHUM":238,"SPECIES_SNEASEL":215,"SPECIES_SNORLAX":143,"SPECIES_SNORUNT":346,"SPECIES_SNUBBULL":209,"SPECIES_SOLROCK":349,"SPECIES_SPEAROW":21,"SPECIES_SPHEAL":341,"SPECIES_SPINARAK":167,"SPECIES_SPINDA":308,"SPECIES_SPOINK":351,"SPECIES_SQUIRTLE":7,"SPECIES_STANTLER":234,"SPECIES_STARMIE":121,"SPECIES_STARYU":120,"SPECIES_STEELIX":208,"SPECIES_SUDOWOODO":185,"SPECIES_SUICUNE":245,"SPECIES_SUNFLORA":192,"SPECIES_SUNKERN":191,"SPECIES_SURSKIT":311,"SPECIES_SWABLU":358,"SPECIES_SWALOT":368,"SPECIES_SWAMPERT":285,"SPECIES_SWELLOW":305,"SPECIES_SWINUB":220,"SPECIES_TAILLOW":304,"SPECIES_TANGELA":114,"SPECIES_TAUROS":128,"SPECIES_TEDDIURSA":216,"SPECIES_TENTACOOL":72,"SPECIES_TENTACRUEL":73,"SPECIES_TOGEPI":175,"SPECIES_TOGETIC":176,"SPECIES_TORCHIC":280,"SPECIES_TORKOAL":321,"SPECIES_TOTODILE":158,"SPECIES_TRAPINCH":332,"SPECIES_TREECKO":277,"SPECIES_TROPIUS":369,"SPECIES_TYPHLOSION":157,"SPECIES_TYRANITAR":248,"SPECIES_TYROGUE":236,"SPECIES_UMBREON":197,"SPECIES_UNOWN":201,"SPECIES_UNOWN_B":413,"SPECIES_UNOWN_C":414,"SPECIES_UNOWN_D":415,"SPECIES_UNOWN_E":416,"SPECIES_UNOWN_EMARK":438,"SPECIES_UNOWN_F":417,"SPECIES_UNOWN_G":418,"SPECIES_UNOWN_H":419,"SPECIES_UNOWN_I":420,"SPECIES_UNOWN_J":421,"SPECIES_UNOWN_K":422,"SPECIES_UNOWN_L":423,"SPECIES_UNOWN_M":424,"SPECIES_UNOWN_N":425,"SPECIES_UNOWN_O":426,"SPECIES_UNOWN_P":427,"SPECIES_UNOWN_Q":428,"SPECIES_UNOWN_QMARK":439,"SPECIES_UNOWN_R":429,"SPECIES_UNOWN_S":430,"SPECIES_UNOWN_T":431,"SPECIES_UNOWN_U":432,"SPECIES_UNOWN_V":433,"SPECIES_UNOWN_W":434,"SPECIES_UNOWN_X":435,"SPECIES_UNOWN_Y":436,"SPECIES_UNOWN_Z":437,"SPECIES_URSARING":217,"SPECIES_VAPOREON":134,"SPECIES_VENOMOTH":49,"SPECIES_VENONAT":48,"SPECIES_VENUSAUR":3,"SPECIES_VIBRAVA":333,"SPECIES_VICTREEBEL":71,"SPECIES_VIGOROTH":365,"SPECIES_VILEPLUME":45,"SPECIES_VOLBEAT":386,"SPECIES_VOLTORB":100,"SPECIES_VULPIX":37,"SPECIES_WAILMER":313,"SPECIES_WAILORD":314,"SPECIES_WALREIN":343,"SPECIES_WARTORTLE":8,"SPECIES_WEEDLE":13,"SPECIES_WEEPINBELL":70,"SPECIES_WEEZING":110,"SPECIES_WHISCASH":324,"SPECIES_WHISMUR":370,"SPECIES_WIGGLYTUFF":40,"SPECIES_WINGULL":309,"SPECIES_WOBBUFFET":202,"SPECIES_WOOPER":194,"SPECIES_WURMPLE":290,"SPECIES_WYNAUT":360,"SPECIES_XATU":178,"SPECIES_YANMA":193,"SPECIES_ZANGOOSE":380,"SPECIES_ZAPDOS":145,"SPECIES_ZIGZAGOON":288,"SPECIES_ZUBAT":41,"SUPER_ROD":2,"SYSTEM_FLAGS":2144,"TEMP_FLAGS_END":31,"TEMP_FLAGS_START":0,"TRAINERS_COUNT":855,"TRAINER_AARON":397,"TRAINER_ABIGAIL_1":358,"TRAINER_ABIGAIL_2":360,"TRAINER_ABIGAIL_3":361,"TRAINER_ABIGAIL_4":362,"TRAINER_ABIGAIL_5":363,"TRAINER_AIDAN":674,"TRAINER_AISHA":757,"TRAINER_ALAN":630,"TRAINER_ALBERT":80,"TRAINER_ALBERTO":12,"TRAINER_ALEX":413,"TRAINER_ALEXA":670,"TRAINER_ALEXIA":90,"TRAINER_ALEXIS":248,"TRAINER_ALICE":448,"TRAINER_ALIX":750,"TRAINER_ALLEN":333,"TRAINER_ALLISON":387,"TRAINER_ALVARO":849,"TRAINER_ALYSSA":701,"TRAINER_AMY_AND_LIV_1":481,"TRAINER_AMY_AND_LIV_2":482,"TRAINER_AMY_AND_LIV_3":485,"TRAINER_AMY_AND_LIV_4":487,"TRAINER_AMY_AND_LIV_5":488,"TRAINER_AMY_AND_LIV_6":489,"TRAINER_ANABEL":805,"TRAINER_ANDREA":613,"TRAINER_ANDRES_1":737,"TRAINER_ANDRES_2":812,"TRAINER_ANDRES_3":813,"TRAINER_ANDRES_4":814,"TRAINER_ANDRES_5":815,"TRAINER_ANDREW":336,"TRAINER_ANGELICA":436,"TRAINER_ANGELINA":712,"TRAINER_ANGELO":802,"TRAINER_ANNA_AND_MEG_1":287,"TRAINER_ANNA_AND_MEG_2":288,"TRAINER_ANNA_AND_MEG_3":289,"TRAINER_ANNA_AND_MEG_4":290,"TRAINER_ANNA_AND_MEG_5":291,"TRAINER_ANNIKA":502,"TRAINER_ANTHONY":352,"TRAINER_ARCHIE":34,"TRAINER_ASHLEY":655,"TRAINER_ATHENA":577,"TRAINER_ATSUSHI":190,"TRAINER_AURON":506,"TRAINER_AUSTINA":58,"TRAINER_AUTUMN":217,"TRAINER_AXLE":203,"TRAINER_BARNY":343,"TRAINER_BARRY":163,"TRAINER_BEAU":212,"TRAINER_BECK":414,"TRAINER_BECKY":470,"TRAINER_BEN":323,"TRAINER_BENJAMIN_1":353,"TRAINER_BENJAMIN_2":354,"TRAINER_BENJAMIN_3":355,"TRAINER_BENJAMIN_4":356,"TRAINER_BENJAMIN_5":357,"TRAINER_BENNY":407,"TRAINER_BERKE":74,"TRAINER_BERNIE_1":206,"TRAINER_BERNIE_2":207,"TRAINER_BERNIE_3":208,"TRAINER_BERNIE_4":209,"TRAINER_BERNIE_5":210,"TRAINER_BETH":445,"TRAINER_BETHANY":301,"TRAINER_BEVERLY":441,"TRAINER_BIANCA":706,"TRAINER_BILLY":319,"TRAINER_BLAKE":235,"TRAINER_BRANDEN":745,"TRAINER_BRANDI":756,"TRAINER_BRANDON":811,"TRAINER_BRAWLY_1":266,"TRAINER_BRAWLY_2":774,"TRAINER_BRAWLY_3":775,"TRAINER_BRAWLY_4":776,"TRAINER_BRAWLY_5":777,"TRAINER_BRAXTON":75,"TRAINER_BRENDA":454,"TRAINER_BRENDAN_LILYCOVE_MUDKIP":661,"TRAINER_BRENDAN_LILYCOVE_TORCHIC":663,"TRAINER_BRENDAN_LILYCOVE_TREECKO":662,"TRAINER_BRENDAN_PLACEHOLDER":853,"TRAINER_BRENDAN_ROUTE_103_MUDKIP":520,"TRAINER_BRENDAN_ROUTE_103_TORCHIC":526,"TRAINER_BRENDAN_ROUTE_103_TREECKO":523,"TRAINER_BRENDAN_ROUTE_110_MUDKIP":521,"TRAINER_BRENDAN_ROUTE_110_TORCHIC":527,"TRAINER_BRENDAN_ROUTE_110_TREECKO":524,"TRAINER_BRENDAN_ROUTE_119_MUDKIP":522,"TRAINER_BRENDAN_ROUTE_119_TORCHIC":528,"TRAINER_BRENDAN_ROUTE_119_TREECKO":525,"TRAINER_BRENDAN_RUSTBORO_MUDKIP":593,"TRAINER_BRENDAN_RUSTBORO_TORCHIC":599,"TRAINER_BRENDAN_RUSTBORO_TREECKO":592,"TRAINER_BRENDEN":572,"TRAINER_BRENT":223,"TRAINER_BRIANNA":118,"TRAINER_BRICE":626,"TRAINER_BRIDGET":129,"TRAINER_BROOKE_1":94,"TRAINER_BROOKE_2":101,"TRAINER_BROOKE_3":102,"TRAINER_BROOKE_4":103,"TRAINER_BROOKE_5":104,"TRAINER_BRYAN":744,"TRAINER_BRYANT":746,"TRAINER_CALE":764,"TRAINER_CALLIE":763,"TRAINER_CALVIN_1":318,"TRAINER_CALVIN_2":328,"TRAINER_CALVIN_3":329,"TRAINER_CALVIN_4":330,"TRAINER_CALVIN_5":331,"TRAINER_CAMDEN":374,"TRAINER_CAMERON_1":238,"TRAINER_CAMERON_2":239,"TRAINER_CAMERON_3":240,"TRAINER_CAMERON_4":241,"TRAINER_CAMERON_5":242,"TRAINER_CAMRON":739,"TRAINER_CARLEE":464,"TRAINER_CAROL":471,"TRAINER_CAROLINA":741,"TRAINER_CAROLINE":99,"TRAINER_CARTER":345,"TRAINER_CATHERINE_1":559,"TRAINER_CATHERINE_2":562,"TRAINER_CATHERINE_3":563,"TRAINER_CATHERINE_4":564,"TRAINER_CATHERINE_5":565,"TRAINER_CEDRIC":475,"TRAINER_CELIA":743,"TRAINER_CELINA":705,"TRAINER_CHAD":174,"TRAINER_CHANDLER":698,"TRAINER_CHARLIE":66,"TRAINER_CHARLOTTE":714,"TRAINER_CHASE":378,"TRAINER_CHESTER":408,"TRAINER_CHIP":45,"TRAINER_CHRIS":693,"TRAINER_CINDY_1":114,"TRAINER_CINDY_2":117,"TRAINER_CINDY_3":120,"TRAINER_CINDY_4":121,"TRAINER_CINDY_5":122,"TRAINER_CINDY_6":123,"TRAINER_CLARENCE":580,"TRAINER_CLARISSA":435,"TRAINER_CLARK":631,"TRAINER_CLAUDE":338,"TRAINER_CLIFFORD":584,"TRAINER_COBY":709,"TRAINER_COLE":201,"TRAINER_COLIN":405,"TRAINER_COLTON":294,"TRAINER_CONNIE":128,"TRAINER_CONOR":511,"TRAINER_CORA":428,"TRAINER_CORY_1":740,"TRAINER_CORY_2":816,"TRAINER_CORY_3":817,"TRAINER_CORY_4":818,"TRAINER_CORY_5":819,"TRAINER_CRISSY":614,"TRAINER_CRISTIAN":574,"TRAINER_CRISTIN_1":767,"TRAINER_CRISTIN_2":828,"TRAINER_CRISTIN_3":829,"TRAINER_CRISTIN_4":830,"TRAINER_CRISTIN_5":831,"TRAINER_CYNDY_1":427,"TRAINER_CYNDY_2":430,"TRAINER_CYNDY_3":431,"TRAINER_CYNDY_4":432,"TRAINER_CYNDY_5":433,"TRAINER_DAISUKE":189,"TRAINER_DAISY":36,"TRAINER_DALE":341,"TRAINER_DALTON_1":196,"TRAINER_DALTON_2":197,"TRAINER_DALTON_3":198,"TRAINER_DALTON_4":199,"TRAINER_DALTON_5":200,"TRAINER_DANA":458,"TRAINER_DANIELLE":650,"TRAINER_DAPHNE":115,"TRAINER_DARCY":733,"TRAINER_DARIAN":696,"TRAINER_DARIUS":803,"TRAINER_DARRIN":154,"TRAINER_DAVID":158,"TRAINER_DAVIS":539,"TRAINER_DAWSON":694,"TRAINER_DAYTON":760,"TRAINER_DEAN":164,"TRAINER_DEANDRE":715,"TRAINER_DEBRA":460,"TRAINER_DECLAN":15,"TRAINER_DEMETRIUS":375,"TRAINER_DENISE":444,"TRAINER_DEREK":227,"TRAINER_DEVAN":753,"TRAINER_DEZ_AND_LUKE":640,"TRAINER_DIANA_1":474,"TRAINER_DIANA_2":477,"TRAINER_DIANA_3":478,"TRAINER_DIANA_4":479,"TRAINER_DIANA_5":480,"TRAINER_DIANNE":417,"TRAINER_DILLON":327,"TRAINER_DOMINIK":152,"TRAINER_DONALD":224,"TRAINER_DONNY":384,"TRAINER_DOUG":618,"TRAINER_DOUGLAS":153,"TRAINER_DRAKE":264,"TRAINER_DREW":211,"TRAINER_DUDLEY":173,"TRAINER_DUNCAN":496,"TRAINER_DUSTY_1":44,"TRAINER_DUSTY_2":47,"TRAINER_DUSTY_3":48,"TRAINER_DUSTY_4":49,"TRAINER_DUSTY_5":50,"TRAINER_DWAYNE":493,"TRAINER_DYLAN_1":364,"TRAINER_DYLAN_2":365,"TRAINER_DYLAN_3":366,"TRAINER_DYLAN_4":367,"TRAINER_DYLAN_5":368,"TRAINER_ED":13,"TRAINER_EDDIE":332,"TRAINER_EDGAR":79,"TRAINER_EDMOND":491,"TRAINER_EDWARD":232,"TRAINER_EDWARDO":404,"TRAINER_EDWIN_1":512,"TRAINER_EDWIN_2":515,"TRAINER_EDWIN_3":516,"TRAINER_EDWIN_4":517,"TRAINER_EDWIN_5":518,"TRAINER_ELI":501,"TRAINER_ELIJAH":742,"TRAINER_ELLIOT_1":339,"TRAINER_ELLIOT_2":346,"TRAINER_ELLIOT_3":347,"TRAINER_ELLIOT_4":348,"TRAINER_ELLIOT_5":349,"TRAINER_ERIC":632,"TRAINER_ERNEST_1":492,"TRAINER_ERNEST_2":497,"TRAINER_ERNEST_3":498,"TRAINER_ERNEST_4":499,"TRAINER_ERNEST_5":500,"TRAINER_ETHAN_1":216,"TRAINER_ETHAN_2":219,"TRAINER_ETHAN_3":220,"TRAINER_ETHAN_4":221,"TRAINER_ETHAN_5":222,"TRAINER_EVERETT":850,"TRAINER_FABIAN":759,"TRAINER_FELIX":38,"TRAINER_FERNANDO_1":195,"TRAINER_FERNANDO_2":832,"TRAINER_FERNANDO_3":833,"TRAINER_FERNANDO_4":834,"TRAINER_FERNANDO_5":835,"TRAINER_FLAGS_END":2143,"TRAINER_FLAGS_START":1280,"TRAINER_FLANNERY_1":268,"TRAINER_FLANNERY_2":782,"TRAINER_FLANNERY_3":783,"TRAINER_FLANNERY_4":784,"TRAINER_FLANNERY_5":785,"TRAINER_FLINT":654,"TRAINER_FOSTER":46,"TRAINER_FRANKLIN":170,"TRAINER_FREDRICK":29,"TRAINER_GABBY_AND_TY_1":51,"TRAINER_GABBY_AND_TY_2":52,"TRAINER_GABBY_AND_TY_3":53,"TRAINER_GABBY_AND_TY_4":54,"TRAINER_GABBY_AND_TY_5":55,"TRAINER_GABBY_AND_TY_6":56,"TRAINER_GABRIELLE_1":9,"TRAINER_GABRIELLE_2":840,"TRAINER_GABRIELLE_3":841,"TRAINER_GABRIELLE_4":842,"TRAINER_GABRIELLE_5":843,"TRAINER_GARRET":138,"TRAINER_GARRISON":547,"TRAINER_GEORGE":73,"TRAINER_GEORGIA":281,"TRAINER_GERALD":648,"TRAINER_GILBERT":169,"TRAINER_GINA_AND_MIA_1":483,"TRAINER_GINA_AND_MIA_2":486,"TRAINER_GLACIA":263,"TRAINER_GRACE":450,"TRAINER_GREG":619,"TRAINER_GRETA":808,"TRAINER_GRUNT_AQUA_HIDEOUT_1":2,"TRAINER_GRUNT_AQUA_HIDEOUT_2":3,"TRAINER_GRUNT_AQUA_HIDEOUT_3":4,"TRAINER_GRUNT_AQUA_HIDEOUT_4":5,"TRAINER_GRUNT_AQUA_HIDEOUT_5":27,"TRAINER_GRUNT_AQUA_HIDEOUT_6":28,"TRAINER_GRUNT_AQUA_HIDEOUT_7":192,"TRAINER_GRUNT_AQUA_HIDEOUT_8":193,"TRAINER_GRUNT_JAGGED_PASS":570,"TRAINER_GRUNT_MAGMA_HIDEOUT_1":716,"TRAINER_GRUNT_MAGMA_HIDEOUT_10":725,"TRAINER_GRUNT_MAGMA_HIDEOUT_11":726,"TRAINER_GRUNT_MAGMA_HIDEOUT_12":727,"TRAINER_GRUNT_MAGMA_HIDEOUT_13":728,"TRAINER_GRUNT_MAGMA_HIDEOUT_14":729,"TRAINER_GRUNT_MAGMA_HIDEOUT_15":730,"TRAINER_GRUNT_MAGMA_HIDEOUT_16":731,"TRAINER_GRUNT_MAGMA_HIDEOUT_2":717,"TRAINER_GRUNT_MAGMA_HIDEOUT_3":718,"TRAINER_GRUNT_MAGMA_HIDEOUT_4":719,"TRAINER_GRUNT_MAGMA_HIDEOUT_5":720,"TRAINER_GRUNT_MAGMA_HIDEOUT_6":721,"TRAINER_GRUNT_MAGMA_HIDEOUT_7":722,"TRAINER_GRUNT_MAGMA_HIDEOUT_8":723,"TRAINER_GRUNT_MAGMA_HIDEOUT_9":724,"TRAINER_GRUNT_MT_CHIMNEY_1":146,"TRAINER_GRUNT_MT_CHIMNEY_2":579,"TRAINER_GRUNT_MT_PYRE_1":23,"TRAINER_GRUNT_MT_PYRE_2":24,"TRAINER_GRUNT_MT_PYRE_3":25,"TRAINER_GRUNT_MT_PYRE_4":569,"TRAINER_GRUNT_MUSEUM_1":20,"TRAINER_GRUNT_MUSEUM_2":21,"TRAINER_GRUNT_PETALBURG_WOODS":10,"TRAINER_GRUNT_RUSTURF_TUNNEL":16,"TRAINER_GRUNT_SEAFLOOR_CAVERN_1":6,"TRAINER_GRUNT_SEAFLOOR_CAVERN_2":7,"TRAINER_GRUNT_SEAFLOOR_CAVERN_3":8,"TRAINER_GRUNT_SEAFLOOR_CAVERN_4":14,"TRAINER_GRUNT_SEAFLOOR_CAVERN_5":567,"TRAINER_GRUNT_SPACE_CENTER_1":22,"TRAINER_GRUNT_SPACE_CENTER_2":116,"TRAINER_GRUNT_SPACE_CENTER_3":586,"TRAINER_GRUNT_SPACE_CENTER_4":587,"TRAINER_GRUNT_SPACE_CENTER_5":588,"TRAINER_GRUNT_SPACE_CENTER_6":589,"TRAINER_GRUNT_SPACE_CENTER_7":590,"TRAINER_GRUNT_UNUSED":568,"TRAINER_GRUNT_WEATHER_INST_1":17,"TRAINER_GRUNT_WEATHER_INST_2":18,"TRAINER_GRUNT_WEATHER_INST_3":19,"TRAINER_GRUNT_WEATHER_INST_4":26,"TRAINER_GRUNT_WEATHER_INST_5":596,"TRAINER_GWEN":59,"TRAINER_HAILEY":697,"TRAINER_HALEY_1":604,"TRAINER_HALEY_2":607,"TRAINER_HALEY_3":608,"TRAINER_HALEY_4":609,"TRAINER_HALEY_5":610,"TRAINER_HALLE":546,"TRAINER_HANNAH":244,"TRAINER_HARRISON":578,"TRAINER_HAYDEN":707,"TRAINER_HECTOR":513,"TRAINER_HEIDI":469,"TRAINER_HELENE":751,"TRAINER_HENRY":668,"TRAINER_HERMAN":167,"TRAINER_HIDEO":651,"TRAINER_HITOSHI":180,"TRAINER_HOPE":96,"TRAINER_HUDSON":510,"TRAINER_HUEY":490,"TRAINER_HUGH":399,"TRAINER_HUMBERTO":402,"TRAINER_IMANI":442,"TRAINER_IRENE":476,"TRAINER_ISAAC_1":538,"TRAINER_ISAAC_2":541,"TRAINER_ISAAC_3":542,"TRAINER_ISAAC_4":543,"TRAINER_ISAAC_5":544,"TRAINER_ISABELLA":595,"TRAINER_ISABELLE":736,"TRAINER_ISABEL_1":302,"TRAINER_ISABEL_2":303,"TRAINER_ISABEL_3":304,"TRAINER_ISABEL_4":305,"TRAINER_ISABEL_5":306,"TRAINER_ISAIAH_1":376,"TRAINER_ISAIAH_2":379,"TRAINER_ISAIAH_3":380,"TRAINER_ISAIAH_4":381,"TRAINER_ISAIAH_5":382,"TRAINER_ISOBEL":383,"TRAINER_IVAN":337,"TRAINER_JACE":204,"TRAINER_JACK":172,"TRAINER_JACKI_1":249,"TRAINER_JACKI_2":250,"TRAINER_JACKI_3":251,"TRAINER_JACKI_4":252,"TRAINER_JACKI_5":253,"TRAINER_JACKSON_1":552,"TRAINER_JACKSON_2":555,"TRAINER_JACKSON_3":556,"TRAINER_JACKSON_4":557,"TRAINER_JACKSON_5":558,"TRAINER_JACLYN":243,"TRAINER_JACOB":351,"TRAINER_JAIDEN":749,"TRAINER_JAMES_1":621,"TRAINER_JAMES_2":622,"TRAINER_JAMES_3":623,"TRAINER_JAMES_4":624,"TRAINER_JAMES_5":625,"TRAINER_JANI":418,"TRAINER_JANICE":605,"TRAINER_JARED":401,"TRAINER_JASMINE":359,"TRAINER_JAYLEN":326,"TRAINER_JAZMYN":503,"TRAINER_JEFF":202,"TRAINER_JEFFREY_1":226,"TRAINER_JEFFREY_2":228,"TRAINER_JEFFREY_3":229,"TRAINER_JEFFREY_4":230,"TRAINER_JEFFREY_5":231,"TRAINER_JENNA":560,"TRAINER_JENNIFER":95,"TRAINER_JENNY_1":449,"TRAINER_JENNY_2":465,"TRAINER_JENNY_3":466,"TRAINER_JENNY_4":467,"TRAINER_JENNY_5":468,"TRAINER_JEROME":156,"TRAINER_JERRY_1":273,"TRAINER_JERRY_2":276,"TRAINER_JERRY_3":277,"TRAINER_JERRY_4":278,"TRAINER_JERRY_5":279,"TRAINER_JESSICA_1":127,"TRAINER_JESSICA_2":132,"TRAINER_JESSICA_3":133,"TRAINER_JESSICA_4":134,"TRAINER_JESSICA_5":135,"TRAINER_JOCELYN":425,"TRAINER_JODY":91,"TRAINER_JOEY":322,"TRAINER_JOHANNA":647,"TRAINER_JOHNSON":754,"TRAINER_JOHN_AND_JAY_1":681,"TRAINER_JOHN_AND_JAY_2":682,"TRAINER_JOHN_AND_JAY_3":683,"TRAINER_JOHN_AND_JAY_4":684,"TRAINER_JOHN_AND_JAY_5":685,"TRAINER_JONAH":667,"TRAINER_JONAS":504,"TRAINER_JONATHAN":598,"TRAINER_JOSE":617,"TRAINER_JOSEPH":700,"TRAINER_JOSH":320,"TRAINER_JOSHUA":237,"TRAINER_JOSUE":738,"TRAINER_JUAN_1":272,"TRAINER_JUAN_2":798,"TRAINER_JUAN_3":799,"TRAINER_JUAN_4":800,"TRAINER_JUAN_5":801,"TRAINER_JULIE":100,"TRAINER_JULIO":566,"TRAINER_JUSTIN":215,"TRAINER_KAI":713,"TRAINER_KALEB":699,"TRAINER_KARA":457,"TRAINER_KAREN_1":280,"TRAINER_KAREN_2":282,"TRAINER_KAREN_3":283,"TRAINER_KAREN_4":284,"TRAINER_KAREN_5":285,"TRAINER_KATELYNN":325,"TRAINER_KATELYN_1":386,"TRAINER_KATELYN_2":388,"TRAINER_KATELYN_3":389,"TRAINER_KATELYN_4":390,"TRAINER_KATELYN_5":391,"TRAINER_KATE_AND_JOY":286,"TRAINER_KATHLEEN":583,"TRAINER_KATIE":455,"TRAINER_KAYLA":247,"TRAINER_KAYLEE":462,"TRAINER_KAYLEY":505,"TRAINER_KEEGAN":205,"TRAINER_KEIGO":652,"TRAINER_KEIRA":93,"TRAINER_KELVIN":507,"TRAINER_KENT":620,"TRAINER_KEVIN":171,"TRAINER_KIM_AND_IRIS":678,"TRAINER_KINDRA":106,"TRAINER_KIRA_AND_DAN_1":642,"TRAINER_KIRA_AND_DAN_2":643,"TRAINER_KIRA_AND_DAN_3":644,"TRAINER_KIRA_AND_DAN_4":645,"TRAINER_KIRA_AND_DAN_5":646,"TRAINER_KIRK":191,"TRAINER_KIYO":181,"TRAINER_KOICHI":182,"TRAINER_KOJI_1":672,"TRAINER_KOJI_2":824,"TRAINER_KOJI_3":825,"TRAINER_KOJI_4":826,"TRAINER_KOJI_5":827,"TRAINER_KYLA":443,"TRAINER_KYRA":748,"TRAINER_LAO_1":419,"TRAINER_LAO_2":421,"TRAINER_LAO_3":422,"TRAINER_LAO_4":423,"TRAINER_LAO_5":424,"TRAINER_LARRY":213,"TRAINER_LAURA":426,"TRAINER_LAUREL":463,"TRAINER_LAWRENCE":710,"TRAINER_LEAF":852,"TRAINER_LEAH":35,"TRAINER_LEA_AND_JED":641,"TRAINER_LENNY":628,"TRAINER_LEONARD":495,"TRAINER_LEONARDO":576,"TRAINER_LEONEL":762,"TRAINER_LEROY":77,"TRAINER_LILA_AND_ROY_1":687,"TRAINER_LILA_AND_ROY_2":688,"TRAINER_LILA_AND_ROY_3":689,"TRAINER_LILA_AND_ROY_4":690,"TRAINER_LILA_AND_ROY_5":691,"TRAINER_LILITH":573,"TRAINER_LINDA":461,"TRAINER_LISA_AND_RAY":692,"TRAINER_LOLA_1":57,"TRAINER_LOLA_2":60,"TRAINER_LOLA_3":61,"TRAINER_LOLA_4":62,"TRAINER_LOLA_5":63,"TRAINER_LORENZO":553,"TRAINER_LUCAS_1":629,"TRAINER_LUCAS_2":633,"TRAINER_LUCY":810,"TRAINER_LUIS":151,"TRAINER_LUNG":420,"TRAINER_LYDIA_1":545,"TRAINER_LYDIA_2":548,"TRAINER_LYDIA_3":549,"TRAINER_LYDIA_4":550,"TRAINER_LYDIA_5":551,"TRAINER_LYLE":616,"TRAINER_MACEY":591,"TRAINER_MADELINE_1":434,"TRAINER_MADELINE_2":437,"TRAINER_MADELINE_3":438,"TRAINER_MADELINE_4":439,"TRAINER_MADELINE_5":440,"TRAINER_MAKAYLA":758,"TRAINER_MARC":571,"TRAINER_MARCEL":11,"TRAINER_MARCOS":702,"TRAINER_MARIA_1":369,"TRAINER_MARIA_2":370,"TRAINER_MARIA_3":371,"TRAINER_MARIA_4":372,"TRAINER_MARIA_5":373,"TRAINER_MARIELA":848,"TRAINER_MARK":145,"TRAINER_MARLENE":752,"TRAINER_MARLEY":508,"TRAINER_MARTHA":473,"TRAINER_MARY":89,"TRAINER_MATT":30,"TRAINER_MATTHEW":157,"TRAINER_MAURA":246,"TRAINER_MAXIE_MAGMA_HIDEOUT":601,"TRAINER_MAXIE_MOSSDEEP":734,"TRAINER_MAXIE_MT_CHIMNEY":602,"TRAINER_MAY_LILYCOVE_MUDKIP":664,"TRAINER_MAY_LILYCOVE_TORCHIC":666,"TRAINER_MAY_LILYCOVE_TREECKO":665,"TRAINER_MAY_PLACEHOLDER":854,"TRAINER_MAY_ROUTE_103_MUDKIP":529,"TRAINER_MAY_ROUTE_103_TORCHIC":535,"TRAINER_MAY_ROUTE_103_TREECKO":532,"TRAINER_MAY_ROUTE_110_MUDKIP":530,"TRAINER_MAY_ROUTE_110_TORCHIC":536,"TRAINER_MAY_ROUTE_110_TREECKO":533,"TRAINER_MAY_ROUTE_119_MUDKIP":531,"TRAINER_MAY_ROUTE_119_TORCHIC":537,"TRAINER_MAY_ROUTE_119_TREECKO":534,"TRAINER_MAY_RUSTBORO_MUDKIP":600,"TRAINER_MAY_RUSTBORO_TORCHIC":769,"TRAINER_MAY_RUSTBORO_TREECKO":768,"TRAINER_MELINA":755,"TRAINER_MELISSA":124,"TRAINER_MEL_AND_PAUL":680,"TRAINER_MICAH":255,"TRAINER_MICHELLE":98,"TRAINER_MIGUEL_1":293,"TRAINER_MIGUEL_2":295,"TRAINER_MIGUEL_3":296,"TRAINER_MIGUEL_4":297,"TRAINER_MIGUEL_5":298,"TRAINER_MIKE_1":634,"TRAINER_MIKE_2":635,"TRAINER_MISSY":447,"TRAINER_MITCHELL":540,"TRAINER_MIU_AND_YUKI":484,"TRAINER_MOLLIE":137,"TRAINER_MYLES":765,"TRAINER_NANCY":472,"TRAINER_NAOMI":119,"TRAINER_NATE":582,"TRAINER_NED":340,"TRAINER_NICHOLAS":585,"TRAINER_NICOLAS_1":392,"TRAINER_NICOLAS_2":393,"TRAINER_NICOLAS_3":394,"TRAINER_NICOLAS_4":395,"TRAINER_NICOLAS_5":396,"TRAINER_NIKKI":453,"TRAINER_NOB_1":183,"TRAINER_NOB_2":184,"TRAINER_NOB_3":185,"TRAINER_NOB_4":186,"TRAINER_NOB_5":187,"TRAINER_NOLAN":342,"TRAINER_NOLAND":809,"TRAINER_NOLEN":161,"TRAINER_NONE":0,"TRAINER_NORMAN_1":269,"TRAINER_NORMAN_2":786,"TRAINER_NORMAN_3":787,"TRAINER_NORMAN_4":788,"TRAINER_NORMAN_5":789,"TRAINER_OLIVIA":130,"TRAINER_OWEN":83,"TRAINER_PABLO_1":377,"TRAINER_PABLO_2":820,"TRAINER_PABLO_3":821,"TRAINER_PABLO_4":822,"TRAINER_PABLO_5":823,"TRAINER_PARKER":72,"TRAINER_PAT":766,"TRAINER_PATRICIA":105,"TRAINER_PAUL":275,"TRAINER_PAULA":429,"TRAINER_PAXTON":594,"TRAINER_PERRY":398,"TRAINER_PETE":735,"TRAINER_PHIL":400,"TRAINER_PHILLIP":494,"TRAINER_PHOEBE":262,"TRAINER_PRESLEY":403,"TRAINER_PRESTON":233,"TRAINER_QUINCY":324,"TRAINER_RACHEL":761,"TRAINER_RANDALL":71,"TRAINER_RED":851,"TRAINER_REED":675,"TRAINER_RELI_AND_IAN":686,"TRAINER_REYNA":509,"TRAINER_RHETT":703,"TRAINER_RICHARD":166,"TRAINER_RICK":615,"TRAINER_RICKY_1":64,"TRAINER_RICKY_2":67,"TRAINER_RICKY_3":68,"TRAINER_RICKY_4":69,"TRAINER_RICKY_5":70,"TRAINER_RILEY":653,"TRAINER_ROBERT_1":406,"TRAINER_ROBERT_2":409,"TRAINER_ROBERT_3":410,"TRAINER_ROBERT_4":411,"TRAINER_ROBERT_5":412,"TRAINER_ROBIN":612,"TRAINER_RODNEY":165,"TRAINER_ROGER":669,"TRAINER_ROLAND":160,"TRAINER_RONALD":350,"TRAINER_ROSE_1":37,"TRAINER_ROSE_2":40,"TRAINER_ROSE_3":41,"TRAINER_ROSE_4":42,"TRAINER_ROSE_5":43,"TRAINER_ROXANNE_1":265,"TRAINER_ROXANNE_2":770,"TRAINER_ROXANNE_3":771,"TRAINER_ROXANNE_4":772,"TRAINER_ROXANNE_5":773,"TRAINER_RUBEN":671,"TRAINER_SALLY":611,"TRAINER_SAMANTHA":245,"TRAINER_SAMUEL":81,"TRAINER_SANTIAGO":168,"TRAINER_SARAH":695,"TRAINER_SAWYER_1":1,"TRAINER_SAWYER_2":836,"TRAINER_SAWYER_3":837,"TRAINER_SAWYER_4":838,"TRAINER_SAWYER_5":839,"TRAINER_SEBASTIAN":554,"TRAINER_SHANE":214,"TRAINER_SHANNON":97,"TRAINER_SHARON":452,"TRAINER_SHAWN":194,"TRAINER_SHAYLA":747,"TRAINER_SHEILA":125,"TRAINER_SHELBY_1":313,"TRAINER_SHELBY_2":314,"TRAINER_SHELBY_3":315,"TRAINER_SHELBY_4":316,"TRAINER_SHELBY_5":317,"TRAINER_SHELLY_SEAFLOOR_CAVERN":33,"TRAINER_SHELLY_WEATHER_INSTITUTE":32,"TRAINER_SHIRLEY":126,"TRAINER_SIDNEY":261,"TRAINER_SIENNA":459,"TRAINER_SIMON":65,"TRAINER_SOPHIA":561,"TRAINER_SOPHIE":708,"TRAINER_SPENCER":159,"TRAINER_SPENSER":807,"TRAINER_STAN":162,"TRAINER_STEVEN":804,"TRAINER_STEVE_1":143,"TRAINER_STEVE_2":147,"TRAINER_STEVE_3":148,"TRAINER_STEVE_4":149,"TRAINER_STEVE_5":150,"TRAINER_SUSIE":456,"TRAINER_SYLVIA":575,"TRAINER_TABITHA_MAGMA_HIDEOUT":732,"TRAINER_TABITHA_MOSSDEEP":514,"TRAINER_TABITHA_MT_CHIMNEY":597,"TRAINER_TAKAO":179,"TRAINER_TAKASHI":416,"TRAINER_TALIA":385,"TRAINER_TAMMY":107,"TRAINER_TANYA":451,"TRAINER_TARA":446,"TRAINER_TASHA":109,"TRAINER_TATE_AND_LIZA_1":271,"TRAINER_TATE_AND_LIZA_2":794,"TRAINER_TATE_AND_LIZA_3":795,"TRAINER_TATE_AND_LIZA_4":796,"TRAINER_TATE_AND_LIZA_5":797,"TRAINER_TAYLOR":225,"TRAINER_TED":274,"TRAINER_TERRY":581,"TRAINER_THALIA_1":144,"TRAINER_THALIA_2":844,"TRAINER_THALIA_3":845,"TRAINER_THALIA_4":846,"TRAINER_THALIA_5":847,"TRAINER_THOMAS":256,"TRAINER_TIANA":603,"TRAINER_TIFFANY":131,"TRAINER_TIMMY":334,"TRAINER_TIMOTHY_1":307,"TRAINER_TIMOTHY_2":308,"TRAINER_TIMOTHY_3":309,"TRAINER_TIMOTHY_4":310,"TRAINER_TIMOTHY_5":311,"TRAINER_TISHA":676,"TRAINER_TOMMY":321,"TRAINER_TONY_1":155,"TRAINER_TONY_2":175,"TRAINER_TONY_3":176,"TRAINER_TONY_4":177,"TRAINER_TONY_5":178,"TRAINER_TORI_AND_TIA":677,"TRAINER_TRAVIS":218,"TRAINER_TRENT_1":627,"TRAINER_TRENT_2":636,"TRAINER_TRENT_3":637,"TRAINER_TRENT_4":638,"TRAINER_TRENT_5":639,"TRAINER_TUCKER":806,"TRAINER_TYRA_AND_IVY":679,"TRAINER_TYRON":704,"TRAINER_VALERIE_1":108,"TRAINER_VALERIE_2":110,"TRAINER_VALERIE_3":111,"TRAINER_VALERIE_4":112,"TRAINER_VALERIE_5":113,"TRAINER_VANESSA":300,"TRAINER_VICKY":312,"TRAINER_VICTOR":292,"TRAINER_VICTORIA":299,"TRAINER_VINCENT":76,"TRAINER_VIOLET":39,"TRAINER_VIRGIL":234,"TRAINER_VITO":82,"TRAINER_VIVI":606,"TRAINER_VIVIAN":649,"TRAINER_WADE":344,"TRAINER_WALLACE":335,"TRAINER_WALLY_MAUVILLE":656,"TRAINER_WALLY_VR_1":519,"TRAINER_WALLY_VR_2":657,"TRAINER_WALLY_VR_3":658,"TRAINER_WALLY_VR_4":659,"TRAINER_WALLY_VR_5":660,"TRAINER_WALTER_1":254,"TRAINER_WALTER_2":257,"TRAINER_WALTER_3":258,"TRAINER_WALTER_4":259,"TRAINER_WALTER_5":260,"TRAINER_WARREN":88,"TRAINER_WATTSON_1":267,"TRAINER_WATTSON_2":778,"TRAINER_WATTSON_3":779,"TRAINER_WATTSON_4":780,"TRAINER_WATTSON_5":781,"TRAINER_WAYNE":673,"TRAINER_WENDY":92,"TRAINER_WILLIAM":236,"TRAINER_WILTON_1":78,"TRAINER_WILTON_2":84,"TRAINER_WILTON_3":85,"TRAINER_WILTON_4":86,"TRAINER_WILTON_5":87,"TRAINER_WINONA_1":270,"TRAINER_WINONA_2":790,"TRAINER_WINONA_3":791,"TRAINER_WINONA_4":792,"TRAINER_WINONA_5":793,"TRAINER_WINSTON_1":136,"TRAINER_WINSTON_2":139,"TRAINER_WINSTON_3":140,"TRAINER_WINSTON_4":141,"TRAINER_WINSTON_5":142,"TRAINER_WYATT":711,"TRAINER_YASU":415,"TRAINER_YUJI":188,"TRAINER_ZANDER":31},"locations":{"BADGE_1":{"default_item":226,"flag":1182,"rom_address":2181887},"BADGE_2":{"default_item":227,"flag":1183,"rom_address":2089138},"BADGE_3":{"default_item":228,"flag":1184,"rom_address":2161147},"BADGE_4":{"default_item":229,"flag":1185,"rom_address":2097239},"BADGE_5":{"default_item":230,"flag":1186,"rom_address":2123748},"BADGE_6":{"default_item":231,"flag":1187,"rom_address":2195957},"BADGE_7":{"default_item":232,"flag":1188,"rom_address":2237755},"BADGE_8":{"default_item":233,"flag":1189,"rom_address":2256065},"HIDDEN_ITEM_ABANDONED_SHIP_RM_1_KEY":{"default_item":281,"flag":531,"rom_address":5479240},"HIDDEN_ITEM_ABANDONED_SHIP_RM_2_KEY":{"default_item":282,"flag":532,"rom_address":5479252},"HIDDEN_ITEM_ABANDONED_SHIP_RM_4_KEY":{"default_item":283,"flag":533,"rom_address":5479264},"HIDDEN_ITEM_ABANDONED_SHIP_RM_6_KEY":{"default_item":284,"flag":534,"rom_address":5479276},"HIDDEN_ITEM_ARTISAN_CAVE_B1F_CALCIUM":{"default_item":67,"flag":601,"rom_address":5482140},"HIDDEN_ITEM_ARTISAN_CAVE_B1F_IRON":{"default_item":65,"flag":604,"rom_address":5482164},"HIDDEN_ITEM_ARTISAN_CAVE_B1F_PROTEIN":{"default_item":64,"flag":603,"rom_address":5482152},"HIDDEN_ITEM_ARTISAN_CAVE_B1F_ZINC":{"default_item":70,"flag":602,"rom_address":5482128},"HIDDEN_ITEM_FALLARBOR_TOWN_NUGGET":{"default_item":110,"flag":528,"rom_address":5417964},"HIDDEN_ITEM_GRANITE_CAVE_B2F_EVERSTONE_1":{"default_item":195,"flag":548,"rom_address":5469412},"HIDDEN_ITEM_GRANITE_CAVE_B2F_EVERSTONE_2":{"default_item":195,"flag":549,"rom_address":5469424},"HIDDEN_ITEM_JAGGED_PASS_FULL_HEAL":{"default_item":23,"flag":577,"rom_address":5471156},"HIDDEN_ITEM_JAGGED_PASS_GREAT_BALL":{"default_item":3,"flag":576,"rom_address":5471168},"HIDDEN_ITEM_LAVARIDGE_TOWN_ICE_HEAL":{"default_item":16,"flag":500,"rom_address":5417712},"HIDDEN_ITEM_LILYCOVE_CITY_HEART_SCALE":{"default_item":111,"flag":527,"rom_address":5414672},"HIDDEN_ITEM_LILYCOVE_CITY_POKE_BALL":{"default_item":4,"flag":575,"rom_address":5414696},"HIDDEN_ITEM_LILYCOVE_CITY_PP_UP":{"default_item":69,"flag":543,"rom_address":5414684},"HIDDEN_ITEM_MT_PYRE_EXTERIOR_MAX_ETHER":{"default_item":35,"flag":578,"rom_address":5472480},"HIDDEN_ITEM_MT_PYRE_EXTERIOR_ULTRA_BALL":{"default_item":2,"flag":529,"rom_address":5472468},"HIDDEN_ITEM_MT_PYRE_SUMMIT_RARE_CANDY":{"default_item":68,"flag":580,"rom_address":5472836},"HIDDEN_ITEM_MT_PYRE_SUMMIT_ZINC":{"default_item":70,"flag":579,"rom_address":5472824},"HIDDEN_ITEM_NAVEL_ROCK_TOP_SACRED_ASH":{"default_item":45,"flag":609,"rom_address":5507844},"HIDDEN_ITEM_PETALBURG_CITY_RARE_CANDY":{"default_item":68,"flag":595,"rom_address":5411036},"HIDDEN_ITEM_PETALBURG_WOODS_POKE_BALL":{"default_item":4,"flag":561,"rom_address":5469948},"HIDDEN_ITEM_PETALBURG_WOODS_POTION":{"default_item":13,"flag":558,"rom_address":5469912},"HIDDEN_ITEM_PETALBURG_WOODS_TINY_MUSHROOM_1":{"default_item":103,"flag":559,"rom_address":5469924},"HIDDEN_ITEM_PETALBURG_WOODS_TINY_MUSHROOM_2":{"default_item":103,"flag":560,"rom_address":5469936},"HIDDEN_ITEM_ROUTE_104_ANTIDOTE":{"default_item":14,"flag":585,"rom_address":5420532},"HIDDEN_ITEM_ROUTE_104_HEART_SCALE":{"default_item":111,"flag":588,"rom_address":5420544},"HIDDEN_ITEM_ROUTE_104_POKE_BALL":{"default_item":4,"flag":562,"rom_address":5420508},"HIDDEN_ITEM_ROUTE_104_POTION":{"default_item":13,"flag":537,"rom_address":5420520},"HIDDEN_ITEM_ROUTE_104_SUPER_POTION":{"default_item":22,"flag":544,"rom_address":5420496},"HIDDEN_ITEM_ROUTE_105_BIG_PEARL":{"default_item":107,"flag":611,"rom_address":5420788},"HIDDEN_ITEM_ROUTE_105_HEART_SCALE":{"default_item":111,"flag":589,"rom_address":5420776},"HIDDEN_ITEM_ROUTE_106_HEART_SCALE":{"default_item":111,"flag":547,"rom_address":5420972},"HIDDEN_ITEM_ROUTE_106_POKE_BALL":{"default_item":4,"flag":563,"rom_address":5420948},"HIDDEN_ITEM_ROUTE_106_STARDUST":{"default_item":108,"flag":546,"rom_address":5420960},"HIDDEN_ITEM_ROUTE_108_RARE_CANDY":{"default_item":68,"flag":586,"rom_address":5421380},"HIDDEN_ITEM_ROUTE_109_ETHER":{"default_item":34,"flag":564,"rom_address":5422056},"HIDDEN_ITEM_ROUTE_109_GREAT_BALL":{"default_item":3,"flag":551,"rom_address":5422044},"HIDDEN_ITEM_ROUTE_109_HEART_SCALE_1":{"default_item":111,"flag":552,"rom_address":5422032},"HIDDEN_ITEM_ROUTE_109_HEART_SCALE_2":{"default_item":111,"flag":590,"rom_address":5422068},"HIDDEN_ITEM_ROUTE_109_HEART_SCALE_3":{"default_item":111,"flag":591,"rom_address":5422080},"HIDDEN_ITEM_ROUTE_109_REVIVE":{"default_item":24,"flag":550,"rom_address":5422020},"HIDDEN_ITEM_ROUTE_110_FULL_HEAL":{"default_item":23,"flag":555,"rom_address":5423348},"HIDDEN_ITEM_ROUTE_110_GREAT_BALL":{"default_item":3,"flag":553,"rom_address":5423324},"HIDDEN_ITEM_ROUTE_110_POKE_BALL":{"default_item":4,"flag":565,"rom_address":5423336},"HIDDEN_ITEM_ROUTE_110_REVIVE":{"default_item":24,"flag":554,"rom_address":5423312},"HIDDEN_ITEM_ROUTE_111_PROTEIN":{"default_item":64,"flag":556,"rom_address":5425260},"HIDDEN_ITEM_ROUTE_111_RARE_CANDY":{"default_item":68,"flag":557,"rom_address":5425272},"HIDDEN_ITEM_ROUTE_111_STARDUST":{"default_item":108,"flag":502,"rom_address":5425200},"HIDDEN_ITEM_ROUTE_113_ETHER":{"default_item":34,"flag":503,"rom_address":5426528},"HIDDEN_ITEM_ROUTE_113_NUGGET":{"default_item":110,"flag":598,"rom_address":5426552},"HIDDEN_ITEM_ROUTE_113_TM32":{"default_item":320,"flag":530,"rom_address":5426540},"HIDDEN_ITEM_ROUTE_114_CARBOS":{"default_item":66,"flag":504,"rom_address":5427380},"HIDDEN_ITEM_ROUTE_114_REVIVE":{"default_item":24,"flag":542,"rom_address":5427404},"HIDDEN_ITEM_ROUTE_115_HEART_SCALE":{"default_item":111,"flag":597,"rom_address":5428216},"HIDDEN_ITEM_ROUTE_116_BLACK_GLASSES":{"default_item":206,"flag":596,"rom_address":5429096},"HIDDEN_ITEM_ROUTE_116_SUPER_POTION":{"default_item":22,"flag":545,"rom_address":5429084},"HIDDEN_ITEM_ROUTE_117_REPEL":{"default_item":86,"flag":572,"rom_address":5429748},"HIDDEN_ITEM_ROUTE_118_HEART_SCALE":{"default_item":111,"flag":566,"rom_address":5430444},"HIDDEN_ITEM_ROUTE_118_IRON":{"default_item":65,"flag":567,"rom_address":5430432},"HIDDEN_ITEM_ROUTE_119_CALCIUM":{"default_item":67,"flag":505,"rom_address":5432012},"HIDDEN_ITEM_ROUTE_119_FULL_HEAL":{"default_item":23,"flag":568,"rom_address":5432096},"HIDDEN_ITEM_ROUTE_119_MAX_ETHER":{"default_item":35,"flag":587,"rom_address":5432108},"HIDDEN_ITEM_ROUTE_119_ULTRA_BALL":{"default_item":2,"flag":506,"rom_address":5432024},"HIDDEN_ITEM_ROUTE_120_RARE_CANDY_1":{"default_item":68,"flag":571,"rom_address":5433636},"HIDDEN_ITEM_ROUTE_120_RARE_CANDY_2":{"default_item":68,"flag":569,"rom_address":5433660},"HIDDEN_ITEM_ROUTE_120_REVIVE":{"default_item":24,"flag":584,"rom_address":5433648},"HIDDEN_ITEM_ROUTE_120_ZINC":{"default_item":70,"flag":570,"rom_address":5433672},"HIDDEN_ITEM_ROUTE_121_FULL_HEAL":{"default_item":23,"flag":573,"rom_address":5434580},"HIDDEN_ITEM_ROUTE_121_HP_UP":{"default_item":63,"flag":539,"rom_address":5434556},"HIDDEN_ITEM_ROUTE_121_MAX_REVIVE":{"default_item":25,"flag":600,"rom_address":5434592},"HIDDEN_ITEM_ROUTE_121_NUGGET":{"default_item":110,"flag":540,"rom_address":5434568},"HIDDEN_ITEM_ROUTE_123_HYPER_POTION":{"default_item":21,"flag":574,"rom_address":5436140},"HIDDEN_ITEM_ROUTE_123_PP_UP":{"default_item":69,"flag":599,"rom_address":5436152},"HIDDEN_ITEM_ROUTE_123_RARE_CANDY":{"default_item":68,"flag":610,"rom_address":5436164},"HIDDEN_ITEM_ROUTE_123_REVIVE":{"default_item":24,"flag":541,"rom_address":5436128},"HIDDEN_ITEM_ROUTE_123_SUPER_REPEL":{"default_item":83,"flag":507,"rom_address":5436092},"HIDDEN_ITEM_ROUTE_128_HEART_SCALE_1":{"default_item":111,"flag":592,"rom_address":5437660},"HIDDEN_ITEM_ROUTE_128_HEART_SCALE_2":{"default_item":111,"flag":593,"rom_address":5437672},"HIDDEN_ITEM_ROUTE_128_HEART_SCALE_3":{"default_item":111,"flag":594,"rom_address":5437684},"HIDDEN_ITEM_SAFARI_ZONE_NORTH_EAST_RARE_CANDY":{"default_item":68,"flag":606,"rom_address":5499296},"HIDDEN_ITEM_SAFARI_ZONE_NORTH_EAST_ZINC":{"default_item":70,"flag":607,"rom_address":5499308},"HIDDEN_ITEM_SAFARI_ZONE_SOUTH_EAST_FULL_RESTORE":{"default_item":19,"flag":605,"rom_address":5499472},"HIDDEN_ITEM_SAFARI_ZONE_SOUTH_EAST_PP_UP":{"default_item":69,"flag":608,"rom_address":5499460},"HIDDEN_ITEM_SS_TIDAL_LOWER_DECK_LEFTOVERS":{"default_item":200,"flag":535,"rom_address":5493332},"HIDDEN_ITEM_TRICK_HOUSE_NUGGET":{"default_item":110,"flag":501,"rom_address":5508756},"HIDDEN_ITEM_UNDERWATER_124_BIG_PEARL":{"default_item":107,"flag":511,"rom_address":5439032},"HIDDEN_ITEM_UNDERWATER_124_CALCIUM":{"default_item":67,"flag":536,"rom_address":5439056},"HIDDEN_ITEM_UNDERWATER_124_CARBOS":{"default_item":66,"flag":508,"rom_address":5438996},"HIDDEN_ITEM_UNDERWATER_124_GREEN_SHARD":{"default_item":51,"flag":509,"rom_address":5439008},"HIDDEN_ITEM_UNDERWATER_124_HEART_SCALE_1":{"default_item":111,"flag":513,"rom_address":5439044},"HIDDEN_ITEM_UNDERWATER_124_HEART_SCALE_2":{"default_item":111,"flag":538,"rom_address":5439068},"HIDDEN_ITEM_UNDERWATER_124_PEARL":{"default_item":106,"flag":510,"rom_address":5439020},"HIDDEN_ITEM_UNDERWATER_126_BIG_PEARL":{"default_item":107,"flag":520,"rom_address":5439180},"HIDDEN_ITEM_UNDERWATER_126_BLUE_SHARD":{"default_item":49,"flag":512,"rom_address":5439192},"HIDDEN_ITEM_UNDERWATER_126_HEART_SCALE":{"default_item":111,"flag":514,"rom_address":5439108},"HIDDEN_ITEM_UNDERWATER_126_IRON":{"default_item":65,"flag":519,"rom_address":5439156},"HIDDEN_ITEM_UNDERWATER_126_PEARL":{"default_item":106,"flag":517,"rom_address":5439144},"HIDDEN_ITEM_UNDERWATER_126_STARDUST":{"default_item":108,"flag":516,"rom_address":5439132},"HIDDEN_ITEM_UNDERWATER_126_ULTRA_BALL":{"default_item":2,"flag":515,"rom_address":5439120},"HIDDEN_ITEM_UNDERWATER_126_YELLOW_SHARD":{"default_item":50,"flag":518,"rom_address":5439168},"HIDDEN_ITEM_UNDERWATER_127_HEART_SCALE":{"default_item":111,"flag":523,"rom_address":5439264},"HIDDEN_ITEM_UNDERWATER_127_HP_UP":{"default_item":63,"flag":522,"rom_address":5439252},"HIDDEN_ITEM_UNDERWATER_127_RED_SHARD":{"default_item":48,"flag":524,"rom_address":5439276},"HIDDEN_ITEM_UNDERWATER_127_STAR_PIECE":{"default_item":109,"flag":521,"rom_address":5439240},"HIDDEN_ITEM_UNDERWATER_128_PEARL":{"default_item":106,"flag":526,"rom_address":5439328},"HIDDEN_ITEM_UNDERWATER_128_PROTEIN":{"default_item":64,"flag":525,"rom_address":5439316},"HIDDEN_ITEM_VICTORY_ROAD_1F_ULTRA_BALL":{"default_item":2,"flag":581,"rom_address":5475972},"HIDDEN_ITEM_VICTORY_ROAD_B2F_ELIXIR":{"default_item":36,"flag":582,"rom_address":5476784},"HIDDEN_ITEM_VICTORY_ROAD_B2F_MAX_REPEL":{"default_item":84,"flag":583,"rom_address":5476796},"ITEM_ABANDONED_SHIP_CAPTAINS_OFFICE_STORAGE_KEY":{"default_item":285,"flag":1100,"rom_address":2701736},"ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_1_TM18":{"default_item":306,"flag":1102,"rom_address":2701788},"ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_3_WATER_STONE":{"default_item":97,"flag":1101,"rom_address":2701775},"ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_4_SCANNER":{"default_item":278,"flag":1078,"rom_address":2701762},"ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_6_LUXURY_BALL":{"default_item":11,"flag":1077,"rom_address":2701749},"ITEM_ABANDONED_SHIP_ROOMS_1F_HARBOR_MAIL":{"default_item":122,"flag":1095,"rom_address":2701671},"ITEM_ABANDONED_SHIP_ROOMS_2_1F_REVIVE":{"default_item":24,"flag":1099,"rom_address":2701723},"ITEM_ABANDONED_SHIP_ROOMS_2_B1F_DIVE_BALL":{"default_item":7,"flag":1097,"rom_address":2701697},"ITEM_ABANDONED_SHIP_ROOMS_B1F_ESCAPE_ROPE":{"default_item":85,"flag":1096,"rom_address":2701684},"ITEM_ABANDONED_SHIP_ROOMS_B1F_TM13":{"default_item":301,"flag":1098,"rom_address":2701710},"ITEM_AQUA_HIDEOUT_B1F_MASTER_BALL":{"default_item":1,"flag":1124,"rom_address":2701970},"ITEM_AQUA_HIDEOUT_B1F_MAX_ELIXIR":{"default_item":37,"flag":1071,"rom_address":2701996},"ITEM_AQUA_HIDEOUT_B1F_NUGGET":{"default_item":110,"flag":1132,"rom_address":2701983},"ITEM_AQUA_HIDEOUT_B2F_NEST_BALL":{"default_item":8,"flag":1072,"rom_address":2702009},"ITEM_ARTISAN_CAVE_1F_CARBOS":{"default_item":66,"flag":1163,"rom_address":2702347},"ITEM_ARTISAN_CAVE_B1F_HP_UP":{"default_item":63,"flag":1162,"rom_address":2702334},"ITEM_FIERY_PATH_FIRE_STONE":{"default_item":95,"flag":1111,"rom_address":2701515},"ITEM_FIERY_PATH_TM06":{"default_item":294,"flag":1091,"rom_address":2701528},"ITEM_GRANITE_CAVE_1F_ESCAPE_ROPE":{"default_item":85,"flag":1050,"rom_address":2701450},"ITEM_GRANITE_CAVE_B1F_POKE_BALL":{"default_item":4,"flag":1051,"rom_address":2701463},"ITEM_GRANITE_CAVE_B2F_RARE_CANDY":{"default_item":68,"flag":1054,"rom_address":2701489},"ITEM_GRANITE_CAVE_B2F_REPEL":{"default_item":86,"flag":1053,"rom_address":2701476},"ITEM_JAGGED_PASS_BURN_HEAL":{"default_item":15,"flag":1070,"rom_address":2701502},"ITEM_LILYCOVE_CITY_MAX_REPEL":{"default_item":84,"flag":1042,"rom_address":2701346},"ITEM_MAGMA_HIDEOUT_1F_RARE_CANDY":{"default_item":68,"flag":1151,"rom_address":2702360},"ITEM_MAGMA_HIDEOUT_2F_2R_FULL_RESTORE":{"default_item":19,"flag":1165,"rom_address":2702386},"ITEM_MAGMA_HIDEOUT_2F_2R_MAX_ELIXIR":{"default_item":37,"flag":1164,"rom_address":2702373},"ITEM_MAGMA_HIDEOUT_3F_1R_NUGGET":{"default_item":110,"flag":1166,"rom_address":2702399},"ITEM_MAGMA_HIDEOUT_3F_2R_PP_MAX":{"default_item":71,"flag":1167,"rom_address":2702412},"ITEM_MAGMA_HIDEOUT_3F_3R_ECAPE_ROPE":{"default_item":85,"flag":1059,"rom_address":2702438},"ITEM_MAGMA_HIDEOUT_4F_MAX_REVIVE":{"default_item":25,"flag":1168,"rom_address":2702425},"ITEM_MAUVILLE_CITY_X_SPEED":{"default_item":77,"flag":1116,"rom_address":2701320},"ITEM_METEOR_FALLS_1F_1R_FULL_HEAL":{"default_item":23,"flag":1045,"rom_address":2701554},"ITEM_METEOR_FALLS_1F_1R_MOON_STONE":{"default_item":94,"flag":1046,"rom_address":2701567},"ITEM_METEOR_FALLS_1F_1R_PP_UP":{"default_item":69,"flag":1047,"rom_address":2701580},"ITEM_METEOR_FALLS_1F_1R_TM23":{"default_item":311,"flag":1044,"rom_address":2701541},"ITEM_METEOR_FALLS_B1F_2R_TM02":{"default_item":290,"flag":1080,"rom_address":2701593},"ITEM_MOSSDEEP_CITY_NET_BALL":{"default_item":6,"flag":1043,"rom_address":2701359},"ITEM_MT_PYRE_2F_ULTRA_BALL":{"default_item":2,"flag":1129,"rom_address":2701879},"ITEM_MT_PYRE_3F_SUPER_REPEL":{"default_item":83,"flag":1120,"rom_address":2701892},"ITEM_MT_PYRE_4F_SEA_INCENSE":{"default_item":220,"flag":1130,"rom_address":2701905},"ITEM_MT_PYRE_5F_LAX_INCENSE":{"default_item":221,"flag":1052,"rom_address":2701918},"ITEM_MT_PYRE_6F_TM30":{"default_item":318,"flag":1089,"rom_address":2701931},"ITEM_MT_PYRE_EXTERIOR_MAX_POTION":{"default_item":20,"flag":1073,"rom_address":2701944},"ITEM_MT_PYRE_EXTERIOR_TM48":{"default_item":336,"flag":1074,"rom_address":2701957},"ITEM_NEW_MAUVILLE_ESCAPE_ROPE":{"default_item":85,"flag":1076,"rom_address":2701619},"ITEM_NEW_MAUVILLE_FULL_HEAL":{"default_item":23,"flag":1122,"rom_address":2701645},"ITEM_NEW_MAUVILLE_PARALYZE_HEAL":{"default_item":18,"flag":1123,"rom_address":2701658},"ITEM_NEW_MAUVILLE_THUNDER_STONE":{"default_item":96,"flag":1110,"rom_address":2701632},"ITEM_NEW_MAUVILLE_ULTRA_BALL":{"default_item":2,"flag":1075,"rom_address":2701606},"ITEM_PETALBURG_CITY_ETHER":{"default_item":34,"flag":1040,"rom_address":2701307},"ITEM_PETALBURG_CITY_MAX_REVIVE":{"default_item":25,"flag":1039,"rom_address":2701294},"ITEM_PETALBURG_WOODS_ETHER":{"default_item":34,"flag":1058,"rom_address":2701398},"ITEM_PETALBURG_WOODS_GREAT_BALL":{"default_item":3,"flag":1056,"rom_address":2701385},"ITEM_PETALBURG_WOODS_PARALYZE_HEAL":{"default_item":18,"flag":1117,"rom_address":2701411},"ITEM_PETALBURG_WOODS_X_ATTACK":{"default_item":75,"flag":1055,"rom_address":2701372},"ITEM_ROUTE_102_POTION":{"default_item":13,"flag":1000,"rom_address":2700306},"ITEM_ROUTE_103_GUARD_SPEC":{"default_item":73,"flag":1114,"rom_address":2700319},"ITEM_ROUTE_103_PP_UP":{"default_item":69,"flag":1137,"rom_address":2700332},"ITEM_ROUTE_104_POKE_BALL":{"default_item":4,"flag":1057,"rom_address":2700358},"ITEM_ROUTE_104_POTION":{"default_item":13,"flag":1135,"rom_address":2700384},"ITEM_ROUTE_104_PP_UP":{"default_item":69,"flag":1002,"rom_address":2700345},"ITEM_ROUTE_104_X_ACCURACY":{"default_item":78,"flag":1115,"rom_address":2700371},"ITEM_ROUTE_105_IRON":{"default_item":65,"flag":1003,"rom_address":2700397},"ITEM_ROUTE_106_PROTEIN":{"default_item":64,"flag":1004,"rom_address":2700410},"ITEM_ROUTE_108_STAR_PIECE":{"default_item":109,"flag":1139,"rom_address":2700423},"ITEM_ROUTE_109_POTION":{"default_item":13,"flag":1140,"rom_address":2700449},"ITEM_ROUTE_109_PP_UP":{"default_item":69,"flag":1005,"rom_address":2700436},"ITEM_ROUTE_110_DIRE_HIT":{"default_item":74,"flag":1007,"rom_address":2700475},"ITEM_ROUTE_110_ELIXIR":{"default_item":36,"flag":1141,"rom_address":2700488},"ITEM_ROUTE_110_RARE_CANDY":{"default_item":68,"flag":1006,"rom_address":2700462},"ITEM_ROUTE_111_ELIXIR":{"default_item":36,"flag":1142,"rom_address":2700540},"ITEM_ROUTE_111_HP_UP":{"default_item":63,"flag":1010,"rom_address":2700527},"ITEM_ROUTE_111_STARDUST":{"default_item":108,"flag":1009,"rom_address":2700514},"ITEM_ROUTE_111_TM37":{"default_item":325,"flag":1008,"rom_address":2700501},"ITEM_ROUTE_112_NUGGET":{"default_item":110,"flag":1011,"rom_address":2700553},"ITEM_ROUTE_113_HYPER_POTION":{"default_item":21,"flag":1143,"rom_address":2700592},"ITEM_ROUTE_113_MAX_ETHER":{"default_item":35,"flag":1012,"rom_address":2700566},"ITEM_ROUTE_113_SUPER_REPEL":{"default_item":83,"flag":1013,"rom_address":2700579},"ITEM_ROUTE_114_ENERGY_POWDER":{"default_item":30,"flag":1160,"rom_address":2700631},"ITEM_ROUTE_114_PROTEIN":{"default_item":64,"flag":1015,"rom_address":2700618},"ITEM_ROUTE_114_RARE_CANDY":{"default_item":68,"flag":1014,"rom_address":2700605},"ITEM_ROUTE_115_GREAT_BALL":{"default_item":3,"flag":1118,"rom_address":2700683},"ITEM_ROUTE_115_HEAL_POWDER":{"default_item":32,"flag":1144,"rom_address":2700696},"ITEM_ROUTE_115_IRON":{"default_item":65,"flag":1018,"rom_address":2700670},"ITEM_ROUTE_115_PP_UP":{"default_item":69,"flag":1161,"rom_address":2700709},"ITEM_ROUTE_115_SUPER_POTION":{"default_item":22,"flag":1016,"rom_address":2700644},"ITEM_ROUTE_115_TM01":{"default_item":289,"flag":1017,"rom_address":2700657},"ITEM_ROUTE_116_ETHER":{"default_item":34,"flag":1019,"rom_address":2700735},"ITEM_ROUTE_116_HP_UP":{"default_item":63,"flag":1021,"rom_address":2700761},"ITEM_ROUTE_116_POTION":{"default_item":13,"flag":1146,"rom_address":2700774},"ITEM_ROUTE_116_REPEL":{"default_item":86,"flag":1020,"rom_address":2700748},"ITEM_ROUTE_116_X_SPECIAL":{"default_item":79,"flag":1001,"rom_address":2700722},"ITEM_ROUTE_117_GREAT_BALL":{"default_item":3,"flag":1022,"rom_address":2700787},"ITEM_ROUTE_117_REVIVE":{"default_item":24,"flag":1023,"rom_address":2700800},"ITEM_ROUTE_118_HYPER_POTION":{"default_item":21,"flag":1121,"rom_address":2700813},"ITEM_ROUTE_119_ELIXIR_1":{"default_item":36,"flag":1026,"rom_address":2700852},"ITEM_ROUTE_119_ELIXIR_2":{"default_item":36,"flag":1147,"rom_address":2700917},"ITEM_ROUTE_119_HYPER_POTION_1":{"default_item":21,"flag":1029,"rom_address":2700891},"ITEM_ROUTE_119_HYPER_POTION_2":{"default_item":21,"flag":1106,"rom_address":2700904},"ITEM_ROUTE_119_LEAF_STONE":{"default_item":98,"flag":1027,"rom_address":2700865},"ITEM_ROUTE_119_NUGGET":{"default_item":110,"flag":1134,"rom_address":2702035},"ITEM_ROUTE_119_RARE_CANDY":{"default_item":68,"flag":1028,"rom_address":2700878},"ITEM_ROUTE_119_SUPER_REPEL":{"default_item":83,"flag":1024,"rom_address":2700826},"ITEM_ROUTE_119_ZINC":{"default_item":70,"flag":1025,"rom_address":2700839},"ITEM_ROUTE_120_FULL_HEAL":{"default_item":23,"flag":1031,"rom_address":2700943},"ITEM_ROUTE_120_HYPER_POTION":{"default_item":21,"flag":1107,"rom_address":2700956},"ITEM_ROUTE_120_NEST_BALL":{"default_item":8,"flag":1108,"rom_address":2700969},"ITEM_ROUTE_120_NUGGET":{"default_item":110,"flag":1030,"rom_address":2700930},"ITEM_ROUTE_120_REVIVE":{"default_item":24,"flag":1148,"rom_address":2700982},"ITEM_ROUTE_121_CARBOS":{"default_item":66,"flag":1103,"rom_address":2700995},"ITEM_ROUTE_121_REVIVE":{"default_item":24,"flag":1149,"rom_address":2701008},"ITEM_ROUTE_121_ZINC":{"default_item":70,"flag":1150,"rom_address":2701021},"ITEM_ROUTE_123_CALCIUM":{"default_item":67,"flag":1032,"rom_address":2701034},"ITEM_ROUTE_123_ELIXIR":{"default_item":36,"flag":1109,"rom_address":2701060},"ITEM_ROUTE_123_PP_UP":{"default_item":69,"flag":1152,"rom_address":2701073},"ITEM_ROUTE_123_REVIVAL_HERB":{"default_item":33,"flag":1153,"rom_address":2701086},"ITEM_ROUTE_123_ULTRA_BALL":{"default_item":2,"flag":1104,"rom_address":2701047},"ITEM_ROUTE_124_BLUE_SHARD":{"default_item":49,"flag":1093,"rom_address":2701112},"ITEM_ROUTE_124_RED_SHARD":{"default_item":48,"flag":1092,"rom_address":2701099},"ITEM_ROUTE_124_YELLOW_SHARD":{"default_item":50,"flag":1066,"rom_address":2701125},"ITEM_ROUTE_125_BIG_PEARL":{"default_item":107,"flag":1154,"rom_address":2701138},"ITEM_ROUTE_126_GREEN_SHARD":{"default_item":51,"flag":1105,"rom_address":2701151},"ITEM_ROUTE_127_CARBOS":{"default_item":66,"flag":1035,"rom_address":2701177},"ITEM_ROUTE_127_RARE_CANDY":{"default_item":68,"flag":1155,"rom_address":2701190},"ITEM_ROUTE_127_ZINC":{"default_item":70,"flag":1034,"rom_address":2701164},"ITEM_ROUTE_132_PROTEIN":{"default_item":64,"flag":1156,"rom_address":2701216},"ITEM_ROUTE_132_RARE_CANDY":{"default_item":68,"flag":1036,"rom_address":2701203},"ITEM_ROUTE_133_BIG_PEARL":{"default_item":107,"flag":1037,"rom_address":2701229},"ITEM_ROUTE_133_MAX_REVIVE":{"default_item":25,"flag":1157,"rom_address":2701255},"ITEM_ROUTE_133_STAR_PIECE":{"default_item":109,"flag":1038,"rom_address":2701242},"ITEM_ROUTE_134_CARBOS":{"default_item":66,"flag":1158,"rom_address":2701268},"ITEM_ROUTE_134_STAR_PIECE":{"default_item":109,"flag":1159,"rom_address":2701281},"ITEM_RUSTBORO_CITY_X_DEFEND":{"default_item":76,"flag":1041,"rom_address":2701333},"ITEM_RUSTURF_TUNNEL_MAX_ETHER":{"default_item":35,"flag":1049,"rom_address":2701437},"ITEM_RUSTURF_TUNNEL_POKE_BALL":{"default_item":4,"flag":1048,"rom_address":2701424},"ITEM_SAFARI_ZONE_NORTH_CALCIUM":{"default_item":67,"flag":1119,"rom_address":2701827},"ITEM_SAFARI_ZONE_NORTH_EAST_NUGGET":{"default_item":110,"flag":1169,"rom_address":2701853},"ITEM_SAFARI_ZONE_NORTH_WEST_TM22":{"default_item":310,"flag":1094,"rom_address":2701814},"ITEM_SAFARI_ZONE_SOUTH_EAST_BIG_PEARL":{"default_item":107,"flag":1170,"rom_address":2701866},"ITEM_SAFARI_ZONE_SOUTH_WEST_MAX_REVIVE":{"default_item":25,"flag":1131,"rom_address":2701840},"ITEM_SCORCHED_SLAB_TM11":{"default_item":299,"flag":1079,"rom_address":2701801},"ITEM_SEAFLOOR_CAVERN_ROOM_9_TM26":{"default_item":314,"flag":1090,"rom_address":2702139},"ITEM_SHOAL_CAVE_ENTRANCE_BIG_PEARL":{"default_item":107,"flag":1081,"rom_address":2702074},"ITEM_SHOAL_CAVE_ICE_ROOM_NEVER_MELT_ICE":{"default_item":212,"flag":1113,"rom_address":2702126},"ITEM_SHOAL_CAVE_ICE_ROOM_TM07":{"default_item":295,"flag":1112,"rom_address":2702113},"ITEM_SHOAL_CAVE_INNER_ROOM_RARE_CANDY":{"default_item":68,"flag":1082,"rom_address":2702087},"ITEM_SHOAL_CAVE_STAIRS_ROOM_ICE_HEAL":{"default_item":16,"flag":1083,"rom_address":2702100},"ITEM_TRICK_HOUSE_PUZZLE_1_ORANGE_MAIL":{"default_item":121,"flag":1060,"rom_address":2702152},"ITEM_TRICK_HOUSE_PUZZLE_2_HARBOR_MAIL":{"default_item":122,"flag":1061,"rom_address":2702165},"ITEM_TRICK_HOUSE_PUZZLE_2_WAVE_MAIL":{"default_item":126,"flag":1062,"rom_address":2702178},"ITEM_TRICK_HOUSE_PUZZLE_3_SHADOW_MAIL":{"default_item":128,"flag":1063,"rom_address":2702191},"ITEM_TRICK_HOUSE_PUZZLE_3_WOOD_MAIL":{"default_item":125,"flag":1064,"rom_address":2702204},"ITEM_TRICK_HOUSE_PUZZLE_4_MECH_MAIL":{"default_item":124,"flag":1065,"rom_address":2702217},"ITEM_TRICK_HOUSE_PUZZLE_6_GLITTER_MAIL":{"default_item":123,"flag":1067,"rom_address":2702230},"ITEM_TRICK_HOUSE_PUZZLE_7_TROPIC_MAIL":{"default_item":129,"flag":1068,"rom_address":2702243},"ITEM_TRICK_HOUSE_PUZZLE_8_BEAD_MAIL":{"default_item":127,"flag":1069,"rom_address":2702256},"ITEM_VICTORY_ROAD_1F_MAX_ELIXIR":{"default_item":37,"flag":1084,"rom_address":2702269},"ITEM_VICTORY_ROAD_1F_PP_UP":{"default_item":69,"flag":1085,"rom_address":2702282},"ITEM_VICTORY_ROAD_B1F_FULL_RESTORE":{"default_item":19,"flag":1087,"rom_address":2702308},"ITEM_VICTORY_ROAD_B1F_TM29":{"default_item":317,"flag":1086,"rom_address":2702295},"ITEM_VICTORY_ROAD_B2F_FULL_HEAL":{"default_item":23,"flag":1088,"rom_address":2702321},"NPC_GIFT_GOT_BASEMENT_KEY_FROM_WATTSON":{"default_item":271,"flag":208,"rom_address":1967134},"NPC_GIFT_GOT_TM24_FROM_WATTSON":{"default_item":312,"flag":209,"rom_address":1967168},"NPC_GIFT_RECEIVED_6_SODA_POP":{"default_item":27,"flag":140,"rom_address":2537096},"NPC_GIFT_RECEIVED_ACRO_BIKE":{"default_item":272,"flag":1181,"rom_address":2164456},"NPC_GIFT_RECEIVED_AMULET_COIN":{"default_item":189,"flag":133,"rom_address":2708208},"NPC_GIFT_RECEIVED_CHARCOAL":{"default_item":215,"flag":254,"rom_address":2096554},"NPC_GIFT_RECEIVED_CHESTO_BERRY_ROUTE_104":{"default_item":134,"flag":246,"rom_address":2022873},"NPC_GIFT_RECEIVED_CLEANSE_TAG":{"default_item":190,"flag":282,"rom_address":2305748},"NPC_GIFT_RECEIVED_COIN_CASE":{"default_item":260,"flag":258,"rom_address":2172913},"NPC_GIFT_RECEIVED_DEEP_SEA_SCALE":{"default_item":193,"flag":1190,"rom_address":2156474},"NPC_GIFT_RECEIVED_DEEP_SEA_TOOTH":{"default_item":192,"flag":1191,"rom_address":2156462},"NPC_GIFT_RECEIVED_DEVON_GOODS_RUSTURF_TUNNEL":{"default_item":269,"flag":1172,"rom_address":2289508},"NPC_GIFT_RECEIVED_DEVON_SCOPE":{"default_item":288,"flag":285,"rom_address":2059186},"NPC_GIFT_RECEIVED_EXP_SHARE":{"default_item":182,"flag":272,"rom_address":2179378},"NPC_GIFT_RECEIVED_FOCUS_BAND":{"default_item":196,"flag":283,"rom_address":2331353},"NPC_GIFT_RECEIVED_GOOD_ROD":{"default_item":263,"flag":227,"rom_address":2052491},"NPC_GIFT_RECEIVED_GO_GOGGLES":{"default_item":279,"flag":221,"rom_address":2011954},"NPC_GIFT_RECEIVED_GREAT_BALL_PETALBURG_WOODS":{"default_item":3,"flag":1171,"rom_address":2293794},"NPC_GIFT_RECEIVED_GREAT_BALL_RUSTBORO_CITY":{"default_item":3,"flag":1173,"rom_address":1972558},"NPC_GIFT_RECEIVED_HM01":{"default_item":339,"flag":137,"rom_address":2193371},"NPC_GIFT_RECEIVED_HM02":{"default_item":340,"flag":110,"rom_address":2054604},"NPC_GIFT_RECEIVED_HM03":{"default_item":341,"flag":122,"rom_address":2120645},"NPC_GIFT_RECEIVED_HM04":{"default_item":342,"flag":106,"rom_address":2289001},"NPC_GIFT_RECEIVED_HM05":{"default_item":343,"flag":109,"rom_address":2291966},"NPC_GIFT_RECEIVED_HM06":{"default_item":344,"flag":107,"rom_address":2167989},"NPC_GIFT_RECEIVED_HM07":{"default_item":345,"flag":312,"rom_address":1995198},"NPC_GIFT_RECEIVED_HM08":{"default_item":346,"flag":123,"rom_address":2245872},"NPC_GIFT_RECEIVED_ITEMFINDER":{"default_item":261,"flag":1176,"rom_address":2034013},"NPC_GIFT_RECEIVED_KINGS_ROCK":{"default_item":187,"flag":276,"rom_address":1989011},"NPC_GIFT_RECEIVED_LETTER":{"default_item":274,"flag":1174,"rom_address":2179156},"NPC_GIFT_RECEIVED_MACHO_BRACE":{"default_item":181,"flag":277,"rom_address":2278172},"NPC_GIFT_RECEIVED_MACH_BIKE":{"default_item":259,"flag":1180,"rom_address":2164441},"NPC_GIFT_RECEIVED_MAGMA_EMBLEM":{"default_item":375,"flag":1177,"rom_address":2310318},"NPC_GIFT_RECEIVED_MENTAL_HERB":{"default_item":185,"flag":223,"rom_address":2201924},"NPC_GIFT_RECEIVED_METEORITE":{"default_item":280,"flag":115,"rom_address":2297863},"NPC_GIFT_RECEIVED_MIRACLE_SEED":{"default_item":205,"flag":297,"rom_address":2294010},"NPC_GIFT_RECEIVED_OLD_ROD":{"default_item":262,"flag":257,"rom_address":2007886},"NPC_GIFT_RECEIVED_POKEBLOCK_CASE":{"default_item":273,"flag":95,"rom_address":2606136},"NPC_GIFT_RECEIVED_POTION_OLDALE":{"default_item":13,"flag":132,"rom_address":2006235},"NPC_GIFT_RECEIVED_POWDER_JAR":{"default_item":372,"flag":337,"rom_address":1957927},"NPC_GIFT_RECEIVED_PREMIER_BALL_RUSTBORO":{"default_item":12,"flag":213,"rom_address":2194408},"NPC_GIFT_RECEIVED_QUICK_CLAW":{"default_item":183,"flag":275,"rom_address":2186071},"NPC_GIFT_RECEIVED_REPEAT_BALL":{"default_item":9,"flag":256,"rom_address":2047827},"NPC_GIFT_RECEIVED_SECRET_POWER":{"default_item":331,"flag":96,"rom_address":2591201},"NPC_GIFT_RECEIVED_SILK_SCARF":{"default_item":217,"flag":289,"rom_address":2095828},"NPC_GIFT_RECEIVED_SOFT_SAND":{"default_item":203,"flag":280,"rom_address":2029841},"NPC_GIFT_RECEIVED_SOOTHE_BELL":{"default_item":184,"flag":278,"rom_address":2145210},"NPC_GIFT_RECEIVED_SS_TICKET":{"default_item":265,"flag":291,"rom_address":2708464},"NPC_GIFT_RECEIVED_SUN_STONE_MOSSDEEP":{"default_item":93,"flag":192,"rom_address":2248181},"NPC_GIFT_RECEIVED_SUPER_ROD":{"default_item":264,"flag":152,"rom_address":2245339},"NPC_GIFT_RECEIVED_TM03":{"default_item":291,"flag":172,"rom_address":2256148},"NPC_GIFT_RECEIVED_TM04":{"default_item":292,"flag":171,"rom_address":2237855},"NPC_GIFT_RECEIVED_TM05":{"default_item":293,"flag":231,"rom_address":2045877},"NPC_GIFT_RECEIVED_TM08":{"default_item":296,"flag":166,"rom_address":2089212},"NPC_GIFT_RECEIVED_TM09":{"default_item":297,"flag":262,"rom_address":2023076},"NPC_GIFT_RECEIVED_TM10":{"default_item":298,"flag":264,"rom_address":2200728},"NPC_GIFT_RECEIVED_TM19":{"default_item":307,"flag":232,"rom_address":2062050},"NPC_GIFT_RECEIVED_TM21":{"default_item":309,"flag":1179,"rom_address":2118086},"NPC_GIFT_RECEIVED_TM27":{"default_item":315,"flag":229,"rom_address":2107533},"NPC_GIFT_RECEIVED_TM27_2":{"default_item":315,"flag":1178,"rom_address":2118033},"NPC_GIFT_RECEIVED_TM28":{"default_item":316,"flag":261,"rom_address":2280367},"NPC_GIFT_RECEIVED_TM31":{"default_item":319,"flag":121,"rom_address":2262824},"NPC_GIFT_RECEIVED_TM34":{"default_item":322,"flag":167,"rom_address":2161230},"NPC_GIFT_RECEIVED_TM36":{"default_item":324,"flag":230,"rom_address":2093189},"NPC_GIFT_RECEIVED_TM39":{"default_item":327,"flag":165,"rom_address":2181934},"NPC_GIFT_RECEIVED_TM40":{"default_item":328,"flag":170,"rom_address":2196031},"NPC_GIFT_RECEIVED_TM41":{"default_item":329,"flag":265,"rom_address":2139219},"NPC_GIFT_RECEIVED_TM42":{"default_item":330,"flag":169,"rom_address":2123871},"NPC_GIFT_RECEIVED_TM44":{"default_item":332,"flag":234,"rom_address":2230771},"NPC_GIFT_RECEIVED_TM45":{"default_item":333,"flag":235,"rom_address":2110398},"NPC_GIFT_RECEIVED_TM46":{"default_item":334,"flag":269,"rom_address":2148628},"NPC_GIFT_RECEIVED_TM47":{"default_item":335,"flag":1175,"rom_address":2292543},"NPC_GIFT_RECEIVED_TM49":{"default_item":337,"flag":260,"rom_address":2354181},"NPC_GIFT_RECEIVED_TM50":{"default_item":338,"flag":168,"rom_address":2097316},"NPC_GIFT_RECEIVED_WAILMER_PAIL":{"default_item":268,"flag":94,"rom_address":2278027},"NPC_GIFT_RECEIVED_WHITE_HERB":{"default_item":180,"flag":279,"rom_address":2022938}},"maps":{"MAP_ABANDONED_SHIP_CAPTAINS_OFFICE":{"fishing_encounters":null,"header_rom_address":4748492,"land_encounters":null,"warp_table_rom_address":5478884,"water_encounters":null},"MAP_ABANDONED_SHIP_CORRIDORS_1F":{"fishing_encounters":null,"header_rom_address":4748268,"land_encounters":null,"warp_table_rom_address":5477960,"water_encounters":null},"MAP_ABANDONED_SHIP_CORRIDORS_B1F":{"fishing_encounters":null,"header_rom_address":4748324,"land_encounters":null,"warp_table_rom_address":5478288,"water_encounters":null},"MAP_ABANDONED_SHIP_DECK":{"fishing_encounters":null,"header_rom_address":4748240,"land_encounters":null,"warp_table_rom_address":5477852,"water_encounters":null},"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS":{"fishing_encounters":{"encounter_slots":[129,72,129,72,72,72,72,73,73,73],"rom_address":5589416},"header_rom_address":4748548,"land_encounters":null,"warp_table_rom_address":5478948,"water_encounters":{"encounter_slots":[72,72,72,72,73],"rom_address":5589388}},"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS":{"fishing_encounters":null,"header_rom_address":4748576,"land_encounters":null,"warp_table_rom_address":5479160,"water_encounters":null},"MAP_ABANDONED_SHIP_ROOMS2_1F":{"fishing_encounters":null,"header_rom_address":4748464,"land_encounters":null,"warp_table_rom_address":5478792,"water_encounters":null},"MAP_ABANDONED_SHIP_ROOMS2_B1F":{"fishing_encounters":null,"header_rom_address":4748380,"land_encounters":null,"warp_table_rom_address":5478524,"water_encounters":null},"MAP_ABANDONED_SHIP_ROOMS_1F":{"fishing_encounters":null,"header_rom_address":4748296,"land_encounters":null,"warp_table_rom_address":5478172,"water_encounters":null},"MAP_ABANDONED_SHIP_ROOMS_B1F":{"fishing_encounters":{"encounter_slots":[129,72,129,72,72,72,72,73,73,73],"rom_address":5586652},"header_rom_address":4748352,"land_encounters":null,"warp_table_rom_address":5478432,"water_encounters":{"encounter_slots":[72,72,72,72,73],"rom_address":5586624}},"MAP_ABANDONED_SHIP_ROOM_B1F":{"fishing_encounters":null,"header_rom_address":4748436,"land_encounters":null,"warp_table_rom_address":5478636,"water_encounters":null},"MAP_ABANDONED_SHIP_UNDERWATER1":{"fishing_encounters":null,"header_rom_address":4748408,"land_encounters":null,"warp_table_rom_address":5478576,"water_encounters":null},"MAP_ABANDONED_SHIP_UNDERWATER2":{"fishing_encounters":null,"header_rom_address":4748520,"land_encounters":null,"warp_table_rom_address":5478920,"water_encounters":null},"MAP_ALTERING_CAVE":{"fishing_encounters":null,"header_rom_address":4749696,"land_encounters":{"encounter_slots":[41,41,41,41,41,41,41,41,41,41,41,41],"rom_address":5593728},"warp_table_rom_address":5482476,"water_encounters":null},"MAP_ANCIENT_TOMB":{"fishing_encounters":null,"header_rom_address":4748632,"land_encounters":null,"warp_table_rom_address":5479500,"water_encounters":null},"MAP_AQUA_HIDEOUT_1F":{"fishing_encounters":null,"header_rom_address":4747372,"land_encounters":null,"warp_table_rom_address":5472932,"water_encounters":null},"MAP_AQUA_HIDEOUT_B1F":{"fishing_encounters":null,"header_rom_address":4747400,"land_encounters":null,"warp_table_rom_address":5473192,"water_encounters":null},"MAP_AQUA_HIDEOUT_B2F":{"fishing_encounters":null,"header_rom_address":4747428,"land_encounters":null,"warp_table_rom_address":5473556,"water_encounters":null},"MAP_AQUA_HIDEOUT_UNUSED_RUBY_MAP1":{"fishing_encounters":null,"header_rom_address":4748800,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_AQUA_HIDEOUT_UNUSED_RUBY_MAP2":{"fishing_encounters":null,"header_rom_address":4748828,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_AQUA_HIDEOUT_UNUSED_RUBY_MAP3":{"fishing_encounters":null,"header_rom_address":4748856,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_ARTISAN_CAVE_1F":{"fishing_encounters":null,"header_rom_address":4749528,"land_encounters":{"encounter_slots":[235,235,235,235,235,235,235,235,235,235,235,235],"rom_address":5593672},"warp_table_rom_address":5482212,"water_encounters":null},"MAP_ARTISAN_CAVE_B1F":{"fishing_encounters":null,"header_rom_address":4749500,"land_encounters":{"encounter_slots":[235,235,235,235,235,235,235,235,235,235,235,235],"rom_address":5593616},"warp_table_rom_address":5482104,"water_encounters":null},"MAP_BATTLE_COLOSSEUM_2P":{"fishing_encounters":null,"header_rom_address":4750424,"land_encounters":null,"warp_table_rom_address":5491892,"water_encounters":null},"MAP_BATTLE_COLOSSEUM_4P":{"fishing_encounters":null,"header_rom_address":4750508,"land_encounters":null,"warp_table_rom_address":5492192,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_ARENA_BATTLE_ROOM":{"fishing_encounters":null,"header_rom_address":4752300,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_ARENA_CORRIDOR":{"fishing_encounters":null,"header_rom_address":4752272,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY":{"fishing_encounters":null,"header_rom_address":4752244,"land_encounters":null,"warp_table_rom_address":5502948,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_DOME_BATTLE_ROOM":{"fishing_encounters":null,"header_rom_address":4752048,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_DOME_CORRIDOR":{"fishing_encounters":null,"header_rom_address":4751992,"land_encounters":null,"warp_table_rom_address":5501116,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY":{"fishing_encounters":null,"header_rom_address":4751964,"land_encounters":null,"warp_table_rom_address":5501008,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_DOME_PRE_BATTLE_ROOM":{"fishing_encounters":null,"header_rom_address":4752020,"land_encounters":null,"warp_table_rom_address":5501176,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_BATTLE_ROOM":{"fishing_encounters":null,"header_rom_address":4752384,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY":{"fishing_encounters":null,"header_rom_address":4752328,"land_encounters":null,"warp_table_rom_address":5503424,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_PRE_BATTLE_ROOM":{"fishing_encounters":null,"header_rom_address":4752356,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM":{"fishing_encounters":null,"header_rom_address":4752132,"land_encounters":null,"warp_table_rom_address":5502156,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR":{"fishing_encounters":null,"header_rom_address":4752104,"land_encounters":null,"warp_table_rom_address":5501984,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY":{"fishing_encounters":null,"header_rom_address":4752076,"land_encounters":null,"warp_table_rom_address":5501736,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_PIKE_CORRIDOR":{"fishing_encounters":null,"header_rom_address":4752440,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY":{"fishing_encounters":null,"header_rom_address":4752412,"land_encounters":null,"warp_table_rom_address":5503848,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_FINAL":{"fishing_encounters":null,"header_rom_address":4752524,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_NORMAL":{"fishing_encounters":null,"header_rom_address":4752496,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS":{"fishing_encounters":null,"header_rom_address":4752552,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_PIKE_THREE_PATH_ROOM":{"fishing_encounters":null,"header_rom_address":4752468,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR":{"fishing_encounters":null,"header_rom_address":4752188,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY":{"fishing_encounters":null,"header_rom_address":4752160,"land_encounters":null,"warp_table_rom_address":5502288,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_TOP":{"fishing_encounters":null,"header_rom_address":4752216,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM":{"fishing_encounters":null,"header_rom_address":4751684,"land_encounters":null,"warp_table_rom_address":5498736,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_TOWER_CORRIDOR":{"fishing_encounters":null,"header_rom_address":4751656,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_TOWER_ELEVATOR":{"fishing_encounters":null,"header_rom_address":4751628,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY":{"fishing_encounters":null,"header_rom_address":4751600,"land_encounters":null,"warp_table_rom_address":5498472,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_BATTLE_ROOM":{"fishing_encounters":null,"header_rom_address":4751936,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_CORRIDOR":{"fishing_encounters":null,"header_rom_address":4751908,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_PARTNER_ROOM":{"fishing_encounters":null,"header_rom_address":4751880,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER":{"fishing_encounters":null,"header_rom_address":4752636,"land_encounters":null,"warp_table_rom_address":5505096,"water_encounters":null},"MAP_BATTLE_FRONTIER_LOUNGE1":{"fishing_encounters":null,"header_rom_address":4752608,"land_encounters":null,"warp_table_rom_address":5504852,"water_encounters":null},"MAP_BATTLE_FRONTIER_LOUNGE2":{"fishing_encounters":null,"header_rom_address":4752664,"land_encounters":null,"warp_table_rom_address":5505260,"water_encounters":null},"MAP_BATTLE_FRONTIER_LOUNGE3":{"fishing_encounters":null,"header_rom_address":4752692,"land_encounters":null,"warp_table_rom_address":5505416,"water_encounters":null},"MAP_BATTLE_FRONTIER_LOUNGE4":{"fishing_encounters":null,"header_rom_address":4752720,"land_encounters":null,"warp_table_rom_address":5505516,"water_encounters":null},"MAP_BATTLE_FRONTIER_LOUNGE5":{"fishing_encounters":null,"header_rom_address":4752776,"land_encounters":null,"warp_table_rom_address":5505700,"water_encounters":null},"MAP_BATTLE_FRONTIER_LOUNGE6":{"fishing_encounters":null,"header_rom_address":4752804,"land_encounters":null,"warp_table_rom_address":5505760,"water_encounters":null},"MAP_BATTLE_FRONTIER_LOUNGE7":{"fishing_encounters":null,"header_rom_address":4752832,"land_encounters":null,"warp_table_rom_address":5505884,"water_encounters":null},"MAP_BATTLE_FRONTIER_LOUNGE8":{"fishing_encounters":null,"header_rom_address":4752888,"land_encounters":null,"warp_table_rom_address":5506140,"water_encounters":null},"MAP_BATTLE_FRONTIER_LOUNGE9":{"fishing_encounters":null,"header_rom_address":4752916,"land_encounters":null,"warp_table_rom_address":5506192,"water_encounters":null},"MAP_BATTLE_FRONTIER_MART":{"fishing_encounters":null,"header_rom_address":4753000,"land_encounters":null,"warp_table_rom_address":5506628,"water_encounters":null},"MAP_BATTLE_FRONTIER_OUTSIDE_EAST":{"fishing_encounters":null,"header_rom_address":4751852,"land_encounters":null,"warp_table_rom_address":5500120,"water_encounters":null},"MAP_BATTLE_FRONTIER_OUTSIDE_WEST":{"fishing_encounters":null,"header_rom_address":4751572,"land_encounters":null,"warp_table_rom_address":5498088,"water_encounters":null},"MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4752944,"land_encounters":null,"warp_table_rom_address":5506348,"water_encounters":null},"MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4752972,"land_encounters":null,"warp_table_rom_address":5506488,"water_encounters":null},"MAP_BATTLE_FRONTIER_RANKING_HALL":{"fishing_encounters":null,"header_rom_address":4752580,"land_encounters":null,"warp_table_rom_address":5504600,"water_encounters":null},"MAP_BATTLE_FRONTIER_RECEPTION_GATE":{"fishing_encounters":null,"header_rom_address":4752860,"land_encounters":null,"warp_table_rom_address":5506032,"water_encounters":null},"MAP_BATTLE_FRONTIER_SCOTTS_HOUSE":{"fishing_encounters":null,"header_rom_address":4752748,"land_encounters":null,"warp_table_rom_address":5505568,"water_encounters":null},"MAP_BATTLE_PYRAMID_SQUARE01":{"fishing_encounters":null,"header_rom_address":4750984,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_PYRAMID_SQUARE02":{"fishing_encounters":null,"header_rom_address":4751012,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_PYRAMID_SQUARE03":{"fishing_encounters":null,"header_rom_address":4751040,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_PYRAMID_SQUARE04":{"fishing_encounters":null,"header_rom_address":4751068,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_PYRAMID_SQUARE05":{"fishing_encounters":null,"header_rom_address":4751096,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_PYRAMID_SQUARE06":{"fishing_encounters":null,"header_rom_address":4751124,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_PYRAMID_SQUARE07":{"fishing_encounters":null,"header_rom_address":4751152,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_PYRAMID_SQUARE08":{"fishing_encounters":null,"header_rom_address":4751180,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_PYRAMID_SQUARE09":{"fishing_encounters":null,"header_rom_address":4751208,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_PYRAMID_SQUARE10":{"fishing_encounters":null,"header_rom_address":4751236,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_PYRAMID_SQUARE11":{"fishing_encounters":null,"header_rom_address":4751264,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_PYRAMID_SQUARE12":{"fishing_encounters":null,"header_rom_address":4751292,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_PYRAMID_SQUARE13":{"fishing_encounters":null,"header_rom_address":4751320,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_PYRAMID_SQUARE14":{"fishing_encounters":null,"header_rom_address":4751348,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_PYRAMID_SQUARE15":{"fishing_encounters":null,"header_rom_address":4751376,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BATTLE_PYRAMID_SQUARE16":{"fishing_encounters":null,"header_rom_address":4751404,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_BIRTH_ISLAND_EXTERIOR":{"fishing_encounters":null,"header_rom_address":4753084,"land_encounters":null,"warp_table_rom_address":5506916,"water_encounters":null},"MAP_BIRTH_ISLAND_HARBOR":{"fishing_encounters":null,"header_rom_address":4753112,"land_encounters":null,"warp_table_rom_address":5506992,"water_encounters":null},"MAP_CAVE_OF_ORIGIN_1F":{"fishing_encounters":null,"header_rom_address":4747792,"land_encounters":{"encounter_slots":[41,41,41,322,322,322,41,41,42,42,42,42],"rom_address":5590196},"warp_table_rom_address":5475480,"water_encounters":null},"MAP_CAVE_OF_ORIGIN_B1F":{"fishing_encounters":null,"header_rom_address":4747904,"land_encounters":null,"warp_table_rom_address":5475648,"water_encounters":null},"MAP_CAVE_OF_ORIGIN_ENTRANCE":{"fishing_encounters":null,"header_rom_address":4747764,"land_encounters":{"encounter_slots":[41,41,41,41,41,41,41,41,42,42,42,42],"rom_address":5590140},"warp_table_rom_address":5475444,"water_encounters":null},"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1":{"fishing_encounters":null,"header_rom_address":4747820,"land_encounters":{"encounter_slots":[41,41,41,322,322,322,41,41,42,42,42,42],"rom_address":5590252},"warp_table_rom_address":5475516,"water_encounters":null},"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2":{"fishing_encounters":null,"header_rom_address":4747848,"land_encounters":{"encounter_slots":[41,41,41,322,322,322,41,41,42,42,42,42],"rom_address":5590308},"warp_table_rom_address":5475552,"water_encounters":null},"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3":{"fishing_encounters":null,"header_rom_address":4747876,"land_encounters":{"encounter_slots":[41,41,41,322,322,322,41,41,42,42,42,42],"rom_address":5590364},"warp_table_rom_address":5475588,"water_encounters":null},"MAP_CONTEST_HALL":{"fishing_encounters":null,"header_rom_address":4750536,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_CONTEST_HALL_BEAUTY":{"fishing_encounters":null,"header_rom_address":4750732,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_CONTEST_HALL_COOL":{"fishing_encounters":null,"header_rom_address":4750788,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_CONTEST_HALL_CUTE":{"fishing_encounters":null,"header_rom_address":4750844,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_CONTEST_HALL_SMART":{"fishing_encounters":null,"header_rom_address":4750816,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_CONTEST_HALL_TOUGH":{"fishing_encounters":null,"header_rom_address":4750760,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_DESERT_RUINS":{"fishing_encounters":null,"header_rom_address":4746896,"land_encounters":null,"warp_table_rom_address":5468868,"water_encounters":null},"MAP_DESERT_UNDERPASS":{"fishing_encounters":null,"header_rom_address":4749472,"land_encounters":{"encounter_slots":[132,370,132,371,132,370,371,132,370,132,371,132],"rom_address":5593560},"warp_table_rom_address":5482052,"water_encounters":null},"MAP_DEWFORD_TOWN":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,313,313,313],"rom_address":5591916},"header_rom_address":4740372,"land_encounters":null,"warp_table_rom_address":5417220,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5591888}},"MAP_DEWFORD_TOWN_GYM":{"fishing_encounters":null,"header_rom_address":4742024,"land_encounters":null,"warp_table_rom_address":5442380,"water_encounters":null},"MAP_DEWFORD_TOWN_HALL":{"fishing_encounters":null,"header_rom_address":4742052,"land_encounters":null,"warp_table_rom_address":5442680,"water_encounters":null},"MAP_DEWFORD_TOWN_HOUSE1":{"fishing_encounters":null,"header_rom_address":4741940,"land_encounters":null,"warp_table_rom_address":5441896,"water_encounters":null},"MAP_DEWFORD_TOWN_HOUSE2":{"fishing_encounters":null,"header_rom_address":4742080,"land_encounters":null,"warp_table_rom_address":5442788,"water_encounters":null},"MAP_DEWFORD_TOWN_POKEMON_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4741968,"land_encounters":null,"warp_table_rom_address":5442004,"water_encounters":null},"MAP_DEWFORD_TOWN_POKEMON_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4741996,"land_encounters":null,"warp_table_rom_address":5442144,"water_encounters":null},"MAP_EVER_GRANDE_CITY":{"fishing_encounters":{"encounter_slots":[129,72,129,325,313,325,313,222,313,313],"rom_address":5592220},"header_rom_address":4740288,"land_encounters":null,"warp_table_rom_address":5416112,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5592192}},"MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM":{"fishing_encounters":null,"header_rom_address":4746084,"land_encounters":null,"warp_table_rom_address":5465760,"water_encounters":null},"MAP_EVER_GRANDE_CITY_DRAKES_ROOM":{"fishing_encounters":null,"header_rom_address":4746056,"land_encounters":null,"warp_table_rom_address":5465652,"water_encounters":null},"MAP_EVER_GRANDE_CITY_GLACIAS_ROOM":{"fishing_encounters":null,"header_rom_address":4746028,"land_encounters":null,"warp_table_rom_address":5465592,"water_encounters":null},"MAP_EVER_GRANDE_CITY_HALL1":{"fishing_encounters":null,"header_rom_address":4746112,"land_encounters":null,"warp_table_rom_address":5465796,"water_encounters":null},"MAP_EVER_GRANDE_CITY_HALL2":{"fishing_encounters":null,"header_rom_address":4746140,"land_encounters":null,"warp_table_rom_address":5465848,"water_encounters":null},"MAP_EVER_GRANDE_CITY_HALL3":{"fishing_encounters":null,"header_rom_address":4746168,"land_encounters":null,"warp_table_rom_address":5465900,"water_encounters":null},"MAP_EVER_GRANDE_CITY_HALL4":{"fishing_encounters":null,"header_rom_address":4746196,"land_encounters":null,"warp_table_rom_address":5465952,"water_encounters":null},"MAP_EVER_GRANDE_CITY_HALL5":{"fishing_encounters":null,"header_rom_address":4746224,"land_encounters":null,"warp_table_rom_address":5465988,"water_encounters":null},"MAP_EVER_GRANDE_CITY_HALL_OF_FAME":{"fishing_encounters":null,"header_rom_address":4746280,"land_encounters":null,"warp_table_rom_address":5466220,"water_encounters":null},"MAP_EVER_GRANDE_CITY_PHOEBES_ROOM":{"fishing_encounters":null,"header_rom_address":4746000,"land_encounters":null,"warp_table_rom_address":5465532,"water_encounters":null},"MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4746308,"land_encounters":null,"warp_table_rom_address":5466344,"water_encounters":null},"MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4746336,"land_encounters":null,"warp_table_rom_address":5466484,"water_encounters":null},"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F":{"fishing_encounters":null,"header_rom_address":4746252,"land_encounters":null,"warp_table_rom_address":5466136,"water_encounters":null},"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F":{"fishing_encounters":null,"header_rom_address":4746364,"land_encounters":null,"warp_table_rom_address":5466624,"water_encounters":null},"MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM":{"fishing_encounters":null,"header_rom_address":4745972,"land_encounters":null,"warp_table_rom_address":5465472,"water_encounters":null},"MAP_FALLARBOR_TOWN":{"fishing_encounters":null,"header_rom_address":4740428,"land_encounters":null,"warp_table_rom_address":5417832,"water_encounters":null},"MAP_FALLARBOR_TOWN_BATTLE_TENT_BATTLE_ROOM":{"fishing_encounters":null,"header_rom_address":4742388,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_FALLARBOR_TOWN_BATTLE_TENT_CORRIDOR":{"fishing_encounters":null,"header_rom_address":4742360,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_FALLARBOR_TOWN_BATTLE_TENT_LOBBY":{"fishing_encounters":null,"header_rom_address":4742332,"land_encounters":null,"warp_table_rom_address":5444416,"water_encounters":null},"MAP_FALLARBOR_TOWN_COZMOS_HOUSE":{"fishing_encounters":null,"header_rom_address":4742472,"land_encounters":null,"warp_table_rom_address":5444928,"water_encounters":null},"MAP_FALLARBOR_TOWN_MART":{"fishing_encounters":null,"header_rom_address":4742304,"land_encounters":null,"warp_table_rom_address":5444260,"water_encounters":null},"MAP_FALLARBOR_TOWN_MOVE_RELEARNERS_HOUSE":{"fishing_encounters":null,"header_rom_address":4742500,"land_encounters":null,"warp_table_rom_address":5444988,"water_encounters":null},"MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4742416,"land_encounters":null,"warp_table_rom_address":5444696,"water_encounters":null},"MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4742444,"land_encounters":null,"warp_table_rom_address":5444836,"water_encounters":null},"MAP_FARAWAY_ISLAND_ENTRANCE":{"fishing_encounters":null,"header_rom_address":4753028,"land_encounters":null,"warp_table_rom_address":5506712,"water_encounters":null},"MAP_FARAWAY_ISLAND_INTERIOR":{"fishing_encounters":null,"header_rom_address":4753056,"land_encounters":null,"warp_table_rom_address":5506832,"water_encounters":null},"MAP_FIERY_PATH":{"fishing_encounters":null,"header_rom_address":4747120,"land_encounters":{"encounter_slots":[339,109,339,66,321,218,109,66,321,321,88,88],"rom_address":5586784},"warp_table_rom_address":5471384,"water_encounters":null},"MAP_FORTREE_CITY":{"fishing_encounters":null,"header_rom_address":4740176,"land_encounters":null,"warp_table_rom_address":5413740,"water_encounters":null},"MAP_FORTREE_CITY_DECORATION_SHOP":{"fishing_encounters":null,"header_rom_address":4744516,"land_encounters":null,"warp_table_rom_address":5455976,"water_encounters":null},"MAP_FORTREE_CITY_GYM":{"fishing_encounters":null,"header_rom_address":4744292,"land_encounters":null,"warp_table_rom_address":5455024,"water_encounters":null},"MAP_FORTREE_CITY_HOUSE1":{"fishing_encounters":null,"header_rom_address":4744264,"land_encounters":null,"warp_table_rom_address":5454796,"water_encounters":null},"MAP_FORTREE_CITY_HOUSE2":{"fishing_encounters":null,"header_rom_address":4744404,"land_encounters":null,"warp_table_rom_address":5455544,"water_encounters":null},"MAP_FORTREE_CITY_HOUSE3":{"fishing_encounters":null,"header_rom_address":4744432,"land_encounters":null,"warp_table_rom_address":5455628,"water_encounters":null},"MAP_FORTREE_CITY_HOUSE4":{"fishing_encounters":null,"header_rom_address":4744460,"land_encounters":null,"warp_table_rom_address":5455736,"water_encounters":null},"MAP_FORTREE_CITY_HOUSE5":{"fishing_encounters":null,"header_rom_address":4744488,"land_encounters":null,"warp_table_rom_address":5455844,"water_encounters":null},"MAP_FORTREE_CITY_MART":{"fishing_encounters":null,"header_rom_address":4744376,"land_encounters":null,"warp_table_rom_address":5455460,"water_encounters":null},"MAP_FORTREE_CITY_POKEMON_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4744320,"land_encounters":null,"warp_table_rom_address":5455180,"water_encounters":null},"MAP_FORTREE_CITY_POKEMON_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4744348,"land_encounters":null,"warp_table_rom_address":5455320,"water_encounters":null},"MAP_GRANITE_CAVE_1F":{"fishing_encounters":null,"header_rom_address":4746924,"land_encounters":{"encounter_slots":[41,335,335,41,335,63,335,335,74,74,74,74],"rom_address":5586316},"warp_table_rom_address":5468996,"water_encounters":null},"MAP_GRANITE_CAVE_B1F":{"fishing_encounters":null,"header_rom_address":4746952,"land_encounters":{"encounter_slots":[41,382,382,382,41,63,335,335,322,322,322,322],"rom_address":5586372},"warp_table_rom_address":5469072,"water_encounters":null},"MAP_GRANITE_CAVE_B2F":{"fishing_encounters":null,"header_rom_address":4746980,"land_encounters":{"encounter_slots":[41,382,382,41,382,63,322,322,322,322,322,322],"rom_address":5586700},"warp_table_rom_address":5469364,"water_encounters":null},"MAP_GRANITE_CAVE_STEVENS_ROOM":{"fishing_encounters":null,"header_rom_address":4747008,"land_encounters":{"encounter_slots":[41,335,335,41,335,63,335,335,382,382,382,382],"rom_address":5588516},"warp_table_rom_address":5469472,"water_encounters":null},"MAP_INSIDE_OF_TRUCK":{"fishing_encounters":null,"header_rom_address":4750872,"land_encounters":null,"warp_table_rom_address":5492760,"water_encounters":null},"MAP_ISLAND_CAVE":{"fishing_encounters":null,"header_rom_address":4748604,"land_encounters":null,"warp_table_rom_address":5479396,"water_encounters":null},"MAP_JAGGED_PASS":{"fishing_encounters":null,"header_rom_address":4747092,"land_encounters":{"encounter_slots":[339,339,66,339,351,66,351,66,339,351,339,351],"rom_address":5586972},"warp_table_rom_address":5470948,"water_encounters":null},"MAP_LAVARIDGE_TOWN":{"fishing_encounters":null,"header_rom_address":4740400,"land_encounters":null,"warp_table_rom_address":5417556,"water_encounters":null},"MAP_LAVARIDGE_TOWN_GYM_1F":{"fishing_encounters":null,"header_rom_address":4742136,"land_encounters":null,"warp_table_rom_address":5443076,"water_encounters":null},"MAP_LAVARIDGE_TOWN_GYM_B1F":{"fishing_encounters":null,"header_rom_address":4742164,"land_encounters":null,"warp_table_rom_address":5443424,"water_encounters":null},"MAP_LAVARIDGE_TOWN_HERB_SHOP":{"fishing_encounters":null,"header_rom_address":4742108,"land_encounters":null,"warp_table_rom_address":5442896,"water_encounters":null},"MAP_LAVARIDGE_TOWN_HOUSE":{"fishing_encounters":null,"header_rom_address":4742192,"land_encounters":null,"warp_table_rom_address":5443708,"water_encounters":null},"MAP_LAVARIDGE_TOWN_MART":{"fishing_encounters":null,"header_rom_address":4742220,"land_encounters":null,"warp_table_rom_address":5443816,"water_encounters":null},"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4742248,"land_encounters":null,"warp_table_rom_address":5443948,"water_encounters":null},"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4742276,"land_encounters":null,"warp_table_rom_address":5444096,"water_encounters":null},"MAP_LILYCOVE_CITY":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,120,313,313],"rom_address":5591840},"header_rom_address":4740204,"land_encounters":null,"warp_table_rom_address":5414432,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5591812}},"MAP_LILYCOVE_CITY_CONTEST_HALL":{"fishing_encounters":null,"header_rom_address":4744684,"land_encounters":null,"warp_table_rom_address":5458600,"water_encounters":null},"MAP_LILYCOVE_CITY_CONTEST_LOBBY":{"fishing_encounters":null,"header_rom_address":4744656,"land_encounters":null,"warp_table_rom_address":5457636,"water_encounters":null},"MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F":{"fishing_encounters":null,"header_rom_address":4744544,"land_encounters":null,"warp_table_rom_address":5456036,"water_encounters":null},"MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_2F":{"fishing_encounters":null,"header_rom_address":4744572,"land_encounters":null,"warp_table_rom_address":5456264,"water_encounters":null},"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F":{"fishing_encounters":null,"header_rom_address":4744992,"land_encounters":null,"warp_table_rom_address":5460084,"water_encounters":null},"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F":{"fishing_encounters":null,"header_rom_address":4745020,"land_encounters":null,"warp_table_rom_address":5460268,"water_encounters":null},"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F":{"fishing_encounters":null,"header_rom_address":4745048,"land_encounters":null,"warp_table_rom_address":5460432,"water_encounters":null},"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F":{"fishing_encounters":null,"header_rom_address":4745076,"land_encounters":null,"warp_table_rom_address":5460596,"water_encounters":null},"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F":{"fishing_encounters":null,"header_rom_address":4745104,"land_encounters":null,"warp_table_rom_address":5460808,"water_encounters":null},"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR":{"fishing_encounters":null,"header_rom_address":4745160,"land_encounters":null,"warp_table_rom_address":5461024,"water_encounters":null},"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP":{"fishing_encounters":null,"header_rom_address":4745132,"land_encounters":null,"warp_table_rom_address":5460948,"water_encounters":null},"MAP_LILYCOVE_CITY_HARBOR":{"fishing_encounters":null,"header_rom_address":4744824,"land_encounters":null,"warp_table_rom_address":5459436,"water_encounters":null},"MAP_LILYCOVE_CITY_HOUSE1":{"fishing_encounters":null,"header_rom_address":4744880,"land_encounters":null,"warp_table_rom_address":5459580,"water_encounters":null},"MAP_LILYCOVE_CITY_HOUSE2":{"fishing_encounters":null,"header_rom_address":4744908,"land_encounters":null,"warp_table_rom_address":5459640,"water_encounters":null},"MAP_LILYCOVE_CITY_HOUSE3":{"fishing_encounters":null,"header_rom_address":4744936,"land_encounters":null,"warp_table_rom_address":5459820,"water_encounters":null},"MAP_LILYCOVE_CITY_HOUSE4":{"fishing_encounters":null,"header_rom_address":4744964,"land_encounters":null,"warp_table_rom_address":5459904,"water_encounters":null},"MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F":{"fishing_encounters":null,"header_rom_address":4744600,"land_encounters":null,"warp_table_rom_address":5456532,"water_encounters":null},"MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_2F":{"fishing_encounters":null,"header_rom_address":4744628,"land_encounters":null,"warp_table_rom_address":5456864,"water_encounters":null},"MAP_LILYCOVE_CITY_MOVE_DELETERS_HOUSE":{"fishing_encounters":null,"header_rom_address":4744852,"land_encounters":null,"warp_table_rom_address":5459496,"water_encounters":null},"MAP_LILYCOVE_CITY_POKEMON_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4744712,"land_encounters":null,"warp_table_rom_address":5458844,"water_encounters":null},"MAP_LILYCOVE_CITY_POKEMON_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4744740,"land_encounters":null,"warp_table_rom_address":5458984,"water_encounters":null},"MAP_LILYCOVE_CITY_POKEMON_TRAINER_FAN_CLUB":{"fishing_encounters":null,"header_rom_address":4744796,"land_encounters":null,"warp_table_rom_address":5459280,"water_encounters":null},"MAP_LILYCOVE_CITY_UNUSED_MART":{"fishing_encounters":null,"header_rom_address":4744768,"land_encounters":null,"warp_table_rom_address":5459028,"water_encounters":null},"MAP_LITTLEROOT_TOWN":{"fishing_encounters":null,"header_rom_address":4740316,"land_encounters":null,"warp_table_rom_address":5416592,"water_encounters":null},"MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F":{"fishing_encounters":null,"header_rom_address":4741660,"land_encounters":null,"warp_table_rom_address":5439628,"water_encounters":null},"MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F":{"fishing_encounters":null,"header_rom_address":4741688,"land_encounters":null,"warp_table_rom_address":5440120,"water_encounters":null},"MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F":{"fishing_encounters":null,"header_rom_address":4741716,"land_encounters":null,"warp_table_rom_address":5440364,"water_encounters":null},"MAP_LITTLEROOT_TOWN_MAYS_HOUSE_2F":{"fishing_encounters":null,"header_rom_address":4741744,"land_encounters":null,"warp_table_rom_address":5440856,"water_encounters":null},"MAP_LITTLEROOT_TOWN_PROFESSOR_BIRCHS_LAB":{"fishing_encounters":null,"header_rom_address":4741772,"land_encounters":null,"warp_table_rom_address":5441076,"water_encounters":null},"MAP_MAGMA_HIDEOUT_1F":{"fishing_encounters":null,"header_rom_address":4749136,"land_encounters":{"encounter_slots":[74,321,74,321,74,74,74,75,75,75,75,75],"rom_address":5592888},"warp_table_rom_address":5480884,"water_encounters":null},"MAP_MAGMA_HIDEOUT_2F_1R":{"fishing_encounters":null,"header_rom_address":4749164,"land_encounters":{"encounter_slots":[74,321,74,321,74,74,74,75,75,75,75,75],"rom_address":5592944},"warp_table_rom_address":5481032,"water_encounters":null},"MAP_MAGMA_HIDEOUT_2F_2R":{"fishing_encounters":null,"header_rom_address":4749192,"land_encounters":{"encounter_slots":[74,321,74,321,74,74,74,75,75,75,75,75],"rom_address":5593000},"warp_table_rom_address":5481220,"water_encounters":null},"MAP_MAGMA_HIDEOUT_2F_3R":{"fishing_encounters":null,"header_rom_address":4749332,"land_encounters":{"encounter_slots":[74,321,74,321,74,74,74,75,75,75,75,75],"rom_address":5593280},"warp_table_rom_address":5481736,"water_encounters":null},"MAP_MAGMA_HIDEOUT_3F_1R":{"fishing_encounters":null,"header_rom_address":4749220,"land_encounters":{"encounter_slots":[74,321,74,321,74,74,74,75,75,75,75,75],"rom_address":5593056},"warp_table_rom_address":5481328,"water_encounters":null},"MAP_MAGMA_HIDEOUT_3F_2R":{"fishing_encounters":null,"header_rom_address":4749248,"land_encounters":{"encounter_slots":[74,321,74,321,74,74,74,75,75,75,75,75],"rom_address":5593112},"warp_table_rom_address":5481420,"water_encounters":null},"MAP_MAGMA_HIDEOUT_3F_3R":{"fishing_encounters":null,"header_rom_address":4749304,"land_encounters":{"encounter_slots":[74,321,74,321,74,74,74,75,75,75,75,75],"rom_address":5593224},"warp_table_rom_address":5481700,"water_encounters":null},"MAP_MAGMA_HIDEOUT_4F":{"fishing_encounters":null,"header_rom_address":4749276,"land_encounters":{"encounter_slots":[74,321,74,321,74,74,74,75,75,75,75,75],"rom_address":5593168},"warp_table_rom_address":5481640,"water_encounters":null},"MAP_MARINE_CAVE_END":{"fishing_encounters":null,"header_rom_address":4749612,"land_encounters":null,"warp_table_rom_address":5482328,"water_encounters":null},"MAP_MARINE_CAVE_ENTRANCE":{"fishing_encounters":null,"header_rom_address":4749584,"land_encounters":null,"warp_table_rom_address":5482276,"water_encounters":null},"MAP_MAUVILLE_CITY":{"fishing_encounters":null,"header_rom_address":4740120,"land_encounters":null,"warp_table_rom_address":5412444,"water_encounters":null},"MAP_MAUVILLE_CITY_BIKE_SHOP":{"fishing_encounters":null,"header_rom_address":4743592,"land_encounters":null,"warp_table_rom_address":5451272,"water_encounters":null},"MAP_MAUVILLE_CITY_GAME_CORNER":{"fishing_encounters":null,"header_rom_address":4743648,"land_encounters":null,"warp_table_rom_address":5451680,"water_encounters":null},"MAP_MAUVILLE_CITY_GYM":{"fishing_encounters":null,"header_rom_address":4743564,"land_encounters":null,"warp_table_rom_address":5451100,"water_encounters":null},"MAP_MAUVILLE_CITY_HOUSE1":{"fishing_encounters":null,"header_rom_address":4743620,"land_encounters":null,"warp_table_rom_address":5451356,"water_encounters":null},"MAP_MAUVILLE_CITY_HOUSE2":{"fishing_encounters":null,"header_rom_address":4743676,"land_encounters":null,"warp_table_rom_address":5452028,"water_encounters":null},"MAP_MAUVILLE_CITY_MART":{"fishing_encounters":null,"header_rom_address":4743760,"land_encounters":null,"warp_table_rom_address":5452464,"water_encounters":null},"MAP_MAUVILLE_CITY_POKEMON_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4743704,"land_encounters":null,"warp_table_rom_address":5452184,"water_encounters":null},"MAP_MAUVILLE_CITY_POKEMON_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4743732,"land_encounters":null,"warp_table_rom_address":5452348,"water_encounters":null},"MAP_METEOR_FALLS_1F_1R":{"fishing_encounters":{"encounter_slots":[129,118,129,118,323,323,323,323,323,323],"rom_address":5591124},"header_rom_address":4746728,"land_encounters":{"encounter_slots":[41,41,41,41,41,349,349,349,41,41,41,41],"rom_address":5591040},"warp_table_rom_address":5468092,"water_encounters":{"encounter_slots":[41,41,349,349,349],"rom_address":5591096}},"MAP_METEOR_FALLS_1F_2R":{"fishing_encounters":{"encounter_slots":[129,118,129,118,323,323,323,324,324,324],"rom_address":5591256},"header_rom_address":4746756,"land_encounters":{"encounter_slots":[42,42,42,349,349,349,42,349,42,42,42,42],"rom_address":5591172},"warp_table_rom_address":5468260,"water_encounters":{"encounter_slots":[42,42,349,349,349],"rom_address":5591228}},"MAP_METEOR_FALLS_B1F_1R":{"fishing_encounters":{"encounter_slots":[129,118,129,118,323,323,323,324,324,324],"rom_address":5591388},"header_rom_address":4746784,"land_encounters":{"encounter_slots":[42,42,42,349,349,349,42,349,42,42,42,42],"rom_address":5591304},"warp_table_rom_address":5468324,"water_encounters":{"encounter_slots":[42,42,349,349,349],"rom_address":5591360}},"MAP_METEOR_FALLS_B1F_2R":{"fishing_encounters":{"encounter_slots":[129,118,129,118,323,323,323,324,324,324],"rom_address":5586924},"header_rom_address":4746812,"land_encounters":{"encounter_slots":[42,42,395,349,395,349,395,349,42,42,42,42],"rom_address":5586840},"warp_table_rom_address":5468416,"water_encounters":{"encounter_slots":[42,42,349,349,349],"rom_address":5586896}},"MAP_METEOR_FALLS_STEVENS_CAVE":{"fishing_encounters":null,"header_rom_address":4749724,"land_encounters":{"encounter_slots":[42,42,42,349,349,349,42,349,42,42,42,42],"rom_address":5594232},"warp_table_rom_address":5482528,"water_encounters":null},"MAP_MIRAGE_TOWER_1F":{"fishing_encounters":null,"header_rom_address":4749360,"land_encounters":{"encounter_slots":[27,332,27,332,27,332,27,332,27,332,27,332],"rom_address":5593336},"warp_table_rom_address":5481772,"water_encounters":null},"MAP_MIRAGE_TOWER_2F":{"fishing_encounters":null,"header_rom_address":4749388,"land_encounters":{"encounter_slots":[27,332,27,332,27,332,27,332,27,332,27,332],"rom_address":5593392},"warp_table_rom_address":5481808,"water_encounters":null},"MAP_MIRAGE_TOWER_3F":{"fishing_encounters":null,"header_rom_address":4749416,"land_encounters":{"encounter_slots":[27,332,27,332,27,332,27,332,27,332,27,332],"rom_address":5593448},"warp_table_rom_address":5481892,"water_encounters":null},"MAP_MIRAGE_TOWER_4F":{"fishing_encounters":null,"header_rom_address":4749444,"land_encounters":{"encounter_slots":[27,332,27,332,27,332,27,332,27,332,27,332],"rom_address":5593504},"warp_table_rom_address":5482000,"water_encounters":null},"MAP_MOSSDEEP_CITY":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,331,313,313,313,313],"rom_address":5592068},"header_rom_address":4740232,"land_encounters":null,"warp_table_rom_address":5415128,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5592040}},"MAP_MOSSDEEP_CITY_GAME_CORNER_1F":{"fishing_encounters":null,"header_rom_address":4745496,"land_encounters":null,"warp_table_rom_address":5463752,"water_encounters":null},"MAP_MOSSDEEP_CITY_GAME_CORNER_B1F":{"fishing_encounters":null,"header_rom_address":4745524,"land_encounters":null,"warp_table_rom_address":5463856,"water_encounters":null},"MAP_MOSSDEEP_CITY_GYM":{"fishing_encounters":null,"header_rom_address":4745188,"land_encounters":null,"warp_table_rom_address":5461924,"water_encounters":null},"MAP_MOSSDEEP_CITY_HOUSE1":{"fishing_encounters":null,"header_rom_address":4745216,"land_encounters":null,"warp_table_rom_address":5462272,"water_encounters":null},"MAP_MOSSDEEP_CITY_HOUSE2":{"fishing_encounters":null,"header_rom_address":4745244,"land_encounters":null,"warp_table_rom_address":5462380,"water_encounters":null},"MAP_MOSSDEEP_CITY_HOUSE3":{"fishing_encounters":null,"header_rom_address":4745356,"land_encounters":null,"warp_table_rom_address":5462852,"water_encounters":null},"MAP_MOSSDEEP_CITY_HOUSE4":{"fishing_encounters":null,"header_rom_address":4745412,"land_encounters":null,"warp_table_rom_address":5463116,"water_encounters":null},"MAP_MOSSDEEP_CITY_MART":{"fishing_encounters":null,"header_rom_address":4745328,"land_encounters":null,"warp_table_rom_address":5462792,"water_encounters":null},"MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4745272,"land_encounters":null,"warp_table_rom_address":5462488,"water_encounters":null},"MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4745300,"land_encounters":null,"warp_table_rom_address":5462652,"water_encounters":null},"MAP_MOSSDEEP_CITY_SPACE_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4745440,"land_encounters":null,"warp_table_rom_address":5463416,"water_encounters":null},"MAP_MOSSDEEP_CITY_SPACE_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4745468,"land_encounters":null,"warp_table_rom_address":5463676,"water_encounters":null},"MAP_MOSSDEEP_CITY_STEVENS_HOUSE":{"fishing_encounters":null,"header_rom_address":4745384,"land_encounters":null,"warp_table_rom_address":5462960,"water_encounters":null},"MAP_MT_CHIMNEY":{"fishing_encounters":null,"header_rom_address":4747064,"land_encounters":null,"warp_table_rom_address":5470704,"water_encounters":null},"MAP_MT_CHIMNEY_CABLE_CAR_STATION":{"fishing_encounters":null,"header_rom_address":4746532,"land_encounters":null,"warp_table_rom_address":5467184,"water_encounters":null},"MAP_MT_PYRE_1F":{"fishing_encounters":null,"header_rom_address":4747148,"land_encounters":{"encounter_slots":[377,377,377,377,377,377,377,377,377,377,377,377],"rom_address":5586428},"warp_table_rom_address":5471492,"water_encounters":null},"MAP_MT_PYRE_2F":{"fishing_encounters":null,"header_rom_address":4747176,"land_encounters":{"encounter_slots":[377,377,377,377,377,377,377,377,377,377,377,377],"rom_address":5588124},"warp_table_rom_address":5471752,"water_encounters":null},"MAP_MT_PYRE_3F":{"fishing_encounters":null,"header_rom_address":4747204,"land_encounters":{"encounter_slots":[377,377,377,377,377,377,377,377,377,377,377,377],"rom_address":5588180},"warp_table_rom_address":5471908,"water_encounters":null},"MAP_MT_PYRE_4F":{"fishing_encounters":null,"header_rom_address":4747232,"land_encounters":{"encounter_slots":[377,377,377,377,377,377,377,377,361,361,361,361],"rom_address":5588236},"warp_table_rom_address":5472024,"water_encounters":null},"MAP_MT_PYRE_5F":{"fishing_encounters":null,"header_rom_address":4747260,"land_encounters":{"encounter_slots":[377,377,377,377,377,377,377,377,361,361,361,361],"rom_address":5588292},"warp_table_rom_address":5472140,"water_encounters":null},"MAP_MT_PYRE_6F":{"fishing_encounters":null,"header_rom_address":4747288,"land_encounters":{"encounter_slots":[377,377,377,377,377,377,377,377,361,361,361,361],"rom_address":5588348},"warp_table_rom_address":5472272,"water_encounters":null},"MAP_MT_PYRE_EXTERIOR":{"fishing_encounters":null,"header_rom_address":4747316,"land_encounters":{"encounter_slots":[377,377,377,377,37,37,37,37,309,309,309,309],"rom_address":5588404},"warp_table_rom_address":5472356,"water_encounters":null},"MAP_MT_PYRE_SUMMIT":{"fishing_encounters":null,"header_rom_address":4747344,"land_encounters":{"encounter_slots":[377,377,377,377,377,377,377,361,361,361,411,411],"rom_address":5588460},"warp_table_rom_address":5472696,"water_encounters":null},"MAP_NAVEL_ROCK_B1F":{"fishing_encounters":null,"header_rom_address":4753392,"land_encounters":null,"warp_table_rom_address":5507564,"water_encounters":null},"MAP_NAVEL_ROCK_BOTTOM":{"fishing_encounters":null,"header_rom_address":4753896,"land_encounters":null,"warp_table_rom_address":5508288,"water_encounters":null},"MAP_NAVEL_ROCK_DOWN01":{"fishing_encounters":null,"header_rom_address":4753588,"land_encounters":null,"warp_table_rom_address":5507868,"water_encounters":null},"MAP_NAVEL_ROCK_DOWN02":{"fishing_encounters":null,"header_rom_address":4753616,"land_encounters":null,"warp_table_rom_address":5507904,"water_encounters":null},"MAP_NAVEL_ROCK_DOWN03":{"fishing_encounters":null,"header_rom_address":4753644,"land_encounters":null,"warp_table_rom_address":5507940,"water_encounters":null},"MAP_NAVEL_ROCK_DOWN04":{"fishing_encounters":null,"header_rom_address":4753672,"land_encounters":null,"warp_table_rom_address":5507976,"water_encounters":null},"MAP_NAVEL_ROCK_DOWN05":{"fishing_encounters":null,"header_rom_address":4753700,"land_encounters":null,"warp_table_rom_address":5508012,"water_encounters":null},"MAP_NAVEL_ROCK_DOWN06":{"fishing_encounters":null,"header_rom_address":4753728,"land_encounters":null,"warp_table_rom_address":5508048,"water_encounters":null},"MAP_NAVEL_ROCK_DOWN07":{"fishing_encounters":null,"header_rom_address":4753756,"land_encounters":null,"warp_table_rom_address":5508084,"water_encounters":null},"MAP_NAVEL_ROCK_DOWN08":{"fishing_encounters":null,"header_rom_address":4753784,"land_encounters":null,"warp_table_rom_address":5508120,"water_encounters":null},"MAP_NAVEL_ROCK_DOWN09":{"fishing_encounters":null,"header_rom_address":4753812,"land_encounters":null,"warp_table_rom_address":5508156,"water_encounters":null},"MAP_NAVEL_ROCK_DOWN10":{"fishing_encounters":null,"header_rom_address":4753840,"land_encounters":null,"warp_table_rom_address":5508192,"water_encounters":null},"MAP_NAVEL_ROCK_DOWN11":{"fishing_encounters":null,"header_rom_address":4753868,"land_encounters":null,"warp_table_rom_address":5508228,"water_encounters":null},"MAP_NAVEL_ROCK_ENTRANCE":{"fishing_encounters":null,"header_rom_address":4753364,"land_encounters":null,"warp_table_rom_address":5507528,"water_encounters":null},"MAP_NAVEL_ROCK_EXTERIOR":{"fishing_encounters":null,"header_rom_address":4753308,"land_encounters":null,"warp_table_rom_address":5507416,"water_encounters":null},"MAP_NAVEL_ROCK_FORK":{"fishing_encounters":null,"header_rom_address":4753420,"land_encounters":null,"warp_table_rom_address":5507600,"water_encounters":null},"MAP_NAVEL_ROCK_HARBOR":{"fishing_encounters":null,"header_rom_address":4753336,"land_encounters":null,"warp_table_rom_address":5507500,"water_encounters":null},"MAP_NAVEL_ROCK_TOP":{"fishing_encounters":null,"header_rom_address":4753560,"land_encounters":null,"warp_table_rom_address":5507812,"water_encounters":null},"MAP_NAVEL_ROCK_UP1":{"fishing_encounters":null,"header_rom_address":4753448,"land_encounters":null,"warp_table_rom_address":5507644,"water_encounters":null},"MAP_NAVEL_ROCK_UP2":{"fishing_encounters":null,"header_rom_address":4753476,"land_encounters":null,"warp_table_rom_address":5507680,"water_encounters":null},"MAP_NAVEL_ROCK_UP3":{"fishing_encounters":null,"header_rom_address":4753504,"land_encounters":null,"warp_table_rom_address":5507716,"water_encounters":null},"MAP_NAVEL_ROCK_UP4":{"fishing_encounters":null,"header_rom_address":4753532,"land_encounters":null,"warp_table_rom_address":5507752,"water_encounters":null},"MAP_NEW_MAUVILLE_ENTRANCE":{"fishing_encounters":null,"header_rom_address":4748184,"land_encounters":{"encounter_slots":[100,81,100,81,100,81,100,81,100,81,100,81],"rom_address":5590420},"warp_table_rom_address":5477324,"water_encounters":null},"MAP_NEW_MAUVILLE_INSIDE":{"fishing_encounters":null,"header_rom_address":4748212,"land_encounters":{"encounter_slots":[100,81,100,81,100,81,100,81,100,81,101,82],"rom_address":5587464},"warp_table_rom_address":5477568,"water_encounters":null},"MAP_OLDALE_TOWN":{"fishing_encounters":null,"header_rom_address":4740344,"land_encounters":null,"warp_table_rom_address":5416924,"water_encounters":null},"MAP_OLDALE_TOWN_HOUSE1":{"fishing_encounters":null,"header_rom_address":4741800,"land_encounters":null,"warp_table_rom_address":5441316,"water_encounters":null},"MAP_OLDALE_TOWN_HOUSE2":{"fishing_encounters":null,"header_rom_address":4741828,"land_encounters":null,"warp_table_rom_address":5441400,"water_encounters":null},"MAP_OLDALE_TOWN_MART":{"fishing_encounters":null,"header_rom_address":4741912,"land_encounters":null,"warp_table_rom_address":5441788,"water_encounters":null},"MAP_OLDALE_TOWN_POKEMON_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4741856,"land_encounters":null,"warp_table_rom_address":5441532,"water_encounters":null},"MAP_OLDALE_TOWN_POKEMON_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4741884,"land_encounters":null,"warp_table_rom_address":5441672,"water_encounters":null},"MAP_PACIFIDLOG_TOWN":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,331,313,313,313,313],"rom_address":5592144},"header_rom_address":4740484,"land_encounters":null,"warp_table_rom_address":5418328,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5592116}},"MAP_PACIFIDLOG_TOWN_HOUSE1":{"fishing_encounters":null,"header_rom_address":4742836,"land_encounters":null,"warp_table_rom_address":5446440,"water_encounters":null},"MAP_PACIFIDLOG_TOWN_HOUSE2":{"fishing_encounters":null,"header_rom_address":4742864,"land_encounters":null,"warp_table_rom_address":5446548,"water_encounters":null},"MAP_PACIFIDLOG_TOWN_HOUSE3":{"fishing_encounters":null,"header_rom_address":4742892,"land_encounters":null,"warp_table_rom_address":5446632,"water_encounters":null},"MAP_PACIFIDLOG_TOWN_HOUSE4":{"fishing_encounters":null,"header_rom_address":4742920,"land_encounters":null,"warp_table_rom_address":5446740,"water_encounters":null},"MAP_PACIFIDLOG_TOWN_HOUSE5":{"fishing_encounters":null,"header_rom_address":4742948,"land_encounters":null,"warp_table_rom_address":5446824,"water_encounters":null},"MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4742780,"land_encounters":null,"warp_table_rom_address":5446208,"water_encounters":null},"MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4742808,"land_encounters":null,"warp_table_rom_address":5446348,"water_encounters":null},"MAP_PETALBURG_CITY":{"fishing_encounters":{"encounter_slots":[129,118,129,118,326,326,326,326,326,326],"rom_address":5592296},"header_rom_address":4740064,"land_encounters":null,"warp_table_rom_address":5410768,"water_encounters":{"encounter_slots":[183,183,183,183,183],"rom_address":5592268}},"MAP_PETALBURG_CITY_GYM":{"fishing_encounters":null,"header_rom_address":4743004,"land_encounters":null,"warp_table_rom_address":5447208,"water_encounters":null},"MAP_PETALBURG_CITY_HOUSE1":{"fishing_encounters":null,"header_rom_address":4743032,"land_encounters":null,"warp_table_rom_address":5447748,"water_encounters":null},"MAP_PETALBURG_CITY_HOUSE2":{"fishing_encounters":null,"header_rom_address":4743060,"land_encounters":null,"warp_table_rom_address":5447832,"water_encounters":null},"MAP_PETALBURG_CITY_MART":{"fishing_encounters":null,"header_rom_address":4743144,"land_encounters":null,"warp_table_rom_address":5448268,"water_encounters":null},"MAP_PETALBURG_CITY_POKEMON_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4743088,"land_encounters":null,"warp_table_rom_address":5447988,"water_encounters":null},"MAP_PETALBURG_CITY_POKEMON_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4743116,"land_encounters":null,"warp_table_rom_address":5448128,"water_encounters":null},"MAP_PETALBURG_CITY_WALLYS_HOUSE":{"fishing_encounters":null,"header_rom_address":4742976,"land_encounters":null,"warp_table_rom_address":5446908,"water_encounters":null},"MAP_PETALBURG_WOODS":{"fishing_encounters":null,"header_rom_address":4747036,"land_encounters":{"encounter_slots":[286,290,306,286,291,293,290,306,304,364,304,364],"rom_address":5586204},"warp_table_rom_address":5469812,"water_encounters":null},"MAP_RECORD_CORNER":{"fishing_encounters":null,"header_rom_address":4750480,"land_encounters":null,"warp_table_rom_address":5492076,"water_encounters":null},"MAP_ROUTE101":{"fishing_encounters":null,"header_rom_address":4740512,"land_encounters":{"encounter_slots":[290,286,290,290,286,286,290,286,288,288,288,288],"rom_address":5584716},"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_ROUTE102":{"fishing_encounters":{"encounter_slots":[129,118,129,118,326,326,326,326,326,326],"rom_address":5584856},"header_rom_address":4740540,"land_encounters":{"encounter_slots":[286,290,286,290,295,295,288,288,288,392,288,298],"rom_address":5584772},"warp_table_rom_address":4160749568,"water_encounters":{"encounter_slots":[183,183,183,183,118],"rom_address":5584828}},"MAP_ROUTE103":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,331,313,313,313,313],"rom_address":5584988},"header_rom_address":4740568,"land_encounters":{"encounter_slots":[286,286,286,286,309,288,288,288,309,309,309,309],"rom_address":5584904},"warp_table_rom_address":5419492,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5584960}},"MAP_ROUTE104":{"fishing_encounters":{"encounter_slots":[129,129,129,129,129,129,129,129,129,129],"rom_address":5585120},"header_rom_address":4740596,"land_encounters":{"encounter_slots":[286,290,286,183,183,286,304,304,309,309,309,309],"rom_address":5585036},"warp_table_rom_address":5420348,"water_encounters":{"encounter_slots":[309,309,309,310,310],"rom_address":5585092}},"MAP_ROUTE104_MR_BRINEYS_HOUSE":{"fishing_encounters":null,"header_rom_address":4746392,"land_encounters":null,"warp_table_rom_address":5466716,"water_encounters":null},"MAP_ROUTE104_PRETTY_PETAL_FLOWER_SHOP":{"fishing_encounters":null,"header_rom_address":4746420,"land_encounters":null,"warp_table_rom_address":5466824,"water_encounters":null},"MAP_ROUTE104_PROTOTYPE":{"fishing_encounters":null,"header_rom_address":4753952,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_ROUTE104_PROTOTYPE_PRETTY_PETAL_FLOWER_SHOP":{"fishing_encounters":null,"header_rom_address":4753980,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_ROUTE105":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,313,313,313],"rom_address":5585196},"header_rom_address":4740624,"land_encounters":null,"warp_table_rom_address":5420760,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5585168}},"MAP_ROUTE106":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,313,313,313],"rom_address":5587056},"header_rom_address":4740652,"land_encounters":null,"warp_table_rom_address":5420932,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5587028}},"MAP_ROUTE107":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,313,313,313],"rom_address":5587132},"header_rom_address":4740680,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5587104}},"MAP_ROUTE108":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,313,313,313],"rom_address":5587208},"header_rom_address":4740708,"land_encounters":null,"warp_table_rom_address":5421364,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5587180}},"MAP_ROUTE109":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,313,313,313],"rom_address":5587284},"header_rom_address":4740736,"land_encounters":null,"warp_table_rom_address":5421980,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5587256}},"MAP_ROUTE109_SEASHORE_HOUSE":{"fishing_encounters":null,"header_rom_address":4754008,"land_encounters":null,"warp_table_rom_address":5508512,"water_encounters":null},"MAP_ROUTE110":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,313,313,313],"rom_address":5585328},"header_rom_address":4740764,"land_encounters":{"encounter_slots":[286,337,367,337,354,43,354,367,309,309,353,353],"rom_address":5585244},"warp_table_rom_address":5422968,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5585300}},"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE":{"fishing_encounters":null,"header_rom_address":4754344,"land_encounters":null,"warp_table_rom_address":5511440,"water_encounters":null},"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE":{"fishing_encounters":null,"header_rom_address":4754372,"land_encounters":null,"warp_table_rom_address":5511548,"water_encounters":null},"MAP_ROUTE110_TRICK_HOUSE_CORRIDOR":{"fishing_encounters":null,"header_rom_address":4754092,"land_encounters":null,"warp_table_rom_address":5508780,"water_encounters":null},"MAP_ROUTE110_TRICK_HOUSE_END":{"fishing_encounters":null,"header_rom_address":4754064,"land_encounters":null,"warp_table_rom_address":5508716,"water_encounters":null},"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE":{"fishing_encounters":null,"header_rom_address":4754036,"land_encounters":null,"warp_table_rom_address":5508572,"water_encounters":null},"MAP_ROUTE110_TRICK_HOUSE_PUZZLE1":{"fishing_encounters":null,"header_rom_address":4754120,"land_encounters":null,"warp_table_rom_address":5509192,"water_encounters":null},"MAP_ROUTE110_TRICK_HOUSE_PUZZLE2":{"fishing_encounters":null,"header_rom_address":4754148,"land_encounters":null,"warp_table_rom_address":5509368,"water_encounters":null},"MAP_ROUTE110_TRICK_HOUSE_PUZZLE3":{"fishing_encounters":null,"header_rom_address":4754176,"land_encounters":null,"warp_table_rom_address":5509656,"water_encounters":null},"MAP_ROUTE110_TRICK_HOUSE_PUZZLE4":{"fishing_encounters":null,"header_rom_address":4754204,"land_encounters":null,"warp_table_rom_address":5510112,"water_encounters":null},"MAP_ROUTE110_TRICK_HOUSE_PUZZLE5":{"fishing_encounters":null,"header_rom_address":4754232,"land_encounters":null,"warp_table_rom_address":5510288,"water_encounters":null},"MAP_ROUTE110_TRICK_HOUSE_PUZZLE6":{"fishing_encounters":null,"header_rom_address":4754260,"land_encounters":null,"warp_table_rom_address":5510792,"water_encounters":null},"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7":{"fishing_encounters":null,"header_rom_address":4754288,"land_encounters":null,"warp_table_rom_address":5511064,"water_encounters":null},"MAP_ROUTE110_TRICK_HOUSE_PUZZLE8":{"fishing_encounters":null,"header_rom_address":4754316,"land_encounters":null,"warp_table_rom_address":5511360,"water_encounters":null},"MAP_ROUTE111":{"fishing_encounters":{"encounter_slots":[129,118,129,118,323,323,323,323,323,323],"rom_address":5585488},"header_rom_address":4740792,"land_encounters":{"encounter_slots":[27,332,27,332,318,318,27,332,318,344,344,344],"rom_address":5585376},"warp_table_rom_address":5424488,"water_encounters":{"encounter_slots":[183,183,183,183,118],"rom_address":5585432}},"MAP_ROUTE111_OLD_LADYS_REST_STOP":{"fishing_encounters":null,"header_rom_address":4746476,"land_encounters":null,"warp_table_rom_address":5467016,"water_encounters":null},"MAP_ROUTE111_WINSTRATE_FAMILYS_HOUSE":{"fishing_encounters":null,"header_rom_address":4746448,"land_encounters":null,"warp_table_rom_address":5466956,"water_encounters":null},"MAP_ROUTE112":{"fishing_encounters":null,"header_rom_address":4740820,"land_encounters":{"encounter_slots":[339,339,183,339,339,183,339,183,339,339,339,339],"rom_address":5585536},"warp_table_rom_address":5425644,"water_encounters":null},"MAP_ROUTE112_CABLE_CAR_STATION":{"fishing_encounters":null,"header_rom_address":4746504,"land_encounters":null,"warp_table_rom_address":5467100,"water_encounters":null},"MAP_ROUTE113":{"fishing_encounters":null,"header_rom_address":4740848,"land_encounters":{"encounter_slots":[308,308,218,308,308,218,308,218,308,227,308,227],"rom_address":5585592},"warp_table_rom_address":5426132,"water_encounters":null},"MAP_ROUTE113_GLASS_WORKSHOP":{"fishing_encounters":null,"header_rom_address":4754400,"land_encounters":null,"warp_table_rom_address":5511680,"water_encounters":null},"MAP_ROUTE114":{"fishing_encounters":{"encounter_slots":[129,118,129,118,323,323,323,323,323,323],"rom_address":5585760},"header_rom_address":4740876,"land_encounters":{"encounter_slots":[358,295,358,358,295,296,296,296,379,379,379,299],"rom_address":5585648},"warp_table_rom_address":5427224,"water_encounters":{"encounter_slots":[183,183,183,183,118],"rom_address":5585704}},"MAP_ROUTE114_FOSSIL_MANIACS_HOUSE":{"fishing_encounters":null,"header_rom_address":4746560,"land_encounters":null,"warp_table_rom_address":5467244,"water_encounters":null},"MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL":{"fishing_encounters":null,"header_rom_address":4746588,"land_encounters":null,"warp_table_rom_address":5467360,"water_encounters":null},"MAP_ROUTE114_LANETTES_HOUSE":{"fishing_encounters":null,"header_rom_address":4746616,"land_encounters":null,"warp_table_rom_address":5467460,"water_encounters":null},"MAP_ROUTE115":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,313,313,313],"rom_address":5587416},"header_rom_address":4740904,"land_encounters":{"encounter_slots":[358,304,358,304,304,305,39,39,309,309,309,309],"rom_address":5587332},"warp_table_rom_address":5428028,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5587388}},"MAP_ROUTE116":{"fishing_encounters":null,"header_rom_address":4740932,"land_encounters":{"encounter_slots":[286,370,301,63,301,304,304,304,286,286,315,315],"rom_address":5585808},"warp_table_rom_address":5428912,"water_encounters":null},"MAP_ROUTE116_TUNNELERS_REST_HOUSE":{"fishing_encounters":null,"header_rom_address":4746644,"land_encounters":null,"warp_table_rom_address":5467604,"water_encounters":null},"MAP_ROUTE117":{"fishing_encounters":{"encounter_slots":[129,118,129,118,326,326,326,326,326,326],"rom_address":5585948},"header_rom_address":4740960,"land_encounters":{"encounter_slots":[286,43,286,43,183,43,387,387,387,387,386,298],"rom_address":5585864},"warp_table_rom_address":5429696,"water_encounters":{"encounter_slots":[183,183,183,183,118],"rom_address":5585920}},"MAP_ROUTE117_POKEMON_DAY_CARE":{"fishing_encounters":null,"header_rom_address":4746672,"land_encounters":null,"warp_table_rom_address":5467664,"water_encounters":null},"MAP_ROUTE118":{"fishing_encounters":{"encounter_slots":[129,72,129,72,330,331,330,330,330,330],"rom_address":5586080},"header_rom_address":4740988,"land_encounters":{"encounter_slots":[288,337,288,337,289,338,309,309,309,309,309,317],"rom_address":5585996},"warp_table_rom_address":5430276,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5586052}},"MAP_ROUTE119":{"fishing_encounters":{"encounter_slots":[129,72,129,72,330,330,330,330,330,330],"rom_address":5587604},"header_rom_address":4741016,"land_encounters":{"encounter_slots":[288,289,288,43,289,43,43,43,369,369,369,317],"rom_address":5587520},"warp_table_rom_address":5431500,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5587576}},"MAP_ROUTE119_HOUSE":{"fishing_encounters":null,"header_rom_address":4754512,"land_encounters":null,"warp_table_rom_address":5512400,"water_encounters":null},"MAP_ROUTE119_WEATHER_INSTITUTE_1F":{"fishing_encounters":null,"header_rom_address":4754456,"land_encounters":null,"warp_table_rom_address":5511920,"water_encounters":null},"MAP_ROUTE119_WEATHER_INSTITUTE_2F":{"fishing_encounters":null,"header_rom_address":4754484,"land_encounters":null,"warp_table_rom_address":5512204,"water_encounters":null},"MAP_ROUTE120":{"fishing_encounters":{"encounter_slots":[129,118,129,118,323,323,323,323,323,323],"rom_address":5587736},"header_rom_address":4741044,"land_encounters":{"encounter_slots":[286,287,287,43,183,43,43,183,376,376,317,298],"rom_address":5587652},"warp_table_rom_address":5433200,"water_encounters":{"encounter_slots":[183,183,183,183,118],"rom_address":5587708}},"MAP_ROUTE121":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,313,313,313],"rom_address":5587868},"header_rom_address":4741072,"land_encounters":{"encounter_slots":[286,377,287,377,287,43,43,44,309,309,309,317],"rom_address":5587784},"warp_table_rom_address":5434404,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5587840}},"MAP_ROUTE121_SAFARI_ZONE_ENTRANCE":{"fishing_encounters":null,"header_rom_address":4746700,"land_encounters":null,"warp_table_rom_address":5467772,"water_encounters":null},"MAP_ROUTE122":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,331,313,313,313,313],"rom_address":5587944},"header_rom_address":4741100,"land_encounters":null,"warp_table_rom_address":5434616,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5587916}},"MAP_ROUTE123":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,313,313,313],"rom_address":5588076},"header_rom_address":4741128,"land_encounters":{"encounter_slots":[286,377,287,377,287,43,43,44,309,309,309,317],"rom_address":5587992},"warp_table_rom_address":5435676,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5588048}},"MAP_ROUTE123_BERRY_MASTERS_HOUSE":{"fishing_encounters":null,"header_rom_address":4754428,"land_encounters":null,"warp_table_rom_address":5511764,"water_encounters":null},"MAP_ROUTE124":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,331,313,313,313,313],"rom_address":5586156},"header_rom_address":4741156,"land_encounters":null,"warp_table_rom_address":5436476,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5586128}},"MAP_ROUTE124_DIVING_TREASURE_HUNTERS_HOUSE":{"fishing_encounters":null,"header_rom_address":4754540,"land_encounters":null,"warp_table_rom_address":5512460,"water_encounters":null},"MAP_ROUTE125":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,331,313,313,313,313],"rom_address":5588600},"header_rom_address":4741184,"land_encounters":null,"warp_table_rom_address":5436756,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5588572}},"MAP_ROUTE126":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,331,313,313,313,313],"rom_address":5588676},"header_rom_address":4741212,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5588648}},"MAP_ROUTE127":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,331,313,313,313,313],"rom_address":5588752},"header_rom_address":4741240,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5588724}},"MAP_ROUTE128":{"fishing_encounters":{"encounter_slots":[129,72,129,325,313,325,313,222,313,313],"rom_address":5588828},"header_rom_address":4741268,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5588800}},"MAP_ROUTE129":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,331,313,313,313,313],"rom_address":5588904},"header_rom_address":4741296,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":{"encounter_slots":[72,309,309,310,314],"rom_address":5588876}},"MAP_ROUTE130":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,331,313,313,313,313],"rom_address":5589036},"header_rom_address":4741324,"land_encounters":{"encounter_slots":[360,360,360,360,360,360,360,360,360,360,360,360],"rom_address":5588952},"warp_table_rom_address":4160749568,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5589008}},"MAP_ROUTE131":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,331,313,313,313,313],"rom_address":5589112},"header_rom_address":4741352,"land_encounters":null,"warp_table_rom_address":5438156,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5589084}},"MAP_ROUTE132":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,331,313,116,313,313],"rom_address":5589188},"header_rom_address":4741380,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5589160}},"MAP_ROUTE133":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,331,313,116,313,313],"rom_address":5589264},"header_rom_address":4741408,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5589236}},"MAP_ROUTE134":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,331,313,116,313,313],"rom_address":5589340},"header_rom_address":4741436,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5589312}},"MAP_RUSTBORO_CITY":{"fishing_encounters":null,"header_rom_address":4740148,"land_encounters":null,"warp_table_rom_address":5413000,"water_encounters":null},"MAP_RUSTBORO_CITY_CUTTERS_HOUSE":{"fishing_encounters":null,"header_rom_address":4744096,"land_encounters":null,"warp_table_rom_address":5454244,"water_encounters":null},"MAP_RUSTBORO_CITY_DEVON_CORP_1F":{"fishing_encounters":null,"header_rom_address":4743788,"land_encounters":null,"warp_table_rom_address":5452572,"water_encounters":null},"MAP_RUSTBORO_CITY_DEVON_CORP_2F":{"fishing_encounters":null,"header_rom_address":4743816,"land_encounters":null,"warp_table_rom_address":5452784,"water_encounters":null},"MAP_RUSTBORO_CITY_DEVON_CORP_3F":{"fishing_encounters":null,"header_rom_address":4743844,"land_encounters":null,"warp_table_rom_address":5452892,"water_encounters":null},"MAP_RUSTBORO_CITY_FLAT1_1F":{"fishing_encounters":null,"header_rom_address":4744012,"land_encounters":null,"warp_table_rom_address":5453848,"water_encounters":null},"MAP_RUSTBORO_CITY_FLAT1_2F":{"fishing_encounters":null,"header_rom_address":4744040,"land_encounters":null,"warp_table_rom_address":5454084,"water_encounters":null},"MAP_RUSTBORO_CITY_FLAT2_1F":{"fishing_encounters":null,"header_rom_address":4744152,"land_encounters":null,"warp_table_rom_address":5454412,"water_encounters":null},"MAP_RUSTBORO_CITY_FLAT2_2F":{"fishing_encounters":null,"header_rom_address":4744180,"land_encounters":null,"warp_table_rom_address":5454504,"water_encounters":null},"MAP_RUSTBORO_CITY_FLAT2_3F":{"fishing_encounters":null,"header_rom_address":4744208,"land_encounters":null,"warp_table_rom_address":5454588,"water_encounters":null},"MAP_RUSTBORO_CITY_GYM":{"fishing_encounters":null,"header_rom_address":4743872,"land_encounters":null,"warp_table_rom_address":5453064,"water_encounters":null},"MAP_RUSTBORO_CITY_HOUSE1":{"fishing_encounters":null,"header_rom_address":4744068,"land_encounters":null,"warp_table_rom_address":5454160,"water_encounters":null},"MAP_RUSTBORO_CITY_HOUSE2":{"fishing_encounters":null,"header_rom_address":4744124,"land_encounters":null,"warp_table_rom_address":5454328,"water_encounters":null},"MAP_RUSTBORO_CITY_HOUSE3":{"fishing_encounters":null,"header_rom_address":4744236,"land_encounters":null,"warp_table_rom_address":5454688,"water_encounters":null},"MAP_RUSTBORO_CITY_MART":{"fishing_encounters":null,"header_rom_address":4743984,"land_encounters":null,"warp_table_rom_address":5453764,"water_encounters":null},"MAP_RUSTBORO_CITY_POKEMON_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4743928,"land_encounters":null,"warp_table_rom_address":5453484,"water_encounters":null},"MAP_RUSTBORO_CITY_POKEMON_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4743956,"land_encounters":null,"warp_table_rom_address":5453624,"water_encounters":null},"MAP_RUSTBORO_CITY_POKEMON_SCHOOL":{"fishing_encounters":null,"header_rom_address":4743900,"land_encounters":null,"warp_table_rom_address":5453292,"water_encounters":null},"MAP_RUSTURF_TUNNEL":{"fishing_encounters":null,"header_rom_address":4746840,"land_encounters":{"encounter_slots":[370,370,370,370,370,370,370,370,370,370,370,370],"rom_address":5586260},"warp_table_rom_address":5468684,"water_encounters":null},"MAP_SAFARI_ZONE_NORTH":{"fishing_encounters":null,"header_rom_address":4751488,"land_encounters":{"encounter_slots":[231,43,231,43,177,44,44,177,178,214,178,214],"rom_address":5590608},"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_SAFARI_ZONE_NORTHEAST":{"fishing_encounters":null,"header_rom_address":4751796,"land_encounters":{"encounter_slots":[190,216,190,216,191,165,163,204,228,241,228,241],"rom_address":5592804},"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_SAFARI_ZONE_NORTHWEST":{"fishing_encounters":{"encounter_slots":[129,118,129,118,118,118,118,119,119,119],"rom_address":5590776},"header_rom_address":4751460,"land_encounters":{"encounter_slots":[111,43,111,43,84,44,44,84,85,127,85,127],"rom_address":5590692},"warp_table_rom_address":4160749568,"water_encounters":{"encounter_slots":[54,54,54,55,55],"rom_address":5590748}},"MAP_SAFARI_ZONE_REST_HOUSE":{"fishing_encounters":null,"header_rom_address":4751768,"land_encounters":null,"warp_table_rom_address":5499036,"water_encounters":null},"MAP_SAFARI_ZONE_SOUTH":{"fishing_encounters":null,"header_rom_address":4751544,"land_encounters":{"encounter_slots":[43,43,203,203,177,84,44,202,25,202,25,202],"rom_address":5586540},"warp_table_rom_address":5497484,"water_encounters":null},"MAP_SAFARI_ZONE_SOUTHEAST":{"fishing_encounters":{"encounter_slots":[129,118,129,118,223,118,223,223,223,224],"rom_address":5592756},"header_rom_address":4751824,"land_encounters":{"encounter_slots":[191,179,191,179,190,167,163,209,234,207,234,207],"rom_address":5592672},"warp_table_rom_address":4160749568,"water_encounters":{"encounter_slots":[194,183,183,183,195],"rom_address":5592728}},"MAP_SAFARI_ZONE_SOUTHWEST":{"fishing_encounters":{"encounter_slots":[129,118,129,118,118,118,118,119,119,119],"rom_address":5590560},"header_rom_address":4751516,"land_encounters":{"encounter_slots":[43,43,203,203,177,84,44,202,25,202,25,202],"rom_address":5590476},"warp_table_rom_address":5497300,"water_encounters":{"encounter_slots":[54,54,54,54,54],"rom_address":5590532}},"MAP_SCORCHED_SLAB":{"fishing_encounters":null,"header_rom_address":4748772,"land_encounters":null,"warp_table_rom_address":5480184,"water_encounters":null},"MAP_SEAFLOOR_CAVERN_ENTRANCE":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,313,313,313],"rom_address":5590092},"header_rom_address":4747484,"land_encounters":null,"warp_table_rom_address":5473836,"water_encounters":{"encounter_slots":[72,41,41,42,42],"rom_address":5590064}},"MAP_SEAFLOOR_CAVERN_ROOM1":{"fishing_encounters":null,"header_rom_address":4747512,"land_encounters":{"encounter_slots":[41,41,41,41,41,41,41,41,42,42,42,42],"rom_address":5589464},"warp_table_rom_address":5473992,"water_encounters":null},"MAP_SEAFLOOR_CAVERN_ROOM2":{"fishing_encounters":null,"header_rom_address":4747540,"land_encounters":{"encounter_slots":[41,41,41,41,41,41,41,41,42,42,42,42],"rom_address":5589520},"warp_table_rom_address":5474228,"water_encounters":null},"MAP_SEAFLOOR_CAVERN_ROOM3":{"fishing_encounters":null,"header_rom_address":4747568,"land_encounters":{"encounter_slots":[41,41,41,41,41,41,41,41,42,42,42,42],"rom_address":5589576},"warp_table_rom_address":5474496,"water_encounters":null},"MAP_SEAFLOOR_CAVERN_ROOM4":{"fishing_encounters":null,"header_rom_address":4747596,"land_encounters":{"encounter_slots":[41,41,41,41,41,41,41,41,42,42,42,42],"rom_address":5589632},"warp_table_rom_address":5474588,"water_encounters":null},"MAP_SEAFLOOR_CAVERN_ROOM5":{"fishing_encounters":null,"header_rom_address":4747624,"land_encounters":{"encounter_slots":[41,41,41,41,41,41,41,41,42,42,42,42],"rom_address":5589688},"warp_table_rom_address":5474784,"water_encounters":null},"MAP_SEAFLOOR_CAVERN_ROOM6":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,313,313,313],"rom_address":5589828},"header_rom_address":4747652,"land_encounters":{"encounter_slots":[41,41,41,41,41,41,41,41,42,42,42,42],"rom_address":5589744},"warp_table_rom_address":5474828,"water_encounters":{"encounter_slots":[72,41,41,42,42],"rom_address":5589800}},"MAP_SEAFLOOR_CAVERN_ROOM7":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,313,313,313],"rom_address":5589960},"header_rom_address":4747680,"land_encounters":{"encounter_slots":[41,41,41,41,41,41,41,41,42,42,42,42],"rom_address":5589876},"warp_table_rom_address":5474872,"water_encounters":{"encounter_slots":[72,41,41,42,42],"rom_address":5589932}},"MAP_SEAFLOOR_CAVERN_ROOM8":{"fishing_encounters":null,"header_rom_address":4747708,"land_encounters":{"encounter_slots":[41,41,41,41,41,41,41,41,42,42,42,42],"rom_address":5590008},"warp_table_rom_address":5475196,"water_encounters":null},"MAP_SEAFLOOR_CAVERN_ROOM9":{"fishing_encounters":null,"header_rom_address":4747736,"land_encounters":null,"warp_table_rom_address":5475400,"water_encounters":null},"MAP_SEALED_CHAMBER_INNER_ROOM":{"fishing_encounters":null,"header_rom_address":4748744,"land_encounters":null,"warp_table_rom_address":5480024,"water_encounters":null},"MAP_SEALED_CHAMBER_OUTER_ROOM":{"fishing_encounters":null,"header_rom_address":4748716,"land_encounters":null,"warp_table_rom_address":5479648,"water_encounters":null},"MAP_SECRET_BASE_BLUE_CAVE1":{"fishing_encounters":null,"header_rom_address":4749808,"land_encounters":null,"warp_table_rom_address":5483692,"water_encounters":null},"MAP_SECRET_BASE_BLUE_CAVE2":{"fishing_encounters":null,"header_rom_address":4749976,"land_encounters":null,"warp_table_rom_address":5486020,"water_encounters":null},"MAP_SECRET_BASE_BLUE_CAVE3":{"fishing_encounters":null,"header_rom_address":4750144,"land_encounters":null,"warp_table_rom_address":5488348,"water_encounters":null},"MAP_SECRET_BASE_BLUE_CAVE4":{"fishing_encounters":null,"header_rom_address":4750312,"land_encounters":null,"warp_table_rom_address":5490676,"water_encounters":null},"MAP_SECRET_BASE_BROWN_CAVE1":{"fishing_encounters":null,"header_rom_address":4749780,"land_encounters":null,"warp_table_rom_address":5483304,"water_encounters":null},"MAP_SECRET_BASE_BROWN_CAVE2":{"fishing_encounters":null,"header_rom_address":4749948,"land_encounters":null,"warp_table_rom_address":5485632,"water_encounters":null},"MAP_SECRET_BASE_BROWN_CAVE3":{"fishing_encounters":null,"header_rom_address":4750116,"land_encounters":null,"warp_table_rom_address":5487960,"water_encounters":null},"MAP_SECRET_BASE_BROWN_CAVE4":{"fishing_encounters":null,"header_rom_address":4750284,"land_encounters":null,"warp_table_rom_address":5490288,"water_encounters":null},"MAP_SECRET_BASE_RED_CAVE1":{"fishing_encounters":null,"header_rom_address":4749752,"land_encounters":null,"warp_table_rom_address":5482916,"water_encounters":null},"MAP_SECRET_BASE_RED_CAVE2":{"fishing_encounters":null,"header_rom_address":4749920,"land_encounters":null,"warp_table_rom_address":5485244,"water_encounters":null},"MAP_SECRET_BASE_RED_CAVE3":{"fishing_encounters":null,"header_rom_address":4750088,"land_encounters":null,"warp_table_rom_address":5487572,"water_encounters":null},"MAP_SECRET_BASE_RED_CAVE4":{"fishing_encounters":null,"header_rom_address":4750256,"land_encounters":null,"warp_table_rom_address":5489900,"water_encounters":null},"MAP_SECRET_BASE_SHRUB1":{"fishing_encounters":null,"header_rom_address":4749892,"land_encounters":null,"warp_table_rom_address":5484856,"water_encounters":null},"MAP_SECRET_BASE_SHRUB2":{"fishing_encounters":null,"header_rom_address":4750060,"land_encounters":null,"warp_table_rom_address":5487184,"water_encounters":null},"MAP_SECRET_BASE_SHRUB3":{"fishing_encounters":null,"header_rom_address":4750228,"land_encounters":null,"warp_table_rom_address":5489512,"water_encounters":null},"MAP_SECRET_BASE_SHRUB4":{"fishing_encounters":null,"header_rom_address":4750396,"land_encounters":null,"warp_table_rom_address":5491840,"water_encounters":null},"MAP_SECRET_BASE_TREE1":{"fishing_encounters":null,"header_rom_address":4749864,"land_encounters":null,"warp_table_rom_address":5484468,"water_encounters":null},"MAP_SECRET_BASE_TREE2":{"fishing_encounters":null,"header_rom_address":4750032,"land_encounters":null,"warp_table_rom_address":5486796,"water_encounters":null},"MAP_SECRET_BASE_TREE3":{"fishing_encounters":null,"header_rom_address":4750200,"land_encounters":null,"warp_table_rom_address":5489124,"water_encounters":null},"MAP_SECRET_BASE_TREE4":{"fishing_encounters":null,"header_rom_address":4750368,"land_encounters":null,"warp_table_rom_address":5491452,"water_encounters":null},"MAP_SECRET_BASE_YELLOW_CAVE1":{"fishing_encounters":null,"header_rom_address":4749836,"land_encounters":null,"warp_table_rom_address":5484080,"water_encounters":null},"MAP_SECRET_BASE_YELLOW_CAVE2":{"fishing_encounters":null,"header_rom_address":4750004,"land_encounters":null,"warp_table_rom_address":5486408,"water_encounters":null},"MAP_SECRET_BASE_YELLOW_CAVE3":{"fishing_encounters":null,"header_rom_address":4750172,"land_encounters":null,"warp_table_rom_address":5488736,"water_encounters":null},"MAP_SECRET_BASE_YELLOW_CAVE4":{"fishing_encounters":null,"header_rom_address":4750340,"land_encounters":null,"warp_table_rom_address":5491064,"water_encounters":null},"MAP_SHOAL_CAVE_HIGH_TIDE_ENTRANCE_ROOM":{"fishing_encounters":null,"header_rom_address":4748128,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_SHOAL_CAVE_HIGH_TIDE_INNER_ROOM":{"fishing_encounters":null,"header_rom_address":4748156,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,313,313,313],"rom_address":5591764},"header_rom_address":4748016,"land_encounters":{"encounter_slots":[41,341,41,341,41,341,41,341,42,341,42,341],"rom_address":5591680},"warp_table_rom_address":5476868,"water_encounters":{"encounter_slots":[72,41,341,341,341],"rom_address":5591736}},"MAP_SHOAL_CAVE_LOW_TIDE_ICE_ROOM":{"fishing_encounters":null,"header_rom_address":4749052,"land_encounters":{"encounter_slots":[41,341,41,341,41,341,346,341,42,346,42,346],"rom_address":5592372},"warp_table_rom_address":5480584,"water_encounters":null},"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,313,313,313],"rom_address":5591632},"header_rom_address":4748044,"land_encounters":{"encounter_slots":[41,341,41,341,41,341,41,341,42,341,42,341],"rom_address":5591548},"warp_table_rom_address":5476944,"water_encounters":{"encounter_slots":[72,41,341,341,341],"rom_address":5591604}},"MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM":{"fishing_encounters":null,"header_rom_address":4748100,"land_encounters":{"encounter_slots":[41,341,41,341,41,341,41,341,42,341,42,341],"rom_address":5591492},"warp_table_rom_address":5477220,"water_encounters":null},"MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM":{"fishing_encounters":null,"header_rom_address":4748072,"land_encounters":{"encounter_slots":[41,341,41,341,41,341,41,341,42,341,42,341],"rom_address":5591436},"warp_table_rom_address":5477124,"water_encounters":null},"MAP_SKY_PILLAR_1F":{"fishing_encounters":null,"header_rom_address":4748940,"land_encounters":{"encounter_slots":[322,42,42,322,319,378,378,319,319,319,319,319],"rom_address":5592428},"warp_table_rom_address":5480368,"water_encounters":null},"MAP_SKY_PILLAR_2F":{"fishing_encounters":null,"header_rom_address":4748968,"land_encounters":null,"warp_table_rom_address":5480412,"water_encounters":null},"MAP_SKY_PILLAR_3F":{"fishing_encounters":null,"header_rom_address":4748996,"land_encounters":{"encounter_slots":[322,42,42,322,319,378,378,319,319,319,319,319],"rom_address":5592560},"warp_table_rom_address":5480448,"water_encounters":null},"MAP_SKY_PILLAR_4F":{"fishing_encounters":null,"header_rom_address":4749024,"land_encounters":null,"warp_table_rom_address":5480492,"water_encounters":null},"MAP_SKY_PILLAR_5F":{"fishing_encounters":null,"header_rom_address":4749080,"land_encounters":{"encounter_slots":[322,42,42,322,319,378,378,319,319,359,359,359],"rom_address":5592616},"warp_table_rom_address":5480612,"water_encounters":null},"MAP_SKY_PILLAR_ENTRANCE":{"fishing_encounters":null,"header_rom_address":4748884,"land_encounters":null,"warp_table_rom_address":5480272,"water_encounters":null},"MAP_SKY_PILLAR_OUTSIDE":{"fishing_encounters":null,"header_rom_address":4748912,"land_encounters":null,"warp_table_rom_address":5480332,"water_encounters":null},"MAP_SKY_PILLAR_TOP":{"fishing_encounters":null,"header_rom_address":4749108,"land_encounters":null,"warp_table_rom_address":5480696,"water_encounters":null},"MAP_SLATEPORT_CITY":{"fishing_encounters":{"encounter_slots":[129,72,129,72,313,313,313,313,313,313],"rom_address":5591992},"header_rom_address":4740092,"land_encounters":null,"warp_table_rom_address":5411900,"water_encounters":{"encounter_slots":[72,309,309,310,310],"rom_address":5591964}},"MAP_SLATEPORT_CITY_BATTLE_TENT_BATTLE_ROOM":{"fishing_encounters":null,"header_rom_address":4743284,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_SLATEPORT_CITY_BATTLE_TENT_CORRIDOR":{"fishing_encounters":null,"header_rom_address":4743256,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_SLATEPORT_CITY_BATTLE_TENT_LOBBY":{"fishing_encounters":null,"header_rom_address":4743228,"land_encounters":null,"warp_table_rom_address":5448664,"water_encounters":null},"MAP_SLATEPORT_CITY_HARBOR":{"fishing_encounters":null,"header_rom_address":4743424,"land_encounters":null,"warp_table_rom_address":5450368,"water_encounters":null},"MAP_SLATEPORT_CITY_HOUSE":{"fishing_encounters":null,"header_rom_address":4743452,"land_encounters":null,"warp_table_rom_address":5450532,"water_encounters":null},"MAP_SLATEPORT_CITY_MART":{"fishing_encounters":null,"header_rom_address":4743536,"land_encounters":null,"warp_table_rom_address":5450896,"water_encounters":null},"MAP_SLATEPORT_CITY_NAME_RATERS_HOUSE":{"fishing_encounters":null,"header_rom_address":4743312,"land_encounters":null,"warp_table_rom_address":5448872,"water_encounters":null},"MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F":{"fishing_encounters":null,"header_rom_address":4743368,"land_encounters":null,"warp_table_rom_address":5449496,"water_encounters":null},"MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_2F":{"fishing_encounters":null,"header_rom_address":4743396,"land_encounters":null,"warp_table_rom_address":5449896,"water_encounters":null},"MAP_SLATEPORT_CITY_POKEMON_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4743480,"land_encounters":null,"warp_table_rom_address":5450640,"water_encounters":null},"MAP_SLATEPORT_CITY_POKEMON_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4743508,"land_encounters":null,"warp_table_rom_address":5450780,"water_encounters":null},"MAP_SLATEPORT_CITY_POKEMON_FAN_CLUB":{"fishing_encounters":null,"header_rom_address":4743340,"land_encounters":null,"warp_table_rom_address":5449124,"water_encounters":null},"MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F":{"fishing_encounters":null,"header_rom_address":4743172,"land_encounters":null,"warp_table_rom_address":5448400,"water_encounters":null},"MAP_SLATEPORT_CITY_STERNS_SHIPYARD_2F":{"fishing_encounters":null,"header_rom_address":4743200,"land_encounters":null,"warp_table_rom_address":5448516,"water_encounters":null},"MAP_SOOTOPOLIS_CITY":{"fishing_encounters":{"encounter_slots":[129,72,129,129,129,129,129,130,130,130],"rom_address":5592512},"header_rom_address":4740260,"land_encounters":null,"warp_table_rom_address":5415916,"water_encounters":{"encounter_slots":[129,129,129,129,129],"rom_address":5592484}},"MAP_SOOTOPOLIS_CITY_GYM_1F":{"fishing_encounters":null,"header_rom_address":4745552,"land_encounters":null,"warp_table_rom_address":5463932,"water_encounters":null},"MAP_SOOTOPOLIS_CITY_GYM_B1F":{"fishing_encounters":null,"header_rom_address":4745580,"land_encounters":null,"warp_table_rom_address":5464240,"water_encounters":null},"MAP_SOOTOPOLIS_CITY_HOUSE1":{"fishing_encounters":null,"header_rom_address":4745692,"land_encounters":null,"warp_table_rom_address":5464704,"water_encounters":null},"MAP_SOOTOPOLIS_CITY_HOUSE2":{"fishing_encounters":null,"header_rom_address":4745720,"land_encounters":null,"warp_table_rom_address":5464764,"water_encounters":null},"MAP_SOOTOPOLIS_CITY_HOUSE3":{"fishing_encounters":null,"header_rom_address":4745748,"land_encounters":null,"warp_table_rom_address":5464848,"water_encounters":null},"MAP_SOOTOPOLIS_CITY_HOUSE4":{"fishing_encounters":null,"header_rom_address":4745776,"land_encounters":null,"warp_table_rom_address":5464956,"water_encounters":null},"MAP_SOOTOPOLIS_CITY_HOUSE5":{"fishing_encounters":null,"header_rom_address":4745804,"land_encounters":null,"warp_table_rom_address":5465040,"water_encounters":null},"MAP_SOOTOPOLIS_CITY_HOUSE6":{"fishing_encounters":null,"header_rom_address":4745832,"land_encounters":null,"warp_table_rom_address":5465100,"water_encounters":null},"MAP_SOOTOPOLIS_CITY_HOUSE7":{"fishing_encounters":null,"header_rom_address":4745860,"land_encounters":null,"warp_table_rom_address":5465184,"water_encounters":null},"MAP_SOOTOPOLIS_CITY_LOTAD_AND_SEEDOT_HOUSE":{"fishing_encounters":null,"header_rom_address":4745888,"land_encounters":null,"warp_table_rom_address":5465268,"water_encounters":null},"MAP_SOOTOPOLIS_CITY_MART":{"fishing_encounters":null,"header_rom_address":4745664,"land_encounters":null,"warp_table_rom_address":5464620,"water_encounters":null},"MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F":{"fishing_encounters":null,"header_rom_address":4745916,"land_encounters":null,"warp_table_rom_address":5465352,"water_encounters":null},"MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_B1F":{"fishing_encounters":null,"header_rom_address":4745944,"land_encounters":null,"warp_table_rom_address":5465420,"water_encounters":null},"MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4745608,"land_encounters":null,"warp_table_rom_address":5464364,"water_encounters":null},"MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4745636,"land_encounters":null,"warp_table_rom_address":5464504,"water_encounters":null},"MAP_SOUTHERN_ISLAND_EXTERIOR":{"fishing_encounters":null,"header_rom_address":4751712,"land_encounters":null,"warp_table_rom_address":5498820,"water_encounters":null},"MAP_SOUTHERN_ISLAND_INTERIOR":{"fishing_encounters":null,"header_rom_address":4751740,"land_encounters":null,"warp_table_rom_address":5498916,"water_encounters":null},"MAP_SS_TIDAL_CORRIDOR":{"fishing_encounters":null,"header_rom_address":4750900,"land_encounters":null,"warp_table_rom_address":5493032,"water_encounters":null},"MAP_SS_TIDAL_LOWER_DECK":{"fishing_encounters":null,"header_rom_address":4750928,"land_encounters":null,"warp_table_rom_address":5493316,"water_encounters":null},"MAP_SS_TIDAL_ROOMS":{"fishing_encounters":null,"header_rom_address":4750956,"land_encounters":null,"warp_table_rom_address":5493548,"water_encounters":null},"MAP_TERRA_CAVE_END":{"fishing_encounters":null,"header_rom_address":4749668,"land_encounters":null,"warp_table_rom_address":5482432,"water_encounters":null},"MAP_TERRA_CAVE_ENTRANCE":{"fishing_encounters":null,"header_rom_address":4749640,"land_encounters":null,"warp_table_rom_address":5482372,"water_encounters":null},"MAP_TRADE_CENTER":{"fishing_encounters":null,"header_rom_address":4750452,"land_encounters":null,"warp_table_rom_address":5491984,"water_encounters":null},"MAP_TRAINER_HILL_1F":{"fishing_encounters":null,"header_rom_address":4753168,"land_encounters":null,"warp_table_rom_address":5507212,"water_encounters":null},"MAP_TRAINER_HILL_2F":{"fishing_encounters":null,"header_rom_address":4753196,"land_encounters":null,"warp_table_rom_address":5507248,"water_encounters":null},"MAP_TRAINER_HILL_3F":{"fishing_encounters":null,"header_rom_address":4753224,"land_encounters":null,"warp_table_rom_address":5507284,"water_encounters":null},"MAP_TRAINER_HILL_4F":{"fishing_encounters":null,"header_rom_address":4753252,"land_encounters":null,"warp_table_rom_address":5507320,"water_encounters":null},"MAP_TRAINER_HILL_ELEVATOR":{"fishing_encounters":null,"header_rom_address":4753924,"land_encounters":null,"warp_table_rom_address":5508340,"water_encounters":null},"MAP_TRAINER_HILL_ENTRANCE":{"fishing_encounters":null,"header_rom_address":4753140,"land_encounters":null,"warp_table_rom_address":5507140,"water_encounters":null},"MAP_TRAINER_HILL_ROOF":{"fishing_encounters":null,"header_rom_address":4753280,"land_encounters":null,"warp_table_rom_address":5507380,"water_encounters":null},"MAP_UNDERWATER_MARINE_CAVE":{"fishing_encounters":null,"header_rom_address":4749556,"land_encounters":null,"warp_table_rom_address":5482248,"water_encounters":null},"MAP_UNDERWATER_ROUTE105":{"fishing_encounters":null,"header_rom_address":4741604,"land_encounters":null,"warp_table_rom_address":5439388,"water_encounters":null},"MAP_UNDERWATER_ROUTE124":{"fishing_encounters":null,"header_rom_address":4741464,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":{"encounter_slots":[373,170,373,381,381],"rom_address":5592344}},"MAP_UNDERWATER_ROUTE125":{"fishing_encounters":null,"header_rom_address":4741632,"land_encounters":null,"warp_table_rom_address":5439424,"water_encounters":null},"MAP_UNDERWATER_ROUTE126":{"fishing_encounters":null,"header_rom_address":4741492,"land_encounters":null,"warp_table_rom_address":5439092,"water_encounters":{"encounter_slots":[373,170,373,381,381],"rom_address":5586596}},"MAP_UNDERWATER_ROUTE127":{"fishing_encounters":null,"header_rom_address":4741520,"land_encounters":null,"warp_table_rom_address":5439216,"water_encounters":null},"MAP_UNDERWATER_ROUTE128":{"fishing_encounters":null,"header_rom_address":4741548,"land_encounters":null,"warp_table_rom_address":5439300,"water_encounters":null},"MAP_UNDERWATER_ROUTE129":{"fishing_encounters":null,"header_rom_address":4741576,"land_encounters":null,"warp_table_rom_address":5439352,"water_encounters":null},"MAP_UNDERWATER_ROUTE134":{"fishing_encounters":null,"header_rom_address":4748660,"land_encounters":null,"warp_table_rom_address":5479580,"water_encounters":null},"MAP_UNDERWATER_SEAFLOOR_CAVERN":{"fishing_encounters":null,"header_rom_address":4747456,"land_encounters":null,"warp_table_rom_address":5473784,"water_encounters":null},"MAP_UNDERWATER_SEALED_CHAMBER":{"fishing_encounters":null,"header_rom_address":4748688,"land_encounters":null,"warp_table_rom_address":5479608,"water_encounters":null},"MAP_UNDERWATER_SOOTOPOLIS_CITY":{"fishing_encounters":null,"header_rom_address":4746868,"land_encounters":null,"warp_table_rom_address":5468808,"water_encounters":null},"MAP_UNION_ROOM":{"fishing_encounters":null,"header_rom_address":4751432,"land_encounters":null,"warp_table_rom_address":5496912,"water_encounters":null},"MAP_UNUSED_CONTEST_HALL1":{"fishing_encounters":null,"header_rom_address":4750564,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_UNUSED_CONTEST_HALL2":{"fishing_encounters":null,"header_rom_address":4750592,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_UNUSED_CONTEST_HALL3":{"fishing_encounters":null,"header_rom_address":4750620,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_UNUSED_CONTEST_HALL4":{"fishing_encounters":null,"header_rom_address":4750648,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_UNUSED_CONTEST_HALL5":{"fishing_encounters":null,"header_rom_address":4750676,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_UNUSED_CONTEST_HALL6":{"fishing_encounters":null,"header_rom_address":4750704,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_VERDANTURF_TOWN":{"fishing_encounters":null,"header_rom_address":4740456,"land_encounters":null,"warp_table_rom_address":5418084,"water_encounters":null},"MAP_VERDANTURF_TOWN_BATTLE_TENT_BATTLE_ROOM":{"fishing_encounters":null,"header_rom_address":4742584,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_VERDANTURF_TOWN_BATTLE_TENT_CORRIDOR":{"fishing_encounters":null,"header_rom_address":4742556,"land_encounters":null,"warp_table_rom_address":4160749568,"water_encounters":null},"MAP_VERDANTURF_TOWN_BATTLE_TENT_LOBBY":{"fishing_encounters":null,"header_rom_address":4742528,"land_encounters":null,"warp_table_rom_address":5445168,"water_encounters":null},"MAP_VERDANTURF_TOWN_FRIENDSHIP_RATERS_HOUSE":{"fishing_encounters":null,"header_rom_address":4742724,"land_encounters":null,"warp_table_rom_address":5445968,"water_encounters":null},"MAP_VERDANTURF_TOWN_HOUSE":{"fishing_encounters":null,"header_rom_address":4742752,"land_encounters":null,"warp_table_rom_address":5446052,"water_encounters":null},"MAP_VERDANTURF_TOWN_MART":{"fishing_encounters":null,"header_rom_address":4742612,"land_encounters":null,"warp_table_rom_address":5445448,"water_encounters":null},"MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F":{"fishing_encounters":null,"header_rom_address":4742640,"land_encounters":null,"warp_table_rom_address":5445580,"water_encounters":null},"MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F":{"fishing_encounters":null,"header_rom_address":4742668,"land_encounters":null,"warp_table_rom_address":5445720,"water_encounters":null},"MAP_VERDANTURF_TOWN_WANDAS_HOUSE":{"fishing_encounters":null,"header_rom_address":4742696,"land_encounters":null,"warp_table_rom_address":5445884,"water_encounters":null},"MAP_VICTORY_ROAD_1F":{"fishing_encounters":null,"header_rom_address":4747932,"land_encounters":{"encounter_slots":[42,336,383,371,41,335,42,336,382,370,382,370],"rom_address":5586484},"warp_table_rom_address":5475892,"water_encounters":null},"MAP_VICTORY_ROAD_B1F":{"fishing_encounters":null,"header_rom_address":4747960,"land_encounters":{"encounter_slots":[42,336,383,383,42,336,42,336,383,355,383,355],"rom_address":5590824},"warp_table_rom_address":5476500,"water_encounters":null},"MAP_VICTORY_ROAD_B2F":{"fishing_encounters":{"encounter_slots":[129,118,129,118,323,323,323,324,324,324],"rom_address":5590992},"header_rom_address":4747988,"land_encounters":{"encounter_slots":[42,322,383,383,42,322,42,322,383,355,383,355],"rom_address":5590908},"warp_table_rom_address":5476744,"water_encounters":{"encounter_slots":[42,42,42,42,42],"rom_address":5590964}}},"misc_ram_addresses":{"CB2_Overworld":134766684,"gArchipelagoReceivedItem":33792044,"gMain":50340544,"gSaveBlock1Ptr":50355596},"misc_rom_addresses":{"gArchipelagoInfo":5874864,"gArchipelagoOptions":5874840,"gEvolutionTable":3310148,"gLevelUpLearnsets":3326628,"gSpeciesInfo":3288488,"gTMHMLearnsets":3281524,"gTrainers":3221820,"sNewGamePCItems":6172396,"sStarterMon":5983704,"sTMHMMoves":6393984},"species":[{"abilities":[0,0],"base_stats":[0,0,0,0,0,0],"catch_rate":0,"evolutions":[],"friendship":0,"id":0,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":4,"move_id":45},{"level":7,"move_id":73},{"level":10,"move_id":22},{"level":15,"move_id":77},{"level":15,"move_id":79},{"level":20,"move_id":75},{"level":25,"move_id":230},{"level":32,"move_id":74},{"level":39,"move_id":235},{"level":46,"move_id":76}],"rom_address":3300024},"rom_address":3288488,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[65,0],"base_stats":[45,49,49,45,65,65],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":16,"species":2}],"friendship":70,"id":1,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":4,"move_id":45},{"level":7,"move_id":73},{"level":10,"move_id":22},{"level":15,"move_id":77},{"level":15,"move_id":79},{"level":20,"move_id":75},{"level":25,"move_id":230},{"level":32,"move_id":74},{"level":39,"move_id":235},{"level":46,"move_id":76}],"rom_address":3300024},"rom_address":3288516,"tmhm_learnset":"00E41E0884350720","types":[12,3]},{"abilities":[65,0],"base_stats":[60,62,63,60,80,80],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":32,"species":3}],"friendship":70,"id":2,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":73},{"level":4,"move_id":45},{"level":7,"move_id":73},{"level":10,"move_id":22},{"level":15,"move_id":77},{"level":15,"move_id":79},{"level":22,"move_id":75},{"level":29,"move_id":230},{"level":38,"move_id":74},{"level":47,"move_id":235},{"level":56,"move_id":76}],"rom_address":3300052},"rom_address":3288544,"tmhm_learnset":"00E41E0884350720","types":[12,3]},{"abilities":[65,0],"base_stats":[80,82,83,80,100,100],"catch_rate":45,"evolutions":[],"friendship":70,"id":3,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":73},{"level":1,"move_id":22},{"level":4,"move_id":45},{"level":7,"move_id":73},{"level":10,"move_id":22},{"level":15,"move_id":77},{"level":15,"move_id":79},{"level":22,"move_id":75},{"level":29,"move_id":230},{"level":41,"move_id":74},{"level":53,"move_id":235},{"level":65,"move_id":76}],"rom_address":3300082},"rom_address":3288572,"tmhm_learnset":"00E41E0886354730","types":[12,3]},{"abilities":[66,0],"base_stats":[39,52,43,65,60,50],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":16,"species":5}],"friendship":70,"id":4,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":45},{"level":7,"move_id":52},{"level":13,"move_id":108},{"level":19,"move_id":99},{"level":25,"move_id":184},{"level":31,"move_id":53},{"level":37,"move_id":163},{"level":43,"move_id":82},{"level":49,"move_id":83}],"rom_address":3300112},"rom_address":3288600,"tmhm_learnset":"00A61EA4CC510623","types":[10,10]},{"abilities":[66,0],"base_stats":[58,64,58,80,80,65],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":36,"species":6}],"friendship":70,"id":5,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":45},{"level":1,"move_id":52},{"level":7,"move_id":52},{"level":13,"move_id":108},{"level":20,"move_id":99},{"level":27,"move_id":184},{"level":34,"move_id":53},{"level":41,"move_id":163},{"level":48,"move_id":82},{"level":55,"move_id":83}],"rom_address":3300138},"rom_address":3288628,"tmhm_learnset":"00A61EA4CC510623","types":[10,10]},{"abilities":[66,0],"base_stats":[78,84,78,100,109,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":6,"learnset":{"moves":[{"level":1,"move_id":10},{"level":1,"move_id":45},{"level":1,"move_id":52},{"level":1,"move_id":108},{"level":7,"move_id":52},{"level":13,"move_id":108},{"level":20,"move_id":99},{"level":27,"move_id":184},{"level":34,"move_id":53},{"level":36,"move_id":17},{"level":44,"move_id":163},{"level":54,"move_id":82},{"level":64,"move_id":83}],"rom_address":3300164},"rom_address":3288656,"tmhm_learnset":"00AE5EA4CE514633","types":[10,2]},{"abilities":[67,0],"base_stats":[44,48,65,43,50,64],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":16,"species":8}],"friendship":70,"id":7,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":4,"move_id":39},{"level":7,"move_id":145},{"level":10,"move_id":110},{"level":13,"move_id":55},{"level":18,"move_id":44},{"level":23,"move_id":229},{"level":28,"move_id":182},{"level":33,"move_id":240},{"level":40,"move_id":130},{"level":47,"move_id":56}],"rom_address":3300192},"rom_address":3288684,"tmhm_learnset":"03B01E00CC533265","types":[11,11]},{"abilities":[67,0],"base_stats":[59,63,80,58,65,80],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":36,"species":9}],"friendship":70,"id":8,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":145},{"level":4,"move_id":39},{"level":7,"move_id":145},{"level":10,"move_id":110},{"level":13,"move_id":55},{"level":19,"move_id":44},{"level":25,"move_id":229},{"level":31,"move_id":182},{"level":37,"move_id":240},{"level":45,"move_id":130},{"level":53,"move_id":56}],"rom_address":3300222},"rom_address":3288712,"tmhm_learnset":"03B01E00CC533265","types":[11,11]},{"abilities":[67,0],"base_stats":[79,83,100,78,85,105],"catch_rate":45,"evolutions":[],"friendship":70,"id":9,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":145},{"level":1,"move_id":110},{"level":4,"move_id":39},{"level":7,"move_id":145},{"level":10,"move_id":110},{"level":13,"move_id":55},{"level":19,"move_id":44},{"level":25,"move_id":229},{"level":31,"move_id":182},{"level":42,"move_id":240},{"level":55,"move_id":130},{"level":68,"move_id":56}],"rom_address":3300252},"rom_address":3288740,"tmhm_learnset":"03B01E00CE537275","types":[11,11]},{"abilities":[19,0],"base_stats":[45,30,35,45,20,20],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":7,"species":11}],"friendship":70,"id":10,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":81}],"rom_address":3300282},"rom_address":3288768,"tmhm_learnset":"0000000000000000","types":[6,6]},{"abilities":[61,0],"base_stats":[50,20,55,30,25,25],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":10,"species":12}],"friendship":70,"id":11,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":7,"move_id":106}],"rom_address":3300292},"rom_address":3288796,"tmhm_learnset":"0000000000000000","types":[6,6]},{"abilities":[14,0],"base_stats":[60,45,50,70,80,80],"catch_rate":45,"evolutions":[],"friendship":70,"id":12,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":93},{"level":10,"move_id":93},{"level":13,"move_id":77},{"level":14,"move_id":78},{"level":15,"move_id":79},{"level":18,"move_id":48},{"level":23,"move_id":18},{"level":28,"move_id":16},{"level":34,"move_id":60},{"level":40,"move_id":219},{"level":47,"move_id":318}],"rom_address":3300304},"rom_address":3288824,"tmhm_learnset":"0040BE80B43F4620","types":[6,2]},{"abilities":[19,0],"base_stats":[40,35,30,50,20,20],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":7,"species":14}],"friendship":70,"id":13,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":40},{"level":1,"move_id":81}],"rom_address":3300334},"rom_address":3288852,"tmhm_learnset":"0000000000000000","types":[6,3]},{"abilities":[61,0],"base_stats":[45,25,50,35,25,25],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":10,"species":15}],"friendship":70,"id":14,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":7,"move_id":106}],"rom_address":3300344},"rom_address":3288880,"tmhm_learnset":"0000000000000000","types":[6,3]},{"abilities":[68,0],"base_stats":[65,80,40,75,45,80],"catch_rate":45,"evolutions":[],"friendship":70,"id":15,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":31},{"level":10,"move_id":31},{"level":15,"move_id":116},{"level":20,"move_id":41},{"level":25,"move_id":99},{"level":30,"move_id":228},{"level":35,"move_id":42},{"level":40,"move_id":97},{"level":45,"move_id":283}],"rom_address":3300356},"rom_address":3288908,"tmhm_learnset":"00843E88C4354620","types":[6,3]},{"abilities":[51,0],"base_stats":[40,45,40,56,35,35],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":18,"species":17}],"friendship":70,"id":16,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":5,"move_id":28},{"level":9,"move_id":16},{"level":13,"move_id":98},{"level":19,"move_id":18},{"level":25,"move_id":17},{"level":31,"move_id":297},{"level":39,"move_id":97},{"level":47,"move_id":119}],"rom_address":3300382},"rom_address":3288936,"tmhm_learnset":"00087E8084130620","types":[0,2]},{"abilities":[51,0],"base_stats":[63,60,55,71,50,50],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":36,"species":18}],"friendship":70,"id":17,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":28},{"level":1,"move_id":16},{"level":5,"move_id":28},{"level":9,"move_id":16},{"level":13,"move_id":98},{"level":20,"move_id":18},{"level":27,"move_id":17},{"level":34,"move_id":297},{"level":43,"move_id":97},{"level":52,"move_id":119}],"rom_address":3300408},"rom_address":3288964,"tmhm_learnset":"00087E8084130620","types":[0,2]},{"abilities":[51,0],"base_stats":[83,80,75,91,70,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":18,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":28},{"level":1,"move_id":16},{"level":1,"move_id":98},{"level":5,"move_id":28},{"level":9,"move_id":16},{"level":13,"move_id":98},{"level":20,"move_id":18},{"level":27,"move_id":17},{"level":34,"move_id":297},{"level":48,"move_id":97},{"level":62,"move_id":119}],"rom_address":3300434},"rom_address":3288992,"tmhm_learnset":"00087E8084134620","types":[0,2]},{"abilities":[50,62],"base_stats":[30,56,35,72,25,35],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":20,"species":20}],"friendship":70,"id":19,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":7,"move_id":98},{"level":13,"move_id":158},{"level":20,"move_id":116},{"level":27,"move_id":228},{"level":34,"move_id":162},{"level":41,"move_id":283}],"rom_address":3300460},"rom_address":3289020,"tmhm_learnset":"00843E02ADD33E20","types":[0,0]},{"abilities":[50,62],"base_stats":[55,81,60,97,50,70],"catch_rate":127,"evolutions":[],"friendship":70,"id":20,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":98},{"level":7,"move_id":98},{"level":13,"move_id":158},{"level":20,"move_id":184},{"level":30,"move_id":228},{"level":40,"move_id":162},{"level":50,"move_id":283}],"rom_address":3300482},"rom_address":3289048,"tmhm_learnset":"00A43E02ADD37E30","types":[0,0]},{"abilities":[51,0],"base_stats":[40,60,30,70,31,31],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":20,"species":22}],"friendship":70,"id":21,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":1,"move_id":45},{"level":7,"move_id":43},{"level":13,"move_id":31},{"level":19,"move_id":228},{"level":25,"move_id":332},{"level":31,"move_id":119},{"level":37,"move_id":65},{"level":43,"move_id":97}],"rom_address":3300504},"rom_address":3289076,"tmhm_learnset":"00087E8084130620","types":[0,2]},{"abilities":[51,0],"base_stats":[65,90,65,100,61,61],"catch_rate":90,"evolutions":[],"friendship":70,"id":22,"learnset":{"moves":[{"level":1,"move_id":64},{"level":1,"move_id":45},{"level":1,"move_id":43},{"level":1,"move_id":31},{"level":7,"move_id":43},{"level":13,"move_id":31},{"level":26,"move_id":228},{"level":32,"move_id":119},{"level":40,"move_id":65},{"level":47,"move_id":97}],"rom_address":3300528},"rom_address":3289104,"tmhm_learnset":"00087E8084134620","types":[0,2]},{"abilities":[22,61],"base_stats":[35,60,44,55,40,54],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":22,"species":24}],"friendship":70,"id":23,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":35},{"level":1,"move_id":43},{"level":8,"move_id":40},{"level":13,"move_id":44},{"level":20,"move_id":137},{"level":25,"move_id":103},{"level":32,"move_id":51},{"level":37,"move_id":254},{"level":37,"move_id":256},{"level":37,"move_id":255},{"level":44,"move_id":114}],"rom_address":3300550},"rom_address":3289132,"tmhm_learnset":"00213F088E570620","types":[3,3]},{"abilities":[22,61],"base_stats":[60,85,69,80,65,79],"catch_rate":90,"evolutions":[],"friendship":70,"id":24,"learnset":{"moves":[{"level":1,"move_id":35},{"level":1,"move_id":43},{"level":1,"move_id":40},{"level":1,"move_id":44},{"level":8,"move_id":40},{"level":13,"move_id":44},{"level":20,"move_id":137},{"level":28,"move_id":103},{"level":38,"move_id":51},{"level":46,"move_id":254},{"level":46,"move_id":256},{"level":46,"move_id":255},{"level":56,"move_id":114}],"rom_address":3300578},"rom_address":3289160,"tmhm_learnset":"00213F088E574620","types":[3,3]},{"abilities":[9,0],"base_stats":[35,55,30,90,50,40],"catch_rate":190,"evolutions":[{"method":"ITEM","param":96,"species":26}],"friendship":70,"id":25,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":84},{"level":1,"move_id":45},{"level":6,"move_id":39},{"level":8,"move_id":86},{"level":11,"move_id":98},{"level":15,"move_id":104},{"level":20,"move_id":21},{"level":26,"move_id":85},{"level":33,"move_id":97},{"level":41,"move_id":87},{"level":50,"move_id":113}],"rom_address":3300606},"rom_address":3289188,"tmhm_learnset":"00E01E02CDD38221","types":[13,13]},{"abilities":[9,0],"base_stats":[60,90,55,100,90,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":26,"learnset":{"moves":[{"level":1,"move_id":84},{"level":1,"move_id":39},{"level":1,"move_id":98},{"level":1,"move_id":85}],"rom_address":3300634},"rom_address":3289216,"tmhm_learnset":"00E03E02CDD3C221","types":[13,13]},{"abilities":[8,0],"base_stats":[50,75,85,40,20,30],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":22,"species":28}],"friendship":70,"id":27,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":6,"move_id":111},{"level":11,"move_id":28},{"level":17,"move_id":40},{"level":23,"move_id":163},{"level":30,"move_id":129},{"level":37,"move_id":154},{"level":45,"move_id":328},{"level":53,"move_id":201}],"rom_address":3300644},"rom_address":3289244,"tmhm_learnset":"00A43ED0CE510621","types":[4,4]},{"abilities":[8,0],"base_stats":[75,100,110,65,45,55],"catch_rate":90,"evolutions":[],"friendship":70,"id":28,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":111},{"level":1,"move_id":28},{"level":6,"move_id":111},{"level":11,"move_id":28},{"level":17,"move_id":40},{"level":24,"move_id":163},{"level":33,"move_id":129},{"level":42,"move_id":154},{"level":52,"move_id":328},{"level":62,"move_id":201}],"rom_address":3300670},"rom_address":3289272,"tmhm_learnset":"00A43ED0CE514621","types":[4,4]},{"abilities":[38,0],"base_stats":[55,47,52,41,40,40],"catch_rate":235,"evolutions":[{"method":"LEVEL","param":16,"species":30}],"friendship":70,"id":29,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":45},{"level":1,"move_id":10},{"level":8,"move_id":39},{"level":12,"move_id":24},{"level":17,"move_id":40},{"level":20,"move_id":44},{"level":23,"move_id":270},{"level":30,"move_id":154},{"level":38,"move_id":260},{"level":47,"move_id":242}],"rom_address":3300696},"rom_address":3289300,"tmhm_learnset":"00A43E8A8DD33624","types":[3,3]},{"abilities":[38,0],"base_stats":[70,62,67,56,55,55],"catch_rate":120,"evolutions":[{"method":"ITEM","param":94,"species":31}],"friendship":70,"id":30,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":45},{"level":1,"move_id":10},{"level":8,"move_id":39},{"level":12,"move_id":24},{"level":18,"move_id":40},{"level":22,"move_id":44},{"level":26,"move_id":270},{"level":34,"move_id":154},{"level":43,"move_id":260},{"level":53,"move_id":242}],"rom_address":3300722},"rom_address":3289328,"tmhm_learnset":"00A43E8A8DD33624","types":[3,3]},{"abilities":[38,0],"base_stats":[90,82,87,76,75,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":31,"learnset":{"moves":[{"level":1,"move_id":10},{"level":1,"move_id":39},{"level":1,"move_id":24},{"level":1,"move_id":40},{"level":23,"move_id":34}],"rom_address":3300748},"rom_address":3289356,"tmhm_learnset":"00B43FFEEFD37E35","types":[3,4]},{"abilities":[38,0],"base_stats":[46,57,40,50,40,40],"catch_rate":235,"evolutions":[{"method":"LEVEL","param":16,"species":33}],"friendship":70,"id":32,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":64},{"level":8,"move_id":116},{"level":12,"move_id":24},{"level":17,"move_id":40},{"level":20,"move_id":30},{"level":23,"move_id":270},{"level":30,"move_id":31},{"level":38,"move_id":260},{"level":47,"move_id":32}],"rom_address":3300760},"rom_address":3289384,"tmhm_learnset":"00A43E0A8DD33624","types":[3,3]},{"abilities":[38,0],"base_stats":[61,72,57,65,55,55],"catch_rate":120,"evolutions":[{"method":"ITEM","param":94,"species":34}],"friendship":70,"id":33,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":64},{"level":8,"move_id":116},{"level":12,"move_id":24},{"level":18,"move_id":40},{"level":22,"move_id":30},{"level":26,"move_id":270},{"level":34,"move_id":31},{"level":43,"move_id":260},{"level":53,"move_id":32}],"rom_address":3300786},"rom_address":3289412,"tmhm_learnset":"00A43E0A8DD33624","types":[3,3]},{"abilities":[38,0],"base_stats":[81,92,77,85,85,75],"catch_rate":45,"evolutions":[],"friendship":70,"id":34,"learnset":{"moves":[{"level":1,"move_id":64},{"level":1,"move_id":116},{"level":1,"move_id":24},{"level":1,"move_id":40},{"level":23,"move_id":37}],"rom_address":3300812},"rom_address":3289440,"tmhm_learnset":"00B43F7EEFD37E35","types":[3,4]},{"abilities":[56,0],"base_stats":[70,45,48,35,60,65],"catch_rate":150,"evolutions":[{"method":"ITEM","param":94,"species":36}],"friendship":140,"id":35,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":45},{"level":5,"move_id":227},{"level":9,"move_id":47},{"level":13,"move_id":3},{"level":17,"move_id":266},{"level":21,"move_id":107},{"level":25,"move_id":111},{"level":29,"move_id":118},{"level":33,"move_id":322},{"level":37,"move_id":236},{"level":41,"move_id":113},{"level":45,"move_id":309}],"rom_address":3300824},"rom_address":3289468,"tmhm_learnset":"00611E27FDFBB62D","types":[0,0]},{"abilities":[56,0],"base_stats":[95,70,73,60,85,90],"catch_rate":25,"evolutions":[],"friendship":140,"id":36,"learnset":{"moves":[{"level":1,"move_id":47},{"level":1,"move_id":3},{"level":1,"move_id":107},{"level":1,"move_id":118}],"rom_address":3300856},"rom_address":3289496,"tmhm_learnset":"00611E27FDFBF62D","types":[0,0]},{"abilities":[18,0],"base_stats":[38,41,40,65,50,65],"catch_rate":190,"evolutions":[{"method":"ITEM","param":95,"species":38}],"friendship":70,"id":37,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":52},{"level":5,"move_id":39},{"level":9,"move_id":46},{"level":13,"move_id":98},{"level":17,"move_id":261},{"level":21,"move_id":109},{"level":25,"move_id":286},{"level":29,"move_id":53},{"level":33,"move_id":219},{"level":37,"move_id":288},{"level":41,"move_id":83}],"rom_address":3300866},"rom_address":3289524,"tmhm_learnset":"00021E248C590630","types":[10,10]},{"abilities":[18,0],"base_stats":[73,76,75,100,81,100],"catch_rate":75,"evolutions":[],"friendship":70,"id":38,"learnset":{"moves":[{"level":1,"move_id":52},{"level":1,"move_id":98},{"level":1,"move_id":109},{"level":1,"move_id":219},{"level":45,"move_id":83}],"rom_address":3300896},"rom_address":3289552,"tmhm_learnset":"00021E248C594630","types":[10,10]},{"abilities":[56,0],"base_stats":[115,45,20,20,45,25],"catch_rate":170,"evolutions":[{"method":"ITEM","param":94,"species":40}],"friendship":70,"id":39,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":47},{"level":4,"move_id":111},{"level":9,"move_id":1},{"level":14,"move_id":50},{"level":19,"move_id":205},{"level":24,"move_id":3},{"level":29,"move_id":156},{"level":34,"move_id":34},{"level":39,"move_id":102},{"level":44,"move_id":304},{"level":49,"move_id":38}],"rom_address":3300908},"rom_address":3289580,"tmhm_learnset":"00611E27FDBBB625","types":[0,0]},{"abilities":[56,0],"base_stats":[140,70,45,45,75,50],"catch_rate":50,"evolutions":[],"friendship":70,"id":40,"learnset":{"moves":[{"level":1,"move_id":47},{"level":1,"move_id":50},{"level":1,"move_id":111},{"level":1,"move_id":3}],"rom_address":3300938},"rom_address":3289608,"tmhm_learnset":"00611E27FDBBF625","types":[0,0]},{"abilities":[39,0],"base_stats":[40,45,35,55,30,40],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":22,"species":42}],"friendship":70,"id":41,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":141},{"level":6,"move_id":48},{"level":11,"move_id":310},{"level":16,"move_id":44},{"level":21,"move_id":17},{"level":26,"move_id":109},{"level":31,"move_id":314},{"level":36,"move_id":212},{"level":41,"move_id":305},{"level":46,"move_id":114}],"rom_address":3300948},"rom_address":3289636,"tmhm_learnset":"00017F88A4170E20","types":[3,2]},{"abilities":[39,0],"base_stats":[75,80,70,90,65,75],"catch_rate":90,"evolutions":[{"method":"FRIENDSHIP","param":0,"species":169}],"friendship":70,"id":42,"learnset":{"moves":[{"level":1,"move_id":103},{"level":1,"move_id":141},{"level":1,"move_id":48},{"level":1,"move_id":310},{"level":6,"move_id":48},{"level":11,"move_id":310},{"level":16,"move_id":44},{"level":21,"move_id":17},{"level":28,"move_id":109},{"level":35,"move_id":314},{"level":42,"move_id":212},{"level":49,"move_id":305},{"level":56,"move_id":114}],"rom_address":3300976},"rom_address":3289664,"tmhm_learnset":"00017F88A4174E20","types":[3,2]},{"abilities":[34,0],"base_stats":[45,50,55,30,75,65],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":21,"species":44}],"friendship":70,"id":43,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":71},{"level":7,"move_id":230},{"level":14,"move_id":77},{"level":16,"move_id":78},{"level":18,"move_id":79},{"level":23,"move_id":51},{"level":32,"move_id":236},{"level":39,"move_id":80}],"rom_address":3301004},"rom_address":3289692,"tmhm_learnset":"00441E0884350720","types":[12,3]},{"abilities":[34,0],"base_stats":[60,65,70,40,85,75],"catch_rate":120,"evolutions":[{"method":"ITEM","param":98,"species":45},{"method":"ITEM","param":93,"species":182}],"friendship":70,"id":44,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":71},{"level":1,"move_id":230},{"level":1,"move_id":77},{"level":7,"move_id":230},{"level":14,"move_id":77},{"level":16,"move_id":78},{"level":18,"move_id":79},{"level":24,"move_id":51},{"level":35,"move_id":236},{"level":44,"move_id":80}],"rom_address":3301028},"rom_address":3289720,"tmhm_learnset":"00441E0884350720","types":[12,3]},{"abilities":[34,0],"base_stats":[75,80,85,50,100,90],"catch_rate":45,"evolutions":[],"friendship":70,"id":45,"learnset":{"moves":[{"level":1,"move_id":71},{"level":1,"move_id":312},{"level":1,"move_id":78},{"level":1,"move_id":72},{"level":44,"move_id":80}],"rom_address":3301052},"rom_address":3289748,"tmhm_learnset":"00441E0884354720","types":[12,3]},{"abilities":[27,0],"base_stats":[35,70,55,25,45,55],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":24,"species":47}],"friendship":70,"id":46,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":7,"move_id":78},{"level":13,"move_id":77},{"level":19,"move_id":141},{"level":25,"move_id":147},{"level":31,"move_id":163},{"level":37,"move_id":74},{"level":43,"move_id":202},{"level":49,"move_id":312}],"rom_address":3301064},"rom_address":3289776,"tmhm_learnset":"00C43E888C350720","types":[6,12]},{"abilities":[27,0],"base_stats":[60,95,80,30,60,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":47,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":78},{"level":1,"move_id":77},{"level":7,"move_id":78},{"level":13,"move_id":77},{"level":19,"move_id":141},{"level":27,"move_id":147},{"level":35,"move_id":163},{"level":43,"move_id":74},{"level":51,"move_id":202},{"level":59,"move_id":312}],"rom_address":3301090},"rom_address":3289804,"tmhm_learnset":"00C43E888C354720","types":[6,12]},{"abilities":[14,0],"base_stats":[60,55,50,45,40,55],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":31,"species":49}],"friendship":70,"id":48,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":50},{"level":1,"move_id":193},{"level":9,"move_id":48},{"level":17,"move_id":93},{"level":20,"move_id":77},{"level":25,"move_id":141},{"level":28,"move_id":78},{"level":33,"move_id":60},{"level":36,"move_id":79},{"level":41,"move_id":94}],"rom_address":3301116},"rom_address":3289832,"tmhm_learnset":"0040BE0894350620","types":[6,3]},{"abilities":[19,0],"base_stats":[70,65,60,90,90,75],"catch_rate":75,"evolutions":[],"friendship":70,"id":49,"learnset":{"moves":[{"level":1,"move_id":318},{"level":1,"move_id":33},{"level":1,"move_id":50},{"level":1,"move_id":193},{"level":1,"move_id":48},{"level":9,"move_id":48},{"level":17,"move_id":93},{"level":20,"move_id":77},{"level":25,"move_id":141},{"level":28,"move_id":78},{"level":31,"move_id":16},{"level":36,"move_id":60},{"level":42,"move_id":79},{"level":52,"move_id":94}],"rom_address":3301142},"rom_address":3289860,"tmhm_learnset":"0040BE8894354620","types":[6,3]},{"abilities":[8,71],"base_stats":[10,55,25,95,35,45],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":26,"species":51}],"friendship":70,"id":50,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":28},{"level":5,"move_id":45},{"level":9,"move_id":222},{"level":17,"move_id":91},{"level":25,"move_id":189},{"level":33,"move_id":163},{"level":41,"move_id":89},{"level":49,"move_id":90}],"rom_address":3301172},"rom_address":3289888,"tmhm_learnset":"00843EC88E110620","types":[4,4]},{"abilities":[8,71],"base_stats":[35,80,50,120,50,70],"catch_rate":50,"evolutions":[],"friendship":70,"id":51,"learnset":{"moves":[{"level":1,"move_id":161},{"level":1,"move_id":10},{"level":1,"move_id":28},{"level":1,"move_id":45},{"level":5,"move_id":45},{"level":9,"move_id":222},{"level":17,"move_id":91},{"level":25,"move_id":189},{"level":26,"move_id":328},{"level":38,"move_id":163},{"level":51,"move_id":89},{"level":64,"move_id":90}],"rom_address":3301196},"rom_address":3289916,"tmhm_learnset":"00843EC88E114620","types":[4,4]},{"abilities":[53,0],"base_stats":[40,45,35,90,40,40],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":28,"species":53}],"friendship":70,"id":52,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":45},{"level":11,"move_id":44},{"level":20,"move_id":6},{"level":28,"move_id":185},{"level":35,"move_id":103},{"level":41,"move_id":154},{"level":46,"move_id":163},{"level":50,"move_id":252}],"rom_address":3301222},"rom_address":3289944,"tmhm_learnset":"00453F82ADD30E24","types":[0,0]},{"abilities":[7,0],"base_stats":[65,70,60,115,65,65],"catch_rate":90,"evolutions":[],"friendship":70,"id":53,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":45},{"level":1,"move_id":44},{"level":11,"move_id":44},{"level":20,"move_id":6},{"level":29,"move_id":185},{"level":38,"move_id":103},{"level":46,"move_id":154},{"level":53,"move_id":163},{"level":59,"move_id":252}],"rom_address":3301246},"rom_address":3289972,"tmhm_learnset":"00453F82ADD34E34","types":[0,0]},{"abilities":[6,13],"base_stats":[50,52,48,55,65,50],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":33,"species":55}],"friendship":70,"id":54,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":346},{"level":1,"move_id":10},{"level":5,"move_id":39},{"level":10,"move_id":50},{"level":16,"move_id":93},{"level":23,"move_id":103},{"level":31,"move_id":244},{"level":40,"move_id":154},{"level":50,"move_id":56}],"rom_address":3301270},"rom_address":3290000,"tmhm_learnset":"03F01E80CC53326D","types":[11,11]},{"abilities":[6,13],"base_stats":[80,82,78,85,95,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":55,"learnset":{"moves":[{"level":1,"move_id":346},{"level":1,"move_id":10},{"level":1,"move_id":39},{"level":1,"move_id":50},{"level":5,"move_id":39},{"level":10,"move_id":50},{"level":16,"move_id":93},{"level":23,"move_id":103},{"level":31,"move_id":244},{"level":44,"move_id":154},{"level":58,"move_id":56}],"rom_address":3301294},"rom_address":3290028,"tmhm_learnset":"03F01E80CC53726D","types":[11,11]},{"abilities":[72,0],"base_stats":[40,80,35,70,35,45],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":28,"species":57}],"friendship":70,"id":56,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":43},{"level":9,"move_id":67},{"level":15,"move_id":2},{"level":21,"move_id":154},{"level":27,"move_id":116},{"level":33,"move_id":69},{"level":39,"move_id":238},{"level":45,"move_id":103},{"level":51,"move_id":37}],"rom_address":3301318},"rom_address":3290056,"tmhm_learnset":"00A23EC0CFD30EA1","types":[1,1]},{"abilities":[72,0],"base_stats":[65,105,60,95,60,70],"catch_rate":75,"evolutions":[],"friendship":70,"id":57,"learnset":{"moves":[{"level":1,"move_id":10},{"level":1,"move_id":43},{"level":1,"move_id":67},{"level":1,"move_id":99},{"level":9,"move_id":67},{"level":15,"move_id":2},{"level":21,"move_id":154},{"level":27,"move_id":116},{"level":28,"move_id":99},{"level":36,"move_id":69},{"level":45,"move_id":238},{"level":54,"move_id":103},{"level":63,"move_id":37}],"rom_address":3301344},"rom_address":3290084,"tmhm_learnset":"00A23EC0CFD34EA1","types":[1,1]},{"abilities":[22,18],"base_stats":[55,70,45,60,70,50],"catch_rate":190,"evolutions":[{"method":"ITEM","param":95,"species":59}],"friendship":70,"id":58,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":44},{"level":1,"move_id":46},{"level":7,"move_id":52},{"level":13,"move_id":43},{"level":19,"move_id":316},{"level":25,"move_id":36},{"level":31,"move_id":172},{"level":37,"move_id":270},{"level":43,"move_id":97},{"level":49,"move_id":53}],"rom_address":3301372},"rom_address":3290112,"tmhm_learnset":"00A23EA48C510630","types":[10,10]},{"abilities":[22,18],"base_stats":[90,110,80,95,100,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":59,"learnset":{"moves":[{"level":1,"move_id":44},{"level":1,"move_id":46},{"level":1,"move_id":52},{"level":1,"move_id":316},{"level":49,"move_id":245}],"rom_address":3301398},"rom_address":3290140,"tmhm_learnset":"00A23EA48C514630","types":[10,10]},{"abilities":[11,6],"base_stats":[40,50,40,90,40,40],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":25,"species":61}],"friendship":70,"id":60,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":7,"move_id":95},{"level":13,"move_id":55},{"level":19,"move_id":3},{"level":25,"move_id":240},{"level":31,"move_id":34},{"level":37,"move_id":187},{"level":43,"move_id":56}],"rom_address":3301410},"rom_address":3290168,"tmhm_learnset":"03103E009C133264","types":[11,11]},{"abilities":[11,6],"base_stats":[65,65,65,90,50,50],"catch_rate":120,"evolutions":[{"method":"ITEM","param":97,"species":62},{"method":"ITEM","param":187,"species":186}],"friendship":70,"id":61,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":1,"move_id":95},{"level":1,"move_id":55},{"level":7,"move_id":95},{"level":13,"move_id":55},{"level":19,"move_id":3},{"level":27,"move_id":240},{"level":35,"move_id":34},{"level":43,"move_id":187},{"level":51,"move_id":56}],"rom_address":3301434},"rom_address":3290196,"tmhm_learnset":"03B03E00DE133265","types":[11,11]},{"abilities":[11,6],"base_stats":[90,85,95,70,70,90],"catch_rate":45,"evolutions":[],"friendship":70,"id":62,"learnset":{"moves":[{"level":1,"move_id":55},{"level":1,"move_id":95},{"level":1,"move_id":3},{"level":1,"move_id":66},{"level":35,"move_id":66},{"level":51,"move_id":170}],"rom_address":3301458},"rom_address":3290224,"tmhm_learnset":"03B03E40DE1372E5","types":[11,1]},{"abilities":[28,39],"base_stats":[25,20,15,90,105,55],"catch_rate":200,"evolutions":[{"method":"LEVEL","param":16,"species":64}],"friendship":70,"id":63,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":93},{"level":1,"move_id":100}],"rom_address":3301472},"rom_address":3290252,"tmhm_learnset":"0041BF03B45B8E29","types":[14,14]},{"abilities":[28,39],"base_stats":[40,35,30,105,120,70],"catch_rate":100,"evolutions":[{"method":"LEVEL","param":37,"species":65}],"friendship":70,"id":64,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":100},{"level":1,"move_id":134},{"level":1,"move_id":93},{"level":16,"move_id":93},{"level":18,"move_id":50},{"level":21,"move_id":60},{"level":23,"move_id":115},{"level":25,"move_id":105},{"level":30,"move_id":248},{"level":33,"move_id":272},{"level":36,"move_id":94},{"level":43,"move_id":271}],"rom_address":3301482},"rom_address":3290280,"tmhm_learnset":"0041BF03B45B8E29","types":[14,14]},{"abilities":[28,39],"base_stats":[55,50,45,120,135,85],"catch_rate":50,"evolutions":[],"friendship":70,"id":65,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":100},{"level":1,"move_id":134},{"level":1,"move_id":93},{"level":16,"move_id":93},{"level":18,"move_id":50},{"level":21,"move_id":60},{"level":23,"move_id":115},{"level":25,"move_id":105},{"level":30,"move_id":248},{"level":33,"move_id":347},{"level":36,"move_id":94},{"level":43,"move_id":271}],"rom_address":3301510},"rom_address":3290308,"tmhm_learnset":"0041BF03B45BCE29","types":[14,14]},{"abilities":[62,0],"base_stats":[70,80,50,35,35,35],"catch_rate":180,"evolutions":[{"method":"LEVEL","param":28,"species":67}],"friendship":70,"id":66,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":67},{"level":1,"move_id":43},{"level":7,"move_id":116},{"level":13,"move_id":2},{"level":19,"move_id":69},{"level":22,"move_id":193},{"level":25,"move_id":279},{"level":31,"move_id":233},{"level":37,"move_id":66},{"level":40,"move_id":238},{"level":43,"move_id":184},{"level":49,"move_id":223}],"rom_address":3301538},"rom_address":3290336,"tmhm_learnset":"00A03E64CE1306A1","types":[1,1]},{"abilities":[62,0],"base_stats":[80,100,70,45,50,60],"catch_rate":90,"evolutions":[{"method":"LEVEL","param":37,"species":68}],"friendship":70,"id":67,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":67},{"level":1,"move_id":43},{"level":1,"move_id":116},{"level":7,"move_id":116},{"level":13,"move_id":2},{"level":19,"move_id":69},{"level":22,"move_id":193},{"level":25,"move_id":279},{"level":33,"move_id":233},{"level":41,"move_id":66},{"level":46,"move_id":238},{"level":51,"move_id":184},{"level":59,"move_id":223}],"rom_address":3301568},"rom_address":3290364,"tmhm_learnset":"00A03E64CE1306A1","types":[1,1]},{"abilities":[62,0],"base_stats":[90,130,80,55,65,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":68,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":67},{"level":1,"move_id":43},{"level":1,"move_id":116},{"level":7,"move_id":116},{"level":13,"move_id":2},{"level":19,"move_id":69},{"level":22,"move_id":193},{"level":25,"move_id":279},{"level":33,"move_id":233},{"level":41,"move_id":66},{"level":46,"move_id":238},{"level":51,"move_id":184},{"level":59,"move_id":223}],"rom_address":3301598},"rom_address":3290392,"tmhm_learnset":"00A03E64CE1346A1","types":[1,1]},{"abilities":[34,0],"base_stats":[50,75,35,40,70,30],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":21,"species":70}],"friendship":70,"id":69,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":22},{"level":6,"move_id":74},{"level":11,"move_id":35},{"level":15,"move_id":79},{"level":17,"move_id":77},{"level":19,"move_id":78},{"level":23,"move_id":51},{"level":30,"move_id":230},{"level":37,"move_id":75},{"level":45,"move_id":21}],"rom_address":3301628},"rom_address":3290420,"tmhm_learnset":"00443E0884350720","types":[12,3]},{"abilities":[34,0],"base_stats":[65,90,50,55,85,45],"catch_rate":120,"evolutions":[{"method":"ITEM","param":98,"species":71}],"friendship":70,"id":70,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":22},{"level":1,"move_id":74},{"level":1,"move_id":35},{"level":6,"move_id":74},{"level":11,"move_id":35},{"level":15,"move_id":79},{"level":17,"move_id":77},{"level":19,"move_id":78},{"level":24,"move_id":51},{"level":33,"move_id":230},{"level":42,"move_id":75},{"level":54,"move_id":21}],"rom_address":3301656},"rom_address":3290448,"tmhm_learnset":"00443E0884350720","types":[12,3]},{"abilities":[34,0],"base_stats":[80,105,65,70,100,60],"catch_rate":45,"evolutions":[],"friendship":70,"id":71,"learnset":{"moves":[{"level":1,"move_id":22},{"level":1,"move_id":79},{"level":1,"move_id":230},{"level":1,"move_id":75}],"rom_address":3301684},"rom_address":3290476,"tmhm_learnset":"00443E0884354720","types":[12,3]},{"abilities":[29,64],"base_stats":[40,40,35,70,50,100],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":30,"species":73}],"friendship":70,"id":72,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":40},{"level":6,"move_id":48},{"level":12,"move_id":132},{"level":19,"move_id":51},{"level":25,"move_id":61},{"level":30,"move_id":35},{"level":36,"move_id":112},{"level":43,"move_id":103},{"level":49,"move_id":56}],"rom_address":3301694},"rom_address":3290504,"tmhm_learnset":"03143E0884173264","types":[11,3]},{"abilities":[29,64],"base_stats":[80,70,65,100,80,120],"catch_rate":60,"evolutions":[],"friendship":70,"id":73,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":40},{"level":1,"move_id":48},{"level":1,"move_id":132},{"level":6,"move_id":48},{"level":12,"move_id":132},{"level":19,"move_id":51},{"level":25,"move_id":61},{"level":30,"move_id":35},{"level":38,"move_id":112},{"level":47,"move_id":103},{"level":55,"move_id":56}],"rom_address":3301720},"rom_address":3290532,"tmhm_learnset":"03143E0884177264","types":[11,3]},{"abilities":[69,5],"base_stats":[40,80,100,20,30,30],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":25,"species":75}],"friendship":70,"id":74,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":111},{"level":6,"move_id":300},{"level":11,"move_id":88},{"level":16,"move_id":222},{"level":21,"move_id":120},{"level":26,"move_id":205},{"level":31,"move_id":350},{"level":36,"move_id":89},{"level":41,"move_id":153},{"level":46,"move_id":38}],"rom_address":3301746},"rom_address":3290560,"tmhm_learnset":"00A01E74CE110621","types":[5,4]},{"abilities":[69,5],"base_stats":[55,95,115,35,45,45],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":37,"species":76}],"friendship":70,"id":75,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":111},{"level":1,"move_id":300},{"level":1,"move_id":88},{"level":6,"move_id":300},{"level":11,"move_id":88},{"level":16,"move_id":222},{"level":21,"move_id":120},{"level":29,"move_id":205},{"level":37,"move_id":350},{"level":45,"move_id":89},{"level":53,"move_id":153},{"level":62,"move_id":38}],"rom_address":3301774},"rom_address":3290588,"tmhm_learnset":"00A01E74CE110621","types":[5,4]},{"abilities":[69,5],"base_stats":[80,110,130,45,55,65],"catch_rate":45,"evolutions":[],"friendship":70,"id":76,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":111},{"level":1,"move_id":300},{"level":1,"move_id":88},{"level":6,"move_id":300},{"level":11,"move_id":88},{"level":16,"move_id":222},{"level":21,"move_id":120},{"level":29,"move_id":205},{"level":37,"move_id":350},{"level":45,"move_id":89},{"level":53,"move_id":153},{"level":62,"move_id":38}],"rom_address":3301802},"rom_address":3290616,"tmhm_learnset":"00A01E74CE114631","types":[5,4]},{"abilities":[50,18],"base_stats":[50,85,55,90,65,65],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":40,"species":78}],"friendship":70,"id":77,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":5,"move_id":45},{"level":9,"move_id":39},{"level":14,"move_id":52},{"level":19,"move_id":23},{"level":25,"move_id":83},{"level":31,"move_id":36},{"level":38,"move_id":97},{"level":45,"move_id":340},{"level":53,"move_id":126}],"rom_address":3301830},"rom_address":3290644,"tmhm_learnset":"00221E2484710620","types":[10,10]},{"abilities":[50,18],"base_stats":[65,100,70,105,80,80],"catch_rate":60,"evolutions":[],"friendship":70,"id":78,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":39},{"level":1,"move_id":52},{"level":5,"move_id":45},{"level":9,"move_id":39},{"level":14,"move_id":52},{"level":19,"move_id":23},{"level":25,"move_id":83},{"level":31,"move_id":36},{"level":38,"move_id":97},{"level":40,"move_id":31},{"level":50,"move_id":340},{"level":63,"move_id":126}],"rom_address":3301858},"rom_address":3290672,"tmhm_learnset":"00221E2484714620","types":[10,10]},{"abilities":[12,20],"base_stats":[90,65,65,15,40,40],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":37,"species":80},{"method":"ITEM","param":187,"species":199}],"friendship":70,"id":79,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":174},{"level":1,"move_id":281},{"level":1,"move_id":33},{"level":6,"move_id":45},{"level":15,"move_id":55},{"level":20,"move_id":93},{"level":29,"move_id":50},{"level":34,"move_id":29},{"level":43,"move_id":133},{"level":48,"move_id":94}],"rom_address":3301888},"rom_address":3290700,"tmhm_learnset":"02709E24BE5B366C","types":[11,14]},{"abilities":[12,20],"base_stats":[95,75,110,30,100,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":80,"learnset":{"moves":[{"level":1,"move_id":174},{"level":1,"move_id":281},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":6,"move_id":45},{"level":15,"move_id":55},{"level":20,"move_id":93},{"level":29,"move_id":50},{"level":34,"move_id":29},{"level":37,"move_id":110},{"level":46,"move_id":133},{"level":54,"move_id":94}],"rom_address":3301912},"rom_address":3290728,"tmhm_learnset":"02F09E24FE5B766D","types":[11,14]},{"abilities":[42,5],"base_stats":[25,35,70,45,95,55],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":30,"species":82}],"friendship":70,"id":81,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":319},{"level":1,"move_id":33},{"level":6,"move_id":84},{"level":11,"move_id":48},{"level":16,"move_id":49},{"level":21,"move_id":86},{"level":26,"move_id":209},{"level":32,"move_id":199},{"level":38,"move_id":129},{"level":44,"move_id":103},{"level":50,"move_id":192}],"rom_address":3301938},"rom_address":3290756,"tmhm_learnset":"00400E0385930620","types":[13,8]},{"abilities":[42,5],"base_stats":[50,60,95,70,120,70],"catch_rate":60,"evolutions":[],"friendship":70,"id":82,"learnset":{"moves":[{"level":1,"move_id":319},{"level":1,"move_id":33},{"level":1,"move_id":84},{"level":1,"move_id":48},{"level":6,"move_id":84},{"level":11,"move_id":48},{"level":16,"move_id":49},{"level":21,"move_id":86},{"level":26,"move_id":209},{"level":35,"move_id":199},{"level":44,"move_id":161},{"level":53,"move_id":103},{"level":62,"move_id":192}],"rom_address":3301966},"rom_address":3290784,"tmhm_learnset":"00400E0385934620","types":[13,8]},{"abilities":[51,39],"base_stats":[52,65,55,60,58,62],"catch_rate":45,"evolutions":[],"friendship":70,"id":83,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":6,"move_id":28},{"level":11,"move_id":43},{"level":16,"move_id":31},{"level":21,"move_id":282},{"level":26,"move_id":210},{"level":31,"move_id":14},{"level":36,"move_id":97},{"level":41,"move_id":163},{"level":46,"move_id":206}],"rom_address":3301994},"rom_address":3290812,"tmhm_learnset":"000C7E8084510620","types":[0,2]},{"abilities":[50,48],"base_stats":[35,85,45,75,35,35],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":31,"species":85}],"friendship":70,"id":84,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":1,"move_id":45},{"level":9,"move_id":228},{"level":13,"move_id":31},{"level":21,"move_id":161},{"level":25,"move_id":99},{"level":33,"move_id":253},{"level":37,"move_id":65},{"level":45,"move_id":97}],"rom_address":3302022},"rom_address":3290840,"tmhm_learnset":"00087E8084110620","types":[0,2]},{"abilities":[50,48],"base_stats":[60,110,70,100,60,60],"catch_rate":45,"evolutions":[],"friendship":70,"id":85,"learnset":{"moves":[{"level":1,"move_id":64},{"level":1,"move_id":45},{"level":1,"move_id":228},{"level":1,"move_id":31},{"level":9,"move_id":228},{"level":13,"move_id":31},{"level":21,"move_id":161},{"level":25,"move_id":99},{"level":38,"move_id":253},{"level":47,"move_id":65},{"level":60,"move_id":97}],"rom_address":3302046},"rom_address":3290868,"tmhm_learnset":"00087F8084114E20","types":[0,2]},{"abilities":[47,0],"base_stats":[65,45,55,45,45,70],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":34,"species":87}],"friendship":70,"id":86,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":29},{"level":9,"move_id":45},{"level":17,"move_id":196},{"level":21,"move_id":62},{"level":29,"move_id":156},{"level":37,"move_id":36},{"level":41,"move_id":58},{"level":49,"move_id":219}],"rom_address":3302070},"rom_address":3290896,"tmhm_learnset":"03103E00841B3264","types":[11,11]},{"abilities":[47,0],"base_stats":[90,70,80,70,70,95],"catch_rate":75,"evolutions":[],"friendship":70,"id":87,"learnset":{"moves":[{"level":1,"move_id":29},{"level":1,"move_id":45},{"level":1,"move_id":196},{"level":1,"move_id":62},{"level":9,"move_id":45},{"level":17,"move_id":196},{"level":21,"move_id":62},{"level":29,"move_id":156},{"level":34,"move_id":329},{"level":42,"move_id":36},{"level":51,"move_id":58},{"level":64,"move_id":219}],"rom_address":3302094},"rom_address":3290924,"tmhm_learnset":"03103E00841B7264","types":[11,15]},{"abilities":[1,60],"base_stats":[80,80,50,25,40,50],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":38,"species":89}],"friendship":70,"id":88,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":139},{"level":1,"move_id":1},{"level":4,"move_id":106},{"level":8,"move_id":50},{"level":13,"move_id":124},{"level":19,"move_id":107},{"level":26,"move_id":103},{"level":34,"move_id":151},{"level":43,"move_id":188},{"level":53,"move_id":262}],"rom_address":3302120},"rom_address":3290952,"tmhm_learnset":"00003F6E8D970E20","types":[3,3]},{"abilities":[1,60],"base_stats":[105,105,75,50,65,100],"catch_rate":75,"evolutions":[],"friendship":70,"id":89,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":139},{"level":1,"move_id":1},{"level":1,"move_id":106},{"level":4,"move_id":106},{"level":8,"move_id":50},{"level":13,"move_id":124},{"level":19,"move_id":107},{"level":26,"move_id":103},{"level":34,"move_id":151},{"level":47,"move_id":188},{"level":61,"move_id":262}],"rom_address":3302146},"rom_address":3290980,"tmhm_learnset":"00A03F6ECD974E21","types":[3,3]},{"abilities":[75,0],"base_stats":[30,65,100,40,45,25],"catch_rate":190,"evolutions":[{"method":"ITEM","param":97,"species":91}],"friendship":70,"id":90,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":110},{"level":9,"move_id":48},{"level":17,"move_id":62},{"level":25,"move_id":182},{"level":33,"move_id":43},{"level":41,"move_id":128},{"level":49,"move_id":58}],"rom_address":3302172},"rom_address":3291008,"tmhm_learnset":"02101E0084133264","types":[11,11]},{"abilities":[75,0],"base_stats":[50,95,180,70,85,45],"catch_rate":60,"evolutions":[],"friendship":70,"id":91,"learnset":{"moves":[{"level":1,"move_id":110},{"level":1,"move_id":48},{"level":1,"move_id":62},{"level":1,"move_id":182},{"level":33,"move_id":191},{"level":41,"move_id":131}],"rom_address":3302194},"rom_address":3291036,"tmhm_learnset":"02101F0084137264","types":[11,15]},{"abilities":[26,0],"base_stats":[30,35,30,80,100,35],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":25,"species":93}],"friendship":70,"id":92,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":95},{"level":1,"move_id":122},{"level":8,"move_id":180},{"level":13,"move_id":212},{"level":16,"move_id":174},{"level":21,"move_id":101},{"level":28,"move_id":109},{"level":33,"move_id":138},{"level":36,"move_id":194}],"rom_address":3302208},"rom_address":3291064,"tmhm_learnset":"0001BF08B4970E20","types":[7,3]},{"abilities":[26,0],"base_stats":[45,50,45,95,115,55],"catch_rate":90,"evolutions":[{"method":"LEVEL","param":37,"species":94}],"friendship":70,"id":93,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":95},{"level":1,"move_id":122},{"level":1,"move_id":180},{"level":8,"move_id":180},{"level":13,"move_id":212},{"level":16,"move_id":174},{"level":21,"move_id":101},{"level":25,"move_id":325},{"level":31,"move_id":109},{"level":39,"move_id":138},{"level":48,"move_id":194}],"rom_address":3302232},"rom_address":3291092,"tmhm_learnset":"0001BF08B4970E20","types":[7,3]},{"abilities":[26,0],"base_stats":[60,65,60,110,130,75],"catch_rate":45,"evolutions":[],"friendship":70,"id":94,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":95},{"level":1,"move_id":122},{"level":1,"move_id":180},{"level":8,"move_id":180},{"level":13,"move_id":212},{"level":16,"move_id":174},{"level":21,"move_id":101},{"level":25,"move_id":325},{"level":31,"move_id":109},{"level":39,"move_id":138},{"level":48,"move_id":194}],"rom_address":3302258},"rom_address":3291120,"tmhm_learnset":"00A1BF08F5974E21","types":[7,3]},{"abilities":[69,5],"base_stats":[35,45,160,70,30,45],"catch_rate":45,"evolutions":[{"method":"ITEM","param":199,"species":208}],"friendship":70,"id":95,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":103},{"level":9,"move_id":20},{"level":13,"move_id":88},{"level":21,"move_id":106},{"level":25,"move_id":99},{"level":33,"move_id":201},{"level":37,"move_id":21},{"level":45,"move_id":231},{"level":49,"move_id":328},{"level":57,"move_id":38}],"rom_address":3302284},"rom_address":3291148,"tmhm_learnset":"00A01F508E510E30","types":[5,4]},{"abilities":[15,0],"base_stats":[60,48,45,42,43,90],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":26,"species":97}],"friendship":70,"id":96,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":95},{"level":10,"move_id":50},{"level":18,"move_id":93},{"level":25,"move_id":29},{"level":31,"move_id":139},{"level":36,"move_id":96},{"level":40,"move_id":94},{"level":43,"move_id":244},{"level":45,"move_id":248}],"rom_address":3302312},"rom_address":3291176,"tmhm_learnset":"0041BF01F41B8E29","types":[14,14]},{"abilities":[15,0],"base_stats":[85,73,70,67,73,115],"catch_rate":75,"evolutions":[],"friendship":70,"id":97,"learnset":{"moves":[{"level":1,"move_id":1},{"level":1,"move_id":95},{"level":1,"move_id":50},{"level":1,"move_id":93},{"level":10,"move_id":50},{"level":18,"move_id":93},{"level":25,"move_id":29},{"level":33,"move_id":139},{"level":40,"move_id":96},{"level":49,"move_id":94},{"level":55,"move_id":244},{"level":60,"move_id":248}],"rom_address":3302338},"rom_address":3291204,"tmhm_learnset":"0041BF01F41BCE29","types":[14,14]},{"abilities":[52,75],"base_stats":[30,105,90,50,25,25],"catch_rate":225,"evolutions":[{"method":"LEVEL","param":28,"species":99}],"friendship":70,"id":98,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":5,"move_id":43},{"level":12,"move_id":11},{"level":16,"move_id":106},{"level":23,"move_id":341},{"level":27,"move_id":23},{"level":34,"move_id":12},{"level":41,"move_id":182},{"level":45,"move_id":152}],"rom_address":3302364},"rom_address":3291232,"tmhm_learnset":"02B43E408C133264","types":[11,11]},{"abilities":[52,75],"base_stats":[55,130,115,75,50,50],"catch_rate":60,"evolutions":[],"friendship":70,"id":99,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":1,"move_id":43},{"level":1,"move_id":11},{"level":5,"move_id":43},{"level":12,"move_id":11},{"level":16,"move_id":106},{"level":23,"move_id":341},{"level":27,"move_id":23},{"level":38,"move_id":12},{"level":49,"move_id":182},{"level":57,"move_id":152}],"rom_address":3302390},"rom_address":3291260,"tmhm_learnset":"02B43E408C137264","types":[11,11]},{"abilities":[43,9],"base_stats":[40,30,50,100,55,55],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":30,"species":101}],"friendship":70,"id":100,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":268},{"level":1,"move_id":33},{"level":8,"move_id":103},{"level":15,"move_id":49},{"level":21,"move_id":209},{"level":27,"move_id":120},{"level":32,"move_id":205},{"level":37,"move_id":113},{"level":42,"move_id":129},{"level":46,"move_id":153},{"level":49,"move_id":243}],"rom_address":3302416},"rom_address":3291288,"tmhm_learnset":"00402F0285938A20","types":[13,13]},{"abilities":[43,9],"base_stats":[60,50,70,140,80,80],"catch_rate":60,"evolutions":[],"friendship":70,"id":101,"learnset":{"moves":[{"level":1,"move_id":268},{"level":1,"move_id":33},{"level":1,"move_id":103},{"level":1,"move_id":49},{"level":8,"move_id":103},{"level":15,"move_id":49},{"level":21,"move_id":209},{"level":27,"move_id":120},{"level":34,"move_id":205},{"level":41,"move_id":113},{"level":48,"move_id":129},{"level":54,"move_id":153},{"level":59,"move_id":243}],"rom_address":3302444},"rom_address":3291316,"tmhm_learnset":"00402F028593CA20","types":[13,13]},{"abilities":[34,0],"base_stats":[60,40,80,40,60,45],"catch_rate":90,"evolutions":[{"method":"ITEM","param":98,"species":103}],"friendship":70,"id":102,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":140},{"level":1,"move_id":253},{"level":1,"move_id":95},{"level":7,"move_id":115},{"level":13,"move_id":73},{"level":19,"move_id":93},{"level":25,"move_id":78},{"level":31,"move_id":77},{"level":37,"move_id":79},{"level":43,"move_id":76}],"rom_address":3302472},"rom_address":3291344,"tmhm_learnset":"0060BE0994358720","types":[12,14]},{"abilities":[34,0],"base_stats":[95,95,85,55,125,65],"catch_rate":45,"evolutions":[],"friendship":70,"id":103,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":140},{"level":1,"move_id":95},{"level":1,"move_id":93},{"level":19,"move_id":23},{"level":31,"move_id":121}],"rom_address":3302496},"rom_address":3291372,"tmhm_learnset":"0060BE099435C720","types":[12,14]},{"abilities":[69,31],"base_stats":[50,50,95,35,40,50],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":28,"species":105}],"friendship":70,"id":104,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":125},{"level":1,"move_id":45},{"level":5,"move_id":39},{"level":9,"move_id":125},{"level":13,"move_id":29},{"level":17,"move_id":43},{"level":21,"move_id":116},{"level":25,"move_id":155},{"level":29,"move_id":99},{"level":33,"move_id":206},{"level":37,"move_id":37},{"level":41,"move_id":198},{"level":45,"move_id":38}],"rom_address":3302510},"rom_address":3291400,"tmhm_learnset":"00A03EF4CE513621","types":[4,4]},{"abilities":[69,31],"base_stats":[60,80,110,45,50,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":105,"learnset":{"moves":[{"level":1,"move_id":45},{"level":1,"move_id":39},{"level":1,"move_id":125},{"level":1,"move_id":29},{"level":5,"move_id":39},{"level":9,"move_id":125},{"level":13,"move_id":29},{"level":17,"move_id":43},{"level":21,"move_id":116},{"level":25,"move_id":155},{"level":32,"move_id":99},{"level":39,"move_id":206},{"level":46,"move_id":37},{"level":53,"move_id":198},{"level":61,"move_id":38}],"rom_address":3302542},"rom_address":3291428,"tmhm_learnset":"00A03EF4CE517621","types":[4,4]},{"abilities":[7,0],"base_stats":[50,120,53,87,35,110],"catch_rate":45,"evolutions":[],"friendship":70,"id":106,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":279},{"level":1,"move_id":24},{"level":6,"move_id":96},{"level":11,"move_id":27},{"level":16,"move_id":26},{"level":20,"move_id":280},{"level":21,"move_id":116},{"level":26,"move_id":136},{"level":31,"move_id":170},{"level":36,"move_id":193},{"level":41,"move_id":203},{"level":46,"move_id":25},{"level":51,"move_id":179}],"rom_address":3302574},"rom_address":3291456,"tmhm_learnset":"00A03E40C61306A1","types":[1,1]},{"abilities":[51,0],"base_stats":[50,105,79,76,35,110],"catch_rate":45,"evolutions":[],"friendship":70,"id":107,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":279},{"level":1,"move_id":4},{"level":7,"move_id":97},{"level":13,"move_id":228},{"level":20,"move_id":183},{"level":26,"move_id":9},{"level":26,"move_id":8},{"level":26,"move_id":7},{"level":32,"move_id":327},{"level":38,"move_id":5},{"level":44,"move_id":197},{"level":50,"move_id":68}],"rom_address":3302606},"rom_address":3291484,"tmhm_learnset":"00A03E40C61306A1","types":[1,1]},{"abilities":[20,12],"base_stats":[90,55,75,30,60,75],"catch_rate":45,"evolutions":[],"friendship":70,"id":108,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":122},{"level":7,"move_id":48},{"level":12,"move_id":111},{"level":18,"move_id":282},{"level":23,"move_id":23},{"level":29,"move_id":35},{"level":34,"move_id":50},{"level":40,"move_id":21},{"level":45,"move_id":103},{"level":51,"move_id":287}],"rom_address":3302636},"rom_address":3291512,"tmhm_learnset":"00B43E76EFF37625","types":[0,0]},{"abilities":[26,0],"base_stats":[40,65,95,35,60,45],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":35,"species":110}],"friendship":70,"id":109,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":139},{"level":1,"move_id":33},{"level":9,"move_id":123},{"level":17,"move_id":120},{"level":21,"move_id":124},{"level":25,"move_id":108},{"level":33,"move_id":114},{"level":41,"move_id":153},{"level":45,"move_id":194},{"level":49,"move_id":262}],"rom_address":3302664},"rom_address":3291540,"tmhm_learnset":"00403F2EA5930E20","types":[3,3]},{"abilities":[26,0],"base_stats":[65,90,120,60,85,70],"catch_rate":60,"evolutions":[],"friendship":70,"id":110,"learnset":{"moves":[{"level":1,"move_id":139},{"level":1,"move_id":33},{"level":1,"move_id":123},{"level":1,"move_id":120},{"level":9,"move_id":123},{"level":17,"move_id":120},{"level":21,"move_id":124},{"level":25,"move_id":108},{"level":33,"move_id":114},{"level":44,"move_id":153},{"level":51,"move_id":194},{"level":58,"move_id":262}],"rom_address":3302690},"rom_address":3291568,"tmhm_learnset":"00403F2EA5934E20","types":[3,3]},{"abilities":[31,69],"base_stats":[80,85,95,25,30,30],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":42,"species":112}],"friendship":70,"id":111,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":30},{"level":1,"move_id":39},{"level":10,"move_id":23},{"level":15,"move_id":31},{"level":24,"move_id":184},{"level":29,"move_id":350},{"level":38,"move_id":32},{"level":43,"move_id":36},{"level":52,"move_id":89},{"level":57,"move_id":224}],"rom_address":3302716},"rom_address":3291596,"tmhm_learnset":"00A03E768FD33630","types":[4,5]},{"abilities":[31,69],"base_stats":[105,130,120,40,45,45],"catch_rate":60,"evolutions":[],"friendship":70,"id":112,"learnset":{"moves":[{"level":1,"move_id":30},{"level":1,"move_id":39},{"level":1,"move_id":23},{"level":1,"move_id":31},{"level":10,"move_id":23},{"level":15,"move_id":31},{"level":24,"move_id":184},{"level":29,"move_id":350},{"level":38,"move_id":32},{"level":46,"move_id":36},{"level":58,"move_id":89},{"level":66,"move_id":224}],"rom_address":3302742},"rom_address":3291624,"tmhm_learnset":"00B43E76CFD37631","types":[4,5]},{"abilities":[30,32],"base_stats":[250,5,5,50,35,105],"catch_rate":30,"evolutions":[{"method":"FRIENDSHIP","param":0,"species":242}],"friendship":140,"id":113,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":45},{"level":5,"move_id":39},{"level":9,"move_id":287},{"level":13,"move_id":135},{"level":17,"move_id":3},{"level":23,"move_id":107},{"level":29,"move_id":47},{"level":35,"move_id":121},{"level":41,"move_id":111},{"level":49,"move_id":113},{"level":57,"move_id":38}],"rom_address":3302768},"rom_address":3291652,"tmhm_learnset":"00E19E76F7FBF66D","types":[0,0]},{"abilities":[34,0],"base_stats":[65,55,115,60,100,40],"catch_rate":45,"evolutions":[],"friendship":70,"id":114,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":275},{"level":1,"move_id":132},{"level":4,"move_id":79},{"level":10,"move_id":71},{"level":13,"move_id":74},{"level":19,"move_id":77},{"level":22,"move_id":22},{"level":28,"move_id":20},{"level":31,"move_id":72},{"level":37,"move_id":78},{"level":40,"move_id":21},{"level":46,"move_id":321}],"rom_address":3302798},"rom_address":3291680,"tmhm_learnset":"00C43E0884354720","types":[12,12]},{"abilities":[48,0],"base_stats":[105,95,80,90,40,80],"catch_rate":45,"evolutions":[],"friendship":70,"id":115,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":4},{"level":1,"move_id":43},{"level":7,"move_id":44},{"level":13,"move_id":39},{"level":19,"move_id":252},{"level":25,"move_id":5},{"level":31,"move_id":99},{"level":37,"move_id":203},{"level":43,"move_id":146},{"level":49,"move_id":179}],"rom_address":3302828},"rom_address":3291708,"tmhm_learnset":"00B43EF6EFF37675","types":[0,0]},{"abilities":[33,0],"base_stats":[30,40,70,60,70,25],"catch_rate":225,"evolutions":[{"method":"LEVEL","param":32,"species":117}],"friendship":70,"id":116,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":8,"move_id":108},{"level":15,"move_id":43},{"level":22,"move_id":55},{"level":29,"move_id":239},{"level":36,"move_id":97},{"level":43,"move_id":56},{"level":50,"move_id":349}],"rom_address":3302854},"rom_address":3291736,"tmhm_learnset":"03101E0084133264","types":[11,11]},{"abilities":[38,0],"base_stats":[55,65,95,85,95,45],"catch_rate":75,"evolutions":[{"method":"ITEM","param":201,"species":230}],"friendship":70,"id":117,"learnset":{"moves":[{"level":1,"move_id":145},{"level":1,"move_id":108},{"level":1,"move_id":43},{"level":1,"move_id":55},{"level":8,"move_id":108},{"level":15,"move_id":43},{"level":22,"move_id":55},{"level":29,"move_id":239},{"level":40,"move_id":97},{"level":51,"move_id":56},{"level":62,"move_id":349}],"rom_address":3302878},"rom_address":3291764,"tmhm_learnset":"03101E0084137264","types":[11,11]},{"abilities":[33,41],"base_stats":[45,67,60,63,35,50],"catch_rate":225,"evolutions":[{"method":"LEVEL","param":33,"species":119}],"friendship":70,"id":118,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":1,"move_id":39},{"level":1,"move_id":346},{"level":10,"move_id":48},{"level":15,"move_id":30},{"level":24,"move_id":175},{"level":29,"move_id":31},{"level":38,"move_id":127},{"level":43,"move_id":32},{"level":52,"move_id":97}],"rom_address":3302902},"rom_address":3291792,"tmhm_learnset":"03101E0084133264","types":[11,11]},{"abilities":[33,41],"base_stats":[80,92,65,68,65,80],"catch_rate":60,"evolutions":[],"friendship":70,"id":119,"learnset":{"moves":[{"level":1,"move_id":64},{"level":1,"move_id":39},{"level":1,"move_id":346},{"level":1,"move_id":48},{"level":10,"move_id":48},{"level":15,"move_id":30},{"level":24,"move_id":175},{"level":29,"move_id":31},{"level":41,"move_id":127},{"level":49,"move_id":32},{"level":61,"move_id":97}],"rom_address":3302926},"rom_address":3291820,"tmhm_learnset":"03101E0084137264","types":[11,11]},{"abilities":[35,30],"base_stats":[30,45,55,85,70,55],"catch_rate":225,"evolutions":[{"method":"ITEM","param":97,"species":121}],"friendship":70,"id":120,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":6,"move_id":55},{"level":10,"move_id":229},{"level":15,"move_id":105},{"level":19,"move_id":293},{"level":24,"move_id":129},{"level":28,"move_id":61},{"level":33,"move_id":107},{"level":37,"move_id":113},{"level":42,"move_id":322},{"level":46,"move_id":56}],"rom_address":3302950},"rom_address":3291848,"tmhm_learnset":"03500E019593B264","types":[11,11]},{"abilities":[35,30],"base_stats":[60,75,85,115,100,85],"catch_rate":60,"evolutions":[],"friendship":70,"id":121,"learnset":{"moves":[{"level":1,"move_id":55},{"level":1,"move_id":229},{"level":1,"move_id":105},{"level":1,"move_id":129},{"level":33,"move_id":109}],"rom_address":3302980},"rom_address":3291876,"tmhm_learnset":"03508E019593F264","types":[11,14]},{"abilities":[43,0],"base_stats":[40,45,65,90,100,120],"catch_rate":45,"evolutions":[],"friendship":70,"id":122,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":112},{"level":5,"move_id":93},{"level":9,"move_id":164},{"level":13,"move_id":96},{"level":17,"move_id":3},{"level":21,"move_id":113},{"level":21,"move_id":115},{"level":25,"move_id":227},{"level":29,"move_id":60},{"level":33,"move_id":278},{"level":37,"move_id":271},{"level":41,"move_id":272},{"level":45,"move_id":94},{"level":49,"move_id":226},{"level":53,"move_id":219}],"rom_address":3302992},"rom_address":3291904,"tmhm_learnset":"0041BF03F5BBCE29","types":[14,14]},{"abilities":[68,0],"base_stats":[70,110,80,105,55,80],"catch_rate":45,"evolutions":[{"method":"ITEM","param":199,"species":212}],"friendship":70,"id":123,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":98},{"level":1,"move_id":43},{"level":6,"move_id":116},{"level":11,"move_id":228},{"level":16,"move_id":206},{"level":21,"move_id":97},{"level":26,"move_id":17},{"level":31,"move_id":163},{"level":36,"move_id":14},{"level":41,"move_id":104},{"level":46,"move_id":210}],"rom_address":3303030},"rom_address":3291932,"tmhm_learnset":"00847E8084134620","types":[6,2]},{"abilities":[12,0],"base_stats":[65,50,35,95,115,95],"catch_rate":45,"evolutions":[],"friendship":70,"id":124,"learnset":{"moves":[{"level":1,"move_id":1},{"level":1,"move_id":122},{"level":1,"move_id":142},{"level":1,"move_id":181},{"level":9,"move_id":142},{"level":13,"move_id":181},{"level":21,"move_id":3},{"level":25,"move_id":8},{"level":35,"move_id":212},{"level":41,"move_id":313},{"level":51,"move_id":34},{"level":57,"move_id":195},{"level":67,"move_id":59}],"rom_address":3303058},"rom_address":3291960,"tmhm_learnset":"0040BF01F413FA6D","types":[15,14]},{"abilities":[9,0],"base_stats":[65,83,57,105,95,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":125,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":98},{"level":1,"move_id":43},{"level":1,"move_id":9},{"level":9,"move_id":9},{"level":17,"move_id":113},{"level":25,"move_id":129},{"level":36,"move_id":103},{"level":47,"move_id":85},{"level":58,"move_id":87}],"rom_address":3303086},"rom_address":3291988,"tmhm_learnset":"00E03E02D5D3C221","types":[13,13]},{"abilities":[49,0],"base_stats":[65,95,57,93,100,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":126,"learnset":{"moves":[{"level":1,"move_id":52},{"level":1,"move_id":43},{"level":1,"move_id":123},{"level":1,"move_id":7},{"level":7,"move_id":43},{"level":13,"move_id":123},{"level":19,"move_id":7},{"level":25,"move_id":108},{"level":33,"move_id":241},{"level":41,"move_id":53},{"level":49,"move_id":109},{"level":57,"move_id":126}],"rom_address":3303108},"rom_address":3292016,"tmhm_learnset":"00A03E24D4514621","types":[10,10]},{"abilities":[52,0],"base_stats":[65,125,100,85,55,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":127,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":11},{"level":1,"move_id":116},{"level":7,"move_id":20},{"level":13,"move_id":69},{"level":19,"move_id":106},{"level":25,"move_id":279},{"level":31,"move_id":280},{"level":37,"move_id":12},{"level":43,"move_id":66},{"level":49,"move_id":14}],"rom_address":3303134},"rom_address":3292044,"tmhm_learnset":"00A43E40CE1346A1","types":[6,6]},{"abilities":[22,0],"base_stats":[75,100,95,110,40,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":128,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":4,"move_id":39},{"level":8,"move_id":99},{"level":13,"move_id":30},{"level":19,"move_id":184},{"level":26,"move_id":228},{"level":34,"move_id":156},{"level":43,"move_id":37},{"level":53,"move_id":36}],"rom_address":3303160},"rom_address":3292072,"tmhm_learnset":"00B01E7687F37624","types":[0,0]},{"abilities":[33,0],"base_stats":[20,10,55,80,15,20],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":20,"species":130}],"friendship":70,"id":129,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":150},{"level":15,"move_id":33},{"level":30,"move_id":175}],"rom_address":3303186},"rom_address":3292100,"tmhm_learnset":"0000000000000000","types":[11,11]},{"abilities":[22,0],"base_stats":[95,125,79,81,60,100],"catch_rate":45,"evolutions":[],"friendship":70,"id":130,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":37},{"level":20,"move_id":44},{"level":25,"move_id":82},{"level":30,"move_id":43},{"level":35,"move_id":239},{"level":40,"move_id":56},{"level":45,"move_id":240},{"level":50,"move_id":349},{"level":55,"move_id":63}],"rom_address":3303200},"rom_address":3292128,"tmhm_learnset":"03B01F3487937A74","types":[11,2]},{"abilities":[11,75],"base_stats":[130,85,80,60,85,95],"catch_rate":45,"evolutions":[],"friendship":70,"id":131,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":55},{"level":1,"move_id":45},{"level":1,"move_id":47},{"level":7,"move_id":54},{"level":13,"move_id":34},{"level":19,"move_id":109},{"level":25,"move_id":195},{"level":31,"move_id":58},{"level":37,"move_id":240},{"level":43,"move_id":219},{"level":49,"move_id":56},{"level":55,"move_id":329}],"rom_address":3303226},"rom_address":3292156,"tmhm_learnset":"03B01E0295DB7274","types":[11,15]},{"abilities":[7,0],"base_stats":[48,48,48,48,48,48],"catch_rate":35,"evolutions":[],"friendship":70,"id":132,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":144}],"rom_address":3303254},"rom_address":3292184,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[50,0],"base_stats":[55,55,50,55,45,65],"catch_rate":45,"evolutions":[{"method":"ITEM","param":96,"species":135},{"method":"ITEM","param":97,"species":134},{"method":"ITEM","param":95,"species":136},{"method":"FRIENDSHIP_DAY","param":0,"species":196},{"method":"FRIENDSHIP_NIGHT","param":0,"species":197}],"friendship":70,"id":133,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":270},{"level":8,"move_id":28},{"level":16,"move_id":45},{"level":23,"move_id":98},{"level":30,"move_id":44},{"level":36,"move_id":226},{"level":42,"move_id":36}],"rom_address":3303264},"rom_address":3292212,"tmhm_learnset":"00001E00AC530620","types":[0,0]},{"abilities":[11,0],"base_stats":[130,65,60,65,110,95],"catch_rate":45,"evolutions":[],"friendship":70,"id":134,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":270},{"level":8,"move_id":28},{"level":16,"move_id":55},{"level":23,"move_id":98},{"level":30,"move_id":44},{"level":36,"move_id":62},{"level":42,"move_id":114},{"level":47,"move_id":151},{"level":52,"move_id":56}],"rom_address":3303286},"rom_address":3292240,"tmhm_learnset":"03101E00AC537674","types":[11,11]},{"abilities":[10,0],"base_stats":[65,65,60,130,110,95],"catch_rate":45,"evolutions":[],"friendship":70,"id":135,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":270},{"level":8,"move_id":28},{"level":16,"move_id":84},{"level":23,"move_id":98},{"level":30,"move_id":24},{"level":36,"move_id":42},{"level":42,"move_id":86},{"level":47,"move_id":97},{"level":52,"move_id":87}],"rom_address":3303312},"rom_address":3292268,"tmhm_learnset":"00401E02ADD34630","types":[13,13]},{"abilities":[18,0],"base_stats":[65,130,60,65,95,110],"catch_rate":45,"evolutions":[],"friendship":70,"id":136,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":270},{"level":8,"move_id":28},{"level":16,"move_id":52},{"level":23,"move_id":98},{"level":30,"move_id":44},{"level":36,"move_id":83},{"level":42,"move_id":123},{"level":47,"move_id":43},{"level":52,"move_id":53}],"rom_address":3303338},"rom_address":3292296,"tmhm_learnset":"00021E24AC534630","types":[10,10]},{"abilities":[36,0],"base_stats":[65,60,70,40,85,75],"catch_rate":45,"evolutions":[{"method":"ITEM","param":218,"species":233}],"friendship":70,"id":137,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":176},{"level":1,"move_id":33},{"level":1,"move_id":160},{"level":9,"move_id":97},{"level":12,"move_id":60},{"level":20,"move_id":105},{"level":24,"move_id":159},{"level":32,"move_id":199},{"level":36,"move_id":161},{"level":44,"move_id":278},{"level":48,"move_id":192}],"rom_address":3303364},"rom_address":3292324,"tmhm_learnset":"00402E82B5F37620","types":[0,0]},{"abilities":[33,75],"base_stats":[35,40,100,35,90,55],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":40,"species":139}],"friendship":70,"id":138,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":132},{"level":1,"move_id":110},{"level":13,"move_id":44},{"level":19,"move_id":55},{"level":25,"move_id":341},{"level":31,"move_id":43},{"level":37,"move_id":182},{"level":43,"move_id":321},{"level":49,"move_id":246},{"level":55,"move_id":56}],"rom_address":3303390},"rom_address":3292352,"tmhm_learnset":"03903E5084133264","types":[5,11]},{"abilities":[33,75],"base_stats":[70,60,125,55,115,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":139,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":132},{"level":1,"move_id":110},{"level":1,"move_id":44},{"level":13,"move_id":44},{"level":19,"move_id":55},{"level":25,"move_id":341},{"level":31,"move_id":43},{"level":37,"move_id":182},{"level":40,"move_id":131},{"level":46,"move_id":321},{"level":55,"move_id":246},{"level":65,"move_id":56}],"rom_address":3303416},"rom_address":3292380,"tmhm_learnset":"03903E5084137264","types":[5,11]},{"abilities":[33,4],"base_stats":[30,80,90,55,55,45],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":40,"species":141}],"friendship":70,"id":140,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":106},{"level":13,"move_id":71},{"level":19,"move_id":43},{"level":25,"move_id":341},{"level":31,"move_id":28},{"level":37,"move_id":203},{"level":43,"move_id":319},{"level":49,"move_id":72},{"level":55,"move_id":246}],"rom_address":3303444},"rom_address":3292408,"tmhm_learnset":"01903ED08C173264","types":[5,11]},{"abilities":[33,4],"base_stats":[60,115,105,80,65,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":141,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":106},{"level":1,"move_id":71},{"level":13,"move_id":71},{"level":19,"move_id":43},{"level":25,"move_id":341},{"level":31,"move_id":28},{"level":37,"move_id":203},{"level":40,"move_id":163},{"level":46,"move_id":319},{"level":55,"move_id":72},{"level":65,"move_id":246}],"rom_address":3303470},"rom_address":3292436,"tmhm_learnset":"03943ED0CC177264","types":[5,11]},{"abilities":[69,46],"base_stats":[80,105,65,130,60,75],"catch_rate":45,"evolutions":[],"friendship":70,"id":142,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":17},{"level":8,"move_id":97},{"level":15,"move_id":44},{"level":22,"move_id":48},{"level":29,"move_id":246},{"level":36,"move_id":184},{"level":43,"move_id":36},{"level":50,"move_id":63}],"rom_address":3303498},"rom_address":3292464,"tmhm_learnset":"00A87FF486534E32","types":[5,2]},{"abilities":[17,47],"base_stats":[160,110,65,30,65,110],"catch_rate":25,"evolutions":[],"friendship":70,"id":143,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":6,"move_id":133},{"level":10,"move_id":111},{"level":15,"move_id":187},{"level":19,"move_id":29},{"level":24,"move_id":281},{"level":28,"move_id":156},{"level":28,"move_id":173},{"level":33,"move_id":34},{"level":37,"move_id":335},{"level":42,"move_id":343},{"level":46,"move_id":205},{"level":51,"move_id":63}],"rom_address":3303522},"rom_address":3292492,"tmhm_learnset":"00301E76F7B37625","types":[0,0]},{"abilities":[46,0],"base_stats":[90,85,100,85,95,125],"catch_rate":3,"evolutions":[],"friendship":35,"id":144,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":16},{"level":1,"move_id":181},{"level":13,"move_id":54},{"level":25,"move_id":97},{"level":37,"move_id":170},{"level":49,"move_id":58},{"level":61,"move_id":115},{"level":73,"move_id":59},{"level":85,"move_id":329}],"rom_address":3303556},"rom_address":3292520,"tmhm_learnset":"00884E9184137674","types":[15,2]},{"abilities":[46,0],"base_stats":[90,90,85,100,125,90],"catch_rate":3,"evolutions":[],"friendship":35,"id":145,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":1,"move_id":84},{"level":13,"move_id":86},{"level":25,"move_id":97},{"level":37,"move_id":197},{"level":49,"move_id":65},{"level":61,"move_id":268},{"level":73,"move_id":113},{"level":85,"move_id":87}],"rom_address":3303580},"rom_address":3292548,"tmhm_learnset":"00C84E928593C630","types":[13,2]},{"abilities":[46,0],"base_stats":[90,100,90,90,125,85],"catch_rate":3,"evolutions":[],"friendship":35,"id":146,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":17},{"level":1,"move_id":52},{"level":13,"move_id":83},{"level":25,"move_id":97},{"level":37,"move_id":203},{"level":49,"move_id":53},{"level":61,"move_id":219},{"level":73,"move_id":257},{"level":85,"move_id":143}],"rom_address":3303604},"rom_address":3292576,"tmhm_learnset":"008A4EB4841B4630","types":[10,2]},{"abilities":[61,0],"base_stats":[41,64,45,50,50,50],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":30,"species":148}],"friendship":35,"id":147,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":35},{"level":1,"move_id":43},{"level":8,"move_id":86},{"level":15,"move_id":239},{"level":22,"move_id":82},{"level":29,"move_id":21},{"level":36,"move_id":97},{"level":43,"move_id":219},{"level":50,"move_id":200},{"level":57,"move_id":63}],"rom_address":3303628},"rom_address":3292604,"tmhm_learnset":"01101E2685DB7664","types":[16,16]},{"abilities":[61,0],"base_stats":[61,84,65,70,70,70],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":55,"species":149}],"friendship":35,"id":148,"learnset":{"moves":[{"level":1,"move_id":35},{"level":1,"move_id":43},{"level":1,"move_id":86},{"level":1,"move_id":239},{"level":8,"move_id":86},{"level":15,"move_id":239},{"level":22,"move_id":82},{"level":29,"move_id":21},{"level":38,"move_id":97},{"level":47,"move_id":219},{"level":56,"move_id":200},{"level":65,"move_id":63}],"rom_address":3303654},"rom_address":3292632,"tmhm_learnset":"01101E2685DB7664","types":[16,16]},{"abilities":[39,0],"base_stats":[91,134,95,80,100,100],"catch_rate":45,"evolutions":[],"friendship":35,"id":149,"learnset":{"moves":[{"level":1,"move_id":35},{"level":1,"move_id":43},{"level":1,"move_id":86},{"level":1,"move_id":239},{"level":8,"move_id":86},{"level":15,"move_id":239},{"level":22,"move_id":82},{"level":29,"move_id":21},{"level":38,"move_id":97},{"level":47,"move_id":219},{"level":55,"move_id":17},{"level":61,"move_id":200},{"level":75,"move_id":63}],"rom_address":3303680},"rom_address":3292660,"tmhm_learnset":"03BC5EF6C7DB7677","types":[16,2]},{"abilities":[46,0],"base_stats":[106,110,90,130,154,90],"catch_rate":3,"evolutions":[],"friendship":0,"id":150,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":93},{"level":1,"move_id":50},{"level":11,"move_id":112},{"level":22,"move_id":129},{"level":33,"move_id":244},{"level":44,"move_id":248},{"level":55,"move_id":54},{"level":66,"move_id":94},{"level":77,"move_id":133},{"level":88,"move_id":105},{"level":99,"move_id":219}],"rom_address":3303708},"rom_address":3292688,"tmhm_learnset":"00E18FF7F7FBFEED","types":[14,14]},{"abilities":[28,0],"base_stats":[100,100,100,100,100,100],"catch_rate":45,"evolutions":[],"friendship":100,"id":151,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":10,"move_id":144},{"level":20,"move_id":5},{"level":30,"move_id":118},{"level":40,"move_id":94},{"level":50,"move_id":246}],"rom_address":3303736},"rom_address":3292716,"tmhm_learnset":"03FFFFFFFFFFFFFF","types":[14,14]},{"abilities":[65,0],"base_stats":[45,49,65,45,49,65],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":16,"species":153}],"friendship":70,"id":152,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":8,"move_id":75},{"level":12,"move_id":115},{"level":15,"move_id":77},{"level":22,"move_id":235},{"level":29,"move_id":34},{"level":36,"move_id":113},{"level":43,"move_id":219},{"level":50,"move_id":76}],"rom_address":3303756},"rom_address":3292744,"tmhm_learnset":"00441E01847D8720","types":[12,12]},{"abilities":[65,0],"base_stats":[60,62,80,60,63,80],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":32,"species":154}],"friendship":70,"id":153,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":75},{"level":1,"move_id":115},{"level":8,"move_id":75},{"level":12,"move_id":115},{"level":15,"move_id":77},{"level":23,"move_id":235},{"level":31,"move_id":34},{"level":39,"move_id":113},{"level":47,"move_id":219},{"level":55,"move_id":76}],"rom_address":3303782},"rom_address":3292772,"tmhm_learnset":"00E41E01847D8720","types":[12,12]},{"abilities":[65,0],"base_stats":[80,82,100,80,83,100],"catch_rate":45,"evolutions":[],"friendship":70,"id":154,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":75},{"level":1,"move_id":115},{"level":8,"move_id":75},{"level":12,"move_id":115},{"level":15,"move_id":77},{"level":23,"move_id":235},{"level":31,"move_id":34},{"level":41,"move_id":113},{"level":51,"move_id":219},{"level":61,"move_id":76}],"rom_address":3303808},"rom_address":3292800,"tmhm_learnset":"00E41E01867DC720","types":[12,12]},{"abilities":[66,0],"base_stats":[39,52,43,65,60,50],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":14,"species":156}],"friendship":70,"id":155,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":43},{"level":6,"move_id":108},{"level":12,"move_id":52},{"level":19,"move_id":98},{"level":27,"move_id":172},{"level":36,"move_id":129},{"level":46,"move_id":53}],"rom_address":3303834},"rom_address":3292828,"tmhm_learnset":"00061EA48C110620","types":[10,10]},{"abilities":[66,0],"base_stats":[58,64,58,80,80,65],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":36,"species":157}],"friendship":70,"id":156,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":43},{"level":1,"move_id":108},{"level":6,"move_id":108},{"level":12,"move_id":52},{"level":21,"move_id":98},{"level":31,"move_id":172},{"level":42,"move_id":129},{"level":54,"move_id":53}],"rom_address":3303856},"rom_address":3292856,"tmhm_learnset":"00A61EA4CC110631","types":[10,10]},{"abilities":[66,0],"base_stats":[78,84,78,100,109,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":157,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":43},{"level":1,"move_id":108},{"level":1,"move_id":52},{"level":6,"move_id":108},{"level":12,"move_id":52},{"level":21,"move_id":98},{"level":31,"move_id":172},{"level":45,"move_id":129},{"level":60,"move_id":53}],"rom_address":3303878},"rom_address":3292884,"tmhm_learnset":"00A61EA4CE114631","types":[10,10]},{"abilities":[67,0],"base_stats":[50,65,64,43,44,48],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":18,"species":159}],"friendship":70,"id":158,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":43},{"level":7,"move_id":99},{"level":13,"move_id":55},{"level":20,"move_id":44},{"level":27,"move_id":184},{"level":35,"move_id":163},{"level":43,"move_id":103},{"level":52,"move_id":56}],"rom_address":3303900},"rom_address":3292912,"tmhm_learnset":"03141E80CC533265","types":[11,11]},{"abilities":[67,0],"base_stats":[65,80,80,58,59,63],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":30,"species":160}],"friendship":70,"id":159,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":43},{"level":1,"move_id":99},{"level":7,"move_id":99},{"level":13,"move_id":55},{"level":21,"move_id":44},{"level":28,"move_id":184},{"level":37,"move_id":163},{"level":45,"move_id":103},{"level":55,"move_id":56}],"rom_address":3303924},"rom_address":3292940,"tmhm_learnset":"03B41E80CC533275","types":[11,11]},{"abilities":[67,0],"base_stats":[85,105,100,78,79,83],"catch_rate":45,"evolutions":[],"friendship":70,"id":160,"learnset":{"moves":[{"level":1,"move_id":10},{"level":1,"move_id":43},{"level":1,"move_id":99},{"level":1,"move_id":55},{"level":7,"move_id":99},{"level":13,"move_id":55},{"level":21,"move_id":44},{"level":28,"move_id":184},{"level":38,"move_id":163},{"level":47,"move_id":103},{"level":58,"move_id":56}],"rom_address":3303948},"rom_address":3292968,"tmhm_learnset":"03B41E80CE537277","types":[11,11]},{"abilities":[50,51],"base_stats":[35,46,34,20,35,45],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":15,"species":162}],"friendship":70,"id":161,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":4,"move_id":111},{"level":7,"move_id":98},{"level":12,"move_id":154},{"level":17,"move_id":270},{"level":24,"move_id":21},{"level":31,"move_id":266},{"level":40,"move_id":156},{"level":49,"move_id":133}],"rom_address":3303972},"rom_address":3292996,"tmhm_learnset":"00143E06ECF31625","types":[0,0]},{"abilities":[50,51],"base_stats":[85,76,64,90,45,55],"catch_rate":90,"evolutions":[],"friendship":70,"id":162,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":111},{"level":1,"move_id":98},{"level":4,"move_id":111},{"level":7,"move_id":98},{"level":12,"move_id":154},{"level":19,"move_id":270},{"level":28,"move_id":21},{"level":37,"move_id":266},{"level":48,"move_id":156},{"level":59,"move_id":133}],"rom_address":3303998},"rom_address":3293024,"tmhm_learnset":"00B43E06EDF37625","types":[0,0]},{"abilities":[15,51],"base_stats":[60,30,30,50,36,56],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":20,"species":164}],"friendship":70,"id":163,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":6,"move_id":193},{"level":11,"move_id":64},{"level":16,"move_id":95},{"level":22,"move_id":115},{"level":28,"move_id":36},{"level":34,"move_id":93},{"level":48,"move_id":138}],"rom_address":3304024},"rom_address":3293052,"tmhm_learnset":"00487E81B4130620","types":[0,2]},{"abilities":[15,51],"base_stats":[100,50,50,70,76,96],"catch_rate":90,"evolutions":[],"friendship":70,"id":164,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":193},{"level":1,"move_id":64},{"level":6,"move_id":193},{"level":11,"move_id":64},{"level":16,"move_id":95},{"level":25,"move_id":115},{"level":33,"move_id":36},{"level":41,"move_id":93},{"level":57,"move_id":138}],"rom_address":3304048},"rom_address":3293080,"tmhm_learnset":"00487E81B4134620","types":[0,2]},{"abilities":[68,48],"base_stats":[40,20,30,55,40,80],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":18,"species":166}],"friendship":70,"id":165,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":8,"move_id":48},{"level":15,"move_id":4},{"level":22,"move_id":113},{"level":22,"move_id":115},{"level":22,"move_id":219},{"level":29,"move_id":226},{"level":36,"move_id":129},{"level":43,"move_id":97},{"level":50,"move_id":38}],"rom_address":3304072},"rom_address":3293108,"tmhm_learnset":"00403E81CC3D8621","types":[6,2]},{"abilities":[68,48],"base_stats":[55,35,50,85,55,110],"catch_rate":90,"evolutions":[],"friendship":70,"id":166,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":48},{"level":8,"move_id":48},{"level":15,"move_id":4},{"level":24,"move_id":113},{"level":24,"move_id":115},{"level":24,"move_id":219},{"level":33,"move_id":226},{"level":42,"move_id":129},{"level":51,"move_id":97},{"level":60,"move_id":38}],"rom_address":3304100},"rom_address":3293136,"tmhm_learnset":"00403E81CC3DC621","types":[6,2]},{"abilities":[68,15],"base_stats":[40,60,40,30,40,40],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":22,"species":168}],"friendship":70,"id":167,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":40},{"level":1,"move_id":81},{"level":6,"move_id":184},{"level":11,"move_id":132},{"level":17,"move_id":101},{"level":23,"move_id":141},{"level":30,"move_id":154},{"level":37,"move_id":169},{"level":45,"move_id":97},{"level":53,"move_id":94}],"rom_address":3304128},"rom_address":3293164,"tmhm_learnset":"00403E089C350620","types":[6,3]},{"abilities":[68,15],"base_stats":[70,90,70,40,60,60],"catch_rate":90,"evolutions":[],"friendship":70,"id":168,"learnset":{"moves":[{"level":1,"move_id":40},{"level":1,"move_id":81},{"level":1,"move_id":184},{"level":1,"move_id":132},{"level":6,"move_id":184},{"level":11,"move_id":132},{"level":17,"move_id":101},{"level":25,"move_id":141},{"level":34,"move_id":154},{"level":43,"move_id":169},{"level":53,"move_id":97},{"level":63,"move_id":94}],"rom_address":3304154},"rom_address":3293192,"tmhm_learnset":"00403E089C354620","types":[6,3]},{"abilities":[39,0],"base_stats":[85,90,80,130,70,80],"catch_rate":90,"evolutions":[],"friendship":70,"id":169,"learnset":{"moves":[{"level":1,"move_id":103},{"level":1,"move_id":141},{"level":1,"move_id":48},{"level":1,"move_id":310},{"level":6,"move_id":48},{"level":11,"move_id":310},{"level":16,"move_id":44},{"level":21,"move_id":17},{"level":28,"move_id":109},{"level":35,"move_id":314},{"level":42,"move_id":212},{"level":49,"move_id":305},{"level":56,"move_id":114}],"rom_address":3304180},"rom_address":3293220,"tmhm_learnset":"00097F88A4174E20","types":[3,2]},{"abilities":[10,35],"base_stats":[75,38,38,67,56,56],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":27,"species":171}],"friendship":70,"id":170,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":1,"move_id":86},{"level":5,"move_id":48},{"level":13,"move_id":175},{"level":17,"move_id":55},{"level":25,"move_id":209},{"level":29,"move_id":109},{"level":37,"move_id":36},{"level":41,"move_id":56},{"level":49,"move_id":268}],"rom_address":3304208},"rom_address":3293248,"tmhm_learnset":"03501E0285933264","types":[11,13]},{"abilities":[10,35],"base_stats":[125,58,58,67,76,76],"catch_rate":75,"evolutions":[],"friendship":70,"id":171,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":1,"move_id":86},{"level":1,"move_id":48},{"level":5,"move_id":48},{"level":13,"move_id":175},{"level":17,"move_id":55},{"level":25,"move_id":209},{"level":32,"move_id":109},{"level":43,"move_id":36},{"level":50,"move_id":56},{"level":61,"move_id":268}],"rom_address":3304234},"rom_address":3293276,"tmhm_learnset":"03501E0285937264","types":[11,13]},{"abilities":[9,0],"base_stats":[20,40,15,60,35,35],"catch_rate":190,"evolutions":[{"method":"FRIENDSHIP","param":0,"species":25}],"friendship":70,"id":172,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":84},{"level":1,"move_id":204},{"level":6,"move_id":39},{"level":8,"move_id":86},{"level":11,"move_id":186}],"rom_address":3304260},"rom_address":3293304,"tmhm_learnset":"00401E0285D38220","types":[13,13]},{"abilities":[56,0],"base_stats":[50,25,28,15,45,55],"catch_rate":150,"evolutions":[{"method":"FRIENDSHIP","param":0,"species":35}],"friendship":140,"id":173,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":204},{"level":4,"move_id":227},{"level":8,"move_id":47},{"level":13,"move_id":186}],"rom_address":3304276},"rom_address":3293332,"tmhm_learnset":"00401E27BC7B8624","types":[0,0]},{"abilities":[56,0],"base_stats":[90,30,15,15,40,20],"catch_rate":170,"evolutions":[{"method":"FRIENDSHIP","param":0,"species":39}],"friendship":70,"id":174,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":47},{"level":1,"move_id":204},{"level":4,"move_id":111},{"level":9,"move_id":1},{"level":14,"move_id":186}],"rom_address":3304292},"rom_address":3293360,"tmhm_learnset":"00401E27BC3B8624","types":[0,0]},{"abilities":[55,32],"base_stats":[35,20,65,20,40,65],"catch_rate":190,"evolutions":[{"method":"FRIENDSHIP","param":0,"species":176}],"friendship":70,"id":175,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":118},{"level":1,"move_id":45},{"level":1,"move_id":204},{"level":6,"move_id":118},{"level":11,"move_id":186},{"level":16,"move_id":281},{"level":21,"move_id":227},{"level":26,"move_id":266},{"level":31,"move_id":273},{"level":36,"move_id":219},{"level":41,"move_id":38}],"rom_address":3304308},"rom_address":3293388,"tmhm_learnset":"00C01E27B43B8624","types":[0,0]},{"abilities":[55,32],"base_stats":[55,40,85,40,80,105],"catch_rate":75,"evolutions":[],"friendship":70,"id":176,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":118},{"level":1,"move_id":45},{"level":1,"move_id":204},{"level":6,"move_id":118},{"level":11,"move_id":186},{"level":16,"move_id":281},{"level":21,"move_id":227},{"level":26,"move_id":266},{"level":31,"move_id":273},{"level":36,"move_id":219},{"level":41,"move_id":38}],"rom_address":3304334},"rom_address":3293416,"tmhm_learnset":"00C85EA7F43BC625","types":[0,2]},{"abilities":[28,48],"base_stats":[40,50,45,70,70,45],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":25,"species":178}],"friendship":70,"id":177,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":1,"move_id":43},{"level":10,"move_id":101},{"level":20,"move_id":100},{"level":30,"move_id":273},{"level":30,"move_id":248},{"level":40,"move_id":109},{"level":50,"move_id":94}],"rom_address":3304360},"rom_address":3293444,"tmhm_learnset":"0040FE81B4378628","types":[14,2]},{"abilities":[28,48],"base_stats":[65,75,70,95,95,70],"catch_rate":75,"evolutions":[],"friendship":70,"id":178,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":1,"move_id":43},{"level":10,"move_id":101},{"level":20,"move_id":100},{"level":35,"move_id":273},{"level":35,"move_id":248},{"level":50,"move_id":109},{"level":65,"move_id":94}],"rom_address":3304382},"rom_address":3293472,"tmhm_learnset":"0048FE81B437C628","types":[14,2]},{"abilities":[9,0],"base_stats":[55,40,40,35,65,45],"catch_rate":235,"evolutions":[{"method":"LEVEL","param":15,"species":180}],"friendship":70,"id":179,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":9,"move_id":84},{"level":16,"move_id":86},{"level":23,"move_id":178},{"level":30,"move_id":113},{"level":37,"move_id":87}],"rom_address":3304404},"rom_address":3293500,"tmhm_learnset":"00401E0285D38220","types":[13,13]},{"abilities":[9,0],"base_stats":[70,55,55,45,80,60],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":30,"species":181}],"friendship":70,"id":180,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":84},{"level":9,"move_id":84},{"level":18,"move_id":86},{"level":27,"move_id":178},{"level":36,"move_id":113},{"level":45,"move_id":87}],"rom_address":3304424},"rom_address":3293528,"tmhm_learnset":"00E01E02C5D38221","types":[13,13]},{"abilities":[9,0],"base_stats":[90,75,75,55,115,90],"catch_rate":45,"evolutions":[],"friendship":70,"id":181,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":84},{"level":1,"move_id":86},{"level":9,"move_id":84},{"level":18,"move_id":86},{"level":27,"move_id":178},{"level":30,"move_id":9},{"level":42,"move_id":113},{"level":57,"move_id":87}],"rom_address":3304444},"rom_address":3293556,"tmhm_learnset":"00E01E02C5D3C221","types":[13,13]},{"abilities":[34,0],"base_stats":[75,80,85,50,90,100],"catch_rate":45,"evolutions":[],"friendship":70,"id":182,"learnset":{"moves":[{"level":1,"move_id":71},{"level":1,"move_id":230},{"level":1,"move_id":78},{"level":1,"move_id":345},{"level":44,"move_id":80},{"level":55,"move_id":76}],"rom_address":3304466},"rom_address":3293584,"tmhm_learnset":"00441E08843D4720","types":[12,12]},{"abilities":[47,37],"base_stats":[70,20,50,40,20,50],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":18,"species":184}],"friendship":70,"id":183,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":3,"move_id":111},{"level":6,"move_id":39},{"level":10,"move_id":55},{"level":15,"move_id":205},{"level":21,"move_id":61},{"level":28,"move_id":38},{"level":36,"move_id":240},{"level":45,"move_id":56}],"rom_address":3304480},"rom_address":3293612,"tmhm_learnset":"03B01E00CC533265","types":[11,11]},{"abilities":[47,37],"base_stats":[100,50,80,50,50,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":184,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":111},{"level":1,"move_id":39},{"level":1,"move_id":55},{"level":3,"move_id":111},{"level":6,"move_id":39},{"level":10,"move_id":55},{"level":15,"move_id":205},{"level":24,"move_id":61},{"level":34,"move_id":38},{"level":45,"move_id":240},{"level":57,"move_id":56}],"rom_address":3304506},"rom_address":3293640,"tmhm_learnset":"03B01E00CC537265","types":[11,11]},{"abilities":[5,69],"base_stats":[70,100,115,30,30,65],"catch_rate":65,"evolutions":[],"friendship":70,"id":185,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":88},{"level":1,"move_id":102},{"level":9,"move_id":175},{"level":17,"move_id":67},{"level":25,"move_id":157},{"level":33,"move_id":335},{"level":41,"move_id":185},{"level":49,"move_id":21},{"level":57,"move_id":38}],"rom_address":3304532},"rom_address":3293668,"tmhm_learnset":"00A03E50CE110E29","types":[5,5]},{"abilities":[11,6],"base_stats":[90,75,75,70,90,100],"catch_rate":45,"evolutions":[],"friendship":70,"id":186,"learnset":{"moves":[{"level":1,"move_id":55},{"level":1,"move_id":95},{"level":1,"move_id":3},{"level":1,"move_id":195},{"level":35,"move_id":195},{"level":51,"move_id":207}],"rom_address":3304556},"rom_address":3293696,"tmhm_learnset":"03B03E00DE137265","types":[11,11]},{"abilities":[34,0],"base_stats":[35,35,40,50,35,55],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":18,"species":188}],"friendship":70,"id":187,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":150},{"level":5,"move_id":235},{"level":5,"move_id":39},{"level":10,"move_id":33},{"level":13,"move_id":77},{"level":15,"move_id":78},{"level":17,"move_id":79},{"level":20,"move_id":73},{"level":25,"move_id":178},{"level":30,"move_id":72}],"rom_address":3304570},"rom_address":3293724,"tmhm_learnset":"00401E8084350720","types":[12,2]},{"abilities":[34,0],"base_stats":[55,45,50,80,45,65],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":27,"species":189}],"friendship":70,"id":188,"learnset":{"moves":[{"level":1,"move_id":150},{"level":1,"move_id":235},{"level":1,"move_id":39},{"level":1,"move_id":33},{"level":5,"move_id":235},{"level":5,"move_id":39},{"level":10,"move_id":33},{"level":13,"move_id":77},{"level":15,"move_id":78},{"level":17,"move_id":79},{"level":22,"move_id":73},{"level":29,"move_id":178},{"level":36,"move_id":72}],"rom_address":3304598},"rom_address":3293752,"tmhm_learnset":"00401E8084350720","types":[12,2]},{"abilities":[34,0],"base_stats":[75,55,70,110,55,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":189,"learnset":{"moves":[{"level":1,"move_id":150},{"level":1,"move_id":235},{"level":1,"move_id":39},{"level":1,"move_id":33},{"level":5,"move_id":235},{"level":5,"move_id":39},{"level":10,"move_id":33},{"level":13,"move_id":77},{"level":15,"move_id":78},{"level":17,"move_id":79},{"level":22,"move_id":73},{"level":33,"move_id":178},{"level":44,"move_id":72}],"rom_address":3304626},"rom_address":3293780,"tmhm_learnset":"00401E8084354720","types":[12,2]},{"abilities":[50,53],"base_stats":[55,70,55,85,40,55],"catch_rate":45,"evolutions":[],"friendship":70,"id":190,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":39},{"level":6,"move_id":28},{"level":13,"move_id":310},{"level":18,"move_id":226},{"level":25,"move_id":321},{"level":31,"move_id":154},{"level":38,"move_id":129},{"level":43,"move_id":103},{"level":50,"move_id":97}],"rom_address":3304654},"rom_address":3293808,"tmhm_learnset":"00A53E82EDF30E25","types":[0,0]},{"abilities":[34,0],"base_stats":[30,30,30,30,30,30],"catch_rate":235,"evolutions":[{"method":"ITEM","param":93,"species":192}],"friendship":70,"id":191,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":71},{"level":6,"move_id":74},{"level":13,"move_id":72},{"level":18,"move_id":275},{"level":25,"move_id":283},{"level":30,"move_id":241},{"level":37,"move_id":235},{"level":42,"move_id":202}],"rom_address":3304680},"rom_address":3293836,"tmhm_learnset":"00441E08843D8720","types":[12,12]},{"abilities":[34,0],"base_stats":[75,75,55,30,105,85],"catch_rate":120,"evolutions":[],"friendship":70,"id":192,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":71},{"level":1,"move_id":1},{"level":6,"move_id":74},{"level":13,"move_id":75},{"level":18,"move_id":275},{"level":25,"move_id":331},{"level":30,"move_id":241},{"level":37,"move_id":80},{"level":42,"move_id":76}],"rom_address":3304704},"rom_address":3293864,"tmhm_learnset":"00441E08843DC720","types":[12,12]},{"abilities":[3,14],"base_stats":[65,65,45,95,75,45],"catch_rate":75,"evolutions":[],"friendship":70,"id":193,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":193},{"level":7,"move_id":98},{"level":13,"move_id":104},{"level":19,"move_id":49},{"level":25,"move_id":197},{"level":31,"move_id":48},{"level":37,"move_id":253},{"level":43,"move_id":17},{"level":49,"move_id":103}],"rom_address":3304728},"rom_address":3293892,"tmhm_learnset":"00407E80B4350620","types":[6,2]},{"abilities":[6,11],"base_stats":[55,45,45,15,25,25],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":20,"species":195}],"friendship":70,"id":194,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":55},{"level":1,"move_id":39},{"level":11,"move_id":21},{"level":16,"move_id":341},{"level":21,"move_id":133},{"level":31,"move_id":281},{"level":36,"move_id":89},{"level":41,"move_id":240},{"level":51,"move_id":54},{"level":51,"move_id":114}],"rom_address":3304754},"rom_address":3293920,"tmhm_learnset":"03D01E188E533264","types":[11,4]},{"abilities":[6,11],"base_stats":[95,85,85,35,65,65],"catch_rate":90,"evolutions":[],"friendship":70,"id":195,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":55},{"level":1,"move_id":39},{"level":11,"move_id":21},{"level":16,"move_id":341},{"level":23,"move_id":133},{"level":35,"move_id":281},{"level":42,"move_id":89},{"level":49,"move_id":240},{"level":61,"move_id":54},{"level":61,"move_id":114}],"rom_address":3304780},"rom_address":3293948,"tmhm_learnset":"03F01E58CE537265","types":[11,4]},{"abilities":[28,0],"base_stats":[65,65,60,110,130,95],"catch_rate":45,"evolutions":[],"friendship":70,"id":196,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":270},{"level":8,"move_id":28},{"level":16,"move_id":93},{"level":23,"move_id":98},{"level":30,"move_id":129},{"level":36,"move_id":60},{"level":42,"move_id":244},{"level":47,"move_id":94},{"level":52,"move_id":234}],"rom_address":3304806},"rom_address":3293976,"tmhm_learnset":"00449E01BC53C628","types":[14,14]},{"abilities":[28,0],"base_stats":[95,65,110,65,60,130],"catch_rate":45,"evolutions":[],"friendship":35,"id":197,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":270},{"level":8,"move_id":28},{"level":16,"move_id":228},{"level":23,"move_id":98},{"level":30,"move_id":109},{"level":36,"move_id":185},{"level":42,"move_id":212},{"level":47,"move_id":103},{"level":52,"move_id":236}],"rom_address":3304832},"rom_address":3294004,"tmhm_learnset":"00451F00BC534E20","types":[17,17]},{"abilities":[15,0],"base_stats":[60,85,42,91,85,42],"catch_rate":30,"evolutions":[],"friendship":35,"id":198,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":9,"move_id":310},{"level":14,"move_id":228},{"level":22,"move_id":114},{"level":27,"move_id":101},{"level":35,"move_id":185},{"level":40,"move_id":269},{"level":48,"move_id":212}],"rom_address":3304858},"rom_address":3294032,"tmhm_learnset":"00097F80A4130E28","types":[17,2]},{"abilities":[12,20],"base_stats":[95,75,80,30,100,110],"catch_rate":70,"evolutions":[],"friendship":70,"id":199,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":174},{"level":1,"move_id":281},{"level":1,"move_id":33},{"level":6,"move_id":45},{"level":15,"move_id":55},{"level":20,"move_id":93},{"level":29,"move_id":50},{"level":34,"move_id":29},{"level":43,"move_id":207},{"level":48,"move_id":94}],"rom_address":3304882},"rom_address":3294060,"tmhm_learnset":"02F09E24FE5B766D","types":[11,14]},{"abilities":[26,0],"base_stats":[60,60,60,85,85,85],"catch_rate":45,"evolutions":[],"friendship":35,"id":200,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":45},{"level":1,"move_id":149},{"level":6,"move_id":180},{"level":11,"move_id":310},{"level":17,"move_id":109},{"level":23,"move_id":212},{"level":30,"move_id":60},{"level":37,"move_id":220},{"level":45,"move_id":195},{"level":53,"move_id":288}],"rom_address":3304906},"rom_address":3294088,"tmhm_learnset":"0041BF82B5930E28","types":[7,7]},{"abilities":[26,0],"base_stats":[48,72,48,48,72,48],"catch_rate":225,"evolutions":[],"friendship":70,"id":201,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":237}],"rom_address":3304932},"rom_address":3294116,"tmhm_learnset":"0000000000000000","types":[14,14]},{"abilities":[23,0],"base_stats":[190,33,58,33,33,58],"catch_rate":45,"evolutions":[],"friendship":70,"id":202,"learnset":{"moves":[{"level":1,"move_id":68},{"level":1,"move_id":243},{"level":1,"move_id":219},{"level":1,"move_id":194}],"rom_address":3304942},"rom_address":3294144,"tmhm_learnset":"0000000000000000","types":[14,14]},{"abilities":[39,48],"base_stats":[70,80,65,85,90,65],"catch_rate":60,"evolutions":[],"friendship":70,"id":203,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":7,"move_id":310},{"level":13,"move_id":93},{"level":19,"move_id":23},{"level":25,"move_id":316},{"level":31,"move_id":97},{"level":37,"move_id":226},{"level":43,"move_id":60},{"level":49,"move_id":242}],"rom_address":3304952},"rom_address":3294172,"tmhm_learnset":"00E0BE03B7D38628","types":[0,14]},{"abilities":[5,0],"base_stats":[50,65,90,15,35,35],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":31,"species":205}],"friendship":70,"id":204,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":182},{"level":8,"move_id":120},{"level":15,"move_id":36},{"level":22,"move_id":229},{"level":29,"move_id":117},{"level":36,"move_id":153},{"level":43,"move_id":191},{"level":50,"move_id":38}],"rom_address":3304978},"rom_address":3294200,"tmhm_learnset":"00A01E118E358620","types":[6,6]},{"abilities":[5,0],"base_stats":[75,90,140,40,60,60],"catch_rate":75,"evolutions":[],"friendship":70,"id":205,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":182},{"level":1,"move_id":120},{"level":8,"move_id":120},{"level":15,"move_id":36},{"level":22,"move_id":229},{"level":29,"move_id":117},{"level":39,"move_id":153},{"level":49,"move_id":191},{"level":59,"move_id":38}],"rom_address":3305002},"rom_address":3294228,"tmhm_learnset":"00A01E118E35C620","types":[6,8]},{"abilities":[32,50],"base_stats":[100,70,70,45,65,65],"catch_rate":190,"evolutions":[],"friendship":70,"id":206,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":99},{"level":4,"move_id":111},{"level":11,"move_id":281},{"level":14,"move_id":137},{"level":21,"move_id":180},{"level":24,"move_id":228},{"level":31,"move_id":103},{"level":34,"move_id":36},{"level":41,"move_id":283}],"rom_address":3305026},"rom_address":3294256,"tmhm_learnset":"00A03E66AFF3362C","types":[0,0]},{"abilities":[52,8],"base_stats":[65,75,105,85,35,65],"catch_rate":60,"evolutions":[],"friendship":70,"id":207,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":40},{"level":6,"move_id":28},{"level":13,"move_id":106},{"level":20,"move_id":98},{"level":28,"move_id":185},{"level":36,"move_id":163},{"level":44,"move_id":103},{"level":52,"move_id":12}],"rom_address":3305052},"rom_address":3294284,"tmhm_learnset":"00A47ED88E530620","types":[4,2]},{"abilities":[69,5],"base_stats":[75,85,200,30,55,65],"catch_rate":25,"evolutions":[],"friendship":70,"id":208,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":103},{"level":9,"move_id":20},{"level":13,"move_id":88},{"level":21,"move_id":106},{"level":25,"move_id":99},{"level":33,"move_id":201},{"level":37,"move_id":21},{"level":45,"move_id":231},{"level":49,"move_id":242},{"level":57,"move_id":38}],"rom_address":3305076},"rom_address":3294312,"tmhm_learnset":"00A41F508E514E30","types":[8,4]},{"abilities":[22,50],"base_stats":[60,80,50,30,40,40],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":23,"species":210}],"friendship":70,"id":209,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":184},{"level":4,"move_id":39},{"level":8,"move_id":204},{"level":13,"move_id":44},{"level":19,"move_id":122},{"level":26,"move_id":46},{"level":34,"move_id":99},{"level":43,"move_id":36},{"level":53,"move_id":242}],"rom_address":3305104},"rom_address":3294340,"tmhm_learnset":"00A23F2EEFB30EB5","types":[0,0]},{"abilities":[22,22],"base_stats":[90,120,75,45,60,60],"catch_rate":75,"evolutions":[],"friendship":70,"id":210,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":184},{"level":4,"move_id":39},{"level":8,"move_id":204},{"level":13,"move_id":44},{"level":19,"move_id":122},{"level":28,"move_id":46},{"level":38,"move_id":99},{"level":49,"move_id":36},{"level":61,"move_id":242}],"rom_address":3305130},"rom_address":3294368,"tmhm_learnset":"00A23F6EEFF34EB5","types":[0,0]},{"abilities":[38,33],"base_stats":[65,95,75,85,55,55],"catch_rate":45,"evolutions":[],"friendship":70,"id":211,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":191},{"level":1,"move_id":33},{"level":1,"move_id":40},{"level":10,"move_id":106},{"level":10,"move_id":107},{"level":19,"move_id":55},{"level":28,"move_id":42},{"level":37,"move_id":36},{"level":46,"move_id":56}],"rom_address":3305156},"rom_address":3294396,"tmhm_learnset":"03101E0AA4133264","types":[11,3]},{"abilities":[68,0],"base_stats":[70,130,100,65,55,80],"catch_rate":25,"evolutions":[],"friendship":70,"id":212,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":98},{"level":1,"move_id":43},{"level":6,"move_id":116},{"level":11,"move_id":228},{"level":16,"move_id":206},{"level":21,"move_id":97},{"level":26,"move_id":232},{"level":31,"move_id":163},{"level":36,"move_id":14},{"level":41,"move_id":104},{"level":46,"move_id":210}],"rom_address":3305178},"rom_address":3294424,"tmhm_learnset":"00A47E9084134620","types":[6,8]},{"abilities":[5,0],"base_stats":[20,10,230,5,10,230],"catch_rate":190,"evolutions":[],"friendship":70,"id":213,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":132},{"level":1,"move_id":110},{"level":9,"move_id":35},{"level":14,"move_id":227},{"level":23,"move_id":219},{"level":28,"move_id":117},{"level":37,"move_id":156}],"rom_address":3305206},"rom_address":3294452,"tmhm_learnset":"00E01E588E190620","types":[6,5]},{"abilities":[68,62],"base_stats":[80,125,75,85,40,95],"catch_rate":45,"evolutions":[],"friendship":70,"id":214,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":43},{"level":6,"move_id":30},{"level":11,"move_id":203},{"level":17,"move_id":31},{"level":23,"move_id":280},{"level":30,"move_id":68},{"level":37,"move_id":36},{"level":45,"move_id":179},{"level":53,"move_id":224}],"rom_address":3305226},"rom_address":3294480,"tmhm_learnset":"00A43E40CE1346A1","types":[6,1]},{"abilities":[39,51],"base_stats":[55,95,55,115,35,75],"catch_rate":60,"evolutions":[],"friendship":35,"id":215,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":43},{"level":1,"move_id":269},{"level":8,"move_id":98},{"level":15,"move_id":103},{"level":22,"move_id":185},{"level":29,"move_id":154},{"level":36,"move_id":97},{"level":43,"move_id":196},{"level":50,"move_id":163},{"level":57,"move_id":251},{"level":64,"move_id":232}],"rom_address":3305252},"rom_address":3294508,"tmhm_learnset":"00B53F80EC533E69","types":[17,15]},{"abilities":[53,0],"base_stats":[60,80,50,40,50,50],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":30,"species":217}],"friendship":70,"id":216,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":43},{"level":7,"move_id":122},{"level":13,"move_id":154},{"level":19,"move_id":313},{"level":25,"move_id":185},{"level":31,"move_id":156},{"level":37,"move_id":163},{"level":43,"move_id":173},{"level":49,"move_id":37}],"rom_address":3305280},"rom_address":3294536,"tmhm_learnset":"00A43F80CE130EB1","types":[0,0]},{"abilities":[62,0],"base_stats":[90,130,75,55,75,75],"catch_rate":60,"evolutions":[],"friendship":70,"id":217,"learnset":{"moves":[{"level":1,"move_id":10},{"level":1,"move_id":43},{"level":1,"move_id":122},{"level":1,"move_id":154},{"level":7,"move_id":122},{"level":13,"move_id":154},{"level":19,"move_id":313},{"level":25,"move_id":185},{"level":31,"move_id":156},{"level":37,"move_id":163},{"level":43,"move_id":173},{"level":49,"move_id":37}],"rom_address":3305306},"rom_address":3294564,"tmhm_learnset":"00A43FC0CE134EB1","types":[0,0]},{"abilities":[40,49],"base_stats":[40,40,40,20,70,40],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":38,"species":219}],"friendship":70,"id":218,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":281},{"level":1,"move_id":123},{"level":8,"move_id":52},{"level":15,"move_id":88},{"level":22,"move_id":106},{"level":29,"move_id":133},{"level":36,"move_id":53},{"level":43,"move_id":157},{"level":50,"move_id":34}],"rom_address":3305332},"rom_address":3294592,"tmhm_learnset":"00821E2584118620","types":[10,10]},{"abilities":[40,49],"base_stats":[50,50,120,30,80,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":219,"learnset":{"moves":[{"level":1,"move_id":281},{"level":1,"move_id":123},{"level":1,"move_id":52},{"level":1,"move_id":88},{"level":8,"move_id":52},{"level":15,"move_id":88},{"level":22,"move_id":106},{"level":29,"move_id":133},{"level":36,"move_id":53},{"level":48,"move_id":157},{"level":60,"move_id":34}],"rom_address":3305356},"rom_address":3294620,"tmhm_learnset":"00A21E758611C620","types":[10,5]},{"abilities":[12,0],"base_stats":[50,50,40,50,30,30],"catch_rate":225,"evolutions":[{"method":"LEVEL","param":33,"species":221}],"friendship":70,"id":220,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":316},{"level":10,"move_id":181},{"level":19,"move_id":203},{"level":28,"move_id":36},{"level":37,"move_id":54},{"level":46,"move_id":59},{"level":55,"move_id":133}],"rom_address":3305380},"rom_address":3294648,"tmhm_learnset":"00A01E518E13B270","types":[15,4]},{"abilities":[12,0],"base_stats":[100,100,80,50,60,60],"catch_rate":75,"evolutions":[],"friendship":70,"id":221,"learnset":{"moves":[{"level":1,"move_id":30},{"level":1,"move_id":316},{"level":1,"move_id":181},{"level":1,"move_id":203},{"level":10,"move_id":181},{"level":19,"move_id":203},{"level":28,"move_id":36},{"level":33,"move_id":31},{"level":42,"move_id":54},{"level":56,"move_id":59},{"level":70,"move_id":133}],"rom_address":3305402},"rom_address":3294676,"tmhm_learnset":"00A01E518E13F270","types":[15,4]},{"abilities":[55,30],"base_stats":[55,55,85,35,65,85],"catch_rate":60,"evolutions":[],"friendship":70,"id":222,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":6,"move_id":106},{"level":12,"move_id":145},{"level":17,"move_id":105},{"level":17,"move_id":287},{"level":23,"move_id":61},{"level":28,"move_id":131},{"level":34,"move_id":350},{"level":39,"move_id":243},{"level":45,"move_id":246}],"rom_address":3305426},"rom_address":3294704,"tmhm_learnset":"00B01E51BE1BB66C","types":[11,5]},{"abilities":[55,0],"base_stats":[35,65,35,65,65,35],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":25,"species":224}],"friendship":70,"id":223,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":55},{"level":11,"move_id":199},{"level":22,"move_id":60},{"level":22,"move_id":62},{"level":22,"move_id":61},{"level":33,"move_id":116},{"level":44,"move_id":58},{"level":55,"move_id":63}],"rom_address":3305454},"rom_address":3294732,"tmhm_learnset":"03103E2494137624","types":[11,11]},{"abilities":[21,0],"base_stats":[75,105,75,45,105,75],"catch_rate":75,"evolutions":[],"friendship":70,"id":224,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":55},{"level":11,"move_id":132},{"level":22,"move_id":60},{"level":22,"move_id":62},{"level":22,"move_id":61},{"level":25,"move_id":190},{"level":38,"move_id":116},{"level":54,"move_id":58},{"level":70,"move_id":63}],"rom_address":3305478},"rom_address":3294760,"tmhm_learnset":"03103E2C94137724","types":[11,11]},{"abilities":[72,55],"base_stats":[45,55,45,75,65,45],"catch_rate":45,"evolutions":[],"friendship":70,"id":225,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":217}],"rom_address":3305504},"rom_address":3294788,"tmhm_learnset":"00083E8084133265","types":[15,2]},{"abilities":[33,11],"base_stats":[65,40,70,70,80,140],"catch_rate":25,"evolutions":[],"friendship":70,"id":226,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":145},{"level":8,"move_id":48},{"level":15,"move_id":61},{"level":22,"move_id":36},{"level":29,"move_id":97},{"level":36,"move_id":17},{"level":43,"move_id":352},{"level":50,"move_id":109}],"rom_address":3305514},"rom_address":3294816,"tmhm_learnset":"03101E8086133264","types":[11,2]},{"abilities":[51,5],"base_stats":[65,80,140,70,40,70],"catch_rate":25,"evolutions":[],"friendship":70,"id":227,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":64},{"level":10,"move_id":28},{"level":13,"move_id":129},{"level":16,"move_id":97},{"level":26,"move_id":31},{"level":29,"move_id":314},{"level":32,"move_id":211},{"level":42,"move_id":191},{"level":45,"move_id":319}],"rom_address":3305538},"rom_address":3294844,"tmhm_learnset":"008C7F9084110E30","types":[8,2]},{"abilities":[48,18],"base_stats":[45,60,30,65,80,50],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":24,"species":229}],"friendship":35,"id":228,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":52},{"level":7,"move_id":336},{"level":13,"move_id":123},{"level":19,"move_id":46},{"level":25,"move_id":44},{"level":31,"move_id":316},{"level":37,"move_id":185},{"level":43,"move_id":53},{"level":49,"move_id":242}],"rom_address":3305564},"rom_address":3294872,"tmhm_learnset":"00833F2CA4710E30","types":[17,10]},{"abilities":[48,18],"base_stats":[75,90,50,95,110,80],"catch_rate":45,"evolutions":[],"friendship":35,"id":229,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":52},{"level":1,"move_id":336},{"level":7,"move_id":336},{"level":13,"move_id":123},{"level":19,"move_id":46},{"level":27,"move_id":44},{"level":35,"move_id":316},{"level":43,"move_id":185},{"level":51,"move_id":53},{"level":59,"move_id":242}],"rom_address":3305590},"rom_address":3294900,"tmhm_learnset":"00A33F2CA4714E30","types":[17,10]},{"abilities":[33,0],"base_stats":[75,95,95,85,95,95],"catch_rate":45,"evolutions":[],"friendship":70,"id":230,"learnset":{"moves":[{"level":1,"move_id":145},{"level":1,"move_id":108},{"level":1,"move_id":43},{"level":1,"move_id":55},{"level":8,"move_id":108},{"level":15,"move_id":43},{"level":22,"move_id":55},{"level":29,"move_id":239},{"level":40,"move_id":97},{"level":51,"move_id":56},{"level":62,"move_id":349}],"rom_address":3305616},"rom_address":3294928,"tmhm_learnset":"03101E0084137264","types":[11,16]},{"abilities":[53,0],"base_stats":[90,60,60,40,40,40],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":25,"species":232}],"friendship":70,"id":231,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":316},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":9,"move_id":111},{"level":17,"move_id":175},{"level":25,"move_id":36},{"level":33,"move_id":205},{"level":41,"move_id":203},{"level":49,"move_id":38}],"rom_address":3305640},"rom_address":3294956,"tmhm_learnset":"00A01E5086510630","types":[4,4]},{"abilities":[5,0],"base_stats":[90,120,120,50,60,60],"catch_rate":60,"evolutions":[],"friendship":70,"id":232,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":316},{"level":1,"move_id":30},{"level":1,"move_id":45},{"level":9,"move_id":111},{"level":17,"move_id":175},{"level":25,"move_id":31},{"level":33,"move_id":205},{"level":41,"move_id":229},{"level":49,"move_id":89}],"rom_address":3305662},"rom_address":3294984,"tmhm_learnset":"00A01E5086514630","types":[4,4]},{"abilities":[36,0],"base_stats":[85,80,90,60,105,95],"catch_rate":45,"evolutions":[],"friendship":70,"id":233,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":176},{"level":1,"move_id":33},{"level":1,"move_id":160},{"level":9,"move_id":97},{"level":12,"move_id":60},{"level":20,"move_id":105},{"level":24,"move_id":111},{"level":32,"move_id":199},{"level":36,"move_id":161},{"level":44,"move_id":278},{"level":48,"move_id":192}],"rom_address":3305684},"rom_address":3295012,"tmhm_learnset":"00402E82B5F37620","types":[0,0]},{"abilities":[22,0],"base_stats":[73,95,62,85,85,65],"catch_rate":45,"evolutions":[],"friendship":70,"id":234,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":7,"move_id":43},{"level":13,"move_id":310},{"level":19,"move_id":95},{"level":25,"move_id":23},{"level":31,"move_id":28},{"level":37,"move_id":36},{"level":43,"move_id":109},{"level":49,"move_id":347}],"rom_address":3305710},"rom_address":3295040,"tmhm_learnset":"0040BE03B7F38638","types":[0,0]},{"abilities":[20,0],"base_stats":[55,20,35,75,20,45],"catch_rate":45,"evolutions":[],"friendship":70,"id":235,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":166},{"level":11,"move_id":166},{"level":21,"move_id":166},{"level":31,"move_id":166},{"level":41,"move_id":166},{"level":51,"move_id":166},{"level":61,"move_id":166},{"level":71,"move_id":166},{"level":81,"move_id":166},{"level":91,"move_id":166}],"rom_address":3305736},"rom_address":3295068,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[62,0],"base_stats":[35,35,35,35,35,35],"catch_rate":75,"evolutions":[{"method":"LEVEL_ATK_LT_DEF","param":20,"species":107},{"method":"LEVEL_ATK_GT_DEF","param":20,"species":106},{"method":"LEVEL_ATK_EQ_DEF","param":20,"species":237}],"friendship":70,"id":236,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3305764},"rom_address":3295096,"tmhm_learnset":"00A03E00C61306A0","types":[1,1]},{"abilities":[22,0],"base_stats":[50,95,95,70,35,110],"catch_rate":45,"evolutions":[],"friendship":70,"id":237,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":279},{"level":1,"move_id":27},{"level":7,"move_id":116},{"level":13,"move_id":228},{"level":19,"move_id":98},{"level":20,"move_id":167},{"level":25,"move_id":229},{"level":31,"move_id":68},{"level":37,"move_id":97},{"level":43,"move_id":197},{"level":49,"move_id":283}],"rom_address":3305774},"rom_address":3295124,"tmhm_learnset":"00A03E10CE1306A0","types":[1,1]},{"abilities":[12,0],"base_stats":[45,30,15,65,85,65],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":30,"species":124}],"friendship":70,"id":238,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":122},{"level":9,"move_id":186},{"level":13,"move_id":181},{"level":21,"move_id":93},{"level":25,"move_id":47},{"level":33,"move_id":212},{"level":37,"move_id":313},{"level":45,"move_id":94},{"level":49,"move_id":195},{"level":57,"move_id":59}],"rom_address":3305802},"rom_address":3295152,"tmhm_learnset":"0040BE01B413B26C","types":[15,14]},{"abilities":[9,0],"base_stats":[45,63,37,95,65,55],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":30,"species":125}],"friendship":70,"id":239,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":98},{"level":1,"move_id":43},{"level":9,"move_id":9},{"level":17,"move_id":113},{"level":25,"move_id":129},{"level":33,"move_id":103},{"level":41,"move_id":85},{"level":49,"move_id":87}],"rom_address":3305830},"rom_address":3295180,"tmhm_learnset":"00C03E02D5938221","types":[13,13]},{"abilities":[49,0],"base_stats":[45,75,37,83,70,55],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":30,"species":126}],"friendship":70,"id":240,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":52},{"level":7,"move_id":43},{"level":13,"move_id":123},{"level":19,"move_id":7},{"level":25,"move_id":108},{"level":31,"move_id":241},{"level":37,"move_id":53},{"level":43,"move_id":109},{"level":49,"move_id":126}],"rom_address":3305852},"rom_address":3295208,"tmhm_learnset":"00803E24D4510621","types":[10,10]},{"abilities":[47,0],"base_stats":[95,80,105,100,40,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":241,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":4,"move_id":45},{"level":8,"move_id":111},{"level":13,"move_id":23},{"level":19,"move_id":208},{"level":26,"move_id":117},{"level":34,"move_id":205},{"level":43,"move_id":34},{"level":53,"move_id":215}],"rom_address":3305878},"rom_address":3295236,"tmhm_learnset":"00B01E52E7F37625","types":[0,0]},{"abilities":[30,32],"base_stats":[255,10,10,55,75,135],"catch_rate":30,"evolutions":[],"friendship":140,"id":242,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":45},{"level":4,"move_id":39},{"level":7,"move_id":287},{"level":10,"move_id":135},{"level":13,"move_id":3},{"level":18,"move_id":107},{"level":23,"move_id":47},{"level":28,"move_id":121},{"level":33,"move_id":111},{"level":40,"move_id":113},{"level":47,"move_id":38}],"rom_address":3305904},"rom_address":3295264,"tmhm_learnset":"00E19E76F7FBF66D","types":[0,0]},{"abilities":[46,0],"base_stats":[90,85,75,115,115,100],"catch_rate":3,"evolutions":[],"friendship":35,"id":243,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":44},{"level":1,"move_id":43},{"level":11,"move_id":84},{"level":21,"move_id":46},{"level":31,"move_id":98},{"level":41,"move_id":209},{"level":51,"move_id":115},{"level":61,"move_id":242},{"level":71,"move_id":87},{"level":81,"move_id":347}],"rom_address":3305934},"rom_address":3295292,"tmhm_learnset":"00E40E138DD34638","types":[13,13]},{"abilities":[46,0],"base_stats":[115,115,85,100,90,75],"catch_rate":3,"evolutions":[],"friendship":35,"id":244,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":44},{"level":1,"move_id":43},{"level":11,"move_id":52},{"level":21,"move_id":46},{"level":31,"move_id":83},{"level":41,"move_id":23},{"level":51,"move_id":53},{"level":61,"move_id":207},{"level":71,"move_id":126},{"level":81,"move_id":347}],"rom_address":3305960},"rom_address":3295320,"tmhm_learnset":"00E40E358C734638","types":[10,10]},{"abilities":[46,0],"base_stats":[100,75,115,85,90,115],"catch_rate":3,"evolutions":[],"friendship":35,"id":245,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":44},{"level":1,"move_id":43},{"level":11,"move_id":61},{"level":21,"move_id":240},{"level":31,"move_id":16},{"level":41,"move_id":62},{"level":51,"move_id":54},{"level":61,"move_id":243},{"level":71,"move_id":56},{"level":81,"move_id":347}],"rom_address":3305986},"rom_address":3295348,"tmhm_learnset":"03940E118C53767C","types":[11,11]},{"abilities":[62,0],"base_stats":[50,64,50,41,45,50],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":30,"species":247}],"friendship":35,"id":246,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":44},{"level":1,"move_id":43},{"level":8,"move_id":201},{"level":15,"move_id":103},{"level":22,"move_id":157},{"level":29,"move_id":37},{"level":36,"move_id":184},{"level":43,"move_id":242},{"level":50,"move_id":89},{"level":57,"move_id":63}],"rom_address":3306012},"rom_address":3295376,"tmhm_learnset":"00801F10CE134E20","types":[5,4]},{"abilities":[61,0],"base_stats":[70,84,70,51,65,70],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":55,"species":248}],"friendship":35,"id":247,"learnset":{"moves":[{"level":1,"move_id":44},{"level":1,"move_id":43},{"level":1,"move_id":201},{"level":1,"move_id":103},{"level":8,"move_id":201},{"level":15,"move_id":103},{"level":22,"move_id":157},{"level":29,"move_id":37},{"level":38,"move_id":184},{"level":47,"move_id":242},{"level":56,"move_id":89},{"level":65,"move_id":63}],"rom_address":3306038},"rom_address":3295404,"tmhm_learnset":"00801F10CE134E20","types":[5,4]},{"abilities":[45,0],"base_stats":[100,134,110,61,95,100],"catch_rate":45,"evolutions":[],"friendship":35,"id":248,"learnset":{"moves":[{"level":1,"move_id":44},{"level":1,"move_id":43},{"level":1,"move_id":201},{"level":1,"move_id":103},{"level":8,"move_id":201},{"level":15,"move_id":103},{"level":22,"move_id":157},{"level":29,"move_id":37},{"level":38,"move_id":184},{"level":47,"move_id":242},{"level":61,"move_id":89},{"level":75,"move_id":63}],"rom_address":3306064},"rom_address":3295432,"tmhm_learnset":"00B41FF6CFD37E37","types":[5,17]},{"abilities":[46,0],"base_stats":[106,90,130,110,90,154],"catch_rate":3,"evolutions":[],"friendship":0,"id":249,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":16},{"level":1,"move_id":18},{"level":11,"move_id":219},{"level":22,"move_id":16},{"level":33,"move_id":105},{"level":44,"move_id":56},{"level":55,"move_id":240},{"level":66,"move_id":129},{"level":77,"move_id":177},{"level":88,"move_id":246},{"level":99,"move_id":248}],"rom_address":3306090},"rom_address":3295460,"tmhm_learnset":"03B8CE93B7DFF67C","types":[14,2]},{"abilities":[46,0],"base_stats":[106,130,90,90,110,154],"catch_rate":3,"evolutions":[],"friendship":0,"id":250,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":18},{"level":11,"move_id":219},{"level":22,"move_id":16},{"level":33,"move_id":105},{"level":44,"move_id":126},{"level":55,"move_id":241},{"level":66,"move_id":129},{"level":77,"move_id":221},{"level":88,"move_id":246},{"level":99,"move_id":248}],"rom_address":3306118},"rom_address":3295488,"tmhm_learnset":"00EA4EB7B7BFC638","types":[10,2]},{"abilities":[30,0],"base_stats":[100,100,100,100,100,100],"catch_rate":45,"evolutions":[],"friendship":100,"id":251,"learnset":{"moves":[{"level":1,"move_id":73},{"level":1,"move_id":93},{"level":1,"move_id":105},{"level":1,"move_id":215},{"level":10,"move_id":219},{"level":20,"move_id":246},{"level":30,"move_id":248},{"level":40,"move_id":226},{"level":50,"move_id":195}],"rom_address":3306146},"rom_address":3295516,"tmhm_learnset":"00448E93B43FC62C","types":[14,12]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":252,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306166},"rom_address":3295544,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":253,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306176},"rom_address":3295572,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":254,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306186},"rom_address":3295600,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":255,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306196},"rom_address":3295628,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":256,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306206},"rom_address":3295656,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":257,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306216},"rom_address":3295684,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":258,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306226},"rom_address":3295712,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":259,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306236},"rom_address":3295740,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":260,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306246},"rom_address":3295768,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":261,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306256},"rom_address":3295796,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":262,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306266},"rom_address":3295824,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":263,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306276},"rom_address":3295852,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":264,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306286},"rom_address":3295880,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":265,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306296},"rom_address":3295908,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":266,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306306},"rom_address":3295936,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":267,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306316},"rom_address":3295964,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":268,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306326},"rom_address":3295992,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":269,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306336},"rom_address":3296020,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":270,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306346},"rom_address":3296048,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":271,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306356},"rom_address":3296076,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":272,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306366},"rom_address":3296104,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":273,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306376},"rom_address":3296132,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":274,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306386},"rom_address":3296160,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":275,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306396},"rom_address":3296188,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":276,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}],"rom_address":3306406},"rom_address":3296216,"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[65,0],"base_stats":[40,45,35,70,65,55],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":16,"species":278}],"friendship":70,"id":277,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":43},{"level":6,"move_id":71},{"level":11,"move_id":98},{"level":16,"move_id":228},{"level":21,"move_id":103},{"level":26,"move_id":72},{"level":31,"move_id":97},{"level":36,"move_id":21},{"level":41,"move_id":197},{"level":46,"move_id":202}],"rom_address":3306416},"rom_address":3296244,"tmhm_learnset":"00E41EC0CC7D0721","types":[12,12]},{"abilities":[65,0],"base_stats":[50,65,45,95,85,65],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":36,"species":279}],"friendship":70,"id":278,"learnset":{"moves":[{"level":1,"move_id":1},{"level":1,"move_id":43},{"level":1,"move_id":71},{"level":1,"move_id":98},{"level":6,"move_id":71},{"level":11,"move_id":98},{"level":16,"move_id":210},{"level":17,"move_id":228},{"level":23,"move_id":103},{"level":29,"move_id":348},{"level":35,"move_id":97},{"level":41,"move_id":21},{"level":47,"move_id":197},{"level":53,"move_id":206}],"rom_address":3306444},"rom_address":3296272,"tmhm_learnset":"00E41EC0CC7D0721","types":[12,12]},{"abilities":[65,0],"base_stats":[70,85,65,120,105,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":279,"learnset":{"moves":[{"level":1,"move_id":1},{"level":1,"move_id":43},{"level":1,"move_id":71},{"level":1,"move_id":98},{"level":6,"move_id":71},{"level":11,"move_id":98},{"level":16,"move_id":210},{"level":17,"move_id":228},{"level":23,"move_id":103},{"level":29,"move_id":348},{"level":35,"move_id":97},{"level":43,"move_id":21},{"level":51,"move_id":197},{"level":59,"move_id":206}],"rom_address":3306474},"rom_address":3296300,"tmhm_learnset":"00E41EC0CE7D4733","types":[12,12]},{"abilities":[66,0],"base_stats":[45,60,40,45,70,50],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":16,"species":281}],"friendship":70,"id":280,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":45},{"level":7,"move_id":116},{"level":10,"move_id":52},{"level":16,"move_id":64},{"level":19,"move_id":28},{"level":25,"move_id":83},{"level":28,"move_id":98},{"level":34,"move_id":163},{"level":37,"move_id":119},{"level":43,"move_id":53}],"rom_address":3306504},"rom_address":3296328,"tmhm_learnset":"00A61EE48C110620","types":[10,10]},{"abilities":[66,0],"base_stats":[60,85,60,55,85,60],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":36,"species":282}],"friendship":70,"id":281,"learnset":{"moves":[{"level":1,"move_id":10},{"level":1,"move_id":45},{"level":1,"move_id":116},{"level":1,"move_id":52},{"level":7,"move_id":116},{"level":13,"move_id":52},{"level":16,"move_id":24},{"level":17,"move_id":64},{"level":21,"move_id":28},{"level":28,"move_id":339},{"level":32,"move_id":98},{"level":39,"move_id":163},{"level":43,"move_id":119},{"level":50,"move_id":327}],"rom_address":3306532},"rom_address":3296356,"tmhm_learnset":"00A61EE4CC1106A1","types":[10,1]},{"abilities":[66,0],"base_stats":[80,120,70,80,110,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":282,"learnset":{"moves":[{"level":1,"move_id":7},{"level":1,"move_id":10},{"level":1,"move_id":45},{"level":1,"move_id":116},{"level":1,"move_id":52},{"level":7,"move_id":116},{"level":13,"move_id":52},{"level":16,"move_id":24},{"level":17,"move_id":64},{"level":21,"move_id":28},{"level":28,"move_id":339},{"level":32,"move_id":98},{"level":36,"move_id":299},{"level":42,"move_id":163},{"level":49,"move_id":119},{"level":59,"move_id":327}],"rom_address":3306562},"rom_address":3296384,"tmhm_learnset":"00A61EE4CE1146B1","types":[10,1]},{"abilities":[67,0],"base_stats":[50,70,50,40,50,50],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":16,"species":284}],"friendship":70,"id":283,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":6,"move_id":189},{"level":10,"move_id":55},{"level":15,"move_id":117},{"level":19,"move_id":193},{"level":24,"move_id":300},{"level":28,"move_id":36},{"level":33,"move_id":250},{"level":37,"move_id":182},{"level":42,"move_id":56},{"level":46,"move_id":283}],"rom_address":3306596},"rom_address":3296412,"tmhm_learnset":"03B01E408C533264","types":[11,11]},{"abilities":[67,0],"base_stats":[70,85,70,50,60,70],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":36,"species":285}],"friendship":70,"id":284,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":189},{"level":1,"move_id":55},{"level":6,"move_id":189},{"level":10,"move_id":55},{"level":15,"move_id":117},{"level":16,"move_id":341},{"level":20,"move_id":193},{"level":25,"move_id":300},{"level":31,"move_id":36},{"level":37,"move_id":330},{"level":42,"move_id":182},{"level":46,"move_id":89},{"level":53,"move_id":283}],"rom_address":3306626},"rom_address":3296440,"tmhm_learnset":"03B01E408E533264","types":[11,4]},{"abilities":[67,0],"base_stats":[100,110,90,60,85,90],"catch_rate":45,"evolutions":[],"friendship":70,"id":285,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":189},{"level":1,"move_id":55},{"level":6,"move_id":189},{"level":10,"move_id":55},{"level":15,"move_id":117},{"level":16,"move_id":341},{"level":20,"move_id":193},{"level":25,"move_id":300},{"level":31,"move_id":36},{"level":39,"move_id":330},{"level":46,"move_id":182},{"level":52,"move_id":89},{"level":61,"move_id":283}],"rom_address":3306658},"rom_address":3296468,"tmhm_learnset":"03B01E40CE537275","types":[11,4]},{"abilities":[50,0],"base_stats":[35,55,35,35,30,30],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":18,"species":287}],"friendship":70,"id":286,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":5,"move_id":336},{"level":9,"move_id":28},{"level":13,"move_id":44},{"level":17,"move_id":316},{"level":21,"move_id":46},{"level":25,"move_id":207},{"level":29,"move_id":184},{"level":33,"move_id":36},{"level":37,"move_id":269},{"level":41,"move_id":242},{"level":45,"move_id":168}],"rom_address":3306690},"rom_address":3296496,"tmhm_learnset":"00813F00AC530E30","types":[17,17]},{"abilities":[22,0],"base_stats":[70,90,70,70,60,60],"catch_rate":127,"evolutions":[],"friendship":70,"id":287,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":336},{"level":1,"move_id":28},{"level":1,"move_id":44},{"level":5,"move_id":336},{"level":9,"move_id":28},{"level":13,"move_id":44},{"level":17,"move_id":316},{"level":22,"move_id":46},{"level":27,"move_id":207},{"level":32,"move_id":184},{"level":37,"move_id":36},{"level":42,"move_id":269},{"level":47,"move_id":242},{"level":52,"move_id":168}],"rom_address":3306722},"rom_address":3296524,"tmhm_learnset":"00A13F00AC534E30","types":[17,17]},{"abilities":[53,0],"base_stats":[38,30,41,60,30,41],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":20,"species":289}],"friendship":70,"id":288,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":5,"move_id":39},{"level":9,"move_id":29},{"level":13,"move_id":28},{"level":17,"move_id":316},{"level":21,"move_id":300},{"level":25,"move_id":42},{"level":29,"move_id":343},{"level":33,"move_id":175},{"level":37,"move_id":156},{"level":41,"move_id":187}],"rom_address":3306754},"rom_address":3296552,"tmhm_learnset":"00943E02ADD33624","types":[0,0]},{"abilities":[53,0],"base_stats":[78,70,61,100,50,61],"catch_rate":90,"evolutions":[],"friendship":70,"id":289,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":39},{"level":1,"move_id":29},{"level":5,"move_id":39},{"level":9,"move_id":29},{"level":13,"move_id":28},{"level":17,"move_id":316},{"level":23,"move_id":300},{"level":29,"move_id":154},{"level":35,"move_id":343},{"level":41,"move_id":163},{"level":47,"move_id":156},{"level":53,"move_id":187}],"rom_address":3306784},"rom_address":3296580,"tmhm_learnset":"00B43E02ADD37634","types":[0,0]},{"abilities":[19,0],"base_stats":[45,45,35,20,20,30],"catch_rate":255,"evolutions":[{"method":"LEVEL_SILCOON","param":7,"species":291},{"method":"LEVEL_CASCOON","param":7,"species":293}],"friendship":70,"id":290,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":81},{"level":5,"move_id":40}],"rom_address":3306814},"rom_address":3296608,"tmhm_learnset":"0000000000000000","types":[6,6]},{"abilities":[61,0],"base_stats":[50,35,55,15,25,25],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":10,"species":292}],"friendship":70,"id":291,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":7,"move_id":106}],"rom_address":3306826},"rom_address":3296636,"tmhm_learnset":"0000000000000000","types":[6,6]},{"abilities":[68,0],"base_stats":[60,70,50,65,90,50],"catch_rate":45,"evolutions":[],"friendship":70,"id":292,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":71},{"level":10,"move_id":71},{"level":13,"move_id":16},{"level":17,"move_id":78},{"level":20,"move_id":234},{"level":24,"move_id":72},{"level":27,"move_id":18},{"level":31,"move_id":213},{"level":34,"move_id":318},{"level":38,"move_id":202}],"rom_address":3306838},"rom_address":3296664,"tmhm_learnset":"00403E80B43D4620","types":[6,2]},{"abilities":[61,0],"base_stats":[50,35,55,15,25,25],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":10,"species":294}],"friendship":70,"id":293,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":7,"move_id":106}],"rom_address":3306866},"rom_address":3296692,"tmhm_learnset":"0000000000000000","types":[6,6]},{"abilities":[19,0],"base_stats":[60,50,70,65,50,90],"catch_rate":45,"evolutions":[],"friendship":70,"id":294,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":93},{"level":10,"move_id":93},{"level":13,"move_id":16},{"level":17,"move_id":182},{"level":20,"move_id":236},{"level":24,"move_id":60},{"level":27,"move_id":18},{"level":31,"move_id":113},{"level":34,"move_id":318},{"level":38,"move_id":92}],"rom_address":3306878},"rom_address":3296720,"tmhm_learnset":"00403E88B435C620","types":[6,3]},{"abilities":[33,44],"base_stats":[40,30,30,30,40,50],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":14,"species":296}],"friendship":70,"id":295,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":310},{"level":3,"move_id":45},{"level":7,"move_id":71},{"level":13,"move_id":267},{"level":21,"move_id":54},{"level":31,"move_id":240},{"level":43,"move_id":72}],"rom_address":3306906},"rom_address":3296748,"tmhm_learnset":"00503E0084373764","types":[11,12]},{"abilities":[33,44],"base_stats":[60,50,50,50,60,70],"catch_rate":120,"evolutions":[{"method":"ITEM","param":97,"species":297}],"friendship":70,"id":296,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":310},{"level":3,"move_id":45},{"level":7,"move_id":71},{"level":13,"move_id":267},{"level":19,"move_id":252},{"level":25,"move_id":154},{"level":31,"move_id":346},{"level":37,"move_id":168},{"level":43,"move_id":253},{"level":49,"move_id":56}],"rom_address":3306928},"rom_address":3296776,"tmhm_learnset":"03F03E00C4373764","types":[11,12]},{"abilities":[33,44],"base_stats":[80,70,70,70,90,100],"catch_rate":45,"evolutions":[],"friendship":70,"id":297,"learnset":{"moves":[{"level":1,"move_id":310},{"level":1,"move_id":45},{"level":1,"move_id":71},{"level":1,"move_id":267}],"rom_address":3306956},"rom_address":3296804,"tmhm_learnset":"03F03E00C4377765","types":[11,12]},{"abilities":[34,48],"base_stats":[40,40,50,30,30,30],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":14,"species":299}],"friendship":70,"id":298,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":117},{"level":3,"move_id":106},{"level":7,"move_id":74},{"level":13,"move_id":267},{"level":21,"move_id":235},{"level":31,"move_id":241},{"level":43,"move_id":153}],"rom_address":3306966},"rom_address":3296832,"tmhm_learnset":"00C01E00AC350720","types":[12,12]},{"abilities":[34,48],"base_stats":[70,70,40,60,60,40],"catch_rate":120,"evolutions":[{"method":"ITEM","param":98,"species":300}],"friendship":70,"id":299,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":3,"move_id":106},{"level":7,"move_id":74},{"level":13,"move_id":267},{"level":19,"move_id":252},{"level":25,"move_id":259},{"level":31,"move_id":185},{"level":37,"move_id":13},{"level":43,"move_id":207},{"level":49,"move_id":326}],"rom_address":3306988},"rom_address":3296860,"tmhm_learnset":"00E43F40EC354720","types":[12,17]},{"abilities":[34,48],"base_stats":[90,100,60,80,90,60],"catch_rate":45,"evolutions":[],"friendship":70,"id":300,"learnset":{"moves":[{"level":1,"move_id":1},{"level":1,"move_id":106},{"level":1,"move_id":74},{"level":1,"move_id":267}],"rom_address":3307016},"rom_address":3296888,"tmhm_learnset":"00E43FC0EC354720","types":[12,17]},{"abilities":[14,0],"base_stats":[31,45,90,40,30,30],"catch_rate":255,"evolutions":[{"method":"LEVEL_NINJASK","param":20,"species":302},{"method":"LEVEL_SHEDINJA","param":20,"species":303}],"friendship":70,"id":301,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":106},{"level":5,"move_id":141},{"level":9,"move_id":28},{"level":14,"move_id":154},{"level":19,"move_id":170},{"level":25,"move_id":206},{"level":31,"move_id":189},{"level":38,"move_id":232},{"level":45,"move_id":91}],"rom_address":3307026},"rom_address":3296916,"tmhm_learnset":"00440E90AC350620","types":[6,4]},{"abilities":[3,0],"base_stats":[61,90,45,160,50,50],"catch_rate":120,"evolutions":[],"friendship":70,"id":302,"learnset":{"moves":[{"level":1,"move_id":10},{"level":1,"move_id":106},{"level":1,"move_id":141},{"level":1,"move_id":28},{"level":5,"move_id":141},{"level":9,"move_id":28},{"level":14,"move_id":154},{"level":19,"move_id":170},{"level":20,"move_id":104},{"level":20,"move_id":210},{"level":20,"move_id":103},{"level":25,"move_id":14},{"level":31,"move_id":163},{"level":38,"move_id":97},{"level":45,"move_id":226}],"rom_address":3307052},"rom_address":3296944,"tmhm_learnset":"00443E90AC354620","types":[6,2]},{"abilities":[25,0],"base_stats":[1,90,45,40,30,30],"catch_rate":45,"evolutions":[],"friendship":70,"id":303,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":106},{"level":5,"move_id":141},{"level":9,"move_id":28},{"level":14,"move_id":154},{"level":19,"move_id":170},{"level":25,"move_id":180},{"level":31,"move_id":109},{"level":38,"move_id":247},{"level":45,"move_id":288}],"rom_address":3307084},"rom_address":3296972,"tmhm_learnset":"00442E90AC354620","types":[6,7]},{"abilities":[62,0],"base_stats":[40,55,30,85,30,30],"catch_rate":200,"evolutions":[{"method":"LEVEL","param":22,"species":305}],"friendship":70,"id":304,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":1,"move_id":45},{"level":4,"move_id":116},{"level":8,"move_id":98},{"level":13,"move_id":17},{"level":19,"move_id":104},{"level":26,"move_id":283},{"level":34,"move_id":332},{"level":43,"move_id":97}],"rom_address":3307110},"rom_address":3297000,"tmhm_learnset":"00087E8084130620","types":[0,2]},{"abilities":[62,0],"base_stats":[60,85,60,125,50,50],"catch_rate":45,"evolutions":[],"friendship":70,"id":305,"learnset":{"moves":[{"level":1,"move_id":64},{"level":1,"move_id":45},{"level":1,"move_id":116},{"level":1,"move_id":98},{"level":4,"move_id":116},{"level":8,"move_id":98},{"level":13,"move_id":17},{"level":19,"move_id":104},{"level":28,"move_id":283},{"level":38,"move_id":332},{"level":49,"move_id":97}],"rom_address":3307134},"rom_address":3297028,"tmhm_learnset":"00087E8084134620","types":[0,2]},{"abilities":[27,0],"base_stats":[60,40,60,35,40,60],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":23,"species":307}],"friendship":70,"id":306,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":71},{"level":4,"move_id":33},{"level":7,"move_id":78},{"level":10,"move_id":73},{"level":16,"move_id":72},{"level":22,"move_id":29},{"level":28,"move_id":77},{"level":36,"move_id":74},{"level":45,"move_id":202},{"level":54,"move_id":147}],"rom_address":3307158},"rom_address":3297056,"tmhm_learnset":"00411E08843D0720","types":[12,12]},{"abilities":[27,0],"base_stats":[60,130,80,70,60,60],"catch_rate":90,"evolutions":[],"friendship":70,"id":307,"learnset":{"moves":[{"level":1,"move_id":71},{"level":1,"move_id":33},{"level":1,"move_id":78},{"level":1,"move_id":73},{"level":4,"move_id":33},{"level":7,"move_id":78},{"level":10,"move_id":73},{"level":16,"move_id":72},{"level":22,"move_id":29},{"level":23,"move_id":183},{"level":28,"move_id":68},{"level":36,"move_id":327},{"level":45,"move_id":170},{"level":54,"move_id":223}],"rom_address":3307186},"rom_address":3297084,"tmhm_learnset":"00E51E08C47D47A1","types":[12,1]},{"abilities":[20,0],"base_stats":[60,60,60,60,60,60],"catch_rate":255,"evolutions":[],"friendship":70,"id":308,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":5,"move_id":253},{"level":12,"move_id":185},{"level":16,"move_id":60},{"level":23,"move_id":95},{"level":27,"move_id":146},{"level":34,"move_id":298},{"level":38,"move_id":244},{"level":45,"move_id":38},{"level":49,"move_id":175},{"level":56,"move_id":37}],"rom_address":3307216},"rom_address":3297112,"tmhm_learnset":"00E1BE42FC1B062D","types":[0,0]},{"abilities":[51,0],"base_stats":[40,30,30,85,55,30],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":25,"species":310}],"friendship":70,"id":309,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":45},{"level":1,"move_id":55},{"level":7,"move_id":48},{"level":13,"move_id":17},{"level":21,"move_id":54},{"level":31,"move_id":98},{"level":43,"move_id":228},{"level":55,"move_id":97}],"rom_address":3307246},"rom_address":3297140,"tmhm_learnset":"00087E8284133264","types":[11,2]},{"abilities":[51,0],"base_stats":[60,50,100,65,85,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":310,"learnset":{"moves":[{"level":1,"move_id":45},{"level":1,"move_id":55},{"level":1,"move_id":346},{"level":1,"move_id":17},{"level":3,"move_id":55},{"level":7,"move_id":48},{"level":13,"move_id":17},{"level":21,"move_id":54},{"level":25,"move_id":182},{"level":33,"move_id":254},{"level":33,"move_id":256},{"level":47,"move_id":255},{"level":61,"move_id":56}],"rom_address":3307268},"rom_address":3297168,"tmhm_learnset":"00187E8284137264","types":[11,2]},{"abilities":[33,0],"base_stats":[40,30,32,65,50,52],"catch_rate":200,"evolutions":[{"method":"LEVEL","param":22,"species":312}],"friendship":70,"id":311,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":7,"move_id":98},{"level":13,"move_id":230},{"level":19,"move_id":346},{"level":25,"move_id":61},{"level":31,"move_id":97},{"level":37,"move_id":54},{"level":37,"move_id":114}],"rom_address":3307296},"rom_address":3297196,"tmhm_learnset":"00403E00A4373624","types":[6,11]},{"abilities":[22,0],"base_stats":[70,60,62,60,80,82],"catch_rate":75,"evolutions":[],"friendship":70,"id":312,"learnset":{"moves":[{"level":1,"move_id":145},{"level":1,"move_id":98},{"level":1,"move_id":230},{"level":1,"move_id":346},{"level":7,"move_id":98},{"level":13,"move_id":230},{"level":19,"move_id":346},{"level":26,"move_id":16},{"level":33,"move_id":184},{"level":40,"move_id":78},{"level":47,"move_id":318},{"level":53,"move_id":18}],"rom_address":3307320},"rom_address":3297224,"tmhm_learnset":"00403E80A4377624","types":[6,2]},{"abilities":[41,12],"base_stats":[130,70,35,60,70,35],"catch_rate":125,"evolutions":[{"method":"LEVEL","param":40,"species":314}],"friendship":70,"id":313,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":55},{"level":1,"move_id":150},{"level":5,"move_id":45},{"level":10,"move_id":55},{"level":14,"move_id":205},{"level":19,"move_id":250},{"level":23,"move_id":310},{"level":28,"move_id":352},{"level":32,"move_id":54},{"level":37,"move_id":156},{"level":41,"move_id":323},{"level":46,"move_id":133},{"level":50,"move_id":56}],"rom_address":3307346},"rom_address":3297252,"tmhm_learnset":"03B01E4086133274","types":[11,11]},{"abilities":[41,12],"base_stats":[170,90,45,60,90,45],"catch_rate":60,"evolutions":[],"friendship":70,"id":314,"learnset":{"moves":[{"level":1,"move_id":150},{"level":1,"move_id":45},{"level":1,"move_id":55},{"level":1,"move_id":205},{"level":5,"move_id":45},{"level":10,"move_id":55},{"level":14,"move_id":205},{"level":19,"move_id":250},{"level":23,"move_id":310},{"level":28,"move_id":352},{"level":32,"move_id":54},{"level":37,"move_id":156},{"level":44,"move_id":323},{"level":52,"move_id":133},{"level":59,"move_id":56}],"rom_address":3307378},"rom_address":3297280,"tmhm_learnset":"03B01E4086137274","types":[11,11]},{"abilities":[56,0],"base_stats":[50,45,45,50,35,35],"catch_rate":255,"evolutions":[{"method":"ITEM","param":94,"species":316}],"friendship":70,"id":315,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":45},{"level":1,"move_id":33},{"level":3,"move_id":39},{"level":7,"move_id":213},{"level":13,"move_id":47},{"level":15,"move_id":3},{"level":19,"move_id":274},{"level":25,"move_id":204},{"level":27,"move_id":185},{"level":31,"move_id":343},{"level":37,"move_id":215},{"level":39,"move_id":38}],"rom_address":3307410},"rom_address":3297308,"tmhm_learnset":"00401E02ADFB362C","types":[0,0]},{"abilities":[56,0],"base_stats":[70,65,65,70,55,55],"catch_rate":60,"evolutions":[],"friendship":70,"id":316,"learnset":{"moves":[{"level":1,"move_id":45},{"level":1,"move_id":213},{"level":1,"move_id":47},{"level":1,"move_id":3}],"rom_address":3307440},"rom_address":3297336,"tmhm_learnset":"00E01E02ADFB762C","types":[0,0]},{"abilities":[16,0],"base_stats":[60,90,70,40,60,120],"catch_rate":200,"evolutions":[],"friendship":70,"id":317,"learnset":{"moves":[{"level":1,"move_id":168},{"level":1,"move_id":39},{"level":1,"move_id":310},{"level":1,"move_id":122},{"level":1,"move_id":10},{"level":4,"move_id":20},{"level":7,"move_id":185},{"level":12,"move_id":154},{"level":17,"move_id":60},{"level":24,"move_id":103},{"level":31,"move_id":163},{"level":40,"move_id":164},{"level":49,"move_id":246}],"rom_address":3307450},"rom_address":3297364,"tmhm_learnset":"00E5BEE6EDF33625","types":[0,0]},{"abilities":[26,0],"base_stats":[40,40,55,55,40,70],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":36,"species":319}],"friendship":70,"id":318,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":93},{"level":3,"move_id":106},{"level":5,"move_id":229},{"level":7,"move_id":189},{"level":11,"move_id":60},{"level":15,"move_id":317},{"level":19,"move_id":120},{"level":25,"move_id":246},{"level":31,"move_id":201},{"level":37,"move_id":322},{"level":45,"move_id":153}],"rom_address":3307478},"rom_address":3297392,"tmhm_learnset":"00408E51BE339620","types":[4,14]},{"abilities":[26,0],"base_stats":[60,70,105,75,70,120],"catch_rate":90,"evolutions":[],"friendship":70,"id":319,"learnset":{"moves":[{"level":1,"move_id":100},{"level":1,"move_id":93},{"level":1,"move_id":106},{"level":1,"move_id":229},{"level":3,"move_id":106},{"level":5,"move_id":229},{"level":7,"move_id":189},{"level":11,"move_id":60},{"level":15,"move_id":317},{"level":19,"move_id":120},{"level":25,"move_id":246},{"level":31,"move_id":201},{"level":36,"move_id":63},{"level":42,"move_id":322},{"level":55,"move_id":153}],"rom_address":3307508},"rom_address":3297420,"tmhm_learnset":"00E08E51BE33D620","types":[4,14]},{"abilities":[5,42],"base_stats":[30,45,135,30,45,90],"catch_rate":255,"evolutions":[],"friendship":70,"id":320,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":7,"move_id":106},{"level":13,"move_id":88},{"level":16,"move_id":335},{"level":22,"move_id":86},{"level":28,"move_id":157},{"level":31,"move_id":201},{"level":37,"move_id":156},{"level":43,"move_id":192},{"level":46,"move_id":199}],"rom_address":3307540},"rom_address":3297448,"tmhm_learnset":"00A01F5287910E20","types":[5,5]},{"abilities":[73,0],"base_stats":[70,85,140,20,85,70],"catch_rate":90,"evolutions":[],"friendship":70,"id":321,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":52},{"level":4,"move_id":123},{"level":7,"move_id":174},{"level":14,"move_id":108},{"level":17,"move_id":83},{"level":20,"move_id":34},{"level":27,"move_id":182},{"level":30,"move_id":53},{"level":33,"move_id":334},{"level":40,"move_id":133},{"level":43,"move_id":175},{"level":46,"move_id":257}],"rom_address":3307568},"rom_address":3297476,"tmhm_learnset":"00A21E2C84510620","types":[10,10]},{"abilities":[51,0],"base_stats":[50,75,75,50,65,65],"catch_rate":45,"evolutions":[],"friendship":35,"id":322,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":10},{"level":5,"move_id":193},{"level":9,"move_id":101},{"level":13,"move_id":310},{"level":17,"move_id":154},{"level":21,"move_id":252},{"level":25,"move_id":197},{"level":29,"move_id":185},{"level":33,"move_id":282},{"level":37,"move_id":109},{"level":41,"move_id":247},{"level":45,"move_id":212}],"rom_address":3307600},"rom_address":3297504,"tmhm_learnset":"00C53FC2FC130E2D","types":[17,7]},{"abilities":[12,0],"base_stats":[50,48,43,60,46,41],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":30,"species":324}],"friendship":70,"id":323,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":189},{"level":6,"move_id":300},{"level":6,"move_id":346},{"level":11,"move_id":55},{"level":16,"move_id":222},{"level":21,"move_id":133},{"level":26,"move_id":156},{"level":26,"move_id":173},{"level":31,"move_id":89},{"level":36,"move_id":248},{"level":41,"move_id":90}],"rom_address":3307632},"rom_address":3297532,"tmhm_learnset":"03101E5086133264","types":[11,4]},{"abilities":[12,0],"base_stats":[110,78,73,60,76,71],"catch_rate":75,"evolutions":[],"friendship":70,"id":324,"learnset":{"moves":[{"level":1,"move_id":321},{"level":1,"move_id":189},{"level":1,"move_id":300},{"level":1,"move_id":346},{"level":6,"move_id":300},{"level":6,"move_id":346},{"level":11,"move_id":55},{"level":16,"move_id":222},{"level":21,"move_id":133},{"level":26,"move_id":156},{"level":26,"move_id":173},{"level":36,"move_id":89},{"level":46,"move_id":248},{"level":56,"move_id":90}],"rom_address":3307662},"rom_address":3297560,"tmhm_learnset":"03B01E5086137264","types":[11,4]},{"abilities":[33,0],"base_stats":[43,30,55,97,40,65],"catch_rate":225,"evolutions":[],"friendship":70,"id":325,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":4,"move_id":204},{"level":12,"move_id":55},{"level":16,"move_id":97},{"level":24,"move_id":36},{"level":28,"move_id":213},{"level":36,"move_id":186},{"level":40,"move_id":175},{"level":48,"move_id":219}],"rom_address":3307692},"rom_address":3297588,"tmhm_learnset":"03101E00841B3264","types":[11,11]},{"abilities":[52,75],"base_stats":[43,80,65,35,50,35],"catch_rate":205,"evolutions":[{"method":"LEVEL","param":30,"species":327}],"friendship":70,"id":326,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":7,"move_id":106},{"level":10,"move_id":11},{"level":13,"move_id":43},{"level":20,"move_id":61},{"level":23,"move_id":182},{"level":26,"move_id":282},{"level":32,"move_id":269},{"level":35,"move_id":152},{"level":38,"move_id":14},{"level":44,"move_id":12}],"rom_address":3307718},"rom_address":3297616,"tmhm_learnset":"01B41EC8CC133A64","types":[11,11]},{"abilities":[52,75],"base_stats":[63,120,85,55,90,55],"catch_rate":155,"evolutions":[],"friendship":70,"id":327,"learnset":{"moves":[{"level":1,"move_id":145},{"level":1,"move_id":106},{"level":1,"move_id":11},{"level":1,"move_id":43},{"level":7,"move_id":106},{"level":10,"move_id":11},{"level":13,"move_id":43},{"level":20,"move_id":61},{"level":23,"move_id":182},{"level":26,"move_id":282},{"level":34,"move_id":269},{"level":39,"move_id":152},{"level":44,"move_id":14},{"level":52,"move_id":12}],"rom_address":3307748},"rom_address":3297644,"tmhm_learnset":"03B41EC8CC137A64","types":[11,17]},{"abilities":[33,0],"base_stats":[20,15,20,80,10,55],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":30,"species":329}],"friendship":70,"id":328,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":150},{"level":15,"move_id":33},{"level":30,"move_id":175}],"rom_address":3307778},"rom_address":3297672,"tmhm_learnset":"03101E0084133264","types":[11,11]},{"abilities":[63,0],"base_stats":[95,60,79,81,100,125],"catch_rate":60,"evolutions":[],"friendship":70,"id":329,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":55},{"level":5,"move_id":35},{"level":10,"move_id":346},{"level":15,"move_id":287},{"level":20,"move_id":352},{"level":25,"move_id":239},{"level":30,"move_id":105},{"level":35,"move_id":240},{"level":40,"move_id":56},{"level":45,"move_id":213},{"level":50,"move_id":219}],"rom_address":3307792},"rom_address":3297700,"tmhm_learnset":"03101E00845B7264","types":[11,11]},{"abilities":[24,0],"base_stats":[45,90,20,65,65,20],"catch_rate":225,"evolutions":[{"method":"LEVEL","param":30,"species":331}],"friendship":35,"id":330,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":44},{"level":7,"move_id":99},{"level":13,"move_id":116},{"level":16,"move_id":184},{"level":22,"move_id":242},{"level":28,"move_id":103},{"level":31,"move_id":36},{"level":37,"move_id":207},{"level":43,"move_id":97}],"rom_address":3307822},"rom_address":3297728,"tmhm_learnset":"03103F0084133A64","types":[11,17]},{"abilities":[24,0],"base_stats":[70,120,40,95,95,40],"catch_rate":60,"evolutions":[],"friendship":35,"id":331,"learnset":{"moves":[{"level":1,"move_id":43},{"level":1,"move_id":44},{"level":1,"move_id":99},{"level":1,"move_id":116},{"level":7,"move_id":99},{"level":13,"move_id":116},{"level":16,"move_id":184},{"level":22,"move_id":242},{"level":28,"move_id":103},{"level":33,"move_id":163},{"level":38,"move_id":269},{"level":43,"move_id":207},{"level":48,"move_id":130},{"level":53,"move_id":97}],"rom_address":3307848},"rom_address":3297756,"tmhm_learnset":"03B03F4086137A74","types":[11,17]},{"abilities":[52,71],"base_stats":[45,100,45,10,45,45],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":35,"species":333}],"friendship":70,"id":332,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":44},{"level":9,"move_id":28},{"level":17,"move_id":185},{"level":25,"move_id":328},{"level":33,"move_id":242},{"level":41,"move_id":91},{"level":49,"move_id":201},{"level":57,"move_id":63}],"rom_address":3307878},"rom_address":3297784,"tmhm_learnset":"00A01E508E354620","types":[4,4]},{"abilities":[26,26],"base_stats":[50,70,50,70,50,50],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":45,"species":334}],"friendship":70,"id":333,"learnset":{"moves":[{"level":1,"move_id":44},{"level":1,"move_id":28},{"level":1,"move_id":185},{"level":1,"move_id":328},{"level":9,"move_id":28},{"level":17,"move_id":185},{"level":25,"move_id":328},{"level":33,"move_id":242},{"level":35,"move_id":225},{"level":41,"move_id":103},{"level":49,"move_id":201},{"level":57,"move_id":63}],"rom_address":3307902},"rom_address":3297812,"tmhm_learnset":"00A85E508E354620","types":[4,16]},{"abilities":[26,26],"base_stats":[80,100,80,100,80,80],"catch_rate":45,"evolutions":[],"friendship":70,"id":334,"learnset":{"moves":[{"level":1,"move_id":44},{"level":1,"move_id":28},{"level":1,"move_id":185},{"level":1,"move_id":328},{"level":9,"move_id":28},{"level":17,"move_id":185},{"level":25,"move_id":328},{"level":33,"move_id":242},{"level":35,"move_id":225},{"level":41,"move_id":103},{"level":53,"move_id":201},{"level":65,"move_id":63}],"rom_address":3307928},"rom_address":3297840,"tmhm_learnset":"00A85E748E754622","types":[4,16]},{"abilities":[47,62],"base_stats":[72,60,30,25,20,30],"catch_rate":180,"evolutions":[{"method":"LEVEL","param":24,"species":336}],"friendship":70,"id":335,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":116},{"level":4,"move_id":28},{"level":10,"move_id":292},{"level":13,"move_id":233},{"level":19,"move_id":252},{"level":22,"move_id":18},{"level":28,"move_id":282},{"level":31,"move_id":265},{"level":37,"move_id":187},{"level":40,"move_id":203},{"level":46,"move_id":69},{"level":49,"move_id":179}],"rom_address":3307954},"rom_address":3297868,"tmhm_learnset":"00B01E40CE1306A1","types":[1,1]},{"abilities":[47,62],"base_stats":[144,120,60,50,40,60],"catch_rate":200,"evolutions":[],"friendship":70,"id":336,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":116},{"level":1,"move_id":28},{"level":1,"move_id":292},{"level":4,"move_id":28},{"level":10,"move_id":292},{"level":13,"move_id":233},{"level":19,"move_id":252},{"level":22,"move_id":18},{"level":29,"move_id":282},{"level":33,"move_id":265},{"level":40,"move_id":187},{"level":44,"move_id":203},{"level":51,"move_id":69},{"level":55,"move_id":179}],"rom_address":3307986},"rom_address":3297896,"tmhm_learnset":"00B01E40CE1346A1","types":[1,1]},{"abilities":[9,31],"base_stats":[40,45,40,65,65,40],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":26,"species":338}],"friendship":70,"id":337,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":4,"move_id":86},{"level":9,"move_id":43},{"level":12,"move_id":336},{"level":17,"move_id":98},{"level":20,"move_id":209},{"level":25,"move_id":316},{"level":28,"move_id":46},{"level":33,"move_id":44},{"level":36,"move_id":87},{"level":41,"move_id":268}],"rom_address":3308018},"rom_address":3297924,"tmhm_learnset":"00603E0285D30230","types":[13,13]},{"abilities":[9,31],"base_stats":[70,75,60,105,105,60],"catch_rate":45,"evolutions":[],"friendship":70,"id":338,"learnset":{"moves":[{"level":1,"move_id":86},{"level":1,"move_id":43},{"level":1,"move_id":336},{"level":1,"move_id":33},{"level":4,"move_id":86},{"level":9,"move_id":43},{"level":12,"move_id":336},{"level":17,"move_id":98},{"level":20,"move_id":209},{"level":25,"move_id":316},{"level":31,"move_id":46},{"level":39,"move_id":44},{"level":45,"move_id":87},{"level":53,"move_id":268}],"rom_address":3308048},"rom_address":3297952,"tmhm_learnset":"00603E0285D34230","types":[13,13]},{"abilities":[12,0],"base_stats":[60,60,40,35,65,45],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":33,"species":340}],"friendship":70,"id":339,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":45},{"level":1,"move_id":33},{"level":11,"move_id":52},{"level":19,"move_id":222},{"level":25,"move_id":116},{"level":29,"move_id":36},{"level":31,"move_id":133},{"level":35,"move_id":89},{"level":41,"move_id":53},{"level":49,"move_id":38}],"rom_address":3308078},"rom_address":3297980,"tmhm_learnset":"00A21E748E110620","types":[10,4]},{"abilities":[40,0],"base_stats":[70,100,70,40,105,75],"catch_rate":150,"evolutions":[],"friendship":70,"id":340,"learnset":{"moves":[{"level":1,"move_id":45},{"level":1,"move_id":33},{"level":1,"move_id":52},{"level":1,"move_id":222},{"level":11,"move_id":52},{"level":19,"move_id":222},{"level":25,"move_id":116},{"level":29,"move_id":36},{"level":31,"move_id":133},{"level":33,"move_id":157},{"level":37,"move_id":89},{"level":45,"move_id":284},{"level":55,"move_id":90}],"rom_address":3308104},"rom_address":3298008,"tmhm_learnset":"00A21E748E114630","types":[10,4]},{"abilities":[47,0],"base_stats":[70,40,50,25,55,50],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":32,"species":342}],"friendship":70,"id":341,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":181},{"level":1,"move_id":45},{"level":1,"move_id":55},{"level":7,"move_id":227},{"level":13,"move_id":301},{"level":19,"move_id":34},{"level":25,"move_id":62},{"level":31,"move_id":258},{"level":37,"move_id":156},{"level":37,"move_id":173},{"level":43,"move_id":59},{"level":49,"move_id":329}],"rom_address":3308132},"rom_address":3298036,"tmhm_learnset":"03B01E4086533264","types":[15,11]},{"abilities":[47,0],"base_stats":[90,60,70,45,75,70],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":44,"species":343}],"friendship":70,"id":342,"learnset":{"moves":[{"level":1,"move_id":181},{"level":1,"move_id":45},{"level":1,"move_id":55},{"level":1,"move_id":227},{"level":7,"move_id":227},{"level":13,"move_id":301},{"level":19,"move_id":34},{"level":25,"move_id":62},{"level":31,"move_id":258},{"level":39,"move_id":156},{"level":39,"move_id":173},{"level":47,"move_id":59},{"level":55,"move_id":329}],"rom_address":3308160},"rom_address":3298064,"tmhm_learnset":"03B01E4086533274","types":[15,11]},{"abilities":[47,0],"base_stats":[110,80,90,65,95,90],"catch_rate":45,"evolutions":[],"friendship":70,"id":343,"learnset":{"moves":[{"level":1,"move_id":181},{"level":1,"move_id":45},{"level":1,"move_id":55},{"level":1,"move_id":227},{"level":7,"move_id":227},{"level":13,"move_id":301},{"level":19,"move_id":34},{"level":25,"move_id":62},{"level":31,"move_id":258},{"level":39,"move_id":156},{"level":39,"move_id":173},{"level":50,"move_id":59},{"level":61,"move_id":329}],"rom_address":3308188},"rom_address":3298092,"tmhm_learnset":"03B01E4086537274","types":[15,11]},{"abilities":[8,0],"base_stats":[50,85,40,35,85,40],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":32,"species":345}],"friendship":35,"id":344,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":40},{"level":1,"move_id":43},{"level":5,"move_id":71},{"level":9,"move_id":74},{"level":13,"move_id":73},{"level":17,"move_id":28},{"level":21,"move_id":42},{"level":25,"move_id":275},{"level":29,"move_id":185},{"level":33,"move_id":191},{"level":37,"move_id":302},{"level":41,"move_id":178},{"level":45,"move_id":201}],"rom_address":3308216},"rom_address":3298120,"tmhm_learnset":"00441E1084350721","types":[12,12]},{"abilities":[8,0],"base_stats":[70,115,60,55,115,60],"catch_rate":60,"evolutions":[],"friendship":35,"id":345,"learnset":{"moves":[{"level":1,"move_id":40},{"level":1,"move_id":43},{"level":1,"move_id":71},{"level":1,"move_id":74},{"level":5,"move_id":71},{"level":9,"move_id":74},{"level":13,"move_id":73},{"level":17,"move_id":28},{"level":21,"move_id":42},{"level":25,"move_id":275},{"level":29,"move_id":185},{"level":35,"move_id":191},{"level":41,"move_id":302},{"level":47,"move_id":178},{"level":53,"move_id":201}],"rom_address":3308248},"rom_address":3298148,"tmhm_learnset":"00641E1084354721","types":[12,17]},{"abilities":[39,0],"base_stats":[50,50,50,50,50,50],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":42,"species":347}],"friendship":70,"id":346,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":181},{"level":1,"move_id":43},{"level":7,"move_id":104},{"level":10,"move_id":44},{"level":16,"move_id":196},{"level":19,"move_id":29},{"level":25,"move_id":182},{"level":28,"move_id":242},{"level":34,"move_id":58},{"level":37,"move_id":258},{"level":43,"move_id":59}],"rom_address":3308280},"rom_address":3298176,"tmhm_learnset":"00401E00A41BB264","types":[15,15]},{"abilities":[39,0],"base_stats":[80,80,80,80,80,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":347,"learnset":{"moves":[{"level":1,"move_id":181},{"level":1,"move_id":43},{"level":1,"move_id":104},{"level":1,"move_id":44},{"level":7,"move_id":104},{"level":10,"move_id":44},{"level":16,"move_id":196},{"level":19,"move_id":29},{"level":25,"move_id":182},{"level":28,"move_id":242},{"level":34,"move_id":58},{"level":42,"move_id":258},{"level":53,"move_id":59},{"level":61,"move_id":329}],"rom_address":3308308},"rom_address":3298204,"tmhm_learnset":"00401F00A61BFA64","types":[15,15]},{"abilities":[26,0],"base_stats":[70,55,65,70,95,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":348,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":7,"move_id":93},{"level":13,"move_id":88},{"level":19,"move_id":95},{"level":25,"move_id":149},{"level":31,"move_id":322},{"level":37,"move_id":94},{"level":43,"move_id":248},{"level":49,"move_id":153}],"rom_address":3308338},"rom_address":3298232,"tmhm_learnset":"00408E51B61BD228","types":[5,14]},{"abilities":[26,0],"base_stats":[70,95,85,70,55,65],"catch_rate":45,"evolutions":[],"friendship":70,"id":349,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":7,"move_id":93},{"level":13,"move_id":88},{"level":19,"move_id":83},{"level":25,"move_id":149},{"level":31,"move_id":322},{"level":37,"move_id":157},{"level":43,"move_id":76},{"level":49,"move_id":153}],"rom_address":3308364},"rom_address":3298260,"tmhm_learnset":"00428E75B639C628","types":[5,14]},{"abilities":[47,37],"base_stats":[50,20,40,20,20,40],"catch_rate":150,"evolutions":[{"method":"FRIENDSHIP","param":0,"species":183}],"friendship":70,"id":350,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":1,"move_id":150},{"level":3,"move_id":204},{"level":6,"move_id":39},{"level":10,"move_id":145},{"level":15,"move_id":21},{"level":21,"move_id":55}],"rom_address":3308390},"rom_address":3298288,"tmhm_learnset":"01101E0084533264","types":[0,0]},{"abilities":[47,20],"base_stats":[60,25,35,60,70,80],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":32,"species":352}],"friendship":70,"id":351,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":149},{"level":1,"move_id":150},{"level":7,"move_id":149},{"level":10,"move_id":316},{"level":16,"move_id":60},{"level":19,"move_id":244},{"level":25,"move_id":109},{"level":28,"move_id":277},{"level":34,"move_id":94},{"level":37,"move_id":156},{"level":37,"move_id":173},{"level":43,"move_id":340}],"rom_address":3308410},"rom_address":3298316,"tmhm_learnset":"0041BF03B4538E28","types":[14,14]},{"abilities":[47,20],"base_stats":[80,45,65,80,90,110],"catch_rate":60,"evolutions":[],"friendship":70,"id":352,"learnset":{"moves":[{"level":1,"move_id":150},{"level":1,"move_id":149},{"level":1,"move_id":316},{"level":1,"move_id":60},{"level":7,"move_id":149},{"level":10,"move_id":316},{"level":16,"move_id":60},{"level":19,"move_id":244},{"level":25,"move_id":109},{"level":28,"move_id":277},{"level":37,"move_id":94},{"level":43,"move_id":156},{"level":43,"move_id":173},{"level":55,"move_id":340}],"rom_address":3308440},"rom_address":3298344,"tmhm_learnset":"0041BF03B453CE29","types":[14,14]},{"abilities":[57,0],"base_stats":[60,50,40,95,85,75],"catch_rate":200,"evolutions":[],"friendship":70,"id":353,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":98},{"level":1,"move_id":45},{"level":4,"move_id":86},{"level":10,"move_id":98},{"level":13,"move_id":270},{"level":19,"move_id":209},{"level":22,"move_id":227},{"level":28,"move_id":313},{"level":31,"move_id":268},{"level":37,"move_id":87},{"level":40,"move_id":226},{"level":47,"move_id":97}],"rom_address":3308470},"rom_address":3298372,"tmhm_learnset":"00401E0285D38220","types":[13,13]},{"abilities":[58,0],"base_stats":[60,40,50,95,75,85],"catch_rate":200,"evolutions":[],"friendship":70,"id":354,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":98},{"level":1,"move_id":45},{"level":4,"move_id":86},{"level":10,"move_id":98},{"level":13,"move_id":270},{"level":19,"move_id":209},{"level":22,"move_id":227},{"level":28,"move_id":204},{"level":31,"move_id":268},{"level":37,"move_id":87},{"level":40,"move_id":226},{"level":47,"move_id":97}],"rom_address":3308500},"rom_address":3298400,"tmhm_learnset":"00401E0285D38220","types":[13,13]},{"abilities":[52,22],"base_stats":[50,85,85,50,55,55],"catch_rate":45,"evolutions":[],"friendship":70,"id":355,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":310},{"level":6,"move_id":313},{"level":11,"move_id":44},{"level":16,"move_id":230},{"level":21,"move_id":11},{"level":26,"move_id":185},{"level":31,"move_id":226},{"level":36,"move_id":242},{"level":41,"move_id":334},{"level":46,"move_id":254},{"level":46,"move_id":256},{"level":46,"move_id":255}],"rom_address":3308530},"rom_address":3298428,"tmhm_learnset":"00A01F7CC4335E21","types":[8,8]},{"abilities":[74,0],"base_stats":[30,40,55,60,40,55],"catch_rate":180,"evolutions":[{"method":"LEVEL","param":37,"species":357}],"friendship":70,"id":356,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":117},{"level":4,"move_id":96},{"level":9,"move_id":93},{"level":12,"move_id":197},{"level":18,"move_id":237},{"level":22,"move_id":170},{"level":28,"move_id":347},{"level":32,"move_id":136},{"level":38,"move_id":244},{"level":42,"move_id":179},{"level":48,"move_id":105}],"rom_address":3308562},"rom_address":3298456,"tmhm_learnset":"00E01E41F41386A9","types":[1,14]},{"abilities":[74,0],"base_stats":[60,60,75,80,60,75],"catch_rate":90,"evolutions":[],"friendship":70,"id":357,"learnset":{"moves":[{"level":1,"move_id":7},{"level":1,"move_id":9},{"level":1,"move_id":8},{"level":1,"move_id":117},{"level":1,"move_id":96},{"level":1,"move_id":93},{"level":1,"move_id":197},{"level":4,"move_id":96},{"level":9,"move_id":93},{"level":12,"move_id":197},{"level":18,"move_id":237},{"level":22,"move_id":170},{"level":28,"move_id":347},{"level":32,"move_id":136},{"level":40,"move_id":244},{"level":46,"move_id":179},{"level":54,"move_id":105}],"rom_address":3308592},"rom_address":3298484,"tmhm_learnset":"00E01E41F413C6A9","types":[1,14]},{"abilities":[30,0],"base_stats":[45,40,60,50,40,75],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":35,"species":359}],"friendship":70,"id":358,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":1,"move_id":45},{"level":8,"move_id":310},{"level":11,"move_id":47},{"level":18,"move_id":31},{"level":21,"move_id":219},{"level":28,"move_id":54},{"level":31,"move_id":36},{"level":38,"move_id":119},{"level":41,"move_id":287},{"level":48,"move_id":195}],"rom_address":3308628},"rom_address":3298512,"tmhm_learnset":"00087E80843B1620","types":[0,2]},{"abilities":[30,0],"base_stats":[75,70,90,80,70,105],"catch_rate":45,"evolutions":[],"friendship":70,"id":359,"learnset":{"moves":[{"level":1,"move_id":64},{"level":1,"move_id":45},{"level":1,"move_id":310},{"level":1,"move_id":47},{"level":8,"move_id":310},{"level":11,"move_id":47},{"level":18,"move_id":31},{"level":21,"move_id":219},{"level":28,"move_id":54},{"level":31,"move_id":36},{"level":35,"move_id":225},{"level":40,"move_id":349},{"level":45,"move_id":287},{"level":54,"move_id":195},{"level":59,"move_id":143}],"rom_address":3308656},"rom_address":3298540,"tmhm_learnset":"00887EA4867B5632","types":[16,2]},{"abilities":[23,0],"base_stats":[95,23,48,23,23,48],"catch_rate":125,"evolutions":[{"method":"LEVEL","param":15,"species":202}],"friendship":70,"id":360,"learnset":{"moves":[{"level":1,"move_id":68},{"level":1,"move_id":150},{"level":1,"move_id":204},{"level":1,"move_id":227},{"level":15,"move_id":68},{"level":15,"move_id":243},{"level":15,"move_id":219},{"level":15,"move_id":194}],"rom_address":3308688},"rom_address":3298568,"tmhm_learnset":"0000000000000000","types":[14,14]},{"abilities":[26,0],"base_stats":[20,40,90,25,30,90],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":37,"species":362}],"friendship":35,"id":361,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":101},{"level":5,"move_id":50},{"level":12,"move_id":193},{"level":16,"move_id":310},{"level":23,"move_id":109},{"level":27,"move_id":228},{"level":34,"move_id":174},{"level":38,"move_id":261},{"level":45,"move_id":212},{"level":49,"move_id":248}],"rom_address":3308706},"rom_address":3298596,"tmhm_learnset":"0041BF00B4133E28","types":[7,7]},{"abilities":[46,0],"base_stats":[40,70,130,25,60,130],"catch_rate":90,"evolutions":[],"friendship":35,"id":362,"learnset":{"moves":[{"level":1,"move_id":20},{"level":1,"move_id":43},{"level":1,"move_id":101},{"level":1,"move_id":50},{"level":5,"move_id":50},{"level":12,"move_id":193},{"level":16,"move_id":310},{"level":23,"move_id":109},{"level":27,"move_id":228},{"level":34,"move_id":174},{"level":37,"move_id":325},{"level":41,"move_id":261},{"level":51,"move_id":212},{"level":58,"move_id":248}],"rom_address":3308734},"rom_address":3298624,"tmhm_learnset":"00E1BF40B6137E29","types":[7,7]},{"abilities":[30,38],"base_stats":[50,60,45,65,100,80],"catch_rate":150,"evolutions":[],"friendship":70,"id":363,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":71},{"level":5,"move_id":74},{"level":9,"move_id":40},{"level":13,"move_id":78},{"level":17,"move_id":72},{"level":21,"move_id":73},{"level":25,"move_id":345},{"level":29,"move_id":320},{"level":33,"move_id":202},{"level":37,"move_id":230},{"level":41,"move_id":275},{"level":45,"move_id":92},{"level":49,"move_id":80},{"level":53,"move_id":312},{"level":57,"move_id":235}],"rom_address":3308764},"rom_address":3298652,"tmhm_learnset":"00441E08A4350720","types":[12,3]},{"abilities":[54,0],"base_stats":[60,60,60,30,35,35],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":18,"species":365}],"friendship":70,"id":364,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":281},{"level":7,"move_id":227},{"level":13,"move_id":303},{"level":19,"move_id":185},{"level":25,"move_id":133},{"level":31,"move_id":343},{"level":37,"move_id":68},{"level":43,"move_id":175}],"rom_address":3308802},"rom_address":3298680,"tmhm_learnset":"00A41EA6E5B336A5","types":[0,0]},{"abilities":[72,0],"base_stats":[80,80,80,90,55,55],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":36,"species":366}],"friendship":70,"id":365,"learnset":{"moves":[{"level":1,"move_id":10},{"level":1,"move_id":116},{"level":1,"move_id":227},{"level":1,"move_id":253},{"level":7,"move_id":227},{"level":13,"move_id":253},{"level":19,"move_id":154},{"level":25,"move_id":203},{"level":31,"move_id":163},{"level":37,"move_id":68},{"level":43,"move_id":264},{"level":49,"move_id":179}],"rom_address":3308826},"rom_address":3298708,"tmhm_learnset":"00A41EA6E7B33EB5","types":[0,0]},{"abilities":[54,0],"base_stats":[150,160,100,100,95,65],"catch_rate":45,"evolutions":[],"friendship":70,"id":366,"learnset":{"moves":[{"level":1,"move_id":10},{"level":1,"move_id":281},{"level":1,"move_id":227},{"level":1,"move_id":303},{"level":7,"move_id":227},{"level":13,"move_id":303},{"level":19,"move_id":185},{"level":25,"move_id":133},{"level":31,"move_id":343},{"level":36,"move_id":207},{"level":37,"move_id":68},{"level":43,"move_id":175}],"rom_address":3308852},"rom_address":3298736,"tmhm_learnset":"00A41EA6E7B37EB5","types":[0,0]},{"abilities":[64,60],"base_stats":[70,43,53,40,43,53],"catch_rate":225,"evolutions":[{"method":"LEVEL","param":26,"species":368}],"friendship":70,"id":367,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":6,"move_id":281},{"level":9,"move_id":139},{"level":14,"move_id":124},{"level":17,"move_id":133},{"level":23,"move_id":227},{"level":28,"move_id":92},{"level":34,"move_id":254},{"level":34,"move_id":255},{"level":34,"move_id":256},{"level":39,"move_id":188}],"rom_address":3308878},"rom_address":3298764,"tmhm_learnset":"00A11E0AA4371724","types":[3,3]},{"abilities":[64,60],"base_stats":[100,73,83,55,73,83],"catch_rate":75,"evolutions":[],"friendship":70,"id":368,"learnset":{"moves":[{"level":1,"move_id":1},{"level":1,"move_id":281},{"level":1,"move_id":139},{"level":1,"move_id":124},{"level":6,"move_id":281},{"level":9,"move_id":139},{"level":14,"move_id":124},{"level":17,"move_id":133},{"level":23,"move_id":227},{"level":26,"move_id":34},{"level":31,"move_id":92},{"level":40,"move_id":254},{"level":40,"move_id":255},{"level":40,"move_id":256},{"level":48,"move_id":188}],"rom_address":3308908},"rom_address":3298792,"tmhm_learnset":"00A11E0AA4375724","types":[3,3]},{"abilities":[34,0],"base_stats":[99,68,83,51,72,87],"catch_rate":200,"evolutions":[],"friendship":70,"id":369,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":16},{"level":7,"move_id":74},{"level":11,"move_id":75},{"level":17,"move_id":23},{"level":21,"move_id":230},{"level":27,"move_id":18},{"level":31,"move_id":345},{"level":37,"move_id":34},{"level":41,"move_id":76},{"level":47,"move_id":235}],"rom_address":3308940},"rom_address":3298820,"tmhm_learnset":"00EC5E80863D4730","types":[12,2]},{"abilities":[43,0],"base_stats":[64,51,23,28,51,23],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":20,"species":371}],"friendship":70,"id":370,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":5,"move_id":253},{"level":11,"move_id":310},{"level":15,"move_id":336},{"level":21,"move_id":48},{"level":25,"move_id":23},{"level":31,"move_id":103},{"level":35,"move_id":46},{"level":41,"move_id":156},{"level":41,"move_id":214},{"level":45,"move_id":304}],"rom_address":3308968},"rom_address":3298848,"tmhm_learnset":"00001E26A4333634","types":[0,0]},{"abilities":[43,0],"base_stats":[84,71,43,48,71,43],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":40,"species":372}],"friendship":70,"id":371,"learnset":{"moves":[{"level":1,"move_id":1},{"level":1,"move_id":253},{"level":1,"move_id":310},{"level":1,"move_id":336},{"level":5,"move_id":253},{"level":11,"move_id":310},{"level":15,"move_id":336},{"level":23,"move_id":48},{"level":29,"move_id":23},{"level":37,"move_id":103},{"level":43,"move_id":46},{"level":51,"move_id":156},{"level":51,"move_id":214},{"level":57,"move_id":304}],"rom_address":3308998},"rom_address":3298876,"tmhm_learnset":"00A21F26E6333E34","types":[0,0]},{"abilities":[43,0],"base_stats":[104,91,63,68,91,63],"catch_rate":45,"evolutions":[],"friendship":70,"id":372,"learnset":{"moves":[{"level":1,"move_id":1},{"level":1,"move_id":253},{"level":1,"move_id":310},{"level":1,"move_id":336},{"level":5,"move_id":253},{"level":11,"move_id":310},{"level":15,"move_id":336},{"level":23,"move_id":48},{"level":29,"move_id":23},{"level":37,"move_id":103},{"level":40,"move_id":63},{"level":45,"move_id":46},{"level":55,"move_id":156},{"level":55,"move_id":214},{"level":63,"move_id":304}],"rom_address":3309028},"rom_address":3298904,"tmhm_learnset":"00A21F26E6337E34","types":[0,0]},{"abilities":[75,0],"base_stats":[35,64,85,32,74,55],"catch_rate":255,"evolutions":[{"method":"ITEM","param":192,"species":374},{"method":"ITEM","param":193,"species":375}],"friendship":70,"id":373,"learnset":{"moves":[{"level":1,"move_id":128},{"level":1,"move_id":55},{"level":1,"move_id":250},{"level":1,"move_id":334}],"rom_address":3309060},"rom_address":3298932,"tmhm_learnset":"03101E0084133264","types":[11,11]},{"abilities":[33,0],"base_stats":[55,104,105,52,94,75],"catch_rate":60,"evolutions":[],"friendship":70,"id":374,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":250},{"level":8,"move_id":44},{"level":15,"move_id":103},{"level":22,"move_id":352},{"level":29,"move_id":184},{"level":36,"move_id":242},{"level":43,"move_id":226},{"level":50,"move_id":56}],"rom_address":3309070},"rom_address":3298960,"tmhm_learnset":"03111E4084137264","types":[11,11]},{"abilities":[33,0],"base_stats":[55,84,105,52,114,75],"catch_rate":60,"evolutions":[],"friendship":70,"id":375,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":250},{"level":8,"move_id":93},{"level":15,"move_id":97},{"level":22,"move_id":352},{"level":29,"move_id":133},{"level":36,"move_id":94},{"level":43,"move_id":226},{"level":50,"move_id":56}],"rom_address":3309094},"rom_address":3298988,"tmhm_learnset":"03101E00B41B7264","types":[11,11]},{"abilities":[46,0],"base_stats":[65,130,60,75,75,60],"catch_rate":30,"evolutions":[],"friendship":35,"id":376,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":5,"move_id":43},{"level":9,"move_id":269},{"level":13,"move_id":98},{"level":17,"move_id":13},{"level":21,"move_id":44},{"level":26,"move_id":14},{"level":31,"move_id":104},{"level":36,"move_id":163},{"level":41,"move_id":248},{"level":46,"move_id":195}],"rom_address":3309118},"rom_address":3299016,"tmhm_learnset":"00E53FB6A5D37E6C","types":[17,17]},{"abilities":[15,0],"base_stats":[44,75,35,45,63,33],"catch_rate":225,"evolutions":[{"method":"LEVEL","param":37,"species":378}],"friendship":35,"id":377,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":282},{"level":8,"move_id":103},{"level":13,"move_id":101},{"level":20,"move_id":174},{"level":25,"move_id":180},{"level":32,"move_id":261},{"level":37,"move_id":185},{"level":44,"move_id":247},{"level":49,"move_id":289},{"level":56,"move_id":288}],"rom_address":3309148},"rom_address":3299044,"tmhm_learnset":"0041BF02B5930E28","types":[7,7]},{"abilities":[15,0],"base_stats":[64,115,65,65,83,63],"catch_rate":45,"evolutions":[],"friendship":35,"id":378,"learnset":{"moves":[{"level":1,"move_id":282},{"level":1,"move_id":103},{"level":1,"move_id":101},{"level":1,"move_id":174},{"level":8,"move_id":103},{"level":13,"move_id":101},{"level":20,"move_id":174},{"level":25,"move_id":180},{"level":32,"move_id":261},{"level":39,"move_id":185},{"level":48,"move_id":247},{"level":55,"move_id":289},{"level":64,"move_id":288}],"rom_address":3309176},"rom_address":3299072,"tmhm_learnset":"0041BF02B5934E28","types":[7,7]},{"abilities":[61,0],"base_stats":[73,100,60,65,100,60],"catch_rate":90,"evolutions":[],"friendship":70,"id":379,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":35},{"level":7,"move_id":122},{"level":10,"move_id":44},{"level":16,"move_id":342},{"level":19,"move_id":103},{"level":25,"move_id":137},{"level":28,"move_id":242},{"level":34,"move_id":305},{"level":37,"move_id":207},{"level":43,"move_id":114}],"rom_address":3309204},"rom_address":3299100,"tmhm_learnset":"00A13E0C8E570E20","types":[3,3]},{"abilities":[17,0],"base_stats":[73,115,60,90,60,60],"catch_rate":90,"evolutions":[],"friendship":70,"id":380,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":4,"move_id":43},{"level":7,"move_id":98},{"level":10,"move_id":14},{"level":13,"move_id":210},{"level":19,"move_id":163},{"level":25,"move_id":228},{"level":31,"move_id":306},{"level":37,"move_id":269},{"level":46,"move_id":197},{"level":55,"move_id":206}],"rom_address":3309232},"rom_address":3299128,"tmhm_learnset":"00A03EA6EDF73E35","types":[0,0]},{"abilities":[33,69],"base_stats":[100,90,130,55,45,65],"catch_rate":25,"evolutions":[],"friendship":70,"id":381,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":8,"move_id":55},{"level":15,"move_id":317},{"level":22,"move_id":281},{"level":29,"move_id":36},{"level":36,"move_id":300},{"level":43,"move_id":246},{"level":50,"move_id":156},{"level":57,"move_id":38},{"level":64,"move_id":56}],"rom_address":3309262},"rom_address":3299156,"tmhm_learnset":"03901E50861B726C","types":[11,5]},{"abilities":[5,69],"base_stats":[50,70,100,30,40,40],"catch_rate":180,"evolutions":[{"method":"LEVEL","param":32,"species":383}],"friendship":35,"id":382,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":4,"move_id":106},{"level":7,"move_id":189},{"level":10,"move_id":29},{"level":13,"move_id":232},{"level":17,"move_id":334},{"level":21,"move_id":46},{"level":25,"move_id":36},{"level":29,"move_id":231},{"level":34,"move_id":182},{"level":39,"move_id":319},{"level":44,"move_id":38}],"rom_address":3309290},"rom_address":3299184,"tmhm_learnset":"00A41ED28E530634","types":[8,5]},{"abilities":[5,69],"base_stats":[60,90,140,40,50,50],"catch_rate":90,"evolutions":[{"method":"LEVEL","param":42,"species":384}],"friendship":35,"id":383,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":1,"move_id":189},{"level":1,"move_id":29},{"level":4,"move_id":106},{"level":7,"move_id":189},{"level":10,"move_id":29},{"level":13,"move_id":232},{"level":17,"move_id":334},{"level":21,"move_id":46},{"level":25,"move_id":36},{"level":29,"move_id":231},{"level":37,"move_id":182},{"level":45,"move_id":319},{"level":53,"move_id":38}],"rom_address":3309322},"rom_address":3299212,"tmhm_learnset":"00A41ED28E530634","types":[8,5]},{"abilities":[5,69],"base_stats":[70,110,180,50,60,60],"catch_rate":45,"evolutions":[],"friendship":35,"id":384,"learnset":{"moves":[{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":1,"move_id":189},{"level":1,"move_id":29},{"level":4,"move_id":106},{"level":7,"move_id":189},{"level":10,"move_id":29},{"level":13,"move_id":232},{"level":17,"move_id":334},{"level":21,"move_id":46},{"level":25,"move_id":36},{"level":29,"move_id":231},{"level":37,"move_id":182},{"level":50,"move_id":319},{"level":63,"move_id":38}],"rom_address":3309354},"rom_address":3299240,"tmhm_learnset":"00B41EF6CFF37E37","types":[8,5]},{"abilities":[59,0],"base_stats":[70,70,70,70,70,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":385,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":10,"move_id":55},{"level":10,"move_id":52},{"level":10,"move_id":181},{"level":20,"move_id":240},{"level":20,"move_id":241},{"level":20,"move_id":258},{"level":30,"move_id":311}],"rom_address":3309386},"rom_address":3299268,"tmhm_learnset":"00403E36A5B33664","types":[0,0]},{"abilities":[35,68],"base_stats":[65,73,55,85,47,75],"catch_rate":150,"evolutions":[],"friendship":70,"id":386,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":5,"move_id":109},{"level":9,"move_id":104},{"level":13,"move_id":236},{"level":17,"move_id":98},{"level":21,"move_id":294},{"level":25,"move_id":324},{"level":29,"move_id":182},{"level":33,"move_id":270},{"level":37,"move_id":38}],"rom_address":3309410},"rom_address":3299296,"tmhm_learnset":"00403E82E5B78625","types":[6,6]},{"abilities":[12,0],"base_stats":[65,47,55,85,73,75],"catch_rate":150,"evolutions":[],"friendship":70,"id":387,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":5,"move_id":230},{"level":9,"move_id":204},{"level":13,"move_id":236},{"level":17,"move_id":98},{"level":21,"move_id":273},{"level":25,"move_id":227},{"level":29,"move_id":260},{"level":33,"move_id":270},{"level":37,"move_id":343}],"rom_address":3309438},"rom_address":3299324,"tmhm_learnset":"00403E82E5B78625","types":[6,6]},{"abilities":[21,0],"base_stats":[66,41,77,23,61,87],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":40,"species":389}],"friendship":70,"id":388,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":310},{"level":8,"move_id":132},{"level":15,"move_id":51},{"level":22,"move_id":275},{"level":29,"move_id":109},{"level":36,"move_id":133},{"level":43,"move_id":246},{"level":50,"move_id":254},{"level":50,"move_id":255},{"level":50,"move_id":256}],"rom_address":3309466},"rom_address":3299352,"tmhm_learnset":"00001E1884350720","types":[5,12]},{"abilities":[21,0],"base_stats":[86,81,97,43,81,107],"catch_rate":45,"evolutions":[],"friendship":70,"id":389,"learnset":{"moves":[{"level":1,"move_id":310},{"level":1,"move_id":132},{"level":1,"move_id":51},{"level":1,"move_id":275},{"level":8,"move_id":132},{"level":15,"move_id":51},{"level":22,"move_id":275},{"level":29,"move_id":109},{"level":36,"move_id":133},{"level":48,"move_id":246},{"level":60,"move_id":254},{"level":60,"move_id":255},{"level":60,"move_id":256}],"rom_address":3309494},"rom_address":3299380,"tmhm_learnset":"00A01E5886354720","types":[5,12]},{"abilities":[4,0],"base_stats":[45,95,50,75,40,50],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":40,"species":391}],"friendship":70,"id":390,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":7,"move_id":106},{"level":13,"move_id":300},{"level":19,"move_id":55},{"level":25,"move_id":232},{"level":31,"move_id":182},{"level":37,"move_id":246},{"level":43,"move_id":210},{"level":49,"move_id":163},{"level":55,"move_id":350}],"rom_address":3309522},"rom_address":3299408,"tmhm_learnset":"00841ED0CC110624","types":[5,6]},{"abilities":[4,0],"base_stats":[75,125,100,45,70,80],"catch_rate":45,"evolutions":[],"friendship":70,"id":391,"learnset":{"moves":[{"level":1,"move_id":10},{"level":1,"move_id":106},{"level":1,"move_id":300},{"level":1,"move_id":55},{"level":7,"move_id":106},{"level":13,"move_id":300},{"level":19,"move_id":55},{"level":25,"move_id":232},{"level":31,"move_id":182},{"level":37,"move_id":246},{"level":46,"move_id":210},{"level":55,"move_id":163},{"level":64,"move_id":350}],"rom_address":3309550},"rom_address":3299436,"tmhm_learnset":"00A41ED0CE514624","types":[5,6]},{"abilities":[28,36],"base_stats":[28,25,25,40,45,35],"catch_rate":235,"evolutions":[{"method":"LEVEL","param":20,"species":393}],"friendship":35,"id":392,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":93},{"level":1,"move_id":45},{"level":6,"move_id":93},{"level":11,"move_id":104},{"level":16,"move_id":100},{"level":21,"move_id":347},{"level":26,"move_id":94},{"level":31,"move_id":286},{"level":36,"move_id":248},{"level":41,"move_id":95},{"level":46,"move_id":138}],"rom_address":3309578},"rom_address":3299464,"tmhm_learnset":"0041BF03B49B8E28","types":[14,14]},{"abilities":[28,36],"base_stats":[38,35,35,50,65,55],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":30,"species":394}],"friendship":35,"id":393,"learnset":{"moves":[{"level":1,"move_id":45},{"level":1,"move_id":93},{"level":1,"move_id":104},{"level":1,"move_id":100},{"level":6,"move_id":93},{"level":11,"move_id":104},{"level":16,"move_id":100},{"level":21,"move_id":347},{"level":26,"move_id":94},{"level":33,"move_id":286},{"level":40,"move_id":248},{"level":47,"move_id":95},{"level":54,"move_id":138}],"rom_address":3309606},"rom_address":3299492,"tmhm_learnset":"0041BF03B49B8E28","types":[14,14]},{"abilities":[28,36],"base_stats":[68,65,65,80,125,115],"catch_rate":45,"evolutions":[],"friendship":35,"id":394,"learnset":{"moves":[{"level":1,"move_id":45},{"level":1,"move_id":93},{"level":1,"move_id":104},{"level":1,"move_id":100},{"level":6,"move_id":93},{"level":11,"move_id":104},{"level":16,"move_id":100},{"level":21,"move_id":347},{"level":26,"move_id":94},{"level":33,"move_id":286},{"level":42,"move_id":248},{"level":51,"move_id":95},{"level":60,"move_id":138}],"rom_address":3309634},"rom_address":3299520,"tmhm_learnset":"0041BF03B49BCE28","types":[14,14]},{"abilities":[69,0],"base_stats":[45,75,60,50,40,30],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":30,"species":396}],"friendship":35,"id":395,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":99},{"level":5,"move_id":44},{"level":9,"move_id":43},{"level":17,"move_id":29},{"level":21,"move_id":116},{"level":25,"move_id":52},{"level":33,"move_id":225},{"level":37,"move_id":184},{"level":41,"move_id":242},{"level":49,"move_id":337},{"level":53,"move_id":38}],"rom_address":3309662},"rom_address":3299548,"tmhm_learnset":"00A41EE4C4130632","types":[16,16]},{"abilities":[69,0],"base_stats":[65,95,100,50,60,50],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":50,"species":397}],"friendship":35,"id":396,"learnset":{"moves":[{"level":1,"move_id":99},{"level":1,"move_id":44},{"level":1,"move_id":43},{"level":1,"move_id":29},{"level":5,"move_id":44},{"level":9,"move_id":43},{"level":17,"move_id":29},{"level":21,"move_id":116},{"level":25,"move_id":52},{"level":30,"move_id":182},{"level":38,"move_id":225},{"level":47,"move_id":184},{"level":56,"move_id":242},{"level":69,"move_id":337},{"level":78,"move_id":38}],"rom_address":3309692},"rom_address":3299576,"tmhm_learnset":"00A41EE4C4130632","types":[16,16]},{"abilities":[22,0],"base_stats":[95,135,80,100,110,80],"catch_rate":45,"evolutions":[],"friendship":35,"id":397,"learnset":{"moves":[{"level":1,"move_id":99},{"level":1,"move_id":44},{"level":1,"move_id":43},{"level":1,"move_id":29},{"level":5,"move_id":44},{"level":9,"move_id":43},{"level":17,"move_id":29},{"level":21,"move_id":116},{"level":25,"move_id":52},{"level":30,"move_id":182},{"level":38,"move_id":225},{"level":47,"move_id":184},{"level":50,"move_id":19},{"level":61,"move_id":242},{"level":79,"move_id":337},{"level":93,"move_id":38}],"rom_address":3309724},"rom_address":3299604,"tmhm_learnset":"00AC5EE4C6534632","types":[16,2]},{"abilities":[29,0],"base_stats":[40,55,80,30,35,60],"catch_rate":3,"evolutions":[{"method":"LEVEL","param":20,"species":399}],"friendship":35,"id":398,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":36}],"rom_address":3309758},"rom_address":3299632,"tmhm_learnset":"0000000000000000","types":[8,14]},{"abilities":[29,0],"base_stats":[60,75,100,50,55,80],"catch_rate":3,"evolutions":[{"method":"LEVEL","param":45,"species":400}],"friendship":35,"id":399,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":36},{"level":20,"move_id":93},{"level":20,"move_id":232},{"level":26,"move_id":184},{"level":32,"move_id":228},{"level":38,"move_id":94},{"level":44,"move_id":334},{"level":50,"move_id":309},{"level":56,"move_id":97},{"level":62,"move_id":63}],"rom_address":3309768},"rom_address":3299660,"tmhm_learnset":"00E40ED9F613C620","types":[8,14]},{"abilities":[29,0],"base_stats":[80,135,130,70,95,90],"catch_rate":3,"evolutions":[],"friendship":35,"id":400,"learnset":{"moves":[{"level":1,"move_id":36},{"level":1,"move_id":93},{"level":1,"move_id":232},{"level":1,"move_id":184},{"level":20,"move_id":93},{"level":20,"move_id":232},{"level":26,"move_id":184},{"level":32,"move_id":228},{"level":38,"move_id":94},{"level":44,"move_id":334},{"level":55,"move_id":309},{"level":66,"move_id":97},{"level":77,"move_id":63}],"rom_address":3309796},"rom_address":3299688,"tmhm_learnset":"00E40ED9F613C620","types":[8,14]},{"abilities":[29,0],"base_stats":[80,100,200,50,50,100],"catch_rate":3,"evolutions":[],"friendship":35,"id":401,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":88},{"level":1,"move_id":153},{"level":9,"move_id":88},{"level":17,"move_id":174},{"level":25,"move_id":276},{"level":33,"move_id":246},{"level":41,"move_id":334},{"level":49,"move_id":192},{"level":57,"move_id":199},{"level":65,"move_id":63}],"rom_address":3309824},"rom_address":3299716,"tmhm_learnset":"00A00E52CF994621","types":[5,5]},{"abilities":[29,0],"base_stats":[80,50,100,50,100,200],"catch_rate":3,"evolutions":[],"friendship":35,"id":402,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":196},{"level":1,"move_id":153},{"level":9,"move_id":196},{"level":17,"move_id":174},{"level":25,"move_id":276},{"level":33,"move_id":246},{"level":41,"move_id":133},{"level":49,"move_id":192},{"level":57,"move_id":199},{"level":65,"move_id":63}],"rom_address":3309850},"rom_address":3299744,"tmhm_learnset":"00A00E02C79B7261","types":[15,15]},{"abilities":[29,0],"base_stats":[80,75,150,50,75,150],"catch_rate":3,"evolutions":[],"friendship":35,"id":403,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":232},{"level":1,"move_id":153},{"level":9,"move_id":232},{"level":17,"move_id":174},{"level":25,"move_id":276},{"level":33,"move_id":246},{"level":41,"move_id":334},{"level":41,"move_id":133},{"level":49,"move_id":192},{"level":57,"move_id":199},{"level":65,"move_id":63}],"rom_address":3309876},"rom_address":3299772,"tmhm_learnset":"00A00ED2C79B4621","types":[8,8]},{"abilities":[2,0],"base_stats":[100,100,90,90,150,140],"catch_rate":5,"evolutions":[],"friendship":0,"id":404,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":352},{"level":5,"move_id":184},{"level":15,"move_id":246},{"level":20,"move_id":34},{"level":30,"move_id":347},{"level":35,"move_id":58},{"level":45,"move_id":56},{"level":50,"move_id":156},{"level":60,"move_id":329},{"level":65,"move_id":38},{"level":75,"move_id":323}],"rom_address":3309904},"rom_address":3299800,"tmhm_learnset":"03B00E42C79B727C","types":[11,11]},{"abilities":[70,0],"base_stats":[100,150,140,90,100,90],"catch_rate":5,"evolutions":[],"friendship":0,"id":405,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":341},{"level":5,"move_id":184},{"level":15,"move_id":246},{"level":20,"move_id":163},{"level":30,"move_id":339},{"level":35,"move_id":89},{"level":45,"move_id":126},{"level":50,"move_id":156},{"level":60,"move_id":90},{"level":65,"move_id":76},{"level":75,"move_id":284}],"rom_address":3309934},"rom_address":3299828,"tmhm_learnset":"00A60EF6CFF946B2","types":[4,4]},{"abilities":[77,0],"base_stats":[105,150,90,95,150,90],"catch_rate":3,"evolutions":[],"friendship":0,"id":406,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":239},{"level":5,"move_id":184},{"level":15,"move_id":246},{"level":20,"move_id":337},{"level":30,"move_id":349},{"level":35,"move_id":242},{"level":45,"move_id":19},{"level":50,"move_id":156},{"level":60,"move_id":245},{"level":65,"move_id":200},{"level":75,"move_id":63}],"rom_address":3309964},"rom_address":3299856,"tmhm_learnset":"03BA0EB6C7F376B6","types":[16,2]},{"abilities":[26,0],"base_stats":[80,80,90,110,110,130],"catch_rate":3,"evolutions":[],"friendship":90,"id":407,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":149},{"level":5,"move_id":273},{"level":10,"move_id":270},{"level":15,"move_id":219},{"level":20,"move_id":225},{"level":25,"move_id":346},{"level":30,"move_id":287},{"level":35,"move_id":296},{"level":40,"move_id":94},{"level":45,"move_id":105},{"level":50,"move_id":204}],"rom_address":3309994},"rom_address":3299884,"tmhm_learnset":"035C5E93B7BBD63E","types":[16,14]},{"abilities":[26,0],"base_stats":[80,90,80,110,130,110],"catch_rate":3,"evolutions":[],"friendship":90,"id":408,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":149},{"level":5,"move_id":262},{"level":10,"move_id":270},{"level":15,"move_id":219},{"level":20,"move_id":225},{"level":25,"move_id":182},{"level":30,"move_id":287},{"level":35,"move_id":295},{"level":40,"move_id":94},{"level":45,"move_id":105},{"level":50,"move_id":349}],"rom_address":3310024},"rom_address":3299912,"tmhm_learnset":"035C5E93B7BBD63E","types":[16,14]},{"abilities":[32,0],"base_stats":[100,100,100,100,100,100],"catch_rate":3,"evolutions":[],"friendship":100,"id":409,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":273},{"level":1,"move_id":93},{"level":5,"move_id":156},{"level":10,"move_id":129},{"level":15,"move_id":270},{"level":20,"move_id":94},{"level":25,"move_id":287},{"level":30,"move_id":156},{"level":35,"move_id":38},{"level":40,"move_id":248},{"level":45,"move_id":322},{"level":50,"move_id":353}],"rom_address":3310054},"rom_address":3299940,"tmhm_learnset":"00408E93B59BC62C","types":[8,14]},{"abilities":[46,0],"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":410,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":35},{"level":5,"move_id":101},{"level":10,"move_id":104},{"level":15,"move_id":282},{"level":20,"move_id":228},{"level":25,"move_id":94},{"level":30,"move_id":129},{"level":35,"move_id":97},{"level":40,"move_id":105},{"level":45,"move_id":354},{"level":50,"move_id":245}],"rom_address":3310084},"rom_address":3299968,"tmhm_learnset":"00E58FC3F5BBDE2D","types":[14,14]},{"abilities":[26,0],"base_stats":[65,50,70,65,95,80],"catch_rate":45,"evolutions":[],"friendship":70,"id":411,"learnset":{"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":35},{"level":6,"move_id":45},{"level":9,"move_id":310},{"level":14,"move_id":93},{"level":17,"move_id":36},{"level":22,"move_id":253},{"level":25,"move_id":281},{"level":30,"move_id":149},{"level":33,"move_id":38},{"level":38,"move_id":215},{"level":41,"move_id":219},{"level":46,"move_id":94}],"rom_address":3310114},"rom_address":3299996,"tmhm_learnset":"00419F03B41B8E28","types":[14,14]}],"static_encounters":[{"flag":33,"level":50,"rom_address":2379222,"species":407},{"flag":32,"level":50,"rom_address":2379215,"species":408},{"flag":977,"level":30,"rom_address":2316785,"species":101},{"flag":978,"level":30,"rom_address":2316862,"species":101},{"flag":842,"level":40,"rom_address":2379579,"species":185},{"flag":763,"level":30,"rom_address":2531937,"species":410},{"flag":801,"level":70,"rom_address":2536492,"species":250},{"flag":800,"level":70,"rom_address":2536772,"species":249},{"flag":782,"level":70,"rom_address":2347550,"species":404},{"flag":718,"level":30,"rom_address":2531517,"species":151},{"flag":974,"level":25,"rom_address":2332864,"species":100},{"flag":975,"level":25,"rom_address":2332941,"species":100},{"flag":976,"level":25,"rom_address":2333018,"species":100},{"flag":936,"level":40,"rom_address":2338991,"species":402},{"flag":935,"level":40,"rom_address":2291862,"species":401},{"flag":937,"level":40,"rom_address":2339249,"species":403},{"flag":989,"level":30,"rom_address":2573968,"species":317},{"flag":990,"level":30,"rom_address":2573987,"species":317},{"flag":982,"level":30,"rom_address":2573873,"species":317},{"flag":985,"level":30,"rom_address":2573892,"species":317},{"flag":986,"level":30,"rom_address":2573911,"species":317},{"flag":987,"level":30,"rom_address":2573930,"species":317},{"flag":988,"level":30,"rom_address":2573949,"species":317},{"flag":970,"level":30,"rom_address":2059073,"species":317},{"flag":80,"level":70,"rom_address":2340984,"species":406},{"flag":783,"level":70,"rom_address":2347759,"species":405}],"tmhm_moves":[264,337,352,347,46,92,258,339,331,237,241,269,58,59,63,113,182,240,202,219,218,76,231,85,87,89,216,91,94,247,280,104,115,351,53,188,201,126,317,332,259,263,290,156,213,168,211,285,289,315,15,19,57,70,148,249,127,291],"trainers":[{"battle_script_rom_address":0,"party":[],"party_rom_address":4160749568,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3221820},{"battle_script_rom_address":2298147,"party":[{"level":21,"moves":[0,0,0,0],"species":74}],"party_rom_address":3202872,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3221860},{"battle_script_rom_address":2315511,"party":[{"level":32,"moves":[0,0,0,0],"species":286}],"party_rom_address":3202880,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3221900},{"battle_script_rom_address":2316936,"party":[{"level":31,"moves":[0,0,0,0],"species":41},{"level":31,"moves":[0,0,0,0],"species":330}],"party_rom_address":3202888,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3221940},{"battle_script_rom_address":2316983,"party":[{"level":32,"moves":[0,0,0,0],"species":41}],"party_rom_address":3202904,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3221980},{"battle_script_rom_address":2317996,"party":[{"level":32,"moves":[0,0,0,0],"species":330}],"party_rom_address":3202912,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222020},{"battle_script_rom_address":2320418,"party":[{"level":36,"moves":[0,0,0,0],"species":286}],"party_rom_address":3202920,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222060},{"battle_script_rom_address":2320449,"party":[{"level":36,"moves":[0,0,0,0],"species":330}],"party_rom_address":3202928,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222100},{"battle_script_rom_address":2321650,"party":[{"level":36,"moves":[0,0,0,0],"species":41}],"party_rom_address":3202936,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222140},{"battle_script_rom_address":2307885,"party":[{"level":26,"moves":[0,0,0,0],"species":315},{"level":26,"moves":[0,0,0,0],"species":286},{"level":26,"moves":[0,0,0,0],"species":288},{"level":26,"moves":[0,0,0,0],"species":295},{"level":26,"moves":[0,0,0,0],"species":298},{"level":26,"moves":[0,0,0,0],"species":304}],"party_rom_address":3202944,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222180},{"battle_script_rom_address":0,"party":[{"level":9,"moves":[0,0,0,0],"species":286}],"party_rom_address":3202992,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222220},{"battle_script_rom_address":2061615,"party":[{"level":29,"moves":[0,0,0,0],"species":338},{"level":29,"moves":[0,0,0,0],"species":300}],"party_rom_address":3203000,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222260},{"battle_script_rom_address":2062556,"party":[{"level":30,"moves":[0,0,0,0],"species":310},{"level":30,"moves":[0,0,0,0],"species":178}],"party_rom_address":3203016,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222300},{"battle_script_rom_address":2062587,"party":[{"level":30,"moves":[0,0,0,0],"species":380},{"level":30,"moves":[0,0,0,0],"species":379}],"party_rom_address":3203032,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222340},{"battle_script_rom_address":2321681,"party":[{"level":36,"moves":[0,0,0,0],"species":330}],"party_rom_address":3203048,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222380},{"battle_script_rom_address":2063653,"party":[{"level":34,"moves":[0,0,0,0],"species":130}],"party_rom_address":3203056,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222420},{"battle_script_rom_address":0,"party":[{"level":11,"moves":[0,0,0,0],"species":286}],"party_rom_address":3203064,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222460},{"battle_script_rom_address":2563645,"party":[{"level":27,"moves":[0,0,0,0],"species":41},{"level":27,"moves":[0,0,0,0],"species":286}],"party_rom_address":3203072,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222500},{"battle_script_rom_address":2564779,"party":[{"level":27,"moves":[0,0,0,0],"species":286},{"level":27,"moves":[0,0,0,0],"species":330}],"party_rom_address":3203088,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222540},{"battle_script_rom_address":2564810,"party":[{"level":26,"moves":[0,0,0,0],"species":286},{"level":26,"moves":[0,0,0,0],"species":41},{"level":26,"moves":[0,0,0,0],"species":330}],"party_rom_address":3203104,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222580},{"battle_script_rom_address":2151814,"party":[{"level":15,"moves":[0,0,0,0],"species":330}],"party_rom_address":3203128,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222620},{"battle_script_rom_address":2151873,"party":[{"level":14,"moves":[0,0,0,0],"species":41},{"level":14,"moves":[0,0,0,0],"species":330}],"party_rom_address":3203136,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222660},{"battle_script_rom_address":2248406,"party":[{"level":32,"moves":[0,0,0,0],"species":339}],"party_rom_address":3203152,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222700},{"battle_script_rom_address":2311132,"party":[{"level":32,"moves":[0,0,0,0],"species":41}],"party_rom_address":3203160,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222740},{"battle_script_rom_address":2311163,"party":[{"level":32,"moves":[0,0,0,0],"species":330}],"party_rom_address":3203168,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222780},{"battle_script_rom_address":2311194,"party":[{"level":30,"moves":[0,0,0,0],"species":286},{"level":30,"moves":[0,0,0,0],"species":330}],"party_rom_address":3203176,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222820},{"battle_script_rom_address":2563676,"party":[{"level":28,"moves":[0,0,0,0],"species":330}],"party_rom_address":3203192,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222860},{"battle_script_rom_address":2317024,"party":[{"level":32,"moves":[0,0,0,0],"species":330}],"party_rom_address":3203200,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222900},{"battle_script_rom_address":2318037,"party":[{"level":32,"moves":[0,0,0,0],"species":41}],"party_rom_address":3203208,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222940},{"battle_script_rom_address":2062525,"party":[{"level":30,"moves":[0,0,0,0],"species":335},{"level":30,"moves":[0,0,0,0],"species":67}],"party_rom_address":3203216,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3222980},{"battle_script_rom_address":2317860,"party":[{"level":34,"moves":[0,0,0,0],"species":287},{"level":34,"moves":[0,0,0,0],"species":42}],"party_rom_address":3203232,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223020},{"battle_script_rom_address":2306336,"party":[{"level":31,"moves":[0,0,0,0],"species":336}],"party_rom_address":3203248,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223060},{"battle_script_rom_address":2564841,"party":[{"level":28,"moves":[0,0,0,0],"species":330},{"level":28,"moves":[0,0,0,0],"species":287}],"party_rom_address":3203256,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223100},{"battle_script_rom_address":2320766,"party":[{"level":37,"moves":[0,0,0,0],"species":331},{"level":37,"moves":[0,0,0,0],"species":287}],"party_rom_address":3203272,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223140},{"battle_script_rom_address":2322088,"party":[{"level":41,"moves":[0,0,0,0],"species":287},{"level":41,"moves":[0,0,0,0],"species":169},{"level":43,"moves":[0,0,0,0],"species":331}],"party_rom_address":3203288,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223180},{"battle_script_rom_address":2306305,"party":[{"level":31,"moves":[0,0,0,0],"species":351}],"party_rom_address":3203312,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223220},{"battle_script_rom_address":2020252,"party":[{"level":14,"moves":[0,0,0,0],"species":306},{"level":14,"moves":[0,0,0,0],"species":363}],"party_rom_address":3203320,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223260},{"battle_script_rom_address":2052806,"party":[{"level":14,"moves":[0,0,0,0],"species":363},{"level":14,"moves":[0,0,0,0],"species":306},{"level":14,"moves":[0,0,0,0],"species":363}],"party_rom_address":3203336,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223300},{"battle_script_rom_address":2329135,"party":[{"level":43,"moves":[94,0,0,0],"species":357},{"level":43,"moves":[29,89,0,0],"species":319}],"party_rom_address":3203360,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3223340},{"battle_script_rom_address":2062181,"party":[{"level":26,"moves":[0,0,0,0],"species":363},{"level":26,"moves":[0,0,0,0],"species":44}],"party_rom_address":3203392,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223380},{"battle_script_rom_address":0,"party":[{"level":26,"moves":[0,0,0,0],"species":306},{"level":26,"moves":[0,0,0,0],"species":363}],"party_rom_address":3203408,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223420},{"battle_script_rom_address":0,"party":[{"level":28,"moves":[0,0,0,0],"species":306},{"level":28,"moves":[0,0,0,0],"species":44},{"level":28,"moves":[0,0,0,0],"species":363}],"party_rom_address":3203424,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223460},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":306},{"level":31,"moves":[0,0,0,0],"species":44},{"level":31,"moves":[0,0,0,0],"species":363}],"party_rom_address":3203448,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223500},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":307},{"level":34,"moves":[0,0,0,0],"species":44},{"level":34,"moves":[0,0,0,0],"species":363}],"party_rom_address":3203472,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223540},{"battle_script_rom_address":2040619,"party":[{"level":23,"moves":[91,163,28,40],"species":28}],"party_rom_address":3203496,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3223580},{"battle_script_rom_address":2059717,"party":[{"level":27,"moves":[60,120,201,246],"species":318},{"level":27,"moves":[91,163,28,40],"species":27},{"level":27,"moves":[91,163,28,40],"species":28}],"party_rom_address":3203512,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3223620},{"battle_script_rom_address":2027714,"party":[{"level":25,"moves":[91,163,28,40],"species":27},{"level":25,"moves":[91,163,28,40],"species":28}],"party_rom_address":3203560,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3223660},{"battle_script_rom_address":0,"party":[{"level":27,"moves":[91,163,28,40],"species":28}],"party_rom_address":3203592,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3223700},{"battle_script_rom_address":0,"party":[{"level":30,"moves":[91,163,28,40],"species":28}],"party_rom_address":3203608,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3223740},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[91,163,28,40],"species":28}],"party_rom_address":3203624,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3223780},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[91,163,28,40],"species":28}],"party_rom_address":3203640,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3223820},{"battle_script_rom_address":0,"party":[{"level":17,"moves":[0,0,0,0],"species":81},{"level":17,"moves":[0,0,0,0],"species":370}],"party_rom_address":3203656,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223860},{"battle_script_rom_address":0,"party":[{"level":27,"moves":[0,0,0,0],"species":81},{"level":27,"moves":[0,0,0,0],"species":371}],"party_rom_address":3203672,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223900},{"battle_script_rom_address":0,"party":[{"level":30,"moves":[0,0,0,0],"species":82},{"level":30,"moves":[0,0,0,0],"species":371}],"party_rom_address":3203688,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223940},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":82},{"level":33,"moves":[0,0,0,0],"species":371}],"party_rom_address":3203704,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3223980},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[0,0,0,0],"species":82},{"level":36,"moves":[0,0,0,0],"species":371}],"party_rom_address":3203720,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3224020},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[49,86,63,85],"species":82},{"level":39,"moves":[54,23,48,48],"species":372}],"party_rom_address":3203736,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3224060},{"battle_script_rom_address":2030183,"party":[{"level":12,"moves":[0,0,0,0],"species":350},{"level":12,"moves":[0,0,0,0],"species":350}],"party_rom_address":3203768,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3224100},{"battle_script_rom_address":2030293,"party":[{"level":26,"moves":[0,0,0,0],"species":183}],"party_rom_address":3203784,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3224140},{"battle_script_rom_address":2030324,"party":[{"level":26,"moves":[0,0,0,0],"species":183}],"party_rom_address":3203792,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3224180},{"battle_script_rom_address":0,"party":[{"level":26,"moves":[0,0,0,0],"species":183},{"level":26,"moves":[0,0,0,0],"species":183}],"party_rom_address":3203800,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3224220},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[0,0,0,0],"species":183},{"level":29,"moves":[0,0,0,0],"species":183}],"party_rom_address":3203816,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3224260},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[0,0,0,0],"species":183},{"level":32,"moves":[0,0,0,0],"species":183}],"party_rom_address":3203832,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3224300},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[0,0,0,0],"species":184},{"level":35,"moves":[0,0,0,0],"species":184}],"party_rom_address":3203848,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3224340},{"battle_script_rom_address":2030073,"party":[{"level":13,"moves":[28,29,39,57],"species":288}],"party_rom_address":3203864,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3224380},{"battle_script_rom_address":2537320,"party":[{"level":12,"moves":[0,0,0,0],"species":350},{"level":12,"moves":[0,0,0,0],"species":183}],"party_rom_address":3203880,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3224420},{"battle_script_rom_address":2333372,"party":[{"level":26,"moves":[0,0,0,0],"species":183}],"party_rom_address":3203896,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3224460},{"battle_script_rom_address":0,"party":[{"level":27,"moves":[28,42,39,57],"species":289}],"party_rom_address":3203904,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3224500},{"battle_script_rom_address":0,"party":[{"level":30,"moves":[28,42,39,57],"species":289}],"party_rom_address":3203920,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3224540},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[28,42,39,57],"species":289}],"party_rom_address":3203936,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3224580},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[28,42,39,57],"species":289}],"party_rom_address":3203952,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3224620},{"battle_script_rom_address":2125121,"party":[{"level":26,"moves":[98,97,17,0],"species":305}],"party_rom_address":3203968,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3224660},{"battle_script_rom_address":2125185,"party":[{"level":26,"moves":[42,146,8,0],"species":308}],"party_rom_address":3203984,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3224700},{"battle_script_rom_address":2125249,"party":[{"level":26,"moves":[47,68,247,0],"species":364}],"party_rom_address":3204000,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3224740},{"battle_script_rom_address":2125313,"party":[{"level":26,"moves":[116,163,0,0],"species":365}],"party_rom_address":3204016,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3224780},{"battle_script_rom_address":2062150,"party":[{"level":28,"moves":[116,98,17,27],"species":305},{"level":28,"moves":[44,91,185,72],"species":332},{"level":28,"moves":[205,250,54,96],"species":313},{"level":28,"moves":[85,48,86,49],"species":82},{"level":28,"moves":[202,185,104,207],"species":300}],"party_rom_address":3204032,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3224820},{"battle_script_rom_address":2558747,"party":[{"level":44,"moves":[0,0,0,0],"species":322},{"level":44,"moves":[0,0,0,0],"species":357},{"level":44,"moves":[0,0,0,0],"species":331}],"party_rom_address":3204112,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3224860},{"battle_script_rom_address":2558809,"party":[{"level":46,"moves":[0,0,0,0],"species":355},{"level":46,"moves":[0,0,0,0],"species":121}],"party_rom_address":3204136,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3224900},{"battle_script_rom_address":2040822,"party":[{"level":17,"moves":[0,0,0,0],"species":337},{"level":17,"moves":[0,0,0,0],"species":313},{"level":17,"moves":[0,0,0,0],"species":335}],"party_rom_address":3204152,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3224940},{"battle_script_rom_address":2326273,"party":[{"level":43,"moves":[0,0,0,0],"species":345},{"level":43,"moves":[0,0,0,0],"species":310}],"party_rom_address":3204176,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3224980},{"battle_script_rom_address":2326304,"party":[{"level":43,"moves":[0,0,0,0],"species":82},{"level":43,"moves":[0,0,0,0],"species":89}],"party_rom_address":3204192,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225020},{"battle_script_rom_address":2327963,"party":[{"level":42,"moves":[0,0,0,0],"species":305},{"level":42,"moves":[0,0,0,0],"species":355},{"level":42,"moves":[0,0,0,0],"species":64}],"party_rom_address":3204208,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225060},{"battle_script_rom_address":2329011,"party":[{"level":42,"moves":[0,0,0,0],"species":85},{"level":42,"moves":[0,0,0,0],"species":64},{"level":42,"moves":[0,0,0,0],"species":101},{"level":42,"moves":[0,0,0,0],"species":300}],"party_rom_address":3204232,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225100},{"battle_script_rom_address":2329042,"party":[{"level":42,"moves":[0,0,0,0],"species":317},{"level":42,"moves":[0,0,0,0],"species":75},{"level":42,"moves":[0,0,0,0],"species":314}],"party_rom_address":3204264,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225140},{"battle_script_rom_address":0,"party":[{"level":26,"moves":[0,0,0,0],"species":337},{"level":26,"moves":[0,0,0,0],"species":313},{"level":26,"moves":[0,0,0,0],"species":335}],"party_rom_address":3204288,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225180},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[0,0,0,0],"species":338},{"level":29,"moves":[0,0,0,0],"species":313},{"level":29,"moves":[0,0,0,0],"species":335}],"party_rom_address":3204312,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225220},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[0,0,0,0],"species":338},{"level":32,"moves":[0,0,0,0],"species":313},{"level":32,"moves":[0,0,0,0],"species":335}],"party_rom_address":3204336,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225260},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[0,0,0,0],"species":338},{"level":35,"moves":[0,0,0,0],"species":313},{"level":35,"moves":[0,0,0,0],"species":336}],"party_rom_address":3204360,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225300},{"battle_script_rom_address":2067950,"party":[{"level":33,"moves":[0,0,0,0],"species":75},{"level":33,"moves":[0,0,0,0],"species":297}],"party_rom_address":3204384,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225340},{"battle_script_rom_address":2125377,"party":[{"level":26,"moves":[185,95,0,0],"species":316}],"party_rom_address":3204400,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3225380},{"battle_script_rom_address":2125441,"party":[{"level":26,"moves":[111,38,247,0],"species":40}],"party_rom_address":3204416,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3225420},{"battle_script_rom_address":2125505,"party":[{"level":26,"moves":[14,163,0,0],"species":380}],"party_rom_address":3204432,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3225460},{"battle_script_rom_address":2062119,"party":[{"level":29,"moves":[226,185,57,44],"species":355},{"level":29,"moves":[72,89,64,73],"species":363},{"level":29,"moves":[19,55,54,182],"species":310}],"party_rom_address":3204448,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3225500},{"battle_script_rom_address":2558778,"party":[{"level":45,"moves":[0,0,0,0],"species":383},{"level":45,"moves":[0,0,0,0],"species":338}],"party_rom_address":3204496,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225540},{"battle_script_rom_address":2040932,"party":[{"level":17,"moves":[0,0,0,0],"species":309},{"level":17,"moves":[0,0,0,0],"species":339},{"level":17,"moves":[0,0,0,0],"species":363}],"party_rom_address":3204512,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225580},{"battle_script_rom_address":2059686,"party":[{"level":30,"moves":[0,0,0,0],"species":322}],"party_rom_address":3204536,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225620},{"battle_script_rom_address":2326335,"party":[{"level":45,"moves":[0,0,0,0],"species":363}],"party_rom_address":3204544,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225660},{"battle_script_rom_address":2327994,"party":[{"level":45,"moves":[0,0,0,0],"species":319}],"party_rom_address":3204552,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225700},{"battle_script_rom_address":2328025,"party":[{"level":42,"moves":[0,0,0,0],"species":321},{"level":42,"moves":[0,0,0,0],"species":357},{"level":42,"moves":[0,0,0,0],"species":297}],"party_rom_address":3204560,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225740},{"battle_script_rom_address":2329073,"party":[{"level":43,"moves":[0,0,0,0],"species":227},{"level":43,"moves":[0,0,0,0],"species":322}],"party_rom_address":3204584,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225780},{"battle_script_rom_address":2329104,"party":[{"level":42,"moves":[0,0,0,0],"species":28},{"level":42,"moves":[0,0,0,0],"species":38},{"level":42,"moves":[0,0,0,0],"species":369}],"party_rom_address":3204600,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225820},{"battle_script_rom_address":0,"party":[{"level":26,"moves":[0,0,0,0],"species":309},{"level":26,"moves":[0,0,0,0],"species":339},{"level":26,"moves":[0,0,0,0],"species":363}],"party_rom_address":3204624,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225860},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[0,0,0,0],"species":310},{"level":29,"moves":[0,0,0,0],"species":339},{"level":29,"moves":[0,0,0,0],"species":363}],"party_rom_address":3204648,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225900},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[0,0,0,0],"species":310},{"level":32,"moves":[0,0,0,0],"species":339},{"level":32,"moves":[0,0,0,0],"species":363}],"party_rom_address":3204672,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225940},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":310},{"level":34,"moves":[0,0,0,0],"species":340},{"level":34,"moves":[0,0,0,0],"species":363}],"party_rom_address":3204696,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3225980},{"battle_script_rom_address":2557556,"party":[{"level":41,"moves":[0,0,0,0],"species":378},{"level":41,"moves":[0,0,0,0],"species":348}],"party_rom_address":3204720,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3226020},{"battle_script_rom_address":2062494,"party":[{"level":30,"moves":[0,0,0,0],"species":361},{"level":30,"moves":[0,0,0,0],"species":377}],"party_rom_address":3204736,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3226060},{"battle_script_rom_address":2061319,"party":[{"level":29,"moves":[0,0,0,0],"species":361},{"level":29,"moves":[0,0,0,0],"species":377}],"party_rom_address":3204752,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3226100},{"battle_script_rom_address":2309379,"party":[{"level":32,"moves":[0,0,0,0],"species":322}],"party_rom_address":3204768,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3226140},{"battle_script_rom_address":2309166,"party":[{"level":32,"moves":[0,0,0,0],"species":377}],"party_rom_address":3204776,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3226180},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":322},{"level":31,"moves":[0,0,0,0],"species":351}],"party_rom_address":3204784,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3226220},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[0,0,0,0],"species":351},{"level":35,"moves":[0,0,0,0],"species":322}],"party_rom_address":3204800,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3226260},{"battle_script_rom_address":0,"party":[{"level":40,"moves":[0,0,0,0],"species":351},{"level":40,"moves":[0,0,0,0],"species":322}],"party_rom_address":3204816,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3226300},{"battle_script_rom_address":0,"party":[{"level":42,"moves":[0,0,0,0],"species":361},{"level":42,"moves":[0,0,0,0],"species":322},{"level":42,"moves":[0,0,0,0],"species":352}],"party_rom_address":3204832,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3226340},{"battle_script_rom_address":2024261,"party":[{"level":7,"moves":[0,0,0,0],"species":288}],"party_rom_address":3204856,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3226380},{"battle_script_rom_address":2259635,"party":[{"level":39,"moves":[213,186,175,96],"species":325},{"level":39,"moves":[213,219,36,96],"species":325}],"party_rom_address":3204864,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3226420},{"battle_script_rom_address":2248487,"party":[{"level":26,"moves":[0,0,0,0],"species":287},{"level":28,"moves":[0,0,0,0],"species":287},{"level":30,"moves":[0,0,0,0],"species":339}],"party_rom_address":3204896,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3226460},{"battle_script_rom_address":0,"party":[{"level":11,"moves":[33,39,0,0],"species":288}],"party_rom_address":3204920,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3226500},{"battle_script_rom_address":2259418,"party":[{"level":40,"moves":[0,0,0,0],"species":119}],"party_rom_address":3204936,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3226540},{"battle_script_rom_address":2354429,"party":[{"level":45,"moves":[0,0,0,0],"species":363}],"party_rom_address":3204944,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3226580},{"battle_script_rom_address":0,"party":[{"level":27,"moves":[0,0,0,0],"species":289}],"party_rom_address":3204952,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3226620},{"battle_script_rom_address":0,"party":[{"level":30,"moves":[0,0,0,0],"species":289}],"party_rom_address":3204960,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3226660},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":289}],"party_rom_address":3204968,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3226700},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[154,44,60,28],"species":289}],"party_rom_address":3204976,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3226740},{"battle_script_rom_address":2298023,"party":[{"level":21,"moves":[0,0,0,0],"species":183}],"party_rom_address":3204992,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3226780},{"battle_script_rom_address":2298054,"party":[{"level":21,"moves":[0,0,0,0],"species":306}],"party_rom_address":3205000,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3226820},{"battle_script_rom_address":2298085,"party":[{"level":21,"moves":[0,0,0,0],"species":339}],"party_rom_address":3205008,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3226860},{"battle_script_rom_address":2061412,"party":[{"level":29,"moves":[20,122,154,185],"species":317},{"level":29,"moves":[86,103,137,242],"species":379}],"party_rom_address":3205016,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3226900},{"battle_script_rom_address":2259449,"party":[{"level":40,"moves":[0,0,0,0],"species":118}],"party_rom_address":3205048,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3226940},{"battle_script_rom_address":2259480,"party":[{"level":40,"moves":[0,0,0,0],"species":184}],"party_rom_address":3205056,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3226980},{"battle_script_rom_address":2259511,"party":[{"level":35,"moves":[78,250,240,96],"species":373},{"level":37,"moves":[13,152,96,0],"species":326},{"level":39,"moves":[253,154,252,96],"species":296}],"party_rom_address":3205064,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3227020},{"battle_script_rom_address":2259542,"party":[{"level":39,"moves":[0,0,0,0],"species":330},{"level":39,"moves":[0,0,0,0],"species":331}],"party_rom_address":3205112,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3227060},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[20,122,154,185],"species":317},{"level":35,"moves":[86,103,137,242],"species":379}],"party_rom_address":3205128,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3227100},{"battle_script_rom_address":0,"party":[{"level":38,"moves":[20,122,154,185],"species":317},{"level":38,"moves":[86,103,137,242],"species":379}],"party_rom_address":3205160,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3227140},{"battle_script_rom_address":0,"party":[{"level":41,"moves":[20,122,154,185],"species":317},{"level":41,"moves":[86,103,137,242],"species":379}],"party_rom_address":3205192,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3227180},{"battle_script_rom_address":0,"party":[{"level":44,"moves":[20,122,154,185],"species":317},{"level":44,"moves":[86,103,137,242],"species":379}],"party_rom_address":3205224,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3227220},{"battle_script_rom_address":2024075,"party":[{"level":7,"moves":[0,0,0,0],"species":288}],"party_rom_address":3205256,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3227260},{"battle_script_rom_address":2068012,"party":[{"level":33,"moves":[0,0,0,0],"species":324},{"level":33,"moves":[0,0,0,0],"species":356}],"party_rom_address":3205264,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3227300},{"battle_script_rom_address":2354398,"party":[{"level":45,"moves":[0,0,0,0],"species":184}],"party_rom_address":3205280,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3227340},{"battle_script_rom_address":0,"party":[{"level":27,"moves":[0,0,0,0],"species":289}],"party_rom_address":3205288,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3227380},{"battle_script_rom_address":0,"party":[{"level":30,"moves":[0,0,0,0],"species":289}],"party_rom_address":3205296,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3227420},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":289}],"party_rom_address":3205304,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3227460},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[154,44,60,28],"species":289}],"party_rom_address":3205312,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3227500},{"battle_script_rom_address":2046087,"party":[{"level":19,"moves":[0,0,0,0],"species":382}],"party_rom_address":3205328,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3227540},{"battle_script_rom_address":2333649,"party":[{"level":25,"moves":[0,0,0,0],"species":313},{"level":25,"moves":[0,0,0,0],"species":116}],"party_rom_address":3205336,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3227580},{"battle_script_rom_address":2306212,"party":[{"level":31,"moves":[0,0,0,0],"species":111}],"party_rom_address":3205352,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3227620},{"battle_script_rom_address":2298116,"party":[{"level":20,"moves":[0,0,0,0],"species":339}],"party_rom_address":3205360,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3227660},{"battle_script_rom_address":0,"party":[{"level":27,"moves":[0,0,0,0],"species":383}],"party_rom_address":3205368,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3227700},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[0,0,0,0],"species":383},{"level":29,"moves":[0,0,0,0],"species":111}],"party_rom_address":3205376,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3227740},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[0,0,0,0],"species":383},{"level":32,"moves":[0,0,0,0],"species":111}],"party_rom_address":3205392,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3227780},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[0,0,0,0],"species":384},{"level":35,"moves":[0,0,0,0],"species":112}],"party_rom_address":3205408,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3227820},{"battle_script_rom_address":2027745,"party":[{"level":26,"moves":[0,0,0,0],"species":330}],"party_rom_address":3205424,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3227860},{"battle_script_rom_address":2027776,"party":[{"level":26,"moves":[0,0,0,0],"species":72}],"party_rom_address":3205432,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3227900},{"battle_script_rom_address":2028359,"party":[{"level":24,"moves":[0,0,0,0],"species":72},{"level":24,"moves":[0,0,0,0],"species":72}],"party_rom_address":3205440,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3227940},{"battle_script_rom_address":2028653,"party":[{"level":24,"moves":[0,0,0,0],"species":72},{"level":24,"moves":[0,0,0,0],"species":309},{"level":24,"moves":[0,0,0,0],"species":72}],"party_rom_address":3205456,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3227980},{"battle_script_rom_address":2028684,"party":[{"level":26,"moves":[0,0,0,0],"species":330}],"party_rom_address":3205480,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228020},{"battle_script_rom_address":2028950,"party":[{"level":26,"moves":[0,0,0,0],"species":73}],"party_rom_address":3205488,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228060},{"battle_script_rom_address":2028981,"party":[{"level":26,"moves":[0,0,0,0],"species":330}],"party_rom_address":3205496,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228100},{"battle_script_rom_address":2029949,"party":[{"level":25,"moves":[0,0,0,0],"species":72},{"level":25,"moves":[0,0,0,0],"species":330}],"party_rom_address":3205504,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228140},{"battle_script_rom_address":2063211,"party":[{"level":33,"moves":[0,0,0,0],"species":72},{"level":33,"moves":[0,0,0,0],"species":309}],"party_rom_address":3205520,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228180},{"battle_script_rom_address":2063242,"party":[{"level":34,"moves":[0,0,0,0],"species":330}],"party_rom_address":3205536,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228220},{"battle_script_rom_address":2063822,"party":[{"level":34,"moves":[0,0,0,0],"species":73}],"party_rom_address":3205544,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228260},{"battle_script_rom_address":2063853,"party":[{"level":34,"moves":[0,0,0,0],"species":116}],"party_rom_address":3205552,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228300},{"battle_script_rom_address":2064196,"party":[{"level":34,"moves":[0,0,0,0],"species":130}],"party_rom_address":3205560,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228340},{"battle_script_rom_address":2064227,"party":[{"level":31,"moves":[0,0,0,0],"species":330},{"level":31,"moves":[0,0,0,0],"species":309},{"level":31,"moves":[0,0,0,0],"species":330}],"party_rom_address":3205568,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228380},{"battle_script_rom_address":2067229,"party":[{"level":34,"moves":[0,0,0,0],"species":130}],"party_rom_address":3205592,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228420},{"battle_script_rom_address":2067359,"party":[{"level":34,"moves":[0,0,0,0],"species":310}],"party_rom_address":3205600,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228460},{"battle_script_rom_address":2067390,"party":[{"level":33,"moves":[0,0,0,0],"species":309},{"level":33,"moves":[0,0,0,0],"species":73}],"party_rom_address":3205608,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228500},{"battle_script_rom_address":2067291,"party":[{"level":33,"moves":[0,0,0,0],"species":73},{"level":33,"moves":[0,0,0,0],"species":313}],"party_rom_address":3205624,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228540},{"battle_script_rom_address":2067608,"party":[{"level":34,"moves":[0,0,0,0],"species":331}],"party_rom_address":3205640,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228580},{"battle_script_rom_address":2067857,"party":[{"level":34,"moves":[0,0,0,0],"species":342}],"party_rom_address":3205648,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228620},{"battle_script_rom_address":2067576,"party":[{"level":34,"moves":[0,0,0,0],"species":341}],"party_rom_address":3205656,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228660},{"battle_script_rom_address":2068089,"party":[{"level":34,"moves":[0,0,0,0],"species":130}],"party_rom_address":3205664,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228700},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":72},{"level":33,"moves":[0,0,0,0],"species":309},{"level":33,"moves":[0,0,0,0],"species":73}],"party_rom_address":3205672,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228740},{"battle_script_rom_address":2063414,"party":[{"level":33,"moves":[0,0,0,0],"species":72},{"level":33,"moves":[0,0,0,0],"species":313}],"party_rom_address":3205696,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228780},{"battle_script_rom_address":0,"party":[{"level":30,"moves":[0,0,0,0],"species":331}],"party_rom_address":3205712,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228820},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":331}],"party_rom_address":3205720,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228860},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":120},{"level":36,"moves":[0,0,0,0],"species":331}],"party_rom_address":3205728,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228900},{"battle_script_rom_address":0,"party":[{"level":37,"moves":[0,0,0,0],"species":121},{"level":39,"moves":[0,0,0,0],"species":331}],"party_rom_address":3205744,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228940},{"battle_script_rom_address":2089272,"party":[{"level":13,"moves":[0,0,0,0],"species":66}],"party_rom_address":3205760,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3228980},{"battle_script_rom_address":2068213,"party":[{"level":32,"moves":[0,0,0,0],"species":66},{"level":32,"moves":[0,0,0,0],"species":67}],"party_rom_address":3205768,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229020},{"battle_script_rom_address":2067701,"party":[{"level":34,"moves":[0,0,0,0],"species":336}],"party_rom_address":3205784,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229060},{"battle_script_rom_address":2047023,"party":[{"level":24,"moves":[0,0,0,0],"species":66},{"level":28,"moves":[0,0,0,0],"species":67}],"party_rom_address":3205792,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229100},{"battle_script_rom_address":2047054,"party":[{"level":19,"moves":[0,0,0,0],"species":66}],"party_rom_address":3205808,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229140},{"battle_script_rom_address":0,"party":[{"level":27,"moves":[0,0,0,0],"species":67}],"party_rom_address":3205816,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229180},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[0,0,0,0],"species":66},{"level":29,"moves":[0,0,0,0],"species":67}],"party_rom_address":3205824,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229220},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":66},{"level":31,"moves":[0,0,0,0],"species":67},{"level":31,"moves":[0,0,0,0],"species":67}],"party_rom_address":3205840,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229260},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":66},{"level":33,"moves":[0,0,0,0],"species":67},{"level":33,"moves":[0,0,0,0],"species":67},{"level":33,"moves":[0,0,0,0],"species":68}],"party_rom_address":3205864,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3229300},{"battle_script_rom_address":2550585,"party":[{"level":26,"moves":[0,0,0,0],"species":335},{"level":26,"moves":[0,0,0,0],"species":67}],"party_rom_address":3205896,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229340},{"battle_script_rom_address":2040791,"party":[{"level":19,"moves":[0,0,0,0],"species":66}],"party_rom_address":3205912,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229380},{"battle_script_rom_address":2308993,"party":[{"level":32,"moves":[0,0,0,0],"species":336}],"party_rom_address":3205920,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229420},{"battle_script_rom_address":2161493,"party":[{"level":17,"moves":[98,86,209,43],"species":337},{"level":17,"moves":[12,95,103,0],"species":100}],"party_rom_address":3205928,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3229460},{"battle_script_rom_address":2317055,"party":[{"level":31,"moves":[0,0,0,0],"species":286},{"level":31,"moves":[0,0,0,0],"species":41}],"party_rom_address":3205960,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229500},{"battle_script_rom_address":2318068,"party":[{"level":32,"moves":[0,0,0,0],"species":330}],"party_rom_address":3205976,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229540},{"battle_script_rom_address":2161524,"party":[{"level":17,"moves":[0,0,0,0],"species":100},{"level":17,"moves":[0,0,0,0],"species":81}],"party_rom_address":3205984,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229580},{"battle_script_rom_address":2062742,"party":[{"level":30,"moves":[0,0,0,0],"species":337},{"level":30,"moves":[0,0,0,0],"species":371}],"party_rom_address":3206000,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229620},{"battle_script_rom_address":2052978,"party":[{"level":15,"moves":[0,0,0,0],"species":81},{"level":15,"moves":[0,0,0,0],"species":370}],"party_rom_address":3206016,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229660},{"battle_script_rom_address":0,"party":[{"level":25,"moves":[0,0,0,0],"species":81},{"level":25,"moves":[0,0,0,0],"species":370},{"level":25,"moves":[0,0,0,0],"species":81}],"party_rom_address":3206032,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229700},{"battle_script_rom_address":0,"party":[{"level":28,"moves":[0,0,0,0],"species":81},{"level":28,"moves":[0,0,0,0],"species":371},{"level":28,"moves":[0,0,0,0],"species":81}],"party_rom_address":3206056,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229740},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":82},{"level":31,"moves":[0,0,0,0],"species":371},{"level":31,"moves":[0,0,0,0],"species":82}],"party_rom_address":3206080,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229780},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":82},{"level":34,"moves":[0,0,0,0],"species":372},{"level":34,"moves":[0,0,0,0],"species":82}],"party_rom_address":3206104,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229820},{"battle_script_rom_address":2097377,"party":[{"level":23,"moves":[0,0,0,0],"species":339}],"party_rom_address":3206128,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229860},{"battle_script_rom_address":2097584,"party":[{"level":22,"moves":[0,0,0,0],"species":218},{"level":22,"moves":[0,0,0,0],"species":218}],"party_rom_address":3206136,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229900},{"battle_script_rom_address":2097429,"party":[{"level":23,"moves":[0,0,0,0],"species":339}],"party_rom_address":3206152,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229940},{"battle_script_rom_address":2097553,"party":[{"level":23,"moves":[0,0,0,0],"species":218}],"party_rom_address":3206160,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3229980},{"battle_script_rom_address":2097460,"party":[{"level":23,"moves":[0,0,0,0],"species":218}],"party_rom_address":3206168,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230020},{"battle_script_rom_address":2046197,"party":[{"level":18,"moves":[0,0,0,0],"species":218},{"level":18,"moves":[0,0,0,0],"species":309}],"party_rom_address":3206176,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230060},{"battle_script_rom_address":0,"party":[{"level":26,"moves":[0,0,0,0],"species":218},{"level":26,"moves":[0,0,0,0],"species":309}],"party_rom_address":3206192,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230100},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[0,0,0,0],"species":218},{"level":29,"moves":[0,0,0,0],"species":310}],"party_rom_address":3206208,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230140},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[0,0,0,0],"species":218},{"level":32,"moves":[0,0,0,0],"species":310}],"party_rom_address":3206224,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230180},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[0,0,0,0],"species":219},{"level":35,"moves":[0,0,0,0],"species":310}],"party_rom_address":3206240,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230220},{"battle_script_rom_address":2040495,"party":[{"level":23,"moves":[91,28,40,163],"species":27}],"party_rom_address":3206256,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3230260},{"battle_script_rom_address":2040557,"party":[{"level":21,"moves":[229,189,60,61],"species":318},{"level":21,"moves":[40,28,10,91],"species":27},{"level":21,"moves":[229,189,60,61],"species":318}],"party_rom_address":3206272,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3230300},{"battle_script_rom_address":2043958,"party":[{"level":18,"moves":[0,0,0,0],"species":299}],"party_rom_address":3206320,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230340},{"battle_script_rom_address":2046025,"party":[{"level":18,"moves":[0,0,0,0],"species":27},{"level":18,"moves":[0,0,0,0],"species":299}],"party_rom_address":3206328,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230380},{"battle_script_rom_address":2549832,"party":[{"level":24,"moves":[0,0,0,0],"species":317}],"party_rom_address":3206344,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230420},{"battle_script_rom_address":2303835,"party":[{"level":20,"moves":[0,0,0,0],"species":288},{"level":20,"moves":[0,0,0,0],"species":304}],"party_rom_address":3206352,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230460},{"battle_script_rom_address":2303973,"party":[{"level":21,"moves":[0,0,0,0],"species":306}],"party_rom_address":3206368,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230500},{"battle_script_rom_address":2040729,"party":[{"level":18,"moves":[0,0,0,0],"species":27}],"party_rom_address":3206376,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230540},{"battle_script_rom_address":0,"party":[{"level":26,"moves":[0,0,0,0],"species":288},{"level":26,"moves":[0,0,0,0],"species":304}],"party_rom_address":3206384,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230580},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[0,0,0,0],"species":289},{"level":29,"moves":[0,0,0,0],"species":305}],"party_rom_address":3206400,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230620},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":27},{"level":31,"moves":[0,0,0,0],"species":305},{"level":31,"moves":[0,0,0,0],"species":289}],"party_rom_address":3206416,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230660},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":305},{"level":34,"moves":[0,0,0,0],"species":28},{"level":34,"moves":[0,0,0,0],"species":289}],"party_rom_address":3206440,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230700},{"battle_script_rom_address":2054989,"party":[{"level":26,"moves":[0,0,0,0],"species":311}],"party_rom_address":3206464,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230740},{"battle_script_rom_address":2055020,"party":[{"level":24,"moves":[0,0,0,0],"species":290},{"level":24,"moves":[0,0,0,0],"species":291},{"level":24,"moves":[0,0,0,0],"species":292}],"party_rom_address":3206472,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230780},{"battle_script_rom_address":2055051,"party":[{"level":27,"moves":[0,0,0,0],"species":290},{"level":27,"moves":[0,0,0,0],"species":293},{"level":27,"moves":[0,0,0,0],"species":294}],"party_rom_address":3206496,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230820},{"battle_script_rom_address":2059576,"party":[{"level":27,"moves":[0,0,0,0],"species":311},{"level":27,"moves":[0,0,0,0],"species":311},{"level":27,"moves":[0,0,0,0],"species":311}],"party_rom_address":3206520,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230860},{"battle_script_rom_address":2051695,"party":[{"level":16,"moves":[0,0,0,0],"species":294},{"level":16,"moves":[0,0,0,0],"species":292}],"party_rom_address":3206544,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230900},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":311},{"level":31,"moves":[0,0,0,0],"species":311},{"level":31,"moves":[0,0,0,0],"species":311}],"party_rom_address":3206560,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230940},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":311},{"level":34,"moves":[0,0,0,0],"species":311},{"level":34,"moves":[0,0,0,0],"species":312}],"party_rom_address":3206584,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3230980},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[0,0,0,0],"species":311},{"level":36,"moves":[0,0,0,0],"species":290},{"level":36,"moves":[0,0,0,0],"species":311},{"level":36,"moves":[0,0,0,0],"species":312}],"party_rom_address":3206608,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231020},{"battle_script_rom_address":0,"party":[{"level":38,"moves":[0,0,0,0],"species":311},{"level":38,"moves":[0,0,0,0],"species":294},{"level":38,"moves":[0,0,0,0],"species":311},{"level":38,"moves":[0,0,0,0],"species":312},{"level":38,"moves":[0,0,0,0],"species":292}],"party_rom_address":3206640,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3231060},{"battle_script_rom_address":2032546,"party":[{"level":15,"moves":[237,0,0,0],"species":63}],"party_rom_address":3206680,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3231100},{"battle_script_rom_address":2238272,"party":[{"level":36,"moves":[0,0,0,0],"species":393}],"party_rom_address":3206696,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231140},{"battle_script_rom_address":2238303,"party":[{"level":36,"moves":[0,0,0,0],"species":392}],"party_rom_address":3206704,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231180},{"battle_script_rom_address":2238334,"party":[{"level":36,"moves":[0,0,0,0],"species":203}],"party_rom_address":3206712,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231220},{"battle_script_rom_address":2307823,"party":[{"level":26,"moves":[0,0,0,0],"species":392},{"level":26,"moves":[0,0,0,0],"species":392},{"level":26,"moves":[0,0,0,0],"species":393}],"party_rom_address":3206720,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231260},{"battle_script_rom_address":2557525,"party":[{"level":41,"moves":[0,0,0,0],"species":64},{"level":41,"moves":[0,0,0,0],"species":349}],"party_rom_address":3206744,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231300},{"battle_script_rom_address":2062212,"party":[{"level":31,"moves":[0,0,0,0],"species":349}],"party_rom_address":3206760,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231340},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":64},{"level":33,"moves":[0,0,0,0],"species":349}],"party_rom_address":3206768,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231380},{"battle_script_rom_address":0,"party":[{"level":38,"moves":[0,0,0,0],"species":64},{"level":38,"moves":[0,0,0,0],"species":349}],"party_rom_address":3206784,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231420},{"battle_script_rom_address":0,"party":[{"level":41,"moves":[0,0,0,0],"species":64},{"level":41,"moves":[0,0,0,0],"species":349}],"party_rom_address":3206800,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231460},{"battle_script_rom_address":0,"party":[{"level":45,"moves":[0,0,0,0],"species":349},{"level":45,"moves":[0,0,0,0],"species":65}],"party_rom_address":3206816,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231500},{"battle_script_rom_address":2032577,"party":[{"level":16,"moves":[237,0,0,0],"species":63}],"party_rom_address":3206832,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3231540},{"battle_script_rom_address":2238365,"party":[{"level":36,"moves":[0,0,0,0],"species":393}],"party_rom_address":3206848,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231580},{"battle_script_rom_address":2238396,"party":[{"level":36,"moves":[0,0,0,0],"species":178}],"party_rom_address":3206856,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231620},{"battle_script_rom_address":2238427,"party":[{"level":36,"moves":[0,0,0,0],"species":64}],"party_rom_address":3206864,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231660},{"battle_script_rom_address":2307854,"party":[{"level":26,"moves":[0,0,0,0],"species":202},{"level":26,"moves":[0,0,0,0],"species":177},{"level":26,"moves":[0,0,0,0],"species":64}],"party_rom_address":3206872,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231700},{"battle_script_rom_address":2557587,"party":[{"level":41,"moves":[0,0,0,0],"species":393},{"level":41,"moves":[0,0,0,0],"species":178}],"party_rom_address":3206896,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231740},{"battle_script_rom_address":2062322,"party":[{"level":30,"moves":[0,0,0,0],"species":64},{"level":30,"moves":[0,0,0,0],"species":348}],"party_rom_address":3206912,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231780},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":64},{"level":34,"moves":[0,0,0,0],"species":348}],"party_rom_address":3206928,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231820},{"battle_script_rom_address":0,"party":[{"level":37,"moves":[0,0,0,0],"species":64},{"level":37,"moves":[0,0,0,0],"species":348}],"party_rom_address":3206944,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231860},{"battle_script_rom_address":0,"party":[{"level":40,"moves":[0,0,0,0],"species":64},{"level":40,"moves":[0,0,0,0],"species":348}],"party_rom_address":3206960,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231900},{"battle_script_rom_address":0,"party":[{"level":43,"moves":[0,0,0,0],"species":348},{"level":43,"moves":[0,0,0,0],"species":65}],"party_rom_address":3206976,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231940},{"battle_script_rom_address":2061209,"party":[{"level":29,"moves":[0,0,0,0],"species":338}],"party_rom_address":3206992,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3231980},{"battle_script_rom_address":2354274,"party":[{"level":44,"moves":[0,0,0,0],"species":338},{"level":44,"moves":[0,0,0,0],"species":338}],"party_rom_address":3207000,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3232020},{"battle_script_rom_address":2354305,"party":[{"level":45,"moves":[0,0,0,0],"species":380}],"party_rom_address":3207016,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3232060},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":338}],"party_rom_address":3207024,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3232100},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[29,28,60,154],"species":289},{"level":36,"moves":[98,209,60,46],"species":338}],"party_rom_address":3207032,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3232140},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[29,28,60,154],"species":289},{"level":39,"moves":[98,209,60,0],"species":338}],"party_rom_address":3207064,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3232180},{"battle_script_rom_address":0,"party":[{"level":41,"moves":[29,28,60,154],"species":289},{"level":41,"moves":[154,50,93,244],"species":55},{"level":41,"moves":[98,209,60,46],"species":338}],"party_rom_address":3207096,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3232220},{"battle_script_rom_address":2268477,"party":[{"level":46,"moves":[46,38,28,242],"species":287},{"level":48,"moves":[3,104,207,70],"species":300},{"level":46,"moves":[73,185,46,178],"species":345},{"level":48,"moves":[57,14,70,7],"species":327},{"level":49,"moves":[76,157,14,163],"species":376}],"party_rom_address":3207144,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3232260},{"battle_script_rom_address":2269104,"party":[{"level":48,"moves":[69,109,174,182],"species":362},{"level":49,"moves":[247,32,5,185],"species":378},{"level":50,"moves":[247,104,101,185],"species":322},{"level":49,"moves":[247,94,85,7],"species":378},{"level":51,"moves":[247,58,157,89],"species":362}],"party_rom_address":3207224,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3232300},{"battle_script_rom_address":2269786,"party":[{"level":50,"moves":[227,34,2,45],"species":342},{"level":50,"moves":[113,242,196,58],"species":347},{"level":52,"moves":[213,38,2,59],"species":342},{"level":52,"moves":[247,153,2,58],"species":347},{"level":53,"moves":[57,34,58,73],"species":343}],"party_rom_address":3207304,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3232340},{"battle_script_rom_address":2270448,"party":[{"level":52,"moves":[61,81,182,38],"species":396},{"level":54,"moves":[38,225,93,76],"species":359},{"level":53,"moves":[108,93,57,34],"species":230},{"level":53,"moves":[53,242,225,89],"species":334},{"level":55,"moves":[53,81,157,242],"species":397}],"party_rom_address":3207384,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3232380},{"battle_script_rom_address":2181824,"party":[{"level":12,"moves":[33,111,88,61],"species":74},{"level":12,"moves":[33,111,88,61],"species":74},{"level":15,"moves":[79,106,33,61],"species":320}],"party_rom_address":3207464,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3232420},{"battle_script_rom_address":2089070,"party":[{"level":16,"moves":[2,67,69,83],"species":66},{"level":16,"moves":[8,113,115,83],"species":356},{"level":19,"moves":[36,233,179,83],"species":335}],"party_rom_address":3207512,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3232460},{"battle_script_rom_address":2161073,"party":[{"level":20,"moves":[205,209,120,95],"species":100},{"level":20,"moves":[95,43,98,80],"species":337},{"level":22,"moves":[48,95,86,49],"species":82},{"level":24,"moves":[98,86,95,80],"species":338}],"party_rom_address":3207560,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3232500},{"battle_script_rom_address":2097176,"party":[{"level":24,"moves":[59,36,222,241],"species":339},{"level":24,"moves":[59,123,113,241],"species":218},{"level":26,"moves":[59,33,241,213],"species":340},{"level":29,"moves":[59,241,34,213],"species":321}],"party_rom_address":3207624,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3232540},{"battle_script_rom_address":2123720,"party":[{"level":27,"moves":[42,60,7,227],"species":308},{"level":27,"moves":[163,7,227,185],"species":365},{"level":29,"moves":[163,187,7,29],"species":289},{"level":31,"moves":[68,25,7,185],"species":366}],"party_rom_address":3207688,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3232580},{"battle_script_rom_address":2195894,"party":[{"level":29,"moves":[195,119,219,76],"species":358},{"level":29,"moves":[241,76,76,235],"species":369},{"level":30,"moves":[55,48,182,76],"species":310},{"level":31,"moves":[28,31,211,76],"species":227},{"level":33,"moves":[89,225,93,76],"species":359}],"party_rom_address":3207752,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3232620},{"battle_script_rom_address":0,"party":[{"level":41,"moves":[89,246,94,113],"species":319},{"level":41,"moves":[94,241,109,91],"species":178},{"level":42,"moves":[113,94,95,91],"species":348},{"level":42,"moves":[241,76,94,53],"species":349}],"party_rom_address":3207832,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3232660},{"battle_script_rom_address":2255993,"party":[{"level":41,"moves":[96,213,186,175],"species":325},{"level":41,"moves":[240,96,133,89],"species":324},{"level":43,"moves":[227,34,62,96],"species":342},{"level":43,"moves":[96,152,13,43],"species":327},{"level":46,"moves":[96,104,58,156],"species":230}],"party_rom_address":3207896,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3232700},{"battle_script_rom_address":2048342,"party":[{"level":9,"moves":[0,0,0,0],"species":392}],"party_rom_address":3207976,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3232740},{"battle_script_rom_address":2547425,"party":[{"level":17,"moves":[0,0,0,0],"species":392}],"party_rom_address":3207984,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3232780},{"battle_script_rom_address":2547456,"party":[{"level":15,"moves":[0,0,0,0],"species":339},{"level":15,"moves":[0,0,0,0],"species":43},{"level":15,"moves":[0,0,0,0],"species":309}],"party_rom_address":3207992,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3232820},{"battle_script_rom_address":0,"party":[{"level":26,"moves":[0,0,0,0],"species":392},{"level":26,"moves":[0,0,0,0],"species":356}],"party_rom_address":3208016,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3232860},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[0,0,0,0],"species":393},{"level":29,"moves":[0,0,0,0],"species":356}],"party_rom_address":3208032,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3232900},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[0,0,0,0],"species":393},{"level":32,"moves":[0,0,0,0],"species":357}],"party_rom_address":3208048,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3232940},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":393},{"level":34,"moves":[0,0,0,0],"species":378},{"level":34,"moves":[0,0,0,0],"species":357}],"party_rom_address":3208064,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3232980},{"battle_script_rom_address":2048590,"party":[{"level":9,"moves":[0,0,0,0],"species":306}],"party_rom_address":3208088,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3233020},{"battle_script_rom_address":2547487,"party":[{"level":16,"moves":[0,0,0,0],"species":306},{"level":16,"moves":[0,0,0,0],"species":292}],"party_rom_address":3208096,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3233060},{"battle_script_rom_address":0,"party":[{"level":26,"moves":[0,0,0,0],"species":306},{"level":26,"moves":[0,0,0,0],"species":370}],"party_rom_address":3208112,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3233100},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[0,0,0,0],"species":306},{"level":29,"moves":[0,0,0,0],"species":371}],"party_rom_address":3208128,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3233140},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[0,0,0,0],"species":307},{"level":32,"moves":[0,0,0,0],"species":371}],"party_rom_address":3208144,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3233180},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[0,0,0,0],"species":307},{"level":35,"moves":[0,0,0,0],"species":372}],"party_rom_address":3208160,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3233220},{"battle_script_rom_address":0,"party":[{"level":30,"moves":[95,60,146,42],"species":308},{"level":32,"moves":[8,25,47,185],"species":366}],"party_rom_address":3208176,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3233260},{"battle_script_rom_address":0,"party":[{"level":15,"moves":[45,39,29,60],"species":288},{"level":17,"moves":[33,116,36,0],"species":335}],"party_rom_address":3208208,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3233300},{"battle_script_rom_address":0,"party":[{"level":28,"moves":[45,39,29,60],"species":288},{"level":30,"moves":[33,116,36,0],"species":335}],"party_rom_address":3208240,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3233340},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[45,39,29,60],"species":288},{"level":33,"moves":[33,116,36,0],"species":335}],"party_rom_address":3208272,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3233380},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[45,39,29,60],"species":289},{"level":36,"moves":[33,116,36,0],"species":335}],"party_rom_address":3208304,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3233420},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[45,39,29,60],"species":289},{"level":38,"moves":[33,116,36,0],"species":336}],"party_rom_address":3208336,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3233460},{"battle_script_rom_address":2039914,"party":[{"level":16,"moves":[0,0,0,0],"species":304},{"level":16,"moves":[0,0,0,0],"species":288}],"party_rom_address":3208368,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3233500},{"battle_script_rom_address":2020520,"party":[{"level":15,"moves":[0,0,0,0],"species":315}],"party_rom_address":3208384,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3233540},{"battle_script_rom_address":2354243,"party":[{"level":22,"moves":[18,204,185,215],"species":315},{"level":36,"moves":[18,204,185,215],"species":315},{"level":40,"moves":[18,204,185,215],"species":315},{"level":12,"moves":[18,204,185,215],"species":315},{"level":30,"moves":[18,204,185,215],"species":315},{"level":42,"moves":[18,204,185,215],"species":316}],"party_rom_address":3208392,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3233580},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[0,0,0,0],"species":315}],"party_rom_address":3208488,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3233620},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[0,0,0,0],"species":315}],"party_rom_address":3208496,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3233660},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[0,0,0,0],"species":316}],"party_rom_address":3208504,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3233700},{"battle_script_rom_address":0,"party":[{"level":38,"moves":[0,0,0,0],"species":316}],"party_rom_address":3208512,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3233740},{"battle_script_rom_address":2040019,"party":[{"level":17,"moves":[0,0,0,0],"species":363}],"party_rom_address":3208520,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3233780},{"battle_script_rom_address":2061178,"party":[{"level":30,"moves":[0,0,0,0],"species":25}],"party_rom_address":3208528,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3233820},{"battle_script_rom_address":2259573,"party":[{"level":35,"moves":[0,0,0,0],"species":350},{"level":37,"moves":[0,0,0,0],"species":183},{"level":39,"moves":[0,0,0,0],"species":184}],"party_rom_address":3208536,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3233860},{"battle_script_rom_address":2033062,"party":[{"level":14,"moves":[0,0,0,0],"species":353},{"level":14,"moves":[0,0,0,0],"species":354}],"party_rom_address":3208560,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3233900},{"battle_script_rom_address":0,"party":[{"level":26,"moves":[0,0,0,0],"species":353},{"level":26,"moves":[0,0,0,0],"species":354}],"party_rom_address":3208576,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3233940},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[0,0,0,0],"species":353},{"level":29,"moves":[0,0,0,0],"species":354}],"party_rom_address":3208592,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3233980},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[0,0,0,0],"species":353},{"level":32,"moves":[0,0,0,0],"species":354}],"party_rom_address":3208608,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3234020},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[0,0,0,0],"species":353},{"level":35,"moves":[0,0,0,0],"species":354}],"party_rom_address":3208624,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3234060},{"battle_script_rom_address":2046913,"party":[{"level":27,"moves":[0,0,0,0],"species":336}],"party_rom_address":3208640,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3234100},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[36,26,28,91],"species":336}],"party_rom_address":3208648,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3234140},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[36,26,28,91],"species":336}],"party_rom_address":3208664,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3234180},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[36,187,28,91],"species":336}],"party_rom_address":3208680,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3234220},{"battle_script_rom_address":0,"party":[{"level":42,"moves":[36,187,28,91],"species":336}],"party_rom_address":3208696,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3234260},{"battle_script_rom_address":2040229,"party":[{"level":18,"moves":[136,96,93,197],"species":356}],"party_rom_address":3208712,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3234300},{"battle_script_rom_address":2297913,"party":[{"level":21,"moves":[0,0,0,0],"species":356},{"level":21,"moves":[0,0,0,0],"species":335}],"party_rom_address":3208728,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3234340},{"battle_script_rom_address":0,"party":[{"level":30,"moves":[0,0,0,0],"species":356},{"level":30,"moves":[0,0,0,0],"species":335}],"party_rom_address":3208744,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3234380},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":357},{"level":33,"moves":[0,0,0,0],"species":336}],"party_rom_address":3208760,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3234420},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[0,0,0,0],"species":357},{"level":36,"moves":[0,0,0,0],"species":336}],"party_rom_address":3208776,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3234460},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[0,0,0,0],"species":357},{"level":39,"moves":[0,0,0,0],"species":336}],"party_rom_address":3208792,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3234500},{"battle_script_rom_address":2018881,"party":[{"level":5,"moves":[0,0,0,0],"species":286}],"party_rom_address":3208808,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3234540},{"battle_script_rom_address":2023858,"party":[{"level":5,"moves":[0,0,0,0],"species":288},{"level":7,"moves":[0,0,0,0],"species":298}],"party_rom_address":3208816,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3234580},{"battle_script_rom_address":2181995,"party":[{"level":10,"moves":[33,0,0,0],"species":74}],"party_rom_address":3208832,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3234620},{"battle_script_rom_address":2182026,"party":[{"level":8,"moves":[0,0,0,0],"species":74},{"level":8,"moves":[0,0,0,0],"species":74}],"party_rom_address":3208848,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3234660},{"battle_script_rom_address":2048280,"party":[{"level":9,"moves":[0,0,0,0],"species":66}],"party_rom_address":3208864,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3234700},{"battle_script_rom_address":2161555,"party":[{"level":17,"moves":[29,28,45,85],"species":288},{"level":17,"moves":[133,124,25,1],"species":367}],"party_rom_address":3208872,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3234740},{"battle_script_rom_address":2326366,"party":[{"level":43,"moves":[213,58,85,53],"species":366},{"level":43,"moves":[29,182,5,92],"species":362}],"party_rom_address":3208904,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3234780},{"battle_script_rom_address":2326397,"party":[{"level":43,"moves":[29,94,85,91],"species":394},{"level":43,"moves":[89,247,76,24],"species":366}],"party_rom_address":3208936,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3234820},{"battle_script_rom_address":2044723,"party":[{"level":19,"moves":[0,0,0,0],"species":332}],"party_rom_address":3208968,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3234860},{"battle_script_rom_address":2044754,"party":[{"level":19,"moves":[0,0,0,0],"species":382}],"party_rom_address":3208976,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3234900},{"battle_script_rom_address":0,"party":[{"level":27,"moves":[0,0,0,0],"species":287}],"party_rom_address":3208984,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3234940},{"battle_script_rom_address":0,"party":[{"level":28,"moves":[0,0,0,0],"species":305},{"level":30,"moves":[0,0,0,0],"species":287}],"party_rom_address":3208992,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3234980},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":305},{"level":29,"moves":[0,0,0,0],"species":289},{"level":33,"moves":[0,0,0,0],"species":287}],"party_rom_address":3209008,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235020},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":305},{"level":32,"moves":[0,0,0,0],"species":289},{"level":36,"moves":[0,0,0,0],"species":287}],"party_rom_address":3209032,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235060},{"battle_script_rom_address":2546619,"party":[{"level":14,"moves":[0,0,0,0],"species":288},{"level":16,"moves":[0,0,0,0],"species":288}],"party_rom_address":3209056,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235100},{"battle_script_rom_address":2019129,"party":[{"level":4,"moves":[0,0,0,0],"species":288},{"level":3,"moves":[0,0,0,0],"species":304}],"party_rom_address":3209072,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235140},{"battle_script_rom_address":2033172,"party":[{"level":15,"moves":[0,0,0,0],"species":382},{"level":13,"moves":[0,0,0,0],"species":337}],"party_rom_address":3209088,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235180},{"battle_script_rom_address":2271299,"party":[{"level":57,"moves":[240,67,38,59],"species":314},{"level":55,"moves":[92,56,188,58],"species":73},{"level":56,"moves":[202,57,73,104],"species":297},{"level":56,"moves":[89,57,133,63],"species":324},{"level":56,"moves":[93,89,63,57],"species":130},{"level":58,"moves":[105,57,58,92],"species":329}],"party_rom_address":3209104,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3235220},{"battle_script_rom_address":2020489,"party":[{"level":5,"moves":[0,0,0,0],"species":129},{"level":10,"moves":[0,0,0,0],"species":72},{"level":15,"moves":[0,0,0,0],"species":129}],"party_rom_address":3209200,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235260},{"battle_script_rom_address":2023827,"party":[{"level":5,"moves":[0,0,0,0],"species":129},{"level":6,"moves":[0,0,0,0],"species":129},{"level":7,"moves":[0,0,0,0],"species":129}],"party_rom_address":3209224,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235300},{"battle_script_rom_address":2046307,"party":[{"level":16,"moves":[0,0,0,0],"species":129},{"level":17,"moves":[0,0,0,0],"species":118},{"level":18,"moves":[0,0,0,0],"species":323}],"party_rom_address":3209248,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235340},{"battle_script_rom_address":2028421,"party":[{"level":10,"moves":[0,0,0,0],"species":129},{"level":7,"moves":[0,0,0,0],"species":72},{"level":10,"moves":[0,0,0,0],"species":129}],"party_rom_address":3209272,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235380},{"battle_script_rom_address":2028531,"party":[{"level":11,"moves":[0,0,0,0],"species":72}],"party_rom_address":3209296,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235420},{"battle_script_rom_address":2032718,"party":[{"level":11,"moves":[0,0,0,0],"species":72},{"level":14,"moves":[0,0,0,0],"species":313},{"level":11,"moves":[0,0,0,0],"species":72},{"level":14,"moves":[0,0,0,0],"species":313}],"party_rom_address":3209304,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235460},{"battle_script_rom_address":2046338,"party":[{"level":19,"moves":[0,0,0,0],"species":323}],"party_rom_address":3209336,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235500},{"battle_script_rom_address":2052916,"party":[{"level":25,"moves":[0,0,0,0],"species":72},{"level":25,"moves":[0,0,0,0],"species":330}],"party_rom_address":3209344,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235540},{"battle_script_rom_address":2052947,"party":[{"level":16,"moves":[0,0,0,0],"species":72}],"party_rom_address":3209360,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235580},{"battle_script_rom_address":2030355,"party":[{"level":25,"moves":[0,0,0,0],"species":313},{"level":25,"moves":[0,0,0,0],"species":73}],"party_rom_address":3209368,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235620},{"battle_script_rom_address":0,"party":[{"level":24,"moves":[0,0,0,0],"species":72},{"level":27,"moves":[0,0,0,0],"species":130},{"level":27,"moves":[0,0,0,0],"species":130}],"party_rom_address":3209384,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235660},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[0,0,0,0],"species":130},{"level":26,"moves":[0,0,0,0],"species":330},{"level":26,"moves":[0,0,0,0],"species":72},{"level":29,"moves":[0,0,0,0],"species":130}],"party_rom_address":3209408,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235700},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":130},{"level":30,"moves":[0,0,0,0],"species":330},{"level":30,"moves":[0,0,0,0],"species":73},{"level":31,"moves":[0,0,0,0],"species":130}],"party_rom_address":3209440,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235740},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":130},{"level":33,"moves":[0,0,0,0],"species":331},{"level":33,"moves":[0,0,0,0],"species":130},{"level":35,"moves":[0,0,0,0],"species":73}],"party_rom_address":3209472,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235780},{"battle_script_rom_address":2067670,"party":[{"level":19,"moves":[0,0,0,0],"species":129},{"level":21,"moves":[0,0,0,0],"species":130},{"level":23,"moves":[0,0,0,0],"species":130},{"level":26,"moves":[0,0,0,0],"species":130},{"level":30,"moves":[0,0,0,0],"species":130},{"level":35,"moves":[0,0,0,0],"species":130}],"party_rom_address":3209504,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235820},{"battle_script_rom_address":2032749,"party":[{"level":6,"moves":[0,0,0,0],"species":100},{"level":6,"moves":[0,0,0,0],"species":100},{"level":14,"moves":[0,0,0,0],"species":81}],"party_rom_address":3209552,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235860},{"battle_script_rom_address":2032780,"party":[{"level":14,"moves":[0,0,0,0],"species":81},{"level":14,"moves":[0,0,0,0],"species":81}],"party_rom_address":3209576,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235900},{"battle_script_rom_address":2032811,"party":[{"level":16,"moves":[0,0,0,0],"species":81}],"party_rom_address":3209592,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235940},{"battle_script_rom_address":0,"party":[{"level":30,"moves":[0,0,0,0],"species":81}],"party_rom_address":3209600,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3235980},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":81}],"party_rom_address":3209608,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236020},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[0,0,0,0],"species":82}],"party_rom_address":3209616,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236060},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[0,0,0,0],"species":82}],"party_rom_address":3209624,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236100},{"battle_script_rom_address":2032952,"party":[{"level":16,"moves":[0,0,0,0],"species":81}],"party_rom_address":3209632,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236140},{"battle_script_rom_address":2032921,"party":[{"level":14,"moves":[0,0,0,0],"species":81},{"level":14,"moves":[0,0,0,0],"species":81},{"level":6,"moves":[0,0,0,0],"species":100}],"party_rom_address":3209640,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236180},{"battle_script_rom_address":0,"party":[{"level":28,"moves":[0,0,0,0],"species":81}],"party_rom_address":3209664,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236220},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":81}],"party_rom_address":3209672,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236260},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":82}],"party_rom_address":3209680,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236300},{"battle_script_rom_address":0,"party":[{"level":37,"moves":[0,0,0,0],"species":82}],"party_rom_address":3209688,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236340},{"battle_script_rom_address":2051475,"party":[{"level":17,"moves":[0,0,0,0],"species":84}],"party_rom_address":3209696,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236380},{"battle_script_rom_address":0,"party":[{"level":28,"moves":[0,0,0,0],"species":84}],"party_rom_address":3209704,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236420},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":84}],"party_rom_address":3209712,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236460},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":85}],"party_rom_address":3209720,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236500},{"battle_script_rom_address":0,"party":[{"level":37,"moves":[0,0,0,0],"species":85}],"party_rom_address":3209728,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236540},{"battle_script_rom_address":2051585,"party":[{"level":17,"moves":[0,0,0,0],"species":84}],"party_rom_address":3209736,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236580},{"battle_script_rom_address":0,"party":[{"level":28,"moves":[0,0,0,0],"species":84}],"party_rom_address":3209744,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236620},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":84}],"party_rom_address":3209752,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236660},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":85}],"party_rom_address":3209760,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236700},{"battle_script_rom_address":0,"party":[{"level":37,"moves":[0,0,0,0],"species":85}],"party_rom_address":3209768,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236740},{"battle_script_rom_address":2064615,"party":[{"level":33,"moves":[0,0,0,0],"species":120},{"level":33,"moves":[0,0,0,0],"species":120}],"party_rom_address":3209776,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236780},{"battle_script_rom_address":2333618,"party":[{"level":25,"moves":[0,0,0,0],"species":288},{"level":25,"moves":[0,0,0,0],"species":337}],"party_rom_address":3209792,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236820},{"battle_script_rom_address":2065332,"party":[{"level":35,"moves":[0,0,0,0],"species":120}],"party_rom_address":3209808,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236860},{"battle_script_rom_address":2064413,"party":[{"level":33,"moves":[0,0,0,0],"species":120},{"level":33,"moves":[0,0,0,0],"species":120}],"party_rom_address":3209816,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236900},{"battle_script_rom_address":2066978,"party":[{"level":26,"moves":[0,0,0,0],"species":309},{"level":34,"moves":[0,0,0,0],"species":120}],"party_rom_address":3209832,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236940},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[0,0,0,0],"species":120}],"party_rom_address":3209848,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3236980},{"battle_script_rom_address":0,"party":[{"level":42,"moves":[0,0,0,0],"species":120}],"party_rom_address":3209856,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237020},{"battle_script_rom_address":0,"party":[{"level":45,"moves":[0,0,0,0],"species":121}],"party_rom_address":3209864,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237060},{"battle_script_rom_address":0,"party":[{"level":48,"moves":[0,0,0,0],"species":121}],"party_rom_address":3209872,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237100},{"battle_script_rom_address":2064351,"party":[{"level":34,"moves":[0,0,0,0],"species":120}],"party_rom_address":3209880,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237140},{"battle_script_rom_address":2064646,"party":[{"level":26,"moves":[0,0,0,0],"species":309},{"level":34,"moves":[0,0,0,0],"species":120}],"party_rom_address":3209888,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237180},{"battle_script_rom_address":2067545,"party":[{"level":34,"moves":[0,0,0,0],"species":120}],"party_rom_address":3209904,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237220},{"battle_script_rom_address":2065442,"party":[{"level":35,"moves":[0,0,0,0],"species":120}],"party_rom_address":3209912,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237260},{"battle_script_rom_address":2067009,"party":[{"level":27,"moves":[0,0,0,0],"species":309},{"level":33,"moves":[0,0,0,0],"species":120}],"party_rom_address":3209920,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237300},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[0,0,0,0],"species":120}],"party_rom_address":3209936,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237340},{"battle_script_rom_address":0,"party":[{"level":42,"moves":[0,0,0,0],"species":120}],"party_rom_address":3209944,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237380},{"battle_script_rom_address":0,"party":[{"level":45,"moves":[0,0,0,0],"species":121}],"party_rom_address":3209952,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237420},{"battle_script_rom_address":0,"party":[{"level":48,"moves":[0,0,0,0],"species":121}],"party_rom_address":3209960,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237460},{"battle_script_rom_address":2286394,"party":[{"level":37,"moves":[0,0,0,0],"species":359},{"level":37,"moves":[0,0,0,0],"species":359}],"party_rom_address":3209968,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237500},{"battle_script_rom_address":0,"party":[{"level":41,"moves":[0,0,0,0],"species":359},{"level":41,"moves":[0,0,0,0],"species":359}],"party_rom_address":3209984,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237540},{"battle_script_rom_address":0,"party":[{"level":44,"moves":[0,0,0,0],"species":359},{"level":44,"moves":[0,0,0,0],"species":359}],"party_rom_address":3210000,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237580},{"battle_script_rom_address":0,"party":[{"level":46,"moves":[0,0,0,0],"species":395},{"level":46,"moves":[0,0,0,0],"species":359},{"level":46,"moves":[0,0,0,0],"species":359}],"party_rom_address":3210016,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237620},{"battle_script_rom_address":0,"party":[{"level":49,"moves":[0,0,0,0],"species":359},{"level":49,"moves":[0,0,0,0],"species":359},{"level":49,"moves":[0,0,0,0],"species":396}],"party_rom_address":3210040,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3237660},{"battle_script_rom_address":2068182,"party":[{"level":34,"moves":[225,29,116,52],"species":395}],"party_rom_address":3210064,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3237700},{"battle_script_rom_address":2053088,"party":[{"level":26,"moves":[0,0,0,0],"species":309}],"party_rom_address":3210080,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237740},{"battle_script_rom_address":2055395,"party":[{"level":25,"moves":[0,0,0,0],"species":309},{"level":25,"moves":[0,0,0,0],"species":369}],"party_rom_address":3210088,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237780},{"battle_script_rom_address":2055426,"party":[{"level":26,"moves":[0,0,0,0],"species":305}],"party_rom_address":3210104,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237820},{"battle_script_rom_address":2196092,"party":[{"level":27,"moves":[0,0,0,0],"species":84},{"level":27,"moves":[0,0,0,0],"species":227},{"level":27,"moves":[0,0,0,0],"species":369}],"party_rom_address":3210112,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237860},{"battle_script_rom_address":2196216,"party":[{"level":30,"moves":[0,0,0,0],"species":227}],"party_rom_address":3210136,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237900},{"battle_script_rom_address":2064118,"party":[{"level":33,"moves":[0,0,0,0],"species":369},{"level":33,"moves":[0,0,0,0],"species":178}],"party_rom_address":3210144,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237940},{"battle_script_rom_address":2196123,"party":[{"level":29,"moves":[0,0,0,0],"species":84},{"level":29,"moves":[0,0,0,0],"species":310}],"party_rom_address":3210160,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3237980},{"battle_script_rom_address":2059373,"party":[{"level":28,"moves":[0,0,0,0],"species":309},{"level":28,"moves":[0,0,0,0],"species":177}],"party_rom_address":3210176,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238020},{"battle_script_rom_address":2059404,"party":[{"level":29,"moves":[0,0,0,0],"species":358}],"party_rom_address":3210192,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238060},{"battle_script_rom_address":2556084,"party":[{"level":36,"moves":[0,0,0,0],"species":305},{"level":36,"moves":[0,0,0,0],"species":310},{"level":36,"moves":[0,0,0,0],"species":178}],"party_rom_address":3210200,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238100},{"battle_script_rom_address":2053119,"party":[{"level":25,"moves":[0,0,0,0],"species":304},{"level":25,"moves":[0,0,0,0],"species":305}],"party_rom_address":3210224,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238140},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[0,0,0,0],"species":177},{"level":32,"moves":[0,0,0,0],"species":358}],"party_rom_address":3210240,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238180},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[0,0,0,0],"species":177},{"level":35,"moves":[0,0,0,0],"species":359}],"party_rom_address":3210256,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238220},{"battle_script_rom_address":0,"party":[{"level":38,"moves":[0,0,0,0],"species":177},{"level":38,"moves":[0,0,0,0],"species":359}],"party_rom_address":3210272,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238260},{"battle_script_rom_address":0,"party":[{"level":41,"moves":[0,0,0,0],"species":359},{"level":41,"moves":[0,0,0,0],"species":178}],"party_rom_address":3210288,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238300},{"battle_script_rom_address":2068151,"party":[{"level":33,"moves":[0,0,0,0],"species":177},{"level":33,"moves":[0,0,0,0],"species":305}],"party_rom_address":3210304,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238340},{"battle_script_rom_address":2067981,"party":[{"level":34,"moves":[0,0,0,0],"species":369}],"party_rom_address":3210320,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238380},{"battle_script_rom_address":2055457,"party":[{"level":26,"moves":[0,0,0,0],"species":302}],"party_rom_address":3210328,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238420},{"battle_script_rom_address":2055488,"party":[{"level":25,"moves":[0,0,0,0],"species":302},{"level":25,"moves":[0,0,0,0],"species":109}],"party_rom_address":3210336,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238460},{"battle_script_rom_address":2329166,"party":[{"level":43,"moves":[29,89,0,0],"species":319},{"level":43,"moves":[85,89,0,0],"species":171}],"party_rom_address":3210352,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3238500},{"battle_script_rom_address":2335401,"party":[{"level":26,"moves":[0,0,0,0],"species":183}],"party_rom_address":3210384,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238540},{"battle_script_rom_address":2044895,"party":[{"level":17,"moves":[139,33,123,120],"species":109},{"level":17,"moves":[139,33,123,120],"species":109},{"level":17,"moves":[139,33,124,120],"species":109}],"party_rom_address":3210392,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3238580},{"battle_script_rom_address":2045005,"party":[{"level":18,"moves":[0,0,0,0],"species":109},{"level":18,"moves":[0,0,0,0],"species":302}],"party_rom_address":3210440,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238620},{"battle_script_rom_address":0,"party":[{"level":24,"moves":[139,33,124,120],"species":109},{"level":24,"moves":[139,33,124,0],"species":109},{"level":24,"moves":[139,33,124,120],"species":109},{"level":26,"moves":[33,124,0,0],"species":109}],"party_rom_address":3210456,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3238660},{"battle_script_rom_address":0,"party":[{"level":27,"moves":[139,33,124,120],"species":109},{"level":27,"moves":[139,33,124,120],"species":109},{"level":27,"moves":[139,33,124,0],"species":109},{"level":29,"moves":[33,124,0,0],"species":109}],"party_rom_address":3210520,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3238700},{"battle_script_rom_address":0,"party":[{"level":30,"moves":[139,33,124,0],"species":109},{"level":30,"moves":[139,33,124,0],"species":109},{"level":30,"moves":[139,33,124,0],"species":109},{"level":32,"moves":[33,124,0,0],"species":109}],"party_rom_address":3210584,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3238740},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[139,33,124,0],"species":109},{"level":33,"moves":[139,33,124,120],"species":109},{"level":33,"moves":[139,33,124,120],"species":109},{"level":35,"moves":[33,124,0,0],"species":110}],"party_rom_address":3210648,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3238780},{"battle_script_rom_address":2089310,"party":[{"level":13,"moves":[0,0,0,0],"species":356}],"party_rom_address":3210712,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238820},{"battle_script_rom_address":2089348,"party":[{"level":13,"moves":[0,0,0,0],"species":356}],"party_rom_address":3210720,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238860},{"battle_script_rom_address":2047164,"party":[{"level":18,"moves":[0,0,0,0],"species":356},{"level":18,"moves":[0,0,0,0],"species":335}],"party_rom_address":3210728,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238900},{"battle_script_rom_address":2550554,"party":[{"level":27,"moves":[0,0,0,0],"species":356}],"party_rom_address":3210744,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238940},{"battle_script_rom_address":2550616,"party":[{"level":27,"moves":[0,0,0,0],"species":307}],"party_rom_address":3210752,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3238980},{"battle_script_rom_address":0,"party":[{"level":26,"moves":[0,0,0,0],"species":356},{"level":26,"moves":[0,0,0,0],"species":335}],"party_rom_address":3210760,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239020},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[0,0,0,0],"species":356},{"level":29,"moves":[0,0,0,0],"species":335}],"party_rom_address":3210776,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239060},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[0,0,0,0],"species":357},{"level":32,"moves":[0,0,0,0],"species":336}],"party_rom_address":3210792,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239100},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[0,0,0,0],"species":357},{"level":35,"moves":[0,0,0,0],"species":336}],"party_rom_address":3210808,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239140},{"battle_script_rom_address":2044785,"party":[{"level":19,"moves":[52,33,222,241],"species":339}],"party_rom_address":3210824,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3239180},{"battle_script_rom_address":2059748,"party":[{"level":28,"moves":[0,0,0,0],"species":363},{"level":28,"moves":[0,0,0,0],"species":313}],"party_rom_address":3210840,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239220},{"battle_script_rom_address":2059779,"party":[{"level":30,"moves":[240,55,87,96],"species":385}],"party_rom_address":3210856,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3239260},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[52,33,222,241],"species":339}],"party_rom_address":3210872,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3239300},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[52,36,222,241],"species":339}],"party_rom_address":3210888,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3239340},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[73,72,64,241],"species":363},{"level":34,"moves":[53,36,222,241],"species":339}],"party_rom_address":3210904,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3239380},{"battle_script_rom_address":0,"party":[{"level":37,"moves":[73,202,76,241],"species":363},{"level":37,"moves":[53,36,89,241],"species":340}],"party_rom_address":3210936,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3239420},{"battle_script_rom_address":2027807,"party":[{"level":25,"moves":[0,0,0,0],"species":309},{"level":25,"moves":[0,0,0,0],"species":313}],"party_rom_address":3210968,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239460},{"battle_script_rom_address":2027838,"party":[{"level":26,"moves":[0,0,0,0],"species":183}],"party_rom_address":3210984,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239500},{"battle_script_rom_address":2028390,"party":[{"level":26,"moves":[0,0,0,0],"species":313}],"party_rom_address":3210992,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239540},{"battle_script_rom_address":2028794,"party":[{"level":25,"moves":[0,0,0,0],"species":309},{"level":25,"moves":[0,0,0,0],"species":118}],"party_rom_address":3211000,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239580},{"battle_script_rom_address":2028825,"party":[{"level":26,"moves":[0,0,0,0],"species":118}],"party_rom_address":3211016,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239620},{"battle_script_rom_address":2029012,"party":[{"level":25,"moves":[0,0,0,0],"species":116},{"level":25,"moves":[0,0,0,0],"species":183}],"party_rom_address":3211024,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239660},{"battle_script_rom_address":2029043,"party":[{"level":26,"moves":[0,0,0,0],"species":118}],"party_rom_address":3211040,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239700},{"battle_script_rom_address":2029980,"party":[{"level":24,"moves":[0,0,0,0],"species":118},{"level":24,"moves":[0,0,0,0],"species":309},{"level":24,"moves":[0,0,0,0],"species":118}],"party_rom_address":3211048,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239740},{"battle_script_rom_address":2063273,"party":[{"level":34,"moves":[0,0,0,0],"species":313}],"party_rom_address":3211072,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239780},{"battle_script_rom_address":2063383,"party":[{"level":34,"moves":[0,0,0,0],"species":183}],"party_rom_address":3211080,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239820},{"battle_script_rom_address":2063884,"party":[{"level":34,"moves":[0,0,0,0],"species":325}],"party_rom_address":3211088,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239860},{"battle_script_rom_address":2063915,"party":[{"level":34,"moves":[0,0,0,0],"species":119}],"party_rom_address":3211096,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239900},{"battle_script_rom_address":2064258,"party":[{"level":33,"moves":[0,0,0,0],"species":183},{"level":33,"moves":[0,0,0,0],"species":341}],"party_rom_address":3211104,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239940},{"battle_script_rom_address":2064289,"party":[{"level":34,"moves":[0,0,0,0],"species":118}],"party_rom_address":3211120,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3239980},{"battle_script_rom_address":2067260,"party":[{"level":33,"moves":[0,0,0,0],"species":118},{"level":33,"moves":[0,0,0,0],"species":341}],"party_rom_address":3211128,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240020},{"battle_script_rom_address":2067421,"party":[{"level":34,"moves":[0,0,0,0],"species":325}],"party_rom_address":3211144,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240060},{"battle_script_rom_address":2067452,"party":[{"level":34,"moves":[0,0,0,0],"species":119}],"party_rom_address":3211152,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240100},{"battle_script_rom_address":2067639,"party":[{"level":34,"moves":[0,0,0,0],"species":184}],"party_rom_address":3211160,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240140},{"battle_script_rom_address":2064382,"party":[{"level":33,"moves":[0,0,0,0],"species":325},{"level":33,"moves":[0,0,0,0],"species":325}],"party_rom_address":3211168,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240180},{"battle_script_rom_address":2067888,"party":[{"level":34,"moves":[0,0,0,0],"species":119}],"party_rom_address":3211184,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240220},{"battle_script_rom_address":2067919,"party":[{"level":33,"moves":[0,0,0,0],"species":116},{"level":33,"moves":[0,0,0,0],"species":117}],"party_rom_address":3211192,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240260},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":171},{"level":34,"moves":[0,0,0,0],"species":310}],"party_rom_address":3211208,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240300},{"battle_script_rom_address":2068120,"party":[{"level":33,"moves":[0,0,0,0],"species":325},{"level":33,"moves":[0,0,0,0],"species":325}],"party_rom_address":3211224,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240340},{"battle_script_rom_address":2065676,"party":[{"level":35,"moves":[0,0,0,0],"species":119}],"party_rom_address":3211240,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240380},{"battle_script_rom_address":0,"party":[{"level":38,"moves":[0,0,0,0],"species":313}],"party_rom_address":3211248,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240420},{"battle_script_rom_address":0,"party":[{"level":41,"moves":[0,0,0,0],"species":313}],"party_rom_address":3211256,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240460},{"battle_script_rom_address":0,"party":[{"level":43,"moves":[0,0,0,0],"species":120},{"level":43,"moves":[0,0,0,0],"species":313}],"party_rom_address":3211264,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240500},{"battle_script_rom_address":0,"party":[{"level":45,"moves":[0,0,0,0],"species":325},{"level":45,"moves":[0,0,0,0],"species":313},{"level":45,"moves":[0,0,0,0],"species":121}],"party_rom_address":3211280,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240540},{"battle_script_rom_address":2040526,"party":[{"level":22,"moves":[91,28,40,163],"species":27},{"level":22,"moves":[229,189,60,61],"species":318}],"party_rom_address":3211304,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3240580},{"battle_script_rom_address":2040588,"party":[{"level":22,"moves":[28,40,163,91],"species":27},{"level":22,"moves":[205,61,39,111],"species":183}],"party_rom_address":3211336,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3240620},{"battle_script_rom_address":2043989,"party":[{"level":17,"moves":[0,0,0,0],"species":304},{"level":17,"moves":[0,0,0,0],"species":296}],"party_rom_address":3211368,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240660},{"battle_script_rom_address":2046056,"party":[{"level":18,"moves":[0,0,0,0],"species":183},{"level":18,"moves":[0,0,0,0],"species":296}],"party_rom_address":3211384,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240700},{"battle_script_rom_address":2549863,"party":[{"level":23,"moves":[0,0,0,0],"species":315},{"level":23,"moves":[0,0,0,0],"species":358}],"party_rom_address":3211400,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240740},{"battle_script_rom_address":2303728,"party":[{"level":19,"moves":[0,0,0,0],"species":306},{"level":19,"moves":[0,0,0,0],"species":43},{"level":19,"moves":[0,0,0,0],"species":358}],"party_rom_address":3211416,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240780},{"battle_script_rom_address":2309489,"party":[{"level":32,"moves":[194,219,68,243],"species":202}],"party_rom_address":3211440,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3240820},{"battle_script_rom_address":2040760,"party":[{"level":17,"moves":[0,0,0,0],"species":306},{"level":17,"moves":[0,0,0,0],"species":183}],"party_rom_address":3211456,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240860},{"battle_script_rom_address":0,"party":[{"level":25,"moves":[0,0,0,0],"species":306},{"level":25,"moves":[0,0,0,0],"species":44},{"level":25,"moves":[0,0,0,0],"species":358}],"party_rom_address":3211472,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240900},{"battle_script_rom_address":0,"party":[{"level":28,"moves":[0,0,0,0],"species":307},{"level":28,"moves":[0,0,0,0],"species":44},{"level":28,"moves":[0,0,0,0],"species":358}],"party_rom_address":3211496,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240940},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":307},{"level":31,"moves":[0,0,0,0],"species":44},{"level":31,"moves":[0,0,0,0],"species":358}],"party_rom_address":3211520,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3240980},{"battle_script_rom_address":0,"party":[{"level":40,"moves":[0,0,0,0],"species":307},{"level":40,"moves":[0,0,0,0],"species":45},{"level":40,"moves":[0,0,0,0],"species":359}],"party_rom_address":3211544,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241020},{"battle_script_rom_address":0,"party":[{"level":15,"moves":[0,0,0,0],"species":353},{"level":15,"moves":[0,0,0,0],"species":354}],"party_rom_address":3211568,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241060},{"battle_script_rom_address":0,"party":[{"level":27,"moves":[0,0,0,0],"species":353},{"level":27,"moves":[0,0,0,0],"species":354}],"party_rom_address":3211584,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241100},{"battle_script_rom_address":0,"party":[{"level":6,"moves":[0,0,0,0],"species":298},{"level":6,"moves":[0,0,0,0],"species":295}],"party_rom_address":3211600,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241140},{"battle_script_rom_address":0,"party":[{"level":26,"moves":[0,0,0,0],"species":292},{"level":26,"moves":[0,0,0,0],"species":294}],"party_rom_address":3211616,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241180},{"battle_script_rom_address":0,"party":[{"level":9,"moves":[0,0,0,0],"species":353},{"level":9,"moves":[0,0,0,0],"species":354}],"party_rom_address":3211632,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241220},{"battle_script_rom_address":0,"party":[{"level":10,"moves":[101,50,0,0],"species":361},{"level":10,"moves":[71,73,0,0],"species":306}],"party_rom_address":3211648,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3241260},{"battle_script_rom_address":0,"party":[{"level":30,"moves":[0,0,0,0],"species":353},{"level":30,"moves":[0,0,0,0],"species":354}],"party_rom_address":3211680,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241300},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[209,12,57,14],"species":353},{"level":33,"moves":[209,12,204,14],"species":354}],"party_rom_address":3211696,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3241340},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[87,12,57,14],"species":353},{"level":36,"moves":[87,12,204,14],"species":354}],"party_rom_address":3211728,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3241380},{"battle_script_rom_address":2030011,"party":[{"level":12,"moves":[0,0,0,0],"species":309},{"level":12,"moves":[0,0,0,0],"species":66}],"party_rom_address":3211760,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241420},{"battle_script_rom_address":2030042,"party":[{"level":13,"moves":[0,0,0,0],"species":309}],"party_rom_address":3211776,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241460},{"battle_script_rom_address":2063946,"party":[{"level":33,"moves":[0,0,0,0],"species":309},{"level":33,"moves":[0,0,0,0],"species":67}],"party_rom_address":3211784,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241500},{"battle_script_rom_address":2537258,"party":[{"level":11,"moves":[0,0,0,0],"species":309},{"level":11,"moves":[0,0,0,0],"species":66},{"level":11,"moves":[0,0,0,0],"species":72}],"party_rom_address":3211800,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241540},{"battle_script_rom_address":2353667,"party":[{"level":44,"moves":[0,0,0,0],"species":73},{"level":44,"moves":[0,0,0,0],"species":67}],"party_rom_address":3211824,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241580},{"battle_script_rom_address":2353698,"party":[{"level":43,"moves":[0,0,0,0],"species":66},{"level":43,"moves":[0,0,0,0],"species":310},{"level":43,"moves":[0,0,0,0],"species":67}],"party_rom_address":3211840,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241620},{"battle_script_rom_address":2334525,"party":[{"level":25,"moves":[0,0,0,0],"species":341},{"level":25,"moves":[0,0,0,0],"species":67}],"party_rom_address":3211864,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241660},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[0,0,0,0],"species":309},{"level":36,"moves":[0,0,0,0],"species":72},{"level":36,"moves":[0,0,0,0],"species":67}],"party_rom_address":3211880,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241700},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[0,0,0,0],"species":310},{"level":39,"moves":[0,0,0,0],"species":72},{"level":39,"moves":[0,0,0,0],"species":67}],"party_rom_address":3211904,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241740},{"battle_script_rom_address":0,"party":[{"level":42,"moves":[0,0,0,0],"species":310},{"level":42,"moves":[0,0,0,0],"species":72},{"level":42,"moves":[0,0,0,0],"species":67}],"party_rom_address":3211928,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241780},{"battle_script_rom_address":0,"party":[{"level":45,"moves":[0,0,0,0],"species":310},{"level":45,"moves":[0,0,0,0],"species":67},{"level":45,"moves":[0,0,0,0],"species":73}],"party_rom_address":3211952,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241820},{"battle_script_rom_address":2097615,"party":[{"level":23,"moves":[0,0,0,0],"species":339}],"party_rom_address":3211976,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241860},{"battle_script_rom_address":2259604,"party":[{"level":39,"moves":[175,96,216,213],"species":328},{"level":39,"moves":[175,96,216,213],"species":328}],"party_rom_address":3211984,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3241900},{"battle_script_rom_address":2062680,"party":[{"level":27,"moves":[0,0,0,0],"species":376}],"party_rom_address":3212016,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3241940},{"battle_script_rom_address":2062649,"party":[{"level":31,"moves":[92,87,120,188],"species":109}],"party_rom_address":3212024,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3241980},{"battle_script_rom_address":2062618,"party":[{"level":31,"moves":[241,55,53,76],"species":385}],"party_rom_address":3212040,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3242020},{"battle_script_rom_address":2064149,"party":[{"level":33,"moves":[0,0,0,0],"species":338},{"level":33,"moves":[0,0,0,0],"species":68}],"party_rom_address":3212056,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242060},{"battle_script_rom_address":2068337,"party":[{"level":33,"moves":[0,0,0,0],"species":67},{"level":33,"moves":[0,0,0,0],"species":341}],"party_rom_address":3212072,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242100},{"battle_script_rom_address":2068306,"party":[{"level":34,"moves":[44,46,86,85],"species":338}],"party_rom_address":3212088,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3242140},{"battle_script_rom_address":2068275,"party":[{"level":33,"moves":[0,0,0,0],"species":356},{"level":33,"moves":[0,0,0,0],"species":336}],"party_rom_address":3212104,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242180},{"battle_script_rom_address":2068244,"party":[{"level":34,"moves":[0,0,0,0],"species":313}],"party_rom_address":3212120,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242220},{"battle_script_rom_address":2068043,"party":[{"level":33,"moves":[0,0,0,0],"species":170},{"level":33,"moves":[0,0,0,0],"species":336}],"party_rom_address":3212128,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242260},{"battle_script_rom_address":2032608,"party":[{"level":14,"moves":[0,0,0,0],"species":296},{"level":14,"moves":[0,0,0,0],"species":299}],"party_rom_address":3212144,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242300},{"battle_script_rom_address":2047274,"party":[{"level":18,"moves":[0,0,0,0],"species":380},{"level":18,"moves":[0,0,0,0],"species":379}],"party_rom_address":3212160,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242340},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[0,0,0,0],"species":340},{"level":38,"moves":[0,0,0,0],"species":287},{"level":40,"moves":[0,0,0,0],"species":42}],"party_rom_address":3212176,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242380},{"battle_script_rom_address":0,"party":[{"level":26,"moves":[0,0,0,0],"species":296},{"level":26,"moves":[0,0,0,0],"species":299}],"party_rom_address":3212200,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242420},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[0,0,0,0],"species":296},{"level":29,"moves":[0,0,0,0],"species":299}],"party_rom_address":3212216,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242460},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[0,0,0,0],"species":296},{"level":32,"moves":[0,0,0,0],"species":299}],"party_rom_address":3212232,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242500},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[0,0,0,0],"species":297},{"level":35,"moves":[0,0,0,0],"species":300}],"party_rom_address":3212248,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242540},{"battle_script_rom_address":2326117,"party":[{"level":44,"moves":[76,219,225,93],"species":359},{"level":43,"moves":[47,18,204,185],"species":316},{"level":44,"moves":[89,73,202,92],"species":363},{"level":41,"moves":[48,85,161,103],"species":82},{"level":45,"moves":[104,91,94,248],"species":394}],"party_rom_address":3212264,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3242580},{"battle_script_rom_address":2019962,"party":[{"level":5,"moves":[0,0,0,0],"species":277}],"party_rom_address":3212344,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242620},{"battle_script_rom_address":2033952,"party":[{"level":18,"moves":[0,0,0,0],"species":218},{"level":18,"moves":[0,0,0,0],"species":309},{"level":20,"moves":[0,0,0,0],"species":278}],"party_rom_address":3212352,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242660},{"battle_script_rom_address":2054543,"party":[{"level":29,"moves":[0,0,0,0],"species":218},{"level":29,"moves":[0,0,0,0],"species":310},{"level":31,"moves":[0,0,0,0],"species":278}],"party_rom_address":3212376,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242700},{"battle_script_rom_address":2019906,"party":[{"level":5,"moves":[0,0,0,0],"species":280}],"party_rom_address":3212400,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242740},{"battle_script_rom_address":2033896,"party":[{"level":18,"moves":[0,0,0,0],"species":309},{"level":18,"moves":[0,0,0,0],"species":296},{"level":20,"moves":[0,0,0,0],"species":281}],"party_rom_address":3212408,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242780},{"battle_script_rom_address":2054487,"party":[{"level":29,"moves":[0,0,0,0],"species":310},{"level":29,"moves":[0,0,0,0],"species":296},{"level":31,"moves":[0,0,0,0],"species":281}],"party_rom_address":3212432,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242820},{"battle_script_rom_address":2019934,"party":[{"level":5,"moves":[0,0,0,0],"species":283}],"party_rom_address":3212456,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242860},{"battle_script_rom_address":2033924,"party":[{"level":18,"moves":[0,0,0,0],"species":296},{"level":18,"moves":[0,0,0,0],"species":218},{"level":20,"moves":[0,0,0,0],"species":284}],"party_rom_address":3212464,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242900},{"battle_script_rom_address":2054515,"party":[{"level":29,"moves":[0,0,0,0],"species":296},{"level":29,"moves":[0,0,0,0],"species":218},{"level":31,"moves":[0,0,0,0],"species":284}],"party_rom_address":3212488,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242940},{"battle_script_rom_address":2019878,"party":[{"level":5,"moves":[0,0,0,0],"species":277}],"party_rom_address":3212512,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3242980},{"battle_script_rom_address":2033794,"party":[{"level":18,"moves":[0,0,0,0],"species":309},{"level":18,"moves":[0,0,0,0],"species":218},{"level":20,"moves":[0,0,0,0],"species":278}],"party_rom_address":3212520,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243020},{"battle_script_rom_address":2054385,"party":[{"level":29,"moves":[0,0,0,0],"species":218},{"level":29,"moves":[0,0,0,0],"species":296},{"level":31,"moves":[0,0,0,0],"species":278}],"party_rom_address":3212544,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243060},{"battle_script_rom_address":2019822,"party":[{"level":5,"moves":[0,0,0,0],"species":280}],"party_rom_address":3212568,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243100},{"battle_script_rom_address":2033738,"party":[{"level":18,"moves":[0,0,0,0],"species":309},{"level":18,"moves":[0,0,0,0],"species":296},{"level":20,"moves":[0,0,0,0],"species":281}],"party_rom_address":3212576,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243140},{"battle_script_rom_address":2054329,"party":[{"level":29,"moves":[0,0,0,0],"species":310},{"level":29,"moves":[0,0,0,0],"species":296},{"level":31,"moves":[0,0,0,0],"species":281}],"party_rom_address":3212600,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243180},{"battle_script_rom_address":2019850,"party":[{"level":5,"moves":[0,0,0,0],"species":283}],"party_rom_address":3212624,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243220},{"battle_script_rom_address":2033766,"party":[{"level":18,"moves":[0,0,0,0],"species":296},{"level":18,"moves":[0,0,0,0],"species":218},{"level":20,"moves":[0,0,0,0],"species":284}],"party_rom_address":3212632,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243260},{"battle_script_rom_address":2054357,"party":[{"level":29,"moves":[0,0,0,0],"species":296},{"level":29,"moves":[0,0,0,0],"species":218},{"level":31,"moves":[0,0,0,0],"species":284}],"party_rom_address":3212656,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243300},{"battle_script_rom_address":2051255,"party":[{"level":11,"moves":[0,0,0,0],"species":370},{"level":11,"moves":[0,0,0,0],"species":288},{"level":11,"moves":[0,0,0,0],"species":382},{"level":11,"moves":[0,0,0,0],"species":286},{"level":11,"moves":[0,0,0,0],"species":304},{"level":11,"moves":[0,0,0,0],"species":335}],"party_rom_address":3212680,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243340},{"battle_script_rom_address":2062711,"party":[{"level":27,"moves":[0,0,0,0],"species":127}],"party_rom_address":3212728,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243380},{"battle_script_rom_address":2328056,"party":[{"level":43,"moves":[153,115,113,94],"species":348},{"level":43,"moves":[153,115,113,247],"species":349}],"party_rom_address":3212736,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3243420},{"battle_script_rom_address":0,"party":[{"level":22,"moves":[0,0,0,0],"species":371},{"level":22,"moves":[0,0,0,0],"species":289},{"level":22,"moves":[0,0,0,0],"species":382},{"level":22,"moves":[0,0,0,0],"species":287},{"level":22,"moves":[0,0,0,0],"species":305},{"level":22,"moves":[0,0,0,0],"species":335}],"party_rom_address":3212768,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243460},{"battle_script_rom_address":0,"party":[{"level":25,"moves":[0,0,0,0],"species":371},{"level":25,"moves":[0,0,0,0],"species":289},{"level":25,"moves":[0,0,0,0],"species":382},{"level":25,"moves":[0,0,0,0],"species":287},{"level":25,"moves":[0,0,0,0],"species":305},{"level":25,"moves":[0,0,0,0],"species":336}],"party_rom_address":3212816,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243500},{"battle_script_rom_address":0,"party":[{"level":28,"moves":[0,0,0,0],"species":371},{"level":28,"moves":[0,0,0,0],"species":289},{"level":28,"moves":[0,0,0,0],"species":382},{"level":28,"moves":[0,0,0,0],"species":287},{"level":28,"moves":[0,0,0,0],"species":305},{"level":28,"moves":[0,0,0,0],"species":336}],"party_rom_address":3212864,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243540},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":371},{"level":31,"moves":[0,0,0,0],"species":289},{"level":31,"moves":[0,0,0,0],"species":383},{"level":31,"moves":[0,0,0,0],"species":287},{"level":31,"moves":[0,0,0,0],"species":305},{"level":31,"moves":[0,0,0,0],"species":336}],"party_rom_address":3212912,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243580},{"battle_script_rom_address":2051365,"party":[{"level":11,"moves":[0,0,0,0],"species":309},{"level":11,"moves":[0,0,0,0],"species":306},{"level":11,"moves":[0,0,0,0],"species":183},{"level":11,"moves":[0,0,0,0],"species":363},{"level":11,"moves":[0,0,0,0],"species":315},{"level":11,"moves":[0,0,0,0],"species":118}],"party_rom_address":3212960,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243620},{"battle_script_rom_address":2328087,"party":[{"level":43,"moves":[0,0,0,0],"species":322},{"level":43,"moves":[0,0,0,0],"species":376}],"party_rom_address":3213008,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243660},{"battle_script_rom_address":2335432,"party":[{"level":26,"moves":[0,0,0,0],"species":28}],"party_rom_address":3213024,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243700},{"battle_script_rom_address":0,"party":[{"level":22,"moves":[0,0,0,0],"species":309},{"level":22,"moves":[0,0,0,0],"species":306},{"level":22,"moves":[0,0,0,0],"species":183},{"level":22,"moves":[0,0,0,0],"species":363},{"level":22,"moves":[0,0,0,0],"species":315},{"level":22,"moves":[0,0,0,0],"species":118}],"party_rom_address":3213032,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243740},{"battle_script_rom_address":0,"party":[{"level":25,"moves":[0,0,0,0],"species":310},{"level":25,"moves":[0,0,0,0],"species":307},{"level":25,"moves":[0,0,0,0],"species":183},{"level":25,"moves":[0,0,0,0],"species":363},{"level":25,"moves":[0,0,0,0],"species":316},{"level":25,"moves":[0,0,0,0],"species":118}],"party_rom_address":3213080,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243780},{"battle_script_rom_address":0,"party":[{"level":28,"moves":[0,0,0,0],"species":310},{"level":28,"moves":[0,0,0,0],"species":307},{"level":28,"moves":[0,0,0,0],"species":183},{"level":28,"moves":[0,0,0,0],"species":363},{"level":28,"moves":[0,0,0,0],"species":316},{"level":28,"moves":[0,0,0,0],"species":118}],"party_rom_address":3213128,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243820},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":310},{"level":31,"moves":[0,0,0,0],"species":307},{"level":31,"moves":[0,0,0,0],"species":184},{"level":31,"moves":[0,0,0,0],"species":363},{"level":31,"moves":[0,0,0,0],"species":316},{"level":31,"moves":[0,0,0,0],"species":119}],"party_rom_address":3213176,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243860},{"battle_script_rom_address":2055175,"party":[{"level":27,"moves":[0,0,0,0],"species":307}],"party_rom_address":3213224,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243900},{"battle_script_rom_address":2059514,"party":[{"level":28,"moves":[0,0,0,0],"species":298},{"level":28,"moves":[0,0,0,0],"species":299},{"level":28,"moves":[0,0,0,0],"species":296}],"party_rom_address":3213232,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243940},{"battle_script_rom_address":2556115,"party":[{"level":39,"moves":[0,0,0,0],"species":345}],"party_rom_address":3213256,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3243980},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":307}],"party_rom_address":3213264,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244020},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":307}],"party_rom_address":3213272,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244060},{"battle_script_rom_address":0,"party":[{"level":37,"moves":[0,0,0,0],"species":307}],"party_rom_address":3213280,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244100},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[0,0,0,0],"species":317},{"level":39,"moves":[0,0,0,0],"species":307}],"party_rom_address":3213288,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244140},{"battle_script_rom_address":2055285,"party":[{"level":26,"moves":[0,0,0,0],"species":44},{"level":26,"moves":[0,0,0,0],"species":363}],"party_rom_address":3213304,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244180},{"battle_script_rom_address":2059545,"party":[{"level":28,"moves":[0,0,0,0],"species":295},{"level":28,"moves":[0,0,0,0],"species":296},{"level":28,"moves":[0,0,0,0],"species":299}],"party_rom_address":3213320,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244220},{"battle_script_rom_address":2556053,"party":[{"level":38,"moves":[0,0,0,0],"species":358},{"level":38,"moves":[0,0,0,0],"species":363}],"party_rom_address":3213344,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244260},{"battle_script_rom_address":0,"party":[{"level":30,"moves":[0,0,0,0],"species":44},{"level":30,"moves":[0,0,0,0],"species":363}],"party_rom_address":3213360,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244300},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":44},{"level":33,"moves":[0,0,0,0],"species":363}],"party_rom_address":3213376,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244340},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[0,0,0,0],"species":44},{"level":36,"moves":[0,0,0,0],"species":363}],"party_rom_address":3213392,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244380},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[0,0,0,0],"species":182},{"level":39,"moves":[0,0,0,0],"species":363}],"party_rom_address":3213408,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244420},{"battle_script_rom_address":2303942,"party":[{"level":21,"moves":[0,0,0,0],"species":81}],"party_rom_address":3213424,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244460},{"battle_script_rom_address":2320797,"party":[{"level":35,"moves":[0,0,0,0],"species":287},{"level":35,"moves":[0,0,0,0],"species":42}],"party_rom_address":3213432,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244500},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":313},{"level":31,"moves":[0,0,0,0],"species":41}],"party_rom_address":3213448,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244540},{"battle_script_rom_address":2311225,"party":[{"level":30,"moves":[0,0,0,0],"species":313},{"level":30,"moves":[0,0,0,0],"species":41}],"party_rom_address":3213464,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244580},{"battle_script_rom_address":2303629,"party":[{"level":22,"moves":[0,0,0,0],"species":286},{"level":22,"moves":[0,0,0,0],"species":339}],"party_rom_address":3213480,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244620},{"battle_script_rom_address":2182057,"party":[{"level":8,"moves":[0,0,0,0],"species":74},{"level":8,"moves":[0,0,0,0],"species":74}],"party_rom_address":3213496,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244660},{"battle_script_rom_address":2089386,"party":[{"level":13,"moves":[0,0,0,0],"species":66}],"party_rom_address":3213512,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244700},{"battle_script_rom_address":2089462,"party":[{"level":13,"moves":[0,0,0,0],"species":356}],"party_rom_address":3213520,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244740},{"battle_script_rom_address":2089424,"party":[{"level":13,"moves":[0,0,0,0],"species":335}],"party_rom_address":3213528,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244780},{"battle_script_rom_address":2238458,"party":[{"level":36,"moves":[0,0,0,0],"species":356}],"party_rom_address":3213536,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244820},{"battle_script_rom_address":2064320,"party":[{"level":34,"moves":[0,0,0,0],"species":330}],"party_rom_address":3213544,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244860},{"battle_script_rom_address":2064801,"party":[{"level":32,"moves":[87,86,98,0],"species":338},{"level":32,"moves":[57,168,0,0],"species":289}],"party_rom_address":3213552,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3244900},{"battle_script_rom_address":2065645,"party":[{"level":35,"moves":[0,0,0,0],"species":73}],"party_rom_address":3213584,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244940},{"battle_script_rom_address":2297708,"party":[{"level":20,"moves":[0,0,0,0],"species":41}],"party_rom_address":3213592,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3244980},{"battle_script_rom_address":2067102,"party":[{"level":34,"moves":[0,0,0,0],"species":331}],"party_rom_address":3213600,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245020},{"battle_script_rom_address":0,"party":[{"level":37,"moves":[0,0,0,0],"species":203}],"party_rom_address":3213608,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245060},{"battle_script_rom_address":2238489,"party":[{"level":36,"moves":[0,0,0,0],"species":351}],"party_rom_address":3213616,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245100},{"battle_script_rom_address":2238613,"party":[{"level":36,"moves":[0,0,0,0],"species":64}],"party_rom_address":3213624,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245140},{"battle_script_rom_address":2238551,"party":[{"level":36,"moves":[0,0,0,0],"species":203}],"party_rom_address":3213632,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245180},{"battle_script_rom_address":2238582,"party":[{"level":36,"moves":[0,0,0,0],"species":202}],"party_rom_address":3213640,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245220},{"battle_script_rom_address":2248375,"party":[{"level":31,"moves":[0,0,0,0],"species":41},{"level":31,"moves":[0,0,0,0],"species":286}],"party_rom_address":3213648,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245260},{"battle_script_rom_address":2248437,"party":[{"level":32,"moves":[0,0,0,0],"species":318}],"party_rom_address":3213664,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245300},{"battle_script_rom_address":2251538,"party":[{"level":32,"moves":[0,0,0,0],"species":41}],"party_rom_address":3213672,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245340},{"battle_script_rom_address":2251588,"party":[{"level":32,"moves":[0,0,0,0],"species":287}],"party_rom_address":3213680,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245380},{"battle_script_rom_address":2251638,"party":[{"level":32,"moves":[0,0,0,0],"species":318}],"party_rom_address":3213688,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245420},{"battle_script_rom_address":2238520,"party":[{"level":36,"moves":[0,0,0,0],"species":177}],"party_rom_address":3213696,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245460},{"battle_script_rom_address":1973930,"party":[{"level":13,"moves":[0,0,0,0],"species":295},{"level":15,"moves":[0,0,0,0],"species":280}],"party_rom_address":3213704,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245500},{"battle_script_rom_address":1973992,"party":[{"level":13,"moves":[0,0,0,0],"species":309},{"level":15,"moves":[0,0,0,0],"species":277}],"party_rom_address":3213720,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245540},{"battle_script_rom_address":2067732,"party":[{"level":33,"moves":[0,0,0,0],"species":305},{"level":33,"moves":[0,0,0,0],"species":307}],"party_rom_address":3213736,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245580},{"battle_script_rom_address":2063684,"party":[{"level":34,"moves":[0,0,0,0],"species":120}],"party_rom_address":3213752,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245620},{"battle_script_rom_address":2564748,"party":[{"level":27,"moves":[0,0,0,0],"species":41},{"level":27,"moves":[0,0,0,0],"species":286}],"party_rom_address":3213760,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245660},{"battle_script_rom_address":2297677,"party":[{"level":18,"moves":[0,0,0,0],"species":339},{"level":20,"moves":[0,0,0,0],"species":286},{"level":22,"moves":[0,0,0,0],"species":339},{"level":22,"moves":[0,0,0,0],"species":41}],"party_rom_address":3213776,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245700},{"battle_script_rom_address":2067794,"party":[{"level":33,"moves":[0,0,0,0],"species":317},{"level":33,"moves":[0,0,0,0],"species":371}],"party_rom_address":3213808,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245740},{"battle_script_rom_address":1973961,"party":[{"level":13,"moves":[0,0,0,0],"species":218},{"level":15,"moves":[0,0,0,0],"species":283}],"party_rom_address":3213824,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245780},{"battle_script_rom_address":1973706,"party":[{"level":13,"moves":[0,0,0,0],"species":309},{"level":15,"moves":[0,0,0,0],"species":277}],"party_rom_address":3213840,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245820},{"battle_script_rom_address":2344934,"party":[{"level":37,"moves":[0,0,0,0],"species":287},{"level":38,"moves":[0,0,0,0],"species":169},{"level":39,"moves":[0,0,0,0],"species":340}],"party_rom_address":3213856,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245860},{"battle_script_rom_address":2297108,"party":[{"level":24,"moves":[0,0,0,0],"species":287},{"level":24,"moves":[0,0,0,0],"species":41},{"level":25,"moves":[0,0,0,0],"species":340}],"party_rom_address":3213880,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245900},{"battle_script_rom_address":2019098,"party":[{"level":4,"moves":[0,0,0,0],"species":288},{"level":4,"moves":[0,0,0,0],"species":306}],"party_rom_address":3213904,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245940},{"battle_script_rom_address":2023889,"party":[{"level":6,"moves":[0,0,0,0],"species":295},{"level":6,"moves":[0,0,0,0],"species":306}],"party_rom_address":3213920,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3245980},{"battle_script_rom_address":2048559,"party":[{"level":9,"moves":[0,0,0,0],"species":183}],"party_rom_address":3213936,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246020},{"battle_script_rom_address":2040124,"party":[{"level":15,"moves":[0,0,0,0],"species":183},{"level":15,"moves":[0,0,0,0],"species":306},{"level":15,"moves":[0,0,0,0],"species":339}],"party_rom_address":3213944,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246060},{"battle_script_rom_address":0,"party":[{"level":26,"moves":[0,0,0,0],"species":296},{"level":26,"moves":[0,0,0,0],"species":306}],"party_rom_address":3213968,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246100},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[0,0,0,0],"species":296},{"level":29,"moves":[0,0,0,0],"species":307}],"party_rom_address":3213984,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246140},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[0,0,0,0],"species":296},{"level":32,"moves":[0,0,0,0],"species":307}],"party_rom_address":3214000,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246180},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":305},{"level":34,"moves":[0,0,0,0],"species":296},{"level":34,"moves":[0,0,0,0],"species":307}],"party_rom_address":3214016,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246220},{"battle_script_rom_address":2546588,"party":[{"level":16,"moves":[0,0,0,0],"species":43}],"party_rom_address":3214040,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246260},{"battle_script_rom_address":2546650,"party":[{"level":14,"moves":[0,0,0,0],"species":315},{"level":14,"moves":[0,0,0,0],"species":306},{"level":14,"moves":[0,0,0,0],"species":183}],"party_rom_address":3214048,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246300},{"battle_script_rom_address":2259356,"party":[{"level":40,"moves":[0,0,0,0],"species":325}],"party_rom_address":3214072,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246340},{"battle_script_rom_address":2259387,"party":[{"level":39,"moves":[0,0,0,0],"species":118},{"level":39,"moves":[0,0,0,0],"species":313}],"party_rom_address":3214080,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246380},{"battle_script_rom_address":2019067,"party":[{"level":4,"moves":[0,0,0,0],"species":290},{"level":4,"moves":[0,0,0,0],"species":290}],"party_rom_address":3214096,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246420},{"battle_script_rom_address":2294060,"party":[{"level":3,"moves":[0,0,0,0],"species":290},{"level":3,"moves":[0,0,0,0],"species":290},{"level":3,"moves":[0,0,0,0],"species":290},{"level":3,"moves":[0,0,0,0],"species":290}],"party_rom_address":3214112,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246460},{"battle_script_rom_address":2048311,"party":[{"level":8,"moves":[0,0,0,0],"species":290},{"level":8,"moves":[0,0,0,0],"species":301}],"party_rom_address":3214144,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246500},{"battle_script_rom_address":2055082,"party":[{"level":28,"moves":[0,0,0,0],"species":301},{"level":28,"moves":[0,0,0,0],"species":302}],"party_rom_address":3214160,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246540},{"battle_script_rom_address":2055113,"party":[{"level":25,"moves":[0,0,0,0],"species":386},{"level":25,"moves":[0,0,0,0],"species":387}],"party_rom_address":3214176,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246580},{"battle_script_rom_address":2055144,"party":[{"level":25,"moves":[0,0,0,0],"species":302}],"party_rom_address":3214192,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246620},{"battle_script_rom_address":2294091,"party":[{"level":6,"moves":[0,0,0,0],"species":301},{"level":6,"moves":[0,0,0,0],"species":301}],"party_rom_address":3214200,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246660},{"battle_script_rom_address":0,"party":[{"level":27,"moves":[0,0,0,0],"species":302}],"party_rom_address":3214216,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246700},{"battle_script_rom_address":0,"party":[{"level":29,"moves":[0,0,0,0],"species":294},{"level":29,"moves":[0,0,0,0],"species":302}],"party_rom_address":3214224,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246740},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":311},{"level":31,"moves":[0,0,0,0],"species":294},{"level":31,"moves":[0,0,0,0],"species":302}],"party_rom_address":3214240,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246780},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":311},{"level":33,"moves":[0,0,0,0],"species":302},{"level":33,"moves":[0,0,0,0],"species":294},{"level":33,"moves":[0,0,0,0],"species":302}],"party_rom_address":3214264,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246820},{"battle_script_rom_address":2043817,"party":[{"level":17,"moves":[0,0,0,0],"species":339},{"level":17,"moves":[0,0,0,0],"species":66}],"party_rom_address":3214296,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246860},{"battle_script_rom_address":2043848,"party":[{"level":16,"moves":[0,0,0,0],"species":74},{"level":17,"moves":[0,0,0,0],"species":74},{"level":16,"moves":[0,0,0,0],"species":74}],"party_rom_address":3214312,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246900},{"battle_script_rom_address":2045963,"party":[{"level":18,"moves":[0,0,0,0],"species":74},{"level":18,"moves":[0,0,0,0],"species":66}],"party_rom_address":3214336,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246940},{"battle_script_rom_address":2045994,"party":[{"level":18,"moves":[0,0,0,0],"species":74},{"level":18,"moves":[0,0,0,0],"species":339}],"party_rom_address":3214352,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3246980},{"battle_script_rom_address":2549894,"party":[{"level":22,"moves":[0,0,0,0],"species":74},{"level":22,"moves":[0,0,0,0],"species":320},{"level":22,"moves":[0,0,0,0],"species":75}],"party_rom_address":3214368,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247020},{"battle_script_rom_address":2048528,"party":[{"level":8,"moves":[0,0,0,0],"species":74}],"party_rom_address":3214392,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247060},{"battle_script_rom_address":2303697,"party":[{"level":20,"moves":[0,0,0,0],"species":74},{"level":20,"moves":[0,0,0,0],"species":318}],"party_rom_address":3214400,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247100},{"battle_script_rom_address":0,"party":[{"level":9,"moves":[150,55,0,0],"species":313}],"party_rom_address":3214416,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3247140},{"battle_script_rom_address":0,"party":[{"level":10,"moves":[16,45,0,0],"species":310},{"level":10,"moves":[44,184,0,0],"species":286}],"party_rom_address":3214432,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3247180},{"battle_script_rom_address":2289712,"party":[{"level":16,"moves":[0,0,0,0],"species":74},{"level":16,"moves":[0,0,0,0],"species":74},{"level":16,"moves":[0,0,0,0],"species":66}],"party_rom_address":3214464,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247220},{"battle_script_rom_address":0,"party":[{"level":24,"moves":[0,0,0,0],"species":74},{"level":24,"moves":[0,0,0,0],"species":74},{"level":24,"moves":[0,0,0,0],"species":74},{"level":24,"moves":[0,0,0,0],"species":75}],"party_rom_address":3214488,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247260},{"battle_script_rom_address":0,"party":[{"level":27,"moves":[0,0,0,0],"species":74},{"level":27,"moves":[0,0,0,0],"species":74},{"level":27,"moves":[0,0,0,0],"species":75},{"level":27,"moves":[0,0,0,0],"species":75}],"party_rom_address":3214520,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247300},{"battle_script_rom_address":0,"party":[{"level":30,"moves":[0,0,0,0],"species":74},{"level":30,"moves":[0,0,0,0],"species":75},{"level":30,"moves":[0,0,0,0],"species":75},{"level":30,"moves":[0,0,0,0],"species":75}],"party_rom_address":3214552,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247340},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":75},{"level":33,"moves":[0,0,0,0],"species":75},{"level":33,"moves":[0,0,0,0],"species":75},{"level":33,"moves":[0,0,0,0],"species":76}],"party_rom_address":3214584,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247380},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":316},{"level":31,"moves":[0,0,0,0],"species":338}],"party_rom_address":3214616,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247420},{"battle_script_rom_address":0,"party":[{"level":45,"moves":[0,0,0,0],"species":325},{"level":45,"moves":[0,0,0,0],"species":325}],"party_rom_address":3214632,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247460},{"battle_script_rom_address":0,"party":[{"level":25,"moves":[0,0,0,0],"species":386},{"level":25,"moves":[0,0,0,0],"species":387}],"party_rom_address":3214648,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247500},{"battle_script_rom_address":0,"party":[{"level":30,"moves":[0,0,0,0],"species":386},{"level":30,"moves":[0,0,0,0],"species":387}],"party_rom_address":3214664,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247540},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":386},{"level":33,"moves":[0,0,0,0],"species":387}],"party_rom_address":3214680,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247580},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[0,0,0,0],"species":386},{"level":36,"moves":[0,0,0,0],"species":387}],"party_rom_address":3214696,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247620},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[0,0,0,0],"species":386},{"level":39,"moves":[0,0,0,0],"species":387}],"party_rom_address":3214712,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247660},{"battle_script_rom_address":2537289,"party":[{"level":13,"moves":[0,0,0,0],"species":118}],"party_rom_address":3214728,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247700},{"battle_script_rom_address":2097522,"party":[{"level":23,"moves":[53,154,185,20],"species":317}],"party_rom_address":3214736,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3247740},{"battle_script_rom_address":2161586,"party":[{"level":17,"moves":[117,197,93,9],"species":356},{"level":17,"moves":[9,197,93,96],"species":356}],"party_rom_address":3214752,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3247780},{"battle_script_rom_address":2097491,"party":[{"level":23,"moves":[117,197,93,7],"species":356}],"party_rom_address":3214784,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3247820},{"battle_script_rom_address":2055519,"party":[{"level":25,"moves":[33,120,124,108],"species":109},{"level":25,"moves":[33,139,124,108],"species":109}],"party_rom_address":3214800,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3247860},{"battle_script_rom_address":2059810,"party":[{"level":28,"moves":[139,120,124,108],"species":109},{"level":28,"moves":[28,104,210,14],"species":302}],"party_rom_address":3214832,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3247900},{"battle_script_rom_address":2059841,"party":[{"level":28,"moves":[141,154,170,91],"species":301},{"level":28,"moves":[33,120,124,108],"species":109}],"party_rom_address":3214864,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3247940},{"battle_script_rom_address":2196154,"party":[{"level":29,"moves":[0,0,0,0],"species":305},{"level":29,"moves":[0,0,0,0],"species":178}],"party_rom_address":3214896,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3247980},{"battle_script_rom_address":2196185,"party":[{"level":27,"moves":[0,0,0,0],"species":358},{"level":27,"moves":[0,0,0,0],"species":358},{"level":27,"moves":[0,0,0,0],"species":358}],"party_rom_address":3214912,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248020},{"battle_script_rom_address":1966818,"party":[{"level":16,"moves":[0,0,0,0],"species":392}],"party_rom_address":3214936,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248060},{"battle_script_rom_address":2326195,"party":[{"level":47,"moves":[76,219,225,93],"species":359},{"level":46,"moves":[47,18,204,185],"species":316},{"level":47,"moves":[89,73,202,92],"species":363},{"level":44,"moves":[48,85,161,103],"species":82},{"level":48,"moves":[104,91,94,248],"species":394}],"party_rom_address":3214944,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3248100},{"battle_script_rom_address":0,"party":[{"level":50,"moves":[76,219,225,93],"species":359},{"level":49,"moves":[47,18,204,185],"species":316},{"level":50,"moves":[89,73,202,92],"species":363},{"level":47,"moves":[48,85,161,103],"species":82},{"level":51,"moves":[104,91,94,248],"species":394}],"party_rom_address":3215024,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3248140},{"battle_script_rom_address":0,"party":[{"level":53,"moves":[76,219,225,93],"species":359},{"level":52,"moves":[47,18,204,185],"species":316},{"level":53,"moves":[89,73,202,92],"species":363},{"level":50,"moves":[48,85,161,103],"species":82},{"level":54,"moves":[104,91,94,248],"species":394}],"party_rom_address":3215104,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3248180},{"battle_script_rom_address":0,"party":[{"level":56,"moves":[76,219,225,93],"species":359},{"level":55,"moves":[47,18,204,185],"species":316},{"level":56,"moves":[89,73,202,92],"species":363},{"level":53,"moves":[48,85,161,103],"species":82},{"level":57,"moves":[104,91,94,248],"species":394}],"party_rom_address":3215184,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3248220},{"battle_script_rom_address":1981539,"party":[{"level":31,"moves":[0,0,0,0],"species":369},{"level":32,"moves":[0,0,0,0],"species":218},{"level":32,"moves":[0,0,0,0],"species":310},{"level":34,"moves":[0,0,0,0],"species":278}],"party_rom_address":3215264,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248260},{"battle_script_rom_address":1981483,"party":[{"level":31,"moves":[0,0,0,0],"species":369},{"level":32,"moves":[0,0,0,0],"species":310},{"level":32,"moves":[0,0,0,0],"species":297},{"level":34,"moves":[0,0,0,0],"species":281}],"party_rom_address":3215296,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248300},{"battle_script_rom_address":1981511,"party":[{"level":31,"moves":[0,0,0,0],"species":369},{"level":32,"moves":[0,0,0,0],"species":297},{"level":32,"moves":[0,0,0,0],"species":218},{"level":34,"moves":[0,0,0,0],"species":284}],"party_rom_address":3215328,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248340},{"battle_script_rom_address":1981455,"party":[{"level":31,"moves":[0,0,0,0],"species":369},{"level":32,"moves":[0,0,0,0],"species":218},{"level":32,"moves":[0,0,0,0],"species":310},{"level":34,"moves":[0,0,0,0],"species":278}],"party_rom_address":3215360,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248380},{"battle_script_rom_address":1981399,"party":[{"level":31,"moves":[0,0,0,0],"species":369},{"level":32,"moves":[0,0,0,0],"species":310},{"level":32,"moves":[0,0,0,0],"species":297},{"level":34,"moves":[0,0,0,0],"species":281}],"party_rom_address":3215392,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248420},{"battle_script_rom_address":1981427,"party":[{"level":31,"moves":[0,0,0,0],"species":369},{"level":32,"moves":[0,0,0,0],"species":297},{"level":32,"moves":[0,0,0,0],"species":218},{"level":34,"moves":[0,0,0,0],"species":284}],"party_rom_address":3215424,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248460},{"battle_script_rom_address":2064677,"party":[{"level":30,"moves":[0,0,0,0],"species":313},{"level":31,"moves":[0,0,0,0],"species":72},{"level":32,"moves":[0,0,0,0],"species":331}],"party_rom_address":3215456,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248500},{"battle_script_rom_address":2064708,"party":[{"level":31,"moves":[0,0,0,0],"species":330},{"level":34,"moves":[0,0,0,0],"species":73}],"party_rom_address":3215480,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248540},{"battle_script_rom_address":2064739,"party":[{"level":15,"moves":[0,0,0,0],"species":129},{"level":25,"moves":[0,0,0,0],"species":129},{"level":35,"moves":[0,0,0,0],"species":130}],"party_rom_address":3215496,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248580},{"battle_script_rom_address":2065552,"party":[{"level":34,"moves":[0,0,0,0],"species":44},{"level":34,"moves":[0,0,0,0],"species":184}],"party_rom_address":3215520,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248620},{"battle_script_rom_address":2065583,"party":[{"level":34,"moves":[0,0,0,0],"species":300},{"level":34,"moves":[0,0,0,0],"species":320}],"party_rom_address":3215536,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248660},{"battle_script_rom_address":2064832,"party":[{"level":34,"moves":[0,0,0,0],"species":67}],"party_rom_address":3215552,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248700},{"battle_script_rom_address":2065614,"party":[{"level":31,"moves":[0,0,0,0],"species":72},{"level":31,"moves":[0,0,0,0],"species":72},{"level":36,"moves":[0,0,0,0],"species":313}],"party_rom_address":3215560,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248740},{"battle_script_rom_address":2064770,"party":[{"level":32,"moves":[0,0,0,0],"species":305},{"level":32,"moves":[0,0,0,0],"species":227}],"party_rom_address":3215584,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248780},{"battle_script_rom_address":2067040,"party":[{"level":33,"moves":[0,0,0,0],"species":341},{"level":33,"moves":[0,0,0,0],"species":331}],"party_rom_address":3215600,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248820},{"battle_script_rom_address":2067071,"party":[{"level":34,"moves":[0,0,0,0],"species":170}],"party_rom_address":3215616,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248860},{"battle_script_rom_address":0,"party":[{"level":19,"moves":[0,0,0,0],"species":308},{"level":19,"moves":[0,0,0,0],"species":308}],"party_rom_address":3215624,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3248900},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[47,31,219,76],"species":358},{"level":35,"moves":[53,36,156,89],"species":339}],"party_rom_address":3215640,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3248940},{"battle_script_rom_address":0,"party":[{"level":18,"moves":[74,78,72,73],"species":363},{"level":20,"moves":[111,205,44,88],"species":75}],"party_rom_address":3215672,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3248980},{"battle_script_rom_address":0,"party":[{"level":27,"moves":[16,60,92,182],"species":294},{"level":27,"moves":[16,72,213,78],"species":292}],"party_rom_address":3215704,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3249020},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[94,7,244,182],"species":357},{"level":39,"moves":[8,61,156,187],"species":336}],"party_rom_address":3215736,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3249060},{"battle_script_rom_address":0,"party":[{"level":43,"moves":[94,7,244,182],"species":357},{"level":43,"moves":[8,61,156,187],"species":336}],"party_rom_address":3215768,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3249100},{"battle_script_rom_address":0,"party":[{"level":46,"moves":[94,7,244,182],"species":357},{"level":46,"moves":[8,61,156,187],"species":336}],"party_rom_address":3215800,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3249140},{"battle_script_rom_address":0,"party":[{"level":49,"moves":[94,7,244,182],"species":357},{"level":49,"moves":[8,61,156,187],"species":336}],"party_rom_address":3215832,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3249180},{"battle_script_rom_address":0,"party":[{"level":52,"moves":[94,7,244,182],"species":357},{"level":52,"moves":[8,61,156,187],"species":336}],"party_rom_address":3215864,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3249220},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[0,0,0,0],"species":184},{"level":33,"moves":[0,0,0,0],"species":309}],"party_rom_address":3215896,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3249260},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":170},{"level":33,"moves":[0,0,0,0],"species":330}],"party_rom_address":3215912,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3249300},{"battle_script_rom_address":0,"party":[{"level":42,"moves":[0,0,0,0],"species":170},{"level":40,"moves":[0,0,0,0],"species":330}],"party_rom_address":3215928,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3249340},{"battle_script_rom_address":0,"party":[{"level":45,"moves":[0,0,0,0],"species":171},{"level":43,"moves":[0,0,0,0],"species":330}],"party_rom_address":3215944,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3249380},{"battle_script_rom_address":0,"party":[{"level":48,"moves":[0,0,0,0],"species":171},{"level":46,"moves":[0,0,0,0],"species":331}],"party_rom_address":3215960,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3249420},{"battle_script_rom_address":0,"party":[{"level":51,"moves":[0,0,0,0],"species":171},{"level":49,"moves":[0,0,0,0],"species":331}],"party_rom_address":3215976,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3249460},{"battle_script_rom_address":0,"party":[{"level":27,"moves":[0,0,0,0],"species":118},{"level":25,"moves":[0,0,0,0],"species":72}],"party_rom_address":3215992,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3249500},{"battle_script_rom_address":2055550,"party":[{"level":29,"moves":[0,0,0,0],"species":129},{"level":20,"moves":[0,0,0,0],"species":72},{"level":26,"moves":[0,0,0,0],"species":328},{"level":23,"moves":[0,0,0,0],"species":330}],"party_rom_address":3216008,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3249540},{"battle_script_rom_address":2048807,"party":[{"level":8,"moves":[0,0,0,0],"species":288},{"level":8,"moves":[0,0,0,0],"species":286}],"party_rom_address":3216040,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3249580},{"battle_script_rom_address":2048776,"party":[{"level":8,"moves":[0,0,0,0],"species":295},{"level":8,"moves":[0,0,0,0],"species":288}],"party_rom_address":3216056,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3249620},{"battle_script_rom_address":2024517,"party":[{"level":9,"moves":[0,0,0,0],"species":129}],"party_rom_address":3216072,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3249660},{"battle_script_rom_address":2030479,"party":[{"level":13,"moves":[0,0,0,0],"species":183}],"party_rom_address":3216080,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3249700},{"battle_script_rom_address":2030448,"party":[{"level":12,"moves":[0,0,0,0],"species":72},{"level":12,"moves":[0,0,0,0],"species":72}],"party_rom_address":3216088,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3249740},{"battle_script_rom_address":2033204,"party":[{"level":14,"moves":[0,0,0,0],"species":354},{"level":14,"moves":[0,0,0,0],"species":353}],"party_rom_address":3216104,"pokemon_data_type":"ITEM_DEFAULT_MOVES","rom_address":3249780},{"battle_script_rom_address":2033235,"party":[{"level":14,"moves":[0,0,0,0],"species":337},{"level":14,"moves":[0,0,0,0],"species":100}],"party_rom_address":3216120,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3249820},{"battle_script_rom_address":2033266,"party":[{"level":15,"moves":[0,0,0,0],"species":81}],"party_rom_address":3216136,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3249860},{"battle_script_rom_address":2020630,"party":[{"level":15,"moves":[0,0,0,0],"species":100}],"party_rom_address":3216144,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3249900},{"battle_script_rom_address":2020661,"party":[{"level":15,"moves":[0,0,0,0],"species":335}],"party_rom_address":3216152,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3249940},{"battle_script_rom_address":2041104,"party":[{"level":19,"moves":[0,0,0,0],"species":27}],"party_rom_address":3216160,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3249980},{"battle_script_rom_address":2041135,"party":[{"level":18,"moves":[0,0,0,0],"species":363}],"party_rom_address":3216168,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250020},{"battle_script_rom_address":2041073,"party":[{"level":18,"moves":[0,0,0,0],"species":306}],"party_rom_address":3216176,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250060},{"battle_script_rom_address":2041042,"party":[{"level":18,"moves":[0,0,0,0],"species":339}],"party_rom_address":3216184,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250100},{"battle_script_rom_address":2045098,"party":[{"level":17,"moves":[0,0,0,0],"species":183},{"level":19,"moves":[0,0,0,0],"species":296}],"party_rom_address":3216192,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250140},{"battle_script_rom_address":2045129,"party":[{"level":17,"moves":[0,0,0,0],"species":227},{"level":19,"moves":[0,0,0,0],"species":305}],"party_rom_address":3216208,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250180},{"battle_script_rom_address":2045160,"party":[{"level":18,"moves":[0,0,0,0],"species":318},{"level":18,"moves":[0,0,0,0],"species":27}],"party_rom_address":3216224,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250220},{"battle_script_rom_address":2045191,"party":[{"level":18,"moves":[0,0,0,0],"species":382},{"level":18,"moves":[0,0,0,0],"species":382}],"party_rom_address":3216240,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250260},{"battle_script_rom_address":2046431,"party":[{"level":18,"moves":[0,0,0,0],"species":296},{"level":18,"moves":[0,0,0,0],"species":183}],"party_rom_address":3216256,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250300},{"battle_script_rom_address":2046493,"party":[{"level":19,"moves":[0,0,0,0],"species":323}],"party_rom_address":3216272,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250340},{"battle_script_rom_address":2046462,"party":[{"level":19,"moves":[0,0,0,0],"species":299}],"party_rom_address":3216280,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250380},{"battle_script_rom_address":2053150,"party":[{"level":14,"moves":[0,0,0,0],"species":288},{"level":14,"moves":[0,0,0,0],"species":382},{"level":14,"moves":[0,0,0,0],"species":337}],"party_rom_address":3216288,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250420},{"battle_script_rom_address":2341334,"party":[{"level":29,"moves":[0,0,0,0],"species":41}],"party_rom_address":3216312,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250460},{"battle_script_rom_address":2341365,"party":[{"level":29,"moves":[0,0,0,0],"species":286}],"party_rom_address":3216320,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250500},{"battle_script_rom_address":2342090,"party":[{"level":29,"moves":[0,0,0,0],"species":339}],"party_rom_address":3216328,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250540},{"battle_script_rom_address":2342121,"party":[{"level":28,"moves":[0,0,0,0],"species":318},{"level":28,"moves":[0,0,0,0],"species":41}],"party_rom_address":3216336,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250580},{"battle_script_rom_address":2342152,"party":[{"level":28,"moves":[0,0,0,0],"species":318},{"level":28,"moves":[0,0,0,0],"species":339}],"party_rom_address":3216352,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250620},{"battle_script_rom_address":2342817,"party":[{"level":29,"moves":[0,0,0,0],"species":287}],"party_rom_address":3216368,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250660},{"battle_script_rom_address":2342848,"party":[{"level":29,"moves":[0,0,0,0],"species":41}],"party_rom_address":3216376,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250700},{"battle_script_rom_address":2342879,"party":[{"level":29,"moves":[0,0,0,0],"species":286}],"party_rom_address":3216384,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250740},{"battle_script_rom_address":2343757,"party":[{"level":29,"moves":[0,0,0,0],"species":41}],"party_rom_address":3216392,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250780},{"battle_script_rom_address":2344319,"party":[{"level":29,"moves":[0,0,0,0],"species":287}],"party_rom_address":3216400,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250820},{"battle_script_rom_address":2345034,"party":[{"level":29,"moves":[0,0,0,0],"species":318}],"party_rom_address":3216408,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250860},{"battle_script_rom_address":2345065,"party":[{"level":29,"moves":[0,0,0,0],"species":339}],"party_rom_address":3216416,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250900},{"battle_script_rom_address":2345096,"party":[{"level":29,"moves":[0,0,0,0],"species":41}],"party_rom_address":3216424,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250940},{"battle_script_rom_address":2342059,"party":[{"level":29,"moves":[0,0,0,0],"species":287}],"party_rom_address":3216432,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3250980},{"battle_script_rom_address":2342786,"party":[{"level":29,"moves":[0,0,0,0],"species":339}],"party_rom_address":3216440,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251020},{"battle_script_rom_address":2343788,"party":[{"level":29,"moves":[0,0,0,0],"species":318}],"party_rom_address":3216448,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251060},{"battle_script_rom_address":2345127,"party":[{"level":26,"moves":[0,0,0,0],"species":339},{"level":28,"moves":[0,0,0,0],"species":287},{"level":30,"moves":[0,0,0,0],"species":41},{"level":33,"moves":[0,0,0,0],"species":340}],"party_rom_address":3216456,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251100},{"battle_script_rom_address":2067763,"party":[{"level":33,"moves":[0,0,0,0],"species":310},{"level":33,"moves":[0,0,0,0],"species":340}],"party_rom_address":3216488,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251140},{"battle_script_rom_address":0,"party":[{"level":42,"moves":[0,0,0,0],"species":287},{"level":43,"moves":[0,0,0,0],"species":169},{"level":44,"moves":[0,0,0,0],"species":340}],"party_rom_address":3216504,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251180},{"battle_script_rom_address":2020692,"party":[{"level":15,"moves":[0,0,0,0],"species":72}],"party_rom_address":3216528,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251220},{"battle_script_rom_address":2020723,"party":[{"level":15,"moves":[0,0,0,0],"species":183}],"party_rom_address":3216536,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251260},{"battle_script_rom_address":2027900,"party":[{"level":25,"moves":[0,0,0,0],"species":27},{"level":25,"moves":[0,0,0,0],"species":27}],"party_rom_address":3216544,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251300},{"battle_script_rom_address":2027869,"party":[{"level":25,"moves":[0,0,0,0],"species":304},{"level":25,"moves":[0,0,0,0],"species":309}],"party_rom_address":3216560,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251340},{"battle_script_rom_address":2028918,"party":[{"level":26,"moves":[0,0,0,0],"species":120}],"party_rom_address":3216576,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251380},{"battle_script_rom_address":2029105,"party":[{"level":24,"moves":[0,0,0,0],"species":309},{"level":24,"moves":[0,0,0,0],"species":66},{"level":24,"moves":[0,0,0,0],"species":72}],"party_rom_address":3216584,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251420},{"battle_script_rom_address":2029074,"party":[{"level":24,"moves":[0,0,0,0],"species":338},{"level":24,"moves":[0,0,0,0],"species":305},{"level":24,"moves":[0,0,0,0],"species":338}],"party_rom_address":3216608,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251460},{"battle_script_rom_address":2030510,"party":[{"level":25,"moves":[0,0,0,0],"species":227},{"level":25,"moves":[0,0,0,0],"species":227}],"party_rom_address":3216632,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251500},{"battle_script_rom_address":2041166,"party":[{"level":22,"moves":[0,0,0,0],"species":183},{"level":22,"moves":[0,0,0,0],"species":296}],"party_rom_address":3216648,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251540},{"battle_script_rom_address":2041197,"party":[{"level":22,"moves":[0,0,0,0],"species":27},{"level":22,"moves":[0,0,0,0],"species":28}],"party_rom_address":3216664,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251580},{"battle_script_rom_address":2041228,"party":[{"level":22,"moves":[0,0,0,0],"species":304},{"level":22,"moves":[0,0,0,0],"species":299}],"party_rom_address":3216680,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251620},{"battle_script_rom_address":2044020,"party":[{"level":18,"moves":[0,0,0,0],"species":339},{"level":18,"moves":[0,0,0,0],"species":218}],"party_rom_address":3216696,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251660},{"battle_script_rom_address":2044051,"party":[{"level":18,"moves":[0,0,0,0],"species":306},{"level":18,"moves":[0,0,0,0],"species":363}],"party_rom_address":3216712,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251700},{"battle_script_rom_address":2047305,"party":[{"level":26,"moves":[0,0,0,0],"species":84},{"level":26,"moves":[0,0,0,0],"species":85}],"party_rom_address":3216728,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251740},{"battle_script_rom_address":2047336,"party":[{"level":26,"moves":[0,0,0,0],"species":302},{"level":26,"moves":[0,0,0,0],"species":367}],"party_rom_address":3216744,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251780},{"battle_script_rom_address":2047367,"party":[{"level":26,"moves":[0,0,0,0],"species":64},{"level":26,"moves":[0,0,0,0],"species":393}],"party_rom_address":3216760,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251820},{"battle_script_rom_address":2047398,"party":[{"level":26,"moves":[0,0,0,0],"species":356},{"level":26,"moves":[0,0,0,0],"species":335}],"party_rom_address":3216776,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251860},{"battle_script_rom_address":2047429,"party":[{"level":18,"moves":[0,0,0,0],"species":356},{"level":18,"moves":[0,0,0,0],"species":351}],"party_rom_address":3216792,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251900},{"battle_script_rom_address":2048838,"party":[{"level":8,"moves":[0,0,0,0],"species":74},{"level":8,"moves":[0,0,0,0],"species":74}],"party_rom_address":3216808,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251940},{"battle_script_rom_address":2048869,"party":[{"level":8,"moves":[0,0,0,0],"species":306},{"level":8,"moves":[0,0,0,0],"species":295}],"party_rom_address":3216824,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3251980},{"battle_script_rom_address":2051934,"party":[{"level":17,"moves":[0,0,0,0],"species":84}],"party_rom_address":3216840,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3252020},{"battle_script_rom_address":2051965,"party":[{"level":17,"moves":[0,0,0,0],"species":392}],"party_rom_address":3216848,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3252060},{"battle_script_rom_address":2051996,"party":[{"level":17,"moves":[0,0,0,0],"species":356}],"party_rom_address":3216856,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3252100},{"battle_script_rom_address":2067825,"party":[{"level":33,"moves":[0,0,0,0],"species":363},{"level":33,"moves":[0,0,0,0],"species":357}],"party_rom_address":3216864,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3252140},{"battle_script_rom_address":2055581,"party":[{"level":26,"moves":[0,0,0,0],"species":338}],"party_rom_address":3216880,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3252180},{"battle_script_rom_address":2055612,"party":[{"level":25,"moves":[0,0,0,0],"species":218},{"level":25,"moves":[0,0,0,0],"species":339}],"party_rom_address":3216888,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3252220},{"battle_script_rom_address":2055643,"party":[{"level":26,"moves":[0,0,0,0],"species":118}],"party_rom_address":3216904,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3252260},{"battle_script_rom_address":2059872,"party":[{"level":30,"moves":[87,98,86,0],"species":338}],"party_rom_address":3216912,"pokemon_data_type":"NO_ITEM_CUSTOM_MOVES","rom_address":3252300},{"battle_script_rom_address":2059903,"party":[{"level":28,"moves":[0,0,0,0],"species":356},{"level":28,"moves":[0,0,0,0],"species":335}],"party_rom_address":3216928,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3252340},{"battle_script_rom_address":2061522,"party":[{"level":29,"moves":[0,0,0,0],"species":294},{"level":29,"moves":[0,0,0,0],"species":292}],"party_rom_address":3216944,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3252380},{"battle_script_rom_address":2061553,"party":[{"level":25,"moves":[0,0,0,0],"species":335},{"level":25,"moves":[0,0,0,0],"species":309},{"level":25,"moves":[0,0,0,0],"species":369},{"level":25,"moves":[0,0,0,0],"species":288},{"level":25,"moves":[0,0,0,0],"species":337},{"level":25,"moves":[0,0,0,0],"species":339}],"party_rom_address":3216960,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3252420},{"battle_script_rom_address":2061584,"party":[{"level":25,"moves":[0,0,0,0],"species":286},{"level":25,"moves":[0,0,0,0],"species":306},{"level":25,"moves":[0,0,0,0],"species":337},{"level":25,"moves":[0,0,0,0],"species":183},{"level":25,"moves":[0,0,0,0],"species":27},{"level":25,"moves":[0,0,0,0],"species":367}],"party_rom_address":3217008,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3252460},{"battle_script_rom_address":2061646,"party":[{"level":29,"moves":[0,0,0,0],"species":371},{"level":29,"moves":[0,0,0,0],"species":365}],"party_rom_address":3217056,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3252500},{"battle_script_rom_address":1973644,"party":[{"level":13,"moves":[0,0,0,0],"species":295},{"level":15,"moves":[0,0,0,0],"species":280}],"party_rom_address":3217072,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3252540},{"battle_script_rom_address":1973675,"party":[{"level":13,"moves":[0,0,0,0],"species":321},{"level":15,"moves":[0,0,0,0],"species":283}],"party_rom_address":3217088,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3252580},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[182,205,222,153],"species":76},{"level":35,"moves":[14,58,57,157],"species":140},{"level":35,"moves":[231,153,46,157],"species":95},{"level":37,"moves":[104,153,182,157],"species":320}],"party_rom_address":3217104,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3252620},{"battle_script_rom_address":0,"party":[{"level":37,"moves":[182,58,157,57],"species":138},{"level":37,"moves":[182,205,222,153],"species":76},{"level":40,"moves":[14,58,57,157],"species":141},{"level":40,"moves":[231,153,46,157],"species":95},{"level":42,"moves":[104,153,182,157],"species":320}],"party_rom_address":3217168,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3252660},{"battle_script_rom_address":0,"party":[{"level":42,"moves":[182,58,157,57],"species":139},{"level":42,"moves":[182,205,89,153],"species":76},{"level":45,"moves":[14,58,57,157],"species":141},{"level":45,"moves":[231,153,46,157],"species":95},{"level":47,"moves":[104,153,182,157],"species":320}],"party_rom_address":3217248,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3252700},{"battle_script_rom_address":0,"party":[{"level":47,"moves":[157,63,48,182],"species":142},{"level":47,"moves":[8,205,89,153],"species":76},{"level":47,"moves":[182,58,157,57],"species":139},{"level":50,"moves":[14,58,57,157],"species":141},{"level":50,"moves":[231,153,46,157],"species":208},{"level":52,"moves":[104,153,182,157],"species":320}],"party_rom_address":3217328,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3252740},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[2,157,8,83],"species":68},{"level":33,"moves":[94,113,115,8],"species":356},{"level":35,"moves":[228,68,182,167],"species":237},{"level":37,"moves":[252,8,187,89],"species":336}],"party_rom_address":3217424,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3252780},{"battle_script_rom_address":0,"party":[{"level":38,"moves":[2,157,8,83],"species":68},{"level":38,"moves":[94,113,115,8],"species":357},{"level":40,"moves":[228,68,182,167],"species":237},{"level":42,"moves":[252,8,187,89],"species":336}],"party_rom_address":3217488,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3252820},{"battle_script_rom_address":0,"party":[{"level":40,"moves":[71,182,7,8],"species":107},{"level":43,"moves":[2,157,8,83],"species":68},{"level":43,"moves":[8,113,115,94],"species":357},{"level":45,"moves":[228,68,182,167],"species":237},{"level":47,"moves":[252,8,187,89],"species":336}],"party_rom_address":3217552,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3252860},{"battle_script_rom_address":0,"party":[{"level":46,"moves":[25,8,89,83],"species":106},{"level":46,"moves":[71,182,7,8],"species":107},{"level":48,"moves":[238,157,8,83],"species":68},{"level":48,"moves":[8,113,115,94],"species":357},{"level":50,"moves":[228,68,182,167],"species":237},{"level":52,"moves":[252,8,187,89],"species":336}],"party_rom_address":3217632,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3252900},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[87,182,86,113],"species":179},{"level":36,"moves":[205,87,153,240],"species":101},{"level":38,"moves":[48,182,87,240],"species":82},{"level":40,"moves":[44,86,87,182],"species":338}],"party_rom_address":3217728,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3252940},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[87,21,240,95],"species":25},{"level":41,"moves":[87,182,86,113],"species":180},{"level":41,"moves":[205,87,153,240],"species":101},{"level":43,"moves":[48,182,87,240],"species":82},{"level":45,"moves":[44,86,87,182],"species":338}],"party_rom_address":3217792,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3252980},{"battle_script_rom_address":0,"party":[{"level":44,"moves":[87,21,240,182],"species":26},{"level":46,"moves":[87,182,86,113],"species":181},{"level":46,"moves":[205,87,153,240],"species":101},{"level":48,"moves":[48,182,87,240],"species":82},{"level":50,"moves":[44,86,87,182],"species":338}],"party_rom_address":3217872,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253020},{"battle_script_rom_address":0,"party":[{"level":50,"moves":[129,8,9,113],"species":125},{"level":51,"moves":[87,21,240,182],"species":26},{"level":51,"moves":[87,182,86,113],"species":181},{"level":53,"moves":[205,87,153,240],"species":101},{"level":53,"moves":[48,182,87,240],"species":82},{"level":55,"moves":[44,86,87,182],"species":338}],"party_rom_address":3217952,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253060},{"battle_script_rom_address":0,"party":[{"level":38,"moves":[59,213,113,157],"species":219},{"level":36,"moves":[53,213,76,84],"species":77},{"level":38,"moves":[59,241,89,213],"species":340},{"level":40,"moves":[59,241,153,213],"species":321}],"party_rom_address":3218048,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253100},{"battle_script_rom_address":0,"party":[{"level":41,"moves":[14,53,46,241],"species":58},{"level":43,"moves":[59,213,113,157],"species":219},{"level":41,"moves":[53,213,76,84],"species":77},{"level":43,"moves":[59,241,89,213],"species":340},{"level":45,"moves":[59,241,153,213],"species":321}],"party_rom_address":3218112,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253140},{"battle_script_rom_address":0,"party":[{"level":46,"moves":[46,76,13,241],"species":228},{"level":46,"moves":[14,53,241,46],"species":58},{"level":48,"moves":[59,213,113,157],"species":219},{"level":46,"moves":[53,213,76,84],"species":78},{"level":48,"moves":[59,241,89,213],"species":340},{"level":50,"moves":[59,241,153,213],"species":321}],"party_rom_address":3218192,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253180},{"battle_script_rom_address":0,"party":[{"level":51,"moves":[14,53,241,46],"species":59},{"level":53,"moves":[59,213,113,157],"species":219},{"level":51,"moves":[46,76,13,241],"species":229},{"level":51,"moves":[53,213,76,84],"species":78},{"level":53,"moves":[59,241,89,213],"species":340},{"level":55,"moves":[59,241,153,213],"species":321}],"party_rom_address":3218288,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253220},{"battle_script_rom_address":0,"party":[{"level":42,"moves":[113,47,29,8],"species":113},{"level":42,"moves":[59,247,38,126],"species":366},{"level":43,"moves":[42,29,7,95],"species":308},{"level":45,"moves":[63,53,85,247],"species":366}],"party_rom_address":3218384,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253260},{"battle_script_rom_address":0,"party":[{"level":47,"moves":[59,247,38,126],"species":366},{"level":47,"moves":[113,47,29,8],"species":113},{"level":45,"moves":[252,146,203,179],"species":115},{"level":48,"moves":[42,29,7,95],"species":308},{"level":50,"moves":[63,53,85,247],"species":366}],"party_rom_address":3218448,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253300},{"battle_script_rom_address":0,"party":[{"level":52,"moves":[59,247,38,126],"species":366},{"level":52,"moves":[113,47,29,8],"species":242},{"level":50,"moves":[252,146,203,179],"species":115},{"level":53,"moves":[42,29,7,95],"species":308},{"level":55,"moves":[63,53,85,247],"species":366}],"party_rom_address":3218528,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253340},{"battle_script_rom_address":0,"party":[{"level":57,"moves":[59,247,38,126],"species":366},{"level":57,"moves":[182,47,29,8],"species":242},{"level":55,"moves":[252,146,203,179],"species":115},{"level":57,"moves":[36,182,126,89],"species":128},{"level":58,"moves":[42,29,7,95],"species":308},{"level":60,"moves":[63,53,85,247],"species":366}],"party_rom_address":3218608,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253380},{"battle_script_rom_address":0,"party":[{"level":40,"moves":[86,85,182,58],"species":147},{"level":38,"moves":[241,76,76,89],"species":369},{"level":41,"moves":[57,48,182,76],"species":310},{"level":43,"moves":[18,191,211,76],"species":227},{"level":45,"moves":[76,156,93,89],"species":359}],"party_rom_address":3218704,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253420},{"battle_script_rom_address":0,"party":[{"level":43,"moves":[95,94,115,138],"species":163},{"level":43,"moves":[241,76,76,89],"species":369},{"level":45,"moves":[86,85,182,58],"species":148},{"level":46,"moves":[57,48,182,76],"species":310},{"level":48,"moves":[18,191,211,76],"species":227},{"level":50,"moves":[76,156,93,89],"species":359}],"party_rom_address":3218784,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253460},{"battle_script_rom_address":0,"party":[{"level":48,"moves":[95,94,115,138],"species":164},{"level":49,"moves":[241,76,76,89],"species":369},{"level":50,"moves":[86,85,182,58],"species":148},{"level":51,"moves":[57,48,182,76],"species":310},{"level":53,"moves":[18,191,211,76],"species":227},{"level":55,"moves":[76,156,93,89],"species":359}],"party_rom_address":3218880,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253500},{"battle_script_rom_address":0,"party":[{"level":53,"moves":[95,94,115,138],"species":164},{"level":54,"moves":[241,76,76,89],"species":369},{"level":55,"moves":[57,48,182,76],"species":310},{"level":55,"moves":[63,85,89,58],"species":149},{"level":58,"moves":[18,191,211,76],"species":227},{"level":60,"moves":[143,156,93,89],"species":359}],"party_rom_address":3218976,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253540},{"battle_script_rom_address":0,"party":[{"level":48,"moves":[25,94,91,182],"species":79},{"level":49,"moves":[89,246,94,113],"species":319},{"level":49,"moves":[94,156,109,91],"species":178},{"level":50,"moves":[89,94,156,91],"species":348},{"level":50,"moves":[241,76,94,53],"species":349}],"party_rom_address":3219072,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253580},{"battle_script_rom_address":0,"party":[{"level":53,"moves":[95,138,29,182],"species":96},{"level":53,"moves":[25,94,91,182],"species":79},{"level":54,"moves":[89,153,94,113],"species":319},{"level":54,"moves":[94,156,109,91],"species":178},{"level":55,"moves":[89,94,156,91],"species":348},{"level":55,"moves":[241,76,94,53],"species":349}],"party_rom_address":3219152,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253620},{"battle_script_rom_address":0,"party":[{"level":58,"moves":[95,138,29,182],"species":97},{"level":59,"moves":[89,153,94,113],"species":319},{"level":58,"moves":[25,94,91,182],"species":79},{"level":59,"moves":[94,156,109,91],"species":178},{"level":60,"moves":[89,94,156,91],"species":348},{"level":60,"moves":[241,76,94,53],"species":349}],"party_rom_address":3219248,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253660},{"battle_script_rom_address":0,"party":[{"level":63,"moves":[95,138,29,182],"species":97},{"level":64,"moves":[89,153,94,113],"species":319},{"level":63,"moves":[25,94,91,182],"species":199},{"level":64,"moves":[94,156,109,91],"species":178},{"level":65,"moves":[89,94,156,91],"species":348},{"level":65,"moves":[241,76,94,53],"species":349}],"party_rom_address":3219344,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253700},{"battle_script_rom_address":0,"party":[{"level":46,"moves":[95,240,182,56],"species":60},{"level":46,"moves":[240,96,104,90],"species":324},{"level":48,"moves":[96,34,182,58],"species":343},{"level":48,"moves":[156,152,13,104],"species":327},{"level":51,"moves":[96,104,58,156],"species":230}],"party_rom_address":3219440,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253740},{"battle_script_rom_address":0,"party":[{"level":50,"moves":[95,240,182,56],"species":61},{"level":51,"moves":[240,96,104,90],"species":324},{"level":53,"moves":[96,34,182,58],"species":343},{"level":53,"moves":[156,12,13,104],"species":327},{"level":56,"moves":[96,104,58,156],"species":230}],"party_rom_address":3219520,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253780},{"battle_script_rom_address":0,"party":[{"level":56,"moves":[56,195,58,109],"species":131},{"level":58,"moves":[240,96,104,90],"species":324},{"level":56,"moves":[95,240,182,56],"species":61},{"level":58,"moves":[96,34,182,58],"species":343},{"level":58,"moves":[156,12,13,104],"species":327},{"level":61,"moves":[96,104,58,156],"species":230}],"party_rom_address":3219600,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253820},{"battle_script_rom_address":0,"party":[{"level":61,"moves":[56,195,58,109],"species":131},{"level":63,"moves":[240,96,104,90],"species":324},{"level":61,"moves":[95,240,56,195],"species":186},{"level":63,"moves":[96,34,182,73],"species":343},{"level":63,"moves":[156,12,13,104],"species":327},{"level":66,"moves":[96,104,58,156],"species":230}],"party_rom_address":3219696,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253860},{"battle_script_rom_address":2161617,"party":[{"level":17,"moves":[95,98,204,0],"species":387},{"level":17,"moves":[95,98,109,0],"species":386}],"party_rom_address":3219792,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253900},{"battle_script_rom_address":2196247,"party":[{"level":30,"moves":[0,0,0,0],"species":369}],"party_rom_address":3219824,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3253940},{"battle_script_rom_address":2347924,"party":[{"level":77,"moves":[92,76,191,211],"species":227},{"level":75,"moves":[115,113,246,89],"species":319},{"level":76,"moves":[87,89,76,81],"species":384},{"level":76,"moves":[202,246,19,109],"species":389},{"level":76,"moves":[96,246,76,163],"species":391},{"level":78,"moves":[89,94,53,247],"species":400}],"party_rom_address":3219832,"pokemon_data_type":"ITEM_CUSTOM_MOVES","rom_address":3253980},{"battle_script_rom_address":0,"party":[{"level":5,"moves":[0,0,0,0],"species":398}],"party_rom_address":3219928,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254020},{"battle_script_rom_address":0,"party":[{"level":5,"moves":[0,0,0,0],"species":398}],"party_rom_address":3219936,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254060},{"battle_script_rom_address":0,"party":[{"level":5,"moves":[0,0,0,0],"species":398}],"party_rom_address":3219944,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254100},{"battle_script_rom_address":0,"party":[{"level":5,"moves":[0,0,0,0],"species":398}],"party_rom_address":3219952,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254140},{"battle_script_rom_address":0,"party":[{"level":5,"moves":[0,0,0,0],"species":398}],"party_rom_address":3219960,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254180},{"battle_script_rom_address":0,"party":[{"level":5,"moves":[0,0,0,0],"species":398}],"party_rom_address":3219968,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254220},{"battle_script_rom_address":0,"party":[{"level":5,"moves":[0,0,0,0],"species":398}],"party_rom_address":3219976,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254260},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":27},{"level":31,"moves":[0,0,0,0],"species":27}],"party_rom_address":3219984,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254300},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":320},{"level":33,"moves":[0,0,0,0],"species":27},{"level":33,"moves":[0,0,0,0],"species":27}],"party_rom_address":3220000,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254340},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[0,0,0,0],"species":320},{"level":35,"moves":[0,0,0,0],"species":27},{"level":35,"moves":[0,0,0,0],"species":27}],"party_rom_address":3220024,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254380},{"battle_script_rom_address":0,"party":[{"level":37,"moves":[0,0,0,0],"species":320},{"level":37,"moves":[0,0,0,0],"species":28},{"level":37,"moves":[0,0,0,0],"species":28}],"party_rom_address":3220048,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254420},{"battle_script_rom_address":0,"party":[{"level":30,"moves":[0,0,0,0],"species":309},{"level":30,"moves":[0,0,0,0],"species":66},{"level":30,"moves":[0,0,0,0],"species":72}],"party_rom_address":3220072,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254460},{"battle_script_rom_address":0,"party":[{"level":32,"moves":[0,0,0,0],"species":310},{"level":32,"moves":[0,0,0,0],"species":66},{"level":32,"moves":[0,0,0,0],"species":72}],"party_rom_address":3220096,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254500},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":310},{"level":34,"moves":[0,0,0,0],"species":66},{"level":34,"moves":[0,0,0,0],"species":73}],"party_rom_address":3220120,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254540},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[0,0,0,0],"species":310},{"level":36,"moves":[0,0,0,0],"species":67},{"level":36,"moves":[0,0,0,0],"species":73}],"party_rom_address":3220144,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254580},{"battle_script_rom_address":0,"party":[{"level":37,"moves":[0,0,0,0],"species":120},{"level":37,"moves":[0,0,0,0],"species":120}],"party_rom_address":3220168,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254620},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[0,0,0,0],"species":309},{"level":39,"moves":[0,0,0,0],"species":120},{"level":39,"moves":[0,0,0,0],"species":120}],"party_rom_address":3220184,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254660},{"battle_script_rom_address":0,"party":[{"level":41,"moves":[0,0,0,0],"species":310},{"level":41,"moves":[0,0,0,0],"species":120},{"level":41,"moves":[0,0,0,0],"species":120}],"party_rom_address":3220208,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254700},{"battle_script_rom_address":0,"party":[{"level":43,"moves":[0,0,0,0],"species":310},{"level":43,"moves":[0,0,0,0],"species":121},{"level":43,"moves":[0,0,0,0],"species":121}],"party_rom_address":3220232,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254740},{"battle_script_rom_address":0,"party":[{"level":37,"moves":[0,0,0,0],"species":67},{"level":37,"moves":[0,0,0,0],"species":67}],"party_rom_address":3220256,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254780},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[0,0,0,0],"species":335},{"level":39,"moves":[0,0,0,0],"species":67},{"level":39,"moves":[0,0,0,0],"species":67}],"party_rom_address":3220272,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254820},{"battle_script_rom_address":0,"party":[{"level":41,"moves":[0,0,0,0],"species":336},{"level":41,"moves":[0,0,0,0],"species":67},{"level":41,"moves":[0,0,0,0],"species":67}],"party_rom_address":3220296,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254860},{"battle_script_rom_address":0,"party":[{"level":43,"moves":[0,0,0,0],"species":336},{"level":43,"moves":[0,0,0,0],"species":68},{"level":43,"moves":[0,0,0,0],"species":68}],"party_rom_address":3220320,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254900},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[0,0,0,0],"species":371},{"level":35,"moves":[0,0,0,0],"species":365}],"party_rom_address":3220344,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254940},{"battle_script_rom_address":0,"party":[{"level":37,"moves":[0,0,0,0],"species":308},{"level":37,"moves":[0,0,0,0],"species":371},{"level":37,"moves":[0,0,0,0],"species":365}],"party_rom_address":3220360,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3254980},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[0,0,0,0],"species":308},{"level":39,"moves":[0,0,0,0],"species":371},{"level":39,"moves":[0,0,0,0],"species":365}],"party_rom_address":3220384,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255020},{"battle_script_rom_address":0,"party":[{"level":41,"moves":[0,0,0,0],"species":308},{"level":41,"moves":[0,0,0,0],"species":372},{"level":41,"moves":[0,0,0,0],"species":366}],"party_rom_address":3220408,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255060},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[0,0,0,0],"species":337},{"level":35,"moves":[0,0,0,0],"species":337},{"level":35,"moves":[0,0,0,0],"species":371}],"party_rom_address":3220432,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255100},{"battle_script_rom_address":0,"party":[{"level":37,"moves":[0,0,0,0],"species":337},{"level":37,"moves":[0,0,0,0],"species":338},{"level":37,"moves":[0,0,0,0],"species":371}],"party_rom_address":3220456,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255140},{"battle_script_rom_address":0,"party":[{"level":39,"moves":[0,0,0,0],"species":338},{"level":39,"moves":[0,0,0,0],"species":338},{"level":39,"moves":[0,0,0,0],"species":371}],"party_rom_address":3220480,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255180},{"battle_script_rom_address":0,"party":[{"level":41,"moves":[0,0,0,0],"species":338},{"level":41,"moves":[0,0,0,0],"species":338},{"level":41,"moves":[0,0,0,0],"species":372}],"party_rom_address":3220504,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255220},{"battle_script_rom_address":0,"party":[{"level":26,"moves":[0,0,0,0],"species":74},{"level":26,"moves":[0,0,0,0],"species":339}],"party_rom_address":3220528,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255260},{"battle_script_rom_address":0,"party":[{"level":28,"moves":[0,0,0,0],"species":66},{"level":28,"moves":[0,0,0,0],"species":339},{"level":28,"moves":[0,0,0,0],"species":75}],"party_rom_address":3220544,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255300},{"battle_script_rom_address":0,"party":[{"level":30,"moves":[0,0,0,0],"species":66},{"level":30,"moves":[0,0,0,0],"species":339},{"level":30,"moves":[0,0,0,0],"species":75}],"party_rom_address":3220568,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255340},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":67},{"level":33,"moves":[0,0,0,0],"species":340},{"level":33,"moves":[0,0,0,0],"species":76}],"party_rom_address":3220592,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255380},{"battle_script_rom_address":0,"party":[{"level":31,"moves":[0,0,0,0],"species":315},{"level":31,"moves":[0,0,0,0],"species":287},{"level":31,"moves":[0,0,0,0],"species":288},{"level":31,"moves":[0,0,0,0],"species":295},{"level":31,"moves":[0,0,0,0],"species":298},{"level":31,"moves":[0,0,0,0],"species":304}],"party_rom_address":3220616,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255420},{"battle_script_rom_address":0,"party":[{"level":33,"moves":[0,0,0,0],"species":315},{"level":33,"moves":[0,0,0,0],"species":287},{"level":33,"moves":[0,0,0,0],"species":289},{"level":33,"moves":[0,0,0,0],"species":296},{"level":33,"moves":[0,0,0,0],"species":299},{"level":33,"moves":[0,0,0,0],"species":304}],"party_rom_address":3220664,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255460},{"battle_script_rom_address":0,"party":[{"level":35,"moves":[0,0,0,0],"species":316},{"level":35,"moves":[0,0,0,0],"species":287},{"level":35,"moves":[0,0,0,0],"species":289},{"level":35,"moves":[0,0,0,0],"species":296},{"level":35,"moves":[0,0,0,0],"species":299},{"level":35,"moves":[0,0,0,0],"species":305}],"party_rom_address":3220712,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255500},{"battle_script_rom_address":0,"party":[{"level":37,"moves":[0,0,0,0],"species":316},{"level":37,"moves":[0,0,0,0],"species":287},{"level":37,"moves":[0,0,0,0],"species":289},{"level":37,"moves":[0,0,0,0],"species":297},{"level":37,"moves":[0,0,0,0],"species":300},{"level":37,"moves":[0,0,0,0],"species":305}],"party_rom_address":3220760,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255540},{"battle_script_rom_address":0,"party":[{"level":34,"moves":[0,0,0,0],"species":313},{"level":34,"moves":[0,0,0,0],"species":116}],"party_rom_address":3220808,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255580},{"battle_script_rom_address":0,"party":[{"level":36,"moves":[0,0,0,0],"species":325},{"level":36,"moves":[0,0,0,0],"species":313},{"level":36,"moves":[0,0,0,0],"species":117}],"party_rom_address":3220824,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255620},{"battle_script_rom_address":0,"party":[{"level":38,"moves":[0,0,0,0],"species":325},{"level":38,"moves":[0,0,0,0],"species":313},{"level":38,"moves":[0,0,0,0],"species":117}],"party_rom_address":3220848,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255660},{"battle_script_rom_address":0,"party":[{"level":40,"moves":[0,0,0,0],"species":325},{"level":40,"moves":[0,0,0,0],"species":314},{"level":40,"moves":[0,0,0,0],"species":230}],"party_rom_address":3220872,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255700},{"battle_script_rom_address":2557618,"party":[{"level":41,"moves":[0,0,0,0],"species":411}],"party_rom_address":3220896,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255740},{"battle_script_rom_address":2557649,"party":[{"level":41,"moves":[0,0,0,0],"species":378},{"level":41,"moves":[0,0,0,0],"species":64}],"party_rom_address":3220904,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255780},{"battle_script_rom_address":0,"party":[{"level":41,"moves":[0,0,0,0],"species":202}],"party_rom_address":3220920,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255820},{"battle_script_rom_address":0,"party":[{"level":5,"moves":[0,0,0,0],"species":4}],"party_rom_address":3220928,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255860},{"battle_script_rom_address":0,"party":[{"level":5,"moves":[0,0,0,0],"species":1}],"party_rom_address":3220936,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255900},{"battle_script_rom_address":0,"party":[{"level":5,"moves":[0,0,0,0],"species":405}],"party_rom_address":3220944,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255940},{"battle_script_rom_address":0,"party":[{"level":5,"moves":[0,0,0,0],"species":404}],"party_rom_address":3220952,"pokemon_data_type":"NO_ITEM_DEFAULT_MOVES","rom_address":3255980}],"warps":{"MAP_ABANDONED_SHIP_CAPTAINS_OFFICE:0,1/MAP_ABANDONED_SHIP_DECK:4":"MAP_ABANDONED_SHIP_DECK:4/MAP_ABANDONED_SHIP_CAPTAINS_OFFICE:0","MAP_ABANDONED_SHIP_CORRIDORS_1F:0,1/MAP_ABANDONED_SHIP_DECK:2":"MAP_ABANDONED_SHIP_DECK:2/MAP_ABANDONED_SHIP_CORRIDORS_1F:1","MAP_ABANDONED_SHIP_CORRIDORS_1F:10/MAP_ABANDONED_SHIP_CORRIDORS_B1F:6":"MAP_ABANDONED_SHIP_CORRIDORS_B1F:6/MAP_ABANDONED_SHIP_CORRIDORS_1F:10","MAP_ABANDONED_SHIP_CORRIDORS_1F:11/MAP_ABANDONED_SHIP_ROOMS2_1F:2":"MAP_ABANDONED_SHIP_ROOMS2_1F:2/MAP_ABANDONED_SHIP_CORRIDORS_1F:11","MAP_ABANDONED_SHIP_CORRIDORS_1F:2,3/MAP_ABANDONED_SHIP_DECK:3":"MAP_ABANDONED_SHIP_DECK:3/MAP_ABANDONED_SHIP_CORRIDORS_1F:2","MAP_ABANDONED_SHIP_CORRIDORS_1F:4/MAP_ABANDONED_SHIP_ROOMS_1F:0":"MAP_ABANDONED_SHIP_ROOMS_1F:0,1/MAP_ABANDONED_SHIP_CORRIDORS_1F:4","MAP_ABANDONED_SHIP_CORRIDORS_1F:5/MAP_ABANDONED_SHIP_ROOMS_1F:3":"MAP_ABANDONED_SHIP_ROOMS_1F:3,5/MAP_ABANDONED_SHIP_CORRIDORS_1F:5","MAP_ABANDONED_SHIP_CORRIDORS_1F:6/MAP_ABANDONED_SHIP_ROOMS_1F:2":"MAP_ABANDONED_SHIP_ROOMS_1F:2/MAP_ABANDONED_SHIP_CORRIDORS_1F:6","MAP_ABANDONED_SHIP_CORRIDORS_1F:7/MAP_ABANDONED_SHIP_ROOMS_1F:4":"MAP_ABANDONED_SHIP_ROOMS_1F:4/MAP_ABANDONED_SHIP_CORRIDORS_1F:7","MAP_ABANDONED_SHIP_CORRIDORS_1F:8/MAP_ABANDONED_SHIP_ROOMS2_1F:0":"MAP_ABANDONED_SHIP_ROOMS2_1F:0,1/MAP_ABANDONED_SHIP_CORRIDORS_1F:8","MAP_ABANDONED_SHIP_CORRIDORS_1F:9/MAP_ABANDONED_SHIP_CORRIDORS_B1F:7":"MAP_ABANDONED_SHIP_CORRIDORS_B1F:7/MAP_ABANDONED_SHIP_CORRIDORS_1F:9","MAP_ABANDONED_SHIP_CORRIDORS_B1F:0/MAP_ABANDONED_SHIP_ROOMS2_B1F:2":"MAP_ABANDONED_SHIP_ROOMS2_B1F:2,3/MAP_ABANDONED_SHIP_CORRIDORS_B1F:0","MAP_ABANDONED_SHIP_CORRIDORS_B1F:1/MAP_ABANDONED_SHIP_ROOMS2_B1F:0":"MAP_ABANDONED_SHIP_ROOMS2_B1F:0,1/MAP_ABANDONED_SHIP_CORRIDORS_B1F:1","MAP_ABANDONED_SHIP_CORRIDORS_B1F:2/MAP_ABANDONED_SHIP_ROOMS_B1F:0":"MAP_ABANDONED_SHIP_ROOMS_B1F:0/MAP_ABANDONED_SHIP_CORRIDORS_B1F:2","MAP_ABANDONED_SHIP_CORRIDORS_B1F:3/MAP_ABANDONED_SHIP_ROOMS_B1F:1":"MAP_ABANDONED_SHIP_ROOMS_B1F:1/MAP_ABANDONED_SHIP_CORRIDORS_B1F:3","MAP_ABANDONED_SHIP_CORRIDORS_B1F:4/MAP_ABANDONED_SHIP_ROOMS_B1F:2":"MAP_ABANDONED_SHIP_ROOMS_B1F:2/MAP_ABANDONED_SHIP_CORRIDORS_B1F:4","MAP_ABANDONED_SHIP_CORRIDORS_B1F:5/MAP_ABANDONED_SHIP_ROOM_B1F:0":"MAP_ABANDONED_SHIP_ROOM_B1F:0,1/MAP_ABANDONED_SHIP_CORRIDORS_B1F:5","MAP_ABANDONED_SHIP_CORRIDORS_B1F:6/MAP_ABANDONED_SHIP_CORRIDORS_1F:10":"MAP_ABANDONED_SHIP_CORRIDORS_1F:10/MAP_ABANDONED_SHIP_CORRIDORS_B1F:6","MAP_ABANDONED_SHIP_CORRIDORS_B1F:7/MAP_ABANDONED_SHIP_CORRIDORS_1F:9":"MAP_ABANDONED_SHIP_CORRIDORS_1F:9/MAP_ABANDONED_SHIP_CORRIDORS_B1F:7","MAP_ABANDONED_SHIP_DECK:0,1/MAP_ROUTE108:0":"MAP_ROUTE108:0/MAP_ABANDONED_SHIP_DECK:0","MAP_ABANDONED_SHIP_DECK:2/MAP_ABANDONED_SHIP_CORRIDORS_1F:1":"MAP_ABANDONED_SHIP_CORRIDORS_1F:0,1/MAP_ABANDONED_SHIP_DECK:2","MAP_ABANDONED_SHIP_DECK:3/MAP_ABANDONED_SHIP_CORRIDORS_1F:2":"MAP_ABANDONED_SHIP_CORRIDORS_1F:2,3/MAP_ABANDONED_SHIP_DECK:3","MAP_ABANDONED_SHIP_DECK:4/MAP_ABANDONED_SHIP_CAPTAINS_OFFICE:0":"MAP_ABANDONED_SHIP_CAPTAINS_OFFICE:0,1/MAP_ABANDONED_SHIP_DECK:4","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:0/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:0":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:0,1/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:0","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:1/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:2":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:2,3/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:1","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:2/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:4":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:4,5/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:2","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:3/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:6":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:6/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:3","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:4/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:7":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:7/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:4","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:5/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:8":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:8/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:5","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:0,1/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:0":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:0/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:0","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:2,3/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:1":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:1/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:2","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:4,5/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:2":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:2/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:4","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:6/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:3":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:3/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:6","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:7/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:4":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:4/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:7","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:8/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:5":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:5/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:8","MAP_ABANDONED_SHIP_ROOMS2_1F:0,1/MAP_ABANDONED_SHIP_CORRIDORS_1F:8":"MAP_ABANDONED_SHIP_CORRIDORS_1F:8/MAP_ABANDONED_SHIP_ROOMS2_1F:0","MAP_ABANDONED_SHIP_ROOMS2_1F:2/MAP_ABANDONED_SHIP_CORRIDORS_1F:11":"MAP_ABANDONED_SHIP_CORRIDORS_1F:11/MAP_ABANDONED_SHIP_ROOMS2_1F:2","MAP_ABANDONED_SHIP_ROOMS2_B1F:0,1/MAP_ABANDONED_SHIP_CORRIDORS_B1F:1":"MAP_ABANDONED_SHIP_CORRIDORS_B1F:1/MAP_ABANDONED_SHIP_ROOMS2_B1F:0","MAP_ABANDONED_SHIP_ROOMS2_B1F:2,3/MAP_ABANDONED_SHIP_CORRIDORS_B1F:0":"MAP_ABANDONED_SHIP_CORRIDORS_B1F:0/MAP_ABANDONED_SHIP_ROOMS2_B1F:2","MAP_ABANDONED_SHIP_ROOMS_1F:0,1/MAP_ABANDONED_SHIP_CORRIDORS_1F:4":"MAP_ABANDONED_SHIP_CORRIDORS_1F:4/MAP_ABANDONED_SHIP_ROOMS_1F:0","MAP_ABANDONED_SHIP_ROOMS_1F:2/MAP_ABANDONED_SHIP_CORRIDORS_1F:6":"MAP_ABANDONED_SHIP_CORRIDORS_1F:6/MAP_ABANDONED_SHIP_ROOMS_1F:2","MAP_ABANDONED_SHIP_ROOMS_1F:3,5/MAP_ABANDONED_SHIP_CORRIDORS_1F:5":"MAP_ABANDONED_SHIP_CORRIDORS_1F:5/MAP_ABANDONED_SHIP_ROOMS_1F:3","MAP_ABANDONED_SHIP_ROOMS_1F:4/MAP_ABANDONED_SHIP_CORRIDORS_1F:7":"MAP_ABANDONED_SHIP_CORRIDORS_1F:7/MAP_ABANDONED_SHIP_ROOMS_1F:4","MAP_ABANDONED_SHIP_ROOMS_B1F:0/MAP_ABANDONED_SHIP_CORRIDORS_B1F:2":"MAP_ABANDONED_SHIP_CORRIDORS_B1F:2/MAP_ABANDONED_SHIP_ROOMS_B1F:0","MAP_ABANDONED_SHIP_ROOMS_B1F:1/MAP_ABANDONED_SHIP_CORRIDORS_B1F:3":"MAP_ABANDONED_SHIP_CORRIDORS_B1F:3/MAP_ABANDONED_SHIP_ROOMS_B1F:1","MAP_ABANDONED_SHIP_ROOMS_B1F:2/MAP_ABANDONED_SHIP_CORRIDORS_B1F:4":"MAP_ABANDONED_SHIP_CORRIDORS_B1F:4/MAP_ABANDONED_SHIP_ROOMS_B1F:2","MAP_ABANDONED_SHIP_ROOM_B1F:0,1/MAP_ABANDONED_SHIP_CORRIDORS_B1F:5":"MAP_ABANDONED_SHIP_CORRIDORS_B1F:5/MAP_ABANDONED_SHIP_ROOM_B1F:0","MAP_ABANDONED_SHIP_UNDERWATER1:0,1/MAP_ABANDONED_SHIP_UNDERWATER2:0":"MAP_ABANDONED_SHIP_UNDERWATER2:0/MAP_ABANDONED_SHIP_UNDERWATER1:0","MAP_ABANDONED_SHIP_UNDERWATER2:0/MAP_ABANDONED_SHIP_UNDERWATER1:0":"MAP_ABANDONED_SHIP_UNDERWATER1:0,1/MAP_ABANDONED_SHIP_UNDERWATER2:0","MAP_ALTERING_CAVE:0/MAP_ROUTE103:0":"MAP_ROUTE103:0/MAP_ALTERING_CAVE:0","MAP_ANCIENT_TOMB:0/MAP_ROUTE120:0":"MAP_ROUTE120:0/MAP_ANCIENT_TOMB:0","MAP_ANCIENT_TOMB:1/MAP_ANCIENT_TOMB:2":"MAP_ANCIENT_TOMB:2/MAP_ANCIENT_TOMB:1","MAP_ANCIENT_TOMB:2/MAP_ANCIENT_TOMB:1":"MAP_ANCIENT_TOMB:1/MAP_ANCIENT_TOMB:2","MAP_AQUA_HIDEOUT_1F:0,1/MAP_LILYCOVE_CITY:6":"MAP_LILYCOVE_CITY:6/MAP_AQUA_HIDEOUT_1F:0","MAP_AQUA_HIDEOUT_1F:2/MAP_AQUA_HIDEOUT_B1F:0":"MAP_AQUA_HIDEOUT_B1F:0/MAP_AQUA_HIDEOUT_1F:2","MAP_AQUA_HIDEOUT_B1F:0/MAP_AQUA_HIDEOUT_1F:2":"MAP_AQUA_HIDEOUT_1F:2/MAP_AQUA_HIDEOUT_B1F:0","MAP_AQUA_HIDEOUT_B1F:1/MAP_AQUA_HIDEOUT_B2F:0":"MAP_AQUA_HIDEOUT_B2F:0/MAP_AQUA_HIDEOUT_B1F:1","MAP_AQUA_HIDEOUT_B1F:10/MAP_AQUA_HIDEOUT_B1F:6":"MAP_AQUA_HIDEOUT_B1F:6/MAP_AQUA_HIDEOUT_B1F:10","MAP_AQUA_HIDEOUT_B1F:11/MAP_AQUA_HIDEOUT_B1F:22":"MAP_AQUA_HIDEOUT_B1F:22/MAP_AQUA_HIDEOUT_B1F:11","MAP_AQUA_HIDEOUT_B1F:12/MAP_AQUA_HIDEOUT_B1F:9":"MAP_AQUA_HIDEOUT_B1F:9/MAP_AQUA_HIDEOUT_B1F:12","MAP_AQUA_HIDEOUT_B1F:13/MAP_AQUA_HIDEOUT_B1F:18":"MAP_AQUA_HIDEOUT_B1F:18/MAP_AQUA_HIDEOUT_B1F:13","MAP_AQUA_HIDEOUT_B1F:14/MAP_AQUA_HIDEOUT_B1F:12!":"MAP_AQUA_HIDEOUT_B1F:12/MAP_AQUA_HIDEOUT_B1F:9","MAP_AQUA_HIDEOUT_B1F:15/MAP_AQUA_HIDEOUT_B1F:16":"MAP_AQUA_HIDEOUT_B1F:16/MAP_AQUA_HIDEOUT_B1F:15","MAP_AQUA_HIDEOUT_B1F:16/MAP_AQUA_HIDEOUT_B1F:15":"MAP_AQUA_HIDEOUT_B1F:15/MAP_AQUA_HIDEOUT_B1F:16","MAP_AQUA_HIDEOUT_B1F:17/MAP_AQUA_HIDEOUT_B1F:20":"MAP_AQUA_HIDEOUT_B1F:20/MAP_AQUA_HIDEOUT_B1F:17","MAP_AQUA_HIDEOUT_B1F:18/MAP_AQUA_HIDEOUT_B1F:13":"MAP_AQUA_HIDEOUT_B1F:13/MAP_AQUA_HIDEOUT_B1F:18","MAP_AQUA_HIDEOUT_B1F:19/MAP_AQUA_HIDEOUT_B1F:24":"MAP_AQUA_HIDEOUT_B1F:24/MAP_AQUA_HIDEOUT_B1F:19","MAP_AQUA_HIDEOUT_B1F:2/MAP_AQUA_HIDEOUT_B2F:1":"MAP_AQUA_HIDEOUT_B2F:1/MAP_AQUA_HIDEOUT_B1F:2","MAP_AQUA_HIDEOUT_B1F:20/MAP_AQUA_HIDEOUT_B1F:17":"MAP_AQUA_HIDEOUT_B1F:17/MAP_AQUA_HIDEOUT_B1F:20","MAP_AQUA_HIDEOUT_B1F:21/MAP_AQUA_HIDEOUT_B1F:12!":"MAP_AQUA_HIDEOUT_B1F:12/MAP_AQUA_HIDEOUT_B1F:9","MAP_AQUA_HIDEOUT_B1F:22/MAP_AQUA_HIDEOUT_B1F:11":"MAP_AQUA_HIDEOUT_B1F:11/MAP_AQUA_HIDEOUT_B1F:22","MAP_AQUA_HIDEOUT_B1F:23/MAP_AQUA_HIDEOUT_B1F:17!":"MAP_AQUA_HIDEOUT_B1F:17/MAP_AQUA_HIDEOUT_B1F:20","MAP_AQUA_HIDEOUT_B1F:24/MAP_AQUA_HIDEOUT_B1F:19":"MAP_AQUA_HIDEOUT_B1F:19/MAP_AQUA_HIDEOUT_B1F:24","MAP_AQUA_HIDEOUT_B1F:3/MAP_AQUA_HIDEOUT_B2F:2":"MAP_AQUA_HIDEOUT_B2F:2/MAP_AQUA_HIDEOUT_B1F:3","MAP_AQUA_HIDEOUT_B1F:4/MAP_AQUA_HIDEOUT_B1F:7":"MAP_AQUA_HIDEOUT_B1F:7/MAP_AQUA_HIDEOUT_B1F:4","MAP_AQUA_HIDEOUT_B1F:5/MAP_AQUA_HIDEOUT_B1F:8":"MAP_AQUA_HIDEOUT_B1F:8/MAP_AQUA_HIDEOUT_B1F:5","MAP_AQUA_HIDEOUT_B1F:6/MAP_AQUA_HIDEOUT_B1F:10":"MAP_AQUA_HIDEOUT_B1F:10/MAP_AQUA_HIDEOUT_B1F:6","MAP_AQUA_HIDEOUT_B1F:7/MAP_AQUA_HIDEOUT_B1F:4":"MAP_AQUA_HIDEOUT_B1F:4/MAP_AQUA_HIDEOUT_B1F:7","MAP_AQUA_HIDEOUT_B1F:8/MAP_AQUA_HIDEOUT_B1F:5":"MAP_AQUA_HIDEOUT_B1F:5/MAP_AQUA_HIDEOUT_B1F:8","MAP_AQUA_HIDEOUT_B1F:9/MAP_AQUA_HIDEOUT_B1F:12":"MAP_AQUA_HIDEOUT_B1F:12/MAP_AQUA_HIDEOUT_B1F:9","MAP_AQUA_HIDEOUT_B2F:0/MAP_AQUA_HIDEOUT_B1F:1":"MAP_AQUA_HIDEOUT_B1F:1/MAP_AQUA_HIDEOUT_B2F:0","MAP_AQUA_HIDEOUT_B2F:1/MAP_AQUA_HIDEOUT_B1F:2":"MAP_AQUA_HIDEOUT_B1F:2/MAP_AQUA_HIDEOUT_B2F:1","MAP_AQUA_HIDEOUT_B2F:2/MAP_AQUA_HIDEOUT_B1F:3":"MAP_AQUA_HIDEOUT_B1F:3/MAP_AQUA_HIDEOUT_B2F:2","MAP_AQUA_HIDEOUT_B2F:3/MAP_AQUA_HIDEOUT_B2F:5":"MAP_AQUA_HIDEOUT_B2F:5/MAP_AQUA_HIDEOUT_B2F:3","MAP_AQUA_HIDEOUT_B2F:4/MAP_AQUA_HIDEOUT_B2F:8":"MAP_AQUA_HIDEOUT_B2F:8/MAP_AQUA_HIDEOUT_B2F:4","MAP_AQUA_HIDEOUT_B2F:5/MAP_AQUA_HIDEOUT_B2F:3":"MAP_AQUA_HIDEOUT_B2F:3/MAP_AQUA_HIDEOUT_B2F:5","MAP_AQUA_HIDEOUT_B2F:6/MAP_AQUA_HIDEOUT_B2F:7":"MAP_AQUA_HIDEOUT_B2F:7/MAP_AQUA_HIDEOUT_B2F:6","MAP_AQUA_HIDEOUT_B2F:7/MAP_AQUA_HIDEOUT_B2F:6":"MAP_AQUA_HIDEOUT_B2F:6/MAP_AQUA_HIDEOUT_B2F:7","MAP_AQUA_HIDEOUT_B2F:8/MAP_AQUA_HIDEOUT_B2F:4":"MAP_AQUA_HIDEOUT_B2F:4/MAP_AQUA_HIDEOUT_B2F:8","MAP_AQUA_HIDEOUT_B2F:9/MAP_AQUA_HIDEOUT_B1F:4!":"MAP_AQUA_HIDEOUT_B1F:4/MAP_AQUA_HIDEOUT_B1F:7","MAP_ARTISAN_CAVE_1F:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:13":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:13/MAP_ARTISAN_CAVE_1F:0","MAP_ARTISAN_CAVE_1F:1/MAP_ARTISAN_CAVE_B1F:1":"MAP_ARTISAN_CAVE_B1F:1/MAP_ARTISAN_CAVE_1F:1","MAP_ARTISAN_CAVE_B1F:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:10":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:10/MAP_ARTISAN_CAVE_B1F:0","MAP_ARTISAN_CAVE_B1F:1/MAP_ARTISAN_CAVE_1F:1":"MAP_ARTISAN_CAVE_1F:1/MAP_ARTISAN_CAVE_B1F:1","MAP_BATTLE_COLOSSEUM_2P:0,1/MAP_DYNAMIC:-1!":"","MAP_BATTLE_COLOSSEUM_4P:0,1,2,3/MAP_DYNAMIC:-1!":"","MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:1":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:1/MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_DOME_CORRIDOR:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1!":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1/MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1/MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_DOME_PRE_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1!":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1/MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:2":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:2/MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2":"MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0","MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:2":"MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:0","MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0":"MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2","MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:3/MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0!":"MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2","MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:2":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:0":"MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:2","MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY:0,1,2/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:0":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:0/MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:3":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:3/MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:2":"MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:2/MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM:0","MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:0":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:0/MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:2/MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM:0":"MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:2","MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER:0,1,2/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:6":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:6/MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER:0","MAP_BATTLE_FRONTIER_LOUNGE1:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:5":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:5/MAP_BATTLE_FRONTIER_LOUNGE1:0","MAP_BATTLE_FRONTIER_LOUNGE2:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:3":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:3/MAP_BATTLE_FRONTIER_LOUNGE2:0","MAP_BATTLE_FRONTIER_LOUNGE3:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:9":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:9/MAP_BATTLE_FRONTIER_LOUNGE3:0","MAP_BATTLE_FRONTIER_LOUNGE4:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:6":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:6/MAP_BATTLE_FRONTIER_LOUNGE4:0","MAP_BATTLE_FRONTIER_LOUNGE5:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:7":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:7/MAP_BATTLE_FRONTIER_LOUNGE5:0","MAP_BATTLE_FRONTIER_LOUNGE6:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:8":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:8/MAP_BATTLE_FRONTIER_LOUNGE6:0","MAP_BATTLE_FRONTIER_LOUNGE7:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:7":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:7/MAP_BATTLE_FRONTIER_LOUNGE7:0","MAP_BATTLE_FRONTIER_LOUNGE8:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:10":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:10/MAP_BATTLE_FRONTIER_LOUNGE8:0","MAP_BATTLE_FRONTIER_LOUNGE9:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:11":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:11/MAP_BATTLE_FRONTIER_LOUNGE9:0","MAP_BATTLE_FRONTIER_MART:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:4":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:4/MAP_BATTLE_FRONTIER_MART:0","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:0/MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:0":"MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:0","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:1/MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY:0":"MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:1","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:10/MAP_BATTLE_FRONTIER_LOUNGE8:0":"MAP_BATTLE_FRONTIER_LOUNGE8:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:10","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:11/MAP_BATTLE_FRONTIER_LOUNGE9:0":"MAP_BATTLE_FRONTIER_LOUNGE9:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:11","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:12/MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:0":"MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:12","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:13/MAP_ARTISAN_CAVE_1F:0":"MAP_ARTISAN_CAVE_1F:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:13","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:0":"MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:2","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:3/MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY:0":"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:3","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:4/MAP_BATTLE_FRONTIER_RANKING_HALL:0":"MAP_BATTLE_FRONTIER_RANKING_HALL:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:4","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:5/MAP_BATTLE_FRONTIER_LOUNGE1:0":"MAP_BATTLE_FRONTIER_LOUNGE1:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:5","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:6/MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER:0":"MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER:0,1,2/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:6","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:7/MAP_BATTLE_FRONTIER_LOUNGE5:0":"MAP_BATTLE_FRONTIER_LOUNGE5:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:7","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:8/MAP_BATTLE_FRONTIER_LOUNGE6:0":"MAP_BATTLE_FRONTIER_LOUNGE6:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:8","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:9/MAP_BATTLE_FRONTIER_LOUNGE3:0":"MAP_BATTLE_FRONTIER_LOUNGE3:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:9","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:0/MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY:0":"MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY:0,1,2/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:0","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1/MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0":"MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:10/MAP_ARTISAN_CAVE_B1F:0":"MAP_ARTISAN_CAVE_B1F:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:10","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:2/MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY:0":"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:2","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:3/MAP_BATTLE_FRONTIER_LOUNGE2:0":"MAP_BATTLE_FRONTIER_LOUNGE2:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:3","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:4/MAP_BATTLE_FRONTIER_MART:0":"MAP_BATTLE_FRONTIER_MART:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:4","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:5/MAP_BATTLE_FRONTIER_SCOTTS_HOUSE:0":"MAP_BATTLE_FRONTIER_SCOTTS_HOUSE:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:5","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:6/MAP_BATTLE_FRONTIER_LOUNGE4:0":"MAP_BATTLE_FRONTIER_LOUNGE4:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:6","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:7/MAP_BATTLE_FRONTIER_LOUNGE7:0":"MAP_BATTLE_FRONTIER_LOUNGE7:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:7","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:8/MAP_BATTLE_FRONTIER_RECEPTION_GATE:0":"MAP_BATTLE_FRONTIER_RECEPTION_GATE:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:8","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:9/MAP_BATTLE_FRONTIER_RECEPTION_GATE:1":"MAP_BATTLE_FRONTIER_RECEPTION_GATE:1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:9","MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:12":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:12/MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:0","MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:2/MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:0":"MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:0/MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:2","MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:0/MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:2":"MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:2/MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:0","MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_BATTLE_FRONTIER_RANKING_HALL:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:4":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:4/MAP_BATTLE_FRONTIER_RANKING_HALL:0","MAP_BATTLE_FRONTIER_RECEPTION_GATE:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:8":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:8/MAP_BATTLE_FRONTIER_RECEPTION_GATE:0","MAP_BATTLE_FRONTIER_RECEPTION_GATE:1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:9":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:9/MAP_BATTLE_FRONTIER_RECEPTION_GATE:1","MAP_BATTLE_FRONTIER_SCOTTS_HOUSE:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:5":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:5/MAP_BATTLE_FRONTIER_SCOTTS_HOUSE:0","MAP_BIRTH_ISLAND_EXTERIOR:0/MAP_BIRTH_ISLAND_HARBOR:0":"MAP_BIRTH_ISLAND_HARBOR:0/MAP_BIRTH_ISLAND_EXTERIOR:0","MAP_BIRTH_ISLAND_HARBOR:0/MAP_BIRTH_ISLAND_EXTERIOR:0":"MAP_BIRTH_ISLAND_EXTERIOR:0/MAP_BIRTH_ISLAND_HARBOR:0","MAP_CAVE_OF_ORIGIN_1F:0/MAP_CAVE_OF_ORIGIN_ENTRANCE:1":"MAP_CAVE_OF_ORIGIN_ENTRANCE:1/MAP_CAVE_OF_ORIGIN_1F:0","MAP_CAVE_OF_ORIGIN_1F:1/MAP_CAVE_OF_ORIGIN_B1F:0":"MAP_CAVE_OF_ORIGIN_B1F:0/MAP_CAVE_OF_ORIGIN_1F:1","MAP_CAVE_OF_ORIGIN_B1F:0/MAP_CAVE_OF_ORIGIN_1F:1":"MAP_CAVE_OF_ORIGIN_1F:1/MAP_CAVE_OF_ORIGIN_B1F:0","MAP_CAVE_OF_ORIGIN_ENTRANCE:0/MAP_SOOTOPOLIS_CITY:3":"MAP_SOOTOPOLIS_CITY:3/MAP_CAVE_OF_ORIGIN_ENTRANCE:0","MAP_CAVE_OF_ORIGIN_ENTRANCE:1/MAP_CAVE_OF_ORIGIN_1F:0":"MAP_CAVE_OF_ORIGIN_1F:0/MAP_CAVE_OF_ORIGIN_ENTRANCE:1","MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1:0/MAP_CAVE_OF_ORIGIN_1F:1!":"MAP_CAVE_OF_ORIGIN_1F:1/MAP_CAVE_OF_ORIGIN_B1F:0","MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1:1/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:0":"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:0/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1:1","MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:0/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1:1":"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1:1/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:0","MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:1/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:0":"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:0/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:1","MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:0/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:1":"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:1/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:0","MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:1/MAP_CAVE_OF_ORIGIN_B1F:0!":"MAP_CAVE_OF_ORIGIN_B1F:0/MAP_CAVE_OF_ORIGIN_1F:1","MAP_DESERT_RUINS:0/MAP_ROUTE111:1":"MAP_ROUTE111:1/MAP_DESERT_RUINS:0","MAP_DESERT_RUINS:1/MAP_DESERT_RUINS:2":"MAP_DESERT_RUINS:2/MAP_DESERT_RUINS:1","MAP_DESERT_RUINS:2/MAP_DESERT_RUINS:1":"MAP_DESERT_RUINS:1/MAP_DESERT_RUINS:2","MAP_DESERT_UNDERPASS:0/MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:2":"MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:2/MAP_DESERT_UNDERPASS:0","MAP_DEWFORD_TOWN:0/MAP_DEWFORD_TOWN_HALL:0":"MAP_DEWFORD_TOWN_HALL:0,1/MAP_DEWFORD_TOWN:0","MAP_DEWFORD_TOWN:1/MAP_DEWFORD_TOWN_POKEMON_CENTER_1F:0":"MAP_DEWFORD_TOWN_POKEMON_CENTER_1F:0,1/MAP_DEWFORD_TOWN:1","MAP_DEWFORD_TOWN:2/MAP_DEWFORD_TOWN_GYM:0":"MAP_DEWFORD_TOWN_GYM:0,1/MAP_DEWFORD_TOWN:2","MAP_DEWFORD_TOWN:3/MAP_DEWFORD_TOWN_HOUSE1:0":"MAP_DEWFORD_TOWN_HOUSE1:0,1/MAP_DEWFORD_TOWN:3","MAP_DEWFORD_TOWN:4/MAP_DEWFORD_TOWN_HOUSE2:0":"MAP_DEWFORD_TOWN_HOUSE2:0,1/MAP_DEWFORD_TOWN:4","MAP_DEWFORD_TOWN_GYM:0,1/MAP_DEWFORD_TOWN:2":"MAP_DEWFORD_TOWN:2/MAP_DEWFORD_TOWN_GYM:0","MAP_DEWFORD_TOWN_HALL:0,1/MAP_DEWFORD_TOWN:0":"MAP_DEWFORD_TOWN:0/MAP_DEWFORD_TOWN_HALL:0","MAP_DEWFORD_TOWN_HOUSE1:0,1/MAP_DEWFORD_TOWN:3":"MAP_DEWFORD_TOWN:3/MAP_DEWFORD_TOWN_HOUSE1:0","MAP_DEWFORD_TOWN_HOUSE2:0,1/MAP_DEWFORD_TOWN:4":"MAP_DEWFORD_TOWN:4/MAP_DEWFORD_TOWN_HOUSE2:0","MAP_DEWFORD_TOWN_POKEMON_CENTER_1F:0,1/MAP_DEWFORD_TOWN:1":"MAP_DEWFORD_TOWN:1/MAP_DEWFORD_TOWN_POKEMON_CENTER_1F:0","MAP_DEWFORD_TOWN_POKEMON_CENTER_1F:2/MAP_DEWFORD_TOWN_POKEMON_CENTER_2F:0":"MAP_DEWFORD_TOWN_POKEMON_CENTER_2F:0/MAP_DEWFORD_TOWN_POKEMON_CENTER_1F:2","MAP_DEWFORD_TOWN_POKEMON_CENTER_2F:0/MAP_DEWFORD_TOWN_POKEMON_CENTER_1F:2":"MAP_DEWFORD_TOWN_POKEMON_CENTER_1F:2/MAP_DEWFORD_TOWN_POKEMON_CENTER_2F:0","MAP_DEWFORD_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_DEWFORD_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_EVER_GRANDE_CITY:0/MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:0":"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:0,1/MAP_EVER_GRANDE_CITY:0","MAP_EVER_GRANDE_CITY:1/MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F:0":"MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F:0,1/MAP_EVER_GRANDE_CITY:1","MAP_EVER_GRANDE_CITY:2/MAP_VICTORY_ROAD_1F:0":"MAP_VICTORY_ROAD_1F:0/MAP_EVER_GRANDE_CITY:2","MAP_EVER_GRANDE_CITY:3/MAP_VICTORY_ROAD_1F:1":"MAP_VICTORY_ROAD_1F:1/MAP_EVER_GRANDE_CITY:3","MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM:0/MAP_EVER_GRANDE_CITY_HALL4:1":"MAP_EVER_GRANDE_CITY_HALL4:1/MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM:0","MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM:1/MAP_EVER_GRANDE_CITY_HALL_OF_FAME:0":"MAP_EVER_GRANDE_CITY_HALL_OF_FAME:0/MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM:1","MAP_EVER_GRANDE_CITY_DRAKES_ROOM:0/MAP_EVER_GRANDE_CITY_HALL3:1":"MAP_EVER_GRANDE_CITY_HALL3:1/MAP_EVER_GRANDE_CITY_DRAKES_ROOM:0","MAP_EVER_GRANDE_CITY_DRAKES_ROOM:1/MAP_EVER_GRANDE_CITY_HALL4:0":"MAP_EVER_GRANDE_CITY_HALL4:0/MAP_EVER_GRANDE_CITY_DRAKES_ROOM:1","MAP_EVER_GRANDE_CITY_GLACIAS_ROOM:0/MAP_EVER_GRANDE_CITY_HALL2:1":"MAP_EVER_GRANDE_CITY_HALL2:1/MAP_EVER_GRANDE_CITY_GLACIAS_ROOM:0","MAP_EVER_GRANDE_CITY_GLACIAS_ROOM:1/MAP_EVER_GRANDE_CITY_HALL3:0":"MAP_EVER_GRANDE_CITY_HALL3:0,2,3/MAP_EVER_GRANDE_CITY_GLACIAS_ROOM:1","MAP_EVER_GRANDE_CITY_HALL1:0,2,3/MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM:1":"MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM:1/MAP_EVER_GRANDE_CITY_HALL1:0","MAP_EVER_GRANDE_CITY_HALL1:1/MAP_EVER_GRANDE_CITY_PHOEBES_ROOM:0":"MAP_EVER_GRANDE_CITY_PHOEBES_ROOM:0/MAP_EVER_GRANDE_CITY_HALL1:1","MAP_EVER_GRANDE_CITY_HALL2:0,2,3/MAP_EVER_GRANDE_CITY_PHOEBES_ROOM:1":"MAP_EVER_GRANDE_CITY_PHOEBES_ROOM:1/MAP_EVER_GRANDE_CITY_HALL2:0","MAP_EVER_GRANDE_CITY_HALL2:1/MAP_EVER_GRANDE_CITY_GLACIAS_ROOM:0":"MAP_EVER_GRANDE_CITY_GLACIAS_ROOM:0/MAP_EVER_GRANDE_CITY_HALL2:1","MAP_EVER_GRANDE_CITY_HALL3:0,2,3/MAP_EVER_GRANDE_CITY_GLACIAS_ROOM:1":"MAP_EVER_GRANDE_CITY_GLACIAS_ROOM:1/MAP_EVER_GRANDE_CITY_HALL3:0","MAP_EVER_GRANDE_CITY_HALL3:1/MAP_EVER_GRANDE_CITY_DRAKES_ROOM:0":"MAP_EVER_GRANDE_CITY_DRAKES_ROOM:0/MAP_EVER_GRANDE_CITY_HALL3:1","MAP_EVER_GRANDE_CITY_HALL4:0/MAP_EVER_GRANDE_CITY_DRAKES_ROOM:1":"MAP_EVER_GRANDE_CITY_DRAKES_ROOM:1/MAP_EVER_GRANDE_CITY_HALL4:0","MAP_EVER_GRANDE_CITY_HALL4:1/MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM:0":"MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM:0/MAP_EVER_GRANDE_CITY_HALL4:1","MAP_EVER_GRANDE_CITY_HALL5:0,2,3/MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:2":"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:2,3/MAP_EVER_GRANDE_CITY_HALL5:0","MAP_EVER_GRANDE_CITY_HALL5:1/MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM:0":"MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM:0/MAP_EVER_GRANDE_CITY_HALL5:1","MAP_EVER_GRANDE_CITY_HALL_OF_FAME:0/MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM:1":"MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM:1/MAP_EVER_GRANDE_CITY_HALL_OF_FAME:0","MAP_EVER_GRANDE_CITY_PHOEBES_ROOM:0/MAP_EVER_GRANDE_CITY_HALL1:1":"MAP_EVER_GRANDE_CITY_HALL1:1/MAP_EVER_GRANDE_CITY_PHOEBES_ROOM:0","MAP_EVER_GRANDE_CITY_PHOEBES_ROOM:1/MAP_EVER_GRANDE_CITY_HALL2:0":"MAP_EVER_GRANDE_CITY_HALL2:0,2,3/MAP_EVER_GRANDE_CITY_PHOEBES_ROOM:1","MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F:0,1/MAP_EVER_GRANDE_CITY:1":"MAP_EVER_GRANDE_CITY:1/MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F:0","MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F:2/MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F:0":"MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F:0/MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F:2","MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F:0/MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F:2":"MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F:2/MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F:0","MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:0,1/MAP_EVER_GRANDE_CITY:0":"MAP_EVER_GRANDE_CITY:0/MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:0","MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:2,3/MAP_EVER_GRANDE_CITY_HALL5:0":"MAP_EVER_GRANDE_CITY_HALL5:0,2,3/MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:2","MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:4/MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F:0":"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F:0/MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:4","MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F:0/MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:4":"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:4/MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F:0","MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM:0/MAP_EVER_GRANDE_CITY_HALL5:1":"MAP_EVER_GRANDE_CITY_HALL5:1/MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM:0","MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM:1/MAP_EVER_GRANDE_CITY_HALL1:0":"MAP_EVER_GRANDE_CITY_HALL1:0,2,3/MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM:1","MAP_FALLARBOR_TOWN:0/MAP_FALLARBOR_TOWN_MART:0":"MAP_FALLARBOR_TOWN_MART:0,1/MAP_FALLARBOR_TOWN:0","MAP_FALLARBOR_TOWN:1/MAP_FALLARBOR_TOWN_BATTLE_TENT_LOBBY:0":"MAP_FALLARBOR_TOWN_BATTLE_TENT_LOBBY:0,1/MAP_FALLARBOR_TOWN:1","MAP_FALLARBOR_TOWN:2/MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F:0":"MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F:0,1/MAP_FALLARBOR_TOWN:2","MAP_FALLARBOR_TOWN:3/MAP_FALLARBOR_TOWN_COZMOS_HOUSE:0":"MAP_FALLARBOR_TOWN_COZMOS_HOUSE:0,1/MAP_FALLARBOR_TOWN:3","MAP_FALLARBOR_TOWN:4/MAP_FALLARBOR_TOWN_MOVE_RELEARNERS_HOUSE:0":"MAP_FALLARBOR_TOWN_MOVE_RELEARNERS_HOUSE:0,1/MAP_FALLARBOR_TOWN:4","MAP_FALLARBOR_TOWN_BATTLE_TENT_LOBBY:0,1/MAP_FALLARBOR_TOWN:1":"MAP_FALLARBOR_TOWN:1/MAP_FALLARBOR_TOWN_BATTLE_TENT_LOBBY:0","MAP_FALLARBOR_TOWN_COZMOS_HOUSE:0,1/MAP_FALLARBOR_TOWN:3":"MAP_FALLARBOR_TOWN:3/MAP_FALLARBOR_TOWN_COZMOS_HOUSE:0","MAP_FALLARBOR_TOWN_MART:0,1/MAP_FALLARBOR_TOWN:0":"MAP_FALLARBOR_TOWN:0/MAP_FALLARBOR_TOWN_MART:0","MAP_FALLARBOR_TOWN_MOVE_RELEARNERS_HOUSE:0,1/MAP_FALLARBOR_TOWN:4":"MAP_FALLARBOR_TOWN:4/MAP_FALLARBOR_TOWN_MOVE_RELEARNERS_HOUSE:0","MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F:0,1/MAP_FALLARBOR_TOWN:2":"MAP_FALLARBOR_TOWN:2/MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F:0","MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F:2/MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F:0":"MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F:0/MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F:2","MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F:0/MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F:2":"MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F:2/MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F:0","MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_FARAWAY_ISLAND_ENTRANCE:0,1/MAP_FARAWAY_ISLAND_INTERIOR:0,1":"MAP_FARAWAY_ISLAND_INTERIOR:0,1/MAP_FARAWAY_ISLAND_ENTRANCE:0,1","MAP_FARAWAY_ISLAND_INTERIOR:0,1/MAP_FARAWAY_ISLAND_ENTRANCE:0,1":"MAP_FARAWAY_ISLAND_ENTRANCE:0,1/MAP_FARAWAY_ISLAND_INTERIOR:0,1","MAP_FIERY_PATH:0/MAP_ROUTE112:4":"MAP_ROUTE112:4/MAP_FIERY_PATH:0","MAP_FIERY_PATH:1/MAP_ROUTE112:5":"MAP_ROUTE112:5/MAP_FIERY_PATH:1","MAP_FORTREE_CITY:0/MAP_FORTREE_CITY_POKEMON_CENTER_1F:0":"MAP_FORTREE_CITY_POKEMON_CENTER_1F:0,1/MAP_FORTREE_CITY:0","MAP_FORTREE_CITY:1/MAP_FORTREE_CITY_HOUSE1:0":"MAP_FORTREE_CITY_HOUSE1:0,1/MAP_FORTREE_CITY:1","MAP_FORTREE_CITY:2/MAP_FORTREE_CITY_GYM:0":"MAP_FORTREE_CITY_GYM:0,1/MAP_FORTREE_CITY:2","MAP_FORTREE_CITY:3/MAP_FORTREE_CITY_MART:0":"MAP_FORTREE_CITY_MART:0,1/MAP_FORTREE_CITY:3","MAP_FORTREE_CITY:4/MAP_FORTREE_CITY_HOUSE2:0":"MAP_FORTREE_CITY_HOUSE2:0,1/MAP_FORTREE_CITY:4","MAP_FORTREE_CITY:5/MAP_FORTREE_CITY_HOUSE3:0":"MAP_FORTREE_CITY_HOUSE3:0,1/MAP_FORTREE_CITY:5","MAP_FORTREE_CITY:6/MAP_FORTREE_CITY_HOUSE4:0":"MAP_FORTREE_CITY_HOUSE4:0,1/MAP_FORTREE_CITY:6","MAP_FORTREE_CITY:7/MAP_FORTREE_CITY_HOUSE5:0":"MAP_FORTREE_CITY_HOUSE5:0,1/MAP_FORTREE_CITY:7","MAP_FORTREE_CITY:8/MAP_FORTREE_CITY_DECORATION_SHOP:0":"MAP_FORTREE_CITY_DECORATION_SHOP:0,1/MAP_FORTREE_CITY:8","MAP_FORTREE_CITY_DECORATION_SHOP:0,1/MAP_FORTREE_CITY:8":"MAP_FORTREE_CITY:8/MAP_FORTREE_CITY_DECORATION_SHOP:0","MAP_FORTREE_CITY_GYM:0,1/MAP_FORTREE_CITY:2":"MAP_FORTREE_CITY:2/MAP_FORTREE_CITY_GYM:0","MAP_FORTREE_CITY_HOUSE1:0,1/MAP_FORTREE_CITY:1":"MAP_FORTREE_CITY:1/MAP_FORTREE_CITY_HOUSE1:0","MAP_FORTREE_CITY_HOUSE2:0,1/MAP_FORTREE_CITY:4":"MAP_FORTREE_CITY:4/MAP_FORTREE_CITY_HOUSE2:0","MAP_FORTREE_CITY_HOUSE3:0,1/MAP_FORTREE_CITY:5":"MAP_FORTREE_CITY:5/MAP_FORTREE_CITY_HOUSE3:0","MAP_FORTREE_CITY_HOUSE4:0,1/MAP_FORTREE_CITY:6":"MAP_FORTREE_CITY:6/MAP_FORTREE_CITY_HOUSE4:0","MAP_FORTREE_CITY_HOUSE5:0,1/MAP_FORTREE_CITY:7":"MAP_FORTREE_CITY:7/MAP_FORTREE_CITY_HOUSE5:0","MAP_FORTREE_CITY_MART:0,1/MAP_FORTREE_CITY:3":"MAP_FORTREE_CITY:3/MAP_FORTREE_CITY_MART:0","MAP_FORTREE_CITY_POKEMON_CENTER_1F:0,1/MAP_FORTREE_CITY:0":"MAP_FORTREE_CITY:0/MAP_FORTREE_CITY_POKEMON_CENTER_1F:0","MAP_FORTREE_CITY_POKEMON_CENTER_1F:2/MAP_FORTREE_CITY_POKEMON_CENTER_2F:0":"MAP_FORTREE_CITY_POKEMON_CENTER_2F:0/MAP_FORTREE_CITY_POKEMON_CENTER_1F:2","MAP_FORTREE_CITY_POKEMON_CENTER_2F:0/MAP_FORTREE_CITY_POKEMON_CENTER_1F:2":"MAP_FORTREE_CITY_POKEMON_CENTER_1F:2/MAP_FORTREE_CITY_POKEMON_CENTER_2F:0","MAP_FORTREE_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_FORTREE_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_GRANITE_CAVE_1F:0/MAP_ROUTE106:0":"MAP_ROUTE106:0/MAP_GRANITE_CAVE_1F:0","MAP_GRANITE_CAVE_1F:1/MAP_GRANITE_CAVE_B1F:0":"MAP_GRANITE_CAVE_B1F:0/MAP_GRANITE_CAVE_1F:1","MAP_GRANITE_CAVE_1F:2/MAP_GRANITE_CAVE_B1F:1":"MAP_GRANITE_CAVE_B1F:1/MAP_GRANITE_CAVE_1F:2","MAP_GRANITE_CAVE_1F:3/MAP_GRANITE_CAVE_STEVENS_ROOM:0":"MAP_GRANITE_CAVE_STEVENS_ROOM:0/MAP_GRANITE_CAVE_1F:3","MAP_GRANITE_CAVE_B1F:0/MAP_GRANITE_CAVE_1F:1":"MAP_GRANITE_CAVE_1F:1/MAP_GRANITE_CAVE_B1F:0","MAP_GRANITE_CAVE_B1F:1/MAP_GRANITE_CAVE_1F:2":"MAP_GRANITE_CAVE_1F:2/MAP_GRANITE_CAVE_B1F:1","MAP_GRANITE_CAVE_B1F:2/MAP_GRANITE_CAVE_B2F:0":"MAP_GRANITE_CAVE_B2F:0/MAP_GRANITE_CAVE_B1F:2","MAP_GRANITE_CAVE_B1F:3/MAP_GRANITE_CAVE_B2F:1":"MAP_GRANITE_CAVE_B2F:1/MAP_GRANITE_CAVE_B1F:3","MAP_GRANITE_CAVE_B1F:4/MAP_GRANITE_CAVE_B2F:2":"MAP_GRANITE_CAVE_B2F:2/MAP_GRANITE_CAVE_B1F:4","MAP_GRANITE_CAVE_B1F:5/MAP_GRANITE_CAVE_B2F:3":"MAP_GRANITE_CAVE_B2F:3/MAP_GRANITE_CAVE_B1F:5","MAP_GRANITE_CAVE_B1F:6/MAP_GRANITE_CAVE_B2F:4":"MAP_GRANITE_CAVE_B2F:4/MAP_GRANITE_CAVE_B1F:6","MAP_GRANITE_CAVE_B2F:0/MAP_GRANITE_CAVE_B1F:2":"MAP_GRANITE_CAVE_B1F:2/MAP_GRANITE_CAVE_B2F:0","MAP_GRANITE_CAVE_B2F:1/MAP_GRANITE_CAVE_B1F:3":"MAP_GRANITE_CAVE_B1F:3/MAP_GRANITE_CAVE_B2F:1","MAP_GRANITE_CAVE_B2F:2/MAP_GRANITE_CAVE_B1F:4":"MAP_GRANITE_CAVE_B1F:4/MAP_GRANITE_CAVE_B2F:2","MAP_GRANITE_CAVE_B2F:3/MAP_GRANITE_CAVE_B1F:5":"MAP_GRANITE_CAVE_B1F:5/MAP_GRANITE_CAVE_B2F:3","MAP_GRANITE_CAVE_B2F:4/MAP_GRANITE_CAVE_B1F:6":"MAP_GRANITE_CAVE_B1F:6/MAP_GRANITE_CAVE_B2F:4","MAP_GRANITE_CAVE_STEVENS_ROOM:0/MAP_GRANITE_CAVE_1F:3":"MAP_GRANITE_CAVE_1F:3/MAP_GRANITE_CAVE_STEVENS_ROOM:0","MAP_INSIDE_OF_TRUCK:0,1,2/MAP_DYNAMIC:-1!":"","MAP_ISLAND_CAVE:0/MAP_ROUTE105:0":"MAP_ROUTE105:0/MAP_ISLAND_CAVE:0","MAP_ISLAND_CAVE:1/MAP_ISLAND_CAVE:2":"MAP_ISLAND_CAVE:2/MAP_ISLAND_CAVE:1","MAP_ISLAND_CAVE:2/MAP_ISLAND_CAVE:1":"MAP_ISLAND_CAVE:1/MAP_ISLAND_CAVE:2","MAP_JAGGED_PASS:0,1/MAP_ROUTE112:2,3":"MAP_ROUTE112:2,3/MAP_JAGGED_PASS:0,1","MAP_JAGGED_PASS:2,3/MAP_MT_CHIMNEY:2,3":"MAP_MT_CHIMNEY:2,3/MAP_JAGGED_PASS:2,3","MAP_JAGGED_PASS:4/MAP_MAGMA_HIDEOUT_1F:0":"MAP_MAGMA_HIDEOUT_1F:0/MAP_JAGGED_PASS:4","MAP_LAVARIDGE_TOWN:0/MAP_LAVARIDGE_TOWN_HERB_SHOP:0":"MAP_LAVARIDGE_TOWN_HERB_SHOP:0,1/MAP_LAVARIDGE_TOWN:0","MAP_LAVARIDGE_TOWN:1/MAP_LAVARIDGE_TOWN_GYM_1F:0":"MAP_LAVARIDGE_TOWN_GYM_1F:0,1/MAP_LAVARIDGE_TOWN:1","MAP_LAVARIDGE_TOWN:2/MAP_LAVARIDGE_TOWN_MART:0":"MAP_LAVARIDGE_TOWN_MART:0,1/MAP_LAVARIDGE_TOWN:2","MAP_LAVARIDGE_TOWN:3/MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:0":"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:0,1/MAP_LAVARIDGE_TOWN:3","MAP_LAVARIDGE_TOWN:4/MAP_LAVARIDGE_TOWN_HOUSE:0":"MAP_LAVARIDGE_TOWN_HOUSE:0,1/MAP_LAVARIDGE_TOWN:4","MAP_LAVARIDGE_TOWN:5/MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:3":"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:3/MAP_LAVARIDGE_TOWN:5","MAP_LAVARIDGE_TOWN_GYM_1F:0,1/MAP_LAVARIDGE_TOWN:1":"MAP_LAVARIDGE_TOWN:1/MAP_LAVARIDGE_TOWN_GYM_1F:0","MAP_LAVARIDGE_TOWN_GYM_1F:10/MAP_LAVARIDGE_TOWN_GYM_B1F:8":"MAP_LAVARIDGE_TOWN_GYM_B1F:8/MAP_LAVARIDGE_TOWN_GYM_1F:10","MAP_LAVARIDGE_TOWN_GYM_1F:11/MAP_LAVARIDGE_TOWN_GYM_B1F:9":"MAP_LAVARIDGE_TOWN_GYM_B1F:9/MAP_LAVARIDGE_TOWN_GYM_1F:11","MAP_LAVARIDGE_TOWN_GYM_1F:12/MAP_LAVARIDGE_TOWN_GYM_B1F:10":"MAP_LAVARIDGE_TOWN_GYM_B1F:10/MAP_LAVARIDGE_TOWN_GYM_1F:12","MAP_LAVARIDGE_TOWN_GYM_1F:13/MAP_LAVARIDGE_TOWN_GYM_B1F:11":"MAP_LAVARIDGE_TOWN_GYM_B1F:11/MAP_LAVARIDGE_TOWN_GYM_1F:13","MAP_LAVARIDGE_TOWN_GYM_1F:14/MAP_LAVARIDGE_TOWN_GYM_B1F:12":"MAP_LAVARIDGE_TOWN_GYM_B1F:12/MAP_LAVARIDGE_TOWN_GYM_1F:14","MAP_LAVARIDGE_TOWN_GYM_1F:15/MAP_LAVARIDGE_TOWN_GYM_B1F:13":"MAP_LAVARIDGE_TOWN_GYM_B1F:13/MAP_LAVARIDGE_TOWN_GYM_1F:15","MAP_LAVARIDGE_TOWN_GYM_1F:16/MAP_LAVARIDGE_TOWN_GYM_B1F:14":"MAP_LAVARIDGE_TOWN_GYM_B1F:14/MAP_LAVARIDGE_TOWN_GYM_1F:16","MAP_LAVARIDGE_TOWN_GYM_1F:17/MAP_LAVARIDGE_TOWN_GYM_B1F:15":"MAP_LAVARIDGE_TOWN_GYM_B1F:15/MAP_LAVARIDGE_TOWN_GYM_1F:17","MAP_LAVARIDGE_TOWN_GYM_1F:18/MAP_LAVARIDGE_TOWN_GYM_B1F:16":"MAP_LAVARIDGE_TOWN_GYM_B1F:16/MAP_LAVARIDGE_TOWN_GYM_1F:18","MAP_LAVARIDGE_TOWN_GYM_1F:19/MAP_LAVARIDGE_TOWN_GYM_B1F:17":"MAP_LAVARIDGE_TOWN_GYM_B1F:17/MAP_LAVARIDGE_TOWN_GYM_1F:19","MAP_LAVARIDGE_TOWN_GYM_1F:2/MAP_LAVARIDGE_TOWN_GYM_B1F:0":"MAP_LAVARIDGE_TOWN_GYM_B1F:0/MAP_LAVARIDGE_TOWN_GYM_1F:2","MAP_LAVARIDGE_TOWN_GYM_1F:20/MAP_LAVARIDGE_TOWN_GYM_B1F:18":"MAP_LAVARIDGE_TOWN_GYM_B1F:18/MAP_LAVARIDGE_TOWN_GYM_1F:20","MAP_LAVARIDGE_TOWN_GYM_1F:21/MAP_LAVARIDGE_TOWN_GYM_B1F:20":"MAP_LAVARIDGE_TOWN_GYM_B1F:20/MAP_LAVARIDGE_TOWN_GYM_1F:21","MAP_LAVARIDGE_TOWN_GYM_1F:22/MAP_LAVARIDGE_TOWN_GYM_B1F:19":"MAP_LAVARIDGE_TOWN_GYM_B1F:19/MAP_LAVARIDGE_TOWN_GYM_1F:22","MAP_LAVARIDGE_TOWN_GYM_1F:23/MAP_LAVARIDGE_TOWN_GYM_B1F:21":"MAP_LAVARIDGE_TOWN_GYM_B1F:21/MAP_LAVARIDGE_TOWN_GYM_1F:23","MAP_LAVARIDGE_TOWN_GYM_1F:24/MAP_LAVARIDGE_TOWN_GYM_B1F:22":"MAP_LAVARIDGE_TOWN_GYM_B1F:22/MAP_LAVARIDGE_TOWN_GYM_1F:24","MAP_LAVARIDGE_TOWN_GYM_1F:25/MAP_LAVARIDGE_TOWN_GYM_B1F:23":"MAP_LAVARIDGE_TOWN_GYM_B1F:23/MAP_LAVARIDGE_TOWN_GYM_1F:25","MAP_LAVARIDGE_TOWN_GYM_1F:3/MAP_LAVARIDGE_TOWN_GYM_B1F:2":"MAP_LAVARIDGE_TOWN_GYM_B1F:2/MAP_LAVARIDGE_TOWN_GYM_1F:3","MAP_LAVARIDGE_TOWN_GYM_1F:4/MAP_LAVARIDGE_TOWN_GYM_B1F:4":"MAP_LAVARIDGE_TOWN_GYM_B1F:4/MAP_LAVARIDGE_TOWN_GYM_1F:4","MAP_LAVARIDGE_TOWN_GYM_1F:5/MAP_LAVARIDGE_TOWN_GYM_B1F:3":"MAP_LAVARIDGE_TOWN_GYM_B1F:3/MAP_LAVARIDGE_TOWN_GYM_1F:5","MAP_LAVARIDGE_TOWN_GYM_1F:6/MAP_LAVARIDGE_TOWN_GYM_B1F:1":"MAP_LAVARIDGE_TOWN_GYM_B1F:1/MAP_LAVARIDGE_TOWN_GYM_1F:6","MAP_LAVARIDGE_TOWN_GYM_1F:7/MAP_LAVARIDGE_TOWN_GYM_B1F:5":"MAP_LAVARIDGE_TOWN_GYM_B1F:5/MAP_LAVARIDGE_TOWN_GYM_1F:7","MAP_LAVARIDGE_TOWN_GYM_1F:8/MAP_LAVARIDGE_TOWN_GYM_B1F:6":"MAP_LAVARIDGE_TOWN_GYM_B1F:6/MAP_LAVARIDGE_TOWN_GYM_1F:8","MAP_LAVARIDGE_TOWN_GYM_1F:9/MAP_LAVARIDGE_TOWN_GYM_B1F:7":"MAP_LAVARIDGE_TOWN_GYM_B1F:7/MAP_LAVARIDGE_TOWN_GYM_1F:9","MAP_LAVARIDGE_TOWN_GYM_B1F:0/MAP_LAVARIDGE_TOWN_GYM_1F:2":"MAP_LAVARIDGE_TOWN_GYM_1F:2/MAP_LAVARIDGE_TOWN_GYM_B1F:0","MAP_LAVARIDGE_TOWN_GYM_B1F:1/MAP_LAVARIDGE_TOWN_GYM_1F:6":"MAP_LAVARIDGE_TOWN_GYM_1F:6/MAP_LAVARIDGE_TOWN_GYM_B1F:1","MAP_LAVARIDGE_TOWN_GYM_B1F:10/MAP_LAVARIDGE_TOWN_GYM_1F:12":"MAP_LAVARIDGE_TOWN_GYM_1F:12/MAP_LAVARIDGE_TOWN_GYM_B1F:10","MAP_LAVARIDGE_TOWN_GYM_B1F:11/MAP_LAVARIDGE_TOWN_GYM_1F:13":"MAP_LAVARIDGE_TOWN_GYM_1F:13/MAP_LAVARIDGE_TOWN_GYM_B1F:11","MAP_LAVARIDGE_TOWN_GYM_B1F:12/MAP_LAVARIDGE_TOWN_GYM_1F:14":"MAP_LAVARIDGE_TOWN_GYM_1F:14/MAP_LAVARIDGE_TOWN_GYM_B1F:12","MAP_LAVARIDGE_TOWN_GYM_B1F:13/MAP_LAVARIDGE_TOWN_GYM_1F:15":"MAP_LAVARIDGE_TOWN_GYM_1F:15/MAP_LAVARIDGE_TOWN_GYM_B1F:13","MAP_LAVARIDGE_TOWN_GYM_B1F:14/MAP_LAVARIDGE_TOWN_GYM_1F:16":"MAP_LAVARIDGE_TOWN_GYM_1F:16/MAP_LAVARIDGE_TOWN_GYM_B1F:14","MAP_LAVARIDGE_TOWN_GYM_B1F:15/MAP_LAVARIDGE_TOWN_GYM_1F:17":"MAP_LAVARIDGE_TOWN_GYM_1F:17/MAP_LAVARIDGE_TOWN_GYM_B1F:15","MAP_LAVARIDGE_TOWN_GYM_B1F:16/MAP_LAVARIDGE_TOWN_GYM_1F:18":"MAP_LAVARIDGE_TOWN_GYM_1F:18/MAP_LAVARIDGE_TOWN_GYM_B1F:16","MAP_LAVARIDGE_TOWN_GYM_B1F:17/MAP_LAVARIDGE_TOWN_GYM_1F:19":"MAP_LAVARIDGE_TOWN_GYM_1F:19/MAP_LAVARIDGE_TOWN_GYM_B1F:17","MAP_LAVARIDGE_TOWN_GYM_B1F:18/MAP_LAVARIDGE_TOWN_GYM_1F:20":"MAP_LAVARIDGE_TOWN_GYM_1F:20/MAP_LAVARIDGE_TOWN_GYM_B1F:18","MAP_LAVARIDGE_TOWN_GYM_B1F:19/MAP_LAVARIDGE_TOWN_GYM_1F:22":"MAP_LAVARIDGE_TOWN_GYM_1F:22/MAP_LAVARIDGE_TOWN_GYM_B1F:19","MAP_LAVARIDGE_TOWN_GYM_B1F:2/MAP_LAVARIDGE_TOWN_GYM_1F:3":"MAP_LAVARIDGE_TOWN_GYM_1F:3/MAP_LAVARIDGE_TOWN_GYM_B1F:2","MAP_LAVARIDGE_TOWN_GYM_B1F:20/MAP_LAVARIDGE_TOWN_GYM_1F:21":"MAP_LAVARIDGE_TOWN_GYM_1F:21/MAP_LAVARIDGE_TOWN_GYM_B1F:20","MAP_LAVARIDGE_TOWN_GYM_B1F:21/MAP_LAVARIDGE_TOWN_GYM_1F:23":"MAP_LAVARIDGE_TOWN_GYM_1F:23/MAP_LAVARIDGE_TOWN_GYM_B1F:21","MAP_LAVARIDGE_TOWN_GYM_B1F:22/MAP_LAVARIDGE_TOWN_GYM_1F:24":"MAP_LAVARIDGE_TOWN_GYM_1F:24/MAP_LAVARIDGE_TOWN_GYM_B1F:22","MAP_LAVARIDGE_TOWN_GYM_B1F:23/MAP_LAVARIDGE_TOWN_GYM_1F:25":"MAP_LAVARIDGE_TOWN_GYM_1F:25/MAP_LAVARIDGE_TOWN_GYM_B1F:23","MAP_LAVARIDGE_TOWN_GYM_B1F:3/MAP_LAVARIDGE_TOWN_GYM_1F:5":"MAP_LAVARIDGE_TOWN_GYM_1F:5/MAP_LAVARIDGE_TOWN_GYM_B1F:3","MAP_LAVARIDGE_TOWN_GYM_B1F:4/MAP_LAVARIDGE_TOWN_GYM_1F:4":"MAP_LAVARIDGE_TOWN_GYM_1F:4/MAP_LAVARIDGE_TOWN_GYM_B1F:4","MAP_LAVARIDGE_TOWN_GYM_B1F:5/MAP_LAVARIDGE_TOWN_GYM_1F:7":"MAP_LAVARIDGE_TOWN_GYM_1F:7/MAP_LAVARIDGE_TOWN_GYM_B1F:5","MAP_LAVARIDGE_TOWN_GYM_B1F:6/MAP_LAVARIDGE_TOWN_GYM_1F:8":"MAP_LAVARIDGE_TOWN_GYM_1F:8/MAP_LAVARIDGE_TOWN_GYM_B1F:6","MAP_LAVARIDGE_TOWN_GYM_B1F:7/MAP_LAVARIDGE_TOWN_GYM_1F:9":"MAP_LAVARIDGE_TOWN_GYM_1F:9/MAP_LAVARIDGE_TOWN_GYM_B1F:7","MAP_LAVARIDGE_TOWN_GYM_B1F:8/MAP_LAVARIDGE_TOWN_GYM_1F:10":"MAP_LAVARIDGE_TOWN_GYM_1F:10/MAP_LAVARIDGE_TOWN_GYM_B1F:8","MAP_LAVARIDGE_TOWN_GYM_B1F:9/MAP_LAVARIDGE_TOWN_GYM_1F:11":"MAP_LAVARIDGE_TOWN_GYM_1F:11/MAP_LAVARIDGE_TOWN_GYM_B1F:9","MAP_LAVARIDGE_TOWN_HERB_SHOP:0,1/MAP_LAVARIDGE_TOWN:0":"MAP_LAVARIDGE_TOWN:0/MAP_LAVARIDGE_TOWN_HERB_SHOP:0","MAP_LAVARIDGE_TOWN_HOUSE:0,1/MAP_LAVARIDGE_TOWN:4":"MAP_LAVARIDGE_TOWN:4/MAP_LAVARIDGE_TOWN_HOUSE:0","MAP_LAVARIDGE_TOWN_MART:0,1/MAP_LAVARIDGE_TOWN:2":"MAP_LAVARIDGE_TOWN:2/MAP_LAVARIDGE_TOWN_MART:0","MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:0,1/MAP_LAVARIDGE_TOWN:3":"MAP_LAVARIDGE_TOWN:3/MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:0","MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:2/MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F:0":"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F:0/MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:2","MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:3/MAP_LAVARIDGE_TOWN:5":"MAP_LAVARIDGE_TOWN:5/MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:3","MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F:0/MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:2":"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:2/MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F:0","MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_LILYCOVE_CITY:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:0":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:0,1/MAP_LILYCOVE_CITY:0","MAP_LILYCOVE_CITY:1/MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F:0":"MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F:0,1/MAP_LILYCOVE_CITY:1","MAP_LILYCOVE_CITY:10/MAP_LILYCOVE_CITY_HOUSE3:0":"MAP_LILYCOVE_CITY_HOUSE3:0,1/MAP_LILYCOVE_CITY:10","MAP_LILYCOVE_CITY:11/MAP_LILYCOVE_CITY_HOUSE4:0":"MAP_LILYCOVE_CITY_HOUSE4:0,1/MAP_LILYCOVE_CITY:11","MAP_LILYCOVE_CITY:12/MAP_LILYCOVE_CITY_HARBOR:0":"MAP_LILYCOVE_CITY_HARBOR:0,1/MAP_LILYCOVE_CITY:12","MAP_LILYCOVE_CITY:2/MAP_LILYCOVE_CITY_POKEMON_CENTER_1F:0":"MAP_LILYCOVE_CITY_POKEMON_CENTER_1F:0,1/MAP_LILYCOVE_CITY:2","MAP_LILYCOVE_CITY:3,13/MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F:0,1":"MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F:0,1/MAP_LILYCOVE_CITY:3,13","MAP_LILYCOVE_CITY:4/MAP_LILYCOVE_CITY_CONTEST_LOBBY:0":"MAP_LILYCOVE_CITY_CONTEST_LOBBY:0,1/MAP_LILYCOVE_CITY:4","MAP_LILYCOVE_CITY:5/MAP_LILYCOVE_CITY_POKEMON_TRAINER_FAN_CLUB:1":"MAP_LILYCOVE_CITY_POKEMON_TRAINER_FAN_CLUB:0,1/MAP_LILYCOVE_CITY:5","MAP_LILYCOVE_CITY:6/MAP_AQUA_HIDEOUT_1F:0":"MAP_AQUA_HIDEOUT_1F:0,1/MAP_LILYCOVE_CITY:6","MAP_LILYCOVE_CITY:7/MAP_LILYCOVE_CITY_MOVE_DELETERS_HOUSE:0":"MAP_LILYCOVE_CITY_MOVE_DELETERS_HOUSE:0,1/MAP_LILYCOVE_CITY:7","MAP_LILYCOVE_CITY:8/MAP_LILYCOVE_CITY_HOUSE1:0":"MAP_LILYCOVE_CITY_HOUSE1:0,1/MAP_LILYCOVE_CITY:8","MAP_LILYCOVE_CITY:9/MAP_LILYCOVE_CITY_HOUSE2:0":"MAP_LILYCOVE_CITY_HOUSE2:0,1/MAP_LILYCOVE_CITY:9","MAP_LILYCOVE_CITY_CONTEST_HALL:0,2/MAP_LILYCOVE_CITY_CONTEST_LOBBY:2":"MAP_LILYCOVE_CITY_CONTEST_LOBBY:2/MAP_LILYCOVE_CITY_CONTEST_HALL:0","MAP_LILYCOVE_CITY_CONTEST_HALL:1,3/MAP_LILYCOVE_CITY_CONTEST_LOBBY:3":"MAP_LILYCOVE_CITY_CONTEST_LOBBY:3/MAP_LILYCOVE_CITY_CONTEST_HALL:1","MAP_LILYCOVE_CITY_CONTEST_LOBBY:0,1/MAP_LILYCOVE_CITY:4":"MAP_LILYCOVE_CITY:4/MAP_LILYCOVE_CITY_CONTEST_LOBBY:0","MAP_LILYCOVE_CITY_CONTEST_LOBBY:2/MAP_LILYCOVE_CITY_CONTEST_HALL:0":"MAP_LILYCOVE_CITY_CONTEST_HALL:0,2/MAP_LILYCOVE_CITY_CONTEST_LOBBY:2","MAP_LILYCOVE_CITY_CONTEST_LOBBY:3/MAP_LILYCOVE_CITY_CONTEST_HALL:1":"MAP_LILYCOVE_CITY_CONTEST_HALL:1,3/MAP_LILYCOVE_CITY_CONTEST_LOBBY:3","MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F:0,1/MAP_LILYCOVE_CITY:1":"MAP_LILYCOVE_CITY:1/MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F:0","MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F:2/MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_2F:0":"MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_2F:0/MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F:2","MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_2F:0/MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F:2":"MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F:2/MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_2F:0","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:0,1/MAP_LILYCOVE_CITY:0":"MAP_LILYCOVE_CITY:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:0","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:0":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:2","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:3/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0,1/MAP_DYNAMIC:-1!","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:2":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:0","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:1/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:0":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:1","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0,1/MAP_DYNAMIC:-1!","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:1":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:1/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:0","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:1/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:0":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:1","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0,1/MAP_DYNAMIC:-1!","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:1":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:1/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:0","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:1/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:0":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:1","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0,1/MAP_DYNAMIC:-1!","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:1":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:1/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:0","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:1/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0,1/MAP_DYNAMIC:-1!","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP:0":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:2","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0,1/MAP_DYNAMIC:-1!":"","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:2":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP:0","MAP_LILYCOVE_CITY_HARBOR:0,1/MAP_LILYCOVE_CITY:12":"MAP_LILYCOVE_CITY:12/MAP_LILYCOVE_CITY_HARBOR:0","MAP_LILYCOVE_CITY_HOUSE1:0,1/MAP_LILYCOVE_CITY:8":"MAP_LILYCOVE_CITY:8/MAP_LILYCOVE_CITY_HOUSE1:0","MAP_LILYCOVE_CITY_HOUSE2:0,1/MAP_LILYCOVE_CITY:9":"MAP_LILYCOVE_CITY:9/MAP_LILYCOVE_CITY_HOUSE2:0","MAP_LILYCOVE_CITY_HOUSE3:0,1/MAP_LILYCOVE_CITY:10":"MAP_LILYCOVE_CITY:10/MAP_LILYCOVE_CITY_HOUSE3:0","MAP_LILYCOVE_CITY_HOUSE4:0,1/MAP_LILYCOVE_CITY:11":"MAP_LILYCOVE_CITY:11/MAP_LILYCOVE_CITY_HOUSE4:0","MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F:0,1/MAP_LILYCOVE_CITY:3,13":"MAP_LILYCOVE_CITY:3,13/MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F:0,1","MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F:2/MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_2F:0":"MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_2F:0/MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F:2","MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_2F:0/MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F:2":"MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F:2/MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_2F:0","MAP_LILYCOVE_CITY_MOVE_DELETERS_HOUSE:0,1/MAP_LILYCOVE_CITY:7":"MAP_LILYCOVE_CITY:7/MAP_LILYCOVE_CITY_MOVE_DELETERS_HOUSE:0","MAP_LILYCOVE_CITY_POKEMON_CENTER_1F:0,1/MAP_LILYCOVE_CITY:2":"MAP_LILYCOVE_CITY:2/MAP_LILYCOVE_CITY_POKEMON_CENTER_1F:0","MAP_LILYCOVE_CITY_POKEMON_CENTER_1F:2/MAP_LILYCOVE_CITY_POKEMON_CENTER_2F:0":"MAP_LILYCOVE_CITY_POKEMON_CENTER_2F:0/MAP_LILYCOVE_CITY_POKEMON_CENTER_1F:2","MAP_LILYCOVE_CITY_POKEMON_CENTER_2F:0/MAP_LILYCOVE_CITY_POKEMON_CENTER_1F:2":"MAP_LILYCOVE_CITY_POKEMON_CENTER_1F:2/MAP_LILYCOVE_CITY_POKEMON_CENTER_2F:0","MAP_LILYCOVE_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_LILYCOVE_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_LILYCOVE_CITY_POKEMON_TRAINER_FAN_CLUB:0,1/MAP_LILYCOVE_CITY:5":"MAP_LILYCOVE_CITY:5/MAP_LILYCOVE_CITY_POKEMON_TRAINER_FAN_CLUB:1","MAP_LILYCOVE_CITY_UNUSED_MART:0,1/MAP_LILYCOVE_CITY:0!":"MAP_LILYCOVE_CITY:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:0","MAP_LITTLEROOT_TOWN:0/MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F:1":"MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F:0,1/MAP_LITTLEROOT_TOWN:0","MAP_LITTLEROOT_TOWN:1/MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F:1":"MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F:0,1/MAP_LITTLEROOT_TOWN:1","MAP_LITTLEROOT_TOWN:2/MAP_LITTLEROOT_TOWN_PROFESSOR_BIRCHS_LAB:0":"MAP_LITTLEROOT_TOWN_PROFESSOR_BIRCHS_LAB:0,1/MAP_LITTLEROOT_TOWN:2","MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F:0,1/MAP_LITTLEROOT_TOWN:1":"MAP_LITTLEROOT_TOWN:1/MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F:1","MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F:2/MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F:0":"MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F:0/MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F:2","MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F:0/MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F:2":"MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F:2/MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F:0","MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F:0,1/MAP_LITTLEROOT_TOWN:0":"MAP_LITTLEROOT_TOWN:0/MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F:1","MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F:2/MAP_LITTLEROOT_TOWN_MAYS_HOUSE_2F:0":"MAP_LITTLEROOT_TOWN_MAYS_HOUSE_2F:0/MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F:2","MAP_LITTLEROOT_TOWN_MAYS_HOUSE_2F:0/MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F:2":"MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F:2/MAP_LITTLEROOT_TOWN_MAYS_HOUSE_2F:0","MAP_LITTLEROOT_TOWN_PROFESSOR_BIRCHS_LAB:0,1/MAP_LITTLEROOT_TOWN:2":"MAP_LITTLEROOT_TOWN:2/MAP_LITTLEROOT_TOWN_PROFESSOR_BIRCHS_LAB:0","MAP_MAGMA_HIDEOUT_1F:0/MAP_JAGGED_PASS:4":"MAP_JAGGED_PASS:4/MAP_MAGMA_HIDEOUT_1F:0","MAP_MAGMA_HIDEOUT_1F:1/MAP_MAGMA_HIDEOUT_2F_1R:1":"MAP_MAGMA_HIDEOUT_2F_1R:1/MAP_MAGMA_HIDEOUT_1F:1","MAP_MAGMA_HIDEOUT_1F:2/MAP_MAGMA_HIDEOUT_2F_2R:1":"MAP_MAGMA_HIDEOUT_2F_2R:1/MAP_MAGMA_HIDEOUT_1F:2","MAP_MAGMA_HIDEOUT_1F:3/MAP_MAGMA_HIDEOUT_2F_3R:0":"MAP_MAGMA_HIDEOUT_2F_3R:0/MAP_MAGMA_HIDEOUT_1F:3","MAP_MAGMA_HIDEOUT_2F_1R:0/MAP_MAGMA_HIDEOUT_2F_2R:0":"MAP_MAGMA_HIDEOUT_2F_2R:0/MAP_MAGMA_HIDEOUT_2F_1R:0","MAP_MAGMA_HIDEOUT_2F_1R:1/MAP_MAGMA_HIDEOUT_1F:1":"MAP_MAGMA_HIDEOUT_1F:1/MAP_MAGMA_HIDEOUT_2F_1R:1","MAP_MAGMA_HIDEOUT_2F_1R:2/MAP_MAGMA_HIDEOUT_3F_1R:2":"MAP_MAGMA_HIDEOUT_3F_1R:2/MAP_MAGMA_HIDEOUT_2F_1R:2","MAP_MAGMA_HIDEOUT_2F_2R:0/MAP_MAGMA_HIDEOUT_2F_1R:0":"MAP_MAGMA_HIDEOUT_2F_1R:0/MAP_MAGMA_HIDEOUT_2F_2R:0","MAP_MAGMA_HIDEOUT_2F_2R:1/MAP_MAGMA_HIDEOUT_1F:2":"MAP_MAGMA_HIDEOUT_1F:2/MAP_MAGMA_HIDEOUT_2F_2R:1","MAP_MAGMA_HIDEOUT_2F_3R:0/MAP_MAGMA_HIDEOUT_1F:3":"MAP_MAGMA_HIDEOUT_1F:3/MAP_MAGMA_HIDEOUT_2F_3R:0","MAP_MAGMA_HIDEOUT_2F_3R:1/MAP_MAGMA_HIDEOUT_3F_3R:0":"MAP_MAGMA_HIDEOUT_3F_3R:0/MAP_MAGMA_HIDEOUT_2F_3R:1","MAP_MAGMA_HIDEOUT_3F_1R:0/MAP_MAGMA_HIDEOUT_4F:0":"MAP_MAGMA_HIDEOUT_4F:0/MAP_MAGMA_HIDEOUT_3F_1R:0","MAP_MAGMA_HIDEOUT_3F_1R:1/MAP_MAGMA_HIDEOUT_3F_2R:0":"MAP_MAGMA_HIDEOUT_3F_2R:0/MAP_MAGMA_HIDEOUT_3F_1R:1","MAP_MAGMA_HIDEOUT_3F_1R:2/MAP_MAGMA_HIDEOUT_2F_1R:2":"MAP_MAGMA_HIDEOUT_2F_1R:2/MAP_MAGMA_HIDEOUT_3F_1R:2","MAP_MAGMA_HIDEOUT_3F_2R:0/MAP_MAGMA_HIDEOUT_3F_1R:1":"MAP_MAGMA_HIDEOUT_3F_1R:1/MAP_MAGMA_HIDEOUT_3F_2R:0","MAP_MAGMA_HIDEOUT_3F_3R:0/MAP_MAGMA_HIDEOUT_2F_3R:1":"MAP_MAGMA_HIDEOUT_2F_3R:1/MAP_MAGMA_HIDEOUT_3F_3R:0","MAP_MAGMA_HIDEOUT_3F_3R:1/MAP_MAGMA_HIDEOUT_4F:1":"MAP_MAGMA_HIDEOUT_4F:1/MAP_MAGMA_HIDEOUT_3F_3R:1","MAP_MAGMA_HIDEOUT_4F:0/MAP_MAGMA_HIDEOUT_3F_1R:0":"MAP_MAGMA_HIDEOUT_3F_1R:0/MAP_MAGMA_HIDEOUT_4F:0","MAP_MAGMA_HIDEOUT_4F:1/MAP_MAGMA_HIDEOUT_3F_3R:1":"MAP_MAGMA_HIDEOUT_3F_3R:1/MAP_MAGMA_HIDEOUT_4F:1","MAP_MARINE_CAVE_END:0/MAP_MARINE_CAVE_ENTRANCE:0":"MAP_MARINE_CAVE_ENTRANCE:0/MAP_MARINE_CAVE_END:0","MAP_MARINE_CAVE_ENTRANCE:0/MAP_MARINE_CAVE_END:0":"MAP_MARINE_CAVE_END:0/MAP_MARINE_CAVE_ENTRANCE:0","MAP_MAUVILLE_CITY:0/MAP_MAUVILLE_CITY_GYM:0":"MAP_MAUVILLE_CITY_GYM:0,1/MAP_MAUVILLE_CITY:0","MAP_MAUVILLE_CITY:1/MAP_MAUVILLE_CITY_POKEMON_CENTER_1F:0":"MAP_MAUVILLE_CITY_POKEMON_CENTER_1F:0,1/MAP_MAUVILLE_CITY:1","MAP_MAUVILLE_CITY:2/MAP_MAUVILLE_CITY_BIKE_SHOP:0":"MAP_MAUVILLE_CITY_BIKE_SHOP:0,1/MAP_MAUVILLE_CITY:2","MAP_MAUVILLE_CITY:3/MAP_MAUVILLE_CITY_MART:0":"MAP_MAUVILLE_CITY_MART:0,1/MAP_MAUVILLE_CITY:3","MAP_MAUVILLE_CITY:4/MAP_MAUVILLE_CITY_HOUSE1:0":"MAP_MAUVILLE_CITY_HOUSE1:0,1/MAP_MAUVILLE_CITY:4","MAP_MAUVILLE_CITY:5/MAP_MAUVILLE_CITY_GAME_CORNER:0":"MAP_MAUVILLE_CITY_GAME_CORNER:0,1/MAP_MAUVILLE_CITY:5","MAP_MAUVILLE_CITY:6/MAP_MAUVILLE_CITY_HOUSE2:0":"MAP_MAUVILLE_CITY_HOUSE2:0,1/MAP_MAUVILLE_CITY:6","MAP_MAUVILLE_CITY_BIKE_SHOP:0,1/MAP_MAUVILLE_CITY:2":"MAP_MAUVILLE_CITY:2/MAP_MAUVILLE_CITY_BIKE_SHOP:0","MAP_MAUVILLE_CITY_GAME_CORNER:0,1/MAP_MAUVILLE_CITY:5":"MAP_MAUVILLE_CITY:5/MAP_MAUVILLE_CITY_GAME_CORNER:0","MAP_MAUVILLE_CITY_GYM:0,1/MAP_MAUVILLE_CITY:0":"MAP_MAUVILLE_CITY:0/MAP_MAUVILLE_CITY_GYM:0","MAP_MAUVILLE_CITY_HOUSE1:0,1/MAP_MAUVILLE_CITY:4":"MAP_MAUVILLE_CITY:4/MAP_MAUVILLE_CITY_HOUSE1:0","MAP_MAUVILLE_CITY_HOUSE2:0,1/MAP_MAUVILLE_CITY:6":"MAP_MAUVILLE_CITY:6/MAP_MAUVILLE_CITY_HOUSE2:0","MAP_MAUVILLE_CITY_MART:0,1/MAP_MAUVILLE_CITY:3":"MAP_MAUVILLE_CITY:3/MAP_MAUVILLE_CITY_MART:0","MAP_MAUVILLE_CITY_POKEMON_CENTER_1F:0,1/MAP_MAUVILLE_CITY:1":"MAP_MAUVILLE_CITY:1/MAP_MAUVILLE_CITY_POKEMON_CENTER_1F:0","MAP_MAUVILLE_CITY_POKEMON_CENTER_1F:2/MAP_MAUVILLE_CITY_POKEMON_CENTER_2F:0":"MAP_MAUVILLE_CITY_POKEMON_CENTER_2F:0/MAP_MAUVILLE_CITY_POKEMON_CENTER_1F:2","MAP_MAUVILLE_CITY_POKEMON_CENTER_2F:0/MAP_MAUVILLE_CITY_POKEMON_CENTER_1F:2":"MAP_MAUVILLE_CITY_POKEMON_CENTER_1F:2/MAP_MAUVILLE_CITY_POKEMON_CENTER_2F:0","MAP_MAUVILLE_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_MAUVILLE_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_METEOR_FALLS_1F_1R:0/MAP_ROUTE114:0":"MAP_ROUTE114:0/MAP_METEOR_FALLS_1F_1R:0","MAP_METEOR_FALLS_1F_1R:1/MAP_ROUTE115:0":"MAP_ROUTE115:0/MAP_METEOR_FALLS_1F_1R:1","MAP_METEOR_FALLS_1F_1R:2/MAP_METEOR_FALLS_1F_2R:0":"MAP_METEOR_FALLS_1F_2R:0/MAP_METEOR_FALLS_1F_1R:2","MAP_METEOR_FALLS_1F_1R:3/MAP_METEOR_FALLS_B1F_1R:4":"MAP_METEOR_FALLS_B1F_1R:4/MAP_METEOR_FALLS_1F_1R:3","MAP_METEOR_FALLS_1F_1R:4/MAP_METEOR_FALLS_B1F_1R:5":"MAP_METEOR_FALLS_B1F_1R:5/MAP_METEOR_FALLS_1F_1R:4","MAP_METEOR_FALLS_1F_1R:5/MAP_METEOR_FALLS_STEVENS_CAVE:0":"MAP_METEOR_FALLS_STEVENS_CAVE:0/MAP_METEOR_FALLS_1F_1R:5","MAP_METEOR_FALLS_1F_2R:0/MAP_METEOR_FALLS_1F_1R:2":"MAP_METEOR_FALLS_1F_1R:2/MAP_METEOR_FALLS_1F_2R:0","MAP_METEOR_FALLS_1F_2R:1/MAP_METEOR_FALLS_B1F_1R:0":"MAP_METEOR_FALLS_B1F_1R:0/MAP_METEOR_FALLS_1F_2R:1","MAP_METEOR_FALLS_1F_2R:2/MAP_METEOR_FALLS_B1F_1R:1":"MAP_METEOR_FALLS_B1F_1R:1/MAP_METEOR_FALLS_1F_2R:2","MAP_METEOR_FALLS_1F_2R:3/MAP_METEOR_FALLS_B1F_1R:2":"MAP_METEOR_FALLS_B1F_1R:2/MAP_METEOR_FALLS_1F_2R:3","MAP_METEOR_FALLS_B1F_1R:0/MAP_METEOR_FALLS_1F_2R:1":"MAP_METEOR_FALLS_1F_2R:1/MAP_METEOR_FALLS_B1F_1R:0","MAP_METEOR_FALLS_B1F_1R:1/MAP_METEOR_FALLS_1F_2R:2":"MAP_METEOR_FALLS_1F_2R:2/MAP_METEOR_FALLS_B1F_1R:1","MAP_METEOR_FALLS_B1F_1R:2/MAP_METEOR_FALLS_1F_2R:3":"MAP_METEOR_FALLS_1F_2R:3/MAP_METEOR_FALLS_B1F_1R:2","MAP_METEOR_FALLS_B1F_1R:3/MAP_METEOR_FALLS_B1F_2R:0":"MAP_METEOR_FALLS_B1F_2R:0/MAP_METEOR_FALLS_B1F_1R:3","MAP_METEOR_FALLS_B1F_1R:4/MAP_METEOR_FALLS_1F_1R:3":"MAP_METEOR_FALLS_1F_1R:3/MAP_METEOR_FALLS_B1F_1R:4","MAP_METEOR_FALLS_B1F_1R:5/MAP_METEOR_FALLS_1F_1R:4":"MAP_METEOR_FALLS_1F_1R:4/MAP_METEOR_FALLS_B1F_1R:5","MAP_METEOR_FALLS_B1F_2R:0/MAP_METEOR_FALLS_B1F_1R:3":"MAP_METEOR_FALLS_B1F_1R:3/MAP_METEOR_FALLS_B1F_2R:0","MAP_METEOR_FALLS_STEVENS_CAVE:0/MAP_METEOR_FALLS_1F_1R:5":"MAP_METEOR_FALLS_1F_1R:5/MAP_METEOR_FALLS_STEVENS_CAVE:0","MAP_MIRAGE_TOWER_1F:0/MAP_ROUTE111:3":"MAP_ROUTE111:3/MAP_MIRAGE_TOWER_1F:0","MAP_MIRAGE_TOWER_1F:1/MAP_MIRAGE_TOWER_2F:1":"MAP_MIRAGE_TOWER_2F:1/MAP_MIRAGE_TOWER_1F:1","MAP_MIRAGE_TOWER_2F:0/MAP_MIRAGE_TOWER_3F:0":"MAP_MIRAGE_TOWER_3F:0/MAP_MIRAGE_TOWER_2F:0","MAP_MIRAGE_TOWER_2F:1/MAP_MIRAGE_TOWER_1F:1":"MAP_MIRAGE_TOWER_1F:1/MAP_MIRAGE_TOWER_2F:1","MAP_MIRAGE_TOWER_3F:0/MAP_MIRAGE_TOWER_2F:0":"MAP_MIRAGE_TOWER_2F:0/MAP_MIRAGE_TOWER_3F:0","MAP_MIRAGE_TOWER_3F:1/MAP_MIRAGE_TOWER_4F:0":"MAP_MIRAGE_TOWER_4F:0/MAP_MIRAGE_TOWER_3F:1","MAP_MIRAGE_TOWER_4F:0/MAP_MIRAGE_TOWER_3F:1":"MAP_MIRAGE_TOWER_3F:1/MAP_MIRAGE_TOWER_4F:0","MAP_MOSSDEEP_CITY:0/MAP_MOSSDEEP_CITY_HOUSE1:0":"MAP_MOSSDEEP_CITY_HOUSE1:0,1/MAP_MOSSDEEP_CITY:0","MAP_MOSSDEEP_CITY:1/MAP_MOSSDEEP_CITY_GYM:0":"MAP_MOSSDEEP_CITY_GYM:0,1/MAP_MOSSDEEP_CITY:1","MAP_MOSSDEEP_CITY:2/MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F:0":"MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F:0,1/MAP_MOSSDEEP_CITY:2","MAP_MOSSDEEP_CITY:3/MAP_MOSSDEEP_CITY_HOUSE2:0":"MAP_MOSSDEEP_CITY_HOUSE2:0,1/MAP_MOSSDEEP_CITY:3","MAP_MOSSDEEP_CITY:4/MAP_MOSSDEEP_CITY_MART:0":"MAP_MOSSDEEP_CITY_MART:0,1/MAP_MOSSDEEP_CITY:4","MAP_MOSSDEEP_CITY:5/MAP_MOSSDEEP_CITY_HOUSE3:0":"MAP_MOSSDEEP_CITY_HOUSE3:0,1/MAP_MOSSDEEP_CITY:5","MAP_MOSSDEEP_CITY:6/MAP_MOSSDEEP_CITY_STEVENS_HOUSE:0":"MAP_MOSSDEEP_CITY_STEVENS_HOUSE:0,1/MAP_MOSSDEEP_CITY:6","MAP_MOSSDEEP_CITY:7/MAP_MOSSDEEP_CITY_HOUSE4:1":"MAP_MOSSDEEP_CITY_HOUSE4:0,1/MAP_MOSSDEEP_CITY:7","MAP_MOSSDEEP_CITY:8/MAP_MOSSDEEP_CITY_SPACE_CENTER_1F:0":"MAP_MOSSDEEP_CITY_SPACE_CENTER_1F:0,1/MAP_MOSSDEEP_CITY:8","MAP_MOSSDEEP_CITY:9/MAP_MOSSDEEP_CITY_GAME_CORNER_1F:0":"MAP_MOSSDEEP_CITY_GAME_CORNER_1F:0,1/MAP_MOSSDEEP_CITY:9","MAP_MOSSDEEP_CITY_GAME_CORNER_1F:0,1/MAP_MOSSDEEP_CITY:9":"MAP_MOSSDEEP_CITY:9/MAP_MOSSDEEP_CITY_GAME_CORNER_1F:0","MAP_MOSSDEEP_CITY_GAME_CORNER_1F:2/MAP_MOSSDEEP_CITY_GAME_CORNER_B1F:0":"MAP_MOSSDEEP_CITY_GAME_CORNER_B1F:0/MAP_MOSSDEEP_CITY_GAME_CORNER_1F:2","MAP_MOSSDEEP_CITY_GAME_CORNER_B1F:0/MAP_MOSSDEEP_CITY_GAME_CORNER_1F:2":"MAP_MOSSDEEP_CITY_GAME_CORNER_1F:2/MAP_MOSSDEEP_CITY_GAME_CORNER_B1F:0","MAP_MOSSDEEP_CITY_GYM:0,1/MAP_MOSSDEEP_CITY:1":"MAP_MOSSDEEP_CITY:1/MAP_MOSSDEEP_CITY_GYM:0","MAP_MOSSDEEP_CITY_GYM:10/MAP_MOSSDEEP_CITY_GYM:11":"MAP_MOSSDEEP_CITY_GYM:11/MAP_MOSSDEEP_CITY_GYM:10","MAP_MOSSDEEP_CITY_GYM:11/MAP_MOSSDEEP_CITY_GYM:10":"MAP_MOSSDEEP_CITY_GYM:10/MAP_MOSSDEEP_CITY_GYM:11","MAP_MOSSDEEP_CITY_GYM:12/MAP_MOSSDEEP_CITY_GYM:13":"MAP_MOSSDEEP_CITY_GYM:13/MAP_MOSSDEEP_CITY_GYM:12","MAP_MOSSDEEP_CITY_GYM:13/MAP_MOSSDEEP_CITY_GYM:12":"MAP_MOSSDEEP_CITY_GYM:12/MAP_MOSSDEEP_CITY_GYM:13","MAP_MOSSDEEP_CITY_GYM:2/MAP_MOSSDEEP_CITY_GYM:3":"MAP_MOSSDEEP_CITY_GYM:3/MAP_MOSSDEEP_CITY_GYM:2","MAP_MOSSDEEP_CITY_GYM:3/MAP_MOSSDEEP_CITY_GYM:2":"MAP_MOSSDEEP_CITY_GYM:2/MAP_MOSSDEEP_CITY_GYM:3","MAP_MOSSDEEP_CITY_GYM:4/MAP_MOSSDEEP_CITY_GYM:5":"MAP_MOSSDEEP_CITY_GYM:5/MAP_MOSSDEEP_CITY_GYM:4","MAP_MOSSDEEP_CITY_GYM:5/MAP_MOSSDEEP_CITY_GYM:4":"MAP_MOSSDEEP_CITY_GYM:4/MAP_MOSSDEEP_CITY_GYM:5","MAP_MOSSDEEP_CITY_GYM:6/MAP_MOSSDEEP_CITY_GYM:7":"MAP_MOSSDEEP_CITY_GYM:7/MAP_MOSSDEEP_CITY_GYM:6","MAP_MOSSDEEP_CITY_GYM:7/MAP_MOSSDEEP_CITY_GYM:6":"MAP_MOSSDEEP_CITY_GYM:6/MAP_MOSSDEEP_CITY_GYM:7","MAP_MOSSDEEP_CITY_GYM:8/MAP_MOSSDEEP_CITY_GYM:9":"MAP_MOSSDEEP_CITY_GYM:9/MAP_MOSSDEEP_CITY_GYM:8","MAP_MOSSDEEP_CITY_GYM:9/MAP_MOSSDEEP_CITY_GYM:8":"MAP_MOSSDEEP_CITY_GYM:8/MAP_MOSSDEEP_CITY_GYM:9","MAP_MOSSDEEP_CITY_HOUSE1:0,1/MAP_MOSSDEEP_CITY:0":"MAP_MOSSDEEP_CITY:0/MAP_MOSSDEEP_CITY_HOUSE1:0","MAP_MOSSDEEP_CITY_HOUSE2:0,1/MAP_MOSSDEEP_CITY:3":"MAP_MOSSDEEP_CITY:3/MAP_MOSSDEEP_CITY_HOUSE2:0","MAP_MOSSDEEP_CITY_HOUSE3:0,1/MAP_MOSSDEEP_CITY:5":"MAP_MOSSDEEP_CITY:5/MAP_MOSSDEEP_CITY_HOUSE3:0","MAP_MOSSDEEP_CITY_HOUSE4:0,1/MAP_MOSSDEEP_CITY:7":"MAP_MOSSDEEP_CITY:7/MAP_MOSSDEEP_CITY_HOUSE4:1","MAP_MOSSDEEP_CITY_MART:0,1/MAP_MOSSDEEP_CITY:4":"MAP_MOSSDEEP_CITY:4/MAP_MOSSDEEP_CITY_MART:0","MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F:0,1/MAP_MOSSDEEP_CITY:2":"MAP_MOSSDEEP_CITY:2/MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F:0","MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F:2/MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F:0":"MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F:0/MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F:2","MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F:0/MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F:2":"MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F:2/MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F:0","MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_MOSSDEEP_CITY_SPACE_CENTER_1F:0,1/MAP_MOSSDEEP_CITY:8":"MAP_MOSSDEEP_CITY:8/MAP_MOSSDEEP_CITY_SPACE_CENTER_1F:0","MAP_MOSSDEEP_CITY_SPACE_CENTER_1F:2/MAP_MOSSDEEP_CITY_SPACE_CENTER_2F:0":"MAP_MOSSDEEP_CITY_SPACE_CENTER_2F:0/MAP_MOSSDEEP_CITY_SPACE_CENTER_1F:2","MAP_MOSSDEEP_CITY_SPACE_CENTER_2F:0/MAP_MOSSDEEP_CITY_SPACE_CENTER_1F:2":"MAP_MOSSDEEP_CITY_SPACE_CENTER_1F:2/MAP_MOSSDEEP_CITY_SPACE_CENTER_2F:0","MAP_MOSSDEEP_CITY_STEVENS_HOUSE:0,1/MAP_MOSSDEEP_CITY:6":"MAP_MOSSDEEP_CITY:6/MAP_MOSSDEEP_CITY_STEVENS_HOUSE:0","MAP_MT_CHIMNEY:0,1/MAP_MT_CHIMNEY_CABLE_CAR_STATION:0,1":"MAP_MT_CHIMNEY_CABLE_CAR_STATION:0,1/MAP_MT_CHIMNEY:0,1","MAP_MT_CHIMNEY:2,3/MAP_JAGGED_PASS:2,3":"MAP_JAGGED_PASS:2,3/MAP_MT_CHIMNEY:2,3","MAP_MT_CHIMNEY_CABLE_CAR_STATION:0,1/MAP_MT_CHIMNEY:0,1":"MAP_MT_CHIMNEY:0,1/MAP_MT_CHIMNEY_CABLE_CAR_STATION:0,1","MAP_MT_PYRE_1F:0,2/MAP_ROUTE122:0":"MAP_ROUTE122:0/MAP_MT_PYRE_1F:0","MAP_MT_PYRE_1F:1,3/MAP_MT_PYRE_EXTERIOR:0":"MAP_MT_PYRE_EXTERIOR:0/MAP_MT_PYRE_1F:1","MAP_MT_PYRE_1F:4/MAP_MT_PYRE_2F:0":"MAP_MT_PYRE_2F:0/MAP_MT_PYRE_1F:4","MAP_MT_PYRE_1F:5/MAP_MT_PYRE_2F:4":"MAP_MT_PYRE_2F:4/MAP_MT_PYRE_1F:5","MAP_MT_PYRE_2F:0/MAP_MT_PYRE_1F:4":"MAP_MT_PYRE_1F:4/MAP_MT_PYRE_2F:0","MAP_MT_PYRE_2F:1/MAP_MT_PYRE_3F:0":"MAP_MT_PYRE_3F:0/MAP_MT_PYRE_2F:1","MAP_MT_PYRE_2F:2/MAP_MT_PYRE_3F:4":"MAP_MT_PYRE_3F:4/MAP_MT_PYRE_2F:2","MAP_MT_PYRE_2F:3/MAP_MT_PYRE_3F:5":"MAP_MT_PYRE_3F:5/MAP_MT_PYRE_2F:3","MAP_MT_PYRE_2F:4/MAP_MT_PYRE_1F:5":"MAP_MT_PYRE_1F:5/MAP_MT_PYRE_2F:4","MAP_MT_PYRE_3F:0/MAP_MT_PYRE_2F:1":"MAP_MT_PYRE_2F:1/MAP_MT_PYRE_3F:0","MAP_MT_PYRE_3F:1/MAP_MT_PYRE_4F:1":"MAP_MT_PYRE_4F:1/MAP_MT_PYRE_3F:1","MAP_MT_PYRE_3F:2/MAP_MT_PYRE_4F:4":"MAP_MT_PYRE_4F:4/MAP_MT_PYRE_3F:2","MAP_MT_PYRE_3F:3/MAP_MT_PYRE_4F:5":"MAP_MT_PYRE_4F:5/MAP_MT_PYRE_3F:3","MAP_MT_PYRE_3F:4/MAP_MT_PYRE_2F:2":"MAP_MT_PYRE_2F:2/MAP_MT_PYRE_3F:4","MAP_MT_PYRE_3F:5/MAP_MT_PYRE_2F:3":"MAP_MT_PYRE_2F:3/MAP_MT_PYRE_3F:5","MAP_MT_PYRE_4F:0/MAP_MT_PYRE_5F:1":"MAP_MT_PYRE_5F:1/MAP_MT_PYRE_4F:0","MAP_MT_PYRE_4F:1/MAP_MT_PYRE_3F:1":"MAP_MT_PYRE_3F:1/MAP_MT_PYRE_4F:1","MAP_MT_PYRE_4F:2/MAP_MT_PYRE_5F:3":"MAP_MT_PYRE_5F:3/MAP_MT_PYRE_4F:2","MAP_MT_PYRE_4F:3/MAP_MT_PYRE_5F:4":"MAP_MT_PYRE_5F:4/MAP_MT_PYRE_4F:3","MAP_MT_PYRE_4F:4/MAP_MT_PYRE_3F:2":"MAP_MT_PYRE_3F:2/MAP_MT_PYRE_4F:4","MAP_MT_PYRE_4F:5/MAP_MT_PYRE_3F:3":"MAP_MT_PYRE_3F:3/MAP_MT_PYRE_4F:5","MAP_MT_PYRE_5F:0/MAP_MT_PYRE_6F:0":"MAP_MT_PYRE_6F:0/MAP_MT_PYRE_5F:0","MAP_MT_PYRE_5F:1/MAP_MT_PYRE_4F:0":"MAP_MT_PYRE_4F:0/MAP_MT_PYRE_5F:1","MAP_MT_PYRE_5F:2/MAP_MT_PYRE_6F:1":"MAP_MT_PYRE_6F:1/MAP_MT_PYRE_5F:2","MAP_MT_PYRE_5F:3/MAP_MT_PYRE_4F:2":"MAP_MT_PYRE_4F:2/MAP_MT_PYRE_5F:3","MAP_MT_PYRE_5F:4/MAP_MT_PYRE_4F:3":"MAP_MT_PYRE_4F:3/MAP_MT_PYRE_5F:4","MAP_MT_PYRE_6F:0/MAP_MT_PYRE_5F:0":"MAP_MT_PYRE_5F:0/MAP_MT_PYRE_6F:0","MAP_MT_PYRE_6F:1/MAP_MT_PYRE_5F:2":"MAP_MT_PYRE_5F:2/MAP_MT_PYRE_6F:1","MAP_MT_PYRE_EXTERIOR:0/MAP_MT_PYRE_1F:1":"MAP_MT_PYRE_1F:1,3/MAP_MT_PYRE_EXTERIOR:0","MAP_MT_PYRE_EXTERIOR:1,2/MAP_MT_PYRE_SUMMIT:1":"MAP_MT_PYRE_SUMMIT:0,1,2/MAP_MT_PYRE_EXTERIOR:1","MAP_MT_PYRE_SUMMIT:0,1,2/MAP_MT_PYRE_EXTERIOR:1":"MAP_MT_PYRE_EXTERIOR:1,2/MAP_MT_PYRE_SUMMIT:1","MAP_NAVEL_ROCK_B1F:0/MAP_NAVEL_ROCK_ENTRANCE:0":"MAP_NAVEL_ROCK_ENTRANCE:0/MAP_NAVEL_ROCK_B1F:0","MAP_NAVEL_ROCK_B1F:1/MAP_NAVEL_ROCK_FORK:1":"MAP_NAVEL_ROCK_FORK:1/MAP_NAVEL_ROCK_B1F:1","MAP_NAVEL_ROCK_BOTTOM:0/MAP_NAVEL_ROCK_DOWN11:0":"MAP_NAVEL_ROCK_DOWN11:0/MAP_NAVEL_ROCK_BOTTOM:0","MAP_NAVEL_ROCK_DOWN01:0/MAP_NAVEL_ROCK_FORK:2":"MAP_NAVEL_ROCK_FORK:2/MAP_NAVEL_ROCK_DOWN01:0","MAP_NAVEL_ROCK_DOWN01:1/MAP_NAVEL_ROCK_DOWN02:0":"MAP_NAVEL_ROCK_DOWN02:0/MAP_NAVEL_ROCK_DOWN01:1","MAP_NAVEL_ROCK_DOWN02:0/MAP_NAVEL_ROCK_DOWN01:1":"MAP_NAVEL_ROCK_DOWN01:1/MAP_NAVEL_ROCK_DOWN02:0","MAP_NAVEL_ROCK_DOWN02:1/MAP_NAVEL_ROCK_DOWN03:0":"MAP_NAVEL_ROCK_DOWN03:0/MAP_NAVEL_ROCK_DOWN02:1","MAP_NAVEL_ROCK_DOWN03:0/MAP_NAVEL_ROCK_DOWN02:1":"MAP_NAVEL_ROCK_DOWN02:1/MAP_NAVEL_ROCK_DOWN03:0","MAP_NAVEL_ROCK_DOWN03:1/MAP_NAVEL_ROCK_DOWN04:0":"MAP_NAVEL_ROCK_DOWN04:0/MAP_NAVEL_ROCK_DOWN03:1","MAP_NAVEL_ROCK_DOWN04:0/MAP_NAVEL_ROCK_DOWN03:1":"MAP_NAVEL_ROCK_DOWN03:1/MAP_NAVEL_ROCK_DOWN04:0","MAP_NAVEL_ROCK_DOWN04:1/MAP_NAVEL_ROCK_DOWN05:0":"MAP_NAVEL_ROCK_DOWN05:0/MAP_NAVEL_ROCK_DOWN04:1","MAP_NAVEL_ROCK_DOWN05:0/MAP_NAVEL_ROCK_DOWN04:1":"MAP_NAVEL_ROCK_DOWN04:1/MAP_NAVEL_ROCK_DOWN05:0","MAP_NAVEL_ROCK_DOWN05:1/MAP_NAVEL_ROCK_DOWN06:0":"MAP_NAVEL_ROCK_DOWN06:0/MAP_NAVEL_ROCK_DOWN05:1","MAP_NAVEL_ROCK_DOWN06:0/MAP_NAVEL_ROCK_DOWN05:1":"MAP_NAVEL_ROCK_DOWN05:1/MAP_NAVEL_ROCK_DOWN06:0","MAP_NAVEL_ROCK_DOWN06:1/MAP_NAVEL_ROCK_DOWN07:0":"MAP_NAVEL_ROCK_DOWN07:0/MAP_NAVEL_ROCK_DOWN06:1","MAP_NAVEL_ROCK_DOWN07:0/MAP_NAVEL_ROCK_DOWN06:1":"MAP_NAVEL_ROCK_DOWN06:1/MAP_NAVEL_ROCK_DOWN07:0","MAP_NAVEL_ROCK_DOWN07:1/MAP_NAVEL_ROCK_DOWN08:0":"MAP_NAVEL_ROCK_DOWN08:0/MAP_NAVEL_ROCK_DOWN07:1","MAP_NAVEL_ROCK_DOWN08:0/MAP_NAVEL_ROCK_DOWN07:1":"MAP_NAVEL_ROCK_DOWN07:1/MAP_NAVEL_ROCK_DOWN08:0","MAP_NAVEL_ROCK_DOWN08:1/MAP_NAVEL_ROCK_DOWN09:0":"MAP_NAVEL_ROCK_DOWN09:0/MAP_NAVEL_ROCK_DOWN08:1","MAP_NAVEL_ROCK_DOWN09:0/MAP_NAVEL_ROCK_DOWN08:1":"MAP_NAVEL_ROCK_DOWN08:1/MAP_NAVEL_ROCK_DOWN09:0","MAP_NAVEL_ROCK_DOWN09:1/MAP_NAVEL_ROCK_DOWN10:0":"MAP_NAVEL_ROCK_DOWN10:0/MAP_NAVEL_ROCK_DOWN09:1","MAP_NAVEL_ROCK_DOWN10:0/MAP_NAVEL_ROCK_DOWN09:1":"MAP_NAVEL_ROCK_DOWN09:1/MAP_NAVEL_ROCK_DOWN10:0","MAP_NAVEL_ROCK_DOWN10:1/MAP_NAVEL_ROCK_DOWN11:1":"MAP_NAVEL_ROCK_DOWN11:1/MAP_NAVEL_ROCK_DOWN10:1","MAP_NAVEL_ROCK_DOWN11:0/MAP_NAVEL_ROCK_BOTTOM:0":"MAP_NAVEL_ROCK_BOTTOM:0/MAP_NAVEL_ROCK_DOWN11:0","MAP_NAVEL_ROCK_DOWN11:1/MAP_NAVEL_ROCK_DOWN10:1":"MAP_NAVEL_ROCK_DOWN10:1/MAP_NAVEL_ROCK_DOWN11:1","MAP_NAVEL_ROCK_ENTRANCE:0/MAP_NAVEL_ROCK_B1F:0":"MAP_NAVEL_ROCK_B1F:0/MAP_NAVEL_ROCK_ENTRANCE:0","MAP_NAVEL_ROCK_ENTRANCE:1/MAP_NAVEL_ROCK_EXTERIOR:1":"MAP_NAVEL_ROCK_EXTERIOR:1/MAP_NAVEL_ROCK_ENTRANCE:1","MAP_NAVEL_ROCK_EXTERIOR:0/MAP_NAVEL_ROCK_HARBOR:0":"MAP_NAVEL_ROCK_HARBOR:0/MAP_NAVEL_ROCK_EXTERIOR:0","MAP_NAVEL_ROCK_EXTERIOR:1/MAP_NAVEL_ROCK_ENTRANCE:1":"MAP_NAVEL_ROCK_ENTRANCE:1/MAP_NAVEL_ROCK_EXTERIOR:1","MAP_NAVEL_ROCK_FORK:0/MAP_NAVEL_ROCK_UP1:0":"MAP_NAVEL_ROCK_UP1:0/MAP_NAVEL_ROCK_FORK:0","MAP_NAVEL_ROCK_FORK:1/MAP_NAVEL_ROCK_B1F:1":"MAP_NAVEL_ROCK_B1F:1/MAP_NAVEL_ROCK_FORK:1","MAP_NAVEL_ROCK_FORK:2/MAP_NAVEL_ROCK_DOWN01:0":"MAP_NAVEL_ROCK_DOWN01:0/MAP_NAVEL_ROCK_FORK:2","MAP_NAVEL_ROCK_HARBOR:0/MAP_NAVEL_ROCK_EXTERIOR:0":"MAP_NAVEL_ROCK_EXTERIOR:0/MAP_NAVEL_ROCK_HARBOR:0","MAP_NAVEL_ROCK_TOP:0/MAP_NAVEL_ROCK_UP4:1":"MAP_NAVEL_ROCK_UP4:1/MAP_NAVEL_ROCK_TOP:0","MAP_NAVEL_ROCK_UP1:0/MAP_NAVEL_ROCK_FORK:0":"MAP_NAVEL_ROCK_FORK:0/MAP_NAVEL_ROCK_UP1:0","MAP_NAVEL_ROCK_UP1:1/MAP_NAVEL_ROCK_UP2:0":"MAP_NAVEL_ROCK_UP2:0/MAP_NAVEL_ROCK_UP1:1","MAP_NAVEL_ROCK_UP2:0/MAP_NAVEL_ROCK_UP1:1":"MAP_NAVEL_ROCK_UP1:1/MAP_NAVEL_ROCK_UP2:0","MAP_NAVEL_ROCK_UP2:1/MAP_NAVEL_ROCK_UP3:0":"MAP_NAVEL_ROCK_UP3:0/MAP_NAVEL_ROCK_UP2:1","MAP_NAVEL_ROCK_UP3:0/MAP_NAVEL_ROCK_UP2:1":"MAP_NAVEL_ROCK_UP2:1/MAP_NAVEL_ROCK_UP3:0","MAP_NAVEL_ROCK_UP3:1/MAP_NAVEL_ROCK_UP4:0":"MAP_NAVEL_ROCK_UP4:0/MAP_NAVEL_ROCK_UP3:1","MAP_NAVEL_ROCK_UP4:0/MAP_NAVEL_ROCK_UP3:1":"MAP_NAVEL_ROCK_UP3:1/MAP_NAVEL_ROCK_UP4:0","MAP_NAVEL_ROCK_UP4:1/MAP_NAVEL_ROCK_TOP:0":"MAP_NAVEL_ROCK_TOP:0/MAP_NAVEL_ROCK_UP4:1","MAP_NEW_MAUVILLE_ENTRANCE:0/MAP_ROUTE110:0":"MAP_ROUTE110:0/MAP_NEW_MAUVILLE_ENTRANCE:0","MAP_NEW_MAUVILLE_ENTRANCE:1/MAP_NEW_MAUVILLE_INSIDE:0":"MAP_NEW_MAUVILLE_INSIDE:0/MAP_NEW_MAUVILLE_ENTRANCE:1","MAP_NEW_MAUVILLE_INSIDE:0/MAP_NEW_MAUVILLE_ENTRANCE:1":"MAP_NEW_MAUVILLE_ENTRANCE:1/MAP_NEW_MAUVILLE_INSIDE:0","MAP_OLDALE_TOWN:0/MAP_OLDALE_TOWN_HOUSE1:0":"MAP_OLDALE_TOWN_HOUSE1:0,1/MAP_OLDALE_TOWN:0","MAP_OLDALE_TOWN:1/MAP_OLDALE_TOWN_HOUSE2:0":"MAP_OLDALE_TOWN_HOUSE2:0,1/MAP_OLDALE_TOWN:1","MAP_OLDALE_TOWN:2/MAP_OLDALE_TOWN_POKEMON_CENTER_1F:0":"MAP_OLDALE_TOWN_POKEMON_CENTER_1F:0,1/MAP_OLDALE_TOWN:2","MAP_OLDALE_TOWN:3/MAP_OLDALE_TOWN_MART:0":"MAP_OLDALE_TOWN_MART:0,1/MAP_OLDALE_TOWN:3","MAP_OLDALE_TOWN_HOUSE1:0,1/MAP_OLDALE_TOWN:0":"MAP_OLDALE_TOWN:0/MAP_OLDALE_TOWN_HOUSE1:0","MAP_OLDALE_TOWN_HOUSE2:0,1/MAP_OLDALE_TOWN:1":"MAP_OLDALE_TOWN:1/MAP_OLDALE_TOWN_HOUSE2:0","MAP_OLDALE_TOWN_MART:0,1/MAP_OLDALE_TOWN:3":"MAP_OLDALE_TOWN:3/MAP_OLDALE_TOWN_MART:0","MAP_OLDALE_TOWN_POKEMON_CENTER_1F:0,1/MAP_OLDALE_TOWN:2":"MAP_OLDALE_TOWN:2/MAP_OLDALE_TOWN_POKEMON_CENTER_1F:0","MAP_OLDALE_TOWN_POKEMON_CENTER_1F:2/MAP_OLDALE_TOWN_POKEMON_CENTER_2F:0":"MAP_OLDALE_TOWN_POKEMON_CENTER_2F:0/MAP_OLDALE_TOWN_POKEMON_CENTER_1F:2","MAP_OLDALE_TOWN_POKEMON_CENTER_2F:0/MAP_OLDALE_TOWN_POKEMON_CENTER_1F:2":"MAP_OLDALE_TOWN_POKEMON_CENTER_1F:2/MAP_OLDALE_TOWN_POKEMON_CENTER_2F:0","MAP_OLDALE_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_OLDALE_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_PACIFIDLOG_TOWN:0/MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F:0":"MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F:0,1/MAP_PACIFIDLOG_TOWN:0","MAP_PACIFIDLOG_TOWN:1/MAP_PACIFIDLOG_TOWN_HOUSE1:0":"MAP_PACIFIDLOG_TOWN_HOUSE1:0,1/MAP_PACIFIDLOG_TOWN:1","MAP_PACIFIDLOG_TOWN:2/MAP_PACIFIDLOG_TOWN_HOUSE2:0":"MAP_PACIFIDLOG_TOWN_HOUSE2:0,1/MAP_PACIFIDLOG_TOWN:2","MAP_PACIFIDLOG_TOWN:3/MAP_PACIFIDLOG_TOWN_HOUSE3:0":"MAP_PACIFIDLOG_TOWN_HOUSE3:0,1/MAP_PACIFIDLOG_TOWN:3","MAP_PACIFIDLOG_TOWN:4/MAP_PACIFIDLOG_TOWN_HOUSE4:0":"MAP_PACIFIDLOG_TOWN_HOUSE4:0,1/MAP_PACIFIDLOG_TOWN:4","MAP_PACIFIDLOG_TOWN:5/MAP_PACIFIDLOG_TOWN_HOUSE5:0":"MAP_PACIFIDLOG_TOWN_HOUSE5:0,1/MAP_PACIFIDLOG_TOWN:5","MAP_PACIFIDLOG_TOWN_HOUSE1:0,1/MAP_PACIFIDLOG_TOWN:1":"MAP_PACIFIDLOG_TOWN:1/MAP_PACIFIDLOG_TOWN_HOUSE1:0","MAP_PACIFIDLOG_TOWN_HOUSE2:0,1/MAP_PACIFIDLOG_TOWN:2":"MAP_PACIFIDLOG_TOWN:2/MAP_PACIFIDLOG_TOWN_HOUSE2:0","MAP_PACIFIDLOG_TOWN_HOUSE3:0,1/MAP_PACIFIDLOG_TOWN:3":"MAP_PACIFIDLOG_TOWN:3/MAP_PACIFIDLOG_TOWN_HOUSE3:0","MAP_PACIFIDLOG_TOWN_HOUSE4:0,1/MAP_PACIFIDLOG_TOWN:4":"MAP_PACIFIDLOG_TOWN:4/MAP_PACIFIDLOG_TOWN_HOUSE4:0","MAP_PACIFIDLOG_TOWN_HOUSE5:0,1/MAP_PACIFIDLOG_TOWN:5":"MAP_PACIFIDLOG_TOWN:5/MAP_PACIFIDLOG_TOWN_HOUSE5:0","MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F:0,1/MAP_PACIFIDLOG_TOWN:0":"MAP_PACIFIDLOG_TOWN:0/MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F:0","MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F:2/MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F:0":"MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F:0/MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F:2","MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F:0/MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F:2":"MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F:2/MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F:0","MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_PETALBURG_CITY:0/MAP_PETALBURG_CITY_HOUSE1:0":"MAP_PETALBURG_CITY_HOUSE1:0,1/MAP_PETALBURG_CITY:0","MAP_PETALBURG_CITY:1/MAP_PETALBURG_CITY_WALLYS_HOUSE:0":"MAP_PETALBURG_CITY_WALLYS_HOUSE:0,1/MAP_PETALBURG_CITY:1","MAP_PETALBURG_CITY:2/MAP_PETALBURG_CITY_GYM:0":"MAP_PETALBURG_CITY_GYM:0,1/MAP_PETALBURG_CITY:2","MAP_PETALBURG_CITY:3/MAP_PETALBURG_CITY_POKEMON_CENTER_1F:0":"MAP_PETALBURG_CITY_POKEMON_CENTER_1F:0,1/MAP_PETALBURG_CITY:3","MAP_PETALBURG_CITY:4/MAP_PETALBURG_CITY_HOUSE2:0":"MAP_PETALBURG_CITY_HOUSE2:0,1/MAP_PETALBURG_CITY:4","MAP_PETALBURG_CITY:5/MAP_PETALBURG_CITY_MART:0":"MAP_PETALBURG_CITY_MART:0,1/MAP_PETALBURG_CITY:5","MAP_PETALBURG_CITY_GYM:0,1/MAP_PETALBURG_CITY:2":"MAP_PETALBURG_CITY:2/MAP_PETALBURG_CITY_GYM:0","MAP_PETALBURG_CITY_GYM:10,11/MAP_PETALBURG_CITY_GYM:8":"MAP_PETALBURG_CITY_GYM:8/MAP_PETALBURG_CITY_GYM:10","MAP_PETALBURG_CITY_GYM:12,13/MAP_PETALBURG_CITY_GYM:9":"MAP_PETALBURG_CITY_GYM:9/MAP_PETALBURG_CITY_GYM:12","MAP_PETALBURG_CITY_GYM:14/MAP_PETALBURG_CITY_GYM:16":"MAP_PETALBURG_CITY_GYM:16,17/MAP_PETALBURG_CITY_GYM:14","MAP_PETALBURG_CITY_GYM:15/MAP_PETALBURG_CITY_GYM:18":"MAP_PETALBURG_CITY_GYM:18,19/MAP_PETALBURG_CITY_GYM:15","MAP_PETALBURG_CITY_GYM:16,17/MAP_PETALBURG_CITY_GYM:14":"MAP_PETALBURG_CITY_GYM:14/MAP_PETALBURG_CITY_GYM:16","MAP_PETALBURG_CITY_GYM:18,19/MAP_PETALBURG_CITY_GYM:15":"MAP_PETALBURG_CITY_GYM:15/MAP_PETALBURG_CITY_GYM:18","MAP_PETALBURG_CITY_GYM:2/MAP_PETALBURG_CITY_GYM:3":"MAP_PETALBURG_CITY_GYM:3,4/MAP_PETALBURG_CITY_GYM:2","MAP_PETALBURG_CITY_GYM:20/MAP_PETALBURG_CITY_GYM:24":"MAP_PETALBURG_CITY_GYM:24,25/MAP_PETALBURG_CITY_GYM:20","MAP_PETALBURG_CITY_GYM:21/MAP_PETALBURG_CITY_GYM:26":"MAP_PETALBURG_CITY_GYM:26,27/MAP_PETALBURG_CITY_GYM:21","MAP_PETALBURG_CITY_GYM:22/MAP_PETALBURG_CITY_GYM:28":"MAP_PETALBURG_CITY_GYM:28,29/MAP_PETALBURG_CITY_GYM:22","MAP_PETALBURG_CITY_GYM:23/MAP_PETALBURG_CITY_GYM:30":"MAP_PETALBURG_CITY_GYM:30,31/MAP_PETALBURG_CITY_GYM:23","MAP_PETALBURG_CITY_GYM:24,25/MAP_PETALBURG_CITY_GYM:20":"MAP_PETALBURG_CITY_GYM:20/MAP_PETALBURG_CITY_GYM:24","MAP_PETALBURG_CITY_GYM:26,27/MAP_PETALBURG_CITY_GYM:21":"MAP_PETALBURG_CITY_GYM:21/MAP_PETALBURG_CITY_GYM:26","MAP_PETALBURG_CITY_GYM:28,29/MAP_PETALBURG_CITY_GYM:22":"MAP_PETALBURG_CITY_GYM:22/MAP_PETALBURG_CITY_GYM:28","MAP_PETALBURG_CITY_GYM:3,4/MAP_PETALBURG_CITY_GYM:2":"MAP_PETALBURG_CITY_GYM:2/MAP_PETALBURG_CITY_GYM:3","MAP_PETALBURG_CITY_GYM:30,31/MAP_PETALBURG_CITY_GYM:23":"MAP_PETALBURG_CITY_GYM:23/MAP_PETALBURG_CITY_GYM:30","MAP_PETALBURG_CITY_GYM:32/MAP_PETALBURG_CITY_GYM:34":"MAP_PETALBURG_CITY_GYM:34,35/MAP_PETALBURG_CITY_GYM:32","MAP_PETALBURG_CITY_GYM:33/MAP_PETALBURG_CITY_GYM:36":"MAP_PETALBURG_CITY_GYM:36,37/MAP_PETALBURG_CITY_GYM:33","MAP_PETALBURG_CITY_GYM:34,35/MAP_PETALBURG_CITY_GYM:32":"MAP_PETALBURG_CITY_GYM:32/MAP_PETALBURG_CITY_GYM:34","MAP_PETALBURG_CITY_GYM:36,37/MAP_PETALBURG_CITY_GYM:33":"MAP_PETALBURG_CITY_GYM:33/MAP_PETALBURG_CITY_GYM:36","MAP_PETALBURG_CITY_GYM:5/MAP_PETALBURG_CITY_GYM:6":"MAP_PETALBURG_CITY_GYM:6,7/MAP_PETALBURG_CITY_GYM:5","MAP_PETALBURG_CITY_GYM:6,7/MAP_PETALBURG_CITY_GYM:5":"MAP_PETALBURG_CITY_GYM:5/MAP_PETALBURG_CITY_GYM:6","MAP_PETALBURG_CITY_GYM:8/MAP_PETALBURG_CITY_GYM:10":"MAP_PETALBURG_CITY_GYM:10,11/MAP_PETALBURG_CITY_GYM:8","MAP_PETALBURG_CITY_GYM:9/MAP_PETALBURG_CITY_GYM:12":"MAP_PETALBURG_CITY_GYM:12,13/MAP_PETALBURG_CITY_GYM:9","MAP_PETALBURG_CITY_HOUSE1:0,1/MAP_PETALBURG_CITY:0":"MAP_PETALBURG_CITY:0/MAP_PETALBURG_CITY_HOUSE1:0","MAP_PETALBURG_CITY_HOUSE2:0,1/MAP_PETALBURG_CITY:4":"MAP_PETALBURG_CITY:4/MAP_PETALBURG_CITY_HOUSE2:0","MAP_PETALBURG_CITY_MART:0,1/MAP_PETALBURG_CITY:5":"MAP_PETALBURG_CITY:5/MAP_PETALBURG_CITY_MART:0","MAP_PETALBURG_CITY_POKEMON_CENTER_1F:0,1/MAP_PETALBURG_CITY:3":"MAP_PETALBURG_CITY:3/MAP_PETALBURG_CITY_POKEMON_CENTER_1F:0","MAP_PETALBURG_CITY_POKEMON_CENTER_1F:2/MAP_PETALBURG_CITY_POKEMON_CENTER_2F:0":"MAP_PETALBURG_CITY_POKEMON_CENTER_2F:0/MAP_PETALBURG_CITY_POKEMON_CENTER_1F:2","MAP_PETALBURG_CITY_POKEMON_CENTER_2F:0/MAP_PETALBURG_CITY_POKEMON_CENTER_1F:2":"MAP_PETALBURG_CITY_POKEMON_CENTER_1F:2/MAP_PETALBURG_CITY_POKEMON_CENTER_2F:0","MAP_PETALBURG_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_PETALBURG_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_PETALBURG_CITY_WALLYS_HOUSE:0,1/MAP_PETALBURG_CITY:1":"MAP_PETALBURG_CITY:1/MAP_PETALBURG_CITY_WALLYS_HOUSE:0","MAP_PETALBURG_WOODS:0,1/MAP_ROUTE104:2,3":"MAP_ROUTE104:2,3/MAP_PETALBURG_WOODS:0,1","MAP_PETALBURG_WOODS:2,3/MAP_ROUTE104:4,5":"MAP_ROUTE104:4,5/MAP_PETALBURG_WOODS:2,3","MAP_PETALBURG_WOODS:4,5/MAP_ROUTE104:6,7":"MAP_ROUTE104:6,7/MAP_PETALBURG_WOODS:4,5","MAP_RECORD_CORNER:0,1,2,3/MAP_DYNAMIC:-1!":"","MAP_ROUTE103:0/MAP_ALTERING_CAVE:0":"MAP_ALTERING_CAVE:0/MAP_ROUTE103:0","MAP_ROUTE104:0/MAP_ROUTE104_MR_BRINEYS_HOUSE:0":"MAP_ROUTE104_MR_BRINEYS_HOUSE:0,1/MAP_ROUTE104:0","MAP_ROUTE104:1/MAP_ROUTE104_PRETTY_PETAL_FLOWER_SHOP:0":"MAP_ROUTE104_PRETTY_PETAL_FLOWER_SHOP:0,1/MAP_ROUTE104:1","MAP_ROUTE104:2,3/MAP_PETALBURG_WOODS:0,1":"MAP_PETALBURG_WOODS:0,1/MAP_ROUTE104:2,3","MAP_ROUTE104:4,5/MAP_PETALBURG_WOODS:2,3":"MAP_PETALBURG_WOODS:2,3/MAP_ROUTE104:4,5","MAP_ROUTE104:6,7/MAP_PETALBURG_WOODS:4,5":"MAP_PETALBURG_WOODS:4,5/MAP_ROUTE104:6,7","MAP_ROUTE104_MR_BRINEYS_HOUSE:0,1/MAP_ROUTE104:0":"MAP_ROUTE104:0/MAP_ROUTE104_MR_BRINEYS_HOUSE:0","MAP_ROUTE104_PRETTY_PETAL_FLOWER_SHOP:0,1/MAP_ROUTE104:1":"MAP_ROUTE104:1/MAP_ROUTE104_PRETTY_PETAL_FLOWER_SHOP:0","MAP_ROUTE105:0/MAP_ISLAND_CAVE:0":"MAP_ISLAND_CAVE:0/MAP_ROUTE105:0","MAP_ROUTE106:0/MAP_GRANITE_CAVE_1F:0":"MAP_GRANITE_CAVE_1F:0/MAP_ROUTE106:0","MAP_ROUTE108:0/MAP_ABANDONED_SHIP_DECK:0":"MAP_ABANDONED_SHIP_DECK:0,1/MAP_ROUTE108:0","MAP_ROUTE109:0/MAP_ROUTE109_SEASHORE_HOUSE:0":"MAP_ROUTE109_SEASHORE_HOUSE:0,1/MAP_ROUTE109:0","MAP_ROUTE109_SEASHORE_HOUSE:0,1/MAP_ROUTE109:0":"MAP_ROUTE109:0/MAP_ROUTE109_SEASHORE_HOUSE:0","MAP_ROUTE110:0/MAP_NEW_MAUVILLE_ENTRANCE:0":"MAP_NEW_MAUVILLE_ENTRANCE:0/MAP_ROUTE110:0","MAP_ROUTE110:1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:0":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:0,1/MAP_ROUTE110:1","MAP_ROUTE110:2/MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE:0":"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE:0,1/MAP_ROUTE110:2","MAP_ROUTE110:3/MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE:2":"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE:2,3/MAP_ROUTE110:3","MAP_ROUTE110:4/MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE:0":"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE:0,1/MAP_ROUTE110:4","MAP_ROUTE110:5/MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE:2":"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE:2,3/MAP_ROUTE110:5","MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE:0,1/MAP_ROUTE110:4":"MAP_ROUTE110:4/MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE:0","MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE:2,3/MAP_ROUTE110:5":"MAP_ROUTE110:5/MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE:2","MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE:0,1/MAP_ROUTE110:2":"MAP_ROUTE110:2/MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE:0","MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE:2,3/MAP_ROUTE110:3":"MAP_ROUTE110:3/MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE:2","MAP_ROUTE110_TRICK_HOUSE_CORRIDOR:0,1/MAP_ROUTE110_TRICK_HOUSE_END:1":"MAP_ROUTE110_TRICK_HOUSE_END:1/MAP_ROUTE110_TRICK_HOUSE_CORRIDOR:0","MAP_ROUTE110_TRICK_HOUSE_CORRIDOR:2,3/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2/MAP_ROUTE110_TRICK_HOUSE_END:0","MAP_ROUTE110_TRICK_HOUSE_END:1/MAP_ROUTE110_TRICK_HOUSE_CORRIDOR:0":"MAP_ROUTE110_TRICK_HOUSE_CORRIDOR:0,1/MAP_ROUTE110_TRICK_HOUSE_END:1","MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:0,1/MAP_ROUTE110:1":"MAP_ROUTE110:1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:0","MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2","MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2/MAP_ROUTE110_TRICK_HOUSE_END:0":"MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2","MAP_ROUTE110_TRICK_HOUSE_PUZZLE2:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_PUZZLE2:2/MAP_ROUTE110_TRICK_HOUSE_END:0!":"MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2","MAP_ROUTE110_TRICK_HOUSE_PUZZLE3:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_PUZZLE3:2/MAP_ROUTE110_TRICK_HOUSE_END:0!":"MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2","MAP_ROUTE110_TRICK_HOUSE_PUZZLE4:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_PUZZLE4:2/MAP_ROUTE110_TRICK_HOUSE_END:0!":"MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2","MAP_ROUTE110_TRICK_HOUSE_PUZZLE5:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_PUZZLE5:2/MAP_ROUTE110_TRICK_HOUSE_END:0!":"MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2","MAP_ROUTE110_TRICK_HOUSE_PUZZLE6:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_PUZZLE6:2/MAP_ROUTE110_TRICK_HOUSE_END:0!":"MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:10/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:9":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:9/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:10","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:11/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:12":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:12/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:11","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:12/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:11":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:11/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:12","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:2/MAP_ROUTE110_TRICK_HOUSE_END:0!":"MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:3/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:4":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:4/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:3","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:4/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:3":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:3/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:4","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:5/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:6":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:6/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:5","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:6/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:5":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:5/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:6","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:7/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:8":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:8/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:7","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:8/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:7":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:7/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:8","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:9/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:10":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:10/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:9","MAP_ROUTE110_TRICK_HOUSE_PUZZLE8:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_PUZZLE8:2/MAP_ROUTE110_TRICK_HOUSE_END:0!":"MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2","MAP_ROUTE111:0/MAP_ROUTE111_WINSTRATE_FAMILYS_HOUSE:0":"MAP_ROUTE111_WINSTRATE_FAMILYS_HOUSE:0,1/MAP_ROUTE111:0","MAP_ROUTE111:1/MAP_DESERT_RUINS:0":"MAP_DESERT_RUINS:0/MAP_ROUTE111:1","MAP_ROUTE111:2/MAP_ROUTE111_OLD_LADYS_REST_STOP:0":"MAP_ROUTE111_OLD_LADYS_REST_STOP:0,1/MAP_ROUTE111:2","MAP_ROUTE111:3/MAP_MIRAGE_TOWER_1F:0":"MAP_MIRAGE_TOWER_1F:0/MAP_ROUTE111:3","MAP_ROUTE111:4/MAP_TRAINER_HILL_ENTRANCE:0":"MAP_TRAINER_HILL_ENTRANCE:0,1/MAP_ROUTE111:4","MAP_ROUTE111_OLD_LADYS_REST_STOP:0,1/MAP_ROUTE111:2":"MAP_ROUTE111:2/MAP_ROUTE111_OLD_LADYS_REST_STOP:0","MAP_ROUTE111_WINSTRATE_FAMILYS_HOUSE:0,1/MAP_ROUTE111:0":"MAP_ROUTE111:0/MAP_ROUTE111_WINSTRATE_FAMILYS_HOUSE:0","MAP_ROUTE112:0,1/MAP_ROUTE112_CABLE_CAR_STATION:0,1":"MAP_ROUTE112_CABLE_CAR_STATION:0,1/MAP_ROUTE112:0,1","MAP_ROUTE112:2,3/MAP_JAGGED_PASS:0,1":"MAP_JAGGED_PASS:0,1/MAP_ROUTE112:2,3","MAP_ROUTE112:4/MAP_FIERY_PATH:0":"MAP_FIERY_PATH:0/MAP_ROUTE112:4","MAP_ROUTE112:5/MAP_FIERY_PATH:1":"MAP_FIERY_PATH:1/MAP_ROUTE112:5","MAP_ROUTE112_CABLE_CAR_STATION:0,1/MAP_ROUTE112:0,1":"MAP_ROUTE112:0,1/MAP_ROUTE112_CABLE_CAR_STATION:0,1","MAP_ROUTE113:0/MAP_ROUTE113_GLASS_WORKSHOP:0":"MAP_ROUTE113_GLASS_WORKSHOP:0,1/MAP_ROUTE113:0","MAP_ROUTE113:1/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE113:2/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE113_GLASS_WORKSHOP:0,1/MAP_ROUTE113:0":"MAP_ROUTE113:0/MAP_ROUTE113_GLASS_WORKSHOP:0","MAP_ROUTE114:0/MAP_METEOR_FALLS_1F_1R:0":"MAP_METEOR_FALLS_1F_1R:0/MAP_ROUTE114:0","MAP_ROUTE114:1/MAP_ROUTE114_FOSSIL_MANIACS_HOUSE:0":"MAP_ROUTE114_FOSSIL_MANIACS_HOUSE:0,1/MAP_ROUTE114:1","MAP_ROUTE114:2/MAP_ROUTE114_LANETTES_HOUSE:0":"MAP_ROUTE114_LANETTES_HOUSE:0,1/MAP_ROUTE114:2","MAP_ROUTE114:3/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE114:4/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE114_FOSSIL_MANIACS_HOUSE:0,1/MAP_ROUTE114:1":"MAP_ROUTE114:1/MAP_ROUTE114_FOSSIL_MANIACS_HOUSE:0","MAP_ROUTE114_FOSSIL_MANIACS_HOUSE:2/MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:0":"MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:0,1/MAP_ROUTE114_FOSSIL_MANIACS_HOUSE:2","MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:0,1/MAP_ROUTE114_FOSSIL_MANIACS_HOUSE:2":"MAP_ROUTE114_FOSSIL_MANIACS_HOUSE:2/MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:0","MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:2/MAP_DESERT_UNDERPASS:0":"MAP_DESERT_UNDERPASS:0/MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:2","MAP_ROUTE114_LANETTES_HOUSE:0,1/MAP_ROUTE114:2":"MAP_ROUTE114:2/MAP_ROUTE114_LANETTES_HOUSE:0","MAP_ROUTE115:0/MAP_METEOR_FALLS_1F_1R:1":"MAP_METEOR_FALLS_1F_1R:1/MAP_ROUTE115:0","MAP_ROUTE115:1/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE115:2/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE116:0/MAP_RUSTURF_TUNNEL:0":"MAP_RUSTURF_TUNNEL:0/MAP_ROUTE116:0","MAP_ROUTE116:1/MAP_ROUTE116_TUNNELERS_REST_HOUSE:0":"MAP_ROUTE116_TUNNELERS_REST_HOUSE:0,1/MAP_ROUTE116:1","MAP_ROUTE116:2/MAP_RUSTURF_TUNNEL:2":"MAP_RUSTURF_TUNNEL:2/MAP_ROUTE116:2","MAP_ROUTE116:3/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE116:4/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE116_TUNNELERS_REST_HOUSE:0,1/MAP_ROUTE116:1":"MAP_ROUTE116:1/MAP_ROUTE116_TUNNELERS_REST_HOUSE:0","MAP_ROUTE117:0/MAP_ROUTE117_POKEMON_DAY_CARE:0":"MAP_ROUTE117_POKEMON_DAY_CARE:0,1/MAP_ROUTE117:0","MAP_ROUTE117_POKEMON_DAY_CARE:0,1/MAP_ROUTE117:0":"MAP_ROUTE117:0/MAP_ROUTE117_POKEMON_DAY_CARE:0","MAP_ROUTE118:0/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE118:1/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE119:0/MAP_ROUTE119_WEATHER_INSTITUTE_1F:0":"MAP_ROUTE119_WEATHER_INSTITUTE_1F:0,1/MAP_ROUTE119:0","MAP_ROUTE119:1/MAP_ROUTE119_HOUSE:0":"MAP_ROUTE119_HOUSE:0,1/MAP_ROUTE119:1","MAP_ROUTE119_HOUSE:0,1/MAP_ROUTE119:1":"MAP_ROUTE119:1/MAP_ROUTE119_HOUSE:0","MAP_ROUTE119_WEATHER_INSTITUTE_1F:0,1/MAP_ROUTE119:0":"MAP_ROUTE119:0/MAP_ROUTE119_WEATHER_INSTITUTE_1F:0","MAP_ROUTE119_WEATHER_INSTITUTE_1F:2/MAP_ROUTE119_WEATHER_INSTITUTE_2F:0":"MAP_ROUTE119_WEATHER_INSTITUTE_2F:0/MAP_ROUTE119_WEATHER_INSTITUTE_1F:2","MAP_ROUTE119_WEATHER_INSTITUTE_2F:0/MAP_ROUTE119_WEATHER_INSTITUTE_1F:2":"MAP_ROUTE119_WEATHER_INSTITUTE_1F:2/MAP_ROUTE119_WEATHER_INSTITUTE_2F:0","MAP_ROUTE120:0/MAP_ANCIENT_TOMB:0":"MAP_ANCIENT_TOMB:0/MAP_ROUTE120:0","MAP_ROUTE120:1/MAP_SCORCHED_SLAB:0":"MAP_SCORCHED_SLAB:0/MAP_ROUTE120:1","MAP_ROUTE121:0/MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:2":"MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:2,3/MAP_ROUTE121:0","MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:0,1/MAP_SAFARI_ZONE_SOUTH:0":"MAP_SAFARI_ZONE_SOUTH:0/MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:0","MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:2,3/MAP_ROUTE121:0":"MAP_ROUTE121:0/MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:2","MAP_ROUTE122:0/MAP_MT_PYRE_1F:0":"MAP_MT_PYRE_1F:0,2/MAP_ROUTE122:0","MAP_ROUTE123:0/MAP_ROUTE123_BERRY_MASTERS_HOUSE:0":"MAP_ROUTE123_BERRY_MASTERS_HOUSE:0,1/MAP_ROUTE123:0","MAP_ROUTE123_BERRY_MASTERS_HOUSE:0,1/MAP_ROUTE123:0":"MAP_ROUTE123:0/MAP_ROUTE123_BERRY_MASTERS_HOUSE:0","MAP_ROUTE124:0/MAP_ROUTE124_DIVING_TREASURE_HUNTERS_HOUSE:0":"MAP_ROUTE124_DIVING_TREASURE_HUNTERS_HOUSE:0,1/MAP_ROUTE124:0","MAP_ROUTE124_DIVING_TREASURE_HUNTERS_HOUSE:0,1/MAP_ROUTE124:0":"MAP_ROUTE124:0/MAP_ROUTE124_DIVING_TREASURE_HUNTERS_HOUSE:0","MAP_ROUTE125:0/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:0":"MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:0/MAP_ROUTE125:0","MAP_ROUTE131:0/MAP_SKY_PILLAR_ENTRANCE:0":"MAP_SKY_PILLAR_ENTRANCE:0/MAP_ROUTE131:0","MAP_RUSTBORO_CITY:0/MAP_RUSTBORO_CITY_GYM:0":"MAP_RUSTBORO_CITY_GYM:0,1/MAP_RUSTBORO_CITY:0","MAP_RUSTBORO_CITY:1/MAP_RUSTBORO_CITY_FLAT1_1F:0":"MAP_RUSTBORO_CITY_FLAT1_1F:0,1/MAP_RUSTBORO_CITY:1","MAP_RUSTBORO_CITY:10/MAP_RUSTBORO_CITY_FLAT2_1F:0":"MAP_RUSTBORO_CITY_FLAT2_1F:0,1/MAP_RUSTBORO_CITY:10","MAP_RUSTBORO_CITY:11/MAP_RUSTBORO_CITY_HOUSE3:0":"MAP_RUSTBORO_CITY_HOUSE3:0,1/MAP_RUSTBORO_CITY:11","MAP_RUSTBORO_CITY:2/MAP_RUSTBORO_CITY_MART:0":"MAP_RUSTBORO_CITY_MART:0,1/MAP_RUSTBORO_CITY:2","MAP_RUSTBORO_CITY:3/MAP_RUSTBORO_CITY_POKEMON_CENTER_1F:0":"MAP_RUSTBORO_CITY_POKEMON_CENTER_1F:0,1/MAP_RUSTBORO_CITY:3","MAP_RUSTBORO_CITY:4/MAP_RUSTBORO_CITY_POKEMON_SCHOOL:0":"MAP_RUSTBORO_CITY_POKEMON_SCHOOL:0,1/MAP_RUSTBORO_CITY:4","MAP_RUSTBORO_CITY:5,6/MAP_RUSTBORO_CITY_DEVON_CORP_1F:0,1":"MAP_RUSTBORO_CITY_DEVON_CORP_1F:0,1/MAP_RUSTBORO_CITY:5,6","MAP_RUSTBORO_CITY:7/MAP_RUSTBORO_CITY_HOUSE1:0":"MAP_RUSTBORO_CITY_HOUSE1:0,1/MAP_RUSTBORO_CITY:7","MAP_RUSTBORO_CITY:8/MAP_RUSTBORO_CITY_CUTTERS_HOUSE:0":"MAP_RUSTBORO_CITY_CUTTERS_HOUSE:0,1/MAP_RUSTBORO_CITY:8","MAP_RUSTBORO_CITY:9/MAP_RUSTBORO_CITY_HOUSE2:0":"MAP_RUSTBORO_CITY_HOUSE2:0,1/MAP_RUSTBORO_CITY:9","MAP_RUSTBORO_CITY_CUTTERS_HOUSE:0,1/MAP_RUSTBORO_CITY:8":"MAP_RUSTBORO_CITY:8/MAP_RUSTBORO_CITY_CUTTERS_HOUSE:0","MAP_RUSTBORO_CITY_DEVON_CORP_1F:0,1/MAP_RUSTBORO_CITY:5,6":"MAP_RUSTBORO_CITY:5,6/MAP_RUSTBORO_CITY_DEVON_CORP_1F:0,1","MAP_RUSTBORO_CITY_DEVON_CORP_1F:2/MAP_RUSTBORO_CITY_DEVON_CORP_2F:0":"MAP_RUSTBORO_CITY_DEVON_CORP_2F:0/MAP_RUSTBORO_CITY_DEVON_CORP_1F:2","MAP_RUSTBORO_CITY_DEVON_CORP_2F:0/MAP_RUSTBORO_CITY_DEVON_CORP_1F:2":"MAP_RUSTBORO_CITY_DEVON_CORP_1F:2/MAP_RUSTBORO_CITY_DEVON_CORP_2F:0","MAP_RUSTBORO_CITY_DEVON_CORP_2F:1/MAP_RUSTBORO_CITY_DEVON_CORP_3F:0":"MAP_RUSTBORO_CITY_DEVON_CORP_3F:0/MAP_RUSTBORO_CITY_DEVON_CORP_2F:1","MAP_RUSTBORO_CITY_DEVON_CORP_3F:0/MAP_RUSTBORO_CITY_DEVON_CORP_2F:1":"MAP_RUSTBORO_CITY_DEVON_CORP_2F:1/MAP_RUSTBORO_CITY_DEVON_CORP_3F:0","MAP_RUSTBORO_CITY_FLAT1_1F:0,1/MAP_RUSTBORO_CITY:1":"MAP_RUSTBORO_CITY:1/MAP_RUSTBORO_CITY_FLAT1_1F:0","MAP_RUSTBORO_CITY_FLAT1_1F:2/MAP_RUSTBORO_CITY_FLAT1_2F:0":"MAP_RUSTBORO_CITY_FLAT1_2F:0/MAP_RUSTBORO_CITY_FLAT1_1F:2","MAP_RUSTBORO_CITY_FLAT1_2F:0/MAP_RUSTBORO_CITY_FLAT1_1F:2":"MAP_RUSTBORO_CITY_FLAT1_1F:2/MAP_RUSTBORO_CITY_FLAT1_2F:0","MAP_RUSTBORO_CITY_FLAT2_1F:0,1/MAP_RUSTBORO_CITY:10":"MAP_RUSTBORO_CITY:10/MAP_RUSTBORO_CITY_FLAT2_1F:0","MAP_RUSTBORO_CITY_FLAT2_1F:2/MAP_RUSTBORO_CITY_FLAT2_2F:0":"MAP_RUSTBORO_CITY_FLAT2_2F:0/MAP_RUSTBORO_CITY_FLAT2_1F:2","MAP_RUSTBORO_CITY_FLAT2_2F:0/MAP_RUSTBORO_CITY_FLAT2_1F:2":"MAP_RUSTBORO_CITY_FLAT2_1F:2/MAP_RUSTBORO_CITY_FLAT2_2F:0","MAP_RUSTBORO_CITY_FLAT2_2F:1/MAP_RUSTBORO_CITY_FLAT2_3F:0":"MAP_RUSTBORO_CITY_FLAT2_3F:0/MAP_RUSTBORO_CITY_FLAT2_2F:1","MAP_RUSTBORO_CITY_FLAT2_3F:0/MAP_RUSTBORO_CITY_FLAT2_2F:1":"MAP_RUSTBORO_CITY_FLAT2_2F:1/MAP_RUSTBORO_CITY_FLAT2_3F:0","MAP_RUSTBORO_CITY_GYM:0,1/MAP_RUSTBORO_CITY:0":"MAP_RUSTBORO_CITY:0/MAP_RUSTBORO_CITY_GYM:0","MAP_RUSTBORO_CITY_HOUSE1:0,1/MAP_RUSTBORO_CITY:7":"MAP_RUSTBORO_CITY:7/MAP_RUSTBORO_CITY_HOUSE1:0","MAP_RUSTBORO_CITY_HOUSE2:0,1/MAP_RUSTBORO_CITY:9":"MAP_RUSTBORO_CITY:9/MAP_RUSTBORO_CITY_HOUSE2:0","MAP_RUSTBORO_CITY_HOUSE3:0,1/MAP_RUSTBORO_CITY:11":"MAP_RUSTBORO_CITY:11/MAP_RUSTBORO_CITY_HOUSE3:0","MAP_RUSTBORO_CITY_MART:0,1/MAP_RUSTBORO_CITY:2":"MAP_RUSTBORO_CITY:2/MAP_RUSTBORO_CITY_MART:0","MAP_RUSTBORO_CITY_POKEMON_CENTER_1F:0,1/MAP_RUSTBORO_CITY:3":"MAP_RUSTBORO_CITY:3/MAP_RUSTBORO_CITY_POKEMON_CENTER_1F:0","MAP_RUSTBORO_CITY_POKEMON_CENTER_1F:2/MAP_RUSTBORO_CITY_POKEMON_CENTER_2F:0":"MAP_RUSTBORO_CITY_POKEMON_CENTER_2F:0/MAP_RUSTBORO_CITY_POKEMON_CENTER_1F:2","MAP_RUSTBORO_CITY_POKEMON_CENTER_2F:0/MAP_RUSTBORO_CITY_POKEMON_CENTER_1F:2":"MAP_RUSTBORO_CITY_POKEMON_CENTER_1F:2/MAP_RUSTBORO_CITY_POKEMON_CENTER_2F:0","MAP_RUSTBORO_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_RUSTBORO_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_RUSTBORO_CITY_POKEMON_SCHOOL:0,1/MAP_RUSTBORO_CITY:4":"MAP_RUSTBORO_CITY:4/MAP_RUSTBORO_CITY_POKEMON_SCHOOL:0","MAP_RUSTURF_TUNNEL:0/MAP_ROUTE116:0":"MAP_ROUTE116:0/MAP_RUSTURF_TUNNEL:0","MAP_RUSTURF_TUNNEL:1/MAP_VERDANTURF_TOWN:4":"MAP_VERDANTURF_TOWN:4/MAP_RUSTURF_TUNNEL:1","MAP_RUSTURF_TUNNEL:2/MAP_ROUTE116:2":"MAP_ROUTE116:2/MAP_RUSTURF_TUNNEL:2","MAP_SAFARI_ZONE_REST_HOUSE:0,1/MAP_SAFARI_ZONE_SOUTHWEST:0":"MAP_SAFARI_ZONE_SOUTHWEST:0/MAP_SAFARI_ZONE_REST_HOUSE:0","MAP_SAFARI_ZONE_SOUTH:0/MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:0":"MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:0,1/MAP_SAFARI_ZONE_SOUTH:0","MAP_SAFARI_ZONE_SOUTHWEST:0/MAP_SAFARI_ZONE_REST_HOUSE:0":"MAP_SAFARI_ZONE_REST_HOUSE:0,1/MAP_SAFARI_ZONE_SOUTHWEST:0","MAP_SCORCHED_SLAB:0/MAP_ROUTE120:1":"MAP_ROUTE120:1/MAP_SCORCHED_SLAB:0","MAP_SEAFLOOR_CAVERN_ENTRANCE:0/MAP_UNDERWATER_ROUTE128:0!":"MAP_UNDERWATER_ROUTE128:0/MAP_UNDERWATER_SEAFLOOR_CAVERN:0","MAP_SEAFLOOR_CAVERN_ENTRANCE:1/MAP_SEAFLOOR_CAVERN_ROOM1:0":"MAP_SEAFLOOR_CAVERN_ROOM1:0/MAP_SEAFLOOR_CAVERN_ENTRANCE:1","MAP_SEAFLOOR_CAVERN_ROOM1:0/MAP_SEAFLOOR_CAVERN_ENTRANCE:1":"MAP_SEAFLOOR_CAVERN_ENTRANCE:1/MAP_SEAFLOOR_CAVERN_ROOM1:0","MAP_SEAFLOOR_CAVERN_ROOM1:1/MAP_SEAFLOOR_CAVERN_ROOM5:0":"MAP_SEAFLOOR_CAVERN_ROOM5:0/MAP_SEAFLOOR_CAVERN_ROOM1:1","MAP_SEAFLOOR_CAVERN_ROOM1:2/MAP_SEAFLOOR_CAVERN_ROOM2:0":"MAP_SEAFLOOR_CAVERN_ROOM2:0/MAP_SEAFLOOR_CAVERN_ROOM1:2","MAP_SEAFLOOR_CAVERN_ROOM2:0/MAP_SEAFLOOR_CAVERN_ROOM1:2":"MAP_SEAFLOOR_CAVERN_ROOM1:2/MAP_SEAFLOOR_CAVERN_ROOM2:0","MAP_SEAFLOOR_CAVERN_ROOM2:1/MAP_SEAFLOOR_CAVERN_ROOM4:0":"MAP_SEAFLOOR_CAVERN_ROOM4:0/MAP_SEAFLOOR_CAVERN_ROOM2:1","MAP_SEAFLOOR_CAVERN_ROOM2:2/MAP_SEAFLOOR_CAVERN_ROOM6:0":"MAP_SEAFLOOR_CAVERN_ROOM6:0/MAP_SEAFLOOR_CAVERN_ROOM2:2","MAP_SEAFLOOR_CAVERN_ROOM2:3/MAP_SEAFLOOR_CAVERN_ROOM7:0":"MAP_SEAFLOOR_CAVERN_ROOM7:0/MAP_SEAFLOOR_CAVERN_ROOM2:3","MAP_SEAFLOOR_CAVERN_ROOM3:0/MAP_SEAFLOOR_CAVERN_ROOM8:1":"MAP_SEAFLOOR_CAVERN_ROOM8:1/MAP_SEAFLOOR_CAVERN_ROOM3:0","MAP_SEAFLOOR_CAVERN_ROOM3:1/MAP_SEAFLOOR_CAVERN_ROOM7:1":"MAP_SEAFLOOR_CAVERN_ROOM7:1/MAP_SEAFLOOR_CAVERN_ROOM3:1","MAP_SEAFLOOR_CAVERN_ROOM3:2/MAP_SEAFLOOR_CAVERN_ROOM6:1":"MAP_SEAFLOOR_CAVERN_ROOM6:1/MAP_SEAFLOOR_CAVERN_ROOM3:2","MAP_SEAFLOOR_CAVERN_ROOM4:0/MAP_SEAFLOOR_CAVERN_ROOM2:1":"MAP_SEAFLOOR_CAVERN_ROOM2:1/MAP_SEAFLOOR_CAVERN_ROOM4:0","MAP_SEAFLOOR_CAVERN_ROOM4:1/MAP_SEAFLOOR_CAVERN_ROOM5:1":"MAP_SEAFLOOR_CAVERN_ROOM5:1/MAP_SEAFLOOR_CAVERN_ROOM4:1","MAP_SEAFLOOR_CAVERN_ROOM4:2/MAP_SEAFLOOR_CAVERN_ROOM5:2":"MAP_SEAFLOOR_CAVERN_ROOM5:2/MAP_SEAFLOOR_CAVERN_ROOM4:2","MAP_SEAFLOOR_CAVERN_ROOM4:3/MAP_SEAFLOOR_CAVERN_ENTRANCE:1!":"MAP_SEAFLOOR_CAVERN_ENTRANCE:1/MAP_SEAFLOOR_CAVERN_ROOM1:0","MAP_SEAFLOOR_CAVERN_ROOM5:0/MAP_SEAFLOOR_CAVERN_ROOM1:1":"MAP_SEAFLOOR_CAVERN_ROOM1:1/MAP_SEAFLOOR_CAVERN_ROOM5:0","MAP_SEAFLOOR_CAVERN_ROOM5:1/MAP_SEAFLOOR_CAVERN_ROOM4:1":"MAP_SEAFLOOR_CAVERN_ROOM4:1/MAP_SEAFLOOR_CAVERN_ROOM5:1","MAP_SEAFLOOR_CAVERN_ROOM5:2/MAP_SEAFLOOR_CAVERN_ROOM4:2":"MAP_SEAFLOOR_CAVERN_ROOM4:2/MAP_SEAFLOOR_CAVERN_ROOM5:2","MAP_SEAFLOOR_CAVERN_ROOM6:0/MAP_SEAFLOOR_CAVERN_ROOM2:2":"MAP_SEAFLOOR_CAVERN_ROOM2:2/MAP_SEAFLOOR_CAVERN_ROOM6:0","MAP_SEAFLOOR_CAVERN_ROOM6:1/MAP_SEAFLOOR_CAVERN_ROOM3:2":"MAP_SEAFLOOR_CAVERN_ROOM3:2/MAP_SEAFLOOR_CAVERN_ROOM6:1","MAP_SEAFLOOR_CAVERN_ROOM6:2/MAP_SEAFLOOR_CAVERN_ENTRANCE:1!":"MAP_SEAFLOOR_CAVERN_ENTRANCE:1/MAP_SEAFLOOR_CAVERN_ROOM1:0","MAP_SEAFLOOR_CAVERN_ROOM7:0/MAP_SEAFLOOR_CAVERN_ROOM2:3":"MAP_SEAFLOOR_CAVERN_ROOM2:3/MAP_SEAFLOOR_CAVERN_ROOM7:0","MAP_SEAFLOOR_CAVERN_ROOM7:1/MAP_SEAFLOOR_CAVERN_ROOM3:1":"MAP_SEAFLOOR_CAVERN_ROOM3:1/MAP_SEAFLOOR_CAVERN_ROOM7:1","MAP_SEAFLOOR_CAVERN_ROOM8:0/MAP_SEAFLOOR_CAVERN_ROOM9:0":"MAP_SEAFLOOR_CAVERN_ROOM9:0/MAP_SEAFLOOR_CAVERN_ROOM8:0","MAP_SEAFLOOR_CAVERN_ROOM8:1/MAP_SEAFLOOR_CAVERN_ROOM3:0":"MAP_SEAFLOOR_CAVERN_ROOM3:0/MAP_SEAFLOOR_CAVERN_ROOM8:1","MAP_SEAFLOOR_CAVERN_ROOM9:0/MAP_SEAFLOOR_CAVERN_ROOM8:0":"MAP_SEAFLOOR_CAVERN_ROOM8:0/MAP_SEAFLOOR_CAVERN_ROOM9:0","MAP_SEALED_CHAMBER_INNER_ROOM:0/MAP_SEALED_CHAMBER_OUTER_ROOM:0":"MAP_SEALED_CHAMBER_OUTER_ROOM:0/MAP_SEALED_CHAMBER_INNER_ROOM:0","MAP_SEALED_CHAMBER_OUTER_ROOM:0/MAP_SEALED_CHAMBER_INNER_ROOM:0":"MAP_SEALED_CHAMBER_INNER_ROOM:0/MAP_SEALED_CHAMBER_OUTER_ROOM:0","MAP_SECRET_BASE_BLUE_CAVE1:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_BLUE_CAVE2:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_BLUE_CAVE3:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_BLUE_CAVE4:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_BROWN_CAVE1:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_BROWN_CAVE2:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_BROWN_CAVE3:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_BROWN_CAVE4:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_RED_CAVE1:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_RED_CAVE2:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_RED_CAVE3:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_RED_CAVE4:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_SHRUB1:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_SHRUB2:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_SHRUB3:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_SHRUB4:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_TREE1:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_TREE2:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_TREE3:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_TREE4:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_YELLOW_CAVE1:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_YELLOW_CAVE2:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_YELLOW_CAVE3:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_YELLOW_CAVE4:0/MAP_DYNAMIC:-2!":"","MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:0/MAP_ROUTE125:0":"MAP_ROUTE125:0/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:0","MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:1/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:0":"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:0/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:1","MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:2/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:6":"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:6/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:2","MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:3/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:7":"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:7/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:3","MAP_SHOAL_CAVE_LOW_TIDE_ICE_ROOM:0/MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:3":"MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:3/MAP_SHOAL_CAVE_LOW_TIDE_ICE_ROOM:0","MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:0/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:1":"MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:1/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:0","MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:1/MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM:0":"MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM:0/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:1","MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:2/MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM:1":"MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM:1/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:2","MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:3/MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:0":"MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:0/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:3","MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:4/MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:1":"MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:1/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:4","MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:5/MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:2":"MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:2/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:5","MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:6/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:2":"MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:2/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:6","MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:7/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:3":"MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:3/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:7","MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:0/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:3":"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:3/MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:0","MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:1/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:4":"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:4/MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:1","MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:2/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:5":"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:5/MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:2","MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:3/MAP_SHOAL_CAVE_LOW_TIDE_ICE_ROOM:0":"MAP_SHOAL_CAVE_LOW_TIDE_ICE_ROOM:0/MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:3","MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM:0/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:1":"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:1/MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM:0","MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM:1/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:2":"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:2/MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM:1","MAP_SKY_PILLAR_1F:0,1/MAP_SKY_PILLAR_OUTSIDE:1":"MAP_SKY_PILLAR_OUTSIDE:1/MAP_SKY_PILLAR_1F:0","MAP_SKY_PILLAR_1F:2/MAP_SKY_PILLAR_2F:0":"MAP_SKY_PILLAR_2F:0/MAP_SKY_PILLAR_1F:2","MAP_SKY_PILLAR_2F:0/MAP_SKY_PILLAR_1F:2":"MAP_SKY_PILLAR_1F:2/MAP_SKY_PILLAR_2F:0","MAP_SKY_PILLAR_2F:1/MAP_SKY_PILLAR_3F:0":"MAP_SKY_PILLAR_3F:0/MAP_SKY_PILLAR_2F:1","MAP_SKY_PILLAR_3F:0/MAP_SKY_PILLAR_2F:1":"MAP_SKY_PILLAR_2F:1/MAP_SKY_PILLAR_3F:0","MAP_SKY_PILLAR_3F:1/MAP_SKY_PILLAR_4F:0":"MAP_SKY_PILLAR_4F:0/MAP_SKY_PILLAR_3F:1","MAP_SKY_PILLAR_3F:2/MAP_SKY_PILLAR_4F:1":"MAP_SKY_PILLAR_4F:1/MAP_SKY_PILLAR_3F:2","MAP_SKY_PILLAR_4F:0/MAP_SKY_PILLAR_3F:1":"MAP_SKY_PILLAR_3F:1/MAP_SKY_PILLAR_4F:0","MAP_SKY_PILLAR_4F:1/MAP_SKY_PILLAR_3F:2":"MAP_SKY_PILLAR_3F:2/MAP_SKY_PILLAR_4F:1","MAP_SKY_PILLAR_4F:2/MAP_SKY_PILLAR_5F:0":"MAP_SKY_PILLAR_5F:0/MAP_SKY_PILLAR_4F:2","MAP_SKY_PILLAR_5F:0/MAP_SKY_PILLAR_4F:2":"MAP_SKY_PILLAR_4F:2/MAP_SKY_PILLAR_5F:0","MAP_SKY_PILLAR_5F:1/MAP_SKY_PILLAR_TOP:0":"MAP_SKY_PILLAR_TOP:0/MAP_SKY_PILLAR_5F:1","MAP_SKY_PILLAR_ENTRANCE:0/MAP_ROUTE131:0":"MAP_ROUTE131:0/MAP_SKY_PILLAR_ENTRANCE:0","MAP_SKY_PILLAR_ENTRANCE:1/MAP_SKY_PILLAR_OUTSIDE:0":"MAP_SKY_PILLAR_OUTSIDE:0/MAP_SKY_PILLAR_ENTRANCE:1","MAP_SKY_PILLAR_OUTSIDE:0/MAP_SKY_PILLAR_ENTRANCE:1":"MAP_SKY_PILLAR_ENTRANCE:1/MAP_SKY_PILLAR_OUTSIDE:0","MAP_SKY_PILLAR_OUTSIDE:1/MAP_SKY_PILLAR_1F:0":"MAP_SKY_PILLAR_1F:0,1/MAP_SKY_PILLAR_OUTSIDE:1","MAP_SKY_PILLAR_TOP:0/MAP_SKY_PILLAR_5F:1":"MAP_SKY_PILLAR_5F:1/MAP_SKY_PILLAR_TOP:0","MAP_SLATEPORT_CITY:0/MAP_SLATEPORT_CITY_POKEMON_CENTER_1F:0":"MAP_SLATEPORT_CITY_POKEMON_CENTER_1F:0,1/MAP_SLATEPORT_CITY:0","MAP_SLATEPORT_CITY:1/MAP_SLATEPORT_CITY_MART:0":"MAP_SLATEPORT_CITY_MART:0,1/MAP_SLATEPORT_CITY:1","MAP_SLATEPORT_CITY:10/MAP_SLATEPORT_CITY_HOUSE:0":"MAP_SLATEPORT_CITY_HOUSE:0,1/MAP_SLATEPORT_CITY:10","MAP_SLATEPORT_CITY:2/MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F:0":"MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F:0,1/MAP_SLATEPORT_CITY:2","MAP_SLATEPORT_CITY:3/MAP_SLATEPORT_CITY_BATTLE_TENT_LOBBY:0":"MAP_SLATEPORT_CITY_BATTLE_TENT_LOBBY:0,1/MAP_SLATEPORT_CITY:3","MAP_SLATEPORT_CITY:4/MAP_SLATEPORT_CITY_POKEMON_FAN_CLUB:0":"MAP_SLATEPORT_CITY_POKEMON_FAN_CLUB:0,1/MAP_SLATEPORT_CITY:4","MAP_SLATEPORT_CITY:5,7/MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F:0,1":"MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F:0,1/MAP_SLATEPORT_CITY:5,7","MAP_SLATEPORT_CITY:6/MAP_SLATEPORT_CITY_NAME_RATERS_HOUSE:0":"MAP_SLATEPORT_CITY_NAME_RATERS_HOUSE:0,1/MAP_SLATEPORT_CITY:6","MAP_SLATEPORT_CITY:8/MAP_SLATEPORT_CITY_HARBOR:0":"MAP_SLATEPORT_CITY_HARBOR:0,1/MAP_SLATEPORT_CITY:8","MAP_SLATEPORT_CITY:9/MAP_SLATEPORT_CITY_HARBOR:2":"MAP_SLATEPORT_CITY_HARBOR:2,3/MAP_SLATEPORT_CITY:9","MAP_SLATEPORT_CITY_BATTLE_TENT_LOBBY:0,1/MAP_SLATEPORT_CITY:3":"MAP_SLATEPORT_CITY:3/MAP_SLATEPORT_CITY_BATTLE_TENT_LOBBY:0","MAP_SLATEPORT_CITY_HARBOR:0,1/MAP_SLATEPORT_CITY:8":"MAP_SLATEPORT_CITY:8/MAP_SLATEPORT_CITY_HARBOR:0","MAP_SLATEPORT_CITY_HARBOR:2,3/MAP_SLATEPORT_CITY:9":"MAP_SLATEPORT_CITY:9/MAP_SLATEPORT_CITY_HARBOR:2","MAP_SLATEPORT_CITY_HOUSE:0,1/MAP_SLATEPORT_CITY:10":"MAP_SLATEPORT_CITY:10/MAP_SLATEPORT_CITY_HOUSE:0","MAP_SLATEPORT_CITY_MART:0,1/MAP_SLATEPORT_CITY:1":"MAP_SLATEPORT_CITY:1/MAP_SLATEPORT_CITY_MART:0","MAP_SLATEPORT_CITY_NAME_RATERS_HOUSE:0,1/MAP_SLATEPORT_CITY:6":"MAP_SLATEPORT_CITY:6/MAP_SLATEPORT_CITY_NAME_RATERS_HOUSE:0","MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F:0,1/MAP_SLATEPORT_CITY:5,7":"MAP_SLATEPORT_CITY:5,7/MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F:0,1","MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F:2/MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_2F:0":"MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_2F:0/MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F:2","MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_2F:0/MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F:2":"MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F:2/MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_2F:0","MAP_SLATEPORT_CITY_POKEMON_CENTER_1F:0,1/MAP_SLATEPORT_CITY:0":"MAP_SLATEPORT_CITY:0/MAP_SLATEPORT_CITY_POKEMON_CENTER_1F:0","MAP_SLATEPORT_CITY_POKEMON_CENTER_1F:2/MAP_SLATEPORT_CITY_POKEMON_CENTER_2F:0":"MAP_SLATEPORT_CITY_POKEMON_CENTER_2F:0/MAP_SLATEPORT_CITY_POKEMON_CENTER_1F:2","MAP_SLATEPORT_CITY_POKEMON_CENTER_2F:0/MAP_SLATEPORT_CITY_POKEMON_CENTER_1F:2":"MAP_SLATEPORT_CITY_POKEMON_CENTER_1F:2/MAP_SLATEPORT_CITY_POKEMON_CENTER_2F:0","MAP_SLATEPORT_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_SLATEPORT_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_SLATEPORT_CITY_POKEMON_FAN_CLUB:0,1/MAP_SLATEPORT_CITY:4":"MAP_SLATEPORT_CITY:4/MAP_SLATEPORT_CITY_POKEMON_FAN_CLUB:0","MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F:0,1/MAP_SLATEPORT_CITY:2":"MAP_SLATEPORT_CITY:2/MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F:0","MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F:2/MAP_SLATEPORT_CITY_STERNS_SHIPYARD_2F:0":"MAP_SLATEPORT_CITY_STERNS_SHIPYARD_2F:0/MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F:2","MAP_SLATEPORT_CITY_STERNS_SHIPYARD_2F:0/MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F:2":"MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F:2/MAP_SLATEPORT_CITY_STERNS_SHIPYARD_2F:0","MAP_SOOTOPOLIS_CITY:0/MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F:0":"MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F:0,1/MAP_SOOTOPOLIS_CITY:0","MAP_SOOTOPOLIS_CITY:1/MAP_SOOTOPOLIS_CITY_MART:0":"MAP_SOOTOPOLIS_CITY_MART:0,1/MAP_SOOTOPOLIS_CITY:1","MAP_SOOTOPOLIS_CITY:10/MAP_SOOTOPOLIS_CITY_HOUSE7:0":"MAP_SOOTOPOLIS_CITY_HOUSE7:0,1/MAP_SOOTOPOLIS_CITY:10","MAP_SOOTOPOLIS_CITY:11/MAP_SOOTOPOLIS_CITY_LOTAD_AND_SEEDOT_HOUSE:0":"MAP_SOOTOPOLIS_CITY_LOTAD_AND_SEEDOT_HOUSE:0,1/MAP_SOOTOPOLIS_CITY:11","MAP_SOOTOPOLIS_CITY:12/MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F:0":"MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F:0,1/MAP_SOOTOPOLIS_CITY:12","MAP_SOOTOPOLIS_CITY:2/MAP_SOOTOPOLIS_CITY_GYM_1F:0":"MAP_SOOTOPOLIS_CITY_GYM_1F:0,1/MAP_SOOTOPOLIS_CITY:2","MAP_SOOTOPOLIS_CITY:3/MAP_CAVE_OF_ORIGIN_ENTRANCE:0":"MAP_CAVE_OF_ORIGIN_ENTRANCE:0/MAP_SOOTOPOLIS_CITY:3","MAP_SOOTOPOLIS_CITY:4/MAP_SOOTOPOLIS_CITY_HOUSE1:0":"MAP_SOOTOPOLIS_CITY_HOUSE1:0,1/MAP_SOOTOPOLIS_CITY:4","MAP_SOOTOPOLIS_CITY:5/MAP_SOOTOPOLIS_CITY_HOUSE2:0":"MAP_SOOTOPOLIS_CITY_HOUSE2:0,1/MAP_SOOTOPOLIS_CITY:5","MAP_SOOTOPOLIS_CITY:6/MAP_SOOTOPOLIS_CITY_HOUSE3:0":"MAP_SOOTOPOLIS_CITY_HOUSE3:0,1/MAP_SOOTOPOLIS_CITY:6","MAP_SOOTOPOLIS_CITY:7/MAP_SOOTOPOLIS_CITY_HOUSE4:0":"MAP_SOOTOPOLIS_CITY_HOUSE4:0,1/MAP_SOOTOPOLIS_CITY:7","MAP_SOOTOPOLIS_CITY:8/MAP_SOOTOPOLIS_CITY_HOUSE5:0":"MAP_SOOTOPOLIS_CITY_HOUSE5:0,1/MAP_SOOTOPOLIS_CITY:8","MAP_SOOTOPOLIS_CITY:9/MAP_SOOTOPOLIS_CITY_HOUSE6:0":"MAP_SOOTOPOLIS_CITY_HOUSE6:0,1/MAP_SOOTOPOLIS_CITY:9","MAP_SOOTOPOLIS_CITY_GYM_1F:0,1/MAP_SOOTOPOLIS_CITY:2":"MAP_SOOTOPOLIS_CITY:2/MAP_SOOTOPOLIS_CITY_GYM_1F:0","MAP_SOOTOPOLIS_CITY_GYM_1F:2/MAP_SOOTOPOLIS_CITY_GYM_B1F:0":"MAP_SOOTOPOLIS_CITY_GYM_B1F:0/MAP_SOOTOPOLIS_CITY_GYM_1F:2","MAP_SOOTOPOLIS_CITY_GYM_B1F:0/MAP_SOOTOPOLIS_CITY_GYM_1F:2":"MAP_SOOTOPOLIS_CITY_GYM_1F:2/MAP_SOOTOPOLIS_CITY_GYM_B1F:0","MAP_SOOTOPOLIS_CITY_HOUSE1:0,1/MAP_SOOTOPOLIS_CITY:4":"MAP_SOOTOPOLIS_CITY:4/MAP_SOOTOPOLIS_CITY_HOUSE1:0","MAP_SOOTOPOLIS_CITY_HOUSE2:0,1/MAP_SOOTOPOLIS_CITY:5":"MAP_SOOTOPOLIS_CITY:5/MAP_SOOTOPOLIS_CITY_HOUSE2:0","MAP_SOOTOPOLIS_CITY_HOUSE3:0,1/MAP_SOOTOPOLIS_CITY:6":"MAP_SOOTOPOLIS_CITY:6/MAP_SOOTOPOLIS_CITY_HOUSE3:0","MAP_SOOTOPOLIS_CITY_HOUSE4:0,1/MAP_SOOTOPOLIS_CITY:7":"MAP_SOOTOPOLIS_CITY:7/MAP_SOOTOPOLIS_CITY_HOUSE4:0","MAP_SOOTOPOLIS_CITY_HOUSE5:0,1/MAP_SOOTOPOLIS_CITY:8":"MAP_SOOTOPOLIS_CITY:8/MAP_SOOTOPOLIS_CITY_HOUSE5:0","MAP_SOOTOPOLIS_CITY_HOUSE6:0,1/MAP_SOOTOPOLIS_CITY:9":"MAP_SOOTOPOLIS_CITY:9/MAP_SOOTOPOLIS_CITY_HOUSE6:0","MAP_SOOTOPOLIS_CITY_HOUSE7:0,1/MAP_SOOTOPOLIS_CITY:10":"MAP_SOOTOPOLIS_CITY:10/MAP_SOOTOPOLIS_CITY_HOUSE7:0","MAP_SOOTOPOLIS_CITY_LOTAD_AND_SEEDOT_HOUSE:0,1/MAP_SOOTOPOLIS_CITY:11":"MAP_SOOTOPOLIS_CITY:11/MAP_SOOTOPOLIS_CITY_LOTAD_AND_SEEDOT_HOUSE:0","MAP_SOOTOPOLIS_CITY_MART:0,1/MAP_SOOTOPOLIS_CITY:1":"MAP_SOOTOPOLIS_CITY:1/MAP_SOOTOPOLIS_CITY_MART:0","MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F:0,1/MAP_SOOTOPOLIS_CITY:12":"MAP_SOOTOPOLIS_CITY:12/MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F:0","MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F:2/MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_B1F:0":"MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_B1F:0/MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F:2","MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_B1F:0/MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F:2":"MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F:2/MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_B1F:0","MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F:0,1/MAP_SOOTOPOLIS_CITY:0":"MAP_SOOTOPOLIS_CITY:0/MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F:0","MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F:2/MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F:0":"MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F:0/MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F:2","MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F:0/MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F:2":"MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F:2/MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F:0","MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_SOUTHERN_ISLAND_EXTERIOR:0,1/MAP_SOUTHERN_ISLAND_INTERIOR:0,1":"MAP_SOUTHERN_ISLAND_INTERIOR:0,1/MAP_SOUTHERN_ISLAND_EXTERIOR:0,1","MAP_SOUTHERN_ISLAND_INTERIOR:0,1/MAP_SOUTHERN_ISLAND_EXTERIOR:0,1":"MAP_SOUTHERN_ISLAND_EXTERIOR:0,1/MAP_SOUTHERN_ISLAND_INTERIOR:0,1","MAP_SS_TIDAL_CORRIDOR:0/MAP_SS_TIDAL_ROOMS:0":"MAP_SS_TIDAL_ROOMS:0,1/MAP_SS_TIDAL_CORRIDOR:0","MAP_SS_TIDAL_CORRIDOR:1/MAP_SS_TIDAL_ROOMS:2":"MAP_SS_TIDAL_ROOMS:2,3/MAP_SS_TIDAL_CORRIDOR:1","MAP_SS_TIDAL_CORRIDOR:2/MAP_SS_TIDAL_ROOMS:4":"MAP_SS_TIDAL_ROOMS:4,5/MAP_SS_TIDAL_CORRIDOR:2","MAP_SS_TIDAL_CORRIDOR:3/MAP_SS_TIDAL_ROOMS:6":"MAP_SS_TIDAL_ROOMS:6,7/MAP_SS_TIDAL_CORRIDOR:3","MAP_SS_TIDAL_CORRIDOR:4/MAP_SS_TIDAL_ROOMS:8":"MAP_SS_TIDAL_ROOMS:8/MAP_SS_TIDAL_CORRIDOR:4","MAP_SS_TIDAL_CORRIDOR:5/MAP_SS_TIDAL_ROOMS:9":"MAP_SS_TIDAL_ROOMS:9/MAP_SS_TIDAL_CORRIDOR:5","MAP_SS_TIDAL_CORRIDOR:6/MAP_SS_TIDAL_ROOMS:10":"MAP_SS_TIDAL_ROOMS:10/MAP_SS_TIDAL_CORRIDOR:6","MAP_SS_TIDAL_CORRIDOR:7/MAP_SS_TIDAL_ROOMS:11":"MAP_SS_TIDAL_ROOMS:11/MAP_SS_TIDAL_CORRIDOR:7","MAP_SS_TIDAL_CORRIDOR:8/MAP_SS_TIDAL_LOWER_DECK:0":"MAP_SS_TIDAL_LOWER_DECK:0/MAP_SS_TIDAL_CORRIDOR:8","MAP_SS_TIDAL_LOWER_DECK:0/MAP_SS_TIDAL_CORRIDOR:8":"MAP_SS_TIDAL_CORRIDOR:8/MAP_SS_TIDAL_LOWER_DECK:0","MAP_SS_TIDAL_ROOMS:0,1/MAP_SS_TIDAL_CORRIDOR:0":"MAP_SS_TIDAL_CORRIDOR:0/MAP_SS_TIDAL_ROOMS:0","MAP_SS_TIDAL_ROOMS:10/MAP_SS_TIDAL_CORRIDOR:6":"MAP_SS_TIDAL_CORRIDOR:6/MAP_SS_TIDAL_ROOMS:10","MAP_SS_TIDAL_ROOMS:11/MAP_SS_TIDAL_CORRIDOR:7":"MAP_SS_TIDAL_CORRIDOR:7/MAP_SS_TIDAL_ROOMS:11","MAP_SS_TIDAL_ROOMS:2,3/MAP_SS_TIDAL_CORRIDOR:1":"MAP_SS_TIDAL_CORRIDOR:1/MAP_SS_TIDAL_ROOMS:2","MAP_SS_TIDAL_ROOMS:4,5/MAP_SS_TIDAL_CORRIDOR:2":"MAP_SS_TIDAL_CORRIDOR:2/MAP_SS_TIDAL_ROOMS:4","MAP_SS_TIDAL_ROOMS:6,7/MAP_SS_TIDAL_CORRIDOR:3":"MAP_SS_TIDAL_CORRIDOR:3/MAP_SS_TIDAL_ROOMS:6","MAP_SS_TIDAL_ROOMS:8/MAP_SS_TIDAL_CORRIDOR:4":"MAP_SS_TIDAL_CORRIDOR:4/MAP_SS_TIDAL_ROOMS:8","MAP_SS_TIDAL_ROOMS:9/MAP_SS_TIDAL_CORRIDOR:5":"MAP_SS_TIDAL_CORRIDOR:5/MAP_SS_TIDAL_ROOMS:9","MAP_TERRA_CAVE_END:0/MAP_TERRA_CAVE_ENTRANCE:1":"MAP_TERRA_CAVE_ENTRANCE:1/MAP_TERRA_CAVE_END:0","MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!":"","MAP_TERRA_CAVE_ENTRANCE:1/MAP_TERRA_CAVE_END:0":"MAP_TERRA_CAVE_END:0/MAP_TERRA_CAVE_ENTRANCE:1","MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!":"","MAP_TRAINER_HILL_1F:0/MAP_TRAINER_HILL_ENTRANCE:2":"MAP_TRAINER_HILL_ENTRANCE:2/MAP_TRAINER_HILL_1F:0","MAP_TRAINER_HILL_1F:1/MAP_TRAINER_HILL_2F:0":"MAP_TRAINER_HILL_2F:0/MAP_TRAINER_HILL_1F:1","MAP_TRAINER_HILL_2F:0/MAP_TRAINER_HILL_1F:1":"MAP_TRAINER_HILL_1F:1/MAP_TRAINER_HILL_2F:0","MAP_TRAINER_HILL_2F:1/MAP_TRAINER_HILL_3F:0":"MAP_TRAINER_HILL_3F:0/MAP_TRAINER_HILL_2F:1","MAP_TRAINER_HILL_3F:0/MAP_TRAINER_HILL_2F:1":"MAP_TRAINER_HILL_2F:1/MAP_TRAINER_HILL_3F:0","MAP_TRAINER_HILL_3F:1/MAP_TRAINER_HILL_4F:0":"MAP_TRAINER_HILL_4F:0/MAP_TRAINER_HILL_3F:1","MAP_TRAINER_HILL_4F:0/MAP_TRAINER_HILL_3F:1":"MAP_TRAINER_HILL_3F:1/MAP_TRAINER_HILL_4F:0","MAP_TRAINER_HILL_4F:1/MAP_TRAINER_HILL_ROOF:0":"MAP_TRAINER_HILL_ROOF:0/MAP_TRAINER_HILL_4F:1","MAP_TRAINER_HILL_ELEVATOR:0,1/MAP_TRAINER_HILL_ROOF:1":"MAP_TRAINER_HILL_ROOF:1/MAP_TRAINER_HILL_ELEVATOR:1","MAP_TRAINER_HILL_ENTRANCE:0,1/MAP_ROUTE111:4":"MAP_ROUTE111:4/MAP_TRAINER_HILL_ENTRANCE:0","MAP_TRAINER_HILL_ENTRANCE:2/MAP_TRAINER_HILL_1F:0":"MAP_TRAINER_HILL_1F:0/MAP_TRAINER_HILL_ENTRANCE:2","MAP_TRAINER_HILL_ROOF:0/MAP_TRAINER_HILL_4F:1":"MAP_TRAINER_HILL_4F:1/MAP_TRAINER_HILL_ROOF:0","MAP_TRAINER_HILL_ROOF:1/MAP_TRAINER_HILL_ELEVATOR:1":"MAP_TRAINER_HILL_ELEVATOR:0,1/MAP_TRAINER_HILL_ROOF:1","MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!":"","MAP_UNDERWATER_ROUTE105:0/MAP_UNDERWATER_MARINE_CAVE:0!":"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!","MAP_UNDERWATER_ROUTE105:1/MAP_UNDERWATER_MARINE_CAVE:0!":"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!","MAP_UNDERWATER_ROUTE125:0/MAP_UNDERWATER_MARINE_CAVE:0!":"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!","MAP_UNDERWATER_ROUTE125:1/MAP_UNDERWATER_MARINE_CAVE:0!":"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!","MAP_UNDERWATER_ROUTE126:0/MAP_UNDERWATER_SOOTOPOLIS_CITY:0":"MAP_UNDERWATER_SOOTOPOLIS_CITY:0,1/MAP_UNDERWATER_ROUTE126:0","MAP_UNDERWATER_ROUTE127:0/MAP_UNDERWATER_MARINE_CAVE:0!":"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!","MAP_UNDERWATER_ROUTE127:1/MAP_UNDERWATER_MARINE_CAVE:0!":"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!","MAP_UNDERWATER_ROUTE128:0/MAP_UNDERWATER_SEAFLOOR_CAVERN:0":"MAP_UNDERWATER_SEAFLOOR_CAVERN:0/MAP_UNDERWATER_ROUTE128:0","MAP_UNDERWATER_ROUTE129:0/MAP_UNDERWATER_MARINE_CAVE:0!":"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!","MAP_UNDERWATER_ROUTE129:1/MAP_UNDERWATER_MARINE_CAVE:0!":"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!","MAP_UNDERWATER_ROUTE134:0/MAP_UNDERWATER_SEALED_CHAMBER:0":"MAP_UNDERWATER_SEALED_CHAMBER:0/MAP_UNDERWATER_ROUTE134:0","MAP_UNDERWATER_SEAFLOOR_CAVERN:0/MAP_UNDERWATER_ROUTE128:0":"MAP_UNDERWATER_ROUTE128:0/MAP_UNDERWATER_SEAFLOOR_CAVERN:0","MAP_UNDERWATER_SEALED_CHAMBER:0/MAP_UNDERWATER_ROUTE134:0":"MAP_UNDERWATER_ROUTE134:0/MAP_UNDERWATER_SEALED_CHAMBER:0","MAP_UNDERWATER_SOOTOPOLIS_CITY:0,1/MAP_UNDERWATER_ROUTE126:0":"MAP_UNDERWATER_ROUTE126:0/MAP_UNDERWATER_SOOTOPOLIS_CITY:0","MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!":"","MAP_VERDANTURF_TOWN:0/MAP_VERDANTURF_TOWN_BATTLE_TENT_LOBBY:0":"MAP_VERDANTURF_TOWN_BATTLE_TENT_LOBBY:0,1/MAP_VERDANTURF_TOWN:0","MAP_VERDANTURF_TOWN:1/MAP_VERDANTURF_TOWN_MART:0":"MAP_VERDANTURF_TOWN_MART:0,1/MAP_VERDANTURF_TOWN:1","MAP_VERDANTURF_TOWN:2/MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F:0":"MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F:0,1/MAP_VERDANTURF_TOWN:2","MAP_VERDANTURF_TOWN:3/MAP_VERDANTURF_TOWN_WANDAS_HOUSE:0":"MAP_VERDANTURF_TOWN_WANDAS_HOUSE:0,1/MAP_VERDANTURF_TOWN:3","MAP_VERDANTURF_TOWN:4/MAP_RUSTURF_TUNNEL:1":"MAP_RUSTURF_TUNNEL:1/MAP_VERDANTURF_TOWN:4","MAP_VERDANTURF_TOWN:5/MAP_VERDANTURF_TOWN_FRIENDSHIP_RATERS_HOUSE:0":"MAP_VERDANTURF_TOWN_FRIENDSHIP_RATERS_HOUSE:0,1/MAP_VERDANTURF_TOWN:5","MAP_VERDANTURF_TOWN:6/MAP_VERDANTURF_TOWN_HOUSE:0":"MAP_VERDANTURF_TOWN_HOUSE:0,1/MAP_VERDANTURF_TOWN:6","MAP_VERDANTURF_TOWN_BATTLE_TENT_LOBBY:0,1/MAP_VERDANTURF_TOWN:0":"MAP_VERDANTURF_TOWN:0/MAP_VERDANTURF_TOWN_BATTLE_TENT_LOBBY:0","MAP_VERDANTURF_TOWN_FRIENDSHIP_RATERS_HOUSE:0,1/MAP_VERDANTURF_TOWN:5":"MAP_VERDANTURF_TOWN:5/MAP_VERDANTURF_TOWN_FRIENDSHIP_RATERS_HOUSE:0","MAP_VERDANTURF_TOWN_HOUSE:0,1/MAP_VERDANTURF_TOWN:6":"MAP_VERDANTURF_TOWN:6/MAP_VERDANTURF_TOWN_HOUSE:0","MAP_VERDANTURF_TOWN_MART:0,1/MAP_VERDANTURF_TOWN:1":"MAP_VERDANTURF_TOWN:1/MAP_VERDANTURF_TOWN_MART:0","MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F:0,1/MAP_VERDANTURF_TOWN:2":"MAP_VERDANTURF_TOWN:2/MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F:0","MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F:2/MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F:0":"MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F:0/MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F:2","MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F:0/MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F:2":"MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F:2/MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F:0","MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_VERDANTURF_TOWN_WANDAS_HOUSE:0,1/MAP_VERDANTURF_TOWN:3":"MAP_VERDANTURF_TOWN:3/MAP_VERDANTURF_TOWN_WANDAS_HOUSE:0","MAP_VICTORY_ROAD_1F:0/MAP_EVER_GRANDE_CITY:2":"MAP_EVER_GRANDE_CITY:2/MAP_VICTORY_ROAD_1F:0","MAP_VICTORY_ROAD_1F:1/MAP_EVER_GRANDE_CITY:3":"MAP_EVER_GRANDE_CITY:3/MAP_VICTORY_ROAD_1F:1","MAP_VICTORY_ROAD_1F:2/MAP_VICTORY_ROAD_B1F:5":"MAP_VICTORY_ROAD_B1F:5/MAP_VICTORY_ROAD_1F:2","MAP_VICTORY_ROAD_1F:3/MAP_VICTORY_ROAD_B1F:2":"MAP_VICTORY_ROAD_B1F:2/MAP_VICTORY_ROAD_1F:3","MAP_VICTORY_ROAD_1F:4/MAP_VICTORY_ROAD_B1F:4":"MAP_VICTORY_ROAD_B1F:4/MAP_VICTORY_ROAD_1F:4","MAP_VICTORY_ROAD_B1F:0/MAP_VICTORY_ROAD_B2F:0":"MAP_VICTORY_ROAD_B2F:0/MAP_VICTORY_ROAD_B1F:0","MAP_VICTORY_ROAD_B1F:1/MAP_VICTORY_ROAD_B2F:2":"MAP_VICTORY_ROAD_B2F:2/MAP_VICTORY_ROAD_B1F:1","MAP_VICTORY_ROAD_B1F:2/MAP_VICTORY_ROAD_1F:3":"MAP_VICTORY_ROAD_1F:3/MAP_VICTORY_ROAD_B1F:2","MAP_VICTORY_ROAD_B1F:3/MAP_VICTORY_ROAD_B2F:1":"MAP_VICTORY_ROAD_B2F:1/MAP_VICTORY_ROAD_B1F:3","MAP_VICTORY_ROAD_B1F:4/MAP_VICTORY_ROAD_1F:4":"MAP_VICTORY_ROAD_1F:4/MAP_VICTORY_ROAD_B1F:4","MAP_VICTORY_ROAD_B1F:5/MAP_VICTORY_ROAD_1F:2":"MAP_VICTORY_ROAD_1F:2/MAP_VICTORY_ROAD_B1F:5","MAP_VICTORY_ROAD_B1F:6/MAP_VICTORY_ROAD_B2F:3":"MAP_VICTORY_ROAD_B2F:3/MAP_VICTORY_ROAD_B1F:6","MAP_VICTORY_ROAD_B2F:0/MAP_VICTORY_ROAD_B1F:0":"MAP_VICTORY_ROAD_B1F:0/MAP_VICTORY_ROAD_B2F:0","MAP_VICTORY_ROAD_B2F:1/MAP_VICTORY_ROAD_B1F:3":"MAP_VICTORY_ROAD_B1F:3/MAP_VICTORY_ROAD_B2F:1","MAP_VICTORY_ROAD_B2F:2/MAP_VICTORY_ROAD_B1F:1":"MAP_VICTORY_ROAD_B1F:1/MAP_VICTORY_ROAD_B2F:2","MAP_VICTORY_ROAD_B2F:3/MAP_VICTORY_ROAD_B1F:6":"MAP_VICTORY_ROAD_B1F:6/MAP_VICTORY_ROAD_B2F:3"}}
+{"_comment":"DO NOT MODIFY. This file was auto-generated. Your changes will likely be overwritten.","_rom_name":"pokemon emerald version / AP 5","constants":{"ABILITIES_COUNT":78,"ABILITY_AIR_LOCK":77,"ABILITY_ARENA_TRAP":71,"ABILITY_BATTLE_ARMOR":4,"ABILITY_BLAZE":66,"ABILITY_CACOPHONY":76,"ABILITY_CHLOROPHYLL":34,"ABILITY_CLEAR_BODY":29,"ABILITY_CLOUD_NINE":13,"ABILITY_COLOR_CHANGE":16,"ABILITY_COMPOUND_EYES":14,"ABILITY_CUTE_CHARM":56,"ABILITY_DAMP":6,"ABILITY_DRIZZLE":2,"ABILITY_DROUGHT":70,"ABILITY_EARLY_BIRD":48,"ABILITY_EFFECT_SPORE":27,"ABILITY_FLAME_BODY":49,"ABILITY_FLASH_FIRE":18,"ABILITY_FORECAST":59,"ABILITY_GUTS":62,"ABILITY_HUGE_POWER":37,"ABILITY_HUSTLE":55,"ABILITY_HYPER_CUTTER":52,"ABILITY_ILLUMINATE":35,"ABILITY_IMMUNITY":17,"ABILITY_INNER_FOCUS":39,"ABILITY_INSOMNIA":15,"ABILITY_INTIMIDATE":22,"ABILITY_KEEN_EYE":51,"ABILITY_LEVITATE":26,"ABILITY_LIGHTNING_ROD":31,"ABILITY_LIMBER":7,"ABILITY_LIQUID_OOZE":64,"ABILITY_MAGMA_ARMOR":40,"ABILITY_MAGNET_PULL":42,"ABILITY_MARVEL_SCALE":63,"ABILITY_MINUS":58,"ABILITY_NATURAL_CURE":30,"ABILITY_NONE":0,"ABILITY_OBLIVIOUS":12,"ABILITY_OVERGROW":65,"ABILITY_OWN_TEMPO":20,"ABILITY_PICKUP":53,"ABILITY_PLUS":57,"ABILITY_POISON_POINT":38,"ABILITY_PRESSURE":46,"ABILITY_PURE_POWER":74,"ABILITY_RAIN_DISH":44,"ABILITY_ROCK_HEAD":69,"ABILITY_ROUGH_SKIN":24,"ABILITY_RUN_AWAY":50,"ABILITY_SAND_STREAM":45,"ABILITY_SAND_VEIL":8,"ABILITY_SERENE_GRACE":32,"ABILITY_SHADOW_TAG":23,"ABILITY_SHED_SKIN":61,"ABILITY_SHELL_ARMOR":75,"ABILITY_SHIELD_DUST":19,"ABILITY_SOUNDPROOF":43,"ABILITY_SPEED_BOOST":3,"ABILITY_STATIC":9,"ABILITY_STENCH":1,"ABILITY_STICKY_HOLD":60,"ABILITY_STURDY":5,"ABILITY_SUCTION_CUPS":21,"ABILITY_SWARM":68,"ABILITY_SWIFT_SWIM":33,"ABILITY_SYNCHRONIZE":28,"ABILITY_THICK_FAT":47,"ABILITY_TORRENT":67,"ABILITY_TRACE":36,"ABILITY_TRUANT":54,"ABILITY_VITAL_SPIRIT":72,"ABILITY_VOLT_ABSORB":10,"ABILITY_WATER_ABSORB":11,"ABILITY_WATER_VEIL":41,"ABILITY_WHITE_SMOKE":73,"ABILITY_WONDER_GUARD":25,"ACRO_BIKE":1,"BAG_ITEM_CAPACITY_DIGITS":2,"BERRY_CAPACITY_DIGITS":3,"BERRY_FIRMNESS_HARD":3,"BERRY_FIRMNESS_SOFT":2,"BERRY_FIRMNESS_SUPER_HARD":5,"BERRY_FIRMNESS_UNKNOWN":0,"BERRY_FIRMNESS_VERY_HARD":4,"BERRY_FIRMNESS_VERY_SOFT":1,"BERRY_NONE":0,"BERRY_STAGE_BERRIES":5,"BERRY_STAGE_FLOWERING":4,"BERRY_STAGE_NO_BERRY":0,"BERRY_STAGE_PLANTED":1,"BERRY_STAGE_SPARKLING":255,"BERRY_STAGE_SPROUTED":2,"BERRY_STAGE_TALLER":3,"BERRY_TREES_COUNT":128,"BERRY_TREE_ROUTE_102_ORAN":2,"BERRY_TREE_ROUTE_102_PECHA":1,"BERRY_TREE_ROUTE_103_CHERI_1":5,"BERRY_TREE_ROUTE_103_CHERI_2":7,"BERRY_TREE_ROUTE_103_LEPPA":6,"BERRY_TREE_ROUTE_104_CHERI_1":8,"BERRY_TREE_ROUTE_104_CHERI_2":76,"BERRY_TREE_ROUTE_104_LEPPA":10,"BERRY_TREE_ROUTE_104_ORAN_1":4,"BERRY_TREE_ROUTE_104_ORAN_2":11,"BERRY_TREE_ROUTE_104_PECHA":13,"BERRY_TREE_ROUTE_104_SOIL_1":3,"BERRY_TREE_ROUTE_104_SOIL_2":9,"BERRY_TREE_ROUTE_104_SOIL_3":12,"BERRY_TREE_ROUTE_104_SOIL_4":75,"BERRY_TREE_ROUTE_110_NANAB_1":16,"BERRY_TREE_ROUTE_110_NANAB_2":17,"BERRY_TREE_ROUTE_110_NANAB_3":18,"BERRY_TREE_ROUTE_111_ORAN_1":80,"BERRY_TREE_ROUTE_111_ORAN_2":81,"BERRY_TREE_ROUTE_111_RAZZ_1":19,"BERRY_TREE_ROUTE_111_RAZZ_2":20,"BERRY_TREE_ROUTE_112_PECHA_1":22,"BERRY_TREE_ROUTE_112_PECHA_2":23,"BERRY_TREE_ROUTE_112_RAWST_1":21,"BERRY_TREE_ROUTE_112_RAWST_2":24,"BERRY_TREE_ROUTE_114_PERSIM_1":68,"BERRY_TREE_ROUTE_114_PERSIM_2":77,"BERRY_TREE_ROUTE_114_PERSIM_3":78,"BERRY_TREE_ROUTE_115_BLUK_1":55,"BERRY_TREE_ROUTE_115_BLUK_2":56,"BERRY_TREE_ROUTE_115_KELPSY_1":69,"BERRY_TREE_ROUTE_115_KELPSY_2":70,"BERRY_TREE_ROUTE_115_KELPSY_3":71,"BERRY_TREE_ROUTE_116_CHESTO_1":26,"BERRY_TREE_ROUTE_116_CHESTO_2":66,"BERRY_TREE_ROUTE_116_PINAP_1":25,"BERRY_TREE_ROUTE_116_PINAP_2":67,"BERRY_TREE_ROUTE_117_WEPEAR_1":27,"BERRY_TREE_ROUTE_117_WEPEAR_2":28,"BERRY_TREE_ROUTE_117_WEPEAR_3":29,"BERRY_TREE_ROUTE_118_SITRUS_1":31,"BERRY_TREE_ROUTE_118_SITRUS_2":33,"BERRY_TREE_ROUTE_118_SOIL":32,"BERRY_TREE_ROUTE_119_HONDEW_1":83,"BERRY_TREE_ROUTE_119_HONDEW_2":84,"BERRY_TREE_ROUTE_119_LEPPA":86,"BERRY_TREE_ROUTE_119_POMEG_1":34,"BERRY_TREE_ROUTE_119_POMEG_2":35,"BERRY_TREE_ROUTE_119_POMEG_3":36,"BERRY_TREE_ROUTE_119_SITRUS":85,"BERRY_TREE_ROUTE_120_ASPEAR_1":37,"BERRY_TREE_ROUTE_120_ASPEAR_2":38,"BERRY_TREE_ROUTE_120_ASPEAR_3":39,"BERRY_TREE_ROUTE_120_NANAB":44,"BERRY_TREE_ROUTE_120_PECHA_1":40,"BERRY_TREE_ROUTE_120_PECHA_2":41,"BERRY_TREE_ROUTE_120_PECHA_3":42,"BERRY_TREE_ROUTE_120_PINAP":45,"BERRY_TREE_ROUTE_120_RAZZ":43,"BERRY_TREE_ROUTE_120_WEPEAR":46,"BERRY_TREE_ROUTE_121_ASPEAR":48,"BERRY_TREE_ROUTE_121_CHESTO":50,"BERRY_TREE_ROUTE_121_NANAB_1":52,"BERRY_TREE_ROUTE_121_NANAB_2":53,"BERRY_TREE_ROUTE_121_PERSIM":47,"BERRY_TREE_ROUTE_121_RAWST":49,"BERRY_TREE_ROUTE_121_SOIL_1":51,"BERRY_TREE_ROUTE_121_SOIL_2":54,"BERRY_TREE_ROUTE_123_GREPA_1":60,"BERRY_TREE_ROUTE_123_GREPA_2":61,"BERRY_TREE_ROUTE_123_GREPA_3":65,"BERRY_TREE_ROUTE_123_GREPA_4":72,"BERRY_TREE_ROUTE_123_LEPPA_1":62,"BERRY_TREE_ROUTE_123_LEPPA_2":64,"BERRY_TREE_ROUTE_123_PECHA":87,"BERRY_TREE_ROUTE_123_POMEG_1":15,"BERRY_TREE_ROUTE_123_POMEG_2":30,"BERRY_TREE_ROUTE_123_POMEG_3":58,"BERRY_TREE_ROUTE_123_POMEG_4":59,"BERRY_TREE_ROUTE_123_QUALOT_1":14,"BERRY_TREE_ROUTE_123_QUALOT_2":73,"BERRY_TREE_ROUTE_123_QUALOT_3":74,"BERRY_TREE_ROUTE_123_QUALOT_4":79,"BERRY_TREE_ROUTE_123_RAWST":57,"BERRY_TREE_ROUTE_123_SITRUS":88,"BERRY_TREE_ROUTE_123_SOIL":63,"BERRY_TREE_ROUTE_130_LIECHI":82,"DAILY_FLAGS_END":2399,"DAILY_FLAGS_START":2336,"FIRST_BALL":1,"FIRST_BERRY_INDEX":133,"FIRST_BERRY_MASTER_BERRY":153,"FIRST_BERRY_MASTER_WIFE_BERRY":133,"FIRST_KIRI_BERRY":153,"FIRST_MAIL_INDEX":121,"FIRST_ROUTE_114_MAN_BERRY":148,"FLAGS_COUNT":2400,"FLAG_ADDED_MATCH_CALL_TO_POKENAV":304,"FLAG_ADVENTURE_STARTED":116,"FLAG_ARRIVED_AT_MARINE_CAVE_EMERGE_SPOT":2265,"FLAG_ARRIVED_AT_NAVEL_ROCK":2273,"FLAG_ARRIVED_AT_TERRA_CAVE_ENTRANCE":2266,"FLAG_ARRIVED_ON_FARAWAY_ISLAND":2264,"FLAG_BADGE01_GET":2151,"FLAG_BADGE02_GET":2152,"FLAG_BADGE03_GET":2153,"FLAG_BADGE04_GET":2154,"FLAG_BADGE05_GET":2155,"FLAG_BADGE06_GET":2156,"FLAG_BADGE07_GET":2157,"FLAG_BADGE08_GET":2158,"FLAG_BATTLE_FRONTIER_TRADE_DONE":156,"FLAG_BEAT_MAGMA_GRUNT_JAGGED_PASS":313,"FLAG_BEAUTY_PAINTING_MADE":161,"FLAG_BERRY_MASTERS_WIFE":1197,"FLAG_BERRY_MASTER_RECEIVED_BERRY_1":1195,"FLAG_BERRY_MASTER_RECEIVED_BERRY_2":1196,"FLAG_BERRY_TREES_START":612,"FLAG_BERRY_TREE_01":612,"FLAG_BERRY_TREE_02":613,"FLAG_BERRY_TREE_03":614,"FLAG_BERRY_TREE_04":615,"FLAG_BERRY_TREE_05":616,"FLAG_BERRY_TREE_06":617,"FLAG_BERRY_TREE_07":618,"FLAG_BERRY_TREE_08":619,"FLAG_BERRY_TREE_09":620,"FLAG_BERRY_TREE_10":621,"FLAG_BERRY_TREE_11":622,"FLAG_BERRY_TREE_12":623,"FLAG_BERRY_TREE_13":624,"FLAG_BERRY_TREE_14":625,"FLAG_BERRY_TREE_15":626,"FLAG_BERRY_TREE_16":627,"FLAG_BERRY_TREE_17":628,"FLAG_BERRY_TREE_18":629,"FLAG_BERRY_TREE_19":630,"FLAG_BERRY_TREE_20":631,"FLAG_BERRY_TREE_21":632,"FLAG_BERRY_TREE_22":633,"FLAG_BERRY_TREE_23":634,"FLAG_BERRY_TREE_24":635,"FLAG_BERRY_TREE_25":636,"FLAG_BERRY_TREE_26":637,"FLAG_BERRY_TREE_27":638,"FLAG_BERRY_TREE_28":639,"FLAG_BERRY_TREE_29":640,"FLAG_BERRY_TREE_30":641,"FLAG_BERRY_TREE_31":642,"FLAG_BERRY_TREE_32":643,"FLAG_BERRY_TREE_33":644,"FLAG_BERRY_TREE_34":645,"FLAG_BERRY_TREE_35":646,"FLAG_BERRY_TREE_36":647,"FLAG_BERRY_TREE_37":648,"FLAG_BERRY_TREE_38":649,"FLAG_BERRY_TREE_39":650,"FLAG_BERRY_TREE_40":651,"FLAG_BERRY_TREE_41":652,"FLAG_BERRY_TREE_42":653,"FLAG_BERRY_TREE_43":654,"FLAG_BERRY_TREE_44":655,"FLAG_BERRY_TREE_45":656,"FLAG_BERRY_TREE_46":657,"FLAG_BERRY_TREE_47":658,"FLAG_BERRY_TREE_48":659,"FLAG_BERRY_TREE_49":660,"FLAG_BERRY_TREE_50":661,"FLAG_BERRY_TREE_51":662,"FLAG_BERRY_TREE_52":663,"FLAG_BERRY_TREE_53":664,"FLAG_BERRY_TREE_54":665,"FLAG_BERRY_TREE_55":666,"FLAG_BERRY_TREE_56":667,"FLAG_BERRY_TREE_57":668,"FLAG_BERRY_TREE_58":669,"FLAG_BERRY_TREE_59":670,"FLAG_BERRY_TREE_60":671,"FLAG_BERRY_TREE_61":672,"FLAG_BERRY_TREE_62":673,"FLAG_BERRY_TREE_63":674,"FLAG_BERRY_TREE_64":675,"FLAG_BERRY_TREE_65":676,"FLAG_BERRY_TREE_66":677,"FLAG_BERRY_TREE_67":678,"FLAG_BERRY_TREE_68":679,"FLAG_BERRY_TREE_69":680,"FLAG_BERRY_TREE_70":681,"FLAG_BERRY_TREE_71":682,"FLAG_BERRY_TREE_72":683,"FLAG_BERRY_TREE_73":684,"FLAG_BERRY_TREE_74":685,"FLAG_BERRY_TREE_75":686,"FLAG_BERRY_TREE_76":687,"FLAG_BERRY_TREE_77":688,"FLAG_BERRY_TREE_78":689,"FLAG_BERRY_TREE_79":690,"FLAG_BERRY_TREE_80":691,"FLAG_BERRY_TREE_81":692,"FLAG_BERRY_TREE_82":693,"FLAG_BERRY_TREE_83":694,"FLAG_BERRY_TREE_84":695,"FLAG_BERRY_TREE_85":696,"FLAG_BERRY_TREE_86":697,"FLAG_BERRY_TREE_87":698,"FLAG_BERRY_TREE_88":699,"FLAG_BETTER_SHOPS_ENABLED":206,"FLAG_BIRCH_AIDE_MET":88,"FLAG_CANCEL_BATTLE_ROOM_CHALLENGE":119,"FLAG_CAUGHT_DEOXYS":429,"FLAG_CAUGHT_GROUDON":480,"FLAG_CAUGHT_HO_OH":146,"FLAG_CAUGHT_KYOGRE":479,"FLAG_CAUGHT_LATIAS":457,"FLAG_CAUGHT_LATIOS":482,"FLAG_CAUGHT_LUGIA":145,"FLAG_CAUGHT_MEW":458,"FLAG_CAUGHT_RAYQUAZA":478,"FLAG_CAUGHT_REGICE":427,"FLAG_CAUGHT_REGIROCK":426,"FLAG_CAUGHT_REGISTEEL":483,"FLAG_CHOSEN_MULTI_BATTLE_NPC_PARTNER":338,"FLAG_CHOSE_CLAW_FOSSIL":336,"FLAG_CHOSE_ROOT_FOSSIL":335,"FLAG_COLLECTED_ALL_GOLD_SYMBOLS":466,"FLAG_COLLECTED_ALL_SILVER_SYMBOLS":92,"FLAG_CONTEST_SKETCH_CREATED":270,"FLAG_COOL_PAINTING_MADE":160,"FLAG_CUTE_PAINTING_MADE":162,"FLAG_DAILY_APPRENTICE_LEAVES":2356,"FLAG_DAILY_BERRY_MASTERS_WIFE":2353,"FLAG_DAILY_BERRY_MASTER_RECEIVED_BERRY":2349,"FLAG_DAILY_CONTEST_LOBBY_RECEIVED_BERRY":2337,"FLAG_DAILY_FLOWER_SHOP_RECEIVED_BERRY":2352,"FLAG_DAILY_LILYCOVE_RECEIVED_BERRY":2351,"FLAG_DAILY_PICKED_LOTO_TICKET":2346,"FLAG_DAILY_ROUTE_111_RECEIVED_BERRY":2348,"FLAG_DAILY_ROUTE_114_RECEIVED_BERRY":2347,"FLAG_DAILY_ROUTE_120_RECEIVED_BERRY":2350,"FLAG_DAILY_SECRET_BASE":2338,"FLAG_DAILY_SOOTOPOLIS_RECEIVED_BERRY":2354,"FLAG_DECLINED_BIKE":89,"FLAG_DECLINED_RIVAL_BATTLE_LILYCOVE":286,"FLAG_DECLINED_WALLY_BATTLE_MAUVILLE":284,"FLAG_DECORATION_1":174,"FLAG_DECORATION_10":183,"FLAG_DECORATION_11":184,"FLAG_DECORATION_12":185,"FLAG_DECORATION_13":186,"FLAG_DECORATION_14":187,"FLAG_DECORATION_2":175,"FLAG_DECORATION_3":176,"FLAG_DECORATION_4":177,"FLAG_DECORATION_5":178,"FLAG_DECORATION_6":179,"FLAG_DECORATION_7":180,"FLAG_DECORATION_8":181,"FLAG_DECORATION_9":182,"FLAG_DEFEATED_DEOXYS":428,"FLAG_DEFEATED_DEWFORD_GYM":1265,"FLAG_DEFEATED_ELECTRODE_1_AQUA_HIDEOUT":452,"FLAG_DEFEATED_ELECTRODE_2_AQUA_HIDEOUT":453,"FLAG_DEFEATED_ELITE_4_DRAKE":1278,"FLAG_DEFEATED_ELITE_4_GLACIA":1277,"FLAG_DEFEATED_ELITE_4_PHOEBE":1276,"FLAG_DEFEATED_ELITE_4_SIDNEY":1275,"FLAG_DEFEATED_EVIL_TEAM_MT_CHIMNEY":139,"FLAG_DEFEATED_FORTREE_GYM":1269,"FLAG_DEFEATED_GROUDON":447,"FLAG_DEFEATED_GRUNT_SPACE_CENTER_1F":191,"FLAG_DEFEATED_HO_OH":476,"FLAG_DEFEATED_KECLEON_1_ROUTE_119":989,"FLAG_DEFEATED_KECLEON_1_ROUTE_120":982,"FLAG_DEFEATED_KECLEON_2_ROUTE_119":990,"FLAG_DEFEATED_KECLEON_2_ROUTE_120":985,"FLAG_DEFEATED_KECLEON_3_ROUTE_120":986,"FLAG_DEFEATED_KECLEON_4_ROUTE_120":987,"FLAG_DEFEATED_KECLEON_5_ROUTE_120":988,"FLAG_DEFEATED_KEKLEON_ROUTE_120_BRIDGE":970,"FLAG_DEFEATED_KYOGRE":446,"FLAG_DEFEATED_LATIAS":456,"FLAG_DEFEATED_LATIOS":481,"FLAG_DEFEATED_LAVARIDGE_GYM":1267,"FLAG_DEFEATED_LUGIA":477,"FLAG_DEFEATED_MAGMA_SPACE_CENTER":117,"FLAG_DEFEATED_MAUVILLE_GYM":1266,"FLAG_DEFEATED_METEOR_FALLS_STEVEN":1272,"FLAG_DEFEATED_MEW":455,"FLAG_DEFEATED_MOSSDEEP_GYM":1270,"FLAG_DEFEATED_PETALBURG_GYM":1268,"FLAG_DEFEATED_RAYQUAZA":448,"FLAG_DEFEATED_REGICE":444,"FLAG_DEFEATED_REGIROCK":443,"FLAG_DEFEATED_REGISTEEL":445,"FLAG_DEFEATED_RIVAL_ROUTE103":130,"FLAG_DEFEATED_RIVAL_ROUTE_104":125,"FLAG_DEFEATED_RIVAL_RUSTBORO":211,"FLAG_DEFEATED_RUSTBORO_GYM":1264,"FLAG_DEFEATED_SEASHORE_HOUSE":141,"FLAG_DEFEATED_SOOTOPOLIS_GYM":1271,"FLAG_DEFEATED_SS_TIDAL_TRAINERS":247,"FLAG_DEFEATED_SUDOWOODO":454,"FLAG_DEFEATED_VOLTORB_1_NEW_MAUVILLE":449,"FLAG_DEFEATED_VOLTORB_2_NEW_MAUVILLE":450,"FLAG_DEFEATED_VOLTORB_3_NEW_MAUVILLE":451,"FLAG_DEFEATED_WALLY_MAUVILLE":190,"FLAG_DEFEATED_WALLY_VICTORY_ROAD":126,"FLAG_DELIVERED_DEVON_GOODS":149,"FLAG_DELIVERED_STEVEN_LETTER":189,"FLAG_DEOXYS_IS_RECOVERING":1258,"FLAG_DEOXYS_ROCK_COMPLETE":2260,"FLAG_DEVON_GOODS_STOLEN":142,"FLAG_DOCK_REJECTED_DEVON_GOODS":148,"FLAG_DONT_TRANSITION_MUSIC":16385,"FLAG_ENABLE_BRAWLY_MATCH_CALL":468,"FLAG_ENABLE_FIRST_WALLY_POKENAV_CALL":136,"FLAG_ENABLE_FLANNERY_MATCH_CALL":470,"FLAG_ENABLE_JUAN_MATCH_CALL":473,"FLAG_ENABLE_MOM_MATCH_CALL":216,"FLAG_ENABLE_MR_STONE_POKENAV":344,"FLAG_ENABLE_MULTI_CORRIDOR_DOOR":16386,"FLAG_ENABLE_NORMAN_MATCH_CALL":306,"FLAG_ENABLE_PROF_BIRCH_MATCH_CALL":281,"FLAG_ENABLE_RIVAL_MATCH_CALL":253,"FLAG_ENABLE_ROXANNE_FIRST_CALL":128,"FLAG_ENABLE_ROXANNE_MATCH_CALL":467,"FLAG_ENABLE_SCOTT_MATCH_CALL":215,"FLAG_ENABLE_SHIP_BIRTH_ISLAND":2261,"FLAG_ENABLE_SHIP_FARAWAY_ISLAND":2262,"FLAG_ENABLE_SHIP_NAVEL_ROCK":2272,"FLAG_ENABLE_SHIP_SOUTHERN_ISLAND":2227,"FLAG_ENABLE_TATE_AND_LIZA_MATCH_CALL":472,"FLAG_ENABLE_WALLY_MATCH_CALL":214,"FLAG_ENABLE_WATTSON_MATCH_CALL":469,"FLAG_ENABLE_WINONA_MATCH_CALL":471,"FLAG_ENTERED_CONTEST":341,"FLAG_ENTERED_ELITE_FOUR":263,"FLAG_ENTERED_MIRAGE_TOWER":2268,"FLAG_EVIL_LEADER_PLEASE_STOP":219,"FLAG_EVIL_TEAM_ESCAPED_STERN_SPOKE":271,"FLAG_EXCHANGED_SCANNER":294,"FLAG_FAN_CLUB_STRENGTH_SHARED":210,"FLAG_FLOWER_SHOP_RECEIVED_BERRY":1207,"FLAG_FORCE_MIRAGE_TOWER_VISIBLE":157,"FLAG_FORTREE_NPC_TRADE_COMPLETED":155,"FLAG_GOOD_LUCK_SAFARI_ZONE":93,"FLAG_GOT_BASEMENT_KEY_FROM_WATTSON":208,"FLAG_GOT_TM_THUNDERBOLT_FROM_WATTSON":209,"FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT":111,"FLAG_GROUDON_IS_RECOVERING":1274,"FLAG_HAS_MATCH_CALL":303,"FLAG_HIDDEN_ITEMS_START":500,"FLAG_HIDDEN_ITEM_ABANDONED_SHIP_RM_1_KEY":531,"FLAG_HIDDEN_ITEM_ABANDONED_SHIP_RM_2_KEY":532,"FLAG_HIDDEN_ITEM_ABANDONED_SHIP_RM_4_KEY":533,"FLAG_HIDDEN_ITEM_ABANDONED_SHIP_RM_6_KEY":534,"FLAG_HIDDEN_ITEM_ARTISAN_CAVE_B1F_CALCIUM":601,"FLAG_HIDDEN_ITEM_ARTISAN_CAVE_B1F_IRON":604,"FLAG_HIDDEN_ITEM_ARTISAN_CAVE_B1F_PROTEIN":603,"FLAG_HIDDEN_ITEM_ARTISAN_CAVE_B1F_ZINC":602,"FLAG_HIDDEN_ITEM_FALLARBOR_TOWN_NUGGET":528,"FLAG_HIDDEN_ITEM_GRANITE_CAVE_B2F_EVERSTONE_1":548,"FLAG_HIDDEN_ITEM_GRANITE_CAVE_B2F_EVERSTONE_2":549,"FLAG_HIDDEN_ITEM_JAGGED_PASS_FULL_HEAL":577,"FLAG_HIDDEN_ITEM_JAGGED_PASS_GREAT_BALL":576,"FLAG_HIDDEN_ITEM_LAVARIDGE_TOWN_ICE_HEAL":500,"FLAG_HIDDEN_ITEM_LILYCOVE_CITY_HEART_SCALE":527,"FLAG_HIDDEN_ITEM_LILYCOVE_CITY_POKE_BALL":575,"FLAG_HIDDEN_ITEM_LILYCOVE_CITY_PP_UP":543,"FLAG_HIDDEN_ITEM_MT_PYRE_EXTERIOR_MAX_ETHER":578,"FLAG_HIDDEN_ITEM_MT_PYRE_EXTERIOR_ULTRA_BALL":529,"FLAG_HIDDEN_ITEM_MT_PYRE_SUMMIT_RARE_CANDY":580,"FLAG_HIDDEN_ITEM_MT_PYRE_SUMMIT_ZINC":579,"FLAG_HIDDEN_ITEM_NAVEL_ROCK_TOP_SACRED_ASH":609,"FLAG_HIDDEN_ITEM_PETALBURG_CITY_RARE_CANDY":595,"FLAG_HIDDEN_ITEM_PETALBURG_WOODS_POKE_BALL":561,"FLAG_HIDDEN_ITEM_PETALBURG_WOODS_POTION":558,"FLAG_HIDDEN_ITEM_PETALBURG_WOODS_TINY_MUSHROOM_1":559,"FLAG_HIDDEN_ITEM_PETALBURG_WOODS_TINY_MUSHROOM_2":560,"FLAG_HIDDEN_ITEM_ROUTE_104_ANTIDOTE":585,"FLAG_HIDDEN_ITEM_ROUTE_104_HEART_SCALE":588,"FLAG_HIDDEN_ITEM_ROUTE_104_POKE_BALL":562,"FLAG_HIDDEN_ITEM_ROUTE_104_POTION":537,"FLAG_HIDDEN_ITEM_ROUTE_104_SUPER_POTION":544,"FLAG_HIDDEN_ITEM_ROUTE_105_BIG_PEARL":611,"FLAG_HIDDEN_ITEM_ROUTE_105_HEART_SCALE":589,"FLAG_HIDDEN_ITEM_ROUTE_106_HEART_SCALE":547,"FLAG_HIDDEN_ITEM_ROUTE_106_POKE_BALL":563,"FLAG_HIDDEN_ITEM_ROUTE_106_STARDUST":546,"FLAG_HIDDEN_ITEM_ROUTE_108_RARE_CANDY":586,"FLAG_HIDDEN_ITEM_ROUTE_109_ETHER":564,"FLAG_HIDDEN_ITEM_ROUTE_109_GREAT_BALL":551,"FLAG_HIDDEN_ITEM_ROUTE_109_HEART_SCALE_1":552,"FLAG_HIDDEN_ITEM_ROUTE_109_HEART_SCALE_2":590,"FLAG_HIDDEN_ITEM_ROUTE_109_HEART_SCALE_3":591,"FLAG_HIDDEN_ITEM_ROUTE_109_REVIVE":550,"FLAG_HIDDEN_ITEM_ROUTE_110_FULL_HEAL":555,"FLAG_HIDDEN_ITEM_ROUTE_110_GREAT_BALL":553,"FLAG_HIDDEN_ITEM_ROUTE_110_POKE_BALL":565,"FLAG_HIDDEN_ITEM_ROUTE_110_REVIVE":554,"FLAG_HIDDEN_ITEM_ROUTE_111_PROTEIN":556,"FLAG_HIDDEN_ITEM_ROUTE_111_RARE_CANDY":557,"FLAG_HIDDEN_ITEM_ROUTE_111_STARDUST":502,"FLAG_HIDDEN_ITEM_ROUTE_113_ETHER":503,"FLAG_HIDDEN_ITEM_ROUTE_113_NUGGET":598,"FLAG_HIDDEN_ITEM_ROUTE_113_TM_DOUBLE_TEAM":530,"FLAG_HIDDEN_ITEM_ROUTE_114_CARBOS":504,"FLAG_HIDDEN_ITEM_ROUTE_114_REVIVE":542,"FLAG_HIDDEN_ITEM_ROUTE_115_HEART_SCALE":597,"FLAG_HIDDEN_ITEM_ROUTE_116_BLACK_GLASSES":596,"FLAG_HIDDEN_ITEM_ROUTE_116_SUPER_POTION":545,"FLAG_HIDDEN_ITEM_ROUTE_117_REPEL":572,"FLAG_HIDDEN_ITEM_ROUTE_118_HEART_SCALE":566,"FLAG_HIDDEN_ITEM_ROUTE_118_IRON":567,"FLAG_HIDDEN_ITEM_ROUTE_119_CALCIUM":505,"FLAG_HIDDEN_ITEM_ROUTE_119_FULL_HEAL":568,"FLAG_HIDDEN_ITEM_ROUTE_119_MAX_ETHER":587,"FLAG_HIDDEN_ITEM_ROUTE_119_ULTRA_BALL":506,"FLAG_HIDDEN_ITEM_ROUTE_120_RARE_CANDY_1":571,"FLAG_HIDDEN_ITEM_ROUTE_120_RARE_CANDY_2":569,"FLAG_HIDDEN_ITEM_ROUTE_120_REVIVE":584,"FLAG_HIDDEN_ITEM_ROUTE_120_ZINC":570,"FLAG_HIDDEN_ITEM_ROUTE_121_FULL_HEAL":573,"FLAG_HIDDEN_ITEM_ROUTE_121_HP_UP":539,"FLAG_HIDDEN_ITEM_ROUTE_121_MAX_REVIVE":600,"FLAG_HIDDEN_ITEM_ROUTE_121_NUGGET":540,"FLAG_HIDDEN_ITEM_ROUTE_123_HYPER_POTION":574,"FLAG_HIDDEN_ITEM_ROUTE_123_PP_UP":599,"FLAG_HIDDEN_ITEM_ROUTE_123_RARE_CANDY":610,"FLAG_HIDDEN_ITEM_ROUTE_123_REVIVE":541,"FLAG_HIDDEN_ITEM_ROUTE_123_SUPER_REPEL":507,"FLAG_HIDDEN_ITEM_ROUTE_128_HEART_SCALE_1":592,"FLAG_HIDDEN_ITEM_ROUTE_128_HEART_SCALE_2":593,"FLAG_HIDDEN_ITEM_ROUTE_128_HEART_SCALE_3":594,"FLAG_HIDDEN_ITEM_SAFARI_ZONE_NORTH_EAST_RARE_CANDY":606,"FLAG_HIDDEN_ITEM_SAFARI_ZONE_NORTH_EAST_ZINC":607,"FLAG_HIDDEN_ITEM_SAFARI_ZONE_SOUTH_EAST_FULL_RESTORE":605,"FLAG_HIDDEN_ITEM_SAFARI_ZONE_SOUTH_EAST_PP_UP":608,"FLAG_HIDDEN_ITEM_SS_TIDAL_LOWER_DECK_LEFTOVERS":535,"FLAG_HIDDEN_ITEM_TRICK_HOUSE_NUGGET":501,"FLAG_HIDDEN_ITEM_UNDERWATER_124_BIG_PEARL":511,"FLAG_HIDDEN_ITEM_UNDERWATER_124_CALCIUM":536,"FLAG_HIDDEN_ITEM_UNDERWATER_124_CARBOS":508,"FLAG_HIDDEN_ITEM_UNDERWATER_124_GREEN_SHARD":509,"FLAG_HIDDEN_ITEM_UNDERWATER_124_HEART_SCALE_1":513,"FLAG_HIDDEN_ITEM_UNDERWATER_124_HEART_SCALE_2":538,"FLAG_HIDDEN_ITEM_UNDERWATER_124_PEARL":510,"FLAG_HIDDEN_ITEM_UNDERWATER_126_BIG_PEARL":520,"FLAG_HIDDEN_ITEM_UNDERWATER_126_BLUE_SHARD":512,"FLAG_HIDDEN_ITEM_UNDERWATER_126_HEART_SCALE":514,"FLAG_HIDDEN_ITEM_UNDERWATER_126_IRON":519,"FLAG_HIDDEN_ITEM_UNDERWATER_126_PEARL":517,"FLAG_HIDDEN_ITEM_UNDERWATER_126_STARDUST":516,"FLAG_HIDDEN_ITEM_UNDERWATER_126_ULTRA_BALL":515,"FLAG_HIDDEN_ITEM_UNDERWATER_126_YELLOW_SHARD":518,"FLAG_HIDDEN_ITEM_UNDERWATER_127_HEART_SCALE":523,"FLAG_HIDDEN_ITEM_UNDERWATER_127_HP_UP":522,"FLAG_HIDDEN_ITEM_UNDERWATER_127_RED_SHARD":524,"FLAG_HIDDEN_ITEM_UNDERWATER_127_STAR_PIECE":521,"FLAG_HIDDEN_ITEM_UNDERWATER_128_PEARL":526,"FLAG_HIDDEN_ITEM_UNDERWATER_128_PROTEIN":525,"FLAG_HIDDEN_ITEM_VICTORY_ROAD_1F_ULTRA_BALL":581,"FLAG_HIDDEN_ITEM_VICTORY_ROAD_B2F_ELIXIR":582,"FLAG_HIDDEN_ITEM_VICTORY_ROAD_B2F_MAX_REPEL":583,"FLAG_HIDE_APPRENTICE":701,"FLAG_HIDE_AQUA_HIDEOUT_1F_GRUNTS_BLOCKING_ENTRANCE":821,"FLAG_HIDE_AQUA_HIDEOUT_B1F_ELECTRODE_1":977,"FLAG_HIDE_AQUA_HIDEOUT_B1F_ELECTRODE_2":978,"FLAG_HIDE_AQUA_HIDEOUT_B2F_SUBMARINE_SHADOW":943,"FLAG_HIDE_AQUA_HIDEOUT_GRUNTS":924,"FLAG_HIDE_BATTLE_FRONTIER_RECEPTION_GATE_SCOTT":836,"FLAG_HIDE_BATTLE_FRONTIER_SUDOWOODO":842,"FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_1":711,"FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_2":712,"FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_3":713,"FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_4":714,"FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_5":715,"FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_6":716,"FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_ALT_1":864,"FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_ALT_2":865,"FLAG_HIDE_BATTLE_TOWER_OPPONENT":888,"FLAG_HIDE_BATTLE_TOWER_REPORTER":918,"FLAG_HIDE_BIRTH_ISLAND_DEOXYS_TRIANGLE":764,"FLAG_HIDE_BRINEYS_HOUSE_MR_BRINEY":739,"FLAG_HIDE_BRINEYS_HOUSE_PEEKO":881,"FLAG_HIDE_CAVE_OF_ORIGIN_B1F_WALLACE":820,"FLAG_HIDE_CHAMPIONS_ROOM_BIRCH":921,"FLAG_HIDE_CHAMPIONS_ROOM_RIVAL":920,"FLAG_HIDE_CONTEST_POKE_BALL":86,"FLAG_HIDE_DEOXYS":763,"FLAG_HIDE_DESERT_UNDERPASS_FOSSIL":874,"FLAG_HIDE_DEWFORD_HALL_SLUDGE_BOMB_MAN":940,"FLAG_HIDE_EVER_GRANDE_POKEMON_CENTER_1F_SCOTT":793,"FLAG_HIDE_FALLARBOR_AZURILL":907,"FLAG_HIDE_FALLARBOR_HOUSE_PROF_COZMO":928,"FLAG_HIDE_FALLARBOR_TOWN_BATTLE_TENT_SCOTT":767,"FLAG_HIDE_FALLORBOR_POKEMON_CENTER_LANETTE":871,"FLAG_HIDE_FANCLUB_BOY":790,"FLAG_HIDE_FANCLUB_LADY":792,"FLAG_HIDE_FANCLUB_LITTLE_BOY":791,"FLAG_HIDE_FANCLUB_OLD_LADY":789,"FLAG_HIDE_FORTREE_CITY_HOUSE_4_WINGULL":933,"FLAG_HIDE_FORTREE_CITY_KECLEON":969,"FLAG_HIDE_GRANITE_CAVE_STEVEN":833,"FLAG_HIDE_HO_OH":801,"FLAG_HIDE_JAGGED_PASS_MAGMA_GUARD":847,"FLAG_HIDE_LANETTES_HOUSE_LANETTE":870,"FLAG_HIDE_LAVARIDGE_TOWN_RIVAL":929,"FLAG_HIDE_LAVARIDGE_TOWN_RIVAL_ON_BIKE":930,"FLAG_HIDE_LILYCOVE_CITY_AQUA_GRUNTS":852,"FLAG_HIDE_LILYCOVE_CITY_RIVAL":971,"FLAG_HIDE_LILYCOVE_CITY_WAILMER":729,"FLAG_HIDE_LILYCOVE_CONTEST_HALL_BLEND_MASTER":832,"FLAG_HIDE_LILYCOVE_CONTEST_HALL_BLEND_MASTER_REPLACEMENT":873,"FLAG_HIDE_LILYCOVE_CONTEST_HALL_CONTEST_ATTENDANT_1":774,"FLAG_HIDE_LILYCOVE_CONTEST_HALL_CONTEST_ATTENDANT_2":895,"FLAG_HIDE_LILYCOVE_CONTEST_HALL_REPORTER":802,"FLAG_HIDE_LILYCOVE_DEPARTMENT_STORE_ROOFTOP_SALE_WOMAN":962,"FLAG_HIDE_LILYCOVE_FAN_CLUB_INTERVIEWER":730,"FLAG_HIDE_LILYCOVE_HARBOR_EVENT_TICKET_TAKER":748,"FLAG_HIDE_LILYCOVE_HARBOR_FERRY_ATTENDANT":908,"FLAG_HIDE_LILYCOVE_HARBOR_FERRY_SAILOR":909,"FLAG_HIDE_LILYCOVE_HARBOR_SSTIDAL":861,"FLAG_HIDE_LILYCOVE_MOTEL_GAME_DESIGNERS":925,"FLAG_HIDE_LILYCOVE_MOTEL_SCOTT":787,"FLAG_HIDE_LILYCOVE_MUSEUM_CURATOR":775,"FLAG_HIDE_LILYCOVE_MUSEUM_PATRON_1":776,"FLAG_HIDE_LILYCOVE_MUSEUM_PATRON_2":777,"FLAG_HIDE_LILYCOVE_MUSEUM_PATRON_3":778,"FLAG_HIDE_LILYCOVE_MUSEUM_PATRON_4":779,"FLAG_HIDE_LILYCOVE_MUSEUM_TOURISTS":780,"FLAG_HIDE_LILYCOVE_POKEMON_CENTER_CONTEST_LADY_MON":993,"FLAG_HIDE_LITTLEROOT_TOWN_BIRCH":795,"FLAG_HIDE_LITTLEROOT_TOWN_BIRCHS_LAB_BIRCH":721,"FLAG_HIDE_LITTLEROOT_TOWN_BIRCHS_LAB_POKEBALL_CHIKORITA":838,"FLAG_HIDE_LITTLEROOT_TOWN_BIRCHS_LAB_POKEBALL_CYNDAQUIL":811,"FLAG_HIDE_LITTLEROOT_TOWN_BIRCHS_LAB_POKEBALL_TOTODILE":812,"FLAG_HIDE_LITTLEROOT_TOWN_BIRCHS_LAB_RIVAL":889,"FLAG_HIDE_LITTLEROOT_TOWN_BIRCHS_LAB_UNKNOWN_0x380":896,"FLAG_HIDE_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F_POKE_BALL":817,"FLAG_HIDE_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F_SWABLU_DOLL":815,"FLAG_HIDE_LITTLEROOT_TOWN_BRENDANS_HOUSE_BRENDAN":745,"FLAG_HIDE_LITTLEROOT_TOWN_BRENDANS_HOUSE_MOM":758,"FLAG_HIDE_LITTLEROOT_TOWN_BRENDANS_HOUSE_RIVAL_BEDROOM":760,"FLAG_HIDE_LITTLEROOT_TOWN_BRENDANS_HOUSE_RIVAL_MOM":784,"FLAG_HIDE_LITTLEROOT_TOWN_BRENDANS_HOUSE_RIVAL_SIBLING":735,"FLAG_HIDE_LITTLEROOT_TOWN_BRENDANS_HOUSE_TRUCK":761,"FLAG_HIDE_LITTLEROOT_TOWN_FAT_MAN":868,"FLAG_HIDE_LITTLEROOT_TOWN_MAYS_HOUSE_2F_PICHU_DOLL":849,"FLAG_HIDE_LITTLEROOT_TOWN_MAYS_HOUSE_2F_POKE_BALL":818,"FLAG_HIDE_LITTLEROOT_TOWN_MAYS_HOUSE_MAY":746,"FLAG_HIDE_LITTLEROOT_TOWN_MAYS_HOUSE_MOM":759,"FLAG_HIDE_LITTLEROOT_TOWN_MAYS_HOUSE_RIVAL_BEDROOM":722,"FLAG_HIDE_LITTLEROOT_TOWN_MAYS_HOUSE_RIVAL_MOM":785,"FLAG_HIDE_LITTLEROOT_TOWN_MAYS_HOUSE_RIVAL_SIBLING":736,"FLAG_HIDE_LITTLEROOT_TOWN_MAYS_HOUSE_TRUCK":762,"FLAG_HIDE_LITTLEROOT_TOWN_MOM_OUTSIDE":752,"FLAG_HIDE_LITTLEROOT_TOWN_PLAYERS_BEDROOM_MOM":757,"FLAG_HIDE_LITTLEROOT_TOWN_PLAYERS_HOUSE_VIGOROTH_1":754,"FLAG_HIDE_LITTLEROOT_TOWN_PLAYERS_HOUSE_VIGOROTH_2":755,"FLAG_HIDE_LITTLEROOT_TOWN_RIVAL":794,"FLAG_HIDE_LUGIA":800,"FLAG_HIDE_MAGMA_HIDEOUT_4F_GROUDON":853,"FLAG_HIDE_MAGMA_HIDEOUT_4F_GROUDON_ASLEEP":850,"FLAG_HIDE_MAGMA_HIDEOUT_GRUNTS":857,"FLAG_HIDE_MAGMA_HIDEOUT_MAXIE":867,"FLAG_HIDE_MAP_NAME_POPUP":16384,"FLAG_HIDE_MARINE_CAVE_KYOGRE":782,"FLAG_HIDE_MAUVILLE_CITY_SCOTT":765,"FLAG_HIDE_MAUVILLE_CITY_WALLY":804,"FLAG_HIDE_MAUVILLE_CITY_WALLYS_UNCLE":805,"FLAG_HIDE_MAUVILLE_CITY_WATTSON":912,"FLAG_HIDE_MAUVILLE_GYM_WATTSON":913,"FLAG_HIDE_METEOR_FALLS_1F_1R_COZMO":942,"FLAG_HIDE_METEOR_FALLS_TEAM_AQUA":938,"FLAG_HIDE_METEOR_FALLS_TEAM_MAGMA":939,"FLAG_HIDE_MEW":718,"FLAG_HIDE_MIRAGE_TOWER_CLAW_FOSSIL":964,"FLAG_HIDE_MIRAGE_TOWER_ROOT_FOSSIL":963,"FLAG_HIDE_MOSSDEEP_CITY_HOUSE_2_WINGULL":934,"FLAG_HIDE_MOSSDEEP_CITY_SCOTT":788,"FLAG_HIDE_MOSSDEEP_CITY_SPACE_CENTER_1F_STEVEN":753,"FLAG_HIDE_MOSSDEEP_CITY_SPACE_CENTER_1F_TEAM_MAGMA":756,"FLAG_HIDE_MOSSDEEP_CITY_SPACE_CENTER_2F_STEVEN":863,"FLAG_HIDE_MOSSDEEP_CITY_SPACE_CENTER_2F_TEAM_MAGMA":862,"FLAG_HIDE_MOSSDEEP_CITY_SPACE_CENTER_MAGMA_NOTE":737,"FLAG_HIDE_MOSSDEEP_CITY_STEVENS_HOUSE_BELDUM_POKEBALL":968,"FLAG_HIDE_MOSSDEEP_CITY_STEVENS_HOUSE_INVISIBLE_NINJA_BOY":727,"FLAG_HIDE_MOSSDEEP_CITY_STEVENS_HOUSE_STEVEN":967,"FLAG_HIDE_MOSSDEEP_CITY_TEAM_MAGMA":823,"FLAG_HIDE_MR_BRINEY_BOAT_DEWFORD_TOWN":743,"FLAG_HIDE_MR_BRINEY_DEWFORD_TOWN":740,"FLAG_HIDE_MT_CHIMNEY_LAVA_COOKIE_LADY":994,"FLAG_HIDE_MT_CHIMNEY_TEAM_AQUA":926,"FLAG_HIDE_MT_CHIMNEY_TEAM_MAGMA":927,"FLAG_HIDE_MT_CHIMNEY_TEAM_MAGMA_BATTLEABLE":981,"FLAG_HIDE_MT_CHIMNEY_TRAINERS":877,"FLAG_HIDE_MT_PYRE_SUMMIT_ARCHIE":916,"FLAG_HIDE_MT_PYRE_SUMMIT_MAXIE":856,"FLAG_HIDE_MT_PYRE_SUMMIT_TEAM_AQUA":917,"FLAG_HIDE_NEW_MAUVILLE_VOLTORB_1":974,"FLAG_HIDE_NEW_MAUVILLE_VOLTORB_2":975,"FLAG_HIDE_NEW_MAUVILLE_VOLTORB_3":976,"FLAG_HIDE_OLDALE_TOWN_RIVAL":979,"FLAG_HIDE_PETALBURG_CITY_SCOTT":995,"FLAG_HIDE_PETALBURG_CITY_WALLY":726,"FLAG_HIDE_PETALBURG_CITY_WALLYS_DAD":830,"FLAG_HIDE_PETALBURG_CITY_WALLYS_MOM":728,"FLAG_HIDE_PETALBURG_GYM_GREETER":781,"FLAG_HIDE_PETALBURG_GYM_NORMAN":772,"FLAG_HIDE_PETALBURG_GYM_WALLY":866,"FLAG_HIDE_PETALBURG_GYM_WALLYS_DAD":824,"FLAG_HIDE_PETALBURG_WOODS_AQUA_GRUNT":725,"FLAG_HIDE_PETALBURG_WOODS_DEVON_EMPLOYEE":724,"FLAG_HIDE_PLAYERS_HOUSE_DAD":734,"FLAG_HIDE_POKEMON_CENTER_2F_MYSTERY_GIFT_MAN":702,"FLAG_HIDE_REGICE":936,"FLAG_HIDE_REGIROCK":935,"FLAG_HIDE_REGISTEEL":937,"FLAG_HIDE_ROUTE_101_BIRCH":897,"FLAG_HIDE_ROUTE_101_BIRCH_STARTERS_BAG":700,"FLAG_HIDE_ROUTE_101_BIRCH_ZIGZAGOON_BATTLE":720,"FLAG_HIDE_ROUTE_101_BOY":991,"FLAG_HIDE_ROUTE_101_ZIGZAGOON":750,"FLAG_HIDE_ROUTE_103_BIRCH":898,"FLAG_HIDE_ROUTE_103_RIVAL":723,"FLAG_HIDE_ROUTE_104_MR_BRINEY":738,"FLAG_HIDE_ROUTE_104_MR_BRINEY_BOAT":742,"FLAG_HIDE_ROUTE_104_RIVAL":719,"FLAG_HIDE_ROUTE_104_WHITE_HERB_FLORIST":906,"FLAG_HIDE_ROUTE_109_MR_BRINEY":741,"FLAG_HIDE_ROUTE_109_MR_BRINEY_BOAT":744,"FLAG_HIDE_ROUTE_110_BIRCH":837,"FLAG_HIDE_ROUTE_110_RIVAL":919,"FLAG_HIDE_ROUTE_110_RIVAL_ON_BIKE":922,"FLAG_HIDE_ROUTE_110_TEAM_AQUA":900,"FLAG_HIDE_ROUTE_111_DESERT_FOSSIL":876,"FLAG_HIDE_ROUTE_111_GABBY_AND_TY_1":796,"FLAG_HIDE_ROUTE_111_GABBY_AND_TY_2":903,"FLAG_HIDE_ROUTE_111_GABBY_AND_TY_3":799,"FLAG_HIDE_ROUTE_111_PLAYER_DESCENT":875,"FLAG_HIDE_ROUTE_111_ROCK_SMASH_TIP_GUY":843,"FLAG_HIDE_ROUTE_111_SECRET_POWER_MAN":960,"FLAG_HIDE_ROUTE_111_VICKY_WINSTRATE":771,"FLAG_HIDE_ROUTE_111_VICTORIA_WINSTRATE":769,"FLAG_HIDE_ROUTE_111_VICTOR_WINSTRATE":768,"FLAG_HIDE_ROUTE_111_VIVI_WINSTRATE":770,"FLAG_HIDE_ROUTE_112_TEAM_MAGMA":819,"FLAG_HIDE_ROUTE_115_BOULDERS":825,"FLAG_HIDE_ROUTE_116_DEVON_EMPLOYEE":947,"FLAG_HIDE_ROUTE_116_DROPPED_GLASSES_MAN":813,"FLAG_HIDE_ROUTE_116_MR_BRINEY":891,"FLAG_HIDE_ROUTE_116_WANDAS_BOYFRIEND":894,"FLAG_HIDE_ROUTE_118_GABBY_AND_TY_1":797,"FLAG_HIDE_ROUTE_118_GABBY_AND_TY_2":901,"FLAG_HIDE_ROUTE_118_GABBY_AND_TY_3":904,"FLAG_HIDE_ROUTE_118_STEVEN":966,"FLAG_HIDE_ROUTE_119_RIVAL":851,"FLAG_HIDE_ROUTE_119_RIVAL_ON_BIKE":923,"FLAG_HIDE_ROUTE_119_SCOTT":786,"FLAG_HIDE_ROUTE_119_TEAM_AQUA":890,"FLAG_HIDE_ROUTE_119_TEAM_AQUA_BRIDGE":822,"FLAG_HIDE_ROUTE_119_TEAM_AQUA_SHELLY":915,"FLAG_HIDE_ROUTE_120_GABBY_AND_TY_1":798,"FLAG_HIDE_ROUTE_120_GABBY_AND_TY_2":902,"FLAG_HIDE_ROUTE_120_STEVEN":972,"FLAG_HIDE_ROUTE_121_TEAM_AQUA_GRUNTS":914,"FLAG_HIDE_ROUTE_128_ARCHIE":944,"FLAG_HIDE_ROUTE_128_MAXIE":945,"FLAG_HIDE_ROUTE_128_STEVEN":834,"FLAG_HIDE_RUSTBORO_CITY_AQUA_GRUNT":731,"FLAG_HIDE_RUSTBORO_CITY_DEVON_CORP_3F_EMPLOYEE":949,"FLAG_HIDE_RUSTBORO_CITY_DEVON_EMPLOYEE_1":732,"FLAG_HIDE_RUSTBORO_CITY_POKEMON_SCHOOL_SCOTT":999,"FLAG_HIDE_RUSTBORO_CITY_RIVAL":814,"FLAG_HIDE_RUSTBORO_CITY_SCIENTIST":844,"FLAG_HIDE_RUSTURF_TUNNEL_AQUA_GRUNT":878,"FLAG_HIDE_RUSTURF_TUNNEL_BRINEY":879,"FLAG_HIDE_RUSTURF_TUNNEL_PEEKO":880,"FLAG_HIDE_RUSTURF_TUNNEL_ROCK_1":931,"FLAG_HIDE_RUSTURF_TUNNEL_ROCK_2":932,"FLAG_HIDE_RUSTURF_TUNNEL_WANDA":983,"FLAG_HIDE_RUSTURF_TUNNEL_WANDAS_BOYFRIEND":807,"FLAG_HIDE_SAFARI_ZONE_SOUTH_CONSTRUCTION_WORKERS":717,"FLAG_HIDE_SAFARI_ZONE_SOUTH_EAST_EXPANSION":747,"FLAG_HIDE_SEAFLOOR_CAVERN_AQUA_GRUNTS":946,"FLAG_HIDE_SEAFLOOR_CAVERN_ENTRANCE_AQUA_GRUNT":941,"FLAG_HIDE_SEAFLOOR_CAVERN_ROOM_9_ARCHIE":828,"FLAG_HIDE_SEAFLOOR_CAVERN_ROOM_9_KYOGRE":859,"FLAG_HIDE_SEAFLOOR_CAVERN_ROOM_9_KYOGRE_ASLEEP":733,"FLAG_HIDE_SEAFLOOR_CAVERN_ROOM_9_MAGMA_GRUNTS":831,"FLAG_HIDE_SEAFLOOR_CAVERN_ROOM_9_MAXIE":829,"FLAG_HIDE_SECRET_BASE_TRAINER":173,"FLAG_HIDE_SKY_PILLAR_TOP_RAYQUAZA":773,"FLAG_HIDE_SKY_PILLAR_TOP_RAYQUAZA_STILL":80,"FLAG_HIDE_SKY_PILLAR_WALLACE":855,"FLAG_HIDE_SLATEPORT_CITY_CAPTAIN_STERN":840,"FLAG_HIDE_SLATEPORT_CITY_CONTEST_REPORTER":803,"FLAG_HIDE_SLATEPORT_CITY_GABBY_AND_TY":835,"FLAG_HIDE_SLATEPORT_CITY_HARBOR_AQUA_GRUNT":845,"FLAG_HIDE_SLATEPORT_CITY_HARBOR_ARCHIE":846,"FLAG_HIDE_SLATEPORT_CITY_HARBOR_CAPTAIN_STERN":841,"FLAG_HIDE_SLATEPORT_CITY_HARBOR_PATRONS":905,"FLAG_HIDE_SLATEPORT_CITY_HARBOR_SS_TIDAL":860,"FLAG_HIDE_SLATEPORT_CITY_HARBOR_SUBMARINE_SHADOW":848,"FLAG_HIDE_SLATEPORT_CITY_OCEANIC_MUSEUM_2F_AQUA_GRUNT_1":884,"FLAG_HIDE_SLATEPORT_CITY_OCEANIC_MUSEUM_2F_AQUA_GRUNT_2":885,"FLAG_HIDE_SLATEPORT_CITY_OCEANIC_MUSEUM_2F_ARCHIE":886,"FLAG_HIDE_SLATEPORT_CITY_OCEANIC_MUSEUM_2F_CAPTAIN_STERN":887,"FLAG_HIDE_SLATEPORT_CITY_OCEANIC_MUSEUM_AQUA_GRUNTS":883,"FLAG_HIDE_SLATEPORT_CITY_OCEANIC_MUSEUM_FAMILIAR_AQUA_GRUNT":965,"FLAG_HIDE_SLATEPORT_CITY_SCOTT":749,"FLAG_HIDE_SLATEPORT_CITY_STERNS_SHIPYARD_MR_BRINEY":869,"FLAG_HIDE_SLATEPORT_CITY_TEAM_AQUA":882,"FLAG_HIDE_SLATEPORT_CITY_TM_SALESMAN":948,"FLAG_HIDE_SLATEPORT_MUSEUM_POPULATION":961,"FLAG_HIDE_SOOTOPOLIS_CITY_ARCHIE":826,"FLAG_HIDE_SOOTOPOLIS_CITY_GROUDON":998,"FLAG_HIDE_SOOTOPOLIS_CITY_KYOGRE":997,"FLAG_HIDE_SOOTOPOLIS_CITY_MAN_1":839,"FLAG_HIDE_SOOTOPOLIS_CITY_MAXIE":827,"FLAG_HIDE_SOOTOPOLIS_CITY_RAYQUAZA":996,"FLAG_HIDE_SOOTOPOLIS_CITY_RESIDENTS":854,"FLAG_HIDE_SOOTOPOLIS_CITY_STEVEN":973,"FLAG_HIDE_SOOTOPOLIS_CITY_WALLACE":816,"FLAG_HIDE_SOUTHERN_ISLAND_EON_STONE":910,"FLAG_HIDE_SOUTHERN_ISLAND_UNCHOSEN_EON_DUO_MON":911,"FLAG_HIDE_SS_TIDAL_CORRIDOR_MR_BRINEY":950,"FLAG_HIDE_SS_TIDAL_CORRIDOR_SCOTT":810,"FLAG_HIDE_SS_TIDAL_ROOMS_SNATCH_GIVER":951,"FLAG_HIDE_TERRA_CAVE_GROUDON":783,"FLAG_HIDE_TRICK_HOUSE_END_MAN":899,"FLAG_HIDE_TRICK_HOUSE_ENTRANCE_MAN":872,"FLAG_HIDE_UNDERWATER_SEA_FLOOR_CAVERN_STOLEN_SUBMARINE":980,"FLAG_HIDE_UNION_ROOM_PLAYER_1":703,"FLAG_HIDE_UNION_ROOM_PLAYER_2":704,"FLAG_HIDE_UNION_ROOM_PLAYER_3":705,"FLAG_HIDE_UNION_ROOM_PLAYER_4":706,"FLAG_HIDE_UNION_ROOM_PLAYER_5":707,"FLAG_HIDE_UNION_ROOM_PLAYER_6":708,"FLAG_HIDE_UNION_ROOM_PLAYER_7":709,"FLAG_HIDE_UNION_ROOM_PLAYER_8":710,"FLAG_HIDE_VERDANTURF_TOWN_SCOTT":766,"FLAG_HIDE_VERDANTURF_TOWN_WANDAS_HOUSE_WALLY":806,"FLAG_HIDE_VERDANTURF_TOWN_WANDAS_HOUSE_WALLYS_UNCLE":809,"FLAG_HIDE_VERDANTURF_TOWN_WANDAS_HOUSE_WANDA":984,"FLAG_HIDE_VERDANTURF_TOWN_WANDAS_HOUSE_WANDAS_BOYFRIEND":808,"FLAG_HIDE_VICTORY_ROAD_ENTRANCE_WALLY":858,"FLAG_HIDE_VICTORY_ROAD_EXIT_WALLY":751,"FLAG_HIDE_WEATHER_INSTITUTE_1F_WORKERS":892,"FLAG_HIDE_WEATHER_INSTITUTE_2F_AQUA_GRUNT_M":992,"FLAG_HIDE_WEATHER_INSTITUTE_2F_WORKERS":893,"FLAG_HO_OH_IS_RECOVERING":1256,"FLAG_INTERACTED_WITH_DEVON_EMPLOYEE_GOODS_STOLEN":159,"FLAG_INTERACTED_WITH_STEVEN_SPACE_CENTER":205,"FLAG_IS_CHAMPION":2175,"FLAG_ITEM_ABANDONED_SHIP_CAPTAINS_OFFICE_STORAGE_KEY":1100,"FLAG_ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_1_TM_RAIN_DANCE":1102,"FLAG_ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_2_SCANNER":1078,"FLAG_ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_3_WATER_STONE":1101,"FLAG_ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_6_LUXURY_BALL":1077,"FLAG_ITEM_ABANDONED_SHIP_ROOMS_1F_HARBOR_MAIL":1095,"FLAG_ITEM_ABANDONED_SHIP_ROOMS_2_1F_REVIVE":1099,"FLAG_ITEM_ABANDONED_SHIP_ROOMS_2_B1F_DIVE_BALL":1097,"FLAG_ITEM_ABANDONED_SHIP_ROOMS_B1F_ESCAPE_ROPE":1096,"FLAG_ITEM_ABANDONED_SHIP_ROOMS_B1F_TM_ICE_BEAM":1098,"FLAG_ITEM_AQUA_HIDEOUT_B1F_MASTER_BALL":1124,"FLAG_ITEM_AQUA_HIDEOUT_B1F_MAX_ELIXIR":1071,"FLAG_ITEM_AQUA_HIDEOUT_B1F_NUGGET":1132,"FLAG_ITEM_AQUA_HIDEOUT_B2F_NEST_BALL":1072,"FLAG_ITEM_ARTISAN_CAVE_1F_CARBOS":1163,"FLAG_ITEM_ARTISAN_CAVE_B1F_HP_UP":1162,"FLAG_ITEM_FIERY_PATH_FIRE_STONE":1111,"FLAG_ITEM_FIERY_PATH_TM_TOXIC":1091,"FLAG_ITEM_GRANITE_CAVE_1F_ESCAPE_ROPE":1050,"FLAG_ITEM_GRANITE_CAVE_B1F_POKE_BALL":1051,"FLAG_ITEM_GRANITE_CAVE_B2F_RARE_CANDY":1054,"FLAG_ITEM_GRANITE_CAVE_B2F_REPEL":1053,"FLAG_ITEM_JAGGED_PASS_BURN_HEAL":1070,"FLAG_ITEM_LILYCOVE_CITY_MAX_REPEL":1042,"FLAG_ITEM_MAGMA_HIDEOUT_1F_RARE_CANDY":1151,"FLAG_ITEM_MAGMA_HIDEOUT_2F_2R_FULL_RESTORE":1165,"FLAG_ITEM_MAGMA_HIDEOUT_2F_2R_MAX_ELIXIR":1164,"FLAG_ITEM_MAGMA_HIDEOUT_3F_1R_NUGGET":1166,"FLAG_ITEM_MAGMA_HIDEOUT_3F_2R_PP_MAX":1167,"FLAG_ITEM_MAGMA_HIDEOUT_3F_3R_ECAPE_ROPE":1059,"FLAG_ITEM_MAGMA_HIDEOUT_4F_MAX_REVIVE":1168,"FLAG_ITEM_MAUVILLE_CITY_X_SPEED":1116,"FLAG_ITEM_METEOR_FALLS_1F_1R_FULL_HEAL":1045,"FLAG_ITEM_METEOR_FALLS_1F_1R_MOON_STONE":1046,"FLAG_ITEM_METEOR_FALLS_1F_1R_PP_UP":1047,"FLAG_ITEM_METEOR_FALLS_1F_1R_TM_IRON_TAIL":1044,"FLAG_ITEM_METEOR_FALLS_B1F_2R_TM_DRAGON_CLAW":1080,"FLAG_ITEM_MOSSDEEP_CITY_NET_BALL":1043,"FLAG_ITEM_MOSSDEEP_STEVENS_HOUSE_HM08":1133,"FLAG_ITEM_MT_PYRE_2F_ULTRA_BALL":1129,"FLAG_ITEM_MT_PYRE_3F_SUPER_REPEL":1120,"FLAG_ITEM_MT_PYRE_4F_SEA_INCENSE":1130,"FLAG_ITEM_MT_PYRE_5F_LAX_INCENSE":1052,"FLAG_ITEM_MT_PYRE_6F_TM_SHADOW_BALL":1089,"FLAG_ITEM_MT_PYRE_EXTERIOR_MAX_POTION":1073,"FLAG_ITEM_MT_PYRE_EXTERIOR_TM_SKILL_SWAP":1074,"FLAG_ITEM_NEW_MAUVILLE_ESCAPE_ROPE":1076,"FLAG_ITEM_NEW_MAUVILLE_FULL_HEAL":1122,"FLAG_ITEM_NEW_MAUVILLE_PARALYZE_HEAL":1123,"FLAG_ITEM_NEW_MAUVILLE_THUNDER_STONE":1110,"FLAG_ITEM_NEW_MAUVILLE_ULTRA_BALL":1075,"FLAG_ITEM_OLD_MAGMA_HIDEOUT_B1F_MASTER_BALL":1125,"FLAG_ITEM_OLD_MAGMA_HIDEOUT_B1F_MAX_ELIXIR":1126,"FLAG_ITEM_OLD_MAGMA_HIDEOUT_B2F_NEST_BALL":1127,"FLAG_ITEM_PETALBURG_CITY_ETHER":1040,"FLAG_ITEM_PETALBURG_CITY_MAX_REVIVE":1039,"FLAG_ITEM_PETALBURG_WOODS_ETHER":1058,"FLAG_ITEM_PETALBURG_WOODS_GREAT_BALL":1056,"FLAG_ITEM_PETALBURG_WOODS_PARALYZE_HEAL":1117,"FLAG_ITEM_PETALBURG_WOODS_X_ATTACK":1055,"FLAG_ITEM_ROUTE_102_POTION":1000,"FLAG_ITEM_ROUTE_103_GUARD_SPEC":1114,"FLAG_ITEM_ROUTE_103_PP_UP":1137,"FLAG_ITEM_ROUTE_104_POKE_BALL":1057,"FLAG_ITEM_ROUTE_104_POTION":1135,"FLAG_ITEM_ROUTE_104_PP_UP":1002,"FLAG_ITEM_ROUTE_104_X_ACCURACY":1115,"FLAG_ITEM_ROUTE_105_IRON":1003,"FLAG_ITEM_ROUTE_106_PROTEIN":1004,"FLAG_ITEM_ROUTE_108_STAR_PIECE":1139,"FLAG_ITEM_ROUTE_109_POTION":1140,"FLAG_ITEM_ROUTE_109_PP_UP":1005,"FLAG_ITEM_ROUTE_110_DIRE_HIT":1007,"FLAG_ITEM_ROUTE_110_ELIXIR":1141,"FLAG_ITEM_ROUTE_110_RARE_CANDY":1006,"FLAG_ITEM_ROUTE_111_ELIXIR":1142,"FLAG_ITEM_ROUTE_111_HP_UP":1010,"FLAG_ITEM_ROUTE_111_STARDUST":1009,"FLAG_ITEM_ROUTE_111_TM_SANDSTORM":1008,"FLAG_ITEM_ROUTE_112_NUGGET":1011,"FLAG_ITEM_ROUTE_113_HYPER_POTION":1143,"FLAG_ITEM_ROUTE_113_MAX_ETHER":1012,"FLAG_ITEM_ROUTE_113_SUPER_REPEL":1013,"FLAG_ITEM_ROUTE_114_ENERGY_POWDER":1160,"FLAG_ITEM_ROUTE_114_PROTEIN":1015,"FLAG_ITEM_ROUTE_114_RARE_CANDY":1014,"FLAG_ITEM_ROUTE_115_GREAT_BALL":1118,"FLAG_ITEM_ROUTE_115_HEAL_POWDER":1144,"FLAG_ITEM_ROUTE_115_IRON":1018,"FLAG_ITEM_ROUTE_115_PP_UP":1161,"FLAG_ITEM_ROUTE_115_SUPER_POTION":1016,"FLAG_ITEM_ROUTE_115_TM_FOCUS_PUNCH":1017,"FLAG_ITEM_ROUTE_116_ETHER":1019,"FLAG_ITEM_ROUTE_116_HP_UP":1021,"FLAG_ITEM_ROUTE_116_POTION":1146,"FLAG_ITEM_ROUTE_116_REPEL":1020,"FLAG_ITEM_ROUTE_116_X_SPECIAL":1001,"FLAG_ITEM_ROUTE_117_GREAT_BALL":1022,"FLAG_ITEM_ROUTE_117_REVIVE":1023,"FLAG_ITEM_ROUTE_118_HYPER_POTION":1121,"FLAG_ITEM_ROUTE_119_ELIXIR_1":1026,"FLAG_ITEM_ROUTE_119_ELIXIR_2":1147,"FLAG_ITEM_ROUTE_119_HYPER_POTION_1":1029,"FLAG_ITEM_ROUTE_119_HYPER_POTION_2":1106,"FLAG_ITEM_ROUTE_119_LEAF_STONE":1027,"FLAG_ITEM_ROUTE_119_NUGGET":1134,"FLAG_ITEM_ROUTE_119_RARE_CANDY":1028,"FLAG_ITEM_ROUTE_119_SUPER_REPEL":1024,"FLAG_ITEM_ROUTE_119_ZINC":1025,"FLAG_ITEM_ROUTE_120_FULL_HEAL":1031,"FLAG_ITEM_ROUTE_120_HYPER_POTION":1107,"FLAG_ITEM_ROUTE_120_NEST_BALL":1108,"FLAG_ITEM_ROUTE_120_NUGGET":1030,"FLAG_ITEM_ROUTE_120_REVIVE":1148,"FLAG_ITEM_ROUTE_121_CARBOS":1103,"FLAG_ITEM_ROUTE_121_REVIVE":1149,"FLAG_ITEM_ROUTE_121_ZINC":1150,"FLAG_ITEM_ROUTE_123_CALCIUM":1032,"FLAG_ITEM_ROUTE_123_ELIXIR":1109,"FLAG_ITEM_ROUTE_123_PP_UP":1152,"FLAG_ITEM_ROUTE_123_REVIVAL_HERB":1153,"FLAG_ITEM_ROUTE_123_ULTRA_BALL":1104,"FLAG_ITEM_ROUTE_124_BLUE_SHARD":1093,"FLAG_ITEM_ROUTE_124_RED_SHARD":1092,"FLAG_ITEM_ROUTE_124_YELLOW_SHARD":1066,"FLAG_ITEM_ROUTE_125_BIG_PEARL":1154,"FLAG_ITEM_ROUTE_126_GREEN_SHARD":1105,"FLAG_ITEM_ROUTE_127_CARBOS":1035,"FLAG_ITEM_ROUTE_127_RARE_CANDY":1155,"FLAG_ITEM_ROUTE_127_ZINC":1034,"FLAG_ITEM_ROUTE_132_PROTEIN":1156,"FLAG_ITEM_ROUTE_132_RARE_CANDY":1036,"FLAG_ITEM_ROUTE_133_BIG_PEARL":1037,"FLAG_ITEM_ROUTE_133_MAX_REVIVE":1157,"FLAG_ITEM_ROUTE_133_STAR_PIECE":1038,"FLAG_ITEM_ROUTE_134_CARBOS":1158,"FLAG_ITEM_ROUTE_134_STAR_PIECE":1159,"FLAG_ITEM_RUSTBORO_CITY_X_DEFEND":1041,"FLAG_ITEM_RUSTURF_TUNNEL_MAX_ETHER":1049,"FLAG_ITEM_RUSTURF_TUNNEL_POKE_BALL":1048,"FLAG_ITEM_SAFARI_ZONE_NORTH_CALCIUM":1119,"FLAG_ITEM_SAFARI_ZONE_NORTH_EAST_NUGGET":1169,"FLAG_ITEM_SAFARI_ZONE_NORTH_WEST_TM_SOLAR_BEAM":1094,"FLAG_ITEM_SAFARI_ZONE_SOUTH_EAST_BIG_PEARL":1170,"FLAG_ITEM_SAFARI_ZONE_SOUTH_WEST_MAX_REVIVE":1131,"FLAG_ITEM_SCORCHED_SLAB_TM_SUNNY_DAY":1079,"FLAG_ITEM_SEAFLOOR_CAVERN_ROOM_9_TM_EARTHQUAKE":1090,"FLAG_ITEM_SHOAL_CAVE_ENTRANCE_BIG_PEARL":1081,"FLAG_ITEM_SHOAL_CAVE_ICE_ROOM_NEVER_MELT_ICE":1113,"FLAG_ITEM_SHOAL_CAVE_ICE_ROOM_TM_HAIL":1112,"FLAG_ITEM_SHOAL_CAVE_INNER_ROOM_RARE_CANDY":1082,"FLAG_ITEM_SHOAL_CAVE_STAIRS_ROOM_ICE_HEAL":1083,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_1_ORANGE_MAIL":1060,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_2_HARBOR_MAIL":1061,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_2_WAVE_MAIL":1062,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_3_SHADOW_MAIL":1063,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_3_WOOD_MAIL":1064,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_4_MECH_MAIL":1065,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_6_GLITTER_MAIL":1067,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_7_TROPIC_MAIL":1068,"FLAG_ITEM_TRICK_HOUSE_PUZZLE_8_BEAD_MAIL":1069,"FLAG_ITEM_VICTORY_ROAD_1F_MAX_ELIXIR":1084,"FLAG_ITEM_VICTORY_ROAD_1F_PP_UP":1085,"FLAG_ITEM_VICTORY_ROAD_B1F_FULL_RESTORE":1087,"FLAG_ITEM_VICTORY_ROAD_B1F_TM_PSYCHIC":1086,"FLAG_ITEM_VICTORY_ROAD_B2F_FULL_HEAL":1088,"FLAG_KECLEON_FLED_FORTREE":295,"FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN":129,"FLAG_KYOGRE_IS_RECOVERING":1273,"FLAG_LANDMARK_ABANDONED_SHIP":2206,"FLAG_LANDMARK_ALTERING_CAVE":2269,"FLAG_LANDMARK_ANCIENT_TOMB":2233,"FLAG_LANDMARK_ARTISAN_CAVE":2271,"FLAG_LANDMARK_BATTLE_FRONTIER":2216,"FLAG_LANDMARK_BERRY_MASTERS_HOUSE":2243,"FLAG_LANDMARK_DESERT_RUINS":2230,"FLAG_LANDMARK_DESERT_UNDERPASS":2270,"FLAG_LANDMARK_FIERY_PATH":2218,"FLAG_LANDMARK_FLOWER_SHOP":2204,"FLAG_LANDMARK_FOSSIL_MANIACS_HOUSE":2231,"FLAG_LANDMARK_GLASS_WORKSHOP":2212,"FLAG_LANDMARK_HUNTERS_HOUSE":2235,"FLAG_LANDMARK_ISLAND_CAVE":2229,"FLAG_LANDMARK_LANETTES_HOUSE":2213,"FLAG_LANDMARK_MIRAGE_TOWER":120,"FLAG_LANDMARK_MR_BRINEY_HOUSE":2205,"FLAG_LANDMARK_NEW_MAUVILLE":2208,"FLAG_LANDMARK_OLD_LADY_REST_SHOP":2209,"FLAG_LANDMARK_POKEMON_DAYCARE":2214,"FLAG_LANDMARK_POKEMON_LEAGUE":2228,"FLAG_LANDMARK_SCORCHED_SLAB":2232,"FLAG_LANDMARK_SEAFLOOR_CAVERN":2215,"FLAG_LANDMARK_SEALED_CHAMBER":2236,"FLAG_LANDMARK_SEASHORE_HOUSE":2207,"FLAG_LANDMARK_SKY_PILLAR":2238,"FLAG_LANDMARK_SOUTHERN_ISLAND":2217,"FLAG_LANDMARK_TRAINER_HILL":2274,"FLAG_LANDMARK_TRICK_HOUSE":2210,"FLAG_LANDMARK_TUNNELERS_REST_HOUSE":2234,"FLAG_LANDMARK_WINSTRATE_FAMILY":2211,"FLAG_LATIAS_IS_RECOVERING":1263,"FLAG_LATIOS_IS_RECOVERING":1255,"FLAG_LATIOS_OR_LATIAS_ROAMING":255,"FLAG_LEGENDARIES_IN_SOOTOPOLIS":83,"FLAG_LILYCOVE_RECEIVED_BERRY":1208,"FLAG_LUGIA_IS_RECOVERING":1257,"FLAG_MAP_SCRIPT_CHECKED_DEOXYS":2259,"FLAG_MATCH_CALL_REGISTERED":348,"FLAG_MAUVILLE_GYM_BARRIERS_STATE":99,"FLAG_MET_ARCHIE_METEOR_FALLS":207,"FLAG_MET_ARCHIE_SOOTOPOLIS":308,"FLAG_MET_BATTLE_FRONTIER_BREEDER":339,"FLAG_MET_BATTLE_FRONTIER_GAMBLER":343,"FLAG_MET_BATTLE_FRONTIER_MANIAC":340,"FLAG_MET_DEVON_EMPLOYEE":287,"FLAG_MET_DIVING_TREASURE_HUNTER":217,"FLAG_MET_FANCLUB_YOUNGER_BROTHER":300,"FLAG_MET_FRONTIER_BEAUTY_MOVE_TUTOR":346,"FLAG_MET_FRONTIER_SWIMMER_MOVE_TUTOR":347,"FLAG_MET_HIDDEN_POWER_GIVER":118,"FLAG_MET_MAXIE_SOOTOPOLIS":309,"FLAG_MET_PRETTY_PETAL_SHOP_OWNER":127,"FLAG_MET_PROF_COZMO":244,"FLAG_MET_RIVAL_IN_HOUSE_AFTER_LILYCOVE":293,"FLAG_MET_RIVAL_LILYCOVE":292,"FLAG_MET_RIVAL_MOM":87,"FLAG_MET_RIVAL_RUSTBORO":288,"FLAG_MET_SCOTT_AFTER_OBTAINING_STONE_BADGE":459,"FLAG_MET_SCOTT_IN_EVERGRANDE":463,"FLAG_MET_SCOTT_IN_FALLARBOR":461,"FLAG_MET_SCOTT_IN_LILYCOVE":462,"FLAG_MET_SCOTT_IN_VERDANTURF":460,"FLAG_MET_SCOTT_ON_SS_TIDAL":464,"FLAG_MET_SCOTT_RUSTBORO":310,"FLAG_MET_SLATEPORT_FANCLUB_CHAIRMAN":342,"FLAG_MET_TEAM_AQUA_HARBOR":97,"FLAG_MET_WAILMER_TRAINER":218,"FLAG_MEW_IS_RECOVERING":1259,"FLAG_MIRAGE_TOWER_VISIBLE":334,"FLAG_MOSSDEEP_GYM_SWITCH_1":100,"FLAG_MOSSDEEP_GYM_SWITCH_2":101,"FLAG_MOSSDEEP_GYM_SWITCH_3":102,"FLAG_MOSSDEEP_GYM_SWITCH_4":103,"FLAG_MOVE_TUTOR_TAUGHT_DOUBLE_EDGE":441,"FLAG_MOVE_TUTOR_TAUGHT_DYNAMICPUNCH":440,"FLAG_MOVE_TUTOR_TAUGHT_EXPLOSION":442,"FLAG_MOVE_TUTOR_TAUGHT_FURY_CUTTER":435,"FLAG_MOVE_TUTOR_TAUGHT_METRONOME":437,"FLAG_MOVE_TUTOR_TAUGHT_MIMIC":436,"FLAG_MOVE_TUTOR_TAUGHT_ROLLOUT":434,"FLAG_MOVE_TUTOR_TAUGHT_SLEEP_TALK":438,"FLAG_MOVE_TUTOR_TAUGHT_SUBSTITUTE":439,"FLAG_MOVE_TUTOR_TAUGHT_SWAGGER":433,"FLAG_MR_BRINEY_SAILING_INTRO":147,"FLAG_MYSTERY_GIFT_1":485,"FLAG_MYSTERY_GIFT_10":494,"FLAG_MYSTERY_GIFT_11":495,"FLAG_MYSTERY_GIFT_12":496,"FLAG_MYSTERY_GIFT_13":497,"FLAG_MYSTERY_GIFT_14":498,"FLAG_MYSTERY_GIFT_15":499,"FLAG_MYSTERY_GIFT_2":486,"FLAG_MYSTERY_GIFT_3":487,"FLAG_MYSTERY_GIFT_4":488,"FLAG_MYSTERY_GIFT_5":489,"FLAG_MYSTERY_GIFT_6":490,"FLAG_MYSTERY_GIFT_7":491,"FLAG_MYSTERY_GIFT_8":492,"FLAG_MYSTERY_GIFT_9":493,"FLAG_MYSTERY_GIFT_DONE":484,"FLAG_NEVER_SET_0x0DC":220,"FLAG_NOT_READY_FOR_BATTLE_ROUTE_120":290,"FLAG_NURSE_MENTIONS_GOLD_CARD":345,"FLAG_NURSE_UNION_ROOM_REMINDER":2176,"FLAG_OCEANIC_MUSEUM_MET_REPORTER":105,"FLAG_OMIT_DIVE_FROM_STEVEN_LETTER":302,"FLAG_PACIFIDLOG_NPC_TRADE_COMPLETED":154,"FLAG_PENDING_DAYCARE_EGG":134,"FLAG_PETALBURG_MART_EXPANDED_ITEMS":296,"FLAG_POKERUS_EXPLAINED":273,"FLAG_PURCHASED_HARBOR_MAIL":104,"FLAG_RAYQUAZA_IS_RECOVERING":1279,"FLAG_RECEIVED_20_COINS":225,"FLAG_RECEIVED_6_SODA_POP":140,"FLAG_RECEIVED_ACRO_BIKE":1181,"FLAG_RECEIVED_AMULET_COIN":133,"FLAG_RECEIVED_AURORA_TICKET":314,"FLAG_RECEIVED_BADGE_1":1182,"FLAG_RECEIVED_BADGE_2":1183,"FLAG_RECEIVED_BADGE_3":1184,"FLAG_RECEIVED_BADGE_4":1185,"FLAG_RECEIVED_BADGE_5":1186,"FLAG_RECEIVED_BADGE_6":1187,"FLAG_RECEIVED_BADGE_7":1188,"FLAG_RECEIVED_BADGE_8":1189,"FLAG_RECEIVED_BELDUM":298,"FLAG_RECEIVED_BELUE_BERRY":252,"FLAG_RECEIVED_BIKE":90,"FLAG_RECEIVED_BLUE_SCARF":201,"FLAG_RECEIVED_CASTFORM":151,"FLAG_RECEIVED_CHARCOAL":254,"FLAG_RECEIVED_CHESTO_BERRY_ROUTE_104":246,"FLAG_RECEIVED_CLEANSE_TAG":282,"FLAG_RECEIVED_COIN_CASE":258,"FLAG_RECEIVED_CONTEST_PASS":150,"FLAG_RECEIVED_DEEP_SEA_SCALE":1190,"FLAG_RECEIVED_DEEP_SEA_TOOTH":1191,"FLAG_RECEIVED_DEVON_GOODS_RUSTURF_TUNNEL":1172,"FLAG_RECEIVED_DEVON_SCOPE":285,"FLAG_RECEIVED_DOLL_LANETTE":131,"FLAG_RECEIVED_DURIN_BERRY":251,"FLAG_RECEIVED_EON_TICKET":474,"FLAG_RECEIVED_EXP_SHARE":272,"FLAG_RECEIVED_FANCLUB_TM_THIS_WEEK":299,"FLAG_RECEIVED_FIRST_POKEBALLS":233,"FLAG_RECEIVED_FOCUS_BAND":283,"FLAG_RECEIVED_GLASS_ORNAMENT":236,"FLAG_RECEIVED_GOLD_SHIELD":238,"FLAG_RECEIVED_GOOD_ROD":227,"FLAG_RECEIVED_GO_GOGGLES":221,"FLAG_RECEIVED_GREAT_BALL_PETALBURG_WOODS":1171,"FLAG_RECEIVED_GREAT_BALL_RUSTBORO_CITY":1173,"FLAG_RECEIVED_GREEN_SCARF":203,"FLAG_RECEIVED_HM_CUT":137,"FLAG_RECEIVED_HM_DIVE":123,"FLAG_RECEIVED_HM_FLASH":109,"FLAG_RECEIVED_HM_FLY":110,"FLAG_RECEIVED_HM_ROCK_SMASH":107,"FLAG_RECEIVED_HM_STRENGTH":106,"FLAG_RECEIVED_HM_SURF":122,"FLAG_RECEIVED_HM_WATERFALL":312,"FLAG_RECEIVED_ITEMFINDER":1176,"FLAG_RECEIVED_KINGS_ROCK":276,"FLAG_RECEIVED_LAVARIDGE_EGG":266,"FLAG_RECEIVED_LETTER":1174,"FLAG_RECEIVED_MACHO_BRACE":277,"FLAG_RECEIVED_MACH_BIKE":1180,"FLAG_RECEIVED_MAGMA_EMBLEM":1177,"FLAG_RECEIVED_MENTAL_HERB":223,"FLAG_RECEIVED_METEORITE":115,"FLAG_RECEIVED_MIRACLE_SEED":297,"FLAG_RECEIVED_MYSTIC_TICKET":315,"FLAG_RECEIVED_OLD_ROD":257,"FLAG_RECEIVED_OLD_SEA_MAP":316,"FLAG_RECEIVED_PAMTRE_BERRY":249,"FLAG_RECEIVED_PINK_SCARF":202,"FLAG_RECEIVED_POKEBLOCK_CASE":95,"FLAG_RECEIVED_POKEDEX_FROM_BIRCH":2276,"FLAG_RECEIVED_POKENAV":188,"FLAG_RECEIVED_POTION_OLDALE":132,"FLAG_RECEIVED_POWDER_JAR":337,"FLAG_RECEIVED_PREMIER_BALL_RUSTBORO":213,"FLAG_RECEIVED_QUICK_CLAW":275,"FLAG_RECEIVED_RED_OR_BLUE_ORB":212,"FLAG_RECEIVED_RED_SCARF":200,"FLAG_RECEIVED_REPEAT_BALL":256,"FLAG_RECEIVED_REVIVED_FOSSIL_MON":267,"FLAG_RECEIVED_RUNNING_SHOES":274,"FLAG_RECEIVED_SECRET_POWER":96,"FLAG_RECEIVED_SHOAL_SALT_1":952,"FLAG_RECEIVED_SHOAL_SALT_2":953,"FLAG_RECEIVED_SHOAL_SALT_3":954,"FLAG_RECEIVED_SHOAL_SALT_4":955,"FLAG_RECEIVED_SHOAL_SHELL_1":956,"FLAG_RECEIVED_SHOAL_SHELL_2":957,"FLAG_RECEIVED_SHOAL_SHELL_3":958,"FLAG_RECEIVED_SHOAL_SHELL_4":959,"FLAG_RECEIVED_SILK_SCARF":289,"FLAG_RECEIVED_SILVER_SHIELD":237,"FLAG_RECEIVED_SOFT_SAND":280,"FLAG_RECEIVED_SOOTHE_BELL":278,"FLAG_RECEIVED_SOOT_SACK":1033,"FLAG_RECEIVED_SPECIAL_PHRASE_HINT":85,"FLAG_RECEIVED_SPELON_BERRY":248,"FLAG_RECEIVED_SS_TICKET":291,"FLAG_RECEIVED_STARTER_DOLL":226,"FLAG_RECEIVED_SUN_STONE_MOSSDEEP":192,"FLAG_RECEIVED_SUPER_ROD":152,"FLAG_RECEIVED_TM_AERIAL_ACE":170,"FLAG_RECEIVED_TM_ATTRACT":235,"FLAG_RECEIVED_TM_BRICK_BREAK":121,"FLAG_RECEIVED_TM_BULK_UP":166,"FLAG_RECEIVED_TM_BULLET_SEED":262,"FLAG_RECEIVED_TM_CALM_MIND":171,"FLAG_RECEIVED_TM_DIG":261,"FLAG_RECEIVED_TM_FACADE":169,"FLAG_RECEIVED_TM_FRUSTRATION":1179,"FLAG_RECEIVED_TM_GIGA_DRAIN":232,"FLAG_RECEIVED_TM_HIDDEN_POWER":264,"FLAG_RECEIVED_TM_OVERHEAT":168,"FLAG_RECEIVED_TM_REST":234,"FLAG_RECEIVED_TM_RETURN":229,"FLAG_RECEIVED_TM_RETURN_2":1178,"FLAG_RECEIVED_TM_ROAR":231,"FLAG_RECEIVED_TM_ROCK_TOMB":165,"FLAG_RECEIVED_TM_SHOCK_WAVE":167,"FLAG_RECEIVED_TM_SLUDGE_BOMB":230,"FLAG_RECEIVED_TM_SNATCH":260,"FLAG_RECEIVED_TM_STEEL_WING":1175,"FLAG_RECEIVED_TM_THIEF":269,"FLAG_RECEIVED_TM_TORMENT":265,"FLAG_RECEIVED_TM_WATER_PULSE":172,"FLAG_RECEIVED_TRICK_HOUSE_REWARD_1":1200,"FLAG_RECEIVED_TRICK_HOUSE_REWARD_2":1201,"FLAG_RECEIVED_TRICK_HOUSE_REWARD_3":1202,"FLAG_RECEIVED_TRICK_HOUSE_REWARD_4":1203,"FLAG_RECEIVED_TRICK_HOUSE_REWARD_5":1204,"FLAG_RECEIVED_TRICK_HOUSE_REWARD_6":1205,"FLAG_RECEIVED_TRICK_HOUSE_REWARD_7":1206,"FLAG_RECEIVED_WAILMER_DOLL":245,"FLAG_RECEIVED_WAILMER_PAIL":94,"FLAG_RECEIVED_WATMEL_BERRY":250,"FLAG_RECEIVED_WHITE_HERB":279,"FLAG_RECEIVED_YELLOW_SCARF":204,"FLAG_RECOVERED_DEVON_GOODS":143,"FLAG_REGICE_IS_RECOVERING":1260,"FLAG_REGIROCK_IS_RECOVERING":1261,"FLAG_REGISTEEL_IS_RECOVERING":1262,"FLAG_REGISTERED_STEVEN_POKENAV":305,"FLAG_REGISTER_RIVAL_POKENAV":124,"FLAG_REGI_DOORS_OPENED":228,"FLAG_REMATCH_ABIGAIL":387,"FLAG_REMATCH_AMY_AND_LIV":399,"FLAG_REMATCH_ANDRES":350,"FLAG_REMATCH_ANNA_AND_MEG":378,"FLAG_REMATCH_BENJAMIN":390,"FLAG_REMATCH_BERNIE":369,"FLAG_REMATCH_BRAWLY":415,"FLAG_REMATCH_BROOKE":356,"FLAG_REMATCH_CALVIN":383,"FLAG_REMATCH_CAMERON":373,"FLAG_REMATCH_CATHERINE":406,"FLAG_REMATCH_CINDY":359,"FLAG_REMATCH_CORY":401,"FLAG_REMATCH_CRISTIN":355,"FLAG_REMATCH_CYNDY":395,"FLAG_REMATCH_DALTON":368,"FLAG_REMATCH_DIANA":398,"FLAG_REMATCH_DRAKE":424,"FLAG_REMATCH_DUSTY":351,"FLAG_REMATCH_DYLAN":388,"FLAG_REMATCH_EDWIN":402,"FLAG_REMATCH_ELLIOT":384,"FLAG_REMATCH_ERNEST":400,"FLAG_REMATCH_ETHAN":370,"FLAG_REMATCH_FERNANDO":367,"FLAG_REMATCH_FLANNERY":417,"FLAG_REMATCH_GABRIELLE":405,"FLAG_REMATCH_GLACIA":423,"FLAG_REMATCH_HALEY":408,"FLAG_REMATCH_ISAAC":404,"FLAG_REMATCH_ISABEL":379,"FLAG_REMATCH_ISAIAH":385,"FLAG_REMATCH_JACKI":374,"FLAG_REMATCH_JACKSON":407,"FLAG_REMATCH_JAMES":409,"FLAG_REMATCH_JEFFREY":372,"FLAG_REMATCH_JENNY":397,"FLAG_REMATCH_JERRY":377,"FLAG_REMATCH_JESSICA":361,"FLAG_REMATCH_JOHN_AND_JAY":371,"FLAG_REMATCH_KAREN":376,"FLAG_REMATCH_KATELYN":389,"FLAG_REMATCH_KIRA_AND_DAN":412,"FLAG_REMATCH_KOJI":366,"FLAG_REMATCH_LAO":394,"FLAG_REMATCH_LILA_AND_ROY":354,"FLAG_REMATCH_LOLA":352,"FLAG_REMATCH_LYDIA":403,"FLAG_REMATCH_MADELINE":396,"FLAG_REMATCH_MARIA":386,"FLAG_REMATCH_MIGUEL":380,"FLAG_REMATCH_NICOLAS":392,"FLAG_REMATCH_NOB":365,"FLAG_REMATCH_NORMAN":418,"FLAG_REMATCH_PABLO":391,"FLAG_REMATCH_PHOEBE":422,"FLAG_REMATCH_RICKY":353,"FLAG_REMATCH_ROBERT":393,"FLAG_REMATCH_ROSE":349,"FLAG_REMATCH_ROXANNE":414,"FLAG_REMATCH_SAWYER":411,"FLAG_REMATCH_SHELBY":382,"FLAG_REMATCH_SIDNEY":421,"FLAG_REMATCH_STEVE":363,"FLAG_REMATCH_TATE_AND_LIZA":420,"FLAG_REMATCH_THALIA":360,"FLAG_REMATCH_TIMOTHY":381,"FLAG_REMATCH_TONY":364,"FLAG_REMATCH_TRENT":410,"FLAG_REMATCH_VALERIE":358,"FLAG_REMATCH_WALLACE":425,"FLAG_REMATCH_WALLY":413,"FLAG_REMATCH_WALTER":375,"FLAG_REMATCH_WATTSON":416,"FLAG_REMATCH_WILTON":357,"FLAG_REMATCH_WINONA":419,"FLAG_REMATCH_WINSTON":362,"FLAG_RESCUED_BIRCH":82,"FLAG_RETURNED_DEVON_GOODS":144,"FLAG_RETURNED_RED_OR_BLUE_ORB":259,"FLAG_RIVAL_LEFT_FOR_ROUTE103":301,"FLAG_ROUTE_111_RECEIVED_BERRY":1192,"FLAG_ROUTE_114_RECEIVED_BERRY":1193,"FLAG_ROUTE_120_RECEIVED_BERRY":1194,"FLAG_RUSTBORO_NPC_TRADE_COMPLETED":153,"FLAG_RUSTURF_TUNNEL_OPENED":199,"FLAG_SCOTT_CALL_BATTLE_FRONTIER":114,"FLAG_SCOTT_CALL_FORTREE_GYM":138,"FLAG_SCOTT_GIVES_BATTLE_POINTS":465,"FLAG_SECRET_BASE_REGISTRY_ENABLED":268,"FLAG_SET_WALL_CLOCK":81,"FLAG_SHOWN_AURORA_TICKET":431,"FLAG_SHOWN_BOX_WAS_FULL_MESSAGE":2263,"FLAG_SHOWN_EON_TICKET":430,"FLAG_SHOWN_MYSTIC_TICKET":475,"FLAG_SHOWN_OLD_SEA_MAP":432,"FLAG_SMART_PAINTING_MADE":163,"FLAG_SOOTOPOLIS_ARCHIE_MAXIE_LEAVE":158,"FLAG_SOOTOPOLIS_RECEIVED_BERRY_1":1198,"FLAG_SOOTOPOLIS_RECEIVED_BERRY_2":1199,"FLAG_SPECIAL_FLAG_UNUSED_0x4003":16387,"FLAG_SS_TIDAL_DISABLED":84,"FLAG_STEVEN_GUIDES_TO_CAVE_OF_ORIGIN":307,"FLAG_STORING_ITEMS_IN_PYRAMID_BAG":16388,"FLAG_SYS_ARENA_GOLD":2251,"FLAG_SYS_ARENA_SILVER":2250,"FLAG_SYS_BRAILLE_DIG":2223,"FLAG_SYS_BRAILLE_REGICE_COMPLETED":2225,"FLAG_SYS_B_DASH":2240,"FLAG_SYS_CAVE_BATTLE":2201,"FLAG_SYS_CAVE_SHIP":2199,"FLAG_SYS_CAVE_WONDER":2200,"FLAG_SYS_CHANGED_DEWFORD_TREND":2195,"FLAG_SYS_CHAT_USED":2149,"FLAG_SYS_CLOCK_SET":2197,"FLAG_SYS_CRUISE_MODE":2189,"FLAG_SYS_CTRL_OBJ_DELETE":2241,"FLAG_SYS_CYCLING_ROAD":2187,"FLAG_SYS_DOME_GOLD":2247,"FLAG_SYS_DOME_SILVER":2246,"FLAG_SYS_ENC_DOWN_ITEM":2222,"FLAG_SYS_ENC_UP_ITEM":2221,"FLAG_SYS_FACTORY_GOLD":2253,"FLAG_SYS_FACTORY_SILVER":2252,"FLAG_SYS_FRONTIER_PASS":2258,"FLAG_SYS_GAME_CLEAR":2148,"FLAG_SYS_MIX_RECORD":2196,"FLAG_SYS_MYSTERY_EVENT_ENABLE":2220,"FLAG_SYS_MYSTERY_GIFT_ENABLE":2267,"FLAG_SYS_NATIONAL_DEX":2198,"FLAG_SYS_PALACE_GOLD":2249,"FLAG_SYS_PALACE_SILVER":2248,"FLAG_SYS_PC_LANETTE":2219,"FLAG_SYS_PIKE_GOLD":2255,"FLAG_SYS_PIKE_SILVER":2254,"FLAG_SYS_POKEDEX_GET":2145,"FLAG_SYS_POKEMON_GET":2144,"FLAG_SYS_POKENAV_GET":2146,"FLAG_SYS_PYRAMID_GOLD":2257,"FLAG_SYS_PYRAMID_SILVER":2256,"FLAG_SYS_REGIROCK_PUZZLE_COMPLETED":2224,"FLAG_SYS_REGISTEEL_PUZZLE_COMPLETED":2226,"FLAG_SYS_RESET_RTC_ENABLE":2242,"FLAG_SYS_RIBBON_GET":2203,"FLAG_SYS_SAFARI_MODE":2188,"FLAG_SYS_SHOAL_ITEM":2239,"FLAG_SYS_SHOAL_TIDE":2202,"FLAG_SYS_TOWER_GOLD":2245,"FLAG_SYS_TOWER_SILVER":2244,"FLAG_SYS_TV_HOME":2192,"FLAG_SYS_TV_LATIAS_LATIOS":2237,"FLAG_SYS_TV_START":2194,"FLAG_SYS_TV_WATCH":2193,"FLAG_SYS_USE_FLASH":2184,"FLAG_SYS_USE_STRENGTH":2185,"FLAG_SYS_WEATHER_CTRL":2186,"FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE":112,"FLAG_TEMP_1":1,"FLAG_TEMP_10":16,"FLAG_TEMP_11":17,"FLAG_TEMP_12":18,"FLAG_TEMP_13":19,"FLAG_TEMP_14":20,"FLAG_TEMP_15":21,"FLAG_TEMP_16":22,"FLAG_TEMP_17":23,"FLAG_TEMP_18":24,"FLAG_TEMP_19":25,"FLAG_TEMP_1A":26,"FLAG_TEMP_1B":27,"FLAG_TEMP_1C":28,"FLAG_TEMP_1D":29,"FLAG_TEMP_1E":30,"FLAG_TEMP_1F":31,"FLAG_TEMP_2":2,"FLAG_TEMP_3":3,"FLAG_TEMP_4":4,"FLAG_TEMP_5":5,"FLAG_TEMP_6":6,"FLAG_TEMP_7":7,"FLAG_TEMP_8":8,"FLAG_TEMP_9":9,"FLAG_TEMP_A":10,"FLAG_TEMP_B":11,"FLAG_TEMP_C":12,"FLAG_TEMP_D":13,"FLAG_TEMP_E":14,"FLAG_TEMP_F":15,"FLAG_TEMP_HIDE_MIRAGE_ISLAND_BERRY_TREE":17,"FLAG_TEMP_REGICE_PUZZLE_FAILED":3,"FLAG_TEMP_REGICE_PUZZLE_STARTED":2,"FLAG_TEMP_SKIP_GABBY_INTERVIEW":1,"FLAG_THANKED_FOR_PLAYING_WITH_WALLY":135,"FLAG_TOUGH_PAINTING_MADE":164,"FLAG_TRICK_HOUSE_PUZZLE_7_SWITCH_1":194,"FLAG_TRICK_HOUSE_PUZZLE_7_SWITCH_2":195,"FLAG_TRICK_HOUSE_PUZZLE_7_SWITCH_3":196,"FLAG_TRICK_HOUSE_PUZZLE_7_SWITCH_4":197,"FLAG_TRICK_HOUSE_PUZZLE_7_SWITCH_5":198,"FLAG_TV_EXPLAINED":98,"FLAG_UNLOCKED_TRENDY_SAYINGS":2150,"FLAG_USED_ROOM_1_KEY":240,"FLAG_USED_ROOM_2_KEY":241,"FLAG_USED_ROOM_4_KEY":242,"FLAG_USED_ROOM_6_KEY":243,"FLAG_USED_STORAGE_KEY":239,"FLAG_VISITED_DEWFORD_TOWN":2161,"FLAG_VISITED_EVER_GRANDE_CITY":2174,"FLAG_VISITED_FALLARBOR_TOWN":2163,"FLAG_VISITED_FORTREE_CITY":2170,"FLAG_VISITED_LAVARIDGE_TOWN":2162,"FLAG_VISITED_LILYCOVE_CITY":2171,"FLAG_VISITED_LITTLEROOT_TOWN":2159,"FLAG_VISITED_MAUVILLE_CITY":2168,"FLAG_VISITED_MOSSDEEP_CITY":2172,"FLAG_VISITED_OLDALE_TOWN":2160,"FLAG_VISITED_PACIFIDLOG_TOWN":2165,"FLAG_VISITED_PETALBURG_CITY":2166,"FLAG_VISITED_RUSTBORO_CITY":2169,"FLAG_VISITED_SLATEPORT_CITY":2167,"FLAG_VISITED_SOOTOPOLIS_CITY":2173,"FLAG_VISITED_VERDANTURF_TOWN":2164,"FLAG_WALLACE_GOES_TO_SKY_PILLAR":311,"FLAG_WALLY_SPEECH":193,"FLAG_WATTSON_REMATCH_AVAILABLE":91,"FLAG_WHITEOUT_TO_LAVARIDGE":108,"FLAG_WINGULL_DELIVERED_MAIL":224,"FLAG_WINGULL_SENT_ON_ERRAND":222,"FLAG_WONDER_CARD_UNUSED_1":317,"FLAG_WONDER_CARD_UNUSED_10":326,"FLAG_WONDER_CARD_UNUSED_11":327,"FLAG_WONDER_CARD_UNUSED_12":328,"FLAG_WONDER_CARD_UNUSED_13":329,"FLAG_WONDER_CARD_UNUSED_14":330,"FLAG_WONDER_CARD_UNUSED_15":331,"FLAG_WONDER_CARD_UNUSED_16":332,"FLAG_WONDER_CARD_UNUSED_17":333,"FLAG_WONDER_CARD_UNUSED_2":318,"FLAG_WONDER_CARD_UNUSED_3":319,"FLAG_WONDER_CARD_UNUSED_4":320,"FLAG_WONDER_CARD_UNUSED_5":321,"FLAG_WONDER_CARD_UNUSED_6":322,"FLAG_WONDER_CARD_UNUSED_7":323,"FLAG_WONDER_CARD_UNUSED_8":324,"FLAG_WONDER_CARD_UNUSED_9":325,"FLAVOR_BITTER":3,"FLAVOR_COUNT":5,"FLAVOR_DRY":1,"FLAVOR_SOUR":4,"FLAVOR_SPICY":0,"FLAVOR_SWEET":2,"GOOD_ROD":1,"ITEMS_COUNT":377,"ITEM_034":52,"ITEM_035":53,"ITEM_036":54,"ITEM_037":55,"ITEM_038":56,"ITEM_039":57,"ITEM_03A":58,"ITEM_03B":59,"ITEM_03C":60,"ITEM_03D":61,"ITEM_03E":62,"ITEM_048":72,"ITEM_052":82,"ITEM_057":87,"ITEM_058":88,"ITEM_059":89,"ITEM_05A":90,"ITEM_05B":91,"ITEM_05C":92,"ITEM_063":99,"ITEM_064":100,"ITEM_065":101,"ITEM_066":102,"ITEM_069":105,"ITEM_071":113,"ITEM_072":114,"ITEM_073":115,"ITEM_074":116,"ITEM_075":117,"ITEM_076":118,"ITEM_077":119,"ITEM_078":120,"ITEM_0EA":234,"ITEM_0EB":235,"ITEM_0EC":236,"ITEM_0ED":237,"ITEM_0EE":238,"ITEM_0EF":239,"ITEM_0F0":240,"ITEM_0F1":241,"ITEM_0F2":242,"ITEM_0F3":243,"ITEM_0F4":244,"ITEM_0F5":245,"ITEM_0F6":246,"ITEM_0F7":247,"ITEM_0F8":248,"ITEM_0F9":249,"ITEM_0FA":250,"ITEM_0FB":251,"ITEM_0FC":252,"ITEM_0FD":253,"ITEM_10B":267,"ITEM_15B":347,"ITEM_15C":348,"ITEM_ACRO_BIKE":272,"ITEM_AGUAV_BERRY":146,"ITEM_AMULET_COIN":189,"ITEM_ANTIDOTE":14,"ITEM_APICOT_BERRY":172,"ITEM_ARCHIPELAGO_PROGRESSION":112,"ITEM_ASPEAR_BERRY":137,"ITEM_AURORA_TICKET":371,"ITEM_AWAKENING":17,"ITEM_BADGE_1":226,"ITEM_BADGE_2":227,"ITEM_BADGE_3":228,"ITEM_BADGE_4":229,"ITEM_BADGE_5":230,"ITEM_BADGE_6":231,"ITEM_BADGE_7":232,"ITEM_BADGE_8":233,"ITEM_BASEMENT_KEY":271,"ITEM_BEAD_MAIL":127,"ITEM_BELUE_BERRY":167,"ITEM_BERRY_JUICE":44,"ITEM_BERRY_POUCH":365,"ITEM_BICYCLE":360,"ITEM_BIG_MUSHROOM":104,"ITEM_BIG_PEARL":107,"ITEM_BIKE_VOUCHER":352,"ITEM_BLACK_BELT":207,"ITEM_BLACK_FLUTE":42,"ITEM_BLACK_GLASSES":206,"ITEM_BLUE_FLUTE":39,"ITEM_BLUE_ORB":277,"ITEM_BLUE_SCARF":255,"ITEM_BLUE_SHARD":49,"ITEM_BLUK_BERRY":149,"ITEM_BRIGHT_POWDER":179,"ITEM_BURN_HEAL":15,"ITEM_B_USE_MEDICINE":1,"ITEM_B_USE_OTHER":2,"ITEM_CALCIUM":67,"ITEM_CARBOS":66,"ITEM_CARD_KEY":355,"ITEM_CHARCOAL":215,"ITEM_CHERI_BERRY":133,"ITEM_CHESTO_BERRY":134,"ITEM_CHOICE_BAND":186,"ITEM_CLAW_FOSSIL":287,"ITEM_CLEANSE_TAG":190,"ITEM_COIN_CASE":260,"ITEM_CONTEST_PASS":266,"ITEM_CORNN_BERRY":159,"ITEM_DEEP_SEA_SCALE":193,"ITEM_DEEP_SEA_TOOTH":192,"ITEM_DEVON_GOODS":269,"ITEM_DEVON_SCOPE":288,"ITEM_DIRE_HIT":74,"ITEM_DIVE_BALL":7,"ITEM_DOME_FOSSIL":358,"ITEM_DRAGON_FANG":216,"ITEM_DRAGON_SCALE":201,"ITEM_DREAM_MAIL":130,"ITEM_DURIN_BERRY":166,"ITEM_ELIXIR":36,"ITEM_ENERGY_POWDER":30,"ITEM_ENERGY_ROOT":31,"ITEM_ENIGMA_BERRY":175,"ITEM_EON_TICKET":275,"ITEM_ESCAPE_ROPE":85,"ITEM_ETHER":34,"ITEM_EVERSTONE":195,"ITEM_EXP_SHARE":182,"ITEM_FAB_MAIL":131,"ITEM_FAME_CHECKER":363,"ITEM_FIGY_BERRY":143,"ITEM_FIRE_STONE":95,"ITEM_FLUFFY_TAIL":81,"ITEM_FOCUS_BAND":196,"ITEM_FRESH_WATER":26,"ITEM_FULL_HEAL":23,"ITEM_FULL_RESTORE":19,"ITEM_GANLON_BERRY":169,"ITEM_GLITTER_MAIL":123,"ITEM_GOLD_TEETH":353,"ITEM_GOOD_ROD":263,"ITEM_GO_GOGGLES":279,"ITEM_GREAT_BALL":3,"ITEM_GREEN_SCARF":257,"ITEM_GREEN_SHARD":51,"ITEM_GREPA_BERRY":157,"ITEM_GUARD_SPEC":73,"ITEM_HARBOR_MAIL":122,"ITEM_HARD_STONE":204,"ITEM_HEAL_POWDER":32,"ITEM_HEART_SCALE":111,"ITEM_HELIX_FOSSIL":357,"ITEM_HM01":339,"ITEM_HM02":340,"ITEM_HM03":341,"ITEM_HM04":342,"ITEM_HM05":343,"ITEM_HM06":344,"ITEM_HM07":345,"ITEM_HM08":346,"ITEM_HM_CUT":339,"ITEM_HM_DIVE":346,"ITEM_HM_FLASH":343,"ITEM_HM_FLY":340,"ITEM_HM_ROCK_SMASH":344,"ITEM_HM_STRENGTH":342,"ITEM_HM_SURF":341,"ITEM_HM_WATERFALL":345,"ITEM_HONDEW_BERRY":156,"ITEM_HP_UP":63,"ITEM_HYPER_POTION":21,"ITEM_IAPAPA_BERRY":147,"ITEM_ICE_HEAL":16,"ITEM_IRON":65,"ITEM_ITEMFINDER":261,"ITEM_KELPSY_BERRY":154,"ITEM_KINGS_ROCK":187,"ITEM_LANSAT_BERRY":173,"ITEM_LAVA_COOKIE":38,"ITEM_LAX_INCENSE":221,"ITEM_LEAF_STONE":98,"ITEM_LEFTOVERS":200,"ITEM_LEMONADE":28,"ITEM_LEPPA_BERRY":138,"ITEM_LETTER":274,"ITEM_LIECHI_BERRY":168,"ITEM_LIFT_KEY":356,"ITEM_LIGHT_BALL":202,"ITEM_LIST_END":65535,"ITEM_LUCKY_EGG":197,"ITEM_LUCKY_PUNCH":222,"ITEM_LUM_BERRY":141,"ITEM_LUXURY_BALL":11,"ITEM_MACHO_BRACE":181,"ITEM_MACH_BIKE":259,"ITEM_MAGMA_EMBLEM":375,"ITEM_MAGNET":208,"ITEM_MAGOST_BERRY":160,"ITEM_MAGO_BERRY":145,"ITEM_MASTER_BALL":1,"ITEM_MAX_ELIXIR":37,"ITEM_MAX_ETHER":35,"ITEM_MAX_POTION":20,"ITEM_MAX_REPEL":84,"ITEM_MAX_REVIVE":25,"ITEM_MECH_MAIL":124,"ITEM_MENTAL_HERB":185,"ITEM_METAL_COAT":199,"ITEM_METAL_POWDER":223,"ITEM_METEORITE":280,"ITEM_MIRACLE_SEED":205,"ITEM_MOOMOO_MILK":29,"ITEM_MOON_STONE":94,"ITEM_MYSTIC_TICKET":370,"ITEM_MYSTIC_WATER":209,"ITEM_NANAB_BERRY":150,"ITEM_NEST_BALL":8,"ITEM_NET_BALL":6,"ITEM_NEVER_MELT_ICE":212,"ITEM_NOMEL_BERRY":162,"ITEM_NONE":0,"ITEM_NUGGET":110,"ITEM_OAKS_PARCEL":349,"ITEM_OLD_AMBER":354,"ITEM_OLD_ROD":262,"ITEM_OLD_SEA_MAP":376,"ITEM_ORANGE_MAIL":121,"ITEM_ORAN_BERRY":139,"ITEM_PAMTRE_BERRY":164,"ITEM_PARALYZE_HEAL":18,"ITEM_PEARL":106,"ITEM_PECHA_BERRY":135,"ITEM_PERSIM_BERRY":140,"ITEM_PETAYA_BERRY":171,"ITEM_PINAP_BERRY":152,"ITEM_PINK_SCARF":256,"ITEM_POISON_BARB":211,"ITEM_POKEBLOCK_CASE":273,"ITEM_POKE_BALL":4,"ITEM_POKE_DOLL":80,"ITEM_POKE_FLUTE":350,"ITEM_POMEG_BERRY":153,"ITEM_POTION":13,"ITEM_POWDER_JAR":372,"ITEM_PP_MAX":71,"ITEM_PP_UP":69,"ITEM_PREMIER_BALL":12,"ITEM_PROTEIN":64,"ITEM_QUALOT_BERRY":155,"ITEM_QUICK_CLAW":183,"ITEM_RABUTA_BERRY":161,"ITEM_RAINBOW_PASS":368,"ITEM_RARE_CANDY":68,"ITEM_RAWST_BERRY":136,"ITEM_RAZZ_BERRY":148,"ITEM_RED_FLUTE":41,"ITEM_RED_ORB":276,"ITEM_RED_SCARF":254,"ITEM_RED_SHARD":48,"ITEM_REPEAT_BALL":9,"ITEM_REPEL":86,"ITEM_RETRO_MAIL":132,"ITEM_REVIVAL_HERB":33,"ITEM_REVIVE":24,"ITEM_ROOM_1_KEY":281,"ITEM_ROOM_2_KEY":282,"ITEM_ROOM_4_KEY":283,"ITEM_ROOM_6_KEY":284,"ITEM_ROOT_FOSSIL":286,"ITEM_RUBY":373,"ITEM_SACRED_ASH":45,"ITEM_SAFARI_BALL":5,"ITEM_SALAC_BERRY":170,"ITEM_SAPPHIRE":374,"ITEM_SCANNER":278,"ITEM_SCOPE_LENS":198,"ITEM_SEA_INCENSE":220,"ITEM_SECRET_KEY":351,"ITEM_SHADOW_MAIL":128,"ITEM_SHARP_BEAK":210,"ITEM_SHELL_BELL":219,"ITEM_SHOAL_SALT":46,"ITEM_SHOAL_SHELL":47,"ITEM_SILK_SCARF":217,"ITEM_SILPH_SCOPE":359,"ITEM_SILVER_POWDER":188,"ITEM_SITRUS_BERRY":142,"ITEM_SMOKE_BALL":194,"ITEM_SODA_POP":27,"ITEM_SOFT_SAND":203,"ITEM_SOOTHE_BELL":184,"ITEM_SOOT_SACK":270,"ITEM_SOUL_DEW":191,"ITEM_SPELL_TAG":213,"ITEM_SPELON_BERRY":163,"ITEM_SS_TICKET":265,"ITEM_STARDUST":108,"ITEM_STARF_BERRY":174,"ITEM_STAR_PIECE":109,"ITEM_STICK":225,"ITEM_STORAGE_KEY":285,"ITEM_SUN_STONE":93,"ITEM_SUPER_POTION":22,"ITEM_SUPER_REPEL":83,"ITEM_SUPER_ROD":264,"ITEM_TAMATO_BERRY":158,"ITEM_TEA":369,"ITEM_TEACHY_TV":366,"ITEM_THICK_CLUB":224,"ITEM_THUNDER_STONE":96,"ITEM_TIMER_BALL":10,"ITEM_TINY_MUSHROOM":103,"ITEM_TM01":289,"ITEM_TM02":290,"ITEM_TM03":291,"ITEM_TM04":292,"ITEM_TM05":293,"ITEM_TM06":294,"ITEM_TM07":295,"ITEM_TM08":296,"ITEM_TM09":297,"ITEM_TM10":298,"ITEM_TM11":299,"ITEM_TM12":300,"ITEM_TM13":301,"ITEM_TM14":302,"ITEM_TM15":303,"ITEM_TM16":304,"ITEM_TM17":305,"ITEM_TM18":306,"ITEM_TM19":307,"ITEM_TM20":308,"ITEM_TM21":309,"ITEM_TM22":310,"ITEM_TM23":311,"ITEM_TM24":312,"ITEM_TM25":313,"ITEM_TM26":314,"ITEM_TM27":315,"ITEM_TM28":316,"ITEM_TM29":317,"ITEM_TM30":318,"ITEM_TM31":319,"ITEM_TM32":320,"ITEM_TM33":321,"ITEM_TM34":322,"ITEM_TM35":323,"ITEM_TM36":324,"ITEM_TM37":325,"ITEM_TM38":326,"ITEM_TM39":327,"ITEM_TM40":328,"ITEM_TM41":329,"ITEM_TM42":330,"ITEM_TM43":331,"ITEM_TM44":332,"ITEM_TM45":333,"ITEM_TM46":334,"ITEM_TM47":335,"ITEM_TM48":336,"ITEM_TM49":337,"ITEM_TM50":338,"ITEM_TM_AERIAL_ACE":328,"ITEM_TM_ATTRACT":333,"ITEM_TM_BLIZZARD":302,"ITEM_TM_BRICK_BREAK":319,"ITEM_TM_BULK_UP":296,"ITEM_TM_BULLET_SEED":297,"ITEM_TM_CALM_MIND":292,"ITEM_TM_CASE":364,"ITEM_TM_DIG":316,"ITEM_TM_DOUBLE_TEAM":320,"ITEM_TM_DRAGON_CLAW":290,"ITEM_TM_EARTHQUAKE":314,"ITEM_TM_FACADE":330,"ITEM_TM_FIRE_BLAST":326,"ITEM_TM_FLAMETHROWER":323,"ITEM_TM_FOCUS_PUNCH":289,"ITEM_TM_FRUSTRATION":309,"ITEM_TM_GIGA_DRAIN":307,"ITEM_TM_HAIL":295,"ITEM_TM_HIDDEN_POWER":298,"ITEM_TM_HYPER_BEAM":303,"ITEM_TM_ICE_BEAM":301,"ITEM_TM_IRON_TAIL":311,"ITEM_TM_LIGHT_SCREEN":304,"ITEM_TM_OVERHEAT":338,"ITEM_TM_PROTECT":305,"ITEM_TM_PSYCHIC":317,"ITEM_TM_RAIN_DANCE":306,"ITEM_TM_REFLECT":321,"ITEM_TM_REST":332,"ITEM_TM_RETURN":315,"ITEM_TM_ROAR":293,"ITEM_TM_ROCK_TOMB":327,"ITEM_TM_SAFEGUARD":308,"ITEM_TM_SANDSTORM":325,"ITEM_TM_SECRET_POWER":331,"ITEM_TM_SHADOW_BALL":318,"ITEM_TM_SHOCK_WAVE":322,"ITEM_TM_SKILL_SWAP":336,"ITEM_TM_SLUDGE_BOMB":324,"ITEM_TM_SNATCH":337,"ITEM_TM_SOLAR_BEAM":310,"ITEM_TM_STEEL_WING":335,"ITEM_TM_SUNNY_DAY":299,"ITEM_TM_TAUNT":300,"ITEM_TM_THIEF":334,"ITEM_TM_THUNDER":313,"ITEM_TM_THUNDERBOLT":312,"ITEM_TM_TORMENT":329,"ITEM_TM_TOXIC":294,"ITEM_TM_WATER_PULSE":291,"ITEM_TOWN_MAP":361,"ITEM_TRI_PASS":367,"ITEM_TROPIC_MAIL":129,"ITEM_TWISTED_SPOON":214,"ITEM_ULTRA_BALL":2,"ITEM_UNUSED_BERRY_1":176,"ITEM_UNUSED_BERRY_2":177,"ITEM_UNUSED_BERRY_3":178,"ITEM_UP_GRADE":218,"ITEM_USE_BAG_MENU":4,"ITEM_USE_FIELD":2,"ITEM_USE_MAIL":0,"ITEM_USE_PARTY_MENU":1,"ITEM_USE_PBLOCK_CASE":3,"ITEM_VS_SEEKER":362,"ITEM_WAILMER_PAIL":268,"ITEM_WATER_STONE":97,"ITEM_WATMEL_BERRY":165,"ITEM_WAVE_MAIL":126,"ITEM_WEPEAR_BERRY":151,"ITEM_WHITE_FLUTE":43,"ITEM_WHITE_HERB":180,"ITEM_WIKI_BERRY":144,"ITEM_WOOD_MAIL":125,"ITEM_X_ACCURACY":78,"ITEM_X_ATTACK":75,"ITEM_X_DEFEND":76,"ITEM_X_SPECIAL":79,"ITEM_X_SPEED":77,"ITEM_YELLOW_FLUTE":40,"ITEM_YELLOW_SCARF":258,"ITEM_YELLOW_SHARD":50,"ITEM_ZINC":70,"LAST_BALL":12,"LAST_BERRY_INDEX":175,"LAST_BERRY_MASTER_BERRY":162,"LAST_BERRY_MASTER_WIFE_BERRY":142,"LAST_KIRI_BERRY":162,"LAST_ROUTE_114_MAN_BERRY":152,"MACH_BIKE":0,"MAIL_NONE":255,"MAP_ABANDONED_SHIP_CAPTAINS_OFFICE":6207,"MAP_ABANDONED_SHIP_CORRIDORS_1F":6199,"MAP_ABANDONED_SHIP_CORRIDORS_B1F":6201,"MAP_ABANDONED_SHIP_DECK":6198,"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS":6209,"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS":6210,"MAP_ABANDONED_SHIP_ROOMS2_1F":6206,"MAP_ABANDONED_SHIP_ROOMS2_B1F":6203,"MAP_ABANDONED_SHIP_ROOMS_1F":6200,"MAP_ABANDONED_SHIP_ROOMS_B1F":6202,"MAP_ABANDONED_SHIP_ROOM_B1F":6205,"MAP_ABANDONED_SHIP_UNDERWATER1":6204,"MAP_ABANDONED_SHIP_UNDERWATER2":6208,"MAP_ALTERING_CAVE":6250,"MAP_ANCIENT_TOMB":6212,"MAP_AQUA_HIDEOUT_1F":6167,"MAP_AQUA_HIDEOUT_B1F":6168,"MAP_AQUA_HIDEOUT_B2F":6169,"MAP_AQUA_HIDEOUT_UNUSED_RUBY_MAP1":6218,"MAP_AQUA_HIDEOUT_UNUSED_RUBY_MAP2":6219,"MAP_AQUA_HIDEOUT_UNUSED_RUBY_MAP3":6220,"MAP_ARTISAN_CAVE_1F":6244,"MAP_ARTISAN_CAVE_B1F":6243,"MAP_BATTLE_COLOSSEUM_2P":6424,"MAP_BATTLE_COLOSSEUM_4P":6427,"MAP_BATTLE_FRONTIER_BATTLE_ARENA_BATTLE_ROOM":6686,"MAP_BATTLE_FRONTIER_BATTLE_ARENA_CORRIDOR":6685,"MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY":6684,"MAP_BATTLE_FRONTIER_BATTLE_DOME_BATTLE_ROOM":6677,"MAP_BATTLE_FRONTIER_BATTLE_DOME_CORRIDOR":6675,"MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY":6674,"MAP_BATTLE_FRONTIER_BATTLE_DOME_PRE_BATTLE_ROOM":6676,"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_BATTLE_ROOM":6689,"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY":6687,"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_PRE_BATTLE_ROOM":6688,"MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM":6680,"MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR":6679,"MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY":6678,"MAP_BATTLE_FRONTIER_BATTLE_PIKE_CORRIDOR":6691,"MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY":6690,"MAP_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_FINAL":6694,"MAP_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_NORMAL":6693,"MAP_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS":6695,"MAP_BATTLE_FRONTIER_BATTLE_PIKE_THREE_PATH_ROOM":6692,"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR":6682,"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY":6681,"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_TOP":6683,"MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM":6664,"MAP_BATTLE_FRONTIER_BATTLE_TOWER_CORRIDOR":6663,"MAP_BATTLE_FRONTIER_BATTLE_TOWER_ELEVATOR":6662,"MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY":6661,"MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_BATTLE_ROOM":6673,"MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_CORRIDOR":6672,"MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_PARTNER_ROOM":6671,"MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER":6698,"MAP_BATTLE_FRONTIER_LOUNGE1":6697,"MAP_BATTLE_FRONTIER_LOUNGE2":6699,"MAP_BATTLE_FRONTIER_LOUNGE3":6700,"MAP_BATTLE_FRONTIER_LOUNGE4":6701,"MAP_BATTLE_FRONTIER_LOUNGE5":6703,"MAP_BATTLE_FRONTIER_LOUNGE6":6704,"MAP_BATTLE_FRONTIER_LOUNGE7":6705,"MAP_BATTLE_FRONTIER_LOUNGE8":6707,"MAP_BATTLE_FRONTIER_LOUNGE9":6708,"MAP_BATTLE_FRONTIER_MART":6711,"MAP_BATTLE_FRONTIER_OUTSIDE_EAST":6670,"MAP_BATTLE_FRONTIER_OUTSIDE_WEST":6660,"MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F":6709,"MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F":6710,"MAP_BATTLE_FRONTIER_RANKING_HALL":6696,"MAP_BATTLE_FRONTIER_RECEPTION_GATE":6706,"MAP_BATTLE_FRONTIER_SCOTTS_HOUSE":6702,"MAP_BATTLE_PYRAMID_SQUARE01":6444,"MAP_BATTLE_PYRAMID_SQUARE02":6445,"MAP_BATTLE_PYRAMID_SQUARE03":6446,"MAP_BATTLE_PYRAMID_SQUARE04":6447,"MAP_BATTLE_PYRAMID_SQUARE05":6448,"MAP_BATTLE_PYRAMID_SQUARE06":6449,"MAP_BATTLE_PYRAMID_SQUARE07":6450,"MAP_BATTLE_PYRAMID_SQUARE08":6451,"MAP_BATTLE_PYRAMID_SQUARE09":6452,"MAP_BATTLE_PYRAMID_SQUARE10":6453,"MAP_BATTLE_PYRAMID_SQUARE11":6454,"MAP_BATTLE_PYRAMID_SQUARE12":6455,"MAP_BATTLE_PYRAMID_SQUARE13":6456,"MAP_BATTLE_PYRAMID_SQUARE14":6457,"MAP_BATTLE_PYRAMID_SQUARE15":6458,"MAP_BATTLE_PYRAMID_SQUARE16":6459,"MAP_BIRTH_ISLAND_EXTERIOR":6714,"MAP_BIRTH_ISLAND_HARBOR":6715,"MAP_CAVE_OF_ORIGIN_1F":6182,"MAP_CAVE_OF_ORIGIN_B1F":6186,"MAP_CAVE_OF_ORIGIN_ENTRANCE":6181,"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1":6183,"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2":6184,"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3":6185,"MAP_CONTEST_HALL":6428,"MAP_CONTEST_HALL_BEAUTY":6435,"MAP_CONTEST_HALL_COOL":6437,"MAP_CONTEST_HALL_CUTE":6439,"MAP_CONTEST_HALL_SMART":6438,"MAP_CONTEST_HALL_TOUGH":6436,"MAP_DESERT_RUINS":6150,"MAP_DESERT_UNDERPASS":6242,"MAP_DEWFORD_TOWN":11,"MAP_DEWFORD_TOWN_GYM":771,"MAP_DEWFORD_TOWN_HALL":772,"MAP_DEWFORD_TOWN_HOUSE1":768,"MAP_DEWFORD_TOWN_HOUSE2":773,"MAP_DEWFORD_TOWN_POKEMON_CENTER_1F":769,"MAP_DEWFORD_TOWN_POKEMON_CENTER_2F":770,"MAP_EVER_GRANDE_CITY":8,"MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM":4100,"MAP_EVER_GRANDE_CITY_DRAKES_ROOM":4099,"MAP_EVER_GRANDE_CITY_GLACIAS_ROOM":4098,"MAP_EVER_GRANDE_CITY_HALL1":4101,"MAP_EVER_GRANDE_CITY_HALL2":4102,"MAP_EVER_GRANDE_CITY_HALL3":4103,"MAP_EVER_GRANDE_CITY_HALL4":4104,"MAP_EVER_GRANDE_CITY_HALL5":4105,"MAP_EVER_GRANDE_CITY_HALL_OF_FAME":4107,"MAP_EVER_GRANDE_CITY_PHOEBES_ROOM":4097,"MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F":4108,"MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F":4109,"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F":4106,"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F":4110,"MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM":4096,"MAP_FALLARBOR_TOWN":13,"MAP_FALLARBOR_TOWN_BATTLE_TENT_BATTLE_ROOM":1283,"MAP_FALLARBOR_TOWN_BATTLE_TENT_CORRIDOR":1282,"MAP_FALLARBOR_TOWN_BATTLE_TENT_LOBBY":1281,"MAP_FALLARBOR_TOWN_COZMOS_HOUSE":1286,"MAP_FALLARBOR_TOWN_MART":1280,"MAP_FALLARBOR_TOWN_MOVE_RELEARNERS_HOUSE":1287,"MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F":1284,"MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F":1285,"MAP_FARAWAY_ISLAND_ENTRANCE":6712,"MAP_FARAWAY_ISLAND_INTERIOR":6713,"MAP_FIERY_PATH":6158,"MAP_FORTREE_CITY":4,"MAP_FORTREE_CITY_DECORATION_SHOP":3081,"MAP_FORTREE_CITY_GYM":3073,"MAP_FORTREE_CITY_HOUSE1":3072,"MAP_FORTREE_CITY_HOUSE2":3077,"MAP_FORTREE_CITY_HOUSE3":3078,"MAP_FORTREE_CITY_HOUSE4":3079,"MAP_FORTREE_CITY_HOUSE5":3080,"MAP_FORTREE_CITY_MART":3076,"MAP_FORTREE_CITY_POKEMON_CENTER_1F":3074,"MAP_FORTREE_CITY_POKEMON_CENTER_2F":3075,"MAP_GRANITE_CAVE_1F":6151,"MAP_GRANITE_CAVE_B1F":6152,"MAP_GRANITE_CAVE_B2F":6153,"MAP_GRANITE_CAVE_STEVENS_ROOM":6154,"MAP_GROUPS_COUNT":34,"MAP_INSIDE_OF_TRUCK":6440,"MAP_ISLAND_CAVE":6211,"MAP_JAGGED_PASS":6157,"MAP_LAVARIDGE_TOWN":12,"MAP_LAVARIDGE_TOWN_GYM_1F":1025,"MAP_LAVARIDGE_TOWN_GYM_B1F":1026,"MAP_LAVARIDGE_TOWN_HERB_SHOP":1024,"MAP_LAVARIDGE_TOWN_HOUSE":1027,"MAP_LAVARIDGE_TOWN_MART":1028,"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F":1029,"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F":1030,"MAP_LILYCOVE_CITY":5,"MAP_LILYCOVE_CITY_CONTEST_HALL":3333,"MAP_LILYCOVE_CITY_CONTEST_LOBBY":3332,"MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F":3328,"MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_2F":3329,"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F":3344,"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F":3345,"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F":3346,"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F":3347,"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F":3348,"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR":3350,"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP":3349,"MAP_LILYCOVE_CITY_HARBOR":3338,"MAP_LILYCOVE_CITY_HOUSE1":3340,"MAP_LILYCOVE_CITY_HOUSE2":3341,"MAP_LILYCOVE_CITY_HOUSE3":3342,"MAP_LILYCOVE_CITY_HOUSE4":3343,"MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F":3330,"MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_2F":3331,"MAP_LILYCOVE_CITY_MOVE_DELETERS_HOUSE":3339,"MAP_LILYCOVE_CITY_POKEMON_CENTER_1F":3334,"MAP_LILYCOVE_CITY_POKEMON_CENTER_2F":3335,"MAP_LILYCOVE_CITY_POKEMON_TRAINER_FAN_CLUB":3337,"MAP_LILYCOVE_CITY_UNUSED_MART":3336,"MAP_LITTLEROOT_TOWN":9,"MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F":256,"MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F":257,"MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F":258,"MAP_LITTLEROOT_TOWN_MAYS_HOUSE_2F":259,"MAP_LITTLEROOT_TOWN_PROFESSOR_BIRCHS_LAB":260,"MAP_MAGMA_HIDEOUT_1F":6230,"MAP_MAGMA_HIDEOUT_2F_1R":6231,"MAP_MAGMA_HIDEOUT_2F_2R":6232,"MAP_MAGMA_HIDEOUT_2F_3R":6237,"MAP_MAGMA_HIDEOUT_3F_1R":6233,"MAP_MAGMA_HIDEOUT_3F_2R":6234,"MAP_MAGMA_HIDEOUT_3F_3R":6236,"MAP_MAGMA_HIDEOUT_4F":6235,"MAP_MARINE_CAVE_END":6247,"MAP_MARINE_CAVE_ENTRANCE":6246,"MAP_MAUVILLE_CITY":2,"MAP_MAUVILLE_CITY_BIKE_SHOP":2561,"MAP_MAUVILLE_CITY_GAME_CORNER":2563,"MAP_MAUVILLE_CITY_GYM":2560,"MAP_MAUVILLE_CITY_HOUSE1":2562,"MAP_MAUVILLE_CITY_HOUSE2":2564,"MAP_MAUVILLE_CITY_MART":2567,"MAP_MAUVILLE_CITY_POKEMON_CENTER_1F":2565,"MAP_MAUVILLE_CITY_POKEMON_CENTER_2F":2566,"MAP_METEOR_FALLS_1F_1R":6144,"MAP_METEOR_FALLS_1F_2R":6145,"MAP_METEOR_FALLS_B1F_1R":6146,"MAP_METEOR_FALLS_B1F_2R":6147,"MAP_METEOR_FALLS_STEVENS_CAVE":6251,"MAP_MIRAGE_TOWER_1F":6238,"MAP_MIRAGE_TOWER_2F":6239,"MAP_MIRAGE_TOWER_3F":6240,"MAP_MIRAGE_TOWER_4F":6241,"MAP_MOSSDEEP_CITY":6,"MAP_MOSSDEEP_CITY_GAME_CORNER_1F":3595,"MAP_MOSSDEEP_CITY_GAME_CORNER_B1F":3596,"MAP_MOSSDEEP_CITY_GYM":3584,"MAP_MOSSDEEP_CITY_HOUSE1":3585,"MAP_MOSSDEEP_CITY_HOUSE2":3586,"MAP_MOSSDEEP_CITY_HOUSE3":3590,"MAP_MOSSDEEP_CITY_HOUSE4":3592,"MAP_MOSSDEEP_CITY_MART":3589,"MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F":3587,"MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F":3588,"MAP_MOSSDEEP_CITY_SPACE_CENTER_1F":3593,"MAP_MOSSDEEP_CITY_SPACE_CENTER_2F":3594,"MAP_MOSSDEEP_CITY_STEVENS_HOUSE":3591,"MAP_MT_CHIMNEY":6156,"MAP_MT_CHIMNEY_CABLE_CAR_STATION":4865,"MAP_MT_PYRE_1F":6159,"MAP_MT_PYRE_2F":6160,"MAP_MT_PYRE_3F":6161,"MAP_MT_PYRE_4F":6162,"MAP_MT_PYRE_5F":6163,"MAP_MT_PYRE_6F":6164,"MAP_MT_PYRE_EXTERIOR":6165,"MAP_MT_PYRE_SUMMIT":6166,"MAP_NAVEL_ROCK_B1F":6725,"MAP_NAVEL_ROCK_BOTTOM":6743,"MAP_NAVEL_ROCK_DOWN01":6732,"MAP_NAVEL_ROCK_DOWN02":6733,"MAP_NAVEL_ROCK_DOWN03":6734,"MAP_NAVEL_ROCK_DOWN04":6735,"MAP_NAVEL_ROCK_DOWN05":6736,"MAP_NAVEL_ROCK_DOWN06":6737,"MAP_NAVEL_ROCK_DOWN07":6738,"MAP_NAVEL_ROCK_DOWN08":6739,"MAP_NAVEL_ROCK_DOWN09":6740,"MAP_NAVEL_ROCK_DOWN10":6741,"MAP_NAVEL_ROCK_DOWN11":6742,"MAP_NAVEL_ROCK_ENTRANCE":6724,"MAP_NAVEL_ROCK_EXTERIOR":6722,"MAP_NAVEL_ROCK_FORK":6726,"MAP_NAVEL_ROCK_HARBOR":6723,"MAP_NAVEL_ROCK_TOP":6731,"MAP_NAVEL_ROCK_UP1":6727,"MAP_NAVEL_ROCK_UP2":6728,"MAP_NAVEL_ROCK_UP3":6729,"MAP_NAVEL_ROCK_UP4":6730,"MAP_NEW_MAUVILLE_ENTRANCE":6196,"MAP_NEW_MAUVILLE_INSIDE":6197,"MAP_OLDALE_TOWN":10,"MAP_OLDALE_TOWN_HOUSE1":512,"MAP_OLDALE_TOWN_HOUSE2":513,"MAP_OLDALE_TOWN_MART":516,"MAP_OLDALE_TOWN_POKEMON_CENTER_1F":514,"MAP_OLDALE_TOWN_POKEMON_CENTER_2F":515,"MAP_PACIFIDLOG_TOWN":15,"MAP_PACIFIDLOG_TOWN_HOUSE1":1794,"MAP_PACIFIDLOG_TOWN_HOUSE2":1795,"MAP_PACIFIDLOG_TOWN_HOUSE3":1796,"MAP_PACIFIDLOG_TOWN_HOUSE4":1797,"MAP_PACIFIDLOG_TOWN_HOUSE5":1798,"MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F":1792,"MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F":1793,"MAP_PETALBURG_CITY":0,"MAP_PETALBURG_CITY_GYM":2049,"MAP_PETALBURG_CITY_HOUSE1":2050,"MAP_PETALBURG_CITY_HOUSE2":2051,"MAP_PETALBURG_CITY_MART":2054,"MAP_PETALBURG_CITY_POKEMON_CENTER_1F":2052,"MAP_PETALBURG_CITY_POKEMON_CENTER_2F":2053,"MAP_PETALBURG_CITY_WALLYS_HOUSE":2048,"MAP_PETALBURG_WOODS":6155,"MAP_RECORD_CORNER":6426,"MAP_ROUTE101":16,"MAP_ROUTE102":17,"MAP_ROUTE103":18,"MAP_ROUTE104":19,"MAP_ROUTE104_MR_BRINEYS_HOUSE":4352,"MAP_ROUTE104_PRETTY_PETAL_FLOWER_SHOP":4353,"MAP_ROUTE104_PROTOTYPE":6912,"MAP_ROUTE104_PROTOTYPE_PRETTY_PETAL_FLOWER_SHOP":6913,"MAP_ROUTE105":20,"MAP_ROUTE106":21,"MAP_ROUTE107":22,"MAP_ROUTE108":23,"MAP_ROUTE109":24,"MAP_ROUTE109_SEASHORE_HOUSE":7168,"MAP_ROUTE110":25,"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE":7435,"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE":7436,"MAP_ROUTE110_TRICK_HOUSE_CORRIDOR":7426,"MAP_ROUTE110_TRICK_HOUSE_END":7425,"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE":7424,"MAP_ROUTE110_TRICK_HOUSE_PUZZLE1":7427,"MAP_ROUTE110_TRICK_HOUSE_PUZZLE2":7428,"MAP_ROUTE110_TRICK_HOUSE_PUZZLE3":7429,"MAP_ROUTE110_TRICK_HOUSE_PUZZLE4":7430,"MAP_ROUTE110_TRICK_HOUSE_PUZZLE5":7431,"MAP_ROUTE110_TRICK_HOUSE_PUZZLE6":7432,"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7":7433,"MAP_ROUTE110_TRICK_HOUSE_PUZZLE8":7434,"MAP_ROUTE111":26,"MAP_ROUTE111_OLD_LADYS_REST_STOP":4609,"MAP_ROUTE111_WINSTRATE_FAMILYS_HOUSE":4608,"MAP_ROUTE112":27,"MAP_ROUTE112_CABLE_CAR_STATION":4864,"MAP_ROUTE113":28,"MAP_ROUTE113_GLASS_WORKSHOP":7680,"MAP_ROUTE114":29,"MAP_ROUTE114_FOSSIL_MANIACS_HOUSE":5120,"MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL":5121,"MAP_ROUTE114_LANETTES_HOUSE":5122,"MAP_ROUTE115":30,"MAP_ROUTE116":31,"MAP_ROUTE116_TUNNELERS_REST_HOUSE":5376,"MAP_ROUTE117":32,"MAP_ROUTE117_POKEMON_DAY_CARE":5632,"MAP_ROUTE118":33,"MAP_ROUTE119":34,"MAP_ROUTE119_HOUSE":8194,"MAP_ROUTE119_WEATHER_INSTITUTE_1F":8192,"MAP_ROUTE119_WEATHER_INSTITUTE_2F":8193,"MAP_ROUTE120":35,"MAP_ROUTE121":36,"MAP_ROUTE121_SAFARI_ZONE_ENTRANCE":5888,"MAP_ROUTE122":37,"MAP_ROUTE123":38,"MAP_ROUTE123_BERRY_MASTERS_HOUSE":7936,"MAP_ROUTE124":39,"MAP_ROUTE124_DIVING_TREASURE_HUNTERS_HOUSE":8448,"MAP_ROUTE125":40,"MAP_ROUTE126":41,"MAP_ROUTE127":42,"MAP_ROUTE128":43,"MAP_ROUTE129":44,"MAP_ROUTE130":45,"MAP_ROUTE131":46,"MAP_ROUTE132":47,"MAP_ROUTE133":48,"MAP_ROUTE134":49,"MAP_RUSTBORO_CITY":3,"MAP_RUSTBORO_CITY_CUTTERS_HOUSE":2827,"MAP_RUSTBORO_CITY_DEVON_CORP_1F":2816,"MAP_RUSTBORO_CITY_DEVON_CORP_2F":2817,"MAP_RUSTBORO_CITY_DEVON_CORP_3F":2818,"MAP_RUSTBORO_CITY_FLAT1_1F":2824,"MAP_RUSTBORO_CITY_FLAT1_2F":2825,"MAP_RUSTBORO_CITY_FLAT2_1F":2829,"MAP_RUSTBORO_CITY_FLAT2_2F":2830,"MAP_RUSTBORO_CITY_FLAT2_3F":2831,"MAP_RUSTBORO_CITY_GYM":2819,"MAP_RUSTBORO_CITY_HOUSE1":2826,"MAP_RUSTBORO_CITY_HOUSE2":2828,"MAP_RUSTBORO_CITY_HOUSE3":2832,"MAP_RUSTBORO_CITY_MART":2823,"MAP_RUSTBORO_CITY_POKEMON_CENTER_1F":2821,"MAP_RUSTBORO_CITY_POKEMON_CENTER_2F":2822,"MAP_RUSTBORO_CITY_POKEMON_SCHOOL":2820,"MAP_RUSTURF_TUNNEL":6148,"MAP_SAFARI_ZONE_NORTH":6657,"MAP_SAFARI_ZONE_NORTHEAST":6668,"MAP_SAFARI_ZONE_NORTHWEST":6656,"MAP_SAFARI_ZONE_REST_HOUSE":6667,"MAP_SAFARI_ZONE_SOUTH":6659,"MAP_SAFARI_ZONE_SOUTHEAST":6669,"MAP_SAFARI_ZONE_SOUTHWEST":6658,"MAP_SCORCHED_SLAB":6217,"MAP_SEAFLOOR_CAVERN_ENTRANCE":6171,"MAP_SEAFLOOR_CAVERN_ROOM1":6172,"MAP_SEAFLOOR_CAVERN_ROOM2":6173,"MAP_SEAFLOOR_CAVERN_ROOM3":6174,"MAP_SEAFLOOR_CAVERN_ROOM4":6175,"MAP_SEAFLOOR_CAVERN_ROOM5":6176,"MAP_SEAFLOOR_CAVERN_ROOM6":6177,"MAP_SEAFLOOR_CAVERN_ROOM7":6178,"MAP_SEAFLOOR_CAVERN_ROOM8":6179,"MAP_SEAFLOOR_CAVERN_ROOM9":6180,"MAP_SEALED_CHAMBER_INNER_ROOM":6216,"MAP_SEALED_CHAMBER_OUTER_ROOM":6215,"MAP_SECRET_BASE_BLUE_CAVE1":6402,"MAP_SECRET_BASE_BLUE_CAVE2":6408,"MAP_SECRET_BASE_BLUE_CAVE3":6414,"MAP_SECRET_BASE_BLUE_CAVE4":6420,"MAP_SECRET_BASE_BROWN_CAVE1":6401,"MAP_SECRET_BASE_BROWN_CAVE2":6407,"MAP_SECRET_BASE_BROWN_CAVE3":6413,"MAP_SECRET_BASE_BROWN_CAVE4":6419,"MAP_SECRET_BASE_RED_CAVE1":6400,"MAP_SECRET_BASE_RED_CAVE2":6406,"MAP_SECRET_BASE_RED_CAVE3":6412,"MAP_SECRET_BASE_RED_CAVE4":6418,"MAP_SECRET_BASE_SHRUB1":6405,"MAP_SECRET_BASE_SHRUB2":6411,"MAP_SECRET_BASE_SHRUB3":6417,"MAP_SECRET_BASE_SHRUB4":6423,"MAP_SECRET_BASE_TREE1":6404,"MAP_SECRET_BASE_TREE2":6410,"MAP_SECRET_BASE_TREE3":6416,"MAP_SECRET_BASE_TREE4":6422,"MAP_SECRET_BASE_YELLOW_CAVE1":6403,"MAP_SECRET_BASE_YELLOW_CAVE2":6409,"MAP_SECRET_BASE_YELLOW_CAVE3":6415,"MAP_SECRET_BASE_YELLOW_CAVE4":6421,"MAP_SHOAL_CAVE_HIGH_TIDE_ENTRANCE_ROOM":6194,"MAP_SHOAL_CAVE_HIGH_TIDE_INNER_ROOM":6195,"MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM":6190,"MAP_SHOAL_CAVE_LOW_TIDE_ICE_ROOM":6227,"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM":6191,"MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM":6193,"MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM":6192,"MAP_SKY_PILLAR_1F":6223,"MAP_SKY_PILLAR_2F":6224,"MAP_SKY_PILLAR_3F":6225,"MAP_SKY_PILLAR_4F":6226,"MAP_SKY_PILLAR_5F":6228,"MAP_SKY_PILLAR_ENTRANCE":6221,"MAP_SKY_PILLAR_OUTSIDE":6222,"MAP_SKY_PILLAR_TOP":6229,"MAP_SLATEPORT_CITY":1,"MAP_SLATEPORT_CITY_BATTLE_TENT_BATTLE_ROOM":2308,"MAP_SLATEPORT_CITY_BATTLE_TENT_CORRIDOR":2307,"MAP_SLATEPORT_CITY_BATTLE_TENT_LOBBY":2306,"MAP_SLATEPORT_CITY_HARBOR":2313,"MAP_SLATEPORT_CITY_HOUSE":2314,"MAP_SLATEPORT_CITY_MART":2317,"MAP_SLATEPORT_CITY_NAME_RATERS_HOUSE":2309,"MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F":2311,"MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_2F":2312,"MAP_SLATEPORT_CITY_POKEMON_CENTER_1F":2315,"MAP_SLATEPORT_CITY_POKEMON_CENTER_2F":2316,"MAP_SLATEPORT_CITY_POKEMON_FAN_CLUB":2310,"MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F":2304,"MAP_SLATEPORT_CITY_STERNS_SHIPYARD_2F":2305,"MAP_SOOTOPOLIS_CITY":7,"MAP_SOOTOPOLIS_CITY_GYM_1F":3840,"MAP_SOOTOPOLIS_CITY_GYM_B1F":3841,"MAP_SOOTOPOLIS_CITY_HOUSE1":3845,"MAP_SOOTOPOLIS_CITY_HOUSE2":3846,"MAP_SOOTOPOLIS_CITY_HOUSE3":3847,"MAP_SOOTOPOLIS_CITY_HOUSE4":3848,"MAP_SOOTOPOLIS_CITY_HOUSE5":3849,"MAP_SOOTOPOLIS_CITY_HOUSE6":3850,"MAP_SOOTOPOLIS_CITY_HOUSE7":3851,"MAP_SOOTOPOLIS_CITY_LOTAD_AND_SEEDOT_HOUSE":3852,"MAP_SOOTOPOLIS_CITY_MART":3844,"MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F":3853,"MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_B1F":3854,"MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F":3842,"MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F":3843,"MAP_SOUTHERN_ISLAND_EXTERIOR":6665,"MAP_SOUTHERN_ISLAND_INTERIOR":6666,"MAP_SS_TIDAL_CORRIDOR":6441,"MAP_SS_TIDAL_LOWER_DECK":6442,"MAP_SS_TIDAL_ROOMS":6443,"MAP_TERRA_CAVE_END":6249,"MAP_TERRA_CAVE_ENTRANCE":6248,"MAP_TRADE_CENTER":6425,"MAP_TRAINER_HILL_1F":6717,"MAP_TRAINER_HILL_2F":6718,"MAP_TRAINER_HILL_3F":6719,"MAP_TRAINER_HILL_4F":6720,"MAP_TRAINER_HILL_ELEVATOR":6744,"MAP_TRAINER_HILL_ENTRANCE":6716,"MAP_TRAINER_HILL_ROOF":6721,"MAP_UNDERWATER_MARINE_CAVE":6245,"MAP_UNDERWATER_ROUTE105":55,"MAP_UNDERWATER_ROUTE124":50,"MAP_UNDERWATER_ROUTE125":56,"MAP_UNDERWATER_ROUTE126":51,"MAP_UNDERWATER_ROUTE127":52,"MAP_UNDERWATER_ROUTE128":53,"MAP_UNDERWATER_ROUTE129":54,"MAP_UNDERWATER_ROUTE134":6213,"MAP_UNDERWATER_SEAFLOOR_CAVERN":6170,"MAP_UNDERWATER_SEALED_CHAMBER":6214,"MAP_UNDERWATER_SOOTOPOLIS_CITY":6149,"MAP_UNION_ROOM":6460,"MAP_UNUSED_CONTEST_HALL1":6429,"MAP_UNUSED_CONTEST_HALL2":6430,"MAP_UNUSED_CONTEST_HALL3":6431,"MAP_UNUSED_CONTEST_HALL4":6432,"MAP_UNUSED_CONTEST_HALL5":6433,"MAP_UNUSED_CONTEST_HALL6":6434,"MAP_VERDANTURF_TOWN":14,"MAP_VERDANTURF_TOWN_BATTLE_TENT_BATTLE_ROOM":1538,"MAP_VERDANTURF_TOWN_BATTLE_TENT_CORRIDOR":1537,"MAP_VERDANTURF_TOWN_BATTLE_TENT_LOBBY":1536,"MAP_VERDANTURF_TOWN_FRIENDSHIP_RATERS_HOUSE":1543,"MAP_VERDANTURF_TOWN_HOUSE":1544,"MAP_VERDANTURF_TOWN_MART":1539,"MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F":1540,"MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F":1541,"MAP_VERDANTURF_TOWN_WANDAS_HOUSE":1542,"MAP_VICTORY_ROAD_1F":6187,"MAP_VICTORY_ROAD_B1F":6188,"MAP_VICTORY_ROAD_B2F":6189,"MAX_BAG_ITEM_CAPACITY":99,"MAX_BERRY_CAPACITY":999,"MAX_BERRY_INDEX":178,"MAX_ITEM_DIGITS":3,"MAX_PC_ITEM_CAPACITY":999,"MAX_TRAINERS_COUNT":864,"MOVES_COUNT":355,"MOVE_ABSORB":71,"MOVE_ACID":51,"MOVE_ACID_ARMOR":151,"MOVE_AERIAL_ACE":332,"MOVE_AEROBLAST":177,"MOVE_AGILITY":97,"MOVE_AIR_CUTTER":314,"MOVE_AMNESIA":133,"MOVE_ANCIENT_POWER":246,"MOVE_ARM_THRUST":292,"MOVE_AROMATHERAPY":312,"MOVE_ASSIST":274,"MOVE_ASTONISH":310,"MOVE_ATTRACT":213,"MOVE_AURORA_BEAM":62,"MOVE_BARRAGE":140,"MOVE_BARRIER":112,"MOVE_BATON_PASS":226,"MOVE_BEAT_UP":251,"MOVE_BELLY_DRUM":187,"MOVE_BIDE":117,"MOVE_BIND":20,"MOVE_BITE":44,"MOVE_BLAST_BURN":307,"MOVE_BLAZE_KICK":299,"MOVE_BLIZZARD":59,"MOVE_BLOCK":335,"MOVE_BODY_SLAM":34,"MOVE_BONEMERANG":155,"MOVE_BONE_CLUB":125,"MOVE_BONE_RUSH":198,"MOVE_BOUNCE":340,"MOVE_BRICK_BREAK":280,"MOVE_BUBBLE":145,"MOVE_BUBBLE_BEAM":61,"MOVE_BULK_UP":339,"MOVE_BULLET_SEED":331,"MOVE_CALM_MIND":347,"MOVE_CAMOUFLAGE":293,"MOVE_CHARGE":268,"MOVE_CHARM":204,"MOVE_CLAMP":128,"MOVE_COMET_PUNCH":4,"MOVE_CONFUSE_RAY":109,"MOVE_CONFUSION":93,"MOVE_CONSTRICT":132,"MOVE_CONVERSION":160,"MOVE_CONVERSION_2":176,"MOVE_COSMIC_POWER":322,"MOVE_COTTON_SPORE":178,"MOVE_COUNTER":68,"MOVE_COVET":343,"MOVE_CRABHAMMER":152,"MOVE_CROSS_CHOP":238,"MOVE_CRUNCH":242,"MOVE_CRUSH_CLAW":306,"MOVE_CURSE":174,"MOVE_CUT":15,"MOVE_DEFENSE_CURL":111,"MOVE_DESTINY_BOND":194,"MOVE_DETECT":197,"MOVE_DIG":91,"MOVE_DISABLE":50,"MOVE_DIVE":291,"MOVE_DIZZY_PUNCH":146,"MOVE_DOOM_DESIRE":353,"MOVE_DOUBLE_EDGE":38,"MOVE_DOUBLE_KICK":24,"MOVE_DOUBLE_SLAP":3,"MOVE_DOUBLE_TEAM":104,"MOVE_DRAGON_BREATH":225,"MOVE_DRAGON_CLAW":337,"MOVE_DRAGON_DANCE":349,"MOVE_DRAGON_RAGE":82,"MOVE_DREAM_EATER":138,"MOVE_DRILL_PECK":65,"MOVE_DYNAMIC_PUNCH":223,"MOVE_EARTHQUAKE":89,"MOVE_EGG_BOMB":121,"MOVE_EMBER":52,"MOVE_ENCORE":227,"MOVE_ENDEAVOR":283,"MOVE_ENDURE":203,"MOVE_ERUPTION":284,"MOVE_EXPLOSION":153,"MOVE_EXTRASENSORY":326,"MOVE_EXTREME_SPEED":245,"MOVE_FACADE":263,"MOVE_FAINT_ATTACK":185,"MOVE_FAKE_OUT":252,"MOVE_FAKE_TEARS":313,"MOVE_FALSE_SWIPE":206,"MOVE_FEATHER_DANCE":297,"MOVE_FIRE_BLAST":126,"MOVE_FIRE_PUNCH":7,"MOVE_FIRE_SPIN":83,"MOVE_FISSURE":90,"MOVE_FLAIL":175,"MOVE_FLAMETHROWER":53,"MOVE_FLAME_WHEEL":172,"MOVE_FLASH":148,"MOVE_FLATTER":260,"MOVE_FLY":19,"MOVE_FOCUS_ENERGY":116,"MOVE_FOCUS_PUNCH":264,"MOVE_FOLLOW_ME":266,"MOVE_FORESIGHT":193,"MOVE_FRENZY_PLANT":338,"MOVE_FRUSTRATION":218,"MOVE_FURY_ATTACK":31,"MOVE_FURY_CUTTER":210,"MOVE_FURY_SWIPES":154,"MOVE_FUTURE_SIGHT":248,"MOVE_GIGA_DRAIN":202,"MOVE_GLARE":137,"MOVE_GRASS_WHISTLE":320,"MOVE_GROWL":45,"MOVE_GROWTH":74,"MOVE_GRUDGE":288,"MOVE_GUILLOTINE":12,"MOVE_GUST":16,"MOVE_HAIL":258,"MOVE_HARDEN":106,"MOVE_HAZE":114,"MOVE_HEADBUTT":29,"MOVE_HEAL_BELL":215,"MOVE_HEAT_WAVE":257,"MOVE_HELPING_HAND":270,"MOVE_HIDDEN_POWER":237,"MOVE_HI_JUMP_KICK":136,"MOVE_HORN_ATTACK":30,"MOVE_HORN_DRILL":32,"MOVE_HOWL":336,"MOVE_HYDRO_CANNON":308,"MOVE_HYDRO_PUMP":56,"MOVE_HYPER_BEAM":63,"MOVE_HYPER_FANG":158,"MOVE_HYPER_VOICE":304,"MOVE_HYPNOSIS":95,"MOVE_ICE_BALL":301,"MOVE_ICE_BEAM":58,"MOVE_ICE_PUNCH":8,"MOVE_ICICLE_SPEAR":333,"MOVE_ICY_WIND":196,"MOVE_IMPRISON":286,"MOVE_INGRAIN":275,"MOVE_IRON_DEFENSE":334,"MOVE_IRON_TAIL":231,"MOVE_JUMP_KICK":26,"MOVE_KARATE_CHOP":2,"MOVE_KINESIS":134,"MOVE_KNOCK_OFF":282,"MOVE_LEAF_BLADE":348,"MOVE_LEECH_LIFE":141,"MOVE_LEECH_SEED":73,"MOVE_LEER":43,"MOVE_LICK":122,"MOVE_LIGHT_SCREEN":113,"MOVE_LOCK_ON":199,"MOVE_LOVELY_KISS":142,"MOVE_LOW_KICK":67,"MOVE_LUSTER_PURGE":295,"MOVE_MACH_PUNCH":183,"MOVE_MAGICAL_LEAF":345,"MOVE_MAGIC_COAT":277,"MOVE_MAGNITUDE":222,"MOVE_MEAN_LOOK":212,"MOVE_MEDITATE":96,"MOVE_MEGAHORN":224,"MOVE_MEGA_DRAIN":72,"MOVE_MEGA_KICK":25,"MOVE_MEGA_PUNCH":5,"MOVE_MEMENTO":262,"MOVE_METAL_CLAW":232,"MOVE_METAL_SOUND":319,"MOVE_METEOR_MASH":309,"MOVE_METRONOME":118,"MOVE_MILK_DRINK":208,"MOVE_MIMIC":102,"MOVE_MIND_READER":170,"MOVE_MINIMIZE":107,"MOVE_MIRROR_COAT":243,"MOVE_MIRROR_MOVE":119,"MOVE_MIST":54,"MOVE_MIST_BALL":296,"MOVE_MOONLIGHT":236,"MOVE_MORNING_SUN":234,"MOVE_MUDDY_WATER":330,"MOVE_MUD_SHOT":341,"MOVE_MUD_SLAP":189,"MOVE_MUD_SPORT":300,"MOVE_NATURE_POWER":267,"MOVE_NEEDLE_ARM":302,"MOVE_NIGHTMARE":171,"MOVE_NIGHT_SHADE":101,"MOVE_NONE":0,"MOVE_OCTAZOOKA":190,"MOVE_ODOR_SLEUTH":316,"MOVE_OUTRAGE":200,"MOVE_OVERHEAT":315,"MOVE_PAIN_SPLIT":220,"MOVE_PAY_DAY":6,"MOVE_PECK":64,"MOVE_PERISH_SONG":195,"MOVE_PETAL_DANCE":80,"MOVE_PIN_MISSILE":42,"MOVE_POISON_FANG":305,"MOVE_POISON_GAS":139,"MOVE_POISON_POWDER":77,"MOVE_POISON_STING":40,"MOVE_POISON_TAIL":342,"MOVE_POUND":1,"MOVE_POWDER_SNOW":181,"MOVE_PRESENT":217,"MOVE_PROTECT":182,"MOVE_PSYBEAM":60,"MOVE_PSYCHIC":94,"MOVE_PSYCHO_BOOST":354,"MOVE_PSYCH_UP":244,"MOVE_PSYWAVE":149,"MOVE_PURSUIT":228,"MOVE_QUICK_ATTACK":98,"MOVE_RAGE":99,"MOVE_RAIN_DANCE":240,"MOVE_RAPID_SPIN":229,"MOVE_RAZOR_LEAF":75,"MOVE_RAZOR_WIND":13,"MOVE_RECOVER":105,"MOVE_RECYCLE":278,"MOVE_REFLECT":115,"MOVE_REFRESH":287,"MOVE_REST":156,"MOVE_RETURN":216,"MOVE_REVENGE":279,"MOVE_REVERSAL":179,"MOVE_ROAR":46,"MOVE_ROCK_BLAST":350,"MOVE_ROCK_SLIDE":157,"MOVE_ROCK_SMASH":249,"MOVE_ROCK_THROW":88,"MOVE_ROCK_TOMB":317,"MOVE_ROLE_PLAY":272,"MOVE_ROLLING_KICK":27,"MOVE_ROLLOUT":205,"MOVE_SACRED_FIRE":221,"MOVE_SAFEGUARD":219,"MOVE_SANDSTORM":201,"MOVE_SAND_ATTACK":28,"MOVE_SAND_TOMB":328,"MOVE_SCARY_FACE":184,"MOVE_SCRATCH":10,"MOVE_SCREECH":103,"MOVE_SECRET_POWER":290,"MOVE_SEISMIC_TOSS":69,"MOVE_SELF_DESTRUCT":120,"MOVE_SHADOW_BALL":247,"MOVE_SHADOW_PUNCH":325,"MOVE_SHARPEN":159,"MOVE_SHEER_COLD":329,"MOVE_SHOCK_WAVE":351,"MOVE_SIGNAL_BEAM":324,"MOVE_SILVER_WIND":318,"MOVE_SING":47,"MOVE_SKETCH":166,"MOVE_SKILL_SWAP":285,"MOVE_SKULL_BASH":130,"MOVE_SKY_ATTACK":143,"MOVE_SKY_UPPERCUT":327,"MOVE_SLACK_OFF":303,"MOVE_SLAM":21,"MOVE_SLASH":163,"MOVE_SLEEP_POWDER":79,"MOVE_SLEEP_TALK":214,"MOVE_SLUDGE":124,"MOVE_SLUDGE_BOMB":188,"MOVE_SMELLING_SALT":265,"MOVE_SMOG":123,"MOVE_SMOKESCREEN":108,"MOVE_SNATCH":289,"MOVE_SNORE":173,"MOVE_SOFT_BOILED":135,"MOVE_SOLAR_BEAM":76,"MOVE_SONIC_BOOM":49,"MOVE_SPARK":209,"MOVE_SPIDER_WEB":169,"MOVE_SPIKES":191,"MOVE_SPIKE_CANNON":131,"MOVE_SPITE":180,"MOVE_SPIT_UP":255,"MOVE_SPLASH":150,"MOVE_SPORE":147,"MOVE_STEEL_WING":211,"MOVE_STOCKPILE":254,"MOVE_STOMP":23,"MOVE_STRENGTH":70,"MOVE_STRING_SHOT":81,"MOVE_STRUGGLE":165,"MOVE_STUN_SPORE":78,"MOVE_SUBMISSION":66,"MOVE_SUBSTITUTE":164,"MOVE_SUNNY_DAY":241,"MOVE_SUPERPOWER":276,"MOVE_SUPERSONIC":48,"MOVE_SUPER_FANG":162,"MOVE_SURF":57,"MOVE_SWAGGER":207,"MOVE_SWALLOW":256,"MOVE_SWEET_KISS":186,"MOVE_SWEET_SCENT":230,"MOVE_SWIFT":129,"MOVE_SWORDS_DANCE":14,"MOVE_SYNTHESIS":235,"MOVE_TACKLE":33,"MOVE_TAIL_GLOW":294,"MOVE_TAIL_WHIP":39,"MOVE_TAKE_DOWN":36,"MOVE_TAUNT":269,"MOVE_TEETER_DANCE":298,"MOVE_TELEPORT":100,"MOVE_THIEF":168,"MOVE_THRASH":37,"MOVE_THUNDER":87,"MOVE_THUNDERBOLT":85,"MOVE_THUNDER_PUNCH":9,"MOVE_THUNDER_SHOCK":84,"MOVE_THUNDER_WAVE":86,"MOVE_TICKLE":321,"MOVE_TORMENT":259,"MOVE_TOXIC":92,"MOVE_TRANSFORM":144,"MOVE_TRICK":271,"MOVE_TRIPLE_KICK":167,"MOVE_TRI_ATTACK":161,"MOVE_TWINEEDLE":41,"MOVE_TWISTER":239,"MOVE_UNAVAILABLE":65535,"MOVE_UPROAR":253,"MOVE_VICE_GRIP":11,"MOVE_VINE_WHIP":22,"MOVE_VITAL_THROW":233,"MOVE_VOLT_TACKLE":344,"MOVE_WATERFALL":127,"MOVE_WATER_GUN":55,"MOVE_WATER_PULSE":352,"MOVE_WATER_SPORT":346,"MOVE_WATER_SPOUT":323,"MOVE_WEATHER_BALL":311,"MOVE_WHIRLPOOL":250,"MOVE_WHIRLWIND":18,"MOVE_WILL_O_WISP":261,"MOVE_WING_ATTACK":17,"MOVE_WISH":273,"MOVE_WITHDRAW":110,"MOVE_WRAP":35,"MOVE_YAWN":281,"MOVE_ZAP_CANNON":192,"MUS_ABANDONED_SHIP":381,"MUS_ABNORMAL_WEATHER":443,"MUS_AQUA_MAGMA_HIDEOUT":430,"MUS_AWAKEN_LEGEND":388,"MUS_BIRCH_LAB":383,"MUS_B_ARENA":458,"MUS_B_DOME":467,"MUS_B_DOME_LOBBY":473,"MUS_B_FACTORY":469,"MUS_B_FRONTIER":457,"MUS_B_PALACE":463,"MUS_B_PIKE":468,"MUS_B_PYRAMID":461,"MUS_B_PYRAMID_TOP":462,"MUS_B_TOWER":465,"MUS_B_TOWER_RS":384,"MUS_CABLE_CAR":425,"MUS_CAUGHT":352,"MUS_CAVE_OF_ORIGIN":386,"MUS_CONTEST":440,"MUS_CONTEST_LOBBY":452,"MUS_CONTEST_RESULTS":446,"MUS_CONTEST_WINNER":439,"MUS_CREDITS":455,"MUS_CYCLING":403,"MUS_C_COMM_CENTER":356,"MUS_C_VS_LEGEND_BEAST":358,"MUS_DESERT":409,"MUS_DEWFORD":427,"MUS_DUMMY":0,"MUS_ENCOUNTER_AQUA":419,"MUS_ENCOUNTER_BRENDAN":421,"MUS_ENCOUNTER_CHAMPION":454,"MUS_ENCOUNTER_COOL":417,"MUS_ENCOUNTER_ELITE_FOUR":450,"MUS_ENCOUNTER_FEMALE":407,"MUS_ENCOUNTER_GIRL":379,"MUS_ENCOUNTER_HIKER":451,"MUS_ENCOUNTER_INTENSE":416,"MUS_ENCOUNTER_INTERVIEWER":453,"MUS_ENCOUNTER_MAGMA":441,"MUS_ENCOUNTER_MALE":380,"MUS_ENCOUNTER_MAY":415,"MUS_ENCOUNTER_RICH":397,"MUS_ENCOUNTER_SUSPICIOUS":423,"MUS_ENCOUNTER_SWIMMER":385,"MUS_ENCOUNTER_TWINS":449,"MUS_END":456,"MUS_EVER_GRANDE":422,"MUS_EVOLUTION":377,"MUS_EVOLUTION_INTRO":376,"MUS_EVOLVED":371,"MUS_FALLARBOR":437,"MUS_FOLLOW_ME":420,"MUS_FORTREE":382,"MUS_GAME_CORNER":426,"MUS_GSC_PEWTER":357,"MUS_GSC_ROUTE38":351,"MUS_GYM":364,"MUS_HALL_OF_FAME":436,"MUS_HALL_OF_FAME_ROOM":447,"MUS_HEAL":368,"MUS_HELP":410,"MUS_INTRO":414,"MUS_INTRO_BATTLE":442,"MUS_LEVEL_UP":367,"MUS_LILYCOVE":408,"MUS_LILYCOVE_MUSEUM":373,"MUS_LINK_CONTEST_P1":393,"MUS_LINK_CONTEST_P2":394,"MUS_LINK_CONTEST_P3":395,"MUS_LINK_CONTEST_P4":396,"MUS_LITTLEROOT":405,"MUS_LITTLEROOT_TEST":350,"MUS_MOVE_DELETED":378,"MUS_MT_CHIMNEY":406,"MUS_MT_PYRE":432,"MUS_MT_PYRE_EXTERIOR":434,"MUS_NONE":65535,"MUS_OBTAIN_BADGE":369,"MUS_OBTAIN_BERRY":387,"MUS_OBTAIN_B_POINTS":459,"MUS_OBTAIN_ITEM":370,"MUS_OBTAIN_SYMBOL":466,"MUS_OBTAIN_TMHM":372,"MUS_OCEANIC_MUSEUM":375,"MUS_OLDALE":363,"MUS_PETALBURG":362,"MUS_PETALBURG_WOODS":366,"MUS_POKE_CENTER":400,"MUS_POKE_MART":404,"MUS_RAYQUAZA_APPEARS":464,"MUS_REGISTER_MATCH_CALL":460,"MUS_RG_BERRY_PICK":542,"MUS_RG_CAUGHT":534,"MUS_RG_CAUGHT_INTRO":531,"MUS_RG_CELADON":521,"MUS_RG_CINNABAR":491,"MUS_RG_CREDITS":502,"MUS_RG_CYCLING":494,"MUS_RG_DEX_RATING":529,"MUS_RG_ENCOUNTER_BOY":497,"MUS_RG_ENCOUNTER_DEOXYS":555,"MUS_RG_ENCOUNTER_GIRL":496,"MUS_RG_ENCOUNTER_GYM_LEADER":554,"MUS_RG_ENCOUNTER_RIVAL":527,"MUS_RG_ENCOUNTER_ROCKET":495,"MUS_RG_FOLLOW_ME":484,"MUS_RG_FUCHSIA":520,"MUS_RG_GAME_CORNER":485,"MUS_RG_GAME_FREAK":533,"MUS_RG_GYM":487,"MUS_RG_HALL_OF_FAME":498,"MUS_RG_HEAL":493,"MUS_RG_INTRO_FIGHT":489,"MUS_RG_JIGGLYPUFF":488,"MUS_RG_LAVENDER":492,"MUS_RG_MT_MOON":500,"MUS_RG_MYSTERY_GIFT":541,"MUS_RG_NET_CENTER":540,"MUS_RG_NEW_GAME_EXIT":537,"MUS_RG_NEW_GAME_INSTRUCT":535,"MUS_RG_NEW_GAME_INTRO":536,"MUS_RG_OAK":514,"MUS_RG_OAK_LAB":513,"MUS_RG_OBTAIN_KEY_ITEM":530,"MUS_RG_PALLET":512,"MUS_RG_PEWTER":526,"MUS_RG_PHOTO":532,"MUS_RG_POKE_CENTER":515,"MUS_RG_POKE_FLUTE":550,"MUS_RG_POKE_JUMP":538,"MUS_RG_POKE_MANSION":501,"MUS_RG_POKE_TOWER":518,"MUS_RG_RIVAL_EXIT":528,"MUS_RG_ROCKET_HIDEOUT":486,"MUS_RG_ROUTE1":503,"MUS_RG_ROUTE11":506,"MUS_RG_ROUTE24":504,"MUS_RG_ROUTE3":505,"MUS_RG_SEVII_123":547,"MUS_RG_SEVII_45":548,"MUS_RG_SEVII_67":549,"MUS_RG_SEVII_CAVE":543,"MUS_RG_SEVII_DUNGEON":546,"MUS_RG_SEVII_ROUTE":545,"MUS_RG_SILPH":519,"MUS_RG_SLOW_PALLET":557,"MUS_RG_SS_ANNE":516,"MUS_RG_SURF":517,"MUS_RG_TEACHY_TV_MENU":558,"MUS_RG_TEACHY_TV_SHOW":544,"MUS_RG_TITLE":490,"MUS_RG_TRAINER_TOWER":556,"MUS_RG_UNION_ROOM":539,"MUS_RG_VERMILLION":525,"MUS_RG_VICTORY_GYM_LEADER":524,"MUS_RG_VICTORY_ROAD":507,"MUS_RG_VICTORY_TRAINER":522,"MUS_RG_VICTORY_WILD":523,"MUS_RG_VIRIDIAN_FOREST":499,"MUS_RG_VS_CHAMPION":511,"MUS_RG_VS_DEOXYS":551,"MUS_RG_VS_GYM_LEADER":508,"MUS_RG_VS_LEGEND":553,"MUS_RG_VS_MEWTWO":552,"MUS_RG_VS_TRAINER":509,"MUS_RG_VS_WILD":510,"MUS_ROULETTE":392,"MUS_ROUTE101":359,"MUS_ROUTE104":401,"MUS_ROUTE110":360,"MUS_ROUTE113":418,"MUS_ROUTE118":32767,"MUS_ROUTE119":402,"MUS_ROUTE120":361,"MUS_ROUTE122":374,"MUS_RUSTBORO":399,"MUS_SAFARI_ZONE":428,"MUS_SAILING":431,"MUS_SCHOOL":435,"MUS_SEALED_CHAMBER":438,"MUS_SLATEPORT":433,"MUS_SLOTS_JACKPOT":389,"MUS_SLOTS_WIN":390,"MUS_SOOTOPOLIS":445,"MUS_SURF":365,"MUS_TITLE":413,"MUS_TOO_BAD":391,"MUS_TRICK_HOUSE":448,"MUS_UNDERWATER":411,"MUS_VERDANTURF":398,"MUS_VICTORY_AQUA_MAGMA":424,"MUS_VICTORY_GYM_LEADER":354,"MUS_VICTORY_LEAGUE":355,"MUS_VICTORY_ROAD":429,"MUS_VICTORY_TRAINER":412,"MUS_VICTORY_WILD":353,"MUS_VS_AQUA_MAGMA":475,"MUS_VS_AQUA_MAGMA_LEADER":483,"MUS_VS_CHAMPION":478,"MUS_VS_ELITE_FOUR":482,"MUS_VS_FRONTIER_BRAIN":471,"MUS_VS_GYM_LEADER":477,"MUS_VS_KYOGRE_GROUDON":480,"MUS_VS_MEW":472,"MUS_VS_RAYQUAZA":470,"MUS_VS_REGI":479,"MUS_VS_RIVAL":481,"MUS_VS_TRAINER":476,"MUS_VS_WILD":474,"MUS_WEATHER_GROUDON":444,"NUM_BADGES":8,"NUM_BERRY_MASTER_BERRIES":10,"NUM_BERRY_MASTER_BERRIES_SKIPPED":20,"NUM_BERRY_MASTER_WIFE_BERRIES":10,"NUM_DAILY_FLAGS":64,"NUM_HIDDEN_MACHINES":8,"NUM_KIRI_BERRIES":10,"NUM_KIRI_BERRIES_SKIPPED":20,"NUM_ROUTE_114_MAN_BERRIES":5,"NUM_ROUTE_114_MAN_BERRIES_SKIPPED":15,"NUM_SPECIAL_FLAGS":128,"NUM_SPECIES":412,"NUM_TECHNICAL_MACHINES":50,"NUM_TEMP_FLAGS":32,"NUM_WATER_STAGES":4,"NUM_WONDER_CARD_FLAGS":20,"OLD_ROD":0,"PH_CHOICE_BLEND":589,"PH_CHOICE_HELD":590,"PH_CHOICE_SOLO":591,"PH_CLOTH_BLEND":565,"PH_CLOTH_HELD":566,"PH_CLOTH_SOLO":567,"PH_CURE_BLEND":604,"PH_CURE_HELD":605,"PH_CURE_SOLO":606,"PH_DRESS_BLEND":568,"PH_DRESS_HELD":569,"PH_DRESS_SOLO":570,"PH_FACE_BLEND":562,"PH_FACE_HELD":563,"PH_FACE_SOLO":564,"PH_FLEECE_BLEND":571,"PH_FLEECE_HELD":572,"PH_FLEECE_SOLO":573,"PH_FOOT_BLEND":595,"PH_FOOT_HELD":596,"PH_FOOT_SOLO":597,"PH_GOAT_BLEND":583,"PH_GOAT_HELD":584,"PH_GOAT_SOLO":585,"PH_GOOSE_BLEND":598,"PH_GOOSE_HELD":599,"PH_GOOSE_SOLO":600,"PH_KIT_BLEND":574,"PH_KIT_HELD":575,"PH_KIT_SOLO":576,"PH_LOT_BLEND":580,"PH_LOT_HELD":581,"PH_LOT_SOLO":582,"PH_MOUTH_BLEND":592,"PH_MOUTH_HELD":593,"PH_MOUTH_SOLO":594,"PH_NURSE_BLEND":607,"PH_NURSE_HELD":608,"PH_NURSE_SOLO":609,"PH_PRICE_BLEND":577,"PH_PRICE_HELD":578,"PH_PRICE_SOLO":579,"PH_STRUT_BLEND":601,"PH_STRUT_HELD":602,"PH_STRUT_SOLO":603,"PH_THOUGHT_BLEND":586,"PH_THOUGHT_HELD":587,"PH_THOUGHT_SOLO":588,"PH_TRAP_BLEND":559,"PH_TRAP_HELD":560,"PH_TRAP_SOLO":561,"SE_A":25,"SE_APPLAUSE":105,"SE_ARENA_TIMEUP1":265,"SE_ARENA_TIMEUP2":266,"SE_BALL":23,"SE_BALLOON_BLUE":75,"SE_BALLOON_RED":74,"SE_BALLOON_YELLOW":76,"SE_BALL_BOUNCE_1":56,"SE_BALL_BOUNCE_2":57,"SE_BALL_BOUNCE_3":58,"SE_BALL_BOUNCE_4":59,"SE_BALL_OPEN":15,"SE_BALL_THROW":61,"SE_BALL_TRADE":60,"SE_BALL_TRAY_BALL":115,"SE_BALL_TRAY_ENTER":114,"SE_BALL_TRAY_EXIT":116,"SE_BANG":20,"SE_BERRY_BLENDER":53,"SE_BIKE_BELL":11,"SE_BIKE_HOP":34,"SE_BOO":22,"SE_BREAKABLE_DOOR":77,"SE_BRIDGE_WALK":71,"SE_CARD":54,"SE_CLICK":36,"SE_CONTEST_CONDITION_LOSE":38,"SE_CONTEST_CURTAIN_FALL":98,"SE_CONTEST_CURTAIN_RISE":97,"SE_CONTEST_HEART":96,"SE_CONTEST_ICON_CHANGE":99,"SE_CONTEST_ICON_CLEAR":100,"SE_CONTEST_MONS_TURN":101,"SE_CONTEST_PLACE":24,"SE_DEX_PAGE":109,"SE_DEX_SCROLL":108,"SE_DEX_SEARCH":112,"SE_DING_DONG":73,"SE_DOOR":8,"SE_DOWNPOUR":83,"SE_DOWNPOUR_STOP":84,"SE_E":28,"SE_EFFECTIVE":13,"SE_EGG_HATCH":113,"SE_ELEVATOR":89,"SE_ESCALATOR":80,"SE_EXIT":9,"SE_EXP":33,"SE_EXP_MAX":91,"SE_FAILURE":32,"SE_FAINT":16,"SE_FALL":43,"SE_FIELD_POISON":79,"SE_FLEE":17,"SE_FU_ZAKU":37,"SE_GLASS_FLUTE":117,"SE_I":26,"SE_ICE_BREAK":41,"SE_ICE_CRACK":42,"SE_ICE_STAIRS":40,"SE_INTRO_BLAST":103,"SE_ITEMFINDER":72,"SE_LAVARIDGE_FALL_WARP":39,"SE_LEDGE":10,"SE_LOW_HEALTH":90,"SE_MUD_BALL":78,"SE_MUGSHOT":104,"SE_M_ABSORB":180,"SE_M_ABSORB_2":179,"SE_M_ACID_ARMOR":218,"SE_M_ATTRACT":226,"SE_M_ATTRACT2":227,"SE_M_BARRIER":208,"SE_M_BATON_PASS":224,"SE_M_BELLY_DRUM":185,"SE_M_BIND":170,"SE_M_BITE":161,"SE_M_BLIZZARD":153,"SE_M_BLIZZARD2":154,"SE_M_BONEMERANG":187,"SE_M_BRICK_BREAK":198,"SE_M_BUBBLE":124,"SE_M_BUBBLE2":125,"SE_M_BUBBLE3":126,"SE_M_BUBBLE_BEAM":182,"SE_M_BUBBLE_BEAM2":183,"SE_M_CHARGE":213,"SE_M_CHARM":212,"SE_M_COMET_PUNCH":139,"SE_M_CONFUSE_RAY":196,"SE_M_COSMIC_POWER":243,"SE_M_CRABHAMMER":142,"SE_M_CUT":128,"SE_M_DETECT":209,"SE_M_DIG":175,"SE_M_DIVE":233,"SE_M_DIZZY_PUNCH":176,"SE_M_DOUBLE_SLAP":134,"SE_M_DOUBLE_TEAM":135,"SE_M_DRAGON_RAGE":171,"SE_M_EARTHQUAKE":234,"SE_M_EMBER":151,"SE_M_ENCORE":222,"SE_M_ENCORE2":223,"SE_M_EXPLOSION":178,"SE_M_FAINT_ATTACK":190,"SE_M_FIRE_PUNCH":147,"SE_M_FLAMETHROWER":146,"SE_M_FLAME_WHEEL":144,"SE_M_FLAME_WHEEL2":145,"SE_M_FLATTER":229,"SE_M_FLY":158,"SE_M_GIGA_DRAIN":199,"SE_M_GRASSWHISTLE":231,"SE_M_GUST":132,"SE_M_GUST2":133,"SE_M_HAIL":242,"SE_M_HARDEN":120,"SE_M_HAZE":246,"SE_M_HEADBUTT":162,"SE_M_HEAL_BELL":195,"SE_M_HEAT_WAVE":240,"SE_M_HORN_ATTACK":166,"SE_M_HYDRO_PUMP":164,"SE_M_HYPER_BEAM":215,"SE_M_HYPER_BEAM2":247,"SE_M_ICY_WIND":137,"SE_M_JUMP_KICK":143,"SE_M_LEER":192,"SE_M_LICK":188,"SE_M_LOCK_ON":210,"SE_M_MEGA_KICK":140,"SE_M_MEGA_KICK2":141,"SE_M_METRONOME":186,"SE_M_MILK_DRINK":225,"SE_M_MINIMIZE":204,"SE_M_MIST":168,"SE_M_MOONLIGHT":211,"SE_M_MORNING_SUN":228,"SE_M_NIGHTMARE":121,"SE_M_PAY_DAY":174,"SE_M_PERISH_SONG":173,"SE_M_PETAL_DANCE":202,"SE_M_POISON_POWDER":169,"SE_M_PSYBEAM":189,"SE_M_PSYBEAM2":200,"SE_M_RAIN_DANCE":127,"SE_M_RAZOR_WIND":136,"SE_M_RAZOR_WIND2":160,"SE_M_REFLECT":207,"SE_M_REVERSAL":217,"SE_M_ROCK_THROW":131,"SE_M_SACRED_FIRE":149,"SE_M_SACRED_FIRE2":150,"SE_M_SANDSTORM":219,"SE_M_SAND_ATTACK":159,"SE_M_SAND_TOMB":230,"SE_M_SCRATCH":155,"SE_M_SCREECH":181,"SE_M_SELF_DESTRUCT":177,"SE_M_SING":172,"SE_M_SKETCH":205,"SE_M_SKY_UPPERCUT":238,"SE_M_SNORE":197,"SE_M_SOLAR_BEAM":201,"SE_M_SPIT_UP":232,"SE_M_STAT_DECREASE":245,"SE_M_STAT_INCREASE":239,"SE_M_STRENGTH":214,"SE_M_STRING_SHOT":129,"SE_M_STRING_SHOT2":130,"SE_M_SUPERSONIC":184,"SE_M_SURF":163,"SE_M_SWAGGER":193,"SE_M_SWAGGER2":194,"SE_M_SWEET_SCENT":236,"SE_M_SWIFT":206,"SE_M_SWORDS_DANCE":191,"SE_M_TAIL_WHIP":167,"SE_M_TAKE_DOWN":152,"SE_M_TEETER_DANCE":244,"SE_M_TELEPORT":203,"SE_M_THUNDERBOLT":118,"SE_M_THUNDERBOLT2":119,"SE_M_THUNDER_WAVE":138,"SE_M_TOXIC":148,"SE_M_TRI_ATTACK":220,"SE_M_TRI_ATTACK2":221,"SE_M_TWISTER":235,"SE_M_UPROAR":241,"SE_M_VICEGRIP":156,"SE_M_VITAL_THROW":122,"SE_M_VITAL_THROW2":123,"SE_M_WATERFALL":216,"SE_M_WHIRLPOOL":165,"SE_M_WING_ATTACK":157,"SE_M_YAWN":237,"SE_N":30,"SE_NOTE_A":67,"SE_NOTE_B":68,"SE_NOTE_C":62,"SE_NOTE_C_HIGH":69,"SE_NOTE_D":63,"SE_NOTE_E":64,"SE_NOTE_F":65,"SE_NOTE_G":66,"SE_NOT_EFFECTIVE":12,"SE_O":29,"SE_ORB":107,"SE_PC_LOGIN":2,"SE_PC_OFF":3,"SE_PC_ON":4,"SE_PIKE_CURTAIN_CLOSE":267,"SE_PIKE_CURTAIN_OPEN":268,"SE_PIN":21,"SE_POKENAV_CALL":263,"SE_POKENAV_HANG_UP":264,"SE_POKENAV_OFF":111,"SE_POKENAV_ON":110,"SE_PUDDLE":70,"SE_RAIN":85,"SE_RAIN_STOP":86,"SE_REPEL":47,"SE_RG_BAG_CURSOR":252,"SE_RG_BAG_POCKET":253,"SE_RG_BALL_CLICK":254,"SE_RG_CARD_FLIP":249,"SE_RG_CARD_FLIPPING":250,"SE_RG_CARD_OPEN":251,"SE_RG_DEOXYS_MOVE":260,"SE_RG_DOOR":248,"SE_RG_HELP_CLOSE":258,"SE_RG_HELP_ERROR":259,"SE_RG_HELP_OPEN":257,"SE_RG_POKE_JUMP_FAILURE":262,"SE_RG_POKE_JUMP_SUCCESS":261,"SE_RG_SHOP":255,"SE_RG_SS_ANNE_HORN":256,"SE_ROTATING_GATE":48,"SE_ROULETTE_BALL":92,"SE_ROULETTE_BALL2":93,"SE_SAVE":55,"SE_SELECT":5,"SE_SHINY":102,"SE_SHIP":19,"SE_SHOP":95,"SE_SLIDING_DOOR":18,"SE_SUCCESS":31,"SE_SUDOWOODO_SHAKE":269,"SE_SUPER_EFFECTIVE":14,"SE_SWITCH":35,"SE_TAILLOW_WING_FLAP":94,"SE_THUNDER":87,"SE_THUNDER2":88,"SE_THUNDERSTORM":81,"SE_THUNDERSTORM_STOP":82,"SE_TRUCK_DOOR":52,"SE_TRUCK_MOVE":49,"SE_TRUCK_STOP":50,"SE_TRUCK_UNLOAD":51,"SE_U":27,"SE_UNLOCK":44,"SE_USE_ITEM":1,"SE_VEND":106,"SE_WALL_HIT":7,"SE_WARP_IN":45,"SE_WARP_OUT":46,"SE_WIN_OPEN":6,"SPECIAL_FLAGS_END":16511,"SPECIAL_FLAGS_START":16384,"SPECIES_ABRA":63,"SPECIES_ABSOL":376,"SPECIES_AERODACTYL":142,"SPECIES_AGGRON":384,"SPECIES_AIPOM":190,"SPECIES_ALAKAZAM":65,"SPECIES_ALTARIA":359,"SPECIES_AMPHAROS":181,"SPECIES_ANORITH":390,"SPECIES_ARBOK":24,"SPECIES_ARCANINE":59,"SPECIES_ARIADOS":168,"SPECIES_ARMALDO":391,"SPECIES_ARON":382,"SPECIES_ARTICUNO":144,"SPECIES_AZUMARILL":184,"SPECIES_AZURILL":350,"SPECIES_BAGON":395,"SPECIES_BALTOY":318,"SPECIES_BANETTE":378,"SPECIES_BARBOACH":323,"SPECIES_BAYLEEF":153,"SPECIES_BEAUTIFLY":292,"SPECIES_BEEDRILL":15,"SPECIES_BELDUM":398,"SPECIES_BELLOSSOM":182,"SPECIES_BELLSPROUT":69,"SPECIES_BLASTOISE":9,"SPECIES_BLAZIKEN":282,"SPECIES_BLISSEY":242,"SPECIES_BRELOOM":307,"SPECIES_BULBASAUR":1,"SPECIES_BUTTERFREE":12,"SPECIES_CACNEA":344,"SPECIES_CACTURNE":345,"SPECIES_CAMERUPT":340,"SPECIES_CARVANHA":330,"SPECIES_CASCOON":293,"SPECIES_CASTFORM":385,"SPECIES_CATERPIE":10,"SPECIES_CELEBI":251,"SPECIES_CHANSEY":113,"SPECIES_CHARIZARD":6,"SPECIES_CHARMANDER":4,"SPECIES_CHARMELEON":5,"SPECIES_CHIKORITA":152,"SPECIES_CHIMECHO":411,"SPECIES_CHINCHOU":170,"SPECIES_CLAMPERL":373,"SPECIES_CLAYDOL":319,"SPECIES_CLEFABLE":36,"SPECIES_CLEFAIRY":35,"SPECIES_CLEFFA":173,"SPECIES_CLOYSTER":91,"SPECIES_COMBUSKEN":281,"SPECIES_CORPHISH":326,"SPECIES_CORSOLA":222,"SPECIES_CRADILY":389,"SPECIES_CRAWDAUNT":327,"SPECIES_CROBAT":169,"SPECIES_CROCONAW":159,"SPECIES_CUBONE":104,"SPECIES_CYNDAQUIL":155,"SPECIES_DELCATTY":316,"SPECIES_DELIBIRD":225,"SPECIES_DEOXYS":410,"SPECIES_DEWGONG":87,"SPECIES_DIGLETT":50,"SPECIES_DITTO":132,"SPECIES_DODRIO":85,"SPECIES_DODUO":84,"SPECIES_DONPHAN":232,"SPECIES_DRAGONAIR":148,"SPECIES_DRAGONITE":149,"SPECIES_DRATINI":147,"SPECIES_DROWZEE":96,"SPECIES_DUGTRIO":51,"SPECIES_DUNSPARCE":206,"SPECIES_DUSCLOPS":362,"SPECIES_DUSKULL":361,"SPECIES_DUSTOX":294,"SPECIES_EEVEE":133,"SPECIES_EGG":412,"SPECIES_EKANS":23,"SPECIES_ELECTABUZZ":125,"SPECIES_ELECTRIKE":337,"SPECIES_ELECTRODE":101,"SPECIES_ELEKID":239,"SPECIES_ENTEI":244,"SPECIES_ESPEON":196,"SPECIES_EXEGGCUTE":102,"SPECIES_EXEGGUTOR":103,"SPECIES_EXPLOUD":372,"SPECIES_FARFETCHD":83,"SPECIES_FEAROW":22,"SPECIES_FEEBAS":328,"SPECIES_FERALIGATR":160,"SPECIES_FLAAFFY":180,"SPECIES_FLAREON":136,"SPECIES_FLYGON":334,"SPECIES_FORRETRESS":205,"SPECIES_FURRET":162,"SPECIES_GARDEVOIR":394,"SPECIES_GASTLY":92,"SPECIES_GENGAR":94,"SPECIES_GEODUDE":74,"SPECIES_GIRAFARIG":203,"SPECIES_GLALIE":347,"SPECIES_GLIGAR":207,"SPECIES_GLOOM":44,"SPECIES_GOLBAT":42,"SPECIES_GOLDEEN":118,"SPECIES_GOLDUCK":55,"SPECIES_GOLEM":76,"SPECIES_GOREBYSS":375,"SPECIES_GRANBULL":210,"SPECIES_GRAVELER":75,"SPECIES_GRIMER":88,"SPECIES_GROUDON":405,"SPECIES_GROVYLE":278,"SPECIES_GROWLITHE":58,"SPECIES_GRUMPIG":352,"SPECIES_GULPIN":367,"SPECIES_GYARADOS":130,"SPECIES_HARIYAMA":336,"SPECIES_HAUNTER":93,"SPECIES_HERACROSS":214,"SPECIES_HITMONCHAN":107,"SPECIES_HITMONLEE":106,"SPECIES_HITMONTOP":237,"SPECIES_HOOTHOOT":163,"SPECIES_HOPPIP":187,"SPECIES_HORSEA":116,"SPECIES_HOUNDOOM":229,"SPECIES_HOUNDOUR":228,"SPECIES_HO_OH":250,"SPECIES_HUNTAIL":374,"SPECIES_HYPNO":97,"SPECIES_IGGLYBUFF":174,"SPECIES_ILLUMISE":387,"SPECIES_IVYSAUR":2,"SPECIES_JIGGLYPUFF":39,"SPECIES_JIRACHI":409,"SPECIES_JOLTEON":135,"SPECIES_JUMPLUFF":189,"SPECIES_JYNX":124,"SPECIES_KABUTO":140,"SPECIES_KABUTOPS":141,"SPECIES_KADABRA":64,"SPECIES_KAKUNA":14,"SPECIES_KANGASKHAN":115,"SPECIES_KECLEON":317,"SPECIES_KINGDRA":230,"SPECIES_KINGLER":99,"SPECIES_KIRLIA":393,"SPECIES_KOFFING":109,"SPECIES_KRABBY":98,"SPECIES_KYOGRE":404,"SPECIES_LAIRON":383,"SPECIES_LANTURN":171,"SPECIES_LAPRAS":131,"SPECIES_LARVITAR":246,"SPECIES_LATIAS":407,"SPECIES_LATIOS":408,"SPECIES_LEDIAN":166,"SPECIES_LEDYBA":165,"SPECIES_LICKITUNG":108,"SPECIES_LILEEP":388,"SPECIES_LINOONE":289,"SPECIES_LOMBRE":296,"SPECIES_LOTAD":295,"SPECIES_LOUDRED":371,"SPECIES_LUDICOLO":297,"SPECIES_LUGIA":249,"SPECIES_LUNATONE":348,"SPECIES_LUVDISC":325,"SPECIES_MACHAMP":68,"SPECIES_MACHOKE":67,"SPECIES_MACHOP":66,"SPECIES_MAGBY":240,"SPECIES_MAGCARGO":219,"SPECIES_MAGIKARP":129,"SPECIES_MAGMAR":126,"SPECIES_MAGNEMITE":81,"SPECIES_MAGNETON":82,"SPECIES_MAKUHITA":335,"SPECIES_MANECTRIC":338,"SPECIES_MANKEY":56,"SPECIES_MANTINE":226,"SPECIES_MAREEP":179,"SPECIES_MARILL":183,"SPECIES_MAROWAK":105,"SPECIES_MARSHTOMP":284,"SPECIES_MASQUERAIN":312,"SPECIES_MAWILE":355,"SPECIES_MEDICHAM":357,"SPECIES_MEDITITE":356,"SPECIES_MEGANIUM":154,"SPECIES_MEOWTH":52,"SPECIES_METAGROSS":400,"SPECIES_METANG":399,"SPECIES_METAPOD":11,"SPECIES_MEW":151,"SPECIES_MEWTWO":150,"SPECIES_MIGHTYENA":287,"SPECIES_MILOTIC":329,"SPECIES_MILTANK":241,"SPECIES_MINUN":354,"SPECIES_MISDREAVUS":200,"SPECIES_MOLTRES":146,"SPECIES_MR_MIME":122,"SPECIES_MUDKIP":283,"SPECIES_MUK":89,"SPECIES_MURKROW":198,"SPECIES_NATU":177,"SPECIES_NIDOKING":34,"SPECIES_NIDOQUEEN":31,"SPECIES_NIDORAN_F":29,"SPECIES_NIDORAN_M":32,"SPECIES_NIDORINA":30,"SPECIES_NIDORINO":33,"SPECIES_NINCADA":301,"SPECIES_NINETALES":38,"SPECIES_NINJASK":302,"SPECIES_NOCTOWL":164,"SPECIES_NONE":0,"SPECIES_NOSEPASS":320,"SPECIES_NUMEL":339,"SPECIES_NUZLEAF":299,"SPECIES_OCTILLERY":224,"SPECIES_ODDISH":43,"SPECIES_OLD_UNOWN_B":252,"SPECIES_OLD_UNOWN_C":253,"SPECIES_OLD_UNOWN_D":254,"SPECIES_OLD_UNOWN_E":255,"SPECIES_OLD_UNOWN_F":256,"SPECIES_OLD_UNOWN_G":257,"SPECIES_OLD_UNOWN_H":258,"SPECIES_OLD_UNOWN_I":259,"SPECIES_OLD_UNOWN_J":260,"SPECIES_OLD_UNOWN_K":261,"SPECIES_OLD_UNOWN_L":262,"SPECIES_OLD_UNOWN_M":263,"SPECIES_OLD_UNOWN_N":264,"SPECIES_OLD_UNOWN_O":265,"SPECIES_OLD_UNOWN_P":266,"SPECIES_OLD_UNOWN_Q":267,"SPECIES_OLD_UNOWN_R":268,"SPECIES_OLD_UNOWN_S":269,"SPECIES_OLD_UNOWN_T":270,"SPECIES_OLD_UNOWN_U":271,"SPECIES_OLD_UNOWN_V":272,"SPECIES_OLD_UNOWN_W":273,"SPECIES_OLD_UNOWN_X":274,"SPECIES_OLD_UNOWN_Y":275,"SPECIES_OLD_UNOWN_Z":276,"SPECIES_OMANYTE":138,"SPECIES_OMASTAR":139,"SPECIES_ONIX":95,"SPECIES_PARAS":46,"SPECIES_PARASECT":47,"SPECIES_PELIPPER":310,"SPECIES_PERSIAN":53,"SPECIES_PHANPY":231,"SPECIES_PICHU":172,"SPECIES_PIDGEOT":18,"SPECIES_PIDGEOTTO":17,"SPECIES_PIDGEY":16,"SPECIES_PIKACHU":25,"SPECIES_PILOSWINE":221,"SPECIES_PINECO":204,"SPECIES_PINSIR":127,"SPECIES_PLUSLE":353,"SPECIES_POLITOED":186,"SPECIES_POLIWAG":60,"SPECIES_POLIWHIRL":61,"SPECIES_POLIWRATH":62,"SPECIES_PONYTA":77,"SPECIES_POOCHYENA":286,"SPECIES_PORYGON":137,"SPECIES_PORYGON2":233,"SPECIES_PRIMEAPE":57,"SPECIES_PSYDUCK":54,"SPECIES_PUPITAR":247,"SPECIES_QUAGSIRE":195,"SPECIES_QUILAVA":156,"SPECIES_QWILFISH":211,"SPECIES_RAICHU":26,"SPECIES_RAIKOU":243,"SPECIES_RALTS":392,"SPECIES_RAPIDASH":78,"SPECIES_RATICATE":20,"SPECIES_RATTATA":19,"SPECIES_RAYQUAZA":406,"SPECIES_REGICE":402,"SPECIES_REGIROCK":401,"SPECIES_REGISTEEL":403,"SPECIES_RELICANTH":381,"SPECIES_REMORAID":223,"SPECIES_RHYDON":112,"SPECIES_RHYHORN":111,"SPECIES_ROSELIA":363,"SPECIES_SABLEYE":322,"SPECIES_SALAMENCE":397,"SPECIES_SANDSHREW":27,"SPECIES_SANDSLASH":28,"SPECIES_SCEPTILE":279,"SPECIES_SCIZOR":212,"SPECIES_SCYTHER":123,"SPECIES_SEADRA":117,"SPECIES_SEAKING":119,"SPECIES_SEALEO":342,"SPECIES_SEEDOT":298,"SPECIES_SEEL":86,"SPECIES_SENTRET":161,"SPECIES_SEVIPER":379,"SPECIES_SHARPEDO":331,"SPECIES_SHEDINJA":303,"SPECIES_SHELGON":396,"SPECIES_SHELLDER":90,"SPECIES_SHIFTRY":300,"SPECIES_SHROOMISH":306,"SPECIES_SHUCKLE":213,"SPECIES_SHUPPET":377,"SPECIES_SILCOON":291,"SPECIES_SKARMORY":227,"SPECIES_SKIPLOOM":188,"SPECIES_SKITTY":315,"SPECIES_SLAKING":366,"SPECIES_SLAKOTH":364,"SPECIES_SLOWBRO":80,"SPECIES_SLOWKING":199,"SPECIES_SLOWPOKE":79,"SPECIES_SLUGMA":218,"SPECIES_SMEARGLE":235,"SPECIES_SMOOCHUM":238,"SPECIES_SNEASEL":215,"SPECIES_SNORLAX":143,"SPECIES_SNORUNT":346,"SPECIES_SNUBBULL":209,"SPECIES_SOLROCK":349,"SPECIES_SPEAROW":21,"SPECIES_SPHEAL":341,"SPECIES_SPINARAK":167,"SPECIES_SPINDA":308,"SPECIES_SPOINK":351,"SPECIES_SQUIRTLE":7,"SPECIES_STANTLER":234,"SPECIES_STARMIE":121,"SPECIES_STARYU":120,"SPECIES_STEELIX":208,"SPECIES_SUDOWOODO":185,"SPECIES_SUICUNE":245,"SPECIES_SUNFLORA":192,"SPECIES_SUNKERN":191,"SPECIES_SURSKIT":311,"SPECIES_SWABLU":358,"SPECIES_SWALOT":368,"SPECIES_SWAMPERT":285,"SPECIES_SWELLOW":305,"SPECIES_SWINUB":220,"SPECIES_TAILLOW":304,"SPECIES_TANGELA":114,"SPECIES_TAUROS":128,"SPECIES_TEDDIURSA":216,"SPECIES_TENTACOOL":72,"SPECIES_TENTACRUEL":73,"SPECIES_TOGEPI":175,"SPECIES_TOGETIC":176,"SPECIES_TORCHIC":280,"SPECIES_TORKOAL":321,"SPECIES_TOTODILE":158,"SPECIES_TRAPINCH":332,"SPECIES_TREECKO":277,"SPECIES_TROPIUS":369,"SPECIES_TYPHLOSION":157,"SPECIES_TYRANITAR":248,"SPECIES_TYROGUE":236,"SPECIES_UMBREON":197,"SPECIES_UNOWN":201,"SPECIES_UNOWN_B":413,"SPECIES_UNOWN_C":414,"SPECIES_UNOWN_D":415,"SPECIES_UNOWN_E":416,"SPECIES_UNOWN_EMARK":438,"SPECIES_UNOWN_F":417,"SPECIES_UNOWN_G":418,"SPECIES_UNOWN_H":419,"SPECIES_UNOWN_I":420,"SPECIES_UNOWN_J":421,"SPECIES_UNOWN_K":422,"SPECIES_UNOWN_L":423,"SPECIES_UNOWN_M":424,"SPECIES_UNOWN_N":425,"SPECIES_UNOWN_O":426,"SPECIES_UNOWN_P":427,"SPECIES_UNOWN_Q":428,"SPECIES_UNOWN_QMARK":439,"SPECIES_UNOWN_R":429,"SPECIES_UNOWN_S":430,"SPECIES_UNOWN_T":431,"SPECIES_UNOWN_U":432,"SPECIES_UNOWN_V":433,"SPECIES_UNOWN_W":434,"SPECIES_UNOWN_X":435,"SPECIES_UNOWN_Y":436,"SPECIES_UNOWN_Z":437,"SPECIES_URSARING":217,"SPECIES_VAPOREON":134,"SPECIES_VENOMOTH":49,"SPECIES_VENONAT":48,"SPECIES_VENUSAUR":3,"SPECIES_VIBRAVA":333,"SPECIES_VICTREEBEL":71,"SPECIES_VIGOROTH":365,"SPECIES_VILEPLUME":45,"SPECIES_VOLBEAT":386,"SPECIES_VOLTORB":100,"SPECIES_VULPIX":37,"SPECIES_WAILMER":313,"SPECIES_WAILORD":314,"SPECIES_WALREIN":343,"SPECIES_WARTORTLE":8,"SPECIES_WEEDLE":13,"SPECIES_WEEPINBELL":70,"SPECIES_WEEZING":110,"SPECIES_WHISCASH":324,"SPECIES_WHISMUR":370,"SPECIES_WIGGLYTUFF":40,"SPECIES_WINGULL":309,"SPECIES_WOBBUFFET":202,"SPECIES_WOOPER":194,"SPECIES_WURMPLE":290,"SPECIES_WYNAUT":360,"SPECIES_XATU":178,"SPECIES_YANMA":193,"SPECIES_ZANGOOSE":380,"SPECIES_ZAPDOS":145,"SPECIES_ZIGZAGOON":288,"SPECIES_ZUBAT":41,"SUPER_ROD":2,"SYSTEM_FLAGS":2144,"TEMP_FLAGS_END":31,"TEMP_FLAGS_START":0,"TRAINERS_COUNT":855,"TRAINER_AARON":397,"TRAINER_ABIGAIL_1":358,"TRAINER_ABIGAIL_2":360,"TRAINER_ABIGAIL_3":361,"TRAINER_ABIGAIL_4":362,"TRAINER_ABIGAIL_5":363,"TRAINER_AIDAN":674,"TRAINER_AISHA":757,"TRAINER_ALAN":630,"TRAINER_ALBERT":80,"TRAINER_ALBERTO":12,"TRAINER_ALEX":413,"TRAINER_ALEXA":670,"TRAINER_ALEXIA":90,"TRAINER_ALEXIS":248,"TRAINER_ALICE":448,"TRAINER_ALIX":750,"TRAINER_ALLEN":333,"TRAINER_ALLISON":387,"TRAINER_ALVARO":849,"TRAINER_ALYSSA":701,"TRAINER_AMY_AND_LIV_1":481,"TRAINER_AMY_AND_LIV_2":482,"TRAINER_AMY_AND_LIV_3":485,"TRAINER_AMY_AND_LIV_4":487,"TRAINER_AMY_AND_LIV_5":488,"TRAINER_AMY_AND_LIV_6":489,"TRAINER_ANABEL":805,"TRAINER_ANDREA":613,"TRAINER_ANDRES_1":737,"TRAINER_ANDRES_2":812,"TRAINER_ANDRES_3":813,"TRAINER_ANDRES_4":814,"TRAINER_ANDRES_5":815,"TRAINER_ANDREW":336,"TRAINER_ANGELICA":436,"TRAINER_ANGELINA":712,"TRAINER_ANGELO":802,"TRAINER_ANNA_AND_MEG_1":287,"TRAINER_ANNA_AND_MEG_2":288,"TRAINER_ANNA_AND_MEG_3":289,"TRAINER_ANNA_AND_MEG_4":290,"TRAINER_ANNA_AND_MEG_5":291,"TRAINER_ANNIKA":502,"TRAINER_ANTHONY":352,"TRAINER_ARCHIE":34,"TRAINER_ASHLEY":655,"TRAINER_ATHENA":577,"TRAINER_ATSUSHI":190,"TRAINER_AURON":506,"TRAINER_AUSTINA":58,"TRAINER_AUTUMN":217,"TRAINER_AXLE":203,"TRAINER_BARNY":343,"TRAINER_BARRY":163,"TRAINER_BEAU":212,"TRAINER_BECK":414,"TRAINER_BECKY":470,"TRAINER_BEN":323,"TRAINER_BENJAMIN_1":353,"TRAINER_BENJAMIN_2":354,"TRAINER_BENJAMIN_3":355,"TRAINER_BENJAMIN_4":356,"TRAINER_BENJAMIN_5":357,"TRAINER_BENNY":407,"TRAINER_BERKE":74,"TRAINER_BERNIE_1":206,"TRAINER_BERNIE_2":207,"TRAINER_BERNIE_3":208,"TRAINER_BERNIE_4":209,"TRAINER_BERNIE_5":210,"TRAINER_BETH":445,"TRAINER_BETHANY":301,"TRAINER_BEVERLY":441,"TRAINER_BIANCA":706,"TRAINER_BILLY":319,"TRAINER_BLAKE":235,"TRAINER_BRANDEN":745,"TRAINER_BRANDI":756,"TRAINER_BRANDON":811,"TRAINER_BRAWLY_1":266,"TRAINER_BRAWLY_2":774,"TRAINER_BRAWLY_3":775,"TRAINER_BRAWLY_4":776,"TRAINER_BRAWLY_5":777,"TRAINER_BRAXTON":75,"TRAINER_BRENDA":454,"TRAINER_BRENDAN_LILYCOVE_MUDKIP":661,"TRAINER_BRENDAN_LILYCOVE_TORCHIC":663,"TRAINER_BRENDAN_LILYCOVE_TREECKO":662,"TRAINER_BRENDAN_PLACEHOLDER":853,"TRAINER_BRENDAN_ROUTE_103_MUDKIP":520,"TRAINER_BRENDAN_ROUTE_103_TORCHIC":526,"TRAINER_BRENDAN_ROUTE_103_TREECKO":523,"TRAINER_BRENDAN_ROUTE_110_MUDKIP":521,"TRAINER_BRENDAN_ROUTE_110_TORCHIC":527,"TRAINER_BRENDAN_ROUTE_110_TREECKO":524,"TRAINER_BRENDAN_ROUTE_119_MUDKIP":522,"TRAINER_BRENDAN_ROUTE_119_TORCHIC":528,"TRAINER_BRENDAN_ROUTE_119_TREECKO":525,"TRAINER_BRENDAN_RUSTBORO_MUDKIP":593,"TRAINER_BRENDAN_RUSTBORO_TORCHIC":599,"TRAINER_BRENDAN_RUSTBORO_TREECKO":592,"TRAINER_BRENDEN":572,"TRAINER_BRENT":223,"TRAINER_BRIANNA":118,"TRAINER_BRICE":626,"TRAINER_BRIDGET":129,"TRAINER_BROOKE_1":94,"TRAINER_BROOKE_2":101,"TRAINER_BROOKE_3":102,"TRAINER_BROOKE_4":103,"TRAINER_BROOKE_5":104,"TRAINER_BRYAN":744,"TRAINER_BRYANT":746,"TRAINER_CALE":764,"TRAINER_CALLIE":763,"TRAINER_CALVIN_1":318,"TRAINER_CALVIN_2":328,"TRAINER_CALVIN_3":329,"TRAINER_CALVIN_4":330,"TRAINER_CALVIN_5":331,"TRAINER_CAMDEN":374,"TRAINER_CAMERON_1":238,"TRAINER_CAMERON_2":239,"TRAINER_CAMERON_3":240,"TRAINER_CAMERON_4":241,"TRAINER_CAMERON_5":242,"TRAINER_CAMRON":739,"TRAINER_CARLEE":464,"TRAINER_CAROL":471,"TRAINER_CAROLINA":741,"TRAINER_CAROLINE":99,"TRAINER_CARTER":345,"TRAINER_CATHERINE_1":559,"TRAINER_CATHERINE_2":562,"TRAINER_CATHERINE_3":563,"TRAINER_CATHERINE_4":564,"TRAINER_CATHERINE_5":565,"TRAINER_CEDRIC":475,"TRAINER_CELIA":743,"TRAINER_CELINA":705,"TRAINER_CHAD":174,"TRAINER_CHANDLER":698,"TRAINER_CHARLIE":66,"TRAINER_CHARLOTTE":714,"TRAINER_CHASE":378,"TRAINER_CHESTER":408,"TRAINER_CHIP":45,"TRAINER_CHRIS":693,"TRAINER_CINDY_1":114,"TRAINER_CINDY_2":117,"TRAINER_CINDY_3":120,"TRAINER_CINDY_4":121,"TRAINER_CINDY_5":122,"TRAINER_CINDY_6":123,"TRAINER_CLARENCE":580,"TRAINER_CLARISSA":435,"TRAINER_CLARK":631,"TRAINER_CLAUDE":338,"TRAINER_CLIFFORD":584,"TRAINER_COBY":709,"TRAINER_COLE":201,"TRAINER_COLIN":405,"TRAINER_COLTON":294,"TRAINER_CONNIE":128,"TRAINER_CONOR":511,"TRAINER_CORA":428,"TRAINER_CORY_1":740,"TRAINER_CORY_2":816,"TRAINER_CORY_3":817,"TRAINER_CORY_4":818,"TRAINER_CORY_5":819,"TRAINER_CRISSY":614,"TRAINER_CRISTIAN":574,"TRAINER_CRISTIN_1":767,"TRAINER_CRISTIN_2":828,"TRAINER_CRISTIN_3":829,"TRAINER_CRISTIN_4":830,"TRAINER_CRISTIN_5":831,"TRAINER_CYNDY_1":427,"TRAINER_CYNDY_2":430,"TRAINER_CYNDY_3":431,"TRAINER_CYNDY_4":432,"TRAINER_CYNDY_5":433,"TRAINER_DAISUKE":189,"TRAINER_DAISY":36,"TRAINER_DALE":341,"TRAINER_DALTON_1":196,"TRAINER_DALTON_2":197,"TRAINER_DALTON_3":198,"TRAINER_DALTON_4":199,"TRAINER_DALTON_5":200,"TRAINER_DANA":458,"TRAINER_DANIELLE":650,"TRAINER_DAPHNE":115,"TRAINER_DARCY":733,"TRAINER_DARIAN":696,"TRAINER_DARIUS":803,"TRAINER_DARRIN":154,"TRAINER_DAVID":158,"TRAINER_DAVIS":539,"TRAINER_DAWSON":694,"TRAINER_DAYTON":760,"TRAINER_DEAN":164,"TRAINER_DEANDRE":715,"TRAINER_DEBRA":460,"TRAINER_DECLAN":15,"TRAINER_DEMETRIUS":375,"TRAINER_DENISE":444,"TRAINER_DEREK":227,"TRAINER_DEVAN":753,"TRAINER_DEZ_AND_LUKE":640,"TRAINER_DIANA_1":474,"TRAINER_DIANA_2":477,"TRAINER_DIANA_3":478,"TRAINER_DIANA_4":479,"TRAINER_DIANA_5":480,"TRAINER_DIANNE":417,"TRAINER_DILLON":327,"TRAINER_DOMINIK":152,"TRAINER_DONALD":224,"TRAINER_DONNY":384,"TRAINER_DOUG":618,"TRAINER_DOUGLAS":153,"TRAINER_DRAKE":264,"TRAINER_DREW":211,"TRAINER_DUDLEY":173,"TRAINER_DUNCAN":496,"TRAINER_DUSTY_1":44,"TRAINER_DUSTY_2":47,"TRAINER_DUSTY_3":48,"TRAINER_DUSTY_4":49,"TRAINER_DUSTY_5":50,"TRAINER_DWAYNE":493,"TRAINER_DYLAN_1":364,"TRAINER_DYLAN_2":365,"TRAINER_DYLAN_3":366,"TRAINER_DYLAN_4":367,"TRAINER_DYLAN_5":368,"TRAINER_ED":13,"TRAINER_EDDIE":332,"TRAINER_EDGAR":79,"TRAINER_EDMOND":491,"TRAINER_EDWARD":232,"TRAINER_EDWARDO":404,"TRAINER_EDWIN_1":512,"TRAINER_EDWIN_2":515,"TRAINER_EDWIN_3":516,"TRAINER_EDWIN_4":517,"TRAINER_EDWIN_5":518,"TRAINER_ELI":501,"TRAINER_ELIJAH":742,"TRAINER_ELLIOT_1":339,"TRAINER_ELLIOT_2":346,"TRAINER_ELLIOT_3":347,"TRAINER_ELLIOT_4":348,"TRAINER_ELLIOT_5":349,"TRAINER_ERIC":632,"TRAINER_ERNEST_1":492,"TRAINER_ERNEST_2":497,"TRAINER_ERNEST_3":498,"TRAINER_ERNEST_4":499,"TRAINER_ERNEST_5":500,"TRAINER_ETHAN_1":216,"TRAINER_ETHAN_2":219,"TRAINER_ETHAN_3":220,"TRAINER_ETHAN_4":221,"TRAINER_ETHAN_5":222,"TRAINER_EVERETT":850,"TRAINER_FABIAN":759,"TRAINER_FELIX":38,"TRAINER_FERNANDO_1":195,"TRAINER_FERNANDO_2":832,"TRAINER_FERNANDO_3":833,"TRAINER_FERNANDO_4":834,"TRAINER_FERNANDO_5":835,"TRAINER_FLAGS_END":2143,"TRAINER_FLAGS_START":1280,"TRAINER_FLANNERY_1":268,"TRAINER_FLANNERY_2":782,"TRAINER_FLANNERY_3":783,"TRAINER_FLANNERY_4":784,"TRAINER_FLANNERY_5":785,"TRAINER_FLINT":654,"TRAINER_FOSTER":46,"TRAINER_FRANKLIN":170,"TRAINER_FREDRICK":29,"TRAINER_GABBY_AND_TY_1":51,"TRAINER_GABBY_AND_TY_2":52,"TRAINER_GABBY_AND_TY_3":53,"TRAINER_GABBY_AND_TY_4":54,"TRAINER_GABBY_AND_TY_5":55,"TRAINER_GABBY_AND_TY_6":56,"TRAINER_GABRIELLE_1":9,"TRAINER_GABRIELLE_2":840,"TRAINER_GABRIELLE_3":841,"TRAINER_GABRIELLE_4":842,"TRAINER_GABRIELLE_5":843,"TRAINER_GARRET":138,"TRAINER_GARRISON":547,"TRAINER_GEORGE":73,"TRAINER_GEORGIA":281,"TRAINER_GERALD":648,"TRAINER_GILBERT":169,"TRAINER_GINA_AND_MIA_1":483,"TRAINER_GINA_AND_MIA_2":486,"TRAINER_GLACIA":263,"TRAINER_GRACE":450,"TRAINER_GREG":619,"TRAINER_GRETA":808,"TRAINER_GRUNT_AQUA_HIDEOUT_1":2,"TRAINER_GRUNT_AQUA_HIDEOUT_2":3,"TRAINER_GRUNT_AQUA_HIDEOUT_3":4,"TRAINER_GRUNT_AQUA_HIDEOUT_4":5,"TRAINER_GRUNT_AQUA_HIDEOUT_5":27,"TRAINER_GRUNT_AQUA_HIDEOUT_6":28,"TRAINER_GRUNT_AQUA_HIDEOUT_7":192,"TRAINER_GRUNT_AQUA_HIDEOUT_8":193,"TRAINER_GRUNT_JAGGED_PASS":570,"TRAINER_GRUNT_MAGMA_HIDEOUT_1":716,"TRAINER_GRUNT_MAGMA_HIDEOUT_10":725,"TRAINER_GRUNT_MAGMA_HIDEOUT_11":726,"TRAINER_GRUNT_MAGMA_HIDEOUT_12":727,"TRAINER_GRUNT_MAGMA_HIDEOUT_13":728,"TRAINER_GRUNT_MAGMA_HIDEOUT_14":729,"TRAINER_GRUNT_MAGMA_HIDEOUT_15":730,"TRAINER_GRUNT_MAGMA_HIDEOUT_16":731,"TRAINER_GRUNT_MAGMA_HIDEOUT_2":717,"TRAINER_GRUNT_MAGMA_HIDEOUT_3":718,"TRAINER_GRUNT_MAGMA_HIDEOUT_4":719,"TRAINER_GRUNT_MAGMA_HIDEOUT_5":720,"TRAINER_GRUNT_MAGMA_HIDEOUT_6":721,"TRAINER_GRUNT_MAGMA_HIDEOUT_7":722,"TRAINER_GRUNT_MAGMA_HIDEOUT_8":723,"TRAINER_GRUNT_MAGMA_HIDEOUT_9":724,"TRAINER_GRUNT_MT_CHIMNEY_1":146,"TRAINER_GRUNT_MT_CHIMNEY_2":579,"TRAINER_GRUNT_MT_PYRE_1":23,"TRAINER_GRUNT_MT_PYRE_2":24,"TRAINER_GRUNT_MT_PYRE_3":25,"TRAINER_GRUNT_MT_PYRE_4":569,"TRAINER_GRUNT_MUSEUM_1":20,"TRAINER_GRUNT_MUSEUM_2":21,"TRAINER_GRUNT_PETALBURG_WOODS":10,"TRAINER_GRUNT_RUSTURF_TUNNEL":16,"TRAINER_GRUNT_SEAFLOOR_CAVERN_1":6,"TRAINER_GRUNT_SEAFLOOR_CAVERN_2":7,"TRAINER_GRUNT_SEAFLOOR_CAVERN_3":8,"TRAINER_GRUNT_SEAFLOOR_CAVERN_4":14,"TRAINER_GRUNT_SEAFLOOR_CAVERN_5":567,"TRAINER_GRUNT_SPACE_CENTER_1":22,"TRAINER_GRUNT_SPACE_CENTER_2":116,"TRAINER_GRUNT_SPACE_CENTER_3":586,"TRAINER_GRUNT_SPACE_CENTER_4":587,"TRAINER_GRUNT_SPACE_CENTER_5":588,"TRAINER_GRUNT_SPACE_CENTER_6":589,"TRAINER_GRUNT_SPACE_CENTER_7":590,"TRAINER_GRUNT_UNUSED":568,"TRAINER_GRUNT_WEATHER_INST_1":17,"TRAINER_GRUNT_WEATHER_INST_2":18,"TRAINER_GRUNT_WEATHER_INST_3":19,"TRAINER_GRUNT_WEATHER_INST_4":26,"TRAINER_GRUNT_WEATHER_INST_5":596,"TRAINER_GWEN":59,"TRAINER_HAILEY":697,"TRAINER_HALEY_1":604,"TRAINER_HALEY_2":607,"TRAINER_HALEY_3":608,"TRAINER_HALEY_4":609,"TRAINER_HALEY_5":610,"TRAINER_HALLE":546,"TRAINER_HANNAH":244,"TRAINER_HARRISON":578,"TRAINER_HAYDEN":707,"TRAINER_HECTOR":513,"TRAINER_HEIDI":469,"TRAINER_HELENE":751,"TRAINER_HENRY":668,"TRAINER_HERMAN":167,"TRAINER_HIDEO":651,"TRAINER_HITOSHI":180,"TRAINER_HOPE":96,"TRAINER_HUDSON":510,"TRAINER_HUEY":490,"TRAINER_HUGH":399,"TRAINER_HUMBERTO":402,"TRAINER_IMANI":442,"TRAINER_IRENE":476,"TRAINER_ISAAC_1":538,"TRAINER_ISAAC_2":541,"TRAINER_ISAAC_3":542,"TRAINER_ISAAC_4":543,"TRAINER_ISAAC_5":544,"TRAINER_ISABELLA":595,"TRAINER_ISABELLE":736,"TRAINER_ISABEL_1":302,"TRAINER_ISABEL_2":303,"TRAINER_ISABEL_3":304,"TRAINER_ISABEL_4":305,"TRAINER_ISABEL_5":306,"TRAINER_ISAIAH_1":376,"TRAINER_ISAIAH_2":379,"TRAINER_ISAIAH_3":380,"TRAINER_ISAIAH_4":381,"TRAINER_ISAIAH_5":382,"TRAINER_ISOBEL":383,"TRAINER_IVAN":337,"TRAINER_JACE":204,"TRAINER_JACK":172,"TRAINER_JACKI_1":249,"TRAINER_JACKI_2":250,"TRAINER_JACKI_3":251,"TRAINER_JACKI_4":252,"TRAINER_JACKI_5":253,"TRAINER_JACKSON_1":552,"TRAINER_JACKSON_2":555,"TRAINER_JACKSON_3":556,"TRAINER_JACKSON_4":557,"TRAINER_JACKSON_5":558,"TRAINER_JACLYN":243,"TRAINER_JACOB":351,"TRAINER_JAIDEN":749,"TRAINER_JAMES_1":621,"TRAINER_JAMES_2":622,"TRAINER_JAMES_3":623,"TRAINER_JAMES_4":624,"TRAINER_JAMES_5":625,"TRAINER_JANI":418,"TRAINER_JANICE":605,"TRAINER_JARED":401,"TRAINER_JASMINE":359,"TRAINER_JAYLEN":326,"TRAINER_JAZMYN":503,"TRAINER_JEFF":202,"TRAINER_JEFFREY_1":226,"TRAINER_JEFFREY_2":228,"TRAINER_JEFFREY_3":229,"TRAINER_JEFFREY_4":230,"TRAINER_JEFFREY_5":231,"TRAINER_JENNA":560,"TRAINER_JENNIFER":95,"TRAINER_JENNY_1":449,"TRAINER_JENNY_2":465,"TRAINER_JENNY_3":466,"TRAINER_JENNY_4":467,"TRAINER_JENNY_5":468,"TRAINER_JEROME":156,"TRAINER_JERRY_1":273,"TRAINER_JERRY_2":276,"TRAINER_JERRY_3":277,"TRAINER_JERRY_4":278,"TRAINER_JERRY_5":279,"TRAINER_JESSICA_1":127,"TRAINER_JESSICA_2":132,"TRAINER_JESSICA_3":133,"TRAINER_JESSICA_4":134,"TRAINER_JESSICA_5":135,"TRAINER_JOCELYN":425,"TRAINER_JODY":91,"TRAINER_JOEY":322,"TRAINER_JOHANNA":647,"TRAINER_JOHNSON":754,"TRAINER_JOHN_AND_JAY_1":681,"TRAINER_JOHN_AND_JAY_2":682,"TRAINER_JOHN_AND_JAY_3":683,"TRAINER_JOHN_AND_JAY_4":684,"TRAINER_JOHN_AND_JAY_5":685,"TRAINER_JONAH":667,"TRAINER_JONAS":504,"TRAINER_JONATHAN":598,"TRAINER_JOSE":617,"TRAINER_JOSEPH":700,"TRAINER_JOSH":320,"TRAINER_JOSHUA":237,"TRAINER_JOSUE":738,"TRAINER_JUAN_1":272,"TRAINER_JUAN_2":798,"TRAINER_JUAN_3":799,"TRAINER_JUAN_4":800,"TRAINER_JUAN_5":801,"TRAINER_JULIE":100,"TRAINER_JULIO":566,"TRAINER_JUSTIN":215,"TRAINER_KAI":713,"TRAINER_KALEB":699,"TRAINER_KARA":457,"TRAINER_KAREN_1":280,"TRAINER_KAREN_2":282,"TRAINER_KAREN_3":283,"TRAINER_KAREN_4":284,"TRAINER_KAREN_5":285,"TRAINER_KATELYNN":325,"TRAINER_KATELYN_1":386,"TRAINER_KATELYN_2":388,"TRAINER_KATELYN_3":389,"TRAINER_KATELYN_4":390,"TRAINER_KATELYN_5":391,"TRAINER_KATE_AND_JOY":286,"TRAINER_KATHLEEN":583,"TRAINER_KATIE":455,"TRAINER_KAYLA":247,"TRAINER_KAYLEE":462,"TRAINER_KAYLEY":505,"TRAINER_KEEGAN":205,"TRAINER_KEIGO":652,"TRAINER_KEIRA":93,"TRAINER_KELVIN":507,"TRAINER_KENT":620,"TRAINER_KEVIN":171,"TRAINER_KIM_AND_IRIS":678,"TRAINER_KINDRA":106,"TRAINER_KIRA_AND_DAN_1":642,"TRAINER_KIRA_AND_DAN_2":643,"TRAINER_KIRA_AND_DAN_3":644,"TRAINER_KIRA_AND_DAN_4":645,"TRAINER_KIRA_AND_DAN_5":646,"TRAINER_KIRK":191,"TRAINER_KIYO":181,"TRAINER_KOICHI":182,"TRAINER_KOJI_1":672,"TRAINER_KOJI_2":824,"TRAINER_KOJI_3":825,"TRAINER_KOJI_4":826,"TRAINER_KOJI_5":827,"TRAINER_KYLA":443,"TRAINER_KYRA":748,"TRAINER_LAO_1":419,"TRAINER_LAO_2":421,"TRAINER_LAO_3":422,"TRAINER_LAO_4":423,"TRAINER_LAO_5":424,"TRAINER_LARRY":213,"TRAINER_LAURA":426,"TRAINER_LAUREL":463,"TRAINER_LAWRENCE":710,"TRAINER_LEAF":852,"TRAINER_LEAH":35,"TRAINER_LEA_AND_JED":641,"TRAINER_LENNY":628,"TRAINER_LEONARD":495,"TRAINER_LEONARDO":576,"TRAINER_LEONEL":762,"TRAINER_LEROY":77,"TRAINER_LILA_AND_ROY_1":687,"TRAINER_LILA_AND_ROY_2":688,"TRAINER_LILA_AND_ROY_3":689,"TRAINER_LILA_AND_ROY_4":690,"TRAINER_LILA_AND_ROY_5":691,"TRAINER_LILITH":573,"TRAINER_LINDA":461,"TRAINER_LISA_AND_RAY":692,"TRAINER_LOLA_1":57,"TRAINER_LOLA_2":60,"TRAINER_LOLA_3":61,"TRAINER_LOLA_4":62,"TRAINER_LOLA_5":63,"TRAINER_LORENZO":553,"TRAINER_LUCAS_1":629,"TRAINER_LUCAS_2":633,"TRAINER_LUCY":810,"TRAINER_LUIS":151,"TRAINER_LUNG":420,"TRAINER_LYDIA_1":545,"TRAINER_LYDIA_2":548,"TRAINER_LYDIA_3":549,"TRAINER_LYDIA_4":550,"TRAINER_LYDIA_5":551,"TRAINER_LYLE":616,"TRAINER_MACEY":591,"TRAINER_MADELINE_1":434,"TRAINER_MADELINE_2":437,"TRAINER_MADELINE_3":438,"TRAINER_MADELINE_4":439,"TRAINER_MADELINE_5":440,"TRAINER_MAKAYLA":758,"TRAINER_MARC":571,"TRAINER_MARCEL":11,"TRAINER_MARCOS":702,"TRAINER_MARIA_1":369,"TRAINER_MARIA_2":370,"TRAINER_MARIA_3":371,"TRAINER_MARIA_4":372,"TRAINER_MARIA_5":373,"TRAINER_MARIELA":848,"TRAINER_MARK":145,"TRAINER_MARLENE":752,"TRAINER_MARLEY":508,"TRAINER_MARTHA":473,"TRAINER_MARY":89,"TRAINER_MATT":30,"TRAINER_MATTHEW":157,"TRAINER_MAURA":246,"TRAINER_MAXIE_MAGMA_HIDEOUT":601,"TRAINER_MAXIE_MOSSDEEP":734,"TRAINER_MAXIE_MT_CHIMNEY":602,"TRAINER_MAY_LILYCOVE_MUDKIP":664,"TRAINER_MAY_LILYCOVE_TORCHIC":666,"TRAINER_MAY_LILYCOVE_TREECKO":665,"TRAINER_MAY_PLACEHOLDER":854,"TRAINER_MAY_ROUTE_103_MUDKIP":529,"TRAINER_MAY_ROUTE_103_TORCHIC":535,"TRAINER_MAY_ROUTE_103_TREECKO":532,"TRAINER_MAY_ROUTE_110_MUDKIP":530,"TRAINER_MAY_ROUTE_110_TORCHIC":536,"TRAINER_MAY_ROUTE_110_TREECKO":533,"TRAINER_MAY_ROUTE_119_MUDKIP":531,"TRAINER_MAY_ROUTE_119_TORCHIC":537,"TRAINER_MAY_ROUTE_119_TREECKO":534,"TRAINER_MAY_RUSTBORO_MUDKIP":600,"TRAINER_MAY_RUSTBORO_TORCHIC":769,"TRAINER_MAY_RUSTBORO_TREECKO":768,"TRAINER_MELINA":755,"TRAINER_MELISSA":124,"TRAINER_MEL_AND_PAUL":680,"TRAINER_MICAH":255,"TRAINER_MICHELLE":98,"TRAINER_MIGUEL_1":293,"TRAINER_MIGUEL_2":295,"TRAINER_MIGUEL_3":296,"TRAINER_MIGUEL_4":297,"TRAINER_MIGUEL_5":298,"TRAINER_MIKE_1":634,"TRAINER_MIKE_2":635,"TRAINER_MISSY":447,"TRAINER_MITCHELL":540,"TRAINER_MIU_AND_YUKI":484,"TRAINER_MOLLIE":137,"TRAINER_MYLES":765,"TRAINER_NANCY":472,"TRAINER_NAOMI":119,"TRAINER_NATE":582,"TRAINER_NED":340,"TRAINER_NICHOLAS":585,"TRAINER_NICOLAS_1":392,"TRAINER_NICOLAS_2":393,"TRAINER_NICOLAS_3":394,"TRAINER_NICOLAS_4":395,"TRAINER_NICOLAS_5":396,"TRAINER_NIKKI":453,"TRAINER_NOB_1":183,"TRAINER_NOB_2":184,"TRAINER_NOB_3":185,"TRAINER_NOB_4":186,"TRAINER_NOB_5":187,"TRAINER_NOLAN":342,"TRAINER_NOLAND":809,"TRAINER_NOLEN":161,"TRAINER_NONE":0,"TRAINER_NORMAN_1":269,"TRAINER_NORMAN_2":786,"TRAINER_NORMAN_3":787,"TRAINER_NORMAN_4":788,"TRAINER_NORMAN_5":789,"TRAINER_OLIVIA":130,"TRAINER_OWEN":83,"TRAINER_PABLO_1":377,"TRAINER_PABLO_2":820,"TRAINER_PABLO_3":821,"TRAINER_PABLO_4":822,"TRAINER_PABLO_5":823,"TRAINER_PARKER":72,"TRAINER_PAT":766,"TRAINER_PATRICIA":105,"TRAINER_PAUL":275,"TRAINER_PAULA":429,"TRAINER_PAXTON":594,"TRAINER_PERRY":398,"TRAINER_PETE":735,"TRAINER_PHIL":400,"TRAINER_PHILLIP":494,"TRAINER_PHOEBE":262,"TRAINER_PRESLEY":403,"TRAINER_PRESTON":233,"TRAINER_QUINCY":324,"TRAINER_RACHEL":761,"TRAINER_RANDALL":71,"TRAINER_RED":851,"TRAINER_REED":675,"TRAINER_RELI_AND_IAN":686,"TRAINER_REYNA":509,"TRAINER_RHETT":703,"TRAINER_RICHARD":166,"TRAINER_RICK":615,"TRAINER_RICKY_1":64,"TRAINER_RICKY_2":67,"TRAINER_RICKY_3":68,"TRAINER_RICKY_4":69,"TRAINER_RICKY_5":70,"TRAINER_RILEY":653,"TRAINER_ROBERT_1":406,"TRAINER_ROBERT_2":409,"TRAINER_ROBERT_3":410,"TRAINER_ROBERT_4":411,"TRAINER_ROBERT_5":412,"TRAINER_ROBIN":612,"TRAINER_RODNEY":165,"TRAINER_ROGER":669,"TRAINER_ROLAND":160,"TRAINER_RONALD":350,"TRAINER_ROSE_1":37,"TRAINER_ROSE_2":40,"TRAINER_ROSE_3":41,"TRAINER_ROSE_4":42,"TRAINER_ROSE_5":43,"TRAINER_ROXANNE_1":265,"TRAINER_ROXANNE_2":770,"TRAINER_ROXANNE_3":771,"TRAINER_ROXANNE_4":772,"TRAINER_ROXANNE_5":773,"TRAINER_RUBEN":671,"TRAINER_SALLY":611,"TRAINER_SAMANTHA":245,"TRAINER_SAMUEL":81,"TRAINER_SANTIAGO":168,"TRAINER_SARAH":695,"TRAINER_SAWYER_1":1,"TRAINER_SAWYER_2":836,"TRAINER_SAWYER_3":837,"TRAINER_SAWYER_4":838,"TRAINER_SAWYER_5":839,"TRAINER_SEBASTIAN":554,"TRAINER_SHANE":214,"TRAINER_SHANNON":97,"TRAINER_SHARON":452,"TRAINER_SHAWN":194,"TRAINER_SHAYLA":747,"TRAINER_SHEILA":125,"TRAINER_SHELBY_1":313,"TRAINER_SHELBY_2":314,"TRAINER_SHELBY_3":315,"TRAINER_SHELBY_4":316,"TRAINER_SHELBY_5":317,"TRAINER_SHELLY_SEAFLOOR_CAVERN":33,"TRAINER_SHELLY_WEATHER_INSTITUTE":32,"TRAINER_SHIRLEY":126,"TRAINER_SIDNEY":261,"TRAINER_SIENNA":459,"TRAINER_SIMON":65,"TRAINER_SOPHIA":561,"TRAINER_SOPHIE":708,"TRAINER_SPENCER":159,"TRAINER_SPENSER":807,"TRAINER_STAN":162,"TRAINER_STEVEN":804,"TRAINER_STEVE_1":143,"TRAINER_STEVE_2":147,"TRAINER_STEVE_3":148,"TRAINER_STEVE_4":149,"TRAINER_STEVE_5":150,"TRAINER_SUSIE":456,"TRAINER_SYLVIA":575,"TRAINER_TABITHA_MAGMA_HIDEOUT":732,"TRAINER_TABITHA_MOSSDEEP":514,"TRAINER_TABITHA_MT_CHIMNEY":597,"TRAINER_TAKAO":179,"TRAINER_TAKASHI":416,"TRAINER_TALIA":385,"TRAINER_TAMMY":107,"TRAINER_TANYA":451,"TRAINER_TARA":446,"TRAINER_TASHA":109,"TRAINER_TATE_AND_LIZA_1":271,"TRAINER_TATE_AND_LIZA_2":794,"TRAINER_TATE_AND_LIZA_3":795,"TRAINER_TATE_AND_LIZA_4":796,"TRAINER_TATE_AND_LIZA_5":797,"TRAINER_TAYLOR":225,"TRAINER_TED":274,"TRAINER_TERRY":581,"TRAINER_THALIA_1":144,"TRAINER_THALIA_2":844,"TRAINER_THALIA_3":845,"TRAINER_THALIA_4":846,"TRAINER_THALIA_5":847,"TRAINER_THOMAS":256,"TRAINER_TIANA":603,"TRAINER_TIFFANY":131,"TRAINER_TIMMY":334,"TRAINER_TIMOTHY_1":307,"TRAINER_TIMOTHY_2":308,"TRAINER_TIMOTHY_3":309,"TRAINER_TIMOTHY_4":310,"TRAINER_TIMOTHY_5":311,"TRAINER_TISHA":676,"TRAINER_TOMMY":321,"TRAINER_TONY_1":155,"TRAINER_TONY_2":175,"TRAINER_TONY_3":176,"TRAINER_TONY_4":177,"TRAINER_TONY_5":178,"TRAINER_TORI_AND_TIA":677,"TRAINER_TRAVIS":218,"TRAINER_TRENT_1":627,"TRAINER_TRENT_2":636,"TRAINER_TRENT_3":637,"TRAINER_TRENT_4":638,"TRAINER_TRENT_5":639,"TRAINER_TUCKER":806,"TRAINER_TYRA_AND_IVY":679,"TRAINER_TYRON":704,"TRAINER_VALERIE_1":108,"TRAINER_VALERIE_2":110,"TRAINER_VALERIE_3":111,"TRAINER_VALERIE_4":112,"TRAINER_VALERIE_5":113,"TRAINER_VANESSA":300,"TRAINER_VICKY":312,"TRAINER_VICTOR":292,"TRAINER_VICTORIA":299,"TRAINER_VINCENT":76,"TRAINER_VIOLET":39,"TRAINER_VIRGIL":234,"TRAINER_VITO":82,"TRAINER_VIVI":606,"TRAINER_VIVIAN":649,"TRAINER_WADE":344,"TRAINER_WALLACE":335,"TRAINER_WALLY_MAUVILLE":656,"TRAINER_WALLY_VR_1":519,"TRAINER_WALLY_VR_2":657,"TRAINER_WALLY_VR_3":658,"TRAINER_WALLY_VR_4":659,"TRAINER_WALLY_VR_5":660,"TRAINER_WALTER_1":254,"TRAINER_WALTER_2":257,"TRAINER_WALTER_3":258,"TRAINER_WALTER_4":259,"TRAINER_WALTER_5":260,"TRAINER_WARREN":88,"TRAINER_WATTSON_1":267,"TRAINER_WATTSON_2":778,"TRAINER_WATTSON_3":779,"TRAINER_WATTSON_4":780,"TRAINER_WATTSON_5":781,"TRAINER_WAYNE":673,"TRAINER_WENDY":92,"TRAINER_WILLIAM":236,"TRAINER_WILTON_1":78,"TRAINER_WILTON_2":84,"TRAINER_WILTON_3":85,"TRAINER_WILTON_4":86,"TRAINER_WILTON_5":87,"TRAINER_WINONA_1":270,"TRAINER_WINONA_2":790,"TRAINER_WINONA_3":791,"TRAINER_WINONA_4":792,"TRAINER_WINONA_5":793,"TRAINER_WINSTON_1":136,"TRAINER_WINSTON_2":139,"TRAINER_WINSTON_3":140,"TRAINER_WINSTON_4":141,"TRAINER_WINSTON_5":142,"TRAINER_WYATT":711,"TRAINER_YASU":415,"TRAINER_YUJI":188,"TRAINER_ZANDER":31},"legendary_encounters":[{"address":2538600,"catch_flag":429,"defeat_flag":428,"level":30,"species":410},{"address":2354334,"catch_flag":480,"defeat_flag":447,"level":70,"species":405},{"address":2543160,"catch_flag":146,"defeat_flag":476,"level":70,"species":250},{"address":2354112,"catch_flag":479,"defeat_flag":446,"level":70,"species":404},{"address":2385623,"catch_flag":457,"defeat_flag":456,"level":50,"species":407},{"address":2385687,"catch_flag":482,"defeat_flag":481,"level":50,"species":408},{"address":2543443,"catch_flag":145,"defeat_flag":477,"level":70,"species":249},{"address":2538177,"catch_flag":458,"defeat_flag":455,"level":30,"species":151},{"address":2347488,"catch_flag":478,"defeat_flag":448,"level":70,"species":406},{"address":2345460,"catch_flag":427,"defeat_flag":444,"level":40,"species":402},{"address":2298183,"catch_flag":426,"defeat_flag":443,"level":40,"species":401},{"address":2345731,"catch_flag":483,"defeat_flag":445,"level":40,"species":403}],"locations":{"BADGE_1":{"address":2188036,"default_item":226,"flag":1182},"BADGE_2":{"address":2095131,"default_item":227,"flag":1183},"BADGE_3":{"address":2167252,"default_item":228,"flag":1184},"BADGE_4":{"address":2103246,"default_item":229,"flag":1185},"BADGE_5":{"address":2129781,"default_item":230,"flag":1186},"BADGE_6":{"address":2202122,"default_item":231,"flag":1187},"BADGE_7":{"address":2243964,"default_item":232,"flag":1188},"BADGE_8":{"address":2262314,"default_item":233,"flag":1189},"BERRY_TREE_01":{"address":5843562,"default_item":135,"flag":612},"BERRY_TREE_02":{"address":5843564,"default_item":139,"flag":613},"BERRY_TREE_03":{"address":5843566,"default_item":142,"flag":614},"BERRY_TREE_04":{"address":5843568,"default_item":139,"flag":615},"BERRY_TREE_05":{"address":5843570,"default_item":133,"flag":616},"BERRY_TREE_06":{"address":5843572,"default_item":138,"flag":617},"BERRY_TREE_07":{"address":5843574,"default_item":133,"flag":618},"BERRY_TREE_08":{"address":5843576,"default_item":133,"flag":619},"BERRY_TREE_09":{"address":5843578,"default_item":142,"flag":620},"BERRY_TREE_10":{"address":5843580,"default_item":138,"flag":621},"BERRY_TREE_11":{"address":5843582,"default_item":139,"flag":622},"BERRY_TREE_12":{"address":5843584,"default_item":142,"flag":623},"BERRY_TREE_13":{"address":5843586,"default_item":135,"flag":624},"BERRY_TREE_14":{"address":5843588,"default_item":155,"flag":625},"BERRY_TREE_15":{"address":5843590,"default_item":153,"flag":626},"BERRY_TREE_16":{"address":5843592,"default_item":150,"flag":627},"BERRY_TREE_17":{"address":5843594,"default_item":150,"flag":628},"BERRY_TREE_18":{"address":5843596,"default_item":150,"flag":629},"BERRY_TREE_19":{"address":5843598,"default_item":148,"flag":630},"BERRY_TREE_20":{"address":5843600,"default_item":148,"flag":631},"BERRY_TREE_21":{"address":5843602,"default_item":136,"flag":632},"BERRY_TREE_22":{"address":5843604,"default_item":135,"flag":633},"BERRY_TREE_23":{"address":5843606,"default_item":135,"flag":634},"BERRY_TREE_24":{"address":5843608,"default_item":136,"flag":635},"BERRY_TREE_25":{"address":5843610,"default_item":152,"flag":636},"BERRY_TREE_26":{"address":5843612,"default_item":134,"flag":637},"BERRY_TREE_27":{"address":5843614,"default_item":151,"flag":638},"BERRY_TREE_28":{"address":5843616,"default_item":151,"flag":639},"BERRY_TREE_29":{"address":5843618,"default_item":151,"flag":640},"BERRY_TREE_30":{"address":5843620,"default_item":153,"flag":641},"BERRY_TREE_31":{"address":5843622,"default_item":142,"flag":642},"BERRY_TREE_32":{"address":5843624,"default_item":142,"flag":643},"BERRY_TREE_33":{"address":5843626,"default_item":142,"flag":644},"BERRY_TREE_34":{"address":5843628,"default_item":153,"flag":645},"BERRY_TREE_35":{"address":5843630,"default_item":153,"flag":646},"BERRY_TREE_36":{"address":5843632,"default_item":153,"flag":647},"BERRY_TREE_37":{"address":5843634,"default_item":137,"flag":648},"BERRY_TREE_38":{"address":5843636,"default_item":137,"flag":649},"BERRY_TREE_39":{"address":5843638,"default_item":137,"flag":650},"BERRY_TREE_40":{"address":5843640,"default_item":135,"flag":651},"BERRY_TREE_41":{"address":5843642,"default_item":135,"flag":652},"BERRY_TREE_42":{"address":5843644,"default_item":135,"flag":653},"BERRY_TREE_43":{"address":5843646,"default_item":148,"flag":654},"BERRY_TREE_44":{"address":5843648,"default_item":150,"flag":655},"BERRY_TREE_45":{"address":5843650,"default_item":152,"flag":656},"BERRY_TREE_46":{"address":5843652,"default_item":151,"flag":657},"BERRY_TREE_47":{"address":5843654,"default_item":140,"flag":658},"BERRY_TREE_48":{"address":5843656,"default_item":137,"flag":659},"BERRY_TREE_49":{"address":5843658,"default_item":136,"flag":660},"BERRY_TREE_50":{"address":5843660,"default_item":134,"flag":661},"BERRY_TREE_51":{"address":5843662,"default_item":142,"flag":662},"BERRY_TREE_52":{"address":5843664,"default_item":150,"flag":663},"BERRY_TREE_53":{"address":5843666,"default_item":150,"flag":664},"BERRY_TREE_54":{"address":5843668,"default_item":142,"flag":665},"BERRY_TREE_55":{"address":5843670,"default_item":149,"flag":666},"BERRY_TREE_56":{"address":5843672,"default_item":149,"flag":667},"BERRY_TREE_57":{"address":5843674,"default_item":136,"flag":668},"BERRY_TREE_58":{"address":5843676,"default_item":153,"flag":669},"BERRY_TREE_59":{"address":5843678,"default_item":153,"flag":670},"BERRY_TREE_60":{"address":5843680,"default_item":157,"flag":671},"BERRY_TREE_61":{"address":5843682,"default_item":157,"flag":672},"BERRY_TREE_62":{"address":5843684,"default_item":138,"flag":673},"BERRY_TREE_63":{"address":5843686,"default_item":142,"flag":674},"BERRY_TREE_64":{"address":5843688,"default_item":138,"flag":675},"BERRY_TREE_65":{"address":5843690,"default_item":157,"flag":676},"BERRY_TREE_66":{"address":5843692,"default_item":134,"flag":677},"BERRY_TREE_67":{"address":5843694,"default_item":152,"flag":678},"BERRY_TREE_68":{"address":5843696,"default_item":140,"flag":679},"BERRY_TREE_69":{"address":5843698,"default_item":154,"flag":680},"BERRY_TREE_70":{"address":5843700,"default_item":154,"flag":681},"BERRY_TREE_71":{"address":5843702,"default_item":154,"flag":682},"BERRY_TREE_72":{"address":5843704,"default_item":157,"flag":683},"BERRY_TREE_73":{"address":5843706,"default_item":155,"flag":684},"BERRY_TREE_74":{"address":5843708,"default_item":155,"flag":685},"BERRY_TREE_75":{"address":5843710,"default_item":142,"flag":686},"BERRY_TREE_76":{"address":5843712,"default_item":133,"flag":687},"BERRY_TREE_77":{"address":5843714,"default_item":140,"flag":688},"BERRY_TREE_78":{"address":5843716,"default_item":140,"flag":689},"BERRY_TREE_79":{"address":5843718,"default_item":155,"flag":690},"BERRY_TREE_80":{"address":5843720,"default_item":139,"flag":691},"BERRY_TREE_81":{"address":5843722,"default_item":139,"flag":692},"BERRY_TREE_82":{"address":5843724,"default_item":168,"flag":693},"BERRY_TREE_83":{"address":5843726,"default_item":156,"flag":694},"BERRY_TREE_84":{"address":5843728,"default_item":156,"flag":695},"BERRY_TREE_85":{"address":5843730,"default_item":142,"flag":696},"BERRY_TREE_86":{"address":5843732,"default_item":138,"flag":697},"BERRY_TREE_87":{"address":5843734,"default_item":135,"flag":698},"BERRY_TREE_88":{"address":5843736,"default_item":142,"flag":699},"HIDDEN_ITEM_ABANDONED_SHIP_RM_1_KEY":{"address":5497200,"default_item":281,"flag":531},"HIDDEN_ITEM_ABANDONED_SHIP_RM_2_KEY":{"address":5497212,"default_item":282,"flag":532},"HIDDEN_ITEM_ABANDONED_SHIP_RM_4_KEY":{"address":5497224,"default_item":283,"flag":533},"HIDDEN_ITEM_ABANDONED_SHIP_RM_6_KEY":{"address":5497236,"default_item":284,"flag":534},"HIDDEN_ITEM_ARTISAN_CAVE_B1F_CALCIUM":{"address":5500100,"default_item":67,"flag":601},"HIDDEN_ITEM_ARTISAN_CAVE_B1F_IRON":{"address":5500124,"default_item":65,"flag":604},"HIDDEN_ITEM_ARTISAN_CAVE_B1F_PROTEIN":{"address":5500112,"default_item":64,"flag":603},"HIDDEN_ITEM_ARTISAN_CAVE_B1F_ZINC":{"address":5500088,"default_item":70,"flag":602},"HIDDEN_ITEM_FALLARBOR_TOWN_NUGGET":{"address":5435924,"default_item":110,"flag":528},"HIDDEN_ITEM_GRANITE_CAVE_B2F_EVERSTONE_1":{"address":5487372,"default_item":195,"flag":548},"HIDDEN_ITEM_GRANITE_CAVE_B2F_EVERSTONE_2":{"address":5487384,"default_item":195,"flag":549},"HIDDEN_ITEM_JAGGED_PASS_FULL_HEAL":{"address":5489116,"default_item":23,"flag":577},"HIDDEN_ITEM_JAGGED_PASS_GREAT_BALL":{"address":5489128,"default_item":3,"flag":576},"HIDDEN_ITEM_LAVARIDGE_TOWN_ICE_HEAL":{"address":5435672,"default_item":16,"flag":500},"HIDDEN_ITEM_LILYCOVE_CITY_HEART_SCALE":{"address":5432608,"default_item":111,"flag":527},"HIDDEN_ITEM_LILYCOVE_CITY_POKE_BALL":{"address":5432632,"default_item":4,"flag":575},"HIDDEN_ITEM_LILYCOVE_CITY_PP_UP":{"address":5432620,"default_item":69,"flag":543},"HIDDEN_ITEM_MT_PYRE_EXTERIOR_MAX_ETHER":{"address":5490440,"default_item":35,"flag":578},"HIDDEN_ITEM_MT_PYRE_EXTERIOR_ULTRA_BALL":{"address":5490428,"default_item":2,"flag":529},"HIDDEN_ITEM_MT_PYRE_SUMMIT_RARE_CANDY":{"address":5490796,"default_item":68,"flag":580},"HIDDEN_ITEM_MT_PYRE_SUMMIT_ZINC":{"address":5490784,"default_item":70,"flag":579},"HIDDEN_ITEM_NAVEL_ROCK_TOP_SACRED_ASH":{"address":5525804,"default_item":45,"flag":609},"HIDDEN_ITEM_PETALBURG_CITY_RARE_CANDY":{"address":5428972,"default_item":68,"flag":595},"HIDDEN_ITEM_PETALBURG_WOODS_POKE_BALL":{"address":5487908,"default_item":4,"flag":561},"HIDDEN_ITEM_PETALBURG_WOODS_POTION":{"address":5487872,"default_item":13,"flag":558},"HIDDEN_ITEM_PETALBURG_WOODS_TINY_MUSHROOM_1":{"address":5487884,"default_item":103,"flag":559},"HIDDEN_ITEM_PETALBURG_WOODS_TINY_MUSHROOM_2":{"address":5487896,"default_item":103,"flag":560},"HIDDEN_ITEM_ROUTE_104_ANTIDOTE":{"address":5438492,"default_item":14,"flag":585},"HIDDEN_ITEM_ROUTE_104_HEART_SCALE":{"address":5438504,"default_item":111,"flag":588},"HIDDEN_ITEM_ROUTE_104_POKE_BALL":{"address":5438468,"default_item":4,"flag":562},"HIDDEN_ITEM_ROUTE_104_POTION":{"address":5438480,"default_item":13,"flag":537},"HIDDEN_ITEM_ROUTE_104_SUPER_POTION":{"address":5438456,"default_item":22,"flag":544},"HIDDEN_ITEM_ROUTE_105_BIG_PEARL":{"address":5438748,"default_item":107,"flag":611},"HIDDEN_ITEM_ROUTE_105_HEART_SCALE":{"address":5438736,"default_item":111,"flag":589},"HIDDEN_ITEM_ROUTE_106_HEART_SCALE":{"address":5438932,"default_item":111,"flag":547},"HIDDEN_ITEM_ROUTE_106_POKE_BALL":{"address":5438908,"default_item":4,"flag":563},"HIDDEN_ITEM_ROUTE_106_STARDUST":{"address":5438920,"default_item":108,"flag":546},"HIDDEN_ITEM_ROUTE_108_RARE_CANDY":{"address":5439340,"default_item":68,"flag":586},"HIDDEN_ITEM_ROUTE_109_ETHER":{"address":5440016,"default_item":34,"flag":564},"HIDDEN_ITEM_ROUTE_109_GREAT_BALL":{"address":5440004,"default_item":3,"flag":551},"HIDDEN_ITEM_ROUTE_109_HEART_SCALE_1":{"address":5439992,"default_item":111,"flag":552},"HIDDEN_ITEM_ROUTE_109_HEART_SCALE_2":{"address":5440028,"default_item":111,"flag":590},"HIDDEN_ITEM_ROUTE_109_HEART_SCALE_3":{"address":5440040,"default_item":111,"flag":591},"HIDDEN_ITEM_ROUTE_109_REVIVE":{"address":5439980,"default_item":24,"flag":550},"HIDDEN_ITEM_ROUTE_110_FULL_HEAL":{"address":5441308,"default_item":23,"flag":555},"HIDDEN_ITEM_ROUTE_110_GREAT_BALL":{"address":5441284,"default_item":3,"flag":553},"HIDDEN_ITEM_ROUTE_110_POKE_BALL":{"address":5441296,"default_item":4,"flag":565},"HIDDEN_ITEM_ROUTE_110_REVIVE":{"address":5441272,"default_item":24,"flag":554},"HIDDEN_ITEM_ROUTE_111_PROTEIN":{"address":5443220,"default_item":64,"flag":556},"HIDDEN_ITEM_ROUTE_111_RARE_CANDY":{"address":5443232,"default_item":68,"flag":557},"HIDDEN_ITEM_ROUTE_111_STARDUST":{"address":5443160,"default_item":108,"flag":502},"HIDDEN_ITEM_ROUTE_113_ETHER":{"address":5444488,"default_item":34,"flag":503},"HIDDEN_ITEM_ROUTE_113_NUGGET":{"address":5444512,"default_item":110,"flag":598},"HIDDEN_ITEM_ROUTE_113_TM_DOUBLE_TEAM":{"address":5444500,"default_item":320,"flag":530},"HIDDEN_ITEM_ROUTE_114_CARBOS":{"address":5445340,"default_item":66,"flag":504},"HIDDEN_ITEM_ROUTE_114_REVIVE":{"address":5445364,"default_item":24,"flag":542},"HIDDEN_ITEM_ROUTE_115_HEART_SCALE":{"address":5446176,"default_item":111,"flag":597},"HIDDEN_ITEM_ROUTE_116_BLACK_GLASSES":{"address":5447056,"default_item":206,"flag":596},"HIDDEN_ITEM_ROUTE_116_SUPER_POTION":{"address":5447044,"default_item":22,"flag":545},"HIDDEN_ITEM_ROUTE_117_REPEL":{"address":5447708,"default_item":86,"flag":572},"HIDDEN_ITEM_ROUTE_118_HEART_SCALE":{"address":5448404,"default_item":111,"flag":566},"HIDDEN_ITEM_ROUTE_118_IRON":{"address":5448392,"default_item":65,"flag":567},"HIDDEN_ITEM_ROUTE_119_CALCIUM":{"address":5449972,"default_item":67,"flag":505},"HIDDEN_ITEM_ROUTE_119_FULL_HEAL":{"address":5450056,"default_item":23,"flag":568},"HIDDEN_ITEM_ROUTE_119_MAX_ETHER":{"address":5450068,"default_item":35,"flag":587},"HIDDEN_ITEM_ROUTE_119_ULTRA_BALL":{"address":5449984,"default_item":2,"flag":506},"HIDDEN_ITEM_ROUTE_120_RARE_CANDY_1":{"address":5451596,"default_item":68,"flag":571},"HIDDEN_ITEM_ROUTE_120_RARE_CANDY_2":{"address":5451620,"default_item":68,"flag":569},"HIDDEN_ITEM_ROUTE_120_REVIVE":{"address":5451608,"default_item":24,"flag":584},"HIDDEN_ITEM_ROUTE_120_ZINC":{"address":5451632,"default_item":70,"flag":570},"HIDDEN_ITEM_ROUTE_121_FULL_HEAL":{"address":5452540,"default_item":23,"flag":573},"HIDDEN_ITEM_ROUTE_121_HP_UP":{"address":5452516,"default_item":63,"flag":539},"HIDDEN_ITEM_ROUTE_121_MAX_REVIVE":{"address":5452552,"default_item":25,"flag":600},"HIDDEN_ITEM_ROUTE_121_NUGGET":{"address":5452528,"default_item":110,"flag":540},"HIDDEN_ITEM_ROUTE_123_HYPER_POTION":{"address":5454100,"default_item":21,"flag":574},"HIDDEN_ITEM_ROUTE_123_PP_UP":{"address":5454112,"default_item":69,"flag":599},"HIDDEN_ITEM_ROUTE_123_RARE_CANDY":{"address":5454124,"default_item":68,"flag":610},"HIDDEN_ITEM_ROUTE_123_REVIVE":{"address":5454088,"default_item":24,"flag":541},"HIDDEN_ITEM_ROUTE_123_SUPER_REPEL":{"address":5454052,"default_item":83,"flag":507},"HIDDEN_ITEM_ROUTE_128_HEART_SCALE_1":{"address":5455620,"default_item":111,"flag":592},"HIDDEN_ITEM_ROUTE_128_HEART_SCALE_2":{"address":5455632,"default_item":111,"flag":593},"HIDDEN_ITEM_ROUTE_128_HEART_SCALE_3":{"address":5455644,"default_item":111,"flag":594},"HIDDEN_ITEM_SAFARI_ZONE_NORTH_EAST_RARE_CANDY":{"address":5517256,"default_item":68,"flag":606},"HIDDEN_ITEM_SAFARI_ZONE_NORTH_EAST_ZINC":{"address":5517268,"default_item":70,"flag":607},"HIDDEN_ITEM_SAFARI_ZONE_SOUTH_EAST_FULL_RESTORE":{"address":5517432,"default_item":19,"flag":605},"HIDDEN_ITEM_SAFARI_ZONE_SOUTH_EAST_PP_UP":{"address":5517420,"default_item":69,"flag":608},"HIDDEN_ITEM_SS_TIDAL_LOWER_DECK_LEFTOVERS":{"address":5511292,"default_item":200,"flag":535},"HIDDEN_ITEM_TRICK_HOUSE_NUGGET":{"address":5526716,"default_item":110,"flag":501},"HIDDEN_ITEM_UNDERWATER_124_BIG_PEARL":{"address":5456992,"default_item":107,"flag":511},"HIDDEN_ITEM_UNDERWATER_124_CALCIUM":{"address":5457016,"default_item":67,"flag":536},"HIDDEN_ITEM_UNDERWATER_124_CARBOS":{"address":5456956,"default_item":66,"flag":508},"HIDDEN_ITEM_UNDERWATER_124_GREEN_SHARD":{"address":5456968,"default_item":51,"flag":509},"HIDDEN_ITEM_UNDERWATER_124_HEART_SCALE_1":{"address":5457004,"default_item":111,"flag":513},"HIDDEN_ITEM_UNDERWATER_124_HEART_SCALE_2":{"address":5457028,"default_item":111,"flag":538},"HIDDEN_ITEM_UNDERWATER_124_PEARL":{"address":5456980,"default_item":106,"flag":510},"HIDDEN_ITEM_UNDERWATER_126_BIG_PEARL":{"address":5457140,"default_item":107,"flag":520},"HIDDEN_ITEM_UNDERWATER_126_BLUE_SHARD":{"address":5457152,"default_item":49,"flag":512},"HIDDEN_ITEM_UNDERWATER_126_HEART_SCALE":{"address":5457068,"default_item":111,"flag":514},"HIDDEN_ITEM_UNDERWATER_126_IRON":{"address":5457116,"default_item":65,"flag":519},"HIDDEN_ITEM_UNDERWATER_126_PEARL":{"address":5457104,"default_item":106,"flag":517},"HIDDEN_ITEM_UNDERWATER_126_STARDUST":{"address":5457092,"default_item":108,"flag":516},"HIDDEN_ITEM_UNDERWATER_126_ULTRA_BALL":{"address":5457080,"default_item":2,"flag":515},"HIDDEN_ITEM_UNDERWATER_126_YELLOW_SHARD":{"address":5457128,"default_item":50,"flag":518},"HIDDEN_ITEM_UNDERWATER_127_HEART_SCALE":{"address":5457224,"default_item":111,"flag":523},"HIDDEN_ITEM_UNDERWATER_127_HP_UP":{"address":5457212,"default_item":63,"flag":522},"HIDDEN_ITEM_UNDERWATER_127_RED_SHARD":{"address":5457236,"default_item":48,"flag":524},"HIDDEN_ITEM_UNDERWATER_127_STAR_PIECE":{"address":5457200,"default_item":109,"flag":521},"HIDDEN_ITEM_UNDERWATER_128_PEARL":{"address":5457288,"default_item":106,"flag":526},"HIDDEN_ITEM_UNDERWATER_128_PROTEIN":{"address":5457276,"default_item":64,"flag":525},"HIDDEN_ITEM_VICTORY_ROAD_1F_ULTRA_BALL":{"address":5493932,"default_item":2,"flag":581},"HIDDEN_ITEM_VICTORY_ROAD_B2F_ELIXIR":{"address":5494744,"default_item":36,"flag":582},"HIDDEN_ITEM_VICTORY_ROAD_B2F_MAX_REPEL":{"address":5494756,"default_item":84,"flag":583},"ITEM_ABANDONED_SHIP_CAPTAINS_OFFICE_STORAGE_KEY":{"address":2709805,"default_item":285,"flag":1100},"ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_1_TM_RAIN_DANCE":{"address":2709857,"default_item":306,"flag":1102},"ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_2_SCANNER":{"address":2709831,"default_item":278,"flag":1078},"ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_3_WATER_STONE":{"address":2709844,"default_item":97,"flag":1101},"ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_6_LUXURY_BALL":{"address":2709818,"default_item":11,"flag":1077},"ITEM_ABANDONED_SHIP_ROOMS_1F_HARBOR_MAIL":{"address":2709740,"default_item":122,"flag":1095},"ITEM_ABANDONED_SHIP_ROOMS_2_1F_REVIVE":{"address":2709792,"default_item":24,"flag":1099},"ITEM_ABANDONED_SHIP_ROOMS_2_B1F_DIVE_BALL":{"address":2709766,"default_item":7,"flag":1097},"ITEM_ABANDONED_SHIP_ROOMS_B1F_ESCAPE_ROPE":{"address":2709753,"default_item":85,"flag":1096},"ITEM_ABANDONED_SHIP_ROOMS_B1F_TM_ICE_BEAM":{"address":2709779,"default_item":301,"flag":1098},"ITEM_AQUA_HIDEOUT_B1F_MASTER_BALL":{"address":2710039,"default_item":1,"flag":1124},"ITEM_AQUA_HIDEOUT_B1F_MAX_ELIXIR":{"address":2710065,"default_item":37,"flag":1071},"ITEM_AQUA_HIDEOUT_B1F_NUGGET":{"address":2710052,"default_item":110,"flag":1132},"ITEM_AQUA_HIDEOUT_B2F_NEST_BALL":{"address":2710078,"default_item":8,"flag":1072},"ITEM_ARTISAN_CAVE_1F_CARBOS":{"address":2710416,"default_item":66,"flag":1163},"ITEM_ARTISAN_CAVE_B1F_HP_UP":{"address":2710403,"default_item":63,"flag":1162},"ITEM_FIERY_PATH_FIRE_STONE":{"address":2709584,"default_item":95,"flag":1111},"ITEM_FIERY_PATH_TM_TOXIC":{"address":2709597,"default_item":294,"flag":1091},"ITEM_GRANITE_CAVE_1F_ESCAPE_ROPE":{"address":2709519,"default_item":85,"flag":1050},"ITEM_GRANITE_CAVE_B1F_POKE_BALL":{"address":2709532,"default_item":4,"flag":1051},"ITEM_GRANITE_CAVE_B2F_RARE_CANDY":{"address":2709558,"default_item":68,"flag":1054},"ITEM_GRANITE_CAVE_B2F_REPEL":{"address":2709545,"default_item":86,"flag":1053},"ITEM_JAGGED_PASS_BURN_HEAL":{"address":2709571,"default_item":15,"flag":1070},"ITEM_LILYCOVE_CITY_MAX_REPEL":{"address":2709415,"default_item":84,"flag":1042},"ITEM_MAGMA_HIDEOUT_1F_RARE_CANDY":{"address":2710429,"default_item":68,"flag":1151},"ITEM_MAGMA_HIDEOUT_2F_2R_FULL_RESTORE":{"address":2710455,"default_item":19,"flag":1165},"ITEM_MAGMA_HIDEOUT_2F_2R_MAX_ELIXIR":{"address":2710442,"default_item":37,"flag":1164},"ITEM_MAGMA_HIDEOUT_3F_1R_NUGGET":{"address":2710468,"default_item":110,"flag":1166},"ITEM_MAGMA_HIDEOUT_3F_2R_PP_MAX":{"address":2710481,"default_item":71,"flag":1167},"ITEM_MAGMA_HIDEOUT_3F_3R_ECAPE_ROPE":{"address":2710507,"default_item":85,"flag":1059},"ITEM_MAGMA_HIDEOUT_4F_MAX_REVIVE":{"address":2710494,"default_item":25,"flag":1168},"ITEM_MAUVILLE_CITY_X_SPEED":{"address":2709389,"default_item":77,"flag":1116},"ITEM_METEOR_FALLS_1F_1R_FULL_HEAL":{"address":2709623,"default_item":23,"flag":1045},"ITEM_METEOR_FALLS_1F_1R_MOON_STONE":{"address":2709636,"default_item":94,"flag":1046},"ITEM_METEOR_FALLS_1F_1R_PP_UP":{"address":2709649,"default_item":69,"flag":1047},"ITEM_METEOR_FALLS_1F_1R_TM_IRON_TAIL":{"address":2709610,"default_item":311,"flag":1044},"ITEM_METEOR_FALLS_B1F_2R_TM_DRAGON_CLAW":{"address":2709662,"default_item":290,"flag":1080},"ITEM_MOSSDEEP_CITY_NET_BALL":{"address":2709428,"default_item":6,"flag":1043},"ITEM_MT_PYRE_2F_ULTRA_BALL":{"address":2709948,"default_item":2,"flag":1129},"ITEM_MT_PYRE_3F_SUPER_REPEL":{"address":2709961,"default_item":83,"flag":1120},"ITEM_MT_PYRE_4F_SEA_INCENSE":{"address":2709974,"default_item":220,"flag":1130},"ITEM_MT_PYRE_5F_LAX_INCENSE":{"address":2709987,"default_item":221,"flag":1052},"ITEM_MT_PYRE_6F_TM_SHADOW_BALL":{"address":2710000,"default_item":318,"flag":1089},"ITEM_MT_PYRE_EXTERIOR_MAX_POTION":{"address":2710013,"default_item":20,"flag":1073},"ITEM_MT_PYRE_EXTERIOR_TM_SKILL_SWAP":{"address":2710026,"default_item":336,"flag":1074},"ITEM_NEW_MAUVILLE_ESCAPE_ROPE":{"address":2709688,"default_item":85,"flag":1076},"ITEM_NEW_MAUVILLE_FULL_HEAL":{"address":2709714,"default_item":23,"flag":1122},"ITEM_NEW_MAUVILLE_PARALYZE_HEAL":{"address":2709727,"default_item":18,"flag":1123},"ITEM_NEW_MAUVILLE_THUNDER_STONE":{"address":2709701,"default_item":96,"flag":1110},"ITEM_NEW_MAUVILLE_ULTRA_BALL":{"address":2709675,"default_item":2,"flag":1075},"ITEM_PETALBURG_CITY_ETHER":{"address":2709376,"default_item":34,"flag":1040},"ITEM_PETALBURG_CITY_MAX_REVIVE":{"address":2709363,"default_item":25,"flag":1039},"ITEM_PETALBURG_WOODS_ETHER":{"address":2709467,"default_item":34,"flag":1058},"ITEM_PETALBURG_WOODS_GREAT_BALL":{"address":2709454,"default_item":3,"flag":1056},"ITEM_PETALBURG_WOODS_PARALYZE_HEAL":{"address":2709480,"default_item":18,"flag":1117},"ITEM_PETALBURG_WOODS_X_ATTACK":{"address":2709441,"default_item":75,"flag":1055},"ITEM_ROUTE_102_POTION":{"address":2708375,"default_item":13,"flag":1000},"ITEM_ROUTE_103_GUARD_SPEC":{"address":2708388,"default_item":73,"flag":1114},"ITEM_ROUTE_103_PP_UP":{"address":2708401,"default_item":69,"flag":1137},"ITEM_ROUTE_104_POKE_BALL":{"address":2708427,"default_item":4,"flag":1057},"ITEM_ROUTE_104_POTION":{"address":2708453,"default_item":13,"flag":1135},"ITEM_ROUTE_104_PP_UP":{"address":2708414,"default_item":69,"flag":1002},"ITEM_ROUTE_104_X_ACCURACY":{"address":2708440,"default_item":78,"flag":1115},"ITEM_ROUTE_105_IRON":{"address":2708466,"default_item":65,"flag":1003},"ITEM_ROUTE_106_PROTEIN":{"address":2708479,"default_item":64,"flag":1004},"ITEM_ROUTE_108_STAR_PIECE":{"address":2708492,"default_item":109,"flag":1139},"ITEM_ROUTE_109_POTION":{"address":2708518,"default_item":13,"flag":1140},"ITEM_ROUTE_109_PP_UP":{"address":2708505,"default_item":69,"flag":1005},"ITEM_ROUTE_110_DIRE_HIT":{"address":2708544,"default_item":74,"flag":1007},"ITEM_ROUTE_110_ELIXIR":{"address":2708557,"default_item":36,"flag":1141},"ITEM_ROUTE_110_RARE_CANDY":{"address":2708531,"default_item":68,"flag":1006},"ITEM_ROUTE_111_ELIXIR":{"address":2708609,"default_item":36,"flag":1142},"ITEM_ROUTE_111_HP_UP":{"address":2708596,"default_item":63,"flag":1010},"ITEM_ROUTE_111_STARDUST":{"address":2708583,"default_item":108,"flag":1009},"ITEM_ROUTE_111_TM_SANDSTORM":{"address":2708570,"default_item":325,"flag":1008},"ITEM_ROUTE_112_NUGGET":{"address":2708622,"default_item":110,"flag":1011},"ITEM_ROUTE_113_HYPER_POTION":{"address":2708661,"default_item":21,"flag":1143},"ITEM_ROUTE_113_MAX_ETHER":{"address":2708635,"default_item":35,"flag":1012},"ITEM_ROUTE_113_SUPER_REPEL":{"address":2708648,"default_item":83,"flag":1013},"ITEM_ROUTE_114_ENERGY_POWDER":{"address":2708700,"default_item":30,"flag":1160},"ITEM_ROUTE_114_PROTEIN":{"address":2708687,"default_item":64,"flag":1015},"ITEM_ROUTE_114_RARE_CANDY":{"address":2708674,"default_item":68,"flag":1014},"ITEM_ROUTE_115_GREAT_BALL":{"address":2708752,"default_item":3,"flag":1118},"ITEM_ROUTE_115_HEAL_POWDER":{"address":2708765,"default_item":32,"flag":1144},"ITEM_ROUTE_115_IRON":{"address":2708739,"default_item":65,"flag":1018},"ITEM_ROUTE_115_PP_UP":{"address":2708778,"default_item":69,"flag":1161},"ITEM_ROUTE_115_SUPER_POTION":{"address":2708713,"default_item":22,"flag":1016},"ITEM_ROUTE_115_TM_FOCUS_PUNCH":{"address":2708726,"default_item":289,"flag":1017},"ITEM_ROUTE_116_ETHER":{"address":2708804,"default_item":34,"flag":1019},"ITEM_ROUTE_116_HP_UP":{"address":2708830,"default_item":63,"flag":1021},"ITEM_ROUTE_116_POTION":{"address":2708843,"default_item":13,"flag":1146},"ITEM_ROUTE_116_REPEL":{"address":2708817,"default_item":86,"flag":1020},"ITEM_ROUTE_116_X_SPECIAL":{"address":2708791,"default_item":79,"flag":1001},"ITEM_ROUTE_117_GREAT_BALL":{"address":2708856,"default_item":3,"flag":1022},"ITEM_ROUTE_117_REVIVE":{"address":2708869,"default_item":24,"flag":1023},"ITEM_ROUTE_118_HYPER_POTION":{"address":2708882,"default_item":21,"flag":1121},"ITEM_ROUTE_119_ELIXIR_1":{"address":2708921,"default_item":36,"flag":1026},"ITEM_ROUTE_119_ELIXIR_2":{"address":2708986,"default_item":36,"flag":1147},"ITEM_ROUTE_119_HYPER_POTION_1":{"address":2708960,"default_item":21,"flag":1029},"ITEM_ROUTE_119_HYPER_POTION_2":{"address":2708973,"default_item":21,"flag":1106},"ITEM_ROUTE_119_LEAF_STONE":{"address":2708934,"default_item":98,"flag":1027},"ITEM_ROUTE_119_NUGGET":{"address":2710104,"default_item":110,"flag":1134},"ITEM_ROUTE_119_RARE_CANDY":{"address":2708947,"default_item":68,"flag":1028},"ITEM_ROUTE_119_SUPER_REPEL":{"address":2708895,"default_item":83,"flag":1024},"ITEM_ROUTE_119_ZINC":{"address":2708908,"default_item":70,"flag":1025},"ITEM_ROUTE_120_FULL_HEAL":{"address":2709012,"default_item":23,"flag":1031},"ITEM_ROUTE_120_HYPER_POTION":{"address":2709025,"default_item":21,"flag":1107},"ITEM_ROUTE_120_NEST_BALL":{"address":2709038,"default_item":8,"flag":1108},"ITEM_ROUTE_120_NUGGET":{"address":2708999,"default_item":110,"flag":1030},"ITEM_ROUTE_120_REVIVE":{"address":2709051,"default_item":24,"flag":1148},"ITEM_ROUTE_121_CARBOS":{"address":2709064,"default_item":66,"flag":1103},"ITEM_ROUTE_121_REVIVE":{"address":2709077,"default_item":24,"flag":1149},"ITEM_ROUTE_121_ZINC":{"address":2709090,"default_item":70,"flag":1150},"ITEM_ROUTE_123_CALCIUM":{"address":2709103,"default_item":67,"flag":1032},"ITEM_ROUTE_123_ELIXIR":{"address":2709129,"default_item":36,"flag":1109},"ITEM_ROUTE_123_PP_UP":{"address":2709142,"default_item":69,"flag":1152},"ITEM_ROUTE_123_REVIVAL_HERB":{"address":2709155,"default_item":33,"flag":1153},"ITEM_ROUTE_123_ULTRA_BALL":{"address":2709116,"default_item":2,"flag":1104},"ITEM_ROUTE_124_BLUE_SHARD":{"address":2709181,"default_item":49,"flag":1093},"ITEM_ROUTE_124_RED_SHARD":{"address":2709168,"default_item":48,"flag":1092},"ITEM_ROUTE_124_YELLOW_SHARD":{"address":2709194,"default_item":50,"flag":1066},"ITEM_ROUTE_125_BIG_PEARL":{"address":2709207,"default_item":107,"flag":1154},"ITEM_ROUTE_126_GREEN_SHARD":{"address":2709220,"default_item":51,"flag":1105},"ITEM_ROUTE_127_CARBOS":{"address":2709246,"default_item":66,"flag":1035},"ITEM_ROUTE_127_RARE_CANDY":{"address":2709259,"default_item":68,"flag":1155},"ITEM_ROUTE_127_ZINC":{"address":2709233,"default_item":70,"flag":1034},"ITEM_ROUTE_132_PROTEIN":{"address":2709285,"default_item":64,"flag":1156},"ITEM_ROUTE_132_RARE_CANDY":{"address":2709272,"default_item":68,"flag":1036},"ITEM_ROUTE_133_BIG_PEARL":{"address":2709298,"default_item":107,"flag":1037},"ITEM_ROUTE_133_MAX_REVIVE":{"address":2709324,"default_item":25,"flag":1157},"ITEM_ROUTE_133_STAR_PIECE":{"address":2709311,"default_item":109,"flag":1038},"ITEM_ROUTE_134_CARBOS":{"address":2709337,"default_item":66,"flag":1158},"ITEM_ROUTE_134_STAR_PIECE":{"address":2709350,"default_item":109,"flag":1159},"ITEM_RUSTBORO_CITY_X_DEFEND":{"address":2709402,"default_item":76,"flag":1041},"ITEM_RUSTURF_TUNNEL_MAX_ETHER":{"address":2709506,"default_item":35,"flag":1049},"ITEM_RUSTURF_TUNNEL_POKE_BALL":{"address":2709493,"default_item":4,"flag":1048},"ITEM_SAFARI_ZONE_NORTH_CALCIUM":{"address":2709896,"default_item":67,"flag":1119},"ITEM_SAFARI_ZONE_NORTH_EAST_NUGGET":{"address":2709922,"default_item":110,"flag":1169},"ITEM_SAFARI_ZONE_NORTH_WEST_TM_SOLAR_BEAM":{"address":2709883,"default_item":310,"flag":1094},"ITEM_SAFARI_ZONE_SOUTH_EAST_BIG_PEARL":{"address":2709935,"default_item":107,"flag":1170},"ITEM_SAFARI_ZONE_SOUTH_WEST_MAX_REVIVE":{"address":2709909,"default_item":25,"flag":1131},"ITEM_SCORCHED_SLAB_TM_SUNNY_DAY":{"address":2709870,"default_item":299,"flag":1079},"ITEM_SEAFLOOR_CAVERN_ROOM_9_TM_EARTHQUAKE":{"address":2710208,"default_item":314,"flag":1090},"ITEM_SHOAL_CAVE_ENTRANCE_BIG_PEARL":{"address":2710143,"default_item":107,"flag":1081},"ITEM_SHOAL_CAVE_ICE_ROOM_NEVER_MELT_ICE":{"address":2710195,"default_item":212,"flag":1113},"ITEM_SHOAL_CAVE_ICE_ROOM_TM_HAIL":{"address":2710182,"default_item":295,"flag":1112},"ITEM_SHOAL_CAVE_INNER_ROOM_RARE_CANDY":{"address":2710156,"default_item":68,"flag":1082},"ITEM_SHOAL_CAVE_STAIRS_ROOM_ICE_HEAL":{"address":2710169,"default_item":16,"flag":1083},"ITEM_TRICK_HOUSE_PUZZLE_1_ORANGE_MAIL":{"address":[2710221,2551006],"default_item":121,"flag":1060},"ITEM_TRICK_HOUSE_PUZZLE_2_HARBOR_MAIL":{"address":[2710234,2551032],"default_item":122,"flag":1061},"ITEM_TRICK_HOUSE_PUZZLE_2_WAVE_MAIL":{"address":[2710247,2551058],"default_item":126,"flag":1062},"ITEM_TRICK_HOUSE_PUZZLE_3_SHADOW_MAIL":{"address":[2710260,2551084],"default_item":128,"flag":1063},"ITEM_TRICK_HOUSE_PUZZLE_3_WOOD_MAIL":{"address":[2710273,2551110],"default_item":125,"flag":1064},"ITEM_TRICK_HOUSE_PUZZLE_4_MECH_MAIL":{"address":[2710286,2551136],"default_item":124,"flag":1065},"ITEM_TRICK_HOUSE_PUZZLE_6_GLITTER_MAIL":{"address":[2710299,2551162],"default_item":123,"flag":1067},"ITEM_TRICK_HOUSE_PUZZLE_7_TROPIC_MAIL":{"address":[2710312,2551188],"default_item":129,"flag":1068},"ITEM_TRICK_HOUSE_PUZZLE_8_BEAD_MAIL":{"address":[2710325,2551214],"default_item":127,"flag":1069},"ITEM_VICTORY_ROAD_1F_MAX_ELIXIR":{"address":2710338,"default_item":37,"flag":1084},"ITEM_VICTORY_ROAD_1F_PP_UP":{"address":2710351,"default_item":69,"flag":1085},"ITEM_VICTORY_ROAD_B1F_FULL_RESTORE":{"address":2710377,"default_item":19,"flag":1087},"ITEM_VICTORY_ROAD_B1F_TM_PSYCHIC":{"address":2710364,"default_item":317,"flag":1086},"ITEM_VICTORY_ROAD_B2F_FULL_HEAL":{"address":2710390,"default_item":23,"flag":1088},"NPC_GIFT_BERRY_MASTERS_WIFE":{"address":2570453,"default_item":133,"flag":1197},"NPC_GIFT_BERRY_MASTER_RECEIVED_BERRY_1":{"address":2570263,"default_item":153,"flag":1195},"NPC_GIFT_BERRY_MASTER_RECEIVED_BERRY_2":{"address":2570315,"default_item":154,"flag":1196},"NPC_GIFT_FLOWER_SHOP_RECEIVED_BERRY":{"address":2284375,"default_item":133,"flag":1207},"NPC_GIFT_GOT_BASEMENT_KEY_FROM_WATTSON":{"address":1971718,"default_item":271,"flag":208},"NPC_GIFT_GOT_TM_THUNDERBOLT_FROM_WATTSON":{"address":1971754,"default_item":312,"flag":209},"NPC_GIFT_LILYCOVE_RECEIVED_BERRY":{"address":1985277,"default_item":141,"flag":1208},"NPC_GIFT_RECEIVED_6_SODA_POP":{"address":2543767,"default_item":27,"flag":140},"NPC_GIFT_RECEIVED_ACRO_BIKE":{"address":2170570,"default_item":272,"flag":1181},"NPC_GIFT_RECEIVED_AMULET_COIN":{"address":2716248,"default_item":189,"flag":133},"NPC_GIFT_RECEIVED_AURORA_TICKET":{"address":2716523,"default_item":371,"flag":314},"NPC_GIFT_RECEIVED_CHARCOAL":{"address":2102559,"default_item":215,"flag":254},"NPC_GIFT_RECEIVED_CHESTO_BERRY_ROUTE_104":{"address":2028703,"default_item":134,"flag":246},"NPC_GIFT_RECEIVED_CLEANSE_TAG":{"address":2312109,"default_item":190,"flag":282},"NPC_GIFT_RECEIVED_COIN_CASE":{"address":2179054,"default_item":260,"flag":258},"NPC_GIFT_RECEIVED_DEEP_SEA_SCALE":{"address":2162572,"default_item":193,"flag":1190},"NPC_GIFT_RECEIVED_DEEP_SEA_TOOTH":{"address":2162555,"default_item":192,"flag":1191},"NPC_GIFT_RECEIVED_DEVON_GOODS_RUSTURF_TUNNEL":{"address":2295814,"default_item":269,"flag":1172},"NPC_GIFT_RECEIVED_DEVON_SCOPE":{"address":2065146,"default_item":288,"flag":285},"NPC_GIFT_RECEIVED_EON_TICKET":{"address":2716574,"default_item":275,"flag":474},"NPC_GIFT_RECEIVED_EXP_SHARE":{"address":2185525,"default_item":182,"flag":272},"NPC_GIFT_RECEIVED_FIRST_POKEBALLS":{"address":2085751,"default_item":4,"flag":233},"NPC_GIFT_RECEIVED_FOCUS_BAND":{"address":2337807,"default_item":196,"flag":283},"NPC_GIFT_RECEIVED_GOOD_ROD":{"address":2058408,"default_item":263,"flag":227},"NPC_GIFT_RECEIVED_GO_GOGGLES":{"address":2017746,"default_item":279,"flag":221},"NPC_GIFT_RECEIVED_GREAT_BALL_PETALBURG_WOODS":{"address":2300119,"default_item":3,"flag":1171},"NPC_GIFT_RECEIVED_GREAT_BALL_RUSTBORO_CITY":{"address":1977146,"default_item":3,"flag":1173},"NPC_GIFT_RECEIVED_HM_CUT":{"address":2199532,"default_item":339,"flag":137},"NPC_GIFT_RECEIVED_HM_DIVE":{"address":2252095,"default_item":346,"flag":123},"NPC_GIFT_RECEIVED_HM_FLASH":{"address":2298287,"default_item":343,"flag":109},"NPC_GIFT_RECEIVED_HM_FLY":{"address":2060636,"default_item":340,"flag":110},"NPC_GIFT_RECEIVED_HM_ROCK_SMASH":{"address":2174128,"default_item":344,"flag":107},"NPC_GIFT_RECEIVED_HM_STRENGTH":{"address":2295305,"default_item":342,"flag":106},"NPC_GIFT_RECEIVED_HM_SURF":{"address":2126671,"default_item":341,"flag":122},"NPC_GIFT_RECEIVED_HM_WATERFALL":{"address":1999854,"default_item":345,"flag":312},"NPC_GIFT_RECEIVED_ITEMFINDER":{"address":2039874,"default_item":261,"flag":1176},"NPC_GIFT_RECEIVED_KINGS_ROCK":{"address":1993670,"default_item":187,"flag":276},"NPC_GIFT_RECEIVED_LETTER":{"address":2185301,"default_item":274,"flag":1174},"NPC_GIFT_RECEIVED_MACHO_BRACE":{"address":2284472,"default_item":181,"flag":277},"NPC_GIFT_RECEIVED_MACH_BIKE":{"address":2170553,"default_item":259,"flag":1180},"NPC_GIFT_RECEIVED_MAGMA_EMBLEM":{"address":2316671,"default_item":375,"flag":1177},"NPC_GIFT_RECEIVED_MENTAL_HERB":{"address":2208103,"default_item":185,"flag":223},"NPC_GIFT_RECEIVED_METEORITE":{"address":2304222,"default_item":280,"flag":115},"NPC_GIFT_RECEIVED_MIRACLE_SEED":{"address":2300337,"default_item":205,"flag":297},"NPC_GIFT_RECEIVED_MYSTIC_TICKET":{"address":2716540,"default_item":370,"flag":315},"NPC_GIFT_RECEIVED_OLD_ROD":{"address":2012541,"default_item":262,"flag":257},"NPC_GIFT_RECEIVED_OLD_SEA_MAP":{"address":2716557,"default_item":376,"flag":316},"NPC_GIFT_RECEIVED_POKEBLOCK_CASE":{"address":2614193,"default_item":273,"flag":95},"NPC_GIFT_RECEIVED_POTION_OLDALE":{"address":2010888,"default_item":13,"flag":132},"NPC_GIFT_RECEIVED_POWDER_JAR":{"address":1962504,"default_item":372,"flag":337},"NPC_GIFT_RECEIVED_PREMIER_BALL_RUSTBORO":{"address":2200571,"default_item":12,"flag":213},"NPC_GIFT_RECEIVED_QUICK_CLAW":{"address":2192227,"default_item":183,"flag":275},"NPC_GIFT_RECEIVED_REPEAT_BALL":{"address":2053722,"default_item":9,"flag":256},"NPC_GIFT_RECEIVED_SECRET_POWER":{"address":2598914,"default_item":331,"flag":96},"NPC_GIFT_RECEIVED_SILK_SCARF":{"address":2101830,"default_item":217,"flag":289},"NPC_GIFT_RECEIVED_SOFT_SAND":{"address":2035664,"default_item":203,"flag":280},"NPC_GIFT_RECEIVED_SOOTHE_BELL":{"address":2151278,"default_item":184,"flag":278},"NPC_GIFT_RECEIVED_SOOT_SACK":{"address":2567245,"default_item":270,"flag":1033},"NPC_GIFT_RECEIVED_SS_TICKET":{"address":2716506,"default_item":265,"flag":291},"NPC_GIFT_RECEIVED_SUN_STONE_MOSSDEEP":{"address":2254406,"default_item":93,"flag":192},"NPC_GIFT_RECEIVED_SUPER_ROD":{"address":2251560,"default_item":264,"flag":152},"NPC_GIFT_RECEIVED_TM_AERIAL_ACE":{"address":2202201,"default_item":328,"flag":170},"NPC_GIFT_RECEIVED_TM_ATTRACT":{"address":2116413,"default_item":333,"flag":235},"NPC_GIFT_RECEIVED_TM_BRICK_BREAK":{"address":2269085,"default_item":319,"flag":121},"NPC_GIFT_RECEIVED_TM_BULK_UP":{"address":2095210,"default_item":296,"flag":166},"NPC_GIFT_RECEIVED_TM_BULLET_SEED":{"address":2028910,"default_item":297,"flag":262},"NPC_GIFT_RECEIVED_TM_CALM_MIND":{"address":2244066,"default_item":292,"flag":171},"NPC_GIFT_RECEIVED_TM_DIG":{"address":2286669,"default_item":316,"flag":261},"NPC_GIFT_RECEIVED_TM_FACADE":{"address":2129909,"default_item":330,"flag":169},"NPC_GIFT_RECEIVED_TM_FRUSTRATION":{"address":2124110,"default_item":309,"flag":1179},"NPC_GIFT_RECEIVED_TM_GIGA_DRAIN":{"address":2068012,"default_item":307,"flag":232},"NPC_GIFT_RECEIVED_TM_HIDDEN_POWER":{"address":2206905,"default_item":298,"flag":264},"NPC_GIFT_RECEIVED_TM_OVERHEAT":{"address":2103328,"default_item":338,"flag":168},"NPC_GIFT_RECEIVED_TM_REST":{"address":2236966,"default_item":332,"flag":234},"NPC_GIFT_RECEIVED_TM_RETURN":{"address":2113546,"default_item":315,"flag":229},"NPC_GIFT_RECEIVED_TM_RETURN_2":{"address":2124055,"default_item":315,"flag":1178},"NPC_GIFT_RECEIVED_TM_ROAR":{"address":2051750,"default_item":293,"flag":231},"NPC_GIFT_RECEIVED_TM_ROCK_TOMB":{"address":2188088,"default_item":327,"flag":165},"NPC_GIFT_RECEIVED_TM_SHOCK_WAVE":{"address":2167340,"default_item":322,"flag":167},"NPC_GIFT_RECEIVED_TM_SLUDGE_BOMB":{"address":2099189,"default_item":324,"flag":230},"NPC_GIFT_RECEIVED_TM_SNATCH":{"address":2360766,"default_item":337,"flag":260},"NPC_GIFT_RECEIVED_TM_STEEL_WING":{"address":2298866,"default_item":335,"flag":1175},"NPC_GIFT_RECEIVED_TM_THIEF":{"address":2154698,"default_item":334,"flag":269},"NPC_GIFT_RECEIVED_TM_TORMENT":{"address":2145260,"default_item":329,"flag":265},"NPC_GIFT_RECEIVED_TM_WATER_PULSE":{"address":2262402,"default_item":291,"flag":172},"NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_1":{"address":2550316,"default_item":68,"flag":1200},"NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_2":{"address":2550390,"default_item":10,"flag":1201},"NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_3":{"address":2550473,"default_item":204,"flag":1202},"NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_4":{"address":2550556,"default_item":194,"flag":1203},"NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_5":{"address":2550630,"default_item":300,"flag":1204},"NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_6":{"address":2550695,"default_item":208,"flag":1205},"NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_7":{"address":2550769,"default_item":71,"flag":1206},"NPC_GIFT_RECEIVED_WAILMER_PAIL":{"address":2284320,"default_item":268,"flag":94},"NPC_GIFT_RECEIVED_WHITE_HERB":{"address":2028770,"default_item":180,"flag":279},"NPC_GIFT_ROUTE_111_RECEIVED_BERRY":{"address":2045493,"default_item":148,"flag":1192},"NPC_GIFT_ROUTE_114_RECEIVED_BERRY":{"address":2051680,"default_item":149,"flag":1193},"NPC_GIFT_ROUTE_120_RECEIVED_BERRY":{"address":2064727,"default_item":143,"flag":1194},"NPC_GIFT_SOOTOPOLIS_RECEIVED_BERRY_1":{"address":1998521,"default_item":153,"flag":1198},"NPC_GIFT_SOOTOPOLIS_RECEIVED_BERRY_2":{"address":1998566,"default_item":143,"flag":1199},"POKEDEX_REWARD_001":{"address":5729368,"default_item":3,"flag":0},"POKEDEX_REWARD_002":{"address":5729370,"default_item":3,"flag":0},"POKEDEX_REWARD_003":{"address":5729372,"default_item":3,"flag":0},"POKEDEX_REWARD_004":{"address":5729374,"default_item":3,"flag":0},"POKEDEX_REWARD_005":{"address":5729376,"default_item":3,"flag":0},"POKEDEX_REWARD_006":{"address":5729378,"default_item":3,"flag":0},"POKEDEX_REWARD_007":{"address":5729380,"default_item":3,"flag":0},"POKEDEX_REWARD_008":{"address":5729382,"default_item":3,"flag":0},"POKEDEX_REWARD_009":{"address":5729384,"default_item":3,"flag":0},"POKEDEX_REWARD_010":{"address":5729386,"default_item":3,"flag":0},"POKEDEX_REWARD_011":{"address":5729388,"default_item":3,"flag":0},"POKEDEX_REWARD_012":{"address":5729390,"default_item":3,"flag":0},"POKEDEX_REWARD_013":{"address":5729392,"default_item":3,"flag":0},"POKEDEX_REWARD_014":{"address":5729394,"default_item":3,"flag":0},"POKEDEX_REWARD_015":{"address":5729396,"default_item":3,"flag":0},"POKEDEX_REWARD_016":{"address":5729398,"default_item":3,"flag":0},"POKEDEX_REWARD_017":{"address":5729400,"default_item":3,"flag":0},"POKEDEX_REWARD_018":{"address":5729402,"default_item":3,"flag":0},"POKEDEX_REWARD_019":{"address":5729404,"default_item":3,"flag":0},"POKEDEX_REWARD_020":{"address":5729406,"default_item":3,"flag":0},"POKEDEX_REWARD_021":{"address":5729408,"default_item":3,"flag":0},"POKEDEX_REWARD_022":{"address":5729410,"default_item":3,"flag":0},"POKEDEX_REWARD_023":{"address":5729412,"default_item":3,"flag":0},"POKEDEX_REWARD_024":{"address":5729414,"default_item":3,"flag":0},"POKEDEX_REWARD_025":{"address":5729416,"default_item":3,"flag":0},"POKEDEX_REWARD_026":{"address":5729418,"default_item":3,"flag":0},"POKEDEX_REWARD_027":{"address":5729420,"default_item":3,"flag":0},"POKEDEX_REWARD_028":{"address":5729422,"default_item":3,"flag":0},"POKEDEX_REWARD_029":{"address":5729424,"default_item":3,"flag":0},"POKEDEX_REWARD_030":{"address":5729426,"default_item":3,"flag":0},"POKEDEX_REWARD_031":{"address":5729428,"default_item":3,"flag":0},"POKEDEX_REWARD_032":{"address":5729430,"default_item":3,"flag":0},"POKEDEX_REWARD_033":{"address":5729432,"default_item":3,"flag":0},"POKEDEX_REWARD_034":{"address":5729434,"default_item":3,"flag":0},"POKEDEX_REWARD_035":{"address":5729436,"default_item":3,"flag":0},"POKEDEX_REWARD_036":{"address":5729438,"default_item":3,"flag":0},"POKEDEX_REWARD_037":{"address":5729440,"default_item":3,"flag":0},"POKEDEX_REWARD_038":{"address":5729442,"default_item":3,"flag":0},"POKEDEX_REWARD_039":{"address":5729444,"default_item":3,"flag":0},"POKEDEX_REWARD_040":{"address":5729446,"default_item":3,"flag":0},"POKEDEX_REWARD_041":{"address":5729448,"default_item":3,"flag":0},"POKEDEX_REWARD_042":{"address":5729450,"default_item":3,"flag":0},"POKEDEX_REWARD_043":{"address":5729452,"default_item":3,"flag":0},"POKEDEX_REWARD_044":{"address":5729454,"default_item":3,"flag":0},"POKEDEX_REWARD_045":{"address":5729456,"default_item":3,"flag":0},"POKEDEX_REWARD_046":{"address":5729458,"default_item":3,"flag":0},"POKEDEX_REWARD_047":{"address":5729460,"default_item":3,"flag":0},"POKEDEX_REWARD_048":{"address":5729462,"default_item":3,"flag":0},"POKEDEX_REWARD_049":{"address":5729464,"default_item":3,"flag":0},"POKEDEX_REWARD_050":{"address":5729466,"default_item":3,"flag":0},"POKEDEX_REWARD_051":{"address":5729468,"default_item":3,"flag":0},"POKEDEX_REWARD_052":{"address":5729470,"default_item":3,"flag":0},"POKEDEX_REWARD_053":{"address":5729472,"default_item":3,"flag":0},"POKEDEX_REWARD_054":{"address":5729474,"default_item":3,"flag":0},"POKEDEX_REWARD_055":{"address":5729476,"default_item":3,"flag":0},"POKEDEX_REWARD_056":{"address":5729478,"default_item":3,"flag":0},"POKEDEX_REWARD_057":{"address":5729480,"default_item":3,"flag":0},"POKEDEX_REWARD_058":{"address":5729482,"default_item":3,"flag":0},"POKEDEX_REWARD_059":{"address":5729484,"default_item":3,"flag":0},"POKEDEX_REWARD_060":{"address":5729486,"default_item":3,"flag":0},"POKEDEX_REWARD_061":{"address":5729488,"default_item":3,"flag":0},"POKEDEX_REWARD_062":{"address":5729490,"default_item":3,"flag":0},"POKEDEX_REWARD_063":{"address":5729492,"default_item":3,"flag":0},"POKEDEX_REWARD_064":{"address":5729494,"default_item":3,"flag":0},"POKEDEX_REWARD_065":{"address":5729496,"default_item":3,"flag":0},"POKEDEX_REWARD_066":{"address":5729498,"default_item":3,"flag":0},"POKEDEX_REWARD_067":{"address":5729500,"default_item":3,"flag":0},"POKEDEX_REWARD_068":{"address":5729502,"default_item":3,"flag":0},"POKEDEX_REWARD_069":{"address":5729504,"default_item":3,"flag":0},"POKEDEX_REWARD_070":{"address":5729506,"default_item":3,"flag":0},"POKEDEX_REWARD_071":{"address":5729508,"default_item":3,"flag":0},"POKEDEX_REWARD_072":{"address":5729510,"default_item":3,"flag":0},"POKEDEX_REWARD_073":{"address":5729512,"default_item":3,"flag":0},"POKEDEX_REWARD_074":{"address":5729514,"default_item":3,"flag":0},"POKEDEX_REWARD_075":{"address":5729516,"default_item":3,"flag":0},"POKEDEX_REWARD_076":{"address":5729518,"default_item":3,"flag":0},"POKEDEX_REWARD_077":{"address":5729520,"default_item":3,"flag":0},"POKEDEX_REWARD_078":{"address":5729522,"default_item":3,"flag":0},"POKEDEX_REWARD_079":{"address":5729524,"default_item":3,"flag":0},"POKEDEX_REWARD_080":{"address":5729526,"default_item":3,"flag":0},"POKEDEX_REWARD_081":{"address":5729528,"default_item":3,"flag":0},"POKEDEX_REWARD_082":{"address":5729530,"default_item":3,"flag":0},"POKEDEX_REWARD_083":{"address":5729532,"default_item":3,"flag":0},"POKEDEX_REWARD_084":{"address":5729534,"default_item":3,"flag":0},"POKEDEX_REWARD_085":{"address":5729536,"default_item":3,"flag":0},"POKEDEX_REWARD_086":{"address":5729538,"default_item":3,"flag":0},"POKEDEX_REWARD_087":{"address":5729540,"default_item":3,"flag":0},"POKEDEX_REWARD_088":{"address":5729542,"default_item":3,"flag":0},"POKEDEX_REWARD_089":{"address":5729544,"default_item":3,"flag":0},"POKEDEX_REWARD_090":{"address":5729546,"default_item":3,"flag":0},"POKEDEX_REWARD_091":{"address":5729548,"default_item":3,"flag":0},"POKEDEX_REWARD_092":{"address":5729550,"default_item":3,"flag":0},"POKEDEX_REWARD_093":{"address":5729552,"default_item":3,"flag":0},"POKEDEX_REWARD_094":{"address":5729554,"default_item":3,"flag":0},"POKEDEX_REWARD_095":{"address":5729556,"default_item":3,"flag":0},"POKEDEX_REWARD_096":{"address":5729558,"default_item":3,"flag":0},"POKEDEX_REWARD_097":{"address":5729560,"default_item":3,"flag":0},"POKEDEX_REWARD_098":{"address":5729562,"default_item":3,"flag":0},"POKEDEX_REWARD_099":{"address":5729564,"default_item":3,"flag":0},"POKEDEX_REWARD_100":{"address":5729566,"default_item":3,"flag":0},"POKEDEX_REWARD_101":{"address":5729568,"default_item":3,"flag":0},"POKEDEX_REWARD_102":{"address":5729570,"default_item":3,"flag":0},"POKEDEX_REWARD_103":{"address":5729572,"default_item":3,"flag":0},"POKEDEX_REWARD_104":{"address":5729574,"default_item":3,"flag":0},"POKEDEX_REWARD_105":{"address":5729576,"default_item":3,"flag":0},"POKEDEX_REWARD_106":{"address":5729578,"default_item":3,"flag":0},"POKEDEX_REWARD_107":{"address":5729580,"default_item":3,"flag":0},"POKEDEX_REWARD_108":{"address":5729582,"default_item":3,"flag":0},"POKEDEX_REWARD_109":{"address":5729584,"default_item":3,"flag":0},"POKEDEX_REWARD_110":{"address":5729586,"default_item":3,"flag":0},"POKEDEX_REWARD_111":{"address":5729588,"default_item":3,"flag":0},"POKEDEX_REWARD_112":{"address":5729590,"default_item":3,"flag":0},"POKEDEX_REWARD_113":{"address":5729592,"default_item":3,"flag":0},"POKEDEX_REWARD_114":{"address":5729594,"default_item":3,"flag":0},"POKEDEX_REWARD_115":{"address":5729596,"default_item":3,"flag":0},"POKEDEX_REWARD_116":{"address":5729598,"default_item":3,"flag":0},"POKEDEX_REWARD_117":{"address":5729600,"default_item":3,"flag":0},"POKEDEX_REWARD_118":{"address":5729602,"default_item":3,"flag":0},"POKEDEX_REWARD_119":{"address":5729604,"default_item":3,"flag":0},"POKEDEX_REWARD_120":{"address":5729606,"default_item":3,"flag":0},"POKEDEX_REWARD_121":{"address":5729608,"default_item":3,"flag":0},"POKEDEX_REWARD_122":{"address":5729610,"default_item":3,"flag":0},"POKEDEX_REWARD_123":{"address":5729612,"default_item":3,"flag":0},"POKEDEX_REWARD_124":{"address":5729614,"default_item":3,"flag":0},"POKEDEX_REWARD_125":{"address":5729616,"default_item":3,"flag":0},"POKEDEX_REWARD_126":{"address":5729618,"default_item":3,"flag":0},"POKEDEX_REWARD_127":{"address":5729620,"default_item":3,"flag":0},"POKEDEX_REWARD_128":{"address":5729622,"default_item":3,"flag":0},"POKEDEX_REWARD_129":{"address":5729624,"default_item":3,"flag":0},"POKEDEX_REWARD_130":{"address":5729626,"default_item":3,"flag":0},"POKEDEX_REWARD_131":{"address":5729628,"default_item":3,"flag":0},"POKEDEX_REWARD_132":{"address":5729630,"default_item":3,"flag":0},"POKEDEX_REWARD_133":{"address":5729632,"default_item":3,"flag":0},"POKEDEX_REWARD_134":{"address":5729634,"default_item":3,"flag":0},"POKEDEX_REWARD_135":{"address":5729636,"default_item":3,"flag":0},"POKEDEX_REWARD_136":{"address":5729638,"default_item":3,"flag":0},"POKEDEX_REWARD_137":{"address":5729640,"default_item":3,"flag":0},"POKEDEX_REWARD_138":{"address":5729642,"default_item":3,"flag":0},"POKEDEX_REWARD_139":{"address":5729644,"default_item":3,"flag":0},"POKEDEX_REWARD_140":{"address":5729646,"default_item":3,"flag":0},"POKEDEX_REWARD_141":{"address":5729648,"default_item":3,"flag":0},"POKEDEX_REWARD_142":{"address":5729650,"default_item":3,"flag":0},"POKEDEX_REWARD_143":{"address":5729652,"default_item":3,"flag":0},"POKEDEX_REWARD_144":{"address":5729654,"default_item":3,"flag":0},"POKEDEX_REWARD_145":{"address":5729656,"default_item":3,"flag":0},"POKEDEX_REWARD_146":{"address":5729658,"default_item":3,"flag":0},"POKEDEX_REWARD_147":{"address":5729660,"default_item":3,"flag":0},"POKEDEX_REWARD_148":{"address":5729662,"default_item":3,"flag":0},"POKEDEX_REWARD_149":{"address":5729664,"default_item":3,"flag":0},"POKEDEX_REWARD_150":{"address":5729666,"default_item":3,"flag":0},"POKEDEX_REWARD_151":{"address":5729668,"default_item":3,"flag":0},"POKEDEX_REWARD_152":{"address":5729670,"default_item":3,"flag":0},"POKEDEX_REWARD_153":{"address":5729672,"default_item":3,"flag":0},"POKEDEX_REWARD_154":{"address":5729674,"default_item":3,"flag":0},"POKEDEX_REWARD_155":{"address":5729676,"default_item":3,"flag":0},"POKEDEX_REWARD_156":{"address":5729678,"default_item":3,"flag":0},"POKEDEX_REWARD_157":{"address":5729680,"default_item":3,"flag":0},"POKEDEX_REWARD_158":{"address":5729682,"default_item":3,"flag":0},"POKEDEX_REWARD_159":{"address":5729684,"default_item":3,"flag":0},"POKEDEX_REWARD_160":{"address":5729686,"default_item":3,"flag":0},"POKEDEX_REWARD_161":{"address":5729688,"default_item":3,"flag":0},"POKEDEX_REWARD_162":{"address":5729690,"default_item":3,"flag":0},"POKEDEX_REWARD_163":{"address":5729692,"default_item":3,"flag":0},"POKEDEX_REWARD_164":{"address":5729694,"default_item":3,"flag":0},"POKEDEX_REWARD_165":{"address":5729696,"default_item":3,"flag":0},"POKEDEX_REWARD_166":{"address":5729698,"default_item":3,"flag":0},"POKEDEX_REWARD_167":{"address":5729700,"default_item":3,"flag":0},"POKEDEX_REWARD_168":{"address":5729702,"default_item":3,"flag":0},"POKEDEX_REWARD_169":{"address":5729704,"default_item":3,"flag":0},"POKEDEX_REWARD_170":{"address":5729706,"default_item":3,"flag":0},"POKEDEX_REWARD_171":{"address":5729708,"default_item":3,"flag":0},"POKEDEX_REWARD_172":{"address":5729710,"default_item":3,"flag":0},"POKEDEX_REWARD_173":{"address":5729712,"default_item":3,"flag":0},"POKEDEX_REWARD_174":{"address":5729714,"default_item":3,"flag":0},"POKEDEX_REWARD_175":{"address":5729716,"default_item":3,"flag":0},"POKEDEX_REWARD_176":{"address":5729718,"default_item":3,"flag":0},"POKEDEX_REWARD_177":{"address":5729720,"default_item":3,"flag":0},"POKEDEX_REWARD_178":{"address":5729722,"default_item":3,"flag":0},"POKEDEX_REWARD_179":{"address":5729724,"default_item":3,"flag":0},"POKEDEX_REWARD_180":{"address":5729726,"default_item":3,"flag":0},"POKEDEX_REWARD_181":{"address":5729728,"default_item":3,"flag":0},"POKEDEX_REWARD_182":{"address":5729730,"default_item":3,"flag":0},"POKEDEX_REWARD_183":{"address":5729732,"default_item":3,"flag":0},"POKEDEX_REWARD_184":{"address":5729734,"default_item":3,"flag":0},"POKEDEX_REWARD_185":{"address":5729736,"default_item":3,"flag":0},"POKEDEX_REWARD_186":{"address":5729738,"default_item":3,"flag":0},"POKEDEX_REWARD_187":{"address":5729740,"default_item":3,"flag":0},"POKEDEX_REWARD_188":{"address":5729742,"default_item":3,"flag":0},"POKEDEX_REWARD_189":{"address":5729744,"default_item":3,"flag":0},"POKEDEX_REWARD_190":{"address":5729746,"default_item":3,"flag":0},"POKEDEX_REWARD_191":{"address":5729748,"default_item":3,"flag":0},"POKEDEX_REWARD_192":{"address":5729750,"default_item":3,"flag":0},"POKEDEX_REWARD_193":{"address":5729752,"default_item":3,"flag":0},"POKEDEX_REWARD_194":{"address":5729754,"default_item":3,"flag":0},"POKEDEX_REWARD_195":{"address":5729756,"default_item":3,"flag":0},"POKEDEX_REWARD_196":{"address":5729758,"default_item":3,"flag":0},"POKEDEX_REWARD_197":{"address":5729760,"default_item":3,"flag":0},"POKEDEX_REWARD_198":{"address":5729762,"default_item":3,"flag":0},"POKEDEX_REWARD_199":{"address":5729764,"default_item":3,"flag":0},"POKEDEX_REWARD_200":{"address":5729766,"default_item":3,"flag":0},"POKEDEX_REWARD_201":{"address":5729768,"default_item":3,"flag":0},"POKEDEX_REWARD_202":{"address":5729770,"default_item":3,"flag":0},"POKEDEX_REWARD_203":{"address":5729772,"default_item":3,"flag":0},"POKEDEX_REWARD_204":{"address":5729774,"default_item":3,"flag":0},"POKEDEX_REWARD_205":{"address":5729776,"default_item":3,"flag":0},"POKEDEX_REWARD_206":{"address":5729778,"default_item":3,"flag":0},"POKEDEX_REWARD_207":{"address":5729780,"default_item":3,"flag":0},"POKEDEX_REWARD_208":{"address":5729782,"default_item":3,"flag":0},"POKEDEX_REWARD_209":{"address":5729784,"default_item":3,"flag":0},"POKEDEX_REWARD_210":{"address":5729786,"default_item":3,"flag":0},"POKEDEX_REWARD_211":{"address":5729788,"default_item":3,"flag":0},"POKEDEX_REWARD_212":{"address":5729790,"default_item":3,"flag":0},"POKEDEX_REWARD_213":{"address":5729792,"default_item":3,"flag":0},"POKEDEX_REWARD_214":{"address":5729794,"default_item":3,"flag":0},"POKEDEX_REWARD_215":{"address":5729796,"default_item":3,"flag":0},"POKEDEX_REWARD_216":{"address":5729798,"default_item":3,"flag":0},"POKEDEX_REWARD_217":{"address":5729800,"default_item":3,"flag":0},"POKEDEX_REWARD_218":{"address":5729802,"default_item":3,"flag":0},"POKEDEX_REWARD_219":{"address":5729804,"default_item":3,"flag":0},"POKEDEX_REWARD_220":{"address":5729806,"default_item":3,"flag":0},"POKEDEX_REWARD_221":{"address":5729808,"default_item":3,"flag":0},"POKEDEX_REWARD_222":{"address":5729810,"default_item":3,"flag":0},"POKEDEX_REWARD_223":{"address":5729812,"default_item":3,"flag":0},"POKEDEX_REWARD_224":{"address":5729814,"default_item":3,"flag":0},"POKEDEX_REWARD_225":{"address":5729816,"default_item":3,"flag":0},"POKEDEX_REWARD_226":{"address":5729818,"default_item":3,"flag":0},"POKEDEX_REWARD_227":{"address":5729820,"default_item":3,"flag":0},"POKEDEX_REWARD_228":{"address":5729822,"default_item":3,"flag":0},"POKEDEX_REWARD_229":{"address":5729824,"default_item":3,"flag":0},"POKEDEX_REWARD_230":{"address":5729826,"default_item":3,"flag":0},"POKEDEX_REWARD_231":{"address":5729828,"default_item":3,"flag":0},"POKEDEX_REWARD_232":{"address":5729830,"default_item":3,"flag":0},"POKEDEX_REWARD_233":{"address":5729832,"default_item":3,"flag":0},"POKEDEX_REWARD_234":{"address":5729834,"default_item":3,"flag":0},"POKEDEX_REWARD_235":{"address":5729836,"default_item":3,"flag":0},"POKEDEX_REWARD_236":{"address":5729838,"default_item":3,"flag":0},"POKEDEX_REWARD_237":{"address":5729840,"default_item":3,"flag":0},"POKEDEX_REWARD_238":{"address":5729842,"default_item":3,"flag":0},"POKEDEX_REWARD_239":{"address":5729844,"default_item":3,"flag":0},"POKEDEX_REWARD_240":{"address":5729846,"default_item":3,"flag":0},"POKEDEX_REWARD_241":{"address":5729848,"default_item":3,"flag":0},"POKEDEX_REWARD_242":{"address":5729850,"default_item":3,"flag":0},"POKEDEX_REWARD_243":{"address":5729852,"default_item":3,"flag":0},"POKEDEX_REWARD_244":{"address":5729854,"default_item":3,"flag":0},"POKEDEX_REWARD_245":{"address":5729856,"default_item":3,"flag":0},"POKEDEX_REWARD_246":{"address":5729858,"default_item":3,"flag":0},"POKEDEX_REWARD_247":{"address":5729860,"default_item":3,"flag":0},"POKEDEX_REWARD_248":{"address":5729862,"default_item":3,"flag":0},"POKEDEX_REWARD_249":{"address":5729864,"default_item":3,"flag":0},"POKEDEX_REWARD_250":{"address":5729866,"default_item":3,"flag":0},"POKEDEX_REWARD_251":{"address":5729868,"default_item":3,"flag":0},"POKEDEX_REWARD_252":{"address":5729870,"default_item":3,"flag":0},"POKEDEX_REWARD_253":{"address":5729872,"default_item":3,"flag":0},"POKEDEX_REWARD_254":{"address":5729874,"default_item":3,"flag":0},"POKEDEX_REWARD_255":{"address":5729876,"default_item":3,"flag":0},"POKEDEX_REWARD_256":{"address":5729878,"default_item":3,"flag":0},"POKEDEX_REWARD_257":{"address":5729880,"default_item":3,"flag":0},"POKEDEX_REWARD_258":{"address":5729882,"default_item":3,"flag":0},"POKEDEX_REWARD_259":{"address":5729884,"default_item":3,"flag":0},"POKEDEX_REWARD_260":{"address":5729886,"default_item":3,"flag":0},"POKEDEX_REWARD_261":{"address":5729888,"default_item":3,"flag":0},"POKEDEX_REWARD_262":{"address":5729890,"default_item":3,"flag":0},"POKEDEX_REWARD_263":{"address":5729892,"default_item":3,"flag":0},"POKEDEX_REWARD_264":{"address":5729894,"default_item":3,"flag":0},"POKEDEX_REWARD_265":{"address":5729896,"default_item":3,"flag":0},"POKEDEX_REWARD_266":{"address":5729898,"default_item":3,"flag":0},"POKEDEX_REWARD_267":{"address":5729900,"default_item":3,"flag":0},"POKEDEX_REWARD_268":{"address":5729902,"default_item":3,"flag":0},"POKEDEX_REWARD_269":{"address":5729904,"default_item":3,"flag":0},"POKEDEX_REWARD_270":{"address":5729906,"default_item":3,"flag":0},"POKEDEX_REWARD_271":{"address":5729908,"default_item":3,"flag":0},"POKEDEX_REWARD_272":{"address":5729910,"default_item":3,"flag":0},"POKEDEX_REWARD_273":{"address":5729912,"default_item":3,"flag":0},"POKEDEX_REWARD_274":{"address":5729914,"default_item":3,"flag":0},"POKEDEX_REWARD_275":{"address":5729916,"default_item":3,"flag":0},"POKEDEX_REWARD_276":{"address":5729918,"default_item":3,"flag":0},"POKEDEX_REWARD_277":{"address":5729920,"default_item":3,"flag":0},"POKEDEX_REWARD_278":{"address":5729922,"default_item":3,"flag":0},"POKEDEX_REWARD_279":{"address":5729924,"default_item":3,"flag":0},"POKEDEX_REWARD_280":{"address":5729926,"default_item":3,"flag":0},"POKEDEX_REWARD_281":{"address":5729928,"default_item":3,"flag":0},"POKEDEX_REWARD_282":{"address":5729930,"default_item":3,"flag":0},"POKEDEX_REWARD_283":{"address":5729932,"default_item":3,"flag":0},"POKEDEX_REWARD_284":{"address":5729934,"default_item":3,"flag":0},"POKEDEX_REWARD_285":{"address":5729936,"default_item":3,"flag":0},"POKEDEX_REWARD_286":{"address":5729938,"default_item":3,"flag":0},"POKEDEX_REWARD_287":{"address":5729940,"default_item":3,"flag":0},"POKEDEX_REWARD_288":{"address":5729942,"default_item":3,"flag":0},"POKEDEX_REWARD_289":{"address":5729944,"default_item":3,"flag":0},"POKEDEX_REWARD_290":{"address":5729946,"default_item":3,"flag":0},"POKEDEX_REWARD_291":{"address":5729948,"default_item":3,"flag":0},"POKEDEX_REWARD_292":{"address":5729950,"default_item":3,"flag":0},"POKEDEX_REWARD_293":{"address":5729952,"default_item":3,"flag":0},"POKEDEX_REWARD_294":{"address":5729954,"default_item":3,"flag":0},"POKEDEX_REWARD_295":{"address":5729956,"default_item":3,"flag":0},"POKEDEX_REWARD_296":{"address":5729958,"default_item":3,"flag":0},"POKEDEX_REWARD_297":{"address":5729960,"default_item":3,"flag":0},"POKEDEX_REWARD_298":{"address":5729962,"default_item":3,"flag":0},"POKEDEX_REWARD_299":{"address":5729964,"default_item":3,"flag":0},"POKEDEX_REWARD_300":{"address":5729966,"default_item":3,"flag":0},"POKEDEX_REWARD_301":{"address":5729968,"default_item":3,"flag":0},"POKEDEX_REWARD_302":{"address":5729970,"default_item":3,"flag":0},"POKEDEX_REWARD_303":{"address":5729972,"default_item":3,"flag":0},"POKEDEX_REWARD_304":{"address":5729974,"default_item":3,"flag":0},"POKEDEX_REWARD_305":{"address":5729976,"default_item":3,"flag":0},"POKEDEX_REWARD_306":{"address":5729978,"default_item":3,"flag":0},"POKEDEX_REWARD_307":{"address":5729980,"default_item":3,"flag":0},"POKEDEX_REWARD_308":{"address":5729982,"default_item":3,"flag":0},"POKEDEX_REWARD_309":{"address":5729984,"default_item":3,"flag":0},"POKEDEX_REWARD_310":{"address":5729986,"default_item":3,"flag":0},"POKEDEX_REWARD_311":{"address":5729988,"default_item":3,"flag":0},"POKEDEX_REWARD_312":{"address":5729990,"default_item":3,"flag":0},"POKEDEX_REWARD_313":{"address":5729992,"default_item":3,"flag":0},"POKEDEX_REWARD_314":{"address":5729994,"default_item":3,"flag":0},"POKEDEX_REWARD_315":{"address":5729996,"default_item":3,"flag":0},"POKEDEX_REWARD_316":{"address":5729998,"default_item":3,"flag":0},"POKEDEX_REWARD_317":{"address":5730000,"default_item":3,"flag":0},"POKEDEX_REWARD_318":{"address":5730002,"default_item":3,"flag":0},"POKEDEX_REWARD_319":{"address":5730004,"default_item":3,"flag":0},"POKEDEX_REWARD_320":{"address":5730006,"default_item":3,"flag":0},"POKEDEX_REWARD_321":{"address":5730008,"default_item":3,"flag":0},"POKEDEX_REWARD_322":{"address":5730010,"default_item":3,"flag":0},"POKEDEX_REWARD_323":{"address":5730012,"default_item":3,"flag":0},"POKEDEX_REWARD_324":{"address":5730014,"default_item":3,"flag":0},"POKEDEX_REWARD_325":{"address":5730016,"default_item":3,"flag":0},"POKEDEX_REWARD_326":{"address":5730018,"default_item":3,"flag":0},"POKEDEX_REWARD_327":{"address":5730020,"default_item":3,"flag":0},"POKEDEX_REWARD_328":{"address":5730022,"default_item":3,"flag":0},"POKEDEX_REWARD_329":{"address":5730024,"default_item":3,"flag":0},"POKEDEX_REWARD_330":{"address":5730026,"default_item":3,"flag":0},"POKEDEX_REWARD_331":{"address":5730028,"default_item":3,"flag":0},"POKEDEX_REWARD_332":{"address":5730030,"default_item":3,"flag":0},"POKEDEX_REWARD_333":{"address":5730032,"default_item":3,"flag":0},"POKEDEX_REWARD_334":{"address":5730034,"default_item":3,"flag":0},"POKEDEX_REWARD_335":{"address":5730036,"default_item":3,"flag":0},"POKEDEX_REWARD_336":{"address":5730038,"default_item":3,"flag":0},"POKEDEX_REWARD_337":{"address":5730040,"default_item":3,"flag":0},"POKEDEX_REWARD_338":{"address":5730042,"default_item":3,"flag":0},"POKEDEX_REWARD_339":{"address":5730044,"default_item":3,"flag":0},"POKEDEX_REWARD_340":{"address":5730046,"default_item":3,"flag":0},"POKEDEX_REWARD_341":{"address":5730048,"default_item":3,"flag":0},"POKEDEX_REWARD_342":{"address":5730050,"default_item":3,"flag":0},"POKEDEX_REWARD_343":{"address":5730052,"default_item":3,"flag":0},"POKEDEX_REWARD_344":{"address":5730054,"default_item":3,"flag":0},"POKEDEX_REWARD_345":{"address":5730056,"default_item":3,"flag":0},"POKEDEX_REWARD_346":{"address":5730058,"default_item":3,"flag":0},"POKEDEX_REWARD_347":{"address":5730060,"default_item":3,"flag":0},"POKEDEX_REWARD_348":{"address":5730062,"default_item":3,"flag":0},"POKEDEX_REWARD_349":{"address":5730064,"default_item":3,"flag":0},"POKEDEX_REWARD_350":{"address":5730066,"default_item":3,"flag":0},"POKEDEX_REWARD_351":{"address":5730068,"default_item":3,"flag":0},"POKEDEX_REWARD_352":{"address":5730070,"default_item":3,"flag":0},"POKEDEX_REWARD_353":{"address":5730072,"default_item":3,"flag":0},"POKEDEX_REWARD_354":{"address":5730074,"default_item":3,"flag":0},"POKEDEX_REWARD_355":{"address":5730076,"default_item":3,"flag":0},"POKEDEX_REWARD_356":{"address":5730078,"default_item":3,"flag":0},"POKEDEX_REWARD_357":{"address":5730080,"default_item":3,"flag":0},"POKEDEX_REWARD_358":{"address":5730082,"default_item":3,"flag":0},"POKEDEX_REWARD_359":{"address":5730084,"default_item":3,"flag":0},"POKEDEX_REWARD_360":{"address":5730086,"default_item":3,"flag":0},"POKEDEX_REWARD_361":{"address":5730088,"default_item":3,"flag":0},"POKEDEX_REWARD_362":{"address":5730090,"default_item":3,"flag":0},"POKEDEX_REWARD_363":{"address":5730092,"default_item":3,"flag":0},"POKEDEX_REWARD_364":{"address":5730094,"default_item":3,"flag":0},"POKEDEX_REWARD_365":{"address":5730096,"default_item":3,"flag":0},"POKEDEX_REWARD_366":{"address":5730098,"default_item":3,"flag":0},"POKEDEX_REWARD_367":{"address":5730100,"default_item":3,"flag":0},"POKEDEX_REWARD_368":{"address":5730102,"default_item":3,"flag":0},"POKEDEX_REWARD_369":{"address":5730104,"default_item":3,"flag":0},"POKEDEX_REWARD_370":{"address":5730106,"default_item":3,"flag":0},"POKEDEX_REWARD_371":{"address":5730108,"default_item":3,"flag":0},"POKEDEX_REWARD_372":{"address":5730110,"default_item":3,"flag":0},"POKEDEX_REWARD_373":{"address":5730112,"default_item":3,"flag":0},"POKEDEX_REWARD_374":{"address":5730114,"default_item":3,"flag":0},"POKEDEX_REWARD_375":{"address":5730116,"default_item":3,"flag":0},"POKEDEX_REWARD_376":{"address":5730118,"default_item":3,"flag":0},"POKEDEX_REWARD_377":{"address":5730120,"default_item":3,"flag":0},"POKEDEX_REWARD_378":{"address":5730122,"default_item":3,"flag":0},"POKEDEX_REWARD_379":{"address":5730124,"default_item":3,"flag":0},"POKEDEX_REWARD_380":{"address":5730126,"default_item":3,"flag":0},"POKEDEX_REWARD_381":{"address":5730128,"default_item":3,"flag":0},"POKEDEX_REWARD_382":{"address":5730130,"default_item":3,"flag":0},"POKEDEX_REWARD_383":{"address":5730132,"default_item":3,"flag":0},"POKEDEX_REWARD_384":{"address":5730134,"default_item":3,"flag":0},"POKEDEX_REWARD_385":{"address":5730136,"default_item":3,"flag":0},"POKEDEX_REWARD_386":{"address":5730138,"default_item":3,"flag":0},"TRAINER_AARON_REWARD":{"address":5602878,"default_item":104,"flag":1677},"TRAINER_ABIGAIL_1_REWARD":{"address":5602800,"default_item":106,"flag":1638},"TRAINER_AIDAN_REWARD":{"address":5603432,"default_item":104,"flag":1954},"TRAINER_AISHA_REWARD":{"address":5603598,"default_item":106,"flag":2037},"TRAINER_ALBERTO_REWARD":{"address":5602108,"default_item":108,"flag":1292},"TRAINER_ALBERT_REWARD":{"address":5602244,"default_item":104,"flag":1360},"TRAINER_ALEXA_REWARD":{"address":5603424,"default_item":104,"flag":1950},"TRAINER_ALEXIA_REWARD":{"address":5602264,"default_item":104,"flag":1370},"TRAINER_ALEX_REWARD":{"address":5602910,"default_item":104,"flag":1693},"TRAINER_ALICE_REWARD":{"address":5602980,"default_item":103,"flag":1728},"TRAINER_ALIX_REWARD":{"address":5603584,"default_item":106,"flag":2030},"TRAINER_ALLEN_REWARD":{"address":5602750,"default_item":103,"flag":1613},"TRAINER_ALLISON_REWARD":{"address":5602858,"default_item":104,"flag":1667},"TRAINER_ALYSSA_REWARD":{"address":5603486,"default_item":106,"flag":1981},"TRAINER_AMY_AND_LIV_1_REWARD":{"address":5603046,"default_item":103,"flag":1761},"TRAINER_ANDREA_REWARD":{"address":5603310,"default_item":106,"flag":1893},"TRAINER_ANDRES_1_REWARD":{"address":5603558,"default_item":104,"flag":2017},"TRAINER_ANDREW_REWARD":{"address":5602756,"default_item":106,"flag":1616},"TRAINER_ANGELICA_REWARD":{"address":5602956,"default_item":104,"flag":1716},"TRAINER_ANGELINA_REWARD":{"address":5603508,"default_item":106,"flag":1992},"TRAINER_ANGELO_REWARD":{"address":5603688,"default_item":104,"flag":2082},"TRAINER_ANNA_AND_MEG_1_REWARD":{"address":5602658,"default_item":106,"flag":1567},"TRAINER_ANNIKA_REWARD":{"address":5603088,"default_item":107,"flag":1782},"TRAINER_ANTHONY_REWARD":{"address":5602788,"default_item":106,"flag":1632},"TRAINER_ARCHIE_REWARD":{"address":5602152,"default_item":107,"flag":1314},"TRAINER_ASHLEY_REWARD":{"address":5603394,"default_item":106,"flag":1935},"TRAINER_ATHENA_REWARD":{"address":5603238,"default_item":104,"flag":1857},"TRAINER_ATSUSHI_REWARD":{"address":5602464,"default_item":104,"flag":1470},"TRAINER_AURON_REWARD":{"address":5603096,"default_item":104,"flag":1786},"TRAINER_AUSTINA_REWARD":{"address":5602200,"default_item":103,"flag":1338},"TRAINER_AUTUMN_REWARD":{"address":5602518,"default_item":106,"flag":1497},"TRAINER_AXLE_REWARD":{"address":5602490,"default_item":108,"flag":1483},"TRAINER_BARNY_REWARD":{"address":5602770,"default_item":104,"flag":1623},"TRAINER_BARRY_REWARD":{"address":5602410,"default_item":106,"flag":1443},"TRAINER_BEAU_REWARD":{"address":5602508,"default_item":106,"flag":1492},"TRAINER_BECKY_REWARD":{"address":5603024,"default_item":106,"flag":1750},"TRAINER_BECK_REWARD":{"address":5602912,"default_item":104,"flag":1694},"TRAINER_BENJAMIN_1_REWARD":{"address":5602790,"default_item":106,"flag":1633},"TRAINER_BEN_REWARD":{"address":5602730,"default_item":106,"flag":1603},"TRAINER_BERKE_REWARD":{"address":5602232,"default_item":104,"flag":1354},"TRAINER_BERNIE_1_REWARD":{"address":5602496,"default_item":106,"flag":1486},"TRAINER_BETHANY_REWARD":{"address":5602686,"default_item":107,"flag":1581},"TRAINER_BETH_REWARD":{"address":5602974,"default_item":103,"flag":1725},"TRAINER_BEVERLY_REWARD":{"address":5602966,"default_item":103,"flag":1721},"TRAINER_BIANCA_REWARD":{"address":5603496,"default_item":106,"flag":1986},"TRAINER_BILLY_REWARD":{"address":5602722,"default_item":103,"flag":1599},"TRAINER_BLAKE_REWARD":{"address":5602554,"default_item":108,"flag":1515},"TRAINER_BRANDEN_REWARD":{"address":5603574,"default_item":106,"flag":2025},"TRAINER_BRANDI_REWARD":{"address":5603596,"default_item":106,"flag":2036},"TRAINER_BRAWLY_1_REWARD":{"address":5602616,"default_item":104,"flag":1546},"TRAINER_BRAXTON_REWARD":{"address":5602234,"default_item":104,"flag":1355},"TRAINER_BRENDAN_LILYCOVE_MUDKIP_REWARD":{"address":5603406,"default_item":104,"flag":1941},"TRAINER_BRENDAN_LILYCOVE_TORCHIC_REWARD":{"address":5603410,"default_item":104,"flag":1943},"TRAINER_BRENDAN_LILYCOVE_TREECKO_REWARD":{"address":5603408,"default_item":104,"flag":1942},"TRAINER_BRENDAN_ROUTE_103_MUDKIP_REWARD":{"address":5603124,"default_item":106,"flag":1800},"TRAINER_BRENDAN_ROUTE_103_TORCHIC_REWARD":{"address":5603136,"default_item":106,"flag":1806},"TRAINER_BRENDAN_ROUTE_103_TREECKO_REWARD":{"address":5603130,"default_item":106,"flag":1803},"TRAINER_BRENDAN_ROUTE_110_MUDKIP_REWARD":{"address":5603126,"default_item":104,"flag":1801},"TRAINER_BRENDAN_ROUTE_110_TORCHIC_REWARD":{"address":5603138,"default_item":104,"flag":1807},"TRAINER_BRENDAN_ROUTE_110_TREECKO_REWARD":{"address":5603132,"default_item":104,"flag":1804},"TRAINER_BRENDAN_ROUTE_119_MUDKIP_REWARD":{"address":5603128,"default_item":104,"flag":1802},"TRAINER_BRENDAN_ROUTE_119_TORCHIC_REWARD":{"address":5603140,"default_item":104,"flag":1808},"TRAINER_BRENDAN_ROUTE_119_TREECKO_REWARD":{"address":5603134,"default_item":104,"flag":1805},"TRAINER_BRENDAN_RUSTBORO_MUDKIP_REWARD":{"address":5603270,"default_item":108,"flag":1873},"TRAINER_BRENDAN_RUSTBORO_TORCHIC_REWARD":{"address":5603282,"default_item":108,"flag":1879},"TRAINER_BRENDAN_RUSTBORO_TREECKO_REWARD":{"address":5603268,"default_item":108,"flag":1872},"TRAINER_BRENDA_REWARD":{"address":5602992,"default_item":106,"flag":1734},"TRAINER_BRENDEN_REWARD":{"address":5603228,"default_item":106,"flag":1852},"TRAINER_BRENT_REWARD":{"address":5602530,"default_item":104,"flag":1503},"TRAINER_BRIANNA_REWARD":{"address":5602320,"default_item":110,"flag":1398},"TRAINER_BRICE_REWARD":{"address":5603336,"default_item":106,"flag":1906},"TRAINER_BRIDGET_REWARD":{"address":5602342,"default_item":107,"flag":1409},"TRAINER_BROOKE_1_REWARD":{"address":5602272,"default_item":108,"flag":1374},"TRAINER_BRYANT_REWARD":{"address":5603576,"default_item":106,"flag":2026},"TRAINER_BRYAN_REWARD":{"address":5603572,"default_item":104,"flag":2024},"TRAINER_CALE_REWARD":{"address":5603612,"default_item":104,"flag":2044},"TRAINER_CALLIE_REWARD":{"address":5603610,"default_item":106,"flag":2043},"TRAINER_CALVIN_1_REWARD":{"address":5602720,"default_item":103,"flag":1598},"TRAINER_CAMDEN_REWARD":{"address":5602832,"default_item":104,"flag":1654},"TRAINER_CAMERON_1_REWARD":{"address":5602560,"default_item":108,"flag":1518},"TRAINER_CAMRON_REWARD":{"address":5603562,"default_item":104,"flag":2019},"TRAINER_CARLEE_REWARD":{"address":5603012,"default_item":106,"flag":1744},"TRAINER_CAROLINA_REWARD":{"address":5603566,"default_item":104,"flag":2021},"TRAINER_CAROLINE_REWARD":{"address":5602282,"default_item":104,"flag":1379},"TRAINER_CAROL_REWARD":{"address":5603026,"default_item":106,"flag":1751},"TRAINER_CARTER_REWARD":{"address":5602774,"default_item":104,"flag":1625},"TRAINER_CATHERINE_1_REWARD":{"address":5603202,"default_item":104,"flag":1839},"TRAINER_CEDRIC_REWARD":{"address":5603034,"default_item":108,"flag":1755},"TRAINER_CELIA_REWARD":{"address":5603570,"default_item":106,"flag":2023},"TRAINER_CELINA_REWARD":{"address":5603494,"default_item":108,"flag":1985},"TRAINER_CHAD_REWARD":{"address":5602432,"default_item":106,"flag":1454},"TRAINER_CHANDLER_REWARD":{"address":5603480,"default_item":103,"flag":1978},"TRAINER_CHARLIE_REWARD":{"address":5602216,"default_item":103,"flag":1346},"TRAINER_CHARLOTTE_REWARD":{"address":5603512,"default_item":106,"flag":1994},"TRAINER_CHASE_REWARD":{"address":5602840,"default_item":104,"flag":1658},"TRAINER_CHESTER_REWARD":{"address":5602900,"default_item":108,"flag":1688},"TRAINER_CHIP_REWARD":{"address":5602174,"default_item":104,"flag":1325},"TRAINER_CHRIS_REWARD":{"address":5603470,"default_item":108,"flag":1973},"TRAINER_CINDY_1_REWARD":{"address":5602312,"default_item":104,"flag":1394},"TRAINER_CLARENCE_REWARD":{"address":5603244,"default_item":106,"flag":1860},"TRAINER_CLARISSA_REWARD":{"address":5602954,"default_item":104,"flag":1715},"TRAINER_CLARK_REWARD":{"address":5603346,"default_item":106,"flag":1911},"TRAINER_CLAUDE_REWARD":{"address":5602760,"default_item":108,"flag":1618},"TRAINER_CLIFFORD_REWARD":{"address":5603252,"default_item":107,"flag":1864},"TRAINER_COBY_REWARD":{"address":5603502,"default_item":106,"flag":1989},"TRAINER_COLE_REWARD":{"address":5602486,"default_item":108,"flag":1481},"TRAINER_COLIN_REWARD":{"address":5602894,"default_item":108,"flag":1685},"TRAINER_COLTON_REWARD":{"address":5602672,"default_item":107,"flag":1574},"TRAINER_CONNIE_REWARD":{"address":5602340,"default_item":107,"flag":1408},"TRAINER_CONOR_REWARD":{"address":5603106,"default_item":104,"flag":1791},"TRAINER_CORY_1_REWARD":{"address":5603564,"default_item":108,"flag":2020},"TRAINER_CRISSY_REWARD":{"address":5603312,"default_item":106,"flag":1894},"TRAINER_CRISTIAN_REWARD":{"address":5603232,"default_item":106,"flag":1854},"TRAINER_CRISTIN_1_REWARD":{"address":5603618,"default_item":104,"flag":2047},"TRAINER_CYNDY_1_REWARD":{"address":5602938,"default_item":106,"flag":1707},"TRAINER_DAISUKE_REWARD":{"address":5602462,"default_item":106,"flag":1469},"TRAINER_DAISY_REWARD":{"address":5602156,"default_item":106,"flag":1316},"TRAINER_DALE_REWARD":{"address":5602766,"default_item":106,"flag":1621},"TRAINER_DALTON_1_REWARD":{"address":5602476,"default_item":106,"flag":1476},"TRAINER_DANA_REWARD":{"address":5603000,"default_item":106,"flag":1738},"TRAINER_DANIELLE_REWARD":{"address":5603384,"default_item":106,"flag":1930},"TRAINER_DAPHNE_REWARD":{"address":5602314,"default_item":110,"flag":1395},"TRAINER_DARCY_REWARD":{"address":5603550,"default_item":104,"flag":2013},"TRAINER_DARIAN_REWARD":{"address":5603476,"default_item":106,"flag":1976},"TRAINER_DARIUS_REWARD":{"address":5603690,"default_item":108,"flag":2083},"TRAINER_DARRIN_REWARD":{"address":5602392,"default_item":103,"flag":1434},"TRAINER_DAVID_REWARD":{"address":5602400,"default_item":103,"flag":1438},"TRAINER_DAVIS_REWARD":{"address":5603162,"default_item":106,"flag":1819},"TRAINER_DAWSON_REWARD":{"address":5603472,"default_item":104,"flag":1974},"TRAINER_DAYTON_REWARD":{"address":5603604,"default_item":108,"flag":2040},"TRAINER_DEANDRE_REWARD":{"address":5603514,"default_item":103,"flag":1995},"TRAINER_DEAN_REWARD":{"address":5602412,"default_item":103,"flag":1444},"TRAINER_DEBRA_REWARD":{"address":5603004,"default_item":106,"flag":1740},"TRAINER_DECLAN_REWARD":{"address":5602114,"default_item":106,"flag":1295},"TRAINER_DEMETRIUS_REWARD":{"address":5602834,"default_item":106,"flag":1655},"TRAINER_DENISE_REWARD":{"address":5602972,"default_item":103,"flag":1724},"TRAINER_DEREK_REWARD":{"address":5602538,"default_item":108,"flag":1507},"TRAINER_DEVAN_REWARD":{"address":5603590,"default_item":106,"flag":2033},"TRAINER_DEZ_AND_LUKE_REWARD":{"address":5603364,"default_item":108,"flag":1920},"TRAINER_DIANA_1_REWARD":{"address":5603032,"default_item":106,"flag":1754},"TRAINER_DIANNE_REWARD":{"address":5602918,"default_item":104,"flag":1697},"TRAINER_DILLON_REWARD":{"address":5602738,"default_item":106,"flag":1607},"TRAINER_DOMINIK_REWARD":{"address":5602388,"default_item":103,"flag":1432},"TRAINER_DONALD_REWARD":{"address":5602532,"default_item":104,"flag":1504},"TRAINER_DONNY_REWARD":{"address":5602852,"default_item":104,"flag":1664},"TRAINER_DOUGLAS_REWARD":{"address":5602390,"default_item":103,"flag":1433},"TRAINER_DOUG_REWARD":{"address":5603320,"default_item":106,"flag":1898},"TRAINER_DRAKE_REWARD":{"address":5602612,"default_item":110,"flag":1544},"TRAINER_DREW_REWARD":{"address":5602506,"default_item":106,"flag":1491},"TRAINER_DUNCAN_REWARD":{"address":5603076,"default_item":108,"flag":1776},"TRAINER_DUSTY_1_REWARD":{"address":5602172,"default_item":104,"flag":1324},"TRAINER_DWAYNE_REWARD":{"address":5603070,"default_item":106,"flag":1773},"TRAINER_DYLAN_1_REWARD":{"address":5602812,"default_item":106,"flag":1644},"TRAINER_EDGAR_REWARD":{"address":5602242,"default_item":104,"flag":1359},"TRAINER_EDMOND_REWARD":{"address":5603066,"default_item":106,"flag":1771},"TRAINER_EDWARDO_REWARD":{"address":5602892,"default_item":108,"flag":1684},"TRAINER_EDWARD_REWARD":{"address":5602548,"default_item":106,"flag":1512},"TRAINER_EDWIN_1_REWARD":{"address":5603108,"default_item":108,"flag":1792},"TRAINER_ED_REWARD":{"address":5602110,"default_item":104,"flag":1293},"TRAINER_ELIJAH_REWARD":{"address":5603568,"default_item":108,"flag":2022},"TRAINER_ELI_REWARD":{"address":5603086,"default_item":108,"flag":1781},"TRAINER_ELLIOT_1_REWARD":{"address":5602762,"default_item":106,"flag":1619},"TRAINER_ERIC_REWARD":{"address":5603348,"default_item":108,"flag":1912},"TRAINER_ERNEST_1_REWARD":{"address":5603068,"default_item":104,"flag":1772},"TRAINER_ETHAN_1_REWARD":{"address":5602516,"default_item":106,"flag":1496},"TRAINER_FABIAN_REWARD":{"address":5603602,"default_item":108,"flag":2039},"TRAINER_FELIX_REWARD":{"address":5602160,"default_item":104,"flag":1318},"TRAINER_FERNANDO_1_REWARD":{"address":5602474,"default_item":108,"flag":1475},"TRAINER_FLANNERY_1_REWARD":{"address":5602620,"default_item":107,"flag":1548},"TRAINER_FLINT_REWARD":{"address":5603392,"default_item":106,"flag":1934},"TRAINER_FOSTER_REWARD":{"address":5602176,"default_item":104,"flag":1326},"TRAINER_FRANKLIN_REWARD":{"address":5602424,"default_item":106,"flag":1450},"TRAINER_FREDRICK_REWARD":{"address":5602142,"default_item":104,"flag":1309},"TRAINER_GABRIELLE_1_REWARD":{"address":5602102,"default_item":104,"flag":1289},"TRAINER_GARRET_REWARD":{"address":5602360,"default_item":110,"flag":1418},"TRAINER_GARRISON_REWARD":{"address":5603178,"default_item":104,"flag":1827},"TRAINER_GEORGE_REWARD":{"address":5602230,"default_item":104,"flag":1353},"TRAINER_GERALD_REWARD":{"address":5603380,"default_item":104,"flag":1928},"TRAINER_GILBERT_REWARD":{"address":5602422,"default_item":106,"flag":1449},"TRAINER_GINA_AND_MIA_1_REWARD":{"address":5603050,"default_item":103,"flag":1763},"TRAINER_GLACIA_REWARD":{"address":5602610,"default_item":110,"flag":1543},"TRAINER_GRACE_REWARD":{"address":5602984,"default_item":106,"flag":1730},"TRAINER_GREG_REWARD":{"address":5603322,"default_item":106,"flag":1899},"TRAINER_GRUNT_AQUA_HIDEOUT_1_REWARD":{"address":5602088,"default_item":106,"flag":1282},"TRAINER_GRUNT_AQUA_HIDEOUT_2_REWARD":{"address":5602090,"default_item":106,"flag":1283},"TRAINER_GRUNT_AQUA_HIDEOUT_3_REWARD":{"address":5602092,"default_item":106,"flag":1284},"TRAINER_GRUNT_AQUA_HIDEOUT_4_REWARD":{"address":5602094,"default_item":106,"flag":1285},"TRAINER_GRUNT_AQUA_HIDEOUT_5_REWARD":{"address":5602138,"default_item":106,"flag":1307},"TRAINER_GRUNT_AQUA_HIDEOUT_6_REWARD":{"address":5602140,"default_item":106,"flag":1308},"TRAINER_GRUNT_AQUA_HIDEOUT_7_REWARD":{"address":5602468,"default_item":106,"flag":1472},"TRAINER_GRUNT_AQUA_HIDEOUT_8_REWARD":{"address":5602470,"default_item":106,"flag":1473},"TRAINER_GRUNT_MAGMA_HIDEOUT_10_REWARD":{"address":5603534,"default_item":106,"flag":2005},"TRAINER_GRUNT_MAGMA_HIDEOUT_11_REWARD":{"address":5603536,"default_item":106,"flag":2006},"TRAINER_GRUNT_MAGMA_HIDEOUT_12_REWARD":{"address":5603538,"default_item":106,"flag":2007},"TRAINER_GRUNT_MAGMA_HIDEOUT_13_REWARD":{"address":5603540,"default_item":106,"flag":2008},"TRAINER_GRUNT_MAGMA_HIDEOUT_14_REWARD":{"address":5603542,"default_item":106,"flag":2009},"TRAINER_GRUNT_MAGMA_HIDEOUT_15_REWARD":{"address":5603544,"default_item":106,"flag":2010},"TRAINER_GRUNT_MAGMA_HIDEOUT_16_REWARD":{"address":5603546,"default_item":106,"flag":2011},"TRAINER_GRUNT_MAGMA_HIDEOUT_1_REWARD":{"address":5603516,"default_item":106,"flag":1996},"TRAINER_GRUNT_MAGMA_HIDEOUT_2_REWARD":{"address":5603518,"default_item":106,"flag":1997},"TRAINER_GRUNT_MAGMA_HIDEOUT_3_REWARD":{"address":5603520,"default_item":106,"flag":1998},"TRAINER_GRUNT_MAGMA_HIDEOUT_4_REWARD":{"address":5603522,"default_item":106,"flag":1999},"TRAINER_GRUNT_MAGMA_HIDEOUT_5_REWARD":{"address":5603524,"default_item":106,"flag":2000},"TRAINER_GRUNT_MAGMA_HIDEOUT_6_REWARD":{"address":5603526,"default_item":106,"flag":2001},"TRAINER_GRUNT_MAGMA_HIDEOUT_7_REWARD":{"address":5603528,"default_item":106,"flag":2002},"TRAINER_GRUNT_MAGMA_HIDEOUT_8_REWARD":{"address":5603530,"default_item":106,"flag":2003},"TRAINER_GRUNT_MAGMA_HIDEOUT_9_REWARD":{"address":5603532,"default_item":106,"flag":2004},"TRAINER_GRUNT_MT_CHIMNEY_1_REWARD":{"address":5602376,"default_item":106,"flag":1426},"TRAINER_GRUNT_MT_CHIMNEY_2_REWARD":{"address":5603242,"default_item":106,"flag":1859},"TRAINER_GRUNT_MT_PYRE_1_REWARD":{"address":5602130,"default_item":106,"flag":1303},"TRAINER_GRUNT_MT_PYRE_2_REWARD":{"address":5602132,"default_item":106,"flag":1304},"TRAINER_GRUNT_MT_PYRE_3_REWARD":{"address":5602134,"default_item":106,"flag":1305},"TRAINER_GRUNT_MT_PYRE_4_REWARD":{"address":5603222,"default_item":106,"flag":1849},"TRAINER_GRUNT_MUSEUM_1_REWARD":{"address":5602124,"default_item":106,"flag":1300},"TRAINER_GRUNT_MUSEUM_2_REWARD":{"address":5602126,"default_item":106,"flag":1301},"TRAINER_GRUNT_PETALBURG_WOODS_REWARD":{"address":5602104,"default_item":103,"flag":1290},"TRAINER_GRUNT_RUSTURF_TUNNEL_REWARD":{"address":5602116,"default_item":103,"flag":1296},"TRAINER_GRUNT_SEAFLOOR_CAVERN_1_REWARD":{"address":5602096,"default_item":108,"flag":1286},"TRAINER_GRUNT_SEAFLOOR_CAVERN_2_REWARD":{"address":5602098,"default_item":108,"flag":1287},"TRAINER_GRUNT_SEAFLOOR_CAVERN_3_REWARD":{"address":5602100,"default_item":108,"flag":1288},"TRAINER_GRUNT_SEAFLOOR_CAVERN_4_REWARD":{"address":5602112,"default_item":108,"flag":1294},"TRAINER_GRUNT_SEAFLOOR_CAVERN_5_REWARD":{"address":5603218,"default_item":108,"flag":1847},"TRAINER_GRUNT_SPACE_CENTER_1_REWARD":{"address":5602128,"default_item":106,"flag":1302},"TRAINER_GRUNT_SPACE_CENTER_2_REWARD":{"address":5602316,"default_item":106,"flag":1396},"TRAINER_GRUNT_SPACE_CENTER_3_REWARD":{"address":5603256,"default_item":106,"flag":1866},"TRAINER_GRUNT_SPACE_CENTER_4_REWARD":{"address":5603258,"default_item":106,"flag":1867},"TRAINER_GRUNT_SPACE_CENTER_5_REWARD":{"address":5603260,"default_item":106,"flag":1868},"TRAINER_GRUNT_SPACE_CENTER_6_REWARD":{"address":5603262,"default_item":106,"flag":1869},"TRAINER_GRUNT_SPACE_CENTER_7_REWARD":{"address":5603264,"default_item":106,"flag":1870},"TRAINER_GRUNT_WEATHER_INST_1_REWARD":{"address":5602118,"default_item":106,"flag":1297},"TRAINER_GRUNT_WEATHER_INST_2_REWARD":{"address":5602120,"default_item":106,"flag":1298},"TRAINER_GRUNT_WEATHER_INST_3_REWARD":{"address":5602122,"default_item":106,"flag":1299},"TRAINER_GRUNT_WEATHER_INST_4_REWARD":{"address":5602136,"default_item":106,"flag":1306},"TRAINER_GRUNT_WEATHER_INST_5_REWARD":{"address":5603276,"default_item":106,"flag":1876},"TRAINER_GWEN_REWARD":{"address":5602202,"default_item":103,"flag":1339},"TRAINER_HAILEY_REWARD":{"address":5603478,"default_item":103,"flag":1977},"TRAINER_HALEY_1_REWARD":{"address":5603292,"default_item":103,"flag":1884},"TRAINER_HALLE_REWARD":{"address":5603176,"default_item":104,"flag":1826},"TRAINER_HANNAH_REWARD":{"address":5602572,"default_item":108,"flag":1524},"TRAINER_HARRISON_REWARD":{"address":5603240,"default_item":106,"flag":1858},"TRAINER_HAYDEN_REWARD":{"address":5603498,"default_item":106,"flag":1987},"TRAINER_HECTOR_REWARD":{"address":5603110,"default_item":104,"flag":1793},"TRAINER_HEIDI_REWARD":{"address":5603022,"default_item":106,"flag":1749},"TRAINER_HELENE_REWARD":{"address":5603586,"default_item":106,"flag":2031},"TRAINER_HENRY_REWARD":{"address":5603420,"default_item":104,"flag":1948},"TRAINER_HERMAN_REWARD":{"address":5602418,"default_item":106,"flag":1447},"TRAINER_HIDEO_REWARD":{"address":5603386,"default_item":106,"flag":1931},"TRAINER_HITOSHI_REWARD":{"address":5602444,"default_item":104,"flag":1460},"TRAINER_HOPE_REWARD":{"address":5602276,"default_item":104,"flag":1376},"TRAINER_HUDSON_REWARD":{"address":5603104,"default_item":104,"flag":1790},"TRAINER_HUEY_REWARD":{"address":5603064,"default_item":106,"flag":1770},"TRAINER_HUGH_REWARD":{"address":5602882,"default_item":108,"flag":1679},"TRAINER_HUMBERTO_REWARD":{"address":5602888,"default_item":108,"flag":1682},"TRAINER_IMANI_REWARD":{"address":5602968,"default_item":103,"flag":1722},"TRAINER_IRENE_REWARD":{"address":5603036,"default_item":106,"flag":1756},"TRAINER_ISAAC_1_REWARD":{"address":5603160,"default_item":106,"flag":1818},"TRAINER_ISABELLA_REWARD":{"address":5603274,"default_item":104,"flag":1875},"TRAINER_ISABELLE_REWARD":{"address":5603556,"default_item":103,"flag":2016},"TRAINER_ISABEL_1_REWARD":{"address":5602688,"default_item":104,"flag":1582},"TRAINER_ISAIAH_1_REWARD":{"address":5602836,"default_item":104,"flag":1656},"TRAINER_ISOBEL_REWARD":{"address":5602850,"default_item":104,"flag":1663},"TRAINER_IVAN_REWARD":{"address":5602758,"default_item":106,"flag":1617},"TRAINER_JACE_REWARD":{"address":5602492,"default_item":108,"flag":1484},"TRAINER_JACKI_1_REWARD":{"address":5602582,"default_item":108,"flag":1529},"TRAINER_JACKSON_1_REWARD":{"address":5603188,"default_item":104,"flag":1832},"TRAINER_JACK_REWARD":{"address":5602428,"default_item":106,"flag":1452},"TRAINER_JACLYN_REWARD":{"address":5602570,"default_item":106,"flag":1523},"TRAINER_JACOB_REWARD":{"address":5602786,"default_item":106,"flag":1631},"TRAINER_JAIDEN_REWARD":{"address":5603582,"default_item":106,"flag":2029},"TRAINER_JAMES_1_REWARD":{"address":5603326,"default_item":103,"flag":1901},"TRAINER_JANICE_REWARD":{"address":5603294,"default_item":103,"flag":1885},"TRAINER_JANI_REWARD":{"address":5602920,"default_item":103,"flag":1698},"TRAINER_JARED_REWARD":{"address":5602886,"default_item":108,"flag":1681},"TRAINER_JASMINE_REWARD":{"address":5602802,"default_item":103,"flag":1639},"TRAINER_JAYLEN_REWARD":{"address":5602736,"default_item":106,"flag":1606},"TRAINER_JAZMYN_REWARD":{"address":5603090,"default_item":106,"flag":1783},"TRAINER_JEFFREY_1_REWARD":{"address":5602536,"default_item":104,"flag":1506},"TRAINER_JEFF_REWARD":{"address":5602488,"default_item":108,"flag":1482},"TRAINER_JENNA_REWARD":{"address":5603204,"default_item":104,"flag":1840},"TRAINER_JENNIFER_REWARD":{"address":5602274,"default_item":104,"flag":1375},"TRAINER_JENNY_1_REWARD":{"address":5602982,"default_item":106,"flag":1729},"TRAINER_JEROME_REWARD":{"address":5602396,"default_item":103,"flag":1436},"TRAINER_JERRY_1_REWARD":{"address":5602630,"default_item":103,"flag":1553},"TRAINER_JESSICA_1_REWARD":{"address":5602338,"default_item":104,"flag":1407},"TRAINER_JOCELYN_REWARD":{"address":5602934,"default_item":106,"flag":1705},"TRAINER_JODY_REWARD":{"address":5602266,"default_item":104,"flag":1371},"TRAINER_JOEY_REWARD":{"address":5602728,"default_item":103,"flag":1602},"TRAINER_JOHANNA_REWARD":{"address":5603378,"default_item":104,"flag":1927},"TRAINER_JOHNSON_REWARD":{"address":5603592,"default_item":103,"flag":2034},"TRAINER_JOHN_AND_JAY_1_REWARD":{"address":5603446,"default_item":104,"flag":1961},"TRAINER_JONAH_REWARD":{"address":5603418,"default_item":104,"flag":1947},"TRAINER_JONAS_REWARD":{"address":5603092,"default_item":106,"flag":1784},"TRAINER_JONATHAN_REWARD":{"address":5603280,"default_item":104,"flag":1878},"TRAINER_JOSEPH_REWARD":{"address":5603484,"default_item":106,"flag":1980},"TRAINER_JOSE_REWARD":{"address":5603318,"default_item":103,"flag":1897},"TRAINER_JOSH_REWARD":{"address":5602724,"default_item":103,"flag":1600},"TRAINER_JOSUE_REWARD":{"address":5603560,"default_item":108,"flag":2018},"TRAINER_JUAN_1_REWARD":{"address":5602628,"default_item":109,"flag":1552},"TRAINER_JULIE_REWARD":{"address":5602284,"default_item":104,"flag":1380},"TRAINER_JULIO_REWARD":{"address":5603216,"default_item":108,"flag":1846},"TRAINER_KAI_REWARD":{"address":5603510,"default_item":108,"flag":1993},"TRAINER_KALEB_REWARD":{"address":5603482,"default_item":104,"flag":1979},"TRAINER_KARA_REWARD":{"address":5602998,"default_item":106,"flag":1737},"TRAINER_KAREN_1_REWARD":{"address":5602644,"default_item":103,"flag":1560},"TRAINER_KATELYNN_REWARD":{"address":5602734,"default_item":104,"flag":1605},"TRAINER_KATELYN_1_REWARD":{"address":5602856,"default_item":104,"flag":1666},"TRAINER_KATE_AND_JOY_REWARD":{"address":5602656,"default_item":106,"flag":1566},"TRAINER_KATHLEEN_REWARD":{"address":5603250,"default_item":108,"flag":1863},"TRAINER_KATIE_REWARD":{"address":5602994,"default_item":106,"flag":1735},"TRAINER_KAYLA_REWARD":{"address":5602578,"default_item":106,"flag":1527},"TRAINER_KAYLEY_REWARD":{"address":5603094,"default_item":104,"flag":1785},"TRAINER_KEEGAN_REWARD":{"address":5602494,"default_item":108,"flag":1485},"TRAINER_KEIGO_REWARD":{"address":5603388,"default_item":106,"flag":1932},"TRAINER_KELVIN_REWARD":{"address":5603098,"default_item":104,"flag":1787},"TRAINER_KENT_REWARD":{"address":5603324,"default_item":106,"flag":1900},"TRAINER_KEVIN_REWARD":{"address":5602426,"default_item":106,"flag":1451},"TRAINER_KIM_AND_IRIS_REWARD":{"address":5603440,"default_item":106,"flag":1958},"TRAINER_KINDRA_REWARD":{"address":5602296,"default_item":108,"flag":1386},"TRAINER_KIRA_AND_DAN_1_REWARD":{"address":5603368,"default_item":108,"flag":1922},"TRAINER_KIRK_REWARD":{"address":5602466,"default_item":106,"flag":1471},"TRAINER_KIYO_REWARD":{"address":5602446,"default_item":104,"flag":1461},"TRAINER_KOICHI_REWARD":{"address":5602448,"default_item":108,"flag":1462},"TRAINER_KOJI_1_REWARD":{"address":5603428,"default_item":104,"flag":1952},"TRAINER_KYLA_REWARD":{"address":5602970,"default_item":103,"flag":1723},"TRAINER_KYRA_REWARD":{"address":5603580,"default_item":104,"flag":2028},"TRAINER_LAO_1_REWARD":{"address":5602922,"default_item":103,"flag":1699},"TRAINER_LARRY_REWARD":{"address":5602510,"default_item":106,"flag":1493},"TRAINER_LAURA_REWARD":{"address":5602936,"default_item":106,"flag":1706},"TRAINER_LAUREL_REWARD":{"address":5603010,"default_item":106,"flag":1743},"TRAINER_LAWRENCE_REWARD":{"address":5603504,"default_item":106,"flag":1990},"TRAINER_LEAH_REWARD":{"address":5602154,"default_item":108,"flag":1315},"TRAINER_LEA_AND_JED_REWARD":{"address":5603366,"default_item":104,"flag":1921},"TRAINER_LENNY_REWARD":{"address":5603340,"default_item":108,"flag":1908},"TRAINER_LEONARDO_REWARD":{"address":5603236,"default_item":106,"flag":1856},"TRAINER_LEONARD_REWARD":{"address":5603074,"default_item":104,"flag":1775},"TRAINER_LEONEL_REWARD":{"address":5603608,"default_item":104,"flag":2042},"TRAINER_LILA_AND_ROY_1_REWARD":{"address":5603458,"default_item":106,"flag":1967},"TRAINER_LILITH_REWARD":{"address":5603230,"default_item":106,"flag":1853},"TRAINER_LINDA_REWARD":{"address":5603006,"default_item":106,"flag":1741},"TRAINER_LISA_AND_RAY_REWARD":{"address":5603468,"default_item":106,"flag":1972},"TRAINER_LOLA_1_REWARD":{"address":5602198,"default_item":103,"flag":1337},"TRAINER_LORENZO_REWARD":{"address":5603190,"default_item":104,"flag":1833},"TRAINER_LUCAS_1_REWARD":{"address":5603342,"default_item":108,"flag":1909},"TRAINER_LUIS_REWARD":{"address":5602386,"default_item":103,"flag":1431},"TRAINER_LUNG_REWARD":{"address":5602924,"default_item":103,"flag":1700},"TRAINER_LYDIA_1_REWARD":{"address":5603174,"default_item":106,"flag":1825},"TRAINER_LYLE_REWARD":{"address":5603316,"default_item":103,"flag":1896},"TRAINER_MACEY_REWARD":{"address":5603266,"default_item":108,"flag":1871},"TRAINER_MADELINE_1_REWARD":{"address":5602952,"default_item":108,"flag":1714},"TRAINER_MAKAYLA_REWARD":{"address":5603600,"default_item":104,"flag":2038},"TRAINER_MARCEL_REWARD":{"address":5602106,"default_item":104,"flag":1291},"TRAINER_MARCOS_REWARD":{"address":5603488,"default_item":106,"flag":1982},"TRAINER_MARC_REWARD":{"address":5603226,"default_item":106,"flag":1851},"TRAINER_MARIA_1_REWARD":{"address":5602822,"default_item":106,"flag":1649},"TRAINER_MARK_REWARD":{"address":5602374,"default_item":104,"flag":1425},"TRAINER_MARLENE_REWARD":{"address":5603588,"default_item":106,"flag":2032},"TRAINER_MARLEY_REWARD":{"address":5603100,"default_item":104,"flag":1788},"TRAINER_MARY_REWARD":{"address":5602262,"default_item":104,"flag":1369},"TRAINER_MATTHEW_REWARD":{"address":5602398,"default_item":103,"flag":1437},"TRAINER_MATT_REWARD":{"address":5602144,"default_item":104,"flag":1310},"TRAINER_MAURA_REWARD":{"address":5602576,"default_item":108,"flag":1526},"TRAINER_MAXIE_MAGMA_HIDEOUT_REWARD":{"address":5603286,"default_item":107,"flag":1881},"TRAINER_MAXIE_MT_CHIMNEY_REWARD":{"address":5603288,"default_item":104,"flag":1882},"TRAINER_MAY_LILYCOVE_MUDKIP_REWARD":{"address":5603412,"default_item":104,"flag":1944},"TRAINER_MAY_LILYCOVE_TORCHIC_REWARD":{"address":5603416,"default_item":104,"flag":1946},"TRAINER_MAY_LILYCOVE_TREECKO_REWARD":{"address":5603414,"default_item":104,"flag":1945},"TRAINER_MAY_ROUTE_103_MUDKIP_REWARD":{"address":5603142,"default_item":106,"flag":1809},"TRAINER_MAY_ROUTE_103_TORCHIC_REWARD":{"address":5603154,"default_item":106,"flag":1815},"TRAINER_MAY_ROUTE_103_TREECKO_REWARD":{"address":5603148,"default_item":106,"flag":1812},"TRAINER_MAY_ROUTE_110_MUDKIP_REWARD":{"address":5603144,"default_item":104,"flag":1810},"TRAINER_MAY_ROUTE_110_TORCHIC_REWARD":{"address":5603156,"default_item":104,"flag":1816},"TRAINER_MAY_ROUTE_110_TREECKO_REWARD":{"address":5603150,"default_item":104,"flag":1813},"TRAINER_MAY_ROUTE_119_MUDKIP_REWARD":{"address":5603146,"default_item":104,"flag":1811},"TRAINER_MAY_ROUTE_119_TORCHIC_REWARD":{"address":5603158,"default_item":104,"flag":1817},"TRAINER_MAY_ROUTE_119_TREECKO_REWARD":{"address":5603152,"default_item":104,"flag":1814},"TRAINER_MAY_RUSTBORO_MUDKIP_REWARD":{"address":5603284,"default_item":108,"flag":1880},"TRAINER_MAY_RUSTBORO_TORCHIC_REWARD":{"address":5603622,"default_item":108,"flag":2049},"TRAINER_MAY_RUSTBORO_TREECKO_REWARD":{"address":5603620,"default_item":108,"flag":2048},"TRAINER_MELINA_REWARD":{"address":5603594,"default_item":106,"flag":2035},"TRAINER_MELISSA_REWARD":{"address":5602332,"default_item":104,"flag":1404},"TRAINER_MEL_AND_PAUL_REWARD":{"address":5603444,"default_item":108,"flag":1960},"TRAINER_MICAH_REWARD":{"address":5602594,"default_item":107,"flag":1535},"TRAINER_MICHELLE_REWARD":{"address":5602280,"default_item":104,"flag":1378},"TRAINER_MIGUEL_1_REWARD":{"address":5602670,"default_item":104,"flag":1573},"TRAINER_MIKE_2_REWARD":{"address":5603354,"default_item":106,"flag":1915},"TRAINER_MISSY_REWARD":{"address":5602978,"default_item":103,"flag":1727},"TRAINER_MITCHELL_REWARD":{"address":5603164,"default_item":104,"flag":1820},"TRAINER_MIU_AND_YUKI_REWARD":{"address":5603052,"default_item":106,"flag":1764},"TRAINER_MOLLIE_REWARD":{"address":5602358,"default_item":104,"flag":1417},"TRAINER_MYLES_REWARD":{"address":5603614,"default_item":104,"flag":2045},"TRAINER_NANCY_REWARD":{"address":5603028,"default_item":106,"flag":1752},"TRAINER_NAOMI_REWARD":{"address":5602322,"default_item":110,"flag":1399},"TRAINER_NATE_REWARD":{"address":5603248,"default_item":107,"flag":1862},"TRAINER_NED_REWARD":{"address":5602764,"default_item":106,"flag":1620},"TRAINER_NICHOLAS_REWARD":{"address":5603254,"default_item":108,"flag":1865},"TRAINER_NICOLAS_1_REWARD":{"address":5602868,"default_item":104,"flag":1672},"TRAINER_NIKKI_REWARD":{"address":5602990,"default_item":106,"flag":1733},"TRAINER_NOB_1_REWARD":{"address":5602450,"default_item":106,"flag":1463},"TRAINER_NOLAN_REWARD":{"address":5602768,"default_item":108,"flag":1622},"TRAINER_NOLEN_REWARD":{"address":5602406,"default_item":106,"flag":1441},"TRAINER_NORMAN_1_REWARD":{"address":5602622,"default_item":107,"flag":1549},"TRAINER_OLIVIA_REWARD":{"address":5602344,"default_item":107,"flag":1410},"TRAINER_OWEN_REWARD":{"address":5602250,"default_item":104,"flag":1363},"TRAINER_PABLO_1_REWARD":{"address":5602838,"default_item":104,"flag":1657},"TRAINER_PARKER_REWARD":{"address":5602228,"default_item":104,"flag":1352},"TRAINER_PAT_REWARD":{"address":5603616,"default_item":104,"flag":2046},"TRAINER_PAXTON_REWARD":{"address":5603272,"default_item":104,"flag":1874},"TRAINER_PERRY_REWARD":{"address":5602880,"default_item":108,"flag":1678},"TRAINER_PETE_REWARD":{"address":5603554,"default_item":103,"flag":2015},"TRAINER_PHILLIP_REWARD":{"address":5603072,"default_item":104,"flag":1774},"TRAINER_PHIL_REWARD":{"address":5602884,"default_item":108,"flag":1680},"TRAINER_PHOEBE_REWARD":{"address":5602608,"default_item":110,"flag":1542},"TRAINER_PRESLEY_REWARD":{"address":5602890,"default_item":104,"flag":1683},"TRAINER_PRESTON_REWARD":{"address":5602550,"default_item":108,"flag":1513},"TRAINER_QUINCY_REWARD":{"address":5602732,"default_item":104,"flag":1604},"TRAINER_RACHEL_REWARD":{"address":5603606,"default_item":104,"flag":2041},"TRAINER_RANDALL_REWARD":{"address":5602226,"default_item":104,"flag":1351},"TRAINER_REED_REWARD":{"address":5603434,"default_item":106,"flag":1955},"TRAINER_RELI_AND_IAN_REWARD":{"address":5603456,"default_item":106,"flag":1966},"TRAINER_REYNA_REWARD":{"address":5603102,"default_item":108,"flag":1789},"TRAINER_RHETT_REWARD":{"address":5603490,"default_item":106,"flag":1983},"TRAINER_RICHARD_REWARD":{"address":5602416,"default_item":106,"flag":1446},"TRAINER_RICKY_1_REWARD":{"address":5602212,"default_item":103,"flag":1344},"TRAINER_RICK_REWARD":{"address":5603314,"default_item":103,"flag":1895},"TRAINER_RILEY_REWARD":{"address":5603390,"default_item":106,"flag":1933},"TRAINER_ROBERT_1_REWARD":{"address":5602896,"default_item":108,"flag":1686},"TRAINER_RODNEY_REWARD":{"address":5602414,"default_item":106,"flag":1445},"TRAINER_ROGER_REWARD":{"address":5603422,"default_item":104,"flag":1949},"TRAINER_ROLAND_REWARD":{"address":5602404,"default_item":106,"flag":1440},"TRAINER_RONALD_REWARD":{"address":5602784,"default_item":104,"flag":1630},"TRAINER_ROSE_1_REWARD":{"address":5602158,"default_item":106,"flag":1317},"TRAINER_ROXANNE_1_REWARD":{"address":5602614,"default_item":104,"flag":1545},"TRAINER_RUBEN_REWARD":{"address":5603426,"default_item":104,"flag":1951},"TRAINER_SAMANTHA_REWARD":{"address":5602574,"default_item":108,"flag":1525},"TRAINER_SAMUEL_REWARD":{"address":5602246,"default_item":104,"flag":1361},"TRAINER_SANTIAGO_REWARD":{"address":5602420,"default_item":106,"flag":1448},"TRAINER_SARAH_REWARD":{"address":5603474,"default_item":104,"flag":1975},"TRAINER_SAWYER_1_REWARD":{"address":5602086,"default_item":108,"flag":1281},"TRAINER_SHANE_REWARD":{"address":5602512,"default_item":106,"flag":1494},"TRAINER_SHANNON_REWARD":{"address":5602278,"default_item":104,"flag":1377},"TRAINER_SHARON_REWARD":{"address":5602988,"default_item":106,"flag":1732},"TRAINER_SHAWN_REWARD":{"address":5602472,"default_item":106,"flag":1474},"TRAINER_SHAYLA_REWARD":{"address":5603578,"default_item":108,"flag":2027},"TRAINER_SHEILA_REWARD":{"address":5602334,"default_item":104,"flag":1405},"TRAINER_SHELBY_1_REWARD":{"address":5602710,"default_item":108,"flag":1593},"TRAINER_SHELLY_SEAFLOOR_CAVERN_REWARD":{"address":5602150,"default_item":104,"flag":1313},"TRAINER_SHELLY_WEATHER_INSTITUTE_REWARD":{"address":5602148,"default_item":104,"flag":1312},"TRAINER_SHIRLEY_REWARD":{"address":5602336,"default_item":104,"flag":1406},"TRAINER_SIDNEY_REWARD":{"address":5602606,"default_item":110,"flag":1541},"TRAINER_SIENNA_REWARD":{"address":5603002,"default_item":106,"flag":1739},"TRAINER_SIMON_REWARD":{"address":5602214,"default_item":103,"flag":1345},"TRAINER_SOPHIE_REWARD":{"address":5603500,"default_item":106,"flag":1988},"TRAINER_SPENCER_REWARD":{"address":5602402,"default_item":106,"flag":1439},"TRAINER_STAN_REWARD":{"address":5602408,"default_item":106,"flag":1442},"TRAINER_STEVEN_REWARD":{"address":5603692,"default_item":109,"flag":2084},"TRAINER_STEVE_1_REWARD":{"address":5602370,"default_item":104,"flag":1423},"TRAINER_SUSIE_REWARD":{"address":5602996,"default_item":106,"flag":1736},"TRAINER_SYLVIA_REWARD":{"address":5603234,"default_item":108,"flag":1855},"TRAINER_TABITHA_MAGMA_HIDEOUT_REWARD":{"address":5603548,"default_item":104,"flag":2012},"TRAINER_TABITHA_MT_CHIMNEY_REWARD":{"address":5603278,"default_item":108,"flag":1877},"TRAINER_TAKAO_REWARD":{"address":5602442,"default_item":106,"flag":1459},"TRAINER_TAKASHI_REWARD":{"address":5602916,"default_item":106,"flag":1696},"TRAINER_TALIA_REWARD":{"address":5602854,"default_item":104,"flag":1665},"TRAINER_TAMMY_REWARD":{"address":5602298,"default_item":106,"flag":1387},"TRAINER_TANYA_REWARD":{"address":5602986,"default_item":106,"flag":1731},"TRAINER_TARA_REWARD":{"address":5602976,"default_item":103,"flag":1726},"TRAINER_TASHA_REWARD":{"address":5602302,"default_item":108,"flag":1389},"TRAINER_TATE_AND_LIZA_1_REWARD":{"address":5602626,"default_item":109,"flag":1551},"TRAINER_TAYLOR_REWARD":{"address":5602534,"default_item":104,"flag":1505},"TRAINER_THALIA_1_REWARD":{"address":5602372,"default_item":104,"flag":1424},"TRAINER_THOMAS_REWARD":{"address":5602596,"default_item":107,"flag":1536},"TRAINER_TIANA_REWARD":{"address":5603290,"default_item":103,"flag":1883},"TRAINER_TIFFANY_REWARD":{"address":5602346,"default_item":107,"flag":1411},"TRAINER_TIMMY_REWARD":{"address":5602752,"default_item":103,"flag":1614},"TRAINER_TIMOTHY_1_REWARD":{"address":5602698,"default_item":104,"flag":1587},"TRAINER_TISHA_REWARD":{"address":5603436,"default_item":106,"flag":1956},"TRAINER_TOMMY_REWARD":{"address":5602726,"default_item":103,"flag":1601},"TRAINER_TONY_1_REWARD":{"address":5602394,"default_item":103,"flag":1435},"TRAINER_TORI_AND_TIA_REWARD":{"address":5603438,"default_item":103,"flag":1957},"TRAINER_TRAVIS_REWARD":{"address":5602520,"default_item":106,"flag":1498},"TRAINER_TRENT_1_REWARD":{"address":5603338,"default_item":106,"flag":1907},"TRAINER_TYRA_AND_IVY_REWARD":{"address":5603442,"default_item":106,"flag":1959},"TRAINER_TYRON_REWARD":{"address":5603492,"default_item":106,"flag":1984},"TRAINER_VALERIE_1_REWARD":{"address":5602300,"default_item":108,"flag":1388},"TRAINER_VANESSA_REWARD":{"address":5602684,"default_item":104,"flag":1580},"TRAINER_VICKY_REWARD":{"address":5602708,"default_item":108,"flag":1592},"TRAINER_VICTORIA_REWARD":{"address":5602682,"default_item":106,"flag":1579},"TRAINER_VICTOR_REWARD":{"address":5602668,"default_item":106,"flag":1572},"TRAINER_VIOLET_REWARD":{"address":5602162,"default_item":104,"flag":1319},"TRAINER_VIRGIL_REWARD":{"address":5602552,"default_item":108,"flag":1514},"TRAINER_VITO_REWARD":{"address":5602248,"default_item":104,"flag":1362},"TRAINER_VIVIAN_REWARD":{"address":5603382,"default_item":106,"flag":1929},"TRAINER_VIVI_REWARD":{"address":5603296,"default_item":106,"flag":1886},"TRAINER_WADE_REWARD":{"address":5602772,"default_item":106,"flag":1624},"TRAINER_WALLACE_REWARD":{"address":5602754,"default_item":110,"flag":1615},"TRAINER_WALLY_MAUVILLE_REWARD":{"address":5603396,"default_item":108,"flag":1936},"TRAINER_WALLY_VR_1_REWARD":{"address":5603122,"default_item":107,"flag":1799},"TRAINER_WALTER_1_REWARD":{"address":5602592,"default_item":104,"flag":1534},"TRAINER_WARREN_REWARD":{"address":5602260,"default_item":104,"flag":1368},"TRAINER_WATTSON_1_REWARD":{"address":5602618,"default_item":104,"flag":1547},"TRAINER_WAYNE_REWARD":{"address":5603430,"default_item":104,"flag":1953},"TRAINER_WENDY_REWARD":{"address":5602268,"default_item":104,"flag":1372},"TRAINER_WILLIAM_REWARD":{"address":5602556,"default_item":106,"flag":1516},"TRAINER_WILTON_1_REWARD":{"address":5602240,"default_item":108,"flag":1358},"TRAINER_WINONA_1_REWARD":{"address":5602624,"default_item":107,"flag":1550},"TRAINER_WINSTON_1_REWARD":{"address":5602356,"default_item":104,"flag":1416},"TRAINER_WYATT_REWARD":{"address":5603506,"default_item":104,"flag":1991},"TRAINER_YASU_REWARD":{"address":5602914,"default_item":106,"flag":1695},"TRAINER_ZANDER_REWARD":{"address":5602146,"default_item":108,"flag":1311}},"maps":{"MAP_ABANDONED_SHIP_CAPTAINS_OFFICE":{"header_address":4766420,"warp_table_address":5496844},"MAP_ABANDONED_SHIP_CORRIDORS_1F":{"header_address":4766196,"warp_table_address":5495920},"MAP_ABANDONED_SHIP_CORRIDORS_B1F":{"header_address":4766252,"warp_table_address":5496248},"MAP_ABANDONED_SHIP_DECK":{"header_address":4766168,"warp_table_address":5495812},"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS":{"fishing_encounters":{"address":5609088,"slots":[129,72,129,72,72,72,72,73,73,73]},"header_address":4766476,"warp_table_address":5496908,"water_encounters":{"address":5609060,"slots":[72,72,72,72,73]}},"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS":{"header_address":4766504,"warp_table_address":5497120},"MAP_ABANDONED_SHIP_ROOMS2_1F":{"header_address":4766392,"warp_table_address":5496752},"MAP_ABANDONED_SHIP_ROOMS2_B1F":{"header_address":4766308,"warp_table_address":5496484},"MAP_ABANDONED_SHIP_ROOMS_1F":{"header_address":4766224,"warp_table_address":5496132},"MAP_ABANDONED_SHIP_ROOMS_B1F":{"fishing_encounters":{"address":5606324,"slots":[129,72,129,72,72,72,72,73,73,73]},"header_address":4766280,"warp_table_address":5496392,"water_encounters":{"address":5606296,"slots":[72,72,72,72,73]}},"MAP_ABANDONED_SHIP_ROOM_B1F":{"header_address":4766364,"warp_table_address":5496596},"MAP_ABANDONED_SHIP_UNDERWATER1":{"header_address":4766336,"warp_table_address":5496536},"MAP_ABANDONED_SHIP_UNDERWATER2":{"header_address":4766448,"warp_table_address":5496880},"MAP_ALTERING_CAVE":{"header_address":4767624,"land_encounters":{"address":5613400,"slots":[41,41,41,41,41,41,41,41,41,41,41,41]},"warp_table_address":5500436},"MAP_ANCIENT_TOMB":{"header_address":4766560,"warp_table_address":5497460},"MAP_AQUA_HIDEOUT_1F":{"header_address":4765300,"warp_table_address":5490892},"MAP_AQUA_HIDEOUT_B1F":{"header_address":4765328,"warp_table_address":5491152},"MAP_AQUA_HIDEOUT_B2F":{"header_address":4765356,"warp_table_address":5491516},"MAP_AQUA_HIDEOUT_UNUSED_RUBY_MAP1":{"header_address":4766728,"warp_table_address":4160749568},"MAP_AQUA_HIDEOUT_UNUSED_RUBY_MAP2":{"header_address":4766756,"warp_table_address":4160749568},"MAP_AQUA_HIDEOUT_UNUSED_RUBY_MAP3":{"header_address":4766784,"warp_table_address":4160749568},"MAP_ARTISAN_CAVE_1F":{"header_address":4767456,"land_encounters":{"address":5613344,"slots":[235,235,235,235,235,235,235,235,235,235,235,235]},"warp_table_address":5500172},"MAP_ARTISAN_CAVE_B1F":{"header_address":4767428,"land_encounters":{"address":5613288,"slots":[235,235,235,235,235,235,235,235,235,235,235,235]},"warp_table_address":5500064},"MAP_BATTLE_COLOSSEUM_2P":{"header_address":4768352,"warp_table_address":5509852},"MAP_BATTLE_COLOSSEUM_4P":{"header_address":4768436,"warp_table_address":5510152},"MAP_BATTLE_FRONTIER_BATTLE_ARENA_BATTLE_ROOM":{"header_address":4770228,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_BATTLE_ARENA_CORRIDOR":{"header_address":4770200,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY":{"header_address":4770172,"warp_table_address":5520908},"MAP_BATTLE_FRONTIER_BATTLE_DOME_BATTLE_ROOM":{"header_address":4769976,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_BATTLE_DOME_CORRIDOR":{"header_address":4769920,"warp_table_address":5519076},"MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY":{"header_address":4769892,"warp_table_address":5518968},"MAP_BATTLE_FRONTIER_BATTLE_DOME_PRE_BATTLE_ROOM":{"header_address":4769948,"warp_table_address":5519136},"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_BATTLE_ROOM":{"header_address":4770312,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY":{"header_address":4770256,"warp_table_address":5521384},"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_PRE_BATTLE_ROOM":{"header_address":4770284,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM":{"header_address":4770060,"warp_table_address":5520116},"MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR":{"header_address":4770032,"warp_table_address":5519944},"MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY":{"header_address":4770004,"warp_table_address":5519696},"MAP_BATTLE_FRONTIER_BATTLE_PIKE_CORRIDOR":{"header_address":4770368,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY":{"header_address":4770340,"warp_table_address":5521808},"MAP_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_FINAL":{"header_address":4770452,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_NORMAL":{"header_address":4770424,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS":{"header_address":4770480,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_BATTLE_PIKE_THREE_PATH_ROOM":{"header_address":4770396,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR":{"header_address":4770116,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY":{"header_address":4770088,"warp_table_address":5520248},"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_TOP":{"header_address":4770144,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM":{"header_address":4769612,"warp_table_address":5516696},"MAP_BATTLE_FRONTIER_BATTLE_TOWER_CORRIDOR":{"header_address":4769584,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_BATTLE_TOWER_ELEVATOR":{"header_address":4769556,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY":{"header_address":4769528,"warp_table_address":5516432},"MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_BATTLE_ROOM":{"header_address":4769864,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_CORRIDOR":{"header_address":4769836,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_PARTNER_ROOM":{"header_address":4769808,"warp_table_address":4160749568},"MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER":{"header_address":4770564,"warp_table_address":5523056},"MAP_BATTLE_FRONTIER_LOUNGE1":{"header_address":4770536,"warp_table_address":5522812},"MAP_BATTLE_FRONTIER_LOUNGE2":{"header_address":4770592,"warp_table_address":5523220},"MAP_BATTLE_FRONTIER_LOUNGE3":{"header_address":4770620,"warp_table_address":5523376},"MAP_BATTLE_FRONTIER_LOUNGE4":{"header_address":4770648,"warp_table_address":5523476},"MAP_BATTLE_FRONTIER_LOUNGE5":{"header_address":4770704,"warp_table_address":5523660},"MAP_BATTLE_FRONTIER_LOUNGE6":{"header_address":4770732,"warp_table_address":5523720},"MAP_BATTLE_FRONTIER_LOUNGE7":{"header_address":4770760,"warp_table_address":5523844},"MAP_BATTLE_FRONTIER_LOUNGE8":{"header_address":4770816,"warp_table_address":5524100},"MAP_BATTLE_FRONTIER_LOUNGE9":{"header_address":4770844,"warp_table_address":5524152},"MAP_BATTLE_FRONTIER_MART":{"header_address":4770928,"warp_table_address":5524588},"MAP_BATTLE_FRONTIER_OUTSIDE_EAST":{"header_address":4769780,"warp_table_address":5518080},"MAP_BATTLE_FRONTIER_OUTSIDE_WEST":{"header_address":4769500,"warp_table_address":5516048},"MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F":{"header_address":4770872,"warp_table_address":5524308},"MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F":{"header_address":4770900,"warp_table_address":5524448},"MAP_BATTLE_FRONTIER_RANKING_HALL":{"header_address":4770508,"warp_table_address":5522560},"MAP_BATTLE_FRONTIER_RECEPTION_GATE":{"header_address":4770788,"warp_table_address":5523992},"MAP_BATTLE_FRONTIER_SCOTTS_HOUSE":{"header_address":4770676,"warp_table_address":5523528},"MAP_BATTLE_PYRAMID_SQUARE01":{"header_address":4768912,"warp_table_address":4160749568},"MAP_BATTLE_PYRAMID_SQUARE02":{"header_address":4768940,"warp_table_address":4160749568},"MAP_BATTLE_PYRAMID_SQUARE03":{"header_address":4768968,"warp_table_address":4160749568},"MAP_BATTLE_PYRAMID_SQUARE04":{"header_address":4768996,"warp_table_address":4160749568},"MAP_BATTLE_PYRAMID_SQUARE05":{"header_address":4769024,"warp_table_address":4160749568},"MAP_BATTLE_PYRAMID_SQUARE06":{"header_address":4769052,"warp_table_address":4160749568},"MAP_BATTLE_PYRAMID_SQUARE07":{"header_address":4769080,"warp_table_address":4160749568},"MAP_BATTLE_PYRAMID_SQUARE08":{"header_address":4769108,"warp_table_address":4160749568},"MAP_BATTLE_PYRAMID_SQUARE09":{"header_address":4769136,"warp_table_address":4160749568},"MAP_BATTLE_PYRAMID_SQUARE10":{"header_address":4769164,"warp_table_address":4160749568},"MAP_BATTLE_PYRAMID_SQUARE11":{"header_address":4769192,"warp_table_address":4160749568},"MAP_BATTLE_PYRAMID_SQUARE12":{"header_address":4769220,"warp_table_address":4160749568},"MAP_BATTLE_PYRAMID_SQUARE13":{"header_address":4769248,"warp_table_address":4160749568},"MAP_BATTLE_PYRAMID_SQUARE14":{"header_address":4769276,"warp_table_address":4160749568},"MAP_BATTLE_PYRAMID_SQUARE15":{"header_address":4769304,"warp_table_address":4160749568},"MAP_BATTLE_PYRAMID_SQUARE16":{"header_address":4769332,"warp_table_address":4160749568},"MAP_BIRTH_ISLAND_EXTERIOR":{"header_address":4771012,"warp_table_address":5524876},"MAP_BIRTH_ISLAND_HARBOR":{"header_address":4771040,"warp_table_address":5524952},"MAP_CAVE_OF_ORIGIN_1F":{"header_address":4765720,"land_encounters":{"address":5609868,"slots":[41,41,41,322,322,322,41,41,42,42,42,42]},"warp_table_address":5493440},"MAP_CAVE_OF_ORIGIN_B1F":{"header_address":4765832,"warp_table_address":5493608},"MAP_CAVE_OF_ORIGIN_ENTRANCE":{"header_address":4765692,"land_encounters":{"address":5609812,"slots":[41,41,41,41,41,41,41,41,42,42,42,42]},"warp_table_address":5493404},"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1":{"header_address":4765748,"land_encounters":{"address":5609924,"slots":[41,41,41,322,322,322,41,41,42,42,42,42]},"warp_table_address":5493476},"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2":{"header_address":4765776,"land_encounters":{"address":5609980,"slots":[41,41,41,322,322,322,41,41,42,42,42,42]},"warp_table_address":5493512},"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3":{"header_address":4765804,"land_encounters":{"address":5610036,"slots":[41,41,41,322,322,322,41,41,42,42,42,42]},"warp_table_address":5493548},"MAP_CONTEST_HALL":{"header_address":4768464,"warp_table_address":4160749568},"MAP_CONTEST_HALL_BEAUTY":{"header_address":4768660,"warp_table_address":4160749568},"MAP_CONTEST_HALL_COOL":{"header_address":4768716,"warp_table_address":4160749568},"MAP_CONTEST_HALL_CUTE":{"header_address":4768772,"warp_table_address":4160749568},"MAP_CONTEST_HALL_SMART":{"header_address":4768744,"warp_table_address":4160749568},"MAP_CONTEST_HALL_TOUGH":{"header_address":4768688,"warp_table_address":4160749568},"MAP_DESERT_RUINS":{"header_address":4764824,"warp_table_address":5486828},"MAP_DESERT_UNDERPASS":{"header_address":4767400,"land_encounters":{"address":5613232,"slots":[132,370,132,371,132,370,371,132,370,132,371,132]},"warp_table_address":5500012},"MAP_DEWFORD_TOWN":{"fishing_encounters":{"address":5611588,"slots":[129,72,129,72,313,313,313,313,313,313]},"header_address":4758300,"warp_table_address":5435180,"water_encounters":{"address":5611560,"slots":[72,309,309,310,310]}},"MAP_DEWFORD_TOWN_GYM":{"header_address":4759952,"warp_table_address":5460340},"MAP_DEWFORD_TOWN_HALL":{"header_address":4759980,"warp_table_address":5460640},"MAP_DEWFORD_TOWN_HOUSE1":{"header_address":4759868,"warp_table_address":5459856},"MAP_DEWFORD_TOWN_HOUSE2":{"header_address":4760008,"warp_table_address":5460748},"MAP_DEWFORD_TOWN_POKEMON_CENTER_1F":{"header_address":4759896,"warp_table_address":5459964},"MAP_DEWFORD_TOWN_POKEMON_CENTER_2F":{"header_address":4759924,"warp_table_address":5460104},"MAP_EVER_GRANDE_CITY":{"fishing_encounters":{"address":5611892,"slots":[129,72,129,325,313,325,313,222,313,313]},"header_address":4758216,"warp_table_address":5434048,"water_encounters":{"address":5611864,"slots":[72,309,309,310,310]}},"MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM":{"header_address":4764012,"warp_table_address":5483720},"MAP_EVER_GRANDE_CITY_DRAKES_ROOM":{"header_address":4763984,"warp_table_address":5483612},"MAP_EVER_GRANDE_CITY_GLACIAS_ROOM":{"header_address":4763956,"warp_table_address":5483552},"MAP_EVER_GRANDE_CITY_HALL1":{"header_address":4764040,"warp_table_address":5483756},"MAP_EVER_GRANDE_CITY_HALL2":{"header_address":4764068,"warp_table_address":5483808},"MAP_EVER_GRANDE_CITY_HALL3":{"header_address":4764096,"warp_table_address":5483860},"MAP_EVER_GRANDE_CITY_HALL4":{"header_address":4764124,"warp_table_address":5483912},"MAP_EVER_GRANDE_CITY_HALL5":{"header_address":4764152,"warp_table_address":5483948},"MAP_EVER_GRANDE_CITY_HALL_OF_FAME":{"header_address":4764208,"warp_table_address":5484180},"MAP_EVER_GRANDE_CITY_PHOEBES_ROOM":{"header_address":4763928,"warp_table_address":5483492},"MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F":{"header_address":4764236,"warp_table_address":5484304},"MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F":{"header_address":4764264,"warp_table_address":5484444},"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F":{"header_address":4764180,"warp_table_address":5484096},"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F":{"header_address":4764292,"warp_table_address":5484584},"MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM":{"header_address":4763900,"warp_table_address":5483432},"MAP_FALLARBOR_TOWN":{"header_address":4758356,"warp_table_address":5435792},"MAP_FALLARBOR_TOWN_BATTLE_TENT_BATTLE_ROOM":{"header_address":4760316,"warp_table_address":4160749568},"MAP_FALLARBOR_TOWN_BATTLE_TENT_CORRIDOR":{"header_address":4760288,"warp_table_address":4160749568},"MAP_FALLARBOR_TOWN_BATTLE_TENT_LOBBY":{"header_address":4760260,"warp_table_address":5462376},"MAP_FALLARBOR_TOWN_COZMOS_HOUSE":{"header_address":4760400,"warp_table_address":5462888},"MAP_FALLARBOR_TOWN_MART":{"header_address":4760232,"warp_table_address":5462220},"MAP_FALLARBOR_TOWN_MOVE_RELEARNERS_HOUSE":{"header_address":4760428,"warp_table_address":5462948},"MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F":{"header_address":4760344,"warp_table_address":5462656},"MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F":{"header_address":4760372,"warp_table_address":5462796},"MAP_FARAWAY_ISLAND_ENTRANCE":{"header_address":4770956,"warp_table_address":5524672},"MAP_FARAWAY_ISLAND_INTERIOR":{"header_address":4770984,"warp_table_address":5524792},"MAP_FIERY_PATH":{"header_address":4765048,"land_encounters":{"address":5606456,"slots":[339,109,339,66,321,218,109,66,321,321,88,88]},"warp_table_address":5489344},"MAP_FORTREE_CITY":{"header_address":4758104,"warp_table_address":5431676},"MAP_FORTREE_CITY_DECORATION_SHOP":{"header_address":4762444,"warp_table_address":5473936},"MAP_FORTREE_CITY_GYM":{"header_address":4762220,"warp_table_address":5472984},"MAP_FORTREE_CITY_HOUSE1":{"header_address":4762192,"warp_table_address":5472756},"MAP_FORTREE_CITY_HOUSE2":{"header_address":4762332,"warp_table_address":5473504},"MAP_FORTREE_CITY_HOUSE3":{"header_address":4762360,"warp_table_address":5473588},"MAP_FORTREE_CITY_HOUSE4":{"header_address":4762388,"warp_table_address":5473696},"MAP_FORTREE_CITY_HOUSE5":{"header_address":4762416,"warp_table_address":5473804},"MAP_FORTREE_CITY_MART":{"header_address":4762304,"warp_table_address":5473420},"MAP_FORTREE_CITY_POKEMON_CENTER_1F":{"header_address":4762248,"warp_table_address":5473140},"MAP_FORTREE_CITY_POKEMON_CENTER_2F":{"header_address":4762276,"warp_table_address":5473280},"MAP_GRANITE_CAVE_1F":{"header_address":4764852,"land_encounters":{"address":5605988,"slots":[41,335,335,41,335,63,335,335,74,74,74,74]},"warp_table_address":5486956},"MAP_GRANITE_CAVE_B1F":{"header_address":4764880,"land_encounters":{"address":5606044,"slots":[41,382,382,382,41,63,335,335,322,322,322,322]},"warp_table_address":5487032},"MAP_GRANITE_CAVE_B2F":{"header_address":4764908,"land_encounters":{"address":5606372,"slots":[41,382,382,41,382,63,322,322,322,322,322,322]},"warp_table_address":5487324},"MAP_GRANITE_CAVE_STEVENS_ROOM":{"header_address":4764936,"land_encounters":{"address":5608188,"slots":[41,335,335,41,335,63,335,335,382,382,382,382]},"warp_table_address":5487432},"MAP_INSIDE_OF_TRUCK":{"header_address":4768800,"warp_table_address":5510720},"MAP_ISLAND_CAVE":{"header_address":4766532,"warp_table_address":5497356},"MAP_JAGGED_PASS":{"header_address":4765020,"land_encounters":{"address":5606644,"slots":[339,339,66,339,351,66,351,66,339,351,339,351]},"warp_table_address":5488908},"MAP_LAVARIDGE_TOWN":{"header_address":4758328,"warp_table_address":5435516},"MAP_LAVARIDGE_TOWN_GYM_1F":{"header_address":4760064,"warp_table_address":5461036},"MAP_LAVARIDGE_TOWN_GYM_B1F":{"header_address":4760092,"warp_table_address":5461384},"MAP_LAVARIDGE_TOWN_HERB_SHOP":{"header_address":4760036,"warp_table_address":5460856},"MAP_LAVARIDGE_TOWN_HOUSE":{"header_address":4760120,"warp_table_address":5461668},"MAP_LAVARIDGE_TOWN_MART":{"header_address":4760148,"warp_table_address":5461776},"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F":{"header_address":4760176,"warp_table_address":5461908},"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F":{"header_address":4760204,"warp_table_address":5462056},"MAP_LILYCOVE_CITY":{"fishing_encounters":{"address":5611512,"slots":[129,72,129,72,313,313,313,120,313,313]},"header_address":4758132,"warp_table_address":5432368,"water_encounters":{"address":5611484,"slots":[72,309,309,310,310]}},"MAP_LILYCOVE_CITY_CONTEST_HALL":{"header_address":4762612,"warp_table_address":5476560},"MAP_LILYCOVE_CITY_CONTEST_LOBBY":{"header_address":4762584,"warp_table_address":5475596},"MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F":{"header_address":4762472,"warp_table_address":5473996},"MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_2F":{"header_address":4762500,"warp_table_address":5474224},"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F":{"header_address":4762920,"warp_table_address":5478044},"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F":{"header_address":4762948,"warp_table_address":5478228},"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F":{"header_address":4762976,"warp_table_address":5478392},"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F":{"header_address":4763004,"warp_table_address":5478556},"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F":{"header_address":4763032,"warp_table_address":5478768},"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR":{"header_address":4763088,"warp_table_address":5478984},"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP":{"header_address":4763060,"warp_table_address":5478908},"MAP_LILYCOVE_CITY_HARBOR":{"header_address":4762752,"warp_table_address":5477396},"MAP_LILYCOVE_CITY_HOUSE1":{"header_address":4762808,"warp_table_address":5477540},"MAP_LILYCOVE_CITY_HOUSE2":{"header_address":4762836,"warp_table_address":5477600},"MAP_LILYCOVE_CITY_HOUSE3":{"header_address":4762864,"warp_table_address":5477780},"MAP_LILYCOVE_CITY_HOUSE4":{"header_address":4762892,"warp_table_address":5477864},"MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F":{"header_address":4762528,"warp_table_address":5474492},"MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_2F":{"header_address":4762556,"warp_table_address":5474824},"MAP_LILYCOVE_CITY_MOVE_DELETERS_HOUSE":{"header_address":4762780,"warp_table_address":5477456},"MAP_LILYCOVE_CITY_POKEMON_CENTER_1F":{"header_address":4762640,"warp_table_address":5476804},"MAP_LILYCOVE_CITY_POKEMON_CENTER_2F":{"header_address":4762668,"warp_table_address":5476944},"MAP_LILYCOVE_CITY_POKEMON_TRAINER_FAN_CLUB":{"header_address":4762724,"warp_table_address":5477240},"MAP_LILYCOVE_CITY_UNUSED_MART":{"header_address":4762696,"warp_table_address":5476988},"MAP_LITTLEROOT_TOWN":{"header_address":4758244,"warp_table_address":5434528},"MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F":{"header_address":4759588,"warp_table_address":5457588},"MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F":{"header_address":4759616,"warp_table_address":5458080},"MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F":{"header_address":4759644,"warp_table_address":5458324},"MAP_LITTLEROOT_TOWN_MAYS_HOUSE_2F":{"header_address":4759672,"warp_table_address":5458816},"MAP_LITTLEROOT_TOWN_PROFESSOR_BIRCHS_LAB":{"header_address":4759700,"warp_table_address":5459036},"MAP_MAGMA_HIDEOUT_1F":{"header_address":4767064,"land_encounters":{"address":5612560,"slots":[74,321,74,321,74,74,74,75,75,75,75,75]},"warp_table_address":5498844},"MAP_MAGMA_HIDEOUT_2F_1R":{"header_address":4767092,"land_encounters":{"address":5612616,"slots":[74,321,74,321,74,74,74,75,75,75,75,75]},"warp_table_address":5498992},"MAP_MAGMA_HIDEOUT_2F_2R":{"header_address":4767120,"land_encounters":{"address":5612672,"slots":[74,321,74,321,74,74,74,75,75,75,75,75]},"warp_table_address":5499180},"MAP_MAGMA_HIDEOUT_2F_3R":{"header_address":4767260,"land_encounters":{"address":5612952,"slots":[74,321,74,321,74,74,74,75,75,75,75,75]},"warp_table_address":5499696},"MAP_MAGMA_HIDEOUT_3F_1R":{"header_address":4767148,"land_encounters":{"address":5612728,"slots":[74,321,74,321,74,74,74,75,75,75,75,75]},"warp_table_address":5499288},"MAP_MAGMA_HIDEOUT_3F_2R":{"header_address":4767176,"land_encounters":{"address":5612784,"slots":[74,321,74,321,74,74,74,75,75,75,75,75]},"warp_table_address":5499380},"MAP_MAGMA_HIDEOUT_3F_3R":{"header_address":4767232,"land_encounters":{"address":5612896,"slots":[74,321,74,321,74,74,74,75,75,75,75,75]},"warp_table_address":5499660},"MAP_MAGMA_HIDEOUT_4F":{"header_address":4767204,"land_encounters":{"address":5612840,"slots":[74,321,74,321,74,74,74,75,75,75,75,75]},"warp_table_address":5499600},"MAP_MARINE_CAVE_END":{"header_address":4767540,"warp_table_address":5500288},"MAP_MARINE_CAVE_ENTRANCE":{"header_address":4767512,"warp_table_address":5500236},"MAP_MAUVILLE_CITY":{"header_address":4758048,"warp_table_address":5430380},"MAP_MAUVILLE_CITY_BIKE_SHOP":{"header_address":4761520,"warp_table_address":5469232},"MAP_MAUVILLE_CITY_GAME_CORNER":{"header_address":4761576,"warp_table_address":5469640},"MAP_MAUVILLE_CITY_GYM":{"header_address":4761492,"warp_table_address":5469060},"MAP_MAUVILLE_CITY_HOUSE1":{"header_address":4761548,"warp_table_address":5469316},"MAP_MAUVILLE_CITY_HOUSE2":{"header_address":4761604,"warp_table_address":5469988},"MAP_MAUVILLE_CITY_MART":{"header_address":4761688,"warp_table_address":5470424},"MAP_MAUVILLE_CITY_POKEMON_CENTER_1F":{"header_address":4761632,"warp_table_address":5470144},"MAP_MAUVILLE_CITY_POKEMON_CENTER_2F":{"header_address":4761660,"warp_table_address":5470308},"MAP_METEOR_FALLS_1F_1R":{"fishing_encounters":{"address":5610796,"slots":[129,118,129,118,323,323,323,323,323,323]},"header_address":4764656,"land_encounters":{"address":5610712,"slots":[41,41,41,41,41,349,349,349,41,41,41,41]},"warp_table_address":5486052,"water_encounters":{"address":5610768,"slots":[41,41,349,349,349]}},"MAP_METEOR_FALLS_1F_2R":{"fishing_encounters":{"address":5610928,"slots":[129,118,129,118,323,323,323,324,324,324]},"header_address":4764684,"land_encounters":{"address":5610844,"slots":[42,42,42,349,349,349,42,349,42,42,42,42]},"warp_table_address":5486220,"water_encounters":{"address":5610900,"slots":[42,42,349,349,349]}},"MAP_METEOR_FALLS_B1F_1R":{"fishing_encounters":{"address":5611060,"slots":[129,118,129,118,323,323,323,324,324,324]},"header_address":4764712,"land_encounters":{"address":5610976,"slots":[42,42,42,349,349,349,42,349,42,42,42,42]},"warp_table_address":5486284,"water_encounters":{"address":5611032,"slots":[42,42,349,349,349]}},"MAP_METEOR_FALLS_B1F_2R":{"fishing_encounters":{"address":5606596,"slots":[129,118,129,118,323,323,323,324,324,324]},"header_address":4764740,"land_encounters":{"address":5606512,"slots":[42,42,395,349,395,349,395,349,42,42,42,42]},"warp_table_address":5486376,"water_encounters":{"address":5606568,"slots":[42,42,349,349,349]}},"MAP_METEOR_FALLS_STEVENS_CAVE":{"header_address":4767652,"land_encounters":{"address":5613904,"slots":[42,42,42,349,349,349,42,349,42,42,42,42]},"warp_table_address":5500488},"MAP_MIRAGE_TOWER_1F":{"header_address":4767288,"land_encounters":{"address":5613008,"slots":[27,332,27,332,27,332,27,332,27,332,27,332]},"warp_table_address":5499732},"MAP_MIRAGE_TOWER_2F":{"header_address":4767316,"land_encounters":{"address":5613064,"slots":[27,332,27,332,27,332,27,332,27,332,27,332]},"warp_table_address":5499768},"MAP_MIRAGE_TOWER_3F":{"header_address":4767344,"land_encounters":{"address":5613120,"slots":[27,332,27,332,27,332,27,332,27,332,27,332]},"warp_table_address":5499852},"MAP_MIRAGE_TOWER_4F":{"header_address":4767372,"land_encounters":{"address":5613176,"slots":[27,332,27,332,27,332,27,332,27,332,27,332]},"warp_table_address":5499960},"MAP_MOSSDEEP_CITY":{"fishing_encounters":{"address":5611740,"slots":[129,72,129,72,313,331,313,313,313,313]},"header_address":4758160,"warp_table_address":5433064,"water_encounters":{"address":5611712,"slots":[72,309,309,310,310]}},"MAP_MOSSDEEP_CITY_GAME_CORNER_1F":{"header_address":4763424,"warp_table_address":5481712},"MAP_MOSSDEEP_CITY_GAME_CORNER_B1F":{"header_address":4763452,"warp_table_address":5481816},"MAP_MOSSDEEP_CITY_GYM":{"header_address":4763116,"warp_table_address":5479884},"MAP_MOSSDEEP_CITY_HOUSE1":{"header_address":4763144,"warp_table_address":5480232},"MAP_MOSSDEEP_CITY_HOUSE2":{"header_address":4763172,"warp_table_address":5480340},"MAP_MOSSDEEP_CITY_HOUSE3":{"header_address":4763284,"warp_table_address":5480812},"MAP_MOSSDEEP_CITY_HOUSE4":{"header_address":4763340,"warp_table_address":5481076},"MAP_MOSSDEEP_CITY_MART":{"header_address":4763256,"warp_table_address":5480752},"MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F":{"header_address":4763200,"warp_table_address":5480448},"MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F":{"header_address":4763228,"warp_table_address":5480612},"MAP_MOSSDEEP_CITY_SPACE_CENTER_1F":{"header_address":4763368,"warp_table_address":5481376},"MAP_MOSSDEEP_CITY_SPACE_CENTER_2F":{"header_address":4763396,"warp_table_address":5481636},"MAP_MOSSDEEP_CITY_STEVENS_HOUSE":{"header_address":4763312,"warp_table_address":5480920},"MAP_MT_CHIMNEY":{"header_address":4764992,"warp_table_address":5488664},"MAP_MT_CHIMNEY_CABLE_CAR_STATION":{"header_address":4764460,"warp_table_address":5485144},"MAP_MT_PYRE_1F":{"header_address":4765076,"land_encounters":{"address":5606100,"slots":[377,377,377,377,377,377,377,377,377,377,377,377]},"warp_table_address":5489452},"MAP_MT_PYRE_2F":{"header_address":4765104,"land_encounters":{"address":5607796,"slots":[377,377,377,377,377,377,377,377,377,377,377,377]},"warp_table_address":5489712},"MAP_MT_PYRE_3F":{"header_address":4765132,"land_encounters":{"address":5607852,"slots":[377,377,377,377,377,377,377,377,377,377,377,377]},"warp_table_address":5489868},"MAP_MT_PYRE_4F":{"header_address":4765160,"land_encounters":{"address":5607908,"slots":[377,377,377,377,377,377,377,377,361,361,361,361]},"warp_table_address":5489984},"MAP_MT_PYRE_5F":{"header_address":4765188,"land_encounters":{"address":5607964,"slots":[377,377,377,377,377,377,377,377,361,361,361,361]},"warp_table_address":5490100},"MAP_MT_PYRE_6F":{"header_address":4765216,"land_encounters":{"address":5608020,"slots":[377,377,377,377,377,377,377,377,361,361,361,361]},"warp_table_address":5490232},"MAP_MT_PYRE_EXTERIOR":{"header_address":4765244,"land_encounters":{"address":5608076,"slots":[377,377,377,377,37,37,37,37,309,309,309,309]},"warp_table_address":5490316},"MAP_MT_PYRE_SUMMIT":{"header_address":4765272,"land_encounters":{"address":5608132,"slots":[377,377,377,377,377,377,377,361,361,361,411,411]},"warp_table_address":5490656},"MAP_NAVEL_ROCK_B1F":{"header_address":4771320,"warp_table_address":5525524},"MAP_NAVEL_ROCK_BOTTOM":{"header_address":4771824,"warp_table_address":5526248},"MAP_NAVEL_ROCK_DOWN01":{"header_address":4771516,"warp_table_address":5525828},"MAP_NAVEL_ROCK_DOWN02":{"header_address":4771544,"warp_table_address":5525864},"MAP_NAVEL_ROCK_DOWN03":{"header_address":4771572,"warp_table_address":5525900},"MAP_NAVEL_ROCK_DOWN04":{"header_address":4771600,"warp_table_address":5525936},"MAP_NAVEL_ROCK_DOWN05":{"header_address":4771628,"warp_table_address":5525972},"MAP_NAVEL_ROCK_DOWN06":{"header_address":4771656,"warp_table_address":5526008},"MAP_NAVEL_ROCK_DOWN07":{"header_address":4771684,"warp_table_address":5526044},"MAP_NAVEL_ROCK_DOWN08":{"header_address":4771712,"warp_table_address":5526080},"MAP_NAVEL_ROCK_DOWN09":{"header_address":4771740,"warp_table_address":5526116},"MAP_NAVEL_ROCK_DOWN10":{"header_address":4771768,"warp_table_address":5526152},"MAP_NAVEL_ROCK_DOWN11":{"header_address":4771796,"warp_table_address":5526188},"MAP_NAVEL_ROCK_ENTRANCE":{"header_address":4771292,"warp_table_address":5525488},"MAP_NAVEL_ROCK_EXTERIOR":{"header_address":4771236,"warp_table_address":5525376},"MAP_NAVEL_ROCK_FORK":{"header_address":4771348,"warp_table_address":5525560},"MAP_NAVEL_ROCK_HARBOR":{"header_address":4771264,"warp_table_address":5525460},"MAP_NAVEL_ROCK_TOP":{"header_address":4771488,"warp_table_address":5525772},"MAP_NAVEL_ROCK_UP1":{"header_address":4771376,"warp_table_address":5525604},"MAP_NAVEL_ROCK_UP2":{"header_address":4771404,"warp_table_address":5525640},"MAP_NAVEL_ROCK_UP3":{"header_address":4771432,"warp_table_address":5525676},"MAP_NAVEL_ROCK_UP4":{"header_address":4771460,"warp_table_address":5525712},"MAP_NEW_MAUVILLE_ENTRANCE":{"header_address":4766112,"land_encounters":{"address":5610092,"slots":[100,81,100,81,100,81,100,81,100,81,100,81]},"warp_table_address":5495284},"MAP_NEW_MAUVILLE_INSIDE":{"header_address":4766140,"land_encounters":{"address":5607136,"slots":[100,81,100,81,100,81,100,81,100,81,101,82]},"warp_table_address":5495528},"MAP_OLDALE_TOWN":{"header_address":4758272,"warp_table_address":5434860},"MAP_OLDALE_TOWN_HOUSE1":{"header_address":4759728,"warp_table_address":5459276},"MAP_OLDALE_TOWN_HOUSE2":{"header_address":4759756,"warp_table_address":5459360},"MAP_OLDALE_TOWN_MART":{"header_address":4759840,"warp_table_address":5459748},"MAP_OLDALE_TOWN_POKEMON_CENTER_1F":{"header_address":4759784,"warp_table_address":5459492},"MAP_OLDALE_TOWN_POKEMON_CENTER_2F":{"header_address":4759812,"warp_table_address":5459632},"MAP_PACIFIDLOG_TOWN":{"fishing_encounters":{"address":5611816,"slots":[129,72,129,72,313,331,313,313,313,313]},"header_address":4758412,"warp_table_address":5436288,"water_encounters":{"address":5611788,"slots":[72,309,309,310,310]}},"MAP_PACIFIDLOG_TOWN_HOUSE1":{"header_address":4760764,"warp_table_address":5464400},"MAP_PACIFIDLOG_TOWN_HOUSE2":{"header_address":4760792,"warp_table_address":5464508},"MAP_PACIFIDLOG_TOWN_HOUSE3":{"header_address":4760820,"warp_table_address":5464592},"MAP_PACIFIDLOG_TOWN_HOUSE4":{"header_address":4760848,"warp_table_address":5464700},"MAP_PACIFIDLOG_TOWN_HOUSE5":{"header_address":4760876,"warp_table_address":5464784},"MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F":{"header_address":4760708,"warp_table_address":5464168},"MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F":{"header_address":4760736,"warp_table_address":5464308},"MAP_PETALBURG_CITY":{"fishing_encounters":{"address":5611968,"slots":[129,118,129,118,326,326,326,326,326,326]},"header_address":4757992,"warp_table_address":5428704,"water_encounters":{"address":5611940,"slots":[183,183,183,183,183]}},"MAP_PETALBURG_CITY_GYM":{"header_address":4760932,"warp_table_address":5465168},"MAP_PETALBURG_CITY_HOUSE1":{"header_address":4760960,"warp_table_address":5465708},"MAP_PETALBURG_CITY_HOUSE2":{"header_address":4760988,"warp_table_address":5465792},"MAP_PETALBURG_CITY_MART":{"header_address":4761072,"warp_table_address":5466228},"MAP_PETALBURG_CITY_POKEMON_CENTER_1F":{"header_address":4761016,"warp_table_address":5465948},"MAP_PETALBURG_CITY_POKEMON_CENTER_2F":{"header_address":4761044,"warp_table_address":5466088},"MAP_PETALBURG_CITY_WALLYS_HOUSE":{"header_address":4760904,"warp_table_address":5464868},"MAP_PETALBURG_WOODS":{"header_address":4764964,"land_encounters":{"address":5605876,"slots":[286,290,306,286,291,293,290,306,304,364,304,364]},"warp_table_address":5487772},"MAP_RECORD_CORNER":{"header_address":4768408,"warp_table_address":5510036},"MAP_ROUTE101":{"header_address":4758440,"land_encounters":{"address":5604388,"slots":[290,286,290,290,286,286,290,286,288,288,288,288]},"warp_table_address":4160749568},"MAP_ROUTE102":{"fishing_encounters":{"address":5604528,"slots":[129,118,129,118,326,326,326,326,326,326]},"header_address":4758468,"land_encounters":{"address":5604444,"slots":[286,290,286,290,295,295,288,288,288,392,288,298]},"warp_table_address":4160749568,"water_encounters":{"address":5604500,"slots":[183,183,183,183,118]}},"MAP_ROUTE103":{"fishing_encounters":{"address":5604660,"slots":[129,72,129,72,313,331,313,313,313,313]},"header_address":4758496,"land_encounters":{"address":5604576,"slots":[286,286,286,286,309,288,288,288,309,309,309,309]},"warp_table_address":5437452,"water_encounters":{"address":5604632,"slots":[72,309,309,310,310]}},"MAP_ROUTE104":{"fishing_encounters":{"address":5604792,"slots":[129,129,129,129,129,129,129,129,129,129]},"header_address":4758524,"land_encounters":{"address":5604708,"slots":[286,290,286,183,183,286,304,304,309,309,309,309]},"warp_table_address":5438308,"water_encounters":{"address":5604764,"slots":[309,309,309,310,310]}},"MAP_ROUTE104_MR_BRINEYS_HOUSE":{"header_address":4764320,"warp_table_address":5484676},"MAP_ROUTE104_PRETTY_PETAL_FLOWER_SHOP":{"header_address":4764348,"warp_table_address":5484784},"MAP_ROUTE104_PROTOTYPE":{"header_address":4771880,"warp_table_address":4160749568},"MAP_ROUTE104_PROTOTYPE_PRETTY_PETAL_FLOWER_SHOP":{"header_address":4771908,"warp_table_address":4160749568},"MAP_ROUTE105":{"fishing_encounters":{"address":5604868,"slots":[129,72,129,72,313,313,313,313,313,313]},"header_address":4758552,"warp_table_address":5438720,"water_encounters":{"address":5604840,"slots":[72,309,309,310,310]}},"MAP_ROUTE106":{"fishing_encounters":{"address":5606728,"slots":[129,72,129,72,313,313,313,313,313,313]},"header_address":4758580,"warp_table_address":5438892,"water_encounters":{"address":5606700,"slots":[72,309,309,310,310]}},"MAP_ROUTE107":{"fishing_encounters":{"address":5606804,"slots":[129,72,129,72,313,313,313,313,313,313]},"header_address":4758608,"warp_table_address":4160749568,"water_encounters":{"address":5606776,"slots":[72,309,309,310,310]}},"MAP_ROUTE108":{"fishing_encounters":{"address":5606880,"slots":[129,72,129,72,313,313,313,313,313,313]},"header_address":4758636,"warp_table_address":5439324,"water_encounters":{"address":5606852,"slots":[72,309,309,310,310]}},"MAP_ROUTE109":{"fishing_encounters":{"address":5606956,"slots":[129,72,129,72,313,313,313,313,313,313]},"header_address":4758664,"warp_table_address":5439940,"water_encounters":{"address":5606928,"slots":[72,309,309,310,310]}},"MAP_ROUTE109_SEASHORE_HOUSE":{"header_address":4771936,"warp_table_address":5526472},"MAP_ROUTE110":{"fishing_encounters":{"address":5605000,"slots":[129,72,129,72,313,313,313,313,313,313]},"header_address":4758692,"land_encounters":{"address":5604916,"slots":[286,337,367,337,354,43,354,367,309,309,353,353]},"warp_table_address":5440928,"water_encounters":{"address":5604972,"slots":[72,309,309,310,310]}},"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE":{"header_address":4772272,"warp_table_address":5529400},"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE":{"header_address":4772300,"warp_table_address":5529508},"MAP_ROUTE110_TRICK_HOUSE_CORRIDOR":{"header_address":4772020,"warp_table_address":5526740},"MAP_ROUTE110_TRICK_HOUSE_END":{"header_address":4771992,"warp_table_address":5526676},"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE":{"header_address":4771964,"warp_table_address":5526532},"MAP_ROUTE110_TRICK_HOUSE_PUZZLE1":{"header_address":4772048,"warp_table_address":5527152},"MAP_ROUTE110_TRICK_HOUSE_PUZZLE2":{"header_address":4772076,"warp_table_address":5527328},"MAP_ROUTE110_TRICK_HOUSE_PUZZLE3":{"header_address":4772104,"warp_table_address":5527616},"MAP_ROUTE110_TRICK_HOUSE_PUZZLE4":{"header_address":4772132,"warp_table_address":5528072},"MAP_ROUTE110_TRICK_HOUSE_PUZZLE5":{"header_address":4772160,"warp_table_address":5528248},"MAP_ROUTE110_TRICK_HOUSE_PUZZLE6":{"header_address":4772188,"warp_table_address":5528752},"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7":{"header_address":4772216,"warp_table_address":5529024},"MAP_ROUTE110_TRICK_HOUSE_PUZZLE8":{"header_address":4772244,"warp_table_address":5529320},"MAP_ROUTE111":{"fishing_encounters":{"address":5605160,"slots":[129,118,129,118,323,323,323,323,323,323]},"header_address":4758720,"land_encounters":{"address":5605048,"slots":[27,332,27,332,318,318,27,332,318,344,344,344]},"warp_table_address":5442448,"water_encounters":{"address":5605104,"slots":[183,183,183,183,118]}},"MAP_ROUTE111_OLD_LADYS_REST_STOP":{"header_address":4764404,"warp_table_address":5484976},"MAP_ROUTE111_WINSTRATE_FAMILYS_HOUSE":{"header_address":4764376,"warp_table_address":5484916},"MAP_ROUTE112":{"header_address":4758748,"land_encounters":{"address":5605208,"slots":[339,339,183,339,339,183,339,183,339,339,339,339]},"warp_table_address":5443604},"MAP_ROUTE112_CABLE_CAR_STATION":{"header_address":4764432,"warp_table_address":5485060},"MAP_ROUTE113":{"header_address":4758776,"land_encounters":{"address":5605264,"slots":[308,308,218,308,308,218,308,218,308,227,308,227]},"warp_table_address":5444092},"MAP_ROUTE113_GLASS_WORKSHOP":{"header_address":4772328,"warp_table_address":5529640},"MAP_ROUTE114":{"fishing_encounters":{"address":5605432,"slots":[129,118,129,118,323,323,323,323,323,323]},"header_address":4758804,"land_encounters":{"address":5605320,"slots":[358,295,358,358,295,296,296,296,379,379,379,299]},"warp_table_address":5445184,"water_encounters":{"address":5605376,"slots":[183,183,183,183,118]}},"MAP_ROUTE114_FOSSIL_MANIACS_HOUSE":{"header_address":4764488,"warp_table_address":5485204},"MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL":{"header_address":4764516,"warp_table_address":5485320},"MAP_ROUTE114_LANETTES_HOUSE":{"header_address":4764544,"warp_table_address":5485420},"MAP_ROUTE115":{"fishing_encounters":{"address":5607088,"slots":[129,72,129,72,313,313,313,313,313,313]},"header_address":4758832,"land_encounters":{"address":5607004,"slots":[358,304,358,304,304,305,39,39,309,309,309,309]},"warp_table_address":5445988,"water_encounters":{"address":5607060,"slots":[72,309,309,310,310]}},"MAP_ROUTE116":{"header_address":4758860,"land_encounters":{"address":5605480,"slots":[286,370,301,63,301,304,304,304,286,286,315,315]},"warp_table_address":5446872},"MAP_ROUTE116_TUNNELERS_REST_HOUSE":{"header_address":4764572,"warp_table_address":5485564},"MAP_ROUTE117":{"fishing_encounters":{"address":5605620,"slots":[129,118,129,118,326,326,326,326,326,326]},"header_address":4758888,"land_encounters":{"address":5605536,"slots":[286,43,286,43,183,43,387,387,387,387,386,298]},"warp_table_address":5447656,"water_encounters":{"address":5605592,"slots":[183,183,183,183,118]}},"MAP_ROUTE117_POKEMON_DAY_CARE":{"header_address":4764600,"warp_table_address":5485624},"MAP_ROUTE118":{"fishing_encounters":{"address":5605752,"slots":[129,72,129,72,330,331,330,330,330,330]},"header_address":4758916,"land_encounters":{"address":5605668,"slots":[288,337,288,337,289,338,309,309,309,309,309,317]},"warp_table_address":5448236,"water_encounters":{"address":5605724,"slots":[72,309,309,310,310]}},"MAP_ROUTE119":{"fishing_encounters":{"address":5607276,"slots":[129,72,129,72,330,330,330,330,330,330]},"header_address":4758944,"land_encounters":{"address":5607192,"slots":[288,289,288,43,289,43,43,43,369,369,369,317]},"warp_table_address":5449460,"water_encounters":{"address":5607248,"slots":[72,309,309,310,310]}},"MAP_ROUTE119_HOUSE":{"header_address":4772440,"warp_table_address":5530360},"MAP_ROUTE119_WEATHER_INSTITUTE_1F":{"header_address":4772384,"warp_table_address":5529880},"MAP_ROUTE119_WEATHER_INSTITUTE_2F":{"header_address":4772412,"warp_table_address":5530164},"MAP_ROUTE120":{"fishing_encounters":{"address":5607408,"slots":[129,118,129,118,323,323,323,323,323,323]},"header_address":4758972,"land_encounters":{"address":5607324,"slots":[286,287,287,43,183,43,43,183,376,376,317,298]},"warp_table_address":5451160,"water_encounters":{"address":5607380,"slots":[183,183,183,183,118]}},"MAP_ROUTE121":{"fishing_encounters":{"address":5607540,"slots":[129,72,129,72,313,313,313,313,313,313]},"header_address":4759000,"land_encounters":{"address":5607456,"slots":[286,377,287,377,287,43,43,44,309,309,309,317]},"warp_table_address":5452364,"water_encounters":{"address":5607512,"slots":[72,309,309,310,310]}},"MAP_ROUTE121_SAFARI_ZONE_ENTRANCE":{"header_address":4764628,"warp_table_address":5485732},"MAP_ROUTE122":{"fishing_encounters":{"address":5607616,"slots":[129,72,129,72,313,331,313,313,313,313]},"header_address":4759028,"warp_table_address":5452576,"water_encounters":{"address":5607588,"slots":[72,309,309,310,310]}},"MAP_ROUTE123":{"fishing_encounters":{"address":5607748,"slots":[129,72,129,72,313,313,313,313,313,313]},"header_address":4759056,"land_encounters":{"address":5607664,"slots":[286,377,287,377,287,43,43,44,309,309,309,317]},"warp_table_address":5453636,"water_encounters":{"address":5607720,"slots":[72,309,309,310,310]}},"MAP_ROUTE123_BERRY_MASTERS_HOUSE":{"header_address":4772356,"warp_table_address":5529724},"MAP_ROUTE124":{"fishing_encounters":{"address":5605828,"slots":[129,72,129,72,313,331,313,313,313,313]},"header_address":4759084,"warp_table_address":5454436,"water_encounters":{"address":5605800,"slots":[72,309,309,310,310]}},"MAP_ROUTE124_DIVING_TREASURE_HUNTERS_HOUSE":{"header_address":4772468,"warp_table_address":5530420},"MAP_ROUTE125":{"fishing_encounters":{"address":5608272,"slots":[129,72,129,72,313,331,313,313,313,313]},"header_address":4759112,"warp_table_address":5454716,"water_encounters":{"address":5608244,"slots":[72,309,309,310,310]}},"MAP_ROUTE126":{"fishing_encounters":{"address":5608348,"slots":[129,72,129,72,313,331,313,313,313,313]},"header_address":4759140,"warp_table_address":4160749568,"water_encounters":{"address":5608320,"slots":[72,309,309,310,310]}},"MAP_ROUTE127":{"fishing_encounters":{"address":5608424,"slots":[129,72,129,72,313,331,313,313,313,313]},"header_address":4759168,"warp_table_address":4160749568,"water_encounters":{"address":5608396,"slots":[72,309,309,310,310]}},"MAP_ROUTE128":{"fishing_encounters":{"address":5608500,"slots":[129,72,129,325,313,325,313,222,313,313]},"header_address":4759196,"warp_table_address":4160749568,"water_encounters":{"address":5608472,"slots":[72,309,309,310,310]}},"MAP_ROUTE129":{"fishing_encounters":{"address":5608576,"slots":[129,72,129,72,313,331,313,313,313,313]},"header_address":4759224,"warp_table_address":4160749568,"water_encounters":{"address":5608548,"slots":[72,309,309,310,314]}},"MAP_ROUTE130":{"fishing_encounters":{"address":5608708,"slots":[129,72,129,72,313,331,313,313,313,313]},"header_address":4759252,"land_encounters":{"address":5608624,"slots":[360,360,360,360,360,360,360,360,360,360,360,360]},"warp_table_address":4160749568,"water_encounters":{"address":5608680,"slots":[72,309,309,310,310]}},"MAP_ROUTE131":{"fishing_encounters":{"address":5608784,"slots":[129,72,129,72,313,331,313,313,313,313]},"header_address":4759280,"warp_table_address":5456116,"water_encounters":{"address":5608756,"slots":[72,309,309,310,310]}},"MAP_ROUTE132":{"fishing_encounters":{"address":5608860,"slots":[129,72,129,72,313,331,313,116,313,313]},"header_address":4759308,"warp_table_address":4160749568,"water_encounters":{"address":5608832,"slots":[72,309,309,310,310]}},"MAP_ROUTE133":{"fishing_encounters":{"address":5608936,"slots":[129,72,129,72,313,331,313,116,313,313]},"header_address":4759336,"warp_table_address":4160749568,"water_encounters":{"address":5608908,"slots":[72,309,309,310,310]}},"MAP_ROUTE134":{"fishing_encounters":{"address":5609012,"slots":[129,72,129,72,313,331,313,116,313,313]},"header_address":4759364,"warp_table_address":4160749568,"water_encounters":{"address":5608984,"slots":[72,309,309,310,310]}},"MAP_RUSTBORO_CITY":{"header_address":4758076,"warp_table_address":5430936},"MAP_RUSTBORO_CITY_CUTTERS_HOUSE":{"header_address":4762024,"warp_table_address":5472204},"MAP_RUSTBORO_CITY_DEVON_CORP_1F":{"header_address":4761716,"warp_table_address":5470532},"MAP_RUSTBORO_CITY_DEVON_CORP_2F":{"header_address":4761744,"warp_table_address":5470744},"MAP_RUSTBORO_CITY_DEVON_CORP_3F":{"header_address":4761772,"warp_table_address":5470852},"MAP_RUSTBORO_CITY_FLAT1_1F":{"header_address":4761940,"warp_table_address":5471808},"MAP_RUSTBORO_CITY_FLAT1_2F":{"header_address":4761968,"warp_table_address":5472044},"MAP_RUSTBORO_CITY_FLAT2_1F":{"header_address":4762080,"warp_table_address":5472372},"MAP_RUSTBORO_CITY_FLAT2_2F":{"header_address":4762108,"warp_table_address":5472464},"MAP_RUSTBORO_CITY_FLAT2_3F":{"header_address":4762136,"warp_table_address":5472548},"MAP_RUSTBORO_CITY_GYM":{"header_address":4761800,"warp_table_address":5471024},"MAP_RUSTBORO_CITY_HOUSE1":{"header_address":4761996,"warp_table_address":5472120},"MAP_RUSTBORO_CITY_HOUSE2":{"header_address":4762052,"warp_table_address":5472288},"MAP_RUSTBORO_CITY_HOUSE3":{"header_address":4762164,"warp_table_address":5472648},"MAP_RUSTBORO_CITY_MART":{"header_address":4761912,"warp_table_address":5471724},"MAP_RUSTBORO_CITY_POKEMON_CENTER_1F":{"header_address":4761856,"warp_table_address":5471444},"MAP_RUSTBORO_CITY_POKEMON_CENTER_2F":{"header_address":4761884,"warp_table_address":5471584},"MAP_RUSTBORO_CITY_POKEMON_SCHOOL":{"header_address":4761828,"warp_table_address":5471252},"MAP_RUSTURF_TUNNEL":{"header_address":4764768,"land_encounters":{"address":5605932,"slots":[370,370,370,370,370,370,370,370,370,370,370,370]},"warp_table_address":5486644},"MAP_SAFARI_ZONE_NORTH":{"header_address":4769416,"land_encounters":{"address":5610280,"slots":[231,43,231,43,177,44,44,177,178,214,178,214]},"warp_table_address":4160749568},"MAP_SAFARI_ZONE_NORTHEAST":{"header_address":4769724,"land_encounters":{"address":5612476,"slots":[190,216,190,216,191,165,163,204,228,241,228,241]},"warp_table_address":4160749568},"MAP_SAFARI_ZONE_NORTHWEST":{"fishing_encounters":{"address":5610448,"slots":[129,118,129,118,118,118,118,119,119,119]},"header_address":4769388,"land_encounters":{"address":5610364,"slots":[111,43,111,43,84,44,44,84,85,127,85,127]},"warp_table_address":4160749568,"water_encounters":{"address":5610420,"slots":[54,54,54,55,55]}},"MAP_SAFARI_ZONE_REST_HOUSE":{"header_address":4769696,"warp_table_address":5516996},"MAP_SAFARI_ZONE_SOUTH":{"header_address":4769472,"land_encounters":{"address":5606212,"slots":[43,43,203,203,177,84,44,202,25,202,25,202]},"warp_table_address":5515444},"MAP_SAFARI_ZONE_SOUTHEAST":{"fishing_encounters":{"address":5612428,"slots":[129,118,129,118,223,118,223,223,223,224]},"header_address":4769752,"land_encounters":{"address":5612344,"slots":[191,179,191,179,190,167,163,209,234,207,234,207]},"warp_table_address":4160749568,"water_encounters":{"address":5612400,"slots":[194,183,183,183,195]}},"MAP_SAFARI_ZONE_SOUTHWEST":{"fishing_encounters":{"address":5610232,"slots":[129,118,129,118,118,118,118,119,119,119]},"header_address":4769444,"land_encounters":{"address":5610148,"slots":[43,43,203,203,177,84,44,202,25,202,25,202]},"warp_table_address":5515260,"water_encounters":{"address":5610204,"slots":[54,54,54,54,54]}},"MAP_SCORCHED_SLAB":{"header_address":4766700,"warp_table_address":5498144},"MAP_SEAFLOOR_CAVERN_ENTRANCE":{"fishing_encounters":{"address":5609764,"slots":[129,72,129,72,313,313,313,313,313,313]},"header_address":4765412,"warp_table_address":5491796,"water_encounters":{"address":5609736,"slots":[72,41,41,42,42]}},"MAP_SEAFLOOR_CAVERN_ROOM1":{"header_address":4765440,"land_encounters":{"address":5609136,"slots":[41,41,41,41,41,41,41,41,42,42,42,42]},"warp_table_address":5491952},"MAP_SEAFLOOR_CAVERN_ROOM2":{"header_address":4765468,"land_encounters":{"address":5609192,"slots":[41,41,41,41,41,41,41,41,42,42,42,42]},"warp_table_address":5492188},"MAP_SEAFLOOR_CAVERN_ROOM3":{"header_address":4765496,"land_encounters":{"address":5609248,"slots":[41,41,41,41,41,41,41,41,42,42,42,42]},"warp_table_address":5492456},"MAP_SEAFLOOR_CAVERN_ROOM4":{"header_address":4765524,"land_encounters":{"address":5609304,"slots":[41,41,41,41,41,41,41,41,42,42,42,42]},"warp_table_address":5492548},"MAP_SEAFLOOR_CAVERN_ROOM5":{"header_address":4765552,"land_encounters":{"address":5609360,"slots":[41,41,41,41,41,41,41,41,42,42,42,42]},"warp_table_address":5492744},"MAP_SEAFLOOR_CAVERN_ROOM6":{"fishing_encounters":{"address":5609500,"slots":[129,72,129,72,313,313,313,313,313,313]},"header_address":4765580,"land_encounters":{"address":5609416,"slots":[41,41,41,41,41,41,41,41,42,42,42,42]},"warp_table_address":5492788,"water_encounters":{"address":5609472,"slots":[72,41,41,42,42]}},"MAP_SEAFLOOR_CAVERN_ROOM7":{"fishing_encounters":{"address":5609632,"slots":[129,72,129,72,313,313,313,313,313,313]},"header_address":4765608,"land_encounters":{"address":5609548,"slots":[41,41,41,41,41,41,41,41,42,42,42,42]},"warp_table_address":5492832,"water_encounters":{"address":5609604,"slots":[72,41,41,42,42]}},"MAP_SEAFLOOR_CAVERN_ROOM8":{"header_address":4765636,"land_encounters":{"address":5609680,"slots":[41,41,41,41,41,41,41,41,42,42,42,42]},"warp_table_address":5493156},"MAP_SEAFLOOR_CAVERN_ROOM9":{"header_address":4765664,"warp_table_address":5493360},"MAP_SEALED_CHAMBER_INNER_ROOM":{"header_address":4766672,"warp_table_address":5497984},"MAP_SEALED_CHAMBER_OUTER_ROOM":{"header_address":4766644,"warp_table_address":5497608},"MAP_SECRET_BASE_BLUE_CAVE1":{"header_address":4767736,"warp_table_address":5501652},"MAP_SECRET_BASE_BLUE_CAVE2":{"header_address":4767904,"warp_table_address":5503980},"MAP_SECRET_BASE_BLUE_CAVE3":{"header_address":4768072,"warp_table_address":5506308},"MAP_SECRET_BASE_BLUE_CAVE4":{"header_address":4768240,"warp_table_address":5508636},"MAP_SECRET_BASE_BROWN_CAVE1":{"header_address":4767708,"warp_table_address":5501264},"MAP_SECRET_BASE_BROWN_CAVE2":{"header_address":4767876,"warp_table_address":5503592},"MAP_SECRET_BASE_BROWN_CAVE3":{"header_address":4768044,"warp_table_address":5505920},"MAP_SECRET_BASE_BROWN_CAVE4":{"header_address":4768212,"warp_table_address":5508248},"MAP_SECRET_BASE_RED_CAVE1":{"header_address":4767680,"warp_table_address":5500876},"MAP_SECRET_BASE_RED_CAVE2":{"header_address":4767848,"warp_table_address":5503204},"MAP_SECRET_BASE_RED_CAVE3":{"header_address":4768016,"warp_table_address":5505532},"MAP_SECRET_BASE_RED_CAVE4":{"header_address":4768184,"warp_table_address":5507860},"MAP_SECRET_BASE_SHRUB1":{"header_address":4767820,"warp_table_address":5502816},"MAP_SECRET_BASE_SHRUB2":{"header_address":4767988,"warp_table_address":5505144},"MAP_SECRET_BASE_SHRUB3":{"header_address":4768156,"warp_table_address":5507472},"MAP_SECRET_BASE_SHRUB4":{"header_address":4768324,"warp_table_address":5509800},"MAP_SECRET_BASE_TREE1":{"header_address":4767792,"warp_table_address":5502428},"MAP_SECRET_BASE_TREE2":{"header_address":4767960,"warp_table_address":5504756},"MAP_SECRET_BASE_TREE3":{"header_address":4768128,"warp_table_address":5507084},"MAP_SECRET_BASE_TREE4":{"header_address":4768296,"warp_table_address":5509412},"MAP_SECRET_BASE_YELLOW_CAVE1":{"header_address":4767764,"warp_table_address":5502040},"MAP_SECRET_BASE_YELLOW_CAVE2":{"header_address":4767932,"warp_table_address":5504368},"MAP_SECRET_BASE_YELLOW_CAVE3":{"header_address":4768100,"warp_table_address":5506696},"MAP_SECRET_BASE_YELLOW_CAVE4":{"header_address":4768268,"warp_table_address":5509024},"MAP_SHOAL_CAVE_HIGH_TIDE_ENTRANCE_ROOM":{"header_address":4766056,"warp_table_address":4160749568},"MAP_SHOAL_CAVE_HIGH_TIDE_INNER_ROOM":{"header_address":4766084,"warp_table_address":4160749568},"MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM":{"fishing_encounters":{"address":5611436,"slots":[129,72,129,72,313,313,313,313,313,313]},"header_address":4765944,"land_encounters":{"address":5611352,"slots":[41,341,41,341,41,341,41,341,42,341,42,341]},"warp_table_address":5494828,"water_encounters":{"address":5611408,"slots":[72,41,341,341,341]}},"MAP_SHOAL_CAVE_LOW_TIDE_ICE_ROOM":{"header_address":4766980,"land_encounters":{"address":5612044,"slots":[41,341,41,341,41,341,346,341,42,346,42,346]},"warp_table_address":5498544},"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM":{"fishing_encounters":{"address":5611304,"slots":[129,72,129,72,313,313,313,313,313,313]},"header_address":4765972,"land_encounters":{"address":5611220,"slots":[41,341,41,341,41,341,41,341,42,341,42,341]},"warp_table_address":5494904,"water_encounters":{"address":5611276,"slots":[72,41,341,341,341]}},"MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM":{"header_address":4766028,"land_encounters":{"address":5611164,"slots":[41,341,41,341,41,341,41,341,42,341,42,341]},"warp_table_address":5495180},"MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM":{"header_address":4766000,"land_encounters":{"address":5611108,"slots":[41,341,41,341,41,341,41,341,42,341,42,341]},"warp_table_address":5495084},"MAP_SKY_PILLAR_1F":{"header_address":4766868,"land_encounters":{"address":5612100,"slots":[322,42,42,322,319,378,378,319,319,319,319,319]},"warp_table_address":5498328},"MAP_SKY_PILLAR_2F":{"header_address":4766896,"warp_table_address":5498372},"MAP_SKY_PILLAR_3F":{"header_address":4766924,"land_encounters":{"address":5612232,"slots":[322,42,42,322,319,378,378,319,319,319,319,319]},"warp_table_address":5498408},"MAP_SKY_PILLAR_4F":{"header_address":4766952,"warp_table_address":5498452},"MAP_SKY_PILLAR_5F":{"header_address":4767008,"land_encounters":{"address":5612288,"slots":[322,42,42,322,319,378,378,319,319,359,359,359]},"warp_table_address":5498572},"MAP_SKY_PILLAR_ENTRANCE":{"header_address":4766812,"warp_table_address":5498232},"MAP_SKY_PILLAR_OUTSIDE":{"header_address":4766840,"warp_table_address":5498292},"MAP_SKY_PILLAR_TOP":{"header_address":4767036,"warp_table_address":5498656},"MAP_SLATEPORT_CITY":{"fishing_encounters":{"address":5611664,"slots":[129,72,129,72,313,313,313,313,313,313]},"header_address":4758020,"warp_table_address":5429836,"water_encounters":{"address":5611636,"slots":[72,309,309,310,310]}},"MAP_SLATEPORT_CITY_BATTLE_TENT_BATTLE_ROOM":{"header_address":4761212,"warp_table_address":4160749568},"MAP_SLATEPORT_CITY_BATTLE_TENT_CORRIDOR":{"header_address":4761184,"warp_table_address":4160749568},"MAP_SLATEPORT_CITY_BATTLE_TENT_LOBBY":{"header_address":4761156,"warp_table_address":5466624},"MAP_SLATEPORT_CITY_HARBOR":{"header_address":4761352,"warp_table_address":5468328},"MAP_SLATEPORT_CITY_HOUSE":{"header_address":4761380,"warp_table_address":5468492},"MAP_SLATEPORT_CITY_MART":{"header_address":4761464,"warp_table_address":5468856},"MAP_SLATEPORT_CITY_NAME_RATERS_HOUSE":{"header_address":4761240,"warp_table_address":5466832},"MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F":{"header_address":4761296,"warp_table_address":5467456},"MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_2F":{"header_address":4761324,"warp_table_address":5467856},"MAP_SLATEPORT_CITY_POKEMON_CENTER_1F":{"header_address":4761408,"warp_table_address":5468600},"MAP_SLATEPORT_CITY_POKEMON_CENTER_2F":{"header_address":4761436,"warp_table_address":5468740},"MAP_SLATEPORT_CITY_POKEMON_FAN_CLUB":{"header_address":4761268,"warp_table_address":5467084},"MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F":{"header_address":4761100,"warp_table_address":5466360},"MAP_SLATEPORT_CITY_STERNS_SHIPYARD_2F":{"header_address":4761128,"warp_table_address":5466476},"MAP_SOOTOPOLIS_CITY":{"fishing_encounters":{"address":5612184,"slots":[129,72,129,129,129,129,129,130,130,130]},"header_address":4758188,"warp_table_address":5433852,"water_encounters":{"address":5612156,"slots":[129,129,129,129,129]}},"MAP_SOOTOPOLIS_CITY_GYM_1F":{"header_address":4763480,"warp_table_address":5481892},"MAP_SOOTOPOLIS_CITY_GYM_B1F":{"header_address":4763508,"warp_table_address":5482200},"MAP_SOOTOPOLIS_CITY_HOUSE1":{"header_address":4763620,"warp_table_address":5482664},"MAP_SOOTOPOLIS_CITY_HOUSE2":{"header_address":4763648,"warp_table_address":5482724},"MAP_SOOTOPOLIS_CITY_HOUSE3":{"header_address":4763676,"warp_table_address":5482808},"MAP_SOOTOPOLIS_CITY_HOUSE4":{"header_address":4763704,"warp_table_address":5482916},"MAP_SOOTOPOLIS_CITY_HOUSE5":{"header_address":4763732,"warp_table_address":5483000},"MAP_SOOTOPOLIS_CITY_HOUSE6":{"header_address":4763760,"warp_table_address":5483060},"MAP_SOOTOPOLIS_CITY_HOUSE7":{"header_address":4763788,"warp_table_address":5483144},"MAP_SOOTOPOLIS_CITY_LOTAD_AND_SEEDOT_HOUSE":{"header_address":4763816,"warp_table_address":5483228},"MAP_SOOTOPOLIS_CITY_MART":{"header_address":4763592,"warp_table_address":5482580},"MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F":{"header_address":4763844,"warp_table_address":5483312},"MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_B1F":{"header_address":4763872,"warp_table_address":5483380},"MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F":{"header_address":4763536,"warp_table_address":5482324},"MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F":{"header_address":4763564,"warp_table_address":5482464},"MAP_SOUTHERN_ISLAND_EXTERIOR":{"header_address":4769640,"warp_table_address":5516780},"MAP_SOUTHERN_ISLAND_INTERIOR":{"header_address":4769668,"warp_table_address":5516876},"MAP_SS_TIDAL_CORRIDOR":{"header_address":4768828,"warp_table_address":5510992},"MAP_SS_TIDAL_LOWER_DECK":{"header_address":4768856,"warp_table_address":5511276},"MAP_SS_TIDAL_ROOMS":{"header_address":4768884,"warp_table_address":5511508},"MAP_TERRA_CAVE_END":{"header_address":4767596,"warp_table_address":5500392},"MAP_TERRA_CAVE_ENTRANCE":{"header_address":4767568,"warp_table_address":5500332},"MAP_TRADE_CENTER":{"header_address":4768380,"warp_table_address":5509944},"MAP_TRAINER_HILL_1F":{"header_address":4771096,"warp_table_address":5525172},"MAP_TRAINER_HILL_2F":{"header_address":4771124,"warp_table_address":5525208},"MAP_TRAINER_HILL_3F":{"header_address":4771152,"warp_table_address":5525244},"MAP_TRAINER_HILL_4F":{"header_address":4771180,"warp_table_address":5525280},"MAP_TRAINER_HILL_ELEVATOR":{"header_address":4771852,"warp_table_address":5526300},"MAP_TRAINER_HILL_ENTRANCE":{"header_address":4771068,"warp_table_address":5525100},"MAP_TRAINER_HILL_ROOF":{"header_address":4771208,"warp_table_address":5525340},"MAP_UNDERWATER_MARINE_CAVE":{"header_address":4767484,"warp_table_address":5500208},"MAP_UNDERWATER_ROUTE105":{"header_address":4759532,"warp_table_address":5457348},"MAP_UNDERWATER_ROUTE124":{"header_address":4759392,"warp_table_address":4160749568,"water_encounters":{"address":5612016,"slots":[373,170,373,381,381]}},"MAP_UNDERWATER_ROUTE125":{"header_address":4759560,"warp_table_address":5457384},"MAP_UNDERWATER_ROUTE126":{"header_address":4759420,"warp_table_address":5457052,"water_encounters":{"address":5606268,"slots":[373,170,373,381,381]}},"MAP_UNDERWATER_ROUTE127":{"header_address":4759448,"warp_table_address":5457176},"MAP_UNDERWATER_ROUTE128":{"header_address":4759476,"warp_table_address":5457260},"MAP_UNDERWATER_ROUTE129":{"header_address":4759504,"warp_table_address":5457312},"MAP_UNDERWATER_ROUTE134":{"header_address":4766588,"warp_table_address":5497540},"MAP_UNDERWATER_SEAFLOOR_CAVERN":{"header_address":4765384,"warp_table_address":5491744},"MAP_UNDERWATER_SEALED_CHAMBER":{"header_address":4766616,"warp_table_address":5497568},"MAP_UNDERWATER_SOOTOPOLIS_CITY":{"header_address":4764796,"warp_table_address":5486768},"MAP_UNION_ROOM":{"header_address":4769360,"warp_table_address":5514872},"MAP_UNUSED_CONTEST_HALL1":{"header_address":4768492,"warp_table_address":4160749568},"MAP_UNUSED_CONTEST_HALL2":{"header_address":4768520,"warp_table_address":4160749568},"MAP_UNUSED_CONTEST_HALL3":{"header_address":4768548,"warp_table_address":4160749568},"MAP_UNUSED_CONTEST_HALL4":{"header_address":4768576,"warp_table_address":4160749568},"MAP_UNUSED_CONTEST_HALL5":{"header_address":4768604,"warp_table_address":4160749568},"MAP_UNUSED_CONTEST_HALL6":{"header_address":4768632,"warp_table_address":4160749568},"MAP_VERDANTURF_TOWN":{"header_address":4758384,"warp_table_address":5436044},"MAP_VERDANTURF_TOWN_BATTLE_TENT_BATTLE_ROOM":{"header_address":4760512,"warp_table_address":4160749568},"MAP_VERDANTURF_TOWN_BATTLE_TENT_CORRIDOR":{"header_address":4760484,"warp_table_address":4160749568},"MAP_VERDANTURF_TOWN_BATTLE_TENT_LOBBY":{"header_address":4760456,"warp_table_address":5463128},"MAP_VERDANTURF_TOWN_FRIENDSHIP_RATERS_HOUSE":{"header_address":4760652,"warp_table_address":5463928},"MAP_VERDANTURF_TOWN_HOUSE":{"header_address":4760680,"warp_table_address":5464012},"MAP_VERDANTURF_TOWN_MART":{"header_address":4760540,"warp_table_address":5463408},"MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F":{"header_address":4760568,"warp_table_address":5463540},"MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F":{"header_address":4760596,"warp_table_address":5463680},"MAP_VERDANTURF_TOWN_WANDAS_HOUSE":{"header_address":4760624,"warp_table_address":5463844},"MAP_VICTORY_ROAD_1F":{"header_address":4765860,"land_encounters":{"address":5606156,"slots":[42,336,383,371,41,335,42,336,382,370,382,370]},"warp_table_address":5493852},"MAP_VICTORY_ROAD_B1F":{"header_address":4765888,"land_encounters":{"address":5610496,"slots":[42,336,383,383,42,336,42,336,383,355,383,355]},"warp_table_address":5494460},"MAP_VICTORY_ROAD_B2F":{"fishing_encounters":{"address":5610664,"slots":[129,118,129,118,323,323,323,324,324,324]},"header_address":4765916,"land_encounters":{"address":5610580,"slots":[42,322,383,383,42,322,42,322,383,355,383,355]},"warp_table_address":5494704,"water_encounters":{"address":5610636,"slots":[42,42,42,42,42]}}},"misc_pokemon":[{"address":2572358,"species":385},{"address":2018148,"species":360},{"address":2323175,"species":101},{"address":2323252,"species":101},{"address":2581669,"species":317},{"address":2581574,"species":317},{"address":2581688,"species":317},{"address":2581593,"species":317},{"address":2581612,"species":317},{"address":2581631,"species":317},{"address":2581650,"species":317},{"address":2065036,"species":317},{"address":2386223,"species":185},{"address":2339323,"species":100},{"address":2339400,"species":100},{"address":2339477,"species":100}],"misc_ram_addresses":{"CB2_Overworld":134768624,"gArchipelagoDeathLinkQueued":33804824,"gArchipelagoReceivedItem":33804776,"gMain":50340544,"gPlayerParty":33703196,"gSaveBlock1Ptr":50355596,"gSaveBlock2Ptr":50355600},"misc_rom_addresses":{"gArchipelagoInfo":5912960,"gArchipelagoItemNames":5896457,"gArchipelagoNameTable":5905457,"gArchipelagoOptions":5895556,"gArchipelagoPlayerNames":5895607,"gBattleMoves":3281380,"gEvolutionTable":3318404,"gLevelUpLearnsets":3334884,"gRandomizedBerryTreeItems":5843560,"gRandomizedSoundTable":10155508,"gSpeciesInfo":3296744,"gTMHMLearnsets":3289780,"gTrainers":3230072,"gTutorMoves":6428060,"sFanfares":5422580,"sNewGamePCItems":6210444,"sStarterMon":6021752,"sTMHMMoves":6432208,"sTutorLearnsets":6428120},"species":[{"abilities":[0,0],"address":3296744,"base_stats":[0,0,0,0,0,0],"catch_rate":0,"evolutions":[],"friendship":0,"id":0,"learnset":{"address":3308280,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":4,"move_id":45},{"level":7,"move_id":73},{"level":10,"move_id":22},{"level":15,"move_id":77},{"level":15,"move_id":79},{"level":20,"move_id":75},{"level":25,"move_id":230},{"level":32,"move_id":74},{"level":39,"move_id":235},{"level":46,"move_id":76}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[65,0],"address":3296772,"base_stats":[45,49,49,45,65,65],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":16,"species":2}],"friendship":70,"id":1,"learnset":{"address":3308280,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":4,"move_id":45},{"level":7,"move_id":73},{"level":10,"move_id":22},{"level":15,"move_id":77},{"level":15,"move_id":79},{"level":20,"move_id":75},{"level":25,"move_id":230},{"level":32,"move_id":74},{"level":39,"move_id":235},{"level":46,"move_id":76}]},"tmhm_learnset":"00E41E0884350720","types":[12,3]},{"abilities":[65,0],"address":3296800,"base_stats":[60,62,63,60,80,80],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":32,"species":3}],"friendship":70,"id":2,"learnset":{"address":3308308,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":73},{"level":4,"move_id":45},{"level":7,"move_id":73},{"level":10,"move_id":22},{"level":15,"move_id":77},{"level":15,"move_id":79},{"level":22,"move_id":75},{"level":29,"move_id":230},{"level":38,"move_id":74},{"level":47,"move_id":235},{"level":56,"move_id":76}]},"tmhm_learnset":"00E41E0884350720","types":[12,3]},{"abilities":[65,0],"address":3296828,"base_stats":[80,82,83,80,100,100],"catch_rate":45,"evolutions":[],"friendship":70,"id":3,"learnset":{"address":3308338,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":73},{"level":1,"move_id":22},{"level":4,"move_id":45},{"level":7,"move_id":73},{"level":10,"move_id":22},{"level":15,"move_id":77},{"level":15,"move_id":79},{"level":22,"move_id":75},{"level":29,"move_id":230},{"level":41,"move_id":74},{"level":53,"move_id":235},{"level":65,"move_id":76}]},"tmhm_learnset":"00E41E0886354730","types":[12,3]},{"abilities":[66,0],"address":3296856,"base_stats":[39,52,43,65,60,50],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":16,"species":5}],"friendship":70,"id":4,"learnset":{"address":3308368,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":45},{"level":7,"move_id":52},{"level":13,"move_id":108},{"level":19,"move_id":99},{"level":25,"move_id":184},{"level":31,"move_id":53},{"level":37,"move_id":163},{"level":43,"move_id":82},{"level":49,"move_id":83}]},"tmhm_learnset":"00A61EA4CC510623","types":[10,10]},{"abilities":[66,0],"address":3296884,"base_stats":[58,64,58,80,80,65],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":36,"species":6}],"friendship":70,"id":5,"learnset":{"address":3308394,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":45},{"level":1,"move_id":52},{"level":7,"move_id":52},{"level":13,"move_id":108},{"level":20,"move_id":99},{"level":27,"move_id":184},{"level":34,"move_id":53},{"level":41,"move_id":163},{"level":48,"move_id":82},{"level":55,"move_id":83}]},"tmhm_learnset":"00A61EA4CC510623","types":[10,10]},{"abilities":[66,0],"address":3296912,"base_stats":[78,84,78,100,109,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":6,"learnset":{"address":3308420,"moves":[{"level":1,"move_id":10},{"level":1,"move_id":45},{"level":1,"move_id":52},{"level":1,"move_id":108},{"level":7,"move_id":52},{"level":13,"move_id":108},{"level":20,"move_id":99},{"level":27,"move_id":184},{"level":34,"move_id":53},{"level":36,"move_id":17},{"level":44,"move_id":163},{"level":54,"move_id":82},{"level":64,"move_id":83}]},"tmhm_learnset":"00AE5EA4CE514633","types":[10,2]},{"abilities":[67,0],"address":3296940,"base_stats":[44,48,65,43,50,64],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":16,"species":8}],"friendship":70,"id":7,"learnset":{"address":3308448,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":4,"move_id":39},{"level":7,"move_id":145},{"level":10,"move_id":110},{"level":13,"move_id":55},{"level":18,"move_id":44},{"level":23,"move_id":229},{"level":28,"move_id":182},{"level":33,"move_id":240},{"level":40,"move_id":130},{"level":47,"move_id":56}]},"tmhm_learnset":"03B01E00CC533265","types":[11,11]},{"abilities":[67,0],"address":3296968,"base_stats":[59,63,80,58,65,80],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":36,"species":9}],"friendship":70,"id":8,"learnset":{"address":3308478,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":145},{"level":4,"move_id":39},{"level":7,"move_id":145},{"level":10,"move_id":110},{"level":13,"move_id":55},{"level":19,"move_id":44},{"level":25,"move_id":229},{"level":31,"move_id":182},{"level":37,"move_id":240},{"level":45,"move_id":130},{"level":53,"move_id":56}]},"tmhm_learnset":"03B01E00CC533265","types":[11,11]},{"abilities":[67,0],"address":3296996,"base_stats":[79,83,100,78,85,105],"catch_rate":45,"evolutions":[],"friendship":70,"id":9,"learnset":{"address":3308508,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":145},{"level":1,"move_id":110},{"level":4,"move_id":39},{"level":7,"move_id":145},{"level":10,"move_id":110},{"level":13,"move_id":55},{"level":19,"move_id":44},{"level":25,"move_id":229},{"level":31,"move_id":182},{"level":42,"move_id":240},{"level":55,"move_id":130},{"level":68,"move_id":56}]},"tmhm_learnset":"03B01E00CE537275","types":[11,11]},{"abilities":[19,0],"address":3297024,"base_stats":[45,30,35,45,20,20],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":7,"species":11}],"friendship":70,"id":10,"learnset":{"address":3308538,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":81}]},"tmhm_learnset":"0000000000000000","types":[6,6]},{"abilities":[61,0],"address":3297052,"base_stats":[50,20,55,30,25,25],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":10,"species":12}],"friendship":70,"id":11,"learnset":{"address":3308548,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":7,"move_id":106}]},"tmhm_learnset":"0000000000000000","types":[6,6]},{"abilities":[14,0],"address":3297080,"base_stats":[60,45,50,70,80,80],"catch_rate":45,"evolutions":[],"friendship":70,"id":12,"learnset":{"address":3308560,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":93},{"level":10,"move_id":93},{"level":13,"move_id":77},{"level":14,"move_id":78},{"level":15,"move_id":79},{"level":18,"move_id":48},{"level":23,"move_id":18},{"level":28,"move_id":16},{"level":34,"move_id":60},{"level":40,"move_id":219},{"level":47,"move_id":318}]},"tmhm_learnset":"0040BE80B43F4620","types":[6,2]},{"abilities":[19,0],"address":3297108,"base_stats":[40,35,30,50,20,20],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":7,"species":14}],"friendship":70,"id":13,"learnset":{"address":3308590,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":40},{"level":1,"move_id":81}]},"tmhm_learnset":"0000000000000000","types":[6,3]},{"abilities":[61,0],"address":3297136,"base_stats":[45,25,50,35,25,25],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":10,"species":15}],"friendship":70,"id":14,"learnset":{"address":3308600,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":7,"move_id":106}]},"tmhm_learnset":"0000000000000000","types":[6,3]},{"abilities":[68,0],"address":3297164,"base_stats":[65,80,40,75,45,80],"catch_rate":45,"evolutions":[],"friendship":70,"id":15,"learnset":{"address":3308612,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":31},{"level":10,"move_id":31},{"level":15,"move_id":116},{"level":20,"move_id":41},{"level":25,"move_id":99},{"level":30,"move_id":228},{"level":35,"move_id":42},{"level":40,"move_id":97},{"level":45,"move_id":283}]},"tmhm_learnset":"00843E88C4354620","types":[6,3]},{"abilities":[51,0],"address":3297192,"base_stats":[40,45,40,56,35,35],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":18,"species":17}],"friendship":70,"id":16,"learnset":{"address":3308638,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":5,"move_id":28},{"level":9,"move_id":16},{"level":13,"move_id":98},{"level":19,"move_id":18},{"level":25,"move_id":17},{"level":31,"move_id":297},{"level":39,"move_id":97},{"level":47,"move_id":119}]},"tmhm_learnset":"00087E8084130620","types":[0,2]},{"abilities":[51,0],"address":3297220,"base_stats":[63,60,55,71,50,50],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":36,"species":18}],"friendship":70,"id":17,"learnset":{"address":3308664,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":28},{"level":1,"move_id":16},{"level":5,"move_id":28},{"level":9,"move_id":16},{"level":13,"move_id":98},{"level":20,"move_id":18},{"level":27,"move_id":17},{"level":34,"move_id":297},{"level":43,"move_id":97},{"level":52,"move_id":119}]},"tmhm_learnset":"00087E8084130620","types":[0,2]},{"abilities":[51,0],"address":3297248,"base_stats":[83,80,75,91,70,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":18,"learnset":{"address":3308690,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":28},{"level":1,"move_id":16},{"level":1,"move_id":98},{"level":5,"move_id":28},{"level":9,"move_id":16},{"level":13,"move_id":98},{"level":20,"move_id":18},{"level":27,"move_id":17},{"level":34,"move_id":297},{"level":48,"move_id":97},{"level":62,"move_id":119}]},"tmhm_learnset":"00087E8084134620","types":[0,2]},{"abilities":[50,62],"address":3297276,"base_stats":[30,56,35,72,25,35],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":20,"species":20}],"friendship":70,"id":19,"learnset":{"address":3308716,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":7,"move_id":98},{"level":13,"move_id":158},{"level":20,"move_id":116},{"level":27,"move_id":228},{"level":34,"move_id":162},{"level":41,"move_id":283}]},"tmhm_learnset":"00843E02ADD33E20","types":[0,0]},{"abilities":[50,62],"address":3297304,"base_stats":[55,81,60,97,50,70],"catch_rate":127,"evolutions":[],"friendship":70,"id":20,"learnset":{"address":3308738,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":98},{"level":7,"move_id":98},{"level":13,"move_id":158},{"level":20,"move_id":184},{"level":30,"move_id":228},{"level":40,"move_id":162},{"level":50,"move_id":283}]},"tmhm_learnset":"00A43E02ADD37E30","types":[0,0]},{"abilities":[51,0],"address":3297332,"base_stats":[40,60,30,70,31,31],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":20,"species":22}],"friendship":70,"id":21,"learnset":{"address":3308760,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":1,"move_id":45},{"level":7,"move_id":43},{"level":13,"move_id":31},{"level":19,"move_id":228},{"level":25,"move_id":332},{"level":31,"move_id":119},{"level":37,"move_id":65},{"level":43,"move_id":97}]},"tmhm_learnset":"00087E8084130620","types":[0,2]},{"abilities":[51,0],"address":3297360,"base_stats":[65,90,65,100,61,61],"catch_rate":90,"evolutions":[],"friendship":70,"id":22,"learnset":{"address":3308784,"moves":[{"level":1,"move_id":64},{"level":1,"move_id":45},{"level":1,"move_id":43},{"level":1,"move_id":31},{"level":7,"move_id":43},{"level":13,"move_id":31},{"level":26,"move_id":228},{"level":32,"move_id":119},{"level":40,"move_id":65},{"level":47,"move_id":97}]},"tmhm_learnset":"00087E8084134620","types":[0,2]},{"abilities":[22,61],"address":3297388,"base_stats":[35,60,44,55,40,54],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":22,"species":24}],"friendship":70,"id":23,"learnset":{"address":3308806,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":35},{"level":1,"move_id":43},{"level":8,"move_id":40},{"level":13,"move_id":44},{"level":20,"move_id":137},{"level":25,"move_id":103},{"level":32,"move_id":51},{"level":37,"move_id":254},{"level":37,"move_id":256},{"level":37,"move_id":255},{"level":44,"move_id":114}]},"tmhm_learnset":"00213F088E570620","types":[3,3]},{"abilities":[22,61],"address":3297416,"base_stats":[60,85,69,80,65,79],"catch_rate":90,"evolutions":[],"friendship":70,"id":24,"learnset":{"address":3308834,"moves":[{"level":1,"move_id":35},{"level":1,"move_id":43},{"level":1,"move_id":40},{"level":1,"move_id":44},{"level":8,"move_id":40},{"level":13,"move_id":44},{"level":20,"move_id":137},{"level":28,"move_id":103},{"level":38,"move_id":51},{"level":46,"move_id":254},{"level":46,"move_id":256},{"level":46,"move_id":255},{"level":56,"move_id":114}]},"tmhm_learnset":"00213F088E574620","types":[3,3]},{"abilities":[9,0],"address":3297444,"base_stats":[35,55,30,90,50,40],"catch_rate":190,"evolutions":[{"method":"ITEM","param":96,"species":26}],"friendship":70,"id":25,"learnset":{"address":3308862,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":84},{"level":1,"move_id":45},{"level":6,"move_id":39},{"level":8,"move_id":86},{"level":11,"move_id":98},{"level":15,"move_id":104},{"level":20,"move_id":21},{"level":26,"move_id":85},{"level":33,"move_id":97},{"level":41,"move_id":87},{"level":50,"move_id":113}]},"tmhm_learnset":"00E01E02CDD38221","types":[13,13]},{"abilities":[9,0],"address":3297472,"base_stats":[60,90,55,100,90,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":26,"learnset":{"address":3308890,"moves":[{"level":1,"move_id":84},{"level":1,"move_id":39},{"level":1,"move_id":98},{"level":1,"move_id":85}]},"tmhm_learnset":"00E03E02CDD3C221","types":[13,13]},{"abilities":[8,0],"address":3297500,"base_stats":[50,75,85,40,20,30],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":22,"species":28}],"friendship":70,"id":27,"learnset":{"address":3308900,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":6,"move_id":111},{"level":11,"move_id":28},{"level":17,"move_id":40},{"level":23,"move_id":163},{"level":30,"move_id":129},{"level":37,"move_id":154},{"level":45,"move_id":328},{"level":53,"move_id":201}]},"tmhm_learnset":"00A43ED0CE510621","types":[4,4]},{"abilities":[8,0],"address":3297528,"base_stats":[75,100,110,65,45,55],"catch_rate":90,"evolutions":[],"friendship":70,"id":28,"learnset":{"address":3308926,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":111},{"level":1,"move_id":28},{"level":6,"move_id":111},{"level":11,"move_id":28},{"level":17,"move_id":40},{"level":24,"move_id":163},{"level":33,"move_id":129},{"level":42,"move_id":154},{"level":52,"move_id":328},{"level":62,"move_id":201}]},"tmhm_learnset":"00A43ED0CE514621","types":[4,4]},{"abilities":[38,0],"address":3297556,"base_stats":[55,47,52,41,40,40],"catch_rate":235,"evolutions":[{"method":"LEVEL","param":16,"species":30}],"friendship":70,"id":29,"learnset":{"address":3308952,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":45},{"level":1,"move_id":10},{"level":8,"move_id":39},{"level":12,"move_id":24},{"level":17,"move_id":40},{"level":20,"move_id":44},{"level":23,"move_id":270},{"level":30,"move_id":154},{"level":38,"move_id":260},{"level":47,"move_id":242}]},"tmhm_learnset":"00A43E8A8DD33624","types":[3,3]},{"abilities":[38,0],"address":3297584,"base_stats":[70,62,67,56,55,55],"catch_rate":120,"evolutions":[{"method":"ITEM","param":94,"species":31}],"friendship":70,"id":30,"learnset":{"address":3308978,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":45},{"level":1,"move_id":10},{"level":8,"move_id":39},{"level":12,"move_id":24},{"level":18,"move_id":40},{"level":22,"move_id":44},{"level":26,"move_id":270},{"level":34,"move_id":154},{"level":43,"move_id":260},{"level":53,"move_id":242}]},"tmhm_learnset":"00A43E8A8DD33624","types":[3,3]},{"abilities":[38,0],"address":3297612,"base_stats":[90,82,87,76,75,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":31,"learnset":{"address":3309004,"moves":[{"level":1,"move_id":10},{"level":1,"move_id":39},{"level":1,"move_id":24},{"level":1,"move_id":40},{"level":23,"move_id":34}]},"tmhm_learnset":"00B43FFEEFD37E35","types":[3,4]},{"abilities":[38,0],"address":3297640,"base_stats":[46,57,40,50,40,40],"catch_rate":235,"evolutions":[{"method":"LEVEL","param":16,"species":33}],"friendship":70,"id":32,"learnset":{"address":3309016,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":64},{"level":8,"move_id":116},{"level":12,"move_id":24},{"level":17,"move_id":40},{"level":20,"move_id":30},{"level":23,"move_id":270},{"level":30,"move_id":31},{"level":38,"move_id":260},{"level":47,"move_id":32}]},"tmhm_learnset":"00A43E0A8DD33624","types":[3,3]},{"abilities":[38,0],"address":3297668,"base_stats":[61,72,57,65,55,55],"catch_rate":120,"evolutions":[{"method":"ITEM","param":94,"species":34}],"friendship":70,"id":33,"learnset":{"address":3309042,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":64},{"level":8,"move_id":116},{"level":12,"move_id":24},{"level":18,"move_id":40},{"level":22,"move_id":30},{"level":26,"move_id":270},{"level":34,"move_id":31},{"level":43,"move_id":260},{"level":53,"move_id":32}]},"tmhm_learnset":"00A43E0A8DD33624","types":[3,3]},{"abilities":[38,0],"address":3297696,"base_stats":[81,92,77,85,85,75],"catch_rate":45,"evolutions":[],"friendship":70,"id":34,"learnset":{"address":3309068,"moves":[{"level":1,"move_id":64},{"level":1,"move_id":116},{"level":1,"move_id":24},{"level":1,"move_id":40},{"level":23,"move_id":37}]},"tmhm_learnset":"00B43F7EEFD37E35","types":[3,4]},{"abilities":[56,0],"address":3297724,"base_stats":[70,45,48,35,60,65],"catch_rate":150,"evolutions":[{"method":"ITEM","param":94,"species":36}],"friendship":140,"id":35,"learnset":{"address":3309080,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":45},{"level":5,"move_id":227},{"level":9,"move_id":47},{"level":13,"move_id":3},{"level":17,"move_id":266},{"level":21,"move_id":107},{"level":25,"move_id":111},{"level":29,"move_id":118},{"level":33,"move_id":322},{"level":37,"move_id":236},{"level":41,"move_id":113},{"level":45,"move_id":309}]},"tmhm_learnset":"00611E27FDFBB62D","types":[0,0]},{"abilities":[56,0],"address":3297752,"base_stats":[95,70,73,60,85,90],"catch_rate":25,"evolutions":[],"friendship":140,"id":36,"learnset":{"address":3309112,"moves":[{"level":1,"move_id":47},{"level":1,"move_id":3},{"level":1,"move_id":107},{"level":1,"move_id":118}]},"tmhm_learnset":"00611E27FDFBF62D","types":[0,0]},{"abilities":[18,0],"address":3297780,"base_stats":[38,41,40,65,50,65],"catch_rate":190,"evolutions":[{"method":"ITEM","param":95,"species":38}],"friendship":70,"id":37,"learnset":{"address":3309122,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":52},{"level":5,"move_id":39},{"level":9,"move_id":46},{"level":13,"move_id":98},{"level":17,"move_id":261},{"level":21,"move_id":109},{"level":25,"move_id":286},{"level":29,"move_id":53},{"level":33,"move_id":219},{"level":37,"move_id":288},{"level":41,"move_id":83}]},"tmhm_learnset":"00021E248C590630","types":[10,10]},{"abilities":[18,0],"address":3297808,"base_stats":[73,76,75,100,81,100],"catch_rate":75,"evolutions":[],"friendship":70,"id":38,"learnset":{"address":3309152,"moves":[{"level":1,"move_id":52},{"level":1,"move_id":98},{"level":1,"move_id":109},{"level":1,"move_id":219},{"level":45,"move_id":83}]},"tmhm_learnset":"00021E248C594630","types":[10,10]},{"abilities":[56,0],"address":3297836,"base_stats":[115,45,20,20,45,25],"catch_rate":170,"evolutions":[{"method":"ITEM","param":94,"species":40}],"friendship":70,"id":39,"learnset":{"address":3309164,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":47},{"level":4,"move_id":111},{"level":9,"move_id":1},{"level":14,"move_id":50},{"level":19,"move_id":205},{"level":24,"move_id":3},{"level":29,"move_id":156},{"level":34,"move_id":34},{"level":39,"move_id":102},{"level":44,"move_id":304},{"level":49,"move_id":38}]},"tmhm_learnset":"00611E27FDBBB625","types":[0,0]},{"abilities":[56,0],"address":3297864,"base_stats":[140,70,45,45,75,50],"catch_rate":50,"evolutions":[],"friendship":70,"id":40,"learnset":{"address":3309194,"moves":[{"level":1,"move_id":47},{"level":1,"move_id":50},{"level":1,"move_id":111},{"level":1,"move_id":3}]},"tmhm_learnset":"00611E27FDBBF625","types":[0,0]},{"abilities":[39,0],"address":3297892,"base_stats":[40,45,35,55,30,40],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":22,"species":42}],"friendship":70,"id":41,"learnset":{"address":3309204,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":141},{"level":6,"move_id":48},{"level":11,"move_id":310},{"level":16,"move_id":44},{"level":21,"move_id":17},{"level":26,"move_id":109},{"level":31,"move_id":314},{"level":36,"move_id":212},{"level":41,"move_id":305},{"level":46,"move_id":114}]},"tmhm_learnset":"00017F88A4170E20","types":[3,2]},{"abilities":[39,0],"address":3297920,"base_stats":[75,80,70,90,65,75],"catch_rate":90,"evolutions":[{"method":"FRIENDSHIP","param":0,"species":169}],"friendship":70,"id":42,"learnset":{"address":3309232,"moves":[{"level":1,"move_id":103},{"level":1,"move_id":141},{"level":1,"move_id":48},{"level":1,"move_id":310},{"level":6,"move_id":48},{"level":11,"move_id":310},{"level":16,"move_id":44},{"level":21,"move_id":17},{"level":28,"move_id":109},{"level":35,"move_id":314},{"level":42,"move_id":212},{"level":49,"move_id":305},{"level":56,"move_id":114}]},"tmhm_learnset":"00017F88A4174E20","types":[3,2]},{"abilities":[34,0],"address":3297948,"base_stats":[45,50,55,30,75,65],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":21,"species":44}],"friendship":70,"id":43,"learnset":{"address":3309260,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":71},{"level":7,"move_id":230},{"level":14,"move_id":77},{"level":16,"move_id":78},{"level":18,"move_id":79},{"level":23,"move_id":51},{"level":32,"move_id":236},{"level":39,"move_id":80}]},"tmhm_learnset":"00441E0884350720","types":[12,3]},{"abilities":[34,0],"address":3297976,"base_stats":[60,65,70,40,85,75],"catch_rate":120,"evolutions":[{"method":"ITEM","param":98,"species":45},{"method":"ITEM","param":93,"species":182}],"friendship":70,"id":44,"learnset":{"address":3309284,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":71},{"level":1,"move_id":230},{"level":1,"move_id":77},{"level":7,"move_id":230},{"level":14,"move_id":77},{"level":16,"move_id":78},{"level":18,"move_id":79},{"level":24,"move_id":51},{"level":35,"move_id":236},{"level":44,"move_id":80}]},"tmhm_learnset":"00441E0884350720","types":[12,3]},{"abilities":[34,0],"address":3298004,"base_stats":[75,80,85,50,100,90],"catch_rate":45,"evolutions":[],"friendship":70,"id":45,"learnset":{"address":3309308,"moves":[{"level":1,"move_id":71},{"level":1,"move_id":312},{"level":1,"move_id":78},{"level":1,"move_id":72},{"level":44,"move_id":80}]},"tmhm_learnset":"00441E0884354720","types":[12,3]},{"abilities":[27,0],"address":3298032,"base_stats":[35,70,55,25,45,55],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":24,"species":47}],"friendship":70,"id":46,"learnset":{"address":3309320,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":7,"move_id":78},{"level":13,"move_id":77},{"level":19,"move_id":141},{"level":25,"move_id":147},{"level":31,"move_id":163},{"level":37,"move_id":74},{"level":43,"move_id":202},{"level":49,"move_id":312}]},"tmhm_learnset":"00C43E888C350720","types":[6,12]},{"abilities":[27,0],"address":3298060,"base_stats":[60,95,80,30,60,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":47,"learnset":{"address":3309346,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":78},{"level":1,"move_id":77},{"level":7,"move_id":78},{"level":13,"move_id":77},{"level":19,"move_id":141},{"level":27,"move_id":147},{"level":35,"move_id":163},{"level":43,"move_id":74},{"level":51,"move_id":202},{"level":59,"move_id":312}]},"tmhm_learnset":"00C43E888C354720","types":[6,12]},{"abilities":[14,0],"address":3298088,"base_stats":[60,55,50,45,40,55],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":31,"species":49}],"friendship":70,"id":48,"learnset":{"address":3309372,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":50},{"level":1,"move_id":193},{"level":9,"move_id":48},{"level":17,"move_id":93},{"level":20,"move_id":77},{"level":25,"move_id":141},{"level":28,"move_id":78},{"level":33,"move_id":60},{"level":36,"move_id":79},{"level":41,"move_id":94}]},"tmhm_learnset":"0040BE0894350620","types":[6,3]},{"abilities":[19,0],"address":3298116,"base_stats":[70,65,60,90,90,75],"catch_rate":75,"evolutions":[],"friendship":70,"id":49,"learnset":{"address":3309398,"moves":[{"level":1,"move_id":318},{"level":1,"move_id":33},{"level":1,"move_id":50},{"level":1,"move_id":193},{"level":1,"move_id":48},{"level":9,"move_id":48},{"level":17,"move_id":93},{"level":20,"move_id":77},{"level":25,"move_id":141},{"level":28,"move_id":78},{"level":31,"move_id":16},{"level":36,"move_id":60},{"level":42,"move_id":79},{"level":52,"move_id":94}]},"tmhm_learnset":"0040BE8894354620","types":[6,3]},{"abilities":[8,71],"address":3298144,"base_stats":[10,55,25,95,35,45],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":26,"species":51}],"friendship":70,"id":50,"learnset":{"address":3309428,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":28},{"level":5,"move_id":45},{"level":9,"move_id":222},{"level":17,"move_id":91},{"level":25,"move_id":189},{"level":33,"move_id":163},{"level":41,"move_id":89},{"level":49,"move_id":90}]},"tmhm_learnset":"00843EC88E110620","types":[4,4]},{"abilities":[8,71],"address":3298172,"base_stats":[35,80,50,120,50,70],"catch_rate":50,"evolutions":[],"friendship":70,"id":51,"learnset":{"address":3309452,"moves":[{"level":1,"move_id":161},{"level":1,"move_id":10},{"level":1,"move_id":28},{"level":1,"move_id":45},{"level":5,"move_id":45},{"level":9,"move_id":222},{"level":17,"move_id":91},{"level":25,"move_id":189},{"level":26,"move_id":328},{"level":38,"move_id":163},{"level":51,"move_id":89},{"level":64,"move_id":90}]},"tmhm_learnset":"00843EC88E114620","types":[4,4]},{"abilities":[53,0],"address":3298200,"base_stats":[40,45,35,90,40,40],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":28,"species":53}],"friendship":70,"id":52,"learnset":{"address":3309478,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":45},{"level":11,"move_id":44},{"level":20,"move_id":6},{"level":28,"move_id":185},{"level":35,"move_id":103},{"level":41,"move_id":154},{"level":46,"move_id":163},{"level":50,"move_id":252}]},"tmhm_learnset":"00453F82ADD30E24","types":[0,0]},{"abilities":[7,0],"address":3298228,"base_stats":[65,70,60,115,65,65],"catch_rate":90,"evolutions":[],"friendship":70,"id":53,"learnset":{"address":3309502,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":45},{"level":1,"move_id":44},{"level":11,"move_id":44},{"level":20,"move_id":6},{"level":29,"move_id":185},{"level":38,"move_id":103},{"level":46,"move_id":154},{"level":53,"move_id":163},{"level":59,"move_id":252}]},"tmhm_learnset":"00453F82ADD34E34","types":[0,0]},{"abilities":[6,13],"address":3298256,"base_stats":[50,52,48,55,65,50],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":33,"species":55}],"friendship":70,"id":54,"learnset":{"address":3309526,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":346},{"level":1,"move_id":10},{"level":5,"move_id":39},{"level":10,"move_id":50},{"level":16,"move_id":93},{"level":23,"move_id":103},{"level":31,"move_id":244},{"level":40,"move_id":154},{"level":50,"move_id":56}]},"tmhm_learnset":"03F01E80CC53326D","types":[11,11]},{"abilities":[6,13],"address":3298284,"base_stats":[80,82,78,85,95,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":55,"learnset":{"address":3309550,"moves":[{"level":1,"move_id":346},{"level":1,"move_id":10},{"level":1,"move_id":39},{"level":1,"move_id":50},{"level":5,"move_id":39},{"level":10,"move_id":50},{"level":16,"move_id":93},{"level":23,"move_id":103},{"level":31,"move_id":244},{"level":44,"move_id":154},{"level":58,"move_id":56}]},"tmhm_learnset":"03F01E80CC53726D","types":[11,11]},{"abilities":[72,0],"address":3298312,"base_stats":[40,80,35,70,35,45],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":28,"species":57}],"friendship":70,"id":56,"learnset":{"address":3309574,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":43},{"level":9,"move_id":67},{"level":15,"move_id":2},{"level":21,"move_id":154},{"level":27,"move_id":116},{"level":33,"move_id":69},{"level":39,"move_id":238},{"level":45,"move_id":103},{"level":51,"move_id":37}]},"tmhm_learnset":"00A23EC0CFD30EA1","types":[1,1]},{"abilities":[72,0],"address":3298340,"base_stats":[65,105,60,95,60,70],"catch_rate":75,"evolutions":[],"friendship":70,"id":57,"learnset":{"address":3309600,"moves":[{"level":1,"move_id":10},{"level":1,"move_id":43},{"level":1,"move_id":67},{"level":1,"move_id":99},{"level":9,"move_id":67},{"level":15,"move_id":2},{"level":21,"move_id":154},{"level":27,"move_id":116},{"level":28,"move_id":99},{"level":36,"move_id":69},{"level":45,"move_id":238},{"level":54,"move_id":103},{"level":63,"move_id":37}]},"tmhm_learnset":"00A23EC0CFD34EA1","types":[1,1]},{"abilities":[22,18],"address":3298368,"base_stats":[55,70,45,60,70,50],"catch_rate":190,"evolutions":[{"method":"ITEM","param":95,"species":59}],"friendship":70,"id":58,"learnset":{"address":3309628,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":44},{"level":1,"move_id":46},{"level":7,"move_id":52},{"level":13,"move_id":43},{"level":19,"move_id":316},{"level":25,"move_id":36},{"level":31,"move_id":172},{"level":37,"move_id":270},{"level":43,"move_id":97},{"level":49,"move_id":53}]},"tmhm_learnset":"00A23EA48C510630","types":[10,10]},{"abilities":[22,18],"address":3298396,"base_stats":[90,110,80,95,100,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":59,"learnset":{"address":3309654,"moves":[{"level":1,"move_id":44},{"level":1,"move_id":46},{"level":1,"move_id":52},{"level":1,"move_id":316},{"level":49,"move_id":245}]},"tmhm_learnset":"00A23EA48C514630","types":[10,10]},{"abilities":[11,6],"address":3298424,"base_stats":[40,50,40,90,40,40],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":25,"species":61}],"friendship":70,"id":60,"learnset":{"address":3309666,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":7,"move_id":95},{"level":13,"move_id":55},{"level":19,"move_id":3},{"level":25,"move_id":240},{"level":31,"move_id":34},{"level":37,"move_id":187},{"level":43,"move_id":56}]},"tmhm_learnset":"03103E009C133264","types":[11,11]},{"abilities":[11,6],"address":3298452,"base_stats":[65,65,65,90,50,50],"catch_rate":120,"evolutions":[{"method":"ITEM","param":97,"species":62},{"method":"ITEM","param":187,"species":186}],"friendship":70,"id":61,"learnset":{"address":3309690,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":1,"move_id":95},{"level":1,"move_id":55},{"level":7,"move_id":95},{"level":13,"move_id":55},{"level":19,"move_id":3},{"level":27,"move_id":240},{"level":35,"move_id":34},{"level":43,"move_id":187},{"level":51,"move_id":56}]},"tmhm_learnset":"03B03E00DE133265","types":[11,11]},{"abilities":[11,6],"address":3298480,"base_stats":[90,85,95,70,70,90],"catch_rate":45,"evolutions":[],"friendship":70,"id":62,"learnset":{"address":3309714,"moves":[{"level":1,"move_id":55},{"level":1,"move_id":95},{"level":1,"move_id":3},{"level":1,"move_id":66},{"level":35,"move_id":66},{"level":51,"move_id":170}]},"tmhm_learnset":"03B03E40DE1372E5","types":[11,1]},{"abilities":[28,39],"address":3298508,"base_stats":[25,20,15,90,105,55],"catch_rate":200,"evolutions":[{"method":"LEVEL","param":16,"species":64}],"friendship":70,"id":63,"learnset":{"address":3309728,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":93},{"level":1,"move_id":100}]},"tmhm_learnset":"0041BF03B45B8E29","types":[14,14]},{"abilities":[28,39],"address":3298536,"base_stats":[40,35,30,105,120,70],"catch_rate":100,"evolutions":[{"method":"LEVEL","param":37,"species":65}],"friendship":70,"id":64,"learnset":{"address":3309738,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":100},{"level":1,"move_id":134},{"level":1,"move_id":93},{"level":16,"move_id":93},{"level":18,"move_id":50},{"level":21,"move_id":60},{"level":23,"move_id":115},{"level":25,"move_id":105},{"level":30,"move_id":248},{"level":33,"move_id":272},{"level":36,"move_id":94},{"level":43,"move_id":271}]},"tmhm_learnset":"0041BF03B45B8E29","types":[14,14]},{"abilities":[28,39],"address":3298564,"base_stats":[55,50,45,120,135,85],"catch_rate":50,"evolutions":[],"friendship":70,"id":65,"learnset":{"address":3309766,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":100},{"level":1,"move_id":134},{"level":1,"move_id":93},{"level":16,"move_id":93},{"level":18,"move_id":50},{"level":21,"move_id":60},{"level":23,"move_id":115},{"level":25,"move_id":105},{"level":30,"move_id":248},{"level":33,"move_id":347},{"level":36,"move_id":94},{"level":43,"move_id":271}]},"tmhm_learnset":"0041BF03B45BCE29","types":[14,14]},{"abilities":[62,0],"address":3298592,"base_stats":[70,80,50,35,35,35],"catch_rate":180,"evolutions":[{"method":"LEVEL","param":28,"species":67}],"friendship":70,"id":66,"learnset":{"address":3309794,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":67},{"level":1,"move_id":43},{"level":7,"move_id":116},{"level":13,"move_id":2},{"level":19,"move_id":69},{"level":22,"move_id":193},{"level":25,"move_id":279},{"level":31,"move_id":233},{"level":37,"move_id":66},{"level":40,"move_id":238},{"level":43,"move_id":184},{"level":49,"move_id":223}]},"tmhm_learnset":"00A03E64CE1306A1","types":[1,1]},{"abilities":[62,0],"address":3298620,"base_stats":[80,100,70,45,50,60],"catch_rate":90,"evolutions":[{"method":"LEVEL","param":37,"species":68}],"friendship":70,"id":67,"learnset":{"address":3309824,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":67},{"level":1,"move_id":43},{"level":1,"move_id":116},{"level":7,"move_id":116},{"level":13,"move_id":2},{"level":19,"move_id":69},{"level":22,"move_id":193},{"level":25,"move_id":279},{"level":33,"move_id":233},{"level":41,"move_id":66},{"level":46,"move_id":238},{"level":51,"move_id":184},{"level":59,"move_id":223}]},"tmhm_learnset":"00A03E64CE1306A1","types":[1,1]},{"abilities":[62,0],"address":3298648,"base_stats":[90,130,80,55,65,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":68,"learnset":{"address":3309854,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":67},{"level":1,"move_id":43},{"level":1,"move_id":116},{"level":7,"move_id":116},{"level":13,"move_id":2},{"level":19,"move_id":69},{"level":22,"move_id":193},{"level":25,"move_id":279},{"level":33,"move_id":233},{"level":41,"move_id":66},{"level":46,"move_id":238},{"level":51,"move_id":184},{"level":59,"move_id":223}]},"tmhm_learnset":"00A03E64CE1346A1","types":[1,1]},{"abilities":[34,0],"address":3298676,"base_stats":[50,75,35,40,70,30],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":21,"species":70}],"friendship":70,"id":69,"learnset":{"address":3309884,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":22},{"level":6,"move_id":74},{"level":11,"move_id":35},{"level":15,"move_id":79},{"level":17,"move_id":77},{"level":19,"move_id":78},{"level":23,"move_id":51},{"level":30,"move_id":230},{"level":37,"move_id":75},{"level":45,"move_id":21}]},"tmhm_learnset":"00443E0884350720","types":[12,3]},{"abilities":[34,0],"address":3298704,"base_stats":[65,90,50,55,85,45],"catch_rate":120,"evolutions":[{"method":"ITEM","param":98,"species":71}],"friendship":70,"id":70,"learnset":{"address":3309912,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":22},{"level":1,"move_id":74},{"level":1,"move_id":35},{"level":6,"move_id":74},{"level":11,"move_id":35},{"level":15,"move_id":79},{"level":17,"move_id":77},{"level":19,"move_id":78},{"level":24,"move_id":51},{"level":33,"move_id":230},{"level":42,"move_id":75},{"level":54,"move_id":21}]},"tmhm_learnset":"00443E0884350720","types":[12,3]},{"abilities":[34,0],"address":3298732,"base_stats":[80,105,65,70,100,60],"catch_rate":45,"evolutions":[],"friendship":70,"id":71,"learnset":{"address":3309940,"moves":[{"level":1,"move_id":22},{"level":1,"move_id":79},{"level":1,"move_id":230},{"level":1,"move_id":75}]},"tmhm_learnset":"00443E0884354720","types":[12,3]},{"abilities":[29,64],"address":3298760,"base_stats":[40,40,35,70,50,100],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":30,"species":73}],"friendship":70,"id":72,"learnset":{"address":3309950,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":40},{"level":6,"move_id":48},{"level":12,"move_id":132},{"level":19,"move_id":51},{"level":25,"move_id":61},{"level":30,"move_id":35},{"level":36,"move_id":112},{"level":43,"move_id":103},{"level":49,"move_id":56}]},"tmhm_learnset":"03143E0884173264","types":[11,3]},{"abilities":[29,64],"address":3298788,"base_stats":[80,70,65,100,80,120],"catch_rate":60,"evolutions":[],"friendship":70,"id":73,"learnset":{"address":3309976,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":40},{"level":1,"move_id":48},{"level":1,"move_id":132},{"level":6,"move_id":48},{"level":12,"move_id":132},{"level":19,"move_id":51},{"level":25,"move_id":61},{"level":30,"move_id":35},{"level":38,"move_id":112},{"level":47,"move_id":103},{"level":55,"move_id":56}]},"tmhm_learnset":"03143E0884177264","types":[11,3]},{"abilities":[69,5],"address":3298816,"base_stats":[40,80,100,20,30,30],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":25,"species":75}],"friendship":70,"id":74,"learnset":{"address":3310002,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":111},{"level":6,"move_id":300},{"level":11,"move_id":88},{"level":16,"move_id":222},{"level":21,"move_id":120},{"level":26,"move_id":205},{"level":31,"move_id":350},{"level":36,"move_id":89},{"level":41,"move_id":153},{"level":46,"move_id":38}]},"tmhm_learnset":"00A01E74CE110621","types":[5,4]},{"abilities":[69,5],"address":3298844,"base_stats":[55,95,115,35,45,45],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":37,"species":76}],"friendship":70,"id":75,"learnset":{"address":3310030,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":111},{"level":1,"move_id":300},{"level":1,"move_id":88},{"level":6,"move_id":300},{"level":11,"move_id":88},{"level":16,"move_id":222},{"level":21,"move_id":120},{"level":29,"move_id":205},{"level":37,"move_id":350},{"level":45,"move_id":89},{"level":53,"move_id":153},{"level":62,"move_id":38}]},"tmhm_learnset":"00A01E74CE110621","types":[5,4]},{"abilities":[69,5],"address":3298872,"base_stats":[80,110,130,45,55,65],"catch_rate":45,"evolutions":[],"friendship":70,"id":76,"learnset":{"address":3310058,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":111},{"level":1,"move_id":300},{"level":1,"move_id":88},{"level":6,"move_id":300},{"level":11,"move_id":88},{"level":16,"move_id":222},{"level":21,"move_id":120},{"level":29,"move_id":205},{"level":37,"move_id":350},{"level":45,"move_id":89},{"level":53,"move_id":153},{"level":62,"move_id":38}]},"tmhm_learnset":"00A01E74CE114631","types":[5,4]},{"abilities":[50,18],"address":3298900,"base_stats":[50,85,55,90,65,65],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":40,"species":78}],"friendship":70,"id":77,"learnset":{"address":3310086,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":5,"move_id":45},{"level":9,"move_id":39},{"level":14,"move_id":52},{"level":19,"move_id":23},{"level":25,"move_id":83},{"level":31,"move_id":36},{"level":38,"move_id":97},{"level":45,"move_id":340},{"level":53,"move_id":126}]},"tmhm_learnset":"00221E2484710620","types":[10,10]},{"abilities":[50,18],"address":3298928,"base_stats":[65,100,70,105,80,80],"catch_rate":60,"evolutions":[],"friendship":70,"id":78,"learnset":{"address":3310114,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":39},{"level":1,"move_id":52},{"level":5,"move_id":45},{"level":9,"move_id":39},{"level":14,"move_id":52},{"level":19,"move_id":23},{"level":25,"move_id":83},{"level":31,"move_id":36},{"level":38,"move_id":97},{"level":40,"move_id":31},{"level":50,"move_id":340},{"level":63,"move_id":126}]},"tmhm_learnset":"00221E2484714620","types":[10,10]},{"abilities":[12,20],"address":3298956,"base_stats":[90,65,65,15,40,40],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":37,"species":80},{"method":"ITEM","param":187,"species":199}],"friendship":70,"id":79,"learnset":{"address":3310144,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":174},{"level":1,"move_id":281},{"level":1,"move_id":33},{"level":6,"move_id":45},{"level":15,"move_id":55},{"level":20,"move_id":93},{"level":29,"move_id":50},{"level":34,"move_id":29},{"level":43,"move_id":133},{"level":48,"move_id":94}]},"tmhm_learnset":"02709E24BE5B366C","types":[11,14]},{"abilities":[12,20],"address":3298984,"base_stats":[95,75,110,30,100,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":80,"learnset":{"address":3310168,"moves":[{"level":1,"move_id":174},{"level":1,"move_id":281},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":6,"move_id":45},{"level":15,"move_id":55},{"level":20,"move_id":93},{"level":29,"move_id":50},{"level":34,"move_id":29},{"level":37,"move_id":110},{"level":46,"move_id":133},{"level":54,"move_id":94}]},"tmhm_learnset":"02F09E24FE5B766D","types":[11,14]},{"abilities":[42,5],"address":3299012,"base_stats":[25,35,70,45,95,55],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":30,"species":82}],"friendship":70,"id":81,"learnset":{"address":3310194,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":319},{"level":1,"move_id":33},{"level":6,"move_id":84},{"level":11,"move_id":48},{"level":16,"move_id":49},{"level":21,"move_id":86},{"level":26,"move_id":209},{"level":32,"move_id":199},{"level":38,"move_id":129},{"level":44,"move_id":103},{"level":50,"move_id":192}]},"tmhm_learnset":"00400E0385930620","types":[13,8]},{"abilities":[42,5],"address":3299040,"base_stats":[50,60,95,70,120,70],"catch_rate":60,"evolutions":[],"friendship":70,"id":82,"learnset":{"address":3310222,"moves":[{"level":1,"move_id":319},{"level":1,"move_id":33},{"level":1,"move_id":84},{"level":1,"move_id":48},{"level":6,"move_id":84},{"level":11,"move_id":48},{"level":16,"move_id":49},{"level":21,"move_id":86},{"level":26,"move_id":209},{"level":35,"move_id":199},{"level":44,"move_id":161},{"level":53,"move_id":103},{"level":62,"move_id":192}]},"tmhm_learnset":"00400E0385934620","types":[13,8]},{"abilities":[51,39],"address":3299068,"base_stats":[52,65,55,60,58,62],"catch_rate":45,"evolutions":[],"friendship":70,"id":83,"learnset":{"address":3310250,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":6,"move_id":28},{"level":11,"move_id":43},{"level":16,"move_id":31},{"level":21,"move_id":282},{"level":26,"move_id":210},{"level":31,"move_id":14},{"level":36,"move_id":97},{"level":41,"move_id":163},{"level":46,"move_id":206}]},"tmhm_learnset":"000C7E8084510620","types":[0,2]},{"abilities":[50,48],"address":3299096,"base_stats":[35,85,45,75,35,35],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":31,"species":85}],"friendship":70,"id":84,"learnset":{"address":3310278,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":1,"move_id":45},{"level":9,"move_id":228},{"level":13,"move_id":31},{"level":21,"move_id":161},{"level":25,"move_id":99},{"level":33,"move_id":253},{"level":37,"move_id":65},{"level":45,"move_id":97}]},"tmhm_learnset":"00087E8084110620","types":[0,2]},{"abilities":[50,48],"address":3299124,"base_stats":[60,110,70,100,60,60],"catch_rate":45,"evolutions":[],"friendship":70,"id":85,"learnset":{"address":3310302,"moves":[{"level":1,"move_id":64},{"level":1,"move_id":45},{"level":1,"move_id":228},{"level":1,"move_id":31},{"level":9,"move_id":228},{"level":13,"move_id":31},{"level":21,"move_id":161},{"level":25,"move_id":99},{"level":38,"move_id":253},{"level":47,"move_id":65},{"level":60,"move_id":97}]},"tmhm_learnset":"00087F8084114E20","types":[0,2]},{"abilities":[47,0],"address":3299152,"base_stats":[65,45,55,45,45,70],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":34,"species":87}],"friendship":70,"id":86,"learnset":{"address":3310326,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":29},{"level":9,"move_id":45},{"level":17,"move_id":196},{"level":21,"move_id":62},{"level":29,"move_id":156},{"level":37,"move_id":36},{"level":41,"move_id":58},{"level":49,"move_id":219}]},"tmhm_learnset":"03103E00841B3264","types":[11,11]},{"abilities":[47,0],"address":3299180,"base_stats":[90,70,80,70,70,95],"catch_rate":75,"evolutions":[],"friendship":70,"id":87,"learnset":{"address":3310350,"moves":[{"level":1,"move_id":29},{"level":1,"move_id":45},{"level":1,"move_id":196},{"level":1,"move_id":62},{"level":9,"move_id":45},{"level":17,"move_id":196},{"level":21,"move_id":62},{"level":29,"move_id":156},{"level":34,"move_id":329},{"level":42,"move_id":36},{"level":51,"move_id":58},{"level":64,"move_id":219}]},"tmhm_learnset":"03103E00841B7264","types":[11,15]},{"abilities":[1,60],"address":3299208,"base_stats":[80,80,50,25,40,50],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":38,"species":89}],"friendship":70,"id":88,"learnset":{"address":3310376,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":139},{"level":1,"move_id":1},{"level":4,"move_id":106},{"level":8,"move_id":50},{"level":13,"move_id":124},{"level":19,"move_id":107},{"level":26,"move_id":103},{"level":34,"move_id":151},{"level":43,"move_id":188},{"level":53,"move_id":262}]},"tmhm_learnset":"00003F6E8D970E20","types":[3,3]},{"abilities":[1,60],"address":3299236,"base_stats":[105,105,75,50,65,100],"catch_rate":75,"evolutions":[],"friendship":70,"id":89,"learnset":{"address":3310402,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":139},{"level":1,"move_id":1},{"level":1,"move_id":106},{"level":4,"move_id":106},{"level":8,"move_id":50},{"level":13,"move_id":124},{"level":19,"move_id":107},{"level":26,"move_id":103},{"level":34,"move_id":151},{"level":47,"move_id":188},{"level":61,"move_id":262}]},"tmhm_learnset":"00A03F6ECD974E21","types":[3,3]},{"abilities":[75,0],"address":3299264,"base_stats":[30,65,100,40,45,25],"catch_rate":190,"evolutions":[{"method":"ITEM","param":97,"species":91}],"friendship":70,"id":90,"learnset":{"address":3310428,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":110},{"level":9,"move_id":48},{"level":17,"move_id":62},{"level":25,"move_id":182},{"level":33,"move_id":43},{"level":41,"move_id":128},{"level":49,"move_id":58}]},"tmhm_learnset":"02101E0084133264","types":[11,11]},{"abilities":[75,0],"address":3299292,"base_stats":[50,95,180,70,85,45],"catch_rate":60,"evolutions":[],"friendship":70,"id":91,"learnset":{"address":3310450,"moves":[{"level":1,"move_id":110},{"level":1,"move_id":48},{"level":1,"move_id":62},{"level":1,"move_id":182},{"level":33,"move_id":191},{"level":41,"move_id":131}]},"tmhm_learnset":"02101F0084137264","types":[11,15]},{"abilities":[26,0],"address":3299320,"base_stats":[30,35,30,80,100,35],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":25,"species":93}],"friendship":70,"id":92,"learnset":{"address":3310464,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":95},{"level":1,"move_id":122},{"level":8,"move_id":180},{"level":13,"move_id":212},{"level":16,"move_id":174},{"level":21,"move_id":101},{"level":28,"move_id":109},{"level":33,"move_id":138},{"level":36,"move_id":194}]},"tmhm_learnset":"0001BF08B4970E20","types":[7,3]},{"abilities":[26,0],"address":3299348,"base_stats":[45,50,45,95,115,55],"catch_rate":90,"evolutions":[{"method":"LEVEL","param":37,"species":94}],"friendship":70,"id":93,"learnset":{"address":3310488,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":95},{"level":1,"move_id":122},{"level":1,"move_id":180},{"level":8,"move_id":180},{"level":13,"move_id":212},{"level":16,"move_id":174},{"level":21,"move_id":101},{"level":25,"move_id":325},{"level":31,"move_id":109},{"level":39,"move_id":138},{"level":48,"move_id":194}]},"tmhm_learnset":"0001BF08B4970E20","types":[7,3]},{"abilities":[26,0],"address":3299376,"base_stats":[60,65,60,110,130,75],"catch_rate":45,"evolutions":[],"friendship":70,"id":94,"learnset":{"address":3310514,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":95},{"level":1,"move_id":122},{"level":1,"move_id":180},{"level":8,"move_id":180},{"level":13,"move_id":212},{"level":16,"move_id":174},{"level":21,"move_id":101},{"level":25,"move_id":325},{"level":31,"move_id":109},{"level":39,"move_id":138},{"level":48,"move_id":194}]},"tmhm_learnset":"00A1BF08F5974E21","types":[7,3]},{"abilities":[69,5],"address":3299404,"base_stats":[35,45,160,70,30,45],"catch_rate":45,"evolutions":[{"method":"ITEM","param":199,"species":208}],"friendship":70,"id":95,"learnset":{"address":3310540,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":103},{"level":9,"move_id":20},{"level":13,"move_id":88},{"level":21,"move_id":106},{"level":25,"move_id":99},{"level":33,"move_id":201},{"level":37,"move_id":21},{"level":45,"move_id":231},{"level":49,"move_id":328},{"level":57,"move_id":38}]},"tmhm_learnset":"00A01F508E510E30","types":[5,4]},{"abilities":[15,0],"address":3299432,"base_stats":[60,48,45,42,43,90],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":26,"species":97}],"friendship":70,"id":96,"learnset":{"address":3310568,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":95},{"level":10,"move_id":50},{"level":18,"move_id":93},{"level":25,"move_id":29},{"level":31,"move_id":139},{"level":36,"move_id":96},{"level":40,"move_id":94},{"level":43,"move_id":244},{"level":45,"move_id":248}]},"tmhm_learnset":"0041BF01F41B8E29","types":[14,14]},{"abilities":[15,0],"address":3299460,"base_stats":[85,73,70,67,73,115],"catch_rate":75,"evolutions":[],"friendship":70,"id":97,"learnset":{"address":3310594,"moves":[{"level":1,"move_id":1},{"level":1,"move_id":95},{"level":1,"move_id":50},{"level":1,"move_id":93},{"level":10,"move_id":50},{"level":18,"move_id":93},{"level":25,"move_id":29},{"level":33,"move_id":139},{"level":40,"move_id":96},{"level":49,"move_id":94},{"level":55,"move_id":244},{"level":60,"move_id":248}]},"tmhm_learnset":"0041BF01F41BCE29","types":[14,14]},{"abilities":[52,75],"address":3299488,"base_stats":[30,105,90,50,25,25],"catch_rate":225,"evolutions":[{"method":"LEVEL","param":28,"species":99}],"friendship":70,"id":98,"learnset":{"address":3310620,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":5,"move_id":43},{"level":12,"move_id":11},{"level":16,"move_id":106},{"level":23,"move_id":341},{"level":27,"move_id":23},{"level":34,"move_id":12},{"level":41,"move_id":182},{"level":45,"move_id":152}]},"tmhm_learnset":"02B43E408C133264","types":[11,11]},{"abilities":[52,75],"address":3299516,"base_stats":[55,130,115,75,50,50],"catch_rate":60,"evolutions":[],"friendship":70,"id":99,"learnset":{"address":3310646,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":1,"move_id":43},{"level":1,"move_id":11},{"level":5,"move_id":43},{"level":12,"move_id":11},{"level":16,"move_id":106},{"level":23,"move_id":341},{"level":27,"move_id":23},{"level":38,"move_id":12},{"level":49,"move_id":182},{"level":57,"move_id":152}]},"tmhm_learnset":"02B43E408C137264","types":[11,11]},{"abilities":[43,9],"address":3299544,"base_stats":[40,30,50,100,55,55],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":30,"species":101}],"friendship":70,"id":100,"learnset":{"address":3310672,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":268},{"level":1,"move_id":33},{"level":8,"move_id":103},{"level":15,"move_id":49},{"level":21,"move_id":209},{"level":27,"move_id":120},{"level":32,"move_id":205},{"level":37,"move_id":113},{"level":42,"move_id":129},{"level":46,"move_id":153},{"level":49,"move_id":243}]},"tmhm_learnset":"00402F0285938A20","types":[13,13]},{"abilities":[43,9],"address":3299572,"base_stats":[60,50,70,140,80,80],"catch_rate":60,"evolutions":[],"friendship":70,"id":101,"learnset":{"address":3310700,"moves":[{"level":1,"move_id":268},{"level":1,"move_id":33},{"level":1,"move_id":103},{"level":1,"move_id":49},{"level":8,"move_id":103},{"level":15,"move_id":49},{"level":21,"move_id":209},{"level":27,"move_id":120},{"level":34,"move_id":205},{"level":41,"move_id":113},{"level":48,"move_id":129},{"level":54,"move_id":153},{"level":59,"move_id":243}]},"tmhm_learnset":"00402F028593CA20","types":[13,13]},{"abilities":[34,0],"address":3299600,"base_stats":[60,40,80,40,60,45],"catch_rate":90,"evolutions":[{"method":"ITEM","param":98,"species":103}],"friendship":70,"id":102,"learnset":{"address":3310728,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":140},{"level":1,"move_id":253},{"level":1,"move_id":95},{"level":7,"move_id":115},{"level":13,"move_id":73},{"level":19,"move_id":93},{"level":25,"move_id":78},{"level":31,"move_id":77},{"level":37,"move_id":79},{"level":43,"move_id":76}]},"tmhm_learnset":"0060BE0994358720","types":[12,14]},{"abilities":[34,0],"address":3299628,"base_stats":[95,95,85,55,125,65],"catch_rate":45,"evolutions":[],"friendship":70,"id":103,"learnset":{"address":3310752,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":140},{"level":1,"move_id":95},{"level":1,"move_id":93},{"level":19,"move_id":23},{"level":31,"move_id":121}]},"tmhm_learnset":"0060BE099435C720","types":[12,14]},{"abilities":[69,31],"address":3299656,"base_stats":[50,50,95,35,40,50],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":28,"species":105}],"friendship":70,"id":104,"learnset":{"address":3310766,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":125},{"level":1,"move_id":45},{"level":5,"move_id":39},{"level":9,"move_id":125},{"level":13,"move_id":29},{"level":17,"move_id":43},{"level":21,"move_id":116},{"level":25,"move_id":155},{"level":29,"move_id":99},{"level":33,"move_id":206},{"level":37,"move_id":37},{"level":41,"move_id":198},{"level":45,"move_id":38}]},"tmhm_learnset":"00A03EF4CE513621","types":[4,4]},{"abilities":[69,31],"address":3299684,"base_stats":[60,80,110,45,50,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":105,"learnset":{"address":3310798,"moves":[{"level":1,"move_id":45},{"level":1,"move_id":39},{"level":1,"move_id":125},{"level":1,"move_id":29},{"level":5,"move_id":39},{"level":9,"move_id":125},{"level":13,"move_id":29},{"level":17,"move_id":43},{"level":21,"move_id":116},{"level":25,"move_id":155},{"level":32,"move_id":99},{"level":39,"move_id":206},{"level":46,"move_id":37},{"level":53,"move_id":198},{"level":61,"move_id":38}]},"tmhm_learnset":"00A03EF4CE517621","types":[4,4]},{"abilities":[7,0],"address":3299712,"base_stats":[50,120,53,87,35,110],"catch_rate":45,"evolutions":[],"friendship":70,"id":106,"learnset":{"address":3310830,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":279},{"level":1,"move_id":24},{"level":6,"move_id":96},{"level":11,"move_id":27},{"level":16,"move_id":26},{"level":20,"move_id":280},{"level":21,"move_id":116},{"level":26,"move_id":136},{"level":31,"move_id":170},{"level":36,"move_id":193},{"level":41,"move_id":203},{"level":46,"move_id":25},{"level":51,"move_id":179}]},"tmhm_learnset":"00A03E40C61306A1","types":[1,1]},{"abilities":[51,0],"address":3299740,"base_stats":[50,105,79,76,35,110],"catch_rate":45,"evolutions":[],"friendship":70,"id":107,"learnset":{"address":3310862,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":279},{"level":1,"move_id":4},{"level":7,"move_id":97},{"level":13,"move_id":228},{"level":20,"move_id":183},{"level":26,"move_id":9},{"level":26,"move_id":8},{"level":26,"move_id":7},{"level":32,"move_id":327},{"level":38,"move_id":5},{"level":44,"move_id":197},{"level":50,"move_id":68}]},"tmhm_learnset":"00A03E40C61306A1","types":[1,1]},{"abilities":[20,12],"address":3299768,"base_stats":[90,55,75,30,60,75],"catch_rate":45,"evolutions":[],"friendship":70,"id":108,"learnset":{"address":3310892,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":122},{"level":7,"move_id":48},{"level":12,"move_id":111},{"level":18,"move_id":282},{"level":23,"move_id":23},{"level":29,"move_id":35},{"level":34,"move_id":50},{"level":40,"move_id":21},{"level":45,"move_id":103},{"level":51,"move_id":287}]},"tmhm_learnset":"00B43E76EFF37625","types":[0,0]},{"abilities":[26,0],"address":3299796,"base_stats":[40,65,95,35,60,45],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":35,"species":110}],"friendship":70,"id":109,"learnset":{"address":3310920,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":139},{"level":1,"move_id":33},{"level":9,"move_id":123},{"level":17,"move_id":120},{"level":21,"move_id":124},{"level":25,"move_id":108},{"level":33,"move_id":114},{"level":41,"move_id":153},{"level":45,"move_id":194},{"level":49,"move_id":262}]},"tmhm_learnset":"00403F2EA5930E20","types":[3,3]},{"abilities":[26,0],"address":3299824,"base_stats":[65,90,120,60,85,70],"catch_rate":60,"evolutions":[],"friendship":70,"id":110,"learnset":{"address":3310946,"moves":[{"level":1,"move_id":139},{"level":1,"move_id":33},{"level":1,"move_id":123},{"level":1,"move_id":120},{"level":9,"move_id":123},{"level":17,"move_id":120},{"level":21,"move_id":124},{"level":25,"move_id":108},{"level":33,"move_id":114},{"level":44,"move_id":153},{"level":51,"move_id":194},{"level":58,"move_id":262}]},"tmhm_learnset":"00403F2EA5934E20","types":[3,3]},{"abilities":[31,69],"address":3299852,"base_stats":[80,85,95,25,30,30],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":42,"species":112}],"friendship":70,"id":111,"learnset":{"address":3310972,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":30},{"level":1,"move_id":39},{"level":10,"move_id":23},{"level":15,"move_id":31},{"level":24,"move_id":184},{"level":29,"move_id":350},{"level":38,"move_id":32},{"level":43,"move_id":36},{"level":52,"move_id":89},{"level":57,"move_id":224}]},"tmhm_learnset":"00A03E768FD33630","types":[4,5]},{"abilities":[31,69],"address":3299880,"base_stats":[105,130,120,40,45,45],"catch_rate":60,"evolutions":[],"friendship":70,"id":112,"learnset":{"address":3310998,"moves":[{"level":1,"move_id":30},{"level":1,"move_id":39},{"level":1,"move_id":23},{"level":1,"move_id":31},{"level":10,"move_id":23},{"level":15,"move_id":31},{"level":24,"move_id":184},{"level":29,"move_id":350},{"level":38,"move_id":32},{"level":46,"move_id":36},{"level":58,"move_id":89},{"level":66,"move_id":224}]},"tmhm_learnset":"00B43E76CFD37631","types":[4,5]},{"abilities":[30,32],"address":3299908,"base_stats":[250,5,5,50,35,105],"catch_rate":30,"evolutions":[{"method":"FRIENDSHIP","param":0,"species":242}],"friendship":140,"id":113,"learnset":{"address":3311024,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":45},{"level":5,"move_id":39},{"level":9,"move_id":287},{"level":13,"move_id":135},{"level":17,"move_id":3},{"level":23,"move_id":107},{"level":29,"move_id":47},{"level":35,"move_id":121},{"level":41,"move_id":111},{"level":49,"move_id":113},{"level":57,"move_id":38}]},"tmhm_learnset":"00E19E76F7FBF66D","types":[0,0]},{"abilities":[34,0],"address":3299936,"base_stats":[65,55,115,60,100,40],"catch_rate":45,"evolutions":[],"friendship":70,"id":114,"learnset":{"address":3311054,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":275},{"level":1,"move_id":132},{"level":4,"move_id":79},{"level":10,"move_id":71},{"level":13,"move_id":74},{"level":19,"move_id":77},{"level":22,"move_id":22},{"level":28,"move_id":20},{"level":31,"move_id":72},{"level":37,"move_id":78},{"level":40,"move_id":21},{"level":46,"move_id":321}]},"tmhm_learnset":"00C43E0884354720","types":[12,12]},{"abilities":[48,0],"address":3299964,"base_stats":[105,95,80,90,40,80],"catch_rate":45,"evolutions":[],"friendship":70,"id":115,"learnset":{"address":3311084,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":4},{"level":1,"move_id":43},{"level":7,"move_id":44},{"level":13,"move_id":39},{"level":19,"move_id":252},{"level":25,"move_id":5},{"level":31,"move_id":99},{"level":37,"move_id":203},{"level":43,"move_id":146},{"level":49,"move_id":179}]},"tmhm_learnset":"00B43EF6EFF37675","types":[0,0]},{"abilities":[33,0],"address":3299992,"base_stats":[30,40,70,60,70,25],"catch_rate":225,"evolutions":[{"method":"LEVEL","param":32,"species":117}],"friendship":70,"id":116,"learnset":{"address":3311110,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":8,"move_id":108},{"level":15,"move_id":43},{"level":22,"move_id":55},{"level":29,"move_id":239},{"level":36,"move_id":97},{"level":43,"move_id":56},{"level":50,"move_id":349}]},"tmhm_learnset":"03101E0084133264","types":[11,11]},{"abilities":[38,0],"address":3300020,"base_stats":[55,65,95,85,95,45],"catch_rate":75,"evolutions":[{"method":"ITEM","param":201,"species":230}],"friendship":70,"id":117,"learnset":{"address":3311134,"moves":[{"level":1,"move_id":145},{"level":1,"move_id":108},{"level":1,"move_id":43},{"level":1,"move_id":55},{"level":8,"move_id":108},{"level":15,"move_id":43},{"level":22,"move_id":55},{"level":29,"move_id":239},{"level":40,"move_id":97},{"level":51,"move_id":56},{"level":62,"move_id":349}]},"tmhm_learnset":"03101E0084137264","types":[11,11]},{"abilities":[33,41],"address":3300048,"base_stats":[45,67,60,63,35,50],"catch_rate":225,"evolutions":[{"method":"LEVEL","param":33,"species":119}],"friendship":70,"id":118,"learnset":{"address":3311158,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":1,"move_id":39},{"level":1,"move_id":346},{"level":10,"move_id":48},{"level":15,"move_id":30},{"level":24,"move_id":175},{"level":29,"move_id":31},{"level":38,"move_id":127},{"level":43,"move_id":32},{"level":52,"move_id":97}]},"tmhm_learnset":"03101E0084133264","types":[11,11]},{"abilities":[33,41],"address":3300076,"base_stats":[80,92,65,68,65,80],"catch_rate":60,"evolutions":[],"friendship":70,"id":119,"learnset":{"address":3311182,"moves":[{"level":1,"move_id":64},{"level":1,"move_id":39},{"level":1,"move_id":346},{"level":1,"move_id":48},{"level":10,"move_id":48},{"level":15,"move_id":30},{"level":24,"move_id":175},{"level":29,"move_id":31},{"level":41,"move_id":127},{"level":49,"move_id":32},{"level":61,"move_id":97}]},"tmhm_learnset":"03101E0084137264","types":[11,11]},{"abilities":[35,30],"address":3300104,"base_stats":[30,45,55,85,70,55],"catch_rate":225,"evolutions":[{"method":"ITEM","param":97,"species":121}],"friendship":70,"id":120,"learnset":{"address":3311206,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":6,"move_id":55},{"level":10,"move_id":229},{"level":15,"move_id":105},{"level":19,"move_id":293},{"level":24,"move_id":129},{"level":28,"move_id":61},{"level":33,"move_id":107},{"level":37,"move_id":113},{"level":42,"move_id":322},{"level":46,"move_id":56}]},"tmhm_learnset":"03500E019593B264","types":[11,11]},{"abilities":[35,30],"address":3300132,"base_stats":[60,75,85,115,100,85],"catch_rate":60,"evolutions":[],"friendship":70,"id":121,"learnset":{"address":3311236,"moves":[{"level":1,"move_id":55},{"level":1,"move_id":229},{"level":1,"move_id":105},{"level":1,"move_id":129},{"level":33,"move_id":109}]},"tmhm_learnset":"03508E019593F264","types":[11,14]},{"abilities":[43,0],"address":3300160,"base_stats":[40,45,65,90,100,120],"catch_rate":45,"evolutions":[],"friendship":70,"id":122,"learnset":{"address":3311248,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":112},{"level":5,"move_id":93},{"level":9,"move_id":164},{"level":13,"move_id":96},{"level":17,"move_id":3},{"level":21,"move_id":113},{"level":21,"move_id":115},{"level":25,"move_id":227},{"level":29,"move_id":60},{"level":33,"move_id":278},{"level":37,"move_id":271},{"level":41,"move_id":272},{"level":45,"move_id":94},{"level":49,"move_id":226},{"level":53,"move_id":219}]},"tmhm_learnset":"0041BF03F5BBCE29","types":[14,14]},{"abilities":[68,0],"address":3300188,"base_stats":[70,110,80,105,55,80],"catch_rate":45,"evolutions":[{"method":"ITEM","param":199,"species":212}],"friendship":70,"id":123,"learnset":{"address":3311286,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":98},{"level":1,"move_id":43},{"level":6,"move_id":116},{"level":11,"move_id":228},{"level":16,"move_id":206},{"level":21,"move_id":97},{"level":26,"move_id":17},{"level":31,"move_id":163},{"level":36,"move_id":14},{"level":41,"move_id":104},{"level":46,"move_id":210}]},"tmhm_learnset":"00847E8084134620","types":[6,2]},{"abilities":[12,0],"address":3300216,"base_stats":[65,50,35,95,115,95],"catch_rate":45,"evolutions":[],"friendship":70,"id":124,"learnset":{"address":3311314,"moves":[{"level":1,"move_id":1},{"level":1,"move_id":122},{"level":1,"move_id":142},{"level":1,"move_id":181},{"level":9,"move_id":142},{"level":13,"move_id":181},{"level":21,"move_id":3},{"level":25,"move_id":8},{"level":35,"move_id":212},{"level":41,"move_id":313},{"level":51,"move_id":34},{"level":57,"move_id":195},{"level":67,"move_id":59}]},"tmhm_learnset":"0040BF01F413FA6D","types":[15,14]},{"abilities":[9,0],"address":3300244,"base_stats":[65,83,57,105,95,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":125,"learnset":{"address":3311342,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":98},{"level":1,"move_id":43},{"level":1,"move_id":9},{"level":9,"move_id":9},{"level":17,"move_id":113},{"level":25,"move_id":129},{"level":36,"move_id":103},{"level":47,"move_id":85},{"level":58,"move_id":87}]},"tmhm_learnset":"00E03E02D5D3C221","types":[13,13]},{"abilities":[49,0],"address":3300272,"base_stats":[65,95,57,93,100,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":126,"learnset":{"address":3311364,"moves":[{"level":1,"move_id":52},{"level":1,"move_id":43},{"level":1,"move_id":123},{"level":1,"move_id":7},{"level":7,"move_id":43},{"level":13,"move_id":123},{"level":19,"move_id":7},{"level":25,"move_id":108},{"level":33,"move_id":241},{"level":41,"move_id":53},{"level":49,"move_id":109},{"level":57,"move_id":126}]},"tmhm_learnset":"00A03E24D4514621","types":[10,10]},{"abilities":[52,0],"address":3300300,"base_stats":[65,125,100,85,55,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":127,"learnset":{"address":3311390,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":11},{"level":1,"move_id":116},{"level":7,"move_id":20},{"level":13,"move_id":69},{"level":19,"move_id":106},{"level":25,"move_id":279},{"level":31,"move_id":280},{"level":37,"move_id":12},{"level":43,"move_id":66},{"level":49,"move_id":14}]},"tmhm_learnset":"00A43E40CE1346A1","types":[6,6]},{"abilities":[22,0],"address":3300328,"base_stats":[75,100,95,110,40,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":128,"learnset":{"address":3311416,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":4,"move_id":39},{"level":8,"move_id":99},{"level":13,"move_id":30},{"level":19,"move_id":184},{"level":26,"move_id":228},{"level":34,"move_id":156},{"level":43,"move_id":37},{"level":53,"move_id":36}]},"tmhm_learnset":"00B01E7687F37624","types":[0,0]},{"abilities":[33,0],"address":3300356,"base_stats":[20,10,55,80,15,20],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":20,"species":130}],"friendship":70,"id":129,"learnset":{"address":3311442,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":150},{"level":15,"move_id":33},{"level":30,"move_id":175}]},"tmhm_learnset":"0000000000000000","types":[11,11]},{"abilities":[22,0],"address":3300384,"base_stats":[95,125,79,81,60,100],"catch_rate":45,"evolutions":[],"friendship":70,"id":130,"learnset":{"address":3311456,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":37},{"level":20,"move_id":44},{"level":25,"move_id":82},{"level":30,"move_id":43},{"level":35,"move_id":239},{"level":40,"move_id":56},{"level":45,"move_id":240},{"level":50,"move_id":349},{"level":55,"move_id":63}]},"tmhm_learnset":"03B01F3487937A74","types":[11,2]},{"abilities":[11,75],"address":3300412,"base_stats":[130,85,80,60,85,95],"catch_rate":45,"evolutions":[],"friendship":70,"id":131,"learnset":{"address":3311482,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":55},{"level":1,"move_id":45},{"level":1,"move_id":47},{"level":7,"move_id":54},{"level":13,"move_id":34},{"level":19,"move_id":109},{"level":25,"move_id":195},{"level":31,"move_id":58},{"level":37,"move_id":240},{"level":43,"move_id":219},{"level":49,"move_id":56},{"level":55,"move_id":329}]},"tmhm_learnset":"03B01E0295DB7274","types":[11,15]},{"abilities":[7,0],"address":3300440,"base_stats":[48,48,48,48,48,48],"catch_rate":35,"evolutions":[],"friendship":70,"id":132,"learnset":{"address":3311510,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":144}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[50,0],"address":3300468,"base_stats":[55,55,50,55,45,65],"catch_rate":45,"evolutions":[{"method":"ITEM","param":96,"species":135},{"method":"ITEM","param":97,"species":134},{"method":"ITEM","param":95,"species":136},{"method":"FRIENDSHIP_DAY","param":0,"species":196},{"method":"FRIENDSHIP_NIGHT","param":0,"species":197}],"friendship":70,"id":133,"learnset":{"address":3311520,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":270},{"level":8,"move_id":28},{"level":16,"move_id":45},{"level":23,"move_id":98},{"level":30,"move_id":44},{"level":36,"move_id":226},{"level":42,"move_id":36}]},"tmhm_learnset":"00001E00AC530620","types":[0,0]},{"abilities":[11,0],"address":3300496,"base_stats":[130,65,60,65,110,95],"catch_rate":45,"evolutions":[],"friendship":70,"id":134,"learnset":{"address":3311542,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":270},{"level":8,"move_id":28},{"level":16,"move_id":55},{"level":23,"move_id":98},{"level":30,"move_id":44},{"level":36,"move_id":62},{"level":42,"move_id":114},{"level":47,"move_id":151},{"level":52,"move_id":56}]},"tmhm_learnset":"03101E00AC537674","types":[11,11]},{"abilities":[10,0],"address":3300524,"base_stats":[65,65,60,130,110,95],"catch_rate":45,"evolutions":[],"friendship":70,"id":135,"learnset":{"address":3311568,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":270},{"level":8,"move_id":28},{"level":16,"move_id":84},{"level":23,"move_id":98},{"level":30,"move_id":24},{"level":36,"move_id":42},{"level":42,"move_id":86},{"level":47,"move_id":97},{"level":52,"move_id":87}]},"tmhm_learnset":"00401E02ADD34630","types":[13,13]},{"abilities":[18,0],"address":3300552,"base_stats":[65,130,60,65,95,110],"catch_rate":45,"evolutions":[],"friendship":70,"id":136,"learnset":{"address":3311594,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":270},{"level":8,"move_id":28},{"level":16,"move_id":52},{"level":23,"move_id":98},{"level":30,"move_id":44},{"level":36,"move_id":83},{"level":42,"move_id":123},{"level":47,"move_id":43},{"level":52,"move_id":53}]},"tmhm_learnset":"00021E24AC534630","types":[10,10]},{"abilities":[36,0],"address":3300580,"base_stats":[65,60,70,40,85,75],"catch_rate":45,"evolutions":[{"method":"ITEM","param":218,"species":233}],"friendship":70,"id":137,"learnset":{"address":3311620,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":176},{"level":1,"move_id":33},{"level":1,"move_id":160},{"level":9,"move_id":97},{"level":12,"move_id":60},{"level":20,"move_id":105},{"level":24,"move_id":159},{"level":32,"move_id":199},{"level":36,"move_id":161},{"level":44,"move_id":278},{"level":48,"move_id":192}]},"tmhm_learnset":"00402E82B5F37620","types":[0,0]},{"abilities":[33,75],"address":3300608,"base_stats":[35,40,100,35,90,55],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":40,"species":139}],"friendship":70,"id":138,"learnset":{"address":3311646,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":132},{"level":1,"move_id":110},{"level":13,"move_id":44},{"level":19,"move_id":55},{"level":25,"move_id":341},{"level":31,"move_id":43},{"level":37,"move_id":182},{"level":43,"move_id":321},{"level":49,"move_id":246},{"level":55,"move_id":56}]},"tmhm_learnset":"03903E5084133264","types":[5,11]},{"abilities":[33,75],"address":3300636,"base_stats":[70,60,125,55,115,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":139,"learnset":{"address":3311672,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":132},{"level":1,"move_id":110},{"level":1,"move_id":44},{"level":13,"move_id":44},{"level":19,"move_id":55},{"level":25,"move_id":341},{"level":31,"move_id":43},{"level":37,"move_id":182},{"level":40,"move_id":131},{"level":46,"move_id":321},{"level":55,"move_id":246},{"level":65,"move_id":56}]},"tmhm_learnset":"03903E5084137264","types":[5,11]},{"abilities":[33,4],"address":3300664,"base_stats":[30,80,90,55,55,45],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":40,"species":141}],"friendship":70,"id":140,"learnset":{"address":3311700,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":106},{"level":13,"move_id":71},{"level":19,"move_id":43},{"level":25,"move_id":341},{"level":31,"move_id":28},{"level":37,"move_id":203},{"level":43,"move_id":319},{"level":49,"move_id":72},{"level":55,"move_id":246}]},"tmhm_learnset":"01903ED08C173264","types":[5,11]},{"abilities":[33,4],"address":3300692,"base_stats":[60,115,105,80,65,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":141,"learnset":{"address":3311726,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":106},{"level":1,"move_id":71},{"level":13,"move_id":71},{"level":19,"move_id":43},{"level":25,"move_id":341},{"level":31,"move_id":28},{"level":37,"move_id":203},{"level":40,"move_id":163},{"level":46,"move_id":319},{"level":55,"move_id":72},{"level":65,"move_id":246}]},"tmhm_learnset":"03943ED0CC177264","types":[5,11]},{"abilities":[69,46],"address":3300720,"base_stats":[80,105,65,130,60,75],"catch_rate":45,"evolutions":[],"friendship":70,"id":142,"learnset":{"address":3311754,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":17},{"level":8,"move_id":97},{"level":15,"move_id":44},{"level":22,"move_id":48},{"level":29,"move_id":246},{"level":36,"move_id":184},{"level":43,"move_id":36},{"level":50,"move_id":63}]},"tmhm_learnset":"00A87FF486534E32","types":[5,2]},{"abilities":[17,47],"address":3300748,"base_stats":[160,110,65,30,65,110],"catch_rate":25,"evolutions":[],"friendship":70,"id":143,"learnset":{"address":3311778,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":6,"move_id":133},{"level":10,"move_id":111},{"level":15,"move_id":187},{"level":19,"move_id":29},{"level":24,"move_id":281},{"level":28,"move_id":156},{"level":28,"move_id":173},{"level":33,"move_id":34},{"level":37,"move_id":335},{"level":42,"move_id":343},{"level":46,"move_id":205},{"level":51,"move_id":63}]},"tmhm_learnset":"00301E76F7B37625","types":[0,0]},{"abilities":[46,0],"address":3300776,"base_stats":[90,85,100,85,95,125],"catch_rate":3,"evolutions":[],"friendship":35,"id":144,"learnset":{"address":3311812,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":16},{"level":1,"move_id":181},{"level":13,"move_id":54},{"level":25,"move_id":97},{"level":37,"move_id":170},{"level":49,"move_id":58},{"level":61,"move_id":115},{"level":73,"move_id":59},{"level":85,"move_id":329}]},"tmhm_learnset":"00884E9184137674","types":[15,2]},{"abilities":[46,0],"address":3300804,"base_stats":[90,90,85,100,125,90],"catch_rate":3,"evolutions":[],"friendship":35,"id":145,"learnset":{"address":3311836,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":1,"move_id":84},{"level":13,"move_id":86},{"level":25,"move_id":97},{"level":37,"move_id":197},{"level":49,"move_id":65},{"level":61,"move_id":268},{"level":73,"move_id":113},{"level":85,"move_id":87}]},"tmhm_learnset":"00C84E928593C630","types":[13,2]},{"abilities":[46,0],"address":3300832,"base_stats":[90,100,90,90,125,85],"catch_rate":3,"evolutions":[],"friendship":35,"id":146,"learnset":{"address":3311860,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":17},{"level":1,"move_id":52},{"level":13,"move_id":83},{"level":25,"move_id":97},{"level":37,"move_id":203},{"level":49,"move_id":53},{"level":61,"move_id":219},{"level":73,"move_id":257},{"level":85,"move_id":143}]},"tmhm_learnset":"008A4EB4841B4630","types":[10,2]},{"abilities":[61,0],"address":3300860,"base_stats":[41,64,45,50,50,50],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":30,"species":148}],"friendship":35,"id":147,"learnset":{"address":3311884,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":35},{"level":1,"move_id":43},{"level":8,"move_id":86},{"level":15,"move_id":239},{"level":22,"move_id":82},{"level":29,"move_id":21},{"level":36,"move_id":97},{"level":43,"move_id":219},{"level":50,"move_id":200},{"level":57,"move_id":63}]},"tmhm_learnset":"01101E2685DB7664","types":[16,16]},{"abilities":[61,0],"address":3300888,"base_stats":[61,84,65,70,70,70],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":55,"species":149}],"friendship":35,"id":148,"learnset":{"address":3311910,"moves":[{"level":1,"move_id":35},{"level":1,"move_id":43},{"level":1,"move_id":86},{"level":1,"move_id":239},{"level":8,"move_id":86},{"level":15,"move_id":239},{"level":22,"move_id":82},{"level":29,"move_id":21},{"level":38,"move_id":97},{"level":47,"move_id":219},{"level":56,"move_id":200},{"level":65,"move_id":63}]},"tmhm_learnset":"01101E2685DB7664","types":[16,16]},{"abilities":[39,0],"address":3300916,"base_stats":[91,134,95,80,100,100],"catch_rate":45,"evolutions":[],"friendship":35,"id":149,"learnset":{"address":3311936,"moves":[{"level":1,"move_id":35},{"level":1,"move_id":43},{"level":1,"move_id":86},{"level":1,"move_id":239},{"level":8,"move_id":86},{"level":15,"move_id":239},{"level":22,"move_id":82},{"level":29,"move_id":21},{"level":38,"move_id":97},{"level":47,"move_id":219},{"level":55,"move_id":17},{"level":61,"move_id":200},{"level":75,"move_id":63}]},"tmhm_learnset":"03BC5EF6C7DB7677","types":[16,2]},{"abilities":[46,0],"address":3300944,"base_stats":[106,110,90,130,154,90],"catch_rate":3,"evolutions":[],"friendship":0,"id":150,"learnset":{"address":3311964,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":93},{"level":1,"move_id":50},{"level":11,"move_id":112},{"level":22,"move_id":129},{"level":33,"move_id":244},{"level":44,"move_id":248},{"level":55,"move_id":54},{"level":66,"move_id":94},{"level":77,"move_id":133},{"level":88,"move_id":105},{"level":99,"move_id":219}]},"tmhm_learnset":"00E18FF7F7FBFEED","types":[14,14]},{"abilities":[28,0],"address":3300972,"base_stats":[100,100,100,100,100,100],"catch_rate":45,"evolutions":[],"friendship":100,"id":151,"learnset":{"address":3311992,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":10,"move_id":144},{"level":20,"move_id":5},{"level":30,"move_id":118},{"level":40,"move_id":94},{"level":50,"move_id":246}]},"tmhm_learnset":"03FFFFFFFFFFFFFF","types":[14,14]},{"abilities":[65,0],"address":3301000,"base_stats":[45,49,65,45,49,65],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":16,"species":153}],"friendship":70,"id":152,"learnset":{"address":3312012,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":8,"move_id":75},{"level":12,"move_id":115},{"level":15,"move_id":77},{"level":22,"move_id":235},{"level":29,"move_id":34},{"level":36,"move_id":113},{"level":43,"move_id":219},{"level":50,"move_id":76}]},"tmhm_learnset":"00441E01847D8720","types":[12,12]},{"abilities":[65,0],"address":3301028,"base_stats":[60,62,80,60,63,80],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":32,"species":154}],"friendship":70,"id":153,"learnset":{"address":3312038,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":75},{"level":1,"move_id":115},{"level":8,"move_id":75},{"level":12,"move_id":115},{"level":15,"move_id":77},{"level":23,"move_id":235},{"level":31,"move_id":34},{"level":39,"move_id":113},{"level":47,"move_id":219},{"level":55,"move_id":76}]},"tmhm_learnset":"00E41E01847D8720","types":[12,12]},{"abilities":[65,0],"address":3301056,"base_stats":[80,82,100,80,83,100],"catch_rate":45,"evolutions":[],"friendship":70,"id":154,"learnset":{"address":3312064,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":75},{"level":1,"move_id":115},{"level":8,"move_id":75},{"level":12,"move_id":115},{"level":15,"move_id":77},{"level":23,"move_id":235},{"level":31,"move_id":34},{"level":41,"move_id":113},{"level":51,"move_id":219},{"level":61,"move_id":76}]},"tmhm_learnset":"00E41E01867DC720","types":[12,12]},{"abilities":[66,0],"address":3301084,"base_stats":[39,52,43,65,60,50],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":14,"species":156}],"friendship":70,"id":155,"learnset":{"address":3312090,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":43},{"level":6,"move_id":108},{"level":12,"move_id":52},{"level":19,"move_id":98},{"level":27,"move_id":172},{"level":36,"move_id":129},{"level":46,"move_id":53}]},"tmhm_learnset":"00061EA48C110620","types":[10,10]},{"abilities":[66,0],"address":3301112,"base_stats":[58,64,58,80,80,65],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":36,"species":157}],"friendship":70,"id":156,"learnset":{"address":3312112,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":43},{"level":1,"move_id":108},{"level":6,"move_id":108},{"level":12,"move_id":52},{"level":21,"move_id":98},{"level":31,"move_id":172},{"level":42,"move_id":129},{"level":54,"move_id":53}]},"tmhm_learnset":"00A61EA4CC110631","types":[10,10]},{"abilities":[66,0],"address":3301140,"base_stats":[78,84,78,100,109,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":157,"learnset":{"address":3312134,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":43},{"level":1,"move_id":108},{"level":1,"move_id":52},{"level":6,"move_id":108},{"level":12,"move_id":52},{"level":21,"move_id":98},{"level":31,"move_id":172},{"level":45,"move_id":129},{"level":60,"move_id":53}]},"tmhm_learnset":"00A61EA4CE114631","types":[10,10]},{"abilities":[67,0],"address":3301168,"base_stats":[50,65,64,43,44,48],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":18,"species":159}],"friendship":70,"id":158,"learnset":{"address":3312156,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":43},{"level":7,"move_id":99},{"level":13,"move_id":55},{"level":20,"move_id":44},{"level":27,"move_id":184},{"level":35,"move_id":163},{"level":43,"move_id":103},{"level":52,"move_id":56}]},"tmhm_learnset":"03141E80CC533265","types":[11,11]},{"abilities":[67,0],"address":3301196,"base_stats":[65,80,80,58,59,63],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":30,"species":160}],"friendship":70,"id":159,"learnset":{"address":3312180,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":43},{"level":1,"move_id":99},{"level":7,"move_id":99},{"level":13,"move_id":55},{"level":21,"move_id":44},{"level":28,"move_id":184},{"level":37,"move_id":163},{"level":45,"move_id":103},{"level":55,"move_id":56}]},"tmhm_learnset":"03B41E80CC533275","types":[11,11]},{"abilities":[67,0],"address":3301224,"base_stats":[85,105,100,78,79,83],"catch_rate":45,"evolutions":[],"friendship":70,"id":160,"learnset":{"address":3312204,"moves":[{"level":1,"move_id":10},{"level":1,"move_id":43},{"level":1,"move_id":99},{"level":1,"move_id":55},{"level":7,"move_id":99},{"level":13,"move_id":55},{"level":21,"move_id":44},{"level":28,"move_id":184},{"level":38,"move_id":163},{"level":47,"move_id":103},{"level":58,"move_id":56}]},"tmhm_learnset":"03B41E80CE537277","types":[11,11]},{"abilities":[50,51],"address":3301252,"base_stats":[35,46,34,20,35,45],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":15,"species":162}],"friendship":70,"id":161,"learnset":{"address":3312228,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":4,"move_id":111},{"level":7,"move_id":98},{"level":12,"move_id":154},{"level":17,"move_id":270},{"level":24,"move_id":21},{"level":31,"move_id":266},{"level":40,"move_id":156},{"level":49,"move_id":133}]},"tmhm_learnset":"00143E06ECF31625","types":[0,0]},{"abilities":[50,51],"address":3301280,"base_stats":[85,76,64,90,45,55],"catch_rate":90,"evolutions":[],"friendship":70,"id":162,"learnset":{"address":3312254,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":111},{"level":1,"move_id":98},{"level":4,"move_id":111},{"level":7,"move_id":98},{"level":12,"move_id":154},{"level":19,"move_id":270},{"level":28,"move_id":21},{"level":37,"move_id":266},{"level":48,"move_id":156},{"level":59,"move_id":133}]},"tmhm_learnset":"00B43E06EDF37625","types":[0,0]},{"abilities":[15,51],"address":3301308,"base_stats":[60,30,30,50,36,56],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":20,"species":164}],"friendship":70,"id":163,"learnset":{"address":3312280,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":6,"move_id":193},{"level":11,"move_id":64},{"level":16,"move_id":95},{"level":22,"move_id":115},{"level":28,"move_id":36},{"level":34,"move_id":93},{"level":48,"move_id":138}]},"tmhm_learnset":"00487E81B4130620","types":[0,2]},{"abilities":[15,51],"address":3301336,"base_stats":[100,50,50,70,76,96],"catch_rate":90,"evolutions":[],"friendship":70,"id":164,"learnset":{"address":3312304,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":193},{"level":1,"move_id":64},{"level":6,"move_id":193},{"level":11,"move_id":64},{"level":16,"move_id":95},{"level":25,"move_id":115},{"level":33,"move_id":36},{"level":41,"move_id":93},{"level":57,"move_id":138}]},"tmhm_learnset":"00487E81B4134620","types":[0,2]},{"abilities":[68,48],"address":3301364,"base_stats":[40,20,30,55,40,80],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":18,"species":166}],"friendship":70,"id":165,"learnset":{"address":3312328,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":8,"move_id":48},{"level":15,"move_id":4},{"level":22,"move_id":113},{"level":22,"move_id":115},{"level":22,"move_id":219},{"level":29,"move_id":226},{"level":36,"move_id":129},{"level":43,"move_id":97},{"level":50,"move_id":38}]},"tmhm_learnset":"00403E81CC3D8621","types":[6,2]},{"abilities":[68,48],"address":3301392,"base_stats":[55,35,50,85,55,110],"catch_rate":90,"evolutions":[],"friendship":70,"id":166,"learnset":{"address":3312356,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":48},{"level":8,"move_id":48},{"level":15,"move_id":4},{"level":24,"move_id":113},{"level":24,"move_id":115},{"level":24,"move_id":219},{"level":33,"move_id":226},{"level":42,"move_id":129},{"level":51,"move_id":97},{"level":60,"move_id":38}]},"tmhm_learnset":"00403E81CC3DC621","types":[6,2]},{"abilities":[68,15],"address":3301420,"base_stats":[40,60,40,30,40,40],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":22,"species":168}],"friendship":70,"id":167,"learnset":{"address":3312384,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":40},{"level":1,"move_id":81},{"level":6,"move_id":184},{"level":11,"move_id":132},{"level":17,"move_id":101},{"level":23,"move_id":141},{"level":30,"move_id":154},{"level":37,"move_id":169},{"level":45,"move_id":97},{"level":53,"move_id":94}]},"tmhm_learnset":"00403E089C350620","types":[6,3]},{"abilities":[68,15],"address":3301448,"base_stats":[70,90,70,40,60,60],"catch_rate":90,"evolutions":[],"friendship":70,"id":168,"learnset":{"address":3312410,"moves":[{"level":1,"move_id":40},{"level":1,"move_id":81},{"level":1,"move_id":184},{"level":1,"move_id":132},{"level":6,"move_id":184},{"level":11,"move_id":132},{"level":17,"move_id":101},{"level":25,"move_id":141},{"level":34,"move_id":154},{"level":43,"move_id":169},{"level":53,"move_id":97},{"level":63,"move_id":94}]},"tmhm_learnset":"00403E089C354620","types":[6,3]},{"abilities":[39,0],"address":3301476,"base_stats":[85,90,80,130,70,80],"catch_rate":90,"evolutions":[],"friendship":70,"id":169,"learnset":{"address":3312436,"moves":[{"level":1,"move_id":103},{"level":1,"move_id":141},{"level":1,"move_id":48},{"level":1,"move_id":310},{"level":6,"move_id":48},{"level":11,"move_id":310},{"level":16,"move_id":44},{"level":21,"move_id":17},{"level":28,"move_id":109},{"level":35,"move_id":314},{"level":42,"move_id":212},{"level":49,"move_id":305},{"level":56,"move_id":114}]},"tmhm_learnset":"00097F88A4174E20","types":[3,2]},{"abilities":[10,35],"address":3301504,"base_stats":[75,38,38,67,56,56],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":27,"species":171}],"friendship":70,"id":170,"learnset":{"address":3312464,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":1,"move_id":86},{"level":5,"move_id":48},{"level":13,"move_id":175},{"level":17,"move_id":55},{"level":25,"move_id":209},{"level":29,"move_id":109},{"level":37,"move_id":36},{"level":41,"move_id":56},{"level":49,"move_id":268}]},"tmhm_learnset":"03501E0285933264","types":[11,13]},{"abilities":[10,35],"address":3301532,"base_stats":[125,58,58,67,76,76],"catch_rate":75,"evolutions":[],"friendship":70,"id":171,"learnset":{"address":3312490,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":1,"move_id":86},{"level":1,"move_id":48},{"level":5,"move_id":48},{"level":13,"move_id":175},{"level":17,"move_id":55},{"level":25,"move_id":209},{"level":32,"move_id":109},{"level":43,"move_id":36},{"level":50,"move_id":56},{"level":61,"move_id":268}]},"tmhm_learnset":"03501E0285937264","types":[11,13]},{"abilities":[9,0],"address":3301560,"base_stats":[20,40,15,60,35,35],"catch_rate":190,"evolutions":[{"method":"FRIENDSHIP","param":0,"species":25}],"friendship":70,"id":172,"learnset":{"address":3312516,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":84},{"level":1,"move_id":204},{"level":6,"move_id":39},{"level":8,"move_id":86},{"level":11,"move_id":186}]},"tmhm_learnset":"00401E0285D38220","types":[13,13]},{"abilities":[56,0],"address":3301588,"base_stats":[50,25,28,15,45,55],"catch_rate":150,"evolutions":[{"method":"FRIENDSHIP","param":0,"species":35}],"friendship":140,"id":173,"learnset":{"address":3312532,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":204},{"level":4,"move_id":227},{"level":8,"move_id":47},{"level":13,"move_id":186}]},"tmhm_learnset":"00401E27BC7B8624","types":[0,0]},{"abilities":[56,0],"address":3301616,"base_stats":[90,30,15,15,40,20],"catch_rate":170,"evolutions":[{"method":"FRIENDSHIP","param":0,"species":39}],"friendship":70,"id":174,"learnset":{"address":3312548,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":47},{"level":1,"move_id":204},{"level":4,"move_id":111},{"level":9,"move_id":1},{"level":14,"move_id":186}]},"tmhm_learnset":"00401E27BC3B8624","types":[0,0]},{"abilities":[55,32],"address":3301644,"base_stats":[35,20,65,20,40,65],"catch_rate":190,"evolutions":[{"method":"FRIENDSHIP","param":0,"species":176}],"friendship":70,"id":175,"learnset":{"address":3312564,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":118},{"level":1,"move_id":45},{"level":1,"move_id":204},{"level":6,"move_id":118},{"level":11,"move_id":186},{"level":16,"move_id":281},{"level":21,"move_id":227},{"level":26,"move_id":266},{"level":31,"move_id":273},{"level":36,"move_id":219},{"level":41,"move_id":38}]},"tmhm_learnset":"00C01E27B43B8624","types":[0,0]},{"abilities":[55,32],"address":3301672,"base_stats":[55,40,85,40,80,105],"catch_rate":75,"evolutions":[],"friendship":70,"id":176,"learnset":{"address":3312590,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":118},{"level":1,"move_id":45},{"level":1,"move_id":204},{"level":6,"move_id":118},{"level":11,"move_id":186},{"level":16,"move_id":281},{"level":21,"move_id":227},{"level":26,"move_id":266},{"level":31,"move_id":273},{"level":36,"move_id":219},{"level":41,"move_id":38}]},"tmhm_learnset":"00C85EA7F43BC625","types":[0,2]},{"abilities":[28,48],"address":3301700,"base_stats":[40,50,45,70,70,45],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":25,"species":178}],"friendship":70,"id":177,"learnset":{"address":3312616,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":1,"move_id":43},{"level":10,"move_id":101},{"level":20,"move_id":100},{"level":30,"move_id":273},{"level":30,"move_id":248},{"level":40,"move_id":109},{"level":50,"move_id":94}]},"tmhm_learnset":"0040FE81B4378628","types":[14,2]},{"abilities":[28,48],"address":3301728,"base_stats":[65,75,70,95,95,70],"catch_rate":75,"evolutions":[],"friendship":70,"id":178,"learnset":{"address":3312638,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":1,"move_id":43},{"level":10,"move_id":101},{"level":20,"move_id":100},{"level":35,"move_id":273},{"level":35,"move_id":248},{"level":50,"move_id":109},{"level":65,"move_id":94}]},"tmhm_learnset":"0048FE81B437C628","types":[14,2]},{"abilities":[9,0],"address":3301756,"base_stats":[55,40,40,35,65,45],"catch_rate":235,"evolutions":[{"method":"LEVEL","param":15,"species":180}],"friendship":70,"id":179,"learnset":{"address":3312660,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":9,"move_id":84},{"level":16,"move_id":86},{"level":23,"move_id":178},{"level":30,"move_id":113},{"level":37,"move_id":87}]},"tmhm_learnset":"00401E0285D38220","types":[13,13]},{"abilities":[9,0],"address":3301784,"base_stats":[70,55,55,45,80,60],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":30,"species":181}],"friendship":70,"id":180,"learnset":{"address":3312680,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":84},{"level":9,"move_id":84},{"level":18,"move_id":86},{"level":27,"move_id":178},{"level":36,"move_id":113},{"level":45,"move_id":87}]},"tmhm_learnset":"00E01E02C5D38221","types":[13,13]},{"abilities":[9,0],"address":3301812,"base_stats":[90,75,75,55,115,90],"catch_rate":45,"evolutions":[],"friendship":70,"id":181,"learnset":{"address":3312700,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":84},{"level":1,"move_id":86},{"level":9,"move_id":84},{"level":18,"move_id":86},{"level":27,"move_id":178},{"level":30,"move_id":9},{"level":42,"move_id":113},{"level":57,"move_id":87}]},"tmhm_learnset":"00E01E02C5D3C221","types":[13,13]},{"abilities":[34,0],"address":3301840,"base_stats":[75,80,85,50,90,100],"catch_rate":45,"evolutions":[],"friendship":70,"id":182,"learnset":{"address":3312722,"moves":[{"level":1,"move_id":71},{"level":1,"move_id":230},{"level":1,"move_id":78},{"level":1,"move_id":345},{"level":44,"move_id":80},{"level":55,"move_id":76}]},"tmhm_learnset":"00441E08843D4720","types":[12,12]},{"abilities":[47,37],"address":3301868,"base_stats":[70,20,50,40,20,50],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":18,"species":184}],"friendship":70,"id":183,"learnset":{"address":3312736,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":3,"move_id":111},{"level":6,"move_id":39},{"level":10,"move_id":55},{"level":15,"move_id":205},{"level":21,"move_id":61},{"level":28,"move_id":38},{"level":36,"move_id":240},{"level":45,"move_id":56}]},"tmhm_learnset":"03B01E00CC533265","types":[11,11]},{"abilities":[47,37],"address":3301896,"base_stats":[100,50,80,50,50,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":184,"learnset":{"address":3312762,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":111},{"level":1,"move_id":39},{"level":1,"move_id":55},{"level":3,"move_id":111},{"level":6,"move_id":39},{"level":10,"move_id":55},{"level":15,"move_id":205},{"level":24,"move_id":61},{"level":34,"move_id":38},{"level":45,"move_id":240},{"level":57,"move_id":56}]},"tmhm_learnset":"03B01E00CC537265","types":[11,11]},{"abilities":[5,69],"address":3301924,"base_stats":[70,100,115,30,30,65],"catch_rate":65,"evolutions":[],"friendship":70,"id":185,"learnset":{"address":3312788,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":88},{"level":1,"move_id":102},{"level":9,"move_id":175},{"level":17,"move_id":67},{"level":25,"move_id":157},{"level":33,"move_id":335},{"level":41,"move_id":185},{"level":49,"move_id":21},{"level":57,"move_id":38}]},"tmhm_learnset":"00A03E50CE110E29","types":[5,5]},{"abilities":[11,6],"address":3301952,"base_stats":[90,75,75,70,90,100],"catch_rate":45,"evolutions":[],"friendship":70,"id":186,"learnset":{"address":3312812,"moves":[{"level":1,"move_id":55},{"level":1,"move_id":95},{"level":1,"move_id":3},{"level":1,"move_id":195},{"level":35,"move_id":195},{"level":51,"move_id":207}]},"tmhm_learnset":"03B03E00DE137265","types":[11,11]},{"abilities":[34,0],"address":3301980,"base_stats":[35,35,40,50,35,55],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":18,"species":188}],"friendship":70,"id":187,"learnset":{"address":3312826,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":150},{"level":5,"move_id":235},{"level":5,"move_id":39},{"level":10,"move_id":33},{"level":13,"move_id":77},{"level":15,"move_id":78},{"level":17,"move_id":79},{"level":20,"move_id":73},{"level":25,"move_id":178},{"level":30,"move_id":72}]},"tmhm_learnset":"00401E8084350720","types":[12,2]},{"abilities":[34,0],"address":3302008,"base_stats":[55,45,50,80,45,65],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":27,"species":189}],"friendship":70,"id":188,"learnset":{"address":3312854,"moves":[{"level":1,"move_id":150},{"level":1,"move_id":235},{"level":1,"move_id":39},{"level":1,"move_id":33},{"level":5,"move_id":235},{"level":5,"move_id":39},{"level":10,"move_id":33},{"level":13,"move_id":77},{"level":15,"move_id":78},{"level":17,"move_id":79},{"level":22,"move_id":73},{"level":29,"move_id":178},{"level":36,"move_id":72}]},"tmhm_learnset":"00401E8084350720","types":[12,2]},{"abilities":[34,0],"address":3302036,"base_stats":[75,55,70,110,55,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":189,"learnset":{"address":3312882,"moves":[{"level":1,"move_id":150},{"level":1,"move_id":235},{"level":1,"move_id":39},{"level":1,"move_id":33},{"level":5,"move_id":235},{"level":5,"move_id":39},{"level":10,"move_id":33},{"level":13,"move_id":77},{"level":15,"move_id":78},{"level":17,"move_id":79},{"level":22,"move_id":73},{"level":33,"move_id":178},{"level":44,"move_id":72}]},"tmhm_learnset":"00401E8084354720","types":[12,2]},{"abilities":[50,53],"address":3302064,"base_stats":[55,70,55,85,40,55],"catch_rate":45,"evolutions":[],"friendship":70,"id":190,"learnset":{"address":3312910,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":39},{"level":6,"move_id":28},{"level":13,"move_id":310},{"level":18,"move_id":226},{"level":25,"move_id":321},{"level":31,"move_id":154},{"level":38,"move_id":129},{"level":43,"move_id":103},{"level":50,"move_id":97}]},"tmhm_learnset":"00A53E82EDF30E25","types":[0,0]},{"abilities":[34,0],"address":3302092,"base_stats":[30,30,30,30,30,30],"catch_rate":235,"evolutions":[{"method":"ITEM","param":93,"species":192}],"friendship":70,"id":191,"learnset":{"address":3312936,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":71},{"level":6,"move_id":74},{"level":13,"move_id":72},{"level":18,"move_id":275},{"level":25,"move_id":283},{"level":30,"move_id":241},{"level":37,"move_id":235},{"level":42,"move_id":202}]},"tmhm_learnset":"00441E08843D8720","types":[12,12]},{"abilities":[34,0],"address":3302120,"base_stats":[75,75,55,30,105,85],"catch_rate":120,"evolutions":[],"friendship":70,"id":192,"learnset":{"address":3312960,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":71},{"level":1,"move_id":1},{"level":6,"move_id":74},{"level":13,"move_id":75},{"level":18,"move_id":275},{"level":25,"move_id":331},{"level":30,"move_id":241},{"level":37,"move_id":80},{"level":42,"move_id":76}]},"tmhm_learnset":"00441E08843DC720","types":[12,12]},{"abilities":[3,14],"address":3302148,"base_stats":[65,65,45,95,75,45],"catch_rate":75,"evolutions":[],"friendship":70,"id":193,"learnset":{"address":3312984,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":193},{"level":7,"move_id":98},{"level":13,"move_id":104},{"level":19,"move_id":49},{"level":25,"move_id":197},{"level":31,"move_id":48},{"level":37,"move_id":253},{"level":43,"move_id":17},{"level":49,"move_id":103}]},"tmhm_learnset":"00407E80B4350620","types":[6,2]},{"abilities":[6,11],"address":3302176,"base_stats":[55,45,45,15,25,25],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":20,"species":195}],"friendship":70,"id":194,"learnset":{"address":3313010,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":55},{"level":1,"move_id":39},{"level":11,"move_id":21},{"level":16,"move_id":341},{"level":21,"move_id":133},{"level":31,"move_id":281},{"level":36,"move_id":89},{"level":41,"move_id":240},{"level":51,"move_id":54},{"level":51,"move_id":114}]},"tmhm_learnset":"03D01E188E533264","types":[11,4]},{"abilities":[6,11],"address":3302204,"base_stats":[95,85,85,35,65,65],"catch_rate":90,"evolutions":[],"friendship":70,"id":195,"learnset":{"address":3313036,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":55},{"level":1,"move_id":39},{"level":11,"move_id":21},{"level":16,"move_id":341},{"level":23,"move_id":133},{"level":35,"move_id":281},{"level":42,"move_id":89},{"level":49,"move_id":240},{"level":61,"move_id":54},{"level":61,"move_id":114}]},"tmhm_learnset":"03F01E58CE537265","types":[11,4]},{"abilities":[28,0],"address":3302232,"base_stats":[65,65,60,110,130,95],"catch_rate":45,"evolutions":[],"friendship":70,"id":196,"learnset":{"address":3313062,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":270},{"level":8,"move_id":28},{"level":16,"move_id":93},{"level":23,"move_id":98},{"level":30,"move_id":129},{"level":36,"move_id":60},{"level":42,"move_id":244},{"level":47,"move_id":94},{"level":52,"move_id":234}]},"tmhm_learnset":"00449E01BC53C628","types":[14,14]},{"abilities":[28,0],"address":3302260,"base_stats":[95,65,110,65,60,130],"catch_rate":45,"evolutions":[],"friendship":35,"id":197,"learnset":{"address":3313088,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":39},{"level":1,"move_id":270},{"level":8,"move_id":28},{"level":16,"move_id":228},{"level":23,"move_id":98},{"level":30,"move_id":109},{"level":36,"move_id":185},{"level":42,"move_id":212},{"level":47,"move_id":103},{"level":52,"move_id":236}]},"tmhm_learnset":"00451F00BC534E20","types":[17,17]},{"abilities":[15,0],"address":3302288,"base_stats":[60,85,42,91,85,42],"catch_rate":30,"evolutions":[],"friendship":35,"id":198,"learnset":{"address":3313114,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":9,"move_id":310},{"level":14,"move_id":228},{"level":22,"move_id":114},{"level":27,"move_id":101},{"level":35,"move_id":185},{"level":40,"move_id":269},{"level":48,"move_id":212}]},"tmhm_learnset":"00097F80A4130E28","types":[17,2]},{"abilities":[12,20],"address":3302316,"base_stats":[95,75,80,30,100,110],"catch_rate":70,"evolutions":[],"friendship":70,"id":199,"learnset":{"address":3313138,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":174},{"level":1,"move_id":281},{"level":1,"move_id":33},{"level":6,"move_id":45},{"level":15,"move_id":55},{"level":20,"move_id":93},{"level":29,"move_id":50},{"level":34,"move_id":29},{"level":43,"move_id":207},{"level":48,"move_id":94}]},"tmhm_learnset":"02F09E24FE5B766D","types":[11,14]},{"abilities":[26,0],"address":3302344,"base_stats":[60,60,60,85,85,85],"catch_rate":45,"evolutions":[],"friendship":35,"id":200,"learnset":{"address":3313162,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":45},{"level":1,"move_id":149},{"level":6,"move_id":180},{"level":11,"move_id":310},{"level":17,"move_id":109},{"level":23,"move_id":212},{"level":30,"move_id":60},{"level":37,"move_id":220},{"level":45,"move_id":195},{"level":53,"move_id":288}]},"tmhm_learnset":"0041BF82B5930E28","types":[7,7]},{"abilities":[26,0],"address":3302372,"base_stats":[48,72,48,48,72,48],"catch_rate":225,"evolutions":[],"friendship":70,"id":201,"learnset":{"address":3313188,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":237}]},"tmhm_learnset":"0000000000000000","types":[14,14]},{"abilities":[23,0],"address":3302400,"base_stats":[190,33,58,33,33,58],"catch_rate":45,"evolutions":[],"friendship":70,"id":202,"learnset":{"address":3313198,"moves":[{"level":1,"move_id":68},{"level":1,"move_id":243},{"level":1,"move_id":219},{"level":1,"move_id":194}]},"tmhm_learnset":"0000000000000000","types":[14,14]},{"abilities":[39,48],"address":3302428,"base_stats":[70,80,65,85,90,65],"catch_rate":60,"evolutions":[],"friendship":70,"id":203,"learnset":{"address":3313208,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":7,"move_id":310},{"level":13,"move_id":93},{"level":19,"move_id":23},{"level":25,"move_id":316},{"level":31,"move_id":97},{"level":37,"move_id":226},{"level":43,"move_id":60},{"level":49,"move_id":242}]},"tmhm_learnset":"00E0BE03B7D38628","types":[0,14]},{"abilities":[5,0],"address":3302456,"base_stats":[50,65,90,15,35,35],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":31,"species":205}],"friendship":70,"id":204,"learnset":{"address":3313234,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":182},{"level":8,"move_id":120},{"level":15,"move_id":36},{"level":22,"move_id":229},{"level":29,"move_id":117},{"level":36,"move_id":153},{"level":43,"move_id":191},{"level":50,"move_id":38}]},"tmhm_learnset":"00A01E118E358620","types":[6,6]},{"abilities":[5,0],"address":3302484,"base_stats":[75,90,140,40,60,60],"catch_rate":75,"evolutions":[],"friendship":70,"id":205,"learnset":{"address":3313258,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":182},{"level":1,"move_id":120},{"level":8,"move_id":120},{"level":15,"move_id":36},{"level":22,"move_id":229},{"level":29,"move_id":117},{"level":39,"move_id":153},{"level":49,"move_id":191},{"level":59,"move_id":38}]},"tmhm_learnset":"00A01E118E35C620","types":[6,8]},{"abilities":[32,50],"address":3302512,"base_stats":[100,70,70,45,65,65],"catch_rate":190,"evolutions":[],"friendship":70,"id":206,"learnset":{"address":3313282,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":99},{"level":4,"move_id":111},{"level":11,"move_id":281},{"level":14,"move_id":137},{"level":21,"move_id":180},{"level":24,"move_id":228},{"level":31,"move_id":103},{"level":34,"move_id":36},{"level":41,"move_id":283}]},"tmhm_learnset":"00A03E66AFF3362C","types":[0,0]},{"abilities":[52,8],"address":3302540,"base_stats":[65,75,105,85,35,65],"catch_rate":60,"evolutions":[],"friendship":70,"id":207,"learnset":{"address":3313308,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":40},{"level":6,"move_id":28},{"level":13,"move_id":106},{"level":20,"move_id":98},{"level":28,"move_id":185},{"level":36,"move_id":163},{"level":44,"move_id":103},{"level":52,"move_id":12}]},"tmhm_learnset":"00A47ED88E530620","types":[4,2]},{"abilities":[69,5],"address":3302568,"base_stats":[75,85,200,30,55,65],"catch_rate":25,"evolutions":[],"friendship":70,"id":208,"learnset":{"address":3313332,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":103},{"level":9,"move_id":20},{"level":13,"move_id":88},{"level":21,"move_id":106},{"level":25,"move_id":99},{"level":33,"move_id":201},{"level":37,"move_id":21},{"level":45,"move_id":231},{"level":49,"move_id":242},{"level":57,"move_id":38}]},"tmhm_learnset":"00A41F508E514E30","types":[8,4]},{"abilities":[22,50],"address":3302596,"base_stats":[60,80,50,30,40,40],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":23,"species":210}],"friendship":70,"id":209,"learnset":{"address":3313360,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":184},{"level":4,"move_id":39},{"level":8,"move_id":204},{"level":13,"move_id":44},{"level":19,"move_id":122},{"level":26,"move_id":46},{"level":34,"move_id":99},{"level":43,"move_id":36},{"level":53,"move_id":242}]},"tmhm_learnset":"00A23F2EEFB30EB5","types":[0,0]},{"abilities":[22,22],"address":3302624,"base_stats":[90,120,75,45,60,60],"catch_rate":75,"evolutions":[],"friendship":70,"id":210,"learnset":{"address":3313386,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":184},{"level":4,"move_id":39},{"level":8,"move_id":204},{"level":13,"move_id":44},{"level":19,"move_id":122},{"level":28,"move_id":46},{"level":38,"move_id":99},{"level":49,"move_id":36},{"level":61,"move_id":242}]},"tmhm_learnset":"00A23F6EEFF34EB5","types":[0,0]},{"abilities":[38,33],"address":3302652,"base_stats":[65,95,75,85,55,55],"catch_rate":45,"evolutions":[],"friendship":70,"id":211,"learnset":{"address":3313412,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":191},{"level":1,"move_id":33},{"level":1,"move_id":40},{"level":10,"move_id":106},{"level":10,"move_id":107},{"level":19,"move_id":55},{"level":28,"move_id":42},{"level":37,"move_id":36},{"level":46,"move_id":56}]},"tmhm_learnset":"03101E0AA4133264","types":[11,3]},{"abilities":[68,0],"address":3302680,"base_stats":[70,130,100,65,55,80],"catch_rate":25,"evolutions":[],"friendship":70,"id":212,"learnset":{"address":3313434,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":98},{"level":1,"move_id":43},{"level":6,"move_id":116},{"level":11,"move_id":228},{"level":16,"move_id":206},{"level":21,"move_id":97},{"level":26,"move_id":232},{"level":31,"move_id":163},{"level":36,"move_id":14},{"level":41,"move_id":104},{"level":46,"move_id":210}]},"tmhm_learnset":"00A47E9084134620","types":[6,8]},{"abilities":[5,0],"address":3302708,"base_stats":[20,10,230,5,10,230],"catch_rate":190,"evolutions":[],"friendship":70,"id":213,"learnset":{"address":3313462,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":132},{"level":1,"move_id":110},{"level":9,"move_id":35},{"level":14,"move_id":227},{"level":23,"move_id":219},{"level":28,"move_id":117},{"level":37,"move_id":156}]},"tmhm_learnset":"00E01E588E190620","types":[6,5]},{"abilities":[68,62],"address":3302736,"base_stats":[80,125,75,85,40,95],"catch_rate":45,"evolutions":[],"friendship":70,"id":214,"learnset":{"address":3313482,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":43},{"level":6,"move_id":30},{"level":11,"move_id":203},{"level":17,"move_id":31},{"level":23,"move_id":280},{"level":30,"move_id":68},{"level":37,"move_id":36},{"level":45,"move_id":179},{"level":53,"move_id":224}]},"tmhm_learnset":"00A43E40CE1346A1","types":[6,1]},{"abilities":[39,51],"address":3302764,"base_stats":[55,95,55,115,35,75],"catch_rate":60,"evolutions":[],"friendship":35,"id":215,"learnset":{"address":3313508,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":43},{"level":1,"move_id":269},{"level":8,"move_id":98},{"level":15,"move_id":103},{"level":22,"move_id":185},{"level":29,"move_id":154},{"level":36,"move_id":97},{"level":43,"move_id":196},{"level":50,"move_id":163},{"level":57,"move_id":251},{"level":64,"move_id":232}]},"tmhm_learnset":"00B53F80EC533E69","types":[17,15]},{"abilities":[53,0],"address":3302792,"base_stats":[60,80,50,40,50,50],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":30,"species":217}],"friendship":70,"id":216,"learnset":{"address":3313536,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":43},{"level":7,"move_id":122},{"level":13,"move_id":154},{"level":19,"move_id":313},{"level":25,"move_id":185},{"level":31,"move_id":156},{"level":37,"move_id":163},{"level":43,"move_id":173},{"level":49,"move_id":37}]},"tmhm_learnset":"00A43F80CE130EB1","types":[0,0]},{"abilities":[62,0],"address":3302820,"base_stats":[90,130,75,55,75,75],"catch_rate":60,"evolutions":[],"friendship":70,"id":217,"learnset":{"address":3313562,"moves":[{"level":1,"move_id":10},{"level":1,"move_id":43},{"level":1,"move_id":122},{"level":1,"move_id":154},{"level":7,"move_id":122},{"level":13,"move_id":154},{"level":19,"move_id":313},{"level":25,"move_id":185},{"level":31,"move_id":156},{"level":37,"move_id":163},{"level":43,"move_id":173},{"level":49,"move_id":37}]},"tmhm_learnset":"00A43FC0CE134EB1","types":[0,0]},{"abilities":[40,49],"address":3302848,"base_stats":[40,40,40,20,70,40],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":38,"species":219}],"friendship":70,"id":218,"learnset":{"address":3313588,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":281},{"level":1,"move_id":123},{"level":8,"move_id":52},{"level":15,"move_id":88},{"level":22,"move_id":106},{"level":29,"move_id":133},{"level":36,"move_id":53},{"level":43,"move_id":157},{"level":50,"move_id":34}]},"tmhm_learnset":"00821E2584118620","types":[10,10]},{"abilities":[40,49],"address":3302876,"base_stats":[50,50,120,30,80,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":219,"learnset":{"address":3313612,"moves":[{"level":1,"move_id":281},{"level":1,"move_id":123},{"level":1,"move_id":52},{"level":1,"move_id":88},{"level":8,"move_id":52},{"level":15,"move_id":88},{"level":22,"move_id":106},{"level":29,"move_id":133},{"level":36,"move_id":53},{"level":48,"move_id":157},{"level":60,"move_id":34}]},"tmhm_learnset":"00A21E758611C620","types":[10,5]},{"abilities":[12,0],"address":3302904,"base_stats":[50,50,40,50,30,30],"catch_rate":225,"evolutions":[{"method":"LEVEL","param":33,"species":221}],"friendship":70,"id":220,"learnset":{"address":3313636,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":316},{"level":10,"move_id":181},{"level":19,"move_id":203},{"level":28,"move_id":36},{"level":37,"move_id":54},{"level":46,"move_id":59},{"level":55,"move_id":133}]},"tmhm_learnset":"00A01E518E13B270","types":[15,4]},{"abilities":[12,0],"address":3302932,"base_stats":[100,100,80,50,60,60],"catch_rate":75,"evolutions":[],"friendship":70,"id":221,"learnset":{"address":3313658,"moves":[{"level":1,"move_id":30},{"level":1,"move_id":316},{"level":1,"move_id":181},{"level":1,"move_id":203},{"level":10,"move_id":181},{"level":19,"move_id":203},{"level":28,"move_id":36},{"level":33,"move_id":31},{"level":42,"move_id":54},{"level":56,"move_id":59},{"level":70,"move_id":133}]},"tmhm_learnset":"00A01E518E13F270","types":[15,4]},{"abilities":[55,30],"address":3302960,"base_stats":[55,55,85,35,65,85],"catch_rate":60,"evolutions":[],"friendship":70,"id":222,"learnset":{"address":3313682,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":6,"move_id":106},{"level":12,"move_id":145},{"level":17,"move_id":105},{"level":17,"move_id":287},{"level":23,"move_id":61},{"level":28,"move_id":131},{"level":34,"move_id":350},{"level":39,"move_id":243},{"level":45,"move_id":246}]},"tmhm_learnset":"00B01E51BE1BB66C","types":[11,5]},{"abilities":[55,0],"address":3302988,"base_stats":[35,65,35,65,65,35],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":25,"species":224}],"friendship":70,"id":223,"learnset":{"address":3313710,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":55},{"level":11,"move_id":199},{"level":22,"move_id":60},{"level":22,"move_id":62},{"level":22,"move_id":61},{"level":33,"move_id":116},{"level":44,"move_id":58},{"level":55,"move_id":63}]},"tmhm_learnset":"03103E2494137624","types":[11,11]},{"abilities":[21,0],"address":3303016,"base_stats":[75,105,75,45,105,75],"catch_rate":75,"evolutions":[],"friendship":70,"id":224,"learnset":{"address":3313734,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":55},{"level":11,"move_id":132},{"level":22,"move_id":60},{"level":22,"move_id":62},{"level":22,"move_id":61},{"level":25,"move_id":190},{"level":38,"move_id":116},{"level":54,"move_id":58},{"level":70,"move_id":63}]},"tmhm_learnset":"03103E2C94137724","types":[11,11]},{"abilities":[72,55],"address":3303044,"base_stats":[45,55,45,75,65,45],"catch_rate":45,"evolutions":[],"friendship":70,"id":225,"learnset":{"address":3313760,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":217}]},"tmhm_learnset":"00083E8084133265","types":[15,2]},{"abilities":[33,11],"address":3303072,"base_stats":[65,40,70,70,80,140],"catch_rate":25,"evolutions":[],"friendship":70,"id":226,"learnset":{"address":3313770,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":145},{"level":8,"move_id":48},{"level":15,"move_id":61},{"level":22,"move_id":36},{"level":29,"move_id":97},{"level":36,"move_id":17},{"level":43,"move_id":352},{"level":50,"move_id":109}]},"tmhm_learnset":"03101E8086133264","types":[11,2]},{"abilities":[51,5],"address":3303100,"base_stats":[65,80,140,70,40,70],"catch_rate":25,"evolutions":[],"friendship":70,"id":227,"learnset":{"address":3313794,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":64},{"level":10,"move_id":28},{"level":13,"move_id":129},{"level":16,"move_id":97},{"level":26,"move_id":31},{"level":29,"move_id":314},{"level":32,"move_id":211},{"level":42,"move_id":191},{"level":45,"move_id":319}]},"tmhm_learnset":"008C7F9084110E30","types":[8,2]},{"abilities":[48,18],"address":3303128,"base_stats":[45,60,30,65,80,50],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":24,"species":229}],"friendship":35,"id":228,"learnset":{"address":3313820,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":52},{"level":7,"move_id":336},{"level":13,"move_id":123},{"level":19,"move_id":46},{"level":25,"move_id":44},{"level":31,"move_id":316},{"level":37,"move_id":185},{"level":43,"move_id":53},{"level":49,"move_id":242}]},"tmhm_learnset":"00833F2CA4710E30","types":[17,10]},{"abilities":[48,18],"address":3303156,"base_stats":[75,90,50,95,110,80],"catch_rate":45,"evolutions":[],"friendship":35,"id":229,"learnset":{"address":3313846,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":52},{"level":1,"move_id":336},{"level":7,"move_id":336},{"level":13,"move_id":123},{"level":19,"move_id":46},{"level":27,"move_id":44},{"level":35,"move_id":316},{"level":43,"move_id":185},{"level":51,"move_id":53},{"level":59,"move_id":242}]},"tmhm_learnset":"00A33F2CA4714E30","types":[17,10]},{"abilities":[33,0],"address":3303184,"base_stats":[75,95,95,85,95,95],"catch_rate":45,"evolutions":[],"friendship":70,"id":230,"learnset":{"address":3313872,"moves":[{"level":1,"move_id":145},{"level":1,"move_id":108},{"level":1,"move_id":43},{"level":1,"move_id":55},{"level":8,"move_id":108},{"level":15,"move_id":43},{"level":22,"move_id":55},{"level":29,"move_id":239},{"level":40,"move_id":97},{"level":51,"move_id":56},{"level":62,"move_id":349}]},"tmhm_learnset":"03101E0084137264","types":[11,16]},{"abilities":[53,0],"address":3303212,"base_stats":[90,60,60,40,40,40],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":25,"species":232}],"friendship":70,"id":231,"learnset":{"address":3313896,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":316},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":9,"move_id":111},{"level":17,"move_id":175},{"level":25,"move_id":36},{"level":33,"move_id":205},{"level":41,"move_id":203},{"level":49,"move_id":38}]},"tmhm_learnset":"00A01E5086510630","types":[4,4]},{"abilities":[5,0],"address":3303240,"base_stats":[90,120,120,50,60,60],"catch_rate":60,"evolutions":[],"friendship":70,"id":232,"learnset":{"address":3313918,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":316},{"level":1,"move_id":30},{"level":1,"move_id":45},{"level":9,"move_id":111},{"level":17,"move_id":175},{"level":25,"move_id":31},{"level":33,"move_id":205},{"level":41,"move_id":229},{"level":49,"move_id":89}]},"tmhm_learnset":"00A01E5086514630","types":[4,4]},{"abilities":[36,0],"address":3303268,"base_stats":[85,80,90,60,105,95],"catch_rate":45,"evolutions":[],"friendship":70,"id":233,"learnset":{"address":3313940,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":176},{"level":1,"move_id":33},{"level":1,"move_id":160},{"level":9,"move_id":97},{"level":12,"move_id":60},{"level":20,"move_id":105},{"level":24,"move_id":111},{"level":32,"move_id":199},{"level":36,"move_id":161},{"level":44,"move_id":278},{"level":48,"move_id":192}]},"tmhm_learnset":"00402E82B5F37620","types":[0,0]},{"abilities":[22,0],"address":3303296,"base_stats":[73,95,62,85,85,65],"catch_rate":45,"evolutions":[],"friendship":70,"id":234,"learnset":{"address":3313966,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":7,"move_id":43},{"level":13,"move_id":310},{"level":19,"move_id":95},{"level":25,"move_id":23},{"level":31,"move_id":28},{"level":37,"move_id":36},{"level":43,"move_id":109},{"level":49,"move_id":347}]},"tmhm_learnset":"0040BE03B7F38638","types":[0,0]},{"abilities":[20,0],"address":3303324,"base_stats":[55,20,35,75,20,45],"catch_rate":45,"evolutions":[],"friendship":70,"id":235,"learnset":{"address":3313992,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":166},{"level":11,"move_id":166},{"level":21,"move_id":166},{"level":31,"move_id":166},{"level":41,"move_id":166},{"level":51,"move_id":166},{"level":61,"move_id":166},{"level":71,"move_id":166},{"level":81,"move_id":166},{"level":91,"move_id":166}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[62,0],"address":3303352,"base_stats":[35,35,35,35,35,35],"catch_rate":75,"evolutions":[{"method":"LEVEL_ATK_LT_DEF","param":20,"species":107},{"method":"LEVEL_ATK_GT_DEF","param":20,"species":106},{"method":"LEVEL_ATK_EQ_DEF","param":20,"species":237}],"friendship":70,"id":236,"learnset":{"address":3314020,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"00A03E00C61306A0","types":[1,1]},{"abilities":[22,0],"address":3303380,"base_stats":[50,95,95,70,35,110],"catch_rate":45,"evolutions":[],"friendship":70,"id":237,"learnset":{"address":3314030,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":279},{"level":1,"move_id":27},{"level":7,"move_id":116},{"level":13,"move_id":228},{"level":19,"move_id":98},{"level":20,"move_id":167},{"level":25,"move_id":229},{"level":31,"move_id":68},{"level":37,"move_id":97},{"level":43,"move_id":197},{"level":49,"move_id":283}]},"tmhm_learnset":"00A03E10CE1306A0","types":[1,1]},{"abilities":[12,0],"address":3303408,"base_stats":[45,30,15,65,85,65],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":30,"species":124}],"friendship":70,"id":238,"learnset":{"address":3314058,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":122},{"level":9,"move_id":186},{"level":13,"move_id":181},{"level":21,"move_id":93},{"level":25,"move_id":47},{"level":33,"move_id":212},{"level":37,"move_id":313},{"level":45,"move_id":94},{"level":49,"move_id":195},{"level":57,"move_id":59}]},"tmhm_learnset":"0040BE01B413B26C","types":[15,14]},{"abilities":[9,0],"address":3303436,"base_stats":[45,63,37,95,65,55],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":30,"species":125}],"friendship":70,"id":239,"learnset":{"address":3314086,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":98},{"level":1,"move_id":43},{"level":9,"move_id":9},{"level":17,"move_id":113},{"level":25,"move_id":129},{"level":33,"move_id":103},{"level":41,"move_id":85},{"level":49,"move_id":87}]},"tmhm_learnset":"00C03E02D5938221","types":[13,13]},{"abilities":[49,0],"address":3303464,"base_stats":[45,75,37,83,70,55],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":30,"species":126}],"friendship":70,"id":240,"learnset":{"address":3314108,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":52},{"level":7,"move_id":43},{"level":13,"move_id":123},{"level":19,"move_id":7},{"level":25,"move_id":108},{"level":31,"move_id":241},{"level":37,"move_id":53},{"level":43,"move_id":109},{"level":49,"move_id":126}]},"tmhm_learnset":"00803E24D4510621","types":[10,10]},{"abilities":[47,0],"address":3303492,"base_stats":[95,80,105,100,40,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":241,"learnset":{"address":3314134,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":4,"move_id":45},{"level":8,"move_id":111},{"level":13,"move_id":23},{"level":19,"move_id":208},{"level":26,"move_id":117},{"level":34,"move_id":205},{"level":43,"move_id":34},{"level":53,"move_id":215}]},"tmhm_learnset":"00B01E52E7F37625","types":[0,0]},{"abilities":[30,32],"address":3303520,"base_stats":[255,10,10,55,75,135],"catch_rate":30,"evolutions":[],"friendship":140,"id":242,"learnset":{"address":3314160,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":45},{"level":4,"move_id":39},{"level":7,"move_id":287},{"level":10,"move_id":135},{"level":13,"move_id":3},{"level":18,"move_id":107},{"level":23,"move_id":47},{"level":28,"move_id":121},{"level":33,"move_id":111},{"level":40,"move_id":113},{"level":47,"move_id":38}]},"tmhm_learnset":"00E19E76F7FBF66D","types":[0,0]},{"abilities":[46,0],"address":3303548,"base_stats":[90,85,75,115,115,100],"catch_rate":3,"evolutions":[],"friendship":35,"id":243,"learnset":{"address":3314190,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":44},{"level":1,"move_id":43},{"level":11,"move_id":84},{"level":21,"move_id":46},{"level":31,"move_id":98},{"level":41,"move_id":209},{"level":51,"move_id":115},{"level":61,"move_id":242},{"level":71,"move_id":87},{"level":81,"move_id":347}]},"tmhm_learnset":"00E40E138DD34638","types":[13,13]},{"abilities":[46,0],"address":3303576,"base_stats":[115,115,85,100,90,75],"catch_rate":3,"evolutions":[],"friendship":35,"id":244,"learnset":{"address":3314216,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":44},{"level":1,"move_id":43},{"level":11,"move_id":52},{"level":21,"move_id":46},{"level":31,"move_id":83},{"level":41,"move_id":23},{"level":51,"move_id":53},{"level":61,"move_id":207},{"level":71,"move_id":126},{"level":81,"move_id":347}]},"tmhm_learnset":"00E40E358C734638","types":[10,10]},{"abilities":[46,0],"address":3303604,"base_stats":[100,75,115,85,90,115],"catch_rate":3,"evolutions":[],"friendship":35,"id":245,"learnset":{"address":3314242,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":44},{"level":1,"move_id":43},{"level":11,"move_id":61},{"level":21,"move_id":240},{"level":31,"move_id":16},{"level":41,"move_id":62},{"level":51,"move_id":54},{"level":61,"move_id":243},{"level":71,"move_id":56},{"level":81,"move_id":347}]},"tmhm_learnset":"03940E118C53767C","types":[11,11]},{"abilities":[62,0],"address":3303632,"base_stats":[50,64,50,41,45,50],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":30,"species":247}],"friendship":35,"id":246,"learnset":{"address":3314268,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":44},{"level":1,"move_id":43},{"level":8,"move_id":201},{"level":15,"move_id":103},{"level":22,"move_id":157},{"level":29,"move_id":37},{"level":36,"move_id":184},{"level":43,"move_id":242},{"level":50,"move_id":89},{"level":57,"move_id":63}]},"tmhm_learnset":"00801F10CE134E20","types":[5,4]},{"abilities":[61,0],"address":3303660,"base_stats":[70,84,70,51,65,70],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":55,"species":248}],"friendship":35,"id":247,"learnset":{"address":3314294,"moves":[{"level":1,"move_id":44},{"level":1,"move_id":43},{"level":1,"move_id":201},{"level":1,"move_id":103},{"level":8,"move_id":201},{"level":15,"move_id":103},{"level":22,"move_id":157},{"level":29,"move_id":37},{"level":38,"move_id":184},{"level":47,"move_id":242},{"level":56,"move_id":89},{"level":65,"move_id":63}]},"tmhm_learnset":"00801F10CE134E20","types":[5,4]},{"abilities":[45,0],"address":3303688,"base_stats":[100,134,110,61,95,100],"catch_rate":45,"evolutions":[],"friendship":35,"id":248,"learnset":{"address":3314320,"moves":[{"level":1,"move_id":44},{"level":1,"move_id":43},{"level":1,"move_id":201},{"level":1,"move_id":103},{"level":8,"move_id":201},{"level":15,"move_id":103},{"level":22,"move_id":157},{"level":29,"move_id":37},{"level":38,"move_id":184},{"level":47,"move_id":242},{"level":61,"move_id":89},{"level":75,"move_id":63}]},"tmhm_learnset":"00B41FF6CFD37E37","types":[5,17]},{"abilities":[46,0],"address":3303716,"base_stats":[106,90,130,110,90,154],"catch_rate":3,"evolutions":[],"friendship":0,"id":249,"learnset":{"address":3314346,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":16},{"level":1,"move_id":18},{"level":11,"move_id":219},{"level":22,"move_id":16},{"level":33,"move_id":105},{"level":44,"move_id":56},{"level":55,"move_id":240},{"level":66,"move_id":129},{"level":77,"move_id":177},{"level":88,"move_id":246},{"level":99,"move_id":248}]},"tmhm_learnset":"03B8CE93B7DFF67C","types":[14,2]},{"abilities":[46,0],"address":3303744,"base_stats":[106,130,90,90,110,154],"catch_rate":3,"evolutions":[],"friendship":0,"id":250,"learnset":{"address":3314374,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":18},{"level":11,"move_id":219},{"level":22,"move_id":16},{"level":33,"move_id":105},{"level":44,"move_id":126},{"level":55,"move_id":241},{"level":66,"move_id":129},{"level":77,"move_id":221},{"level":88,"move_id":246},{"level":99,"move_id":248}]},"tmhm_learnset":"00EA4EB7B7BFC638","types":[10,2]},{"abilities":[30,0],"address":3303772,"base_stats":[100,100,100,100,100,100],"catch_rate":45,"evolutions":[],"friendship":100,"id":251,"learnset":{"address":3314402,"moves":[{"level":1,"move_id":73},{"level":1,"move_id":93},{"level":1,"move_id":105},{"level":1,"move_id":215},{"level":10,"move_id":219},{"level":20,"move_id":246},{"level":30,"move_id":248},{"level":40,"move_id":226},{"level":50,"move_id":195}]},"tmhm_learnset":"00448E93B43FC62C","types":[14,12]},{"abilities":[0,0],"address":3303800,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":252,"learnset":{"address":3314422,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3303828,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":253,"learnset":{"address":3314432,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3303856,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":254,"learnset":{"address":3314442,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3303884,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":255,"learnset":{"address":3314452,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3303912,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":256,"learnset":{"address":3314462,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3303940,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":257,"learnset":{"address":3314472,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3303968,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":258,"learnset":{"address":3314482,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3303996,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":259,"learnset":{"address":3314492,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304024,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":260,"learnset":{"address":3314502,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304052,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":261,"learnset":{"address":3314512,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304080,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":262,"learnset":{"address":3314522,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304108,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":263,"learnset":{"address":3314532,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304136,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":264,"learnset":{"address":3314542,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304164,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":265,"learnset":{"address":3314552,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304192,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":266,"learnset":{"address":3314562,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304220,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":267,"learnset":{"address":3314572,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304248,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":268,"learnset":{"address":3314582,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304276,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":269,"learnset":{"address":3314592,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304304,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":270,"learnset":{"address":3314602,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304332,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":271,"learnset":{"address":3314612,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304360,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":272,"learnset":{"address":3314622,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304388,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":273,"learnset":{"address":3314632,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304416,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":274,"learnset":{"address":3314642,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304444,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":275,"learnset":{"address":3314652,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[0,0],"address":3304472,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":276,"learnset":{"address":3314662,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33}]},"tmhm_learnset":"0000000000000000","types":[0,0]},{"abilities":[65,0],"address":3304500,"base_stats":[40,45,35,70,65,55],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":16,"species":278}],"friendship":70,"id":277,"learnset":{"address":3314672,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":1,"move_id":43},{"level":6,"move_id":71},{"level":11,"move_id":98},{"level":16,"move_id":228},{"level":21,"move_id":103},{"level":26,"move_id":72},{"level":31,"move_id":97},{"level":36,"move_id":21},{"level":41,"move_id":197},{"level":46,"move_id":202}]},"tmhm_learnset":"00E41EC0CC7D0721","types":[12,12]},{"abilities":[65,0],"address":3304528,"base_stats":[50,65,45,95,85,65],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":36,"species":279}],"friendship":70,"id":278,"learnset":{"address":3314700,"moves":[{"level":1,"move_id":1},{"level":1,"move_id":43},{"level":1,"move_id":71},{"level":1,"move_id":98},{"level":6,"move_id":71},{"level":11,"move_id":98},{"level":16,"move_id":210},{"level":17,"move_id":228},{"level":23,"move_id":103},{"level":29,"move_id":348},{"level":35,"move_id":97},{"level":41,"move_id":21},{"level":47,"move_id":197},{"level":53,"move_id":206}]},"tmhm_learnset":"00E41EC0CC7D0721","types":[12,12]},{"abilities":[65,0],"address":3304556,"base_stats":[70,85,65,120,105,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":279,"learnset":{"address":3314730,"moves":[{"level":1,"move_id":1},{"level":1,"move_id":43},{"level":1,"move_id":71},{"level":1,"move_id":98},{"level":6,"move_id":71},{"level":11,"move_id":98},{"level":16,"move_id":210},{"level":17,"move_id":228},{"level":23,"move_id":103},{"level":29,"move_id":348},{"level":35,"move_id":97},{"level":43,"move_id":21},{"level":51,"move_id":197},{"level":59,"move_id":206}]},"tmhm_learnset":"00E41EC0CE7D4733","types":[12,12]},{"abilities":[66,0],"address":3304584,"base_stats":[45,60,40,45,70,50],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":16,"species":281}],"friendship":70,"id":280,"learnset":{"address":3314760,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":45},{"level":7,"move_id":116},{"level":10,"move_id":52},{"level":16,"move_id":64},{"level":19,"move_id":28},{"level":25,"move_id":83},{"level":28,"move_id":98},{"level":34,"move_id":163},{"level":37,"move_id":119},{"level":43,"move_id":53}]},"tmhm_learnset":"00A61EE48C110620","types":[10,10]},{"abilities":[66,0],"address":3304612,"base_stats":[60,85,60,55,85,60],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":36,"species":282}],"friendship":70,"id":281,"learnset":{"address":3314788,"moves":[{"level":1,"move_id":10},{"level":1,"move_id":45},{"level":1,"move_id":116},{"level":1,"move_id":52},{"level":7,"move_id":116},{"level":13,"move_id":52},{"level":16,"move_id":24},{"level":17,"move_id":64},{"level":21,"move_id":28},{"level":28,"move_id":339},{"level":32,"move_id":98},{"level":39,"move_id":163},{"level":43,"move_id":119},{"level":50,"move_id":327}]},"tmhm_learnset":"00A61EE4CC1106A1","types":[10,1]},{"abilities":[66,0],"address":3304640,"base_stats":[80,120,70,80,110,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":282,"learnset":{"address":3314818,"moves":[{"level":1,"move_id":7},{"level":1,"move_id":10},{"level":1,"move_id":45},{"level":1,"move_id":116},{"level":1,"move_id":52},{"level":7,"move_id":116},{"level":13,"move_id":52},{"level":16,"move_id":24},{"level":17,"move_id":64},{"level":21,"move_id":28},{"level":28,"move_id":339},{"level":32,"move_id":98},{"level":36,"move_id":299},{"level":42,"move_id":163},{"level":49,"move_id":119},{"level":59,"move_id":327}]},"tmhm_learnset":"00A61EE4CE1146B1","types":[10,1]},{"abilities":[67,0],"address":3304668,"base_stats":[50,70,50,40,50,50],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":16,"species":284}],"friendship":70,"id":283,"learnset":{"address":3314852,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":6,"move_id":189},{"level":10,"move_id":55},{"level":15,"move_id":117},{"level":19,"move_id":193},{"level":24,"move_id":300},{"level":28,"move_id":36},{"level":33,"move_id":250},{"level":37,"move_id":182},{"level":42,"move_id":56},{"level":46,"move_id":283}]},"tmhm_learnset":"03B01E408C533264","types":[11,11]},{"abilities":[67,0],"address":3304696,"base_stats":[70,85,70,50,60,70],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":36,"species":285}],"friendship":70,"id":284,"learnset":{"address":3314882,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":189},{"level":1,"move_id":55},{"level":6,"move_id":189},{"level":10,"move_id":55},{"level":15,"move_id":117},{"level":16,"move_id":341},{"level":20,"move_id":193},{"level":25,"move_id":300},{"level":31,"move_id":36},{"level":37,"move_id":330},{"level":42,"move_id":182},{"level":46,"move_id":89},{"level":53,"move_id":283}]},"tmhm_learnset":"03B01E408E533264","types":[11,4]},{"abilities":[67,0],"address":3304724,"base_stats":[100,110,90,60,85,90],"catch_rate":45,"evolutions":[],"friendship":70,"id":285,"learnset":{"address":3314914,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":189},{"level":1,"move_id":55},{"level":6,"move_id":189},{"level":10,"move_id":55},{"level":15,"move_id":117},{"level":16,"move_id":341},{"level":20,"move_id":193},{"level":25,"move_id":300},{"level":31,"move_id":36},{"level":39,"move_id":330},{"level":46,"move_id":182},{"level":52,"move_id":89},{"level":61,"move_id":283}]},"tmhm_learnset":"03B01E40CE537275","types":[11,4]},{"abilities":[50,0],"address":3304752,"base_stats":[35,55,35,35,30,30],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":18,"species":287}],"friendship":70,"id":286,"learnset":{"address":3314946,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":5,"move_id":336},{"level":9,"move_id":28},{"level":13,"move_id":44},{"level":17,"move_id":316},{"level":21,"move_id":46},{"level":25,"move_id":207},{"level":29,"move_id":184},{"level":33,"move_id":36},{"level":37,"move_id":269},{"level":41,"move_id":242},{"level":45,"move_id":168}]},"tmhm_learnset":"00813F00AC530E30","types":[17,17]},{"abilities":[22,0],"address":3304780,"base_stats":[70,90,70,70,60,60],"catch_rate":127,"evolutions":[],"friendship":70,"id":287,"learnset":{"address":3314978,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":336},{"level":1,"move_id":28},{"level":1,"move_id":44},{"level":5,"move_id":336},{"level":9,"move_id":28},{"level":13,"move_id":44},{"level":17,"move_id":316},{"level":22,"move_id":46},{"level":27,"move_id":207},{"level":32,"move_id":184},{"level":37,"move_id":36},{"level":42,"move_id":269},{"level":47,"move_id":242},{"level":52,"move_id":168}]},"tmhm_learnset":"00A13F00AC534E30","types":[17,17]},{"abilities":[53,0],"address":3304808,"base_stats":[38,30,41,60,30,41],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":20,"species":289}],"friendship":70,"id":288,"learnset":{"address":3315010,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":5,"move_id":39},{"level":9,"move_id":29},{"level":13,"move_id":28},{"level":17,"move_id":316},{"level":21,"move_id":300},{"level":25,"move_id":42},{"level":29,"move_id":343},{"level":33,"move_id":175},{"level":37,"move_id":156},{"level":41,"move_id":187}]},"tmhm_learnset":"00943E02ADD33624","types":[0,0]},{"abilities":[53,0],"address":3304836,"base_stats":[78,70,61,100,50,61],"catch_rate":90,"evolutions":[],"friendship":70,"id":289,"learnset":{"address":3315040,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":45},{"level":1,"move_id":39},{"level":1,"move_id":29},{"level":5,"move_id":39},{"level":9,"move_id":29},{"level":13,"move_id":28},{"level":17,"move_id":316},{"level":23,"move_id":300},{"level":29,"move_id":154},{"level":35,"move_id":343},{"level":41,"move_id":163},{"level":47,"move_id":156},{"level":53,"move_id":187}]},"tmhm_learnset":"00B43E02ADD37634","types":[0,0]},{"abilities":[19,0],"address":3304864,"base_stats":[45,45,35,20,20,30],"catch_rate":255,"evolutions":[{"method":"LEVEL_SILCOON","param":7,"species":291},{"method":"LEVEL_CASCOON","param":7,"species":293}],"friendship":70,"id":290,"learnset":{"address":3315070,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":81},{"level":5,"move_id":40}]},"tmhm_learnset":"0000000000000000","types":[6,6]},{"abilities":[61,0],"address":3304892,"base_stats":[50,35,55,15,25,25],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":10,"species":292}],"friendship":70,"id":291,"learnset":{"address":3315082,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":7,"move_id":106}]},"tmhm_learnset":"0000000000000000","types":[6,6]},{"abilities":[68,0],"address":3304920,"base_stats":[60,70,50,65,90,50],"catch_rate":45,"evolutions":[],"friendship":70,"id":292,"learnset":{"address":3315094,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":71},{"level":10,"move_id":71},{"level":13,"move_id":16},{"level":17,"move_id":78},{"level":20,"move_id":234},{"level":24,"move_id":72},{"level":27,"move_id":18},{"level":31,"move_id":213},{"level":34,"move_id":318},{"level":38,"move_id":202}]},"tmhm_learnset":"00403E80B43D4620","types":[6,2]},{"abilities":[61,0],"address":3304948,"base_stats":[50,35,55,15,25,25],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":10,"species":294}],"friendship":70,"id":293,"learnset":{"address":3315122,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":7,"move_id":106}]},"tmhm_learnset":"0000000000000000","types":[6,6]},{"abilities":[19,0],"address":3304976,"base_stats":[60,50,70,65,50,90],"catch_rate":45,"evolutions":[],"friendship":70,"id":294,"learnset":{"address":3315134,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":93},{"level":10,"move_id":93},{"level":13,"move_id":16},{"level":17,"move_id":182},{"level":20,"move_id":236},{"level":24,"move_id":60},{"level":27,"move_id":18},{"level":31,"move_id":113},{"level":34,"move_id":318},{"level":38,"move_id":92}]},"tmhm_learnset":"00403E88B435C620","types":[6,3]},{"abilities":[33,44],"address":3305004,"base_stats":[40,30,30,30,40,50],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":14,"species":296}],"friendship":70,"id":295,"learnset":{"address":3315162,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":310},{"level":3,"move_id":45},{"level":7,"move_id":71},{"level":13,"move_id":267},{"level":21,"move_id":54},{"level":31,"move_id":240},{"level":43,"move_id":72}]},"tmhm_learnset":"00503E0084373764","types":[11,12]},{"abilities":[33,44],"address":3305032,"base_stats":[60,50,50,50,60,70],"catch_rate":120,"evolutions":[{"method":"ITEM","param":97,"species":297}],"friendship":70,"id":296,"learnset":{"address":3315184,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":310},{"level":3,"move_id":45},{"level":7,"move_id":71},{"level":13,"move_id":267},{"level":19,"move_id":252},{"level":25,"move_id":154},{"level":31,"move_id":346},{"level":37,"move_id":168},{"level":43,"move_id":253},{"level":49,"move_id":56}]},"tmhm_learnset":"03F03E00C4373764","types":[11,12]},{"abilities":[33,44],"address":3305060,"base_stats":[80,70,70,70,90,100],"catch_rate":45,"evolutions":[],"friendship":70,"id":297,"learnset":{"address":3315212,"moves":[{"level":1,"move_id":310},{"level":1,"move_id":45},{"level":1,"move_id":71},{"level":1,"move_id":267}]},"tmhm_learnset":"03F03E00C4377765","types":[11,12]},{"abilities":[34,48],"address":3305088,"base_stats":[40,40,50,30,30,30],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":14,"species":299}],"friendship":70,"id":298,"learnset":{"address":3315222,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":117},{"level":3,"move_id":106},{"level":7,"move_id":74},{"level":13,"move_id":267},{"level":21,"move_id":235},{"level":31,"move_id":241},{"level":43,"move_id":153}]},"tmhm_learnset":"00C01E00AC350720","types":[12,12]},{"abilities":[34,48],"address":3305116,"base_stats":[70,70,40,60,60,40],"catch_rate":120,"evolutions":[{"method":"ITEM","param":98,"species":300}],"friendship":70,"id":299,"learnset":{"address":3315244,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":3,"move_id":106},{"level":7,"move_id":74},{"level":13,"move_id":267},{"level":19,"move_id":252},{"level":25,"move_id":259},{"level":31,"move_id":185},{"level":37,"move_id":13},{"level":43,"move_id":207},{"level":49,"move_id":326}]},"tmhm_learnset":"00E43F40EC354720","types":[12,17]},{"abilities":[34,48],"address":3305144,"base_stats":[90,100,60,80,90,60],"catch_rate":45,"evolutions":[],"friendship":70,"id":300,"learnset":{"address":3315272,"moves":[{"level":1,"move_id":1},{"level":1,"move_id":106},{"level":1,"move_id":74},{"level":1,"move_id":267}]},"tmhm_learnset":"00E43FC0EC354720","types":[12,17]},{"abilities":[14,0],"address":3305172,"base_stats":[31,45,90,40,30,30],"catch_rate":255,"evolutions":[{"method":"LEVEL_NINJASK","param":20,"species":302},{"method":"LEVEL_SHEDINJA","param":20,"species":303}],"friendship":70,"id":301,"learnset":{"address":3315282,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":106},{"level":5,"move_id":141},{"level":9,"move_id":28},{"level":14,"move_id":154},{"level":19,"move_id":170},{"level":25,"move_id":206},{"level":31,"move_id":189},{"level":38,"move_id":232},{"level":45,"move_id":91}]},"tmhm_learnset":"00440E90AC350620","types":[6,4]},{"abilities":[3,0],"address":3305200,"base_stats":[61,90,45,160,50,50],"catch_rate":120,"evolutions":[],"friendship":70,"id":302,"learnset":{"address":3315308,"moves":[{"level":1,"move_id":10},{"level":1,"move_id":106},{"level":1,"move_id":141},{"level":1,"move_id":28},{"level":5,"move_id":141},{"level":9,"move_id":28},{"level":14,"move_id":154},{"level":19,"move_id":170},{"level":20,"move_id":104},{"level":20,"move_id":210},{"level":20,"move_id":103},{"level":25,"move_id":14},{"level":31,"move_id":163},{"level":38,"move_id":97},{"level":45,"move_id":226}]},"tmhm_learnset":"00443E90AC354620","types":[6,2]},{"abilities":[25,0],"address":3305228,"base_stats":[1,90,45,40,30,30],"catch_rate":45,"evolutions":[],"friendship":70,"id":303,"learnset":{"address":3315340,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":106},{"level":5,"move_id":141},{"level":9,"move_id":28},{"level":14,"move_id":154},{"level":19,"move_id":170},{"level":25,"move_id":180},{"level":31,"move_id":109},{"level":38,"move_id":247},{"level":45,"move_id":288}]},"tmhm_learnset":"00442E90AC354620","types":[6,7]},{"abilities":[62,0],"address":3305256,"base_stats":[40,55,30,85,30,30],"catch_rate":200,"evolutions":[{"method":"LEVEL","param":22,"species":305}],"friendship":70,"id":304,"learnset":{"address":3315366,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":1,"move_id":45},{"level":4,"move_id":116},{"level":8,"move_id":98},{"level":13,"move_id":17},{"level":19,"move_id":104},{"level":26,"move_id":283},{"level":34,"move_id":332},{"level":43,"move_id":97}]},"tmhm_learnset":"00087E8084130620","types":[0,2]},{"abilities":[62,0],"address":3305284,"base_stats":[60,85,60,125,50,50],"catch_rate":45,"evolutions":[],"friendship":70,"id":305,"learnset":{"address":3315390,"moves":[{"level":1,"move_id":64},{"level":1,"move_id":45},{"level":1,"move_id":116},{"level":1,"move_id":98},{"level":4,"move_id":116},{"level":8,"move_id":98},{"level":13,"move_id":17},{"level":19,"move_id":104},{"level":28,"move_id":283},{"level":38,"move_id":332},{"level":49,"move_id":97}]},"tmhm_learnset":"00087E8084134620","types":[0,2]},{"abilities":[27,0],"address":3305312,"base_stats":[60,40,60,35,40,60],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":23,"species":307}],"friendship":70,"id":306,"learnset":{"address":3315414,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":71},{"level":4,"move_id":33},{"level":7,"move_id":78},{"level":10,"move_id":73},{"level":16,"move_id":72},{"level":22,"move_id":29},{"level":28,"move_id":77},{"level":36,"move_id":74},{"level":45,"move_id":202},{"level":54,"move_id":147}]},"tmhm_learnset":"00411E08843D0720","types":[12,12]},{"abilities":[27,0],"address":3305340,"base_stats":[60,130,80,70,60,60],"catch_rate":90,"evolutions":[],"friendship":70,"id":307,"learnset":{"address":3315442,"moves":[{"level":1,"move_id":71},{"level":1,"move_id":33},{"level":1,"move_id":78},{"level":1,"move_id":73},{"level":4,"move_id":33},{"level":7,"move_id":78},{"level":10,"move_id":73},{"level":16,"move_id":72},{"level":22,"move_id":29},{"level":23,"move_id":183},{"level":28,"move_id":68},{"level":36,"move_id":327},{"level":45,"move_id":170},{"level":54,"move_id":223}]},"tmhm_learnset":"00E51E08C47D47A1","types":[12,1]},{"abilities":[20,0],"address":3305368,"base_stats":[60,60,60,60,60,60],"catch_rate":255,"evolutions":[],"friendship":70,"id":308,"learnset":{"address":3315472,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":5,"move_id":253},{"level":12,"move_id":185},{"level":16,"move_id":60},{"level":23,"move_id":95},{"level":27,"move_id":146},{"level":34,"move_id":298},{"level":38,"move_id":244},{"level":45,"move_id":38},{"level":49,"move_id":175},{"level":56,"move_id":37}]},"tmhm_learnset":"00E1BE42FC1B062D","types":[0,0]},{"abilities":[51,0],"address":3305396,"base_stats":[40,30,30,85,55,30],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":25,"species":310}],"friendship":70,"id":309,"learnset":{"address":3315502,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":45},{"level":1,"move_id":55},{"level":7,"move_id":48},{"level":13,"move_id":17},{"level":21,"move_id":54},{"level":31,"move_id":98},{"level":43,"move_id":228},{"level":55,"move_id":97}]},"tmhm_learnset":"00087E8284133264","types":[11,2]},{"abilities":[51,0],"address":3305424,"base_stats":[60,50,100,65,85,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":310,"learnset":{"address":3315524,"moves":[{"level":1,"move_id":45},{"level":1,"move_id":55},{"level":1,"move_id":346},{"level":1,"move_id":17},{"level":3,"move_id":55},{"level":7,"move_id":48},{"level":13,"move_id":17},{"level":21,"move_id":54},{"level":25,"move_id":182},{"level":33,"move_id":254},{"level":33,"move_id":256},{"level":47,"move_id":255},{"level":61,"move_id":56}]},"tmhm_learnset":"00187E8284137264","types":[11,2]},{"abilities":[33,0],"address":3305452,"base_stats":[40,30,32,65,50,52],"catch_rate":200,"evolutions":[{"method":"LEVEL","param":22,"species":312}],"friendship":70,"id":311,"learnset":{"address":3315552,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":7,"move_id":98},{"level":13,"move_id":230},{"level":19,"move_id":346},{"level":25,"move_id":61},{"level":31,"move_id":97},{"level":37,"move_id":54},{"level":37,"move_id":114}]},"tmhm_learnset":"00403E00A4373624","types":[6,11]},{"abilities":[22,0],"address":3305480,"base_stats":[70,60,62,60,80,82],"catch_rate":75,"evolutions":[],"friendship":70,"id":312,"learnset":{"address":3315576,"moves":[{"level":1,"move_id":145},{"level":1,"move_id":98},{"level":1,"move_id":230},{"level":1,"move_id":346},{"level":7,"move_id":98},{"level":13,"move_id":230},{"level":19,"move_id":346},{"level":26,"move_id":16},{"level":33,"move_id":184},{"level":40,"move_id":78},{"level":47,"move_id":318},{"level":53,"move_id":18}]},"tmhm_learnset":"00403E80A4377624","types":[6,2]},{"abilities":[41,12],"address":3305508,"base_stats":[130,70,35,60,70,35],"catch_rate":125,"evolutions":[{"method":"LEVEL","param":40,"species":314}],"friendship":70,"id":313,"learnset":{"address":3315602,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":55},{"level":1,"move_id":150},{"level":5,"move_id":45},{"level":10,"move_id":55},{"level":14,"move_id":205},{"level":19,"move_id":250},{"level":23,"move_id":310},{"level":28,"move_id":352},{"level":32,"move_id":54},{"level":37,"move_id":156},{"level":41,"move_id":323},{"level":46,"move_id":133},{"level":50,"move_id":56}]},"tmhm_learnset":"03B01E4086133274","types":[11,11]},{"abilities":[41,12],"address":3305536,"base_stats":[170,90,45,60,90,45],"catch_rate":60,"evolutions":[],"friendship":70,"id":314,"learnset":{"address":3315634,"moves":[{"level":1,"move_id":150},{"level":1,"move_id":45},{"level":1,"move_id":55},{"level":1,"move_id":205},{"level":5,"move_id":45},{"level":10,"move_id":55},{"level":14,"move_id":205},{"level":19,"move_id":250},{"level":23,"move_id":310},{"level":28,"move_id":352},{"level":32,"move_id":54},{"level":37,"move_id":156},{"level":44,"move_id":323},{"level":52,"move_id":133},{"level":59,"move_id":56}]},"tmhm_learnset":"03B01E4086137274","types":[11,11]},{"abilities":[56,0],"address":3305564,"base_stats":[50,45,45,50,35,35],"catch_rate":255,"evolutions":[{"method":"ITEM","param":94,"species":316}],"friendship":70,"id":315,"learnset":{"address":3315666,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":45},{"level":1,"move_id":33},{"level":3,"move_id":39},{"level":7,"move_id":213},{"level":13,"move_id":47},{"level":15,"move_id":3},{"level":19,"move_id":274},{"level":25,"move_id":204},{"level":27,"move_id":185},{"level":31,"move_id":343},{"level":37,"move_id":215},{"level":39,"move_id":38}]},"tmhm_learnset":"00401E02ADFB362C","types":[0,0]},{"abilities":[56,0],"address":3305592,"base_stats":[70,65,65,70,55,55],"catch_rate":60,"evolutions":[],"friendship":70,"id":316,"learnset":{"address":3315696,"moves":[{"level":1,"move_id":45},{"level":1,"move_id":213},{"level":1,"move_id":47},{"level":1,"move_id":3}]},"tmhm_learnset":"00E01E02ADFB762C","types":[0,0]},{"abilities":[16,0],"address":3305620,"base_stats":[60,90,70,40,60,120],"catch_rate":200,"evolutions":[],"friendship":70,"id":317,"learnset":{"address":3315706,"moves":[{"level":1,"move_id":168},{"level":1,"move_id":39},{"level":1,"move_id":310},{"level":1,"move_id":122},{"level":1,"move_id":10},{"level":4,"move_id":20},{"level":7,"move_id":185},{"level":12,"move_id":154},{"level":17,"move_id":60},{"level":24,"move_id":103},{"level":31,"move_id":163},{"level":40,"move_id":164},{"level":49,"move_id":246}]},"tmhm_learnset":"00E5BEE6EDF33625","types":[0,0]},{"abilities":[26,0],"address":3305648,"base_stats":[40,40,55,55,40,70],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":36,"species":319}],"friendship":70,"id":318,"learnset":{"address":3315734,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":93},{"level":3,"move_id":106},{"level":5,"move_id":229},{"level":7,"move_id":189},{"level":11,"move_id":60},{"level":15,"move_id":317},{"level":19,"move_id":120},{"level":25,"move_id":246},{"level":31,"move_id":201},{"level":37,"move_id":322},{"level":45,"move_id":153}]},"tmhm_learnset":"00408E51BE339620","types":[4,14]},{"abilities":[26,0],"address":3305676,"base_stats":[60,70,105,75,70,120],"catch_rate":90,"evolutions":[],"friendship":70,"id":319,"learnset":{"address":3315764,"moves":[{"level":1,"move_id":100},{"level":1,"move_id":93},{"level":1,"move_id":106},{"level":1,"move_id":229},{"level":3,"move_id":106},{"level":5,"move_id":229},{"level":7,"move_id":189},{"level":11,"move_id":60},{"level":15,"move_id":317},{"level":19,"move_id":120},{"level":25,"move_id":246},{"level":31,"move_id":201},{"level":36,"move_id":63},{"level":42,"move_id":322},{"level":55,"move_id":153}]},"tmhm_learnset":"00E08E51BE33D620","types":[4,14]},{"abilities":[5,42],"address":3305704,"base_stats":[30,45,135,30,45,90],"catch_rate":255,"evolutions":[],"friendship":70,"id":320,"learnset":{"address":3315796,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":7,"move_id":106},{"level":13,"move_id":88},{"level":16,"move_id":335},{"level":22,"move_id":86},{"level":28,"move_id":157},{"level":31,"move_id":201},{"level":37,"move_id":156},{"level":43,"move_id":192},{"level":46,"move_id":199}]},"tmhm_learnset":"00A01F5287910E20","types":[5,5]},{"abilities":[73,0],"address":3305732,"base_stats":[70,85,140,20,85,70],"catch_rate":90,"evolutions":[],"friendship":70,"id":321,"learnset":{"address":3315824,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":52},{"level":4,"move_id":123},{"level":7,"move_id":174},{"level":14,"move_id":108},{"level":17,"move_id":83},{"level":20,"move_id":34},{"level":27,"move_id":182},{"level":30,"move_id":53},{"level":33,"move_id":334},{"level":40,"move_id":133},{"level":43,"move_id":175},{"level":46,"move_id":257}]},"tmhm_learnset":"00A21E2C84510620","types":[10,10]},{"abilities":[51,0],"address":3305760,"base_stats":[50,75,75,50,65,65],"catch_rate":45,"evolutions":[],"friendship":35,"id":322,"learnset":{"address":3315856,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":10},{"level":5,"move_id":193},{"level":9,"move_id":101},{"level":13,"move_id":310},{"level":17,"move_id":154},{"level":21,"move_id":252},{"level":25,"move_id":197},{"level":29,"move_id":185},{"level":33,"move_id":282},{"level":37,"move_id":109},{"level":41,"move_id":247},{"level":45,"move_id":212}]},"tmhm_learnset":"00C53FC2FC130E2D","types":[17,7]},{"abilities":[12,0],"address":3305788,"base_stats":[50,48,43,60,46,41],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":30,"species":324}],"friendship":70,"id":323,"learnset":{"address":3315888,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":189},{"level":6,"move_id":300},{"level":6,"move_id":346},{"level":11,"move_id":55},{"level":16,"move_id":222},{"level":21,"move_id":133},{"level":26,"move_id":156},{"level":26,"move_id":173},{"level":31,"move_id":89},{"level":36,"move_id":248},{"level":41,"move_id":90}]},"tmhm_learnset":"03101E5086133264","types":[11,4]},{"abilities":[12,0],"address":3305816,"base_stats":[110,78,73,60,76,71],"catch_rate":75,"evolutions":[],"friendship":70,"id":324,"learnset":{"address":3315918,"moves":[{"level":1,"move_id":321},{"level":1,"move_id":189},{"level":1,"move_id":300},{"level":1,"move_id":346},{"level":6,"move_id":300},{"level":6,"move_id":346},{"level":11,"move_id":55},{"level":16,"move_id":222},{"level":21,"move_id":133},{"level":26,"move_id":156},{"level":26,"move_id":173},{"level":36,"move_id":89},{"level":46,"move_id":248},{"level":56,"move_id":90}]},"tmhm_learnset":"03B01E5086137264","types":[11,4]},{"abilities":[33,0],"address":3305844,"base_stats":[43,30,55,97,40,65],"catch_rate":225,"evolutions":[],"friendship":70,"id":325,"learnset":{"address":3315948,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":4,"move_id":204},{"level":12,"move_id":55},{"level":16,"move_id":97},{"level":24,"move_id":36},{"level":28,"move_id":213},{"level":36,"move_id":186},{"level":40,"move_id":175},{"level":48,"move_id":219}]},"tmhm_learnset":"03101E00841B3264","types":[11,11]},{"abilities":[52,75],"address":3305872,"base_stats":[43,80,65,35,50,35],"catch_rate":205,"evolutions":[{"method":"LEVEL","param":30,"species":327}],"friendship":70,"id":326,"learnset":{"address":3315974,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":7,"move_id":106},{"level":10,"move_id":11},{"level":13,"move_id":43},{"level":20,"move_id":61},{"level":23,"move_id":182},{"level":26,"move_id":282},{"level":32,"move_id":269},{"level":35,"move_id":152},{"level":38,"move_id":14},{"level":44,"move_id":12}]},"tmhm_learnset":"01B41EC8CC133A64","types":[11,11]},{"abilities":[52,75],"address":3305900,"base_stats":[63,120,85,55,90,55],"catch_rate":155,"evolutions":[],"friendship":70,"id":327,"learnset":{"address":3316004,"moves":[{"level":1,"move_id":145},{"level":1,"move_id":106},{"level":1,"move_id":11},{"level":1,"move_id":43},{"level":7,"move_id":106},{"level":10,"move_id":11},{"level":13,"move_id":43},{"level":20,"move_id":61},{"level":23,"move_id":182},{"level":26,"move_id":282},{"level":34,"move_id":269},{"level":39,"move_id":152},{"level":44,"move_id":14},{"level":52,"move_id":12}]},"tmhm_learnset":"03B41EC8CC137A64","types":[11,17]},{"abilities":[33,0],"address":3305928,"base_stats":[20,15,20,80,10,55],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":30,"species":329}],"friendship":70,"id":328,"learnset":{"address":3316034,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":150},{"level":15,"move_id":33},{"level":30,"move_id":175}]},"tmhm_learnset":"03101E0084133264","types":[11,11]},{"abilities":[63,0],"address":3305956,"base_stats":[95,60,79,81,100,125],"catch_rate":60,"evolutions":[],"friendship":70,"id":329,"learnset":{"address":3316048,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":55},{"level":5,"move_id":35},{"level":10,"move_id":346},{"level":15,"move_id":287},{"level":20,"move_id":352},{"level":25,"move_id":239},{"level":30,"move_id":105},{"level":35,"move_id":240},{"level":40,"move_id":56},{"level":45,"move_id":213},{"level":50,"move_id":219}]},"tmhm_learnset":"03101E00845B7264","types":[11,11]},{"abilities":[24,0],"address":3305984,"base_stats":[45,90,20,65,65,20],"catch_rate":225,"evolutions":[{"method":"LEVEL","param":30,"species":331}],"friendship":35,"id":330,"learnset":{"address":3316078,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":44},{"level":7,"move_id":99},{"level":13,"move_id":116},{"level":16,"move_id":184},{"level":22,"move_id":242},{"level":28,"move_id":103},{"level":31,"move_id":36},{"level":37,"move_id":207},{"level":43,"move_id":97}]},"tmhm_learnset":"03103F0084133A64","types":[11,17]},{"abilities":[24,0],"address":3306012,"base_stats":[70,120,40,95,95,40],"catch_rate":60,"evolutions":[],"friendship":35,"id":331,"learnset":{"address":3316104,"moves":[{"level":1,"move_id":43},{"level":1,"move_id":44},{"level":1,"move_id":99},{"level":1,"move_id":116},{"level":7,"move_id":99},{"level":13,"move_id":116},{"level":16,"move_id":184},{"level":22,"move_id":242},{"level":28,"move_id":103},{"level":33,"move_id":163},{"level":38,"move_id":269},{"level":43,"move_id":207},{"level":48,"move_id":130},{"level":53,"move_id":97}]},"tmhm_learnset":"03B03F4086137A74","types":[11,17]},{"abilities":[52,71],"address":3306040,"base_stats":[45,100,45,10,45,45],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":35,"species":333}],"friendship":70,"id":332,"learnset":{"address":3316134,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":44},{"level":9,"move_id":28},{"level":17,"move_id":185},{"level":25,"move_id":328},{"level":33,"move_id":242},{"level":41,"move_id":91},{"level":49,"move_id":201},{"level":57,"move_id":63}]},"tmhm_learnset":"00A01E508E354620","types":[4,4]},{"abilities":[26,26],"address":3306068,"base_stats":[50,70,50,70,50,50],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":45,"species":334}],"friendship":70,"id":333,"learnset":{"address":3316158,"moves":[{"level":1,"move_id":44},{"level":1,"move_id":28},{"level":1,"move_id":185},{"level":1,"move_id":328},{"level":9,"move_id":28},{"level":17,"move_id":185},{"level":25,"move_id":328},{"level":33,"move_id":242},{"level":35,"move_id":225},{"level":41,"move_id":103},{"level":49,"move_id":201},{"level":57,"move_id":63}]},"tmhm_learnset":"00A85E508E354620","types":[4,16]},{"abilities":[26,26],"address":3306096,"base_stats":[80,100,80,100,80,80],"catch_rate":45,"evolutions":[],"friendship":70,"id":334,"learnset":{"address":3316184,"moves":[{"level":1,"move_id":44},{"level":1,"move_id":28},{"level":1,"move_id":185},{"level":1,"move_id":328},{"level":9,"move_id":28},{"level":17,"move_id":185},{"level":25,"move_id":328},{"level":33,"move_id":242},{"level":35,"move_id":225},{"level":41,"move_id":103},{"level":53,"move_id":201},{"level":65,"move_id":63}]},"tmhm_learnset":"00A85E748E754622","types":[4,16]},{"abilities":[47,62],"address":3306124,"base_stats":[72,60,30,25,20,30],"catch_rate":180,"evolutions":[{"method":"LEVEL","param":24,"species":336}],"friendship":70,"id":335,"learnset":{"address":3316210,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":116},{"level":4,"move_id":28},{"level":10,"move_id":292},{"level":13,"move_id":233},{"level":19,"move_id":252},{"level":22,"move_id":18},{"level":28,"move_id":282},{"level":31,"move_id":265},{"level":37,"move_id":187},{"level":40,"move_id":203},{"level":46,"move_id":69},{"level":49,"move_id":179}]},"tmhm_learnset":"00B01E40CE1306A1","types":[1,1]},{"abilities":[47,62],"address":3306152,"base_stats":[144,120,60,50,40,60],"catch_rate":200,"evolutions":[],"friendship":70,"id":336,"learnset":{"address":3316242,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":116},{"level":1,"move_id":28},{"level":1,"move_id":292},{"level":4,"move_id":28},{"level":10,"move_id":292},{"level":13,"move_id":233},{"level":19,"move_id":252},{"level":22,"move_id":18},{"level":29,"move_id":282},{"level":33,"move_id":265},{"level":40,"move_id":187},{"level":44,"move_id":203},{"level":51,"move_id":69},{"level":55,"move_id":179}]},"tmhm_learnset":"00B01E40CE1346A1","types":[1,1]},{"abilities":[9,31],"address":3306180,"base_stats":[40,45,40,65,65,40],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":26,"species":338}],"friendship":70,"id":337,"learnset":{"address":3316274,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":4,"move_id":86},{"level":9,"move_id":43},{"level":12,"move_id":336},{"level":17,"move_id":98},{"level":20,"move_id":209},{"level":25,"move_id":316},{"level":28,"move_id":46},{"level":33,"move_id":44},{"level":36,"move_id":87},{"level":41,"move_id":268}]},"tmhm_learnset":"00603E0285D30230","types":[13,13]},{"abilities":[9,31],"address":3306208,"base_stats":[70,75,60,105,105,60],"catch_rate":45,"evolutions":[],"friendship":70,"id":338,"learnset":{"address":3316304,"moves":[{"level":1,"move_id":86},{"level":1,"move_id":43},{"level":1,"move_id":336},{"level":1,"move_id":33},{"level":4,"move_id":86},{"level":9,"move_id":43},{"level":12,"move_id":336},{"level":17,"move_id":98},{"level":20,"move_id":209},{"level":25,"move_id":316},{"level":31,"move_id":46},{"level":39,"move_id":44},{"level":45,"move_id":87},{"level":53,"move_id":268}]},"tmhm_learnset":"00603E0285D34230","types":[13,13]},{"abilities":[12,0],"address":3306236,"base_stats":[60,60,40,35,65,45],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":33,"species":340}],"friendship":70,"id":339,"learnset":{"address":3316334,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":45},{"level":1,"move_id":33},{"level":11,"move_id":52},{"level":19,"move_id":222},{"level":25,"move_id":116},{"level":29,"move_id":36},{"level":31,"move_id":133},{"level":35,"move_id":89},{"level":41,"move_id":53},{"level":49,"move_id":38}]},"tmhm_learnset":"00A21E748E110620","types":[10,4]},{"abilities":[40,0],"address":3306264,"base_stats":[70,100,70,40,105,75],"catch_rate":150,"evolutions":[],"friendship":70,"id":340,"learnset":{"address":3316360,"moves":[{"level":1,"move_id":45},{"level":1,"move_id":33},{"level":1,"move_id":52},{"level":1,"move_id":222},{"level":11,"move_id":52},{"level":19,"move_id":222},{"level":25,"move_id":116},{"level":29,"move_id":36},{"level":31,"move_id":133},{"level":33,"move_id":157},{"level":37,"move_id":89},{"level":45,"move_id":284},{"level":55,"move_id":90}]},"tmhm_learnset":"00A21E748E114630","types":[10,4]},{"abilities":[47,0],"address":3306292,"base_stats":[70,40,50,25,55,50],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":32,"species":342}],"friendship":70,"id":341,"learnset":{"address":3316388,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":181},{"level":1,"move_id":45},{"level":1,"move_id":55},{"level":7,"move_id":227},{"level":13,"move_id":301},{"level":19,"move_id":34},{"level":25,"move_id":62},{"level":31,"move_id":258},{"level":37,"move_id":156},{"level":37,"move_id":173},{"level":43,"move_id":59},{"level":49,"move_id":329}]},"tmhm_learnset":"03B01E4086533264","types":[15,11]},{"abilities":[47,0],"address":3306320,"base_stats":[90,60,70,45,75,70],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":44,"species":343}],"friendship":70,"id":342,"learnset":{"address":3316416,"moves":[{"level":1,"move_id":181},{"level":1,"move_id":45},{"level":1,"move_id":55},{"level":1,"move_id":227},{"level":7,"move_id":227},{"level":13,"move_id":301},{"level":19,"move_id":34},{"level":25,"move_id":62},{"level":31,"move_id":258},{"level":39,"move_id":156},{"level":39,"move_id":173},{"level":47,"move_id":59},{"level":55,"move_id":329}]},"tmhm_learnset":"03B01E4086533274","types":[15,11]},{"abilities":[47,0],"address":3306348,"base_stats":[110,80,90,65,95,90],"catch_rate":45,"evolutions":[],"friendship":70,"id":343,"learnset":{"address":3316444,"moves":[{"level":1,"move_id":181},{"level":1,"move_id":45},{"level":1,"move_id":55},{"level":1,"move_id":227},{"level":7,"move_id":227},{"level":13,"move_id":301},{"level":19,"move_id":34},{"level":25,"move_id":62},{"level":31,"move_id":258},{"level":39,"move_id":156},{"level":39,"move_id":173},{"level":50,"move_id":59},{"level":61,"move_id":329}]},"tmhm_learnset":"03B01E4086537274","types":[15,11]},{"abilities":[8,0],"address":3306376,"base_stats":[50,85,40,35,85,40],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":32,"species":345}],"friendship":35,"id":344,"learnset":{"address":3316472,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":40},{"level":1,"move_id":43},{"level":5,"move_id":71},{"level":9,"move_id":74},{"level":13,"move_id":73},{"level":17,"move_id":28},{"level":21,"move_id":42},{"level":25,"move_id":275},{"level":29,"move_id":185},{"level":33,"move_id":191},{"level":37,"move_id":302},{"level":41,"move_id":178},{"level":45,"move_id":201}]},"tmhm_learnset":"00441E1084350721","types":[12,12]},{"abilities":[8,0],"address":3306404,"base_stats":[70,115,60,55,115,60],"catch_rate":60,"evolutions":[],"friendship":35,"id":345,"learnset":{"address":3316504,"moves":[{"level":1,"move_id":40},{"level":1,"move_id":43},{"level":1,"move_id":71},{"level":1,"move_id":74},{"level":5,"move_id":71},{"level":9,"move_id":74},{"level":13,"move_id":73},{"level":17,"move_id":28},{"level":21,"move_id":42},{"level":25,"move_id":275},{"level":29,"move_id":185},{"level":35,"move_id":191},{"level":41,"move_id":302},{"level":47,"move_id":178},{"level":53,"move_id":201}]},"tmhm_learnset":"00641E1084354721","types":[12,17]},{"abilities":[39,0],"address":3306432,"base_stats":[50,50,50,50,50,50],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":42,"species":347}],"friendship":70,"id":346,"learnset":{"address":3316536,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":181},{"level":1,"move_id":43},{"level":7,"move_id":104},{"level":10,"move_id":44},{"level":16,"move_id":196},{"level":19,"move_id":29},{"level":25,"move_id":182},{"level":28,"move_id":242},{"level":34,"move_id":58},{"level":37,"move_id":258},{"level":43,"move_id":59}]},"tmhm_learnset":"00401E00A41BB264","types":[15,15]},{"abilities":[39,0],"address":3306460,"base_stats":[80,80,80,80,80,80],"catch_rate":75,"evolutions":[],"friendship":70,"id":347,"learnset":{"address":3316564,"moves":[{"level":1,"move_id":181},{"level":1,"move_id":43},{"level":1,"move_id":104},{"level":1,"move_id":44},{"level":7,"move_id":104},{"level":10,"move_id":44},{"level":16,"move_id":196},{"level":19,"move_id":29},{"level":25,"move_id":182},{"level":28,"move_id":242},{"level":34,"move_id":58},{"level":42,"move_id":258},{"level":53,"move_id":59},{"level":61,"move_id":329}]},"tmhm_learnset":"00401F00A61BFA64","types":[15,15]},{"abilities":[26,0],"address":3306488,"base_stats":[70,55,65,70,95,85],"catch_rate":45,"evolutions":[],"friendship":70,"id":348,"learnset":{"address":3316594,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":7,"move_id":93},{"level":13,"move_id":88},{"level":19,"move_id":95},{"level":25,"move_id":149},{"level":31,"move_id":322},{"level":37,"move_id":94},{"level":43,"move_id":248},{"level":49,"move_id":153}]},"tmhm_learnset":"00408E51B61BD228","types":[5,14]},{"abilities":[26,0],"address":3306516,"base_stats":[70,95,85,70,55,65],"catch_rate":45,"evolutions":[],"friendship":70,"id":349,"learnset":{"address":3316620,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":7,"move_id":93},{"level":13,"move_id":88},{"level":19,"move_id":83},{"level":25,"move_id":149},{"level":31,"move_id":322},{"level":37,"move_id":157},{"level":43,"move_id":76},{"level":49,"move_id":153}]},"tmhm_learnset":"00428E75B639C628","types":[5,14]},{"abilities":[47,37],"address":3306544,"base_stats":[50,20,40,20,20,40],"catch_rate":150,"evolutions":[{"method":"FRIENDSHIP","param":0,"species":183}],"friendship":70,"id":350,"learnset":{"address":3316646,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":145},{"level":1,"move_id":150},{"level":3,"move_id":204},{"level":6,"move_id":39},{"level":10,"move_id":145},{"level":15,"move_id":21},{"level":21,"move_id":55}]},"tmhm_learnset":"01101E0084533264","types":[0,0]},{"abilities":[47,20],"address":3306572,"base_stats":[60,25,35,60,70,80],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":32,"species":352}],"friendship":70,"id":351,"learnset":{"address":3316666,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":149},{"level":1,"move_id":150},{"level":7,"move_id":149},{"level":10,"move_id":316},{"level":16,"move_id":60},{"level":19,"move_id":244},{"level":25,"move_id":109},{"level":28,"move_id":277},{"level":34,"move_id":94},{"level":37,"move_id":156},{"level":37,"move_id":173},{"level":43,"move_id":340}]},"tmhm_learnset":"0041BF03B4538E28","types":[14,14]},{"abilities":[47,20],"address":3306600,"base_stats":[80,45,65,80,90,110],"catch_rate":60,"evolutions":[],"friendship":70,"id":352,"learnset":{"address":3316696,"moves":[{"level":1,"move_id":150},{"level":1,"move_id":149},{"level":1,"move_id":316},{"level":1,"move_id":60},{"level":7,"move_id":149},{"level":10,"move_id":316},{"level":16,"move_id":60},{"level":19,"move_id":244},{"level":25,"move_id":109},{"level":28,"move_id":277},{"level":37,"move_id":94},{"level":43,"move_id":156},{"level":43,"move_id":173},{"level":55,"move_id":340}]},"tmhm_learnset":"0041BF03B453CE29","types":[14,14]},{"abilities":[57,0],"address":3306628,"base_stats":[60,50,40,95,85,75],"catch_rate":200,"evolutions":[],"friendship":70,"id":353,"learnset":{"address":3316726,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":98},{"level":1,"move_id":45},{"level":4,"move_id":86},{"level":10,"move_id":98},{"level":13,"move_id":270},{"level":19,"move_id":209},{"level":22,"move_id":227},{"level":28,"move_id":313},{"level":31,"move_id":268},{"level":37,"move_id":87},{"level":40,"move_id":226},{"level":47,"move_id":97}]},"tmhm_learnset":"00401E0285D38220","types":[13,13]},{"abilities":[58,0],"address":3306656,"base_stats":[60,40,50,95,75,85],"catch_rate":200,"evolutions":[],"friendship":70,"id":354,"learnset":{"address":3316756,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":98},{"level":1,"move_id":45},{"level":4,"move_id":86},{"level":10,"move_id":98},{"level":13,"move_id":270},{"level":19,"move_id":209},{"level":22,"move_id":227},{"level":28,"move_id":204},{"level":31,"move_id":268},{"level":37,"move_id":87},{"level":40,"move_id":226},{"level":47,"move_id":97}]},"tmhm_learnset":"00401E0285D38220","types":[13,13]},{"abilities":[52,22],"address":3306684,"base_stats":[50,85,85,50,55,55],"catch_rate":45,"evolutions":[],"friendship":70,"id":355,"learnset":{"address":3316786,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":310},{"level":6,"move_id":313},{"level":11,"move_id":44},{"level":16,"move_id":230},{"level":21,"move_id":11},{"level":26,"move_id":185},{"level":31,"move_id":226},{"level":36,"move_id":242},{"level":41,"move_id":334},{"level":46,"move_id":254},{"level":46,"move_id":256},{"level":46,"move_id":255}]},"tmhm_learnset":"00A01F7CC4335E21","types":[8,8]},{"abilities":[74,0],"address":3306712,"base_stats":[30,40,55,60,40,55],"catch_rate":180,"evolutions":[{"method":"LEVEL","param":37,"species":357}],"friendship":70,"id":356,"learnset":{"address":3316818,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":117},{"level":4,"move_id":96},{"level":9,"move_id":93},{"level":12,"move_id":197},{"level":18,"move_id":237},{"level":22,"move_id":170},{"level":28,"move_id":347},{"level":32,"move_id":136},{"level":38,"move_id":244},{"level":42,"move_id":179},{"level":48,"move_id":105}]},"tmhm_learnset":"00E01E41F41386A9","types":[1,14]},{"abilities":[74,0],"address":3306740,"base_stats":[60,60,75,80,60,75],"catch_rate":90,"evolutions":[],"friendship":70,"id":357,"learnset":{"address":3316848,"moves":[{"level":1,"move_id":7},{"level":1,"move_id":9},{"level":1,"move_id":8},{"level":1,"move_id":117},{"level":1,"move_id":96},{"level":1,"move_id":93},{"level":1,"move_id":197},{"level":4,"move_id":96},{"level":9,"move_id":93},{"level":12,"move_id":197},{"level":18,"move_id":237},{"level":22,"move_id":170},{"level":28,"move_id":347},{"level":32,"move_id":136},{"level":40,"move_id":244},{"level":46,"move_id":179},{"level":54,"move_id":105}]},"tmhm_learnset":"00E01E41F413C6A9","types":[1,14]},{"abilities":[30,0],"address":3306768,"base_stats":[45,40,60,50,40,75],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":35,"species":359}],"friendship":70,"id":358,"learnset":{"address":3316884,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":64},{"level":1,"move_id":45},{"level":8,"move_id":310},{"level":11,"move_id":47},{"level":18,"move_id":31},{"level":21,"move_id":219},{"level":28,"move_id":54},{"level":31,"move_id":36},{"level":38,"move_id":119},{"level":41,"move_id":287},{"level":48,"move_id":195}]},"tmhm_learnset":"00087E80843B1620","types":[0,2]},{"abilities":[30,0],"address":3306796,"base_stats":[75,70,90,80,70,105],"catch_rate":45,"evolutions":[],"friendship":70,"id":359,"learnset":{"address":3316912,"moves":[{"level":1,"move_id":64},{"level":1,"move_id":45},{"level":1,"move_id":310},{"level":1,"move_id":47},{"level":8,"move_id":310},{"level":11,"move_id":47},{"level":18,"move_id":31},{"level":21,"move_id":219},{"level":28,"move_id":54},{"level":31,"move_id":36},{"level":35,"move_id":225},{"level":40,"move_id":349},{"level":45,"move_id":287},{"level":54,"move_id":195},{"level":59,"move_id":143}]},"tmhm_learnset":"00887EA4867B5632","types":[16,2]},{"abilities":[23,0],"address":3306824,"base_stats":[95,23,48,23,23,48],"catch_rate":125,"evolutions":[{"method":"LEVEL","param":15,"species":202}],"friendship":70,"id":360,"learnset":{"address":3316944,"moves":[{"level":1,"move_id":68},{"level":1,"move_id":150},{"level":1,"move_id":204},{"level":1,"move_id":227},{"level":15,"move_id":68},{"level":15,"move_id":243},{"level":15,"move_id":219},{"level":15,"move_id":194}]},"tmhm_learnset":"0000000000000000","types":[14,14]},{"abilities":[26,0],"address":3306852,"base_stats":[20,40,90,25,30,90],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":37,"species":362}],"friendship":35,"id":361,"learnset":{"address":3316962,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":101},{"level":5,"move_id":50},{"level":12,"move_id":193},{"level":16,"move_id":310},{"level":23,"move_id":109},{"level":27,"move_id":228},{"level":34,"move_id":174},{"level":38,"move_id":261},{"level":45,"move_id":212},{"level":49,"move_id":248}]},"tmhm_learnset":"0041BF00B4133E28","types":[7,7]},{"abilities":[46,0],"address":3306880,"base_stats":[40,70,130,25,60,130],"catch_rate":90,"evolutions":[],"friendship":35,"id":362,"learnset":{"address":3316990,"moves":[{"level":1,"move_id":20},{"level":1,"move_id":43},{"level":1,"move_id":101},{"level":1,"move_id":50},{"level":5,"move_id":50},{"level":12,"move_id":193},{"level":16,"move_id":310},{"level":23,"move_id":109},{"level":27,"move_id":228},{"level":34,"move_id":174},{"level":37,"move_id":325},{"level":41,"move_id":261},{"level":51,"move_id":212},{"level":58,"move_id":248}]},"tmhm_learnset":"00E1BF40B6137E29","types":[7,7]},{"abilities":[30,38],"address":3306908,"base_stats":[50,60,45,65,100,80],"catch_rate":150,"evolutions":[],"friendship":70,"id":363,"learnset":{"address":3317020,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":71},{"level":5,"move_id":74},{"level":9,"move_id":40},{"level":13,"move_id":78},{"level":17,"move_id":72},{"level":21,"move_id":73},{"level":25,"move_id":345},{"level":29,"move_id":320},{"level":33,"move_id":202},{"level":37,"move_id":230},{"level":41,"move_id":275},{"level":45,"move_id":92},{"level":49,"move_id":80},{"level":53,"move_id":312},{"level":57,"move_id":235}]},"tmhm_learnset":"00441E08A4350720","types":[12,3]},{"abilities":[54,0],"address":3306936,"base_stats":[60,60,60,30,35,35],"catch_rate":255,"evolutions":[{"method":"LEVEL","param":18,"species":365}],"friendship":70,"id":364,"learnset":{"address":3317058,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":1,"move_id":281},{"level":7,"move_id":227},{"level":13,"move_id":303},{"level":19,"move_id":185},{"level":25,"move_id":133},{"level":31,"move_id":343},{"level":37,"move_id":68},{"level":43,"move_id":175}]},"tmhm_learnset":"00A41EA6E5B336A5","types":[0,0]},{"abilities":[72,0],"address":3306964,"base_stats":[80,80,80,90,55,55],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":36,"species":366}],"friendship":70,"id":365,"learnset":{"address":3317082,"moves":[{"level":1,"move_id":10},{"level":1,"move_id":116},{"level":1,"move_id":227},{"level":1,"move_id":253},{"level":7,"move_id":227},{"level":13,"move_id":253},{"level":19,"move_id":154},{"level":25,"move_id":203},{"level":31,"move_id":163},{"level":37,"move_id":68},{"level":43,"move_id":264},{"level":49,"move_id":179}]},"tmhm_learnset":"00A41EA6E7B33EB5","types":[0,0]},{"abilities":[54,0],"address":3306992,"base_stats":[150,160,100,100,95,65],"catch_rate":45,"evolutions":[],"friendship":70,"id":366,"learnset":{"address":3317108,"moves":[{"level":1,"move_id":10},{"level":1,"move_id":281},{"level":1,"move_id":227},{"level":1,"move_id":303},{"level":7,"move_id":227},{"level":13,"move_id":303},{"level":19,"move_id":185},{"level":25,"move_id":133},{"level":31,"move_id":343},{"level":36,"move_id":207},{"level":37,"move_id":68},{"level":43,"move_id":175}]},"tmhm_learnset":"00A41EA6E7B37EB5","types":[0,0]},{"abilities":[64,60],"address":3307020,"base_stats":[70,43,53,40,43,53],"catch_rate":225,"evolutions":[{"method":"LEVEL","param":26,"species":368}],"friendship":70,"id":367,"learnset":{"address":3317134,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":6,"move_id":281},{"level":9,"move_id":139},{"level":14,"move_id":124},{"level":17,"move_id":133},{"level":23,"move_id":227},{"level":28,"move_id":92},{"level":34,"move_id":254},{"level":34,"move_id":255},{"level":34,"move_id":256},{"level":39,"move_id":188}]},"tmhm_learnset":"00A11E0AA4371724","types":[3,3]},{"abilities":[64,60],"address":3307048,"base_stats":[100,73,83,55,73,83],"catch_rate":75,"evolutions":[],"friendship":70,"id":368,"learnset":{"address":3317164,"moves":[{"level":1,"move_id":1},{"level":1,"move_id":281},{"level":1,"move_id":139},{"level":1,"move_id":124},{"level":6,"move_id":281},{"level":9,"move_id":139},{"level":14,"move_id":124},{"level":17,"move_id":133},{"level":23,"move_id":227},{"level":26,"move_id":34},{"level":31,"move_id":92},{"level":40,"move_id":254},{"level":40,"move_id":255},{"level":40,"move_id":256},{"level":48,"move_id":188}]},"tmhm_learnset":"00A11E0AA4375724","types":[3,3]},{"abilities":[34,0],"address":3307076,"base_stats":[99,68,83,51,72,87],"catch_rate":200,"evolutions":[],"friendship":70,"id":369,"learnset":{"address":3317196,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":16},{"level":7,"move_id":74},{"level":11,"move_id":75},{"level":17,"move_id":23},{"level":21,"move_id":230},{"level":27,"move_id":18},{"level":31,"move_id":345},{"level":37,"move_id":34},{"level":41,"move_id":76},{"level":47,"move_id":235}]},"tmhm_learnset":"00EC5E80863D4730","types":[12,2]},{"abilities":[43,0],"address":3307104,"base_stats":[64,51,23,28,51,23],"catch_rate":190,"evolutions":[{"method":"LEVEL","param":20,"species":371}],"friendship":70,"id":370,"learnset":{"address":3317224,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":1},{"level":5,"move_id":253},{"level":11,"move_id":310},{"level":15,"move_id":336},{"level":21,"move_id":48},{"level":25,"move_id":23},{"level":31,"move_id":103},{"level":35,"move_id":46},{"level":41,"move_id":156},{"level":41,"move_id":214},{"level":45,"move_id":304}]},"tmhm_learnset":"00001E26A4333634","types":[0,0]},{"abilities":[43,0],"address":3307132,"base_stats":[84,71,43,48,71,43],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":40,"species":372}],"friendship":70,"id":371,"learnset":{"address":3317254,"moves":[{"level":1,"move_id":1},{"level":1,"move_id":253},{"level":1,"move_id":310},{"level":1,"move_id":336},{"level":5,"move_id":253},{"level":11,"move_id":310},{"level":15,"move_id":336},{"level":23,"move_id":48},{"level":29,"move_id":23},{"level":37,"move_id":103},{"level":43,"move_id":46},{"level":51,"move_id":156},{"level":51,"move_id":214},{"level":57,"move_id":304}]},"tmhm_learnset":"00A21F26E6333E34","types":[0,0]},{"abilities":[43,0],"address":3307160,"base_stats":[104,91,63,68,91,63],"catch_rate":45,"evolutions":[],"friendship":70,"id":372,"learnset":{"address":3317284,"moves":[{"level":1,"move_id":1},{"level":1,"move_id":253},{"level":1,"move_id":310},{"level":1,"move_id":336},{"level":5,"move_id":253},{"level":11,"move_id":310},{"level":15,"move_id":336},{"level":23,"move_id":48},{"level":29,"move_id":23},{"level":37,"move_id":103},{"level":40,"move_id":63},{"level":45,"move_id":46},{"level":55,"move_id":156},{"level":55,"move_id":214},{"level":63,"move_id":304}]},"tmhm_learnset":"00A21F26E6337E34","types":[0,0]},{"abilities":[75,0],"address":3307188,"base_stats":[35,64,85,32,74,55],"catch_rate":255,"evolutions":[{"method":"ITEM","param":192,"species":374},{"method":"ITEM","param":193,"species":375}],"friendship":70,"id":373,"learnset":{"address":3317316,"moves":[{"level":1,"move_id":128},{"level":1,"move_id":55},{"level":1,"move_id":250},{"level":1,"move_id":334}]},"tmhm_learnset":"03101E0084133264","types":[11,11]},{"abilities":[33,0],"address":3307216,"base_stats":[55,104,105,52,94,75],"catch_rate":60,"evolutions":[],"friendship":70,"id":374,"learnset":{"address":3317326,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":250},{"level":8,"move_id":44},{"level":15,"move_id":103},{"level":22,"move_id":352},{"level":29,"move_id":184},{"level":36,"move_id":242},{"level":43,"move_id":226},{"level":50,"move_id":56}]},"tmhm_learnset":"03111E4084137264","types":[11,11]},{"abilities":[33,0],"address":3307244,"base_stats":[55,84,105,52,114,75],"catch_rate":60,"evolutions":[],"friendship":70,"id":375,"learnset":{"address":3317350,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":250},{"level":8,"move_id":93},{"level":15,"move_id":97},{"level":22,"move_id":352},{"level":29,"move_id":133},{"level":36,"move_id":94},{"level":43,"move_id":226},{"level":50,"move_id":56}]},"tmhm_learnset":"03101E00B41B7264","types":[11,11]},{"abilities":[46,0],"address":3307272,"base_stats":[65,130,60,75,75,60],"catch_rate":30,"evolutions":[],"friendship":35,"id":376,"learnset":{"address":3317374,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":5,"move_id":43},{"level":9,"move_id":269},{"level":13,"move_id":98},{"level":17,"move_id":13},{"level":21,"move_id":44},{"level":26,"move_id":14},{"level":31,"move_id":104},{"level":36,"move_id":163},{"level":41,"move_id":248},{"level":46,"move_id":195}]},"tmhm_learnset":"00E53FB6A5D37E6C","types":[17,17]},{"abilities":[15,0],"address":3307300,"base_stats":[44,75,35,45,63,33],"catch_rate":225,"evolutions":[{"method":"LEVEL","param":37,"species":378}],"friendship":35,"id":377,"learnset":{"address":3317404,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":282},{"level":8,"move_id":103},{"level":13,"move_id":101},{"level":20,"move_id":174},{"level":25,"move_id":180},{"level":32,"move_id":261},{"level":37,"move_id":185},{"level":44,"move_id":247},{"level":49,"move_id":289},{"level":56,"move_id":288}]},"tmhm_learnset":"0041BF02B5930E28","types":[7,7]},{"abilities":[15,0],"address":3307328,"base_stats":[64,115,65,65,83,63],"catch_rate":45,"evolutions":[],"friendship":35,"id":378,"learnset":{"address":3317432,"moves":[{"level":1,"move_id":282},{"level":1,"move_id":103},{"level":1,"move_id":101},{"level":1,"move_id":174},{"level":8,"move_id":103},{"level":13,"move_id":101},{"level":20,"move_id":174},{"level":25,"move_id":180},{"level":32,"move_id":261},{"level":39,"move_id":185},{"level":48,"move_id":247},{"level":55,"move_id":289},{"level":64,"move_id":288}]},"tmhm_learnset":"0041BF02B5934E28","types":[7,7]},{"abilities":[61,0],"address":3307356,"base_stats":[73,100,60,65,100,60],"catch_rate":90,"evolutions":[],"friendship":70,"id":379,"learnset":{"address":3317460,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":35},{"level":7,"move_id":122},{"level":10,"move_id":44},{"level":16,"move_id":342},{"level":19,"move_id":103},{"level":25,"move_id":137},{"level":28,"move_id":242},{"level":34,"move_id":305},{"level":37,"move_id":207},{"level":43,"move_id":114}]},"tmhm_learnset":"00A13E0C8E570E20","types":[3,3]},{"abilities":[17,0],"address":3307384,"base_stats":[73,115,60,90,60,60],"catch_rate":90,"evolutions":[],"friendship":70,"id":380,"learnset":{"address":3317488,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":4,"move_id":43},{"level":7,"move_id":98},{"level":10,"move_id":14},{"level":13,"move_id":210},{"level":19,"move_id":163},{"level":25,"move_id":228},{"level":31,"move_id":306},{"level":37,"move_id":269},{"level":46,"move_id":197},{"level":55,"move_id":206}]},"tmhm_learnset":"00A03EA6EDF73E35","types":[0,0]},{"abilities":[33,69],"address":3307412,"base_stats":[100,90,130,55,45,65],"catch_rate":25,"evolutions":[],"friendship":70,"id":381,"learnset":{"address":3317518,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":8,"move_id":55},{"level":15,"move_id":317},{"level":22,"move_id":281},{"level":29,"move_id":36},{"level":36,"move_id":300},{"level":43,"move_id":246},{"level":50,"move_id":156},{"level":57,"move_id":38},{"level":64,"move_id":56}]},"tmhm_learnset":"03901E50861B726C","types":[11,5]},{"abilities":[5,69],"address":3307440,"base_stats":[50,70,100,30,40,40],"catch_rate":180,"evolutions":[{"method":"LEVEL","param":32,"species":383}],"friendship":35,"id":382,"learnset":{"address":3317546,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":4,"move_id":106},{"level":7,"move_id":189},{"level":10,"move_id":29},{"level":13,"move_id":232},{"level":17,"move_id":334},{"level":21,"move_id":46},{"level":25,"move_id":36},{"level":29,"move_id":231},{"level":34,"move_id":182},{"level":39,"move_id":319},{"level":44,"move_id":38}]},"tmhm_learnset":"00A41ED28E530634","types":[8,5]},{"abilities":[5,69],"address":3307468,"base_stats":[60,90,140,40,50,50],"catch_rate":90,"evolutions":[{"method":"LEVEL","param":42,"species":384}],"friendship":35,"id":383,"learnset":{"address":3317578,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":1,"move_id":189},{"level":1,"move_id":29},{"level":4,"move_id":106},{"level":7,"move_id":189},{"level":10,"move_id":29},{"level":13,"move_id":232},{"level":17,"move_id":334},{"level":21,"move_id":46},{"level":25,"move_id":36},{"level":29,"move_id":231},{"level":37,"move_id":182},{"level":45,"move_id":319},{"level":53,"move_id":38}]},"tmhm_learnset":"00A41ED28E530634","types":[8,5]},{"abilities":[5,69],"address":3307496,"base_stats":[70,110,180,50,60,60],"catch_rate":45,"evolutions":[],"friendship":35,"id":384,"learnset":{"address":3317610,"moves":[{"level":1,"move_id":33},{"level":1,"move_id":106},{"level":1,"move_id":189},{"level":1,"move_id":29},{"level":4,"move_id":106},{"level":7,"move_id":189},{"level":10,"move_id":29},{"level":13,"move_id":232},{"level":17,"move_id":334},{"level":21,"move_id":46},{"level":25,"move_id":36},{"level":29,"move_id":231},{"level":37,"move_id":182},{"level":50,"move_id":319},{"level":63,"move_id":38}]},"tmhm_learnset":"00B41EF6CFF37E37","types":[8,5]},{"abilities":[59,0],"address":3307524,"base_stats":[70,70,70,70,70,70],"catch_rate":45,"evolutions":[],"friendship":70,"id":385,"learnset":{"address":3317642,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":10,"move_id":55},{"level":10,"move_id":52},{"level":10,"move_id":181},{"level":20,"move_id":240},{"level":20,"move_id":241},{"level":20,"move_id":258},{"level":30,"move_id":311}]},"tmhm_learnset":"00403E36A5B33664","types":[0,0]},{"abilities":[35,68],"address":3307552,"base_stats":[65,73,55,85,47,75],"catch_rate":150,"evolutions":[],"friendship":70,"id":386,"learnset":{"address":3317666,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":5,"move_id":109},{"level":9,"move_id":104},{"level":13,"move_id":236},{"level":17,"move_id":98},{"level":21,"move_id":294},{"level":25,"move_id":324},{"level":29,"move_id":182},{"level":33,"move_id":270},{"level":37,"move_id":38}]},"tmhm_learnset":"00403E82E5B78625","types":[6,6]},{"abilities":[12,0],"address":3307580,"base_stats":[65,47,55,85,73,75],"catch_rate":150,"evolutions":[],"friendship":70,"id":387,"learnset":{"address":3317694,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":33},{"level":5,"move_id":230},{"level":9,"move_id":204},{"level":13,"move_id":236},{"level":17,"move_id":98},{"level":21,"move_id":273},{"level":25,"move_id":227},{"level":29,"move_id":260},{"level":33,"move_id":270},{"level":37,"move_id":343}]},"tmhm_learnset":"00403E82E5B78625","types":[6,6]},{"abilities":[21,0],"address":3307608,"base_stats":[66,41,77,23,61,87],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":40,"species":389}],"friendship":70,"id":388,"learnset":{"address":3317722,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":310},{"level":8,"move_id":132},{"level":15,"move_id":51},{"level":22,"move_id":275},{"level":29,"move_id":109},{"level":36,"move_id":133},{"level":43,"move_id":246},{"level":50,"move_id":254},{"level":50,"move_id":255},{"level":50,"move_id":256}]},"tmhm_learnset":"00001E1884350720","types":[5,12]},{"abilities":[21,0],"address":3307636,"base_stats":[86,81,97,43,81,107],"catch_rate":45,"evolutions":[],"friendship":70,"id":389,"learnset":{"address":3317750,"moves":[{"level":1,"move_id":310},{"level":1,"move_id":132},{"level":1,"move_id":51},{"level":1,"move_id":275},{"level":8,"move_id":132},{"level":15,"move_id":51},{"level":22,"move_id":275},{"level":29,"move_id":109},{"level":36,"move_id":133},{"level":48,"move_id":246},{"level":60,"move_id":254},{"level":60,"move_id":255},{"level":60,"move_id":256}]},"tmhm_learnset":"00A01E5886354720","types":[5,12]},{"abilities":[4,0],"address":3307664,"base_stats":[45,95,50,75,40,50],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":40,"species":391}],"friendship":70,"id":390,"learnset":{"address":3317778,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":10},{"level":7,"move_id":106},{"level":13,"move_id":300},{"level":19,"move_id":55},{"level":25,"move_id":232},{"level":31,"move_id":182},{"level":37,"move_id":246},{"level":43,"move_id":210},{"level":49,"move_id":163},{"level":55,"move_id":350}]},"tmhm_learnset":"00841ED0CC110624","types":[5,6]},{"abilities":[4,0],"address":3307692,"base_stats":[75,125,100,45,70,80],"catch_rate":45,"evolutions":[],"friendship":70,"id":391,"learnset":{"address":3317806,"moves":[{"level":1,"move_id":10},{"level":1,"move_id":106},{"level":1,"move_id":300},{"level":1,"move_id":55},{"level":7,"move_id":106},{"level":13,"move_id":300},{"level":19,"move_id":55},{"level":25,"move_id":232},{"level":31,"move_id":182},{"level":37,"move_id":246},{"level":46,"move_id":210},{"level":55,"move_id":163},{"level":64,"move_id":350}]},"tmhm_learnset":"00A41ED0CE514624","types":[5,6]},{"abilities":[28,36],"address":3307720,"base_stats":[28,25,25,40,45,35],"catch_rate":235,"evolutions":[{"method":"LEVEL","param":20,"species":393}],"friendship":35,"id":392,"learnset":{"address":3317834,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":93},{"level":1,"move_id":45},{"level":6,"move_id":93},{"level":11,"move_id":104},{"level":16,"move_id":100},{"level":21,"move_id":347},{"level":26,"move_id":94},{"level":31,"move_id":286},{"level":36,"move_id":248},{"level":41,"move_id":95},{"level":46,"move_id":138}]},"tmhm_learnset":"0041BF03B49B8E28","types":[14,14]},{"abilities":[28,36],"address":3307748,"base_stats":[38,35,35,50,65,55],"catch_rate":120,"evolutions":[{"method":"LEVEL","param":30,"species":394}],"friendship":35,"id":393,"learnset":{"address":3317862,"moves":[{"level":1,"move_id":45},{"level":1,"move_id":93},{"level":1,"move_id":104},{"level":1,"move_id":100},{"level":6,"move_id":93},{"level":11,"move_id":104},{"level":16,"move_id":100},{"level":21,"move_id":347},{"level":26,"move_id":94},{"level":33,"move_id":286},{"level":40,"move_id":248},{"level":47,"move_id":95},{"level":54,"move_id":138}]},"tmhm_learnset":"0041BF03B49B8E28","types":[14,14]},{"abilities":[28,36],"address":3307776,"base_stats":[68,65,65,80,125,115],"catch_rate":45,"evolutions":[],"friendship":35,"id":394,"learnset":{"address":3317890,"moves":[{"level":1,"move_id":45},{"level":1,"move_id":93},{"level":1,"move_id":104},{"level":1,"move_id":100},{"level":6,"move_id":93},{"level":11,"move_id":104},{"level":16,"move_id":100},{"level":21,"move_id":347},{"level":26,"move_id":94},{"level":33,"move_id":286},{"level":42,"move_id":248},{"level":51,"move_id":95},{"level":60,"move_id":138}]},"tmhm_learnset":"0041BF03B49BCE28","types":[14,14]},{"abilities":[69,0],"address":3307804,"base_stats":[45,75,60,50,40,30],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":30,"species":396}],"friendship":35,"id":395,"learnset":{"address":3317918,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":99},{"level":5,"move_id":44},{"level":9,"move_id":43},{"level":17,"move_id":29},{"level":21,"move_id":116},{"level":25,"move_id":52},{"level":33,"move_id":225},{"level":37,"move_id":184},{"level":41,"move_id":242},{"level":49,"move_id":337},{"level":53,"move_id":38}]},"tmhm_learnset":"00A41EE4C4130632","types":[16,16]},{"abilities":[69,0],"address":3307832,"base_stats":[65,95,100,50,60,50],"catch_rate":45,"evolutions":[{"method":"LEVEL","param":50,"species":397}],"friendship":35,"id":396,"learnset":{"address":3317948,"moves":[{"level":1,"move_id":99},{"level":1,"move_id":44},{"level":1,"move_id":43},{"level":1,"move_id":29},{"level":5,"move_id":44},{"level":9,"move_id":43},{"level":17,"move_id":29},{"level":21,"move_id":116},{"level":25,"move_id":52},{"level":30,"move_id":182},{"level":38,"move_id":225},{"level":47,"move_id":184},{"level":56,"move_id":242},{"level":69,"move_id":337},{"level":78,"move_id":38}]},"tmhm_learnset":"00A41EE4C4130632","types":[16,16]},{"abilities":[22,0],"address":3307860,"base_stats":[95,135,80,100,110,80],"catch_rate":45,"evolutions":[],"friendship":35,"id":397,"learnset":{"address":3317980,"moves":[{"level":1,"move_id":99},{"level":1,"move_id":44},{"level":1,"move_id":43},{"level":1,"move_id":29},{"level":5,"move_id":44},{"level":9,"move_id":43},{"level":17,"move_id":29},{"level":21,"move_id":116},{"level":25,"move_id":52},{"level":30,"move_id":182},{"level":38,"move_id":225},{"level":47,"move_id":184},{"level":50,"move_id":19},{"level":61,"move_id":242},{"level":79,"move_id":337},{"level":93,"move_id":38}]},"tmhm_learnset":"00AC5EE4C6534632","types":[16,2]},{"abilities":[29,0],"address":3307888,"base_stats":[40,55,80,30,35,60],"catch_rate":3,"evolutions":[{"method":"LEVEL","param":20,"species":399}],"friendship":35,"id":398,"learnset":{"address":3318014,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":36}]},"tmhm_learnset":"0000000000000000","types":[8,14]},{"abilities":[29,0],"address":3307916,"base_stats":[60,75,100,50,55,80],"catch_rate":3,"evolutions":[{"method":"LEVEL","param":45,"species":400}],"friendship":35,"id":399,"learnset":{"address":3318024,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":36},{"level":20,"move_id":93},{"level":20,"move_id":232},{"level":26,"move_id":184},{"level":32,"move_id":228},{"level":38,"move_id":94},{"level":44,"move_id":334},{"level":50,"move_id":309},{"level":56,"move_id":97},{"level":62,"move_id":63}]},"tmhm_learnset":"00E40ED9F613C620","types":[8,14]},{"abilities":[29,0],"address":3307944,"base_stats":[80,135,130,70,95,90],"catch_rate":3,"evolutions":[],"friendship":35,"id":400,"learnset":{"address":3318052,"moves":[{"level":1,"move_id":36},{"level":1,"move_id":93},{"level":1,"move_id":232},{"level":1,"move_id":184},{"level":20,"move_id":93},{"level":20,"move_id":232},{"level":26,"move_id":184},{"level":32,"move_id":228},{"level":38,"move_id":94},{"level":44,"move_id":334},{"level":55,"move_id":309},{"level":66,"move_id":97},{"level":77,"move_id":63}]},"tmhm_learnset":"00E40ED9F613C620","types":[8,14]},{"abilities":[29,0],"address":3307972,"base_stats":[80,100,200,50,50,100],"catch_rate":3,"evolutions":[],"friendship":35,"id":401,"learnset":{"address":3318080,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":88},{"level":1,"move_id":153},{"level":9,"move_id":88},{"level":17,"move_id":174},{"level":25,"move_id":276},{"level":33,"move_id":246},{"level":41,"move_id":334},{"level":49,"move_id":192},{"level":57,"move_id":199},{"level":65,"move_id":63}]},"tmhm_learnset":"00A00E52CF994621","types":[5,5]},{"abilities":[29,0],"address":3308000,"base_stats":[80,50,100,50,100,200],"catch_rate":3,"evolutions":[],"friendship":35,"id":402,"learnset":{"address":3318106,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":196},{"level":1,"move_id":153},{"level":9,"move_id":196},{"level":17,"move_id":174},{"level":25,"move_id":276},{"level":33,"move_id":246},{"level":41,"move_id":133},{"level":49,"move_id":192},{"level":57,"move_id":199},{"level":65,"move_id":63}]},"tmhm_learnset":"00A00E02C79B7261","types":[15,15]},{"abilities":[29,0],"address":3308028,"base_stats":[80,75,150,50,75,150],"catch_rate":3,"evolutions":[],"friendship":35,"id":403,"learnset":{"address":3318132,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":232},{"level":1,"move_id":153},{"level":9,"move_id":232},{"level":17,"move_id":174},{"level":25,"move_id":276},{"level":33,"move_id":246},{"level":41,"move_id":334},{"level":41,"move_id":133},{"level":49,"move_id":192},{"level":57,"move_id":199},{"level":65,"move_id":63}]},"tmhm_learnset":"00A00ED2C79B4621","types":[8,8]},{"abilities":[2,0],"address":3308056,"base_stats":[100,100,90,90,150,140],"catch_rate":5,"evolutions":[],"friendship":0,"id":404,"learnset":{"address":3318160,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":352},{"level":5,"move_id":184},{"level":15,"move_id":246},{"level":20,"move_id":34},{"level":30,"move_id":347},{"level":35,"move_id":58},{"level":45,"move_id":56},{"level":50,"move_id":156},{"level":60,"move_id":329},{"level":65,"move_id":38},{"level":75,"move_id":323}]},"tmhm_learnset":"03B00E42C79B727C","types":[11,11]},{"abilities":[70,0],"address":3308084,"base_stats":[100,150,140,90,100,90],"catch_rate":5,"evolutions":[],"friendship":0,"id":405,"learnset":{"address":3318190,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":341},{"level":5,"move_id":184},{"level":15,"move_id":246},{"level":20,"move_id":163},{"level":30,"move_id":339},{"level":35,"move_id":89},{"level":45,"move_id":126},{"level":50,"move_id":156},{"level":60,"move_id":90},{"level":65,"move_id":76},{"level":75,"move_id":284}]},"tmhm_learnset":"00A60EF6CFF946B2","types":[4,4]},{"abilities":[77,0],"address":3308112,"base_stats":[105,150,90,95,150,90],"catch_rate":3,"evolutions":[],"friendship":0,"id":406,"learnset":{"address":3318220,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":239},{"level":5,"move_id":184},{"level":15,"move_id":246},{"level":20,"move_id":337},{"level":30,"move_id":349},{"level":35,"move_id":242},{"level":45,"move_id":19},{"level":50,"move_id":156},{"level":60,"move_id":245},{"level":65,"move_id":200},{"level":75,"move_id":63}]},"tmhm_learnset":"03BA0EB6C7F376B6","types":[16,2]},{"abilities":[26,0],"address":3308140,"base_stats":[80,80,90,110,110,130],"catch_rate":3,"evolutions":[],"friendship":90,"id":407,"learnset":{"address":3318250,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":149},{"level":5,"move_id":273},{"level":10,"move_id":270},{"level":15,"move_id":219},{"level":20,"move_id":225},{"level":25,"move_id":346},{"level":30,"move_id":287},{"level":35,"move_id":296},{"level":40,"move_id":94},{"level":45,"move_id":105},{"level":50,"move_id":204}]},"tmhm_learnset":"035C5E93B7BBD63E","types":[16,14]},{"abilities":[26,0],"address":3308168,"base_stats":[80,90,80,110,130,110],"catch_rate":3,"evolutions":[],"friendship":90,"id":408,"learnset":{"address":3318280,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":149},{"level":5,"move_id":262},{"level":10,"move_id":270},{"level":15,"move_id":219},{"level":20,"move_id":225},{"level":25,"move_id":182},{"level":30,"move_id":287},{"level":35,"move_id":295},{"level":40,"move_id":94},{"level":45,"move_id":105},{"level":50,"move_id":349}]},"tmhm_learnset":"035C5E93B7BBD63E","types":[16,14]},{"abilities":[32,0],"address":3308196,"base_stats":[100,100,100,100,100,100],"catch_rate":3,"evolutions":[],"friendship":100,"id":409,"learnset":{"address":3318310,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":273},{"level":1,"move_id":93},{"level":5,"move_id":156},{"level":10,"move_id":129},{"level":15,"move_id":270},{"level":20,"move_id":94},{"level":25,"move_id":287},{"level":30,"move_id":156},{"level":35,"move_id":38},{"level":40,"move_id":248},{"level":45,"move_id":322},{"level":50,"move_id":353}]},"tmhm_learnset":"00408E93B59BC62C","types":[8,14]},{"abilities":[46,0],"address":3308224,"base_stats":[50,150,50,150,150,50],"catch_rate":3,"evolutions":[],"friendship":0,"id":410,"learnset":{"address":3318340,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":43},{"level":1,"move_id":35},{"level":5,"move_id":101},{"level":10,"move_id":104},{"level":15,"move_id":282},{"level":20,"move_id":228},{"level":25,"move_id":94},{"level":30,"move_id":129},{"level":35,"move_id":97},{"level":40,"move_id":105},{"level":45,"move_id":354},{"level":50,"move_id":245}]},"tmhm_learnset":"00E58FC3F5BBDE2D","types":[14,14]},{"abilities":[26,0],"address":3308252,"base_stats":[65,50,70,65,95,80],"catch_rate":45,"evolutions":[],"friendship":70,"id":411,"learnset":{"address":3318370,"moves":[{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":0},{"level":1,"move_id":35},{"level":6,"move_id":45},{"level":9,"move_id":310},{"level":14,"move_id":93},{"level":17,"move_id":36},{"level":22,"move_id":253},{"level":25,"move_id":281},{"level":30,"move_id":149},{"level":33,"move_id":38},{"level":38,"move_id":215},{"level":41,"move_id":219},{"level":46,"move_id":94}]},"tmhm_learnset":"00419F03B41B8E28","types":[14,14]}],"tmhm_moves":[264,337,352,347,46,92,258,339,331,237,241,269,58,59,63,113,182,240,202,219,218,76,231,85,87,89,216,91,94,247,280,104,115,351,53,188,201,126,317,332,259,263,290,156,213,168,211,285,289,315,15,19,57,70,148,249,127,291],"trainers":[{"address":3230072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[],"party_address":4160749568,"script_address":0},{"address":3230112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":21,"species":74}],"party_address":3211124,"script_address":2304511},{"address":3230152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":286}],"party_address":3211132,"script_address":2321901},{"address":3230192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":41},{"level":31,"species":330}],"party_address":3211140,"script_address":2323326},{"address":3230232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":41}],"party_address":3211156,"script_address":2323373},{"address":3230272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":330}],"party_address":3211164,"script_address":2324386},{"address":3230312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":286}],"party_address":3211172,"script_address":2326808},{"address":3230352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":330}],"party_address":3211180,"script_address":2326839},{"address":3230392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":41}],"party_address":3211188,"script_address":2328040},{"address":3230432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":315},{"level":26,"species":286},{"level":26,"species":288},{"level":26,"species":295},{"level":26,"species":298},{"level":26,"species":304}],"party_address":3211196,"script_address":2314251},{"address":3230472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":9,"species":286}],"party_address":3211244,"script_address":0},{"address":3230512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":338},{"level":29,"species":300}],"party_address":3211252,"script_address":2067580},{"address":3230552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":310},{"level":30,"species":178}],"party_address":3211268,"script_address":2068523},{"address":3230592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":380},{"level":30,"species":379}],"party_address":3211284,"script_address":2068554},{"address":3230632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":330}],"party_address":3211300,"script_address":2328071},{"address":3230672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":130}],"party_address":3211308,"script_address":2069620},{"address":3230712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":11,"species":286}],"party_address":3211316,"script_address":0},{"address":3230752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":41},{"level":27,"species":286}],"party_address":3211324,"script_address":2570959},{"address":3230792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":286},{"level":27,"species":330}],"party_address":3211340,"script_address":2572093},{"address":3230832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":286},{"level":26,"species":41},{"level":26,"species":330}],"party_address":3211356,"script_address":2572124},{"address":3230872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":15,"species":330}],"party_address":3211380,"script_address":2157889},{"address":3230912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":14,"species":41},{"level":14,"species":330}],"party_address":3211388,"script_address":2157948},{"address":3230952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":339}],"party_address":3211404,"script_address":2254636},{"address":3230992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":41}],"party_address":3211412,"script_address":2317522},{"address":3231032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":330}],"party_address":3211420,"script_address":2317553},{"address":3231072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":286},{"level":30,"species":330}],"party_address":3211428,"script_address":2317584},{"address":3231112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":330}],"party_address":3211444,"script_address":2570990},{"address":3231152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":330}],"party_address":3211452,"script_address":2323414},{"address":3231192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":41}],"party_address":3211460,"script_address":2324427},{"address":3231232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":335},{"level":30,"species":67}],"party_address":3211468,"script_address":2068492},{"address":3231272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":287},{"level":34,"species":42}],"party_address":3211484,"script_address":2324250},{"address":3231312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":336}],"party_address":3211500,"script_address":2312702},{"address":3231352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":330},{"level":28,"species":287}],"party_address":3211508,"script_address":2572155},{"address":3231392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":37,"species":331},{"level":37,"species":287}],"party_address":3211524,"script_address":2327156},{"address":3231432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":41,"species":287},{"level":41,"species":169},{"level":43,"species":331}],"party_address":3211540,"script_address":2328478},{"address":3231472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":351}],"party_address":3211564,"script_address":2312671},{"address":3231512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":14,"species":306},{"level":14,"species":363}],"party_address":3211572,"script_address":2026085},{"address":3231552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":14,"species":363},{"level":14,"species":306},{"level":14,"species":363}],"party_address":3211588,"script_address":2058784},{"address":3231592,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":43,"moves":[94,0,0,0],"species":357},{"level":43,"moves":[29,89,0,0],"species":319}],"party_address":3211612,"script_address":2335547},{"address":3231632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":363},{"level":26,"species":44}],"party_address":3211644,"script_address":2068148},{"address":3231672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":306},{"level":26,"species":363}],"party_address":3211660,"script_address":0},{"address":3231712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":306},{"level":28,"species":44},{"level":28,"species":363}],"party_address":3211676,"script_address":0},{"address":3231752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":306},{"level":31,"species":44},{"level":31,"species":363}],"party_address":3211700,"script_address":0},{"address":3231792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":307},{"level":34,"species":44},{"level":34,"species":363}],"party_address":3211724,"script_address":0},{"address":3231832,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":23,"moves":[91,163,28,40],"species":28}],"party_address":3211748,"script_address":2046490},{"address":3231872,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":27,"moves":[60,120,201,246],"species":318},{"level":27,"moves":[91,163,28,40],"species":27},{"level":27,"moves":[91,163,28,40],"species":28}],"party_address":3211764,"script_address":2065682},{"address":3231912,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":25,"moves":[91,163,28,40],"species":27},{"level":25,"moves":[91,163,28,40],"species":28}],"party_address":3211812,"script_address":2033540},{"address":3231952,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":27,"moves":[91,163,28,40],"species":28}],"party_address":3211844,"script_address":0},{"address":3231992,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":30,"moves":[91,163,28,40],"species":28}],"party_address":3211860,"script_address":0},{"address":3232032,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":33,"moves":[91,163,28,40],"species":28}],"party_address":3211876,"script_address":0},{"address":3232072,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":36,"moves":[91,163,28,40],"species":28}],"party_address":3211892,"script_address":0},{"address":3232112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":17,"species":81},{"level":17,"species":370}],"party_address":3211908,"script_address":0},{"address":3232152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":81},{"level":27,"species":371}],"party_address":3211924,"script_address":0},{"address":3232192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":82},{"level":30,"species":371}],"party_address":3211940,"script_address":0},{"address":3232232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":82},{"level":33,"species":371}],"party_address":3211956,"script_address":0},{"address":3232272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":82},{"level":36,"species":371}],"party_address":3211972,"script_address":0},{"address":3232312,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":39,"moves":[49,86,63,85],"species":82},{"level":39,"moves":[54,23,48,48],"species":372}],"party_address":3211988,"script_address":0},{"address":3232352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":12,"species":350},{"level":12,"species":350}],"party_address":3212020,"script_address":2036011},{"address":3232392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":183}],"party_address":3212036,"script_address":2036121},{"address":3232432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":183}],"party_address":3212044,"script_address":2036152},{"address":3232472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":183},{"level":26,"species":183}],"party_address":3212052,"script_address":0},{"address":3232512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":183},{"level":29,"species":183}],"party_address":3212068,"script_address":0},{"address":3232552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":183},{"level":32,"species":183}],"party_address":3212084,"script_address":0},{"address":3232592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":184},{"level":35,"species":184}],"party_address":3212100,"script_address":0},{"address":3232632,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":13,"moves":[28,29,39,57],"species":288}],"party_address":3212116,"script_address":2035901},{"address":3232672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":12,"species":350},{"level":12,"species":183}],"party_address":3212132,"script_address":2544001},{"address":3232712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":183}],"party_address":3212148,"script_address":2339831},{"address":3232752,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":27,"moves":[28,42,39,57],"species":289}],"party_address":3212156,"script_address":0},{"address":3232792,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":30,"moves":[28,42,39,57],"species":289}],"party_address":3212172,"script_address":0},{"address":3232832,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":33,"moves":[28,42,39,57],"species":289}],"party_address":3212188,"script_address":0},{"address":3232872,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":36,"moves":[28,42,39,57],"species":289}],"party_address":3212204,"script_address":0},{"address":3232912,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":26,"moves":[98,97,17,0],"species":305}],"party_address":3212220,"script_address":2131164},{"address":3232952,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":26,"moves":[42,146,8,0],"species":308}],"party_address":3212236,"script_address":2131228},{"address":3232992,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":26,"moves":[47,68,247,0],"species":364}],"party_address":3212252,"script_address":2131292},{"address":3233032,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":26,"moves":[116,163,0,0],"species":365}],"party_address":3212268,"script_address":2131356},{"address":3233072,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":28,"moves":[116,98,17,27],"species":305},{"level":28,"moves":[44,91,185,72],"species":332},{"level":28,"moves":[205,250,54,96],"species":313},{"level":28,"moves":[85,48,86,49],"species":82},{"level":28,"moves":[202,185,104,207],"species":300}],"party_address":3212284,"script_address":2068117},{"address":3233112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":44,"species":322},{"level":44,"species":357},{"level":44,"species":331}],"party_address":3212364,"script_address":2565920},{"address":3233152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":46,"species":355},{"level":46,"species":121}],"party_address":3212388,"script_address":2565982},{"address":3233192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":17,"species":337},{"level":17,"species":313},{"level":17,"species":335}],"party_address":3212404,"script_address":2046693},{"address":3233232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":43,"species":345},{"level":43,"species":310}],"party_address":3212428,"script_address":2332685},{"address":3233272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":43,"species":82},{"level":43,"species":89}],"party_address":3212444,"script_address":2332716},{"address":3233312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":42,"species":305},{"level":42,"species":355},{"level":42,"species":64}],"party_address":3212460,"script_address":2334375},{"address":3233352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":42,"species":85},{"level":42,"species":64},{"level":42,"species":101},{"level":42,"species":300}],"party_address":3212484,"script_address":2335423},{"address":3233392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":42,"species":317},{"level":42,"species":75},{"level":42,"species":314}],"party_address":3212516,"script_address":2335454},{"address":3233432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":337},{"level":26,"species":313},{"level":26,"species":335}],"party_address":3212540,"script_address":0},{"address":3233472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":338},{"level":29,"species":313},{"level":29,"species":335}],"party_address":3212564,"script_address":0},{"address":3233512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":338},{"level":32,"species":313},{"level":32,"species":335}],"party_address":3212588,"script_address":0},{"address":3233552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":338},{"level":35,"species":313},{"level":35,"species":336}],"party_address":3212612,"script_address":0},{"address":3233592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":75},{"level":33,"species":297}],"party_address":3212636,"script_address":2073950},{"address":3233632,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":26,"moves":[185,95,0,0],"species":316}],"party_address":3212652,"script_address":2131420},{"address":3233672,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":26,"moves":[111,38,247,0],"species":40}],"party_address":3212668,"script_address":2131484},{"address":3233712,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":26,"moves":[14,163,0,0],"species":380}],"party_address":3212684,"script_address":2131548},{"address":3233752,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":29,"moves":[226,185,57,44],"species":355},{"level":29,"moves":[72,89,64,73],"species":363},{"level":29,"moves":[19,55,54,182],"species":310}],"party_address":3212700,"script_address":2068086},{"address":3233792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":45,"species":383},{"level":45,"species":338}],"party_address":3212748,"script_address":2565951},{"address":3233832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":17,"species":309},{"level":17,"species":339},{"level":17,"species":363}],"party_address":3212764,"script_address":2046803},{"address":3233872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":322}],"party_address":3212788,"script_address":2065651},{"address":3233912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":45,"species":363}],"party_address":3212796,"script_address":2332747},{"address":3233952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":45,"species":319}],"party_address":3212804,"script_address":2334406},{"address":3233992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":42,"species":321},{"level":42,"species":357},{"level":42,"species":297}],"party_address":3212812,"script_address":2334437},{"address":3234032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":43,"species":227},{"level":43,"species":322}],"party_address":3212836,"script_address":2335485},{"address":3234072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":42,"species":28},{"level":42,"species":38},{"level":42,"species":369}],"party_address":3212852,"script_address":2335516},{"address":3234112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":309},{"level":26,"species":339},{"level":26,"species":363}],"party_address":3212876,"script_address":0},{"address":3234152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":310},{"level":29,"species":339},{"level":29,"species":363}],"party_address":3212900,"script_address":0},{"address":3234192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":310},{"level":32,"species":339},{"level":32,"species":363}],"party_address":3212924,"script_address":0},{"address":3234232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":310},{"level":34,"species":340},{"level":34,"species":363}],"party_address":3212948,"script_address":0},{"address":3234272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":41,"species":378},{"level":41,"species":348}],"party_address":3212972,"script_address":2564729},{"address":3234312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":361},{"level":30,"species":377}],"party_address":3212988,"script_address":2068461},{"address":3234352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":361},{"level":29,"species":377}],"party_address":3213004,"script_address":2067284},{"address":3234392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":322}],"party_address":3213020,"script_address":2315745},{"address":3234432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":377}],"party_address":3213028,"script_address":2315532},{"address":3234472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":322},{"level":31,"species":351}],"party_address":3213036,"script_address":0},{"address":3234512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":351},{"level":35,"species":322}],"party_address":3213052,"script_address":0},{"address":3234552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":40,"species":351},{"level":40,"species":322}],"party_address":3213068,"script_address":0},{"address":3234592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":42,"species":361},{"level":42,"species":322},{"level":42,"species":352}],"party_address":3213084,"script_address":0},{"address":3234632,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":7,"species":288}],"party_address":3213108,"script_address":2030087},{"address":3234672,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":39,"moves":[213,186,175,96],"species":325},{"level":39,"moves":[213,219,36,96],"species":325}],"party_address":3213116,"script_address":2265894},{"address":3234712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":287},{"level":28,"species":287},{"level":30,"species":339}],"party_address":3213148,"script_address":2254717},{"address":3234752,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":11,"moves":[33,39,0,0],"species":288}],"party_address":3213172,"script_address":0},{"address":3234792,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":40,"species":119}],"party_address":3213188,"script_address":2265677},{"address":3234832,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":45,"species":363}],"party_address":3213196,"script_address":2361019},{"address":3234872,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":27,"species":289}],"party_address":3213204,"script_address":0},{"address":3234912,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":30,"species":289}],"party_address":3213212,"script_address":0},{"address":3234952,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":33,"species":289}],"party_address":3213220,"script_address":0},{"address":3234992,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":36,"moves":[154,44,60,28],"species":289}],"party_address":3213228,"script_address":0},{"address":3235032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":21,"species":183}],"party_address":3213244,"script_address":2304387},{"address":3235072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":21,"species":306}],"party_address":3213252,"script_address":2304418},{"address":3235112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":21,"species":339}],"party_address":3213260,"script_address":2304449},{"address":3235152,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":29,"moves":[20,122,154,185],"species":317},{"level":29,"moves":[86,103,137,242],"species":379}],"party_address":3213268,"script_address":2067377},{"address":3235192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":40,"species":118}],"party_address":3213300,"script_address":2265708},{"address":3235232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":40,"species":184}],"party_address":3213308,"script_address":2265739},{"address":3235272,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":35,"moves":[78,250,240,96],"species":373},{"level":37,"moves":[13,152,96,0],"species":326},{"level":39,"moves":[253,154,252,96],"species":296}],"party_address":3213316,"script_address":2265770},{"address":3235312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":39,"species":330},{"level":39,"species":331}],"party_address":3213364,"script_address":2265801},{"address":3235352,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":35,"moves":[20,122,154,185],"species":317},{"level":35,"moves":[86,103,137,242],"species":379}],"party_address":3213380,"script_address":0},{"address":3235392,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":38,"moves":[20,122,154,185],"species":317},{"level":38,"moves":[86,103,137,242],"species":379}],"party_address":3213412,"script_address":0},{"address":3235432,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":41,"moves":[20,122,154,185],"species":317},{"level":41,"moves":[86,103,137,242],"species":379}],"party_address":3213444,"script_address":0},{"address":3235472,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":44,"moves":[20,122,154,185],"species":317},{"level":44,"moves":[86,103,137,242],"species":379}],"party_address":3213476,"script_address":0},{"address":3235512,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":7,"species":288}],"party_address":3213508,"script_address":2029901},{"address":3235552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":324},{"level":33,"species":356}],"party_address":3213516,"script_address":2074012},{"address":3235592,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":45,"species":184}],"party_address":3213532,"script_address":2360988},{"address":3235632,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":27,"species":289}],"party_address":3213540,"script_address":0},{"address":3235672,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":30,"species":289}],"party_address":3213548,"script_address":0},{"address":3235712,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":33,"species":289}],"party_address":3213556,"script_address":0},{"address":3235752,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":36,"moves":[154,44,60,28],"species":289}],"party_address":3213564,"script_address":0},{"address":3235792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":19,"species":382}],"party_address":3213580,"script_address":2051965},{"address":3235832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":313},{"level":25,"species":116}],"party_address":3213588,"script_address":2340108},{"address":3235872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":111}],"party_address":3213604,"script_address":2312578},{"address":3235912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":20,"species":339}],"party_address":3213612,"script_address":2304480},{"address":3235952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":383}],"party_address":3213620,"script_address":0},{"address":3235992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":383},{"level":29,"species":111}],"party_address":3213628,"script_address":0},{"address":3236032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":383},{"level":32,"species":111}],"party_address":3213644,"script_address":0},{"address":3236072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":384},{"level":35,"species":112}],"party_address":3213660,"script_address":0},{"address":3236112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":330}],"party_address":3213676,"script_address":2033571},{"address":3236152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":72}],"party_address":3213684,"script_address":2033602},{"address":3236192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":24,"species":72},{"level":24,"species":72}],"party_address":3213692,"script_address":2034185},{"address":3236232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":24,"species":72},{"level":24,"species":309},{"level":24,"species":72}],"party_address":3213708,"script_address":2034479},{"address":3236272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":330}],"party_address":3213732,"script_address":2034510},{"address":3236312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":73}],"party_address":3213740,"script_address":2034776},{"address":3236352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":330}],"party_address":3213748,"script_address":2034807},{"address":3236392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":72},{"level":25,"species":330}],"party_address":3213756,"script_address":2035777},{"address":3236432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":72},{"level":33,"species":309}],"party_address":3213772,"script_address":2069178},{"address":3236472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":330}],"party_address":3213788,"script_address":2069209},{"address":3236512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":73}],"party_address":3213796,"script_address":2069789},{"address":3236552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":116}],"party_address":3213804,"script_address":2069820},{"address":3236592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":130}],"party_address":3213812,"script_address":2070163},{"address":3236632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":330},{"level":31,"species":309},{"level":31,"species":330}],"party_address":3213820,"script_address":2070194},{"address":3236672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":130}],"party_address":3213844,"script_address":2073229},{"address":3236712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":310}],"party_address":3213852,"script_address":2073359},{"address":3236752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":309},{"level":33,"species":73}],"party_address":3213860,"script_address":2073390},{"address":3236792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":73},{"level":33,"species":313}],"party_address":3213876,"script_address":2073291},{"address":3236832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":331}],"party_address":3213892,"script_address":2073608},{"address":3236872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":342}],"party_address":3213900,"script_address":2073857},{"address":3236912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":341}],"party_address":3213908,"script_address":2073576},{"address":3236952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":130}],"party_address":3213916,"script_address":2074089},{"address":3236992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":72},{"level":33,"species":309},{"level":33,"species":73}],"party_address":3213924,"script_address":0},{"address":3237032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":72},{"level":33,"species":313}],"party_address":3213948,"script_address":2069381},{"address":3237072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":331}],"party_address":3213964,"script_address":0},{"address":3237112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":331}],"party_address":3213972,"script_address":0},{"address":3237152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":120},{"level":36,"species":331}],"party_address":3213980,"script_address":0},{"address":3237192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":37,"species":121},{"level":39,"species":331}],"party_address":3213996,"script_address":0},{"address":3237232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":13,"species":66}],"party_address":3214012,"script_address":2095275},{"address":3237272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":66},{"level":32,"species":67}],"party_address":3214020,"script_address":2074213},{"address":3237312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":336}],"party_address":3214036,"script_address":2073701},{"address":3237352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":24,"species":66},{"level":28,"species":67}],"party_address":3214044,"script_address":2052921},{"address":3237392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":19,"species":66}],"party_address":3214060,"script_address":2052952},{"address":3237432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":67}],"party_address":3214068,"script_address":0},{"address":3237472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":66},{"level":29,"species":67}],"party_address":3214076,"script_address":0},{"address":3237512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":66},{"level":31,"species":67},{"level":31,"species":67}],"party_address":3214092,"script_address":0},{"address":3237552,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":33,"species":66},{"level":33,"species":67},{"level":33,"species":67},{"level":33,"species":68}],"party_address":3214116,"script_address":0},{"address":3237592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":335},{"level":26,"species":67}],"party_address":3214148,"script_address":2557758},{"address":3237632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":19,"species":66}],"party_address":3214164,"script_address":2046662},{"address":3237672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":336}],"party_address":3214172,"script_address":2315359},{"address":3237712,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":17,"moves":[98,86,209,43],"species":337},{"level":17,"moves":[12,95,103,0],"species":100}],"party_address":3214180,"script_address":2167608},{"address":3237752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":286},{"level":31,"species":41}],"party_address":3214212,"script_address":2323445},{"address":3237792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":330}],"party_address":3214228,"script_address":2324458},{"address":3237832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":17,"species":100},{"level":17,"species":81}],"party_address":3214236,"script_address":2167639},{"address":3237872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":337},{"level":30,"species":371}],"party_address":3214252,"script_address":2068709},{"address":3237912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":15,"species":81},{"level":15,"species":370}],"party_address":3214268,"script_address":2058956},{"address":3237952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":81},{"level":25,"species":370},{"level":25,"species":81}],"party_address":3214284,"script_address":0},{"address":3237992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":81},{"level":28,"species":371},{"level":28,"species":81}],"party_address":3214308,"script_address":0},{"address":3238032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":82},{"level":31,"species":371},{"level":31,"species":82}],"party_address":3214332,"script_address":0},{"address":3238072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":82},{"level":34,"species":372},{"level":34,"species":82}],"party_address":3214356,"script_address":0},{"address":3238112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":23,"species":339}],"party_address":3214380,"script_address":2103394},{"address":3238152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":22,"species":218},{"level":22,"species":218}],"party_address":3214388,"script_address":2103601},{"address":3238192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":23,"species":339}],"party_address":3214404,"script_address":2103446},{"address":3238232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":23,"species":218}],"party_address":3214412,"script_address":2103570},{"address":3238272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":23,"species":218}],"party_address":3214420,"script_address":2103477},{"address":3238312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":218},{"level":18,"species":309}],"party_address":3214428,"script_address":2052075},{"address":3238352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":218},{"level":26,"species":309}],"party_address":3214444,"script_address":0},{"address":3238392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":218},{"level":29,"species":310}],"party_address":3214460,"script_address":0},{"address":3238432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":218},{"level":32,"species":310}],"party_address":3214476,"script_address":0},{"address":3238472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":219},{"level":35,"species":310}],"party_address":3214492,"script_address":0},{"address":3238512,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":23,"moves":[91,28,40,163],"species":27}],"party_address":3214508,"script_address":2046366},{"address":3238552,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":21,"moves":[229,189,60,61],"species":318},{"level":21,"moves":[40,28,10,91],"species":27},{"level":21,"moves":[229,189,60,61],"species":318}],"party_address":3214524,"script_address":2046428},{"address":3238592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":299}],"party_address":3214572,"script_address":2049829},{"address":3238632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":27},{"level":18,"species":299}],"party_address":3214580,"script_address":2051903},{"address":3238672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":24,"species":317}],"party_address":3214596,"script_address":2557005},{"address":3238712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":20,"species":288},{"level":20,"species":304}],"party_address":3214604,"script_address":2310199},{"address":3238752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":21,"species":306}],"party_address":3214620,"script_address":2310337},{"address":3238792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":27}],"party_address":3214628,"script_address":2046600},{"address":3238832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":288},{"level":26,"species":304}],"party_address":3214636,"script_address":0},{"address":3238872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":289},{"level":29,"species":305}],"party_address":3214652,"script_address":0},{"address":3238912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":27},{"level":31,"species":305},{"level":31,"species":289}],"party_address":3214668,"script_address":0},{"address":3238952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":305},{"level":34,"species":28},{"level":34,"species":289}],"party_address":3214692,"script_address":0},{"address":3238992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":311}],"party_address":3214716,"script_address":2061044},{"address":3239032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":24,"species":290},{"level":24,"species":291},{"level":24,"species":292}],"party_address":3214724,"script_address":2061075},{"address":3239072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":290},{"level":27,"species":293},{"level":27,"species":294}],"party_address":3214748,"script_address":2061106},{"address":3239112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":311},{"level":27,"species":311},{"level":27,"species":311}],"party_address":3214772,"script_address":2065541},{"address":3239152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":16,"species":294},{"level":16,"species":292}],"party_address":3214796,"script_address":2057595},{"address":3239192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":311},{"level":31,"species":311},{"level":31,"species":311}],"party_address":3214812,"script_address":0},{"address":3239232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":311},{"level":34,"species":311},{"level":34,"species":312}],"party_address":3214836,"script_address":0},{"address":3239272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":311},{"level":36,"species":290},{"level":36,"species":311},{"level":36,"species":312}],"party_address":3214860,"script_address":0},{"address":3239312,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":38,"species":311},{"level":38,"species":294},{"level":38,"species":311},{"level":38,"species":312},{"level":38,"species":292}],"party_address":3214892,"script_address":0},{"address":3239352,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":15,"moves":[237,0,0,0],"species":63}],"party_address":3214932,"script_address":2038374},{"address":3239392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":393}],"party_address":3214948,"script_address":2244488},{"address":3239432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":392}],"party_address":3214956,"script_address":2244519},{"address":3239472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":203}],"party_address":3214964,"script_address":2244550},{"address":3239512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":392},{"level":26,"species":392},{"level":26,"species":393}],"party_address":3214972,"script_address":2314189},{"address":3239552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":41,"species":64},{"level":41,"species":349}],"party_address":3214996,"script_address":2564698},{"address":3239592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":349}],"party_address":3215012,"script_address":2068179},{"address":3239632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":64},{"level":33,"species":349}],"party_address":3215020,"script_address":0},{"address":3239672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":38,"species":64},{"level":38,"species":349}],"party_address":3215036,"script_address":0},{"address":3239712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":41,"species":64},{"level":41,"species":349}],"party_address":3215052,"script_address":0},{"address":3239752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":45,"species":349},{"level":45,"species":65}],"party_address":3215068,"script_address":0},{"address":3239792,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":16,"moves":[237,0,0,0],"species":63}],"party_address":3215084,"script_address":2038405},{"address":3239832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":393}],"party_address":3215100,"script_address":2244581},{"address":3239872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":178}],"party_address":3215108,"script_address":2244612},{"address":3239912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":64}],"party_address":3215116,"script_address":2244643},{"address":3239952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":202},{"level":26,"species":177},{"level":26,"species":64}],"party_address":3215124,"script_address":2314220},{"address":3239992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":41,"species":393},{"level":41,"species":178}],"party_address":3215148,"script_address":2564760},{"address":3240032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":64},{"level":30,"species":348}],"party_address":3215164,"script_address":2068289},{"address":3240072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":64},{"level":34,"species":348}],"party_address":3215180,"script_address":0},{"address":3240112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":37,"species":64},{"level":37,"species":348}],"party_address":3215196,"script_address":0},{"address":3240152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":40,"species":64},{"level":40,"species":348}],"party_address":3215212,"script_address":0},{"address":3240192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":43,"species":348},{"level":43,"species":65}],"party_address":3215228,"script_address":0},{"address":3240232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":338}],"party_address":3215244,"script_address":2067174},{"address":3240272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":44,"species":338},{"level":44,"species":338}],"party_address":3215252,"script_address":2360864},{"address":3240312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":45,"species":380}],"party_address":3215268,"script_address":2360895},{"address":3240352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":338}],"party_address":3215276,"script_address":0},{"address":3240392,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":36,"moves":[29,28,60,154],"species":289},{"level":36,"moves":[98,209,60,46],"species":338}],"party_address":3215284,"script_address":0},{"address":3240432,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":39,"moves":[29,28,60,154],"species":289},{"level":39,"moves":[98,209,60,0],"species":338}],"party_address":3215316,"script_address":0},{"address":3240472,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":41,"moves":[29,28,60,154],"species":289},{"level":41,"moves":[154,50,93,244],"species":55},{"level":41,"moves":[98,209,60,46],"species":338}],"party_address":3215348,"script_address":0},{"address":3240512,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":46,"moves":[46,38,28,242],"species":287},{"level":48,"moves":[3,104,207,70],"species":300},{"level":46,"moves":[73,185,46,178],"species":345},{"level":48,"moves":[57,14,70,7],"species":327},{"level":49,"moves":[76,157,14,163],"species":376}],"party_address":3215396,"script_address":2274753},{"address":3240552,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":48,"moves":[69,109,174,182],"species":362},{"level":49,"moves":[247,32,5,185],"species":378},{"level":50,"moves":[247,104,101,185],"species":322},{"level":49,"moves":[247,94,85,7],"species":378},{"level":51,"moves":[247,58,157,89],"species":362}],"party_address":3215476,"script_address":2275380},{"address":3240592,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":50,"moves":[227,34,2,45],"species":342},{"level":50,"moves":[113,242,196,58],"species":347},{"level":52,"moves":[213,38,2,59],"species":342},{"level":52,"moves":[247,153,2,58],"species":347},{"level":53,"moves":[57,34,58,73],"species":343}],"party_address":3215556,"script_address":2276062},{"address":3240632,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":52,"moves":[61,81,182,38],"species":396},{"level":54,"moves":[38,225,93,76],"species":359},{"level":53,"moves":[108,93,57,34],"species":230},{"level":53,"moves":[53,242,225,89],"species":334},{"level":55,"moves":[53,81,157,242],"species":397}],"party_address":3215636,"script_address":2276724},{"address":3240672,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":12,"moves":[33,111,88,61],"species":74},{"level":12,"moves":[33,111,88,61],"species":74},{"level":15,"moves":[79,106,33,61],"species":320}],"party_address":3215716,"script_address":2187976},{"address":3240712,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":16,"moves":[2,67,69,83],"species":66},{"level":16,"moves":[8,113,115,83],"species":356},{"level":19,"moves":[36,233,179,83],"species":335}],"party_address":3215764,"script_address":2095066},{"address":3240752,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":20,"moves":[205,209,120,95],"species":100},{"level":20,"moves":[95,43,98,80],"species":337},{"level":22,"moves":[48,95,86,49],"species":82},{"level":24,"moves":[98,86,95,80],"species":338}],"party_address":3215812,"script_address":2167181},{"address":3240792,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":24,"moves":[59,36,222,241],"species":339},{"level":24,"moves":[59,123,113,241],"species":218},{"level":26,"moves":[59,33,241,213],"species":340},{"level":29,"moves":[59,241,34,213],"species":321}],"party_address":3215876,"script_address":2103186},{"address":3240832,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":27,"moves":[42,60,7,227],"species":308},{"level":27,"moves":[163,7,227,185],"species":365},{"level":29,"moves":[163,187,7,29],"species":289},{"level":31,"moves":[68,25,7,185],"species":366}],"party_address":3215940,"script_address":2129756},{"address":3240872,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":29,"moves":[195,119,219,76],"species":358},{"level":29,"moves":[241,76,76,235],"species":369},{"level":30,"moves":[55,48,182,76],"species":310},{"level":31,"moves":[28,31,211,76],"species":227},{"level":33,"moves":[89,225,93,76],"species":359}],"party_address":3216004,"script_address":2202062},{"address":3240912,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":41,"moves":[89,246,94,113],"species":319},{"level":41,"moves":[94,241,109,91],"species":178},{"level":42,"moves":[113,94,95,91],"species":348},{"level":42,"moves":[241,76,94,53],"species":349}],"party_address":3216084,"script_address":0},{"address":3240952,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":41,"moves":[96,213,186,175],"species":325},{"level":41,"moves":[240,96,133,89],"species":324},{"level":43,"moves":[227,34,62,96],"species":342},{"level":43,"moves":[96,152,13,43],"species":327},{"level":46,"moves":[96,104,58,156],"species":230}],"party_address":3216148,"script_address":2262245},{"address":3240992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":9,"species":392}],"party_address":3216228,"script_address":2054242},{"address":3241032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":17,"species":392}],"party_address":3216236,"script_address":2554598},{"address":3241072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":15,"species":339},{"level":15,"species":43},{"level":15,"species":309}],"party_address":3216244,"script_address":2554629},{"address":3241112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":392},{"level":26,"species":356}],"party_address":3216268,"script_address":0},{"address":3241152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":393},{"level":29,"species":356}],"party_address":3216284,"script_address":0},{"address":3241192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":393},{"level":32,"species":357}],"party_address":3216300,"script_address":0},{"address":3241232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":393},{"level":34,"species":378},{"level":34,"species":357}],"party_address":3216316,"script_address":0},{"address":3241272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":9,"species":306}],"party_address":3216340,"script_address":2054490},{"address":3241312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":16,"species":306},{"level":16,"species":292}],"party_address":3216348,"script_address":2554660},{"address":3241352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":306},{"level":26,"species":370}],"party_address":3216364,"script_address":0},{"address":3241392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":306},{"level":29,"species":371}],"party_address":3216380,"script_address":0},{"address":3241432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":307},{"level":32,"species":371}],"party_address":3216396,"script_address":0},{"address":3241472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":307},{"level":35,"species":372}],"party_address":3216412,"script_address":0},{"address":3241512,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":30,"moves":[95,60,146,42],"species":308},{"level":32,"moves":[8,25,47,185],"species":366}],"party_address":3216428,"script_address":0},{"address":3241552,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":15,"moves":[45,39,29,60],"species":288},{"level":17,"moves":[33,116,36,0],"species":335}],"party_address":3216460,"script_address":0},{"address":3241592,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":28,"moves":[45,39,29,60],"species":288},{"level":30,"moves":[33,116,36,0],"species":335}],"party_address":3216492,"script_address":0},{"address":3241632,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":31,"moves":[45,39,29,60],"species":288},{"level":33,"moves":[33,116,36,0],"species":335}],"party_address":3216524,"script_address":0},{"address":3241672,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":34,"moves":[45,39,29,60],"species":289},{"level":36,"moves":[33,116,36,0],"species":335}],"party_address":3216556,"script_address":0},{"address":3241712,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":36,"moves":[45,39,29,60],"species":289},{"level":38,"moves":[33,116,36,0],"species":336}],"party_address":3216588,"script_address":0},{"address":3241752,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":16,"species":304},{"level":16,"species":288}],"party_address":3216620,"script_address":2045785},{"address":3241792,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":15,"species":315}],"party_address":3216636,"script_address":2026353},{"address":3241832,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":22,"moves":[18,204,185,215],"species":315},{"level":36,"moves":[18,204,185,215],"species":315},{"level":40,"moves":[18,204,185,215],"species":315},{"level":12,"moves":[18,204,185,215],"species":315},{"level":30,"moves":[18,204,185,215],"species":315},{"level":42,"moves":[18,204,185,215],"species":316}],"party_address":3216644,"script_address":2360833},{"address":3241872,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":29,"species":315}],"party_address":3216740,"script_address":0},{"address":3241912,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":32,"species":315}],"party_address":3216748,"script_address":0},{"address":3241952,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":35,"species":316}],"party_address":3216756,"script_address":0},{"address":3241992,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":38,"species":316}],"party_address":3216764,"script_address":0},{"address":3242032,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":17,"species":363}],"party_address":3216772,"script_address":2045890},{"address":3242072,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":30,"species":25}],"party_address":3216780,"script_address":2067143},{"address":3242112,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":35,"species":350},{"level":37,"species":183},{"level":39,"species":184}],"party_address":3216788,"script_address":2265832},{"address":3242152,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":14,"species":353},{"level":14,"species":354}],"party_address":3216812,"script_address":2038890},{"address":3242192,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":26,"species":353},{"level":26,"species":354}],"party_address":3216828,"script_address":0},{"address":3242232,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":29,"species":353},{"level":29,"species":354}],"party_address":3216844,"script_address":0},{"address":3242272,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":32,"species":353},{"level":32,"species":354}],"party_address":3216860,"script_address":0},{"address":3242312,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":35,"species":353},{"level":35,"species":354}],"party_address":3216876,"script_address":0},{"address":3242352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":336}],"party_address":3216892,"script_address":2052811},{"address":3242392,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":33,"moves":[36,26,28,91],"species":336}],"party_address":3216900,"script_address":0},{"address":3242432,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":36,"moves":[36,26,28,91],"species":336}],"party_address":3216916,"script_address":0},{"address":3242472,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":39,"moves":[36,187,28,91],"species":336}],"party_address":3216932,"script_address":0},{"address":3242512,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":42,"moves":[36,187,28,91],"species":336}],"party_address":3216948,"script_address":0},{"address":3242552,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":18,"moves":[136,96,93,197],"species":356}],"party_address":3216964,"script_address":2046100},{"address":3242592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":21,"species":356},{"level":21,"species":335}],"party_address":3216980,"script_address":2304277},{"address":3242632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":356},{"level":30,"species":335}],"party_address":3216996,"script_address":0},{"address":3242672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":357},{"level":33,"species":336}],"party_address":3217012,"script_address":0},{"address":3242712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":357},{"level":36,"species":336}],"party_address":3217028,"script_address":0},{"address":3242752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":39,"species":357},{"level":39,"species":336}],"party_address":3217044,"script_address":0},{"address":3242792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":286}],"party_address":3217060,"script_address":2024678},{"address":3242832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":288},{"level":7,"species":298}],"party_address":3217068,"script_address":2029684},{"address":3242872,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":10,"moves":[33,0,0,0],"species":74}],"party_address":3217084,"script_address":2188154},{"address":3242912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":8,"species":74},{"level":8,"species":74}],"party_address":3217100,"script_address":2188185},{"address":3242952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":9,"species":66}],"party_address":3217116,"script_address":2054180},{"address":3242992,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":17,"moves":[29,28,45,85],"species":288},{"level":17,"moves":[133,124,25,1],"species":367}],"party_address":3217124,"script_address":2167670},{"address":3243032,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":43,"moves":[213,58,85,53],"species":366},{"level":43,"moves":[29,182,5,92],"species":362}],"party_address":3217156,"script_address":2332778},{"address":3243072,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":43,"moves":[29,94,85,91],"species":394},{"level":43,"moves":[89,247,76,24],"species":366}],"party_address":3217188,"script_address":2332809},{"address":3243112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":19,"species":332}],"party_address":3217220,"script_address":2050594},{"address":3243152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":19,"species":382}],"party_address":3217228,"script_address":2050625},{"address":3243192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":287}],"party_address":3217236,"script_address":0},{"address":3243232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":305},{"level":30,"species":287}],"party_address":3217244,"script_address":0},{"address":3243272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":305},{"level":29,"species":289},{"level":33,"species":287}],"party_address":3217260,"script_address":0},{"address":3243312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":305},{"level":32,"species":289},{"level":36,"species":287}],"party_address":3217284,"script_address":0},{"address":3243352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":14,"species":288},{"level":16,"species":288}],"party_address":3217308,"script_address":2553792},{"address":3243392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":4,"species":288},{"level":3,"species":304}],"party_address":3217324,"script_address":2024926},{"address":3243432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":15,"species":382},{"level":13,"species":337}],"party_address":3217340,"script_address":2039000},{"address":3243472,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":57,"moves":[240,67,38,59],"species":314},{"level":55,"moves":[92,56,188,58],"species":73},{"level":56,"moves":[202,57,73,104],"species":297},{"level":56,"moves":[89,57,133,63],"species":324},{"level":56,"moves":[93,89,63,57],"species":130},{"level":58,"moves":[105,57,58,92],"species":329}],"party_address":3217356,"script_address":2277575},{"address":3243512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":129},{"level":10,"species":72},{"level":15,"species":129}],"party_address":3217452,"script_address":2026322},{"address":3243552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":129},{"level":6,"species":129},{"level":7,"species":129}],"party_address":3217476,"script_address":2029653},{"address":3243592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":16,"species":129},{"level":17,"species":118},{"level":18,"species":323}],"party_address":3217500,"script_address":2052185},{"address":3243632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":10,"species":129},{"level":7,"species":72},{"level":10,"species":129}],"party_address":3217524,"script_address":2034247},{"address":3243672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":11,"species":72}],"party_address":3217548,"script_address":2034357},{"address":3243712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":11,"species":72},{"level":14,"species":313},{"level":11,"species":72},{"level":14,"species":313}],"party_address":3217556,"script_address":2038546},{"address":3243752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":19,"species":323}],"party_address":3217588,"script_address":2052216},{"address":3243792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":72},{"level":25,"species":330}],"party_address":3217596,"script_address":2058894},{"address":3243832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":16,"species":72}],"party_address":3217612,"script_address":2058925},{"address":3243872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":313},{"level":25,"species":73}],"party_address":3217620,"script_address":2036183},{"address":3243912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":24,"species":72},{"level":27,"species":130},{"level":27,"species":130}],"party_address":3217636,"script_address":0},{"address":3243952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":130},{"level":26,"species":330},{"level":26,"species":72},{"level":29,"species":130}],"party_address":3217660,"script_address":0},{"address":3243992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":130},{"level":30,"species":330},{"level":30,"species":73},{"level":31,"species":130}],"party_address":3217692,"script_address":0},{"address":3244032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":130},{"level":33,"species":331},{"level":33,"species":130},{"level":35,"species":73}],"party_address":3217724,"script_address":0},{"address":3244072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":19,"species":129},{"level":21,"species":130},{"level":23,"species":130},{"level":26,"species":130},{"level":30,"species":130},{"level":35,"species":130}],"party_address":3217756,"script_address":2073670},{"address":3244112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":6,"species":100},{"level":6,"species":100},{"level":14,"species":81}],"party_address":3217804,"script_address":2038577},{"address":3244152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":14,"species":81},{"level":14,"species":81}],"party_address":3217828,"script_address":2038608},{"address":3244192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":16,"species":81}],"party_address":3217844,"script_address":2038639},{"address":3244232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":81}],"party_address":3217852,"script_address":0},{"address":3244272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":81}],"party_address":3217860,"script_address":0},{"address":3244312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":82}],"party_address":3217868,"script_address":0},{"address":3244352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":39,"species":82}],"party_address":3217876,"script_address":0},{"address":3244392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":16,"species":81}],"party_address":3217884,"script_address":2038780},{"address":3244432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":14,"species":81},{"level":14,"species":81},{"level":6,"species":100}],"party_address":3217892,"script_address":2038749},{"address":3244472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":81}],"party_address":3217916,"script_address":0},{"address":3244512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":81}],"party_address":3217924,"script_address":0},{"address":3244552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":82}],"party_address":3217932,"script_address":0},{"address":3244592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":37,"species":82}],"party_address":3217940,"script_address":0},{"address":3244632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":17,"species":84}],"party_address":3217948,"script_address":2057375},{"address":3244672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":84}],"party_address":3217956,"script_address":0},{"address":3244712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":84}],"party_address":3217964,"script_address":0},{"address":3244752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":85}],"party_address":3217972,"script_address":0},{"address":3244792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":37,"species":85}],"party_address":3217980,"script_address":0},{"address":3244832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":17,"species":84}],"party_address":3217988,"script_address":2057485},{"address":3244872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":84}],"party_address":3217996,"script_address":0},{"address":3244912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":84}],"party_address":3218004,"script_address":0},{"address":3244952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":85}],"party_address":3218012,"script_address":0},{"address":3244992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":37,"species":85}],"party_address":3218020,"script_address":0},{"address":3245032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":120},{"level":33,"species":120}],"party_address":3218028,"script_address":2070582},{"address":3245072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":288},{"level":25,"species":337}],"party_address":3218044,"script_address":2340077},{"address":3245112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":120}],"party_address":3218060,"script_address":2071332},{"address":3245152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":120},{"level":33,"species":120}],"party_address":3218068,"script_address":2070380},{"address":3245192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":309},{"level":34,"species":120}],"party_address":3218084,"script_address":2072978},{"address":3245232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":39,"species":120}],"party_address":3218100,"script_address":0},{"address":3245272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":42,"species":120}],"party_address":3218108,"script_address":0},{"address":3245312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":45,"species":121}],"party_address":3218116,"script_address":0},{"address":3245352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":48,"species":121}],"party_address":3218124,"script_address":0},{"address":3245392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":120}],"party_address":3218132,"script_address":2070318},{"address":3245432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":309},{"level":34,"species":120}],"party_address":3218140,"script_address":2070613},{"address":3245472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":120}],"party_address":3218156,"script_address":2073545},{"address":3245512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":120}],"party_address":3218164,"script_address":2071442},{"address":3245552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":309},{"level":33,"species":120}],"party_address":3218172,"script_address":2073009},{"address":3245592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":39,"species":120}],"party_address":3218188,"script_address":0},{"address":3245632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":42,"species":120}],"party_address":3218196,"script_address":0},{"address":3245672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":45,"species":121}],"party_address":3218204,"script_address":0},{"address":3245712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":48,"species":121}],"party_address":3218212,"script_address":0},{"address":3245752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":37,"species":359},{"level":37,"species":359}],"party_address":3218220,"script_address":2292701},{"address":3245792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":41,"species":359},{"level":41,"species":359}],"party_address":3218236,"script_address":0},{"address":3245832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":44,"species":359},{"level":44,"species":359}],"party_address":3218252,"script_address":0},{"address":3245872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":46,"species":395},{"level":46,"species":359},{"level":46,"species":359}],"party_address":3218268,"script_address":0},{"address":3245912,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":49,"species":359},{"level":49,"species":359},{"level":49,"species":396}],"party_address":3218292,"script_address":0},{"address":3245952,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":34,"moves":[225,29,116,52],"species":395}],"party_address":3218316,"script_address":2074182},{"address":3245992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":309}],"party_address":3218332,"script_address":2059066},{"address":3246032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":309},{"level":25,"species":369}],"party_address":3218340,"script_address":2061450},{"address":3246072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":305}],"party_address":3218356,"script_address":2061481},{"address":3246112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":84},{"level":27,"species":227},{"level":27,"species":369}],"party_address":3218364,"script_address":2202267},{"address":3246152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":227}],"party_address":3218388,"script_address":2202391},{"address":3246192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":369},{"level":33,"species":178}],"party_address":3218396,"script_address":2070085},{"address":3246232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":84},{"level":29,"species":310}],"party_address":3218412,"script_address":2202298},{"address":3246272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":309},{"level":28,"species":177}],"party_address":3218428,"script_address":2065338},{"address":3246312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":358}],"party_address":3218444,"script_address":2065369},{"address":3246352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":305},{"level":36,"species":310},{"level":36,"species":178}],"party_address":3218452,"script_address":2563257},{"address":3246392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":304},{"level":25,"species":305}],"party_address":3218476,"script_address":2059097},{"address":3246432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":177},{"level":32,"species":358}],"party_address":3218492,"script_address":0},{"address":3246472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":177},{"level":35,"species":359}],"party_address":3218508,"script_address":0},{"address":3246512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":38,"species":177},{"level":38,"species":359}],"party_address":3218524,"script_address":0},{"address":3246552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":41,"species":359},{"level":41,"species":178}],"party_address":3218540,"script_address":0},{"address":3246592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":177},{"level":33,"species":305}],"party_address":3218556,"script_address":2074151},{"address":3246632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":369}],"party_address":3218572,"script_address":2073981},{"address":3246672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":302}],"party_address":3218580,"script_address":2061512},{"address":3246712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":302},{"level":25,"species":109}],"party_address":3218588,"script_address":2061543},{"address":3246752,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":43,"moves":[29,89,0,0],"species":319},{"level":43,"moves":[85,89,0,0],"species":171}],"party_address":3218604,"script_address":2335578},{"address":3246792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":183}],"party_address":3218636,"script_address":2341860},{"address":3246832,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":17,"moves":[139,33,123,120],"species":109},{"level":17,"moves":[139,33,123,120],"species":109},{"level":17,"moves":[139,33,124,120],"species":109}],"party_address":3218644,"script_address":2050766},{"address":3246872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":109},{"level":18,"species":302}],"party_address":3218692,"script_address":2050876},{"address":3246912,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":24,"moves":[139,33,124,120],"species":109},{"level":24,"moves":[139,33,124,0],"species":109},{"level":24,"moves":[139,33,124,120],"species":109},{"level":26,"moves":[33,124,0,0],"species":109}],"party_address":3218708,"script_address":0},{"address":3246952,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":27,"moves":[139,33,124,120],"species":109},{"level":27,"moves":[139,33,124,120],"species":109},{"level":27,"moves":[139,33,124,0],"species":109},{"level":29,"moves":[33,124,0,0],"species":109}],"party_address":3218772,"script_address":0},{"address":3246992,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":30,"moves":[139,33,124,0],"species":109},{"level":30,"moves":[139,33,124,0],"species":109},{"level":30,"moves":[139,33,124,0],"species":109},{"level":32,"moves":[33,124,0,0],"species":109}],"party_address":3218836,"script_address":0},{"address":3247032,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":33,"moves":[139,33,124,0],"species":109},{"level":33,"moves":[139,33,124,120],"species":109},{"level":33,"moves":[139,33,124,120],"species":109},{"level":35,"moves":[33,124,0,0],"species":110}],"party_address":3218900,"script_address":0},{"address":3247072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":13,"species":356}],"party_address":3218964,"script_address":2095313},{"address":3247112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":13,"species":356}],"party_address":3218972,"script_address":2095351},{"address":3247152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":356},{"level":18,"species":335}],"party_address":3218980,"script_address":2053062},{"address":3247192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":356}],"party_address":3218996,"script_address":2557727},{"address":3247232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":307}],"party_address":3219004,"script_address":2557789},{"address":3247272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":356},{"level":26,"species":335}],"party_address":3219012,"script_address":0},{"address":3247312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":356},{"level":29,"species":335}],"party_address":3219028,"script_address":0},{"address":3247352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":357},{"level":32,"species":336}],"party_address":3219044,"script_address":0},{"address":3247392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":357},{"level":35,"species":336}],"party_address":3219060,"script_address":0},{"address":3247432,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":19,"moves":[52,33,222,241],"species":339}],"party_address":3219076,"script_address":2050656},{"address":3247472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":363},{"level":28,"species":313}],"party_address":3219092,"script_address":2065713},{"address":3247512,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":30,"moves":[240,55,87,96],"species":385}],"party_address":3219108,"script_address":2065744},{"address":3247552,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":29,"moves":[52,33,222,241],"species":339}],"party_address":3219124,"script_address":0},{"address":3247592,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":32,"moves":[52,36,222,241],"species":339}],"party_address":3219140,"script_address":0},{"address":3247632,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":34,"moves":[73,72,64,241],"species":363},{"level":34,"moves":[53,36,222,241],"species":339}],"party_address":3219156,"script_address":0},{"address":3247672,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":37,"moves":[73,202,76,241],"species":363},{"level":37,"moves":[53,36,89,241],"species":340}],"party_address":3219188,"script_address":0},{"address":3247712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":309},{"level":25,"species":313}],"party_address":3219220,"script_address":2033633},{"address":3247752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":183}],"party_address":3219236,"script_address":2033664},{"address":3247792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":313}],"party_address":3219244,"script_address":2034216},{"address":3247832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":309},{"level":25,"species":118}],"party_address":3219252,"script_address":2034620},{"address":3247872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":118}],"party_address":3219268,"script_address":2034651},{"address":3247912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":116},{"level":25,"species":183}],"party_address":3219276,"script_address":2034838},{"address":3247952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":118}],"party_address":3219292,"script_address":2034869},{"address":3247992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":24,"species":118},{"level":24,"species":309},{"level":24,"species":118}],"party_address":3219300,"script_address":2035808},{"address":3248032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":313}],"party_address":3219324,"script_address":2069240},{"address":3248072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":183}],"party_address":3219332,"script_address":2069350},{"address":3248112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":325}],"party_address":3219340,"script_address":2069851},{"address":3248152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":119}],"party_address":3219348,"script_address":2069882},{"address":3248192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":183},{"level":33,"species":341}],"party_address":3219356,"script_address":2070225},{"address":3248232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":118}],"party_address":3219372,"script_address":2070256},{"address":3248272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":118},{"level":33,"species":341}],"party_address":3219380,"script_address":2073260},{"address":3248312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":325}],"party_address":3219396,"script_address":2073421},{"address":3248352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":119}],"party_address":3219404,"script_address":2073452},{"address":3248392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":184}],"party_address":3219412,"script_address":2073639},{"address":3248432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":325},{"level":33,"species":325}],"party_address":3219420,"script_address":2070349},{"address":3248472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":119}],"party_address":3219436,"script_address":2073888},{"address":3248512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":116},{"level":33,"species":117}],"party_address":3219444,"script_address":2073919},{"address":3248552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":171},{"level":34,"species":310}],"party_address":3219460,"script_address":0},{"address":3248592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":325},{"level":33,"species":325}],"party_address":3219476,"script_address":2074120},{"address":3248632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":119}],"party_address":3219492,"script_address":2071676},{"address":3248672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":38,"species":313}],"party_address":3219500,"script_address":0},{"address":3248712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":41,"species":313}],"party_address":3219508,"script_address":0},{"address":3248752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":43,"species":120},{"level":43,"species":313}],"party_address":3219516,"script_address":0},{"address":3248792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":45,"species":325},{"level":45,"species":313},{"level":45,"species":121}],"party_address":3219532,"script_address":0},{"address":3248832,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":22,"moves":[91,28,40,163],"species":27},{"level":22,"moves":[229,189,60,61],"species":318}],"party_address":3219556,"script_address":2046397},{"address":3248872,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":22,"moves":[28,40,163,91],"species":27},{"level":22,"moves":[205,61,39,111],"species":183}],"party_address":3219588,"script_address":2046459},{"address":3248912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":17,"species":304},{"level":17,"species":296}],"party_address":3219620,"script_address":2049860},{"address":3248952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":183},{"level":18,"species":296}],"party_address":3219636,"script_address":2051934},{"address":3248992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":23,"species":315},{"level":23,"species":358}],"party_address":3219652,"script_address":2557036},{"address":3249032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":19,"species":306},{"level":19,"species":43},{"level":19,"species":358}],"party_address":3219668,"script_address":2310092},{"address":3249072,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":32,"moves":[194,219,68,243],"species":202}],"party_address":3219692,"script_address":2315855},{"address":3249112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":17,"species":306},{"level":17,"species":183}],"party_address":3219708,"script_address":2046631},{"address":3249152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":306},{"level":25,"species":44},{"level":25,"species":358}],"party_address":3219724,"script_address":0},{"address":3249192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":307},{"level":28,"species":44},{"level":28,"species":358}],"party_address":3219748,"script_address":0},{"address":3249232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":307},{"level":31,"species":44},{"level":31,"species":358}],"party_address":3219772,"script_address":0},{"address":3249272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":40,"species":307},{"level":40,"species":45},{"level":40,"species":359}],"party_address":3219796,"script_address":0},{"address":3249312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":15,"species":353},{"level":15,"species":354}],"party_address":3219820,"script_address":0},{"address":3249352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":353},{"level":27,"species":354}],"party_address":3219836,"script_address":0},{"address":3249392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":6,"species":298},{"level":6,"species":295}],"party_address":3219852,"script_address":0},{"address":3249432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":292},{"level":26,"species":294}],"party_address":3219868,"script_address":0},{"address":3249472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":9,"species":353},{"level":9,"species":354}],"party_address":3219884,"script_address":0},{"address":3249512,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":10,"moves":[101,50,0,0],"species":361},{"level":10,"moves":[71,73,0,0],"species":306}],"party_address":3219900,"script_address":0},{"address":3249552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":353},{"level":30,"species":354}],"party_address":3219932,"script_address":0},{"address":3249592,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":33,"moves":[209,12,57,14],"species":353},{"level":33,"moves":[209,12,204,14],"species":354}],"party_address":3219948,"script_address":0},{"address":3249632,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":36,"moves":[87,12,57,14],"species":353},{"level":36,"moves":[87,12,204,14],"species":354}],"party_address":3219980,"script_address":0},{"address":3249672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":12,"species":309},{"level":12,"species":66}],"party_address":3220012,"script_address":2035839},{"address":3249712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":13,"species":309}],"party_address":3220028,"script_address":2035870},{"address":3249752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":309},{"level":33,"species":67}],"party_address":3220036,"script_address":2069913},{"address":3249792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":11,"species":309},{"level":11,"species":66},{"level":11,"species":72}],"party_address":3220052,"script_address":2543939},{"address":3249832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":44,"species":73},{"level":44,"species":67}],"party_address":3220076,"script_address":2360255},{"address":3249872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":43,"species":66},{"level":43,"species":310},{"level":43,"species":67}],"party_address":3220092,"script_address":2360286},{"address":3249912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":341},{"level":25,"species":67}],"party_address":3220116,"script_address":2340984},{"address":3249952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":309},{"level":36,"species":72},{"level":36,"species":67}],"party_address":3220132,"script_address":0},{"address":3249992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":39,"species":310},{"level":39,"species":72},{"level":39,"species":67}],"party_address":3220156,"script_address":0},{"address":3250032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":42,"species":310},{"level":42,"species":72},{"level":42,"species":67}],"party_address":3220180,"script_address":0},{"address":3250072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":45,"species":310},{"level":45,"species":67},{"level":45,"species":73}],"party_address":3220204,"script_address":0},{"address":3250112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":23,"species":339}],"party_address":3220228,"script_address":2103632},{"address":3250152,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":39,"moves":[175,96,216,213],"species":328},{"level":39,"moves":[175,96,216,213],"species":328}],"party_address":3220236,"script_address":2265863},{"address":3250192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":376}],"party_address":3220268,"script_address":2068647},{"address":3250232,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":31,"moves":[92,87,120,188],"species":109}],"party_address":3220276,"script_address":2068616},{"address":3250272,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":31,"moves":[241,55,53,76],"species":385}],"party_address":3220292,"script_address":2068585},{"address":3250312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":338},{"level":33,"species":68}],"party_address":3220308,"script_address":2070116},{"address":3250352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":67},{"level":33,"species":341}],"party_address":3220324,"script_address":2074337},{"address":3250392,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":34,"moves":[44,46,86,85],"species":338}],"party_address":3220340,"script_address":2074306},{"address":3250432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":356},{"level":33,"species":336}],"party_address":3220356,"script_address":2074275},{"address":3250472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":313}],"party_address":3220372,"script_address":2074244},{"address":3250512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":170},{"level":33,"species":336}],"party_address":3220380,"script_address":2074043},{"address":3250552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":14,"species":296},{"level":14,"species":299}],"party_address":3220396,"script_address":2038436},{"address":3250592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":380},{"level":18,"species":379}],"party_address":3220412,"script_address":2053172},{"address":3250632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":340},{"level":38,"species":287},{"level":40,"species":42}],"party_address":3220428,"script_address":0},{"address":3250672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":296},{"level":26,"species":299}],"party_address":3220452,"script_address":0},{"address":3250712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":296},{"level":29,"species":299}],"party_address":3220468,"script_address":0},{"address":3250752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":296},{"level":32,"species":299}],"party_address":3220484,"script_address":0},{"address":3250792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":297},{"level":35,"species":300}],"party_address":3220500,"script_address":0},{"address":3250832,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":44,"moves":[76,219,225,93],"species":359},{"level":43,"moves":[47,18,204,185],"species":316},{"level":44,"moves":[89,73,202,92],"species":363},{"level":41,"moves":[48,85,161,103],"species":82},{"level":45,"moves":[104,91,94,248],"species":394}],"party_address":3220516,"script_address":2332529},{"address":3250872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":277}],"party_address":3220596,"script_address":2025759},{"address":3250912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":218},{"level":18,"species":309},{"level":20,"species":278}],"party_address":3220604,"script_address":2039798},{"address":3250952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":218},{"level":29,"species":310},{"level":31,"species":278}],"party_address":3220628,"script_address":2060578},{"address":3250992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":280}],"party_address":3220652,"script_address":2025703},{"address":3251032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":309},{"level":18,"species":296},{"level":20,"species":281}],"party_address":3220660,"script_address":2039742},{"address":3251072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":310},{"level":29,"species":296},{"level":31,"species":281}],"party_address":3220684,"script_address":2060522},{"address":3251112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":283}],"party_address":3220708,"script_address":2025731},{"address":3251152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":296},{"level":18,"species":218},{"level":20,"species":284}],"party_address":3220716,"script_address":2039770},{"address":3251192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":296},{"level":29,"species":218},{"level":31,"species":284}],"party_address":3220740,"script_address":2060550},{"address":3251232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":277}],"party_address":3220764,"script_address":2025675},{"address":3251272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":309},{"level":18,"species":218},{"level":20,"species":278}],"party_address":3220772,"script_address":2039622},{"address":3251312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":218},{"level":29,"species":296},{"level":31,"species":278}],"party_address":3220796,"script_address":2060420},{"address":3251352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":280}],"party_address":3220820,"script_address":2025619},{"address":3251392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":309},{"level":18,"species":296},{"level":20,"species":281}],"party_address":3220828,"script_address":2039566},{"address":3251432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":310},{"level":29,"species":296},{"level":31,"species":281}],"party_address":3220852,"script_address":2060364},{"address":3251472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":283}],"party_address":3220876,"script_address":2025647},{"address":3251512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":296},{"level":18,"species":218},{"level":20,"species":284}],"party_address":3220884,"script_address":2039594},{"address":3251552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":296},{"level":29,"species":218},{"level":31,"species":284}],"party_address":3220908,"script_address":2060392},{"address":3251592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":11,"species":370},{"level":11,"species":288},{"level":11,"species":382},{"level":11,"species":286},{"level":11,"species":304},{"level":11,"species":335}],"party_address":3220932,"script_address":2057155},{"address":3251632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":127}],"party_address":3220980,"script_address":2068678},{"address":3251672,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":43,"moves":[153,115,113,94],"species":348},{"level":43,"moves":[153,115,113,247],"species":349}],"party_address":3220988,"script_address":2334468},{"address":3251712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":22,"species":371},{"level":22,"species":289},{"level":22,"species":382},{"level":22,"species":287},{"level":22,"species":305},{"level":22,"species":335}],"party_address":3221020,"script_address":0},{"address":3251752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":371},{"level":25,"species":289},{"level":25,"species":382},{"level":25,"species":287},{"level":25,"species":305},{"level":25,"species":336}],"party_address":3221068,"script_address":0},{"address":3251792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":371},{"level":28,"species":289},{"level":28,"species":382},{"level":28,"species":287},{"level":28,"species":305},{"level":28,"species":336}],"party_address":3221116,"script_address":0},{"address":3251832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":371},{"level":31,"species":289},{"level":31,"species":383},{"level":31,"species":287},{"level":31,"species":305},{"level":31,"species":336}],"party_address":3221164,"script_address":0},{"address":3251872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":11,"species":309},{"level":11,"species":306},{"level":11,"species":183},{"level":11,"species":363},{"level":11,"species":315},{"level":11,"species":118}],"party_address":3221212,"script_address":2057265},{"address":3251912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":43,"species":322},{"level":43,"species":376}],"party_address":3221260,"script_address":2334499},{"address":3251952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":28}],"party_address":3221276,"script_address":2341891},{"address":3251992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":22,"species":309},{"level":22,"species":306},{"level":22,"species":183},{"level":22,"species":363},{"level":22,"species":315},{"level":22,"species":118}],"party_address":3221284,"script_address":0},{"address":3252032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":310},{"level":25,"species":307},{"level":25,"species":183},{"level":25,"species":363},{"level":25,"species":316},{"level":25,"species":118}],"party_address":3221332,"script_address":0},{"address":3252072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":310},{"level":28,"species":307},{"level":28,"species":183},{"level":28,"species":363},{"level":28,"species":316},{"level":28,"species":118}],"party_address":3221380,"script_address":0},{"address":3252112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":310},{"level":31,"species":307},{"level":31,"species":184},{"level":31,"species":363},{"level":31,"species":316},{"level":31,"species":119}],"party_address":3221428,"script_address":0},{"address":3252152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":307}],"party_address":3221476,"script_address":2061230},{"address":3252192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":298},{"level":28,"species":299},{"level":28,"species":296}],"party_address":3221484,"script_address":2065479},{"address":3252232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":39,"species":345}],"party_address":3221508,"script_address":2563288},{"address":3252272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":307}],"party_address":3221516,"script_address":0},{"address":3252312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":307}],"party_address":3221524,"script_address":0},{"address":3252352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":37,"species":307}],"party_address":3221532,"script_address":0},{"address":3252392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":39,"species":317},{"level":39,"species":307}],"party_address":3221540,"script_address":0},{"address":3252432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":44},{"level":26,"species":363}],"party_address":3221556,"script_address":2061340},{"address":3252472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":295},{"level":28,"species":296},{"level":28,"species":299}],"party_address":3221572,"script_address":2065510},{"address":3252512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":38,"species":358},{"level":38,"species":363}],"party_address":3221596,"script_address":2563226},{"address":3252552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":44},{"level":30,"species":363}],"party_address":3221612,"script_address":0},{"address":3252592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":44},{"level":33,"species":363}],"party_address":3221628,"script_address":0},{"address":3252632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":44},{"level":36,"species":363}],"party_address":3221644,"script_address":0},{"address":3252672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":39,"species":182},{"level":39,"species":363}],"party_address":3221660,"script_address":0},{"address":3252712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":21,"species":81}],"party_address":3221676,"script_address":2310306},{"address":3252752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":287},{"level":35,"species":42}],"party_address":3221684,"script_address":2327187},{"address":3252792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":313},{"level":31,"species":41}],"party_address":3221700,"script_address":0},{"address":3252832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":313},{"level":30,"species":41}],"party_address":3221716,"script_address":2317615},{"address":3252872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":22,"species":286},{"level":22,"species":339}],"party_address":3221732,"script_address":2309993},{"address":3252912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":8,"species":74},{"level":8,"species":74}],"party_address":3221748,"script_address":2188216},{"address":3252952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":13,"species":66}],"party_address":3221764,"script_address":2095389},{"address":3252992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":13,"species":356}],"party_address":3221772,"script_address":2095465},{"address":3253032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":13,"species":335}],"party_address":3221780,"script_address":2095427},{"address":3253072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":356}],"party_address":3221788,"script_address":2244674},{"address":3253112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":330}],"party_address":3221796,"script_address":2070287},{"address":3253152,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":32,"moves":[87,86,98,0],"species":338},{"level":32,"moves":[57,168,0,0],"species":289}],"party_address":3221804,"script_address":2070768},{"address":3253192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":73}],"party_address":3221836,"script_address":2071645},{"address":3253232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":20,"species":41}],"party_address":3221844,"script_address":2304070},{"address":3253272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":331}],"party_address":3221852,"script_address":2073102},{"address":3253312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":37,"species":203}],"party_address":3221860,"script_address":0},{"address":3253352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":351}],"party_address":3221868,"script_address":2244705},{"address":3253392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":64}],"party_address":3221876,"script_address":2244829},{"address":3253432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":203}],"party_address":3221884,"script_address":2244767},{"address":3253472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":202}],"party_address":3221892,"script_address":2244798},{"address":3253512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":41},{"level":31,"species":286}],"party_address":3221900,"script_address":2254605},{"address":3253552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":318}],"party_address":3221916,"script_address":2254667},{"address":3253592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":41}],"party_address":3221924,"script_address":2257768},{"address":3253632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":287}],"party_address":3221932,"script_address":2257818},{"address":3253672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":318}],"party_address":3221940,"script_address":2257868},{"address":3253712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":177}],"party_address":3221948,"script_address":2244736},{"address":3253752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":13,"species":295},{"level":15,"species":280}],"party_address":3221956,"script_address":1978559},{"address":3253792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":13,"species":309},{"level":15,"species":277}],"party_address":3221972,"script_address":1978621},{"address":3253832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":305},{"level":33,"species":307}],"party_address":3221988,"script_address":2073732},{"address":3253872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":120}],"party_address":3222004,"script_address":2069651},{"address":3253912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":41},{"level":27,"species":286}],"party_address":3222012,"script_address":2572062},{"address":3253952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":339},{"level":20,"species":286},{"level":22,"species":339},{"level":22,"species":41}],"party_address":3222028,"script_address":2304039},{"address":3253992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":317},{"level":33,"species":371}],"party_address":3222060,"script_address":2073794},{"address":3254032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":13,"species":218},{"level":15,"species":283}],"party_address":3222076,"script_address":1978590},{"address":3254072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":13,"species":309},{"level":15,"species":277}],"party_address":3222092,"script_address":1978317},{"address":3254112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":37,"species":287},{"level":38,"species":169},{"level":39,"species":340}],"party_address":3222108,"script_address":2351441},{"address":3254152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":24,"species":287},{"level":24,"species":41},{"level":25,"species":340}],"party_address":3222132,"script_address":2303440},{"address":3254192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":4,"species":288},{"level":4,"species":306}],"party_address":3222156,"script_address":2024895},{"address":3254232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":6,"species":295},{"level":6,"species":306}],"party_address":3222172,"script_address":2029715},{"address":3254272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":9,"species":183}],"party_address":3222188,"script_address":2054459},{"address":3254312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":15,"species":183},{"level":15,"species":306},{"level":15,"species":339}],"party_address":3222196,"script_address":2045995},{"address":3254352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":296},{"level":26,"species":306}],"party_address":3222220,"script_address":0},{"address":3254392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":296},{"level":29,"species":307}],"party_address":3222236,"script_address":0},{"address":3254432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":296},{"level":32,"species":307}],"party_address":3222252,"script_address":0},{"address":3254472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":305},{"level":34,"species":296},{"level":34,"species":307}],"party_address":3222268,"script_address":0},{"address":3254512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":16,"species":43}],"party_address":3222292,"script_address":2553761},{"address":3254552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":14,"species":315},{"level":14,"species":306},{"level":14,"species":183}],"party_address":3222300,"script_address":2553823},{"address":3254592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":40,"species":325}],"party_address":3222324,"script_address":2265615},{"address":3254632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":39,"species":118},{"level":39,"species":313}],"party_address":3222332,"script_address":2265646},{"address":3254672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":4,"species":290},{"level":4,"species":290}],"party_address":3222348,"script_address":2024864},{"address":3254712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":3,"species":290},{"level":3,"species":290},{"level":3,"species":290},{"level":3,"species":290}],"party_address":3222364,"script_address":2300392},{"address":3254752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":8,"species":290},{"level":8,"species":301}],"party_address":3222396,"script_address":2054211},{"address":3254792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":301},{"level":28,"species":302}],"party_address":3222412,"script_address":2061137},{"address":3254832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":386},{"level":25,"species":387}],"party_address":3222428,"script_address":2061168},{"address":3254872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":302}],"party_address":3222444,"script_address":2061199},{"address":3254912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":6,"species":301},{"level":6,"species":301}],"party_address":3222452,"script_address":2300423},{"address":3254952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":302}],"party_address":3222468,"script_address":0},{"address":3254992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":294},{"level":29,"species":302}],"party_address":3222476,"script_address":0},{"address":3255032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":311},{"level":31,"species":294},{"level":31,"species":302}],"party_address":3222492,"script_address":0},{"address":3255072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":311},{"level":33,"species":302},{"level":33,"species":294},{"level":33,"species":302}],"party_address":3222516,"script_address":0},{"address":3255112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":17,"species":339},{"level":17,"species":66}],"party_address":3222548,"script_address":2049688},{"address":3255152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":16,"species":74},{"level":17,"species":74},{"level":16,"species":74}],"party_address":3222564,"script_address":2049719},{"address":3255192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":74},{"level":18,"species":66}],"party_address":3222588,"script_address":2051841},{"address":3255232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":74},{"level":18,"species":339}],"party_address":3222604,"script_address":2051872},{"address":3255272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":22,"species":74},{"level":22,"species":320},{"level":22,"species":75}],"party_address":3222620,"script_address":2557067},{"address":3255312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":8,"species":74}],"party_address":3222644,"script_address":2054428},{"address":3255352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":20,"species":74},{"level":20,"species":318}],"party_address":3222652,"script_address":2310061},{"address":3255392,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":9,"moves":[150,55,0,0],"species":313}],"party_address":3222668,"script_address":0},{"address":3255432,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":10,"moves":[16,45,0,0],"species":310},{"level":10,"moves":[44,184,0,0],"species":286}],"party_address":3222684,"script_address":0},{"address":3255472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":16,"species":74},{"level":16,"species":74},{"level":16,"species":66}],"party_address":3222716,"script_address":2296023},{"address":3255512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":24,"species":74},{"level":24,"species":74},{"level":24,"species":74},{"level":24,"species":75}],"party_address":3222740,"script_address":0},{"address":3255552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":74},{"level":27,"species":74},{"level":27,"species":75},{"level":27,"species":75}],"party_address":3222772,"script_address":0},{"address":3255592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":74},{"level":30,"species":75},{"level":30,"species":75},{"level":30,"species":75}],"party_address":3222804,"script_address":0},{"address":3255632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":75},{"level":33,"species":75},{"level":33,"species":75},{"level":33,"species":76}],"party_address":3222836,"script_address":0},{"address":3255672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":316},{"level":31,"species":338}],"party_address":3222868,"script_address":0},{"address":3255712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":45,"species":325},{"level":45,"species":325}],"party_address":3222884,"script_address":0},{"address":3255752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":386},{"level":25,"species":387}],"party_address":3222900,"script_address":0},{"address":3255792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":386},{"level":30,"species":387}],"party_address":3222916,"script_address":0},{"address":3255832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":386},{"level":33,"species":387}],"party_address":3222932,"script_address":0},{"address":3255872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":386},{"level":36,"species":387}],"party_address":3222948,"script_address":0},{"address":3255912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":39,"species":386},{"level":39,"species":387}],"party_address":3222964,"script_address":0},{"address":3255952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":13,"species":118}],"party_address":3222980,"script_address":2543970},{"address":3255992,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":23,"moves":[53,154,185,20],"species":317}],"party_address":3222988,"script_address":2103539},{"address":3256032,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":17,"moves":[117,197,93,9],"species":356},{"level":17,"moves":[9,197,93,96],"species":356}],"party_address":3223004,"script_address":2167701},{"address":3256072,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":23,"moves":[117,197,93,7],"species":356}],"party_address":3223036,"script_address":2103508},{"address":3256112,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":25,"moves":[33,120,124,108],"species":109},{"level":25,"moves":[33,139,124,108],"species":109}],"party_address":3223052,"script_address":2061574},{"address":3256152,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":28,"moves":[139,120,124,108],"species":109},{"level":28,"moves":[28,104,210,14],"species":302}],"party_address":3223084,"script_address":2065775},{"address":3256192,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":28,"moves":[141,154,170,91],"species":301},{"level":28,"moves":[33,120,124,108],"species":109}],"party_address":3223116,"script_address":2065806},{"address":3256232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":305},{"level":29,"species":178}],"party_address":3223148,"script_address":2202329},{"address":3256272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":358},{"level":27,"species":358},{"level":27,"species":358}],"party_address":3223164,"script_address":2202360},{"address":3256312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":16,"species":392}],"party_address":3223188,"script_address":1971405},{"address":3256352,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":47,"moves":[76,219,225,93],"species":359},{"level":46,"moves":[47,18,204,185],"species":316},{"level":47,"moves":[89,73,202,92],"species":363},{"level":44,"moves":[48,85,161,103],"species":82},{"level":48,"moves":[104,91,94,248],"species":394}],"party_address":3223196,"script_address":2332607},{"address":3256392,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":50,"moves":[76,219,225,93],"species":359},{"level":49,"moves":[47,18,204,185],"species":316},{"level":50,"moves":[89,73,202,92],"species":363},{"level":47,"moves":[48,85,161,103],"species":82},{"level":51,"moves":[104,91,94,248],"species":394}],"party_address":3223276,"script_address":0},{"address":3256432,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":53,"moves":[76,219,225,93],"species":359},{"level":52,"moves":[47,18,204,185],"species":316},{"level":53,"moves":[89,73,202,92],"species":363},{"level":50,"moves":[48,85,161,103],"species":82},{"level":54,"moves":[104,91,94,248],"species":394}],"party_address":3223356,"script_address":0},{"address":3256472,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":56,"moves":[76,219,225,93],"species":359},{"level":55,"moves":[47,18,204,185],"species":316},{"level":56,"moves":[89,73,202,92],"species":363},{"level":53,"moves":[48,85,161,103],"species":82},{"level":57,"moves":[104,91,94,248],"species":394}],"party_address":3223436,"script_address":0},{"address":3256512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":369},{"level":32,"species":218},{"level":32,"species":310},{"level":34,"species":278}],"party_address":3223516,"script_address":1986165},{"address":3256552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":369},{"level":32,"species":310},{"level":32,"species":297},{"level":34,"species":281}],"party_address":3223548,"script_address":1986109},{"address":3256592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":369},{"level":32,"species":297},{"level":32,"species":218},{"level":34,"species":284}],"party_address":3223580,"script_address":1986137},{"address":3256632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":369},{"level":32,"species":218},{"level":32,"species":310},{"level":34,"species":278}],"party_address":3223612,"script_address":1986081},{"address":3256672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":369},{"level":32,"species":310},{"level":32,"species":297},{"level":34,"species":281}],"party_address":3223644,"script_address":1986025},{"address":3256712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":369},{"level":32,"species":297},{"level":32,"species":218},{"level":34,"species":284}],"party_address":3223676,"script_address":1986053},{"address":3256752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":313},{"level":31,"species":72},{"level":32,"species":331}],"party_address":3223708,"script_address":2070644},{"address":3256792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":330},{"level":34,"species":73}],"party_address":3223732,"script_address":2070675},{"address":3256832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":15,"species":129},{"level":25,"species":129},{"level":35,"species":130}],"party_address":3223748,"script_address":2070706},{"address":3256872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":44},{"level":34,"species":184}],"party_address":3223772,"script_address":2071552},{"address":3256912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":300},{"level":34,"species":320}],"party_address":3223788,"script_address":2071583},{"address":3256952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":67}],"party_address":3223804,"script_address":2070799},{"address":3256992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":72},{"level":31,"species":72},{"level":36,"species":313}],"party_address":3223812,"script_address":2071614},{"address":3257032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":305},{"level":32,"species":227}],"party_address":3223836,"script_address":2070737},{"address":3257072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":341},{"level":33,"species":331}],"party_address":3223852,"script_address":2073040},{"address":3257112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":170}],"party_address":3223868,"script_address":2073071},{"address":3257152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":19,"species":308},{"level":19,"species":308}],"party_address":3223876,"script_address":0},{"address":3257192,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":32,"moves":[47,31,219,76],"species":358},{"level":35,"moves":[53,36,156,89],"species":339}],"party_address":3223892,"script_address":0},{"address":3257232,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":18,"moves":[74,78,72,73],"species":363},{"level":20,"moves":[111,205,44,88],"species":75}],"party_address":3223924,"script_address":0},{"address":3257272,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":27,"moves":[16,60,92,182],"species":294},{"level":27,"moves":[16,72,213,78],"species":292}],"party_address":3223956,"script_address":0},{"address":3257312,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":39,"moves":[94,7,244,182],"species":357},{"level":39,"moves":[8,61,156,187],"species":336}],"party_address":3223988,"script_address":0},{"address":3257352,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":43,"moves":[94,7,244,182],"species":357},{"level":43,"moves":[8,61,156,187],"species":336}],"party_address":3224020,"script_address":0},{"address":3257392,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":46,"moves":[94,7,244,182],"species":357},{"level":46,"moves":[8,61,156,187],"species":336}],"party_address":3224052,"script_address":0},{"address":3257432,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":49,"moves":[94,7,244,182],"species":357},{"level":49,"moves":[8,61,156,187],"species":336}],"party_address":3224084,"script_address":0},{"address":3257472,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":52,"moves":[94,7,244,182],"species":357},{"level":52,"moves":[8,61,156,187],"species":336}],"party_address":3224116,"script_address":0},{"address":3257512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":184},{"level":33,"species":309}],"party_address":3224148,"script_address":0},{"address":3257552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":170},{"level":33,"species":330}],"party_address":3224164,"script_address":0},{"address":3257592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":42,"species":170},{"level":40,"species":330}],"party_address":3224180,"script_address":0},{"address":3257632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":45,"species":171},{"level":43,"species":330}],"party_address":3224196,"script_address":0},{"address":3257672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":48,"species":171},{"level":46,"species":331}],"party_address":3224212,"script_address":0},{"address":3257712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":51,"species":171},{"level":49,"species":331}],"party_address":3224228,"script_address":0},{"address":3257752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":27,"species":118},{"level":25,"species":72}],"party_address":3224244,"script_address":0},{"address":3257792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":129},{"level":20,"species":72},{"level":26,"species":328},{"level":23,"species":330}],"party_address":3224260,"script_address":2061605},{"address":3257832,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":8,"species":288},{"level":8,"species":286}],"party_address":3224292,"script_address":2054707},{"address":3257872,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":8,"species":295},{"level":8,"species":288}],"party_address":3224308,"script_address":2054676},{"address":3257912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":9,"species":129}],"party_address":3224324,"script_address":2030343},{"address":3257952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":13,"species":183}],"party_address":3224332,"script_address":2036307},{"address":3257992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":12,"species":72},{"level":12,"species":72}],"party_address":3224340,"script_address":2036276},{"address":3258032,"data_type":"ITEM_DEFAULT_MOVES","party":[{"level":14,"species":354},{"level":14,"species":353}],"party_address":3224356,"script_address":2039032},{"address":3258072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":14,"species":337},{"level":14,"species":100}],"party_address":3224372,"script_address":2039063},{"address":3258112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":15,"species":81}],"party_address":3224388,"script_address":2039094},{"address":3258152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":15,"species":100}],"party_address":3224396,"script_address":2026463},{"address":3258192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":15,"species":335}],"party_address":3224404,"script_address":2026494},{"address":3258232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":19,"species":27}],"party_address":3224412,"script_address":2046975},{"address":3258272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":363}],"party_address":3224420,"script_address":2047006},{"address":3258312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":306}],"party_address":3224428,"script_address":2046944},{"address":3258352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":339}],"party_address":3224436,"script_address":2046913},{"address":3258392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":17,"species":183},{"level":19,"species":296}],"party_address":3224444,"script_address":2050969},{"address":3258432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":17,"species":227},{"level":19,"species":305}],"party_address":3224460,"script_address":2051000},{"address":3258472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":318},{"level":18,"species":27}],"party_address":3224476,"script_address":2051031},{"address":3258512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":382},{"level":18,"species":382}],"party_address":3224492,"script_address":2051062},{"address":3258552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":296},{"level":18,"species":183}],"party_address":3224508,"script_address":2052309},{"address":3258592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":19,"species":323}],"party_address":3224524,"script_address":2052371},{"address":3258632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":19,"species":299}],"party_address":3224532,"script_address":2052340},{"address":3258672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":14,"species":288},{"level":14,"species":382},{"level":14,"species":337}],"party_address":3224540,"script_address":2059128},{"address":3258712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":41}],"party_address":3224564,"script_address":2347841},{"address":3258752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":286}],"party_address":3224572,"script_address":2347872},{"address":3258792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":339}],"party_address":3224580,"script_address":2348597},{"address":3258832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":318},{"level":28,"species":41}],"party_address":3224588,"script_address":2348628},{"address":3258872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":318},{"level":28,"species":339}],"party_address":3224604,"script_address":2348659},{"address":3258912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":287}],"party_address":3224620,"script_address":2349324},{"address":3258952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":41}],"party_address":3224628,"script_address":2349355},{"address":3258992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":286}],"party_address":3224636,"script_address":2349386},{"address":3259032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":41}],"party_address":3224644,"script_address":2350264},{"address":3259072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":287}],"party_address":3224652,"script_address":2350826},{"address":3259112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":318}],"party_address":3224660,"script_address":2351566},{"address":3259152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":339}],"party_address":3224668,"script_address":2351597},{"address":3259192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":41}],"party_address":3224676,"script_address":2351628},{"address":3259232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":287}],"party_address":3224684,"script_address":2348566},{"address":3259272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":339}],"party_address":3224692,"script_address":2349293},{"address":3259312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":318}],"party_address":3224700,"script_address":2350295},{"address":3259352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":339},{"level":28,"species":287},{"level":30,"species":41},{"level":33,"species":340}],"party_address":3224708,"script_address":2351659},{"address":3259392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":310},{"level":33,"species":340}],"party_address":3224740,"script_address":2073763},{"address":3259432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":42,"species":287},{"level":43,"species":169},{"level":44,"species":340}],"party_address":3224756,"script_address":0},{"address":3259472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":15,"species":72}],"party_address":3224780,"script_address":2026525},{"address":3259512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":15,"species":183}],"party_address":3224788,"script_address":2026556},{"address":3259552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":27},{"level":25,"species":27}],"party_address":3224796,"script_address":2033726},{"address":3259592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":304},{"level":25,"species":309}],"party_address":3224812,"script_address":2033695},{"address":3259632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":120}],"party_address":3224828,"script_address":2034744},{"address":3259672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":24,"species":309},{"level":24,"species":66},{"level":24,"species":72}],"party_address":3224836,"script_address":2034931},{"address":3259712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":24,"species":338},{"level":24,"species":305},{"level":24,"species":338}],"party_address":3224860,"script_address":2034900},{"address":3259752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":227},{"level":25,"species":227}],"party_address":3224884,"script_address":2036338},{"address":3259792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":22,"species":183},{"level":22,"species":296}],"party_address":3224900,"script_address":2047037},{"address":3259832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":22,"species":27},{"level":22,"species":28}],"party_address":3224916,"script_address":2047068},{"address":3259872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":22,"species":304},{"level":22,"species":299}],"party_address":3224932,"script_address":2047099},{"address":3259912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":339},{"level":18,"species":218}],"party_address":3224948,"script_address":2049891},{"address":3259952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":306},{"level":18,"species":363}],"party_address":3224964,"script_address":2049922},{"address":3259992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":84},{"level":26,"species":85}],"party_address":3224980,"script_address":2053203},{"address":3260032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":302},{"level":26,"species":367}],"party_address":3224996,"script_address":2053234},{"address":3260072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":64},{"level":26,"species":393}],"party_address":3225012,"script_address":2053265},{"address":3260112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":356},{"level":26,"species":335}],"party_address":3225028,"script_address":2053296},{"address":3260152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":18,"species":356},{"level":18,"species":351}],"party_address":3225044,"script_address":2053327},{"address":3260192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":8,"species":74},{"level":8,"species":74}],"party_address":3225060,"script_address":2054738},{"address":3260232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":8,"species":306},{"level":8,"species":295}],"party_address":3225076,"script_address":2054769},{"address":3260272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":17,"species":84}],"party_address":3225092,"script_address":2057834},{"address":3260312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":17,"species":392}],"party_address":3225100,"script_address":2057865},{"address":3260352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":17,"species":356}],"party_address":3225108,"script_address":2057896},{"address":3260392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":363},{"level":33,"species":357}],"party_address":3225116,"script_address":2073825},{"address":3260432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":338}],"party_address":3225132,"script_address":2061636},{"address":3260472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":218},{"level":25,"species":339}],"party_address":3225140,"script_address":2061667},{"address":3260512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":118}],"party_address":3225156,"script_address":2061698},{"address":3260552,"data_type":"NO_ITEM_CUSTOM_MOVES","party":[{"level":30,"moves":[87,98,86,0],"species":338}],"party_address":3225164,"script_address":2065837},{"address":3260592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":356},{"level":28,"species":335}],"party_address":3225180,"script_address":2065868},{"address":3260632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":294},{"level":29,"species":292}],"party_address":3225196,"script_address":2067487},{"address":3260672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":335},{"level":25,"species":309},{"level":25,"species":369},{"level":25,"species":288},{"level":25,"species":337},{"level":25,"species":339}],"party_address":3225212,"script_address":2067518},{"address":3260712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":25,"species":286},{"level":25,"species":306},{"level":25,"species":337},{"level":25,"species":183},{"level":25,"species":27},{"level":25,"species":367}],"party_address":3225260,"script_address":2067549},{"address":3260752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":29,"species":371},{"level":29,"species":365}],"party_address":3225308,"script_address":2067611},{"address":3260792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":13,"species":295},{"level":15,"species":280}],"party_address":3225324,"script_address":1978255},{"address":3260832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":13,"species":321},{"level":15,"species":283}],"party_address":3225340,"script_address":1978286},{"address":3260872,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":32,"moves":[182,205,222,153],"species":76},{"level":35,"moves":[14,58,57,157],"species":140},{"level":35,"moves":[231,153,46,157],"species":95},{"level":37,"moves":[104,153,182,157],"species":320}],"party_address":3225356,"script_address":0},{"address":3260912,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":37,"moves":[182,58,157,57],"species":138},{"level":37,"moves":[182,205,222,153],"species":76},{"level":40,"moves":[14,58,57,157],"species":141},{"level":40,"moves":[231,153,46,157],"species":95},{"level":42,"moves":[104,153,182,157],"species":320}],"party_address":3225420,"script_address":0},{"address":3260952,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":42,"moves":[182,58,157,57],"species":139},{"level":42,"moves":[182,205,89,153],"species":76},{"level":45,"moves":[14,58,57,157],"species":141},{"level":45,"moves":[231,153,46,157],"species":95},{"level":47,"moves":[104,153,182,157],"species":320}],"party_address":3225500,"script_address":0},{"address":3260992,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":47,"moves":[157,63,48,182],"species":142},{"level":47,"moves":[8,205,89,153],"species":76},{"level":47,"moves":[182,58,157,57],"species":139},{"level":50,"moves":[14,58,57,157],"species":141},{"level":50,"moves":[231,153,46,157],"species":208},{"level":52,"moves":[104,153,182,157],"species":320}],"party_address":3225580,"script_address":0},{"address":3261032,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":33,"moves":[2,157,8,83],"species":68},{"level":33,"moves":[94,113,115,8],"species":356},{"level":35,"moves":[228,68,182,167],"species":237},{"level":37,"moves":[252,8,187,89],"species":336}],"party_address":3225676,"script_address":0},{"address":3261072,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":38,"moves":[2,157,8,83],"species":68},{"level":38,"moves":[94,113,115,8],"species":357},{"level":40,"moves":[228,68,182,167],"species":237},{"level":42,"moves":[252,8,187,89],"species":336}],"party_address":3225740,"script_address":0},{"address":3261112,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":40,"moves":[71,182,7,8],"species":107},{"level":43,"moves":[2,157,8,83],"species":68},{"level":43,"moves":[8,113,115,94],"species":357},{"level":45,"moves":[228,68,182,167],"species":237},{"level":47,"moves":[252,8,187,89],"species":336}],"party_address":3225804,"script_address":0},{"address":3261152,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":46,"moves":[25,8,89,83],"species":106},{"level":46,"moves":[71,182,7,8],"species":107},{"level":48,"moves":[238,157,8,83],"species":68},{"level":48,"moves":[8,113,115,94],"species":357},{"level":50,"moves":[228,68,182,167],"species":237},{"level":52,"moves":[252,8,187,89],"species":336}],"party_address":3225884,"script_address":0},{"address":3261192,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":36,"moves":[87,182,86,113],"species":179},{"level":36,"moves":[205,87,153,240],"species":101},{"level":38,"moves":[48,182,87,240],"species":82},{"level":40,"moves":[44,86,87,182],"species":338}],"party_address":3225980,"script_address":0},{"address":3261232,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":39,"moves":[87,21,240,95],"species":25},{"level":41,"moves":[87,182,86,113],"species":180},{"level":41,"moves":[205,87,153,240],"species":101},{"level":43,"moves":[48,182,87,240],"species":82},{"level":45,"moves":[44,86,87,182],"species":338}],"party_address":3226044,"script_address":0},{"address":3261272,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":44,"moves":[87,21,240,182],"species":26},{"level":46,"moves":[87,182,86,113],"species":181},{"level":46,"moves":[205,87,153,240],"species":101},{"level":48,"moves":[48,182,87,240],"species":82},{"level":50,"moves":[44,86,87,182],"species":338}],"party_address":3226124,"script_address":0},{"address":3261312,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":50,"moves":[129,8,9,113],"species":125},{"level":51,"moves":[87,21,240,182],"species":26},{"level":51,"moves":[87,182,86,113],"species":181},{"level":53,"moves":[205,87,153,240],"species":101},{"level":53,"moves":[48,182,87,240],"species":82},{"level":55,"moves":[44,86,87,182],"species":338}],"party_address":3226204,"script_address":0},{"address":3261352,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":38,"moves":[59,213,113,157],"species":219},{"level":36,"moves":[53,213,76,84],"species":77},{"level":38,"moves":[59,241,89,213],"species":340},{"level":40,"moves":[59,241,153,213],"species":321}],"party_address":3226300,"script_address":0},{"address":3261392,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":41,"moves":[14,53,46,241],"species":58},{"level":43,"moves":[59,213,113,157],"species":219},{"level":41,"moves":[53,213,76,84],"species":77},{"level":43,"moves":[59,241,89,213],"species":340},{"level":45,"moves":[59,241,153,213],"species":321}],"party_address":3226364,"script_address":0},{"address":3261432,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":46,"moves":[46,76,13,241],"species":228},{"level":46,"moves":[14,53,241,46],"species":58},{"level":48,"moves":[59,213,113,157],"species":219},{"level":46,"moves":[53,213,76,84],"species":78},{"level":48,"moves":[59,241,89,213],"species":340},{"level":50,"moves":[59,241,153,213],"species":321}],"party_address":3226444,"script_address":0},{"address":3261472,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":51,"moves":[14,53,241,46],"species":59},{"level":53,"moves":[59,213,113,157],"species":219},{"level":51,"moves":[46,76,13,241],"species":229},{"level":51,"moves":[53,213,76,84],"species":78},{"level":53,"moves":[59,241,89,213],"species":340},{"level":55,"moves":[59,241,153,213],"species":321}],"party_address":3226540,"script_address":0},{"address":3261512,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":42,"moves":[113,47,29,8],"species":113},{"level":42,"moves":[59,247,38,126],"species":366},{"level":43,"moves":[42,29,7,95],"species":308},{"level":45,"moves":[63,53,85,247],"species":366}],"party_address":3226636,"script_address":0},{"address":3261552,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":47,"moves":[59,247,38,126],"species":366},{"level":47,"moves":[113,47,29,8],"species":113},{"level":45,"moves":[252,146,203,179],"species":115},{"level":48,"moves":[42,29,7,95],"species":308},{"level":50,"moves":[63,53,85,247],"species":366}],"party_address":3226700,"script_address":0},{"address":3261592,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":52,"moves":[59,247,38,126],"species":366},{"level":52,"moves":[113,47,29,8],"species":242},{"level":50,"moves":[252,146,203,179],"species":115},{"level":53,"moves":[42,29,7,95],"species":308},{"level":55,"moves":[63,53,85,247],"species":366}],"party_address":3226780,"script_address":0},{"address":3261632,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":57,"moves":[59,247,38,126],"species":366},{"level":57,"moves":[182,47,29,8],"species":242},{"level":55,"moves":[252,146,203,179],"species":115},{"level":57,"moves":[36,182,126,89],"species":128},{"level":58,"moves":[42,29,7,95],"species":308},{"level":60,"moves":[63,53,85,247],"species":366}],"party_address":3226860,"script_address":0},{"address":3261672,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":40,"moves":[86,85,182,58],"species":147},{"level":38,"moves":[241,76,76,89],"species":369},{"level":41,"moves":[57,48,182,76],"species":310},{"level":43,"moves":[18,191,211,76],"species":227},{"level":45,"moves":[76,156,93,89],"species":359}],"party_address":3226956,"script_address":0},{"address":3261712,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":43,"moves":[95,94,115,138],"species":163},{"level":43,"moves":[241,76,76,89],"species":369},{"level":45,"moves":[86,85,182,58],"species":148},{"level":46,"moves":[57,48,182,76],"species":310},{"level":48,"moves":[18,191,211,76],"species":227},{"level":50,"moves":[76,156,93,89],"species":359}],"party_address":3227036,"script_address":0},{"address":3261752,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":48,"moves":[95,94,115,138],"species":164},{"level":49,"moves":[241,76,76,89],"species":369},{"level":50,"moves":[86,85,182,58],"species":148},{"level":51,"moves":[57,48,182,76],"species":310},{"level":53,"moves":[18,191,211,76],"species":227},{"level":55,"moves":[76,156,93,89],"species":359}],"party_address":3227132,"script_address":0},{"address":3261792,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":53,"moves":[95,94,115,138],"species":164},{"level":54,"moves":[241,76,76,89],"species":369},{"level":55,"moves":[57,48,182,76],"species":310},{"level":55,"moves":[63,85,89,58],"species":149},{"level":58,"moves":[18,191,211,76],"species":227},{"level":60,"moves":[143,156,93,89],"species":359}],"party_address":3227228,"script_address":0},{"address":3261832,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":48,"moves":[25,94,91,182],"species":79},{"level":49,"moves":[89,246,94,113],"species":319},{"level":49,"moves":[94,156,109,91],"species":178},{"level":50,"moves":[89,94,156,91],"species":348},{"level":50,"moves":[241,76,94,53],"species":349}],"party_address":3227324,"script_address":0},{"address":3261872,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":53,"moves":[95,138,29,182],"species":96},{"level":53,"moves":[25,94,91,182],"species":79},{"level":54,"moves":[89,153,94,113],"species":319},{"level":54,"moves":[94,156,109,91],"species":178},{"level":55,"moves":[89,94,156,91],"species":348},{"level":55,"moves":[241,76,94,53],"species":349}],"party_address":3227404,"script_address":0},{"address":3261912,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":58,"moves":[95,138,29,182],"species":97},{"level":59,"moves":[89,153,94,113],"species":319},{"level":58,"moves":[25,94,91,182],"species":79},{"level":59,"moves":[94,156,109,91],"species":178},{"level":60,"moves":[89,94,156,91],"species":348},{"level":60,"moves":[241,76,94,53],"species":349}],"party_address":3227500,"script_address":0},{"address":3261952,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":63,"moves":[95,138,29,182],"species":97},{"level":64,"moves":[89,153,94,113],"species":319},{"level":63,"moves":[25,94,91,182],"species":199},{"level":64,"moves":[94,156,109,91],"species":178},{"level":65,"moves":[89,94,156,91],"species":348},{"level":65,"moves":[241,76,94,53],"species":349}],"party_address":3227596,"script_address":0},{"address":3261992,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":46,"moves":[95,240,182,56],"species":60},{"level":46,"moves":[240,96,104,90],"species":324},{"level":48,"moves":[96,34,182,58],"species":343},{"level":48,"moves":[156,152,13,104],"species":327},{"level":51,"moves":[96,104,58,156],"species":230}],"party_address":3227692,"script_address":0},{"address":3262032,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":50,"moves":[95,240,182,56],"species":61},{"level":51,"moves":[240,96,104,90],"species":324},{"level":53,"moves":[96,34,182,58],"species":343},{"level":53,"moves":[156,12,13,104],"species":327},{"level":56,"moves":[96,104,58,156],"species":230}],"party_address":3227772,"script_address":0},{"address":3262072,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":56,"moves":[56,195,58,109],"species":131},{"level":58,"moves":[240,96,104,90],"species":324},{"level":56,"moves":[95,240,182,56],"species":61},{"level":58,"moves":[96,34,182,58],"species":343},{"level":58,"moves":[156,12,13,104],"species":327},{"level":61,"moves":[96,104,58,156],"species":230}],"party_address":3227852,"script_address":0},{"address":3262112,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":61,"moves":[56,195,58,109],"species":131},{"level":63,"moves":[240,96,104,90],"species":324},{"level":61,"moves":[95,240,56,195],"species":186},{"level":63,"moves":[96,34,182,73],"species":343},{"level":63,"moves":[156,12,13,104],"species":327},{"level":66,"moves":[96,104,58,156],"species":230}],"party_address":3227948,"script_address":0},{"address":3262152,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":17,"moves":[95,98,204,0],"species":387},{"level":17,"moves":[95,98,109,0],"species":386}],"party_address":3228044,"script_address":2167732},{"address":3262192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":369}],"party_address":3228076,"script_address":2202422},{"address":3262232,"data_type":"ITEM_CUSTOM_MOVES","party":[{"level":77,"moves":[92,76,191,211],"species":227},{"level":75,"moves":[115,113,246,89],"species":319},{"level":76,"moves":[87,89,76,81],"species":384},{"level":76,"moves":[202,246,19,109],"species":389},{"level":76,"moves":[96,246,76,163],"species":391},{"level":78,"moves":[89,94,53,247],"species":400}],"party_address":3228084,"script_address":2354502},{"address":3262272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":398}],"party_address":3228180,"script_address":0},{"address":3262312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":398}],"party_address":3228188,"script_address":0},{"address":3262352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":398}],"party_address":3228196,"script_address":0},{"address":3262392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":398}],"party_address":3228204,"script_address":0},{"address":3262432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":398}],"party_address":3228212,"script_address":0},{"address":3262472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":398}],"party_address":3228220,"script_address":0},{"address":3262512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":398}],"party_address":3228228,"script_address":0},{"address":3262552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":27},{"level":31,"species":27}],"party_address":3228236,"script_address":0},{"address":3262592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":320},{"level":33,"species":27},{"level":33,"species":27}],"party_address":3228252,"script_address":0},{"address":3262632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":320},{"level":35,"species":27},{"level":35,"species":27}],"party_address":3228276,"script_address":0},{"address":3262672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":37,"species":320},{"level":37,"species":28},{"level":37,"species":28}],"party_address":3228300,"script_address":0},{"address":3262712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":309},{"level":30,"species":66},{"level":30,"species":72}],"party_address":3228324,"script_address":0},{"address":3262752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":32,"species":310},{"level":32,"species":66},{"level":32,"species":72}],"party_address":3228348,"script_address":0},{"address":3262792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":310},{"level":34,"species":66},{"level":34,"species":73}],"party_address":3228372,"script_address":0},{"address":3262832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":310},{"level":36,"species":67},{"level":36,"species":73}],"party_address":3228396,"script_address":0},{"address":3262872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":37,"species":120},{"level":37,"species":120}],"party_address":3228420,"script_address":0},{"address":3262912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":39,"species":309},{"level":39,"species":120},{"level":39,"species":120}],"party_address":3228436,"script_address":0},{"address":3262952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":41,"species":310},{"level":41,"species":120},{"level":41,"species":120}],"party_address":3228460,"script_address":0},{"address":3262992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":43,"species":310},{"level":43,"species":121},{"level":43,"species":121}],"party_address":3228484,"script_address":0},{"address":3263032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":37,"species":67},{"level":37,"species":67}],"party_address":3228508,"script_address":0},{"address":3263072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":39,"species":335},{"level":39,"species":67},{"level":39,"species":67}],"party_address":3228524,"script_address":0},{"address":3263112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":41,"species":336},{"level":41,"species":67},{"level":41,"species":67}],"party_address":3228548,"script_address":0},{"address":3263152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":43,"species":336},{"level":43,"species":68},{"level":43,"species":68}],"party_address":3228572,"script_address":0},{"address":3263192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":371},{"level":35,"species":365}],"party_address":3228596,"script_address":0},{"address":3263232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":37,"species":308},{"level":37,"species":371},{"level":37,"species":365}],"party_address":3228612,"script_address":0},{"address":3263272,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":39,"species":308},{"level":39,"species":371},{"level":39,"species":365}],"party_address":3228636,"script_address":0},{"address":3263312,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":41,"species":308},{"level":41,"species":372},{"level":41,"species":366}],"party_address":3228660,"script_address":0},{"address":3263352,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":337},{"level":35,"species":337},{"level":35,"species":371}],"party_address":3228684,"script_address":0},{"address":3263392,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":37,"species":337},{"level":37,"species":338},{"level":37,"species":371}],"party_address":3228708,"script_address":0},{"address":3263432,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":39,"species":338},{"level":39,"species":338},{"level":39,"species":371}],"party_address":3228732,"script_address":0},{"address":3263472,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":41,"species":338},{"level":41,"species":338},{"level":41,"species":372}],"party_address":3228756,"script_address":0},{"address":3263512,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":26,"species":74},{"level":26,"species":339}],"party_address":3228780,"script_address":0},{"address":3263552,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":28,"species":66},{"level":28,"species":339},{"level":28,"species":75}],"party_address":3228796,"script_address":0},{"address":3263592,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":30,"species":66},{"level":30,"species":339},{"level":30,"species":75}],"party_address":3228820,"script_address":0},{"address":3263632,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":67},{"level":33,"species":340},{"level":33,"species":76}],"party_address":3228844,"script_address":0},{"address":3263672,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":31,"species":315},{"level":31,"species":287},{"level":31,"species":288},{"level":31,"species":295},{"level":31,"species":298},{"level":31,"species":304}],"party_address":3228868,"script_address":0},{"address":3263712,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":33,"species":315},{"level":33,"species":287},{"level":33,"species":289},{"level":33,"species":296},{"level":33,"species":299},{"level":33,"species":304}],"party_address":3228916,"script_address":0},{"address":3263752,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":35,"species":316},{"level":35,"species":287},{"level":35,"species":289},{"level":35,"species":296},{"level":35,"species":299},{"level":35,"species":305}],"party_address":3228964,"script_address":0},{"address":3263792,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":37,"species":316},{"level":37,"species":287},{"level":37,"species":289},{"level":37,"species":297},{"level":37,"species":300},{"level":37,"species":305}],"party_address":3229012,"script_address":0},{"address":3263832,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":34,"species":313},{"level":34,"species":116}],"party_address":3229060,"script_address":0},{"address":3263872,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":36,"species":325},{"level":36,"species":313},{"level":36,"species":117}],"party_address":3229076,"script_address":0},{"address":3263912,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":38,"species":325},{"level":38,"species":313},{"level":38,"species":117}],"party_address":3229100,"script_address":0},{"address":3263952,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":40,"species":325},{"level":40,"species":314},{"level":40,"species":230}],"party_address":3229124,"script_address":0},{"address":3263992,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":41,"species":411}],"party_address":3229148,"script_address":2564791},{"address":3264032,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":41,"species":378},{"level":41,"species":64}],"party_address":3229156,"script_address":2564822},{"address":3264072,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":41,"species":202}],"party_address":3229172,"script_address":0},{"address":3264112,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":4}],"party_address":3229180,"script_address":0},{"address":3264152,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":1}],"party_address":3229188,"script_address":0},{"address":3264192,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":405}],"party_address":3229196,"script_address":0},{"address":3264232,"data_type":"NO_ITEM_DEFAULT_MOVES","party":[{"level":5,"species":404}],"party_address":3229204,"script_address":0}],"warps":{"MAP_ABANDONED_SHIP_CAPTAINS_OFFICE:0,1/MAP_ABANDONED_SHIP_DECK:4":"MAP_ABANDONED_SHIP_DECK:4/MAP_ABANDONED_SHIP_CAPTAINS_OFFICE:0","MAP_ABANDONED_SHIP_CORRIDORS_1F:0,1/MAP_ABANDONED_SHIP_DECK:2":"MAP_ABANDONED_SHIP_DECK:2/MAP_ABANDONED_SHIP_CORRIDORS_1F:1","MAP_ABANDONED_SHIP_CORRIDORS_1F:10/MAP_ABANDONED_SHIP_CORRIDORS_B1F:6":"MAP_ABANDONED_SHIP_CORRIDORS_B1F:6/MAP_ABANDONED_SHIP_CORRIDORS_1F:10","MAP_ABANDONED_SHIP_CORRIDORS_1F:11/MAP_ABANDONED_SHIP_ROOMS2_1F:2":"MAP_ABANDONED_SHIP_ROOMS2_1F:2/MAP_ABANDONED_SHIP_CORRIDORS_1F:11","MAP_ABANDONED_SHIP_CORRIDORS_1F:2,3/MAP_ABANDONED_SHIP_DECK:3":"MAP_ABANDONED_SHIP_DECK:3/MAP_ABANDONED_SHIP_CORRIDORS_1F:2","MAP_ABANDONED_SHIP_CORRIDORS_1F:4/MAP_ABANDONED_SHIP_ROOMS_1F:0":"MAP_ABANDONED_SHIP_ROOMS_1F:0,1/MAP_ABANDONED_SHIP_CORRIDORS_1F:4","MAP_ABANDONED_SHIP_CORRIDORS_1F:5/MAP_ABANDONED_SHIP_ROOMS_1F:3":"MAP_ABANDONED_SHIP_ROOMS_1F:3,5/MAP_ABANDONED_SHIP_CORRIDORS_1F:5","MAP_ABANDONED_SHIP_CORRIDORS_1F:6/MAP_ABANDONED_SHIP_ROOMS_1F:2":"MAP_ABANDONED_SHIP_ROOMS_1F:2/MAP_ABANDONED_SHIP_CORRIDORS_1F:6","MAP_ABANDONED_SHIP_CORRIDORS_1F:7/MAP_ABANDONED_SHIP_ROOMS_1F:4":"MAP_ABANDONED_SHIP_ROOMS_1F:4/MAP_ABANDONED_SHIP_CORRIDORS_1F:7","MAP_ABANDONED_SHIP_CORRIDORS_1F:8/MAP_ABANDONED_SHIP_ROOMS2_1F:0":"MAP_ABANDONED_SHIP_ROOMS2_1F:0,1/MAP_ABANDONED_SHIP_CORRIDORS_1F:8","MAP_ABANDONED_SHIP_CORRIDORS_1F:9/MAP_ABANDONED_SHIP_CORRIDORS_B1F:7":"MAP_ABANDONED_SHIP_CORRIDORS_B1F:7/MAP_ABANDONED_SHIP_CORRIDORS_1F:9","MAP_ABANDONED_SHIP_CORRIDORS_B1F:0/MAP_ABANDONED_SHIP_ROOMS2_B1F:2":"MAP_ABANDONED_SHIP_ROOMS2_B1F:2,3/MAP_ABANDONED_SHIP_CORRIDORS_B1F:0","MAP_ABANDONED_SHIP_CORRIDORS_B1F:1/MAP_ABANDONED_SHIP_ROOMS2_B1F:0":"MAP_ABANDONED_SHIP_ROOMS2_B1F:0,1/MAP_ABANDONED_SHIP_CORRIDORS_B1F:1","MAP_ABANDONED_SHIP_CORRIDORS_B1F:2/MAP_ABANDONED_SHIP_ROOMS_B1F:0":"MAP_ABANDONED_SHIP_ROOMS_B1F:0/MAP_ABANDONED_SHIP_CORRIDORS_B1F:2","MAP_ABANDONED_SHIP_CORRIDORS_B1F:3/MAP_ABANDONED_SHIP_ROOMS_B1F:1":"MAP_ABANDONED_SHIP_ROOMS_B1F:1/MAP_ABANDONED_SHIP_CORRIDORS_B1F:3","MAP_ABANDONED_SHIP_CORRIDORS_B1F:4/MAP_ABANDONED_SHIP_ROOMS_B1F:2":"MAP_ABANDONED_SHIP_ROOMS_B1F:2/MAP_ABANDONED_SHIP_CORRIDORS_B1F:4","MAP_ABANDONED_SHIP_CORRIDORS_B1F:5/MAP_ABANDONED_SHIP_ROOM_B1F:0":"MAP_ABANDONED_SHIP_ROOM_B1F:0,1/MAP_ABANDONED_SHIP_CORRIDORS_B1F:5","MAP_ABANDONED_SHIP_CORRIDORS_B1F:6/MAP_ABANDONED_SHIP_CORRIDORS_1F:10":"MAP_ABANDONED_SHIP_CORRIDORS_1F:10/MAP_ABANDONED_SHIP_CORRIDORS_B1F:6","MAP_ABANDONED_SHIP_CORRIDORS_B1F:7/MAP_ABANDONED_SHIP_CORRIDORS_1F:9":"MAP_ABANDONED_SHIP_CORRIDORS_1F:9/MAP_ABANDONED_SHIP_CORRIDORS_B1F:7","MAP_ABANDONED_SHIP_DECK:0,1/MAP_ROUTE108:0":"MAP_ROUTE108:0/MAP_ABANDONED_SHIP_DECK:0","MAP_ABANDONED_SHIP_DECK:2/MAP_ABANDONED_SHIP_CORRIDORS_1F:1":"MAP_ABANDONED_SHIP_CORRIDORS_1F:0,1/MAP_ABANDONED_SHIP_DECK:2","MAP_ABANDONED_SHIP_DECK:3/MAP_ABANDONED_SHIP_CORRIDORS_1F:2":"MAP_ABANDONED_SHIP_CORRIDORS_1F:2,3/MAP_ABANDONED_SHIP_DECK:3","MAP_ABANDONED_SHIP_DECK:4/MAP_ABANDONED_SHIP_CAPTAINS_OFFICE:0":"MAP_ABANDONED_SHIP_CAPTAINS_OFFICE:0,1/MAP_ABANDONED_SHIP_DECK:4","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:0/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:0":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:0,1/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:0","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:1/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:2":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:2,3/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:1","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:2/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:4":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:4,5/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:2","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:3/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:6":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:6/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:3","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:4/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:7":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:7/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:4","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:5/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:8":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:8/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:5","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:0,1/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:0":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:0/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:0","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:2,3/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:1":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:1/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:2","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:4,5/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:2":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:2/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:4","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:6/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:3":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:3/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:6","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:7/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:4":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:4/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:7","MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:8/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:5":"MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:5/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:8","MAP_ABANDONED_SHIP_ROOMS2_1F:0,1/MAP_ABANDONED_SHIP_CORRIDORS_1F:8":"MAP_ABANDONED_SHIP_CORRIDORS_1F:8/MAP_ABANDONED_SHIP_ROOMS2_1F:0","MAP_ABANDONED_SHIP_ROOMS2_1F:2/MAP_ABANDONED_SHIP_CORRIDORS_1F:11":"MAP_ABANDONED_SHIP_CORRIDORS_1F:11/MAP_ABANDONED_SHIP_ROOMS2_1F:2","MAP_ABANDONED_SHIP_ROOMS2_B1F:0,1/MAP_ABANDONED_SHIP_CORRIDORS_B1F:1":"MAP_ABANDONED_SHIP_CORRIDORS_B1F:1/MAP_ABANDONED_SHIP_ROOMS2_B1F:0","MAP_ABANDONED_SHIP_ROOMS2_B1F:2,3/MAP_ABANDONED_SHIP_CORRIDORS_B1F:0":"MAP_ABANDONED_SHIP_CORRIDORS_B1F:0/MAP_ABANDONED_SHIP_ROOMS2_B1F:2","MAP_ABANDONED_SHIP_ROOMS_1F:0,1/MAP_ABANDONED_SHIP_CORRIDORS_1F:4":"MAP_ABANDONED_SHIP_CORRIDORS_1F:4/MAP_ABANDONED_SHIP_ROOMS_1F:0","MAP_ABANDONED_SHIP_ROOMS_1F:2/MAP_ABANDONED_SHIP_CORRIDORS_1F:6":"MAP_ABANDONED_SHIP_CORRIDORS_1F:6/MAP_ABANDONED_SHIP_ROOMS_1F:2","MAP_ABANDONED_SHIP_ROOMS_1F:3,5/MAP_ABANDONED_SHIP_CORRIDORS_1F:5":"MAP_ABANDONED_SHIP_CORRIDORS_1F:5/MAP_ABANDONED_SHIP_ROOMS_1F:3","MAP_ABANDONED_SHIP_ROOMS_1F:4/MAP_ABANDONED_SHIP_CORRIDORS_1F:7":"MAP_ABANDONED_SHIP_CORRIDORS_1F:7/MAP_ABANDONED_SHIP_ROOMS_1F:4","MAP_ABANDONED_SHIP_ROOMS_B1F:0/MAP_ABANDONED_SHIP_CORRIDORS_B1F:2":"MAP_ABANDONED_SHIP_CORRIDORS_B1F:2/MAP_ABANDONED_SHIP_ROOMS_B1F:0","MAP_ABANDONED_SHIP_ROOMS_B1F:1/MAP_ABANDONED_SHIP_CORRIDORS_B1F:3":"MAP_ABANDONED_SHIP_CORRIDORS_B1F:3/MAP_ABANDONED_SHIP_ROOMS_B1F:1","MAP_ABANDONED_SHIP_ROOMS_B1F:2/MAP_ABANDONED_SHIP_CORRIDORS_B1F:4":"MAP_ABANDONED_SHIP_CORRIDORS_B1F:4/MAP_ABANDONED_SHIP_ROOMS_B1F:2","MAP_ABANDONED_SHIP_ROOM_B1F:0,1/MAP_ABANDONED_SHIP_CORRIDORS_B1F:5":"MAP_ABANDONED_SHIP_CORRIDORS_B1F:5/MAP_ABANDONED_SHIP_ROOM_B1F:0","MAP_ABANDONED_SHIP_UNDERWATER1:0,1/MAP_ABANDONED_SHIP_UNDERWATER2:0":"MAP_ABANDONED_SHIP_UNDERWATER2:0/MAP_ABANDONED_SHIP_UNDERWATER1:0","MAP_ABANDONED_SHIP_UNDERWATER2:0/MAP_ABANDONED_SHIP_UNDERWATER1:0":"MAP_ABANDONED_SHIP_UNDERWATER1:0,1/MAP_ABANDONED_SHIP_UNDERWATER2:0","MAP_ALTERING_CAVE:0/MAP_ROUTE103:0":"MAP_ROUTE103:0/MAP_ALTERING_CAVE:0","MAP_ANCIENT_TOMB:0/MAP_ROUTE120:0":"MAP_ROUTE120:0/MAP_ANCIENT_TOMB:0","MAP_ANCIENT_TOMB:1/MAP_ANCIENT_TOMB:2":"MAP_ANCIENT_TOMB:2/MAP_ANCIENT_TOMB:1","MAP_ANCIENT_TOMB:2/MAP_ANCIENT_TOMB:1":"MAP_ANCIENT_TOMB:1/MAP_ANCIENT_TOMB:2","MAP_AQUA_HIDEOUT_1F:0,1/MAP_LILYCOVE_CITY:6":"MAP_LILYCOVE_CITY:6/MAP_AQUA_HIDEOUT_1F:0","MAP_AQUA_HIDEOUT_1F:2/MAP_AQUA_HIDEOUT_B1F:0":"MAP_AQUA_HIDEOUT_B1F:0/MAP_AQUA_HIDEOUT_1F:2","MAP_AQUA_HIDEOUT_B1F:0/MAP_AQUA_HIDEOUT_1F:2":"MAP_AQUA_HIDEOUT_1F:2/MAP_AQUA_HIDEOUT_B1F:0","MAP_AQUA_HIDEOUT_B1F:1/MAP_AQUA_HIDEOUT_B2F:0":"MAP_AQUA_HIDEOUT_B2F:0/MAP_AQUA_HIDEOUT_B1F:1","MAP_AQUA_HIDEOUT_B1F:10/MAP_AQUA_HIDEOUT_B1F:6":"MAP_AQUA_HIDEOUT_B1F:6/MAP_AQUA_HIDEOUT_B1F:10","MAP_AQUA_HIDEOUT_B1F:11/MAP_AQUA_HIDEOUT_B1F:22":"MAP_AQUA_HIDEOUT_B1F:22/MAP_AQUA_HIDEOUT_B1F:11","MAP_AQUA_HIDEOUT_B1F:12/MAP_AQUA_HIDEOUT_B1F:9":"MAP_AQUA_HIDEOUT_B1F:9/MAP_AQUA_HIDEOUT_B1F:12","MAP_AQUA_HIDEOUT_B1F:13/MAP_AQUA_HIDEOUT_B1F:18":"MAP_AQUA_HIDEOUT_B1F:18/MAP_AQUA_HIDEOUT_B1F:13","MAP_AQUA_HIDEOUT_B1F:14/MAP_AQUA_HIDEOUT_B1F:12!":"MAP_AQUA_HIDEOUT_B1F:12/MAP_AQUA_HIDEOUT_B1F:9","MAP_AQUA_HIDEOUT_B1F:15/MAP_AQUA_HIDEOUT_B1F:16":"MAP_AQUA_HIDEOUT_B1F:16/MAP_AQUA_HIDEOUT_B1F:15","MAP_AQUA_HIDEOUT_B1F:16/MAP_AQUA_HIDEOUT_B1F:15":"MAP_AQUA_HIDEOUT_B1F:15/MAP_AQUA_HIDEOUT_B1F:16","MAP_AQUA_HIDEOUT_B1F:17/MAP_AQUA_HIDEOUT_B1F:20":"MAP_AQUA_HIDEOUT_B1F:20/MAP_AQUA_HIDEOUT_B1F:17","MAP_AQUA_HIDEOUT_B1F:18/MAP_AQUA_HIDEOUT_B1F:13":"MAP_AQUA_HIDEOUT_B1F:13/MAP_AQUA_HIDEOUT_B1F:18","MAP_AQUA_HIDEOUT_B1F:19/MAP_AQUA_HIDEOUT_B1F:24":"MAP_AQUA_HIDEOUT_B1F:24/MAP_AQUA_HIDEOUT_B1F:19","MAP_AQUA_HIDEOUT_B1F:2/MAP_AQUA_HIDEOUT_B2F:1":"MAP_AQUA_HIDEOUT_B2F:1/MAP_AQUA_HIDEOUT_B1F:2","MAP_AQUA_HIDEOUT_B1F:20/MAP_AQUA_HIDEOUT_B1F:17":"MAP_AQUA_HIDEOUT_B1F:17/MAP_AQUA_HIDEOUT_B1F:20","MAP_AQUA_HIDEOUT_B1F:21/MAP_AQUA_HIDEOUT_B1F:12!":"MAP_AQUA_HIDEOUT_B1F:12/MAP_AQUA_HIDEOUT_B1F:9","MAP_AQUA_HIDEOUT_B1F:22/MAP_AQUA_HIDEOUT_B1F:11":"MAP_AQUA_HIDEOUT_B1F:11/MAP_AQUA_HIDEOUT_B1F:22","MAP_AQUA_HIDEOUT_B1F:23/MAP_AQUA_HIDEOUT_B1F:17!":"MAP_AQUA_HIDEOUT_B1F:17/MAP_AQUA_HIDEOUT_B1F:20","MAP_AQUA_HIDEOUT_B1F:24/MAP_AQUA_HIDEOUT_B1F:19":"MAP_AQUA_HIDEOUT_B1F:19/MAP_AQUA_HIDEOUT_B1F:24","MAP_AQUA_HIDEOUT_B1F:3/MAP_AQUA_HIDEOUT_B2F:2":"MAP_AQUA_HIDEOUT_B2F:2/MAP_AQUA_HIDEOUT_B1F:3","MAP_AQUA_HIDEOUT_B1F:4/MAP_AQUA_HIDEOUT_B1F:7":"MAP_AQUA_HIDEOUT_B1F:7/MAP_AQUA_HIDEOUT_B1F:4","MAP_AQUA_HIDEOUT_B1F:5/MAP_AQUA_HIDEOUT_B1F:8":"MAP_AQUA_HIDEOUT_B1F:8/MAP_AQUA_HIDEOUT_B1F:5","MAP_AQUA_HIDEOUT_B1F:6/MAP_AQUA_HIDEOUT_B1F:10":"MAP_AQUA_HIDEOUT_B1F:10/MAP_AQUA_HIDEOUT_B1F:6","MAP_AQUA_HIDEOUT_B1F:7/MAP_AQUA_HIDEOUT_B1F:4":"MAP_AQUA_HIDEOUT_B1F:4/MAP_AQUA_HIDEOUT_B1F:7","MAP_AQUA_HIDEOUT_B1F:8/MAP_AQUA_HIDEOUT_B1F:5":"MAP_AQUA_HIDEOUT_B1F:5/MAP_AQUA_HIDEOUT_B1F:8","MAP_AQUA_HIDEOUT_B1F:9/MAP_AQUA_HIDEOUT_B1F:12":"MAP_AQUA_HIDEOUT_B1F:12/MAP_AQUA_HIDEOUT_B1F:9","MAP_AQUA_HIDEOUT_B2F:0/MAP_AQUA_HIDEOUT_B1F:1":"MAP_AQUA_HIDEOUT_B1F:1/MAP_AQUA_HIDEOUT_B2F:0","MAP_AQUA_HIDEOUT_B2F:1/MAP_AQUA_HIDEOUT_B1F:2":"MAP_AQUA_HIDEOUT_B1F:2/MAP_AQUA_HIDEOUT_B2F:1","MAP_AQUA_HIDEOUT_B2F:2/MAP_AQUA_HIDEOUT_B1F:3":"MAP_AQUA_HIDEOUT_B1F:3/MAP_AQUA_HIDEOUT_B2F:2","MAP_AQUA_HIDEOUT_B2F:3/MAP_AQUA_HIDEOUT_B2F:5":"MAP_AQUA_HIDEOUT_B2F:5/MAP_AQUA_HIDEOUT_B2F:3","MAP_AQUA_HIDEOUT_B2F:4/MAP_AQUA_HIDEOUT_B2F:8":"MAP_AQUA_HIDEOUT_B2F:8/MAP_AQUA_HIDEOUT_B2F:4","MAP_AQUA_HIDEOUT_B2F:5/MAP_AQUA_HIDEOUT_B2F:3":"MAP_AQUA_HIDEOUT_B2F:3/MAP_AQUA_HIDEOUT_B2F:5","MAP_AQUA_HIDEOUT_B2F:6/MAP_AQUA_HIDEOUT_B2F:7":"MAP_AQUA_HIDEOUT_B2F:7/MAP_AQUA_HIDEOUT_B2F:6","MAP_AQUA_HIDEOUT_B2F:7/MAP_AQUA_HIDEOUT_B2F:6":"MAP_AQUA_HIDEOUT_B2F:6/MAP_AQUA_HIDEOUT_B2F:7","MAP_AQUA_HIDEOUT_B2F:8/MAP_AQUA_HIDEOUT_B2F:4":"MAP_AQUA_HIDEOUT_B2F:4/MAP_AQUA_HIDEOUT_B2F:8","MAP_AQUA_HIDEOUT_B2F:9/MAP_AQUA_HIDEOUT_B1F:4!":"MAP_AQUA_HIDEOUT_B1F:4/MAP_AQUA_HIDEOUT_B1F:7","MAP_ARTISAN_CAVE_1F:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:13":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:13/MAP_ARTISAN_CAVE_1F:0","MAP_ARTISAN_CAVE_1F:1/MAP_ARTISAN_CAVE_B1F:1":"MAP_ARTISAN_CAVE_B1F:1/MAP_ARTISAN_CAVE_1F:1","MAP_ARTISAN_CAVE_B1F:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:10":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:10/MAP_ARTISAN_CAVE_B1F:0","MAP_ARTISAN_CAVE_B1F:1/MAP_ARTISAN_CAVE_1F:1":"MAP_ARTISAN_CAVE_1F:1/MAP_ARTISAN_CAVE_B1F:1","MAP_BATTLE_COLOSSEUM_2P:0,1/MAP_DYNAMIC:-1!":"","MAP_BATTLE_COLOSSEUM_4P:0,1,2,3/MAP_DYNAMIC:-1!":"","MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:1":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:1/MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_DOME_CORRIDOR:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1!":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1/MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1/MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_DOME_PRE_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1!":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1/MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:2":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:2/MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2":"MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0","MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:2":"MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:0","MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0":"MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2","MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:3/MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0!":"MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2","MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:2":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:0":"MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:2","MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY:0,1,2/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:0":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:0/MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:3":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:3/MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:2":"MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:2/MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM:0","MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:0":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:0/MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:0","MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:2/MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM:0":"MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:2","MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER:0,1,2/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:6":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:6/MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER:0","MAP_BATTLE_FRONTIER_LOUNGE1:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:5":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:5/MAP_BATTLE_FRONTIER_LOUNGE1:0","MAP_BATTLE_FRONTIER_LOUNGE2:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:3":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:3/MAP_BATTLE_FRONTIER_LOUNGE2:0","MAP_BATTLE_FRONTIER_LOUNGE3:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:9":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:9/MAP_BATTLE_FRONTIER_LOUNGE3:0","MAP_BATTLE_FRONTIER_LOUNGE4:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:6":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:6/MAP_BATTLE_FRONTIER_LOUNGE4:0","MAP_BATTLE_FRONTIER_LOUNGE5:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:7":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:7/MAP_BATTLE_FRONTIER_LOUNGE5:0","MAP_BATTLE_FRONTIER_LOUNGE6:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:8":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:8/MAP_BATTLE_FRONTIER_LOUNGE6:0","MAP_BATTLE_FRONTIER_LOUNGE7:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:7":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:7/MAP_BATTLE_FRONTIER_LOUNGE7:0","MAP_BATTLE_FRONTIER_LOUNGE8:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:10":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:10/MAP_BATTLE_FRONTIER_LOUNGE8:0","MAP_BATTLE_FRONTIER_LOUNGE9:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:11":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:11/MAP_BATTLE_FRONTIER_LOUNGE9:0","MAP_BATTLE_FRONTIER_MART:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:4":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:4/MAP_BATTLE_FRONTIER_MART:0","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:0/MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:0":"MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:0","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:1/MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY:0":"MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:1","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:10/MAP_BATTLE_FRONTIER_LOUNGE8:0":"MAP_BATTLE_FRONTIER_LOUNGE8:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:10","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:11/MAP_BATTLE_FRONTIER_LOUNGE9:0":"MAP_BATTLE_FRONTIER_LOUNGE9:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:11","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:12/MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:0":"MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:12","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:13/MAP_ARTISAN_CAVE_1F:0":"MAP_ARTISAN_CAVE_1F:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:13","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:0":"MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:2","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:3/MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY:0":"MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:3","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:4/MAP_BATTLE_FRONTIER_RANKING_HALL:0":"MAP_BATTLE_FRONTIER_RANKING_HALL:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:4","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:5/MAP_BATTLE_FRONTIER_LOUNGE1:0":"MAP_BATTLE_FRONTIER_LOUNGE1:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:5","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:6/MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER:0":"MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER:0,1,2/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:6","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:7/MAP_BATTLE_FRONTIER_LOUNGE5:0":"MAP_BATTLE_FRONTIER_LOUNGE5:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:7","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:8/MAP_BATTLE_FRONTIER_LOUNGE6:0":"MAP_BATTLE_FRONTIER_LOUNGE6:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:8","MAP_BATTLE_FRONTIER_OUTSIDE_EAST:9/MAP_BATTLE_FRONTIER_LOUNGE3:0":"MAP_BATTLE_FRONTIER_LOUNGE3:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:9","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:0/MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY:0":"MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY:0,1,2/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:0","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1/MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0":"MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:10/MAP_ARTISAN_CAVE_B1F:0":"MAP_ARTISAN_CAVE_B1F:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:10","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:2/MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY:0":"MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:2","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:3/MAP_BATTLE_FRONTIER_LOUNGE2:0":"MAP_BATTLE_FRONTIER_LOUNGE2:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:3","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:4/MAP_BATTLE_FRONTIER_MART:0":"MAP_BATTLE_FRONTIER_MART:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:4","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:5/MAP_BATTLE_FRONTIER_SCOTTS_HOUSE:0":"MAP_BATTLE_FRONTIER_SCOTTS_HOUSE:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:5","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:6/MAP_BATTLE_FRONTIER_LOUNGE4:0":"MAP_BATTLE_FRONTIER_LOUNGE4:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:6","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:7/MAP_BATTLE_FRONTIER_LOUNGE7:0":"MAP_BATTLE_FRONTIER_LOUNGE7:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:7","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:8/MAP_BATTLE_FRONTIER_RECEPTION_GATE:0":"MAP_BATTLE_FRONTIER_RECEPTION_GATE:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:8","MAP_BATTLE_FRONTIER_OUTSIDE_WEST:9/MAP_BATTLE_FRONTIER_RECEPTION_GATE:1":"MAP_BATTLE_FRONTIER_RECEPTION_GATE:1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:9","MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:12":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:12/MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:0","MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:2/MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:0":"MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:0/MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:2","MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:0/MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:2":"MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:2/MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:0","MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_BATTLE_FRONTIER_RANKING_HALL:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:4":"MAP_BATTLE_FRONTIER_OUTSIDE_EAST:4/MAP_BATTLE_FRONTIER_RANKING_HALL:0","MAP_BATTLE_FRONTIER_RECEPTION_GATE:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:8":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:8/MAP_BATTLE_FRONTIER_RECEPTION_GATE:0","MAP_BATTLE_FRONTIER_RECEPTION_GATE:1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:9":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:9/MAP_BATTLE_FRONTIER_RECEPTION_GATE:1","MAP_BATTLE_FRONTIER_SCOTTS_HOUSE:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:5":"MAP_BATTLE_FRONTIER_OUTSIDE_WEST:5/MAP_BATTLE_FRONTIER_SCOTTS_HOUSE:0","MAP_BIRTH_ISLAND_EXTERIOR:0/MAP_BIRTH_ISLAND_HARBOR:0":"MAP_BIRTH_ISLAND_HARBOR:0/MAP_BIRTH_ISLAND_EXTERIOR:0","MAP_BIRTH_ISLAND_HARBOR:0/MAP_BIRTH_ISLAND_EXTERIOR:0":"MAP_BIRTH_ISLAND_EXTERIOR:0/MAP_BIRTH_ISLAND_HARBOR:0","MAP_CAVE_OF_ORIGIN_1F:0/MAP_CAVE_OF_ORIGIN_ENTRANCE:1":"MAP_CAVE_OF_ORIGIN_ENTRANCE:1/MAP_CAVE_OF_ORIGIN_1F:0","MAP_CAVE_OF_ORIGIN_1F:1/MAP_CAVE_OF_ORIGIN_B1F:0":"MAP_CAVE_OF_ORIGIN_B1F:0/MAP_CAVE_OF_ORIGIN_1F:1","MAP_CAVE_OF_ORIGIN_B1F:0/MAP_CAVE_OF_ORIGIN_1F:1":"MAP_CAVE_OF_ORIGIN_1F:1/MAP_CAVE_OF_ORIGIN_B1F:0","MAP_CAVE_OF_ORIGIN_ENTRANCE:0/MAP_SOOTOPOLIS_CITY:3":"MAP_SOOTOPOLIS_CITY:3/MAP_CAVE_OF_ORIGIN_ENTRANCE:0","MAP_CAVE_OF_ORIGIN_ENTRANCE:1/MAP_CAVE_OF_ORIGIN_1F:0":"MAP_CAVE_OF_ORIGIN_1F:0/MAP_CAVE_OF_ORIGIN_ENTRANCE:1","MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1:0/MAP_CAVE_OF_ORIGIN_1F:1!":"MAP_CAVE_OF_ORIGIN_1F:1/MAP_CAVE_OF_ORIGIN_B1F:0","MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1:1/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:0":"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:0/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1:1","MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:0/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1:1":"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1:1/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:0","MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:1/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:0":"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:0/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:1","MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:0/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:1":"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:1/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:0","MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:1/MAP_CAVE_OF_ORIGIN_B1F:0!":"MAP_CAVE_OF_ORIGIN_B1F:0/MAP_CAVE_OF_ORIGIN_1F:1","MAP_DESERT_RUINS:0/MAP_ROUTE111:1":"MAP_ROUTE111:1/MAP_DESERT_RUINS:0","MAP_DESERT_RUINS:1/MAP_DESERT_RUINS:2":"MAP_DESERT_RUINS:2/MAP_DESERT_RUINS:1","MAP_DESERT_RUINS:2/MAP_DESERT_RUINS:1":"MAP_DESERT_RUINS:1/MAP_DESERT_RUINS:2","MAP_DESERT_UNDERPASS:0/MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:2":"MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:2/MAP_DESERT_UNDERPASS:0","MAP_DEWFORD_TOWN:0/MAP_DEWFORD_TOWN_HALL:0":"MAP_DEWFORD_TOWN_HALL:0,1/MAP_DEWFORD_TOWN:0","MAP_DEWFORD_TOWN:1/MAP_DEWFORD_TOWN_POKEMON_CENTER_1F:0":"MAP_DEWFORD_TOWN_POKEMON_CENTER_1F:0,1/MAP_DEWFORD_TOWN:1","MAP_DEWFORD_TOWN:2/MAP_DEWFORD_TOWN_GYM:0":"MAP_DEWFORD_TOWN_GYM:0,1/MAP_DEWFORD_TOWN:2","MAP_DEWFORD_TOWN:3/MAP_DEWFORD_TOWN_HOUSE1:0":"MAP_DEWFORD_TOWN_HOUSE1:0,1/MAP_DEWFORD_TOWN:3","MAP_DEWFORD_TOWN:4/MAP_DEWFORD_TOWN_HOUSE2:0":"MAP_DEWFORD_TOWN_HOUSE2:0,1/MAP_DEWFORD_TOWN:4","MAP_DEWFORD_TOWN_GYM:0,1/MAP_DEWFORD_TOWN:2":"MAP_DEWFORD_TOWN:2/MAP_DEWFORD_TOWN_GYM:0","MAP_DEWFORD_TOWN_HALL:0,1/MAP_DEWFORD_TOWN:0":"MAP_DEWFORD_TOWN:0/MAP_DEWFORD_TOWN_HALL:0","MAP_DEWFORD_TOWN_HOUSE1:0,1/MAP_DEWFORD_TOWN:3":"MAP_DEWFORD_TOWN:3/MAP_DEWFORD_TOWN_HOUSE1:0","MAP_DEWFORD_TOWN_HOUSE2:0,1/MAP_DEWFORD_TOWN:4":"MAP_DEWFORD_TOWN:4/MAP_DEWFORD_TOWN_HOUSE2:0","MAP_DEWFORD_TOWN_POKEMON_CENTER_1F:0,1/MAP_DEWFORD_TOWN:1":"MAP_DEWFORD_TOWN:1/MAP_DEWFORD_TOWN_POKEMON_CENTER_1F:0","MAP_DEWFORD_TOWN_POKEMON_CENTER_1F:2/MAP_DEWFORD_TOWN_POKEMON_CENTER_2F:0":"MAP_DEWFORD_TOWN_POKEMON_CENTER_2F:0/MAP_DEWFORD_TOWN_POKEMON_CENTER_1F:2","MAP_DEWFORD_TOWN_POKEMON_CENTER_2F:0/MAP_DEWFORD_TOWN_POKEMON_CENTER_1F:2":"MAP_DEWFORD_TOWN_POKEMON_CENTER_1F:2/MAP_DEWFORD_TOWN_POKEMON_CENTER_2F:0","MAP_DEWFORD_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_DEWFORD_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_EVER_GRANDE_CITY:0/MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:0":"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:0,1/MAP_EVER_GRANDE_CITY:0","MAP_EVER_GRANDE_CITY:1/MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F:0":"MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F:0,1/MAP_EVER_GRANDE_CITY:1","MAP_EVER_GRANDE_CITY:2/MAP_VICTORY_ROAD_1F:0":"MAP_VICTORY_ROAD_1F:0/MAP_EVER_GRANDE_CITY:2","MAP_EVER_GRANDE_CITY:3/MAP_VICTORY_ROAD_1F:1":"MAP_VICTORY_ROAD_1F:1/MAP_EVER_GRANDE_CITY:3","MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM:0/MAP_EVER_GRANDE_CITY_HALL4:1":"MAP_EVER_GRANDE_CITY_HALL4:1/MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM:0","MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM:1/MAP_EVER_GRANDE_CITY_HALL_OF_FAME:0":"MAP_EVER_GRANDE_CITY_HALL_OF_FAME:0/MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM:1","MAP_EVER_GRANDE_CITY_DRAKES_ROOM:0/MAP_EVER_GRANDE_CITY_HALL3:1":"MAP_EVER_GRANDE_CITY_HALL3:1/MAP_EVER_GRANDE_CITY_DRAKES_ROOM:0","MAP_EVER_GRANDE_CITY_DRAKES_ROOM:1/MAP_EVER_GRANDE_CITY_HALL4:0":"MAP_EVER_GRANDE_CITY_HALL4:0/MAP_EVER_GRANDE_CITY_DRAKES_ROOM:1","MAP_EVER_GRANDE_CITY_GLACIAS_ROOM:0/MAP_EVER_GRANDE_CITY_HALL2:1":"MAP_EVER_GRANDE_CITY_HALL2:1/MAP_EVER_GRANDE_CITY_GLACIAS_ROOM:0","MAP_EVER_GRANDE_CITY_GLACIAS_ROOM:1/MAP_EVER_GRANDE_CITY_HALL3:0":"MAP_EVER_GRANDE_CITY_HALL3:0,2,3/MAP_EVER_GRANDE_CITY_GLACIAS_ROOM:1","MAP_EVER_GRANDE_CITY_HALL1:0,2,3/MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM:1":"MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM:1/MAP_EVER_GRANDE_CITY_HALL1:0","MAP_EVER_GRANDE_CITY_HALL1:1/MAP_EVER_GRANDE_CITY_PHOEBES_ROOM:0":"MAP_EVER_GRANDE_CITY_PHOEBES_ROOM:0/MAP_EVER_GRANDE_CITY_HALL1:1","MAP_EVER_GRANDE_CITY_HALL2:0,2,3/MAP_EVER_GRANDE_CITY_PHOEBES_ROOM:1":"MAP_EVER_GRANDE_CITY_PHOEBES_ROOM:1/MAP_EVER_GRANDE_CITY_HALL2:0","MAP_EVER_GRANDE_CITY_HALL2:1/MAP_EVER_GRANDE_CITY_GLACIAS_ROOM:0":"MAP_EVER_GRANDE_CITY_GLACIAS_ROOM:0/MAP_EVER_GRANDE_CITY_HALL2:1","MAP_EVER_GRANDE_CITY_HALL3:0,2,3/MAP_EVER_GRANDE_CITY_GLACIAS_ROOM:1":"MAP_EVER_GRANDE_CITY_GLACIAS_ROOM:1/MAP_EVER_GRANDE_CITY_HALL3:0","MAP_EVER_GRANDE_CITY_HALL3:1/MAP_EVER_GRANDE_CITY_DRAKES_ROOM:0":"MAP_EVER_GRANDE_CITY_DRAKES_ROOM:0/MAP_EVER_GRANDE_CITY_HALL3:1","MAP_EVER_GRANDE_CITY_HALL4:0/MAP_EVER_GRANDE_CITY_DRAKES_ROOM:1":"MAP_EVER_GRANDE_CITY_DRAKES_ROOM:1/MAP_EVER_GRANDE_CITY_HALL4:0","MAP_EVER_GRANDE_CITY_HALL4:1/MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM:0":"MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM:0/MAP_EVER_GRANDE_CITY_HALL4:1","MAP_EVER_GRANDE_CITY_HALL5:0,2,3/MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:2":"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:2,3/MAP_EVER_GRANDE_CITY_HALL5:0","MAP_EVER_GRANDE_CITY_HALL5:1/MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM:0":"MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM:0/MAP_EVER_GRANDE_CITY_HALL5:1","MAP_EVER_GRANDE_CITY_HALL_OF_FAME:0/MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM:1":"MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM:1/MAP_EVER_GRANDE_CITY_HALL_OF_FAME:0","MAP_EVER_GRANDE_CITY_PHOEBES_ROOM:0/MAP_EVER_GRANDE_CITY_HALL1:1":"MAP_EVER_GRANDE_CITY_HALL1:1/MAP_EVER_GRANDE_CITY_PHOEBES_ROOM:0","MAP_EVER_GRANDE_CITY_PHOEBES_ROOM:1/MAP_EVER_GRANDE_CITY_HALL2:0":"MAP_EVER_GRANDE_CITY_HALL2:0,2,3/MAP_EVER_GRANDE_CITY_PHOEBES_ROOM:1","MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F:0,1/MAP_EVER_GRANDE_CITY:1":"MAP_EVER_GRANDE_CITY:1/MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F:0","MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F:2/MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F:0":"MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F:0/MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F:2","MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F:0/MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F:2":"MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F:2/MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F:0","MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:0,1/MAP_EVER_GRANDE_CITY:0":"MAP_EVER_GRANDE_CITY:0/MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:0","MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:2,3/MAP_EVER_GRANDE_CITY_HALL5:0":"MAP_EVER_GRANDE_CITY_HALL5:0,2,3/MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:2","MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:4/MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F:0":"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F:0/MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:4","MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F:0/MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:4":"MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F:4/MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F:0","MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM:0/MAP_EVER_GRANDE_CITY_HALL5:1":"MAP_EVER_GRANDE_CITY_HALL5:1/MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM:0","MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM:1/MAP_EVER_GRANDE_CITY_HALL1:0":"MAP_EVER_GRANDE_CITY_HALL1:0,2,3/MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM:1","MAP_FALLARBOR_TOWN:0/MAP_FALLARBOR_TOWN_MART:0":"MAP_FALLARBOR_TOWN_MART:0,1/MAP_FALLARBOR_TOWN:0","MAP_FALLARBOR_TOWN:1/MAP_FALLARBOR_TOWN_BATTLE_TENT_LOBBY:0":"MAP_FALLARBOR_TOWN_BATTLE_TENT_LOBBY:0,1/MAP_FALLARBOR_TOWN:1","MAP_FALLARBOR_TOWN:2/MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F:0":"MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F:0,1/MAP_FALLARBOR_TOWN:2","MAP_FALLARBOR_TOWN:3/MAP_FALLARBOR_TOWN_COZMOS_HOUSE:0":"MAP_FALLARBOR_TOWN_COZMOS_HOUSE:0,1/MAP_FALLARBOR_TOWN:3","MAP_FALLARBOR_TOWN:4/MAP_FALLARBOR_TOWN_MOVE_RELEARNERS_HOUSE:0":"MAP_FALLARBOR_TOWN_MOVE_RELEARNERS_HOUSE:0,1/MAP_FALLARBOR_TOWN:4","MAP_FALLARBOR_TOWN_BATTLE_TENT_LOBBY:0,1/MAP_FALLARBOR_TOWN:1":"MAP_FALLARBOR_TOWN:1/MAP_FALLARBOR_TOWN_BATTLE_TENT_LOBBY:0","MAP_FALLARBOR_TOWN_COZMOS_HOUSE:0,1/MAP_FALLARBOR_TOWN:3":"MAP_FALLARBOR_TOWN:3/MAP_FALLARBOR_TOWN_COZMOS_HOUSE:0","MAP_FALLARBOR_TOWN_MART:0,1/MAP_FALLARBOR_TOWN:0":"MAP_FALLARBOR_TOWN:0/MAP_FALLARBOR_TOWN_MART:0","MAP_FALLARBOR_TOWN_MOVE_RELEARNERS_HOUSE:0,1/MAP_FALLARBOR_TOWN:4":"MAP_FALLARBOR_TOWN:4/MAP_FALLARBOR_TOWN_MOVE_RELEARNERS_HOUSE:0","MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F:0,1/MAP_FALLARBOR_TOWN:2":"MAP_FALLARBOR_TOWN:2/MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F:0","MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F:2/MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F:0":"MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F:0/MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F:2","MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F:0/MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F:2":"MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F:2/MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F:0","MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_FARAWAY_ISLAND_ENTRANCE:0,1/MAP_FARAWAY_ISLAND_INTERIOR:0,1":"MAP_FARAWAY_ISLAND_INTERIOR:0,1/MAP_FARAWAY_ISLAND_ENTRANCE:0,1","MAP_FARAWAY_ISLAND_INTERIOR:0,1/MAP_FARAWAY_ISLAND_ENTRANCE:0,1":"MAP_FARAWAY_ISLAND_ENTRANCE:0,1/MAP_FARAWAY_ISLAND_INTERIOR:0,1","MAP_FIERY_PATH:0/MAP_ROUTE112:4":"MAP_ROUTE112:4/MAP_FIERY_PATH:0","MAP_FIERY_PATH:1/MAP_ROUTE112:5":"MAP_ROUTE112:5/MAP_FIERY_PATH:1","MAP_FORTREE_CITY:0/MAP_FORTREE_CITY_POKEMON_CENTER_1F:0":"MAP_FORTREE_CITY_POKEMON_CENTER_1F:0,1/MAP_FORTREE_CITY:0","MAP_FORTREE_CITY:1/MAP_FORTREE_CITY_HOUSE1:0":"MAP_FORTREE_CITY_HOUSE1:0,1/MAP_FORTREE_CITY:1","MAP_FORTREE_CITY:2/MAP_FORTREE_CITY_GYM:0":"MAP_FORTREE_CITY_GYM:0,1/MAP_FORTREE_CITY:2","MAP_FORTREE_CITY:3/MAP_FORTREE_CITY_MART:0":"MAP_FORTREE_CITY_MART:0,1/MAP_FORTREE_CITY:3","MAP_FORTREE_CITY:4/MAP_FORTREE_CITY_HOUSE2:0":"MAP_FORTREE_CITY_HOUSE2:0,1/MAP_FORTREE_CITY:4","MAP_FORTREE_CITY:5/MAP_FORTREE_CITY_HOUSE3:0":"MAP_FORTREE_CITY_HOUSE3:0,1/MAP_FORTREE_CITY:5","MAP_FORTREE_CITY:6/MAP_FORTREE_CITY_HOUSE4:0":"MAP_FORTREE_CITY_HOUSE4:0,1/MAP_FORTREE_CITY:6","MAP_FORTREE_CITY:7/MAP_FORTREE_CITY_HOUSE5:0":"MAP_FORTREE_CITY_HOUSE5:0,1/MAP_FORTREE_CITY:7","MAP_FORTREE_CITY:8/MAP_FORTREE_CITY_DECORATION_SHOP:0":"MAP_FORTREE_CITY_DECORATION_SHOP:0,1/MAP_FORTREE_CITY:8","MAP_FORTREE_CITY_DECORATION_SHOP:0,1/MAP_FORTREE_CITY:8":"MAP_FORTREE_CITY:8/MAP_FORTREE_CITY_DECORATION_SHOP:0","MAP_FORTREE_CITY_GYM:0,1/MAP_FORTREE_CITY:2":"MAP_FORTREE_CITY:2/MAP_FORTREE_CITY_GYM:0","MAP_FORTREE_CITY_HOUSE1:0,1/MAP_FORTREE_CITY:1":"MAP_FORTREE_CITY:1/MAP_FORTREE_CITY_HOUSE1:0","MAP_FORTREE_CITY_HOUSE2:0,1/MAP_FORTREE_CITY:4":"MAP_FORTREE_CITY:4/MAP_FORTREE_CITY_HOUSE2:0","MAP_FORTREE_CITY_HOUSE3:0,1/MAP_FORTREE_CITY:5":"MAP_FORTREE_CITY:5/MAP_FORTREE_CITY_HOUSE3:0","MAP_FORTREE_CITY_HOUSE4:0,1/MAP_FORTREE_CITY:6":"MAP_FORTREE_CITY:6/MAP_FORTREE_CITY_HOUSE4:0","MAP_FORTREE_CITY_HOUSE5:0,1/MAP_FORTREE_CITY:7":"MAP_FORTREE_CITY:7/MAP_FORTREE_CITY_HOUSE5:0","MAP_FORTREE_CITY_MART:0,1/MAP_FORTREE_CITY:3":"MAP_FORTREE_CITY:3/MAP_FORTREE_CITY_MART:0","MAP_FORTREE_CITY_POKEMON_CENTER_1F:0,1/MAP_FORTREE_CITY:0":"MAP_FORTREE_CITY:0/MAP_FORTREE_CITY_POKEMON_CENTER_1F:0","MAP_FORTREE_CITY_POKEMON_CENTER_1F:2/MAP_FORTREE_CITY_POKEMON_CENTER_2F:0":"MAP_FORTREE_CITY_POKEMON_CENTER_2F:0/MAP_FORTREE_CITY_POKEMON_CENTER_1F:2","MAP_FORTREE_CITY_POKEMON_CENTER_2F:0/MAP_FORTREE_CITY_POKEMON_CENTER_1F:2":"MAP_FORTREE_CITY_POKEMON_CENTER_1F:2/MAP_FORTREE_CITY_POKEMON_CENTER_2F:0","MAP_FORTREE_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_FORTREE_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_GRANITE_CAVE_1F:0/MAP_ROUTE106:0":"MAP_ROUTE106:0/MAP_GRANITE_CAVE_1F:0","MAP_GRANITE_CAVE_1F:1/MAP_GRANITE_CAVE_B1F:0":"MAP_GRANITE_CAVE_B1F:0/MAP_GRANITE_CAVE_1F:1","MAP_GRANITE_CAVE_1F:2/MAP_GRANITE_CAVE_B1F:1":"MAP_GRANITE_CAVE_B1F:1/MAP_GRANITE_CAVE_1F:2","MAP_GRANITE_CAVE_1F:3/MAP_GRANITE_CAVE_STEVENS_ROOM:0":"MAP_GRANITE_CAVE_STEVENS_ROOM:0/MAP_GRANITE_CAVE_1F:3","MAP_GRANITE_CAVE_B1F:0/MAP_GRANITE_CAVE_1F:1":"MAP_GRANITE_CAVE_1F:1/MAP_GRANITE_CAVE_B1F:0","MAP_GRANITE_CAVE_B1F:1/MAP_GRANITE_CAVE_1F:2":"MAP_GRANITE_CAVE_1F:2/MAP_GRANITE_CAVE_B1F:1","MAP_GRANITE_CAVE_B1F:2/MAP_GRANITE_CAVE_B2F:0":"MAP_GRANITE_CAVE_B2F:0/MAP_GRANITE_CAVE_B1F:2","MAP_GRANITE_CAVE_B1F:3/MAP_GRANITE_CAVE_B2F:1":"MAP_GRANITE_CAVE_B2F:1/MAP_GRANITE_CAVE_B1F:3","MAP_GRANITE_CAVE_B1F:4/MAP_GRANITE_CAVE_B2F:2":"MAP_GRANITE_CAVE_B2F:2/MAP_GRANITE_CAVE_B1F:4","MAP_GRANITE_CAVE_B1F:5/MAP_GRANITE_CAVE_B2F:3":"MAP_GRANITE_CAVE_B2F:3/MAP_GRANITE_CAVE_B1F:5","MAP_GRANITE_CAVE_B1F:6/MAP_GRANITE_CAVE_B2F:4":"MAP_GRANITE_CAVE_B2F:4/MAP_GRANITE_CAVE_B1F:6","MAP_GRANITE_CAVE_B2F:0/MAP_GRANITE_CAVE_B1F:2":"MAP_GRANITE_CAVE_B1F:2/MAP_GRANITE_CAVE_B2F:0","MAP_GRANITE_CAVE_B2F:1/MAP_GRANITE_CAVE_B1F:3":"MAP_GRANITE_CAVE_B1F:3/MAP_GRANITE_CAVE_B2F:1","MAP_GRANITE_CAVE_B2F:2/MAP_GRANITE_CAVE_B1F:4":"MAP_GRANITE_CAVE_B1F:4/MAP_GRANITE_CAVE_B2F:2","MAP_GRANITE_CAVE_B2F:3/MAP_GRANITE_CAVE_B1F:5":"MAP_GRANITE_CAVE_B1F:5/MAP_GRANITE_CAVE_B2F:3","MAP_GRANITE_CAVE_B2F:4/MAP_GRANITE_CAVE_B1F:6":"MAP_GRANITE_CAVE_B1F:6/MAP_GRANITE_CAVE_B2F:4","MAP_GRANITE_CAVE_STEVENS_ROOM:0/MAP_GRANITE_CAVE_1F:3":"MAP_GRANITE_CAVE_1F:3/MAP_GRANITE_CAVE_STEVENS_ROOM:0","MAP_INSIDE_OF_TRUCK:0,1,2/MAP_DYNAMIC:-1!":"","MAP_ISLAND_CAVE:0/MAP_ROUTE105:0":"MAP_ROUTE105:0/MAP_ISLAND_CAVE:0","MAP_ISLAND_CAVE:1/MAP_ISLAND_CAVE:2":"MAP_ISLAND_CAVE:2/MAP_ISLAND_CAVE:1","MAP_ISLAND_CAVE:2/MAP_ISLAND_CAVE:1":"MAP_ISLAND_CAVE:1/MAP_ISLAND_CAVE:2","MAP_JAGGED_PASS:0,1/MAP_ROUTE112:2,3":"MAP_ROUTE112:2,3/MAP_JAGGED_PASS:0,1","MAP_JAGGED_PASS:2,3/MAP_MT_CHIMNEY:2,3":"MAP_MT_CHIMNEY:2,3/MAP_JAGGED_PASS:2,3","MAP_JAGGED_PASS:4/MAP_MAGMA_HIDEOUT_1F:0":"MAP_MAGMA_HIDEOUT_1F:0/MAP_JAGGED_PASS:4","MAP_LAVARIDGE_TOWN:0/MAP_LAVARIDGE_TOWN_HERB_SHOP:0":"MAP_LAVARIDGE_TOWN_HERB_SHOP:0,1/MAP_LAVARIDGE_TOWN:0","MAP_LAVARIDGE_TOWN:1/MAP_LAVARIDGE_TOWN_GYM_1F:0":"MAP_LAVARIDGE_TOWN_GYM_1F:0,1/MAP_LAVARIDGE_TOWN:1","MAP_LAVARIDGE_TOWN:2/MAP_LAVARIDGE_TOWN_MART:0":"MAP_LAVARIDGE_TOWN_MART:0,1/MAP_LAVARIDGE_TOWN:2","MAP_LAVARIDGE_TOWN:3/MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:0":"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:0,1/MAP_LAVARIDGE_TOWN:3","MAP_LAVARIDGE_TOWN:4/MAP_LAVARIDGE_TOWN_HOUSE:0":"MAP_LAVARIDGE_TOWN_HOUSE:0,1/MAP_LAVARIDGE_TOWN:4","MAP_LAVARIDGE_TOWN:5/MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:3":"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:3/MAP_LAVARIDGE_TOWN:5","MAP_LAVARIDGE_TOWN_GYM_1F:0,1/MAP_LAVARIDGE_TOWN:1":"MAP_LAVARIDGE_TOWN:1/MAP_LAVARIDGE_TOWN_GYM_1F:0","MAP_LAVARIDGE_TOWN_GYM_1F:10/MAP_LAVARIDGE_TOWN_GYM_B1F:8":"MAP_LAVARIDGE_TOWN_GYM_B1F:8/MAP_LAVARIDGE_TOWN_GYM_1F:10","MAP_LAVARIDGE_TOWN_GYM_1F:11/MAP_LAVARIDGE_TOWN_GYM_B1F:9":"MAP_LAVARIDGE_TOWN_GYM_B1F:9/MAP_LAVARIDGE_TOWN_GYM_1F:11","MAP_LAVARIDGE_TOWN_GYM_1F:12/MAP_LAVARIDGE_TOWN_GYM_B1F:10":"MAP_LAVARIDGE_TOWN_GYM_B1F:10/MAP_LAVARIDGE_TOWN_GYM_1F:12","MAP_LAVARIDGE_TOWN_GYM_1F:13/MAP_LAVARIDGE_TOWN_GYM_B1F:11":"MAP_LAVARIDGE_TOWN_GYM_B1F:11/MAP_LAVARIDGE_TOWN_GYM_1F:13","MAP_LAVARIDGE_TOWN_GYM_1F:14/MAP_LAVARIDGE_TOWN_GYM_B1F:12":"MAP_LAVARIDGE_TOWN_GYM_B1F:12/MAP_LAVARIDGE_TOWN_GYM_1F:14","MAP_LAVARIDGE_TOWN_GYM_1F:15/MAP_LAVARIDGE_TOWN_GYM_B1F:13":"MAP_LAVARIDGE_TOWN_GYM_B1F:13/MAP_LAVARIDGE_TOWN_GYM_1F:15","MAP_LAVARIDGE_TOWN_GYM_1F:16/MAP_LAVARIDGE_TOWN_GYM_B1F:14":"MAP_LAVARIDGE_TOWN_GYM_B1F:14/MAP_LAVARIDGE_TOWN_GYM_1F:16","MAP_LAVARIDGE_TOWN_GYM_1F:17/MAP_LAVARIDGE_TOWN_GYM_B1F:15":"MAP_LAVARIDGE_TOWN_GYM_B1F:15/MAP_LAVARIDGE_TOWN_GYM_1F:17","MAP_LAVARIDGE_TOWN_GYM_1F:18/MAP_LAVARIDGE_TOWN_GYM_B1F:16":"MAP_LAVARIDGE_TOWN_GYM_B1F:16/MAP_LAVARIDGE_TOWN_GYM_1F:18","MAP_LAVARIDGE_TOWN_GYM_1F:19/MAP_LAVARIDGE_TOWN_GYM_B1F:17":"MAP_LAVARIDGE_TOWN_GYM_B1F:17/MAP_LAVARIDGE_TOWN_GYM_1F:19","MAP_LAVARIDGE_TOWN_GYM_1F:2/MAP_LAVARIDGE_TOWN_GYM_B1F:0":"MAP_LAVARIDGE_TOWN_GYM_B1F:0/MAP_LAVARIDGE_TOWN_GYM_1F:2","MAP_LAVARIDGE_TOWN_GYM_1F:20/MAP_LAVARIDGE_TOWN_GYM_B1F:18":"MAP_LAVARIDGE_TOWN_GYM_B1F:18/MAP_LAVARIDGE_TOWN_GYM_1F:20","MAP_LAVARIDGE_TOWN_GYM_1F:21/MAP_LAVARIDGE_TOWN_GYM_B1F:20":"MAP_LAVARIDGE_TOWN_GYM_B1F:20/MAP_LAVARIDGE_TOWN_GYM_1F:21","MAP_LAVARIDGE_TOWN_GYM_1F:22/MAP_LAVARIDGE_TOWN_GYM_B1F:19":"MAP_LAVARIDGE_TOWN_GYM_B1F:19/MAP_LAVARIDGE_TOWN_GYM_1F:22","MAP_LAVARIDGE_TOWN_GYM_1F:23/MAP_LAVARIDGE_TOWN_GYM_B1F:21":"MAP_LAVARIDGE_TOWN_GYM_B1F:21/MAP_LAVARIDGE_TOWN_GYM_1F:23","MAP_LAVARIDGE_TOWN_GYM_1F:24/MAP_LAVARIDGE_TOWN_GYM_B1F:22":"MAP_LAVARIDGE_TOWN_GYM_B1F:22/MAP_LAVARIDGE_TOWN_GYM_1F:24","MAP_LAVARIDGE_TOWN_GYM_1F:25/MAP_LAVARIDGE_TOWN_GYM_B1F:23":"MAP_LAVARIDGE_TOWN_GYM_B1F:23/MAP_LAVARIDGE_TOWN_GYM_1F:25","MAP_LAVARIDGE_TOWN_GYM_1F:3/MAP_LAVARIDGE_TOWN_GYM_B1F:2":"MAP_LAVARIDGE_TOWN_GYM_B1F:2/MAP_LAVARIDGE_TOWN_GYM_1F:3","MAP_LAVARIDGE_TOWN_GYM_1F:4/MAP_LAVARIDGE_TOWN_GYM_B1F:4":"MAP_LAVARIDGE_TOWN_GYM_B1F:4/MAP_LAVARIDGE_TOWN_GYM_1F:4","MAP_LAVARIDGE_TOWN_GYM_1F:5/MAP_LAVARIDGE_TOWN_GYM_B1F:3":"MAP_LAVARIDGE_TOWN_GYM_B1F:3/MAP_LAVARIDGE_TOWN_GYM_1F:5","MAP_LAVARIDGE_TOWN_GYM_1F:6/MAP_LAVARIDGE_TOWN_GYM_B1F:1":"MAP_LAVARIDGE_TOWN_GYM_B1F:1/MAP_LAVARIDGE_TOWN_GYM_1F:6","MAP_LAVARIDGE_TOWN_GYM_1F:7/MAP_LAVARIDGE_TOWN_GYM_B1F:5":"MAP_LAVARIDGE_TOWN_GYM_B1F:5/MAP_LAVARIDGE_TOWN_GYM_1F:7","MAP_LAVARIDGE_TOWN_GYM_1F:8/MAP_LAVARIDGE_TOWN_GYM_B1F:6":"MAP_LAVARIDGE_TOWN_GYM_B1F:6/MAP_LAVARIDGE_TOWN_GYM_1F:8","MAP_LAVARIDGE_TOWN_GYM_1F:9/MAP_LAVARIDGE_TOWN_GYM_B1F:7":"MAP_LAVARIDGE_TOWN_GYM_B1F:7/MAP_LAVARIDGE_TOWN_GYM_1F:9","MAP_LAVARIDGE_TOWN_GYM_B1F:0/MAP_LAVARIDGE_TOWN_GYM_1F:2":"MAP_LAVARIDGE_TOWN_GYM_1F:2/MAP_LAVARIDGE_TOWN_GYM_B1F:0","MAP_LAVARIDGE_TOWN_GYM_B1F:1/MAP_LAVARIDGE_TOWN_GYM_1F:6":"MAP_LAVARIDGE_TOWN_GYM_1F:6/MAP_LAVARIDGE_TOWN_GYM_B1F:1","MAP_LAVARIDGE_TOWN_GYM_B1F:10/MAP_LAVARIDGE_TOWN_GYM_1F:12":"MAP_LAVARIDGE_TOWN_GYM_1F:12/MAP_LAVARIDGE_TOWN_GYM_B1F:10","MAP_LAVARIDGE_TOWN_GYM_B1F:11/MAP_LAVARIDGE_TOWN_GYM_1F:13":"MAP_LAVARIDGE_TOWN_GYM_1F:13/MAP_LAVARIDGE_TOWN_GYM_B1F:11","MAP_LAVARIDGE_TOWN_GYM_B1F:12/MAP_LAVARIDGE_TOWN_GYM_1F:14":"MAP_LAVARIDGE_TOWN_GYM_1F:14/MAP_LAVARIDGE_TOWN_GYM_B1F:12","MAP_LAVARIDGE_TOWN_GYM_B1F:13/MAP_LAVARIDGE_TOWN_GYM_1F:15":"MAP_LAVARIDGE_TOWN_GYM_1F:15/MAP_LAVARIDGE_TOWN_GYM_B1F:13","MAP_LAVARIDGE_TOWN_GYM_B1F:14/MAP_LAVARIDGE_TOWN_GYM_1F:16":"MAP_LAVARIDGE_TOWN_GYM_1F:16/MAP_LAVARIDGE_TOWN_GYM_B1F:14","MAP_LAVARIDGE_TOWN_GYM_B1F:15/MAP_LAVARIDGE_TOWN_GYM_1F:17":"MAP_LAVARIDGE_TOWN_GYM_1F:17/MAP_LAVARIDGE_TOWN_GYM_B1F:15","MAP_LAVARIDGE_TOWN_GYM_B1F:16/MAP_LAVARIDGE_TOWN_GYM_1F:18":"MAP_LAVARIDGE_TOWN_GYM_1F:18/MAP_LAVARIDGE_TOWN_GYM_B1F:16","MAP_LAVARIDGE_TOWN_GYM_B1F:17/MAP_LAVARIDGE_TOWN_GYM_1F:19":"MAP_LAVARIDGE_TOWN_GYM_1F:19/MAP_LAVARIDGE_TOWN_GYM_B1F:17","MAP_LAVARIDGE_TOWN_GYM_B1F:18/MAP_LAVARIDGE_TOWN_GYM_1F:20":"MAP_LAVARIDGE_TOWN_GYM_1F:20/MAP_LAVARIDGE_TOWN_GYM_B1F:18","MAP_LAVARIDGE_TOWN_GYM_B1F:19/MAP_LAVARIDGE_TOWN_GYM_1F:22":"MAP_LAVARIDGE_TOWN_GYM_1F:22/MAP_LAVARIDGE_TOWN_GYM_B1F:19","MAP_LAVARIDGE_TOWN_GYM_B1F:2/MAP_LAVARIDGE_TOWN_GYM_1F:3":"MAP_LAVARIDGE_TOWN_GYM_1F:3/MAP_LAVARIDGE_TOWN_GYM_B1F:2","MAP_LAVARIDGE_TOWN_GYM_B1F:20/MAP_LAVARIDGE_TOWN_GYM_1F:21":"MAP_LAVARIDGE_TOWN_GYM_1F:21/MAP_LAVARIDGE_TOWN_GYM_B1F:20","MAP_LAVARIDGE_TOWN_GYM_B1F:21/MAP_LAVARIDGE_TOWN_GYM_1F:23":"MAP_LAVARIDGE_TOWN_GYM_1F:23/MAP_LAVARIDGE_TOWN_GYM_B1F:21","MAP_LAVARIDGE_TOWN_GYM_B1F:22/MAP_LAVARIDGE_TOWN_GYM_1F:24":"MAP_LAVARIDGE_TOWN_GYM_1F:24/MAP_LAVARIDGE_TOWN_GYM_B1F:22","MAP_LAVARIDGE_TOWN_GYM_B1F:23/MAP_LAVARIDGE_TOWN_GYM_1F:25":"MAP_LAVARIDGE_TOWN_GYM_1F:25/MAP_LAVARIDGE_TOWN_GYM_B1F:23","MAP_LAVARIDGE_TOWN_GYM_B1F:3/MAP_LAVARIDGE_TOWN_GYM_1F:5":"MAP_LAVARIDGE_TOWN_GYM_1F:5/MAP_LAVARIDGE_TOWN_GYM_B1F:3","MAP_LAVARIDGE_TOWN_GYM_B1F:4/MAP_LAVARIDGE_TOWN_GYM_1F:4":"MAP_LAVARIDGE_TOWN_GYM_1F:4/MAP_LAVARIDGE_TOWN_GYM_B1F:4","MAP_LAVARIDGE_TOWN_GYM_B1F:5/MAP_LAVARIDGE_TOWN_GYM_1F:7":"MAP_LAVARIDGE_TOWN_GYM_1F:7/MAP_LAVARIDGE_TOWN_GYM_B1F:5","MAP_LAVARIDGE_TOWN_GYM_B1F:6/MAP_LAVARIDGE_TOWN_GYM_1F:8":"MAP_LAVARIDGE_TOWN_GYM_1F:8/MAP_LAVARIDGE_TOWN_GYM_B1F:6","MAP_LAVARIDGE_TOWN_GYM_B1F:7/MAP_LAVARIDGE_TOWN_GYM_1F:9":"MAP_LAVARIDGE_TOWN_GYM_1F:9/MAP_LAVARIDGE_TOWN_GYM_B1F:7","MAP_LAVARIDGE_TOWN_GYM_B1F:8/MAP_LAVARIDGE_TOWN_GYM_1F:10":"MAP_LAVARIDGE_TOWN_GYM_1F:10/MAP_LAVARIDGE_TOWN_GYM_B1F:8","MAP_LAVARIDGE_TOWN_GYM_B1F:9/MAP_LAVARIDGE_TOWN_GYM_1F:11":"MAP_LAVARIDGE_TOWN_GYM_1F:11/MAP_LAVARIDGE_TOWN_GYM_B1F:9","MAP_LAVARIDGE_TOWN_HERB_SHOP:0,1/MAP_LAVARIDGE_TOWN:0":"MAP_LAVARIDGE_TOWN:0/MAP_LAVARIDGE_TOWN_HERB_SHOP:0","MAP_LAVARIDGE_TOWN_HOUSE:0,1/MAP_LAVARIDGE_TOWN:4":"MAP_LAVARIDGE_TOWN:4/MAP_LAVARIDGE_TOWN_HOUSE:0","MAP_LAVARIDGE_TOWN_MART:0,1/MAP_LAVARIDGE_TOWN:2":"MAP_LAVARIDGE_TOWN:2/MAP_LAVARIDGE_TOWN_MART:0","MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:0,1/MAP_LAVARIDGE_TOWN:3":"MAP_LAVARIDGE_TOWN:3/MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:0","MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:2/MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F:0":"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F:0/MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:2","MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:3/MAP_LAVARIDGE_TOWN:5":"MAP_LAVARIDGE_TOWN:5/MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:3","MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F:0/MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:2":"MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F:2/MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F:0","MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_LILYCOVE_CITY:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:0":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:0,1/MAP_LILYCOVE_CITY:0","MAP_LILYCOVE_CITY:1/MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F:0":"MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F:0,1/MAP_LILYCOVE_CITY:1","MAP_LILYCOVE_CITY:10/MAP_LILYCOVE_CITY_HOUSE3:0":"MAP_LILYCOVE_CITY_HOUSE3:0,1/MAP_LILYCOVE_CITY:10","MAP_LILYCOVE_CITY:11/MAP_LILYCOVE_CITY_HOUSE4:0":"MAP_LILYCOVE_CITY_HOUSE4:0,1/MAP_LILYCOVE_CITY:11","MAP_LILYCOVE_CITY:12/MAP_LILYCOVE_CITY_HARBOR:0":"MAP_LILYCOVE_CITY_HARBOR:0,1/MAP_LILYCOVE_CITY:12","MAP_LILYCOVE_CITY:2/MAP_LILYCOVE_CITY_POKEMON_CENTER_1F:0":"MAP_LILYCOVE_CITY_POKEMON_CENTER_1F:0,1/MAP_LILYCOVE_CITY:2","MAP_LILYCOVE_CITY:3,13/MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F:0,1":"MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F:0,1/MAP_LILYCOVE_CITY:3,13","MAP_LILYCOVE_CITY:4/MAP_LILYCOVE_CITY_CONTEST_LOBBY:0":"MAP_LILYCOVE_CITY_CONTEST_LOBBY:0,1/MAP_LILYCOVE_CITY:4","MAP_LILYCOVE_CITY:5/MAP_LILYCOVE_CITY_POKEMON_TRAINER_FAN_CLUB:1":"MAP_LILYCOVE_CITY_POKEMON_TRAINER_FAN_CLUB:0,1/MAP_LILYCOVE_CITY:5","MAP_LILYCOVE_CITY:6/MAP_AQUA_HIDEOUT_1F:0":"MAP_AQUA_HIDEOUT_1F:0,1/MAP_LILYCOVE_CITY:6","MAP_LILYCOVE_CITY:7/MAP_LILYCOVE_CITY_MOVE_DELETERS_HOUSE:0":"MAP_LILYCOVE_CITY_MOVE_DELETERS_HOUSE:0,1/MAP_LILYCOVE_CITY:7","MAP_LILYCOVE_CITY:8/MAP_LILYCOVE_CITY_HOUSE1:0":"MAP_LILYCOVE_CITY_HOUSE1:0,1/MAP_LILYCOVE_CITY:8","MAP_LILYCOVE_CITY:9/MAP_LILYCOVE_CITY_HOUSE2:0":"MAP_LILYCOVE_CITY_HOUSE2:0,1/MAP_LILYCOVE_CITY:9","MAP_LILYCOVE_CITY_CONTEST_HALL:0,2/MAP_LILYCOVE_CITY_CONTEST_LOBBY:2":"MAP_LILYCOVE_CITY_CONTEST_LOBBY:2/MAP_LILYCOVE_CITY_CONTEST_HALL:0","MAP_LILYCOVE_CITY_CONTEST_HALL:1,3/MAP_LILYCOVE_CITY_CONTEST_LOBBY:3":"MAP_LILYCOVE_CITY_CONTEST_LOBBY:3/MAP_LILYCOVE_CITY_CONTEST_HALL:1","MAP_LILYCOVE_CITY_CONTEST_LOBBY:0,1/MAP_LILYCOVE_CITY:4":"MAP_LILYCOVE_CITY:4/MAP_LILYCOVE_CITY_CONTEST_LOBBY:0","MAP_LILYCOVE_CITY_CONTEST_LOBBY:2/MAP_LILYCOVE_CITY_CONTEST_HALL:0":"MAP_LILYCOVE_CITY_CONTEST_HALL:0,2/MAP_LILYCOVE_CITY_CONTEST_LOBBY:2","MAP_LILYCOVE_CITY_CONTEST_LOBBY:3/MAP_LILYCOVE_CITY_CONTEST_HALL:1":"MAP_LILYCOVE_CITY_CONTEST_HALL:1,3/MAP_LILYCOVE_CITY_CONTEST_LOBBY:3","MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F:0,1/MAP_LILYCOVE_CITY:1":"MAP_LILYCOVE_CITY:1/MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F:0","MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F:2/MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_2F:0":"MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_2F:0/MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F:2","MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_2F:0/MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F:2":"MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F:2/MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_2F:0","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:0,1/MAP_LILYCOVE_CITY:0":"MAP_LILYCOVE_CITY:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:0","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:0":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:2","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:3/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0,1/MAP_DYNAMIC:-1!","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:2":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:0","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:1/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:0":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:1","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0,1/MAP_DYNAMIC:-1!","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:1":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:1/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:0","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:1/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:0":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:1","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0,1/MAP_DYNAMIC:-1!","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:1":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:1/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:0","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:1/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:0":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:1","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0,1/MAP_DYNAMIC:-1!","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:1":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:1/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:0","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:1/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0,1/MAP_DYNAMIC:-1!","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP:0":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:2","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0,1/MAP_DYNAMIC:-1!":"","MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:2":"MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP:0","MAP_LILYCOVE_CITY_HARBOR:0,1/MAP_LILYCOVE_CITY:12":"MAP_LILYCOVE_CITY:12/MAP_LILYCOVE_CITY_HARBOR:0","MAP_LILYCOVE_CITY_HOUSE1:0,1/MAP_LILYCOVE_CITY:8":"MAP_LILYCOVE_CITY:8/MAP_LILYCOVE_CITY_HOUSE1:0","MAP_LILYCOVE_CITY_HOUSE2:0,1/MAP_LILYCOVE_CITY:9":"MAP_LILYCOVE_CITY:9/MAP_LILYCOVE_CITY_HOUSE2:0","MAP_LILYCOVE_CITY_HOUSE3:0,1/MAP_LILYCOVE_CITY:10":"MAP_LILYCOVE_CITY:10/MAP_LILYCOVE_CITY_HOUSE3:0","MAP_LILYCOVE_CITY_HOUSE4:0,1/MAP_LILYCOVE_CITY:11":"MAP_LILYCOVE_CITY:11/MAP_LILYCOVE_CITY_HOUSE4:0","MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F:0,1/MAP_LILYCOVE_CITY:3,13":"MAP_LILYCOVE_CITY:3,13/MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F:0,1","MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F:2/MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_2F:0":"MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_2F:0/MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F:2","MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_2F:0/MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F:2":"MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F:2/MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_2F:0","MAP_LILYCOVE_CITY_MOVE_DELETERS_HOUSE:0,1/MAP_LILYCOVE_CITY:7":"MAP_LILYCOVE_CITY:7/MAP_LILYCOVE_CITY_MOVE_DELETERS_HOUSE:0","MAP_LILYCOVE_CITY_POKEMON_CENTER_1F:0,1/MAP_LILYCOVE_CITY:2":"MAP_LILYCOVE_CITY:2/MAP_LILYCOVE_CITY_POKEMON_CENTER_1F:0","MAP_LILYCOVE_CITY_POKEMON_CENTER_1F:2/MAP_LILYCOVE_CITY_POKEMON_CENTER_2F:0":"MAP_LILYCOVE_CITY_POKEMON_CENTER_2F:0/MAP_LILYCOVE_CITY_POKEMON_CENTER_1F:2","MAP_LILYCOVE_CITY_POKEMON_CENTER_2F:0/MAP_LILYCOVE_CITY_POKEMON_CENTER_1F:2":"MAP_LILYCOVE_CITY_POKEMON_CENTER_1F:2/MAP_LILYCOVE_CITY_POKEMON_CENTER_2F:0","MAP_LILYCOVE_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_LILYCOVE_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_LILYCOVE_CITY_POKEMON_TRAINER_FAN_CLUB:0,1/MAP_LILYCOVE_CITY:5":"MAP_LILYCOVE_CITY:5/MAP_LILYCOVE_CITY_POKEMON_TRAINER_FAN_CLUB:1","MAP_LILYCOVE_CITY_UNUSED_MART:0,1/MAP_LILYCOVE_CITY:0!":"MAP_LILYCOVE_CITY:0/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:0","MAP_LITTLEROOT_TOWN:0/MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F:1":"MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F:0,1/MAP_LITTLEROOT_TOWN:0","MAP_LITTLEROOT_TOWN:1/MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F:1":"MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F:0,1/MAP_LITTLEROOT_TOWN:1","MAP_LITTLEROOT_TOWN:2/MAP_LITTLEROOT_TOWN_PROFESSOR_BIRCHS_LAB:0":"MAP_LITTLEROOT_TOWN_PROFESSOR_BIRCHS_LAB:0,1/MAP_LITTLEROOT_TOWN:2","MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F:0,1/MAP_LITTLEROOT_TOWN:1":"MAP_LITTLEROOT_TOWN:1/MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F:1","MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F:2/MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F:0":"MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F:0/MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F:2","MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F:0/MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F:2":"MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F:2/MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F:0","MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F:0,1/MAP_LITTLEROOT_TOWN:0":"MAP_LITTLEROOT_TOWN:0/MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F:1","MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F:2/MAP_LITTLEROOT_TOWN_MAYS_HOUSE_2F:0":"MAP_LITTLEROOT_TOWN_MAYS_HOUSE_2F:0/MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F:2","MAP_LITTLEROOT_TOWN_MAYS_HOUSE_2F:0/MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F:2":"MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F:2/MAP_LITTLEROOT_TOWN_MAYS_HOUSE_2F:0","MAP_LITTLEROOT_TOWN_PROFESSOR_BIRCHS_LAB:0,1/MAP_LITTLEROOT_TOWN:2":"MAP_LITTLEROOT_TOWN:2/MAP_LITTLEROOT_TOWN_PROFESSOR_BIRCHS_LAB:0","MAP_MAGMA_HIDEOUT_1F:0/MAP_JAGGED_PASS:4":"MAP_JAGGED_PASS:4/MAP_MAGMA_HIDEOUT_1F:0","MAP_MAGMA_HIDEOUT_1F:1/MAP_MAGMA_HIDEOUT_2F_1R:1":"MAP_MAGMA_HIDEOUT_2F_1R:1/MAP_MAGMA_HIDEOUT_1F:1","MAP_MAGMA_HIDEOUT_1F:2/MAP_MAGMA_HIDEOUT_2F_2R:1":"MAP_MAGMA_HIDEOUT_2F_2R:1/MAP_MAGMA_HIDEOUT_1F:2","MAP_MAGMA_HIDEOUT_1F:3/MAP_MAGMA_HIDEOUT_2F_3R:0":"MAP_MAGMA_HIDEOUT_2F_3R:0/MAP_MAGMA_HIDEOUT_1F:3","MAP_MAGMA_HIDEOUT_2F_1R:0/MAP_MAGMA_HIDEOUT_2F_2R:0":"MAP_MAGMA_HIDEOUT_2F_2R:0/MAP_MAGMA_HIDEOUT_2F_1R:0","MAP_MAGMA_HIDEOUT_2F_1R:1/MAP_MAGMA_HIDEOUT_1F:1":"MAP_MAGMA_HIDEOUT_1F:1/MAP_MAGMA_HIDEOUT_2F_1R:1","MAP_MAGMA_HIDEOUT_2F_1R:2/MAP_MAGMA_HIDEOUT_3F_1R:2":"MAP_MAGMA_HIDEOUT_3F_1R:2/MAP_MAGMA_HIDEOUT_2F_1R:2","MAP_MAGMA_HIDEOUT_2F_2R:0/MAP_MAGMA_HIDEOUT_2F_1R:0":"MAP_MAGMA_HIDEOUT_2F_1R:0/MAP_MAGMA_HIDEOUT_2F_2R:0","MAP_MAGMA_HIDEOUT_2F_2R:1/MAP_MAGMA_HIDEOUT_1F:2":"MAP_MAGMA_HIDEOUT_1F:2/MAP_MAGMA_HIDEOUT_2F_2R:1","MAP_MAGMA_HIDEOUT_2F_3R:0/MAP_MAGMA_HIDEOUT_1F:3":"MAP_MAGMA_HIDEOUT_1F:3/MAP_MAGMA_HIDEOUT_2F_3R:0","MAP_MAGMA_HIDEOUT_2F_3R:1/MAP_MAGMA_HIDEOUT_3F_3R:0":"MAP_MAGMA_HIDEOUT_3F_3R:0/MAP_MAGMA_HIDEOUT_2F_3R:1","MAP_MAGMA_HIDEOUT_3F_1R:0/MAP_MAGMA_HIDEOUT_4F:0":"MAP_MAGMA_HIDEOUT_4F:0/MAP_MAGMA_HIDEOUT_3F_1R:0","MAP_MAGMA_HIDEOUT_3F_1R:1/MAP_MAGMA_HIDEOUT_3F_2R:0":"MAP_MAGMA_HIDEOUT_3F_2R:0/MAP_MAGMA_HIDEOUT_3F_1R:1","MAP_MAGMA_HIDEOUT_3F_1R:2/MAP_MAGMA_HIDEOUT_2F_1R:2":"MAP_MAGMA_HIDEOUT_2F_1R:2/MAP_MAGMA_HIDEOUT_3F_1R:2","MAP_MAGMA_HIDEOUT_3F_2R:0/MAP_MAGMA_HIDEOUT_3F_1R:1":"MAP_MAGMA_HIDEOUT_3F_1R:1/MAP_MAGMA_HIDEOUT_3F_2R:0","MAP_MAGMA_HIDEOUT_3F_3R:0/MAP_MAGMA_HIDEOUT_2F_3R:1":"MAP_MAGMA_HIDEOUT_2F_3R:1/MAP_MAGMA_HIDEOUT_3F_3R:0","MAP_MAGMA_HIDEOUT_3F_3R:1/MAP_MAGMA_HIDEOUT_4F:1":"MAP_MAGMA_HIDEOUT_4F:1/MAP_MAGMA_HIDEOUT_3F_3R:1","MAP_MAGMA_HIDEOUT_4F:0/MAP_MAGMA_HIDEOUT_3F_1R:0":"MAP_MAGMA_HIDEOUT_3F_1R:0/MAP_MAGMA_HIDEOUT_4F:0","MAP_MAGMA_HIDEOUT_4F:1/MAP_MAGMA_HIDEOUT_3F_3R:1":"MAP_MAGMA_HIDEOUT_3F_3R:1/MAP_MAGMA_HIDEOUT_4F:1","MAP_MARINE_CAVE_END:0/MAP_MARINE_CAVE_ENTRANCE:0":"MAP_MARINE_CAVE_ENTRANCE:0/MAP_MARINE_CAVE_END:0","MAP_MARINE_CAVE_ENTRANCE:0/MAP_MARINE_CAVE_END:0":"MAP_MARINE_CAVE_END:0/MAP_MARINE_CAVE_ENTRANCE:0","MAP_MAUVILLE_CITY:0/MAP_MAUVILLE_CITY_GYM:0":"MAP_MAUVILLE_CITY_GYM:0,1/MAP_MAUVILLE_CITY:0","MAP_MAUVILLE_CITY:1/MAP_MAUVILLE_CITY_POKEMON_CENTER_1F:0":"MAP_MAUVILLE_CITY_POKEMON_CENTER_1F:0,1/MAP_MAUVILLE_CITY:1","MAP_MAUVILLE_CITY:2/MAP_MAUVILLE_CITY_BIKE_SHOP:0":"MAP_MAUVILLE_CITY_BIKE_SHOP:0,1/MAP_MAUVILLE_CITY:2","MAP_MAUVILLE_CITY:3/MAP_MAUVILLE_CITY_MART:0":"MAP_MAUVILLE_CITY_MART:0,1/MAP_MAUVILLE_CITY:3","MAP_MAUVILLE_CITY:4/MAP_MAUVILLE_CITY_HOUSE1:0":"MAP_MAUVILLE_CITY_HOUSE1:0,1/MAP_MAUVILLE_CITY:4","MAP_MAUVILLE_CITY:5/MAP_MAUVILLE_CITY_GAME_CORNER:0":"MAP_MAUVILLE_CITY_GAME_CORNER:0,1/MAP_MAUVILLE_CITY:5","MAP_MAUVILLE_CITY:6/MAP_MAUVILLE_CITY_HOUSE2:0":"MAP_MAUVILLE_CITY_HOUSE2:0,1/MAP_MAUVILLE_CITY:6","MAP_MAUVILLE_CITY_BIKE_SHOP:0,1/MAP_MAUVILLE_CITY:2":"MAP_MAUVILLE_CITY:2/MAP_MAUVILLE_CITY_BIKE_SHOP:0","MAP_MAUVILLE_CITY_GAME_CORNER:0,1/MAP_MAUVILLE_CITY:5":"MAP_MAUVILLE_CITY:5/MAP_MAUVILLE_CITY_GAME_CORNER:0","MAP_MAUVILLE_CITY_GYM:0,1/MAP_MAUVILLE_CITY:0":"MAP_MAUVILLE_CITY:0/MAP_MAUVILLE_CITY_GYM:0","MAP_MAUVILLE_CITY_HOUSE1:0,1/MAP_MAUVILLE_CITY:4":"MAP_MAUVILLE_CITY:4/MAP_MAUVILLE_CITY_HOUSE1:0","MAP_MAUVILLE_CITY_HOUSE2:0,1/MAP_MAUVILLE_CITY:6":"MAP_MAUVILLE_CITY:6/MAP_MAUVILLE_CITY_HOUSE2:0","MAP_MAUVILLE_CITY_MART:0,1/MAP_MAUVILLE_CITY:3":"MAP_MAUVILLE_CITY:3/MAP_MAUVILLE_CITY_MART:0","MAP_MAUVILLE_CITY_POKEMON_CENTER_1F:0,1/MAP_MAUVILLE_CITY:1":"MAP_MAUVILLE_CITY:1/MAP_MAUVILLE_CITY_POKEMON_CENTER_1F:0","MAP_MAUVILLE_CITY_POKEMON_CENTER_1F:2/MAP_MAUVILLE_CITY_POKEMON_CENTER_2F:0":"MAP_MAUVILLE_CITY_POKEMON_CENTER_2F:0/MAP_MAUVILLE_CITY_POKEMON_CENTER_1F:2","MAP_MAUVILLE_CITY_POKEMON_CENTER_2F:0/MAP_MAUVILLE_CITY_POKEMON_CENTER_1F:2":"MAP_MAUVILLE_CITY_POKEMON_CENTER_1F:2/MAP_MAUVILLE_CITY_POKEMON_CENTER_2F:0","MAP_MAUVILLE_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_MAUVILLE_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_METEOR_FALLS_1F_1R:0/MAP_ROUTE114:0":"MAP_ROUTE114:0/MAP_METEOR_FALLS_1F_1R:0","MAP_METEOR_FALLS_1F_1R:1/MAP_ROUTE115:0":"MAP_ROUTE115:0/MAP_METEOR_FALLS_1F_1R:1","MAP_METEOR_FALLS_1F_1R:2/MAP_METEOR_FALLS_1F_2R:0":"MAP_METEOR_FALLS_1F_2R:0/MAP_METEOR_FALLS_1F_1R:2","MAP_METEOR_FALLS_1F_1R:3/MAP_METEOR_FALLS_B1F_1R:4":"MAP_METEOR_FALLS_B1F_1R:4/MAP_METEOR_FALLS_1F_1R:3","MAP_METEOR_FALLS_1F_1R:4/MAP_METEOR_FALLS_B1F_1R:5":"MAP_METEOR_FALLS_B1F_1R:5/MAP_METEOR_FALLS_1F_1R:4","MAP_METEOR_FALLS_1F_1R:5/MAP_METEOR_FALLS_STEVENS_CAVE:0":"MAP_METEOR_FALLS_STEVENS_CAVE:0/MAP_METEOR_FALLS_1F_1R:5","MAP_METEOR_FALLS_1F_2R:0/MAP_METEOR_FALLS_1F_1R:2":"MAP_METEOR_FALLS_1F_1R:2/MAP_METEOR_FALLS_1F_2R:0","MAP_METEOR_FALLS_1F_2R:1/MAP_METEOR_FALLS_B1F_1R:0":"MAP_METEOR_FALLS_B1F_1R:0/MAP_METEOR_FALLS_1F_2R:1","MAP_METEOR_FALLS_1F_2R:2/MAP_METEOR_FALLS_B1F_1R:1":"MAP_METEOR_FALLS_B1F_1R:1/MAP_METEOR_FALLS_1F_2R:2","MAP_METEOR_FALLS_1F_2R:3/MAP_METEOR_FALLS_B1F_1R:2":"MAP_METEOR_FALLS_B1F_1R:2/MAP_METEOR_FALLS_1F_2R:3","MAP_METEOR_FALLS_B1F_1R:0/MAP_METEOR_FALLS_1F_2R:1":"MAP_METEOR_FALLS_1F_2R:1/MAP_METEOR_FALLS_B1F_1R:0","MAP_METEOR_FALLS_B1F_1R:1/MAP_METEOR_FALLS_1F_2R:2":"MAP_METEOR_FALLS_1F_2R:2/MAP_METEOR_FALLS_B1F_1R:1","MAP_METEOR_FALLS_B1F_1R:2/MAP_METEOR_FALLS_1F_2R:3":"MAP_METEOR_FALLS_1F_2R:3/MAP_METEOR_FALLS_B1F_1R:2","MAP_METEOR_FALLS_B1F_1R:3/MAP_METEOR_FALLS_B1F_2R:0":"MAP_METEOR_FALLS_B1F_2R:0/MAP_METEOR_FALLS_B1F_1R:3","MAP_METEOR_FALLS_B1F_1R:4/MAP_METEOR_FALLS_1F_1R:3":"MAP_METEOR_FALLS_1F_1R:3/MAP_METEOR_FALLS_B1F_1R:4","MAP_METEOR_FALLS_B1F_1R:5/MAP_METEOR_FALLS_1F_1R:4":"MAP_METEOR_FALLS_1F_1R:4/MAP_METEOR_FALLS_B1F_1R:5","MAP_METEOR_FALLS_B1F_2R:0/MAP_METEOR_FALLS_B1F_1R:3":"MAP_METEOR_FALLS_B1F_1R:3/MAP_METEOR_FALLS_B1F_2R:0","MAP_METEOR_FALLS_STEVENS_CAVE:0/MAP_METEOR_FALLS_1F_1R:5":"MAP_METEOR_FALLS_1F_1R:5/MAP_METEOR_FALLS_STEVENS_CAVE:0","MAP_MIRAGE_TOWER_1F:0/MAP_ROUTE111:3":"MAP_ROUTE111:3/MAP_MIRAGE_TOWER_1F:0","MAP_MIRAGE_TOWER_1F:1/MAP_MIRAGE_TOWER_2F:1":"MAP_MIRAGE_TOWER_2F:1/MAP_MIRAGE_TOWER_1F:1","MAP_MIRAGE_TOWER_2F:0/MAP_MIRAGE_TOWER_3F:0":"MAP_MIRAGE_TOWER_3F:0/MAP_MIRAGE_TOWER_2F:0","MAP_MIRAGE_TOWER_2F:1/MAP_MIRAGE_TOWER_1F:1":"MAP_MIRAGE_TOWER_1F:1/MAP_MIRAGE_TOWER_2F:1","MAP_MIRAGE_TOWER_3F:0/MAP_MIRAGE_TOWER_2F:0":"MAP_MIRAGE_TOWER_2F:0/MAP_MIRAGE_TOWER_3F:0","MAP_MIRAGE_TOWER_3F:1/MAP_MIRAGE_TOWER_4F:0":"MAP_MIRAGE_TOWER_4F:0/MAP_MIRAGE_TOWER_3F:1","MAP_MIRAGE_TOWER_4F:0/MAP_MIRAGE_TOWER_3F:1":"MAP_MIRAGE_TOWER_3F:1/MAP_MIRAGE_TOWER_4F:0","MAP_MOSSDEEP_CITY:0/MAP_MOSSDEEP_CITY_HOUSE1:0":"MAP_MOSSDEEP_CITY_HOUSE1:0,1/MAP_MOSSDEEP_CITY:0","MAP_MOSSDEEP_CITY:1/MAP_MOSSDEEP_CITY_GYM:0":"MAP_MOSSDEEP_CITY_GYM:0,1/MAP_MOSSDEEP_CITY:1","MAP_MOSSDEEP_CITY:2/MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F:0":"MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F:0,1/MAP_MOSSDEEP_CITY:2","MAP_MOSSDEEP_CITY:3/MAP_MOSSDEEP_CITY_HOUSE2:0":"MAP_MOSSDEEP_CITY_HOUSE2:0,1/MAP_MOSSDEEP_CITY:3","MAP_MOSSDEEP_CITY:4/MAP_MOSSDEEP_CITY_MART:0":"MAP_MOSSDEEP_CITY_MART:0,1/MAP_MOSSDEEP_CITY:4","MAP_MOSSDEEP_CITY:5/MAP_MOSSDEEP_CITY_HOUSE3:0":"MAP_MOSSDEEP_CITY_HOUSE3:0,1/MAP_MOSSDEEP_CITY:5","MAP_MOSSDEEP_CITY:6/MAP_MOSSDEEP_CITY_STEVENS_HOUSE:0":"MAP_MOSSDEEP_CITY_STEVENS_HOUSE:0,1/MAP_MOSSDEEP_CITY:6","MAP_MOSSDEEP_CITY:7/MAP_MOSSDEEP_CITY_HOUSE4:1":"MAP_MOSSDEEP_CITY_HOUSE4:0,1/MAP_MOSSDEEP_CITY:7","MAP_MOSSDEEP_CITY:8/MAP_MOSSDEEP_CITY_SPACE_CENTER_1F:0":"MAP_MOSSDEEP_CITY_SPACE_CENTER_1F:0,1/MAP_MOSSDEEP_CITY:8","MAP_MOSSDEEP_CITY:9/MAP_MOSSDEEP_CITY_GAME_CORNER_1F:0":"MAP_MOSSDEEP_CITY_GAME_CORNER_1F:0,1/MAP_MOSSDEEP_CITY:9","MAP_MOSSDEEP_CITY_GAME_CORNER_1F:0,1/MAP_MOSSDEEP_CITY:9":"MAP_MOSSDEEP_CITY:9/MAP_MOSSDEEP_CITY_GAME_CORNER_1F:0","MAP_MOSSDEEP_CITY_GAME_CORNER_1F:2/MAP_MOSSDEEP_CITY_GAME_CORNER_B1F:0":"MAP_MOSSDEEP_CITY_GAME_CORNER_B1F:0/MAP_MOSSDEEP_CITY_GAME_CORNER_1F:2","MAP_MOSSDEEP_CITY_GAME_CORNER_B1F:0/MAP_MOSSDEEP_CITY_GAME_CORNER_1F:2":"MAP_MOSSDEEP_CITY_GAME_CORNER_1F:2/MAP_MOSSDEEP_CITY_GAME_CORNER_B1F:0","MAP_MOSSDEEP_CITY_GYM:0,1/MAP_MOSSDEEP_CITY:1":"MAP_MOSSDEEP_CITY:1/MAP_MOSSDEEP_CITY_GYM:0","MAP_MOSSDEEP_CITY_GYM:10/MAP_MOSSDEEP_CITY_GYM:11":"MAP_MOSSDEEP_CITY_GYM:11/MAP_MOSSDEEP_CITY_GYM:10","MAP_MOSSDEEP_CITY_GYM:11/MAP_MOSSDEEP_CITY_GYM:10":"MAP_MOSSDEEP_CITY_GYM:10/MAP_MOSSDEEP_CITY_GYM:11","MAP_MOSSDEEP_CITY_GYM:12/MAP_MOSSDEEP_CITY_GYM:13":"MAP_MOSSDEEP_CITY_GYM:13/MAP_MOSSDEEP_CITY_GYM:12","MAP_MOSSDEEP_CITY_GYM:13/MAP_MOSSDEEP_CITY_GYM:12":"MAP_MOSSDEEP_CITY_GYM:12/MAP_MOSSDEEP_CITY_GYM:13","MAP_MOSSDEEP_CITY_GYM:2/MAP_MOSSDEEP_CITY_GYM:3":"MAP_MOSSDEEP_CITY_GYM:3/MAP_MOSSDEEP_CITY_GYM:2","MAP_MOSSDEEP_CITY_GYM:3/MAP_MOSSDEEP_CITY_GYM:2":"MAP_MOSSDEEP_CITY_GYM:2/MAP_MOSSDEEP_CITY_GYM:3","MAP_MOSSDEEP_CITY_GYM:4/MAP_MOSSDEEP_CITY_GYM:5":"MAP_MOSSDEEP_CITY_GYM:5/MAP_MOSSDEEP_CITY_GYM:4","MAP_MOSSDEEP_CITY_GYM:5/MAP_MOSSDEEP_CITY_GYM:4":"MAP_MOSSDEEP_CITY_GYM:4/MAP_MOSSDEEP_CITY_GYM:5","MAP_MOSSDEEP_CITY_GYM:6/MAP_MOSSDEEP_CITY_GYM:7":"MAP_MOSSDEEP_CITY_GYM:7/MAP_MOSSDEEP_CITY_GYM:6","MAP_MOSSDEEP_CITY_GYM:7/MAP_MOSSDEEP_CITY_GYM:6":"MAP_MOSSDEEP_CITY_GYM:6/MAP_MOSSDEEP_CITY_GYM:7","MAP_MOSSDEEP_CITY_GYM:8/MAP_MOSSDEEP_CITY_GYM:9":"MAP_MOSSDEEP_CITY_GYM:9/MAP_MOSSDEEP_CITY_GYM:8","MAP_MOSSDEEP_CITY_GYM:9/MAP_MOSSDEEP_CITY_GYM:8":"MAP_MOSSDEEP_CITY_GYM:8/MAP_MOSSDEEP_CITY_GYM:9","MAP_MOSSDEEP_CITY_HOUSE1:0,1/MAP_MOSSDEEP_CITY:0":"MAP_MOSSDEEP_CITY:0/MAP_MOSSDEEP_CITY_HOUSE1:0","MAP_MOSSDEEP_CITY_HOUSE2:0,1/MAP_MOSSDEEP_CITY:3":"MAP_MOSSDEEP_CITY:3/MAP_MOSSDEEP_CITY_HOUSE2:0","MAP_MOSSDEEP_CITY_HOUSE3:0,1/MAP_MOSSDEEP_CITY:5":"MAP_MOSSDEEP_CITY:5/MAP_MOSSDEEP_CITY_HOUSE3:0","MAP_MOSSDEEP_CITY_HOUSE4:0,1/MAP_MOSSDEEP_CITY:7":"MAP_MOSSDEEP_CITY:7/MAP_MOSSDEEP_CITY_HOUSE4:1","MAP_MOSSDEEP_CITY_MART:0,1/MAP_MOSSDEEP_CITY:4":"MAP_MOSSDEEP_CITY:4/MAP_MOSSDEEP_CITY_MART:0","MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F:0,1/MAP_MOSSDEEP_CITY:2":"MAP_MOSSDEEP_CITY:2/MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F:0","MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F:2/MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F:0":"MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F:0/MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F:2","MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F:0/MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F:2":"MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F:2/MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F:0","MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_MOSSDEEP_CITY_SPACE_CENTER_1F:0,1/MAP_MOSSDEEP_CITY:8":"MAP_MOSSDEEP_CITY:8/MAP_MOSSDEEP_CITY_SPACE_CENTER_1F:0","MAP_MOSSDEEP_CITY_SPACE_CENTER_1F:2/MAP_MOSSDEEP_CITY_SPACE_CENTER_2F:0":"MAP_MOSSDEEP_CITY_SPACE_CENTER_2F:0/MAP_MOSSDEEP_CITY_SPACE_CENTER_1F:2","MAP_MOSSDEEP_CITY_SPACE_CENTER_2F:0/MAP_MOSSDEEP_CITY_SPACE_CENTER_1F:2":"MAP_MOSSDEEP_CITY_SPACE_CENTER_1F:2/MAP_MOSSDEEP_CITY_SPACE_CENTER_2F:0","MAP_MOSSDEEP_CITY_STEVENS_HOUSE:0,1/MAP_MOSSDEEP_CITY:6":"MAP_MOSSDEEP_CITY:6/MAP_MOSSDEEP_CITY_STEVENS_HOUSE:0","MAP_MT_CHIMNEY:0,1/MAP_MT_CHIMNEY_CABLE_CAR_STATION:0,1":"MAP_MT_CHIMNEY_CABLE_CAR_STATION:0,1/MAP_MT_CHIMNEY:0,1","MAP_MT_CHIMNEY:2,3/MAP_JAGGED_PASS:2,3":"MAP_JAGGED_PASS:2,3/MAP_MT_CHIMNEY:2,3","MAP_MT_CHIMNEY_CABLE_CAR_STATION:0,1/MAP_MT_CHIMNEY:0,1":"MAP_MT_CHIMNEY:0,1/MAP_MT_CHIMNEY_CABLE_CAR_STATION:0,1","MAP_MT_PYRE_1F:0,2/MAP_ROUTE122:0":"MAP_ROUTE122:0/MAP_MT_PYRE_1F:0","MAP_MT_PYRE_1F:1,3/MAP_MT_PYRE_EXTERIOR:0":"MAP_MT_PYRE_EXTERIOR:0/MAP_MT_PYRE_1F:1","MAP_MT_PYRE_1F:4/MAP_MT_PYRE_2F:0":"MAP_MT_PYRE_2F:0/MAP_MT_PYRE_1F:4","MAP_MT_PYRE_1F:5/MAP_MT_PYRE_2F:4":"MAP_MT_PYRE_2F:4/MAP_MT_PYRE_1F:5","MAP_MT_PYRE_2F:0/MAP_MT_PYRE_1F:4":"MAP_MT_PYRE_1F:4/MAP_MT_PYRE_2F:0","MAP_MT_PYRE_2F:1/MAP_MT_PYRE_3F:0":"MAP_MT_PYRE_3F:0/MAP_MT_PYRE_2F:1","MAP_MT_PYRE_2F:2/MAP_MT_PYRE_3F:4":"MAP_MT_PYRE_3F:4/MAP_MT_PYRE_2F:2","MAP_MT_PYRE_2F:3/MAP_MT_PYRE_3F:5":"MAP_MT_PYRE_3F:5/MAP_MT_PYRE_2F:3","MAP_MT_PYRE_2F:4/MAP_MT_PYRE_1F:5":"MAP_MT_PYRE_1F:5/MAP_MT_PYRE_2F:4","MAP_MT_PYRE_3F:0/MAP_MT_PYRE_2F:1":"MAP_MT_PYRE_2F:1/MAP_MT_PYRE_3F:0","MAP_MT_PYRE_3F:1/MAP_MT_PYRE_4F:1":"MAP_MT_PYRE_4F:1/MAP_MT_PYRE_3F:1","MAP_MT_PYRE_3F:2/MAP_MT_PYRE_4F:4":"MAP_MT_PYRE_4F:4/MAP_MT_PYRE_3F:2","MAP_MT_PYRE_3F:3/MAP_MT_PYRE_4F:5":"MAP_MT_PYRE_4F:5/MAP_MT_PYRE_3F:3","MAP_MT_PYRE_3F:4/MAP_MT_PYRE_2F:2":"MAP_MT_PYRE_2F:2/MAP_MT_PYRE_3F:4","MAP_MT_PYRE_3F:5/MAP_MT_PYRE_2F:3":"MAP_MT_PYRE_2F:3/MAP_MT_PYRE_3F:5","MAP_MT_PYRE_4F:0/MAP_MT_PYRE_5F:1":"MAP_MT_PYRE_5F:1/MAP_MT_PYRE_4F:0","MAP_MT_PYRE_4F:1/MAP_MT_PYRE_3F:1":"MAP_MT_PYRE_3F:1/MAP_MT_PYRE_4F:1","MAP_MT_PYRE_4F:2/MAP_MT_PYRE_5F:3":"MAP_MT_PYRE_5F:3/MAP_MT_PYRE_4F:2","MAP_MT_PYRE_4F:3/MAP_MT_PYRE_5F:4":"MAP_MT_PYRE_5F:4/MAP_MT_PYRE_4F:3","MAP_MT_PYRE_4F:4/MAP_MT_PYRE_3F:2":"MAP_MT_PYRE_3F:2/MAP_MT_PYRE_4F:4","MAP_MT_PYRE_4F:5/MAP_MT_PYRE_3F:3":"MAP_MT_PYRE_3F:3/MAP_MT_PYRE_4F:5","MAP_MT_PYRE_5F:0/MAP_MT_PYRE_6F:0":"MAP_MT_PYRE_6F:0/MAP_MT_PYRE_5F:0","MAP_MT_PYRE_5F:1/MAP_MT_PYRE_4F:0":"MAP_MT_PYRE_4F:0/MAP_MT_PYRE_5F:1","MAP_MT_PYRE_5F:2/MAP_MT_PYRE_6F:1":"MAP_MT_PYRE_6F:1/MAP_MT_PYRE_5F:2","MAP_MT_PYRE_5F:3/MAP_MT_PYRE_4F:2":"MAP_MT_PYRE_4F:2/MAP_MT_PYRE_5F:3","MAP_MT_PYRE_5F:4/MAP_MT_PYRE_4F:3":"MAP_MT_PYRE_4F:3/MAP_MT_PYRE_5F:4","MAP_MT_PYRE_6F:0/MAP_MT_PYRE_5F:0":"MAP_MT_PYRE_5F:0/MAP_MT_PYRE_6F:0","MAP_MT_PYRE_6F:1/MAP_MT_PYRE_5F:2":"MAP_MT_PYRE_5F:2/MAP_MT_PYRE_6F:1","MAP_MT_PYRE_EXTERIOR:0/MAP_MT_PYRE_1F:1":"MAP_MT_PYRE_1F:1,3/MAP_MT_PYRE_EXTERIOR:0","MAP_MT_PYRE_EXTERIOR:1,2/MAP_MT_PYRE_SUMMIT:1":"MAP_MT_PYRE_SUMMIT:0,1,2/MAP_MT_PYRE_EXTERIOR:1","MAP_MT_PYRE_SUMMIT:0,1,2/MAP_MT_PYRE_EXTERIOR:1":"MAP_MT_PYRE_EXTERIOR:1,2/MAP_MT_PYRE_SUMMIT:1","MAP_NAVEL_ROCK_B1F:0/MAP_NAVEL_ROCK_ENTRANCE:0":"MAP_NAVEL_ROCK_ENTRANCE:0/MAP_NAVEL_ROCK_B1F:0","MAP_NAVEL_ROCK_B1F:1/MAP_NAVEL_ROCK_FORK:1":"MAP_NAVEL_ROCK_FORK:1/MAP_NAVEL_ROCK_B1F:1","MAP_NAVEL_ROCK_BOTTOM:0/MAP_NAVEL_ROCK_DOWN11:0":"MAP_NAVEL_ROCK_DOWN11:0/MAP_NAVEL_ROCK_BOTTOM:0","MAP_NAVEL_ROCK_DOWN01:0/MAP_NAVEL_ROCK_FORK:2":"MAP_NAVEL_ROCK_FORK:2/MAP_NAVEL_ROCK_DOWN01:0","MAP_NAVEL_ROCK_DOWN01:1/MAP_NAVEL_ROCK_DOWN02:0":"MAP_NAVEL_ROCK_DOWN02:0/MAP_NAVEL_ROCK_DOWN01:1","MAP_NAVEL_ROCK_DOWN02:0/MAP_NAVEL_ROCK_DOWN01:1":"MAP_NAVEL_ROCK_DOWN01:1/MAP_NAVEL_ROCK_DOWN02:0","MAP_NAVEL_ROCK_DOWN02:1/MAP_NAVEL_ROCK_DOWN03:0":"MAP_NAVEL_ROCK_DOWN03:0/MAP_NAVEL_ROCK_DOWN02:1","MAP_NAVEL_ROCK_DOWN03:0/MAP_NAVEL_ROCK_DOWN02:1":"MAP_NAVEL_ROCK_DOWN02:1/MAP_NAVEL_ROCK_DOWN03:0","MAP_NAVEL_ROCK_DOWN03:1/MAP_NAVEL_ROCK_DOWN04:0":"MAP_NAVEL_ROCK_DOWN04:0/MAP_NAVEL_ROCK_DOWN03:1","MAP_NAVEL_ROCK_DOWN04:0/MAP_NAVEL_ROCK_DOWN03:1":"MAP_NAVEL_ROCK_DOWN03:1/MAP_NAVEL_ROCK_DOWN04:0","MAP_NAVEL_ROCK_DOWN04:1/MAP_NAVEL_ROCK_DOWN05:0":"MAP_NAVEL_ROCK_DOWN05:0/MAP_NAVEL_ROCK_DOWN04:1","MAP_NAVEL_ROCK_DOWN05:0/MAP_NAVEL_ROCK_DOWN04:1":"MAP_NAVEL_ROCK_DOWN04:1/MAP_NAVEL_ROCK_DOWN05:0","MAP_NAVEL_ROCK_DOWN05:1/MAP_NAVEL_ROCK_DOWN06:0":"MAP_NAVEL_ROCK_DOWN06:0/MAP_NAVEL_ROCK_DOWN05:1","MAP_NAVEL_ROCK_DOWN06:0/MAP_NAVEL_ROCK_DOWN05:1":"MAP_NAVEL_ROCK_DOWN05:1/MAP_NAVEL_ROCK_DOWN06:0","MAP_NAVEL_ROCK_DOWN06:1/MAP_NAVEL_ROCK_DOWN07:0":"MAP_NAVEL_ROCK_DOWN07:0/MAP_NAVEL_ROCK_DOWN06:1","MAP_NAVEL_ROCK_DOWN07:0/MAP_NAVEL_ROCK_DOWN06:1":"MAP_NAVEL_ROCK_DOWN06:1/MAP_NAVEL_ROCK_DOWN07:0","MAP_NAVEL_ROCK_DOWN07:1/MAP_NAVEL_ROCK_DOWN08:0":"MAP_NAVEL_ROCK_DOWN08:0/MAP_NAVEL_ROCK_DOWN07:1","MAP_NAVEL_ROCK_DOWN08:0/MAP_NAVEL_ROCK_DOWN07:1":"MAP_NAVEL_ROCK_DOWN07:1/MAP_NAVEL_ROCK_DOWN08:0","MAP_NAVEL_ROCK_DOWN08:1/MAP_NAVEL_ROCK_DOWN09:0":"MAP_NAVEL_ROCK_DOWN09:0/MAP_NAVEL_ROCK_DOWN08:1","MAP_NAVEL_ROCK_DOWN09:0/MAP_NAVEL_ROCK_DOWN08:1":"MAP_NAVEL_ROCK_DOWN08:1/MAP_NAVEL_ROCK_DOWN09:0","MAP_NAVEL_ROCK_DOWN09:1/MAP_NAVEL_ROCK_DOWN10:0":"MAP_NAVEL_ROCK_DOWN10:0/MAP_NAVEL_ROCK_DOWN09:1","MAP_NAVEL_ROCK_DOWN10:0/MAP_NAVEL_ROCK_DOWN09:1":"MAP_NAVEL_ROCK_DOWN09:1/MAP_NAVEL_ROCK_DOWN10:0","MAP_NAVEL_ROCK_DOWN10:1/MAP_NAVEL_ROCK_DOWN11:1":"MAP_NAVEL_ROCK_DOWN11:1/MAP_NAVEL_ROCK_DOWN10:1","MAP_NAVEL_ROCK_DOWN11:0/MAP_NAVEL_ROCK_BOTTOM:0":"MAP_NAVEL_ROCK_BOTTOM:0/MAP_NAVEL_ROCK_DOWN11:0","MAP_NAVEL_ROCK_DOWN11:1/MAP_NAVEL_ROCK_DOWN10:1":"MAP_NAVEL_ROCK_DOWN10:1/MAP_NAVEL_ROCK_DOWN11:1","MAP_NAVEL_ROCK_ENTRANCE:0/MAP_NAVEL_ROCK_B1F:0":"MAP_NAVEL_ROCK_B1F:0/MAP_NAVEL_ROCK_ENTRANCE:0","MAP_NAVEL_ROCK_ENTRANCE:1/MAP_NAVEL_ROCK_EXTERIOR:1":"MAP_NAVEL_ROCK_EXTERIOR:1/MAP_NAVEL_ROCK_ENTRANCE:1","MAP_NAVEL_ROCK_EXTERIOR:0/MAP_NAVEL_ROCK_HARBOR:0":"MAP_NAVEL_ROCK_HARBOR:0/MAP_NAVEL_ROCK_EXTERIOR:0","MAP_NAVEL_ROCK_EXTERIOR:1/MAP_NAVEL_ROCK_ENTRANCE:1":"MAP_NAVEL_ROCK_ENTRANCE:1/MAP_NAVEL_ROCK_EXTERIOR:1","MAP_NAVEL_ROCK_FORK:0/MAP_NAVEL_ROCK_UP1:0":"MAP_NAVEL_ROCK_UP1:0/MAP_NAVEL_ROCK_FORK:0","MAP_NAVEL_ROCK_FORK:1/MAP_NAVEL_ROCK_B1F:1":"MAP_NAVEL_ROCK_B1F:1/MAP_NAVEL_ROCK_FORK:1","MAP_NAVEL_ROCK_FORK:2/MAP_NAVEL_ROCK_DOWN01:0":"MAP_NAVEL_ROCK_DOWN01:0/MAP_NAVEL_ROCK_FORK:2","MAP_NAVEL_ROCK_HARBOR:0/MAP_NAVEL_ROCK_EXTERIOR:0":"MAP_NAVEL_ROCK_EXTERIOR:0/MAP_NAVEL_ROCK_HARBOR:0","MAP_NAVEL_ROCK_TOP:0/MAP_NAVEL_ROCK_UP4:1":"MAP_NAVEL_ROCK_UP4:1/MAP_NAVEL_ROCK_TOP:0","MAP_NAVEL_ROCK_UP1:0/MAP_NAVEL_ROCK_FORK:0":"MAP_NAVEL_ROCK_FORK:0/MAP_NAVEL_ROCK_UP1:0","MAP_NAVEL_ROCK_UP1:1/MAP_NAVEL_ROCK_UP2:0":"MAP_NAVEL_ROCK_UP2:0/MAP_NAVEL_ROCK_UP1:1","MAP_NAVEL_ROCK_UP2:0/MAP_NAVEL_ROCK_UP1:1":"MAP_NAVEL_ROCK_UP1:1/MAP_NAVEL_ROCK_UP2:0","MAP_NAVEL_ROCK_UP2:1/MAP_NAVEL_ROCK_UP3:0":"MAP_NAVEL_ROCK_UP3:0/MAP_NAVEL_ROCK_UP2:1","MAP_NAVEL_ROCK_UP3:0/MAP_NAVEL_ROCK_UP2:1":"MAP_NAVEL_ROCK_UP2:1/MAP_NAVEL_ROCK_UP3:0","MAP_NAVEL_ROCK_UP3:1/MAP_NAVEL_ROCK_UP4:0":"MAP_NAVEL_ROCK_UP4:0/MAP_NAVEL_ROCK_UP3:1","MAP_NAVEL_ROCK_UP4:0/MAP_NAVEL_ROCK_UP3:1":"MAP_NAVEL_ROCK_UP3:1/MAP_NAVEL_ROCK_UP4:0","MAP_NAVEL_ROCK_UP4:1/MAP_NAVEL_ROCK_TOP:0":"MAP_NAVEL_ROCK_TOP:0/MAP_NAVEL_ROCK_UP4:1","MAP_NEW_MAUVILLE_ENTRANCE:0/MAP_ROUTE110:0":"MAP_ROUTE110:0/MAP_NEW_MAUVILLE_ENTRANCE:0","MAP_NEW_MAUVILLE_ENTRANCE:1/MAP_NEW_MAUVILLE_INSIDE:0":"MAP_NEW_MAUVILLE_INSIDE:0/MAP_NEW_MAUVILLE_ENTRANCE:1","MAP_NEW_MAUVILLE_INSIDE:0/MAP_NEW_MAUVILLE_ENTRANCE:1":"MAP_NEW_MAUVILLE_ENTRANCE:1/MAP_NEW_MAUVILLE_INSIDE:0","MAP_OLDALE_TOWN:0/MAP_OLDALE_TOWN_HOUSE1:0":"MAP_OLDALE_TOWN_HOUSE1:0,1/MAP_OLDALE_TOWN:0","MAP_OLDALE_TOWN:1/MAP_OLDALE_TOWN_HOUSE2:0":"MAP_OLDALE_TOWN_HOUSE2:0,1/MAP_OLDALE_TOWN:1","MAP_OLDALE_TOWN:2/MAP_OLDALE_TOWN_POKEMON_CENTER_1F:0":"MAP_OLDALE_TOWN_POKEMON_CENTER_1F:0,1/MAP_OLDALE_TOWN:2","MAP_OLDALE_TOWN:3/MAP_OLDALE_TOWN_MART:0":"MAP_OLDALE_TOWN_MART:0,1/MAP_OLDALE_TOWN:3","MAP_OLDALE_TOWN_HOUSE1:0,1/MAP_OLDALE_TOWN:0":"MAP_OLDALE_TOWN:0/MAP_OLDALE_TOWN_HOUSE1:0","MAP_OLDALE_TOWN_HOUSE2:0,1/MAP_OLDALE_TOWN:1":"MAP_OLDALE_TOWN:1/MAP_OLDALE_TOWN_HOUSE2:0","MAP_OLDALE_TOWN_MART:0,1/MAP_OLDALE_TOWN:3":"MAP_OLDALE_TOWN:3/MAP_OLDALE_TOWN_MART:0","MAP_OLDALE_TOWN_POKEMON_CENTER_1F:0,1/MAP_OLDALE_TOWN:2":"MAP_OLDALE_TOWN:2/MAP_OLDALE_TOWN_POKEMON_CENTER_1F:0","MAP_OLDALE_TOWN_POKEMON_CENTER_1F:2/MAP_OLDALE_TOWN_POKEMON_CENTER_2F:0":"MAP_OLDALE_TOWN_POKEMON_CENTER_2F:0/MAP_OLDALE_TOWN_POKEMON_CENTER_1F:2","MAP_OLDALE_TOWN_POKEMON_CENTER_2F:0/MAP_OLDALE_TOWN_POKEMON_CENTER_1F:2":"MAP_OLDALE_TOWN_POKEMON_CENTER_1F:2/MAP_OLDALE_TOWN_POKEMON_CENTER_2F:0","MAP_OLDALE_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_OLDALE_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_PACIFIDLOG_TOWN:0/MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F:0":"MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F:0,1/MAP_PACIFIDLOG_TOWN:0","MAP_PACIFIDLOG_TOWN:1/MAP_PACIFIDLOG_TOWN_HOUSE1:0":"MAP_PACIFIDLOG_TOWN_HOUSE1:0,1/MAP_PACIFIDLOG_TOWN:1","MAP_PACIFIDLOG_TOWN:2/MAP_PACIFIDLOG_TOWN_HOUSE2:0":"MAP_PACIFIDLOG_TOWN_HOUSE2:0,1/MAP_PACIFIDLOG_TOWN:2","MAP_PACIFIDLOG_TOWN:3/MAP_PACIFIDLOG_TOWN_HOUSE3:0":"MAP_PACIFIDLOG_TOWN_HOUSE3:0,1/MAP_PACIFIDLOG_TOWN:3","MAP_PACIFIDLOG_TOWN:4/MAP_PACIFIDLOG_TOWN_HOUSE4:0":"MAP_PACIFIDLOG_TOWN_HOUSE4:0,1/MAP_PACIFIDLOG_TOWN:4","MAP_PACIFIDLOG_TOWN:5/MAP_PACIFIDLOG_TOWN_HOUSE5:0":"MAP_PACIFIDLOG_TOWN_HOUSE5:0,1/MAP_PACIFIDLOG_TOWN:5","MAP_PACIFIDLOG_TOWN_HOUSE1:0,1/MAP_PACIFIDLOG_TOWN:1":"MAP_PACIFIDLOG_TOWN:1/MAP_PACIFIDLOG_TOWN_HOUSE1:0","MAP_PACIFIDLOG_TOWN_HOUSE2:0,1/MAP_PACIFIDLOG_TOWN:2":"MAP_PACIFIDLOG_TOWN:2/MAP_PACIFIDLOG_TOWN_HOUSE2:0","MAP_PACIFIDLOG_TOWN_HOUSE3:0,1/MAP_PACIFIDLOG_TOWN:3":"MAP_PACIFIDLOG_TOWN:3/MAP_PACIFIDLOG_TOWN_HOUSE3:0","MAP_PACIFIDLOG_TOWN_HOUSE4:0,1/MAP_PACIFIDLOG_TOWN:4":"MAP_PACIFIDLOG_TOWN:4/MAP_PACIFIDLOG_TOWN_HOUSE4:0","MAP_PACIFIDLOG_TOWN_HOUSE5:0,1/MAP_PACIFIDLOG_TOWN:5":"MAP_PACIFIDLOG_TOWN:5/MAP_PACIFIDLOG_TOWN_HOUSE5:0","MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F:0,1/MAP_PACIFIDLOG_TOWN:0":"MAP_PACIFIDLOG_TOWN:0/MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F:0","MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F:2/MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F:0":"MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F:0/MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F:2","MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F:0/MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F:2":"MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F:2/MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F:0","MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_PETALBURG_CITY:0/MAP_PETALBURG_CITY_HOUSE1:0":"MAP_PETALBURG_CITY_HOUSE1:0,1/MAP_PETALBURG_CITY:0","MAP_PETALBURG_CITY:1/MAP_PETALBURG_CITY_WALLYS_HOUSE:0":"MAP_PETALBURG_CITY_WALLYS_HOUSE:0,1/MAP_PETALBURG_CITY:1","MAP_PETALBURG_CITY:2/MAP_PETALBURG_CITY_GYM:0":"MAP_PETALBURG_CITY_GYM:0,1/MAP_PETALBURG_CITY:2","MAP_PETALBURG_CITY:3/MAP_PETALBURG_CITY_POKEMON_CENTER_1F:0":"MAP_PETALBURG_CITY_POKEMON_CENTER_1F:0,1/MAP_PETALBURG_CITY:3","MAP_PETALBURG_CITY:4/MAP_PETALBURG_CITY_HOUSE2:0":"MAP_PETALBURG_CITY_HOUSE2:0,1/MAP_PETALBURG_CITY:4","MAP_PETALBURG_CITY:5/MAP_PETALBURG_CITY_MART:0":"MAP_PETALBURG_CITY_MART:0,1/MAP_PETALBURG_CITY:5","MAP_PETALBURG_CITY_GYM:0,1/MAP_PETALBURG_CITY:2":"MAP_PETALBURG_CITY:2/MAP_PETALBURG_CITY_GYM:0","MAP_PETALBURG_CITY_GYM:10,11/MAP_PETALBURG_CITY_GYM:8":"MAP_PETALBURG_CITY_GYM:8/MAP_PETALBURG_CITY_GYM:10","MAP_PETALBURG_CITY_GYM:12,13/MAP_PETALBURG_CITY_GYM:9":"MAP_PETALBURG_CITY_GYM:9/MAP_PETALBURG_CITY_GYM:12","MAP_PETALBURG_CITY_GYM:14/MAP_PETALBURG_CITY_GYM:16":"MAP_PETALBURG_CITY_GYM:16,17/MAP_PETALBURG_CITY_GYM:14","MAP_PETALBURG_CITY_GYM:15/MAP_PETALBURG_CITY_GYM:18":"MAP_PETALBURG_CITY_GYM:18,19/MAP_PETALBURG_CITY_GYM:15","MAP_PETALBURG_CITY_GYM:16,17/MAP_PETALBURG_CITY_GYM:14":"MAP_PETALBURG_CITY_GYM:14/MAP_PETALBURG_CITY_GYM:16","MAP_PETALBURG_CITY_GYM:18,19/MAP_PETALBURG_CITY_GYM:15":"MAP_PETALBURG_CITY_GYM:15/MAP_PETALBURG_CITY_GYM:18","MAP_PETALBURG_CITY_GYM:2/MAP_PETALBURG_CITY_GYM:3":"MAP_PETALBURG_CITY_GYM:3,4/MAP_PETALBURG_CITY_GYM:2","MAP_PETALBURG_CITY_GYM:20/MAP_PETALBURG_CITY_GYM:24":"MAP_PETALBURG_CITY_GYM:24,25/MAP_PETALBURG_CITY_GYM:20","MAP_PETALBURG_CITY_GYM:21/MAP_PETALBURG_CITY_GYM:26":"MAP_PETALBURG_CITY_GYM:26,27/MAP_PETALBURG_CITY_GYM:21","MAP_PETALBURG_CITY_GYM:22/MAP_PETALBURG_CITY_GYM:28":"MAP_PETALBURG_CITY_GYM:28,29/MAP_PETALBURG_CITY_GYM:22","MAP_PETALBURG_CITY_GYM:23/MAP_PETALBURG_CITY_GYM:30":"MAP_PETALBURG_CITY_GYM:30,31/MAP_PETALBURG_CITY_GYM:23","MAP_PETALBURG_CITY_GYM:24,25/MAP_PETALBURG_CITY_GYM:20":"MAP_PETALBURG_CITY_GYM:20/MAP_PETALBURG_CITY_GYM:24","MAP_PETALBURG_CITY_GYM:26,27/MAP_PETALBURG_CITY_GYM:21":"MAP_PETALBURG_CITY_GYM:21/MAP_PETALBURG_CITY_GYM:26","MAP_PETALBURG_CITY_GYM:28,29/MAP_PETALBURG_CITY_GYM:22":"MAP_PETALBURG_CITY_GYM:22/MAP_PETALBURG_CITY_GYM:28","MAP_PETALBURG_CITY_GYM:3,4/MAP_PETALBURG_CITY_GYM:2":"MAP_PETALBURG_CITY_GYM:2/MAP_PETALBURG_CITY_GYM:3","MAP_PETALBURG_CITY_GYM:30,31/MAP_PETALBURG_CITY_GYM:23":"MAP_PETALBURG_CITY_GYM:23/MAP_PETALBURG_CITY_GYM:30","MAP_PETALBURG_CITY_GYM:32/MAP_PETALBURG_CITY_GYM:34":"MAP_PETALBURG_CITY_GYM:34,35/MAP_PETALBURG_CITY_GYM:32","MAP_PETALBURG_CITY_GYM:33/MAP_PETALBURG_CITY_GYM:36":"MAP_PETALBURG_CITY_GYM:36,37/MAP_PETALBURG_CITY_GYM:33","MAP_PETALBURG_CITY_GYM:34,35/MAP_PETALBURG_CITY_GYM:32":"MAP_PETALBURG_CITY_GYM:32/MAP_PETALBURG_CITY_GYM:34","MAP_PETALBURG_CITY_GYM:36,37/MAP_PETALBURG_CITY_GYM:33":"MAP_PETALBURG_CITY_GYM:33/MAP_PETALBURG_CITY_GYM:36","MAP_PETALBURG_CITY_GYM:5/MAP_PETALBURG_CITY_GYM:6":"MAP_PETALBURG_CITY_GYM:6,7/MAP_PETALBURG_CITY_GYM:5","MAP_PETALBURG_CITY_GYM:6,7/MAP_PETALBURG_CITY_GYM:5":"MAP_PETALBURG_CITY_GYM:5/MAP_PETALBURG_CITY_GYM:6","MAP_PETALBURG_CITY_GYM:8/MAP_PETALBURG_CITY_GYM:10":"MAP_PETALBURG_CITY_GYM:10,11/MAP_PETALBURG_CITY_GYM:8","MAP_PETALBURG_CITY_GYM:9/MAP_PETALBURG_CITY_GYM:12":"MAP_PETALBURG_CITY_GYM:12,13/MAP_PETALBURG_CITY_GYM:9","MAP_PETALBURG_CITY_HOUSE1:0,1/MAP_PETALBURG_CITY:0":"MAP_PETALBURG_CITY:0/MAP_PETALBURG_CITY_HOUSE1:0","MAP_PETALBURG_CITY_HOUSE2:0,1/MAP_PETALBURG_CITY:4":"MAP_PETALBURG_CITY:4/MAP_PETALBURG_CITY_HOUSE2:0","MAP_PETALBURG_CITY_MART:0,1/MAP_PETALBURG_CITY:5":"MAP_PETALBURG_CITY:5/MAP_PETALBURG_CITY_MART:0","MAP_PETALBURG_CITY_POKEMON_CENTER_1F:0,1/MAP_PETALBURG_CITY:3":"MAP_PETALBURG_CITY:3/MAP_PETALBURG_CITY_POKEMON_CENTER_1F:0","MAP_PETALBURG_CITY_POKEMON_CENTER_1F:2/MAP_PETALBURG_CITY_POKEMON_CENTER_2F:0":"MAP_PETALBURG_CITY_POKEMON_CENTER_2F:0/MAP_PETALBURG_CITY_POKEMON_CENTER_1F:2","MAP_PETALBURG_CITY_POKEMON_CENTER_2F:0/MAP_PETALBURG_CITY_POKEMON_CENTER_1F:2":"MAP_PETALBURG_CITY_POKEMON_CENTER_1F:2/MAP_PETALBURG_CITY_POKEMON_CENTER_2F:0","MAP_PETALBURG_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_PETALBURG_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_PETALBURG_CITY_WALLYS_HOUSE:0,1/MAP_PETALBURG_CITY:1":"MAP_PETALBURG_CITY:1/MAP_PETALBURG_CITY_WALLYS_HOUSE:0","MAP_PETALBURG_WOODS:0,1/MAP_ROUTE104:2,3":"MAP_ROUTE104:2,3/MAP_PETALBURG_WOODS:0,1","MAP_PETALBURG_WOODS:2,3/MAP_ROUTE104:4,5":"MAP_ROUTE104:4,5/MAP_PETALBURG_WOODS:2,3","MAP_PETALBURG_WOODS:4,5/MAP_ROUTE104:6,7":"MAP_ROUTE104:6,7/MAP_PETALBURG_WOODS:4,5","MAP_RECORD_CORNER:0,1,2,3/MAP_DYNAMIC:-1!":"","MAP_ROUTE103:0/MAP_ALTERING_CAVE:0":"MAP_ALTERING_CAVE:0/MAP_ROUTE103:0","MAP_ROUTE104:0/MAP_ROUTE104_MR_BRINEYS_HOUSE:0":"MAP_ROUTE104_MR_BRINEYS_HOUSE:0,1/MAP_ROUTE104:0","MAP_ROUTE104:1/MAP_ROUTE104_PRETTY_PETAL_FLOWER_SHOP:0":"MAP_ROUTE104_PRETTY_PETAL_FLOWER_SHOP:0,1/MAP_ROUTE104:1","MAP_ROUTE104:2,3/MAP_PETALBURG_WOODS:0,1":"MAP_PETALBURG_WOODS:0,1/MAP_ROUTE104:2,3","MAP_ROUTE104:4,5/MAP_PETALBURG_WOODS:2,3":"MAP_PETALBURG_WOODS:2,3/MAP_ROUTE104:4,5","MAP_ROUTE104:6,7/MAP_PETALBURG_WOODS:4,5":"MAP_PETALBURG_WOODS:4,5/MAP_ROUTE104:6,7","MAP_ROUTE104_MR_BRINEYS_HOUSE:0,1/MAP_ROUTE104:0":"MAP_ROUTE104:0/MAP_ROUTE104_MR_BRINEYS_HOUSE:0","MAP_ROUTE104_PRETTY_PETAL_FLOWER_SHOP:0,1/MAP_ROUTE104:1":"MAP_ROUTE104:1/MAP_ROUTE104_PRETTY_PETAL_FLOWER_SHOP:0","MAP_ROUTE105:0/MAP_ISLAND_CAVE:0":"MAP_ISLAND_CAVE:0/MAP_ROUTE105:0","MAP_ROUTE106:0/MAP_GRANITE_CAVE_1F:0":"MAP_GRANITE_CAVE_1F:0/MAP_ROUTE106:0","MAP_ROUTE108:0/MAP_ABANDONED_SHIP_DECK:0":"MAP_ABANDONED_SHIP_DECK:0,1/MAP_ROUTE108:0","MAP_ROUTE109:0/MAP_ROUTE109_SEASHORE_HOUSE:0":"MAP_ROUTE109_SEASHORE_HOUSE:0,1/MAP_ROUTE109:0","MAP_ROUTE109_SEASHORE_HOUSE:0,1/MAP_ROUTE109:0":"MAP_ROUTE109:0/MAP_ROUTE109_SEASHORE_HOUSE:0","MAP_ROUTE110:0/MAP_NEW_MAUVILLE_ENTRANCE:0":"MAP_NEW_MAUVILLE_ENTRANCE:0/MAP_ROUTE110:0","MAP_ROUTE110:1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:0":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:0,1/MAP_ROUTE110:1","MAP_ROUTE110:2/MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE:0":"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE:0,1/MAP_ROUTE110:2","MAP_ROUTE110:3/MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE:2":"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE:2,3/MAP_ROUTE110:3","MAP_ROUTE110:4/MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE:0":"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE:0,1/MAP_ROUTE110:4","MAP_ROUTE110:5/MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE:2":"MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE:2,3/MAP_ROUTE110:5","MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE:0,1/MAP_ROUTE110:4":"MAP_ROUTE110:4/MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE:0","MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE:2,3/MAP_ROUTE110:5":"MAP_ROUTE110:5/MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE:2","MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE:0,1/MAP_ROUTE110:2":"MAP_ROUTE110:2/MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE:0","MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE:2,3/MAP_ROUTE110:3":"MAP_ROUTE110:3/MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE:2","MAP_ROUTE110_TRICK_HOUSE_CORRIDOR:0,1/MAP_ROUTE110_TRICK_HOUSE_END:1":"MAP_ROUTE110_TRICK_HOUSE_END:1/MAP_ROUTE110_TRICK_HOUSE_CORRIDOR:0","MAP_ROUTE110_TRICK_HOUSE_CORRIDOR:2,3/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2/MAP_ROUTE110_TRICK_HOUSE_END:0","MAP_ROUTE110_TRICK_HOUSE_END:1/MAP_ROUTE110_TRICK_HOUSE_CORRIDOR:0":"MAP_ROUTE110_TRICK_HOUSE_CORRIDOR:0,1/MAP_ROUTE110_TRICK_HOUSE_END:1","MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:0,1/MAP_ROUTE110:1":"MAP_ROUTE110:1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:0","MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2","MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2/MAP_ROUTE110_TRICK_HOUSE_END:0":"MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2","MAP_ROUTE110_TRICK_HOUSE_PUZZLE2:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_PUZZLE2:2/MAP_ROUTE110_TRICK_HOUSE_END:0!":"MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2","MAP_ROUTE110_TRICK_HOUSE_PUZZLE3:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_PUZZLE3:2/MAP_ROUTE110_TRICK_HOUSE_END:0!":"MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2","MAP_ROUTE110_TRICK_HOUSE_PUZZLE4:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_PUZZLE4:2/MAP_ROUTE110_TRICK_HOUSE_END:0!":"MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2","MAP_ROUTE110_TRICK_HOUSE_PUZZLE5:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_PUZZLE5:2/MAP_ROUTE110_TRICK_HOUSE_END:0!":"MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2","MAP_ROUTE110_TRICK_HOUSE_PUZZLE6:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_PUZZLE6:2/MAP_ROUTE110_TRICK_HOUSE_END:0!":"MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:10/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:9":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:9/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:10","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:11/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:12":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:12/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:11","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:12/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:11":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:11/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:12","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:2/MAP_ROUTE110_TRICK_HOUSE_END:0!":"MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:3/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:4":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:4/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:3","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:4/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:3":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:3/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:4","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:5/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:6":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:6/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:5","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:6/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:5":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:5/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:6","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:7/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:8":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:8/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:7","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:8/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:7":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:7/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:8","MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:9/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:10":"MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:10/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:9","MAP_ROUTE110_TRICK_HOUSE_PUZZLE8:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!":"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0","MAP_ROUTE110_TRICK_HOUSE_PUZZLE8:2/MAP_ROUTE110_TRICK_HOUSE_END:0!":"MAP_ROUTE110_TRICK_HOUSE_END:0/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2","MAP_ROUTE111:0/MAP_ROUTE111_WINSTRATE_FAMILYS_HOUSE:0":"MAP_ROUTE111_WINSTRATE_FAMILYS_HOUSE:0,1/MAP_ROUTE111:0","MAP_ROUTE111:1/MAP_DESERT_RUINS:0":"MAP_DESERT_RUINS:0/MAP_ROUTE111:1","MAP_ROUTE111:2/MAP_ROUTE111_OLD_LADYS_REST_STOP:0":"MAP_ROUTE111_OLD_LADYS_REST_STOP:0,1/MAP_ROUTE111:2","MAP_ROUTE111:3/MAP_MIRAGE_TOWER_1F:0":"MAP_MIRAGE_TOWER_1F:0/MAP_ROUTE111:3","MAP_ROUTE111:4/MAP_TRAINER_HILL_ENTRANCE:0":"MAP_TRAINER_HILL_ENTRANCE:0,1/MAP_ROUTE111:4","MAP_ROUTE111_OLD_LADYS_REST_STOP:0,1/MAP_ROUTE111:2":"MAP_ROUTE111:2/MAP_ROUTE111_OLD_LADYS_REST_STOP:0","MAP_ROUTE111_WINSTRATE_FAMILYS_HOUSE:0,1/MAP_ROUTE111:0":"MAP_ROUTE111:0/MAP_ROUTE111_WINSTRATE_FAMILYS_HOUSE:0","MAP_ROUTE112:0,1/MAP_ROUTE112_CABLE_CAR_STATION:0,1":"MAP_ROUTE112_CABLE_CAR_STATION:0,1/MAP_ROUTE112:0,1","MAP_ROUTE112:2,3/MAP_JAGGED_PASS:0,1":"MAP_JAGGED_PASS:0,1/MAP_ROUTE112:2,3","MAP_ROUTE112:4/MAP_FIERY_PATH:0":"MAP_FIERY_PATH:0/MAP_ROUTE112:4","MAP_ROUTE112:5/MAP_FIERY_PATH:1":"MAP_FIERY_PATH:1/MAP_ROUTE112:5","MAP_ROUTE112_CABLE_CAR_STATION:0,1/MAP_ROUTE112:0,1":"MAP_ROUTE112:0,1/MAP_ROUTE112_CABLE_CAR_STATION:0,1","MAP_ROUTE113:0/MAP_ROUTE113_GLASS_WORKSHOP:0":"MAP_ROUTE113_GLASS_WORKSHOP:0,1/MAP_ROUTE113:0","MAP_ROUTE113:1/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE113:2/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE113_GLASS_WORKSHOP:0,1/MAP_ROUTE113:0":"MAP_ROUTE113:0/MAP_ROUTE113_GLASS_WORKSHOP:0","MAP_ROUTE114:0/MAP_METEOR_FALLS_1F_1R:0":"MAP_METEOR_FALLS_1F_1R:0/MAP_ROUTE114:0","MAP_ROUTE114:1/MAP_ROUTE114_FOSSIL_MANIACS_HOUSE:0":"MAP_ROUTE114_FOSSIL_MANIACS_HOUSE:0,1/MAP_ROUTE114:1","MAP_ROUTE114:2/MAP_ROUTE114_LANETTES_HOUSE:0":"MAP_ROUTE114_LANETTES_HOUSE:0,1/MAP_ROUTE114:2","MAP_ROUTE114:3/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE114:4/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE114_FOSSIL_MANIACS_HOUSE:0,1/MAP_ROUTE114:1":"MAP_ROUTE114:1/MAP_ROUTE114_FOSSIL_MANIACS_HOUSE:0","MAP_ROUTE114_FOSSIL_MANIACS_HOUSE:2/MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:0":"MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:0,1/MAP_ROUTE114_FOSSIL_MANIACS_HOUSE:2","MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:0,1/MAP_ROUTE114_FOSSIL_MANIACS_HOUSE:2":"MAP_ROUTE114_FOSSIL_MANIACS_HOUSE:2/MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:0","MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:2/MAP_DESERT_UNDERPASS:0":"MAP_DESERT_UNDERPASS:0/MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:2","MAP_ROUTE114_LANETTES_HOUSE:0,1/MAP_ROUTE114:2":"MAP_ROUTE114:2/MAP_ROUTE114_LANETTES_HOUSE:0","MAP_ROUTE115:0/MAP_METEOR_FALLS_1F_1R:1":"MAP_METEOR_FALLS_1F_1R:1/MAP_ROUTE115:0","MAP_ROUTE115:1/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE115:2/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE116:0/MAP_RUSTURF_TUNNEL:0":"MAP_RUSTURF_TUNNEL:0/MAP_ROUTE116:0","MAP_ROUTE116:1/MAP_ROUTE116_TUNNELERS_REST_HOUSE:0":"MAP_ROUTE116_TUNNELERS_REST_HOUSE:0,1/MAP_ROUTE116:1","MAP_ROUTE116:2/MAP_RUSTURF_TUNNEL:2":"MAP_RUSTURF_TUNNEL:2/MAP_ROUTE116:2","MAP_ROUTE116:3/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE116:4/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE116_TUNNELERS_REST_HOUSE:0,1/MAP_ROUTE116:1":"MAP_ROUTE116:1/MAP_ROUTE116_TUNNELERS_REST_HOUSE:0","MAP_ROUTE117:0/MAP_ROUTE117_POKEMON_DAY_CARE:0":"MAP_ROUTE117_POKEMON_DAY_CARE:0,1/MAP_ROUTE117:0","MAP_ROUTE117_POKEMON_DAY_CARE:0,1/MAP_ROUTE117:0":"MAP_ROUTE117:0/MAP_ROUTE117_POKEMON_DAY_CARE:0","MAP_ROUTE118:0/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE118:1/MAP_TERRA_CAVE_ENTRANCE:0!":"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!","MAP_ROUTE119:0/MAP_ROUTE119_WEATHER_INSTITUTE_1F:0":"MAP_ROUTE119_WEATHER_INSTITUTE_1F:0,1/MAP_ROUTE119:0","MAP_ROUTE119:1/MAP_ROUTE119_HOUSE:0":"MAP_ROUTE119_HOUSE:0,1/MAP_ROUTE119:1","MAP_ROUTE119_HOUSE:0,1/MAP_ROUTE119:1":"MAP_ROUTE119:1/MAP_ROUTE119_HOUSE:0","MAP_ROUTE119_WEATHER_INSTITUTE_1F:0,1/MAP_ROUTE119:0":"MAP_ROUTE119:0/MAP_ROUTE119_WEATHER_INSTITUTE_1F:0","MAP_ROUTE119_WEATHER_INSTITUTE_1F:2/MAP_ROUTE119_WEATHER_INSTITUTE_2F:0":"MAP_ROUTE119_WEATHER_INSTITUTE_2F:0/MAP_ROUTE119_WEATHER_INSTITUTE_1F:2","MAP_ROUTE119_WEATHER_INSTITUTE_2F:0/MAP_ROUTE119_WEATHER_INSTITUTE_1F:2":"MAP_ROUTE119_WEATHER_INSTITUTE_1F:2/MAP_ROUTE119_WEATHER_INSTITUTE_2F:0","MAP_ROUTE120:0/MAP_ANCIENT_TOMB:0":"MAP_ANCIENT_TOMB:0/MAP_ROUTE120:0","MAP_ROUTE120:1/MAP_SCORCHED_SLAB:0":"MAP_SCORCHED_SLAB:0/MAP_ROUTE120:1","MAP_ROUTE121:0/MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:2":"MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:2,3/MAP_ROUTE121:0","MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:0,1/MAP_SAFARI_ZONE_SOUTH:0":"MAP_SAFARI_ZONE_SOUTH:0/MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:0","MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:2,3/MAP_ROUTE121:0":"MAP_ROUTE121:0/MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:2","MAP_ROUTE122:0/MAP_MT_PYRE_1F:0":"MAP_MT_PYRE_1F:0,2/MAP_ROUTE122:0","MAP_ROUTE123:0/MAP_ROUTE123_BERRY_MASTERS_HOUSE:0":"MAP_ROUTE123_BERRY_MASTERS_HOUSE:0,1/MAP_ROUTE123:0","MAP_ROUTE123_BERRY_MASTERS_HOUSE:0,1/MAP_ROUTE123:0":"MAP_ROUTE123:0/MAP_ROUTE123_BERRY_MASTERS_HOUSE:0","MAP_ROUTE124:0/MAP_ROUTE124_DIVING_TREASURE_HUNTERS_HOUSE:0":"MAP_ROUTE124_DIVING_TREASURE_HUNTERS_HOUSE:0,1/MAP_ROUTE124:0","MAP_ROUTE124_DIVING_TREASURE_HUNTERS_HOUSE:0,1/MAP_ROUTE124:0":"MAP_ROUTE124:0/MAP_ROUTE124_DIVING_TREASURE_HUNTERS_HOUSE:0","MAP_ROUTE125:0/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:0":"MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:0/MAP_ROUTE125:0","MAP_ROUTE131:0/MAP_SKY_PILLAR_ENTRANCE:0":"MAP_SKY_PILLAR_ENTRANCE:0/MAP_ROUTE131:0","MAP_RUSTBORO_CITY:0/MAP_RUSTBORO_CITY_GYM:0":"MAP_RUSTBORO_CITY_GYM:0,1/MAP_RUSTBORO_CITY:0","MAP_RUSTBORO_CITY:1/MAP_RUSTBORO_CITY_FLAT1_1F:0":"MAP_RUSTBORO_CITY_FLAT1_1F:0,1/MAP_RUSTBORO_CITY:1","MAP_RUSTBORO_CITY:10/MAP_RUSTBORO_CITY_FLAT2_1F:0":"MAP_RUSTBORO_CITY_FLAT2_1F:0,1/MAP_RUSTBORO_CITY:10","MAP_RUSTBORO_CITY:11/MAP_RUSTBORO_CITY_HOUSE3:0":"MAP_RUSTBORO_CITY_HOUSE3:0,1/MAP_RUSTBORO_CITY:11","MAP_RUSTBORO_CITY:2/MAP_RUSTBORO_CITY_MART:0":"MAP_RUSTBORO_CITY_MART:0,1/MAP_RUSTBORO_CITY:2","MAP_RUSTBORO_CITY:3/MAP_RUSTBORO_CITY_POKEMON_CENTER_1F:0":"MAP_RUSTBORO_CITY_POKEMON_CENTER_1F:0,1/MAP_RUSTBORO_CITY:3","MAP_RUSTBORO_CITY:4/MAP_RUSTBORO_CITY_POKEMON_SCHOOL:0":"MAP_RUSTBORO_CITY_POKEMON_SCHOOL:0,1/MAP_RUSTBORO_CITY:4","MAP_RUSTBORO_CITY:5,6/MAP_RUSTBORO_CITY_DEVON_CORP_1F:0,1":"MAP_RUSTBORO_CITY_DEVON_CORP_1F:0,1/MAP_RUSTBORO_CITY:5,6","MAP_RUSTBORO_CITY:7/MAP_RUSTBORO_CITY_HOUSE1:0":"MAP_RUSTBORO_CITY_HOUSE1:0,1/MAP_RUSTBORO_CITY:7","MAP_RUSTBORO_CITY:8/MAP_RUSTBORO_CITY_CUTTERS_HOUSE:0":"MAP_RUSTBORO_CITY_CUTTERS_HOUSE:0,1/MAP_RUSTBORO_CITY:8","MAP_RUSTBORO_CITY:9/MAP_RUSTBORO_CITY_HOUSE2:0":"MAP_RUSTBORO_CITY_HOUSE2:0,1/MAP_RUSTBORO_CITY:9","MAP_RUSTBORO_CITY_CUTTERS_HOUSE:0,1/MAP_RUSTBORO_CITY:8":"MAP_RUSTBORO_CITY:8/MAP_RUSTBORO_CITY_CUTTERS_HOUSE:0","MAP_RUSTBORO_CITY_DEVON_CORP_1F:0,1/MAP_RUSTBORO_CITY:5,6":"MAP_RUSTBORO_CITY:5,6/MAP_RUSTBORO_CITY_DEVON_CORP_1F:0,1","MAP_RUSTBORO_CITY_DEVON_CORP_1F:2/MAP_RUSTBORO_CITY_DEVON_CORP_2F:0":"MAP_RUSTBORO_CITY_DEVON_CORP_2F:0/MAP_RUSTBORO_CITY_DEVON_CORP_1F:2","MAP_RUSTBORO_CITY_DEVON_CORP_2F:0/MAP_RUSTBORO_CITY_DEVON_CORP_1F:2":"MAP_RUSTBORO_CITY_DEVON_CORP_1F:2/MAP_RUSTBORO_CITY_DEVON_CORP_2F:0","MAP_RUSTBORO_CITY_DEVON_CORP_2F:1/MAP_RUSTBORO_CITY_DEVON_CORP_3F:0":"MAP_RUSTBORO_CITY_DEVON_CORP_3F:0/MAP_RUSTBORO_CITY_DEVON_CORP_2F:1","MAP_RUSTBORO_CITY_DEVON_CORP_3F:0/MAP_RUSTBORO_CITY_DEVON_CORP_2F:1":"MAP_RUSTBORO_CITY_DEVON_CORP_2F:1/MAP_RUSTBORO_CITY_DEVON_CORP_3F:0","MAP_RUSTBORO_CITY_FLAT1_1F:0,1/MAP_RUSTBORO_CITY:1":"MAP_RUSTBORO_CITY:1/MAP_RUSTBORO_CITY_FLAT1_1F:0","MAP_RUSTBORO_CITY_FLAT1_1F:2/MAP_RUSTBORO_CITY_FLAT1_2F:0":"MAP_RUSTBORO_CITY_FLAT1_2F:0/MAP_RUSTBORO_CITY_FLAT1_1F:2","MAP_RUSTBORO_CITY_FLAT1_2F:0/MAP_RUSTBORO_CITY_FLAT1_1F:2":"MAP_RUSTBORO_CITY_FLAT1_1F:2/MAP_RUSTBORO_CITY_FLAT1_2F:0","MAP_RUSTBORO_CITY_FLAT2_1F:0,1/MAP_RUSTBORO_CITY:10":"MAP_RUSTBORO_CITY:10/MAP_RUSTBORO_CITY_FLAT2_1F:0","MAP_RUSTBORO_CITY_FLAT2_1F:2/MAP_RUSTBORO_CITY_FLAT2_2F:0":"MAP_RUSTBORO_CITY_FLAT2_2F:0/MAP_RUSTBORO_CITY_FLAT2_1F:2","MAP_RUSTBORO_CITY_FLAT2_2F:0/MAP_RUSTBORO_CITY_FLAT2_1F:2":"MAP_RUSTBORO_CITY_FLAT2_1F:2/MAP_RUSTBORO_CITY_FLAT2_2F:0","MAP_RUSTBORO_CITY_FLAT2_2F:1/MAP_RUSTBORO_CITY_FLAT2_3F:0":"MAP_RUSTBORO_CITY_FLAT2_3F:0/MAP_RUSTBORO_CITY_FLAT2_2F:1","MAP_RUSTBORO_CITY_FLAT2_3F:0/MAP_RUSTBORO_CITY_FLAT2_2F:1":"MAP_RUSTBORO_CITY_FLAT2_2F:1/MAP_RUSTBORO_CITY_FLAT2_3F:0","MAP_RUSTBORO_CITY_GYM:0,1/MAP_RUSTBORO_CITY:0":"MAP_RUSTBORO_CITY:0/MAP_RUSTBORO_CITY_GYM:0","MAP_RUSTBORO_CITY_HOUSE1:0,1/MAP_RUSTBORO_CITY:7":"MAP_RUSTBORO_CITY:7/MAP_RUSTBORO_CITY_HOUSE1:0","MAP_RUSTBORO_CITY_HOUSE2:0,1/MAP_RUSTBORO_CITY:9":"MAP_RUSTBORO_CITY:9/MAP_RUSTBORO_CITY_HOUSE2:0","MAP_RUSTBORO_CITY_HOUSE3:0,1/MAP_RUSTBORO_CITY:11":"MAP_RUSTBORO_CITY:11/MAP_RUSTBORO_CITY_HOUSE3:0","MAP_RUSTBORO_CITY_MART:0,1/MAP_RUSTBORO_CITY:2":"MAP_RUSTBORO_CITY:2/MAP_RUSTBORO_CITY_MART:0","MAP_RUSTBORO_CITY_POKEMON_CENTER_1F:0,1/MAP_RUSTBORO_CITY:3":"MAP_RUSTBORO_CITY:3/MAP_RUSTBORO_CITY_POKEMON_CENTER_1F:0","MAP_RUSTBORO_CITY_POKEMON_CENTER_1F:2/MAP_RUSTBORO_CITY_POKEMON_CENTER_2F:0":"MAP_RUSTBORO_CITY_POKEMON_CENTER_2F:0/MAP_RUSTBORO_CITY_POKEMON_CENTER_1F:2","MAP_RUSTBORO_CITY_POKEMON_CENTER_2F:0/MAP_RUSTBORO_CITY_POKEMON_CENTER_1F:2":"MAP_RUSTBORO_CITY_POKEMON_CENTER_1F:2/MAP_RUSTBORO_CITY_POKEMON_CENTER_2F:0","MAP_RUSTBORO_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_RUSTBORO_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_RUSTBORO_CITY_POKEMON_SCHOOL:0,1/MAP_RUSTBORO_CITY:4":"MAP_RUSTBORO_CITY:4/MAP_RUSTBORO_CITY_POKEMON_SCHOOL:0","MAP_RUSTURF_TUNNEL:0/MAP_ROUTE116:0":"MAP_ROUTE116:0/MAP_RUSTURF_TUNNEL:0","MAP_RUSTURF_TUNNEL:1/MAP_VERDANTURF_TOWN:4":"MAP_VERDANTURF_TOWN:4/MAP_RUSTURF_TUNNEL:1","MAP_RUSTURF_TUNNEL:2/MAP_ROUTE116:2":"MAP_ROUTE116:2/MAP_RUSTURF_TUNNEL:2","MAP_SAFARI_ZONE_REST_HOUSE:0,1/MAP_SAFARI_ZONE_SOUTHWEST:0":"MAP_SAFARI_ZONE_SOUTHWEST:0/MAP_SAFARI_ZONE_REST_HOUSE:0","MAP_SAFARI_ZONE_SOUTH:0/MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:0":"MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:0,1/MAP_SAFARI_ZONE_SOUTH:0","MAP_SAFARI_ZONE_SOUTHWEST:0/MAP_SAFARI_ZONE_REST_HOUSE:0":"MAP_SAFARI_ZONE_REST_HOUSE:0,1/MAP_SAFARI_ZONE_SOUTHWEST:0","MAP_SCORCHED_SLAB:0/MAP_ROUTE120:1":"MAP_ROUTE120:1/MAP_SCORCHED_SLAB:0","MAP_SEAFLOOR_CAVERN_ENTRANCE:0/MAP_UNDERWATER_ROUTE128:0!":"MAP_UNDERWATER_ROUTE128:0/MAP_UNDERWATER_SEAFLOOR_CAVERN:0","MAP_SEAFLOOR_CAVERN_ENTRANCE:1/MAP_SEAFLOOR_CAVERN_ROOM1:0":"MAP_SEAFLOOR_CAVERN_ROOM1:0/MAP_SEAFLOOR_CAVERN_ENTRANCE:1","MAP_SEAFLOOR_CAVERN_ROOM1:0/MAP_SEAFLOOR_CAVERN_ENTRANCE:1":"MAP_SEAFLOOR_CAVERN_ENTRANCE:1/MAP_SEAFLOOR_CAVERN_ROOM1:0","MAP_SEAFLOOR_CAVERN_ROOM1:1/MAP_SEAFLOOR_CAVERN_ROOM5:0":"MAP_SEAFLOOR_CAVERN_ROOM5:0/MAP_SEAFLOOR_CAVERN_ROOM1:1","MAP_SEAFLOOR_CAVERN_ROOM1:2/MAP_SEAFLOOR_CAVERN_ROOM2:0":"MAP_SEAFLOOR_CAVERN_ROOM2:0/MAP_SEAFLOOR_CAVERN_ROOM1:2","MAP_SEAFLOOR_CAVERN_ROOM2:0/MAP_SEAFLOOR_CAVERN_ROOM1:2":"MAP_SEAFLOOR_CAVERN_ROOM1:2/MAP_SEAFLOOR_CAVERN_ROOM2:0","MAP_SEAFLOOR_CAVERN_ROOM2:1/MAP_SEAFLOOR_CAVERN_ROOM4:0":"MAP_SEAFLOOR_CAVERN_ROOM4:0/MAP_SEAFLOOR_CAVERN_ROOM2:1","MAP_SEAFLOOR_CAVERN_ROOM2:2/MAP_SEAFLOOR_CAVERN_ROOM6:0":"MAP_SEAFLOOR_CAVERN_ROOM6:0/MAP_SEAFLOOR_CAVERN_ROOM2:2","MAP_SEAFLOOR_CAVERN_ROOM2:3/MAP_SEAFLOOR_CAVERN_ROOM7:0":"MAP_SEAFLOOR_CAVERN_ROOM7:0/MAP_SEAFLOOR_CAVERN_ROOM2:3","MAP_SEAFLOOR_CAVERN_ROOM3:0/MAP_SEAFLOOR_CAVERN_ROOM8:1":"MAP_SEAFLOOR_CAVERN_ROOM8:1/MAP_SEAFLOOR_CAVERN_ROOM3:0","MAP_SEAFLOOR_CAVERN_ROOM3:1/MAP_SEAFLOOR_CAVERN_ROOM7:1":"MAP_SEAFLOOR_CAVERN_ROOM7:1/MAP_SEAFLOOR_CAVERN_ROOM3:1","MAP_SEAFLOOR_CAVERN_ROOM3:2/MAP_SEAFLOOR_CAVERN_ROOM6:1":"MAP_SEAFLOOR_CAVERN_ROOM6:1/MAP_SEAFLOOR_CAVERN_ROOM3:2","MAP_SEAFLOOR_CAVERN_ROOM4:0/MAP_SEAFLOOR_CAVERN_ROOM2:1":"MAP_SEAFLOOR_CAVERN_ROOM2:1/MAP_SEAFLOOR_CAVERN_ROOM4:0","MAP_SEAFLOOR_CAVERN_ROOM4:1/MAP_SEAFLOOR_CAVERN_ROOM5:1":"MAP_SEAFLOOR_CAVERN_ROOM5:1/MAP_SEAFLOOR_CAVERN_ROOM4:1","MAP_SEAFLOOR_CAVERN_ROOM4:2/MAP_SEAFLOOR_CAVERN_ROOM5:2":"MAP_SEAFLOOR_CAVERN_ROOM5:2/MAP_SEAFLOOR_CAVERN_ROOM4:2","MAP_SEAFLOOR_CAVERN_ROOM4:3/MAP_SEAFLOOR_CAVERN_ENTRANCE:1!":"MAP_SEAFLOOR_CAVERN_ENTRANCE:1/MAP_SEAFLOOR_CAVERN_ROOM1:0","MAP_SEAFLOOR_CAVERN_ROOM5:0/MAP_SEAFLOOR_CAVERN_ROOM1:1":"MAP_SEAFLOOR_CAVERN_ROOM1:1/MAP_SEAFLOOR_CAVERN_ROOM5:0","MAP_SEAFLOOR_CAVERN_ROOM5:1/MAP_SEAFLOOR_CAVERN_ROOM4:1":"MAP_SEAFLOOR_CAVERN_ROOM4:1/MAP_SEAFLOOR_CAVERN_ROOM5:1","MAP_SEAFLOOR_CAVERN_ROOM5:2/MAP_SEAFLOOR_CAVERN_ROOM4:2":"MAP_SEAFLOOR_CAVERN_ROOM4:2/MAP_SEAFLOOR_CAVERN_ROOM5:2","MAP_SEAFLOOR_CAVERN_ROOM6:0/MAP_SEAFLOOR_CAVERN_ROOM2:2":"MAP_SEAFLOOR_CAVERN_ROOM2:2/MAP_SEAFLOOR_CAVERN_ROOM6:0","MAP_SEAFLOOR_CAVERN_ROOM6:1/MAP_SEAFLOOR_CAVERN_ROOM3:2":"MAP_SEAFLOOR_CAVERN_ROOM3:2/MAP_SEAFLOOR_CAVERN_ROOM6:1","MAP_SEAFLOOR_CAVERN_ROOM6:2/MAP_SEAFLOOR_CAVERN_ENTRANCE:1!":"MAP_SEAFLOOR_CAVERN_ENTRANCE:1/MAP_SEAFLOOR_CAVERN_ROOM1:0","MAP_SEAFLOOR_CAVERN_ROOM7:0/MAP_SEAFLOOR_CAVERN_ROOM2:3":"MAP_SEAFLOOR_CAVERN_ROOM2:3/MAP_SEAFLOOR_CAVERN_ROOM7:0","MAP_SEAFLOOR_CAVERN_ROOM7:1/MAP_SEAFLOOR_CAVERN_ROOM3:1":"MAP_SEAFLOOR_CAVERN_ROOM3:1/MAP_SEAFLOOR_CAVERN_ROOM7:1","MAP_SEAFLOOR_CAVERN_ROOM8:0/MAP_SEAFLOOR_CAVERN_ROOM9:0":"MAP_SEAFLOOR_CAVERN_ROOM9:0/MAP_SEAFLOOR_CAVERN_ROOM8:0","MAP_SEAFLOOR_CAVERN_ROOM8:1/MAP_SEAFLOOR_CAVERN_ROOM3:0":"MAP_SEAFLOOR_CAVERN_ROOM3:0/MAP_SEAFLOOR_CAVERN_ROOM8:1","MAP_SEAFLOOR_CAVERN_ROOM9:0/MAP_SEAFLOOR_CAVERN_ROOM8:0":"MAP_SEAFLOOR_CAVERN_ROOM8:0/MAP_SEAFLOOR_CAVERN_ROOM9:0","MAP_SEALED_CHAMBER_INNER_ROOM:0/MAP_SEALED_CHAMBER_OUTER_ROOM:0":"MAP_SEALED_CHAMBER_OUTER_ROOM:0/MAP_SEALED_CHAMBER_INNER_ROOM:0","MAP_SEALED_CHAMBER_OUTER_ROOM:0/MAP_SEALED_CHAMBER_INNER_ROOM:0":"MAP_SEALED_CHAMBER_INNER_ROOM:0/MAP_SEALED_CHAMBER_OUTER_ROOM:0","MAP_SECRET_BASE_BLUE_CAVE1:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_BLUE_CAVE2:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_BLUE_CAVE3:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_BLUE_CAVE4:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_BROWN_CAVE1:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_BROWN_CAVE2:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_BROWN_CAVE3:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_BROWN_CAVE4:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_RED_CAVE1:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_RED_CAVE2:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_RED_CAVE3:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_RED_CAVE4:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_SHRUB1:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_SHRUB2:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_SHRUB3:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_SHRUB4:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_TREE1:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_TREE2:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_TREE3:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_TREE4:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_YELLOW_CAVE1:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_YELLOW_CAVE2:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_YELLOW_CAVE3:0/MAP_DYNAMIC:-2!":"","MAP_SECRET_BASE_YELLOW_CAVE4:0/MAP_DYNAMIC:-2!":"","MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:0/MAP_ROUTE125:0":"MAP_ROUTE125:0/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:0","MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:1/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:0":"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:0/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:1","MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:2/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:6":"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:6/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:2","MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:3/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:7":"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:7/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:3","MAP_SHOAL_CAVE_LOW_TIDE_ICE_ROOM:0/MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:3":"MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:3/MAP_SHOAL_CAVE_LOW_TIDE_ICE_ROOM:0","MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:0/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:1":"MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:1/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:0","MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:1/MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM:0":"MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM:0/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:1","MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:2/MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM:1":"MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM:1/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:2","MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:3/MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:0":"MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:0/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:3","MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:4/MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:1":"MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:1/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:4","MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:5/MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:2":"MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:2/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:5","MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:6/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:2":"MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:2/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:6","MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:7/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:3":"MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:3/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:7","MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:0/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:3":"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:3/MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:0","MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:1/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:4":"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:4/MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:1","MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:2/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:5":"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:5/MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:2","MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:3/MAP_SHOAL_CAVE_LOW_TIDE_ICE_ROOM:0":"MAP_SHOAL_CAVE_LOW_TIDE_ICE_ROOM:0/MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM:3","MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM:0/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:1":"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:1/MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM:0","MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM:1/MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:2":"MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM:2/MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM:1","MAP_SKY_PILLAR_1F:0,1/MAP_SKY_PILLAR_OUTSIDE:1":"MAP_SKY_PILLAR_OUTSIDE:1/MAP_SKY_PILLAR_1F:0","MAP_SKY_PILLAR_1F:2/MAP_SKY_PILLAR_2F:0":"MAP_SKY_PILLAR_2F:0/MAP_SKY_PILLAR_1F:2","MAP_SKY_PILLAR_2F:0/MAP_SKY_PILLAR_1F:2":"MAP_SKY_PILLAR_1F:2/MAP_SKY_PILLAR_2F:0","MAP_SKY_PILLAR_2F:1/MAP_SKY_PILLAR_3F:0":"MAP_SKY_PILLAR_3F:0/MAP_SKY_PILLAR_2F:1","MAP_SKY_PILLAR_3F:0/MAP_SKY_PILLAR_2F:1":"MAP_SKY_PILLAR_2F:1/MAP_SKY_PILLAR_3F:0","MAP_SKY_PILLAR_3F:1/MAP_SKY_PILLAR_4F:0":"MAP_SKY_PILLAR_4F:0/MAP_SKY_PILLAR_3F:1","MAP_SKY_PILLAR_3F:2/MAP_SKY_PILLAR_4F:1":"MAP_SKY_PILLAR_4F:1/MAP_SKY_PILLAR_3F:2","MAP_SKY_PILLAR_4F:0/MAP_SKY_PILLAR_3F:1":"MAP_SKY_PILLAR_3F:1/MAP_SKY_PILLAR_4F:0","MAP_SKY_PILLAR_4F:1/MAP_SKY_PILLAR_3F:2":"MAP_SKY_PILLAR_3F:2/MAP_SKY_PILLAR_4F:1","MAP_SKY_PILLAR_4F:2/MAP_SKY_PILLAR_5F:0":"MAP_SKY_PILLAR_5F:0/MAP_SKY_PILLAR_4F:2","MAP_SKY_PILLAR_5F:0/MAP_SKY_PILLAR_4F:2":"MAP_SKY_PILLAR_4F:2/MAP_SKY_PILLAR_5F:0","MAP_SKY_PILLAR_5F:1/MAP_SKY_PILLAR_TOP:0":"MAP_SKY_PILLAR_TOP:0/MAP_SKY_PILLAR_5F:1","MAP_SKY_PILLAR_ENTRANCE:0/MAP_ROUTE131:0":"MAP_ROUTE131:0/MAP_SKY_PILLAR_ENTRANCE:0","MAP_SKY_PILLAR_ENTRANCE:1/MAP_SKY_PILLAR_OUTSIDE:0":"MAP_SKY_PILLAR_OUTSIDE:0/MAP_SKY_PILLAR_ENTRANCE:1","MAP_SKY_PILLAR_OUTSIDE:0/MAP_SKY_PILLAR_ENTRANCE:1":"MAP_SKY_PILLAR_ENTRANCE:1/MAP_SKY_PILLAR_OUTSIDE:0","MAP_SKY_PILLAR_OUTSIDE:1/MAP_SKY_PILLAR_1F:0":"MAP_SKY_PILLAR_1F:0,1/MAP_SKY_PILLAR_OUTSIDE:1","MAP_SKY_PILLAR_TOP:0/MAP_SKY_PILLAR_5F:1":"MAP_SKY_PILLAR_5F:1/MAP_SKY_PILLAR_TOP:0","MAP_SLATEPORT_CITY:0/MAP_SLATEPORT_CITY_POKEMON_CENTER_1F:0":"MAP_SLATEPORT_CITY_POKEMON_CENTER_1F:0,1/MAP_SLATEPORT_CITY:0","MAP_SLATEPORT_CITY:1/MAP_SLATEPORT_CITY_MART:0":"MAP_SLATEPORT_CITY_MART:0,1/MAP_SLATEPORT_CITY:1","MAP_SLATEPORT_CITY:10/MAP_SLATEPORT_CITY_HOUSE:0":"MAP_SLATEPORT_CITY_HOUSE:0,1/MAP_SLATEPORT_CITY:10","MAP_SLATEPORT_CITY:2/MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F:0":"MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F:0,1/MAP_SLATEPORT_CITY:2","MAP_SLATEPORT_CITY:3/MAP_SLATEPORT_CITY_BATTLE_TENT_LOBBY:0":"MAP_SLATEPORT_CITY_BATTLE_TENT_LOBBY:0,1/MAP_SLATEPORT_CITY:3","MAP_SLATEPORT_CITY:4/MAP_SLATEPORT_CITY_POKEMON_FAN_CLUB:0":"MAP_SLATEPORT_CITY_POKEMON_FAN_CLUB:0,1/MAP_SLATEPORT_CITY:4","MAP_SLATEPORT_CITY:5,7/MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F:0,1":"MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F:0,1/MAP_SLATEPORT_CITY:5,7","MAP_SLATEPORT_CITY:6/MAP_SLATEPORT_CITY_NAME_RATERS_HOUSE:0":"MAP_SLATEPORT_CITY_NAME_RATERS_HOUSE:0,1/MAP_SLATEPORT_CITY:6","MAP_SLATEPORT_CITY:8/MAP_SLATEPORT_CITY_HARBOR:0":"MAP_SLATEPORT_CITY_HARBOR:0,1/MAP_SLATEPORT_CITY:8","MAP_SLATEPORT_CITY:9/MAP_SLATEPORT_CITY_HARBOR:2":"MAP_SLATEPORT_CITY_HARBOR:2,3/MAP_SLATEPORT_CITY:9","MAP_SLATEPORT_CITY_BATTLE_TENT_LOBBY:0,1/MAP_SLATEPORT_CITY:3":"MAP_SLATEPORT_CITY:3/MAP_SLATEPORT_CITY_BATTLE_TENT_LOBBY:0","MAP_SLATEPORT_CITY_HARBOR:0,1/MAP_SLATEPORT_CITY:8":"MAP_SLATEPORT_CITY:8/MAP_SLATEPORT_CITY_HARBOR:0","MAP_SLATEPORT_CITY_HARBOR:2,3/MAP_SLATEPORT_CITY:9":"MAP_SLATEPORT_CITY:9/MAP_SLATEPORT_CITY_HARBOR:2","MAP_SLATEPORT_CITY_HOUSE:0,1/MAP_SLATEPORT_CITY:10":"MAP_SLATEPORT_CITY:10/MAP_SLATEPORT_CITY_HOUSE:0","MAP_SLATEPORT_CITY_MART:0,1/MAP_SLATEPORT_CITY:1":"MAP_SLATEPORT_CITY:1/MAP_SLATEPORT_CITY_MART:0","MAP_SLATEPORT_CITY_NAME_RATERS_HOUSE:0,1/MAP_SLATEPORT_CITY:6":"MAP_SLATEPORT_CITY:6/MAP_SLATEPORT_CITY_NAME_RATERS_HOUSE:0","MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F:0,1/MAP_SLATEPORT_CITY:5,7":"MAP_SLATEPORT_CITY:5,7/MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F:0,1","MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F:2/MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_2F:0":"MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_2F:0/MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F:2","MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_2F:0/MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F:2":"MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F:2/MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_2F:0","MAP_SLATEPORT_CITY_POKEMON_CENTER_1F:0,1/MAP_SLATEPORT_CITY:0":"MAP_SLATEPORT_CITY:0/MAP_SLATEPORT_CITY_POKEMON_CENTER_1F:0","MAP_SLATEPORT_CITY_POKEMON_CENTER_1F:2/MAP_SLATEPORT_CITY_POKEMON_CENTER_2F:0":"MAP_SLATEPORT_CITY_POKEMON_CENTER_2F:0/MAP_SLATEPORT_CITY_POKEMON_CENTER_1F:2","MAP_SLATEPORT_CITY_POKEMON_CENTER_2F:0/MAP_SLATEPORT_CITY_POKEMON_CENTER_1F:2":"MAP_SLATEPORT_CITY_POKEMON_CENTER_1F:2/MAP_SLATEPORT_CITY_POKEMON_CENTER_2F:0","MAP_SLATEPORT_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_SLATEPORT_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_SLATEPORT_CITY_POKEMON_FAN_CLUB:0,1/MAP_SLATEPORT_CITY:4":"MAP_SLATEPORT_CITY:4/MAP_SLATEPORT_CITY_POKEMON_FAN_CLUB:0","MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F:0,1/MAP_SLATEPORT_CITY:2":"MAP_SLATEPORT_CITY:2/MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F:0","MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F:2/MAP_SLATEPORT_CITY_STERNS_SHIPYARD_2F:0":"MAP_SLATEPORT_CITY_STERNS_SHIPYARD_2F:0/MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F:2","MAP_SLATEPORT_CITY_STERNS_SHIPYARD_2F:0/MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F:2":"MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F:2/MAP_SLATEPORT_CITY_STERNS_SHIPYARD_2F:0","MAP_SOOTOPOLIS_CITY:0/MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F:0":"MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F:0,1/MAP_SOOTOPOLIS_CITY:0","MAP_SOOTOPOLIS_CITY:1/MAP_SOOTOPOLIS_CITY_MART:0":"MAP_SOOTOPOLIS_CITY_MART:0,1/MAP_SOOTOPOLIS_CITY:1","MAP_SOOTOPOLIS_CITY:10/MAP_SOOTOPOLIS_CITY_HOUSE7:0":"MAP_SOOTOPOLIS_CITY_HOUSE7:0,1/MAP_SOOTOPOLIS_CITY:10","MAP_SOOTOPOLIS_CITY:11/MAP_SOOTOPOLIS_CITY_LOTAD_AND_SEEDOT_HOUSE:0":"MAP_SOOTOPOLIS_CITY_LOTAD_AND_SEEDOT_HOUSE:0,1/MAP_SOOTOPOLIS_CITY:11","MAP_SOOTOPOLIS_CITY:12/MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F:0":"MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F:0,1/MAP_SOOTOPOLIS_CITY:12","MAP_SOOTOPOLIS_CITY:2/MAP_SOOTOPOLIS_CITY_GYM_1F:0":"MAP_SOOTOPOLIS_CITY_GYM_1F:0,1/MAP_SOOTOPOLIS_CITY:2","MAP_SOOTOPOLIS_CITY:3/MAP_CAVE_OF_ORIGIN_ENTRANCE:0":"MAP_CAVE_OF_ORIGIN_ENTRANCE:0/MAP_SOOTOPOLIS_CITY:3","MAP_SOOTOPOLIS_CITY:4/MAP_SOOTOPOLIS_CITY_HOUSE1:0":"MAP_SOOTOPOLIS_CITY_HOUSE1:0,1/MAP_SOOTOPOLIS_CITY:4","MAP_SOOTOPOLIS_CITY:5/MAP_SOOTOPOLIS_CITY_HOUSE2:0":"MAP_SOOTOPOLIS_CITY_HOUSE2:0,1/MAP_SOOTOPOLIS_CITY:5","MAP_SOOTOPOLIS_CITY:6/MAP_SOOTOPOLIS_CITY_HOUSE3:0":"MAP_SOOTOPOLIS_CITY_HOUSE3:0,1/MAP_SOOTOPOLIS_CITY:6","MAP_SOOTOPOLIS_CITY:7/MAP_SOOTOPOLIS_CITY_HOUSE4:0":"MAP_SOOTOPOLIS_CITY_HOUSE4:0,1/MAP_SOOTOPOLIS_CITY:7","MAP_SOOTOPOLIS_CITY:8/MAP_SOOTOPOLIS_CITY_HOUSE5:0":"MAP_SOOTOPOLIS_CITY_HOUSE5:0,1/MAP_SOOTOPOLIS_CITY:8","MAP_SOOTOPOLIS_CITY:9/MAP_SOOTOPOLIS_CITY_HOUSE6:0":"MAP_SOOTOPOLIS_CITY_HOUSE6:0,1/MAP_SOOTOPOLIS_CITY:9","MAP_SOOTOPOLIS_CITY_GYM_1F:0,1/MAP_SOOTOPOLIS_CITY:2":"MAP_SOOTOPOLIS_CITY:2/MAP_SOOTOPOLIS_CITY_GYM_1F:0","MAP_SOOTOPOLIS_CITY_GYM_1F:2/MAP_SOOTOPOLIS_CITY_GYM_B1F:0":"MAP_SOOTOPOLIS_CITY_GYM_B1F:0/MAP_SOOTOPOLIS_CITY_GYM_1F:2","MAP_SOOTOPOLIS_CITY_GYM_B1F:0/MAP_SOOTOPOLIS_CITY_GYM_1F:2":"MAP_SOOTOPOLIS_CITY_GYM_1F:2/MAP_SOOTOPOLIS_CITY_GYM_B1F:0","MAP_SOOTOPOLIS_CITY_HOUSE1:0,1/MAP_SOOTOPOLIS_CITY:4":"MAP_SOOTOPOLIS_CITY:4/MAP_SOOTOPOLIS_CITY_HOUSE1:0","MAP_SOOTOPOLIS_CITY_HOUSE2:0,1/MAP_SOOTOPOLIS_CITY:5":"MAP_SOOTOPOLIS_CITY:5/MAP_SOOTOPOLIS_CITY_HOUSE2:0","MAP_SOOTOPOLIS_CITY_HOUSE3:0,1/MAP_SOOTOPOLIS_CITY:6":"MAP_SOOTOPOLIS_CITY:6/MAP_SOOTOPOLIS_CITY_HOUSE3:0","MAP_SOOTOPOLIS_CITY_HOUSE4:0,1/MAP_SOOTOPOLIS_CITY:7":"MAP_SOOTOPOLIS_CITY:7/MAP_SOOTOPOLIS_CITY_HOUSE4:0","MAP_SOOTOPOLIS_CITY_HOUSE5:0,1/MAP_SOOTOPOLIS_CITY:8":"MAP_SOOTOPOLIS_CITY:8/MAP_SOOTOPOLIS_CITY_HOUSE5:0","MAP_SOOTOPOLIS_CITY_HOUSE6:0,1/MAP_SOOTOPOLIS_CITY:9":"MAP_SOOTOPOLIS_CITY:9/MAP_SOOTOPOLIS_CITY_HOUSE6:0","MAP_SOOTOPOLIS_CITY_HOUSE7:0,1/MAP_SOOTOPOLIS_CITY:10":"MAP_SOOTOPOLIS_CITY:10/MAP_SOOTOPOLIS_CITY_HOUSE7:0","MAP_SOOTOPOLIS_CITY_LOTAD_AND_SEEDOT_HOUSE:0,1/MAP_SOOTOPOLIS_CITY:11":"MAP_SOOTOPOLIS_CITY:11/MAP_SOOTOPOLIS_CITY_LOTAD_AND_SEEDOT_HOUSE:0","MAP_SOOTOPOLIS_CITY_MART:0,1/MAP_SOOTOPOLIS_CITY:1":"MAP_SOOTOPOLIS_CITY:1/MAP_SOOTOPOLIS_CITY_MART:0","MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F:0,1/MAP_SOOTOPOLIS_CITY:12":"MAP_SOOTOPOLIS_CITY:12/MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F:0","MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F:2/MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_B1F:0":"MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_B1F:0/MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F:2","MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_B1F:0/MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F:2":"MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F:2/MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_B1F:0","MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F:0,1/MAP_SOOTOPOLIS_CITY:0":"MAP_SOOTOPOLIS_CITY:0/MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F:0","MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F:2/MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F:0":"MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F:0/MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F:2","MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F:0/MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F:2":"MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F:2/MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F:0","MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_SOUTHERN_ISLAND_EXTERIOR:0,1/MAP_SOUTHERN_ISLAND_INTERIOR:0,1":"MAP_SOUTHERN_ISLAND_INTERIOR:0,1/MAP_SOUTHERN_ISLAND_EXTERIOR:0,1","MAP_SOUTHERN_ISLAND_INTERIOR:0,1/MAP_SOUTHERN_ISLAND_EXTERIOR:0,1":"MAP_SOUTHERN_ISLAND_EXTERIOR:0,1/MAP_SOUTHERN_ISLAND_INTERIOR:0,1","MAP_SS_TIDAL_CORRIDOR:0/MAP_SS_TIDAL_ROOMS:0":"MAP_SS_TIDAL_ROOMS:0,1/MAP_SS_TIDAL_CORRIDOR:0","MAP_SS_TIDAL_CORRIDOR:1/MAP_SS_TIDAL_ROOMS:2":"MAP_SS_TIDAL_ROOMS:2,3/MAP_SS_TIDAL_CORRIDOR:1","MAP_SS_TIDAL_CORRIDOR:2/MAP_SS_TIDAL_ROOMS:4":"MAP_SS_TIDAL_ROOMS:4,5/MAP_SS_TIDAL_CORRIDOR:2","MAP_SS_TIDAL_CORRIDOR:3/MAP_SS_TIDAL_ROOMS:6":"MAP_SS_TIDAL_ROOMS:6,7/MAP_SS_TIDAL_CORRIDOR:3","MAP_SS_TIDAL_CORRIDOR:4/MAP_SS_TIDAL_ROOMS:8":"MAP_SS_TIDAL_ROOMS:8/MAP_SS_TIDAL_CORRIDOR:4","MAP_SS_TIDAL_CORRIDOR:5/MAP_SS_TIDAL_ROOMS:9":"MAP_SS_TIDAL_ROOMS:9/MAP_SS_TIDAL_CORRIDOR:5","MAP_SS_TIDAL_CORRIDOR:6/MAP_SS_TIDAL_ROOMS:10":"MAP_SS_TIDAL_ROOMS:10/MAP_SS_TIDAL_CORRIDOR:6","MAP_SS_TIDAL_CORRIDOR:7/MAP_SS_TIDAL_ROOMS:11":"MAP_SS_TIDAL_ROOMS:11/MAP_SS_TIDAL_CORRIDOR:7","MAP_SS_TIDAL_CORRIDOR:8/MAP_SS_TIDAL_LOWER_DECK:0":"MAP_SS_TIDAL_LOWER_DECK:0/MAP_SS_TIDAL_CORRIDOR:8","MAP_SS_TIDAL_LOWER_DECK:0/MAP_SS_TIDAL_CORRIDOR:8":"MAP_SS_TIDAL_CORRIDOR:8/MAP_SS_TIDAL_LOWER_DECK:0","MAP_SS_TIDAL_ROOMS:0,1/MAP_SS_TIDAL_CORRIDOR:0":"MAP_SS_TIDAL_CORRIDOR:0/MAP_SS_TIDAL_ROOMS:0","MAP_SS_TIDAL_ROOMS:10/MAP_SS_TIDAL_CORRIDOR:6":"MAP_SS_TIDAL_CORRIDOR:6/MAP_SS_TIDAL_ROOMS:10","MAP_SS_TIDAL_ROOMS:11/MAP_SS_TIDAL_CORRIDOR:7":"MAP_SS_TIDAL_CORRIDOR:7/MAP_SS_TIDAL_ROOMS:11","MAP_SS_TIDAL_ROOMS:2,3/MAP_SS_TIDAL_CORRIDOR:1":"MAP_SS_TIDAL_CORRIDOR:1/MAP_SS_TIDAL_ROOMS:2","MAP_SS_TIDAL_ROOMS:4,5/MAP_SS_TIDAL_CORRIDOR:2":"MAP_SS_TIDAL_CORRIDOR:2/MAP_SS_TIDAL_ROOMS:4","MAP_SS_TIDAL_ROOMS:6,7/MAP_SS_TIDAL_CORRIDOR:3":"MAP_SS_TIDAL_CORRIDOR:3/MAP_SS_TIDAL_ROOMS:6","MAP_SS_TIDAL_ROOMS:8/MAP_SS_TIDAL_CORRIDOR:4":"MAP_SS_TIDAL_CORRIDOR:4/MAP_SS_TIDAL_ROOMS:8","MAP_SS_TIDAL_ROOMS:9/MAP_SS_TIDAL_CORRIDOR:5":"MAP_SS_TIDAL_CORRIDOR:5/MAP_SS_TIDAL_ROOMS:9","MAP_TERRA_CAVE_END:0/MAP_TERRA_CAVE_ENTRANCE:1":"MAP_TERRA_CAVE_ENTRANCE:1/MAP_TERRA_CAVE_END:0","MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!":"","MAP_TERRA_CAVE_ENTRANCE:1/MAP_TERRA_CAVE_END:0":"MAP_TERRA_CAVE_END:0/MAP_TERRA_CAVE_ENTRANCE:1","MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!":"","MAP_TRAINER_HILL_1F:0/MAP_TRAINER_HILL_ENTRANCE:2":"MAP_TRAINER_HILL_ENTRANCE:2/MAP_TRAINER_HILL_1F:0","MAP_TRAINER_HILL_1F:1/MAP_TRAINER_HILL_2F:0":"MAP_TRAINER_HILL_2F:0/MAP_TRAINER_HILL_1F:1","MAP_TRAINER_HILL_2F:0/MAP_TRAINER_HILL_1F:1":"MAP_TRAINER_HILL_1F:1/MAP_TRAINER_HILL_2F:0","MAP_TRAINER_HILL_2F:1/MAP_TRAINER_HILL_3F:0":"MAP_TRAINER_HILL_3F:0/MAP_TRAINER_HILL_2F:1","MAP_TRAINER_HILL_3F:0/MAP_TRAINER_HILL_2F:1":"MAP_TRAINER_HILL_2F:1/MAP_TRAINER_HILL_3F:0","MAP_TRAINER_HILL_3F:1/MAP_TRAINER_HILL_4F:0":"MAP_TRAINER_HILL_4F:0/MAP_TRAINER_HILL_3F:1","MAP_TRAINER_HILL_4F:0/MAP_TRAINER_HILL_3F:1":"MAP_TRAINER_HILL_3F:1/MAP_TRAINER_HILL_4F:0","MAP_TRAINER_HILL_4F:1/MAP_TRAINER_HILL_ROOF:0":"MAP_TRAINER_HILL_ROOF:0/MAP_TRAINER_HILL_4F:1","MAP_TRAINER_HILL_ELEVATOR:0,1/MAP_TRAINER_HILL_ROOF:1":"MAP_TRAINER_HILL_ROOF:1/MAP_TRAINER_HILL_ELEVATOR:1","MAP_TRAINER_HILL_ENTRANCE:0,1/MAP_ROUTE111:4":"MAP_ROUTE111:4/MAP_TRAINER_HILL_ENTRANCE:0","MAP_TRAINER_HILL_ENTRANCE:2/MAP_TRAINER_HILL_1F:0":"MAP_TRAINER_HILL_1F:0/MAP_TRAINER_HILL_ENTRANCE:2","MAP_TRAINER_HILL_ROOF:0/MAP_TRAINER_HILL_4F:1":"MAP_TRAINER_HILL_4F:1/MAP_TRAINER_HILL_ROOF:0","MAP_TRAINER_HILL_ROOF:1/MAP_TRAINER_HILL_ELEVATOR:1":"MAP_TRAINER_HILL_ELEVATOR:0,1/MAP_TRAINER_HILL_ROOF:1","MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!":"","MAP_UNDERWATER_ROUTE105:0/MAP_UNDERWATER_MARINE_CAVE:0!":"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!","MAP_UNDERWATER_ROUTE105:1/MAP_UNDERWATER_MARINE_CAVE:0!":"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!","MAP_UNDERWATER_ROUTE125:0/MAP_UNDERWATER_MARINE_CAVE:0!":"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!","MAP_UNDERWATER_ROUTE125:1/MAP_UNDERWATER_MARINE_CAVE:0!":"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!","MAP_UNDERWATER_ROUTE126:0/MAP_UNDERWATER_SOOTOPOLIS_CITY:0":"MAP_UNDERWATER_SOOTOPOLIS_CITY:0,1/MAP_UNDERWATER_ROUTE126:0","MAP_UNDERWATER_ROUTE127:0/MAP_UNDERWATER_MARINE_CAVE:0!":"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!","MAP_UNDERWATER_ROUTE127:1/MAP_UNDERWATER_MARINE_CAVE:0!":"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!","MAP_UNDERWATER_ROUTE128:0/MAP_UNDERWATER_SEAFLOOR_CAVERN:0":"MAP_UNDERWATER_SEAFLOOR_CAVERN:0/MAP_UNDERWATER_ROUTE128:0","MAP_UNDERWATER_ROUTE129:0/MAP_UNDERWATER_MARINE_CAVE:0!":"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!","MAP_UNDERWATER_ROUTE129:1/MAP_UNDERWATER_MARINE_CAVE:0!":"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!","MAP_UNDERWATER_ROUTE134:0/MAP_UNDERWATER_SEALED_CHAMBER:0":"MAP_UNDERWATER_SEALED_CHAMBER:0/MAP_UNDERWATER_ROUTE134:0","MAP_UNDERWATER_SEAFLOOR_CAVERN:0/MAP_UNDERWATER_ROUTE128:0":"MAP_UNDERWATER_ROUTE128:0/MAP_UNDERWATER_SEAFLOOR_CAVERN:0","MAP_UNDERWATER_SEALED_CHAMBER:0/MAP_UNDERWATER_ROUTE134:0":"MAP_UNDERWATER_ROUTE134:0/MAP_UNDERWATER_SEALED_CHAMBER:0","MAP_UNDERWATER_SOOTOPOLIS_CITY:0,1/MAP_UNDERWATER_ROUTE126:0":"MAP_UNDERWATER_ROUTE126:0/MAP_UNDERWATER_SOOTOPOLIS_CITY:0","MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!":"","MAP_VERDANTURF_TOWN:0/MAP_VERDANTURF_TOWN_BATTLE_TENT_LOBBY:0":"MAP_VERDANTURF_TOWN_BATTLE_TENT_LOBBY:0,1/MAP_VERDANTURF_TOWN:0","MAP_VERDANTURF_TOWN:1/MAP_VERDANTURF_TOWN_MART:0":"MAP_VERDANTURF_TOWN_MART:0,1/MAP_VERDANTURF_TOWN:1","MAP_VERDANTURF_TOWN:2/MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F:0":"MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F:0,1/MAP_VERDANTURF_TOWN:2","MAP_VERDANTURF_TOWN:3/MAP_VERDANTURF_TOWN_WANDAS_HOUSE:0":"MAP_VERDANTURF_TOWN_WANDAS_HOUSE:0,1/MAP_VERDANTURF_TOWN:3","MAP_VERDANTURF_TOWN:4/MAP_RUSTURF_TUNNEL:1":"MAP_RUSTURF_TUNNEL:1/MAP_VERDANTURF_TOWN:4","MAP_VERDANTURF_TOWN:5/MAP_VERDANTURF_TOWN_FRIENDSHIP_RATERS_HOUSE:0":"MAP_VERDANTURF_TOWN_FRIENDSHIP_RATERS_HOUSE:0,1/MAP_VERDANTURF_TOWN:5","MAP_VERDANTURF_TOWN:6/MAP_VERDANTURF_TOWN_HOUSE:0":"MAP_VERDANTURF_TOWN_HOUSE:0,1/MAP_VERDANTURF_TOWN:6","MAP_VERDANTURF_TOWN_BATTLE_TENT_LOBBY:0,1/MAP_VERDANTURF_TOWN:0":"MAP_VERDANTURF_TOWN:0/MAP_VERDANTURF_TOWN_BATTLE_TENT_LOBBY:0","MAP_VERDANTURF_TOWN_FRIENDSHIP_RATERS_HOUSE:0,1/MAP_VERDANTURF_TOWN:5":"MAP_VERDANTURF_TOWN:5/MAP_VERDANTURF_TOWN_FRIENDSHIP_RATERS_HOUSE:0","MAP_VERDANTURF_TOWN_HOUSE:0,1/MAP_VERDANTURF_TOWN:6":"MAP_VERDANTURF_TOWN:6/MAP_VERDANTURF_TOWN_HOUSE:0","MAP_VERDANTURF_TOWN_MART:0,1/MAP_VERDANTURF_TOWN:1":"MAP_VERDANTURF_TOWN:1/MAP_VERDANTURF_TOWN_MART:0","MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F:0,1/MAP_VERDANTURF_TOWN:2":"MAP_VERDANTURF_TOWN:2/MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F:0","MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F:2/MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F:0":"MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F:0/MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F:2","MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F:0/MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F:2":"MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F:2/MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F:0","MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!":"MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!","MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!":"MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!","MAP_VERDANTURF_TOWN_WANDAS_HOUSE:0,1/MAP_VERDANTURF_TOWN:3":"MAP_VERDANTURF_TOWN:3/MAP_VERDANTURF_TOWN_WANDAS_HOUSE:0","MAP_VICTORY_ROAD_1F:0/MAP_EVER_GRANDE_CITY:2":"MAP_EVER_GRANDE_CITY:2/MAP_VICTORY_ROAD_1F:0","MAP_VICTORY_ROAD_1F:1/MAP_EVER_GRANDE_CITY:3":"MAP_EVER_GRANDE_CITY:3/MAP_VICTORY_ROAD_1F:1","MAP_VICTORY_ROAD_1F:2/MAP_VICTORY_ROAD_B1F:5":"MAP_VICTORY_ROAD_B1F:5/MAP_VICTORY_ROAD_1F:2","MAP_VICTORY_ROAD_1F:3/MAP_VICTORY_ROAD_B1F:2":"MAP_VICTORY_ROAD_B1F:2/MAP_VICTORY_ROAD_1F:3","MAP_VICTORY_ROAD_1F:4/MAP_VICTORY_ROAD_B1F:4":"MAP_VICTORY_ROAD_B1F:4/MAP_VICTORY_ROAD_1F:4","MAP_VICTORY_ROAD_B1F:0/MAP_VICTORY_ROAD_B2F:0":"MAP_VICTORY_ROAD_B2F:0/MAP_VICTORY_ROAD_B1F:0","MAP_VICTORY_ROAD_B1F:1/MAP_VICTORY_ROAD_B2F:2":"MAP_VICTORY_ROAD_B2F:2/MAP_VICTORY_ROAD_B1F:1","MAP_VICTORY_ROAD_B1F:2/MAP_VICTORY_ROAD_1F:3":"MAP_VICTORY_ROAD_1F:3/MAP_VICTORY_ROAD_B1F:2","MAP_VICTORY_ROAD_B1F:3/MAP_VICTORY_ROAD_B2F:1":"MAP_VICTORY_ROAD_B2F:1/MAP_VICTORY_ROAD_B1F:3","MAP_VICTORY_ROAD_B1F:4/MAP_VICTORY_ROAD_1F:4":"MAP_VICTORY_ROAD_1F:4/MAP_VICTORY_ROAD_B1F:4","MAP_VICTORY_ROAD_B1F:5/MAP_VICTORY_ROAD_1F:2":"MAP_VICTORY_ROAD_1F:2/MAP_VICTORY_ROAD_B1F:5","MAP_VICTORY_ROAD_B1F:6/MAP_VICTORY_ROAD_B2F:3":"MAP_VICTORY_ROAD_B2F:3/MAP_VICTORY_ROAD_B1F:6","MAP_VICTORY_ROAD_B2F:0/MAP_VICTORY_ROAD_B1F:0":"MAP_VICTORY_ROAD_B1F:0/MAP_VICTORY_ROAD_B2F:0","MAP_VICTORY_ROAD_B2F:1/MAP_VICTORY_ROAD_B1F:3":"MAP_VICTORY_ROAD_B1F:3/MAP_VICTORY_ROAD_B2F:1","MAP_VICTORY_ROAD_B2F:2/MAP_VICTORY_ROAD_B1F:1":"MAP_VICTORY_ROAD_B1F:1/MAP_VICTORY_ROAD_B2F:2","MAP_VICTORY_ROAD_B2F:3/MAP_VICTORY_ROAD_B1F:6":"MAP_VICTORY_ROAD_B1F:6/MAP_VICTORY_ROAD_B2F:3"}}
diff --git a/worlds/pokemon_emerald/data/items.json b/worlds/pokemon_emerald/data/items.json
index cea72eb65047..139d75aad0ab 100644
--- a/worlds/pokemon_emerald/data/items.json
+++ b/worlds/pokemon_emerald/data/items.json
@@ -2,1480 +2,1780 @@
"ITEM_BADGE_1": {
"label": "Stone Badge",
"classification": "PROGRESSION",
- "tags": ["Badge", "Unique"]
+ "tags": ["Badge", "Unique"],
+ "modern_id": null
},
"ITEM_BADGE_2": {
"label": "Knuckle Badge",
"classification": "PROGRESSION",
- "tags": ["Badge", "Unique"]
+ "tags": ["Badge", "Unique"],
+ "modern_id": null
},
"ITEM_BADGE_3": {
"label": "Dynamo Badge",
"classification": "PROGRESSION",
- "tags": ["Badge", "Unique"]
+ "tags": ["Badge", "Unique"],
+ "modern_id": null
},
"ITEM_BADGE_4": {
"label": "Heat Badge",
"classification": "PROGRESSION",
- "tags": ["Badge", "Unique"]
+ "tags": ["Badge", "Unique"],
+ "modern_id": null
},
"ITEM_BADGE_5": {
"label": "Balance Badge",
"classification": "PROGRESSION",
- "tags": ["Badge", "Unique"]
+ "tags": ["Badge", "Unique"],
+ "modern_id": null
},
"ITEM_BADGE_6": {
"label": "Feather Badge",
"classification": "PROGRESSION",
- "tags": ["Badge", "Unique"]
+ "tags": ["Badge", "Unique"],
+ "modern_id": null
},
"ITEM_BADGE_7": {
"label": "Mind Badge",
"classification": "PROGRESSION",
- "tags": ["Badge", "Unique"]
+ "tags": ["Badge", "Unique"],
+ "modern_id": null
},
"ITEM_BADGE_8": {
"label": "Rain Badge",
"classification": "PROGRESSION",
- "tags": ["Badge", "Unique"]
+ "tags": ["Badge", "Unique"],
+ "modern_id": null
},
- "ITEM_HM01_CUT": {
+ "ITEM_HM_CUT": {
"label": "HM01 Cut",
"classification": "PROGRESSION",
- "tags": ["HM", "Unique"]
+ "tags": ["HM", "Unique"],
+ "modern_id": 420
},
- "ITEM_HM02_FLY": {
+ "ITEM_HM_FLY": {
"label": "HM02 Fly",
"classification": "PROGRESSION",
- "tags": ["HM", "Unique"]
+ "tags": ["HM", "Unique"],
+ "modern_id": 421
},
- "ITEM_HM03_SURF": {
+ "ITEM_HM_SURF": {
"label": "HM03 Surf",
"classification": "PROGRESSION",
- "tags": ["HM", "Unique"]
+ "tags": ["HM", "Unique"],
+ "modern_id": 422
},
- "ITEM_HM04_STRENGTH": {
+ "ITEM_HM_STRENGTH": {
"label": "HM04 Strength",
"classification": "PROGRESSION",
- "tags": ["HM", "Unique"]
+ "tags": ["HM", "Unique"],
+ "modern_id": 423
},
- "ITEM_HM05_FLASH": {
+ "ITEM_HM_FLASH": {
"label": "HM05 Flash",
"classification": "PROGRESSION",
- "tags": ["HM", "Unique"]
+ "tags": ["HM", "Unique"],
+ "modern_id": 424
},
- "ITEM_HM06_ROCK_SMASH": {
+ "ITEM_HM_ROCK_SMASH": {
"label": "HM06 Rock Smash",
"classification": "PROGRESSION",
- "tags": ["HM", "Unique"]
+ "tags": ["HM", "Unique"],
+ "modern_id": 425
},
- "ITEM_HM07_WATERFALL": {
+ "ITEM_HM_WATERFALL": {
"label": "HM07 Waterfall",
"classification": "PROGRESSION",
- "tags": ["HM", "Unique"]
+ "tags": ["HM", "Unique"],
+ "modern_id": 737
},
- "ITEM_HM08_DIVE": {
+ "ITEM_HM_DIVE": {
"label": "HM08 Dive",
"classification": "PROGRESSION",
- "tags": ["HM", "Unique"]
+ "tags": ["HM", "Unique"],
+ "modern_id": null
},
"ITEM_MACH_BIKE": {
"label": "Mach Bike",
"classification": "PROGRESSION",
- "tags": ["Bike", "Unique"]
+ "tags": ["Bike", "Unique"],
+ "modern_id": 718
},
"ITEM_ACRO_BIKE": {
"label": "Acro Bike",
"classification": "PROGRESSION",
- "tags": ["Bike", "Unique"]
+ "tags": ["Bike", "Unique"],
+ "modern_id": 719
},
"ITEM_DEVON_GOODS": {
"label": "Devon Goods",
"classification": "PROGRESSION",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 721
},
"ITEM_LETTER": {
"label": "Letter",
"classification": "PROGRESSION",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 725
},
"ITEM_ITEMFINDER": {
"label": "Itemfinder",
"classification": "PROGRESSION",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": null
},
"ITEM_METEORITE": {
"label": "Meteorite",
"classification": "PROGRESSION",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 729
},
"ITEM_GO_GOGGLES": {
"label": "Go Goggles",
"classification": "PROGRESSION",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 728
},
"ITEM_ROOM_1_KEY": {
"label": "Room 1 Key",
"classification": "PROGRESSION",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 730
},
"ITEM_ROOM_2_KEY": {
"label": "Room 2 Key",
"classification": "PROGRESSION",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 731
},
"ITEM_ROOM_4_KEY": {
"label": "Room 4 Key",
"classification": "PROGRESSION",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 732
},
"ITEM_ROOM_6_KEY": {
"label": "Room 6 Key",
"classification": "PROGRESSION",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 733
},
"ITEM_STORAGE_KEY": {
"label": "Storage Key",
"classification": "PROGRESSION",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 734
},
"ITEM_SCANNER": {
"label": "Scanner",
"classification": "PROGRESSION",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 727
},
"ITEM_BASEMENT_KEY": {
"label": "Basement Key",
"classification": "PROGRESSION",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 476
},
"ITEM_DEVON_SCOPE": {
"label": "Devon Scope",
"classification": "PROGRESSION",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 735
},
"ITEM_MAGMA_EMBLEM": {
"label": "Magma Emblem",
"classification": "PROGRESSION",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": null
},
"ITEM_POKEBLOCK_CASE": {
"label": "Pokeblock Case",
"classification": "PROGRESSION",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 724
},
"ITEM_SS_TICKET": {
"label": "S.S. Ticket",
"classification": "PROGRESSION",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 736
},
"ITEM_WAILMER_PAIL": {
"label": "Wailmer Pail",
- "classification": "USEFUL",
- "tags": ["Unique"]
+ "classification": "PROGRESSION",
+ "tags": ["Unique"],
+ "modern_id": 720
},
"ITEM_POWDER_JAR": {
"label": "Powder Jar",
"classification": "FILLER",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": null
},
"ITEM_COIN_CASE": {
"label": "Coin Case",
"classification": "FILLER",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 444
},
"ITEM_CONTEST_PASS": {
"label": "Contest Pass",
"classification": "FILLER",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 457
},
"ITEM_SOOT_SACK": {
"label": "Soot Sack",
"classification": "FILLER",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 722
},
"ITEM_ROOT_FOSSIL": {
"label": "Root Fossil",
"classification": "FILLER",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 99
},
"ITEM_CLAW_FOSSIL": {
"label": "Claw Fossil",
"classification": "FILLER",
- "tags": ["Unique"]
+ "tags": ["Unique"],
+ "modern_id": 100
},
"ITEM_EON_TICKET": {
"label": "Eon Ticket",
- "classification": "FILLER",
- "tags": ["Unique"]
+ "classification": "PROGRESSION",
+ "tags": ["Unique"],
+ "modern_id": 726
},
"ITEM_OLD_SEA_MAP": {
"label": "Old Sea Map",
- "classification": "FILLER",
- "tags": ["Unique"]
+ "classification": "PROGRESSION",
+ "tags": ["Unique"],
+ "modern_id": null
+ },
+ "ITEM_MYSTIC_TICKET": {
+ "label": "Mystic Ticket",
+ "classification": "PROGRESSION",
+ "tags": ["Unique"],
+ "modern_id": null
+ },
+ "ITEM_AURORA_TICKET": {
+ "label": "Aurora Ticket",
+ "classification": "PROGRESSION",
+ "tags": ["Unique"],
+ "modern_id": null
},
-
"ITEM_OLD_ROD": {
"label": "Old Rod",
- "classification": "USEFUL",
- "tags": ["Rod", "Unique"]
+ "classification": "PROGRESSION",
+ "tags": ["Rod", "Unique"],
+ "modern_id": 445
},
"ITEM_GOOD_ROD": {
"label": "Good Rod",
- "classification": "USEFUL",
- "tags": ["Rod", "Unique"]
+ "classification": "PROGRESSION",
+ "tags": ["Rod", "Unique"],
+ "modern_id": 446
},
"ITEM_SUPER_ROD": {
"label": "Super Rod",
- "classification": "USEFUL",
- "tags": ["Rod", "Unique"]
+ "classification": "PROGRESSION",
+ "tags": ["Rod", "Unique"],
+ "modern_id": 447
},
"ITEM_MASTER_BALL": {
"label": "Master Ball",
"classification": "USEFUL",
- "tags": ["Ball"]
+ "tags": ["Ball"],
+ "modern_id": 1
},
"ITEM_ULTRA_BALL": {
"label": "Ultra Ball",
"classification": "FILLER",
- "tags": ["Ball"]
+ "tags": ["Ball"],
+ "modern_id": 2
},
"ITEM_GREAT_BALL": {
"label": "Great Ball",
"classification": "FILLER",
- "tags": ["Ball"]
+ "tags": ["Ball"],
+ "modern_id": 3
},
"ITEM_POKE_BALL": {
"label": "Poke Ball",
"classification": "FILLER",
- "tags": ["Ball"]
+ "tags": ["Ball"],
+ "modern_id": 4
},
"ITEM_SAFARI_BALL": {
"label": "Safari Ball",
"classification": "FILLER",
- "tags": ["Ball"]
+ "tags": ["Ball"],
+ "modern_id": 5
},
"ITEM_NET_BALL": {
"label": "Net Ball",
"classification": "FILLER",
- "tags": ["Ball"]
+ "tags": ["Ball"],
+ "modern_id": 6
},
"ITEM_DIVE_BALL": {
"label": "Dive Ball",
"classification": "FILLER",
- "tags": ["Ball"]
+ "tags": ["Ball"],
+ "modern_id": 7
},
"ITEM_NEST_BALL": {
"label": "Nest Ball",
"classification": "FILLER",
- "tags": ["Ball"]
+ "tags": ["Ball"],
+ "modern_id": 8
},
"ITEM_REPEAT_BALL": {
"label": "Repeat Ball",
"classification": "FILLER",
- "tags": ["Ball"]
+ "tags": ["Ball"],
+ "modern_id": 9
},
"ITEM_TIMER_BALL": {
"label": "Timer Ball",
"classification": "FILLER",
- "tags": ["Ball"]
+ "tags": ["Ball"],
+ "modern_id": 10
},
"ITEM_LUXURY_BALL": {
"label": "Luxury Ball",
"classification": "FILLER",
- "tags": ["Ball"]
+ "tags": ["Ball"],
+ "modern_id": 11
},
"ITEM_PREMIER_BALL": {
"label": "Premier Ball",
"classification": "FILLER",
- "tags": ["Ball"]
+ "tags": ["Ball"],
+ "modern_id": 12
},
"ITEM_POTION": {
"label": "Potion",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 17
},
"ITEM_ANTIDOTE": {
"label": "Antidote",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 18
},
"ITEM_BURN_HEAL": {
"label": "Burn Heal",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 19
},
"ITEM_ICE_HEAL": {
"label": "Ice Heal",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 20
},
"ITEM_AWAKENING": {
"label": "Awakening",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 21
},
"ITEM_PARALYZE_HEAL": {
"label": "Paralyze Heal",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 22
},
"ITEM_FULL_RESTORE": {
"label": "Full Restore",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 23
},
"ITEM_MAX_POTION": {
"label": "Max Potion",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 24
},
"ITEM_HYPER_POTION": {
"label": "Hyper Potion",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 25
},
"ITEM_SUPER_POTION": {
"label": "Super Potion",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 26
},
"ITEM_FULL_HEAL": {
"label": "Full Heal",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 27
},
"ITEM_REVIVE": {
"label": "Revive",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 28
},
"ITEM_MAX_REVIVE": {
"label": "Max Revive",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 29
},
"ITEM_FRESH_WATER": {
"label": "Fresh Water",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 30
},
"ITEM_SODA_POP": {
"label": "Soda Pop",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 31
},
"ITEM_LEMONADE": {
"label": "Lemonade",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 32
},
"ITEM_MOOMOO_MILK": {
"label": "Moomoo Milk",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 33
},
"ITEM_ENERGY_POWDER": {
"label": "Energy Powder",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 34
},
"ITEM_ENERGY_ROOT": {
"label": "Energy Root",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 35
},
"ITEM_HEAL_POWDER": {
"label": "Heal Powder",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 36
},
"ITEM_REVIVAL_HERB": {
"label": "Revival Herb",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 37
},
"ITEM_ETHER": {
"label": "Ether",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 38
},
"ITEM_MAX_ETHER": {
"label": "Max Ether",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 39
},
"ITEM_ELIXIR": {
"label": "Elixir",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 40
},
"ITEM_MAX_ELIXIR": {
"label": "Max Elixir",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 41
},
"ITEM_LAVA_COOKIE": {
"label": "Lava Cookie",
"classification": "FILLER",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 42
+ },
+ "ITEM_BERRY_JUICE": {
+ "label": "Berry Juice",
+ "classification": "FILLER",
+ "tags": ["Heal"],
+ "modern_id": 43
},
"ITEM_SACRED_ASH": {
"label": "Sacred Ash",
"classification": "USEFUL",
- "tags": ["Heal"]
+ "tags": ["Heal"],
+ "modern_id": 44
},
- "ITEM_BERRY_JUICE": {
- "label": "Berry Juice",
- "classification": "FILLER",
- "tags": ["Misc"]
- },
"ITEM_SHOAL_SALT": {
"label": "Shoal Salt",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 70
},
"ITEM_SHOAL_SHELL": {
"label": "Shoal Shell",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 71
},
"ITEM_RED_SHARD": {
"label": "Red Shard",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 72
},
"ITEM_BLUE_SHARD": {
"label": "Blue Shard",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 73
},
"ITEM_YELLOW_SHARD": {
"label": "Yellow Shard",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 74
},
"ITEM_GREEN_SHARD": {
"label": "Green Shard",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 75
},
"ITEM_HP_UP": {
"label": "HP Up",
"classification": "FILLER",
- "tags": ["Vitamin"]
+ "tags": ["Vitamin"],
+ "modern_id": 45
},
"ITEM_PROTEIN": {
"label": "Protein",
"classification": "FILLER",
- "tags": ["Vitamin"]
+ "tags": ["Vitamin"],
+ "modern_id": 46
},
"ITEM_IRON": {
"label": "Iron",
"classification": "FILLER",
- "tags": ["Vitamin"]
+ "tags": ["Vitamin"],
+ "modern_id": 47
},
"ITEM_CARBOS": {
"label": "Carbos",
"classification": "FILLER",
- "tags": ["Vitamin"]
+ "tags": ["Vitamin"],
+ "modern_id": 48
},
"ITEM_CALCIUM": {
"label": "Calcium",
"classification": "FILLER",
- "tags": ["Vitamin"]
+ "tags": ["Vitamin"],
+ "modern_id": 49
},
"ITEM_ZINC": {
"label": "Zinc",
"classification": "FILLER",
- "tags": ["Vitamin"]
+ "tags": ["Vitamin"],
+ "modern_id": 52
},
"ITEM_PP_UP": {
"label": "PP Up",
"classification": "FILLER",
- "tags": ["Vitamin"]
+ "tags": ["Vitamin"],
+ "modern_id": 51
},
"ITEM_PP_MAX": {
"label": "PP Max",
"classification": "FILLER",
- "tags": ["Vitamin"]
+ "tags": ["Vitamin"],
+ "modern_id": 53
},
"ITEM_RARE_CANDY": {
"label": "Rare Candy",
"classification": "USEFUL",
- "tags": ["Vitamin"]
+ "tags": ["Candy"],
+ "modern_id": 50
},
"ITEM_GUARD_SPEC": {
"label": "Guard Spec",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 55
},
"ITEM_DIRE_HIT": {
"label": "Dire Hit",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 56
},
"ITEM_X_ATTACK": {
"label": "X Attack",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 57
},
"ITEM_X_DEFEND": {
"label": "X Defend",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 58
},
"ITEM_X_SPEED": {
"label": "X Speed",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 59
},
"ITEM_X_ACCURACY": {
"label": "X Accuracy",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 60
},
"ITEM_X_SPECIAL": {
"label": "X Special",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 61
},
"ITEM_REPEL": {
"label": "Repel",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 79
},
"ITEM_SUPER_REPEL": {
"label": "Super Repel",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 76
},
"ITEM_MAX_REPEL": {
"label": "Max Repel",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 77
},
"ITEM_POKE_DOLL": {
"label": "Poke Doll",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 63
},
"ITEM_FLUFFY_TAIL": {
"label": "Fluffy Tail",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 64
},
"ITEM_ESCAPE_ROPE": {
"label": "Escape Rope",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 78
},
"ITEM_BLUE_FLUTE": {
"label": "Blue Flute",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 65
},
"ITEM_YELLOW_FLUTE": {
"label": "Yellow Flute",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 66
},
"ITEM_RED_FLUTE": {
"label": "Red Flute",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 67
},
"ITEM_BLACK_FLUTE": {
"label": "Black Flute",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 68
},
"ITEM_WHITE_FLUTE": {
"label": "White Flute",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 69
},
"ITEM_HEART_SCALE": {
"label": "Heart Scale",
"classification": "FILLER",
- "tags": ["Misc"]
+ "tags": ["Misc"],
+ "modern_id": 93
},
"ITEM_SUN_STONE": {
"label": "Sun Stone",
"classification": "USEFUL",
- "tags": ["EvoStone"]
+ "tags": ["EvoStone"],
+ "modern_id": 80
},
"ITEM_MOON_STONE": {
"label": "Moon Stone",
"classification": "USEFUL",
- "tags": ["EvoStone"]
+ "tags": ["EvoStone"],
+ "modern_id": 81
},
"ITEM_FIRE_STONE": {
"label": "Fire Stone",
"classification": "USEFUL",
- "tags": ["EvoStone"]
+ "tags": ["EvoStone"],
+ "modern_id": 82
},
"ITEM_THUNDER_STONE": {
"label": "Thunder Stone",
"classification": "USEFUL",
- "tags": ["EvoStone"]
+ "tags": ["EvoStone"],
+ "modern_id": 83
},
"ITEM_WATER_STONE": {
"label": "Water Stone",
"classification": "USEFUL",
- "tags": ["EvoStone"]
+ "tags": ["EvoStone"],
+ "modern_id": 84
},
"ITEM_LEAF_STONE": {
"label": "Leaf Stone",
"classification": "USEFUL",
- "tags": ["EvoStone"]
+ "tags": ["EvoStone"],
+ "modern_id": 85
},
"ITEM_TINY_MUSHROOM": {
"label": "Tiny Mushroom",
"classification": "FILLER",
- "tags": ["Money"]
+ "tags": ["Money"],
+ "modern_id": 86
},
"ITEM_BIG_MUSHROOM": {
"label": "Big Mushroom",
"classification": "FILLER",
- "tags": ["Money"]
+ "tags": ["Money"],
+ "modern_id": 87
},
"ITEM_PEARL": {
"label": "Pearl",
"classification": "FILLER",
- "tags": ["Money"]
+ "tags": ["Money"],
+ "modern_id": 88
},
"ITEM_BIG_PEARL": {
"label": "Big Pearl",
"classification": "FILLER",
- "tags": ["Money"]
+ "tags": ["Money"],
+ "modern_id": 89
},
"ITEM_STARDUST": {
"label": "Stardust",
"classification": "FILLER",
- "tags": ["Money"]
+ "tags": ["Money"],
+ "modern_id": 90
},
"ITEM_STAR_PIECE": {
"label": "Star Piece",
"classification": "FILLER",
- "tags": ["Money"]
+ "tags": ["Money"],
+ "modern_id": 91
},
"ITEM_NUGGET": {
"label": "Nugget",
"classification": "FILLER",
- "tags": ["Money"]
+ "tags": ["Money"],
+ "modern_id": 92
},
"ITEM_ORANGE_MAIL": {
"label": "Orange Mail",
"classification": "FILLER",
- "tags": ["Mail"]
+ "tags": ["Mail"],
+ "modern_id": 137
},
"ITEM_HARBOR_MAIL": {
"label": "Harbor Mail",
"classification": "FILLER",
- "tags": ["Mail"]
+ "tags": ["Mail"],
+ "modern_id": 138
},
"ITEM_GLITTER_MAIL": {
"label": "Glitter Mail",
"classification": "FILLER",
- "tags": ["Mail"]
+ "tags": ["Mail"],
+ "modern_id": 139
},
"ITEM_MECH_MAIL": {
"label": "Mech Mail",
"classification": "FILLER",
- "tags": ["Mail"]
+ "tags": ["Mail"],
+ "modern_id": 140
},
"ITEM_WOOD_MAIL": {
"label": "Wood Mail",
"classification": "FILLER",
- "tags": ["Mail"]
+ "tags": ["Mail"],
+ "modern_id": 141
},
"ITEM_WAVE_MAIL": {
"label": "Wave Mail",
"classification": "FILLER",
- "tags": ["Mail"]
+ "tags": ["Mail"],
+ "modern_id": 142
},
"ITEM_BEAD_MAIL": {
"label": "Bead Mail",
"classification": "FILLER",
- "tags": ["Mail"]
+ "tags": ["Mail"],
+ "modern_id": 143
},
"ITEM_SHADOW_MAIL": {
"label": "Shadow Mail",
"classification": "FILLER",
- "tags": ["Mail"]
+ "tags": ["Mail"],
+ "modern_id": 144
},
"ITEM_TROPIC_MAIL": {
"label": "Tropic Mail",
"classification": "FILLER",
- "tags": ["Mail"]
+ "tags": ["Mail"],
+ "modern_id": 145
},
"ITEM_DREAM_MAIL": {
"label": "Dream Mail",
"classification": "FILLER",
- "tags": ["Mail"]
+ "tags": ["Mail"],
+ "modern_id": 146
},
"ITEM_FAB_MAIL": {
"label": "Fab Mail",
"classification": "FILLER",
- "tags": ["Mail"]
+ "tags": ["Mail"],
+ "modern_id": 147
},
"ITEM_RETRO_MAIL": {
"label": "Retro Mail",
"classification": "FILLER",
- "tags": ["Mail"]
+ "tags": ["Mail"],
+ "modern_id": 148
},
"ITEM_CHERI_BERRY": {
"label": "Cheri Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 149
},
"ITEM_CHESTO_BERRY": {
"label": "Chesto Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 150
},
"ITEM_PECHA_BERRY": {
"label": "Pecha Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 151
},
"ITEM_RAWST_BERRY": {
"label": "Rawst Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 152
},
"ITEM_ASPEAR_BERRY": {
"label": "Aspear Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 153
},
"ITEM_LEPPA_BERRY": {
"label": "Leppa Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 154
},
"ITEM_ORAN_BERRY": {
"label": "Oran Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 155
},
"ITEM_PERSIM_BERRY": {
"label": "Persim Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 156
},
"ITEM_LUM_BERRY": {
"label": "Lum Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 157
},
"ITEM_SITRUS_BERRY": {
"label": "Sitrus Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 158
},
"ITEM_FIGY_BERRY": {
"label": "Figy Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 159
},
"ITEM_WIKI_BERRY": {
"label": "Wiki Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 160
},
"ITEM_MAGO_BERRY": {
"label": "Mago Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 161
},
"ITEM_AGUAV_BERRY": {
"label": "Aguav Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 162
},
"ITEM_IAPAPA_BERRY": {
"label": "Iapapa Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 163
},
"ITEM_RAZZ_BERRY": {
"label": "Razz Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 164
},
"ITEM_BLUK_BERRY": {
"label": "Bluk Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 165
},
"ITEM_NANAB_BERRY": {
"label": "Nanab Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 166
},
"ITEM_WEPEAR_BERRY": {
"label": "Wepear Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 167
},
"ITEM_PINAP_BERRY": {
"label": "Pinap Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 168
},
"ITEM_POMEG_BERRY": {
"label": "Pomeg Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 169
},
"ITEM_KELPSY_BERRY": {
"label": "Kelpsy Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 170
},
"ITEM_QUALOT_BERRY": {
"label": "Qualot Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 171
},
"ITEM_HONDEW_BERRY": {
"label": "Hondew Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 172
},
"ITEM_GREPA_BERRY": {
"label": "Grepa Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 173
},
"ITEM_TAMATO_BERRY": {
"label": "Tamato Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 174
},
"ITEM_CORNN_BERRY": {
"label": "Cornn Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 175
},
"ITEM_MAGOST_BERRY": {
"label": "Magost Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 176
},
"ITEM_RABUTA_BERRY": {
"label": "Rabuta Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 177
},
"ITEM_NOMEL_BERRY": {
"label": "Nomel Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 178
},
"ITEM_SPELON_BERRY": {
"label": "Spelon Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 179
},
"ITEM_PAMTRE_BERRY": {
"label": "Pamtre Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 180
},
"ITEM_WATMEL_BERRY": {
"label": "Watmel Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 181
},
"ITEM_DURIN_BERRY": {
"label": "Durin Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 182
},
"ITEM_BELUE_BERRY": {
"label": "Belue Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 183
},
"ITEM_LIECHI_BERRY": {
"label": "Liechi Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 201
},
"ITEM_GANLON_BERRY": {
"label": "Ganlon Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 202
},
"ITEM_SALAC_BERRY": {
"label": "Salac Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 203
},
"ITEM_PETAYA_BERRY": {
"label": "Petaya Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 204
},
"ITEM_APICOT_BERRY": {
"label": "Apicot Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 205
},
"ITEM_LANSAT_BERRY": {
"label": "Lansat Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 206
},
"ITEM_STARF_BERRY": {
"label": "Starf Berry",
"classification": "FILLER",
- "tags": ["Berry"]
+ "tags": ["Berry"],
+ "modern_id": 207
},
"ITEM_BRIGHT_POWDER": {
"label": "Bright Powder",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 213
},
"ITEM_WHITE_HERB": {
"label": "White Herb",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 214
},
"ITEM_MACHO_BRACE": {
"label": "Macho Brace",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 215
},
"ITEM_EXP_SHARE": {
"label": "Exp. Share",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 216
},
"ITEM_QUICK_CLAW": {
"label": "Quick Claw",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 217
},
"ITEM_SOOTHE_BELL": {
"label": "Soothe Bell",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 218
},
"ITEM_MENTAL_HERB": {
"label": "Mental Herb",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 219
},
"ITEM_CHOICE_BAND": {
"label": "Choice Band",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 220
},
"ITEM_KINGS_ROCK": {
"label": "King's Rock",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 221
},
"ITEM_SILVER_POWDER": {
"label": "Silver Powder",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 222
},
"ITEM_AMULET_COIN": {
"label": "Amulet Coin",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 223
},
"ITEM_CLEANSE_TAG": {
"label": "Cleanse Tag",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 224
},
"ITEM_SOUL_DEW": {
"label": "Soul Dew",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 225
},
"ITEM_DEEP_SEA_TOOTH": {
"label": "Deep Sea Tooth",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 226
},
"ITEM_DEEP_SEA_SCALE": {
"label": "Deep Sea Scale",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 227
},
"ITEM_SMOKE_BALL": {
"label": "Smoke Ball",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 228
},
"ITEM_EVERSTONE": {
"label": "Everstone",
"classification": "FILLER",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 229
},
"ITEM_FOCUS_BAND": {
"label": "Focus Band",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 230
},
"ITEM_LUCKY_EGG": {
"label": "Lucky Egg",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 231
},
"ITEM_SCOPE_LENS": {
"label": "Scope Lens",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 232
},
"ITEM_METAL_COAT": {
"label": "Metal Coat",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 233
},
"ITEM_LEFTOVERS": {
"label": "Leftovers",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 234
},
"ITEM_DRAGON_SCALE": {
"label": "Dragon Scale",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 235
},
"ITEM_LIGHT_BALL": {
"label": "Light Ball",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 236
},
"ITEM_SOFT_SAND": {
"label": "Soft Sand",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 237
},
"ITEM_HARD_STONE": {
"label": "Hard Stone",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 238
},
"ITEM_MIRACLE_SEED": {
"label": "Miracle Seed",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 239
},
"ITEM_BLACK_GLASSES": {
"label": "Black Glasses",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 240
},
"ITEM_BLACK_BELT": {
"label": "Black Belt",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 241
},
"ITEM_MAGNET": {
"label": "Magnet",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 242
},
"ITEM_MYSTIC_WATER": {
"label": "Mystic Water",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 243
},
"ITEM_SHARP_BEAK": {
"label": "Sharp Beak",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 244
},
"ITEM_POISON_BARB": {
"label": "Poison Barb",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 245
},
"ITEM_NEVER_MELT_ICE": {
"label": "Never-Melt Ice",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 246
},
"ITEM_SPELL_TAG": {
"label": "Spell Tag",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 247
},
"ITEM_TWISTED_SPOON": {
"label": "Twisted Spoon",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 248
},
"ITEM_CHARCOAL": {
"label": "Charcoal",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 249
},
"ITEM_DRAGON_FANG": {
"label": "Dragon Fang",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 250
},
"ITEM_SILK_SCARF": {
"label": "Silk Scarf",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 251
},
"ITEM_UP_GRADE": {
"label": "Up-Grade",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 252
},
"ITEM_SHELL_BELL": {
"label": "Shell Bell",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 253
},
"ITEM_SEA_INCENSE": {
"label": "Sea Incense",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 254
},
"ITEM_LAX_INCENSE": {
"label": "Lax Incense",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 255
},
"ITEM_LUCKY_PUNCH": {
"label": "Lucky Punch",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 256
},
"ITEM_METAL_POWDER": {
"label": "Metal Powder",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 257
},
"ITEM_THICK_CLUB": {
"label": "Thick Club",
"classification": "USEFUL",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 258
},
"ITEM_STICK": {
"label": "Stick",
"classification": "FILLER",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 259
},
"ITEM_RED_SCARF": {
"label": "Red Scarf",
"classification": "FILLER",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 260
},
"ITEM_BLUE_SCARF": {
"label": "Blue Scarf",
"classification": "FILLER",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 261
},
"ITEM_PINK_SCARF": {
"label": "Pink Scarf",
"classification": "FILLER",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 262
},
"ITEM_GREEN_SCARF": {
"label": "Green Scarf",
"classification": "FILLER",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 263
},
"ITEM_YELLOW_SCARF": {
"label": "Yellow Scarf",
"classification": "FILLER",
- "tags": ["Held"]
+ "tags": ["Held"],
+ "modern_id": 264
},
- "ITEM_TM01_FOCUS_PUNCH": {
+ "ITEM_TM_FOCUS_PUNCH": {
"label": "TM01",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 328
},
- "ITEM_TM02_DRAGON_CLAW": {
+ "ITEM_TM_DRAGON_CLAW": {
"label": "TM02",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 329
},
- "ITEM_TM03_WATER_PULSE": {
+ "ITEM_TM_WATER_PULSE": {
"label": "TM03",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 330
},
- "ITEM_TM04_CALM_MIND": {
+ "ITEM_TM_CALM_MIND": {
"label": "TM04",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 331
},
- "ITEM_TM05_ROAR": {
+ "ITEM_TM_ROAR": {
"label": "TM05",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 332
},
- "ITEM_TM06_TOXIC": {
+ "ITEM_TM_TOXIC": {
"label": "TM06",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 333
},
- "ITEM_TM07_HAIL": {
+ "ITEM_TM_HAIL": {
"label": "TM07",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 334
},
- "ITEM_TM08_BULK_UP": {
+ "ITEM_TM_BULK_UP": {
"label": "TM08",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 335
},
- "ITEM_TM09_BULLET_SEED": {
+ "ITEM_TM_BULLET_SEED": {
"label": "TM09",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 336
},
- "ITEM_TM10_HIDDEN_POWER": {
+ "ITEM_TM_HIDDEN_POWER": {
"label": "TM10",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 337
},
- "ITEM_TM11_SUNNY_DAY": {
+ "ITEM_TM_SUNNY_DAY": {
"label": "TM11",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 338
},
- "ITEM_TM12_TAUNT": {
+ "ITEM_TM_TAUNT": {
"label": "TM12",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 339
},
- "ITEM_TM13_ICE_BEAM": {
+ "ITEM_TM_ICE_BEAM": {
"label": "TM13",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 340
},
- "ITEM_TM14_BLIZZARD": {
+ "ITEM_TM_BLIZZARD": {
"label": "TM14",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 341
},
- "ITEM_TM15_HYPER_BEAM": {
+ "ITEM_TM_HYPER_BEAM": {
"label": "TM15",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 342
},
- "ITEM_TM16_LIGHT_SCREEN": {
+ "ITEM_TM_LIGHT_SCREEN": {
"label": "TM16",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 343
},
- "ITEM_TM17_PROTECT": {
+ "ITEM_TM_PROTECT": {
"label": "TM17",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 344
},
- "ITEM_TM18_RAIN_DANCE": {
+ "ITEM_TM_RAIN_DANCE": {
"label": "TM18",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 345
},
- "ITEM_TM19_GIGA_DRAIN": {
+ "ITEM_TM_GIGA_DRAIN": {
"label": "TM19",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 346
},
- "ITEM_TM20_SAFEGUARD": {
+ "ITEM_TM_SAFEGUARD": {
"label": "TM20",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 347
},
- "ITEM_TM21_FRUSTRATION": {
+ "ITEM_TM_FRUSTRATION": {
"label": "TM21",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 348
},
- "ITEM_TM22_SOLAR_BEAM": {
+ "ITEM_TM_SOLAR_BEAM": {
"label": "TM22",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 349
},
- "ITEM_TM23_IRON_TAIL": {
+ "ITEM_TM_IRON_TAIL": {
"label": "TM23",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 350
},
- "ITEM_TM24_THUNDERBOLT": {
+ "ITEM_TM_THUNDERBOLT": {
"label": "TM24",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 351
},
- "ITEM_TM25_THUNDER": {
+ "ITEM_TM_THUNDER": {
"label": "TM25",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 352
},
- "ITEM_TM26_EARTHQUAKE": {
+ "ITEM_TM_EARTHQUAKE": {
"label": "TM26",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 353
},
- "ITEM_TM27_RETURN": {
+ "ITEM_TM_RETURN": {
"label": "TM27",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 354
},
- "ITEM_TM28_DIG": {
+ "ITEM_TM_DIG": {
"label": "TM28",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 355
},
- "ITEM_TM29_PSYCHIC": {
+ "ITEM_TM_PSYCHIC": {
"label": "TM29",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 356
},
- "ITEM_TM30_SHADOW_BALL": {
+ "ITEM_TM_SHADOW_BALL": {
"label": "TM30",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 357
},
- "ITEM_TM31_BRICK_BREAK": {
+ "ITEM_TM_BRICK_BREAK": {
"label": "TM31",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 358
},
- "ITEM_TM32_DOUBLE_TEAM": {
+ "ITEM_TM_DOUBLE_TEAM": {
"label": "TM32",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 359
},
- "ITEM_TM33_REFLECT": {
+ "ITEM_TM_REFLECT": {
"label": "TM33",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 360
},
- "ITEM_TM34_SHOCK_WAVE": {
+ "ITEM_TM_SHOCK_WAVE": {
"label": "TM34",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 361
},
- "ITEM_TM35_FLAMETHROWER": {
+ "ITEM_TM_FLAMETHROWER": {
"label": "TM35",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 362
},
- "ITEM_TM36_SLUDGE_BOMB": {
+ "ITEM_TM_SLUDGE_BOMB": {
"label": "TM36",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 363
},
- "ITEM_TM37_SANDSTORM": {
+ "ITEM_TM_SANDSTORM": {
"label": "TM37",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 364
},
- "ITEM_TM38_FIRE_BLAST": {
+ "ITEM_TM_FIRE_BLAST": {
"label": "TM38",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 365
},
- "ITEM_TM39_ROCK_TOMB": {
+ "ITEM_TM_ROCK_TOMB": {
"label": "TM39",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 366
},
- "ITEM_TM40_AERIAL_ACE": {
+ "ITEM_TM_AERIAL_ACE": {
"label": "TM40",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 367
},
- "ITEM_TM41_TORMENT": {
+ "ITEM_TM_TORMENT": {
"label": "TM41",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 368
},
- "ITEM_TM42_FACADE": {
+ "ITEM_TM_FACADE": {
"label": "TM42",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 369
},
- "ITEM_TM43_SECRET_POWER": {
+ "ITEM_TM_SECRET_POWER": {
"label": "TM43",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 370
},
- "ITEM_TM44_REST": {
+ "ITEM_TM_REST": {
"label": "TM44",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 371
},
- "ITEM_TM45_ATTRACT": {
+ "ITEM_TM_ATTRACT": {
"label": "TM45",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 372
},
- "ITEM_TM46_THIEF": {
+ "ITEM_TM_THIEF": {
"label": "TM46",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 373
},
- "ITEM_TM47_STEEL_WING": {
+ "ITEM_TM_STEEL_WING": {
"label": "TM47",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 374
},
- "ITEM_TM48_SKILL_SWAP": {
+ "ITEM_TM_SKILL_SWAP": {
"label": "TM48",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 375
},
- "ITEM_TM49_SNATCH": {
+ "ITEM_TM_SNATCH": {
"label": "TM49",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 376
},
- "ITEM_TM50_OVERHEAT": {
+ "ITEM_TM_OVERHEAT": {
"label": "TM50",
"classification": "USEFUL",
- "tags": ["TM"]
+ "tags": ["TM"],
+ "modern_id": 377
}
}
diff --git a/worlds/pokemon_emerald/data/locations.json b/worlds/pokemon_emerald/data/locations.json
index a44ec204a02c..6affdf414688 100644
--- a/worlds/pokemon_emerald/data/locations.json
+++ b/worlds/pokemon_emerald/data/locations.json
@@ -32,35 +32,35 @@
"tags": ["Badge"]
},
- "NPC_GIFT_RECEIVED_HM01": {
+ "NPC_GIFT_RECEIVED_HM_CUT": {
"label": "Rustboro City - HM01 from Cutter's House",
"tags": ["HM"]
},
- "NPC_GIFT_RECEIVED_HM02": {
+ "NPC_GIFT_RECEIVED_HM_FLY": {
"label": "Route 119 - HM02 from Rival Battle",
"tags": ["HM"]
},
- "NPC_GIFT_RECEIVED_HM03": {
+ "NPC_GIFT_RECEIVED_HM_SURF": {
"label": "Petalburg City - HM03 from Wally's Uncle",
"tags": ["HM"]
},
- "NPC_GIFT_RECEIVED_HM04": {
+ "NPC_GIFT_RECEIVED_HM_STRENGTH": {
"label": "Rusturf Tunnel - HM04 from Tunneler",
"tags": ["HM"]
},
- "NPC_GIFT_RECEIVED_HM05": {
+ "NPC_GIFT_RECEIVED_HM_FLASH": {
"label": "Granite Cave 1F - HM05 from Hiker",
"tags": ["HM"]
},
- "NPC_GIFT_RECEIVED_HM06": {
+ "NPC_GIFT_RECEIVED_HM_ROCK_SMASH": {
"label": "Mauville City - HM06 from Rock Smash Guy",
"tags": ["HM"]
},
- "NPC_GIFT_RECEIVED_HM07": {
+ "NPC_GIFT_RECEIVED_HM_WATERFALL": {
"label": "Sootopolis City - HM07 from Wallace",
"tags": ["HM"]
},
- "NPC_GIFT_RECEIVED_HM08": {
+ "NPC_GIFT_RECEIVED_HM_DIVE": {
"label": "Mossdeep City - HM08 from Steven's House",
"tags": ["HM"]
},
@@ -119,23 +119,23 @@
"tags": ["KeyItem"]
},
"HIDDEN_ITEM_ABANDONED_SHIP_RM_4_KEY": {
- "label": "Abandoned Ship HF - Hidden Item in Room 1",
+ "label": "Abandoned Ship HF - Room 4 Key",
"tags": ["KeyItem"]
},
"HIDDEN_ITEM_ABANDONED_SHIP_RM_1_KEY": {
- "label": "Abandoned Ship HF - Hidden Item in Room 3",
+ "label": "Abandoned Ship HF - Room 1 Key",
"tags": ["KeyItem"]
},
"HIDDEN_ITEM_ABANDONED_SHIP_RM_6_KEY": {
- "label": "Abandoned Ship HF - Hidden Item in Room 4",
+ "label": "Abandoned Ship HF - Room 6 Key",
"tags": ["KeyItem"]
},
"HIDDEN_ITEM_ABANDONED_SHIP_RM_2_KEY": {
- "label": "Abandoned Ship HF - Hidden Item in Room 5",
+ "label": "Abandoned Ship HF - Room 2 Key",
"tags": ["KeyItem"]
},
- "ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_4_SCANNER": {
- "label": "Abandoned Ship HF - Item in Room 2",
+ "ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_2_SCANNER": {
+ "label": "Abandoned Ship HF - Scanner",
"tags": ["KeyItem"]
},
"NPC_GIFT_RECEIVED_POKEBLOCK_CASE": {
@@ -144,7 +144,23 @@
},
"NPC_GIFT_RECEIVED_SS_TICKET": {
"label": "Littleroot Town - S.S. Ticket from Norman",
- "tags": ["Ferry"]
+ "tags": ["KeyItem"]
+ },
+ "NPC_GIFT_RECEIVED_AURORA_TICKET": {
+ "label": "Littleroot Town - Aurora Ticket from Norman",
+ "tags": ["EventTicket"]
+ },
+ "NPC_GIFT_RECEIVED_EON_TICKET": {
+ "label": "Littleroot Town - Eon Ticket from Norman",
+ "tags": ["EventTicket"]
+ },
+ "NPC_GIFT_RECEIVED_MYSTIC_TICKET": {
+ "label": "Littleroot Town - Mystic Ticket from Norman",
+ "tags": ["EventTicket"]
+ },
+ "NPC_GIFT_RECEIVED_OLD_SEA_MAP": {
+ "label": "Littleroot Town - Old Sea Map from Norman",
+ "tags": ["EventTicket"]
},
"NPC_GIFT_RECEIVED_OLD_ROD": {
@@ -228,10 +244,6 @@
"label": "Mt Pyre Summit - Hidden Item Grave",
"tags": ["HiddenItem"]
},
- "HIDDEN_ITEM_NAVEL_ROCK_TOP_SACRED_ASH": {
- "label": "Navel Rock Top - Hidden Item Sacred Ash",
- "tags": ["HiddenItem"]
- },
"HIDDEN_ITEM_PETALBURG_CITY_RARE_CANDY": {
"label": "Petalburg City - Hidden Item Past Pond South",
"tags": ["HiddenItem"]
@@ -356,7 +368,7 @@
"label": "Route 113 - Hidden Item Mound Between Trainers",
"tags": ["HiddenItem"]
},
- "HIDDEN_ITEM_ROUTE_113_TM32": {
+ "HIDDEN_ITEM_ROUTE_113_TM_DOUBLE_TEAM": {
"label": "Route 113 - Hidden Item Mound West of Workshop",
"tags": ["HiddenItem"]
},
@@ -492,10 +504,6 @@
"label": "SS Tidal - Hidden Item in Lower Deck Trash Can",
"tags": ["HiddenItem"]
},
- "HIDDEN_ITEM_TRICK_HOUSE_NUGGET": {
- "label": "Trick House - Hidden Item",
- "tags": ["HiddenItem"]
- },
"HIDDEN_ITEM_UNDERWATER_124_GREEN_SHARD": {
"label": "Route 124 UW - Hidden Item in Big Area",
"tags": ["HiddenItem"]
@@ -592,8 +600,12 @@
"label": "Victory Road B2F - Hidden Item in Northeast Corner",
"tags": ["HiddenItem"]
},
+ "HIDDEN_ITEM_NAVEL_ROCK_TOP_SACRED_ASH": {
+ "label": "Navel Rock Top - Hidden Item Sacred Ash",
+ "tags": ["HiddenItem"]
+ },
- "ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_1_TM18": {
+ "ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_1_TM_RAIN_DANCE": {
"label": "Abandoned Ship HF - Item in Room 1",
"tags": ["OverworldItem"]
},
@@ -617,7 +629,7 @@
"label": "Abandoned Ship B1F - Item in South Rooms",
"tags": ["OverworldItem"]
},
- "ITEM_ABANDONED_SHIP_ROOMS_B1F_TM13": {
+ "ITEM_ABANDONED_SHIP_ROOMS_B1F_TM_ICE_BEAM": {
"label": "Abandoned Ship B1F - Item in Storage Room",
"tags": ["OverworldItem"]
},
@@ -653,7 +665,7 @@
"label": "Fiery Path - Item Behind Boulders 1",
"tags": ["OverworldItem"]
},
- "ITEM_FIERY_PATH_TM06": {
+ "ITEM_FIERY_PATH_TM_TOXIC": {
"label": "Fiery Path - Item Behind Boulders 2",
"tags": ["OverworldItem"]
},
@@ -725,11 +737,11 @@
"label": "Meteor Falls 1F - Item Below Waterfall",
"tags": ["OverworldItem"]
},
- "ITEM_METEOR_FALLS_1F_1R_TM23": {
+ "ITEM_METEOR_FALLS_1F_1R_TM_IRON_TAIL": {
"label": "Meteor Falls 1F - Item Before Steven's Cave",
"tags": ["OverworldItem"]
},
- "ITEM_METEOR_FALLS_B1F_2R_TM02": {
+ "ITEM_METEOR_FALLS_B1F_2R_TM_DRAGON_CLAW": {
"label": "Meteor Falls B1F - Item in North Cave",
"tags": ["OverworldItem"]
},
@@ -753,11 +765,11 @@
"label": "Mt Pyre 5F - Item",
"tags": ["OverworldItem"]
},
- "ITEM_MT_PYRE_6F_TM30": {
+ "ITEM_MT_PYRE_6F_TM_SHADOW_BALL": {
"label": "Mt Pyre 6F - Item",
"tags": ["OverworldItem"]
},
- "ITEM_MT_PYRE_EXTERIOR_TM48": {
+ "ITEM_MT_PYRE_EXTERIOR_TM_SKILL_SWAP": {
"label": "Mt Pyre Exterior - Item 1",
"tags": ["OverworldItem"]
},
@@ -881,7 +893,7 @@
"label": "Route 111 - Item Desert Near Tower",
"tags": ["OverworldItem"]
},
- "ITEM_ROUTE_111_TM37": {
+ "ITEM_ROUTE_111_TM_SANDSTORM": {
"label": "Route 111 - Item Desert South",
"tags": ["OverworldItem"]
},
@@ -929,7 +941,7 @@
"label": "Route 115 - Item North Near Trainers",
"tags": ["OverworldItem"]
},
- "ITEM_ROUTE_115_TM01": {
+ "ITEM_ROUTE_115_TM_FOCUS_PUNCH": {
"label": "Route 115 - Item Near Mud Slope",
"tags": ["OverworldItem"]
},
@@ -1137,7 +1149,7 @@
"label": "Safari Zone NE - Item on Ledge",
"tags": ["OverworldItem"]
},
- "ITEM_SAFARI_ZONE_NORTH_WEST_TM22": {
+ "ITEM_SAFARI_ZONE_NORTH_WEST_TM_SOLAR_BEAM": {
"label": "Safari Zone NW - Item Behind Pond",
"tags": ["OverworldItem"]
},
@@ -1149,11 +1161,11 @@
"label": "Safari Zone SW - Item Behind Pond",
"tags": ["OverworldItem"]
},
- "ITEM_SCORCHED_SLAB_TM11": {
+ "ITEM_SCORCHED_SLAB_TM_SUNNY_DAY": {
"label": "Scorched Slab - Item",
"tags": ["OverworldItem"]
},
- "ITEM_SEAFLOOR_CAVERN_ROOM_9_TM26": {
+ "ITEM_SEAFLOOR_CAVERN_ROOM_9_TM_EARTHQUAKE": {
"label": "Seafloor Cavern Room 9 - Item Before Kyogre",
"tags": ["OverworldItem"]
},
@@ -1165,7 +1177,7 @@
"label": "Shoal Cave Ice Room - Item 1",
"tags": ["OverworldItem"]
},
- "ITEM_SHOAL_CAVE_ICE_ROOM_TM07": {
+ "ITEM_SHOAL_CAVE_ICE_ROOM_TM_HAIL": {
"label": "Shoal Cave Ice Room - Item 2",
"tags": ["OverworldItem"]
},
@@ -1225,7 +1237,7 @@
"label": "Victory Road B1F - Item Behind Boulders",
"tags": ["OverworldItem"]
},
- "ITEM_VICTORY_ROAD_B1F_TM29": {
+ "ITEM_VICTORY_ROAD_B1F_TM_PSYCHIC": {
"label": "Victory Road B1F - Item on Northeast Ledge",
"tags": ["OverworldItem"]
},
@@ -1234,7 +1246,7 @@
"tags": ["OverworldItem"]
},
- "NPC_GIFT_GOT_TM24_FROM_WATTSON": {
+ "NPC_GIFT_GOT_TM_THUNDERBOLT_FROM_WATTSON": {
"label": "Mauville City - TM24 from Wattson",
"tags": ["NpcGift"]
},
@@ -1275,7 +1287,7 @@
"tags": ["NpcGift"]
},
"NPC_GIFT_RECEIVED_KINGS_ROCK": {
- "label": "Mossdeep City - King's Rock from Kid",
+ "label": "Mossdeep City - King's Rock from Boy",
"tags": ["NpcGift"]
},
"NPC_GIFT_RECEIVED_MACHO_BRACE": {
@@ -1322,107 +1334,111 @@
"label": "Route 109 - Soft Sand from Tuber",
"tags": ["NpcGift"]
},
+ "NPC_GIFT_RECEIVED_SOOT_SACK": {
+ "label": "Route 113 - Soot Sack from Glass Blower",
+ "tags": ["NpcGift"]
+ },
"NPC_GIFT_RECEIVED_SOOTHE_BELL": {
"label": "Slateport City - Soothe Bell from Woman in Fan Club",
"tags": ["NpcGift"]
},
"NPC_GIFT_RECEIVED_SUN_STONE_MOSSDEEP": {
- "label": "Mossdeep City - Gift from Man in Museum",
+ "label": "Space Center - Gift from Man",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM03": {
+ "NPC_GIFT_RECEIVED_TM_WATER_PULSE": {
"label": "Sootopolis Gym - TM03 from Juan",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM04": {
+ "NPC_GIFT_RECEIVED_TM_CALM_MIND": {
"label": "Mossdeep Gym - TM04 from Tate and Liza",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM05": {
+ "NPC_GIFT_RECEIVED_TM_ROAR": {
"label": "Route 114 - TM05 from Roaring Man",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM08": {
+ "NPC_GIFT_RECEIVED_TM_BULK_UP": {
"label": "Dewford Gym - TM08 from Brawly",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM09": {
+ "NPC_GIFT_RECEIVED_TM_BULLET_SEED": {
"label": "Route 104 - TM09 from Boy",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM10": {
+ "NPC_GIFT_RECEIVED_TM_HIDDEN_POWER": {
"label": "Fortree City - TM10 from Hidden Power Lady",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM19": {
+ "NPC_GIFT_RECEIVED_TM_GIGA_DRAIN": {
"label": "Route 123 - TM19 from Girl near Berries",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM21": {
+ "NPC_GIFT_RECEIVED_TM_FRUSTRATION": {
"label": "Pacifidlog Town - TM21 from Man in House",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM27": {
+ "NPC_GIFT_RECEIVED_TM_RETURN": {
"label": "Fallarbor Town - TM27 from Cozmo",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM27_2": {
+ "NPC_GIFT_RECEIVED_TM_RETURN_2": {
"label": "Pacifidlog Town - TM27 from Man in House",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM28": {
- "label": "Route 114 - TM28 from Fossil Maniac",
+ "NPC_GIFT_RECEIVED_TM_DIG": {
+ "label": "Route 114 - TM28 from Fossil Maniac's Brother",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM31": {
+ "NPC_GIFT_RECEIVED_TM_BRICK_BREAK": {
"label": "Sootopolis City - TM31 from Black Belt in House",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM34": {
+ "NPC_GIFT_RECEIVED_TM_SHOCK_WAVE": {
"label": "Mauville Gym - TM34 from Wattson",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM36": {
+ "NPC_GIFT_RECEIVED_TM_SLUDGE_BOMB": {
"label": "Dewford Town - TM36 from Sludge Bomb Man",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM39": {
+ "NPC_GIFT_RECEIVED_TM_ROCK_TOMB": {
"label": "Rustboro Gym - TM39 from Roxanne",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM40": {
+ "NPC_GIFT_RECEIVED_TM_AERIAL_ACE": {
"label": "Fortree Gym - TM40 from Winona",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM41": {
+ "NPC_GIFT_RECEIVED_TM_TORMENT": {
"label": "Slateport City - TM41 from Sailor in Battle Tent",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM42": {
+ "NPC_GIFT_RECEIVED_TM_FACADE": {
"label": "Petalburg Gym - TM42 from Norman",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM44": {
+ "NPC_GIFT_RECEIVED_TM_REST": {
"label": "Lilycove City - TM44 from Man in House",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM45": {
+ "NPC_GIFT_RECEIVED_TM_ATTRACT": {
"label": "Verdanturf Town - TM45 from Woman in Battle Tent",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM46": {
- "label": "Slateport City - TM46 from Aqua Grunt in Museum",
+ "NPC_GIFT_RECEIVED_TM_THIEF": {
+ "label": "Oceanic Museum - TM46 from Aqua Grunt in Museum",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM47": {
+ "NPC_GIFT_RECEIVED_TM_STEEL_WING": {
"label": "Granite Cave 1F - TM47 from Steven",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM49": {
+ "NPC_GIFT_RECEIVED_TM_SNATCH": {
"label": "SS Tidal - TM49 from Thief",
"tags": ["NpcGift"]
},
- "NPC_GIFT_RECEIVED_TM50": {
+ "NPC_GIFT_RECEIVED_TM_OVERHEAT": {
"label": "Lavaridge Gym - TM50 from Flannery",
"tags": ["NpcGift"]
},
@@ -1430,6 +1446,10 @@
"label": "Route 104 - White Herb from Lady Near Flower Shop",
"tags": ["NpcGift"]
},
+ "NPC_GIFT_FLOWER_SHOP_RECEIVED_BERRY": {
+ "label": "Route 104 - Berry from Girl in Flower Shop",
+ "tags": ["NpcGift"]
+ },
"NPC_GIFT_RECEIVED_DEEP_SEA_SCALE": {
"label": "Slateport City - Deep Sea Scale from Capt. Stern",
"tags": ["NpcGift"]
@@ -1437,5 +1457,3908 @@
"NPC_GIFT_RECEIVED_DEEP_SEA_TOOTH": {
"label": "Slateport City - Deep Sea Tooth from Capt. Stern",
"tags": ["NpcGift"]
+ },
+ "NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_1": {
+ "label": "Trick House Puzzle 1 - Reward",
+ "tags": ["NpcGift"]
+ },
+ "NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_2": {
+ "label": "Trick House Puzzle 2 - Reward",
+ "tags": ["NpcGift"]
+ },
+ "NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_3": {
+ "label": "Trick House Puzzle 3 - Reward",
+ "tags": ["NpcGift"]
+ },
+ "NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_4": {
+ "label": "Trick House Puzzle 4 - Reward",
+ "tags": ["NpcGift"]
+ },
+ "NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_5": {
+ "label": "Trick House Puzzle 5 - Reward",
+ "tags": ["NpcGift"]
+ },
+ "NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_6": {
+ "label": "Trick House Puzzle 6 - Reward",
+ "tags": ["NpcGift"]
+ },
+ "NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_7": {
+ "label": "Trick House Puzzle 7 - Reward",
+ "tags": ["NpcGift"]
+ },
+ "NPC_GIFT_RECEIVED_FIRST_POKEBALLS": {
+ "label": "Littleroot Town - Pokeballs from Rival",
+ "tags": ["NpcGift"]
+ },
+ "NPC_GIFT_SOOTOPOLIS_RECEIVED_BERRY_1": {
+ "label": "Sootopolis City - Berry from Girl on Grass 1",
+ "tags": ["NpcGift"]
+ },
+ "NPC_GIFT_SOOTOPOLIS_RECEIVED_BERRY_2": {
+ "label": "Sootopolis City - Berry from Girl on Grass 2",
+ "tags": ["NpcGift"]
+ },
+ "NPC_GIFT_ROUTE_111_RECEIVED_BERRY": {
+ "label": "Route 111 - Berry from Girl Near Berry Trees",
+ "tags": ["NpcGift"]
+ },
+ "NPC_GIFT_ROUTE_114_RECEIVED_BERRY": {
+ "label": "Route 114 - Berry from Man Near House",
+ "tags": ["NpcGift"]
+ },
+ "NPC_GIFT_ROUTE_120_RECEIVED_BERRY": {
+ "label": "Route 120 - Berry from Lady Near Berry Trees",
+ "tags": ["NpcGift"]
+ },
+ "NPC_GIFT_BERRY_MASTER_RECEIVED_BERRY_1": {
+ "label": "Route 123 - Berry from Berry Master 1",
+ "tags": ["NpcGift"]
+ },
+ "NPC_GIFT_BERRY_MASTER_RECEIVED_BERRY_2": {
+ "label": "Route 123 - Berry from Berry Master 2",
+ "tags": ["NpcGift"]
+ },
+ "NPC_GIFT_BERRY_MASTERS_WIFE": {
+ "label": "Route 123 - Berry from Berry Master's Wife",
+ "tags": ["NpcGift"]
+ },
+ "NPC_GIFT_LILYCOVE_RECEIVED_BERRY": {
+ "label": "Lilycove City - Berry from Gentleman Above Ledges",
+ "tags": ["NpcGift"]
+ },
+
+ "BERRY_TREE_01": {
+ "label": "Route 102 - Berry Tree 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_02": {
+ "label": "Route 102 - Berry Tree 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_03": {
+ "label": "Route 104 - Berry Tree Flower Shop 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_04": {
+ "label": "Route 104 - Berry Tree Flower Shop 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_05": {
+ "label": "Route 103 - Berry Tree 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_06": {
+ "label": "Route 103 - Berry Tree 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_07": {
+ "label": "Route 103 - Berry Tree 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_08": {
+ "label": "Route 104 - Berry Tree North 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_09": {
+ "label": "Route 104 - Berry Tree North 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_10": {
+ "label": "Route 104 - Berry Tree North 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_11": {
+ "label": "Route 104 - Berry Tree South 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_12": {
+ "label": "Route 104 - Berry Tree South 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_13": {
+ "label": "Route 104 - Berry Tree South 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_14": {
+ "label": "Route 123 - Berry Tree Berry Master 6",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_15": {
+ "label": "Route 123 - Berry Tree Berry Master 7",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_16": {
+ "label": "Route 110 - Berry Tree 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_17": {
+ "label": "Route 110 - Berry Tree 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_18": {
+ "label": "Route 110 - Berry Tree 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_19": {
+ "label": "Route 111 - Berry Tree 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_20": {
+ "label": "Route 111 - Berry Tree 4",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_21": {
+ "label": "Route 112 - Berry Tree 4",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_22": {
+ "label": "Route 112 - Berry Tree 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_23": {
+ "label": "Route 112 - Berry Tree 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_24": {
+ "label": "Route 112 - Berry Tree 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_25": {
+ "label": "Route 116 - Berry Tree 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_26": {
+ "label": "Route 116 - Berry Tree 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_27": {
+ "label": "Route 117 - Berry Tree 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_28": {
+ "label": "Route 117 - Berry Tree 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_29": {
+ "label": "Route 117 - Berry Tree 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_30": {
+ "label": "Route 123 - Berry Tree Berry Master 8",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_31": {
+ "label": "Route 118 - Berry Tree 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_32": {
+ "label": "Route 118 - Berry Tree 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_33": {
+ "label": "Route 118 - Berry Tree 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_34": {
+ "label": "Route 119 - Berry Tree North 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_35": {
+ "label": "Route 119 - Berry Tree North 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_36": {
+ "label": "Route 119 - Berry Tree North 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_37": {
+ "label": "Route 120 - Berry Tree in Side Area 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_38": {
+ "label": "Route 120 - Berry Tree in Side Area 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_39": {
+ "label": "Route 120 - Berry Tree in Side Area 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_40": {
+ "label": "Route 120 - Berry Tree South 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_41": {
+ "label": "Route 120 - Berry Tree South 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_42": {
+ "label": "Route 120 - Berry Tree South 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_43": {
+ "label": "Route 120 - Berry Tree Pond 4",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_44": {
+ "label": "Route 120 - Berry Tree Pond 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_45": {
+ "label": "Route 120 - Berry Tree Pond 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_46": {
+ "label": "Route 120 - Berry Tree Pond 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_47": {
+ "label": "Route 121 - Berry Tree West 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_48": {
+ "label": "Route 121 - Berry Tree West 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_49": {
+ "label": "Route 121 - Berry Tree West 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_50": {
+ "label": "Route 121 - Berry Tree West 4",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_51": {
+ "label": "Route 121 - Berry Tree East 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_52": {
+ "label": "Route 121 - Berry Tree East 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_53": {
+ "label": "Route 121 - Berry Tree East 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_54": {
+ "label": "Route 121 - Berry Tree East 4",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_55": {
+ "label": "Route 115 - Berry Tree Behind Smashable Rock 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_56": {
+ "label": "Route 115 - Berry Tree Behind Smashable Rock 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_57": {
+ "label": "Route 123 - Berry Tree East 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_58": {
+ "label": "Route 123 - Berry Tree Berry Master 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_59": {
+ "label": "Route 123 - Berry Tree Berry Master 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_60": {
+ "label": "Route 123 - Berry Tree Berry Master 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_61": {
+ "label": "Route 123 - Berry Tree Berry Master 4",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_62": {
+ "label": "Route 123 - Berry Tree East 4",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_63": {
+ "label": "Route 123 - Berry Tree East 5",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_64": {
+ "label": "Route 123 - Berry Tree East 6",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_65": {
+ "label": "Route 123 - Berry Tree Berry Master 9",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_66": {
+ "label": "Route 116 - Berry Tree 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_67": {
+ "label": "Route 116 - Berry Tree 4",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_68": {
+ "label": "Route 114 - Berry Tree 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_69": {
+ "label": "Route 115 - Berry Tree North 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_70": {
+ "label": "Route 115 - Berry Tree North 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_71": {
+ "label": "Route 115 - Berry Tree North 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_72": {
+ "label": "Route 123 - Berry Tree Berry Master 10",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_73": {
+ "label": "Route 123 - Berry Tree Berry Master 11",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_74": {
+ "label": "Route 123 - Berry Tree Berry Master 12",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_75": {
+ "label": "Route 104 - Berry Tree Flower Shop 3",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_76": {
+ "label": "Route 104 - Berry Tree Flower Shop 4",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_77": {
+ "label": "Route 114 - Berry Tree 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_78": {
+ "label": "Route 114 - Berry Tree 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_79": {
+ "label": "Route 123 - Berry Tree Berry Master 5",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_80": {
+ "label": "Route 111 - Berry Tree 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_81": {
+ "label": "Route 111 - Berry Tree 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_82": {
+ "label": "Route 130 - Berry Tree on Mirage Island",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_83": {
+ "label": "Route 119 - Berry Tree Above Waterfall 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_84": {
+ "label": "Route 119 - Berry Tree Above Waterfall 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_85": {
+ "label": "Route 119 - Berry Tree South 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_86": {
+ "label": "Route 119 - Berry Tree South 2",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_87": {
+ "label": "Route 123 - Berry Tree East 1",
+ "tags": ["BerryTree"]
+ },
+ "BERRY_TREE_88": {
+ "label": "Route 123 - Berry Tree East 2",
+ "tags": ["BerryTree"]
+ },
+
+ "POKEDEX_REWARD_001": {
+ "label": "Pokedex - Bulbasaur",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_002": {
+ "label": "Pokedex - Ivysaur",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_003": {
+ "label": "Pokedex - Venusaur",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_004": {
+ "label": "Pokedex - Charmander",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_005": {
+ "label": "Pokedex - Charmeleon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_006": {
+ "label": "Pokedex - Charizard",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_007": {
+ "label": "Pokedex - Squirtle",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_008": {
+ "label": "Pokedex - Wartortle",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_009": {
+ "label": "Pokedex - Blastoise",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_010": {
+ "label": "Pokedex - Caterpie",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_011": {
+ "label": "Pokedex - Metapod",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_012": {
+ "label": "Pokedex - Butterfree",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_013": {
+ "label": "Pokedex - Weedle",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_014": {
+ "label": "Pokedex - Kakuna",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_015": {
+ "label": "Pokedex - Beedrill",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_016": {
+ "label": "Pokedex - Pidgey",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_017": {
+ "label": "Pokedex - Pidgeotto",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_018": {
+ "label": "Pokedex - Pidgeot",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_019": {
+ "label": "Pokedex - Rattata",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_020": {
+ "label": "Pokedex - Raticate",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_021": {
+ "label": "Pokedex - Spearow",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_022": {
+ "label": "Pokedex - Fearow",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_023": {
+ "label": "Pokedex - Ekans",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_024": {
+ "label": "Pokedex - Arbok",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_025": {
+ "label": "Pokedex - Pikachu",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_026": {
+ "label": "Pokedex - Raichu",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_027": {
+ "label": "Pokedex - Sandshrew",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_028": {
+ "label": "Pokedex - Sandslash",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_029": {
+ "label": "Pokedex - Nidoran Female",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_030": {
+ "label": "Pokedex - Nidorina",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_031": {
+ "label": "Pokedex - Nidoqueen",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_032": {
+ "label": "Pokedex - Nidoran Male",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_033": {
+ "label": "Pokedex - Nidorino",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_034": {
+ "label": "Pokedex - Nidoking",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_035": {
+ "label": "Pokedex - Clefairy",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_036": {
+ "label": "Pokedex - Clefable",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_037": {
+ "label": "Pokedex - Vulpix",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_038": {
+ "label": "Pokedex - Ninetales",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_039": {
+ "label": "Pokedex - Jigglypuff",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_040": {
+ "label": "Pokedex - Wigglytuff",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_041": {
+ "label": "Pokedex - Zubat",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_042": {
+ "label": "Pokedex - Golbat",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_043": {
+ "label": "Pokedex - Oddish",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_044": {
+ "label": "Pokedex - Gloom",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_045": {
+ "label": "Pokedex - Vileplume",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_046": {
+ "label": "Pokedex - Paras",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_047": {
+ "label": "Pokedex - Parasect",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_048": {
+ "label": "Pokedex - Venonat",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_049": {
+ "label": "Pokedex - Venomoth",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_050": {
+ "label": "Pokedex - Diglett",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_051": {
+ "label": "Pokedex - Dugtrio",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_052": {
+ "label": "Pokedex - Meowth",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_053": {
+ "label": "Pokedex - Persian",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_054": {
+ "label": "Pokedex - Psyduck",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_055": {
+ "label": "Pokedex - Golduck",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_056": {
+ "label": "Pokedex - Mankey",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_057": {
+ "label": "Pokedex - Primeape",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_058": {
+ "label": "Pokedex - Growlithe",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_059": {
+ "label": "Pokedex - Arcanine",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_060": {
+ "label": "Pokedex - Poliwag",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_061": {
+ "label": "Pokedex - Poliwhirl",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_062": {
+ "label": "Pokedex - Poliwrath",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_063": {
+ "label": "Pokedex - Abra",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_064": {
+ "label": "Pokedex - Kadabra",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_065": {
+ "label": "Pokedex - Alakazam",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_066": {
+ "label": "Pokedex - Machop",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_067": {
+ "label": "Pokedex - Machoke",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_068": {
+ "label": "Pokedex - Machamp",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_069": {
+ "label": "Pokedex - Bellsprout",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_070": {
+ "label": "Pokedex - Weepinbell",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_071": {
+ "label": "Pokedex - Victreebel",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_072": {
+ "label": "Pokedex - Tentacool",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_073": {
+ "label": "Pokedex - Tentacruel",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_074": {
+ "label": "Pokedex - Geodude",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_075": {
+ "label": "Pokedex - Graveler",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_076": {
+ "label": "Pokedex - Golem",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_077": {
+ "label": "Pokedex - Ponyta",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_078": {
+ "label": "Pokedex - Rapidash",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_079": {
+ "label": "Pokedex - Slowpoke",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_080": {
+ "label": "Pokedex - Slowbro",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_081": {
+ "label": "Pokedex - Magnemite",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_082": {
+ "label": "Pokedex - Magneton",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_083": {
+ "label": "Pokedex - Farfetch'd",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_084": {
+ "label": "Pokedex - Doduo",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_085": {
+ "label": "Pokedex - Dodrio",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_086": {
+ "label": "Pokedex - Seel",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_087": {
+ "label": "Pokedex - Dewgong",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_088": {
+ "label": "Pokedex - Grimer",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_089": {
+ "label": "Pokedex - Muk",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_090": {
+ "label": "Pokedex - Shellder",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_091": {
+ "label": "Pokedex - Cloyster",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_092": {
+ "label": "Pokedex - Gastly",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_093": {
+ "label": "Pokedex - Haunter",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_094": {
+ "label": "Pokedex - Gengar",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_095": {
+ "label": "Pokedex - Onix",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_096": {
+ "label": "Pokedex - Drowzee",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_097": {
+ "label": "Pokedex - Hypno",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_098": {
+ "label": "Pokedex - Krabby",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_099": {
+ "label": "Pokedex - Kingler",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_100": {
+ "label": "Pokedex - Voltorb",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_101": {
+ "label": "Pokedex - Electrode",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_102": {
+ "label": "Pokedex - Exeggcute",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_103": {
+ "label": "Pokedex - Exeggutor",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_104": {
+ "label": "Pokedex - Cubone",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_105": {
+ "label": "Pokedex - Marowak",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_106": {
+ "label": "Pokedex - Hitmonlee",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_107": {
+ "label": "Pokedex - Hitmonchan",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_108": {
+ "label": "Pokedex - Lickitung",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_109": {
+ "label": "Pokedex - Koffing",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_110": {
+ "label": "Pokedex - Weezing",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_111": {
+ "label": "Pokedex - Rhyhorn",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_112": {
+ "label": "Pokedex - Rhydon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_113": {
+ "label": "Pokedex - Chansey",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_114": {
+ "label": "Pokedex - Tangela",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_115": {
+ "label": "Pokedex - Kangaskhan",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_116": {
+ "label": "Pokedex - Horsea",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_117": {
+ "label": "Pokedex - Seadra",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_118": {
+ "label": "Pokedex - Goldeen",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_119": {
+ "label": "Pokedex - Seaking",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_120": {
+ "label": "Pokedex - Staryu",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_121": {
+ "label": "Pokedex - Starmie",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_122": {
+ "label": "Pokedex - Mr. Mime",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_123": {
+ "label": "Pokedex - Scyther",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_124": {
+ "label": "Pokedex - Jynx",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_125": {
+ "label": "Pokedex - Electabuzz",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_126": {
+ "label": "Pokedex - Magmar",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_127": {
+ "label": "Pokedex - Pinsir",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_128": {
+ "label": "Pokedex - Tauros",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_129": {
+ "label": "Pokedex - Magikarp",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_130": {
+ "label": "Pokedex - Gyarados",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_131": {
+ "label": "Pokedex - Lapras",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_132": {
+ "label": "Pokedex - Ditto",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_133": {
+ "label": "Pokedex - Eevee",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_134": {
+ "label": "Pokedex - Vaporeon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_135": {
+ "label": "Pokedex - Jolteon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_136": {
+ "label": "Pokedex - Flareon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_137": {
+ "label": "Pokedex - Porygon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_138": {
+ "label": "Pokedex - Omanyte",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_139": {
+ "label": "Pokedex - Omastar",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_140": {
+ "label": "Pokedex - Kabuto",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_141": {
+ "label": "Pokedex - Kabutops",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_142": {
+ "label": "Pokedex - Aerodactyl",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_143": {
+ "label": "Pokedex - Snorlax",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_144": {
+ "label": "Pokedex - Articuno",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_145": {
+ "label": "Pokedex - Zapdos",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_146": {
+ "label": "Pokedex - Moltres",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_147": {
+ "label": "Pokedex - Dratini",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_148": {
+ "label": "Pokedex - Dragonair",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_149": {
+ "label": "Pokedex - Dragonite",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_150": {
+ "label": "Pokedex - Mewtwo",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_151": {
+ "label": "Pokedex - Mew",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_152": {
+ "label": "Pokedex - Chikorita",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_153": {
+ "label": "Pokedex - Bayleef",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_154": {
+ "label": "Pokedex - Meganium",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_155": {
+ "label": "Pokedex - Cyndaquil",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_156": {
+ "label": "Pokedex - Quilava",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_157": {
+ "label": "Pokedex - Typhlosion",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_158": {
+ "label": "Pokedex - Totodile",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_159": {
+ "label": "Pokedex - Croconaw",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_160": {
+ "label": "Pokedex - Feraligatr",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_161": {
+ "label": "Pokedex - Sentret",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_162": {
+ "label": "Pokedex - Furret",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_163": {
+ "label": "Pokedex - Hoothoot",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_164": {
+ "label": "Pokedex - Noctowl",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_165": {
+ "label": "Pokedex - Ledyba",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_166": {
+ "label": "Pokedex - Ledian",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_167": {
+ "label": "Pokedex - Spinarak",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_168": {
+ "label": "Pokedex - Ariados",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_169": {
+ "label": "Pokedex - Crobat",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_170": {
+ "label": "Pokedex - Chinchou",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_171": {
+ "label": "Pokedex - Lanturn",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_172": {
+ "label": "Pokedex - Pichu",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_173": {
+ "label": "Pokedex - Cleffa",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_174": {
+ "label": "Pokedex - Igglybuff",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_175": {
+ "label": "Pokedex - Togepi",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_176": {
+ "label": "Pokedex - Togetic",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_177": {
+ "label": "Pokedex - Natu",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_178": {
+ "label": "Pokedex - Xatu",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_179": {
+ "label": "Pokedex - Mareep",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_180": {
+ "label": "Pokedex - Flaaffy",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_181": {
+ "label": "Pokedex - Ampharos",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_182": {
+ "label": "Pokedex - Bellossom",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_183": {
+ "label": "Pokedex - Marill",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_184": {
+ "label": "Pokedex - Azumarill",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_185": {
+ "label": "Pokedex - Sudowoodo",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_186": {
+ "label": "Pokedex - Politoed",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_187": {
+ "label": "Pokedex - Hoppip",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_188": {
+ "label": "Pokedex - Skiploom",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_189": {
+ "label": "Pokedex - Jumpluff",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_190": {
+ "label": "Pokedex - Aipom",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_191": {
+ "label": "Pokedex - Sunkern",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_192": {
+ "label": "Pokedex - Sunflora",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_193": {
+ "label": "Pokedex - Yanma",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_194": {
+ "label": "Pokedex - Wooper",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_195": {
+ "label": "Pokedex - Quagsire",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_196": {
+ "label": "Pokedex - Espeon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_197": {
+ "label": "Pokedex - Umbreon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_198": {
+ "label": "Pokedex - Murkrow",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_199": {
+ "label": "Pokedex - Slowking",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_200": {
+ "label": "Pokedex - Misdreavus",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_201": {
+ "label": "Pokedex - Unown",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_202": {
+ "label": "Pokedex - Wobbuffet",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_203": {
+ "label": "Pokedex - Girafarig",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_204": {
+ "label": "Pokedex - Pineco",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_205": {
+ "label": "Pokedex - Forretress",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_206": {
+ "label": "Pokedex - Dunsparce",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_207": {
+ "label": "Pokedex - Gligar",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_208": {
+ "label": "Pokedex - Steelix",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_209": {
+ "label": "Pokedex - Snubbull",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_210": {
+ "label": "Pokedex - Granbull",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_211": {
+ "label": "Pokedex - Qwilfish",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_212": {
+ "label": "Pokedex - Scizor",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_213": {
+ "label": "Pokedex - Shuckle",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_214": {
+ "label": "Pokedex - Heracross",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_215": {
+ "label": "Pokedex - Sneasel",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_216": {
+ "label": "Pokedex - Teddiursa",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_217": {
+ "label": "Pokedex - Ursaring",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_218": {
+ "label": "Pokedex - Slugma",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_219": {
+ "label": "Pokedex - Magcargo",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_220": {
+ "label": "Pokedex - Swinub",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_221": {
+ "label": "Pokedex - Piloswine",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_222": {
+ "label": "Pokedex - Corsola",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_223": {
+ "label": "Pokedex - Remoraid",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_224": {
+ "label": "Pokedex - Octillery",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_225": {
+ "label": "Pokedex - Delibird",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_226": {
+ "label": "Pokedex - Mantine",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_227": {
+ "label": "Pokedex - Skarmory",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_228": {
+ "label": "Pokedex - Houndour",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_229": {
+ "label": "Pokedex - Houndoom",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_230": {
+ "label": "Pokedex - Kingdra",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_231": {
+ "label": "Pokedex - Phanpy",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_232": {
+ "label": "Pokedex - Donphan",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_233": {
+ "label": "Pokedex - Porygon2",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_234": {
+ "label": "Pokedex - Stantler",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_235": {
+ "label": "Pokedex - Smeargle",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_236": {
+ "label": "Pokedex - Tyrogue",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_237": {
+ "label": "Pokedex - Hitmontop",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_238": {
+ "label": "Pokedex - Smoochum",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_239": {
+ "label": "Pokedex - Elekid",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_240": {
+ "label": "Pokedex - Magby",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_241": {
+ "label": "Pokedex - Miltank",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_242": {
+ "label": "Pokedex - Blissey",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_243": {
+ "label": "Pokedex - Raikou",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_244": {
+ "label": "Pokedex - Entei",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_245": {
+ "label": "Pokedex - Suicune",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_246": {
+ "label": "Pokedex - Larvitar",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_247": {
+ "label": "Pokedex - Pupitar",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_248": {
+ "label": "Pokedex - Tyranitar",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_249": {
+ "label": "Pokedex - Lugia",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_250": {
+ "label": "Pokedex - Ho-oh",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_251": {
+ "label": "Pokedex - Celebi",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_252": {
+ "label": "Pokedex - Treecko",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_253": {
+ "label": "Pokedex - Grovyle",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_254": {
+ "label": "Pokedex - Sceptile",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_255": {
+ "label": "Pokedex - Torchic",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_256": {
+ "label": "Pokedex - Combusken",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_257": {
+ "label": "Pokedex - Blaziken",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_258": {
+ "label": "Pokedex - Mudkip",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_259": {
+ "label": "Pokedex - Marshtomp",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_260": {
+ "label": "Pokedex - Swampert",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_261": {
+ "label": "Pokedex - Poochyena",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_262": {
+ "label": "Pokedex - Mightyena",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_263": {
+ "label": "Pokedex - Zigzagoon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_264": {
+ "label": "Pokedex - Linoone",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_265": {
+ "label": "Pokedex - Wurmple",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_266": {
+ "label": "Pokedex - Silcoon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_267": {
+ "label": "Pokedex - Beautifly",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_268": {
+ "label": "Pokedex - Cascoon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_269": {
+ "label": "Pokedex - Dustox",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_270": {
+ "label": "Pokedex - Lotad",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_271": {
+ "label": "Pokedex - Lombre",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_272": {
+ "label": "Pokedex - Ludicolo",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_273": {
+ "label": "Pokedex - Seedot",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_274": {
+ "label": "Pokedex - Nuzleaf",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_275": {
+ "label": "Pokedex - Shiftry",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_276": {
+ "label": "Pokedex - Taillow",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_277": {
+ "label": "Pokedex - Swellow",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_278": {
+ "label": "Pokedex - Wingull",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_279": {
+ "label": "Pokedex - Pelipper",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_280": {
+ "label": "Pokedex - Ralts",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_281": {
+ "label": "Pokedex - Kirlia",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_282": {
+ "label": "Pokedex - Gardevoir",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_283": {
+ "label": "Pokedex - Surskit",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_284": {
+ "label": "Pokedex - Masquerain",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_285": {
+ "label": "Pokedex - Shroomish",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_286": {
+ "label": "Pokedex - Breloom",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_287": {
+ "label": "Pokedex - Slakoth",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_288": {
+ "label": "Pokedex - Vigoroth",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_289": {
+ "label": "Pokedex - Slaking",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_290": {
+ "label": "Pokedex - Nincada",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_291": {
+ "label": "Pokedex - Ninjask",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_292": {
+ "label": "Pokedex - Shedinja",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_293": {
+ "label": "Pokedex - Whismur",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_294": {
+ "label": "Pokedex - Loudred",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_295": {
+ "label": "Pokedex - Exploud",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_296": {
+ "label": "Pokedex - Makuhita",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_297": {
+ "label": "Pokedex - Hariyama",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_298": {
+ "label": "Pokedex - Azurill",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_299": {
+ "label": "Pokedex - Nosepass",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_300": {
+ "label": "Pokedex - Skitty",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_301": {
+ "label": "Pokedex - Delcatty",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_302": {
+ "label": "Pokedex - Sableye",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_303": {
+ "label": "Pokedex - Mawile",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_304": {
+ "label": "Pokedex - Aron",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_305": {
+ "label": "Pokedex - Lairon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_306": {
+ "label": "Pokedex - Aggron",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_307": {
+ "label": "Pokedex - Meditite",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_308": {
+ "label": "Pokedex - Medicham",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_309": {
+ "label": "Pokedex - Electrike",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_310": {
+ "label": "Pokedex - Manectric",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_311": {
+ "label": "Pokedex - Plusle",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_312": {
+ "label": "Pokedex - Minun",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_313": {
+ "label": "Pokedex - Volbeat",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_314": {
+ "label": "Pokedex - Illumise",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_315": {
+ "label": "Pokedex - Roselia",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_316": {
+ "label": "Pokedex - Gulpin",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_317": {
+ "label": "Pokedex - Swalot",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_318": {
+ "label": "Pokedex - Carvanha",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_319": {
+ "label": "Pokedex - Sharpedo",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_320": {
+ "label": "Pokedex - Wailmer",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_321": {
+ "label": "Pokedex - Wailord",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_322": {
+ "label": "Pokedex - Numel",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_323": {
+ "label": "Pokedex - Camerupt",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_324": {
+ "label": "Pokedex - Torkoal",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_325": {
+ "label": "Pokedex - Spoink",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_326": {
+ "label": "Pokedex - Grumpig",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_327": {
+ "label": "Pokedex - Spinda",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_328": {
+ "label": "Pokedex - Trapinch",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_329": {
+ "label": "Pokedex - Vibrava",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_330": {
+ "label": "Pokedex - Flygon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_331": {
+ "label": "Pokedex - Cacnea",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_332": {
+ "label": "Pokedex - Cacturne",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_333": {
+ "label": "Pokedex - Swablu",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_334": {
+ "label": "Pokedex - Altaria",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_335": {
+ "label": "Pokedex - Zangoose",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_336": {
+ "label": "Pokedex - Seviper",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_337": {
+ "label": "Pokedex - Lunatone",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_338": {
+ "label": "Pokedex - Solrock",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_339": {
+ "label": "Pokedex - Barboach",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_340": {
+ "label": "Pokedex - Whiscash",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_341": {
+ "label": "Pokedex - Corphish",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_342": {
+ "label": "Pokedex - Crawdaunt",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_343": {
+ "label": "Pokedex - Baltoy",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_344": {
+ "label": "Pokedex - Claydol",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_345": {
+ "label": "Pokedex - Lileep",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_346": {
+ "label": "Pokedex - Cradily",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_347": {
+ "label": "Pokedex - Anorith",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_348": {
+ "label": "Pokedex - Armaldo",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_349": {
+ "label": "Pokedex - Feebas",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_350": {
+ "label": "Pokedex - Milotic",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_351": {
+ "label": "Pokedex - Castform",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_352": {
+ "label": "Pokedex - Kecleon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_353": {
+ "label": "Pokedex - Shuppet",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_354": {
+ "label": "Pokedex - Banette",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_355": {
+ "label": "Pokedex - Duskull",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_356": {
+ "label": "Pokedex - Dusclops",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_357": {
+ "label": "Pokedex - Tropius",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_358": {
+ "label": "Pokedex - Chimecho",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_359": {
+ "label": "Pokedex - Absol",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_360": {
+ "label": "Pokedex - Wynaut",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_361": {
+ "label": "Pokedex - Snorunt",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_362": {
+ "label": "Pokedex - Glalie",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_363": {
+ "label": "Pokedex - Spheal",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_364": {
+ "label": "Pokedex - Sealeo",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_365": {
+ "label": "Pokedex - Walrein",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_366": {
+ "label": "Pokedex - Clamperl",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_367": {
+ "label": "Pokedex - Huntail",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_368": {
+ "label": "Pokedex - Gorebyss",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_369": {
+ "label": "Pokedex - Relicanth",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_370": {
+ "label": "Pokedex - Luvdisc",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_371": {
+ "label": "Pokedex - Bagon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_372": {
+ "label": "Pokedex - Shelgon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_373": {
+ "label": "Pokedex - Salamence",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_374": {
+ "label": "Pokedex - Beldum",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_375": {
+ "label": "Pokedex - Metang",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_376": {
+ "label": "Pokedex - Metagross",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_377": {
+ "label": "Pokedex - Regirock",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_378": {
+ "label": "Pokedex - Regice",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_379": {
+ "label": "Pokedex - Registeel",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_380": {
+ "label": "Pokedex - Latias",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_381": {
+ "label": "Pokedex - Latios",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_382": {
+ "label": "Pokedex - Kyogre",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_383": {
+ "label": "Pokedex - Groudon",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_384": {
+ "label": "Pokedex - Rayquaza",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_385": {
+ "label": "Pokedex - Jirachi",
+ "tags": ["Pokedex"]
+ },
+ "POKEDEX_REWARD_386": {
+ "label": "Pokedex - Deoxys",
+ "tags": ["Pokedex"]
+ },
+
+ "TRAINER_AARON_REWARD": {
+ "label": "Route 134 - Dragon Tamer Aaron",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ABIGAIL_1_REWARD": {
+ "label": "Route 110 - Triathlete Abigail",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_AIDAN_REWARD": {
+ "label": "Route 127 - Bird Keeper Aidan",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_AISHA_REWARD": {
+ "label": "Route 117 - Battle Girl Aisha",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ALBERTO_REWARD": {
+ "label": "Route 123 - Bird Keeper Alberto",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ALBERT_REWARD": {
+ "label": "Victory Road 1F - Cooltrainer Albert",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ALEXA_REWARD": {
+ "label": "Route 128 - Cooltrainer Alexa",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ALEXIA_REWARD": {
+ "label": "Petalburg Gym - Cooltrainer Alexia",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ALEX_REWARD": {
+ "label": "Route 134 - Bird Keeper Alex",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ALICE_REWARD": {
+ "label": "Route 109 - Swimmer Alice",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ALIX_REWARD": {
+ "label": "Route 115 - Psychic Alix",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ALLEN_REWARD": {
+ "label": "Route 102 - Youngster Allen",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ALLISON_REWARD": {
+ "label": "Route 129 - Triathlete Allison",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ALYSSA_REWARD": {
+ "label": "Route 110 - Triathlete Alyssa",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_AMY_AND_LIV_1_REWARD": {
+ "label": "Route 103 - Twins Amy and Liv",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ANNA_AND_MEG_1_REWARD": {
+ "label": "Route 117 - Sr. and Jr. Anna and Meg",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ANDREA_REWARD": {
+ "label": "Sootopolis Gym - Lass Andrea",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ANDRES_1_REWARD": {
+ "label": "Route 105 - Ruin Maniac Andres",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ANDREW_REWARD": {
+ "label": "Route 103 - Fisherman Andrew",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ANGELICA_REWARD": {
+ "label": "Route 120 - Parasol Lady Angelica",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ANGELINA_REWARD": {
+ "label": "Route 114 - Picnicker Angelina",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ANGELO_REWARD": {
+ "label": "Mauville Gym - Bug Maniac Angelo",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ANNIKA_REWARD": {
+ "label": "Sootopolis Gym - Pokefan Annika",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ANTHONY_REWARD": {
+ "label": "Route 110 - Triathlete Anthony",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ARCHIE_REWARD": {
+ "label": "Seafloor Cavern Room 9 - Aqua Leader Archie",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ASHLEY_REWARD": {
+ "label": "Fortree Gym - Picnicker Ashley",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ATHENA_REWARD": {
+ "label": "Route 127 - Cooltrainer Athena",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ATSUSHI_REWARD": {
+ "label": "Mt Pyre 5F - Black Belt Atsushi",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_AURON_REWARD": {
+ "label": "Route 125 - Expert Auron",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_AUSTINA_REWARD": {
+ "label": "Route 109 - Tuber Austina",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_AUTUMN_REWARD": {
+ "label": "Jagged Pass - Picnicker Autumn",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_AXLE_REWARD": {
+ "label": "Lavaridge Gym - Kindler Axle",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BARNY_REWARD": {
+ "label": "Route 118 - Fisherman Barny",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BARRY_REWARD": {
+ "label": "Route 126 - Swimmer Barry",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BEAU_REWARD": {
+ "label": "Route 111 - Camper Beau",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BECKY_REWARD": {
+ "label": "Route 111 - Picnicker Becky",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BECK_REWARD": {
+ "label": "Route 133 - Bird Keeper Beck",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BENJAMIN_1_REWARD": {
+ "label": "Route 110 - Triathlete Benjamin",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BEN_REWARD": {
+ "label": "Mauville Gym - Youngster Ben",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BERKE_REWARD": {
+ "label": "Petalburg Gym - Cooltrainer Berke",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BERNIE_1_REWARD": {
+ "label": "Route 114 - Kindler Bernie",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BETHANY_REWARD": {
+ "label": "Sootopolis Gym - Pokefan Bethany",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BETH_REWARD": {
+ "label": "Route 107 - Swimmer Beth",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BEVERLY_REWARD": {
+ "label": "Route 105 - Swimmer Beverly",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BIANCA_REWARD": {
+ "label": "Route 111 - Picnicker Bianca",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BILLY_REWARD": {
+ "label": "Route 104 - Youngster Billy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BLAKE_REWARD": {
+ "label": "Mossdeep Gym - Psychic Blake",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRANDEN_REWARD": {
+ "label": "Route 111 - Camper Branden",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRANDI_REWARD": {
+ "label": "Route 117 - Psychic Brandi",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRAWLY_1_REWARD": {
+ "label": "Dewford Gym - Leader Brawly",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRAXTON_REWARD": {
+ "label": "Route 123 - Cooltrainer Braxton",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRENDAN_LILYCOVE_MUDKIP_REWARD": {
+ "label": "Lilycove City - Rival Brendan/May",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRENDAN_ROUTE_103_MUDKIP_REWARD": {
+ "label": "Route 103 - Rival Brendan/May",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRENDAN_ROUTE_110_MUDKIP_REWARD": {
+ "label": "Route 110 - Rival Brendan/May",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRENDAN_ROUTE_119_MUDKIP_REWARD": {
+ "label": "Route 119 - Rival Brendan/May",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRENDAN_RUSTBORO_MUDKIP_REWARD": {
+ "label": "Rustboro City - Rival Brendan/May",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRENDA_REWARD": {
+ "label": "Route 126 - Swimmer Brenda",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRENDEN_REWARD": {
+ "label": "Dewford Gym - Sailor Brenden",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRENT_REWARD": {
+ "label": "Route 119 - Bug Maniac Brent",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRIANNA_REWARD": {
+ "label": "Sootopolis Gym - Lady Brianna",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRICE_REWARD": {
+ "label": "Route 112 - Hiker Brice",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRIDGET_REWARD": {
+ "label": "Sootopolis Gym - Beauty Bridget",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BROOKE_1_REWARD": {
+ "label": "Route 111 - Cooltrainer Brooke",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRYANT_REWARD": {
+ "label": "Route 112 - Kindler Bryant",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_BRYAN_REWARD": {
+ "label": "Route 111 - Ruin Maniac Bryan",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CALE_REWARD": {
+ "label": "Route 121 - Bug Maniac Cale",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CALLIE_REWARD": {
+ "label": "Route 120 - Battle Girl Callie",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CALVIN_1_REWARD": {
+ "label": "Route 102 - Youngster Calvin",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CAMDEN_REWARD": {
+ "label": "Route 127 - Triathlete Camden",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CAMERON_1_REWARD": {
+ "label": "Route 123 - Psychic Cameron",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CAMRON_REWARD": {
+ "label": "Route 107 - Triathlete Camron",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CARLEE_REWARD": {
+ "label": "Route 128 - Swimmer Carlee",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CAROLINA_REWARD": {
+ "label": "Route 108 - Cooltrainer Carolina",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CAROLINE_REWARD": {
+ "label": "Victory Road B2F - Cooltrainer Caroline",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CAROL_REWARD": {
+ "label": "Route 112 - Picnicker Carol",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CARTER_REWARD": {
+ "label": "Route 109 - Fisherman Carter",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CATHERINE_1_REWARD": {
+ "label": "Route 119 - Pokemon Ranger Catherine",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CEDRIC_REWARD": {
+ "label": "Mt Pyre 6F - Psychic Cedric",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CELIA_REWARD": {
+ "label": "Route 111 - Picnicker Celia",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CELINA_REWARD": {
+ "label": "Route 111 - Aroma Lady Celina",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CHAD_REWARD": {
+ "label": "Route 124 - Swimmer Chad",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CHANDLER_REWARD": {
+ "label": "Route 109 - Tuber Chandler",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CHARLIE_REWARD": {
+ "label": "Abandoned Ship 1F - Tuber Charlie",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CHARLOTTE_REWARD": {
+ "label": "Route 114 - Picnicker Charlotte",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CHASE_REWARD": {
+ "label": "Route 129 - Triathlete Chase",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CHESTER_REWARD": {
+ "label": "Route 118 - Bird Keeper Chester",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CHIP_REWARD": {
+ "label": "Route 120 - Ruin Maniac Chip",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CHRIS_REWARD": {
+ "label": "Route 119 - Fisherman Chris",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CINDY_1_REWARD": {
+ "label": "Route 104 - Lady Cindy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CLARENCE_REWARD": {
+ "label": "Route 129 - Swimmer Clarence",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CLARISSA_REWARD": {
+ "label": "Route 120 - Parasol Lady Clarissa",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CLARK_REWARD": {
+ "label": "Route 116 - Hiker Clark",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CLAUDE_REWARD": {
+ "label": "Route 114 - Fisherman Claude",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CLIFFORD_REWARD": {
+ "label": "Mossdeep Gym - Gentleman Clifford",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_COBY_REWARD": {
+ "label": "Route 113 - Bird Keeper Coby",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_COLE_REWARD": {
+ "label": "Lavaridge Gym - Kindler Cole",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_COLIN_REWARD": {
+ "label": "Route 120 - Bird Keeper Colin",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_COLTON_REWARD": {
+ "label": "SS Tidal - Pokefan Colton",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CONNIE_REWARD": {
+ "label": "Sootopolis Gym - Beauty Connie",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CONOR_REWARD": {
+ "label": "Route 133 - Expert Conor",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CORY_1_REWARD": {
+ "label": "Route 108 - Sailor Cory",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CRISSY_REWARD": {
+ "label": "Sootopolis Gym - Lass Crissy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CRISTIAN_REWARD": {
+ "label": "Dewford Gym - Black Belt Cristian",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CRISTIN_1_REWARD": {
+ "label": "Route 121 - Cooltrainer Cristin",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_CYNDY_1_REWARD": {
+ "label": "Route 115 - Battle Girl Cyndy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DAISUKE_REWARD": {
+ "label": "Route 111 - Black Belt Daisuke",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DAISY_REWARD": {
+ "label": "Route 103 - Aroma Lady Daisy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DALE_REWARD": {
+ "label": "Route 110 - Fisherman Dale",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DALTON_1_REWARD": {
+ "label": "Route 118 - Guitarist Dalton",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DANA_REWARD": {
+ "label": "Route 132 - Swimmer Dana",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DANIELLE_REWARD": {
+ "label": "Lavaridge Gym - Battle Girl Danielle",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DAPHNE_REWARD": {
+ "label": "Sootopolis Gym - Lady Daphne",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DARCY_REWARD": {
+ "label": "Route 132 - Cooltrainer Darcy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DARIAN_REWARD": {
+ "label": "Route 104 - Fisherman Darian",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DARIUS_REWARD": {
+ "label": "Fortree Gym - Bird Keeper Darius",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DARRIN_REWARD": {
+ "label": "Route 107 - Swimmer Darrin",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DAVID_REWARD": {
+ "label": "Route 109 - Swimmer David",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DAVIS_REWARD": {
+ "label": "Route 123 - Bug Catcher Davis",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DAWSON_REWARD": {
+ "label": "Route 116 - Rich Boy Dawson",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DAYTON_REWARD": {
+ "label": "Route 119 - Kindler Dayton",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DEANDRE_REWARD": {
+ "label": "Route 118 - Youngster Deandre",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DEAN_REWARD": {
+ "label": "Route 126 - Swimmer Dean",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DEBRA_REWARD": {
+ "label": "Route 133 - Swimmer Debra",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DECLAN_REWARD": {
+ "label": "Route 124 - Swimmer Declan",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DEMETRIUS_REWARD": {
+ "label": "Abandoned Ship 1F - Youngster Demetrius",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DENISE_REWARD": {
+ "label": "Route 107 - Swimmer Denise",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DEREK_REWARD": {
+ "label": "Route 117 - Bug Maniac Derek",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DEVAN_REWARD": {
+ "label": "Route 116 - Hiker Devan",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DIANA_1_REWARD": {
+ "label": "Jagged Pass - Picnicker Diana",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DIANNE_REWARD": {
+ "label": "Victory Road B2F - Cooltrainer Dianne",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DILLON_REWARD": {
+ "label": "Route 113 - Youngster Dillon",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DOMINIK_REWARD": {
+ "label": "Route 105 - Swimmer Dominik",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DONALD_REWARD": {
+ "label": "Route 119 - Bug Maniac Donald",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DONNY_REWARD": {
+ "label": "Route 127 - Triathlete Donny",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DOUGLAS_REWARD": {
+ "label": "Route 106 - Swimmer Douglas",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DOUG_REWARD": {
+ "label": "Route 119 - Bug Catcher Doug",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DRAKE_REWARD": {
+ "label": "Ever Grande City - Elite Four Drake",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DREW_REWARD": {
+ "label": "Route 111 - Camper Drew",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DUNCAN_REWARD": {
+ "label": "Abandoned Ship B1F - Sailor Duncan",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DUSTY_1_REWARD": {
+ "label": "Route 111 - Ruin Maniac Dusty",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DWAYNE_REWARD": {
+ "label": "Route 109 - Sailor Dwayne",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DYLAN_1_REWARD": {
+ "label": "Route 117 - Triathlete Dylan",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_DEZ_AND_LUKE_REWARD": {
+ "label": "Mt Pyre 2F - Young Couple Dez and Luke",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_EDGAR_REWARD": {
+ "label": "Victory Road 1F - Cooltrainer Edgar",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_EDMOND_REWARD": {
+ "label": "Route 109 - Sailor Edmond",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_EDWARDO_REWARD": {
+ "label": "Fortree Gym - Bird Keeper Edwardo",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_EDWARD_REWARD": {
+ "label": "Route 110 - Psychic Edward",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_EDWIN_1_REWARD": {
+ "label": "Route 110 - Collector Edwin",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ED_REWARD": {
+ "label": "Route 123 - Collector Ed",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ELIJAH_REWARD": {
+ "label": "Route 109 - Bird Keeper Elijah",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ELI_REWARD": {
+ "label": "Lavaridge Gym - Hiker Eli",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ELLIOT_1_REWARD": {
+ "label": "Route 106 - Fisherman Elliot",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ERIC_REWARD": {
+ "label": "Jagged Pass - Hiker Eric",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ERNEST_1_REWARD": {
+ "label": "Route 125 - Sailor Ernest",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ETHAN_1_REWARD": {
+ "label": "Jagged Pass - Camper Ethan",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_FABIAN_REWARD": {
+ "label": "Route 119 - Guitarist Fabian",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_FELIX_REWARD": {
+ "label": "Victory Road B2F - Cooltrainer Felix",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_FERNANDO_1_REWARD": {
+ "label": "Route 123 - Guitarist Fernando",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_FLANNERY_1_REWARD": {
+ "label": "Lavaridge Gym - Leader Flannery",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_FLINT_REWARD": {
+ "label": "Fortree Gym - Camper Flint",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_FOSTER_REWARD": {
+ "label": "Route 105 - Ruin Maniac Foster",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_FRANKLIN_REWARD": {
+ "label": "Route 133 - Swimmer Franklin",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_FREDRICK_REWARD": {
+ "label": "Route 123 - Expert Fredrick",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GABRIELLE_1_REWARD": {
+ "label": "Mt Pyre 3F - Pokemon Breeder Gabrielle",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GARRET_REWARD": {
+ "label": "SS Tidal - Rich Boy Garret",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GARRISON_REWARD": {
+ "label": "Abandoned Ship 1F - Ruin Maniac Garrison",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GEORGE_REWARD": {
+ "label": "Petalburg Gym - Cooltrainer George",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GERALD_REWARD": {
+ "label": "Lavaridge Gym - Cooltrainer Gerald",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GILBERT_REWARD": {
+ "label": "Route 132 - Swimmer Gilbert",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GINA_AND_MIA_1_REWARD": {
+ "label": "Route 104 - Twins Gina and Mia",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GLACIA_REWARD": {
+ "label": "Ever Grande City - Elite Four Glacia",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRACE_REWARD": {
+ "label": "Route 124 - Swimmer Grace",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GREG_REWARD": {
+ "label": "Route 119 - Bug Catcher Greg",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_AQUA_HIDEOUT_1_REWARD": {
+ "label": "Aqua Hideout 1F - Team Aqua Grunt",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_AQUA_HIDEOUT_2_REWARD": {
+ "label": "Aqua Hideout B1F - Team Aqua Grunt 2",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_AQUA_HIDEOUT_3_REWARD": {
+ "label": "Aqua Hideout B1F - Team Aqua Grunt 4",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_AQUA_HIDEOUT_4_REWARD": {
+ "label": "Aqua Hideout B2F - Team Aqua Grunt 1",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_AQUA_HIDEOUT_5_REWARD": {
+ "label": "Aqua Hideout B1F - Team Aqua Grunt 1",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_AQUA_HIDEOUT_6_REWARD": {
+ "label": "Aqua Hideout B2F - Team Aqua Grunt 2",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_AQUA_HIDEOUT_7_REWARD": {
+ "label": "Aqua Hideout B1F - Team Aqua Grunt 3",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_AQUA_HIDEOUT_8_REWARD": {
+ "label": "Aqua Hideout B2F - Team Aqua Grunt 3",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_10_REWARD": {
+ "label": "Magma Hideout 3F - Team Magma Grunt 3",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_11_REWARD": {
+ "label": "Magma Hideout 4F - Team Magma Grunt 1",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_12_REWARD": {
+ "label": "Magma Hideout 4F - Team Magma Grunt 2",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_13_REWARD": {
+ "label": "Magma Hideout 4F - Team Magma Grunt 3",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_14_REWARD": {
+ "label": "Magma Hideout 2F - Team Magma Grunt 2",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_15_REWARD": {
+ "label": "Magma Hideout 2F - Team Magma Grunt 5",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_16_REWARD": {
+ "label": "Magma Hideout 3F - Team Magma Grunt 2",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_1_REWARD": {
+ "label": "Magma Hideout 1F - Team Magma Grunt 2",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_2_REWARD": {
+ "label": "Magma Hideout 1F - Team Magma Grunt 1",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_3_REWARD": {
+ "label": "Magma Hideout 2F - Team Magma Grunt 1",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_4_REWARD": {
+ "label": "Magma Hideout 2F - Team Magma Grunt 4",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_5_REWARD": {
+ "label": "Magma Hideout 2F - Team Magma Grunt 3",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_6_REWARD": {
+ "label": "Magma Hideout 2F - Team Magma Grunt 6",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_7_REWARD": {
+ "label": "Magma Hideout 2F - Team Magma Grunt 7",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_8_REWARD": {
+ "label": "Magma Hideout 2F - Team Magma Grunt 8",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_9_REWARD": {
+ "label": "Magma Hideout 3F - Team Magma Grunt 1",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MT_CHIMNEY_1_REWARD": {
+ "label": "Mt Chimney - Team Magma Grunt 1",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MT_CHIMNEY_2_REWARD": {
+ "label": "Mt Chimney - Team Magma Grunt 2",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MT_PYRE_1_REWARD": {
+ "label": "Mt Pyre Summit - Team Aqua Grunt 2",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MT_PYRE_2_REWARD": {
+ "label": "Mt Pyre Summit - Team Aqua Grunt 1",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MT_PYRE_3_REWARD": {
+ "label": "Mt Pyre Summit - Team Aqua Grunt 4",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MT_PYRE_4_REWARD": {
+ "label": "Mt Pyre Summit - Team Aqua Grunt 3",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MUSEUM_1_REWARD": {
+ "label": "Oceanic Museum - Team Aqua Grunt 1",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_MUSEUM_2_REWARD": {
+ "label": "Oceanic Museum - Team Aqua Grunt 2",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_PETALBURG_WOODS_REWARD": {
+ "label": "Petalburg Woods - Team Aqua Grunt",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_RUSTURF_TUNNEL_REWARD": {
+ "label": "Rusturf Tunnel - Team Aqua Grunt",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_SEAFLOOR_CAVERN_1_REWARD": {
+ "label": "Seafloor Cavern Room 1 - Team Aqua Grunt 1",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_SEAFLOOR_CAVERN_2_REWARD": {
+ "label": "Seafloor Cavern Room 1 - Team Aqua Grunt 2",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_SEAFLOOR_CAVERN_3_REWARD": {
+ "label": "Seafloor Cavern Room 4 - Team Aqua Grunt 1",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_SEAFLOOR_CAVERN_4_REWARD": {
+ "label": "Seafloor Cavern Room 4 - Team Aqua Grunt 2",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_SEAFLOOR_CAVERN_5_REWARD": {
+ "label": "Seafloor Cavern Room 3 - Team Aqua Grunt",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_SPACE_CENTER_1_REWARD": {
+ "label": "Space Center - Team Magma Grunt 2",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_SPACE_CENTER_2_REWARD": {
+ "label": "Space Center - Team Magma Grunt 4",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_SPACE_CENTER_3_REWARD": {
+ "label": "Space Center - Team Magma Grunt 1",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_SPACE_CENTER_4_REWARD": {
+ "label": "Space Center - Team Magma Grunt 3",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_SPACE_CENTER_5_REWARD": {
+ "label": "Space Center - Team Magma Grunt 5",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_SPACE_CENTER_6_REWARD": {
+ "label": "Space Center - Team Magma Grunt 6",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_SPACE_CENTER_7_REWARD": {
+ "label": "Space Center - Team Magma Grunt 7",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_WEATHER_INST_1_REWARD": {
+ "label": "Weather Institute 1F - Team Aqua Grunt 2",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_WEATHER_INST_2_REWARD": {
+ "label": "Weather Institute 2F - Team Aqua Grunt 1",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_WEATHER_INST_3_REWARD": {
+ "label": "Weather Institute 2F - Team Aqua Grunt 3",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_WEATHER_INST_4_REWARD": {
+ "label": "Weather Institute 1F - Team Aqua Grunt 1",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GRUNT_WEATHER_INST_5_REWARD": {
+ "label": "Weather Institute 2F - Team Aqua Grunt 2",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_GWEN_REWARD": {
+ "label": "Route 109 - Tuber Gwen",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HAILEY_REWARD": {
+ "label": "Route 109 - Tuber Hailey",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HALEY_1_REWARD": {
+ "label": "Route 104 - Lass Haley",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HALLE_REWARD": {
+ "label": "Victory Road B1F - Cooltrainer Halle",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HANNAH_REWARD": {
+ "label": "Mossdeep Gym - Psychic Hannah",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HARRISON_REWARD": {
+ "label": "Route 128 - Swimmer Harrison",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HAYDEN_REWARD": {
+ "label": "Route 111 - Kindler Hayden",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HECTOR_REWARD": {
+ "label": "Route 115 - Collector Hector",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HEIDI_REWARD": {
+ "label": "Route 111 - Picnicker Heidi",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HELENE_REWARD": {
+ "label": "Route 115 - Battle Girl Helene",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HENRY_REWARD": {
+ "label": "Route 127 - Fisherman Henry",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HERMAN_REWARD": {
+ "label": "Route 131 - Swimmer Herman",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HIDEO_REWARD": {
+ "label": "Route 119 - Ninja Boy Hideo",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HITOSHI_REWARD": {
+ "label": "Route 134 - Black Belt Hitoshi",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HOPE_REWARD": {
+ "label": "Victory Road 1F - Cooltrainer Hope",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HUDSON_REWARD": {
+ "label": "Route 134 - Sailor Hudson",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HUEY_REWARD": {
+ "label": "Route 109 - Sailor Huey",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HUGH_REWARD": {
+ "label": "Route 119 - Bird Keeper Hugh",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_HUMBERTO_REWARD": {
+ "label": "Fortree Gym - Bird Keeper Humberto",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_IMANI_REWARD": {
+ "label": "Route 105 - Swimmer Imani",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_IRENE_REWARD": {
+ "label": "Route 111 - Picnicker Irene",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ISAAC_1_REWARD": {
+ "label": "Route 117 - Pokemon Breeder Isaac",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ISABELLA_REWARD": {
+ "label": "Route 124 - Triathlete Isabella",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ISABELLE_REWARD": {
+ "label": "Route 103 - Swimmer Isabelle",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ISABEL_1_REWARD": {
+ "label": "Route 110 - Pokefan Isabel",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ISAIAH_1_REWARD": {
+ "label": "Route 128 - Triathlete Isaiah",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ISOBEL_REWARD": {
+ "label": "Route 126 - Triathlete Isobel",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_IVAN_REWARD": {
+ "label": "Route 104 - Fisherman Ivan",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JACE_REWARD": {
+ "label": "Lavaridge Gym - Kindler Jace",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JACKI_1_REWARD": {
+ "label": "Route 123 - Psychic Jacki",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JACKSON_1_REWARD": {
+ "label": "Route 119 - Pokemon Ranger Jackson",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JACK_REWARD": {
+ "label": "Route 134 - Swimmer Jack",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JACLYN_REWARD": {
+ "label": "Route 110 - Psychic Jaclyn",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JACOB_REWARD": {
+ "label": "Route 110 - Triathlete Jacob",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JAIDEN_REWARD": {
+ "label": "Route 115 - Ninja Boy Jaiden",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JAMES_1_REWARD": {
+ "label": "Petalburg Woods - Bug Catcher James",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JANICE_REWARD": {
+ "label": "Route 116 - Lass Janice",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JANI_REWARD": {
+ "label": "Abandoned Ship 1F - Tuber Jani",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JARED_REWARD": {
+ "label": "Fortree Gym - Bird Keeper Jared",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JASMINE_REWARD": {
+ "label": "Route 110 - Triathlete Jasmine",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JAYLEN_REWARD": {
+ "label": "Route 113 - Youngster Jaylen",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JAZMYN_REWARD": {
+ "label": "Route 123 - Cooltrainer Jazmyn",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JEFFREY_1_REWARD": {
+ "label": "Route 120 - Bug Maniac Jeffrey",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JEFF_REWARD": {
+ "label": "Lavaridge Gym - Kindler Jeff",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JENNA_REWARD": {
+ "label": "Route 120 - Pokemon Ranger Jenna",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JENNIFER_REWARD": {
+ "label": "Route 120 - Cooltrainer Jennifer",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JENNY_1_REWARD": {
+ "label": "Route 124 - Swimmer Jenny",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JEROME_REWARD": {
+ "label": "Route 108 - Swimmer Jerome",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JERRY_1_REWARD": {
+ "label": "Route 116 - School Kid Jerry",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JESSICA_1_REWARD": {
+ "label": "Route 121 - Beauty Jessica",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JOCELYN_REWARD": {
+ "label": "Dewford Gym - Battle Girl Jocelyn",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JODY_REWARD": {
+ "label": "Petalburg Gym - Cooltrainer Jody",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JOEY_REWARD": {
+ "label": "Route 116 - Youngster Joey",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JOHANNA_REWARD": {
+ "label": "Route 109 - Beauty Johanna",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JOHN_AND_JAY_1_REWARD": {
+ "label": "Meteor Falls 1F - Old Couple John and Jay",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JOHNSON_REWARD": {
+ "label": "Route 116 - Youngster Johnson",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JONAH_REWARD": {
+ "label": "Route 127 - Fisherman Jonah",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JONAS_REWARD": {
+ "label": "Route 123 - Ninja Boy Jonas",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JONATHAN_REWARD": {
+ "label": "Route 132 - Cooltrainer Jonathan",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JOSEPH_REWARD": {
+ "label": "Route 110 - Guitarist Joseph",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JOSE_REWARD": {
+ "label": "Route 116 - Bug Catcher Jose",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JOSH_REWARD": {
+ "label": "Rustboro Gym - Youngster Josh",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JOSUE_REWARD": {
+ "label": "Route 105 - Bird Keeper Josue",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JUAN_1_REWARD": {
+ "label": "Sootopolis Gym - Leader Juan",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JULIE_REWARD": {
+ "label": "Victory Road B2F - Cooltrainer Julie",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_JULIO_REWARD": {
+ "label": "Jagged Pass - Triathlete Julio",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KAI_REWARD": {
+ "label": "Route 114 - Fisherman Kai",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KALEB_REWARD": {
+ "label": "Route 110 - Pokefan Kaleb",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KARA_REWARD": {
+ "label": "Route 131 - Swimmer Kara",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KAREN_1_REWARD": {
+ "label": "Route 116 - School Kid Karen",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KATE_AND_JOY_REWARD": {
+ "label": "Route 121 - Sr. and Jr. Kate and Joy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KATELYNN_REWARD": {
+ "label": "Victory Road 1F - Cooltrainer Katelynn",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KATELYN_1_REWARD": {
+ "label": "Route 128 - Triathlete Katelyn",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KATHLEEN_REWARD": {
+ "label": "Mossdeep Gym - Hex Maniac Kathleen",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KATIE_REWARD": {
+ "label": "Route 130 - Swimmer Katie",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KAYLA_REWARD": {
+ "label": "Mt Pyre 3F - Psychic Kayla",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KAYLEY_REWARD": {
+ "label": "Route 123 - Parasol Lady Kayley",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KEEGAN_REWARD": {
+ "label": "Lavaridge Gym - Kindler Keegan",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KEIGO_REWARD": {
+ "label": "Route 120 - Ninja Boy Keigo",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KELVIN_REWARD": {
+ "label": "Route 134 - Sailor Kelvin",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KENT_REWARD": {
+ "label": "Route 119 - Bug Catcher Kent",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KEVIN_REWARD": {
+ "label": "Route 131 - Swimmer Kevin",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KIM_AND_IRIS_REWARD": {
+ "label": "Route 125 - Sr. and Jr. Kim and Iris",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KINDRA_REWARD": {
+ "label": "Route 123 - Hex Maniac Kindra",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KIRA_AND_DAN_1_REWARD": {
+ "label": "Abandoned Ship 1F - Young Couple Kira and Dan",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KIRK_REWARD": {
+ "label": "Mauville Gym - Guitarist Kirk",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KIYO_REWARD": {
+ "label": "Route 132 - Black Belt Kiyo",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KOICHI_REWARD": {
+ "label": "Route 115 - Black Belt Koichi",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KOJI_1_REWARD": {
+ "label": "Route 127 - Black Belt Koji",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KYLA_REWARD": {
+ "label": "Route 106 - Swimmer Kyla",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_KYRA_REWARD": {
+ "label": "Route 115 - Triathlete Kyra",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LAO_1_REWARD": {
+ "label": "Route 113 - Ninja Boy Lao",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LARRY_REWARD": {
+ "label": "Route 112 - Camper Larry",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LAURA_REWARD": {
+ "label": "Dewford Gym - Battle Girl Laura",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LAUREL_REWARD": {
+ "label": "Route 134 - Swimmer Laurel",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LAWRENCE_REWARD": {
+ "label": "Route 113 - Camper Lawrence",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LEA_AND_JED_REWARD": {
+ "label": "SS Tidal - Young Couple Lea and Jed",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LEAH_REWARD": {
+ "label": "Mt Pyre 2F - Hex Maniac Leah",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LENNY_REWARD": {
+ "label": "Route 114 - Hiker Lenny",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LEONARDO_REWARD": {
+ "label": "Route 126 - Swimmer Leonardo",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LEONARD_REWARD": {
+ "label": "SS Tidal - Sailor Leonard",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LEONEL_REWARD": {
+ "label": "Route 120 - Cooltrainer Leonel",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LILA_AND_ROY_1_REWARD": {
+ "label": "Route 124 - Sis and Bro Lila and Roy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LILITH_REWARD": {
+ "label": "Dewford Gym - Battle Girl Lilith",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LINDA_REWARD": {
+ "label": "Route 133 - Swimmer Linda",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LISA_AND_RAY_REWARD": {
+ "label": "Route 107 - Sis and Bro Lisa and Ray",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LOLA_1_REWARD": {
+ "label": "Route 109 - Tuber Lola",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LORENZO_REWARD": {
+ "label": "Route 120 - Pokemon Ranger Lorenzo",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LUCAS_1_REWARD": {
+ "label": "Route 114 - Hiker Lucas",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LUIS_REWARD": {
+ "label": "Route 105 - Swimmer Luis",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LUNG_REWARD": {
+ "label": "Route 113 - Ninja Boy Lung",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LYDIA_1_REWARD": {
+ "label": "Route 117 - Pokemon Breeder Lydia",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_LYLE_REWARD": {
+ "label": "Petalburg Woods - Bug Catcher Lyle",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MACEY_REWARD": {
+ "label": "Mossdeep Gym - Psychic Macey",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MADELINE_1_REWARD": {
+ "label": "Route 113 - Parasol Lady Madeline",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MAKAYLA_REWARD": {
+ "label": "Route 132 - Expert Makayla",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MARCEL_REWARD": {
+ "label": "Route 121 - Cooltrainer Marcel",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MARCOS_REWARD": {
+ "label": "Route 103 - Guitarist Marcos",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MARC_REWARD": {
+ "label": "Rustboro Gym - Hiker Marc",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MARIA_1_REWARD": {
+ "label": "Route 117 - Triathlete Maria",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MARK_REWARD": {
+ "label": "Mt Pyre 2F - Pokemaniac Mark",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MARLENE_REWARD": {
+ "label": "Route 115 - Psychic Marlene",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MARLEY_REWARD": {
+ "label": "Route 134 - Cooltrainer Marley",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MARY_REWARD": {
+ "label": "Petalburg Gym - Cooltrainer Mary",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MATTHEW_REWARD": {
+ "label": "Route 108 - Swimmer Matthew",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MATT_REWARD": {
+ "label": "Aqua Hideout B2F - Aqua Admin Matt",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MAURA_REWARD": {
+ "label": "Mossdeep Gym - Psychic Maura",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MAXIE_MAGMA_HIDEOUT_REWARD": {
+ "label": "Magma Hideout 4F - Magma Leader Maxie",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MAXIE_MT_CHIMNEY_REWARD": {
+ "label": "Mt Chimney - Magma Leader Maxie",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MEL_AND_PAUL_REWARD": {
+ "label": "Route 109 - Young Couple Mel and Paul",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MELINA_REWARD": {
+ "label": "Route 117 - Triathlete Melina",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MELISSA_REWARD": {
+ "label": "Mt Chimney - Beauty Melissa",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MICAH_REWARD": {
+ "label": "SS Tidal - Gentleman Micah",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MICHELLE_REWARD": {
+ "label": "Victory Road B1F - Cooltrainer Michelle",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MIGUEL_1_REWARD": {
+ "label": "Route 103 - Pokefan Miguel",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MIKE_2_REWARD": {
+ "label": "Rusturf Tunnel - Hiker Mike",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MISSY_REWARD": {
+ "label": "Route 108 - Swimmer Missy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MITCHELL_REWARD": {
+ "label": "Victory Road B1F - Cooltrainer Mitchell",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MIU_AND_YUKI_REWARD": {
+ "label": "Route 123 - Twins Miu and Yuki",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MOLLIE_REWARD": {
+ "label": "Route 133 - Expert Mollie",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_MYLES_REWARD": {
+ "label": "Route 121 - Pokemon Breeder Myles",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_NANCY_REWARD": {
+ "label": "Route 114 - Picnicker Nancy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_NAOMI_REWARD": {
+ "label": "SS Tidal - Lady Naomi",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_NATE_REWARD": {
+ "label": "Mossdeep Gym - Gentleman Nate",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_NED_REWARD": {
+ "label": "Route 106 - Fisherman Ned",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_NICHOLAS_REWARD": {
+ "label": "Mossdeep Gym - Psychic Nicholas",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_NICOLAS_1_REWARD": {
+ "label": "Meteor Falls 1F - Dragon Tamer Nicolas",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_NIKKI_REWARD": {
+ "label": "Route 126 - Swimmer Nikki",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_NOB_1_REWARD": {
+ "label": "Route 115 - Black Belt Nob",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_NOLAN_REWARD": {
+ "label": "Route 114 - Fisherman Nolan",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_NOLEN_REWARD": {
+ "label": "Route 125 - Swimmer Nolen",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_NORMAN_1_REWARD": {
+ "label": "Petalburg Gym - Leader Norman",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_OLIVIA_REWARD": {
+ "label": "Sootopolis Gym - Beauty Olivia",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_OWEN_REWARD": {
+ "label": "Victory Road B2F - Cooltrainer Owen",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_PABLO_1_REWARD": {
+ "label": "Route 126 - Triathlete Pablo",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_PARKER_REWARD": {
+ "label": "Petalburg Gym - Cooltrainer Parker",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_PAT_REWARD": {
+ "label": "Route 121 - Pokemon Breeder Pat",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_PAXTON_REWARD": {
+ "label": "Route 132 - Expert Paxton",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_PERRY_REWARD": {
+ "label": "Route 118 - Bird Keeper Perry",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_PETE_REWARD": {
+ "label": "Route 103 - Swimmer Pete",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_PHILLIP_REWARD": {
+ "label": "SS Tidal - Sailor Phillip",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_PHIL_REWARD": {
+ "label": "Route 119 - Bird Keeper Phil",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_PHOEBE_REWARD": {
+ "label": "Ever Grande City - Elite Four Phoebe",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_PRESLEY_REWARD": {
+ "label": "Route 125 - Bird Keeper Presley",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_PRESTON_REWARD": {
+ "label": "Mossdeep Gym - Psychic Preston",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_QUINCY_REWARD": {
+ "label": "Victory Road 1F - Cooltrainer Quincy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_RACHEL_REWARD": {
+ "label": "Route 119 - Parasol Lady Rachel",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_RANDALL_REWARD": {
+ "label": "Petalburg Gym - Cooltrainer Randall",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_REED_REWARD": {
+ "label": "Route 129 - Swimmer Reed",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_RELI_AND_IAN_REWARD": {
+ "label": "Route 131 - Sis and Bro Reli and Ian",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_REYNA_REWARD": {
+ "label": "Route 134 - Battle Girl Reyna",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_RHETT_REWARD": {
+ "label": "Route 103 - Black Belt Rhett",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_RICHARD_REWARD": {
+ "label": "Route 131 - Swimmer Richard",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_RICKY_1_REWARD": {
+ "label": "Route 109 - Tuber Ricky",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_RICK_REWARD": {
+ "label": "Route 102 - Bug Catcher Rick",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_RILEY_REWARD": {
+ "label": "Route 120 - Ninja Boy Riley",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ROBERT_1_REWARD": {
+ "label": "Route 120 - Bird Keeper Robert",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_RODNEY_REWARD": {
+ "label": "Route 130 - Swimmer Rodney",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ROGER_REWARD": {
+ "label": "Route 127 - Fisherman Roger",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ROLAND_REWARD": {
+ "label": "Route 124 - Swimmer Roland",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_RONALD_REWARD": {
+ "label": "Route 132 - Fisherman Ronald",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ROSE_1_REWARD": {
+ "label": "Route 118 - Aroma Lady Rose",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ROXANNE_1_REWARD": {
+ "label": "Rustboro Gym - Leader Roxanne",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_RUBEN_REWARD": {
+ "label": "Route 128 - Cooltrainer Ruben",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SAMANTHA_REWARD": {
+ "label": "Mossdeep Gym - Psychic Samantha",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SAMUEL_REWARD": {
+ "label": "Victory Road B1F - Cooltrainer Samuel",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SANTIAGO_REWARD": {
+ "label": "Route 130 - Swimmer Santiago",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SARAH_REWARD": {
+ "label": "Route 116 - Lady Sarah",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SAWYER_1_REWARD": {
+ "label": "Mt Chimney - Hiker Sawyer",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SHANE_REWARD": {
+ "label": "Route 114 - Camper Shane",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SHANNON_REWARD": {
+ "label": "Victory Road B1F - Cooltrainer Shannon",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SHARON_REWARD": {
+ "label": "Route 125 - Swimmer Sharon",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SHAWN_REWARD": {
+ "label": "Mauville Gym - Guitarist Shawn",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SHAYLA_REWARD": {
+ "label": "Route 112 - Aroma Lady Shayla",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SHEILA_REWARD": {
+ "label": "Mt Chimney - Beauty Sheila",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SHELBY_1_REWARD": {
+ "label": "Mt Chimney - Expert Shelby",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SHELLY_SEAFLOOR_CAVERN_REWARD": {
+ "label": "Seafloor Cavern Room 3 - Aqua Admin Shelly",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SHELLY_WEATHER_INSTITUTE_REWARD": {
+ "label": "Weather Institute 2F - Aqua Admin Shelly",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SHIRLEY_REWARD": {
+ "label": "Mt Chimney - Beauty Shirley",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SIDNEY_REWARD": {
+ "label": "Ever Grande City - Elite Four Sidney",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SIENNA_REWARD": {
+ "label": "Route 126 - Swimmer Sienna",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SIMON_REWARD": {
+ "label": "Route 109 - Tuber Simon",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SOPHIE_REWARD": {
+ "label": "Route 113 - Picnicker Sophie",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SPENCER_REWARD": {
+ "label": "Route 124 - Swimmer Spencer",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_STAN_REWARD": {
+ "label": "Route 125 - Swimmer Stan",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_STEVEN_REWARD": {
+ "label": "Meteor Falls 1F - Rival Steven",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_STEVE_1_REWARD": {
+ "label": "Route 114 - Pokemaniac Steve",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SUSIE_REWARD": {
+ "label": "Route 131 - Swimmer Susie",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_SYLVIA_REWARD": {
+ "label": "Mossdeep Gym - Hex Maniac Sylvia",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TABITHA_MAGMA_HIDEOUT_REWARD": {
+ "label": "Magma Hideout 4F - Magma Admin Tabitha",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TABITHA_MT_CHIMNEY_REWARD": {
+ "label": "Mt Chimney - Magma Admin Tabitha",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TAKAO_REWARD": {
+ "label": "Dewford Gym - Black Belt Takao",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TAKASHI_REWARD": {
+ "label": "Route 119 - Ninja Boy Takashi",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TALIA_REWARD": {
+ "label": "Route 131 - Triathlete Talia",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TAMMY_REWARD": {
+ "label": "Route 121 - Hex Maniac Tammy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TANYA_REWARD": {
+ "label": "Route 125 - Swimmer Tanya",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TARA_REWARD": {
+ "label": "Route 108 - Swimmer Tara",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TASHA_REWARD": {
+ "label": "Mt Pyre 4F - Hex Maniac Tasha",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TATE_AND_LIZA_1_REWARD": {
+ "label": "Mossdeep Gym - Leader Tate and Liza",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TAYLOR_REWARD": {
+ "label": "Route 119 - Bug Maniac Taylor",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TYRA_AND_IVY_REWARD": {
+ "label": "Route 114 - Sr. and Jr. Tyra and Ivy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_THALIA_1_REWARD": {
+ "label": "Abandoned Ship 1F - Beauty Thalia",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_THOMAS_REWARD": {
+ "label": "SS Tidal - Gentleman Thomas",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TIANA_REWARD": {
+ "label": "Route 102 - Lass Tiana",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TIFFANY_REWARD": {
+ "label": "Sootopolis Gym - Beauty Tiffany",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TIMMY_REWARD": {
+ "label": "Route 110 - Youngster Timmy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TIMOTHY_1_REWARD": {
+ "label": "Route 115 - Expert Timothy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TISHA_REWARD": {
+ "label": "Route 129 - Swimmer Tisha",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TOMMY_REWARD": {
+ "label": "Rustboro Gym - Youngster Tommy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TONY_1_REWARD": {
+ "label": "Route 107 - Swimmer Tony",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TORI_AND_TIA_REWARD": {
+ "label": "Route 113 - Twins Tori and Tia",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TRAVIS_REWARD": {
+ "label": "Route 111 - Camper Travis",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TRENT_1_REWARD": {
+ "label": "Route 112 - Hiker Trent",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_TYRON_REWARD": {
+ "label": "Route 111 - Camper Tyron",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_VALERIE_1_REWARD": {
+ "label": "Mt Pyre 6F - Hex Maniac Valerie",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_VANESSA_REWARD": {
+ "label": "Route 121 - Pokefan Vanessa",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_VICKY_REWARD": {
+ "label": "Route 111 - Winstrate Vicky",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_VICTORIA_REWARD": {
+ "label": "Route 111 - Winstrate Victoria",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_VICTOR_REWARD": {
+ "label": "Route 111 - Winstrate Victor",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_VIOLET_REWARD": {
+ "label": "Route 123 - Aroma Lady Violet",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_VIRGIL_REWARD": {
+ "label": "Mossdeep Gym - Psychic Virgil",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_VITO_REWARD": {
+ "label": "Victory Road B2F - Cooltrainer Vito",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_VIVIAN_REWARD": {
+ "label": "Mauville Gym - Battle Girl Vivian",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_VIVI_REWARD": {
+ "label": "Route 111 - Winstrate Vivi",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_WADE_REWARD": {
+ "label": "Route 118 - Fisherman Wade",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_WALLACE_REWARD": {
+ "label": "Ever Grande City - Champion Wallace",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_WALTER_1_REWARD": {
+ "label": "Route 121 - Gentleman Walter",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_WALLY_MAUVILLE_REWARD": {
+ "label": "Mauville City - Rival Wally",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_WALLY_VR_1_REWARD": {
+ "label": "Victory Road 1F - Rival Wally",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_WATTSON_1_REWARD": {
+ "label": "Mauville Gym - Leader Wattson",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_WARREN_REWARD": {
+ "label": "Route 133 - Cooltrainer Warren",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_WAYNE_REWARD": {
+ "label": "Route 128 - Fisherman Wayne",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_WENDY_REWARD": {
+ "label": "Route 123 - Cooltrainer Wendy",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_WILLIAM_REWARD": {
+ "label": "Mt Pyre 3F - Psychic William",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_WILTON_1_REWARD": {
+ "label": "Route 111 - Cooltrainer Wilton",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_WINONA_1_REWARD": {
+ "label": "Fortree Gym - Leader Winona",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_WINSTON_1_REWARD": {
+ "label": "Route 104 - Rich Boy Winston",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_WYATT_REWARD": {
+ "label": "Route 113 - Pokemaniac Wyatt",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_YASU_REWARD": {
+ "label": "Route 119 - Ninja Boy Yasu",
+ "tags": ["Trainer"]
+ },
+ "TRAINER_ZANDER_REWARD": {
+ "label": "Mt Pyre 2F - Black Belt Zander",
+ "tags": ["Trainer"]
}
}
diff --git a/worlds/pokemon_emerald/data/regions/battle_frontier.json b/worlds/pokemon_emerald/data/regions/battle_frontier.json
new file mode 100644
index 000000000000..a391129bd176
--- /dev/null
+++ b/worlds/pokemon_emerald/data/regions/battle_frontier.json
@@ -0,0 +1,458 @@
+{
+ "REGION_BATTLE_FRONTIER_RECEPTION_GATE/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_RECEPTION_GATE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_RECEPTION_GATE:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:8",
+ "MAP_BATTLE_FRONTIER_RECEPTION_GATE:1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:9"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/DOCK": {
+ "parent_map": "MAP_BATTLE_FRONTIER_OUTSIDE_WEST",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_SS_TIDAL_CORRIDOR/MAIN"
+ ],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:8/MAP_BATTLE_FRONTIER_RECEPTION_GATE:0"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_OUTSIDE_WEST",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/MAIN"
+ ],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:7/MAP_BATTLE_FRONTIER_LOUNGE7:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:2/MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1/MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:9/MAP_BATTLE_FRONTIER_RECEPTION_GATE:1",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:0/MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:5/MAP_BATTLE_FRONTIER_SCOTTS_HOUSE:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:3/MAP_BATTLE_FRONTIER_LOUNGE2:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:4/MAP_BATTLE_FRONTIER_MART:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:6/MAP_BATTLE_FRONTIER_LOUNGE4:0"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/WATER": {
+ "parent_map": "MAP_BATTLE_FRONTIER_OUTSIDE_WEST",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/WATER",
+ "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/CAVE_ENTRANCE"
+ ],
+ "warps": []
+ },
+ "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/CAVE_ENTRANCE": {
+ "parent_map": "MAP_BATTLE_FRONTIER_OUTSIDE_WEST",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/WATER"
+ ],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:10/MAP_ARTISAN_CAVE_B1F:0"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_OUTSIDE_EAST",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/MAIN",
+ "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/ABOVE_WATERFALL"
+ ],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:12/MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:5/MAP_BATTLE_FRONTIER_LOUNGE1:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:8/MAP_BATTLE_FRONTIER_LOUNGE6:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:6/MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:10/MAP_BATTLE_FRONTIER_LOUNGE8:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:0/MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:11/MAP_BATTLE_FRONTIER_LOUNGE9:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:7/MAP_BATTLE_FRONTIER_LOUNGE5:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:4/MAP_BATTLE_FRONTIER_RANKING_HALL:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:1/MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:3/MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY:0",
+ "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:9/MAP_BATTLE_FRONTIER_LOUNGE3:0"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/CAVE_ENTRANCE": {
+ "parent_map": "MAP_BATTLE_FRONTIER_OUTSIDE_EAST",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/MAIN"
+ ],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:13/MAP_ARTISAN_CAVE_1F:0"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/ABOVE_WATERFALL": {
+ "parent_map": "MAP_BATTLE_FRONTIER_OUTSIDE_EAST",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/MAIN",
+ "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/WATER"
+ ],
+ "warps": []
+ },
+ "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/WATER": {
+ "parent_map": "MAP_BATTLE_FRONTIER_OUTSIDE_EAST",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/ABOVE_WATERFALL",
+ "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/WATER"
+ ],
+ "warps": []
+ },
+ "REGION_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:2"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY:0,1,2/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:0"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:1"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:3"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_BATTLE_DOME_LOBBY/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:2"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:2/MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM:0",
+ "MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:0"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:2"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER:0,1,2/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:6"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_RANKING_HALL/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_RANKING_HALL",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_RANKING_HALL:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:4"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_POKEMON_CENTER_1F/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:2/MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:0",
+ "MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:12"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_POKEMON_CENTER_2F/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:0/MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:2"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_MART/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_MART",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_MART:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:4"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_SCOTTS_HOUSE/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_SCOTTS_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_SCOTTS_HOUSE:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:5"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_LOUNGE1/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE1",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_LOUNGE1:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:5"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_LOUNGE2/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE2",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_LOUNGE2:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:3"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_LOUNGE3/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE3",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_LOUNGE3:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:9"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_LOUNGE4/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE4",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_LOUNGE4:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:6"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_LOUNGE5/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE5",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_LOUNGE5:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:7"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_LOUNGE6/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE6",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_LOUNGE6:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:8"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_LOUNGE7/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE7",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_LOUNGE7:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:7"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_LOUNGE8/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE8",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_LOUNGE8:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:10"
+ ]
+ },
+ "REGION_BATTLE_FRONTIER_LOUNGE9/MAIN": {
+ "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE9",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_BATTLE_FRONTIER_LOUNGE9:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:11"
+ ]
+ },
+
+ "REGION_ARTISAN_CAVE_1F/MAIN": {
+ "parent_map": "MAP_ARTISAN_CAVE_1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "ITEM_ARTISAN_CAVE_1F_CARBOS"
+ ],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_ARTISAN_CAVE_1F:1/MAP_ARTISAN_CAVE_B1F:1",
+ "MAP_ARTISAN_CAVE_1F:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:13"
+ ]
+ },
+ "REGION_ARTISAN_CAVE_B1F/MAIN": {
+ "parent_map": "MAP_ARTISAN_CAVE_B1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "ITEM_ARTISAN_CAVE_B1F_HP_UP",
+ "HIDDEN_ITEM_ARTISAN_CAVE_B1F_ZINC",
+ "HIDDEN_ITEM_ARTISAN_CAVE_B1F_CALCIUM",
+ "HIDDEN_ITEM_ARTISAN_CAVE_B1F_PROTEIN",
+ "HIDDEN_ITEM_ARTISAN_CAVE_B1F_IRON"
+ ],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_ARTISAN_CAVE_B1F:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:10",
+ "MAP_ARTISAN_CAVE_B1F:1/MAP_ARTISAN_CAVE_1F:1"
+ ]
+ }
+}
diff --git a/worlds/pokemon_emerald/data/regions/cities.json b/worlds/pokemon_emerald/data/regions/cities.json
index d39c0cc847c0..063fb6a12b9b 100644
--- a/worlds/pokemon_emerald/data/regions/cities.json
+++ b/worlds/pokemon_emerald/data/regions/cities.json
@@ -1,6 +1,406 @@
{
+ "REGION_POKEDEX": {
+ "parent_map": "MAP_LITTLEROOT_TOWN",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "POKEDEX_REWARD_001",
+ "POKEDEX_REWARD_002",
+ "POKEDEX_REWARD_003",
+ "POKEDEX_REWARD_004",
+ "POKEDEX_REWARD_005",
+ "POKEDEX_REWARD_006",
+ "POKEDEX_REWARD_007",
+ "POKEDEX_REWARD_008",
+ "POKEDEX_REWARD_009",
+ "POKEDEX_REWARD_010",
+ "POKEDEX_REWARD_011",
+ "POKEDEX_REWARD_012",
+ "POKEDEX_REWARD_013",
+ "POKEDEX_REWARD_014",
+ "POKEDEX_REWARD_015",
+ "POKEDEX_REWARD_016",
+ "POKEDEX_REWARD_017",
+ "POKEDEX_REWARD_018",
+ "POKEDEX_REWARD_019",
+ "POKEDEX_REWARD_020",
+ "POKEDEX_REWARD_021",
+ "POKEDEX_REWARD_022",
+ "POKEDEX_REWARD_023",
+ "POKEDEX_REWARD_024",
+ "POKEDEX_REWARD_025",
+ "POKEDEX_REWARD_026",
+ "POKEDEX_REWARD_027",
+ "POKEDEX_REWARD_028",
+ "POKEDEX_REWARD_029",
+ "POKEDEX_REWARD_030",
+ "POKEDEX_REWARD_031",
+ "POKEDEX_REWARD_032",
+ "POKEDEX_REWARD_033",
+ "POKEDEX_REWARD_034",
+ "POKEDEX_REWARD_035",
+ "POKEDEX_REWARD_036",
+ "POKEDEX_REWARD_037",
+ "POKEDEX_REWARD_038",
+ "POKEDEX_REWARD_039",
+ "POKEDEX_REWARD_040",
+ "POKEDEX_REWARD_041",
+ "POKEDEX_REWARD_042",
+ "POKEDEX_REWARD_043",
+ "POKEDEX_REWARD_044",
+ "POKEDEX_REWARD_045",
+ "POKEDEX_REWARD_046",
+ "POKEDEX_REWARD_047",
+ "POKEDEX_REWARD_048",
+ "POKEDEX_REWARD_049",
+ "POKEDEX_REWARD_050",
+ "POKEDEX_REWARD_051",
+ "POKEDEX_REWARD_052",
+ "POKEDEX_REWARD_053",
+ "POKEDEX_REWARD_054",
+ "POKEDEX_REWARD_055",
+ "POKEDEX_REWARD_056",
+ "POKEDEX_REWARD_057",
+ "POKEDEX_REWARD_058",
+ "POKEDEX_REWARD_059",
+ "POKEDEX_REWARD_060",
+ "POKEDEX_REWARD_061",
+ "POKEDEX_REWARD_062",
+ "POKEDEX_REWARD_063",
+ "POKEDEX_REWARD_064",
+ "POKEDEX_REWARD_065",
+ "POKEDEX_REWARD_066",
+ "POKEDEX_REWARD_067",
+ "POKEDEX_REWARD_068",
+ "POKEDEX_REWARD_069",
+ "POKEDEX_REWARD_070",
+ "POKEDEX_REWARD_071",
+ "POKEDEX_REWARD_072",
+ "POKEDEX_REWARD_073",
+ "POKEDEX_REWARD_074",
+ "POKEDEX_REWARD_075",
+ "POKEDEX_REWARD_076",
+ "POKEDEX_REWARD_077",
+ "POKEDEX_REWARD_078",
+ "POKEDEX_REWARD_079",
+ "POKEDEX_REWARD_080",
+ "POKEDEX_REWARD_081",
+ "POKEDEX_REWARD_082",
+ "POKEDEX_REWARD_083",
+ "POKEDEX_REWARD_084",
+ "POKEDEX_REWARD_085",
+ "POKEDEX_REWARD_086",
+ "POKEDEX_REWARD_087",
+ "POKEDEX_REWARD_088",
+ "POKEDEX_REWARD_089",
+ "POKEDEX_REWARD_090",
+ "POKEDEX_REWARD_091",
+ "POKEDEX_REWARD_092",
+ "POKEDEX_REWARD_093",
+ "POKEDEX_REWARD_094",
+ "POKEDEX_REWARD_095",
+ "POKEDEX_REWARD_096",
+ "POKEDEX_REWARD_097",
+ "POKEDEX_REWARD_098",
+ "POKEDEX_REWARD_099",
+ "POKEDEX_REWARD_100",
+ "POKEDEX_REWARD_101",
+ "POKEDEX_REWARD_102",
+ "POKEDEX_REWARD_103",
+ "POKEDEX_REWARD_104",
+ "POKEDEX_REWARD_105",
+ "POKEDEX_REWARD_106",
+ "POKEDEX_REWARD_107",
+ "POKEDEX_REWARD_108",
+ "POKEDEX_REWARD_109",
+ "POKEDEX_REWARD_110",
+ "POKEDEX_REWARD_111",
+ "POKEDEX_REWARD_112",
+ "POKEDEX_REWARD_113",
+ "POKEDEX_REWARD_114",
+ "POKEDEX_REWARD_115",
+ "POKEDEX_REWARD_116",
+ "POKEDEX_REWARD_117",
+ "POKEDEX_REWARD_118",
+ "POKEDEX_REWARD_119",
+ "POKEDEX_REWARD_120",
+ "POKEDEX_REWARD_121",
+ "POKEDEX_REWARD_122",
+ "POKEDEX_REWARD_123",
+ "POKEDEX_REWARD_124",
+ "POKEDEX_REWARD_125",
+ "POKEDEX_REWARD_126",
+ "POKEDEX_REWARD_127",
+ "POKEDEX_REWARD_128",
+ "POKEDEX_REWARD_129",
+ "POKEDEX_REWARD_130",
+ "POKEDEX_REWARD_131",
+ "POKEDEX_REWARD_132",
+ "POKEDEX_REWARD_133",
+ "POKEDEX_REWARD_134",
+ "POKEDEX_REWARD_135",
+ "POKEDEX_REWARD_136",
+ "POKEDEX_REWARD_137",
+ "POKEDEX_REWARD_138",
+ "POKEDEX_REWARD_139",
+ "POKEDEX_REWARD_140",
+ "POKEDEX_REWARD_141",
+ "POKEDEX_REWARD_142",
+ "POKEDEX_REWARD_143",
+ "POKEDEX_REWARD_144",
+ "POKEDEX_REWARD_145",
+ "POKEDEX_REWARD_146",
+ "POKEDEX_REWARD_147",
+ "POKEDEX_REWARD_148",
+ "POKEDEX_REWARD_149",
+ "POKEDEX_REWARD_150",
+ "POKEDEX_REWARD_151",
+ "POKEDEX_REWARD_152",
+ "POKEDEX_REWARD_153",
+ "POKEDEX_REWARD_154",
+ "POKEDEX_REWARD_155",
+ "POKEDEX_REWARD_156",
+ "POKEDEX_REWARD_157",
+ "POKEDEX_REWARD_158",
+ "POKEDEX_REWARD_159",
+ "POKEDEX_REWARD_160",
+ "POKEDEX_REWARD_161",
+ "POKEDEX_REWARD_162",
+ "POKEDEX_REWARD_163",
+ "POKEDEX_REWARD_164",
+ "POKEDEX_REWARD_165",
+ "POKEDEX_REWARD_166",
+ "POKEDEX_REWARD_167",
+ "POKEDEX_REWARD_168",
+ "POKEDEX_REWARD_169",
+ "POKEDEX_REWARD_170",
+ "POKEDEX_REWARD_171",
+ "POKEDEX_REWARD_172",
+ "POKEDEX_REWARD_173",
+ "POKEDEX_REWARD_174",
+ "POKEDEX_REWARD_175",
+ "POKEDEX_REWARD_176",
+ "POKEDEX_REWARD_177",
+ "POKEDEX_REWARD_178",
+ "POKEDEX_REWARD_179",
+ "POKEDEX_REWARD_180",
+ "POKEDEX_REWARD_181",
+ "POKEDEX_REWARD_182",
+ "POKEDEX_REWARD_183",
+ "POKEDEX_REWARD_184",
+ "POKEDEX_REWARD_185",
+ "POKEDEX_REWARD_186",
+ "POKEDEX_REWARD_187",
+ "POKEDEX_REWARD_188",
+ "POKEDEX_REWARD_189",
+ "POKEDEX_REWARD_190",
+ "POKEDEX_REWARD_191",
+ "POKEDEX_REWARD_192",
+ "POKEDEX_REWARD_193",
+ "POKEDEX_REWARD_194",
+ "POKEDEX_REWARD_195",
+ "POKEDEX_REWARD_196",
+ "POKEDEX_REWARD_197",
+ "POKEDEX_REWARD_198",
+ "POKEDEX_REWARD_199",
+ "POKEDEX_REWARD_200",
+ "POKEDEX_REWARD_201",
+ "POKEDEX_REWARD_202",
+ "POKEDEX_REWARD_203",
+ "POKEDEX_REWARD_204",
+ "POKEDEX_REWARD_205",
+ "POKEDEX_REWARD_206",
+ "POKEDEX_REWARD_207",
+ "POKEDEX_REWARD_208",
+ "POKEDEX_REWARD_209",
+ "POKEDEX_REWARD_210",
+ "POKEDEX_REWARD_211",
+ "POKEDEX_REWARD_212",
+ "POKEDEX_REWARD_213",
+ "POKEDEX_REWARD_214",
+ "POKEDEX_REWARD_215",
+ "POKEDEX_REWARD_216",
+ "POKEDEX_REWARD_217",
+ "POKEDEX_REWARD_218",
+ "POKEDEX_REWARD_219",
+ "POKEDEX_REWARD_220",
+ "POKEDEX_REWARD_221",
+ "POKEDEX_REWARD_222",
+ "POKEDEX_REWARD_223",
+ "POKEDEX_REWARD_224",
+ "POKEDEX_REWARD_225",
+ "POKEDEX_REWARD_226",
+ "POKEDEX_REWARD_227",
+ "POKEDEX_REWARD_228",
+ "POKEDEX_REWARD_229",
+ "POKEDEX_REWARD_230",
+ "POKEDEX_REWARD_231",
+ "POKEDEX_REWARD_232",
+ "POKEDEX_REWARD_233",
+ "POKEDEX_REWARD_234",
+ "POKEDEX_REWARD_235",
+ "POKEDEX_REWARD_236",
+ "POKEDEX_REWARD_237",
+ "POKEDEX_REWARD_238",
+ "POKEDEX_REWARD_239",
+ "POKEDEX_REWARD_240",
+ "POKEDEX_REWARD_241",
+ "POKEDEX_REWARD_242",
+ "POKEDEX_REWARD_243",
+ "POKEDEX_REWARD_244",
+ "POKEDEX_REWARD_245",
+ "POKEDEX_REWARD_246",
+ "POKEDEX_REWARD_247",
+ "POKEDEX_REWARD_248",
+ "POKEDEX_REWARD_249",
+ "POKEDEX_REWARD_250",
+ "POKEDEX_REWARD_251",
+ "POKEDEX_REWARD_252",
+ "POKEDEX_REWARD_253",
+ "POKEDEX_REWARD_254",
+ "POKEDEX_REWARD_255",
+ "POKEDEX_REWARD_256",
+ "POKEDEX_REWARD_257",
+ "POKEDEX_REWARD_258",
+ "POKEDEX_REWARD_259",
+ "POKEDEX_REWARD_260",
+ "POKEDEX_REWARD_261",
+ "POKEDEX_REWARD_262",
+ "POKEDEX_REWARD_263",
+ "POKEDEX_REWARD_264",
+ "POKEDEX_REWARD_265",
+ "POKEDEX_REWARD_266",
+ "POKEDEX_REWARD_267",
+ "POKEDEX_REWARD_268",
+ "POKEDEX_REWARD_269",
+ "POKEDEX_REWARD_270",
+ "POKEDEX_REWARD_271",
+ "POKEDEX_REWARD_272",
+ "POKEDEX_REWARD_273",
+ "POKEDEX_REWARD_274",
+ "POKEDEX_REWARD_275",
+ "POKEDEX_REWARD_276",
+ "POKEDEX_REWARD_277",
+ "POKEDEX_REWARD_278",
+ "POKEDEX_REWARD_279",
+ "POKEDEX_REWARD_280",
+ "POKEDEX_REWARD_281",
+ "POKEDEX_REWARD_282",
+ "POKEDEX_REWARD_283",
+ "POKEDEX_REWARD_284",
+ "POKEDEX_REWARD_285",
+ "POKEDEX_REWARD_286",
+ "POKEDEX_REWARD_287",
+ "POKEDEX_REWARD_288",
+ "POKEDEX_REWARD_289",
+ "POKEDEX_REWARD_290",
+ "POKEDEX_REWARD_291",
+ "POKEDEX_REWARD_292",
+ "POKEDEX_REWARD_293",
+ "POKEDEX_REWARD_294",
+ "POKEDEX_REWARD_295",
+ "POKEDEX_REWARD_296",
+ "POKEDEX_REWARD_297",
+ "POKEDEX_REWARD_298",
+ "POKEDEX_REWARD_299",
+ "POKEDEX_REWARD_300",
+ "POKEDEX_REWARD_301",
+ "POKEDEX_REWARD_302",
+ "POKEDEX_REWARD_303",
+ "POKEDEX_REWARD_304",
+ "POKEDEX_REWARD_305",
+ "POKEDEX_REWARD_306",
+ "POKEDEX_REWARD_307",
+ "POKEDEX_REWARD_308",
+ "POKEDEX_REWARD_309",
+ "POKEDEX_REWARD_310",
+ "POKEDEX_REWARD_311",
+ "POKEDEX_REWARD_312",
+ "POKEDEX_REWARD_313",
+ "POKEDEX_REWARD_314",
+ "POKEDEX_REWARD_315",
+ "POKEDEX_REWARD_316",
+ "POKEDEX_REWARD_317",
+ "POKEDEX_REWARD_318",
+ "POKEDEX_REWARD_319",
+ "POKEDEX_REWARD_320",
+ "POKEDEX_REWARD_321",
+ "POKEDEX_REWARD_322",
+ "POKEDEX_REWARD_323",
+ "POKEDEX_REWARD_324",
+ "POKEDEX_REWARD_325",
+ "POKEDEX_REWARD_326",
+ "POKEDEX_REWARD_327",
+ "POKEDEX_REWARD_328",
+ "POKEDEX_REWARD_329",
+ "POKEDEX_REWARD_330",
+ "POKEDEX_REWARD_331",
+ "POKEDEX_REWARD_332",
+ "POKEDEX_REWARD_333",
+ "POKEDEX_REWARD_334",
+ "POKEDEX_REWARD_335",
+ "POKEDEX_REWARD_336",
+ "POKEDEX_REWARD_337",
+ "POKEDEX_REWARD_338",
+ "POKEDEX_REWARD_339",
+ "POKEDEX_REWARD_340",
+ "POKEDEX_REWARD_341",
+ "POKEDEX_REWARD_342",
+ "POKEDEX_REWARD_343",
+ "POKEDEX_REWARD_344",
+ "POKEDEX_REWARD_345",
+ "POKEDEX_REWARD_346",
+ "POKEDEX_REWARD_347",
+ "POKEDEX_REWARD_348",
+ "POKEDEX_REWARD_349",
+ "POKEDEX_REWARD_350",
+ "POKEDEX_REWARD_351",
+ "POKEDEX_REWARD_352",
+ "POKEDEX_REWARD_353",
+ "POKEDEX_REWARD_354",
+ "POKEDEX_REWARD_355",
+ "POKEDEX_REWARD_356",
+ "POKEDEX_REWARD_357",
+ "POKEDEX_REWARD_358",
+ "POKEDEX_REWARD_359",
+ "POKEDEX_REWARD_360",
+ "POKEDEX_REWARD_361",
+ "POKEDEX_REWARD_362",
+ "POKEDEX_REWARD_363",
+ "POKEDEX_REWARD_364",
+ "POKEDEX_REWARD_365",
+ "POKEDEX_REWARD_366",
+ "POKEDEX_REWARD_367",
+ "POKEDEX_REWARD_368",
+ "POKEDEX_REWARD_369",
+ "POKEDEX_REWARD_370",
+ "POKEDEX_REWARD_371",
+ "POKEDEX_REWARD_372",
+ "POKEDEX_REWARD_373",
+ "POKEDEX_REWARD_374",
+ "POKEDEX_REWARD_375",
+ "POKEDEX_REWARD_376",
+ "POKEDEX_REWARD_377",
+ "POKEDEX_REWARD_378",
+ "POKEDEX_REWARD_379",
+ "POKEDEX_REWARD_380",
+ "POKEDEX_REWARD_381",
+ "POKEDEX_REWARD_382",
+ "POKEDEX_REWARD_383",
+ "POKEDEX_REWARD_384",
+ "POKEDEX_REWARD_385",
+ "POKEDEX_REWARD_386"
+ ],
+ "events": [],
+ "exits": [],
+ "warps": []
+ },
"REGION_SKY": {
- "parent_map": null,
+ "parent_map": "MAP_LITTLEROOT_TOWN",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -25,13 +425,19 @@
"REGION_LITTLEROOT_TOWN/MAIN": {
"parent_map": "MAP_LITTLEROOT_TOWN",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [
"EVENT_VISITED_LITTLEROOT_TOWN",
- "FREE_FLY_LOCATION"
+ "FREE_FLY_LOCATION",
+ "TERRA_CAVE_LOCATION",
+ "MARINE_CAVE_LOCATION"
],
"exits": [
"REGION_ROUTE101/MAIN",
+ "REGION_POKEDEX",
"REGION_SKY"
],
"warps": [
@@ -42,6 +448,9 @@
},
"REGION_LITTLEROOT_TOWN_MAYS_HOUSE_1F/MAIN": {
"parent_map": "MAP_LITTLEROOT_TOWN_MAYS_HOUSE_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -52,6 +461,9 @@
},
"REGION_LITTLEROOT_TOWN_MAYS_HOUSE_2F/MAIN": {
"parent_map": "MAP_LITTLEROOT_TOWN_MAYS_HOUSE_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -61,9 +473,16 @@
},
"REGION_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F/MAIN": {
"parent_map": "MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_AMULET_COIN",
- "NPC_GIFT_RECEIVED_SS_TICKET"
+ "NPC_GIFT_RECEIVED_SS_TICKET",
+ "NPC_GIFT_RECEIVED_AURORA_TICKET",
+ "NPC_GIFT_RECEIVED_EON_TICKET",
+ "NPC_GIFT_RECEIVED_MYSTIC_TICKET",
+ "NPC_GIFT_RECEIVED_OLD_SEA_MAP"
],
"events": [],
"exits": [],
@@ -74,6 +493,9 @@
},
"REGION_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F/MAIN": {
"parent_map": "MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -83,7 +505,12 @@
},
"REGION_LITTLEROOT_TOWN_PROFESSOR_BIRCHS_LAB/MAIN": {
"parent_map": "MAP_LITTLEROOT_TOWN_PROFESSOR_BIRCHS_LAB",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "NPC_GIFT_RECEIVED_FIRST_POKEBALLS"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -93,6 +520,9 @@
"REGION_OLDALE_TOWN/MAIN": {
"parent_map": "MAP_OLDALE_TOWN",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_POTION_OLDALE"
],
@@ -113,6 +543,9 @@
},
"REGION_OLDALE_TOWN_HOUSE1/MAIN": {
"parent_map": "MAP_OLDALE_TOWN_HOUSE1",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -122,6 +555,9 @@
},
"REGION_OLDALE_TOWN_HOUSE2/MAIN": {
"parent_map": "MAP_OLDALE_TOWN_HOUSE2",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -131,6 +567,9 @@
},
"REGION_OLDALE_TOWN_POKEMON_CENTER_1F/MAIN": {
"parent_map": "MAP_OLDALE_TOWN_POKEMON_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -141,6 +580,9 @@
},
"REGION_OLDALE_TOWN_POKEMON_CENTER_2F/MAIN": {
"parent_map": "MAP_OLDALE_TOWN_POKEMON_CENTER_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -150,6 +592,9 @@
},
"REGION_OLDALE_TOWN_MART/MAIN": {
"parent_map": "MAP_OLDALE_TOWN_MART",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -160,6 +605,9 @@
"REGION_PETALBURG_CITY/MAIN": {
"parent_map": "MAP_PETALBURG_CITY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [
"EVENT_VISITED_PETALBURG_CITY"
@@ -181,6 +629,9 @@
},
"REGION_PETALBURG_CITY/NORTH_POND": {
"parent_map": "MAP_PETALBURG_CITY",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_PETALBURG_CITY_MAX_REVIVE"
],
@@ -192,6 +643,9 @@
},
"REGION_PETALBURG_CITY/SOUTH_POND": {
"parent_map": "MAP_PETALBURG_CITY",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_PETALBURG_CITY_ETHER",
"HIDDEN_ITEM_PETALBURG_CITY_RARE_CANDY"
@@ -204,6 +658,9 @@
},
"REGION_PETALBURG_CITY_HOUSE1/MAIN": {
"parent_map": "MAP_PETALBURG_CITY_HOUSE1",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -213,6 +670,9 @@
},
"REGION_PETALBURG_CITY_HOUSE2/MAIN": {
"parent_map": "MAP_PETALBURG_CITY_HOUSE2",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -222,8 +682,11 @@
},
"REGION_PETALBURG_CITY_WALLYS_HOUSE/MAIN": {
"parent_map": "MAP_PETALBURG_CITY_WALLYS_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_HM03"
+ "NPC_GIFT_RECEIVED_HM_SURF"
],
"events": [],
"exits": [],
@@ -233,6 +696,9 @@
},
"REGION_PETALBURG_CITY_GYM/ROOM_1": {
"parent_map": "MAP_PETALBURG_CITY_GYM",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -244,7 +710,12 @@
},
"REGION_PETALBURG_CITY_GYM/ROOM_2": {
"parent_map": "MAP_PETALBURG_CITY_GYM",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_MARY_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -255,7 +726,12 @@
},
"REGION_PETALBURG_CITY_GYM/ROOM_3": {
"parent_map": "MAP_PETALBURG_CITY_GYM",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_RANDALL_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -266,7 +742,12 @@
},
"REGION_PETALBURG_CITY_GYM/ROOM_4": {
"parent_map": "MAP_PETALBURG_CITY_GYM",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_GEORGE_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -276,7 +757,12 @@
},
"REGION_PETALBURG_CITY_GYM/ROOM_5": {
"parent_map": "MAP_PETALBURG_CITY_GYM",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_ALEXIA_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -288,7 +774,12 @@
},
"REGION_PETALBURG_CITY_GYM/ROOM_6": {
"parent_map": "MAP_PETALBURG_CITY_GYM",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_PARKER_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -298,7 +789,12 @@
},
"REGION_PETALBURG_CITY_GYM/ROOM_7": {
"parent_map": "MAP_PETALBURG_CITY_GYM",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_BERKE_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -309,7 +805,12 @@
},
"REGION_PETALBURG_CITY_GYM/ROOM_8": {
"parent_map": "MAP_PETALBURG_CITY_GYM",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_JODY_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -320,9 +821,13 @@
},
"REGION_PETALBURG_CITY_GYM/ROOM_9": {
"parent_map": "MAP_PETALBURG_CITY_GYM",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM42",
- "BADGE_5"
+ "NPC_GIFT_RECEIVED_TM_FACADE",
+ "BADGE_5",
+ "TRAINER_NORMAN_1_REWARD"
],
"events": [
"EVENT_DEFEAT_NORMAN"
@@ -335,6 +840,9 @@
},
"REGION_PETALBURG_CITY_POKEMON_CENTER_1F/MAIN": {
"parent_map": "MAP_PETALBURG_CITY_POKEMON_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -345,6 +853,9 @@
},
"REGION_PETALBURG_CITY_POKEMON_CENTER_2F/MAIN": {
"parent_map": "MAP_PETALBURG_CITY_POKEMON_CENTER_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -354,6 +865,9 @@
},
"REGION_PETALBURG_CITY_MART/MAIN": {
"parent_map": "MAP_PETALBURG_CITY_MART",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -364,9 +878,13 @@
"REGION_RUSTBORO_CITY/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_RUSTBORO_CITY_X_DEFEND",
- "NPC_GIFT_RECEIVED_GREAT_BALL_RUSTBORO_CITY"
+ "NPC_GIFT_RECEIVED_GREAT_BALL_RUSTBORO_CITY",
+ "TRAINER_BRENDAN_RUSTBORO_MUDKIP_REWARD"
],
"events": [
"EVENT_RETURN_DEVON_GOODS",
@@ -393,9 +911,16 @@
},
"REGION_RUSTBORO_CITY_GYM/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_GYM",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM39",
- "BADGE_1"
+ "NPC_GIFT_RECEIVED_TM_ROCK_TOMB",
+ "BADGE_1",
+ "TRAINER_JOSH_REWARD",
+ "TRAINER_TOMMY_REWARD",
+ "TRAINER_MARC_REWARD",
+ "TRAINER_ROXANNE_1_REWARD"
],
"events": [
"EVENT_DEFEAT_ROXANNE"
@@ -407,6 +932,9 @@
},
"REGION_RUSTBORO_CITY_MART/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_MART",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -416,6 +944,9 @@
},
"REGION_RUSTBORO_CITY_POKEMON_CENTER_1F/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_POKEMON_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -426,6 +957,9 @@
},
"REGION_RUSTBORO_CITY_POKEMON_CENTER_2F/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_POKEMON_CENTER_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -435,6 +969,9 @@
},
"REGION_RUSTBORO_CITY_POKEMON_SCHOOL/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_POKEMON_SCHOOL",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_QUICK_CLAW"
],
@@ -446,6 +983,9 @@
},
"REGION_RUSTBORO_CITY_DEVON_CORP_1F/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_DEVON_CORP_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -456,6 +996,9 @@
},
"REGION_RUSTBORO_CITY_DEVON_CORP_2F/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_DEVON_CORP_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -466,6 +1009,9 @@
},
"REGION_RUSTBORO_CITY_DEVON_CORP_3F/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_DEVON_CORP_3F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_LETTER",
"NPC_GIFT_RECEIVED_EXP_SHARE"
@@ -480,6 +1026,9 @@
},
"REGION_RUSTBORO_CITY_FLAT1_1F/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_FLAT1_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -490,6 +1039,9 @@
},
"REGION_RUSTBORO_CITY_FLAT1_2F/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_FLAT1_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -499,6 +1051,9 @@
},
"REGION_RUSTBORO_CITY_FLAT2_1F/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_FLAT2_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -509,6 +1064,9 @@
},
"REGION_RUSTBORO_CITY_FLAT2_2F/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_FLAT2_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_PREMIER_BALL_RUSTBORO"
],
@@ -521,6 +1079,9 @@
},
"REGION_RUSTBORO_CITY_FLAT2_3F/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_FLAT2_3F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -530,6 +1091,9 @@
},
"REGION_RUSTBORO_CITY_HOUSE1/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_HOUSE1",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -539,6 +1103,9 @@
},
"REGION_RUSTBORO_CITY_HOUSE2/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_HOUSE2",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -548,6 +1115,9 @@
},
"REGION_RUSTBORO_CITY_HOUSE3/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_HOUSE3",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -557,8 +1127,11 @@
},
"REGION_RUSTBORO_CITY_CUTTERS_HOUSE/MAIN": {
"parent_map": "MAP_RUSTBORO_CITY_CUTTERS_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_HM01"
+ "NPC_GIFT_RECEIVED_HM_CUT"
],
"events": [],
"exits": [],
@@ -569,6 +1142,9 @@
"REGION_DEWFORD_TOWN/MAIN": {
"parent_map": "MAP_DEWFORD_TOWN",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"NPC_GIFT_RECEIVED_OLD_ROD"
],
@@ -591,8 +1167,11 @@
},
"REGION_DEWFORD_TOWN_HALL/MAIN": {
"parent_map": "MAP_DEWFORD_TOWN_HALL",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM36"
+ "NPC_GIFT_RECEIVED_TM_SLUDGE_BOMB"
],
"events": [],
"exits": [],
@@ -602,6 +1181,9 @@
},
"REGION_DEWFORD_TOWN_POKEMON_CENTER_1F/MAIN": {
"parent_map": "MAP_DEWFORD_TOWN_POKEMON_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -612,6 +1194,9 @@
},
"REGION_DEWFORD_TOWN_POKEMON_CENTER_2F/MAIN": {
"parent_map": "MAP_DEWFORD_TOWN_POKEMON_CENTER_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -621,9 +1206,19 @@
},
"REGION_DEWFORD_TOWN_GYM/MAIN": {
"parent_map": "MAP_DEWFORD_TOWN_GYM",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM08",
- "BADGE_2"
+ "NPC_GIFT_RECEIVED_TM_BULK_UP",
+ "BADGE_2",
+ "TRAINER_LAURA_REWARD",
+ "TRAINER_LILITH_REWARD",
+ "TRAINER_BRENDEN_REWARD",
+ "TRAINER_TAKAO_REWARD",
+ "TRAINER_JOCELYN_REWARD",
+ "TRAINER_CRISTIAN_REWARD",
+ "TRAINER_BRAWLY_1_REWARD"
],
"events": [
"EVENT_DEFEAT_BRAWLY"
@@ -635,6 +1230,9 @@
},
"REGION_DEWFORD_TOWN_HOUSE1/MAIN": {
"parent_map": "MAP_DEWFORD_TOWN_HOUSE1",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -644,6 +1242,9 @@
},
"REGION_DEWFORD_TOWN_HOUSE2/MAIN": {
"parent_map": "MAP_DEWFORD_TOWN_HOUSE2",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_SILK_SCARF"
],
@@ -656,6 +1257,9 @@
"REGION_SLATEPORT_CITY/MAIN": {
"parent_map": "MAP_SLATEPORT_CITY",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"NPC_GIFT_RECEIVED_POWDER_JAR"
],
@@ -683,6 +1287,9 @@
},
"REGION_SLATEPORT_CITY_POKEMON_CENTER_2F/MAIN": {
"parent_map": "MAP_SLATEPORT_CITY_POKEMON_CENTER_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -692,6 +1299,9 @@
},
"REGION_SLATEPORT_CITY_POKEMON_CENTER_1F/MAIN": {
"parent_map": "MAP_SLATEPORT_CITY_POKEMON_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -702,6 +1312,9 @@
},
"REGION_SLATEPORT_CITY_MART/MAIN": {
"parent_map": "MAP_SLATEPORT_CITY_MART",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [
"EVENT_BUY_HARBOR_MAIL"
@@ -713,6 +1326,9 @@
},
"REGION_SLATEPORT_CITY_STERNS_SHIPYARD_1F/MAIN": {
"parent_map": "MAP_SLATEPORT_CITY_STERNS_SHIPYARD_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [
"EVENT_TALK_TO_DOCK"
@@ -725,6 +1341,9 @@
},
"REGION_SLATEPORT_CITY_STERNS_SHIPYARD_2F/MAIN": {
"parent_map": "MAP_SLATEPORT_CITY_STERNS_SHIPYARD_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -734,8 +1353,11 @@
},
"REGION_SLATEPORT_CITY_BATTLE_TENT_LOBBY/MAIN": {
"parent_map": "MAP_SLATEPORT_CITY_BATTLE_TENT_LOBBY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM41"
+ "NPC_GIFT_RECEIVED_TM_TORMENT"
],
"events": [],
"exits": [],
@@ -745,6 +1367,9 @@
},
"REGION_SLATEPORT_CITY_POKEMON_FAN_CLUB/MAIN": {
"parent_map": "MAP_SLATEPORT_CITY_POKEMON_FAN_CLUB",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_SOOTHE_BELL"
],
@@ -756,8 +1381,11 @@
},
"REGION_SLATEPORT_CITY_OCEANIC_MUSEUM_1F/MAIN": {
"parent_map": "MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM46"
+ "NPC_GIFT_RECEIVED_TM_THIEF"
],
"events": [],
"exits": [],
@@ -768,7 +1396,13 @@
},
"REGION_SLATEPORT_CITY_OCEANIC_MUSEUM_2F/MAIN": {
"parent_map": "MAP_SLATEPORT_CITY_OCEANIC_MUSEUM_2F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_GRUNT_MUSEUM_1_REWARD",
+ "TRAINER_GRUNT_MUSEUM_2_REWARD"
+ ],
"events": [
"EVENT_RESCUE_CAPT_STERN"
],
@@ -779,6 +1413,9 @@
},
"REGION_SLATEPORT_CITY_NAME_RATERS_HOUSE/MAIN": {
"parent_map": "MAP_SLATEPORT_CITY_NAME_RATERS_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -788,6 +1425,9 @@
},
"REGION_SLATEPORT_CITY_HARBOR/MAIN": {
"parent_map": "MAP_SLATEPORT_CITY_HARBOR",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_DEEP_SEA_TOOTH",
"NPC_GIFT_RECEIVED_DEEP_SEA_SCALE"
@@ -803,6 +1443,9 @@
},
"REGION_SLATEPORT_CITY_HOUSE/MAIN": {
"parent_map": "MAP_SLATEPORT_CITY_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -813,10 +1456,14 @@
"REGION_MAUVILLE_CITY/MAIN": {
"parent_map": "MAP_MAUVILLE_CITY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_MAUVILLE_CITY_X_SPEED",
"NPC_GIFT_GOT_BASEMENT_KEY_FROM_WATTSON",
- "NPC_GIFT_GOT_TM24_FROM_WATTSON"
+ "NPC_GIFT_GOT_TM_THUNDERBOLT_FROM_WATTSON",
+ "TRAINER_WALLY_MAUVILLE_REWARD"
],
"events": [
"EVENT_VISITED_MAUVILLE_CITY"
@@ -839,9 +1486,18 @@
},
"REGION_MAUVILLE_CITY_GYM/MAIN": {
"parent_map": "MAP_MAUVILLE_CITY_GYM",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM34",
- "BADGE_3"
+ "NPC_GIFT_RECEIVED_TM_SHOCK_WAVE",
+ "BADGE_3",
+ "TRAINER_VIVIAN_REWARD",
+ "TRAINER_KIRK_REWARD",
+ "TRAINER_BEN_REWARD",
+ "TRAINER_ANGELO_REWARD",
+ "TRAINER_SHAWN_REWARD",
+ "TRAINER_WATTSON_1_REWARD"
],
"events": [
"EVENT_DEFEAT_WATTSON"
@@ -853,6 +1509,9 @@
},
"REGION_MAUVILLE_CITY_POKEMON_CENTER_1F/MAIN": {
"parent_map": "MAP_MAUVILLE_CITY_POKEMON_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -863,6 +1522,9 @@
},
"REGION_MAUVILLE_CITY_POKEMON_CENTER_2F/MAIN": {
"parent_map": "MAP_MAUVILLE_CITY_POKEMON_CENTER_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -872,6 +1534,9 @@
},
"REGION_MAUVILLE_CITY_BIKE_SHOP/MAIN": {
"parent_map": "MAP_MAUVILLE_CITY_BIKE_SHOP",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_ACRO_BIKE",
"NPC_GIFT_RECEIVED_MACH_BIKE"
@@ -884,6 +1549,9 @@
},
"REGION_MAUVILLE_CITY_MART/MAIN": {
"parent_map": "MAP_MAUVILLE_CITY_MART",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -893,8 +1561,11 @@
},
"REGION_MAUVILLE_CITY_HOUSE1/MAIN": {
"parent_map": "MAP_MAUVILLE_CITY_HOUSE1",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_HM06"
+ "NPC_GIFT_RECEIVED_HM_ROCK_SMASH"
],
"events": [],
"exits": [],
@@ -904,6 +1575,9 @@
},
"REGION_MAUVILLE_CITY_HOUSE2/MAIN": {
"parent_map": "MAP_MAUVILLE_CITY_HOUSE2",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_COIN_CASE"
],
@@ -915,6 +1589,9 @@
},
"REGION_MAUVILLE_CITY_GAME_CORNER/MAIN": {
"parent_map": "MAP_MAUVILLE_CITY_GAME_CORNER",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -925,6 +1602,9 @@
"REGION_VERDANTURF_TOWN/MAIN": {
"parent_map": "MAP_VERDANTURF_TOWN",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [
"EVENT_VISITED_VERDANTURF_TOWN"
@@ -944,8 +1624,11 @@
},
"REGION_VERDANTURF_TOWN_BATTLE_TENT_LOBBY/MAIN": {
"parent_map": "MAP_VERDANTURF_TOWN_BATTLE_TENT_LOBBY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM45"
+ "NPC_GIFT_RECEIVED_TM_ATTRACT"
],
"events": [],
"exits": [],
@@ -955,6 +1638,9 @@
},
"REGION_VERDANTURF_TOWN_MART/MAIN": {
"parent_map": "MAP_VERDANTURF_TOWN_MART",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -964,6 +1650,9 @@
},
"REGION_VERDANTURF_TOWN_POKEMON_CENTER_1F/MAIN": {
"parent_map": "MAP_VERDANTURF_TOWN_POKEMON_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -974,6 +1663,9 @@
},
"REGION_VERDANTURF_TOWN_POKEMON_CENTER_2F/MAIN": {
"parent_map": "MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -983,6 +1675,9 @@
},
"REGION_VERDANTURF_TOWN_WANDAS_HOUSE/MAIN": {
"parent_map": "MAP_VERDANTURF_TOWN_WANDAS_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -992,6 +1687,9 @@
},
"REGION_VERDANTURF_TOWN_FRIENDSHIP_RATERS_HOUSE/MAIN": {
"parent_map": "MAP_VERDANTURF_TOWN_FRIENDSHIP_RATERS_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1001,6 +1699,9 @@
},
"REGION_VERDANTURF_TOWN_HOUSE/MAIN": {
"parent_map": "MAP_VERDANTURF_TOWN_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1011,6 +1712,9 @@
"REGION_FALLARBOR_TOWN/MAIN": {
"parent_map": "MAP_FALLARBOR_TOWN",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_FALLARBOR_TOWN_NUGGET"
],
@@ -1031,6 +1735,9 @@
},
"REGION_FALLARBOR_TOWN_MART/MAIN": {
"parent_map": "MAP_FALLARBOR_TOWN_MART",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1040,6 +1747,9 @@
},
"REGION_FALLARBOR_TOWN_BATTLE_TENT_LOBBY/MAIN": {
"parent_map": "MAP_FALLARBOR_TOWN_BATTLE_TENT_LOBBY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1049,6 +1759,9 @@
},
"REGION_FALLARBOR_TOWN_POKEMON_CENTER_1F/MAIN": {
"parent_map": "MAP_FALLARBOR_TOWN_POKEMON_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1059,6 +1772,9 @@
},
"REGION_FALLARBOR_TOWN_POKEMON_CENTER_2F/MAIN": {
"parent_map": "MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1068,8 +1784,11 @@
},
"REGION_FALLARBOR_TOWN_COZMOS_HOUSE/MAIN": {
"parent_map": "MAP_FALLARBOR_TOWN_COZMOS_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM27"
+ "NPC_GIFT_RECEIVED_TM_RETURN"
],
"events": [],
"exits": [],
@@ -1079,6 +1798,9 @@
},
"REGION_FALLARBOR_TOWN_MOVE_RELEARNERS_HOUSE/MAIN": {
"parent_map": "MAP_FALLARBOR_TOWN_MOVE_RELEARNERS_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1089,6 +1811,9 @@
"REGION_LAVARIDGE_TOWN/MAIN": {
"parent_map": "MAP_LAVARIDGE_TOWN",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_GO_GOGGLES"
],
@@ -1108,6 +1833,9 @@
},
"REGION_LAVARIDGE_TOWN/SPRINGS": {
"parent_map": "MAP_LAVARIDGE_TOWN",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_LAVARIDGE_TOWN_ICE_HEAL"
],
@@ -1119,6 +1847,9 @@
},
"REGION_LAVARIDGE_TOWN_HERB_SHOP/MAIN": {
"parent_map": "MAP_LAVARIDGE_TOWN_HERB_SHOP",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_CHARCOAL"
],
@@ -1130,6 +1861,9 @@
},
"REGION_LAVARIDGE_TOWN_GYM_1F/ENTRANCE": {
"parent_map": "MAP_LAVARIDGE_TOWN_GYM_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1142,7 +1876,14 @@
},
"REGION_LAVARIDGE_TOWN_GYM_1F/BOTTOM_LEFT_LOWER": {
"parent_map": "MAP_LAVARIDGE_TOWN_GYM_1F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_AXLE_REWARD",
+ "TRAINER_GERALD_REWARD",
+ "TRAINER_COLE_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -1158,6 +1899,9 @@
},
"REGION_LAVARIDGE_TOWN_GYM_1F/BOTTOM_LEFT_UPPER": {
"parent_map": "MAP_LAVARIDGE_TOWN_GYM_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1170,7 +1914,12 @@
},
"REGION_LAVARIDGE_TOWN_GYM_1F/TOP_LEFT": {
"parent_map": "MAP_LAVARIDGE_TOWN_GYM_1F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_DANIELLE_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -1183,6 +1932,9 @@
},
"REGION_LAVARIDGE_TOWN_GYM_1F/TOP_CENTER": {
"parent_map": "MAP_LAVARIDGE_TOWN_GYM_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1194,6 +1946,9 @@
},
"REGION_LAVARIDGE_TOWN_GYM_1F/TOP_RIGHT": {
"parent_map": "MAP_LAVARIDGE_TOWN_GYM_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1204,9 +1959,13 @@
},
"REGION_LAVARIDGE_TOWN_GYM_1F/FLANNERY": {
"parent_map": "MAP_LAVARIDGE_TOWN_GYM_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM50",
- "BADGE_4"
+ "NPC_GIFT_RECEIVED_TM_OVERHEAT",
+ "BADGE_4",
+ "TRAINER_FLANNERY_1_REWARD"
],
"events": [
"EVENT_DEFEAT_FLANNERY"
@@ -1220,6 +1979,9 @@
},
"REGION_LAVARIDGE_TOWN_GYM_B1F/TOP": {
"parent_map": "MAP_LAVARIDGE_TOWN_GYM_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1232,7 +1994,13 @@
},
"REGION_LAVARIDGE_TOWN_GYM_B1F/BOTTOM_LEFT_LOWER": {
"parent_map": "MAP_LAVARIDGE_TOWN_GYM_B1F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_JACE_REWARD",
+ "TRAINER_ELI_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -1249,7 +2017,12 @@
},
"REGION_LAVARIDGE_TOWN_GYM_B1F/BOTTOM_LEFT_UPPER_1": {
"parent_map": "MAP_LAVARIDGE_TOWN_GYM_B1F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_KEEGAN_REWARD"
+ ],
"events": [],
"exits": [
"REGION_LAVARIDGE_TOWN_GYM_B1F/BOTTOM_LEFT_LOWER"
@@ -1263,6 +2036,9 @@
},
"REGION_LAVARIDGE_TOWN_GYM_B1F/BOTTOM_LEFT_UPPER_2": {
"parent_map": "MAP_LAVARIDGE_TOWN_GYM_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1275,7 +2051,12 @@
},
"REGION_LAVARIDGE_TOWN_GYM_B1F/BOTTOM_RIGHT_LOWER": {
"parent_map": "MAP_LAVARIDGE_TOWN_GYM_B1F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_JEFF_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -1285,6 +2066,9 @@
},
"REGION_LAVARIDGE_TOWN_GYM_B1F/BOTTOM_RIGHT_MIDDLE": {
"parent_map": "MAP_LAVARIDGE_TOWN_GYM_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1296,6 +2080,9 @@
},
"REGION_LAVARIDGE_TOWN_GYM_B1F/BOTTOM_RIGHT_UPPER_1": {
"parent_map": "MAP_LAVARIDGE_TOWN_GYM_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1307,6 +2094,9 @@
},
"REGION_LAVARIDGE_TOWN_GYM_B1F/BOTTOM_RIGHT_UPPER_2": {
"parent_map": "MAP_LAVARIDGE_TOWN_GYM_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1318,6 +2108,9 @@
},
"REGION_LAVARIDGE_TOWN_MART/MAIN": {
"parent_map": "MAP_LAVARIDGE_TOWN_MART",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1327,6 +2120,9 @@
},
"REGION_LAVARIDGE_TOWN_POKEMON_CENTER_1F/MAIN": {
"parent_map": "MAP_LAVARIDGE_TOWN_POKEMON_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1338,6 +2134,9 @@
},
"REGION_LAVARIDGE_TOWN_POKEMON_CENTER_2F/MAIN": {
"parent_map": "MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1347,6 +2146,9 @@
},
"REGION_LAVARIDGE_TOWN_HOUSE/MAIN": {
"parent_map": "MAP_LAVARIDGE_TOWN_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1357,6 +2159,9 @@
"REGION_FORTREE_CITY/MAIN": {
"parent_map": "MAP_FORTREE_CITY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [
"EVENT_VISITED_FORTREE_CITY"
@@ -1379,6 +2184,9 @@
},
"REGION_FORTREE_CITY/BEFORE_GYM": {
"parent_map": "MAP_FORTREE_CITY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1390,6 +2198,9 @@
},
"REGION_FORTREE_CITY_POKEMON_CENTER_1F/MAIN": {
"parent_map": "MAP_FORTREE_CITY_POKEMON_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1400,6 +2211,9 @@
},
"REGION_FORTREE_CITY_POKEMON_CENTER_2F/MAIN": {
"parent_map": "MAP_FORTREE_CITY_POKEMON_CENTER_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1409,6 +2223,9 @@
},
"REGION_FORTREE_CITY_HOUSE1/MAIN": {
"parent_map": "MAP_FORTREE_CITY_HOUSE1",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1418,10 +2235,15 @@
},
"REGION_FORTREE_CITY_HOUSE2/MAIN": {
"parent_map": "MAP_FORTREE_CITY_HOUSE2",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM10"
+ "NPC_GIFT_RECEIVED_TM_HIDDEN_POWER"
+ ],
+ "events": [
+ "EVENT_MOVE_TUTOR_DIG"
],
- "events": [],
"exits": [],
"warps": [
"MAP_FORTREE_CITY_HOUSE2:0,1/MAP_FORTREE_CITY:4"
@@ -1429,6 +2251,9 @@
},
"REGION_FORTREE_CITY_HOUSE3/MAIN": {
"parent_map": "MAP_FORTREE_CITY_HOUSE3",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1438,6 +2263,9 @@
},
"REGION_FORTREE_CITY_HOUSE4/MAIN": {
"parent_map": "MAP_FORTREE_CITY_HOUSE4",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_MENTAL_HERB"
],
@@ -1451,6 +2279,9 @@
},
"REGION_FORTREE_CITY_HOUSE5/MAIN": {
"parent_map": "MAP_FORTREE_CITY_HOUSE5",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1460,9 +2291,19 @@
},
"REGION_FORTREE_CITY_GYM/MAIN": {
"parent_map": "MAP_FORTREE_CITY_GYM",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM40",
- "BADGE_6"
+ "NPC_GIFT_RECEIVED_TM_AERIAL_ACE",
+ "BADGE_6",
+ "TRAINER_HUMBERTO_REWARD",
+ "TRAINER_ASHLEY_REWARD",
+ "TRAINER_JARED_REWARD",
+ "TRAINER_FLINT_REWARD",
+ "TRAINER_EDWARDO_REWARD",
+ "TRAINER_DARIUS_REWARD",
+ "TRAINER_WINONA_1_REWARD"
],
"events": [
"EVENT_DEFEAT_WINONA"
@@ -1474,6 +2315,9 @@
},
"REGION_FORTREE_CITY_MART/MAIN": {
"parent_map": "MAP_FORTREE_CITY_MART",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1483,6 +2327,9 @@
},
"REGION_FORTREE_CITY_DECORATION_SHOP/MAIN": {
"parent_map": "MAP_FORTREE_CITY_DECORATION_SHOP",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1493,11 +2340,16 @@
"REGION_LILYCOVE_CITY/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"ITEM_LILYCOVE_CITY_MAX_REPEL",
"HIDDEN_ITEM_LILYCOVE_CITY_HEART_SCALE",
"HIDDEN_ITEM_LILYCOVE_CITY_PP_UP",
- "HIDDEN_ITEM_LILYCOVE_CITY_POKE_BALL"
+ "HIDDEN_ITEM_LILYCOVE_CITY_POKE_BALL",
+ "NPC_GIFT_LILYCOVE_RECEIVED_BERRY",
+ "TRAINER_BRENDAN_LILYCOVE_MUDKIP_REWARD"
],
"events": [
"EVENT_VISITED_LILYCOVE_CITY"
@@ -1523,6 +2375,9 @@
},
"REGION_LILYCOVE_CITY/SEA": {
"parent_map": "MAP_LILYCOVE_CITY",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -1535,6 +2390,9 @@
},
"REGION_LILYCOVE_CITY_DEPARTMENT_STORE_1F/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1547,6 +2405,9 @@
},
"REGION_LILYCOVE_CITY_DEPARTMENT_STORE_2F/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1559,6 +2420,9 @@
},
"REGION_LILYCOVE_CITY_DEPARTMENT_STORE_3F/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1571,6 +2435,9 @@
},
"REGION_LILYCOVE_CITY_DEPARTMENT_STORE_4F/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1583,6 +2450,9 @@
},
"REGION_LILYCOVE_CITY_DEPARTMENT_STORE_5F/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1595,6 +2465,9 @@
},
"REGION_LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1604,6 +2477,9 @@
},
"REGION_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1617,6 +2493,9 @@
},
"REGION_LILYCOVE_CITY_COVE_LILY_MOTEL_1F/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1627,6 +2506,9 @@
},
"REGION_LILYCOVE_CITY_COVE_LILY_MOTEL_2F/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_COVE_LILY_MOTEL_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1636,6 +2518,9 @@
},
"REGION_LILYCOVE_CITY_POKEMON_CENTER_1F/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_POKEMON_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1646,6 +2531,9 @@
},
"REGION_LILYCOVE_CITY_POKEMON_CENTER_2F/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_POKEMON_CENTER_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1655,6 +2543,9 @@
},
"REGION_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1665,6 +2556,9 @@
},
"REGION_LILYCOVE_CITY_LILYCOVE_MUSEUM_2F/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_LILYCOVE_MUSEUM_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1674,6 +2568,9 @@
},
"REGION_LILYCOVE_CITY_CONTEST_LOBBY/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_CONTEST_LOBBY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_POKEBLOCK_CASE"
],
@@ -1687,6 +2584,9 @@
},
"REGION_LILYCOVE_CITY_CONTEST_HALL/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_CONTEST_HALL",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1697,6 +2597,9 @@
},
"REGION_LILYCOVE_CITY_POKEMON_TRAINER_FAN_CLUB/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_POKEMON_TRAINER_FAN_CLUB",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1706,6 +2609,9 @@
},
"REGION_LILYCOVE_CITY_MOVE_DELETERS_HOUSE/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_MOVE_DELETERS_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1715,6 +2621,9 @@
},
"REGION_LILYCOVE_CITY_HOUSE1/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_HOUSE1",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1724,8 +2633,11 @@
},
"REGION_LILYCOVE_CITY_HOUSE2/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_HOUSE2",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM44"
+ "NPC_GIFT_RECEIVED_TM_REST"
],
"events": [],
"exits": [],
@@ -1735,6 +2647,9 @@
},
"REGION_LILYCOVE_CITY_HOUSE3/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_HOUSE3",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1744,6 +2659,9 @@
},
"REGION_LILYCOVE_CITY_HOUSE4/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_HOUSE4",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1753,10 +2671,17 @@
},
"REGION_LILYCOVE_CITY_HARBOR/MAIN": {
"parent_map": "MAP_LILYCOVE_CITY_HARBOR",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
- "REGION_SS_TIDAL_CORRIDOR/MAIN"
+ "REGION_SS_TIDAL_CORRIDOR/MAIN",
+ "REGION_SOUTHERN_ISLAND_EXTERIOR/MAIN",
+ "REGION_FARAWAY_ISLAND_ENTRANCE/MAIN",
+ "REGION_BIRTH_ISLAND_HARBOR/MAIN",
+ "REGION_NAVEL_ROCK_HARBOR/MAIN"
],
"warps": [
"MAP_LILYCOVE_CITY_HARBOR:0,1/MAP_LILYCOVE_CITY:12"
@@ -1764,11 +2689,15 @@
},
"REGION_SS_TIDAL_CORRIDOR/MAIN": {
"parent_map": "MAP_SS_TIDAL_CORRIDOR",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
"REGION_SLATEPORT_CITY_HARBOR/MAIN",
- "REGION_LILYCOVE_CITY_HARBOR/MAIN"
+ "REGION_LILYCOVE_CITY_HARBOR/MAIN",
+ "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/DOCK"
],
"warps": [
"MAP_SS_TIDAL_CORRIDOR:0/MAP_SS_TIDAL_ROOMS:0",
@@ -1784,8 +2713,17 @@
},
"REGION_SS_TIDAL_ROOMS/MAIN": {
"parent_map": "MAP_SS_TIDAL_ROOMS",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM49"
+ "NPC_GIFT_RECEIVED_TM_SNATCH",
+ "TRAINER_COLTON_REWARD",
+ "TRAINER_NAOMI_REWARD",
+ "TRAINER_THOMAS_REWARD",
+ "TRAINER_MICAH_REWARD",
+ "TRAINER_GARRET_REWARD",
+ "TRAINER_LEA_AND_JED_REWARD"
],
"events": [],
"exits": [],
@@ -1802,8 +2740,13 @@
},
"REGION_SS_TIDAL_LOWER_DECK/MAIN": {
"parent_map": "MAP_SS_TIDAL_LOWER_DECK",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "HIDDEN_ITEM_SS_TIDAL_LOWER_DECK_LEFTOVERS"
+ "HIDDEN_ITEM_SS_TIDAL_LOWER_DECK_LEFTOVERS",
+ "TRAINER_LEONARD_REWARD",
+ "TRAINER_PHILLIP_REWARD"
],
"events": [],
"exits": [],
@@ -1814,6 +2757,9 @@
"REGION_MOSSDEEP_CITY/MAIN": {
"parent_map": "MAP_MOSSDEEP_CITY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"ITEM_MOSSDEEP_CITY_NET_BALL",
"NPC_GIFT_RECEIVED_KINGS_ROCK"
@@ -1822,6 +2768,7 @@
"EVENT_VISITED_MOSSDEEP_CITY"
],
"exits": [
+ "REGION_MOSSDEEP_CITY/WATER",
"REGION_ROUTE124/MAIN",
"REGION_ROUTE125/SEA",
"REGION_ROUTE127/MAIN"
@@ -1839,9 +2786,25 @@
"MAP_MOSSDEEP_CITY:9/MAP_MOSSDEEP_CITY_GAME_CORNER_1F:0"
]
},
+ "REGION_MOSSDEEP_CITY/WATER": {
+ "parent_map": "MAP_MOSSDEEP_CITY",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": []
+ },
"REGION_MOSSDEEP_CITY_GYM/ROOM_1": {
"parent_map": "MAP_MOSSDEEP_CITY_GYM",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_MAURA_REWARD",
+ "TRAINER_PRESTON_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -1852,7 +2815,13 @@
},
"REGION_MOSSDEEP_CITY_GYM/ROOM_2": {
"parent_map": "MAP_MOSSDEEP_CITY_GYM",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_SAMANTHA_REWARD",
+ "TRAINER_BLAKE_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -1863,7 +2832,15 @@
},
"REGION_MOSSDEEP_CITY_GYM/ROOM_3": {
"parent_map": "MAP_MOSSDEEP_CITY_GYM",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_VIRGIL_REWARD",
+ "TRAINER_NATE_REWARD",
+ "TRAINER_SYLVIA_REWARD",
+ "TRAINER_HANNAH_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -1873,6 +2850,9 @@
},
"REGION_MOSSDEEP_CITY_GYM/ROOM_4": {
"parent_map": "MAP_MOSSDEEP_CITY_GYM",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1883,7 +2863,15 @@
},
"REGION_MOSSDEEP_CITY_GYM/ROOM_5": {
"parent_map": "MAP_MOSSDEEP_CITY_GYM",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_KATHLEEN_REWARD",
+ "TRAINER_NICHOLAS_REWARD",
+ "TRAINER_MACEY_REWARD",
+ "TRAINER_CLIFFORD_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -1893,9 +2881,13 @@
},
"REGION_MOSSDEEP_CITY_GYM/ROOM_6": {
"parent_map": "MAP_MOSSDEEP_CITY_GYM",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM04",
- "BADGE_7"
+ "NPC_GIFT_RECEIVED_TM_CALM_MIND",
+ "BADGE_7",
+ "TRAINER_TATE_AND_LIZA_1_REWARD"
],
"events": [
"EVENT_DEFEAT_TATE_AND_LIZA"
@@ -1909,6 +2901,9 @@
},
"REGION_MOSSDEEP_CITY_POKEMON_CENTER_1F/MAIN": {
"parent_map": "MAP_MOSSDEEP_CITY_POKEMON_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1919,6 +2914,9 @@
},
"REGION_MOSSDEEP_CITY_MART/MAIN": {
"parent_map": "MAP_MOSSDEEP_CITY_MART",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1928,6 +2926,9 @@
},
"REGION_MOSSDEEP_CITY_POKEMON_CENTER_2F/MAIN": {
"parent_map": "MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1937,8 +2938,15 @@
},
"REGION_MOSSDEEP_CITY_SPACE_CENTER_1F/MAIN": {
"parent_map": "MAP_MOSSDEEP_CITY_SPACE_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_SUN_STONE_MOSSDEEP"
+ "NPC_GIFT_RECEIVED_SUN_STONE_MOSSDEEP",
+ "TRAINER_GRUNT_SPACE_CENTER_1_REWARD",
+ "TRAINER_GRUNT_SPACE_CENTER_2_REWARD",
+ "TRAINER_GRUNT_SPACE_CENTER_3_REWARD",
+ "TRAINER_GRUNT_SPACE_CENTER_4_REWARD"
],
"events": [],
"exits": [],
@@ -1949,7 +2957,14 @@
},
"REGION_MOSSDEEP_CITY_SPACE_CENTER_2F/MAIN": {
"parent_map": "MAP_MOSSDEEP_CITY_SPACE_CENTER_2F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_GRUNT_SPACE_CENTER_5_REWARD",
+ "TRAINER_GRUNT_SPACE_CENTER_6_REWARD",
+ "TRAINER_GRUNT_SPACE_CENTER_7_REWARD"
+ ],
"events": [
"EVENT_DEFEAT_MAXIE_AT_SPACE_STATION"
],
@@ -1960,6 +2975,9 @@
},
"REGION_MOSSDEEP_CITY_GAME_CORNER_1F/MAIN": {
"parent_map": "MAP_MOSSDEEP_CITY_GAME_CORNER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1970,6 +2988,9 @@
},
"REGION_MOSSDEEP_CITY_GAME_CORNER_B1F/MAIN": {
"parent_map": "MAP_MOSSDEEP_CITY_GAME_CORNER_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1979,8 +3000,11 @@
},
"REGION_MOSSDEEP_CITY_STEVENS_HOUSE/MAIN": {
"parent_map": "MAP_MOSSDEEP_CITY_STEVENS_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_HM08"
+ "NPC_GIFT_RECEIVED_HM_DIVE"
],
"events": [
"EVENT_STEVEN_GIVES_DIVE"
@@ -1992,6 +3016,9 @@
},
"REGION_MOSSDEEP_CITY_HOUSE1/MAIN": {
"parent_map": "MAP_MOSSDEEP_CITY_HOUSE1",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2001,6 +3028,9 @@
},
"REGION_MOSSDEEP_CITY_HOUSE2/MAIN": {
"parent_map": "MAP_MOSSDEEP_CITY_HOUSE2",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [
"EVENT_WINGULL_QUEST_2"
@@ -2012,6 +3042,9 @@
},
"REGION_MOSSDEEP_CITY_HOUSE3/MAIN": {
"parent_map": "MAP_MOSSDEEP_CITY_HOUSE3",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_SUPER_ROD"
],
@@ -2023,6 +3056,9 @@
},
"REGION_MOSSDEEP_CITY_HOUSE4/MAIN": {
"parent_map": "MAP_MOSSDEEP_CITY_HOUSE4",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2033,8 +3069,13 @@
"REGION_UNDERWATER_SOOTOPOLIS_CITY/MAIN": {
"parent_map": "MAP_UNDERWATER_SOOTOPOLIS_CITY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
- "events": [],
+ "events": [
+ "EVENT_RAYQUAZA_STOPS_FIGHT"
+ ],
"exits": [
"REGION_SOOTOPOLIS_CITY/WATER"
],
@@ -2044,18 +3085,25 @@
},
"REGION_SOOTOPOLIS_CITY/WATER": {
"parent_map": "MAP_SOOTOPOLIS_CITY",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
"REGION_UNDERWATER_SOOTOPOLIS_CITY/MAIN",
"REGION_SOOTOPOLIS_CITY/EAST",
"REGION_SOOTOPOLIS_CITY/WEST",
+ "REGION_SOOTOPOLIS_CITY/WEST_GRASS",
"REGION_SOOTOPOLIS_CITY/ISLAND"
],
"warps": []
},
"REGION_SOOTOPOLIS_CITY/EAST": {
"parent_map": "MAP_SOOTOPOLIS_CITY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [
"EVENT_VISITED_SOOTOPOLIS_CITY"
@@ -2074,6 +3122,9 @@
},
"REGION_SOOTOPOLIS_CITY/WEST": {
"parent_map": "MAP_SOOTOPOLIS_CITY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -2088,10 +3139,26 @@
"MAP_SOOTOPOLIS_CITY:10/MAP_SOOTOPOLIS_CITY_HOUSE7:0"
]
},
+ "REGION_SOOTOPOLIS_CITY/WEST_GRASS": {
+ "parent_map": "MAP_SOOTOPOLIS_CITY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
+ "locations": [
+ "NPC_GIFT_SOOTOPOLIS_RECEIVED_BERRY_1",
+ "NPC_GIFT_SOOTOPOLIS_RECEIVED_BERRY_2"
+ ],
+ "events": [],
+ "exits": [],
+ "warps": []
+ },
"REGION_SOOTOPOLIS_CITY/ISLAND": {
"parent_map": "MAP_SOOTOPOLIS_CITY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
- "NPC_GIFT_RECEIVED_HM07"
+ "NPC_GIFT_RECEIVED_HM_WATERFALL"
],
"events": [],
"exits": [
@@ -2103,6 +3170,9 @@
},
"REGION_SOOTOPOLIS_CITY_POKEMON_CENTER_1F/MAIN": {
"parent_map": "MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2113,6 +3183,9 @@
},
"REGION_SOOTOPOLIS_CITY_POKEMON_CENTER_2F/MAIN": {
"parent_map": "MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2122,6 +3195,9 @@
},
"REGION_SOOTOPOLIS_CITY_MART/MAIN": {
"parent_map": "MAP_SOOTOPOLIS_CITY_MART",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2131,6 +3207,9 @@
},
"REGION_SOOTOPOLIS_CITY_GYM_1F/ENTRANCE": {
"parent_map": "MAP_SOOTOPOLIS_CITY_GYM_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -2143,6 +3222,9 @@
},
"REGION_SOOTOPOLIS_CITY_GYM_1F/PUZZLE_1": {
"parent_map": "MAP_SOOTOPOLIS_CITY_GYM_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -2154,6 +3236,9 @@
},
"REGION_SOOTOPOLIS_CITY_GYM_1F/PUZZLE_2": {
"parent_map": "MAP_SOOTOPOLIS_CITY_GYM_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -2165,6 +3250,9 @@
},
"REGION_SOOTOPOLIS_CITY_GYM_1F/PUZZLE_3": {
"parent_map": "MAP_SOOTOPOLIS_CITY_GYM_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -2176,9 +3264,13 @@
},
"REGION_SOOTOPOLIS_CITY_GYM_1F/TOP": {
"parent_map": "MAP_SOOTOPOLIS_CITY_GYM_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM03",
- "BADGE_8"
+ "NPC_GIFT_RECEIVED_TM_WATER_PULSE",
+ "BADGE_8",
+ "TRAINER_JUAN_1_REWARD"
],
"events": [
"EVENT_DEFEAT_JUAN"
@@ -2190,6 +3282,9 @@
},
"REGION_SOOTOPOLIS_CITY_GYM_B1F/LEVEL_1": {
"parent_map": "MAP_SOOTOPOLIS_CITY_GYM_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2199,7 +3294,13 @@
},
"REGION_SOOTOPOLIS_CITY_GYM_B1F/LEVEL_2": {
"parent_map": "MAP_SOOTOPOLIS_CITY_GYM_B1F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_ANDREA_REWARD",
+ "TRAINER_CONNIE_REWARD"
+ ],
"events": [],
"exits": [
"REGION_SOOTOPOLIS_CITY_GYM_B1F/LEVEL_1"
@@ -2208,7 +3309,13 @@
},
"REGION_SOOTOPOLIS_CITY_GYM_B1F/LEVEL_3": {
"parent_map": "MAP_SOOTOPOLIS_CITY_GYM_B1F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_ANNIKA_REWARD",
+ "TRAINER_DAPHNE_REWARD"
+ ],
"events": [],
"exits": [
"REGION_SOOTOPOLIS_CITY_GYM_B1F/LEVEL_2"
@@ -2217,7 +3324,17 @@
},
"REGION_SOOTOPOLIS_CITY_GYM_B1F/LEVEL_4": {
"parent_map": "MAP_SOOTOPOLIS_CITY_GYM_B1F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_CRISSY_REWARD",
+ "TRAINER_TIFFANY_REWARD",
+ "TRAINER_BETHANY_REWARD",
+ "TRAINER_OLIVIA_REWARD",
+ "TRAINER_BRIDGET_REWARD",
+ "TRAINER_BRIANNA_REWARD"
+ ],
"events": [],
"exits": [
"REGION_SOOTOPOLIS_CITY_GYM_B1F/LEVEL_3"
@@ -2226,6 +3343,9 @@
},
"REGION_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F/MAIN": {
"parent_map": "MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2236,6 +3356,9 @@
},
"REGION_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_B1F/MAIN": {
"parent_map": "MAP_SOOTOPOLIS_CITY_MYSTERY_EVENTS_HOUSE_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2245,6 +3368,9 @@
},
"REGION_SOOTOPOLIS_CITY_LOTAD_AND_SEEDOT_HOUSE/MAIN": {
"parent_map": "MAP_SOOTOPOLIS_CITY_LOTAD_AND_SEEDOT_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2254,8 +3380,11 @@
},
"REGION_SOOTOPOLIS_CITY_HOUSE1/MAIN": {
"parent_map": "MAP_SOOTOPOLIS_CITY_HOUSE1",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM31"
+ "NPC_GIFT_RECEIVED_TM_BRICK_BREAK"
],
"events": [],
"exits": [],
@@ -2265,6 +3394,9 @@
},
"REGION_SOOTOPOLIS_CITY_HOUSE2/MAIN": {
"parent_map": "MAP_SOOTOPOLIS_CITY_HOUSE2",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2274,6 +3406,9 @@
},
"REGION_SOOTOPOLIS_CITY_HOUSE3/MAIN": {
"parent_map": "MAP_SOOTOPOLIS_CITY_HOUSE3",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2283,6 +3418,9 @@
},
"REGION_SOOTOPOLIS_CITY_HOUSE4/MAIN": {
"parent_map": "MAP_SOOTOPOLIS_CITY_HOUSE4",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2292,6 +3430,9 @@
},
"REGION_SOOTOPOLIS_CITY_HOUSE5/MAIN": {
"parent_map": "MAP_SOOTOPOLIS_CITY_HOUSE5",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2301,6 +3442,9 @@
},
"REGION_SOOTOPOLIS_CITY_HOUSE6/MAIN": {
"parent_map": "MAP_SOOTOPOLIS_CITY_HOUSE6",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2310,6 +3454,9 @@
},
"REGION_SOOTOPOLIS_CITY_HOUSE7/MAIN": {
"parent_map": "MAP_SOOTOPOLIS_CITY_HOUSE7",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2320,11 +3467,15 @@
"REGION_PACIFIDLOG_TOWN/MAIN": {
"parent_map": "MAP_PACIFIDLOG_TOWN",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [
"EVENT_VISITED_PACIFIDLOG_TOWN"
],
"exits": [
+ "REGION_PACIFIDLOG_TOWN/WATER",
"REGION_ROUTE131/MAIN",
"REGION_ROUTE132/EAST"
],
@@ -2337,8 +3488,21 @@
"MAP_PACIFIDLOG_TOWN:5/MAP_PACIFIDLOG_TOWN_HOUSE5:0"
]
},
+ "REGION_PACIFIDLOG_TOWN/WATER": {
+ "parent_map": "MAP_PACIFIDLOG_TOWN",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": []
+ },
"REGION_PACIFIDLOG_TOWN_POKEMON_CENTER_1F/MAIN": {
"parent_map": "MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2349,6 +3513,9 @@
},
"REGION_PACIFIDLOG_TOWN_POKEMON_CENTER_2F/MAIN": {
"parent_map": "MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2358,6 +3525,9 @@
},
"REGION_PACIFIDLOG_TOWN_HOUSE1/MAIN": {
"parent_map": "MAP_PACIFIDLOG_TOWN_HOUSE1",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2367,9 +3537,12 @@
},
"REGION_PACIFIDLOG_TOWN_HOUSE2/MAIN": {
"parent_map": "MAP_PACIFIDLOG_TOWN_HOUSE2",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM27_2",
- "NPC_GIFT_RECEIVED_TM21"
+ "NPC_GIFT_RECEIVED_TM_RETURN_2",
+ "NPC_GIFT_RECEIVED_TM_FRUSTRATION"
],
"events": [],
"exits": [],
@@ -2379,6 +3552,9 @@
},
"REGION_PACIFIDLOG_TOWN_HOUSE3/MAIN": {
"parent_map": "MAP_PACIFIDLOG_TOWN_HOUSE3",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2388,6 +3564,9 @@
},
"REGION_PACIFIDLOG_TOWN_HOUSE4/MAIN": {
"parent_map": "MAP_PACIFIDLOG_TOWN_HOUSE4",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2397,6 +3576,9 @@
},
"REGION_PACIFIDLOG_TOWN_HOUSE5/MAIN": {
"parent_map": "MAP_PACIFIDLOG_TOWN_HOUSE5",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2407,6 +3589,9 @@
"REGION_EVER_GRANDE_CITY/SEA": {
"parent_map": "MAP_EVER_GRANDE_CITY",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -2417,6 +3602,9 @@
},
"REGION_EVER_GRANDE_CITY/SOUTH": {
"parent_map": "MAP_EVER_GRANDE_CITY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [
"EVENT_VISITED_EVER_GRANDE_CITY"
@@ -2431,6 +3619,9 @@
},
"REGION_EVER_GRANDE_CITY/NORTH": {
"parent_map": "MAP_EVER_GRANDE_CITY",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2441,6 +3632,9 @@
},
"REGION_EVER_GRANDE_CITY_POKEMON_CENTER_1F/MAIN": {
"parent_map": "MAP_EVER_GRANDE_CITY_POKEMON_CENTER_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2451,6 +3645,9 @@
},
"REGION_EVER_GRANDE_CITY_POKEMON_CENTER_2F/MAIN": {
"parent_map": "MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2460,6 +3657,9 @@
},
"REGION_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F/MAIN": {
"parent_map": "MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -2472,6 +3672,9 @@
},
"REGION_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F/BEHIND_BADGE_CHECKERS": {
"parent_map": "MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -2483,6 +3686,9 @@
},
"REGION_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F/MAIN": {
"parent_map": "MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2492,6 +3698,9 @@
},
"REGION_EVER_GRANDE_CITY_HALL5/MAIN": {
"parent_map": "MAP_EVER_GRANDE_CITY_HALL5",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2502,7 +3711,12 @@
},
"REGION_EVER_GRANDE_CITY_SIDNEYS_ROOM/MAIN": {
"parent_map": "MAP_EVER_GRANDE_CITY_SIDNEYS_ROOM",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_SIDNEY_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -2512,6 +3726,9 @@
},
"REGION_EVER_GRANDE_CITY_HALL1/MAIN": {
"parent_map": "MAP_EVER_GRANDE_CITY_HALL1",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2522,7 +3739,12 @@
},
"REGION_EVER_GRANDE_CITY_PHOEBES_ROOM/MAIN": {
"parent_map": "MAP_EVER_GRANDE_CITY_PHOEBES_ROOM",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_PHOEBE_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -2532,6 +3754,9 @@
},
"REGION_EVER_GRANDE_CITY_HALL2/MAIN": {
"parent_map": "MAP_EVER_GRANDE_CITY_HALL2",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2542,7 +3767,12 @@
},
"REGION_EVER_GRANDE_CITY_GLACIAS_ROOM/MAIN": {
"parent_map": "MAP_EVER_GRANDE_CITY_GLACIAS_ROOM",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_GLACIA_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -2552,6 +3782,9 @@
},
"REGION_EVER_GRANDE_CITY_HALL3/MAIN": {
"parent_map": "MAP_EVER_GRANDE_CITY_HALL3",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2562,7 +3795,12 @@
},
"REGION_EVER_GRANDE_CITY_DRAKES_ROOM/MAIN": {
"parent_map": "MAP_EVER_GRANDE_CITY_DRAKES_ROOM",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_DRAKE_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -2572,7 +3810,12 @@
},
"REGION_EVER_GRANDE_CITY_CHAMPIONS_ROOM/MAIN": {
"parent_map": "MAP_EVER_GRANDE_CITY_CHAMPIONS_ROOM",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_WALLACE_REWARD"
+ ],
"events": [
"EVENT_DEFEAT_CHAMPION"
],
@@ -2584,6 +3827,9 @@
},
"REGION_EVER_GRANDE_CITY_HALL4/MAIN": {
"parent_map": "MAP_EVER_GRANDE_CITY_HALL4",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2594,6 +3840,9 @@
},
"REGION_EVER_GRANDE_CITY_HALL_OF_FAME/MAIN": {
"parent_map": "MAP_EVER_GRANDE_CITY_HALL_OF_FAME",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
diff --git a/worlds/pokemon_emerald/data/regions/dungeons.json b/worlds/pokemon_emerald/data/regions/dungeons.json
index 1da5e325bad0..040f06d8fea6 100644
--- a/worlds/pokemon_emerald/data/regions/dungeons.json
+++ b/worlds/pokemon_emerald/data/regions/dungeons.json
@@ -1,12 +1,18 @@
{
"REGION_PETALBURG_WOODS/WEST_PATH": {
"parent_map": "MAP_PETALBURG_WOODS",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_PETALBURG_WOODS_ETHER",
"ITEM_PETALBURG_WOODS_PARALYZE_HEAL",
"HIDDEN_ITEM_PETALBURG_WOODS_POTION",
"HIDDEN_ITEM_PETALBURG_WOODS_POKE_BALL",
- "NPC_GIFT_RECEIVED_GREAT_BALL_PETALBURG_WOODS"
+ "NPC_GIFT_RECEIVED_GREAT_BALL_PETALBURG_WOODS",
+ "TRAINER_LYLE_REWARD",
+ "TRAINER_GRUNT_PETALBURG_WOODS_REWARD",
+ "TRAINER_JAMES_1_REWARD"
],
"events": [],
"exits": [
@@ -20,6 +26,9 @@
},
"REGION_PETALBURG_WOODS/EAST_PATH": {
"parent_map": "MAP_PETALBURG_WOODS",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_PETALBURG_WOODS_GREAT_BALL",
"ITEM_PETALBURG_WOODS_X_ATTACK",
@@ -35,9 +44,13 @@
},
"REGION_RUSTURF_TUNNEL/WEST": {
"parent_map": "MAP_RUSTURF_TUNNEL",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_RUSTURF_TUNNEL_POKE_BALL",
- "NPC_GIFT_RECEIVED_DEVON_GOODS_RUSTURF_TUNNEL"
+ "NPC_GIFT_RECEIVED_DEVON_GOODS_RUSTURF_TUNNEL",
+ "TRAINER_GRUNT_RUSTURF_TUNNEL_REWARD"
],
"events": [
"EVENT_RECOVER_DEVON_GOODS"
@@ -51,9 +64,13 @@
},
"REGION_RUSTURF_TUNNEL/EAST": {
"parent_map": "MAP_RUSTURF_TUNNEL",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_RUSTURF_TUNNEL_MAX_ETHER",
- "NPC_GIFT_RECEIVED_HM04"
+ "NPC_GIFT_RECEIVED_HM_STRENGTH",
+ "TRAINER_MIKE_2_REWARD"
],
"events": [],
"exits": [
@@ -66,9 +83,12 @@
},
"REGION_GRANITE_CAVE_1F/LOWER": {
"parent_map": "MAP_GRANITE_CAVE_1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_GRANITE_CAVE_1F_ESCAPE_ROPE",
- "NPC_GIFT_RECEIVED_HM05"
+ "NPC_GIFT_RECEIVED_HM_FLASH"
],
"events": [],
"exits": [],
@@ -79,6 +99,9 @@
},
"REGION_GRANITE_CAVE_1F/UPPER": {
"parent_map": "MAP_GRANITE_CAVE_1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -91,6 +114,9 @@
},
"REGION_GRANITE_CAVE_B1F/LOWER": {
"parent_map": "MAP_GRANITE_CAVE_B1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_GRANITE_CAVE_B1F_POKE_BALL"
],
@@ -105,6 +131,9 @@
},
"REGION_GRANITE_CAVE_B1F/LOWER_PLATFORM": {
"parent_map": "MAP_GRANITE_CAVE_B1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -115,6 +144,9 @@
},
"REGION_GRANITE_CAVE_B1F/UPPER": {
"parent_map": "MAP_GRANITE_CAVE_B1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -128,6 +160,9 @@
},
"REGION_GRANITE_CAVE_B2F/NORTH_LOWER_LANDING": {
"parent_map": "MAP_GRANITE_CAVE_B2F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_GRANITE_CAVE_B2F_REPEL"
],
@@ -139,6 +174,9 @@
},
"REGION_GRANITE_CAVE_B2F/NORTH_UPPER_LANDING": {
"parent_map": "MAP_GRANITE_CAVE_B2F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -150,6 +188,9 @@
},
"REGION_GRANITE_CAVE_B2F/NORTH_EAST_ROOM": {
"parent_map": "MAP_GRANITE_CAVE_B2F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_GRANITE_CAVE_B2F_RARE_CANDY",
"HIDDEN_ITEM_GRANITE_CAVE_B2F_EVERSTONE_1"
@@ -162,6 +203,9 @@
},
"REGION_GRANITE_CAVE_B2F/LOWER": {
"parent_map": "MAP_GRANITE_CAVE_B2F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_GRANITE_CAVE_B2F_EVERSTONE_2"
],
@@ -174,6 +218,9 @@
},
"REGION_GRANITE_CAVE_STEVENS_ROOM/MAIN": {
"parent_map": "MAP_GRANITE_CAVE_STEVENS_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -185,8 +232,11 @@
},
"REGION_GRANITE_CAVE_STEVENS_ROOM/LETTER_DELIVERED": {
"parent_map": "MAP_GRANITE_CAVE_STEVENS_ROOM",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM47"
+ "NPC_GIFT_RECEIVED_TM_STEEL_WING"
],
"events": [
"EVENT_DELIVER_LETTER"
@@ -196,6 +246,9 @@
},
"REGION_TRAINER_HILL_ENTRANCE/MAIN": {
"parent_map": "MAP_TRAINER_HILL_ENTRANCE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -206,6 +259,9 @@
},
"REGION_TRAINER_HILL_1F/MAIN": {
"parent_map": "MAP_TRAINER_HILL_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -216,6 +272,9 @@
},
"REGION_TRAINER_HILL_2F/MAIN": {
"parent_map": "MAP_TRAINER_HILL_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -226,6 +285,9 @@
},
"REGION_TRAINER_HILL_3F/MAIN": {
"parent_map": "MAP_TRAINER_HILL_3F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -236,6 +298,9 @@
},
"REGION_TRAINER_HILL_4F/MAIN": {
"parent_map": "MAP_TRAINER_HILL_4F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -246,6 +311,9 @@
},
"REGION_TRAINER_HILL_ROOF/MAIN": {
"parent_map": "MAP_TRAINER_HILL_ROOF",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -256,6 +324,9 @@
},
"REGION_TRAINER_HILL_ELEVATOR/MAIN": {
"parent_map": "MAP_TRAINER_HILL_ELEVATOR",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -265,6 +336,9 @@
},
"REGION_FIERY_PATH/MAIN": {
"parent_map": "MAP_FIERY_PATH",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -277,8 +351,11 @@
},
"REGION_FIERY_PATH/BEHIND_BOULDER": {
"parent_map": "MAP_FIERY_PATH",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_FIERY_PATH_TM06",
+ "ITEM_FIERY_PATH_TM_TOXIC",
"ITEM_FIERY_PATH_FIRE_STONE"
],
"events": [],
@@ -289,7 +366,12 @@
},
"REGION_MAGMA_HIDEOUT_1F/MAIN": {
"parent_map": "MAP_MAGMA_HIDEOUT_1F",
- "locations": [],
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_2_REWARD"
+ ],
"events": [],
"exits": [
"REGION_MAGMA_HIDEOUT_1F/ENTRANCE"
@@ -300,6 +382,9 @@
},
"REGION_MAGMA_HIDEOUT_1F/CENTER_EXIT": {
"parent_map": "MAP_MAGMA_HIDEOUT_1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -311,8 +396,12 @@
},
"REGION_MAGMA_HIDEOUT_1F/LEDGE": {
"parent_map": "MAP_MAGMA_HIDEOUT_1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_MAGMA_HIDEOUT_1F_RARE_CANDY"
+ "ITEM_MAGMA_HIDEOUT_1F_RARE_CANDY",
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_1_REWARD"
],
"events": [],
"exits": [],
@@ -322,6 +411,9 @@
},
"REGION_MAGMA_HIDEOUT_1F/ENTRANCE": {
"parent_map": "MAP_MAGMA_HIDEOUT_1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -333,7 +425,15 @@
},
"REGION_MAGMA_HIDEOUT_2F_1R/MAIN": {
"parent_map": "MAP_MAGMA_HIDEOUT_2F_1R",
- "locations": [],
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_3_REWARD",
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_4_REWARD",
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_5_REWARD",
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_14_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -344,9 +444,16 @@
},
"REGION_MAGMA_HIDEOUT_2F_2R/MAIN": {
"parent_map": "MAP_MAGMA_HIDEOUT_2F_2R",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_MAGMA_HIDEOUT_2F_2R_MAX_ELIXIR",
- "ITEM_MAGMA_HIDEOUT_2F_2R_FULL_RESTORE"
+ "ITEM_MAGMA_HIDEOUT_2F_2R_FULL_RESTORE",
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_6_REWARD",
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_7_REWARD",
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_8_REWARD",
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_15_REWARD"
],
"events": [],
"exits": [],
@@ -357,6 +464,9 @@
},
"REGION_MAGMA_HIDEOUT_2F_3R/MAIN": {
"parent_map": "MAP_MAGMA_HIDEOUT_2F_3R",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -367,8 +477,13 @@
},
"REGION_MAGMA_HIDEOUT_3F_1R/MAIN": {
"parent_map": "MAP_MAGMA_HIDEOUT_3F_1R",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_MAGMA_HIDEOUT_3F_1R_NUGGET"
+ "ITEM_MAGMA_HIDEOUT_3F_1R_NUGGET",
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_9_REWARD",
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_16_REWARD"
],
"events": [],
"exits": [],
@@ -380,8 +495,12 @@
},
"REGION_MAGMA_HIDEOUT_3F_2R/MAIN": {
"parent_map": "MAP_MAGMA_HIDEOUT_3F_2R",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_MAGMA_HIDEOUT_3F_2R_PP_MAX"
+ "ITEM_MAGMA_HIDEOUT_3F_2R_PP_MAX",
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_10_REWARD"
],
"events": [],
"exits": [],
@@ -391,6 +510,9 @@
},
"REGION_MAGMA_HIDEOUT_3F_3R/MAIN": {
"parent_map": "MAP_MAGMA_HIDEOUT_3F_3R",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_MAGMA_HIDEOUT_3F_3R_ECAPE_ROPE"
],
@@ -403,8 +525,16 @@
},
"REGION_MAGMA_HIDEOUT_4F/MAIN": {
"parent_map": "MAP_MAGMA_HIDEOUT_4F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_MAGMA_HIDEOUT_4F_MAX_REVIVE"
+ "ITEM_MAGMA_HIDEOUT_4F_MAX_REVIVE",
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_11_REWARD",
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_12_REWARD",
+ "TRAINER_GRUNT_MAGMA_HIDEOUT_13_REWARD",
+ "TRAINER_TABITHA_MAGMA_HIDEOUT_REWARD",
+ "TRAINER_MAXIE_MAGMA_HIDEOUT_REWARD"
],
"events": [
"EVENT_RELEASE_GROUDON"
@@ -417,6 +547,9 @@
},
"REGION_MIRAGE_TOWER_1F/MAIN": {
"parent_map": "MAP_MIRAGE_TOWER_1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -427,6 +560,9 @@
},
"REGION_MIRAGE_TOWER_2F/TOP": {
"parent_map": "MAP_MIRAGE_TOWER_2F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -439,6 +575,9 @@
},
"REGION_MIRAGE_TOWER_2F/BOTTOM": {
"parent_map": "MAP_MIRAGE_TOWER_2F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -451,6 +590,9 @@
},
"REGION_MIRAGE_TOWER_3F/TOP": {
"parent_map": "MAP_MIRAGE_TOWER_3F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -462,6 +604,9 @@
},
"REGION_MIRAGE_TOWER_3F/BOTTOM": {
"parent_map": "MAP_MIRAGE_TOWER_3F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -474,6 +619,9 @@
},
"REGION_MIRAGE_TOWER_4F/MAIN": {
"parent_map": "MAP_MIRAGE_TOWER_4F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -485,24 +633,46 @@
},
"REGION_MIRAGE_TOWER_4F/FOSSIL_PLATFORM": {
"parent_map": "MAP_MIRAGE_TOWER_4F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
"warps": []
},
- "REGION_DESERT_RUINS/MAIN": {
+ "REGION_DESERT_RUINS/FRONT": {
"parent_map": "MAP_DESERT_RUINS",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
"warps": [
"MAP_DESERT_RUINS:0/MAP_ROUTE111:1",
- "MAP_DESERT_RUINS:1/MAP_DESERT_RUINS:2",
+ "MAP_DESERT_RUINS:1/MAP_DESERT_RUINS:2"
+ ]
+ },
+ "REGION_DESERT_RUINS/BACK": {
+ "parent_map": "MAP_DESERT_RUINS",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [
+ "EVENT_ENCOUNTER_REGIROCK"
+ ],
+ "exits": [],
+ "warps": [
"MAP_DESERT_RUINS:2/MAP_DESERT_RUINS:1"
]
},
"REGION_METEOR_FALLS_1F_1R/MAIN": {
"parent_map": "MAP_METEOR_FALLS_1F_1R",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"ITEM_METEOR_FALLS_1F_1R_MOON_STONE",
"ITEM_METEOR_FALLS_1F_1R_FULL_HEAL"
@@ -511,19 +681,48 @@
"EVENT_MAGMA_STEALS_METEORITE"
],
"exits": [
- "REGION_METEOR_FALLS_1F_1R/ABOVE_WATERFALL"
+ "REGION_METEOR_FALLS_1F_1R/WATER"
],
"warps": [
"MAP_METEOR_FALLS_1F_1R:0/MAP_ROUTE114:0",
"MAP_METEOR_FALLS_1F_1R:1/MAP_ROUTE115:0"
]
},
+ "REGION_METEOR_FALLS_1F_1R/WATER": {
+ "parent_map": "MAP_METEOR_FALLS_1F_1R",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_METEOR_FALLS_1F_1R/MAIN",
+ "REGION_METEOR_FALLS_1F_1R/WATER_ABOVE_WATERFALL"
+ ],
+ "warps": []
+ },
+ "REGION_METEOR_FALLS_1F_1R/WATER_ABOVE_WATERFALL": {
+ "parent_map": "MAP_METEOR_FALLS_1F_1R",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_METEOR_FALLS_1F_1R/WATER",
+ "REGION_METEOR_FALLS_1F_1R/ABOVE_WATERFALL"
+ ],
+ "warps": []
+ },
"REGION_METEOR_FALLS_1F_1R/ABOVE_WATERFALL": {
"parent_map": "MAP_METEOR_FALLS_1F_1R",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
- "REGION_METEOR_FALLS_1F_1R/MAIN"
+ "REGION_METEOR_FALLS_1F_1R/WATER_ABOVE_WATERFALL"
],
"warps": [
"MAP_METEOR_FALLS_1F_1R:2/MAP_METEOR_FALLS_1F_2R:0"
@@ -531,8 +730,11 @@
},
"REGION_METEOR_FALLS_1F_1R/TOP": {
"parent_map": "MAP_METEOR_FALLS_1F_1R",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_METEOR_FALLS_1F_1R_TM23"
+ "ITEM_METEOR_FALLS_1F_1R_TM_IRON_TAIL"
],
"events": [],
"exits": [],
@@ -543,6 +745,9 @@
},
"REGION_METEOR_FALLS_1F_1R/BOTTOM": {
"parent_map": "MAP_METEOR_FALLS_1F_1R",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_METEOR_FALLS_1F_1R_PP_UP"
],
@@ -554,7 +759,13 @@
},
"REGION_METEOR_FALLS_1F_2R/TOP": {
"parent_map": "MAP_METEOR_FALLS_1F_2R",
- "locations": [],
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_NICOLAS_1_REWARD",
+ "TRAINER_JOHN_AND_JAY_1_REWARD"
+ ],
"events": [],
"exits": [
"REGION_METEOR_FALLS_1F_2R/LEFT_SPLIT",
@@ -566,25 +777,58 @@
},
"REGION_METEOR_FALLS_1F_2R/LEFT_SPLIT": {
"parent_map": "MAP_METEOR_FALLS_1F_2R",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [],
- "exits": [],
+ "exits": [
+ "REGION_METEOR_FALLS_1F_2R/LEFT_SPLIT_WATER"
+ ],
"warps": [
"MAP_METEOR_FALLS_1F_2R:2/MAP_METEOR_FALLS_B1F_1R:1"
]
},
- "REGION_METEOR_FALLS_1F_2R/RIGHT_SPLIT": {
+ "REGION_METEOR_FALLS_1F_2R/LEFT_SPLIT_WATER": {
"parent_map": "MAP_METEOR_FALLS_1F_2R",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [],
+ "warps": []
+ },
+ "REGION_METEOR_FALLS_1F_2R/RIGHT_SPLIT": {
+ "parent_map": "MAP_METEOR_FALLS_1F_2R",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_METEOR_FALLS_1F_2R/RIGHT_SPLIT_WATER"
+ ],
"warps": [
"MAP_METEOR_FALLS_1F_2R:0/MAP_METEOR_FALLS_1F_1R:2",
"MAP_METEOR_FALLS_1F_2R:3/MAP_METEOR_FALLS_B1F_1R:2"
]
},
+ "REGION_METEOR_FALLS_1F_2R/RIGHT_SPLIT_WATER": {
+ "parent_map": "MAP_METEOR_FALLS_1F_2R",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": []
+ },
"REGION_METEOR_FALLS_B1F_1R/UPPER": {
"parent_map": "MAP_METEOR_FALLS_B1F_1R",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -596,6 +840,9 @@
},
"REGION_METEOR_FALLS_B1F_1R/HIGHEST_LADDER": {
"parent_map": "MAP_METEOR_FALLS_B1F_1R",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -607,6 +854,9 @@
},
"REGION_METEOR_FALLS_B1F_1R/NORTH_SHORE": {
"parent_map": "MAP_METEOR_FALLS_B1F_1R",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -618,6 +868,9 @@
},
"REGION_METEOR_FALLS_B1F_1R/SOUTH_SHORE": {
"parent_map": "MAP_METEOR_FALLS_B1F_1R",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -629,6 +882,9 @@
},
"REGION_METEOR_FALLS_B1F_1R/WATER": {
"parent_map": "MAP_METEOR_FALLS_B1F_1R",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -640,6 +896,9 @@
},
"REGION_METEOR_FALLS_B1F_2R/ENTRANCE": {
"parent_map": "MAP_METEOR_FALLS_B1F_2R",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -651,8 +910,11 @@
},
"REGION_METEOR_FALLS_B1F_2R/WATER": {
"parent_map": "MAP_METEOR_FALLS_B1F_2R",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
- "ITEM_METEOR_FALLS_B1F_2R_TM02"
+ "ITEM_METEOR_FALLS_B1F_2R_TM_DRAGON_CLAW"
],
"events": [],
"exits": [
@@ -662,7 +924,12 @@
},
"REGION_METEOR_FALLS_STEVENS_CAVE/MAIN": {
"parent_map": "MAP_METEOR_FALLS_STEVENS_CAVE",
- "locations": [],
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_STEVEN_REWARD"
+ ],
"events": [
"EVENT_DEFEAT_STEVEN"
],
@@ -671,28 +938,38 @@
"MAP_METEOR_FALLS_STEVENS_CAVE:0/MAP_METEOR_FALLS_1F_1R:5"
]
},
- "REGION_ALTERING_CAVE/MAIN": {
- "parent_map": "MAP_ALTERING_CAVE",
+ "REGION_ISLAND_CAVE/FRONT": {
+ "parent_map": "MAP_ISLAND_CAVE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
"warps": [
- "MAP_ALTERING_CAVE:0/MAP_ROUTE103:0"
+ "MAP_ISLAND_CAVE:0/MAP_ROUTE105:0",
+ "MAP_ISLAND_CAVE:1/MAP_ISLAND_CAVE:2"
]
},
- "REGION_ISLAND_CAVE/MAIN": {
+ "REGION_ISLAND_CAVE/BACK": {
"parent_map": "MAP_ISLAND_CAVE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
- "events": [],
+ "events": [
+ "EVENT_ENCOUNTER_REGICE"
+ ],
"exits": [],
"warps": [
- "MAP_ISLAND_CAVE:0/MAP_ROUTE105:0",
- "MAP_ISLAND_CAVE:1/MAP_ISLAND_CAVE:2",
"MAP_ISLAND_CAVE:2/MAP_ISLAND_CAVE:1"
]
},
"REGION_ABANDONED_SHIP_DECK/ENTRANCE": {
"parent_map": "MAP_ABANDONED_SHIP_DECK",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -703,6 +980,9 @@
},
"REGION_ABANDONED_SHIP_DECK/UPPER": {
"parent_map": "MAP_ABANDONED_SHIP_DECK",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -713,6 +993,9 @@
},
"REGION_ABANDONED_SHIP_CAPTAINS_OFFICE/MAIN": {
"parent_map": "MAP_ABANDONED_SHIP_CAPTAINS_OFFICE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_ABANDONED_SHIP_CAPTAINS_OFFICE_STORAGE_KEY"
],
@@ -724,7 +1007,12 @@
},
"REGION_ABANDONED_SHIP_CORRIDORS_1F/WEST": {
"parent_map": "MAP_ABANDONED_SHIP_CORRIDORS_1F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_CHARLIE_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -736,6 +1024,9 @@
},
"REGION_ABANDONED_SHIP_CORRIDORS_1F/EAST": {
"parent_map": "MAP_ABANDONED_SHIP_CORRIDORS_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -750,7 +1041,13 @@
},
"REGION_ABANDONED_SHIP_ROOMS_1F/MAIN": {
"parent_map": "MAP_ABANDONED_SHIP_ROOMS_1F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_THALIA_1_REWARD",
+ "TRAINER_DEMETRIUS_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -761,6 +1058,9 @@
},
"REGION_ABANDONED_SHIP_ROOMS_1F/NORTH_WEST": {
"parent_map": "MAP_ABANDONED_SHIP_ROOMS_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_ABANDONED_SHIP_ROOMS_1F_HARBOR_MAIL"
],
@@ -772,8 +1072,14 @@
},
"REGION_ABANDONED_SHIP_ROOMS2_1F/MAIN": {
"parent_map": "MAP_ABANDONED_SHIP_ROOMS2_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_ABANDONED_SHIP_ROOMS_2_1F_REVIVE"
+ "ITEM_ABANDONED_SHIP_ROOMS_2_1F_REVIVE",
+ "TRAINER_JANI_REWARD",
+ "TRAINER_GARRISON_REWARD",
+ "TRAINER_KIRA_AND_DAN_1_REWARD"
],
"events": [],
"exits": [],
@@ -784,7 +1090,12 @@
},
"REGION_ABANDONED_SHIP_CORRIDORS_B1F/MAIN": {
"parent_map": "MAP_ABANDONED_SHIP_CORRIDORS_B1F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_DUNCAN_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -800,6 +1111,9 @@
},
"REGION_ABANDONED_SHIP_ROOMS_B1F/LEFT": {
"parent_map": "MAP_ABANDONED_SHIP_ROOMS_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_ABANDONED_SHIP_ROOMS_B1F_ESCAPE_ROPE"
],
@@ -811,6 +1125,9 @@
},
"REGION_ABANDONED_SHIP_ROOMS_B1F/CENTER": {
"parent_map": "MAP_ABANDONED_SHIP_ROOMS_B1F",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -822,6 +1139,9 @@
},
"REGION_ABANDONED_SHIP_ROOMS_B1F/RIGHT": {
"parent_map": "MAP_ABANDONED_SHIP_ROOMS_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -831,6 +1151,9 @@
},
"REGION_ABANDONED_SHIP_ROOMS2_B1F/MAIN": {
"parent_map": "MAP_ABANDONED_SHIP_ROOMS2_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_ABANDONED_SHIP_ROOMS_2_B1F_DIVE_BALL"
],
@@ -843,8 +1166,11 @@
},
"REGION_ABANDONED_SHIP_ROOM_B1F/MAIN": {
"parent_map": "MAP_ABANDONED_SHIP_ROOM_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_ABANDONED_SHIP_ROOMS_B1F_TM13"
+ "ITEM_ABANDONED_SHIP_ROOMS_B1F_TM_ICE_BEAM"
],
"events": [],
"exits": [],
@@ -854,6 +1180,9 @@
},
"REGION_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS/MAIN": {
"parent_map": "MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -870,6 +1199,9 @@
},
"REGION_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS/TOP_LEFT": {
"parent_map": "MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_ABANDONED_SHIP_RM_6_KEY"
],
@@ -881,6 +1213,9 @@
},
"REGION_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS/TOP_CENTER_DOORWAY": {
"parent_map": "MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -890,6 +1225,9 @@
},
"REGION_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS/TOP_RIGHT": {
"parent_map": "MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_6_LUXURY_BALL",
"HIDDEN_ITEM_ABANDONED_SHIP_RM_2_KEY"
@@ -902,8 +1240,11 @@
},
"REGION_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS/BOTTOM_LEFT": {
"parent_map": "MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_1_TM18",
+ "ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_1_TM_RAIN_DANCE",
"HIDDEN_ITEM_ABANDONED_SHIP_RM_4_KEY"
],
"events": [],
@@ -914,8 +1255,11 @@
},
"REGION_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS/BOTTOM_CENTER": {
"parent_map": "MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_4_SCANNER"
+ "ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_2_SCANNER"
],
"events": [],
"exits": [],
@@ -925,6 +1269,9 @@
},
"REGION_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS/BOTTOM_RIGHT": {
"parent_map": "MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_ABANDONED_SHIP_HIDDEN_FLOOR_ROOM_3_WATER_STONE",
"HIDDEN_ITEM_ABANDONED_SHIP_RM_1_KEY"
@@ -937,6 +1284,9 @@
},
"REGION_ABANDONED_SHIP_UNDERWATER1/MAIN": {
"parent_map": "MAP_ABANDONED_SHIP_UNDERWATER1",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -948,6 +1298,9 @@
},
"REGION_ABANDONED_SHIP_UNDERWATER2/MAIN": {
"parent_map": "MAP_ABANDONED_SHIP_UNDERWATER2",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -959,6 +1312,9 @@
},
"REGION_NEW_MAUVILLE_ENTRANCE/MAIN": {
"parent_map": "MAP_NEW_MAUVILLE_ENTRANCE",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -969,6 +1325,9 @@
},
"REGION_NEW_MAUVILLE_INSIDE/MAIN": {
"parent_map": "MAP_NEW_MAUVILLE_INSIDE",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_NEW_MAUVILLE_ULTRA_BALL",
"ITEM_NEW_MAUVILLE_ESCAPE_ROPE",
@@ -986,8 +1345,11 @@
},
"REGION_SCORCHED_SLAB/MAIN": {
"parent_map": "MAP_SCORCHED_SLAB",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_SCORCHED_SLAB_TM11"
+ "ITEM_SCORCHED_SLAB_TM_SUNNY_DAY"
],
"events": [],
"exits": [],
@@ -995,19 +1357,38 @@
"MAP_SCORCHED_SLAB:0/MAP_ROUTE120:1"
]
},
- "REGION_ANCIENT_TOMB/MAIN": {
+ "REGION_ANCIENT_TOMB/FRONT": {
"parent_map": "MAP_ANCIENT_TOMB",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
"warps": [
"MAP_ANCIENT_TOMB:0/MAP_ROUTE120:0",
- "MAP_ANCIENT_TOMB:1/MAP_ANCIENT_TOMB:2",
+ "MAP_ANCIENT_TOMB:1/MAP_ANCIENT_TOMB:2"
+ ]
+ },
+ "REGION_ANCIENT_TOMB/BACK": {
+ "parent_map": "MAP_ANCIENT_TOMB",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [
+ "EVENT_ENCOUNTER_REGISTEEL"
+ ],
+ "exits": [],
+ "warps": [
"MAP_ANCIENT_TOMB:2/MAP_ANCIENT_TOMB:1"
]
},
"REGION_MT_PYRE_1F/MAIN": {
"parent_map": "MAP_MT_PYRE_1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_CLEANSE_TAG"
],
@@ -1022,8 +1403,15 @@
},
"REGION_MT_PYRE_2F/MAIN": {
"parent_map": "MAP_MT_PYRE_2F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_MT_PYRE_2F_ULTRA_BALL"
+ "ITEM_MT_PYRE_2F_ULTRA_BALL",
+ "TRAINER_MARK_REWARD",
+ "TRAINER_LEAH_REWARD",
+ "TRAINER_ZANDER_REWARD",
+ "TRAINER_DEZ_AND_LUKE_REWARD"
],
"events": [],
"exits": [],
@@ -1037,8 +1425,14 @@
},
"REGION_MT_PYRE_3F/MAIN": {
"parent_map": "MAP_MT_PYRE_3F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_MT_PYRE_3F_SUPER_REPEL"
+ "ITEM_MT_PYRE_3F_SUPER_REPEL",
+ "TRAINER_WILLIAM_REWARD",
+ "TRAINER_GABRIELLE_1_REWARD",
+ "TRAINER_KAYLA_REWARD"
],
"events": [],
"exits": [],
@@ -1053,8 +1447,12 @@
},
"REGION_MT_PYRE_4F/MAIN": {
"parent_map": "MAP_MT_PYRE_4F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_MT_PYRE_4F_SEA_INCENSE"
+ "ITEM_MT_PYRE_4F_SEA_INCENSE",
+ "TRAINER_TASHA_REWARD"
],
"events": [],
"exits": [],
@@ -1069,8 +1467,12 @@
},
"REGION_MT_PYRE_5F/MAIN": {
"parent_map": "MAP_MT_PYRE_5F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_MT_PYRE_5F_LAX_INCENSE"
+ "ITEM_MT_PYRE_5F_LAX_INCENSE",
+ "TRAINER_ATSUSHI_REWARD"
],
"events": [],
"exits": [],
@@ -1084,8 +1486,13 @@
},
"REGION_MT_PYRE_6F/MAIN": {
"parent_map": "MAP_MT_PYRE_6F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_MT_PYRE_6F_TM30"
+ "ITEM_MT_PYRE_6F_TM_SHADOW_BALL",
+ "TRAINER_VALERIE_1_REWARD",
+ "TRAINER_CEDRIC_REWARD"
],
"events": [],
"exits": [],
@@ -1096,9 +1503,12 @@
},
"REGION_MT_PYRE_EXTERIOR/MAIN": {
"parent_map": "MAP_MT_PYRE_EXTERIOR",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_MT_PYRE_EXTERIOR_MAX_POTION",
- "ITEM_MT_PYRE_EXTERIOR_TM48",
+ "ITEM_MT_PYRE_EXTERIOR_TM_SKILL_SWAP",
"HIDDEN_ITEM_MT_PYRE_EXTERIOR_ULTRA_BALL",
"HIDDEN_ITEM_MT_PYRE_EXTERIOR_MAX_ETHER"
],
@@ -1111,10 +1521,17 @@
},
"REGION_MT_PYRE_SUMMIT/MAIN": {
"parent_map": "MAP_MT_PYRE_SUMMIT",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_MT_PYRE_SUMMIT_ZINC",
"HIDDEN_ITEM_MT_PYRE_SUMMIT_RARE_CANDY",
- "NPC_GIFT_RECEIVED_MAGMA_EMBLEM"
+ "NPC_GIFT_RECEIVED_MAGMA_EMBLEM",
+ "TRAINER_GRUNT_MT_PYRE_1_REWARD",
+ "TRAINER_GRUNT_MT_PYRE_2_REWARD",
+ "TRAINER_GRUNT_MT_PYRE_3_REWARD",
+ "TRAINER_GRUNT_MT_PYRE_4_REWARD"
],
"events": [],
"exits": [],
@@ -1124,7 +1541,12 @@
},
"REGION_AQUA_HIDEOUT_1F/MAIN": {
"parent_map": "MAP_AQUA_HIDEOUT_1F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_GRUNT_AQUA_HIDEOUT_1_REWARD"
+ ],
"events": [],
"exits": [
"REGION_AQUA_HIDEOUT_1F/WATER"
@@ -1135,6 +1557,9 @@
},
"REGION_AQUA_HIDEOUT_1F/WATER": {
"parent_map": "MAP_AQUA_HIDEOUT_1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1146,7 +1571,15 @@
},
"REGION_AQUA_HIDEOUT_B1F/WEST_BOTTOM": {
"parent_map": "MAP_AQUA_HIDEOUT_B1F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_GRUNT_AQUA_HIDEOUT_2_REWARD",
+ "TRAINER_GRUNT_AQUA_HIDEOUT_5_REWARD",
+ "TRAINER_GRUNT_AQUA_HIDEOUT_7_REWARD"
+
+ ],
"events": [],
"exits": [],
"warps": [
@@ -1157,7 +1590,12 @@
},
"REGION_AQUA_HIDEOUT_B1F/WEST_TOP_LEFT": {
"parent_map": "MAP_AQUA_HIDEOUT_B1F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_GRUNT_AQUA_HIDEOUT_3_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -1167,6 +1605,9 @@
},
"REGION_AQUA_HIDEOUT_B1F/WEST_TOP_CENTER": {
"parent_map": "MAP_AQUA_HIDEOUT_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1177,6 +1618,9 @@
},
"REGION_AQUA_HIDEOUT_B1F/WEST_TOP_RIGHT": {
"parent_map": "MAP_AQUA_HIDEOUT_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1188,6 +1632,9 @@
},
"REGION_AQUA_HIDEOUT_B1F/WEST_CENTER_RIGHT": {
"parent_map": "MAP_AQUA_HIDEOUT_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_AQUA_HIDEOUT_B1F_MAX_ELIXIR"
],
@@ -1199,6 +1646,9 @@
},
"REGION_AQUA_HIDEOUT_B1F/WEST_CENTER": {
"parent_map": "MAP_AQUA_HIDEOUT_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_AQUA_HIDEOUT_B1F_NUGGET",
"ITEM_AQUA_HIDEOUT_B1F_MASTER_BALL"
@@ -1211,6 +1661,9 @@
},
"REGION_AQUA_HIDEOUT_B1F/EAST_TOP": {
"parent_map": "MAP_AQUA_HIDEOUT_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1223,6 +1676,9 @@
},
"REGION_AQUA_HIDEOUT_B1F/EAST_ROW_1_RIGHT": {
"parent_map": "MAP_AQUA_HIDEOUT_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1234,6 +1690,9 @@
},
"REGION_AQUA_HIDEOUT_B1F/EAST_ROW_1_CENTER": {
"parent_map": "MAP_AQUA_HIDEOUT_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1246,6 +1705,9 @@
},
"REGION_AQUA_HIDEOUT_B1F/EAST_ROW_1_LEFT": {
"parent_map": "MAP_AQUA_HIDEOUT_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1257,6 +1719,9 @@
},
"REGION_AQUA_HIDEOUT_B1F/EAST_ROW_2_RIGHT": {
"parent_map": "MAP_AQUA_HIDEOUT_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1268,6 +1733,9 @@
},
"REGION_AQUA_HIDEOUT_B1F/EAST_ROW_2_CENTER": {
"parent_map": "MAP_AQUA_HIDEOUT_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1280,6 +1748,9 @@
},
"REGION_AQUA_HIDEOUT_B1F/EAST_ROW_2_LEFT": {
"parent_map": "MAP_AQUA_HIDEOUT_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1291,6 +1762,9 @@
},
"REGION_AQUA_HIDEOUT_B1F/EAST_BOTTOM": {
"parent_map": "MAP_AQUA_HIDEOUT_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1302,6 +1776,9 @@
},
"REGION_AQUA_HIDEOUT_B2F/TOP_LEFT": {
"parent_map": "MAP_AQUA_HIDEOUT_B2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1312,7 +1789,13 @@
},
"REGION_AQUA_HIDEOUT_B2F/TOP_CENTER": {
"parent_map": "MAP_AQUA_HIDEOUT_B2F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_GRUNT_AQUA_HIDEOUT_6_REWARD",
+ "TRAINER_GRUNT_AQUA_HIDEOUT_8_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -1322,7 +1805,12 @@
},
"REGION_AQUA_HIDEOUT_B2F/TOP_RIGHT": {
"parent_map": "MAP_AQUA_HIDEOUT_B2F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_GRUNT_AQUA_HIDEOUT_4_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -1333,6 +1821,9 @@
},
"REGION_AQUA_HIDEOUT_B2F/BOTTOM_LEFT": {
"parent_map": "MAP_AQUA_HIDEOUT_B2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_AQUA_HIDEOUT_B2F_NEST_BALL"
],
@@ -1344,7 +1835,12 @@
},
"REGION_AQUA_HIDEOUT_B2F/BOTTOM_RIGHT": {
"parent_map": "MAP_AQUA_HIDEOUT_B2F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_MATT_REWARD"
+ ],
"events": [
"EVENT_CLEAR_AQUA_HIDEOUT"
],
@@ -1356,6 +1852,9 @@
},
"REGION_SHOAL_CAVE_ENTRANCE_ROOM/SOUTH": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1368,6 +1867,9 @@
},
"REGION_SHOAL_CAVE_ENTRANCE_ROOM/NORTH_WEST_CORNER": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1379,6 +1881,9 @@
},
"REGION_SHOAL_CAVE_ENTRANCE_ROOM/NORTH_EAST_CORNER": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_SHOAL_CAVE_ENTRANCE_BIG_PEARL"
],
@@ -1392,6 +1897,9 @@
},
"REGION_SHOAL_CAVE_ENTRANCE_ROOM/HIGH_TIDE_WATER": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -1402,6 +1910,9 @@
},
"REGION_SHOAL_CAVE_ENTRANCE_ROOM/LOW_TIDE_LOWER": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1413,6 +1924,9 @@
},
"REGION_SHOAL_CAVE_INNER_ROOM/SOUTH_EAST_CORNER": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1424,6 +1938,9 @@
},
"REGION_SHOAL_CAVE_INNER_ROOM/HIGH_TIDE_EAST_MIDDLE_GROUND": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1436,6 +1953,9 @@
},
"REGION_SHOAL_CAVE_INNER_ROOM/LOW_TIDE_EAST_MIDDLE_GROUND": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1446,6 +1966,9 @@
},
"REGION_SHOAL_CAVE_INNER_ROOM/SOUTH_WEST_CORNER": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1457,6 +1980,9 @@
},
"REGION_SHOAL_CAVE_INNER_ROOM/BRIDGES": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1467,6 +1993,9 @@
},
"REGION_SHOAL_CAVE_INNER_ROOM/RARE_CANDY_PLATFORM": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_SHOAL_CAVE_INNER_ROOM_RARE_CANDY"
],
@@ -1478,6 +2007,9 @@
},
"REGION_SHOAL_CAVE_INNER_ROOM/SOUTH_EAST_WATER": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -1488,6 +2020,9 @@
},
"REGION_SHOAL_CAVE_INNER_ROOM/EAST_WATER": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -1497,6 +2032,9 @@
},
"REGION_SHOAL_CAVE_INNER_ROOM/NORTH_WEST_WATER": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -1507,6 +2045,9 @@
},
"REGION_SHOAL_CAVE_INNER_ROOM/LOW_TIDE_SOUTH_EAST_LOWER": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1519,6 +2060,9 @@
},
"REGION_SHOAL_CAVE_INNER_ROOM/LOW_TIDE_EAST_LOWER": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1530,6 +2074,9 @@
},
"REGION_SHOAL_CAVE_INNER_ROOM/LOW_TIDE_NORTH_WEST_LOWER": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_INNER_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1539,6 +2086,9 @@
},
"REGION_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM/MAIN": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_STAIRS_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_SHOAL_CAVE_STAIRS_ROOM_ICE_HEAL"
],
@@ -1551,6 +2101,9 @@
},
"REGION_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM/NORTH_WEST": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_FOCUS_BAND"
],
@@ -1566,6 +2119,9 @@
},
"REGION_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM/SOUTH": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1575,6 +2131,9 @@
},
"REGION_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM/EAST": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1586,8 +2145,11 @@
},
"REGION_SHOAL_CAVE_LOW_TIDE_ICE_ROOM/MAIN": {
"parent_map": "MAP_SHOAL_CAVE_LOW_TIDE_ICE_ROOM",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_SHOAL_CAVE_ICE_ROOM_TM07",
+ "ITEM_SHOAL_CAVE_ICE_ROOM_TM_HAIL",
"ITEM_SHOAL_CAVE_ICE_ROOM_NEVER_MELT_ICE"
],
"events": [],
@@ -1598,30 +2160,56 @@
},
"REGION_UNDERWATER_SEAFLOOR_CAVERN/MAIN": {
"parent_map": "MAP_UNDERWATER_SEAFLOOR_CAVERN",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
- "REGION_SEAFLOOR_CAVERN_ENTRANCE/MAIN"
+ "REGION_SEAFLOOR_CAVERN_ENTRANCE/WATER"
],
"warps": [
"MAP_UNDERWATER_SEAFLOOR_CAVERN:0/MAP_UNDERWATER_ROUTE128:0"
]
},
+ "REGION_SEAFLOOR_CAVERN_ENTRANCE/WATER": {
+ "parent_map": "MAP_SEAFLOOR_CAVERN_ENTRANCE",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_UNDERWATER_SEAFLOOR_CAVERN/MAIN",
+ "REGION_SEAFLOOR_CAVERN_ENTRANCE/MAIN"
+ ],
+ "warps": [
+ "MAP_SEAFLOOR_CAVERN_ENTRANCE:0/MAP_UNDERWATER_ROUTE128:0!"
+ ]
+ },
"REGION_SEAFLOOR_CAVERN_ENTRANCE/MAIN": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ENTRANCE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
- "REGION_UNDERWATER_SEAFLOOR_CAVERN/MAIN"
+ "REGION_SEAFLOOR_CAVERN_ENTRANCE/WATER"
],
"warps": [
- "MAP_SEAFLOOR_CAVERN_ENTRANCE:0/MAP_UNDERWATER_ROUTE128:0!",
"MAP_SEAFLOOR_CAVERN_ENTRANCE:1/MAP_SEAFLOOR_CAVERN_ROOM1:0"
]
},
"REGION_SEAFLOOR_CAVERN_ROOM1/NORTH": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM1",
- "locations": [],
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_GRUNT_SEAFLOOR_CAVERN_1_REWARD",
+ "TRAINER_GRUNT_SEAFLOOR_CAVERN_2_REWARD"
+ ],
"events": [],
"exits": [
"REGION_SEAFLOOR_CAVERN_ROOM1/SOUTH"
@@ -1633,6 +2221,9 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM1/SOUTH": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM1",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1644,6 +2235,9 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM2/SOUTH_WEST": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM2",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1656,6 +2250,9 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM2/NORTH_WEST": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM2",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1669,6 +2266,9 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM2/SOUTH_EAST": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM2",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1680,6 +2280,9 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM2/NORTH_EAST": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM2",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1689,7 +2292,13 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM3/MAIN": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM3",
- "locations": [],
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_GRUNT_SEAFLOOR_CAVERN_5_REWARD",
+ "TRAINER_SHELLY_SEAFLOOR_CAVERN_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -1700,7 +2309,13 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM4/NORTH_WEST": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM4",
- "locations": [],
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_GRUNT_SEAFLOOR_CAVERN_3_REWARD",
+ "TRAINER_GRUNT_SEAFLOOR_CAVERN_4_REWARD"
+ ],
"events": [],
"exits": [
"REGION_SEAFLOOR_CAVERN_ROOM4/EAST"
@@ -1711,6 +2326,9 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM4/EAST": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM4",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1723,6 +2341,9 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM4/SOUTH": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM4",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1732,6 +2353,9 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM5/NORTH_WEST": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM5",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1744,6 +2368,9 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM5/EAST": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM5",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1755,6 +2382,9 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM5/SOUTH_WEST": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM5",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1766,31 +2396,41 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM6/NORTH_WEST": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM6",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
- "REGION_SEAFLOOR_CAVERN_ROOM6/CAVE_ON_WATER"
+ "REGION_SEAFLOOR_CAVERN_ROOM6/WATER"
],
"warps": [
"MAP_SEAFLOOR_CAVERN_ROOM6:1/MAP_SEAFLOOR_CAVERN_ROOM3:2"
]
},
- "REGION_SEAFLOOR_CAVERN_ROOM6/CAVE_ON_WATER": {
+ "REGION_SEAFLOOR_CAVERN_ROOM6/WATER": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM6",
+ "has_grass": true,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
- "exits": [],
+ "exits": [
+ "REGION_SEAFLOOR_CAVERN_ROOM6/NORTH_WEST"
+ ],
"warps": [
"MAP_SEAFLOOR_CAVERN_ROOM6:2/MAP_SEAFLOOR_CAVERN_ENTRANCE:1!"
]
},
"REGION_SEAFLOOR_CAVERN_ROOM6/SOUTH": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM6",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
- "REGION_SEAFLOOR_CAVERN_ROOM6/NORTH_WEST",
- "REGION_SEAFLOOR_CAVERN_ROOM6/CAVE_ON_WATER"
+ "REGION_SEAFLOOR_CAVERN_ROOM6/WATER"
],
"warps": [
"MAP_SEAFLOOR_CAVERN_ROOM6:0/MAP_SEAFLOOR_CAVERN_ROOM2:2"
@@ -1798,21 +2438,39 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM7/NORTH": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM7",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
- "REGION_SEAFLOOR_CAVERN_ROOM7/SOUTH"
+ "REGION_SEAFLOOR_CAVERN_ROOM7/WATER"
],
"warps": [
"MAP_SEAFLOOR_CAVERN_ROOM7:1/MAP_SEAFLOOR_CAVERN_ROOM3:1"
]
},
+ "REGION_SEAFLOOR_CAVERN_ROOM7/WATER": {
+ "parent_map": "MAP_SEAFLOOR_CAVERN_ROOM7",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_SEAFLOOR_CAVERN_ROOM7/SOUTH"
+ ],
+ "warps": []
+ },
"REGION_SEAFLOOR_CAVERN_ROOM7/SOUTH": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM7",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
- "REGION_SEAFLOOR_CAVERN_ROOM7/NORTH"
+ "REGION_SEAFLOOR_CAVERN_ROOM7/WATER"
],
"warps": [
"MAP_SEAFLOOR_CAVERN_ROOM7:0/MAP_SEAFLOOR_CAVERN_ROOM2:3"
@@ -1820,6 +2478,9 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM8/NORTH": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM8",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1831,6 +2492,9 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM8/SOUTH": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM8",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1842,8 +2506,12 @@
},
"REGION_SEAFLOOR_CAVERN_ROOM9/MAIN": {
"parent_map": "MAP_SEAFLOOR_CAVERN_ROOM9",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_SEAFLOOR_CAVERN_ROOM_9_TM26"
+ "ITEM_SEAFLOOR_CAVERN_ROOM_9_TM_EARTHQUAKE",
+ "TRAINER_ARCHIE_REWARD"
],
"events": [
"EVENT_RELEASE_KYOGRE"
@@ -1855,6 +2523,9 @@
},
"REGION_CAVE_OF_ORIGIN_ENTRANCE/MAIN": {
"parent_map": "MAP_CAVE_OF_ORIGIN_ENTRANCE",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1865,6 +2536,9 @@
},
"REGION_CAVE_OF_ORIGIN_1F/MAIN": {
"parent_map": "MAP_CAVE_OF_ORIGIN_1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1875,10 +2549,11 @@
},
"REGION_CAVE_OF_ORIGIN_B1F/MAIN": {
"parent_map": "MAP_CAVE_OF_ORIGIN_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
- "events": [
- "EVENT_WALLACE_GOES_TO_SKY_PILLAR"
- ],
+ "events": [],
"exits": [],
"warps": [
"MAP_CAVE_OF_ORIGIN_B1F:0/MAP_CAVE_OF_ORIGIN_1F:1"
@@ -1886,6 +2561,9 @@
},
"REGION_SKY_PILLAR_ENTRANCE/MAIN": {
"parent_map": "MAP_SKY_PILLAR_ENTRANCE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1896,6 +2574,9 @@
},
"REGION_SKY_PILLAR_OUTSIDE/MAIN": {
"parent_map": "MAP_SKY_PILLAR_OUTSIDE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1906,6 +2587,9 @@
},
"REGION_SKY_PILLAR_1F/MAIN": {
"parent_map": "MAP_SKY_PILLAR_1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1916,6 +2600,9 @@
},
"REGION_SKY_PILLAR_2F/LEFT": {
"parent_map": "MAP_SKY_PILLAR_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1928,6 +2615,9 @@
},
"REGION_SKY_PILLAR_2F/RIGHT": {
"parent_map": "MAP_SKY_PILLAR_2F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1940,6 +2630,9 @@
},
"REGION_SKY_PILLAR_3F/MAIN": {
"parent_map": "MAP_SKY_PILLAR_3F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1950,6 +2643,9 @@
},
"REGION_SKY_PILLAR_3F/TOP_CENTER": {
"parent_map": "MAP_SKY_PILLAR_3F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1959,6 +2655,9 @@
},
"REGION_SKY_PILLAR_4F/MAIN": {
"parent_map": "MAP_SKY_PILLAR_4F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1971,6 +2670,9 @@
},
"REGION_SKY_PILLAR_4F/ABOVE_3F_TOP_CENTER": {
"parent_map": "MAP_SKY_PILLAR_4F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1980,6 +2682,9 @@
},
"REGION_SKY_PILLAR_4F/TOP_LEFT": {
"parent_map": "MAP_SKY_PILLAR_4F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1990,6 +2695,9 @@
},
"REGION_SKY_PILLAR_5F/MAIN": {
"parent_map": "MAP_SKY_PILLAR_5F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -2000,9 +2708,12 @@
},
"REGION_SKY_PILLAR_TOP/MAIN": {
"parent_map": "MAP_SKY_PILLAR_TOP",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [
- "EVENT_WAKE_RAYQUAZA"
+ "EVENT_ENCOUNTER_RAYQUAZA"
],
"exits": [],
"warps": [
@@ -2011,10 +2722,14 @@
},
"REGION_UNDERWATER_SEALED_CHAMBER/MAIN": {
"parent_map": "MAP_UNDERWATER_SEALED_CHAMBER",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
- "REGION_SEALED_CHAMBER_OUTER_ROOM/MAIN"
+ "REGION_SEALED_CHAMBER_OUTER_ROOM/MAIN",
+ "REGION_ROUTE134/MAIN"
],
"warps": [
"MAP_UNDERWATER_SEALED_CHAMBER:0/MAP_UNDERWATER_ROUTE134:0"
@@ -2022,10 +2737,26 @@
},
"REGION_SEALED_CHAMBER_OUTER_ROOM/MAIN": {
"parent_map": "MAP_SEALED_CHAMBER_OUTER_ROOM",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
- "REGION_UNDERWATER_SEALED_CHAMBER/MAIN"
+ "REGION_UNDERWATER_SEALED_CHAMBER/MAIN",
+ "REGION_SEALED_CHAMBER_OUTER_ROOM/CRUMBLED_WALL"
+ ],
+ "warps": []
+ },
+ "REGION_SEALED_CHAMBER_OUTER_ROOM/CRUMBLED_WALL": {
+ "parent_map": "MAP_SEALED_CHAMBER_OUTER_ROOM",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_SEALED_CHAMBER_OUTER_ROOM/MAIN"
],
"warps": [
"MAP_SEALED_CHAMBER_OUTER_ROOM:0/MAP_SEALED_CHAMBER_INNER_ROOM:0"
@@ -2033,8 +2764,13 @@
},
"REGION_SEALED_CHAMBER_INNER_ROOM/MAIN": {
"parent_map": "MAP_SEALED_CHAMBER_INNER_ROOM",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
- "events": [],
+ "events": [
+ "EVENT_UNDO_REGI_SEAL"
+ ],
"exits": [],
"warps": [
"MAP_SEALED_CHAMBER_INNER_ROOM:0/MAP_SEALED_CHAMBER_OUTER_ROOM:0"
@@ -2042,7 +2778,14 @@
},
"REGION_VICTORY_ROAD_1F/NORTH_EAST": {
"parent_map": "MAP_VICTORY_ROAD_1F",
- "locations": [],
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_EDGAR_REWARD",
+ "TRAINER_KATELYNN_REWARD",
+ "TRAINER_QUINCY_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -2052,8 +2795,14 @@
},
"REGION_VICTORY_ROAD_1F/SOUTH_WEST": {
"parent_map": "MAP_VICTORY_ROAD_1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_VICTORY_ROAD_1F_MAX_ELIXIR"
+ "ITEM_VICTORY_ROAD_1F_MAX_ELIXIR",
+ "TRAINER_ALBERT_REWARD",
+ "TRAINER_HOPE_REWARD",
+ "TRAINER_WALLY_VR_1_REWARD"
],
"events": [],
"exits": [],
@@ -2064,6 +2813,9 @@
},
"REGION_VICTORY_ROAD_1F/SOUTH_EAST": {
"parent_map": "MAP_VICTORY_ROAD_1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_VICTORY_ROAD_1F_PP_UP",
"HIDDEN_ITEM_VICTORY_ROAD_1F_ULTRA_BALL"
@@ -2076,8 +2828,11 @@
},
"REGION_VICTORY_ROAD_B1F/NORTH_EAST": {
"parent_map": "MAP_VICTORY_ROAD_B1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_VICTORY_ROAD_B1F_TM29"
+ "ITEM_VICTORY_ROAD_B1F_TM_PSYCHIC"
],
"events": [],
"exits": [],
@@ -2087,7 +2842,14 @@
},
"REGION_VICTORY_ROAD_B1F/SOUTH_WEST_MAIN": {
"parent_map": "MAP_VICTORY_ROAD_B1F",
- "locations": [],
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_MICHELLE_REWARD",
+ "TRAINER_MITCHELL_REWARD",
+ "TRAINER_HALLE_REWARD"
+ ],
"events": [],
"exits": [
"REGION_VICTORY_ROAD_B1F/SOUTH_WEST_LADDER_UP"
@@ -2099,6 +2861,9 @@
},
"REGION_VICTORY_ROAD_B1F/SOUTH_WEST_LADDER_UP": {
"parent_map": "MAP_VICTORY_ROAD_B1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -2110,7 +2875,12 @@
},
"REGION_VICTORY_ROAD_B1F/MAIN_UPPER": {
"parent_map": "MAP_VICTORY_ROAD_B1F",
- "locations": [],
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_SHANNON_REWARD"
+ ],
"events": [],
"exits": [
"REGION_VICTORY_ROAD_B1F/MAIN_LOWER_EAST"
@@ -2121,8 +2891,12 @@
},
"REGION_VICTORY_ROAD_B1F/MAIN_LOWER_EAST": {
"parent_map": "MAP_VICTORY_ROAD_B1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_VICTORY_ROAD_B1F_FULL_RESTORE"
+ "ITEM_VICTORY_ROAD_B1F_FULL_RESTORE",
+ "TRAINER_SAMUEL_REWARD"
],
"events": [],
"exits": [
@@ -2134,6 +2908,9 @@
},
"REGION_VICTORY_ROAD_B1F/MAIN_LOWER_WEST": {
"parent_map": "MAP_VICTORY_ROAD_B1F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -2146,6 +2923,9 @@
},
"REGION_VICTORY_ROAD_B2F/LOWER_WEST": {
"parent_map": "MAP_VICTORY_ROAD_B2F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -2157,6 +2937,9 @@
},
"REGION_VICTORY_ROAD_B2F/LOWER_WEST_ISLAND": {
"parent_map": "MAP_VICTORY_ROAD_B2F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -2168,7 +2951,12 @@
},
"REGION_VICTORY_ROAD_B2F/LOWER_EAST": {
"parent_map": "MAP_VICTORY_ROAD_B2F",
- "locations": [],
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
+ "locations": [
+ "TRAINER_JULIE_REWARD"
+ ],
"events": [],
"exits": [
"REGION_VICTORY_ROAD_B2F/LOWER_EAST_WATER"
@@ -2179,6 +2967,9 @@
},
"REGION_VICTORY_ROAD_B2F/LOWER_WEST_WATER": {
"parent_map": "MAP_VICTORY_ROAD_B2F",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -2190,6 +2981,9 @@
},
"REGION_VICTORY_ROAD_B2F/LOWER_EAST_WATER": {
"parent_map": "MAP_VICTORY_ROAD_B2F",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -2201,8 +2995,16 @@
},
"REGION_VICTORY_ROAD_B2F/UPPER": {
"parent_map": "MAP_VICTORY_ROAD_B2F",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
- "HIDDEN_ITEM_VICTORY_ROAD_B2F_MAX_REPEL"
+ "HIDDEN_ITEM_VICTORY_ROAD_B2F_MAX_REPEL",
+ "TRAINER_OWEN_REWARD",
+ "TRAINER_DIANNE_REWARD",
+ "TRAINER_FELIX_REWARD",
+ "TRAINER_CAROLINE_REWARD"
+
],
"events": [],
"exits": [
@@ -2216,9 +3018,13 @@
},
"REGION_VICTORY_ROAD_B2F/UPPER_WATER": {
"parent_map": "MAP_VICTORY_ROAD_B2F",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_VICTORY_ROAD_B2F_FULL_HEAL",
- "HIDDEN_ITEM_VICTORY_ROAD_B2F_ELIXIR"
+ "HIDDEN_ITEM_VICTORY_ROAD_B2F_ELIXIR",
+ "TRAINER_VITO_REWARD"
],
"events": [],
"exits": [
@@ -2227,5 +3033,71 @@
"REGION_VICTORY_ROAD_B2F/UPPER"
],
"warps": []
+ },
+ "REGION_TERRA_CAVE_ENTRANCE/MAIN": {
+ "parent_map": "MAP_TERRA_CAVE_ENTRANCE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": [
+ "MAP_TERRA_CAVE_ENTRANCE:1/MAP_TERRA_CAVE_END:0"
+ ]
+ },
+ "REGION_TERRA_CAVE_END/MAIN": {
+ "parent_map": "MAP_TERRA_CAVE_END",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [
+ "EVENT_ENCOUNTER_GROUDON"
+ ],
+ "exits": [],
+ "warps": [
+ "MAP_TERRA_CAVE_END:0/MAP_TERRA_CAVE_ENTRANCE:1"
+ ]
+ },
+ "REGION_UNDERWATER_MARINE_CAVE/MAIN": {
+ "parent_map": "MAP_UNDERWATER_MARINE_CAVE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_MARINE_CAVE_ENTRANCE/MAIN"
+ ],
+ "warps": []
+ },
+ "REGION_MARINE_CAVE_ENTRANCE/MAIN": {
+ "parent_map": "MAP_MARINE_CAVE_ENTRANCE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_UNDERWATER_MARINE_CAVE/MAIN"
+ ],
+ "warps": [
+ "MAP_MARINE_CAVE_ENTRANCE:0/MAP_MARINE_CAVE_END:0"
+ ]
+ },
+ "REGION_MARINE_CAVE_END/MAIN": {
+ "parent_map": "MAP_MARINE_CAVE_END",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [
+ "EVENT_ENCOUNTER_KYOGRE"
+ ],
+ "exits": [],
+ "warps": [
+ "MAP_MARINE_CAVE_END:0/MAP_MARINE_CAVE_ENTRANCE:0"
+ ]
}
}
diff --git a/worlds/pokemon_emerald/data/regions/unused/islands.json b/worlds/pokemon_emerald/data/regions/islands.json
similarity index 73%
rename from worlds/pokemon_emerald/data/regions/unused/islands.json
rename to worlds/pokemon_emerald/data/regions/islands.json
index f7d931d1681c..442672935764 100644
--- a/worlds/pokemon_emerald/data/regions/unused/islands.json
+++ b/worlds/pokemon_emerald/data/regions/islands.json
@@ -1,17 +1,27 @@
{
"REGION_SOUTHERN_ISLAND_EXTERIOR/MAIN": {
"parent_map": "MAP_SOUTHERN_ISLAND_EXTERIOR",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
- "exits": [],
+ "exits": [
+ "REGION_LILYCOVE_CITY_HARBOR/MAIN"
+ ],
"warps": [
"MAP_SOUTHERN_ISLAND_EXTERIOR:0,1/MAP_SOUTHERN_ISLAND_INTERIOR:0,1"
]
},
"REGION_SOUTHERN_ISLAND_INTERIOR/MAIN": {
"parent_map": "MAP_SOUTHERN_ISLAND_INTERIOR",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
- "events": [],
+ "events": [
+ "EVENT_ENCOUNTER_LATIAS"
+ ],
"exits": [],
"warps": [
"MAP_SOUTHERN_ISLAND_INTERIOR:0,1/MAP_SOUTHERN_ISLAND_EXTERIOR:0,1"
@@ -19,17 +29,27 @@
},
"REGION_FARAWAY_ISLAND_ENTRANCE/MAIN": {
"parent_map": "MAP_FARAWAY_ISLAND_ENTRANCE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
- "exits": [],
+ "exits": [
+ "REGION_LILYCOVE_CITY_HARBOR/MAIN"
+ ],
"warps": [
"MAP_FARAWAY_ISLAND_ENTRANCE:0,1/MAP_FARAWAY_ISLAND_INTERIOR:0,1"
]
},
"REGION_FARAWAY_ISLAND_INTERIOR/MAIN": {
"parent_map": "MAP_FARAWAY_ISLAND_INTERIOR",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
- "events": [],
+ "events": [
+ "EVENT_ENCOUNTER_MEW"
+ ],
"exits": [],
"warps": [
"MAP_FARAWAY_ISLAND_INTERIOR:0,1/MAP_FARAWAY_ISLAND_ENTRANCE:0,1"
@@ -37,17 +57,27 @@
},
"REGION_BIRTH_ISLAND_HARBOR/MAIN": {
"parent_map": "MAP_BIRTH_ISLAND_HARBOR",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
- "exits": [],
+ "exits": [
+ "REGION_LILYCOVE_CITY_HARBOR/MAIN"
+ ],
"warps": [
"MAP_BIRTH_ISLAND_HARBOR:0/MAP_BIRTH_ISLAND_EXTERIOR:0"
]
},
"REGION_BIRTH_ISLAND_EXTERIOR/MAIN": {
"parent_map": "MAP_BIRTH_ISLAND_EXTERIOR",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
- "events": [],
+ "events": [
+ "EVENT_ENCOUNTER_DEOXYS"
+ ],
"exits": [],
"warps": [
"MAP_BIRTH_ISLAND_EXTERIOR:0/MAP_BIRTH_ISLAND_HARBOR:0"
@@ -55,15 +85,23 @@
},
"REGION_NAVEL_ROCK_HARBOR/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_HARBOR",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
- "exits": [],
+ "exits": [
+ "REGION_LILYCOVE_CITY_HARBOR/MAIN"
+ ],
"warps": [
"MAP_NAVEL_ROCK_HARBOR:0/MAP_NAVEL_ROCK_EXTERIOR:0"
]
},
"REGION_NAVEL_ROCK_EXTERIOR/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_EXTERIOR",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -74,6 +112,9 @@
},
"REGION_NAVEL_ROCK_ENTRANCE/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_ENTRANCE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -84,6 +125,9 @@
},
"REGION_NAVEL_ROCK_B1F/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_B1F",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -94,6 +138,9 @@
},
"REGION_NAVEL_ROCK_FORK/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_FORK",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -105,6 +152,9 @@
},
"REGION_NAVEL_ROCK_DOWN01/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_DOWN01",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -115,6 +165,9 @@
},
"REGION_NAVEL_ROCK_DOWN02/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_DOWN02",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -125,6 +178,9 @@
},
"REGION_NAVEL_ROCK_DOWN03/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_DOWN03",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -135,6 +191,9 @@
},
"REGION_NAVEL_ROCK_DOWN04/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_DOWN04",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -145,6 +204,9 @@
},
"REGION_NAVEL_ROCK_DOWN05/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_DOWN05",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -155,6 +217,9 @@
},
"REGION_NAVEL_ROCK_DOWN06/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_DOWN06",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -165,6 +230,9 @@
},
"REGION_NAVEL_ROCK_DOWN07/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_DOWN07",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -175,6 +243,9 @@
},
"REGION_NAVEL_ROCK_DOWN08/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_DOWN08",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -185,6 +256,9 @@
},
"REGION_NAVEL_ROCK_DOWN09/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_DOWN09",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -195,6 +269,9 @@
},
"REGION_NAVEL_ROCK_DOWN10/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_DOWN10",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -205,6 +282,9 @@
},
"REGION_NAVEL_ROCK_DOWN11/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_DOWN11",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -215,8 +295,13 @@
},
"REGION_NAVEL_ROCK_BOTTOM/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_BOTTOM",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
- "events": [],
+ "events": [
+ "EVENT_ENCOUNTER_LUGIA"
+ ],
"exits": [],
"warps": [
"MAP_NAVEL_ROCK_BOTTOM:0/MAP_NAVEL_ROCK_DOWN11:0"
@@ -224,6 +309,9 @@
},
"REGION_NAVEL_ROCK_UP1/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_UP1",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -234,6 +322,9 @@
},
"REGION_NAVEL_ROCK_UP2/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_UP2",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -244,6 +335,9 @@
},
"REGION_NAVEL_ROCK_UP3/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_UP3",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -254,6 +348,9 @@
},
"REGION_NAVEL_ROCK_UP4/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_UP4",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -264,10 +361,15 @@
},
"REGION_NAVEL_ROCK_TOP/MAIN": {
"parent_map": "MAP_NAVEL_ROCK_TOP",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_NAVEL_ROCK_TOP_SACRED_ASH"
],
- "events": [],
+ "events": [
+ "EVENT_ENCOUNTER_HO_OH"
+ ],
"exits": [],
"warps": [
"MAP_NAVEL_ROCK_TOP:0/MAP_NAVEL_ROCK_UP4:1"
diff --git a/worlds/pokemon_emerald/data/regions/routes.json b/worlds/pokemon_emerald/data/regions/routes.json
index f4b8d935c349..706051e183c8 100644
--- a/worlds/pokemon_emerald/data/regions/routes.json
+++ b/worlds/pokemon_emerald/data/regions/routes.json
@@ -1,6 +1,9 @@
{
"REGION_ROUTE101/MAIN": {
"parent_map": "MAP_ROUTE101",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -11,19 +14,44 @@
},
"REGION_ROUTE102/MAIN": {
"parent_map": "MAP_ROUTE102",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
- "ITEM_ROUTE_102_POTION"
+ "ITEM_ROUTE_102_POTION",
+ "BERRY_TREE_01",
+ "BERRY_TREE_02",
+ "TRAINER_CALVIN_1_REWARD",
+ "TRAINER_RICK_REWARD",
+ "TRAINER_ALLEN_REWARD",
+ "TRAINER_TIANA_REWARD"
],
"events": [],
"exits": [
"REGION_OLDALE_TOWN/MAIN",
- "REGION_PETALBURG_CITY/MAIN"
+ "REGION_PETALBURG_CITY/MAIN",
+ "REGION_ROUTE102/POND"
],
"warps": []
},
+ "REGION_ROUTE102/POND": {
+ "parent_map": "MAP_ROUTE102",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": []
+ },
"REGION_ROUTE103/WEST": {
"parent_map": "MAP_ROUTE103",
- "locations": [],
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
+ "locations": [
+ "TRAINER_BRENDAN_ROUTE_103_MUDKIP_REWARD"
+ ],
"events": [],
"exits": [
"REGION_ROUTE103/WATER",
@@ -33,7 +61,13 @@
},
"REGION_ROUTE103/WATER": {
"parent_map": "MAP_ROUTE103",
- "locations": [],
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [
+ "TRAINER_ISABELLE_REWARD",
+ "TRAINER_PETE_REWARD"
+ ],
"events": [],
"exits": [
"REGION_ROUTE103/WEST",
@@ -43,38 +77,87 @@
},
"REGION_ROUTE103/EAST": {
"parent_map": "MAP_ROUTE103",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
- "ITEM_ROUTE_103_GUARD_SPEC",
- "ITEM_ROUTE_103_PP_UP"
+ "TRAINER_ANDREW_REWARD",
+ "TRAINER_MIGUEL_1_REWARD",
+ "TRAINER_AMY_AND_LIV_1_REWARD",
+ "TRAINER_DAISY_REWARD"
],
"events": [],
"exits": [
"REGION_ROUTE103/WATER",
+ "REGION_ROUTE103/EAST_TREE_MAZE",
"REGION_ROUTE110/MAIN"
],
- "warps": [
- "MAP_ROUTE103:0/MAP_ALTERING_CAVE:0"
- ]
+ "warps": []
+ },
+ "REGION_ROUTE103/EAST_TREE_MAZE": {
+ "parent_map": "MAP_ROUTE103",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "ITEM_ROUTE_103_GUARD_SPEC",
+ "ITEM_ROUTE_103_PP_UP",
+ "BERRY_TREE_05",
+ "BERRY_TREE_06",
+ "BERRY_TREE_07",
+ "TRAINER_MARCOS_REWARD",
+ "TRAINER_RHETT_REWARD"
+ ],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE103/EAST"
+ ],
+ "warps": []
},
"REGION_ROUTE104/SOUTH": {
"parent_map": "MAP_ROUTE104",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"HIDDEN_ITEM_ROUTE_104_POTION",
"HIDDEN_ITEM_ROUTE_104_HEART_SCALE",
- "HIDDEN_ITEM_ROUTE_104_ANTIDOTE"
+ "HIDDEN_ITEM_ROUTE_104_ANTIDOTE",
+ "BERRY_TREE_11",
+ "BERRY_TREE_12",
+ "BERRY_TREE_13",
+ "TRAINER_BILLY_REWARD",
+ "TRAINER_DARIAN_REWARD",
+ "TRAINER_CINDY_1_REWARD"
],
"events": [],
"exits": [
"REGION_PETALBURG_CITY/MAIN",
- "REGION_ROUTE105/MAIN"
+ "REGION_ROUTE104/SOUTH_WATER"
],
"warps": [
"MAP_ROUTE104:0/MAP_ROUTE104_MR_BRINEYS_HOUSE:0",
"MAP_ROUTE104:4,5/MAP_PETALBURG_WOODS:2,3"
]
},
+ "REGION_ROUTE104/SOUTH_WATER": {
+ "parent_map": "MAP_ROUTE104",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE104/SOUTH",
+ "REGION_ROUTE105/MAIN"
+ ],
+ "warps": []
+ },
"REGION_ROUTE104/SOUTH_LEDGE": {
"parent_map": "MAP_ROUTE104",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_ROUTE_104_POKE_BALL"
],
@@ -88,27 +171,82 @@
},
"REGION_ROUTE104/NORTH": {
"parent_map": "MAP_ROUTE104",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
- "ITEM_ROUTE_104_PP_UP",
"ITEM_ROUTE_104_POTION",
- "ITEM_ROUTE_104_X_ACCURACY",
"HIDDEN_ITEM_ROUTE_104_SUPER_POTION",
"HIDDEN_ITEM_ROUTE_104_POKE_BALL",
- "NPC_GIFT_RECEIVED_TM09",
+ "NPC_GIFT_RECEIVED_TM_BULLET_SEED",
"NPC_GIFT_RECEIVED_WHITE_HERB",
- "NPC_GIFT_RECEIVED_CHESTO_BERRY_ROUTE_104"
+ "NPC_GIFT_RECEIVED_CHESTO_BERRY_ROUTE_104",
+ "BERRY_TREE_03",
+ "BERRY_TREE_04",
+ "BERRY_TREE_08",
+ "BERRY_TREE_09",
+ "BERRY_TREE_10",
+ "BERRY_TREE_75",
+ "BERRY_TREE_76",
+ "TRAINER_WINSTON_1_REWARD",
+ "TRAINER_HALEY_1_REWARD",
+ "TRAINER_IVAN_REWARD",
+ "TRAINER_GINA_AND_MIA_1_REWARD"
],
"events": [],
"exits": [
- "REGION_RUSTBORO_CITY/MAIN"
+ "REGION_RUSTBORO_CITY/MAIN",
+ "REGION_ROUTE104/NORTH_POND",
+ "REGION_ROUTE104/TREE_ALCOVE_2"
],
"warps": [
"MAP_ROUTE104:1/MAP_ROUTE104_PRETTY_PETAL_FLOWER_SHOP:0",
"MAP_ROUTE104:2,3/MAP_PETALBURG_WOODS:0,1"
]
},
+ "REGION_ROUTE104/NORTH_POND": {
+ "parent_map": "MAP_ROUTE104",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE104/NORTH",
+ "REGION_ROUTE104/TREE_ALCOVE_1",
+ "REGION_ROUTE104/TREE_ALCOVE_2"
+ ],
+ "warps": []
+ },
+ "REGION_ROUTE104/TREE_ALCOVE_1": {
+ "parent_map": "MAP_ROUTE104",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
+ "locations": [
+ "ITEM_ROUTE_104_PP_UP"
+ ],
+ "events": [],
+ "exits": [],
+ "warps": []
+ },
+ "REGION_ROUTE104/TREE_ALCOVE_2": {
+ "parent_map": "MAP_ROUTE104",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
+ "locations": [
+ "ITEM_ROUTE_104_X_ACCURACY"
+ ],
+ "events": [],
+ "exits": [],
+ "warps": []
+ },
"REGION_ROUTE104_MR_BRINEYS_HOUSE/MAIN": {
"parent_map": "MAP_ROUTE104_MR_BRINEYS_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -120,8 +258,12 @@
},
"REGION_ROUTE104_PRETTY_PETAL_FLOWER_SHOP/MAIN": {
"parent_map": "MAP_ROUTE104_PRETTY_PETAL_FLOWER_SHOP",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_WAILMER_PAIL"
+ "NPC_GIFT_RECEIVED_WAILMER_PAIL",
+ "NPC_GIFT_FLOWER_SHOP_RECEIVED_BERRY"
],
"events": [
"EVENT_MEET_FLOWER_SHOP_OWNER"
@@ -133,32 +275,63 @@
},
"REGION_ROUTE105/MAIN": {
"parent_map": "MAP_ROUTE105",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_105_IRON",
"HIDDEN_ITEM_ROUTE_105_HEART_SCALE",
- "HIDDEN_ITEM_ROUTE_105_BIG_PEARL"
+ "HIDDEN_ITEM_ROUTE_105_BIG_PEARL",
+ "TRAINER_IMANI_REWARD",
+ "TRAINER_DOMINIK_REWARD",
+ "TRAINER_LUIS_REWARD",
+ "TRAINER_FOSTER_REWARD",
+ "TRAINER_JOSUE_REWARD",
+ "TRAINER_ANDRES_1_REWARD",
+ "TRAINER_BEVERLY_REWARD"
],
"events": [],
"exits": [
"REGION_ROUTE104/SOUTH",
"REGION_ROUTE106/SEA",
- "REGION_UNDERWATER_ROUTE105/MAIN"
+ "REGION_UNDERWATER_ROUTE105/MARINE_CAVE_ENTRANCE_1",
+ "REGION_UNDERWATER_ROUTE105/MARINE_CAVE_ENTRANCE_2"
],
"warps": [
"MAP_ROUTE105:0/MAP_ISLAND_CAVE:0"
]
},
- "REGION_UNDERWATER_ROUTE105/MAIN": {
+ "REGION_UNDERWATER_ROUTE105/MARINE_CAVE_ENTRANCE_1": {
"parent_map": "MAP_UNDERWATER_ROUTE105",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
- "REGION_ROUTE105/MAIN"
+ "REGION_ROUTE105/MAIN",
+ "REGION_UNDERWATER_MARINE_CAVE/MAIN"
+ ],
+ "warps": []
+ },
+ "REGION_UNDERWATER_ROUTE105/MARINE_CAVE_ENTRANCE_2": {
+ "parent_map": "MAP_UNDERWATER_ROUTE105",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE105/MAIN",
+ "REGION_UNDERWATER_MARINE_CAVE/MAIN"
],
"warps": []
},
"REGION_ROUTE106/WEST": {
"parent_map": "MAP_ROUTE106",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_106_PROTEIN"
],
@@ -170,7 +343,13 @@
},
"REGION_ROUTE106/SEA": {
"parent_map": "MAP_ROUTE106",
- "locations": [],
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [
+ "TRAINER_DOUGLAS_REWARD",
+ "TRAINER_KYLA_REWARD"
+ ],
"events": [],
"exits": [
"REGION_ROUTE105/MAIN",
@@ -181,10 +360,15 @@
},
"REGION_ROUTE106/EAST": {
"parent_map": "MAP_ROUTE106",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"HIDDEN_ITEM_ROUTE_106_POKE_BALL",
"HIDDEN_ITEM_ROUTE_106_STARDUST",
- "HIDDEN_ITEM_ROUTE_106_HEART_SCALE"
+ "HIDDEN_ITEM_ROUTE_106_HEART_SCALE",
+ "TRAINER_ELLIOT_1_REWARD",
+ "TRAINER_NED_REWARD"
],
"events": [],
"exits": [
@@ -197,7 +381,17 @@
},
"REGION_ROUTE107/MAIN": {
"parent_map": "MAP_ROUTE107",
- "locations": [],
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [
+ "TRAINER_DENISE_REWARD",
+ "TRAINER_TONY_1_REWARD",
+ "TRAINER_DARRIN_REWARD",
+ "TRAINER_CAMRON_REWARD",
+ "TRAINER_BETH_REWARD",
+ "TRAINER_LISA_AND_RAY_REWARD"
+ ],
"events": [],
"exits": [
"REGION_DEWFORD_TOWN/MAIN",
@@ -207,9 +401,18 @@
},
"REGION_ROUTE108/MAIN": {
"parent_map": "MAP_ROUTE108",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_108_STAR_PIECE",
- "HIDDEN_ITEM_ROUTE_108_RARE_CANDY"
+ "HIDDEN_ITEM_ROUTE_108_RARE_CANDY",
+ "TRAINER_MISSY_REWARD",
+ "TRAINER_MATTHEW_REWARD",
+ "TRAINER_TARA_REWARD",
+ "TRAINER_CAROLINA_REWARD",
+ "TRAINER_CORY_1_REWARD",
+ "TRAINER_JEROME_REWARD"
],
"events": [],
"exits": [
@@ -222,9 +425,19 @@
},
"REGION_ROUTE109/SEA": {
"parent_map": "MAP_ROUTE109",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_109_PP_UP",
- "HIDDEN_ITEM_ROUTE_109_HEART_SCALE_3"
+ "HIDDEN_ITEM_ROUTE_109_HEART_SCALE_3",
+ "TRAINER_AUSTINA_REWARD",
+ "TRAINER_GWEN_REWARD",
+ "TRAINER_ELIJAH_REWARD",
+ "TRAINER_CARTER_REWARD",
+ "TRAINER_ALICE_REWARD",
+ "TRAINER_DAVID_REWARD",
+ "TRAINER_MEL_AND_PAUL_REWARD"
],
"events": [],
"exits": [
@@ -235,6 +448,9 @@
},
"REGION_ROUTE109/BEACH": {
"parent_map": "MAP_ROUTE109",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_109_POTION",
"HIDDEN_ITEM_ROUTE_109_REVIVE",
@@ -242,7 +458,13 @@
"HIDDEN_ITEM_ROUTE_109_GREAT_BALL",
"HIDDEN_ITEM_ROUTE_109_ETHER",
"HIDDEN_ITEM_ROUTE_109_HEART_SCALE_2",
- "NPC_GIFT_RECEIVED_SOFT_SAND"
+ "NPC_GIFT_RECEIVED_SOFT_SAND",
+ "TRAINER_HUEY_REWARD",
+ "TRAINER_HAILEY_REWARD",
+ "TRAINER_EDMOND_REWARD",
+ "TRAINER_RICKY_1_REWARD",
+ "TRAINER_LOLA_1_REWARD",
+ "TRAINER_CHANDLER_REWARD"
],
"events": [],
"exits": [
@@ -256,8 +478,14 @@
},
"REGION_ROUTE109_SEASHORE_HOUSE/MAIN": {
"parent_map": "MAP_ROUTE109_SEASHORE_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_6_SODA_POP"
+ "NPC_GIFT_RECEIVED_6_SODA_POP",
+ "TRAINER_DWAYNE_REWARD",
+ "TRAINER_JOHANNA_REWARD",
+ "TRAINER_SIMON_REWARD"
],
"events": [],
"exits": [],
@@ -267,6 +495,9 @@
},
"REGION_ROUTE110/MAIN": {
"parent_map": "MAP_ROUTE110",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_110_DIRE_HIT",
"ITEM_ROUTE_110_ELIXIR",
@@ -274,7 +505,19 @@
"HIDDEN_ITEM_ROUTE_110_GREAT_BALL",
"HIDDEN_ITEM_ROUTE_110_POKE_BALL",
"HIDDEN_ITEM_ROUTE_110_FULL_HEAL",
- "NPC_GIFT_RECEIVED_ITEMFINDER"
+ "NPC_GIFT_RECEIVED_ITEMFINDER",
+ "BERRY_TREE_16",
+ "BERRY_TREE_17",
+ "BERRY_TREE_18",
+ "TRAINER_KALEB_REWARD",
+ "TRAINER_ISABEL_1_REWARD",
+ "TRAINER_TIMMY_REWARD",
+ "TRAINER_JOSEPH_REWARD",
+ "TRAINER_EDWIN_1_REWARD",
+ "TRAINER_ALYSSA_REWARD",
+ "TRAINER_EDWARD_REWARD",
+ "TRAINER_DALE_REWARD",
+ "TRAINER_BRENDAN_ROUTE_110_MUDKIP_REWARD"
],
"events": [],
"exits": [
@@ -291,6 +534,9 @@
},
"REGION_ROUTE110/SOUTH": {
"parent_map": "MAP_ROUTE110",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -303,7 +549,17 @@
},
"REGION_ROUTE110/CYCLING_ROAD": {
"parent_map": "MAP_ROUTE110",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_JACOB_REWARD",
+ "TRAINER_JASMINE_REWARD",
+ "TRAINER_BENJAMIN_1_REWARD",
+ "TRAINER_ANTHONY_REWARD",
+ "TRAINER_ABIGAIL_1_REWARD",
+ "TRAINER_JACLYN_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -313,6 +569,9 @@
},
"REGION_ROUTE110/SOUTH_WATER": {
"parent_map": "MAP_ROUTE110",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_110_RARE_CANDY"
],
@@ -324,6 +583,9 @@
},
"REGION_ROUTE110/NORTH_WATER": {
"parent_map": "MAP_ROUTE110",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -335,26 +597,248 @@
},
"REGION_ROUTE110_TRICK_HOUSE_ENTRANCE/MAIN": {
"parent_map": "MAP_ROUTE110_TRICK_HOUSE_ENTRANCE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
- "exits": [],
+ "exits": [
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE2/ENTRANCE",
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE3/ENTRANCE",
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE4/ENTRANCE",
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE5/ENTRANCE",
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE6/ENTRANCE",
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE7/ENTRANCE",
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE8/ENTRANCE"
+ ],
"warps": [
"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:0,1/MAP_ROUTE110:1",
"MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2/MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0"
]
},
- "REGION_ROUTE110_TRICK_HOUSE_PUZZLE1/MAIN": {
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE1/ENTRANCE": {
"parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE1",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
+ "exits": [
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE1/REWARDS"
+ ],
+ "warps": [
+ "MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2"
+ ]
+ },
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE1/REWARDS": {
+ "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE1",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_1",
+ "ITEM_TRICK_HOUSE_PUZZLE_1_ORANGE_MAIL"
+ ],
+ "events": [
+ "EVENT_COMPLETE_TRICK_HOUSE_1"
+ ],
"exits": [],
"warps": [
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2",
"MAP_ROUTE110_TRICK_HOUSE_PUZZLE1:2/MAP_ROUTE110_TRICK_HOUSE_END:0"
]
},
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE2/ENTRANCE": {
+ "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE2",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE2/REWARDS"
+ ],
+ "warps": []
+ },
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE2/REWARDS": {
+ "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE2",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_2",
+ "ITEM_TRICK_HOUSE_PUZZLE_2_HARBOR_MAIL",
+ "ITEM_TRICK_HOUSE_PUZZLE_2_WAVE_MAIL"
+ ],
+ "events": [
+ "EVENT_COMPLETE_TRICK_HOUSE_2"
+ ],
+ "exits": [],
+ "warps": []
+ },
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE3/ENTRANCE": {
+ "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE3",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE3/REWARDS"
+ ],
+ "warps": []
+ },
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE3/REWARDS": {
+ "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE3",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_3",
+ "ITEM_TRICK_HOUSE_PUZZLE_3_SHADOW_MAIL",
+ "ITEM_TRICK_HOUSE_PUZZLE_3_WOOD_MAIL"
+ ],
+ "events": [
+ "EVENT_COMPLETE_TRICK_HOUSE_3"
+ ],
+ "exits": [],
+ "warps": []
+ },
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE4/ENTRANCE": {
+ "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE4",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE4/REWARDS"
+ ],
+ "warps": []
+ },
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE4/REWARDS": {
+ "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE4",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_4",
+ "ITEM_TRICK_HOUSE_PUZZLE_4_MECH_MAIL"
+ ],
+ "events": [
+ "EVENT_COMPLETE_TRICK_HOUSE_4"
+ ],
+ "exits": [],
+ "warps": []
+ },
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE5/ENTRANCE": {
+ "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE5",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE5/REWARDS"
+ ],
+ "warps": []
+ },
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE5/REWARDS": {
+ "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE5",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_5"
+ ],
+ "events": [
+ "EVENT_COMPLETE_TRICK_HOUSE_5"
+ ],
+ "exits": [],
+ "warps": []
+ },
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE6/ENTRANCE": {
+ "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE6",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE6/REWARDS"
+ ],
+ "warps": []
+ },
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE6/REWARDS": {
+ "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE6",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_6",
+ "ITEM_TRICK_HOUSE_PUZZLE_6_GLITTER_MAIL"
+ ],
+ "events": [
+ "EVENT_COMPLETE_TRICK_HOUSE_6"
+ ],
+ "exits": [],
+ "warps": []
+ },
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE7/ENTRANCE": {
+ "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE7/REWARDS"
+ ],
+ "warps": []
+ },
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE7/REWARDS": {
+ "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "NPC_GIFT_RECEIVED_TRICK_HOUSE_REWARD_7",
+ "ITEM_TRICK_HOUSE_PUZZLE_7_TROPIC_MAIL"
+ ],
+ "events": [
+ "EVENT_COMPLETE_TRICK_HOUSE_7"
+ ],
+ "exits": [],
+ "warps": []
+ },
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE8/ENTRANCE": {
+ "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE8",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE8/REWARDS"
+ ],
+ "warps": []
+ },
+ "REGION_ROUTE110_TRICK_HOUSE_PUZZLE8/REWARDS": {
+ "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE8",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "ITEM_TRICK_HOUSE_PUZZLE_8_BEAD_MAIL"
+ ],
+ "events": [],
+ "exits": [],
+ "warps": []
+ },
"REGION_ROUTE110_TRICK_HOUSE_END/MAIN": {
"parent_map": "MAP_ROUTE110_TRICK_HOUSE_END",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -365,6 +849,9 @@
},
"REGION_ROUTE110_TRICK_HOUSE_CORRIDOR/MAIN": {
"parent_map": "MAP_ROUTE110_TRICK_HOUSE_CORRIDOR",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -375,6 +862,9 @@
},
"REGION_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE/WEST": {
"parent_map": "MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -386,6 +876,9 @@
},
"REGION_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE/EAST": {
"parent_map": "MAP_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -397,6 +890,9 @@
},
"REGION_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE/WEST": {
"parent_map": "MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -408,6 +904,9 @@
},
"REGION_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE/EAST": {
"parent_map": "MAP_ROUTE110_SEASIDE_CYCLING_ROAD_NORTH_ENTRANCE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -419,7 +918,13 @@
},
"REGION_ROUTE111/MIDDLE": {
"parent_map": "MAP_ROUTE111",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_IRENE_REWARD",
+ "TRAINER_TRAVIS_REWARD"
+ ],
"events": [],
"exits": [
"REGION_ROUTE111/DESERT",
@@ -430,8 +935,19 @@
},
"REGION_ROUTE111/SOUTH": {
"parent_map": "MAP_ROUTE111",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
- "ITEM_ROUTE_111_ELIXIR"
+ "ITEM_ROUTE_111_ELIXIR",
+ "TRAINER_CELINA_REWARD",
+ "TRAINER_TYRON_REWARD",
+ "TRAINER_BIANCA_REWARD",
+ "TRAINER_HAYDEN_REWARD",
+ "TRAINER_VICTOR_REWARD",
+ "TRAINER_VICKY_REWARD",
+ "TRAINER_VICTORIA_REWARD",
+ "TRAINER_VIVI_REWARD"
],
"events": [],
"exits": [
@@ -446,6 +962,9 @@
},
"REGION_ROUTE111/SOUTH_POND": {
"parent_map": "MAP_ROUTE111",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_111_HP_UP"
],
@@ -457,12 +976,23 @@
},
"REGION_ROUTE111/DESERT": {
"parent_map": "MAP_ROUTE111",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_ROUTE_111_TM37",
+ "ITEM_ROUTE_111_TM_SANDSTORM",
"ITEM_ROUTE_111_STARDUST",
"HIDDEN_ITEM_ROUTE_111_STARDUST",
"HIDDEN_ITEM_ROUTE_111_PROTEIN",
- "HIDDEN_ITEM_ROUTE_111_RARE_CANDY"
+ "HIDDEN_ITEM_ROUTE_111_RARE_CANDY",
+ "TRAINER_CELIA_REWARD",
+ "TRAINER_BRYAN_REWARD",
+ "TRAINER_BRANDEN_REWARD",
+ "TRAINER_DUSTY_1_REWARD",
+ "TRAINER_BECKY_REWARD",
+ "TRAINER_HEIDI_REWARD",
+ "TRAINER_BEAU_REWARD",
+ "TRAINER_DREW_REWARD"
],
"events": [],
"exits": [
@@ -476,21 +1006,49 @@
},
"REGION_ROUTE111/NORTH": {
"parent_map": "MAP_ROUTE111",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_SECRET_POWER"
+ "NPC_GIFT_RECEIVED_SECRET_POWER",
+ "NPC_GIFT_ROUTE_111_RECEIVED_BERRY",
+ "BERRY_TREE_19",
+ "BERRY_TREE_20",
+ "BERRY_TREE_80",
+ "BERRY_TREE_81",
+ "TRAINER_WILTON_1_REWARD",
+ "TRAINER_BROOKE_1_REWARD"
],
"events": [],
"exits": [
"REGION_ROUTE113/MAIN",
"REGION_ROUTE112/NORTH",
+ "REGION_ROUTE111/ABOVE_SLOPE",
"REGION_ROUTE111/DESERT"
],
"warps": [
"MAP_ROUTE111:2/MAP_ROUTE111_OLD_LADYS_REST_STOP:0"
]
},
+ "REGION_ROUTE111/ABOVE_SLOPE": {
+ "parent_map": "MAP_ROUTE111",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_DAISUKE_REWARD"
+ ],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE111/NORTH"
+ ],
+ "warps": []
+ },
"REGION_ROUTE111_OLD_LADYS_REST_STOP/MAIN": {
"parent_map": "MAP_ROUTE111_OLD_LADYS_REST_STOP",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -500,6 +1058,9 @@
},
"REGION_ROUTE111_WINSTRATE_FAMILYS_HOUSE/MAIN": {
"parent_map": "MAP_ROUTE111_WINSTRATE_FAMILYS_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"NPC_GIFT_RECEIVED_MACHO_BRACE"
],
@@ -511,7 +1072,15 @@
},
"REGION_ROUTE112/SOUTH_EAST": {
"parent_map": "MAP_ROUTE112",
- "locations": [],
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_LARRY_REWARD",
+ "TRAINER_CAROL_REWARD",
+ "TRAINER_TRENT_1_REWARD",
+ "TRAINER_BRICE_REWARD"
+ ],
"events": [],
"exits": [
"REGION_ROUTE112/CABLE_CAR_STATION_ENTRANCE",
@@ -523,6 +1092,9 @@
},
"REGION_ROUTE112/CABLE_CAR_STATION_ENTRANCE": {
"parent_map": "MAP_ROUTE112",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -534,6 +1106,9 @@
},
"REGION_ROUTE112/SOUTH_WEST": {
"parent_map": "MAP_ROUTE112",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_ROUTE_112_NUGGET"
],
@@ -548,7 +1123,17 @@
},
"REGION_ROUTE112/NORTH": {
"parent_map": "MAP_ROUTE112",
- "locations": [],
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "BERRY_TREE_21",
+ "BERRY_TREE_22",
+ "BERRY_TREE_23",
+ "BERRY_TREE_24",
+ "TRAINER_SHAYLA_REWARD",
+ "TRAINER_BRYANT_REWARD"
+ ],
"events": [],
"exits": [
"REGION_ROUTE111/NORTH"
@@ -559,6 +1144,9 @@
},
"REGION_ROUTE112_CABLE_CAR_STATION/MAIN": {
"parent_map": "MAP_ROUTE112_CABLE_CAR_STATION",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -570,6 +1158,9 @@
},
"REGION_MT_CHIMNEY_CABLE_CAR_STATION/MAIN": {
"parent_map": "MAP_MT_CHIMNEY_CABLE_CAR_STATION",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -581,8 +1172,20 @@
},
"REGION_MT_CHIMNEY/MAIN": {
"parent_map": "MAP_MT_CHIMNEY",
- "locations": [
- "NPC_GIFT_RECEIVED_METEORITE"
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "NPC_GIFT_RECEIVED_METEORITE",
+ "TRAINER_SHIRLEY_REWARD",
+ "TRAINER_SHEILA_REWARD",
+ "TRAINER_SHELBY_1_REWARD",
+ "TRAINER_SAWYER_1_REWARD",
+ "TRAINER_MELISSA_REWARD",
+ "TRAINER_GRUNT_MT_CHIMNEY_1_REWARD",
+ "TRAINER_GRUNT_MT_CHIMNEY_2_REWARD",
+ "TRAINER_TABITHA_MT_CHIMNEY_REWARD",
+ "TRAINER_MAXIE_MT_CHIMNEY_REWARD"
],
"events": [
"EVENT_RECOVER_METEORITE"
@@ -595,8 +1198,12 @@
},
"REGION_JAGGED_PASS/TOP": {
"parent_map": "MAP_JAGGED_PASS",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "HIDDEN_ITEM_JAGGED_PASS_FULL_HEAL"
+ "HIDDEN_ITEM_JAGGED_PASS_FULL_HEAL",
+ "TRAINER_ERIC_REWARD"
],
"events": [],
"exits": [
@@ -608,9 +1215,15 @@
},
"REGION_JAGGED_PASS/MIDDLE": {
"parent_map": "MAP_JAGGED_PASS",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_JAGGED_PASS_BURN_HEAL",
- "HIDDEN_ITEM_JAGGED_PASS_GREAT_BALL"
+ "HIDDEN_ITEM_JAGGED_PASS_GREAT_BALL",
+ "TRAINER_DIANA_1_REWARD",
+ "TRAINER_AUTUMN_REWARD",
+ "TRAINER_JULIO_REWARD"
],
"events": [],
"exits": [
@@ -623,7 +1236,12 @@
},
"REGION_JAGGED_PASS/BOTTOM": {
"parent_map": "MAP_JAGGED_PASS",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_ETHAN_1_REWARD"
+ ],
"events": [],
"exits": [
"REGION_JAGGED_PASS/MIDDLE"
@@ -634,13 +1252,26 @@
},
"REGION_ROUTE113/MAIN": {
"parent_map": "MAP_ROUTE113",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_ROUTE_113_MAX_ETHER",
"ITEM_ROUTE_113_SUPER_REPEL",
"ITEM_ROUTE_113_HYPER_POTION",
"HIDDEN_ITEM_ROUTE_113_ETHER",
- "HIDDEN_ITEM_ROUTE_113_TM32",
- "HIDDEN_ITEM_ROUTE_113_NUGGET"
+ "HIDDEN_ITEM_ROUTE_113_TM_DOUBLE_TEAM",
+ "HIDDEN_ITEM_ROUTE_113_NUGGET",
+ "TRAINER_WYATT_REWARD",
+ "TRAINER_LAWRENCE_REWARD",
+ "TRAINER_LUNG_REWARD",
+ "TRAINER_JAYLEN_REWARD",
+ "TRAINER_MADELINE_1_REWARD",
+ "TRAINER_LAO_1_REWARD",
+ "TRAINER_TORI_AND_TIA_REWARD",
+ "TRAINER_DILLON_REWARD",
+ "TRAINER_COBY_REWARD",
+ "TRAINER_SOPHIE_REWARD"
],
"events": [],
"exits": [
@@ -653,7 +1284,12 @@
},
"REGION_ROUTE113_GLASS_WORKSHOP/MAIN": {
"parent_map": "MAP_ROUTE113_GLASS_WORKSHOP",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "NPC_GIFT_RECEIVED_SOOT_SACK"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -662,17 +1298,37 @@
},
"REGION_ROUTE114/MAIN": {
"parent_map": "MAP_ROUTE114",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_114_PROTEIN",
"ITEM_ROUTE_114_ENERGY_POWDER",
"HIDDEN_ITEM_ROUTE_114_REVIVE",
"HIDDEN_ITEM_ROUTE_114_CARBOS",
- "NPC_GIFT_RECEIVED_TM05"
- ],
- "events": [],
- "exits": [
- "REGION_ROUTE114/ABOVE_WATERFALL",
- "REGION_FALLARBOR_TOWN/MAIN"
+ "NPC_GIFT_RECEIVED_TM_ROAR",
+ "NPC_GIFT_ROUTE_114_RECEIVED_BERRY",
+ "BERRY_TREE_68",
+ "BERRY_TREE_77",
+ "BERRY_TREE_78",
+ "TRAINER_NOLAN_REWARD",
+ "TRAINER_KAI_REWARD",
+ "TRAINER_CHARLOTTE_REWARD",
+ "TRAINER_CLAUDE_REWARD",
+ "TRAINER_NANCY_REWARD",
+ "TRAINER_SHANE_REWARD",
+ "TRAINER_STEVE_1_REWARD",
+ "TRAINER_BERNIE_1_REWARD",
+ "TRAINER_LUCAS_1_REWARD",
+ "TRAINER_ANGELINA_REWARD",
+ "TRAINER_LENNY_REWARD",
+ "TRAINER_TYRA_AND_IVY_REWARD"
+ ],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE114/WATER",
+ "REGION_FALLARBOR_TOWN/MAIN",
+ "REGION_TERRA_CAVE_ENTRANCE/MAIN"
],
"warps": [
"MAP_ROUTE114:0/MAP_METEOR_FALLS_1F_1R:0",
@@ -680,21 +1336,41 @@
"MAP_ROUTE114:2/MAP_ROUTE114_LANETTES_HOUSE:0"
]
},
+ "REGION_ROUTE114/WATER": {
+ "parent_map": "MAP_ROUTE114",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE114/MAIN",
+ "REGION_ROUTE114/ABOVE_WATERFALL"
+ ],
+ "warps": []
+ },
"REGION_ROUTE114/ABOVE_WATERFALL": {
"parent_map": "MAP_ROUTE114",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_114_RARE_CANDY"
],
"events": [],
"exits": [
- "REGION_ROUTE114/MAIN"
+ "REGION_ROUTE114/WATER",
+ "REGION_TERRA_CAVE_ENTRANCE/MAIN"
],
"warps": []
},
"REGION_ROUTE114_FOSSIL_MANIACS_HOUSE/MAIN": {
"parent_map": "MAP_ROUTE114_FOSSIL_MANIACS_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "NPC_GIFT_RECEIVED_TM28"
+ "NPC_GIFT_RECEIVED_TM_DIG"
],
"events": [],
"exits": [],
@@ -705,6 +1381,9 @@
},
"REGION_ROUTE114_FOSSIL_MANIACS_TUNNEL/MAIN": {
"parent_map": "MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -715,6 +1394,9 @@
},
"REGION_DESERT_UNDERPASS/MAIN": {
"parent_map": "MAP_DESERT_UNDERPASS",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -724,6 +1406,9 @@
},
"REGION_ROUTE114_LANETTES_HOUSE/MAIN": {
"parent_map": "MAP_ROUTE114_LANETTES_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -733,20 +1418,28 @@
},
"REGION_ROUTE115/SOUTH_BELOW_LEDGE": {
"parent_map": "MAP_ROUTE115",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_115_SUPER_POTION"
],
"events": [],
"exits": [
"REGION_ROUTE115/SEA",
+ "REGION_ROUTE115/SOUTH_ABOVE_LEDGE",
"REGION_RUSTBORO_CITY/MAIN"
],
"warps": []
},
"REGION_ROUTE115/SOUTH_BEACH_NEAR_CAVE": {
"parent_map": "MAP_ROUTE115",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
- "HIDDEN_ITEM_ROUTE_115_HEART_SCALE"
+ "HIDDEN_ITEM_ROUTE_115_HEART_SCALE",
+ "TRAINER_CYNDY_1_REWARD"
],
"events": [],
"exits": [
@@ -757,8 +1450,14 @@
},
"REGION_ROUTE115/SOUTH_ABOVE_LEDGE": {
"parent_map": "MAP_ROUTE115",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_ROUTE_115_PP_UP"
+ "ITEM_ROUTE_115_PP_UP",
+ "TRAINER_NOB_1_REWARD",
+ "TRAINER_MARLENE_REWARD",
+ "TRAINER_HECTOR_REWARD"
],
"events": [],
"exits": [
@@ -772,8 +1471,13 @@
},
"REGION_ROUTE115/SOUTH_BEHIND_ROCK": {
"parent_map": "MAP_ROUTE115",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "ITEM_ROUTE_115_GREAT_BALL"
+ "ITEM_ROUTE_115_GREAT_BALL",
+ "BERRY_TREE_55",
+ "BERRY_TREE_56"
],
"events": [],
"exits": [
@@ -783,30 +1487,50 @@
},
"REGION_ROUTE115/NORTH_BELOW_SLOPE": {
"parent_map": "MAP_ROUTE115",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_115_HEAL_POWDER",
- "ITEM_ROUTE_115_TM01"
+ "ITEM_ROUTE_115_TM_FOCUS_PUNCH",
+ "BERRY_TREE_69",
+ "BERRY_TREE_70",
+ "BERRY_TREE_71",
+ "TRAINER_TIMOTHY_1_REWARD",
+ "TRAINER_KYRA_REWARD",
+ "TRAINER_KOICHI_REWARD",
+ "TRAINER_JAIDEN_REWARD",
+ "TRAINER_ALIX_REWARD",
+ "TRAINER_HELENE_REWARD"
],
"events": [],
"exits": [
"REGION_ROUTE115/NORTH_ABOVE_SLOPE",
- "REGION_ROUTE115/SEA"
+ "REGION_ROUTE115/SEA",
+ "REGION_TERRA_CAVE_ENTRANCE/MAIN"
],
"warps": []
},
"REGION_ROUTE115/NORTH_ABOVE_SLOPE": {
"parent_map": "MAP_ROUTE115",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_ROUTE_115_IRON"
],
"events": [],
"exits": [
- "REGION_ROUTE115/NORTH_BELOW_SLOPE"
+ "REGION_ROUTE115/NORTH_BELOW_SLOPE",
+ "REGION_TERRA_CAVE_ENTRANCE/MAIN"
],
"warps": []
},
"REGION_ROUTE115/SEA": {
"parent_map": "MAP_ROUTE115",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -818,15 +1542,25 @@
},
"REGION_ROUTE116/WEST": {
"parent_map": "MAP_ROUTE116",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_ROUTE_116_REPEL",
"ITEM_ROUTE_116_X_SPECIAL",
- "NPC_GIFT_RECEIVED_REPEAT_BALL"
+ "NPC_GIFT_RECEIVED_REPEAT_BALL",
+ "TRAINER_JOSE_REWARD",
+ "TRAINER_JOEY_REWARD",
+ "TRAINER_KAREN_1_REWARD",
+ "TRAINER_CLARK_REWARD",
+ "TRAINER_JOHNSON_REWARD",
+ "TRAINER_DEVAN_REWARD"
],
"events": [],
"exits": [
"REGION_ROUTE116/WEST_ABOVE_LEDGE",
- "REGION_RUSTBORO_CITY/MAIN"
+ "REGION_RUSTBORO_CITY/MAIN",
+ "REGION_TERRA_CAVE_ENTRANCE/MAIN"
],
"warps": [
"MAP_ROUTE116:0/MAP_RUSTURF_TUNNEL:0",
@@ -835,10 +1569,21 @@
},
"REGION_ROUTE116/WEST_ABOVE_LEDGE": {
"parent_map": "MAP_ROUTE116",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_ROUTE_116_ETHER",
"ITEM_ROUTE_116_POTION",
- "HIDDEN_ITEM_ROUTE_116_SUPER_POTION"
+ "HIDDEN_ITEM_ROUTE_116_SUPER_POTION",
+ "BERRY_TREE_25",
+ "BERRY_TREE_26",
+ "BERRY_TREE_66",
+ "BERRY_TREE_67",
+ "TRAINER_JANICE_REWARD",
+ "TRAINER_JERRY_1_REWARD",
+ "TRAINER_SARAH_REWARD",
+ "TRAINER_DAWSON_REWARD"
],
"events": [],
"exits": [
@@ -848,18 +1593,26 @@
},
"REGION_ROUTE116/EAST": {
"parent_map": "MAP_ROUTE116",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_ROUTE_116_HP_UP",
"HIDDEN_ITEM_ROUTE_116_BLACK_GLASSES"
],
"events": [],
- "exits": [],
+ "exits": [
+ "REGION_TERRA_CAVE_ENTRANCE/MAIN"
+ ],
"warps": [
"MAP_ROUTE116:2/MAP_RUSTURF_TUNNEL:2"
]
},
"REGION_ROUTE116_TUNNELERS_REST_HOUSE/MAIN": {
"parent_map": "MAP_ROUTE116_TUNNELERS_REST_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -869,22 +1622,53 @@
},
"REGION_ROUTE117/MAIN": {
"parent_map": "MAP_ROUTE117",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_117_GREAT_BALL",
"ITEM_ROUTE_117_REVIVE",
- "HIDDEN_ITEM_ROUTE_117_REPEL"
+ "HIDDEN_ITEM_ROUTE_117_REPEL",
+ "BERRY_TREE_27",
+ "BERRY_TREE_28",
+ "BERRY_TREE_29",
+ "TRAINER_LYDIA_1_REWARD",
+ "TRAINER_DEREK_REWARD",
+ "TRAINER_BRANDI_REWARD",
+ "TRAINER_MELINA_REWARD",
+ "TRAINER_AISHA_REWARD",
+ "TRAINER_MARIA_1_REWARD",
+ "TRAINER_ISAAC_1_REWARD",
+ "TRAINER_DYLAN_1_REWARD",
+ "TRAINER_ANNA_AND_MEG_1_REWARD"
+ ],
+ "events": [
+ "EVENT_ENCOUNTER_LATIOS"
],
- "events": [],
"exits": [
"REGION_VERDANTURF_TOWN/MAIN",
- "REGION_MAUVILLE_CITY/MAIN"
+ "REGION_MAUVILLE_CITY/MAIN",
+ "REGION_ROUTE117/PONDS"
],
"warps": [
"MAP_ROUTE117:0/MAP_ROUTE117_POKEMON_DAY_CARE:0"
]
},
+ "REGION_ROUTE117/PONDS": {
+ "parent_map": "MAP_ROUTE117",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": []
+ },
"REGION_ROUTE117_POKEMON_DAY_CARE/MAIN": {
"parent_map": "MAP_ROUTE117_POKEMON_DAY_CARE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -894,52 +1678,99 @@
},
"REGION_ROUTE118/WEST": {
"parent_map": "MAP_ROUTE118",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
- "HIDDEN_ITEM_ROUTE_118_HEART_SCALE"
+ "HIDDEN_ITEM_ROUTE_118_HEART_SCALE",
+ "TRAINER_DEANDRE_REWARD",
+ "TRAINER_ROSE_1_REWARD",
+ "TRAINER_WADE_REWARD",
+ "TRAINER_DALTON_1_REWARD"
],
"events": [],
"exits": [
"REGION_MAUVILLE_CITY/MAIN",
- "REGION_ROUTE118/WATER"
+ "REGION_ROUTE118/WEST_WATER",
+ "REGION_ROUTE118/EAST",
+ "REGION_TERRA_CAVE_ENTRANCE/MAIN"
],
"warps": []
},
- "REGION_ROUTE118/WATER": {
+ "REGION_ROUTE118/WEST_WATER": {
"parent_map": "MAP_ROUTE118",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
"REGION_ROUTE118/WEST",
+ "REGION_ROUTE118/EAST_WATER"
+ ],
+ "warps": []
+ },
+ "REGION_ROUTE118/EAST_WATER": {
+ "parent_map": "MAP_ROUTE118",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE118/WEST_WATER",
"REGION_ROUTE118/EAST"
],
"warps": []
},
"REGION_ROUTE118/EAST": {
"parent_map": "MAP_ROUTE118",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_118_HYPER_POTION",
"HIDDEN_ITEM_ROUTE_118_IRON",
- "NPC_GIFT_RECEIVED_GOOD_ROD"
+ "NPC_GIFT_RECEIVED_GOOD_ROD",
+ "BERRY_TREE_31",
+ "BERRY_TREE_32",
+ "BERRY_TREE_33",
+ "TRAINER_BARNY_REWARD",
+ "TRAINER_CHESTER_REWARD",
+ "TRAINER_PERRY_REWARD"
],
"events": [],
"exits": [
- "REGION_ROUTE118/WATER",
+ "REGION_ROUTE118/EAST_WATER",
+ "REGION_ROUTE118/WEST",
"REGION_ROUTE119/LOWER",
- "REGION_ROUTE123/WEST"
+ "REGION_ROUTE123/WEST",
+ "REGION_TERRA_CAVE_ENTRANCE/MAIN"
],
"warps": []
},
"REGION_ROUTE119/LOWER": {
"parent_map": "MAP_ROUTE119",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_119_SUPER_REPEL",
"ITEM_ROUTE_119_HYPER_POTION_1",
- "HIDDEN_ITEM_ROUTE_119_FULL_HEAL"
+ "HIDDEN_ITEM_ROUTE_119_FULL_HEAL",
+ "BERRY_TREE_85",
+ "BERRY_TREE_86",
+ "TRAINER_DONALD_REWARD",
+ "TRAINER_KENT_REWARD",
+ "TRAINER_TAYLOR_REWARD",
+ "TRAINER_GREG_REWARD",
+ "TRAINER_DOUG_REWARD",
+ "TRAINER_BRENT_REWARD"
],
"events": [],
"exits": [
"REGION_ROUTE119/MIDDLE",
- "REGION_ROUTE119/LOWER_ACROSS_WATER",
+ "REGION_ROUTE119/LOWER_WATER",
"REGION_ROUTE119/LOWER_ACROSS_RAILS",
"REGION_ROUTE118/EAST"
],
@@ -947,19 +1778,37 @@
"MAP_ROUTE119:1/MAP_ROUTE119_HOUSE:0"
]
},
+ "REGION_ROUTE119/LOWER_WATER": {
+ "parent_map": "MAP_ROUTE119",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE119/LOWER",
+ "REGION_ROUTE119/LOWER_ACROSS_WATER"
+ ],
+ "warps": []
+ },
"REGION_ROUTE119/LOWER_ACROSS_WATER": {
"parent_map": "MAP_ROUTE119",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
- "ITEM_ROUTE_119_ZINC"
+ "ITEM_ROUTE_119_ZINC",
+ "TRAINER_CHRIS_REWARD"
],
"events": [],
- "exits": [
- "REGION_ROUTE119/LOWER"
- ],
+ "exits": [],
"warps": []
},
"REGION_ROUTE119/LOWER_ACROSS_RAILS": {
"parent_map": "MAP_ROUTE119",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_ROUTE_119_CALCIUM"
],
@@ -971,9 +1820,19 @@
},
"REGION_ROUTE119/MIDDLE": {
"parent_map": "MAP_ROUTE119",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_ROUTE_119_ELIXIR_1",
- "ITEM_ROUTE_119_HYPER_POTION_2"
+ "ITEM_ROUTE_119_HYPER_POTION_2",
+ "TRAINER_CATHERINE_1_REWARD",
+ "TRAINER_JACKSON_1_REWARD",
+ "TRAINER_RACHEL_REWARD",
+ "TRAINER_PHIL_REWARD",
+ "TRAINER_HUGH_REWARD",
+ "TRAINER_DAYTON_REWARD",
+ "TRAINER_TAKASHI_REWARD"
],
"events": [],
"exits": [
@@ -984,37 +1843,56 @@
"MAP_ROUTE119:0/MAP_ROUTE119_WEATHER_INSTITUTE_1F:0"
]
},
- "REGION_ROUTE119/MIDDLE_RIVER": {
+ "REGION_ROUTE119/UPPER": {
"parent_map": "MAP_ROUTE119",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
- "ITEM_ROUTE_119_LEAF_STONE",
- "HIDDEN_ITEM_ROUTE_119_ULTRA_BALL",
- "HIDDEN_ITEM_ROUTE_119_MAX_ETHER"
+ "ITEM_ROUTE_119_ELIXIR_2",
+ "NPC_GIFT_RECEIVED_HM_FLY",
+ "BERRY_TREE_34",
+ "BERRY_TREE_35",
+ "BERRY_TREE_36",
+ "TRAINER_FABIAN_REWARD",
+ "TRAINER_YASU_REWARD",
+ "TRAINER_HIDEO_REWARD",
+ "TRAINER_BRENDAN_ROUTE_119_MUDKIP_REWARD"
],
"events": [],
"exits": [
- "REGION_ROUTE119/UPPER",
- "REGION_ROUTE119/ABOVE_WATERFALL"
+ "REGION_ROUTE119/MIDDLE",
+ "REGION_ROUTE119/MIDDLE_RIVER",
+ "REGION_FORTREE_CITY/MAIN"
],
"warps": []
},
- "REGION_ROUTE119/UPPER": {
+ "REGION_ROUTE119/MIDDLE_RIVER": {
"parent_map": "MAP_ROUTE119",
+ "has_grass": true,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
- "ITEM_ROUTE_119_ELIXIR_2",
- "NPC_GIFT_RECEIVED_HM02"
+ "ITEM_ROUTE_119_LEAF_STONE",
+ "HIDDEN_ITEM_ROUTE_119_ULTRA_BALL",
+ "HIDDEN_ITEM_ROUTE_119_MAX_ETHER"
],
"events": [],
"exits": [
- "REGION_ROUTE119/MIDDLE",
- "REGION_ROUTE119/MIDDLE_RIVER",
- "REGION_FORTREE_CITY/MAIN"
+ "REGION_ROUTE119/UPPER",
+ "REGION_ROUTE119/ABOVE_WATERFALL"
],
"warps": []
},
"REGION_ROUTE119/ABOVE_WATERFALL": {
"parent_map": "MAP_ROUTE119",
- "locations": [],
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [
+ "BERRY_TREE_83",
+ "BERRY_TREE_84"
+ ],
"events": [],
"exits": [
"REGION_ROUTE119/MIDDLE_RIVER",
@@ -1024,6 +1902,9 @@
},
"REGION_ROUTE119/ABOVE_WATERFALL_ACROSS_RAILS": {
"parent_map": "MAP_ROUTE119",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_ROUTE_119_RARE_CANDY",
"ITEM_ROUTE_119_NUGGET"
@@ -1036,7 +1917,13 @@
},
"REGION_ROUTE119_WEATHER_INSTITUTE_1F/MAIN": {
"parent_map": "MAP_ROUTE119_WEATHER_INSTITUTE_1F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_GRUNT_WEATHER_INST_1_REWARD",
+ "TRAINER_GRUNT_WEATHER_INST_4_REWARD"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -1046,7 +1933,15 @@
},
"REGION_ROUTE119_WEATHER_INSTITUTE_2F/MAIN": {
"parent_map": "MAP_ROUTE119_WEATHER_INSTITUTE_2F",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "TRAINER_GRUNT_WEATHER_INST_2_REWARD",
+ "TRAINER_GRUNT_WEATHER_INST_3_REWARD",
+ "TRAINER_GRUNT_WEATHER_INST_5_REWARD",
+ "TRAINER_SHELLY_WEATHER_INSTITUTE_REWARD"
+ ],
"events": [
"EVENT_DEFEAT_SHELLY"
],
@@ -1057,6 +1952,9 @@
},
"REGION_ROUTE119_HOUSE/MAIN": {
"parent_map": "MAP_ROUTE119_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1066,10 +1964,15 @@
},
"REGION_ROUTE120/NORTH": {
"parent_map": "MAP_ROUTE120",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_ROUTE_120_RARE_CANDY_1",
"HIDDEN_ITEM_ROUTE_120_REVIVE",
- "NPC_GIFT_RECEIVED_DEVON_SCOPE"
+ "NPC_GIFT_RECEIVED_DEVON_SCOPE",
+ "TRAINER_CLARISSA_REWARD",
+ "TRAINER_ROBERT_1_REWARD"
],
"events": [],
"exits": [
@@ -1081,6 +1984,9 @@
},
"REGION_ROUTE120/NORTH_POND_SHORE": {
"parent_map": "MAP_ROUTE120",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_120_NEST_BALL"
],
@@ -1093,6 +1999,9 @@
},
"REGION_ROUTE120/NORTH_POND": {
"parent_map": "MAP_ROUTE120",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -1104,15 +2013,38 @@
},
"REGION_ROUTE120/SOUTH": {
"parent_map": "MAP_ROUTE120",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_120_NUGGET",
"ITEM_ROUTE_120_REVIVE",
"ITEM_ROUTE_120_HYPER_POTION",
- "HIDDEN_ITEM_ROUTE_120_ZINC"
+ "HIDDEN_ITEM_ROUTE_120_ZINC",
+ "NPC_GIFT_ROUTE_120_RECEIVED_BERRY",
+ "BERRY_TREE_40",
+ "BERRY_TREE_41",
+ "BERRY_TREE_42",
+ "BERRY_TREE_43",
+ "BERRY_TREE_44",
+ "BERRY_TREE_45",
+ "BERRY_TREE_46",
+ "TRAINER_COLIN_REWARD",
+ "TRAINER_LEONEL_REWARD",
+ "TRAINER_ANGELICA_REWARD",
+ "TRAINER_CALLIE_REWARD",
+ "TRAINER_RILEY_REWARD",
+ "TRAINER_JENNIFER_REWARD",
+ "TRAINER_JENNA_REWARD",
+ "TRAINER_LORENZO_REWARD",
+ "TRAINER_JEFFREY_1_REWARD",
+ "TRAINER_KEIGO_REWARD",
+ "TRAINER_CHIP_REWARD"
],
"events": [],
"exits": [
"REGION_ROUTE120/NORTH",
+ "REGION_ROUTE120/SOUTH_ALCOVE",
"REGION_ROUTE120/SOUTH_PONDS",
"REGION_ROUTE121/WEST"
],
@@ -1120,8 +2052,27 @@
"MAP_ROUTE120:0/MAP_ANCIENT_TOMB:0"
]
},
+ "REGION_ROUTE120/SOUTH_ALCOVE": {
+ "parent_map": "MAP_ROUTE120",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "BERRY_TREE_37",
+ "BERRY_TREE_38",
+ "BERRY_TREE_39"
+ ],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE120/SOUTH"
+ ],
+ "warps": []
+ },
"REGION_ROUTE120/SOUTH_PONDS": {
"parent_map": "MAP_ROUTE120",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"HIDDEN_ITEM_ROUTE_120_RARE_CANDY_2",
"ITEM_ROUTE_120_FULL_HEAL"
@@ -1132,8 +2083,18 @@
},
"REGION_ROUTE121/WEST": {
"parent_map": "MAP_ROUTE121",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
- "HIDDEN_ITEM_ROUTE_121_HP_UP"
+ "HIDDEN_ITEM_ROUTE_121_HP_UP",
+ "BERRY_TREE_47",
+ "BERRY_TREE_48",
+ "BERRY_TREE_49",
+ "BERRY_TREE_50",
+ "TRAINER_CALE_REWARD",
+ "TRAINER_TAMMY_REWARD",
+ "TRAINER_JESSICA_1_REWARD"
],
"events": [],
"exits": [
@@ -1144,26 +2105,56 @@
},
"REGION_ROUTE121/EAST": {
"parent_map": "MAP_ROUTE121",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_121_CARBOS",
"ITEM_ROUTE_121_REVIVE",
"ITEM_ROUTE_121_ZINC",
"HIDDEN_ITEM_ROUTE_121_NUGGET",
"HIDDEN_ITEM_ROUTE_121_FULL_HEAL",
- "HIDDEN_ITEM_ROUTE_121_MAX_REVIVE"
+ "HIDDEN_ITEM_ROUTE_121_MAX_REVIVE",
+ "BERRY_TREE_51",
+ "BERRY_TREE_52",
+ "BERRY_TREE_53",
+ "BERRY_TREE_54",
+ "TRAINER_KATE_AND_JOY_REWARD",
+ "TRAINER_WALTER_1_REWARD",
+ "TRAINER_PAT_REWARD",
+ "TRAINER_MYLES_REWARD",
+ "TRAINER_VANESSA_REWARD",
+ "TRAINER_MARCEL_REWARD",
+ "TRAINER_CRISTIN_1_REWARD"
],
"events": [],
"exits": [
"REGION_ROUTE121/WEST",
- "REGION_ROUTE122/SEA",
+ "REGION_ROUTE121/WATER",
"REGION_LILYCOVE_CITY/MAIN"
],
"warps": [
"MAP_ROUTE121:0/MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:2"
]
},
+ "REGION_ROUTE121/WATER": {
+ "parent_map": "MAP_ROUTE121",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE121/EAST",
+ "REGION_ROUTE122/SEA"
+ ],
+ "warps": []
+ },
"REGION_ROUTE121_SAFARI_ZONE_ENTRANCE/MAIN": {
"parent_map": "MAP_ROUTE121_SAFARI_ZONE_ENTRANCE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1174,6 +2165,9 @@
},
"REGION_SAFARI_ZONE_NORTH/MAIN": {
"parent_map": "MAP_SAFARI_ZONE_NORTH",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_SAFARI_ZONE_NORTH_CALCIUM"
],
@@ -1185,17 +2179,34 @@
},
"REGION_SAFARI_ZONE_NORTHWEST/MAIN": {
"parent_map": "MAP_SAFARI_ZONE_NORTHWEST",
- "locations": [
- "ITEM_SAFARI_ZONE_NORTH_WEST_TM22"
- ],
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
+ "locations": [],
"events": [],
"exits": [
+ "REGION_SAFARI_ZONE_NORTHWEST/POND",
"REGION_SAFARI_ZONE_SOUTHWEST/MAIN"
],
"warps": []
},
+ "REGION_SAFARI_ZONE_NORTHWEST/POND": {
+ "parent_map": "MAP_SAFARI_ZONE_NORTHWEST",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [
+ "ITEM_SAFARI_ZONE_NORTH_WEST_TM_SOLAR_BEAM"
+ ],
+ "events": [],
+ "exits": [],
+ "warps": []
+ },
"REGION_SAFARI_ZONE_NORTHEAST/MAIN": {
"parent_map": "MAP_SAFARI_ZONE_NORTHEAST",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_SAFARI_ZONE_NORTH_EAST_NUGGET",
"HIDDEN_ITEM_SAFARI_ZONE_NORTH_EAST_RARE_CANDY",
@@ -1209,6 +2220,9 @@
},
"REGION_SAFARI_ZONE_SOUTH/MAIN": {
"parent_map": "MAP_SAFARI_ZONE_SOUTH",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1222,11 +2236,13 @@
},
"REGION_SAFARI_ZONE_SOUTHWEST/MAIN": {
"parent_map": "MAP_SAFARI_ZONE_SOUTHWEST",
- "locations": [
- "ITEM_SAFARI_ZONE_SOUTH_WEST_MAX_REVIVE"
- ],
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
+ "locations": [],
"events": [],
"exits": [
+ "REGION_SAFARI_ZONE_SOUTHWEST/POND",
"REGION_SAFARI_ZONE_SOUTH/MAIN",
"REGION_SAFARI_ZONE_NORTHWEST/MAIN"
],
@@ -1234,22 +2250,65 @@
"MAP_SAFARI_ZONE_SOUTHWEST:0/MAP_SAFARI_ZONE_REST_HOUSE:0"
]
},
+ "REGION_SAFARI_ZONE_SOUTHWEST/POND": {
+ "parent_map": "MAP_SAFARI_ZONE_SOUTHWEST",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [
+ "ITEM_SAFARI_ZONE_SOUTH_WEST_MAX_REVIVE"
+ ],
+ "events": [],
+ "exits": [],
+ "warps": []
+ },
"REGION_SAFARI_ZONE_SOUTHEAST/MAIN": {
"parent_map": "MAP_SAFARI_ZONE_SOUTHEAST",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
- "ITEM_SAFARI_ZONE_SOUTH_EAST_BIG_PEARL",
"HIDDEN_ITEM_SAFARI_ZONE_SOUTH_EAST_PP_UP",
"HIDDEN_ITEM_SAFARI_ZONE_SOUTH_EAST_FULL_RESTORE"
],
"events": [],
"exits": [
+ "REGION_SAFARI_ZONE_SOUTHEAST/WATER",
"REGION_SAFARI_ZONE_SOUTH/MAIN",
"REGION_SAFARI_ZONE_NORTHEAST/MAIN"
],
"warps": []
},
+ "REGION_SAFARI_ZONE_SOUTHEAST/WATER": {
+ "parent_map": "MAP_SAFARI_ZONE_SOUTHEAST",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_SAFARI_ZONE_SOUTHEAST/MAIN",
+ "REGION_SAFARI_ZONE_SOUTHEAST/ISLAND"
+ ],
+ "warps": []
+ },
+ "REGION_SAFARI_ZONE_SOUTHEAST/ISLAND": {
+ "parent_map": "MAP_SAFARI_ZONE_SOUTHEAST",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
+ "locations": [
+ "ITEM_SAFARI_ZONE_SOUTH_EAST_BIG_PEARL"
+ ],
+ "events": [],
+ "exits": [],
+ "warps": []
+ },
"REGION_SAFARI_ZONE_REST_HOUSE/MAIN": {
"parent_map": "MAP_SAFARI_ZONE_REST_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1259,17 +2318,23 @@
},
"REGION_ROUTE122/SEA": {
"parent_map": "MAP_ROUTE122",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
"REGION_ROUTE122/MT_PYRE_ENTRANCE",
- "REGION_ROUTE121/EAST",
+ "REGION_ROUTE121/WATER",
"REGION_ROUTE123/EAST"
],
"warps": []
},
"REGION_ROUTE122/MT_PYRE_ENTRANCE": {
"parent_map": "MAP_ROUTE122",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -1281,9 +2346,28 @@
},
"REGION_ROUTE123/WEST": {
"parent_map": "MAP_ROUTE123",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"ITEM_ROUTE_123_ULTRA_BALL",
- "HIDDEN_ITEM_ROUTE_123_REVIVE"
+ "HIDDEN_ITEM_ROUTE_123_REVIVE",
+ "BERRY_TREE_14",
+ "BERRY_TREE_15",
+ "BERRY_TREE_30",
+ "BERRY_TREE_58",
+ "BERRY_TREE_59",
+ "BERRY_TREE_60",
+ "BERRY_TREE_61",
+ "BERRY_TREE_65",
+ "BERRY_TREE_72",
+ "BERRY_TREE_73",
+ "BERRY_TREE_74",
+ "BERRY_TREE_79",
+ "TRAINER_JAZMYN_REWARD",
+ "TRAINER_DAVIS_REWARD",
+ "TRAINER_VIOLET_REWARD",
+ "TRAINER_MIU_AND_YUKI_REWARD"
],
"events": [],
"exits": [
@@ -1295,6 +2379,9 @@
},
"REGION_ROUTE123/EAST": {
"parent_map": "MAP_ROUTE123",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_123_CALCIUM",
"ITEM_ROUTE_123_ELIXIR",
@@ -1302,21 +2389,52 @@
"ITEM_ROUTE_123_REVIVAL_HERB",
"HIDDEN_ITEM_ROUTE_123_SUPER_REPEL",
"HIDDEN_ITEM_ROUTE_123_HYPER_POTION",
- "NPC_GIFT_RECEIVED_TM19"
+ "NPC_GIFT_RECEIVED_TM_GIGA_DRAIN",
+ "BERRY_TREE_57",
+ "BERRY_TREE_62",
+ "BERRY_TREE_63",
+ "BERRY_TREE_64",
+ "BERRY_TREE_87",
+ "BERRY_TREE_88",
+ "TRAINER_JACKI_1_REWARD",
+ "TRAINER_FREDRICK_REWARD",
+ "TRAINER_BRAXTON_REWARD",
+ "TRAINER_FERNANDO_1_REWARD",
+ "TRAINER_ALBERTO_REWARD",
+ "TRAINER_WENDY_REWARD",
+ "TRAINER_KINDRA_REWARD",
+ "TRAINER_ED_REWARD"
],
"events": [],
"exits": [
"REGION_ROUTE123/WEST",
"REGION_ROUTE123/EAST_BEHIND_TREE",
+ "REGION_ROUTE123/POND",
"REGION_ROUTE122/SEA"
],
"warps": []
},
+ "REGION_ROUTE123/POND": {
+ "parent_map": "MAP_ROUTE123",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [],
+ "events": [],
+ "exits": [],
+ "warps": []
+ },
"REGION_ROUTE123/EAST_BEHIND_TREE": {
"parent_map": "MAP_ROUTE123",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_ROUTE_123_PP_UP",
- "HIDDEN_ITEM_ROUTE_123_RARE_CANDY"
+ "HIDDEN_ITEM_ROUTE_123_RARE_CANDY",
+ "TRAINER_CAMERON_1_REWARD",
+ "TRAINER_JONAS_REWARD",
+ "TRAINER_KAYLEY_REWARD"
],
"events": [],
"exits": [
@@ -1326,7 +2444,14 @@
},
"REGION_ROUTE123_BERRY_MASTERS_HOUSE/MAIN": {
"parent_map": "MAP_ROUTE123_BERRY_MASTERS_HOUSE",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [
+ "NPC_GIFT_BERRY_MASTER_RECEIVED_BERRY_1",
+ "NPC_GIFT_BERRY_MASTER_RECEIVED_BERRY_2",
+ "NPC_GIFT_BERRY_MASTERS_WIFE"
+ ],
"events": [],
"exits": [],
"warps": [
@@ -1335,7 +2460,19 @@
},
"REGION_ROUTE124/MAIN": {
"parent_map": "MAP_ROUTE124",
- "locations": [],
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [
+ "TRAINER_DECLAN_REWARD",
+ "TRAINER_GRACE_REWARD",
+ "TRAINER_LILA_AND_ROY_1_REWARD",
+ "TRAINER_SPENCER_REWARD",
+ "TRAINER_JENNY_1_REWARD",
+ "TRAINER_CHAD_REWARD",
+ "TRAINER_ROLAND_REWARD",
+ "TRAINER_ISABELLA_REWARD"
+ ],
"events": [],
"exits": [
"REGION_LILYCOVE_CITY/MAIN",
@@ -1355,6 +2492,9 @@
},
"REGION_ROUTE124/NORTH_ENCLOSED_AREA_1": {
"parent_map": "MAP_ROUTE124",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_124_RED_SHARD"
],
@@ -1366,6 +2506,9 @@
},
"REGION_ROUTE124/NORTH_ENCLOSED_AREA_2": {
"parent_map": "MAP_ROUTE124",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -1375,6 +2518,9 @@
},
"REGION_ROUTE124/NORTH_ENCLOSED_AREA_3": {
"parent_map": "MAP_ROUTE124",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_124_YELLOW_SHARD"
],
@@ -1386,6 +2532,9 @@
},
"REGION_ROUTE124/SOUTH_ENCLOSED_AREA_1": {
"parent_map": "MAP_ROUTE124",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_124_BLUE_SHARD"
],
@@ -1397,6 +2546,9 @@
},
"REGION_ROUTE124/SOUTH_ENCLOSED_AREA_2": {
"parent_map": "MAP_ROUTE124",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -1406,6 +2558,9 @@
},
"REGION_ROUTE124/SOUTH_ENCLOSED_AREA_3": {
"parent_map": "MAP_ROUTE124",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -1416,6 +2571,9 @@
},
"REGION_UNDERWATER_ROUTE124/BIG_AREA": {
"parent_map": "MAP_UNDERWATER_ROUTE124",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_UNDERWATER_124_GREEN_SHARD"
],
@@ -1427,6 +2585,9 @@
},
"REGION_UNDERWATER_ROUTE124/SMALL_AREA_1": {
"parent_map": "MAP_UNDERWATER_ROUTE124",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_UNDERWATER_124_PEARL"
],
@@ -1438,6 +2599,9 @@
},
"REGION_UNDERWATER_ROUTE124/SMALL_AREA_2": {
"parent_map": "MAP_UNDERWATER_ROUTE124",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_UNDERWATER_124_BIG_PEARL"
],
@@ -1449,6 +2613,9 @@
},
"REGION_UNDERWATER_ROUTE124/SMALL_AREA_3": {
"parent_map": "MAP_UNDERWATER_ROUTE124",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_UNDERWATER_124_HEART_SCALE_1"
],
@@ -1460,6 +2627,9 @@
},
"REGION_UNDERWATER_ROUTE124/TUNNEL_1": {
"parent_map": "MAP_UNDERWATER_ROUTE124",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_UNDERWATER_124_CALCIUM",
"HIDDEN_ITEM_UNDERWATER_124_HEART_SCALE_2"
@@ -1474,6 +2644,9 @@
},
"REGION_UNDERWATER_ROUTE124/TUNNEL_2": {
"parent_map": "MAP_UNDERWATER_ROUTE124",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1484,6 +2657,9 @@
},
"REGION_UNDERWATER_ROUTE124/TUNNEL_3": {
"parent_map": "MAP_UNDERWATER_ROUTE124",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_UNDERWATER_124_CARBOS"
],
@@ -1497,6 +2673,9 @@
},
"REGION_UNDERWATER_ROUTE124/TUNNEL_4": {
"parent_map": "MAP_UNDERWATER_ROUTE124",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1507,6 +2686,9 @@
},
"REGION_ROUTE124_DIVING_TREASURE_HUNTERS_HOUSE/MAIN": {
"parent_map": "MAP_ROUTE124_DIVING_TREASURE_HUNTERS_HOUSE",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -1516,20 +2698,36 @@
},
"REGION_ROUTE125/SEA": {
"parent_map": "MAP_ROUTE125",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
- "ITEM_ROUTE_125_BIG_PEARL"
+ "ITEM_ROUTE_125_BIG_PEARL",
+ "TRAINER_NOLEN_REWARD",
+ "TRAINER_ERNEST_1_REWARD",
+ "TRAINER_SHARON_REWARD",
+ "TRAINER_TANYA_REWARD",
+ "TRAINER_PRESLEY_REWARD",
+ "TRAINER_AURON_REWARD",
+ "TRAINER_STAN_REWARD"
],
"events": [],
"exits": [
"REGION_ROUTE125/SHOAL_CAVE_ENTRANCE",
"REGION_MOSSDEEP_CITY/MAIN",
- "REGION_UNDERWATER_ROUTE125/MAIN"
+ "REGION_UNDERWATER_ROUTE125/MARINE_CAVE_ENTRANCE_1",
+ "REGION_UNDERWATER_ROUTE125/MARINE_CAVE_ENTRANCE_2"
],
"warps": []
},
"REGION_ROUTE125/SHOAL_CAVE_ENTRANCE": {
"parent_map": "MAP_ROUTE125",
- "locations": [],
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": true,
+ "locations": [
+ "TRAINER_KIM_AND_IRIS_REWARD"
+ ],
"events": [],
"exits": [
"REGION_ROUTE125/SEA"
@@ -1538,18 +2736,46 @@
"MAP_ROUTE125:0/MAP_SHOAL_CAVE_LOW_TIDE_ENTRANCE_ROOM:0"
]
},
- "REGION_UNDERWATER_ROUTE125/MAIN": {
+ "REGION_UNDERWATER_ROUTE125/MARINE_CAVE_ENTRANCE_1": {
"parent_map": "MAP_UNDERWATER_ROUTE125",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
- "REGION_ROUTE125/SEA"
+ "REGION_ROUTE125/SEA",
+ "REGION_UNDERWATER_MARINE_CAVE/MAIN"
+ ],
+ "warps": []
+ },
+ "REGION_UNDERWATER_ROUTE125/MARINE_CAVE_ENTRANCE_2": {
+ "parent_map": "MAP_UNDERWATER_ROUTE125",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE125/SEA",
+ "REGION_UNDERWATER_MARINE_CAVE/MAIN"
],
"warps": []
},
"REGION_ROUTE126/MAIN": {
"parent_map": "MAP_ROUTE126",
- "locations": [],
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [
+ "TRAINER_NIKKI_REWARD",
+ "TRAINER_BARRY_REWARD",
+ "TRAINER_SIENNA_REWARD",
+ "TRAINER_PABLO_1_REWARD",
+ "TRAINER_DEAN_REWARD",
+ "TRAINER_LEONARDO_REWARD",
+ "TRAINER_ISOBEL_REWARD"
+ ],
"events": [],
"exits": [
"REGION_ROUTE127/MAIN",
@@ -1560,6 +2786,9 @@
},
"REGION_ROUTE126/NEAR_ROUTE_124": {
"parent_map": "MAP_ROUTE126",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -1570,6 +2799,9 @@
},
"REGION_ROUTE126/NORTH_WEST_CORNER": {
"parent_map": "MAP_ROUTE126",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_126_GREEN_SHARD"
],
@@ -1581,7 +2813,12 @@
},
"REGION_ROUTE126/WEST": {
"parent_map": "MAP_ROUTE126",
- "locations": [],
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [
+ "TRAINER_BRENDA_REWARD"
+ ],
"events": [],
"exits": [
"REGION_UNDERWATER_ROUTE126/SMALL_AREA_1",
@@ -1591,6 +2828,9 @@
},
"REGION_UNDERWATER_ROUTE126/MAIN": {
"parent_map": "MAP_UNDERWATER_ROUTE126",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_UNDERWATER_126_HEART_SCALE",
"HIDDEN_ITEM_UNDERWATER_126_ULTRA_BALL",
@@ -1608,6 +2848,9 @@
},
"REGION_UNDERWATER_ROUTE126/TUNNEL": {
"parent_map": "MAP_UNDERWATER_ROUTE126",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1618,6 +2861,9 @@
},
"REGION_UNDERWATER_ROUTE126/SMALL_AREA_1": {
"parent_map": "MAP_UNDERWATER_ROUTE126",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_UNDERWATER_126_PEARL",
"HIDDEN_ITEM_UNDERWATER_126_IRON",
@@ -1631,6 +2877,9 @@
},
"REGION_UNDERWATER_ROUTE126/SMALL_AREA_2": {
"parent_map": "MAP_UNDERWATER_ROUTE126",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_UNDERWATER_126_BLUE_SHARD"
],
@@ -1642,9 +2891,20 @@
},
"REGION_ROUTE127/MAIN": {
"parent_map": "MAP_ROUTE127",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_127_ZINC",
- "ITEM_ROUTE_127_RARE_CANDY"
+ "ITEM_ROUTE_127_RARE_CANDY",
+ "TRAINER_CAMDEN_REWARD",
+ "TRAINER_ATHENA_REWARD",
+ "TRAINER_AIDAN_REWARD",
+ "TRAINER_JONAH_REWARD",
+ "TRAINER_HENRY_REWARD",
+ "TRAINER_ROGER_REWARD",
+ "TRAINER_DONNY_REWARD",
+ "TRAINER_KOJI_1_REWARD"
],
"events": [],
"exits": [
@@ -1655,12 +2915,17 @@
"REGION_UNDERWATER_ROUTE127/TUNNEL",
"REGION_UNDERWATER_ROUTE127/AREA_1",
"REGION_UNDERWATER_ROUTE127/AREA_2",
- "REGION_UNDERWATER_ROUTE127/AREA_3"
+ "REGION_UNDERWATER_ROUTE127/AREA_3",
+ "REGION_UNDERWATER_ROUTE127/MARINE_CAVE_ENTRANCE_1",
+ "REGION_UNDERWATER_ROUTE127/MARINE_CAVE_ENTRANCE_2"
],
"warps": []
},
"REGION_ROUTE127/ENCLOSED_AREA": {
"parent_map": "MAP_ROUTE127",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_127_CARBOS"
],
@@ -1672,6 +2937,9 @@
},
"REGION_UNDERWATER_ROUTE127/MAIN": {
"parent_map": "MAP_UNDERWATER_ROUTE127",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_UNDERWATER_127_HEART_SCALE"
],
@@ -1684,6 +2952,9 @@
},
"REGION_UNDERWATER_ROUTE127/TUNNEL": {
"parent_map": "MAP_UNDERWATER_ROUTE127",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1694,6 +2965,9 @@
},
"REGION_UNDERWATER_ROUTE127/AREA_1": {
"parent_map": "MAP_UNDERWATER_ROUTE127",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_UNDERWATER_127_STAR_PIECE"
],
@@ -1705,6 +2979,9 @@
},
"REGION_UNDERWATER_ROUTE127/AREA_2": {
"parent_map": "MAP_UNDERWATER_ROUTE127",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_UNDERWATER_127_HP_UP"
],
@@ -1716,6 +2993,9 @@
},
"REGION_UNDERWATER_ROUTE127/AREA_3": {
"parent_map": "MAP_UNDERWATER_ROUTE127",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_UNDERWATER_127_RED_SHARD"
],
@@ -1725,12 +3005,48 @@
],
"warps": []
},
+ "REGION_UNDERWATER_ROUTE127/MARINE_CAVE_ENTRANCE_1": {
+ "parent_map": "MAP_UNDERWATER_ROUTE127",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE127/MAIN",
+ "REGION_UNDERWATER_MARINE_CAVE/MAIN"
+ ],
+ "warps": []
+ },
+ "REGION_UNDERWATER_ROUTE127/MARINE_CAVE_ENTRANCE_2": {
+ "parent_map": "MAP_UNDERWATER_ROUTE127",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE127/MAIN",
+ "REGION_UNDERWATER_MARINE_CAVE/MAIN"
+ ],
+ "warps": []
+ },
"REGION_ROUTE128/MAIN": {
"parent_map": "MAP_ROUTE128",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"HIDDEN_ITEM_ROUTE_128_HEART_SCALE_1",
"HIDDEN_ITEM_ROUTE_128_HEART_SCALE_2",
- "HIDDEN_ITEM_ROUTE_128_HEART_SCALE_3"
+ "HIDDEN_ITEM_ROUTE_128_HEART_SCALE_3",
+ "TRAINER_ALEXA_REWARD",
+ "TRAINER_RUBEN_REWARD",
+ "TRAINER_ISAIAH_1_REWARD",
+ "TRAINER_WAYNE_REWARD",
+ "TRAINER_KATELYN_1_REWARD",
+ "TRAINER_HARRISON_REWARD",
+ "TRAINER_CARLEE_REWARD"
],
"events": [],
"exits": [
@@ -1745,6 +3061,9 @@
},
"REGION_UNDERWATER_ROUTE128/MAIN": {
"parent_map": "MAP_UNDERWATER_ROUTE128",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1757,6 +3076,9 @@
},
"REGION_UNDERWATER_ROUTE128/AREA_1": {
"parent_map": "MAP_UNDERWATER_ROUTE128",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_UNDERWATER_128_PROTEIN"
],
@@ -1768,6 +3090,9 @@
},
"REGION_UNDERWATER_ROUTE128/AREA_2": {
"parent_map": "MAP_UNDERWATER_ROUTE128",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [
"HIDDEN_ITEM_UNDERWATER_128_PEARL"
],
@@ -1779,37 +3104,95 @@
},
"REGION_ROUTE129/MAIN": {
"parent_map": "MAP_ROUTE129",
- "locations": [],
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [
+ "TRAINER_REED_REWARD",
+ "TRAINER_CHASE_REWARD",
+ "TRAINER_ALLISON_REWARD",
+ "TRAINER_TISHA_REWARD",
+ "TRAINER_CLARENCE_REWARD"
+ ],
"events": [],
"exits": [
"REGION_ROUTE130/MAIN",
"REGION_ROUTE128/MAIN",
- "REGION_UNDERWATER_ROUTE129/MAIN"
+ "REGION_UNDERWATER_ROUTE129/MARINE_CAVE_ENTRANCE_1",
+ "REGION_UNDERWATER_ROUTE129/MARINE_CAVE_ENTRANCE_2"
],
"warps": []
},
- "REGION_UNDERWATER_ROUTE129/MAIN": {
+ "REGION_UNDERWATER_ROUTE129/MARINE_CAVE_ENTRANCE_1": {
"parent_map": "MAP_UNDERWATER_ROUTE129",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
- "REGION_ROUTE129/MAIN"
+ "REGION_ROUTE129/MAIN",
+ "REGION_UNDERWATER_MARINE_CAVE/MAIN"
+ ],
+ "warps": []
+ },
+ "REGION_UNDERWATER_ROUTE129/MARINE_CAVE_ENTRANCE_2": {
+ "parent_map": "MAP_UNDERWATER_ROUTE129",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
+ "locations": [],
+ "events": [],
+ "exits": [
+ "REGION_ROUTE129/MAIN",
+ "REGION_UNDERWATER_MARINE_CAVE/MAIN"
],
"warps": []
},
"REGION_ROUTE130/MAIN": {
"parent_map": "MAP_ROUTE130",
- "locations": [],
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [
+ "TRAINER_RODNEY_REWARD",
+ "TRAINER_KATIE_REWARD",
+ "TRAINER_SANTIAGO_REWARD"
+ ],
"events": [],
"exits": [
+ "REGION_ROUTE130/MIRAGE_ISLAND",
"REGION_ROUTE129/MAIN",
"REGION_ROUTE131/MAIN"
],
"warps": []
},
+ "REGION_ROUTE130/MIRAGE_ISLAND": {
+ "parent_map": "MAP_ROUTE130",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": true,
+ "locations": [
+ "BERRY_TREE_82"
+ ],
+ "events": [],
+ "exits": [],
+ "warps": []
+ },
"REGION_ROUTE131/MAIN": {
"parent_map": "MAP_ROUTE131",
- "locations": [],
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
+ "locations": [
+ "TRAINER_KEVIN_REWARD",
+ "TRAINER_TALIA_REWARD",
+ "TRAINER_RICHARD_REWARD",
+ "TRAINER_KARA_REWARD",
+ "TRAINER_HERMAN_REWARD",
+ "TRAINER_SUSIE_REWARD",
+ "TRAINER_RELI_AND_IAN_REWARD"
+ ],
"events": [],
"exits": [
"REGION_PACIFIDLOG_TOWN/MAIN",
@@ -1821,6 +3204,9 @@
},
"REGION_ROUTE132/EAST": {
"parent_map": "MAP_ROUTE132",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -1831,9 +3217,20 @@
},
"REGION_ROUTE132/WEST": {
"parent_map": "MAP_ROUTE132",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_132_RARE_CANDY",
- "ITEM_ROUTE_132_PROTEIN"
+ "ITEM_ROUTE_132_PROTEIN",
+ "TRAINER_GILBERT_REWARD",
+ "TRAINER_RONALD_REWARD",
+ "TRAINER_DARCY_REWARD",
+ "TRAINER_PAXTON_REWARD",
+ "TRAINER_JONATHAN_REWARD",
+ "TRAINER_MAKAYLA_REWARD",
+ "TRAINER_KIYO_REWARD",
+ "TRAINER_DANA_REWARD"
],
"events": [],
"exits": [
@@ -1843,10 +3240,20 @@
},
"REGION_ROUTE133/MAIN": {
"parent_map": "MAP_ROUTE133",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_133_BIG_PEARL",
"ITEM_ROUTE_133_STAR_PIECE",
- "ITEM_ROUTE_133_MAX_REVIVE"
+ "ITEM_ROUTE_133_MAX_REVIVE",
+ "TRAINER_LINDA_REWARD",
+ "TRAINER_FRANKLIN_REWARD",
+ "TRAINER_DEBRA_REWARD",
+ "TRAINER_MOLLIE_REWARD",
+ "TRAINER_CONOR_REWARD",
+ "TRAINER_WARREN_REWARD",
+ "TRAINER_BECK_REWARD"
],
"events": [],
"exits": [
@@ -1856,9 +3263,21 @@
},
"REGION_ROUTE134/MAIN": {
"parent_map": "MAP_ROUTE134",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [
"ITEM_ROUTE_134_CARBOS",
- "ITEM_ROUTE_134_STAR_PIECE"
+ "ITEM_ROUTE_134_STAR_PIECE",
+ "TRAINER_ALEX_REWARD",
+ "TRAINER_KELVIN_REWARD",
+ "TRAINER_HUDSON_REWARD",
+ "TRAINER_REYNA_REWARD",
+ "TRAINER_HITOSHI_REWARD",
+ "TRAINER_MARLEY_REWARD",
+ "TRAINER_AARON_REWARD",
+ "TRAINER_LAUREL_REWARD",
+ "TRAINER_JACK_REWARD"
],
"events": [],
"exits": [
@@ -1869,6 +3288,9 @@
},
"REGION_ROUTE134/WEST": {
"parent_map": "MAP_ROUTE134",
+ "has_grass": false,
+ "has_water": true,
+ "has_fishing": true,
"locations": [],
"events": [],
"exits": [
@@ -1878,6 +3300,9 @@
},
"REGION_UNDERWATER_ROUTE134/MAIN": {
"parent_map": "MAP_UNDERWATER_ROUTE134",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [
@@ -1887,4 +3312,4 @@
"MAP_UNDERWATER_ROUTE134:0/MAP_UNDERWATER_SEALED_CHAMBER:0"
]
}
-}
\ No newline at end of file
+}
diff --git a/worlds/pokemon_emerald/data/regions/unused/battle_frontier.json b/worlds/pokemon_emerald/data/regions/unused/battle_frontier.json
index 3fdab431c22d..cb3cf02a7f13 100644
--- a/worlds/pokemon_emerald/data/regions/unused/battle_frontier.json
+++ b/worlds/pokemon_emerald/data/regions/unused/battle_frontier.json
@@ -1,168 +1,9 @@
{
- "REGION_BATTLE_FRONTIER_RECEPTION_GATE/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_RECEPTION_GATE",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_RECEPTION_GATE:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:8",
- "MAP_BATTLE_FRONTIER_RECEPTION_GATE:1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:9"
- ]
- },
- "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/DOCK": {
- "parent_map": "MAP_BATTLE_FRONTIER_OUTSIDE_WEST",
- "locations": [],
- "events": [],
- "exits": [
- "REGION_SLATEPORT_CITY_HARBOR/MAIN",
- "REGION_LILYCOVE_CITY_HARBOR/MAIN"
- ],
- "warps": [
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:8/MAP_BATTLE_FRONTIER_RECEPTION_GATE:0"
- ]
- },
- "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_OUTSIDE_WEST",
- "locations": [],
- "events": [],
- "exits": [
- "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/MAIN"
- ],
- "warps": [
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:7/MAP_BATTLE_FRONTIER_LOUNGE7:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:2/MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1/MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:9/MAP_BATTLE_FRONTIER_RECEPTION_GATE:1",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:0/MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:5/MAP_BATTLE_FRONTIER_SCOTTS_HOUSE:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:3/MAP_BATTLE_FRONTIER_LOUNGE2:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:4/MAP_BATTLE_FRONTIER_MART:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:6/MAP_BATTLE_FRONTIER_LOUNGE4:0"
- ]
- },
- "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/WATER": {
- "parent_map": "MAP_BATTLE_FRONTIER_OUTSIDE_WEST",
- "locations": [],
- "events": [],
- "exits": [
- "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/WATER",
- "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/CAVE_ENTRANCE"
- ],
- "warps": []
- },
- "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/CAVE_ENTRANCE": {
- "parent_map": "MAP_BATTLE_FRONTIER_OUTSIDE_WEST",
- "locations": [],
- "events": [],
- "exits": [
- "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/WATER"
- ],
- "warps": [
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:10/MAP_ARTISAN_CAVE_B1F:0"
- ]
- },
- "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_OUTSIDE_EAST",
- "locations": [],
- "events": [],
- "exits": [
- "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/MAIN",
- "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/ABOVE_WATERFALL"
- ],
- "warps": [
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:12/MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:5/MAP_BATTLE_FRONTIER_LOUNGE1:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:8/MAP_BATTLE_FRONTIER_LOUNGE6:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:6/MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:10/MAP_BATTLE_FRONTIER_LOUNGE8:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:0/MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:11/MAP_BATTLE_FRONTIER_LOUNGE9:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:7/MAP_BATTLE_FRONTIER_LOUNGE5:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:4/MAP_BATTLE_FRONTIER_RANKING_HALL:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:1/MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:3/MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:9/MAP_BATTLE_FRONTIER_LOUNGE3:0"
- ]
- },
- "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/CAVE_ENTRANCE": {
- "parent_map": "MAP_BATTLE_FRONTIER_OUTSIDE_EAST",
- "locations": [],
- "events": [],
- "exits": [
- "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/MAIN"
- ],
- "warps": [
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:13/MAP_ARTISAN_CAVE_1F:0"
- ]
- },
- "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/ABOVE_WATERFALL": {
- "parent_map": "MAP_BATTLE_FRONTIER_OUTSIDE_EAST",
- "locations": [],
- "events": [],
- "exits": [
- "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/MAIN",
- "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/WATER"
- ],
- "warps": []
- },
- "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/WATER": {
- "parent_map": "MAP_BATTLE_FRONTIER_OUTSIDE_EAST",
- "locations": [],
- "events": [],
- "exits": [
- "REGION_BATTLE_FRONTIER_OUTSIDE_EAST/ABOVE_WATERFALL",
- "REGION_BATTLE_FRONTIER_OUTSIDE_WEST/WATER"
- ],
- "warps": []
- },
- "REGION_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:2"
- ]
- },
- "REGION_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY:0,1,2/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:0"
- ]
- },
- "REGION_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:1"
- ]
- },
- "REGION_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:3"
- ]
- },
- "REGION_BATTLE_FRONTIER_BATTLE_DOME_LOBBY/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1"
- ]
- },
"REGION_BATTLE_FRONTIER_BATTLE_DOME_PRE_BATTLE_ROOM/MAIN": {
"parent_map": "MAP_BATTLE_FRONTIER_BATTLE_DOME_PRE_BATTLE_ROOM",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -172,6 +13,9 @@
},
"REGION_BATTLE_FRONTIER_BATTLE_DOME_CORRIDOR/MAIN": {
"parent_map": "MAP_BATTLE_FRONTIER_BATTLE_DOME_CORRIDOR",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -179,18 +23,11 @@
"MAP_BATTLE_FRONTIER_BATTLE_DOME_CORRIDOR:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1!"
]
},
- "REGION_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:0",
- "MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:2"
- ]
- },
"REGION_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR/MAIN": {
"parent_map": "MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
@@ -202,195 +39,14 @@
},
"REGION_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM/MAIN": {
"parent_map": "MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM",
+ "has_grass": false,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
"warps": [
"MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2"
]
- },
- "REGION_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:2/MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM:0",
- "MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:0"
- ]
- },
- "REGION_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:2"
- ]
- },
- "REGION_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER:0,1,2/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:6"
- ]
- },
- "REGION_BATTLE_FRONTIER_RANKING_HALL/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_RANKING_HALL",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_RANKING_HALL:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:4"
- ]
- },
- "REGION_BATTLE_FRONTIER_POKEMON_CENTER_1F/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:2/MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:0",
- "MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:12"
- ]
- },
- "REGION_BATTLE_FRONTIER_POKEMON_CENTER_2F/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:0/MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:2"
- ]
- },
- "REGION_BATTLE_FRONTIER_MART/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_MART",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_MART:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:4"
- ]
- },
- "REGION_BATTLE_FRONTIER_SCOTTS_HOUSE/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_SCOTTS_HOUSE",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_SCOTTS_HOUSE:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:5"
- ]
- },
- "REGION_BATTLE_FRONTIER_LOUNGE1/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE1",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_LOUNGE1:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:5"
- ]
- },
- "REGION_BATTLE_FRONTIER_LOUNGE2/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE2",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_LOUNGE2:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:3"
- ]
- },
- "REGION_BATTLE_FRONTIER_LOUNGE3/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE3",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_LOUNGE3:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:9"
- ]
- },
- "REGION_BATTLE_FRONTIER_LOUNGE4/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE4",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_LOUNGE4:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:6"
- ]
- },
- "REGION_BATTLE_FRONTIER_LOUNGE5/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE5",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_LOUNGE5:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:7"
- ]
- },
- "REGION_BATTLE_FRONTIER_LOUNGE6/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE6",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_LOUNGE6:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:8"
- ]
- },
- "REGION_BATTLE_FRONTIER_LOUNGE7/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE7",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_LOUNGE7:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:7"
- ]
- },
- "REGION_BATTLE_FRONTIER_LOUNGE8/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE8",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_LOUNGE8:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:10"
- ]
- },
- "REGION_BATTLE_FRONTIER_LOUNGE9/MAIN": {
- "parent_map": "MAP_BATTLE_FRONTIER_LOUNGE9",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_BATTLE_FRONTIER_LOUNGE9:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:11"
- ]
- },
-
- "REGION_ARTISAN_CAVE_1F/MAIN": {
- "parent_map": "MAP_ARTISAN_CAVE_1F",
- "locations": [
- "ITEM_ARTISAN_CAVE_1F_CARBOS"
- ],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_ARTISAN_CAVE_1F:1/MAP_ARTISAN_CAVE_B1F:1",
- "MAP_ARTISAN_CAVE_1F:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:13"
- ]
- },
- "REGION_ARTISAN_CAVE_B1F/MAIN": {
- "parent_map": "MAP_ARTISAN_CAVE_B1F",
- "locations": [
- "ITEM_ARTISAN_CAVE_B1F_HP_UP",
- "HIDDEN_ITEM_ARTISAN_CAVE_B1F_ZINC",
- "HIDDEN_ITEM_ARTISAN_CAVE_B1F_CALCIUM",
- "HIDDEN_ITEM_ARTISAN_CAVE_B1F_PROTEIN",
- "HIDDEN_ITEM_ARTISAN_CAVE_B1F_IRON"
- ],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_ARTISAN_CAVE_B1F:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:10",
- "MAP_ARTISAN_CAVE_B1F:1/MAP_ARTISAN_CAVE_1F:1"
- ]
}
}
diff --git a/worlds/pokemon_emerald/data/regions/unused/dungeons.json b/worlds/pokemon_emerald/data/regions/unused/dungeons.json
index c176de1b33a9..8d15d9bb2849 100644
--- a/worlds/pokemon_emerald/data/regions/unused/dungeons.json
+++ b/worlds/pokemon_emerald/data/regions/unused/dungeons.json
@@ -1,52 +1,14 @@
{
- "REGION_TERRA_CAVE_ENTRANCE/MAIN": {
- "parent_map": "MAP_TERRA_CAVE_ENTRANCE",
+ "REGION_ALTERING_CAVE/MAIN": {
+ "parent_map": "MAP_ALTERING_CAVE",
+ "has_grass": true,
+ "has_water": false,
+ "has_fishing": false,
"locations": [],
"events": [],
"exits": [],
"warps": [
- "MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!",
- "MAP_TERRA_CAVE_ENTRANCE:1/MAP_TERRA_CAVE_END:0"
- ]
- },
- "REGION_TERRA_CAVE_END/MAIN": {
- "parent_map": "MAP_TERRA_CAVE_END",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_TERRA_CAVE_END:0/MAP_TERRA_CAVE_ENTRANCE:1"
- ]
- },
- "REGION_UNDERWATER_MARINE_CAVE/MAIN": {
- "parent_map": "MAP_UNDERWATER_MARINE_CAVE",
- "locations": [],
- "events": [],
- "exits": [
- "REGION_MARINE_CAVE_ENTRANCE/MAIN"
- ],
- "warps": [
- "MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!"
- ]
- },
- "REGION_MARINE_CAVE_ENTRANCE/MAIN": {
- "parent_map": "MAP_MARINE_CAVE_ENTRANCE",
- "locations": [],
- "events": [],
- "exits": [
- "REGION_UNDERWATER_MARINE_CAVE/MAIN"
- ],
- "warps": [
- "MAP_MARINE_CAVE_ENTRANCE:0/MAP_MARINE_CAVE_END:0"
- ]
- },
- "REGION_MARINE_CAVE_END/MAIN": {
- "parent_map": "MAP_MARINE_CAVE_END",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_MARINE_CAVE_END:0/MAP_MARINE_CAVE_ENTRANCE:0"
+ "MAP_ALTERING_CAVE:0/MAP_ROUTE103:0"
]
}
}
diff --git a/worlds/pokemon_emerald/data/regions/unused/routes.json b/worlds/pokemon_emerald/data/regions/unused/routes.json
deleted file mode 100644
index 47cfc4541572..000000000000
--- a/worlds/pokemon_emerald/data/regions/unused/routes.json
+++ /dev/null
@@ -1,82 +0,0 @@
-{
- "REGION_ROUTE110_TRICK_HOUSE_PUZZLE2/MAIN": {
- "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE2",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE2:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE2:2/MAP_ROUTE110_TRICK_HOUSE_END:0!"
- ]
- },
- "REGION_ROUTE110_TRICK_HOUSE_PUZZLE3/MAIN": {
- "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE3",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE3:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE3:2/MAP_ROUTE110_TRICK_HOUSE_END:0!"
- ]
- },
- "REGION_ROUTE110_TRICK_HOUSE_PUZZLE4/MAIN": {
- "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE4",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE4:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE4:2/MAP_ROUTE110_TRICK_HOUSE_END:0!"
- ]
- },
- "REGION_ROUTE110_TRICK_HOUSE_PUZZLE5/MAIN": {
- "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE5",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE5:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE5:2/MAP_ROUTE110_TRICK_HOUSE_END:0!"
- ]
- },
- "REGION_ROUTE110_TRICK_HOUSE_PUZZLE6/MAIN": {
- "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE6",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE6:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE6:2/MAP_ROUTE110_TRICK_HOUSE_END:0!"
- ]
- },
- "REGION_ROUTE110_TRICK_HOUSE_PUZZLE7/MAIN": {
- "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:8/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:7",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:7/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:8",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:11/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:12",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:9/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:10",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:6/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:5",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:10/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:9",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:4/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:3",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:12/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:11",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:2/MAP_ROUTE110_TRICK_HOUSE_END:0!",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:3/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:4",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:5/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:6"
- ]
- },
- "REGION_ROUTE110_TRICK_HOUSE_PUZZLE8/MAIN": {
- "parent_map": "MAP_ROUTE110_TRICK_HOUSE_PUZZLE8",
- "locations": [],
- "events": [],
- "exits": [],
- "warps": [
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE8:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!",
- "MAP_ROUTE110_TRICK_HOUSE_PUZZLE8:2/MAP_ROUTE110_TRICK_HOUSE_END:0!"
- ]
- }
-}
diff --git a/worlds/pokemon_emerald/data/trade_pokemon_schema.json b/worlds/pokemon_emerald/data/trade_pokemon_schema.json
new file mode 100644
index 000000000000..c261c5b08fc9
--- /dev/null
+++ b/worlds/pokemon_emerald/data/trade_pokemon_schema.json
@@ -0,0 +1,162 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Pokemon",
+ "type": "object",
+ "required": ["version", "language", "nickname", "personality", "species", "experience", "ivs", "evs", "moves", "trainer"],
+ "properties": {
+ "version": {
+ "description": "The version of this schema that the data is formatted to match",
+ "type": "string",
+ "const": "1"
+ },
+ "language": {
+ "description": "The language of origin",
+ "enum": [
+ "Japanese",
+ "English",
+ "French",
+ "Italian",
+ "German",
+ "Spanish"
+ ]
+ },
+ "nickname": {
+ "description": "The pokemon's nickname",
+ "type": "string",
+ "minLength": 1
+ },
+ "personality": {
+ "description": "The pokemon's 32-bit personality value",
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 4294967295
+ },
+ "species": {
+ "description": "The national dex number of the pokemon species",
+ "type": "integer",
+ "minimum": 0
+ },
+ "item": {
+ "description": "The id of the item the pokemon is holding according to modern tables",
+ "type": "integer"
+ },
+ "experience": {
+ "description": "The current total EXP",
+ "type": "integer",
+ "minimum": 0
+ },
+ "ability": {
+ "description": "The value of the ability bit (hidden abilities should be a separate bit)",
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 1
+ },
+ "ivs": {
+ "description": "The 6 IVs of the pokemon",
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 31
+ },
+ "minItems": 6,
+ "maxItems": 6
+ },
+ "evs": {
+ "description": "The 6 EVs of the pokemon",
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 255
+ },
+ "minItems": 6,
+ "maxItems": 6
+ },
+ "conditions": {
+ "description": "The 6 condition (contest) stats of the pokemon",
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 255
+ },
+ "minItems": 6,
+ "maxItems": 6
+ },
+ "pokerus": {
+ "description": "The value of the pokerus status byte",
+ "type": "integer",
+ "minimum": 0
+ },
+ "game": {
+ "description": "The id of the game the pokemon originated in",
+ "type": "integer",
+ "minimum": 0
+ },
+ "location_met": {
+ "description": "The location id for where the pokemon was met",
+ "type": "integer",
+ "minimum": 0
+ },
+ "level_met": {
+ "description": "The level the pokemon was met at",
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 100
+ },
+ "ball": {
+ "description": "The type of poke ball the pokemon was caught in",
+ "type": "integer",
+ "minimum": 1
+ },
+ "moves": {
+ "description": "The move id, PP, and PP Ups used for each move slot",
+ "type": "array",
+ "items": {
+ "type": "array",
+ "prefixItems": [
+ {
+ "description": "The move's id according to modern tables (use 0 for an empty slot)",
+ "type": "integer"
+ },
+ {
+ "description": "The move's max PP",
+ "type": "integer",
+ "minimum": 1
+ },
+ {
+ "description": "The number of times a PP Up has been used on this move",
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 3
+ }
+ ],
+ "minLength": 4,
+ "maxLength": 4
+ }
+ },
+ "trainer": {
+ "description": "Original trainer info",
+ "type": "object",
+ "properties": {
+ "name": {
+ "description": "The original trainer's name",
+ "type": "string",
+ "minLength": 1
+ },
+ "id": {
+ "description": "The original trainer's 32-bit ID (includes secret id as higher order bytes)",
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 4294967295
+ },
+ "female": {
+ "description": "Whether the original trainer is female",
+ "type": "boolean"
+ }
+ },
+ "required": ["name", "id"]
+ }
+ }
+}
\ No newline at end of file
diff --git a/worlds/pokemon_emerald/docs/en_Pokemon Emerald.md b/worlds/pokemon_emerald/docs/en_Pokemon Emerald.md
index 8b09b51b38d3..9a3991e97f75 100644
--- a/worlds/pokemon_emerald/docs/en_Pokemon Emerald.md
+++ b/worlds/pokemon_emerald/docs/en_Pokemon Emerald.md
@@ -38,8 +38,8 @@ Except for badges, your starting inventory will be in the PC.
## What does another world's item look like in Pokémon Emerald?
-When you find an item that is not your own, you will instead receive an "ARCHIPELAGO ITEM" which will *not* be added to
-your inventory.
+When you find an item that is not your own, you will see the item's name and its owner while the item received jingle
+plays.
## When the player receives an item, what happens?
@@ -55,9 +55,9 @@ you're playing a multiworld game, the client will sync your game with the server
## Will battle mechanics be updated?
-This is something we'd love to see, but it's unlikely. We don't want to force new mechanics on players who would prefer
-to play with the classic mechanics, but trying to switch between old and new mechanics based on an option would be a
-monumental task, and is probably best solved some other way.
+Unfortunately, no. We don't want to force new mechanics on players who would prefer to play with the classic mechanics,
+but updating would require such drastic changes to the underlying code that it would be unreasonable to toggle between
+them.
## Is this randomizer compatible with other mods?
@@ -68,11 +68,21 @@ suggestion or contribute.
## Can I use tools like the Universal Pokémon Randomizer?
-No, those tools expect data to be in certain locations and in a certain format, but this randomizer has to shift it
+No, tools like UPR expect data to be in certain locations and in a certain format, but this randomizer has to shift it
around. Using tools to try to modify the game would only corrupt the ROM.
We realize this means breaking from established habits when it comes to randomizing Pokémon games, but this randomizer
would be many times more complex to develop if it were constrained by something like UPR.
-The one exception might be PKHeX. You may be able to extract pokémon from your save using PKHeX, but this isn't a
-guarantee, and we make no effort to keep our saves compatible with PKHeX.
+### There are two possible exceptions
+
+#### PKHex
+
+You may be able to extract pokémon from your save using PKHeX, but this isn't a guarantee, and we make no effort to keep
+our saves compatible with PKHeX. Box and party pokémon are the only aspects of your save file likely to work.
+
+#### PokéFinder/RNG Reporter
+
+In the spirit of randomization, Emerald's broken RNG is fixed in Archipelago. More specifically, it's reverted to work
+as it did in Ruby/Sapphire. So while you can't make the assumption that the RNG is seeded at 0, you can set the battery
+to dry, which will seed it in the same way that Ruby/Sapphire are seeded when the battery is dry.
diff --git a/worlds/pokemon_emerald/data/README.md b/worlds/pokemon_emerald/docs/region data.md
similarity index 74%
rename from worlds/pokemon_emerald/data/README.md
rename to worlds/pokemon_emerald/docs/region data.md
index a7c5d3f2932d..767b5cac2c97 100644
--- a/worlds/pokemon_emerald/data/README.md
+++ b/worlds/pokemon_emerald/docs/region data.md
@@ -1,8 +1,7 @@
-## `regions/`
+## Region Data
-These define regions, connections, and where locations are. If you know what you're doing, it should be pretty clear how
-this works by taking a quick look through the files. The rest of this section is pretty verbose to cover everything. Not
-to say you shouldn't read it, but the tl;dr is:
+Regions, connections, and associated locations are defined in `data/regions`. If you know what you're doing, it should
+be pretty clear how the data works by taking a quick look through the files. But the quick tl;dr is:
- Every map, even trivial ones, gets a region definition, and they cannot be coalesced (because of warp rando)
- Stick to the naming convention for regions and events (look at Route 103 and Petalburg City for guidance)
@@ -12,7 +11,7 @@ to say you shouldn't read it, but the tl;dr is:
A `Map`, which you will see referenced in `parent_map` attribute in the region JSON, is an id from the source code.
`Map`s are sets of tiles, encounters, warps, events, and so on. Route 103, Littleroot Town, the Oldale Town Mart, the
second floor of Devon Corp, and each level of Victory Road are all examples of `Map`s. You transition between `Map`s by
-stepping on a warp (warp pads, doorways, etc...) or walking over a border between `Map`s in the overworld. Some warps
+stepping on a warp (warp pads, doorways, etc.) or walking over a border between `Map`s in the overworld. Some warps
don't go to a different `Map`.
Regions usually describe physical areas which are subsets of a `Map`. Every `Map` must have one or more defined regions.
@@ -25,9 +24,9 @@ example is demonstrative). Keeping the name consistent with the `Map` name and a
makes it clearer where we are in the world and where within a `Map` we're describing.
Every region (except `Menu`) is configured here. All files in this directory are combined with each other at runtime,
-and are only split and ordered for organization. Regions defined in `data/regions/unused` are entirely unused because
-they're not yet reachable in the randomizer. They're there for future reference in case we want to pull those maps in
-later. Any locations or warps in here should be ignored. Data for a single region looks like this:
+and are only split and ordered for organization. Regions defined in `data/regions/unused` are remnants from
+automatically generated regions and represent places that exist but aren't reachable or aren't currently relevant to the
+randomizer. Any locations or warps in there should be ignored. Data for a single region looks like this:
```json
"REGION_ROUTE103/EAST": {
@@ -60,9 +59,9 @@ can trigger story progression and unblock roads and buildings. Events are define
rules are set in `rules.py`.
- `exits`: Names of regions that can be directly accessed from this one. Most often regions within the same `Map`,
neighboring maps in the overworld, or transitions from using HM08 Dive. Most connections between maps/regions come from
-warps. Any region in this list should be defined somewhere in `data/regions`.
+warps. Any region in this list should be defined somewhere in `data/regions/`.
- `warps`: Warp events contained within this region. Warps are defined in `data/extracted_data.json`, and must exist
-there to be referenced here. More on warps in [../README.md](../README.md).
+there to be referenced here. More on warps in [../docs/warps.md](../docs/warps.md).
Think of this data as defining which regions are "claiming" a given location, event, or warp. No more than one region
may claim ownership of a location. Even if some "thing" may happen in two different regions and set the same flag, they
@@ -78,22 +77,3 @@ especially remember to rename incoming `exits` defined in other regions which ar
region. `sanity_check.py` should catch you if there are other regions that point to a region that no longer exists, but
if one of your newly-split regions still has the same name as the original, it won't be detected and you may find that
things aren't connected correctly.
-
-## `extracted_data.json`
-
-DO NOT TOUCH
-
-Contains data automatically pulled from the base rom and its source code when it is built. There should be no reason to
-manually modify it. Data from this file is piped through `data.py` to create a data object that's more useful and
-complete.
-
-## `items.json`
-
-A map from items as defined in the `constants` in `extracted_data.json` to useful info like a human-friendly label, the
-type of progression it enables, and tags to associate. There are many unused items and extra helper constants in
-`extracted_data.json`, so this file contains an exhaustive list of items which can actually be found in the modded game.
-
-## `locations.json`
-
-Similar to `items.json`, this associates locations with human-friendly labels and tags that are used for filtering. Any
-locations claimed by any region need an entry here.
diff --git a/worlds/pokemon_emerald/docs/rom changes.md b/worlds/pokemon_emerald/docs/rom changes.md
new file mode 100644
index 000000000000..6dec685f70d2
--- /dev/null
+++ b/worlds/pokemon_emerald/docs/rom changes.md
@@ -0,0 +1,107 @@
+## New Behaviors
+
+- The union room receptionist on the second floor of Pokemon Centers was reworked for wonder trading via Archipelago
+- Norman will give you all event ticket items when he gives you the S.S. Ticket
+- Use of event tickets is streamlined and the scripts are refactored to skip "first time use" stuff
+- The roaming pokemon is forced to Latios
+- The pokemon at Southern Island is forced to Latias
+- There is new code for changing your party's levels during trainer battles which also modifies exp gain
+
+## QoL
+
+- The menu has a GO HOME option instead of EXIT, which will immediately teleport you to Birch's Lab
+- It is possible to teach over HM moves
+- The catch tutorial and cutscenes during your first visit to Petalburg are skipped
+- The match call tutorial after you leave Devon Corp is skipped
+- Random match calls in general are skipped, and trainers no longer ask to register you after a battle
+- Searching by type in the pokedex includes species you have seen but not yet caught
+- Cycling and running is allowed in every map (some exceptions like Fortree and Pacifidlog)
+- When you run out of Repel steps, you'll be prompted to use another one if you have more in your bag
+- Text is always rendered in its entirety on the first frame (instant text)
+- With an option set, text will advance if A is held
+- The message explaining that the trainer is about to send out a new pokemon is shortened to fit on two lines so that
+you can still read the species when deciding whether to change pokemon
+- The Pokemon Center Nurse dialogue is entirely removed except for the final text box
+- When receiving TMs and HMs, the move that it teaches is consistently displayed in the "received item" message (by
+default, certain ways of receiving items would only display the TM/HM number)
+- The Pokedex starts in national mode
+- The fishing minigame is always successful at finding a catch, only requires one round, and will always show four dots
+- With an option in Archipelago, spinning trainers become predictable
+- Removed a ledge on Route 123 which allows you to collect every item without backtracking
+- The Oldale Pokemart sells Poke Balls at the start of the game
+- Pauses during battles (e.g. the ~1 second pause at the start of a turn before an opponent uses a potion) are shorter
+by 62.5%
+- The sliding animation for trainers and wild pokemon at the start of a battle runs at double speed.
+- Bag space was greatly expanded (there is room for one stack of every unique item in every pocket, plus a little bit
+extra for some pockets)
+ - Save data format was changed as a result of this. Shrank some unused space and removed some multiplayer phrases from
+ the save data.
+ - Pretty much any code that checks for bag space is ignored or bypassed (this sounds dangerous, but with expanded bag
+ space you should pretty much never have a full bag unless you're trying to fill it up, and skipping those checks
+ greatly simplifies detecting when items are picked up)
+- Pokemon are never disobedient
+- When moving in the overworld, set the input priority based on the most recently pressed direction rather than by some
+predetermined priority
+- Shoal cave changes state every time you reload the map and is no longer tied to the RTC
+- Increased safari zone steps from 500 to 50000
+- Trainers will not approach the player if the blind trainers option is set
+- Defeating the elite 4 respawns all legendary encounters where the encounter ended by fainting the pokemon
+- The cutscene revealing the existence of Latios also gives you dex info for having seen Latios
+- The braille wall hinting at the solution to the Wailord/Relicanth puzzle gives you dex info for having seen Wailord
+and Relicanth
+- Changed trade evolutions to be possible without trading:
+ - Politoed: Use King's Rock in bag menu
+ - Alakazam: Level 37
+ - Machamp: Level 37
+ - Golem: Level 37
+ - Slowking: Use King's Rock in bag menu
+ - Gengar: Level 37
+ - Steelix: Use Metal Coat in bag menu
+ - Kingdra: Use Dragon Scale in bag menu
+ - Scizor: Use Metal Coat in bag menu
+ - Porygon2: Use Up-Grade in bag menu
+ - Milotic: Level 30
+ - Huntail: Use Deep Sea Tooth in bag menu
+ - Gorebyss: Use Deep Sea Scale in bag menu
+
+## Game State Changes/Softlock Prevention
+
+- Mr. Briney never disappears or stops letting you use his ferry
+- Upon releasing Kyogre, Sootopolis and Sky Pillar will be advanced to after Rayquaza has been awakened, skipping the
+Wallace and Rayquaza fetch quest
+- Prevent the player from flying or surfing until they have received the Pokedex
+- The S.S. Tidal will be available at all times
+- All time-based berry gifts are locked to a one-time gift of a specific berry
+- Terra and Marine Cave are given fixed locations, and the weather events revealing them are permanent until the
+legendary encounter is resolved
+- Mirage Island is always present
+- During dexsanity, certain trainers don't disappear/deactivate
+- During berry randomization, it is impossible to plant berries or for berry trees to change state
+- Some NPCs or tiles are removed on the creation of a new save file based on player options
+- Ensured that every species has some damaging move by level 5
+- Route 115 has an alternate layout (must be enabled through Archipelago) which includes a bumpy slope that can cross
+the ledge normally blocking you from entering Meteor Falls from Rustboro City
+- Route 115 may have strength boulders (must be enabled through Archipelago) between the beach and cave entrance
+- Route 118 has an alternate layout (must be enabled through Archipelago) that blocks you from surfing between shores
+and adds a rail so that it can be crossed using the Acro Bike
+- The Petalburg Gym is set up based on your player options rather than after the first 4 gyms
+- The E4 guards will actually check all your badges (or gyms beaten based on your options) instead of just the Feather
+Badge
+- Steven cuts the conversation short in Granite Cave if you don't have the Letter
+- Dock checks that you have the Devon Goods before asking you to deliver them (and thus opening the museum)
+- Rydel gives you both bikes at the same time
+- The man in Pacifidlog who gives you Frustration and Return will give you both at the same time, does not check
+friendship first, and no longer has any behavior related to the RTC
+- The woman who gives you the Soothe Bell in Slateport does not check friendship
+- When trading the Scanner with Captain Stern, you will receive both the Deep Sea Tooth and Deep Sea Scale
+
+## Misc
+
+- You can no longer try to switch bikes in the bike shop
+- The Seashore House only rewards you with 1 Soda Pop instead of 6
+- Many small changes that make it possible to swap single battles to double battles
+ - Includes some safeguards against two trainers seeing you and initiating a battle while one or both of them are
+ "single trainer double battles"
+- Game now properly waits on vblank instead of spinning in a while loop
+- Misc small changes to text for consistency
+- Many bugfixes to the vanilla game code
diff --git a/worlds/pokemon_emerald/docs/setup_es.md b/worlds/pokemon_emerald/docs/setup_es.md
new file mode 100644
index 000000000000..65a74a9ddc70
--- /dev/null
+++ b/worlds/pokemon_emerald/docs/setup_es.md
@@ -0,0 +1,74 @@
+# Guía de Configuración para Pokémon Emerald
+
+## Software Requerido
+
+- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases)
+- Una ROM de Pokémon Emerald en Inglés. La comunidad de Archipelago no puede proveerla.
+- [BizHawk](https://tasvideos.org/BizHawk/ReleaseHistory) 2.7 o posterior
+
+### Configuración de BizHawk
+
+Una vez que hayas instalado BizHawk, abre `EmuHawk.exe` y cambia las siguientes configuraciones:
+
+- Si estás usando BizHawk 2.7 o 2.8, ve a `Config > Customize`. En la pestaña Advanced, cambia el Lua Core de
+`NLua+KopiLua` a `Lua+LuaInterface`, luego reinicia EmuHawk. (Si estás usando BizHawk 2.9, puedes saltar este paso.)
+- En `Config > Customize`, activa la opción "Run in background" para prevenir desconexiones del cliente mientras
+la aplicación activa no sea EmuHawk.
+- Abre el archivo `.gba` en EmuHawk y luego ve a `Config > Controllers…` para configurar los controles. Si no puedes
+hacer clic en `Controllers…`, debes abrir cualquier ROM `.gba` primeramente.
+- Considera limpiar tus macros y atajos en `Config > Hotkeys…` si no quieres usarlas de manera intencional. Para
+limpiarlas, selecciona el atajo y presiona la tecla Esc.
+
+## Software Opcional
+
+- [Pokémon Emerald AP Tracker](https://github.com/AliceMousie/emerald-ap-tracker/releases/latest), para usar con
+[PopTracker](https://github.com/black-sliver/PopTracker/releases)
+
+## Generando y Parcheando el Juego
+
+1. Crea tu archivo de configuración (YAML). Puedes hacerlo en
+[Página de Opciones de Pokémon Emerald](../../../games/Pokemon%20Emerald/player-options).
+2. Sigue las instrucciones generales de Archipelago para [Generar un juego]
+(../../Archipelago/setup/en#generating-a-game). Esto generará un archivo de salida (output file) para ti. Tu archivo
+de parche tendrá la extensión de archivo`.apemerald`.
+3. Abre `ArchipelagoLauncher.exe`
+4. Selecciona "Open Patch" en el lado derecho y elige tu archivo de parcheo.
+5. Si esta es la primera vez que vas a parchear, se te pedirá que selecciones la ROM sin parchear.
+6. Un archivo parcheado con extensión `.gba` será creado en el mismo lugar que el archivo de parcheo.
+7. La primera vez que abras un archivo parcheado con el BizHawk Client, se te preguntará donde está localizado
+`EmuHawk.exe` en tu instalación de BizHawk.
+
+Si estás jugando una seed Single-Player y no te interesa el auto-tracking o las pistas, puedes parar aquí, cierra el
+cliente, y carga la ROM ya parcheada en cualquier emulador. Pero para partidas multi-worlds y para otras
+implementaciones de Archipelago, continúa usando BizHawk como tu emulador
+
+## Conectando con el Servidor
+
+Por defecto, al abrir un archivo parcheado, se harán de manera automática 1-5 pasos. Aun así, ten en cuenta lo
+siguiente en caso de que debas cerrar y volver a abrir la ventana en mitad de la partida por algún motivo.
+
+1. Pokémon Emerald usa el Archipelago BizHawk Client. Si el cliente no se encuentra abierto al abrir la rom
+parcheada, puedes volver a abrirlo desde el Archipelago Launcher.
+2. Asegúrate que EmuHawk está corriendo la ROM parcheada.
+3. En EmuHawk, ve a `Tools > Lua Console`. Debes tener esta ventana abierta mientras juegas.
+4. En la ventana de Lua Console, ve a `Script > Open Script…`.
+5. Ve a la carpeta donde está instalado Archipelago y abre `data/lua/connector_bizhawk_generic.lua`.
+6. El emulador y el cliente eventualmente se conectarán uno con el otro. La ventana de BizHawk Client indicará que te
+has conectado y reconocerá Pokémon Emerald.
+7. Para conectar el cliente con el servidor, ingresa la dirección y el puerto de la sala (ej. `archipelago.gg:38281`)
+en el campo de texto que se encuentra en la parte superior del cliente y haz click en Connect.
+
+Ahora deberías poder enviar y recibir ítems. Debes seguir estos pasos cada vez que quieras reconectarte. Es seguro
+jugar de manera offline; se sincronizará todo cuando te vuelvas a conectar.
+
+## Tracking Automático
+
+Pokémon Emerald tiene un Map Tracker completamente funcional que soporta auto-tracking.
+
+1. Descarga [Pokémon Emerald AP Tracker](https://github.com/AliceMousie/emerald-ap-tracker/releases/latest) y
+[PopTracker](https://github.com/black-sliver/PopTracker/releases).
+2. Coloca la carpeta del Tracker en la carpeta packs/ dentro de la carpeta de instalación del PopTracker.
+3. Abre PopTracker, y carga el Pack de Pokémon Emerald Map Tracker.
+4. Para utilizar el auto-tracking, haz click en el símbolo "AP" que se encuentra en la parte superior.
+5. Entra la dirección del Servidor de Archipelago (la misma a la que te conectaste para jugar), nombre del jugador, y
+contraseña (deja vacío este campo en caso de no utilizar contraseña).
diff --git a/worlds/pokemon_emerald/docs/warps.md b/worlds/pokemon_emerald/docs/warps.md
new file mode 100644
index 000000000000..671c6aee2179
--- /dev/null
+++ b/worlds/pokemon_emerald/docs/warps.md
@@ -0,0 +1,50 @@
+## Warps
+
+Quick note to start, you should not be defining or modifying encoded warps from this repository. They're encoded in the
+source code repository for the mod, and then assigned to regions in `data/regions/`. All warps in the game already exist
+within `extracted_data.json`, and all relevant warps are already placed in `data/regions/` (unless they were deleted
+accidentally).
+
+Many warps are actually two or three events acting as one logical warp. Doorways, for example, are often 2 tiles wide
+indoors but only 1 tile wide outdoors. Both indoor warps point to the outdoor warp, and the outdoor warp points to only
+one of the indoor warps. We want to describe warps logically in a way that retains information about individual warp
+events. That way a 2-tile-wide doorway doesnt look like a one-way warp next to an unrelated two-way warp, but if we want
+to randomize the destinations of those warps, we can still get back each individual id of the multi-tile warp.
+
+This is how warps are encoded:
+
+`{source_map}:{source_warp_ids}/{dest_map}:{dest_warp_ids}[!]`
+
+- `source_map`: The map the warp events are located in
+- `source_warp_ids`: The ids of all adjacent warp events in source_map which lead to the same destination (these must be
+in ascending order)
+- `dest_map`: The map of the warp event to which this one is connected
+- `dest_warp_ids`: The ids of the warp events in dest_map
+- `[!]`: If the warp expects to lead to a destination which does not lead back to it, add a ! to the end
+
+Example: `MAP_LAVARIDGE_TOWN_HOUSE:0,1/MAP_LAVARIDGE_TOWN:4`
+
+Example 2: `MAP_AQUA_HIDEOUT_B1F:14/MAP_AQUA_HIDEOUT_B1F:12!`
+
+Note: A warp must have its destination set to another warp event. However, that does not guarantee that the destination
+warp event will warp back to the source.
+
+Note 2: Some warps _only_ act as destinations and cannot actually be interacted with by the player as sources. These are
+usually places you fall from a hole above. At the time of writing, these are actually not accounted for, but there are
+no instances where it changes logical access.
+
+Note 3: Some warp destinations go to the map `MAP_DYNAMIC` and have a special warp id. These edge cases are:
+
+- The Moving Truck
+- Terra Cave
+- Marine Cave
+- The Department Store Elevator
+- Secret Bases
+- The Trade Center
+- The Union Room
+- The Record Corner
+- 2P/4P Battle Colosseum
+
+Note 4: The trick house on Route 110 changes the warp destinations of its entrance and ending room as you progress
+through the puzzles, but the source code only sets the trick house up for the first puzzle, and I assume the destination
+gets overwritten at run time when certain flags are set.
diff --git a/worlds/pokemon_emerald/docs/wonder trades.md b/worlds/pokemon_emerald/docs/wonder trades.md
new file mode 100644
index 000000000000..1187e9edb627
--- /dev/null
+++ b/worlds/pokemon_emerald/docs/wonder trades.md
@@ -0,0 +1,103 @@
+# Wonder Trades
+
+Pokemon Emerald uses Archipelago's data storage to reproduce what the Pokemon series calls wonder trading. Wonder
+trading is meant as a sort of gacha game surprise trade where you give up one of your pokemon and at some point in the
+future you'll receive one in return from another player who decided to participate. In practice, small groups will be
+able to use it as a means of simple trading as well by coordinating when they participate.
+
+The goal of the implementation used by Pokemon Emerald is to allow players to interact with an NPC in-game to deposit
+and withdraw pokemon without having to touch their client. The client will automatically detect their state, look for
+available trades, and notify the player when they've received something.
+
+It's also intended to work for Pokemon games other than Emerald, should any other games decide to opt in and implement
+the feature into their clients.
+
+## Data Storage Format
+
+There is one wonder trade entry per team at `pokemon_wonder_trades_{team number}`.
+
+It should be a dict that looks something like this:
+
+```json
+{
+ "_lock": 0,
+ "0": [3, "{some json data}"],
+ "3": [2, "{some json data}"]
+}
+```
+
+### Lock
+
+`_lock` tells you whether you're allowed to try to modify the key. Its value should be either `0` to represent an
+unlocked state, or a timestamp represented by time since Epoch in ms (`int(time.time_ns() / 1000000)`).
+[See below](#preventing-race-conditions) for more info.
+
+### Non-lock Keys
+
+All other keys are just non-negative integers as strings. You can think of them as wonder trade slots. Pidgeon holes
+with a label. For consistency and ease of use, keep the keys between 0 and 255, and prefer the lowest number you can
+use. They ONLY act as names that can be easily written to and removed from.
+- You SHOULD NOT rely on those numbers being contiguous or starting at 0.
+- You SHOULD NOT rely on a "trade" residing at a single slot until it is removed.
+- You SHOULD NOT assume that the number has any significance to a player's slot, or trade order, or anything really.
+
+### Values
+
+The first entry in the tuple represents which slot put the pokemon up for trade. You could use this to display in your
+game or client who the trade came from, but its primary purpose is to discriminate entries you can take from those you
+can't. You don't want to send something to the server, see that the server has something to take, and then take your own
+pokemon right back.
+
+The JSON data should match the schema currently located at `data/trade_pokemon_schema.json`. It should be universally
+understandable by anything trying to interact with wonder trades. Of course, some Pokemon games include more data than
+others for a given pokemon, some games don't have species introduced in later generations, and some data is of a
+different format, has different values, or is even spelled differently. The hope is that translating to and from JSON is
+reasonable for any game (or at least any game likely to be integrated into AP), and you can easily tell from the JSON
+whether your game is capable of giving the pokemon to the player in-game.
+
+## Preventing Race Conditions
+
+This caused by far the most headache of implementing wonder trades. You should be very thorough in trying to prevent
+issues here.
+
+If you prefer more technical explanations, the Pokemon Emerald client has documented wonder trade functions. The rest of
+this section explains what problems are being solved and why the solutions work.
+
+The problem that needs solving is that your client needs to know what the value of the trade data is before it commits
+some sort of action. By design, multiple clients are writing to and removing from the same key in data storage, so if
+two clients try to interact and there's ambiguity in what the data looks like, it will cause issues of duplication and
+loss of data.
+
+For example, client 1 and client 2 both see a pokemon that they can take, so they copy the pokemon to their respective
+games, and both send a command to remove that pokemon from the data store. The first command works and removes the
+entry, which sends an update to both clients that there no longer exists a pokemon at that slot. And then the second
+command, which was already sent, tries to remove the same entry. At best, the data was duplicated, and at worst the
+server raises an exception or crashes.
+
+Thankfully, when you receive an update from the server that a storage value changed, it will tell you both the previous
+and current value. That's where the lock comes in. At a basic level, your client attempts to claim ownership of the key
+temporarily while it makes its modifications, and all other clients respect that claim by not interacting until the lock
+is released. You know you locked the key because the `SetReply` you receive for modifying the lock is the one that set
+it from an unlocked state to a locked state. When two clients try to lock at the same time, one will see an unlocked
+state move to a locked state, and the other will see an already locked state move to a locked state. You can identify
+whether a `SetReply` was triggered by your client's `Set` by attaching a uuid to the `Set` command, which will also be
+attached to the `SetReply`. See the Emerald client for an example.
+
+Which brings us to problem 2, which is the scenario where a client crashes or closes before unlocking the key. One rogue
+client might prevent all other clients from ever interacting with wonder trading again.
+
+So for this reason, the lock is a timestamp, and the key is considered "locked" if that timestamp is less than 5 seconds
+in the past. If a client dies after locking, its lock will expire, and other clients will be able to make modifications.
+Setting the lock to 0 is the canonical way of marking it as unlocked, but it's not a special case really. It's
+equivalent to marking the key as last locked in 1970.
+
+Which brings us to problem 3. Multiple clients which want to obtain the lock can only check whether the lock is
+obtainable by refreshing the current lock's timestamp. So two clients trying to secure a lock made by a dead client may
+trade back and forth, updating the lock to see if it is expired yet, seeing that it is not, and then waiting 5 seconds
+while the other client does the same thing, which causes the lock to again be less than 5 seconds old.
+
+Using a cooldown period longer than the time to expire only increases the minimum number of clients that can trigger
+this cycle. Instead, the solution is to double your cooldown every time you bounce off an expired lock (and reset it
+once you acquire it). Eventually the amount of time every client is waiting will be enough to create a gap large enough
+for one client to consider the lock expired, and it will acquire the lock, make its changes, and set the lock state to
+definitively unlocked, which will let the next client claim it, and so on.
diff --git a/worlds/pokemon_emerald/items.py b/worlds/pokemon_emerald/items.py
index 7963f92384ac..436db771d396 100644
--- a/worlds/pokemon_emerald/items.py
+++ b/worlds/pokemon_emerald/items.py
@@ -51,13 +51,13 @@ def create_item_label_to_code_map() -> Dict[str, int]:
"Stone Badge", "Knuckle Badge",
"Dynamo Badge", "Heat Badge",
"Balance Badge", "Feather Badge",
- "Mind Badge", "Rain Badge"
+ "Mind Badge", "Rain Badge",
},
"HMs": {
"HM01 Cut", "HM02 Fly",
"HM03 Surf", "HM04 Strength",
"HM05 Flash", "HM06 Rock Smash",
- "HM07 Waterfall", "HM08 Dive"
+ "HM07 Waterfall", "HM08 Dive",
},
"HM01": {"HM01 Cut"},
"HM02": {"HM02 Fly"},
@@ -66,7 +66,7 @@ def create_item_label_to_code_map() -> Dict[str, int]:
"HM05": {"HM05 Flash"},
"HM06": {"HM06 Rock Smash"},
"HM07": {"HM07 Waterfall"},
- "HM08": {"HM08 Dive"}
+ "HM08": {"HM08 Dive"},
}
diff --git a/worlds/pokemon_emerald/locations.py b/worlds/pokemon_emerald/locations.py
index bfe5be754585..99d11db9850c 100644
--- a/worlds/pokemon_emerald/locations.py
+++ b/worlds/pokemon_emerald/locations.py
@@ -1,20 +1,84 @@
"""
Classes and functions related to AP locations for Pokemon Emerald
"""
-from typing import TYPE_CHECKING, Dict, List, Optional, FrozenSet, Iterable
+from typing import TYPE_CHECKING, Dict, Optional, FrozenSet, Iterable
from BaseClasses import Location, Region
-from .data import BASE_OFFSET, data
+from .data import BASE_OFFSET, POKEDEX_OFFSET, data
from .items import offset_item_value
if TYPE_CHECKING:
from . import PokemonEmeraldWorld
+LOCATION_GROUPS = {
+ "Badges": {
+ "Rustboro Gym - Stone Badge",
+ "Dewford Gym - Knuckle Badge",
+ "Mauville Gym - Dynamo Badge",
+ "Lavaridge Gym - Heat Badge",
+ "Petalburg Gym - Balance Badge",
+ "Fortree Gym - Feather Badge",
+ "Mossdeep Gym - Mind Badge",
+ "Sootopolis Gym - Rain Badge",
+ },
+ "Gym TMs": {
+ "Rustboro Gym - TM39 from Roxanne",
+ "Dewford Gym - TM08 from Brawly",
+ "Mauville Gym - TM34 from Wattson",
+ "Lavaridge Gym - TM50 from Flannery",
+ "Petalburg Gym - TM42 from Norman",
+ "Fortree Gym - TM40 from Winona",
+ "Mossdeep Gym - TM04 from Tate and Liza",
+ "Sootopolis Gym - TM03 from Juan",
+ },
+ "Trick House": {
+ "Trick House Puzzle 1 - Item",
+ "Trick House Puzzle 2 - Item 1",
+ "Trick House Puzzle 2 - Item 2",
+ "Trick House Puzzle 3 - Item 1",
+ "Trick House Puzzle 3 - Item 2",
+ "Trick House Puzzle 4 - Item",
+ "Trick House Puzzle 6 - Item",
+ "Trick House Puzzle 7 - Item",
+ "Trick House Puzzle 8 - Item",
+ "Trick House Puzzle 1 - Reward",
+ "Trick House Puzzle 2 - Reward",
+ "Trick House Puzzle 3 - Reward",
+ "Trick House Puzzle 4 - Reward",
+ "Trick House Puzzle 5 - Reward",
+ "Trick House Puzzle 6 - Reward",
+ "Trick House Puzzle 7 - Reward",
+ }
+}
+
+
+VISITED_EVENT_NAME_TO_ID = {
+ "EVENT_VISITED_LITTLEROOT_TOWN": 0,
+ "EVENT_VISITED_OLDALE_TOWN": 1,
+ "EVENT_VISITED_PETALBURG_CITY": 2,
+ "EVENT_VISITED_RUSTBORO_CITY": 3,
+ "EVENT_VISITED_DEWFORD_TOWN": 4,
+ "EVENT_VISITED_SLATEPORT_CITY": 5,
+ "EVENT_VISITED_MAUVILLE_CITY": 6,
+ "EVENT_VISITED_VERDANTURF_TOWN": 7,
+ "EVENT_VISITED_FALLARBOR_TOWN": 8,
+ "EVENT_VISITED_LAVARIDGE_TOWN": 9,
+ "EVENT_VISITED_FORTREE_CITY": 10,
+ "EVENT_VISITED_LILYCOVE_CITY": 11,
+ "EVENT_VISITED_MOSSDEEP_CITY": 12,
+ "EVENT_VISITED_SOOTOPOLIS_CITY": 13,
+ "EVENT_VISITED_PACIFIDLOG_TOWN": 14,
+ "EVENT_VISITED_EVER_GRANDE_CITY": 15,
+ "EVENT_VISITED_BATTLE_FRONTIER": 16,
+ "EVENT_VISITED_SOUTHERN_ISLAND": 17,
+}
+
+
class PokemonEmeraldLocation(Location):
game: str = "Pokemon Emerald"
- rom_address: Optional[int]
+ item_address: Optional[int]
default_item_code: Optional[int]
tags: FrozenSet[str]
@@ -22,14 +86,14 @@ def __init__(
self,
player: int,
name: str,
- flag: Optional[int],
+ address: Optional[int],
parent: Optional[Region] = None,
- rom_address: Optional[int] = None,
+ item_address: Optional[int] = None,
default_item_value: Optional[int] = None,
tags: FrozenSet[str] = frozenset()) -> None:
- super().__init__(player, name, None if flag is None else offset_flag(flag), parent)
+ super().__init__(player, name, address, parent)
self.default_item_code = None if default_item_value is None else offset_item_value(default_item_value)
- self.rom_address = rom_address
+ self.item_address = item_address
self.tags = tags
@@ -64,12 +128,17 @@ def create_locations_with_tags(world: "PokemonEmeraldWorld", regions: Dict[str,
for location_name in filtered_locations:
location_data = data.locations[location_name]
+
+ location_id = offset_flag(location_data.flag)
+ if location_data.flag == 0:
+ location_id += POKEDEX_OFFSET + int(location_name[15:])
+
location = PokemonEmeraldLocation(
world.player,
location_data.label,
- location_data.flag,
+ location_id,
region,
- location_data.rom_address,
+ location_data.address,
location_data.default_item,
location_data.tags
)
@@ -84,39 +153,68 @@ def create_location_label_to_id_map() -> Dict[str, int]:
for region_data in data.regions.values():
for location_name in region_data.locations:
location_data = data.locations[location_name]
- label_to_id_map[location_data.label] = offset_flag(location_data.flag)
+
+ if location_data.flag == 0:
+ label_to_id_map[location_data.label] = BASE_OFFSET + POKEDEX_OFFSET + int(location_data.name[15:])
+ else:
+ label_to_id_map[location_data.label] = offset_flag(location_data.flag)
return label_to_id_map
-LOCATION_GROUPS = {
- "Badges": {
- "Rustboro Gym - Stone Badge",
- "Dewford Gym - Knuckle Badge",
- "Mauville Gym - Dynamo Badge",
- "Lavaridge Gym - Heat Badge",
- "Petalburg Gym - Balance Badge",
- "Fortree Gym - Feather Badge",
- "Mossdeep Gym - Mind Badge",
- "Sootopolis Gym - Rain Badge",
- },
- "Gym TMs": {
- "Rustboro Gym - TM39 from Roxanne",
- "Dewford Gym - TM08 from Brawly",
- "Mauville Gym - TM34 from Wattson",
- "Lavaridge Gym - TM50 from Flannery",
- "Petalburg Gym - TM42 from Norman",
- "Fortree Gym - TM40 from Winona",
- "Mossdeep Gym - TM04 from Tate and Liza",
- "Sootopolis Gym - TM03 from Juan",
- },
- "Postgame Locations": {
- "Littleroot Town - S.S. Ticket from Norman",
- "SS Tidal - Hidden Item in Lower Deck Trash Can",
- "SS Tidal - TM49 from Thief",
- "Safari Zone NE - Hidden Item North",
- "Safari Zone NE - Hidden Item East",
- "Safari Zone SE - Hidden Item in South Grass 1",
- "Safari Zone SE - Hidden Item in South Grass 2",
- }
-}
+def set_free_fly(world: "PokemonEmeraldWorld") -> None:
+ # Set our free fly location
+ # If not enabled, set it to Littleroot Town by default
+ fly_location_name = "EVENT_VISITED_LITTLEROOT_TOWN"
+ if world.options.free_fly_location:
+ fly_location_name = world.random.choice([
+ "EVENT_VISITED_SLATEPORT_CITY",
+ "EVENT_VISITED_MAUVILLE_CITY",
+ "EVENT_VISITED_VERDANTURF_TOWN",
+ "EVENT_VISITED_FALLARBOR_TOWN",
+ "EVENT_VISITED_LAVARIDGE_TOWN",
+ "EVENT_VISITED_FORTREE_CITY",
+ "EVENT_VISITED_LILYCOVE_CITY",
+ "EVENT_VISITED_MOSSDEEP_CITY",
+ "EVENT_VISITED_SOOTOPOLIS_CITY",
+ "EVENT_VISITED_EVER_GRANDE_CITY",
+ ])
+
+ world.free_fly_location_id = VISITED_EVENT_NAME_TO_ID[fly_location_name]
+
+ free_fly_location_location = world.multiworld.get_location("FREE_FLY_LOCATION", world.player)
+ free_fly_location_location.item = None
+ free_fly_location_location.place_locked_item(world.create_event(fly_location_name))
+
+
+def set_legendary_cave_entrances(world: "PokemonEmeraldWorld") -> None:
+ # Set Marine Cave and Terra Cave entrances
+ terra_cave_location_name = world.random.choice([
+ "TERRA_CAVE_ROUTE_114_1",
+ "TERRA_CAVE_ROUTE_114_2",
+ "TERRA_CAVE_ROUTE_115_1",
+ "TERRA_CAVE_ROUTE_115_2",
+ "TERRA_CAVE_ROUTE_116_1",
+ "TERRA_CAVE_ROUTE_116_2",
+ "TERRA_CAVE_ROUTE_118_1",
+ "TERRA_CAVE_ROUTE_118_2",
+ ])
+
+ terra_cave_location_location = world.multiworld.get_location("TERRA_CAVE_LOCATION", world.player)
+ terra_cave_location_location.item = None
+ terra_cave_location_location.place_locked_item(world.create_event(terra_cave_location_name))
+
+ marine_cave_location_name = world.random.choice([
+ "MARINE_CAVE_ROUTE_105_1",
+ "MARINE_CAVE_ROUTE_105_2",
+ "MARINE_CAVE_ROUTE_125_1",
+ "MARINE_CAVE_ROUTE_125_2",
+ "MARINE_CAVE_ROUTE_127_1",
+ "MARINE_CAVE_ROUTE_127_2",
+ "MARINE_CAVE_ROUTE_129_1",
+ "MARINE_CAVE_ROUTE_129_2",
+ ])
+
+ marine_cave_location_location = world.multiworld.get_location("MARINE_CAVE_LOCATION", world.player)
+ marine_cave_location_location.item = None
+ marine_cave_location_location.place_locked_item(world.create_event(marine_cave_location_name))
diff --git a/worlds/pokemon_emerald/opponents.py b/worlds/pokemon_emerald/opponents.py
new file mode 100644
index 000000000000..09e947546d7c
--- /dev/null
+++ b/worlds/pokemon_emerald/opponents.py
@@ -0,0 +1,116 @@
+from typing import TYPE_CHECKING, Dict, List, Set
+
+from .data import NUM_REAL_SPECIES, UNEVOLVED_POKEMON, TrainerPokemonData, data
+from .options import RandomizeTrainerParties
+from .pokemon import filter_species_by_nearby_bst
+from .util import int_to_bool_array
+
+if TYPE_CHECKING:
+ from . import PokemonEmeraldWorld
+
+
+def randomize_opponent_parties(world: "PokemonEmeraldWorld") -> None:
+ if world.options.trainer_parties == RandomizeTrainerParties.option_vanilla:
+ return
+
+ from collections import defaultdict
+
+ should_match_bst = world.options.trainer_parties in {
+ RandomizeTrainerParties.option_match_base_stats,
+ RandomizeTrainerParties.option_match_base_stats_and_type,
+ }
+ should_match_type = world.options.trainer_parties in {
+ RandomizeTrainerParties.option_match_type,
+ RandomizeTrainerParties.option_match_base_stats_and_type,
+ }
+
+ per_species_tmhm_moves: Dict[int, List[int]] = {}
+
+ for trainer in world.modified_trainers:
+ new_party = []
+ for pokemon in trainer.party.pokemon:
+ original_species = data.species[pokemon.species_id]
+
+ # Construct progressive tiers of blacklists that can be peeled back if they
+ # collectively cover too much of the pokedex. A lower index in `blacklists`
+ # indicates a more important set of species to avoid. Entries at `0` will
+ # always be blacklisted.
+ blacklists: Dict[int, List[Set[int]]] = defaultdict(list)
+
+ # Blacklist unevolved species
+ if pokemon.level >= world.options.force_fully_evolved:
+ blacklists[0].append(UNEVOLVED_POKEMON)
+
+ # Blacklist from player options
+ blacklists[2].append(world.blacklisted_opponent_pokemon)
+
+ # Type matching blacklist
+ if should_match_type:
+ blacklists[3].append({
+ species.species_id
+ for species in world.modified_species.values()
+ if not bool(set(species.types) & set(original_species.types))
+ })
+
+ merged_blacklist: Set[int] = set()
+ for max_priority in reversed(sorted(blacklists.keys())):
+ merged_blacklist = set()
+ for priority in blacklists.keys():
+ if priority <= max_priority:
+ for blacklist in blacklists[priority]:
+ merged_blacklist |= blacklist
+
+ if len(merged_blacklist) < NUM_REAL_SPECIES:
+ break
+ else:
+ raise RuntimeError("This should never happen")
+
+ candidates = [
+ species
+ for species in world.modified_species.values()
+ if species.species_id not in merged_blacklist
+ ]
+
+ if should_match_bst:
+ candidates = filter_species_by_nearby_bst(candidates, sum(original_species.base_stats))
+
+ new_species = world.random.choice(candidates)
+
+ if new_species.species_id not in per_species_tmhm_moves:
+ per_species_tmhm_moves[new_species.species_id] = sorted({
+ world.modified_tmhm_moves[i]
+ for i, is_compatible in enumerate(int_to_bool_array(new_species.tm_hm_compatibility))
+ if is_compatible and world.modified_tmhm_moves[i] not in world.blacklisted_moves
+ })
+
+ # TMs and HMs compatible with the species
+ tm_hm_movepool = per_species_tmhm_moves[new_species.species_id]
+
+ # Moves the pokemon could have learned by now
+ level_up_movepool = sorted({
+ move.move_id
+ for move in new_species.learnset
+ if move.move_id != 0 and move.level <= pokemon.level
+ })
+
+ if len(level_up_movepool) < 4:
+ level_up_moves = [level_up_movepool[i] if i < len(level_up_movepool) else 0 for i in range(4)]
+ else:
+ level_up_moves = world.random.sample(level_up_movepool, 4)
+
+ if len(tm_hm_movepool) < 4:
+ hm_moves = list(reversed(list(tm_hm_movepool[i] if i < len(tm_hm_movepool) else 0 for i in range(4))))
+ else:
+ hm_moves = world.random.sample(tm_hm_movepool, 4)
+
+ # 25% chance to pick a move from TMs or HMs
+ new_moves = (
+ hm_moves[0] if world.random.random() < 0.25 else level_up_moves[0],
+ hm_moves[1] if world.random.random() < 0.25 else level_up_moves[1],
+ hm_moves[2] if world.random.random() < 0.25 else level_up_moves[2],
+ hm_moves[3] if world.random.random() < 0.25 else level_up_moves[3]
+ )
+
+ new_party.append(TrainerPokemonData(new_species.species_id, pokemon.level, new_moves))
+
+ trainer.party.pokemon = new_party
diff --git a/worlds/pokemon_emerald/options.py b/worlds/pokemon_emerald/options.py
index 655966a2a7b7..69ce47f20775 100644
--- a/worlds/pokemon_emerald/options.py
+++ b/worlds/pokemon_emerald/options.py
@@ -2,9 +2,9 @@
Option definitions for Pokemon Emerald
"""
from dataclasses import dataclass
-from typing import Dict, Type
-from Options import Choice, DefaultOnToggle, Option, OptionSet, Range, Toggle, FreeText, PerGameCommonOptions
+from Options import (Choice, DeathLink, DefaultOnToggle, TextChoice, OptionSet, NamedRange, Range, Toggle, FreeText,
+ PerGameCommonOptions)
from .data import data
@@ -16,12 +16,14 @@ class Goal(Choice):
Champion: Become the champion and enter the hall of fame
Steven: Defeat Steven in Meteor Falls
Norman: Defeat Norman in Petalburg Gym
+ Legendary Hunt: Defeat or catch legendary pokemon (or whatever was randomized into their encounters)
"""
display_name = "Goal"
default = 0
option_champion = 0
option_steven = 1
option_norman = 2
+ option_legendary_hunt = 3
class RandomizeBadges(Choice):
@@ -69,6 +71,13 @@ class RandomizeBikes(Toggle):
display_name = "Randomize Bikes"
+class RandomizeEventTickets(Toggle):
+ """
+ Adds the event tickets to the pool, which let you access legendaries by sailing from Lilycove
+ """
+ display_name = "Randomize Event Tickets"
+
+
class RandomizeRods(Toggle):
"""
Adds fishing rods to the pool
@@ -97,13 +106,40 @@ class RandomizeNpcGifts(Toggle):
display_name = "Randomize NPC Gifts"
+class RandomizeBerryTrees(Toggle):
+ """
+ Adds berry trees to the pool. Empty soil patches are converted to locations and contribute Sitrus Berries to the pool.
+ """
+ display_name = "Randomize Berry Trees"
+
+
+class Dexsanity(Toggle):
+ """
+ Adding a "caught" pokedex entry gives you an item (catching, evolving, trading, etc.).
+
+ Defeating gym leaders provides dex info, allowing you to see where on the map you can catch species you need.
+
+ Each pokedex entry adds a Poke Ball, Great Ball, or Ultra Ball to the pool.
+ """
+ display_name = "Dexsanity"
+
+
+class Trainersanity(Toggle):
+ """
+ Defeating a trainer for the first time gives you an item. Trainers are no longer missable.
+
+ Trainers no longer give you money for winning. Each trainer adds a valuable item (nugget, stardust, etc.) to the pool.
+ """
+ display_name = "Trainersanity"
+
+
class ItemPoolType(Choice):
"""
Determines which non-progression items get put into the item pool
Shuffled: Item pool consists of shuffled vanilla items
Diverse Balanced: Item pool consists of random items approximately proportioned
- according to what they're replacing (i.e. more pokeballs, fewer X items, etc...)
+ according to what they're replacing (i.e. more pokeballs, fewer X items, etc.)
Diverse: Item pool consists of uniformly random (non-unique) items
"""
display_name = "Item Pool Type"
@@ -120,18 +156,16 @@ class HiddenItemsRequireItemfinder(DefaultOnToggle):
display_name = "Require Itemfinder"
-class DarkCavesRequireFlash(DefaultOnToggle):
+class DarkCavesRequireFlash(Choice):
"""
- The lower floors of Granite Cave and Victory Road logically require use of HM05 Flash
+ Determines whether HM05 Flash is logically required to navigate a dark cave
"""
display_name = "Require Flash"
-
-
-class EnableFerry(Toggle):
- """
- The ferry between Slateport, Lilycove, and the Battle Frontier can be used if you have the S.S. Ticket
- """
- display_name = "Enable Ferry"
+ default = 3
+ option_neither = 0
+ option_only_granite_cave = 1
+ option_only_victory_road = 2
+ option_both = 3
class EliteFourRequirement(Choice):
@@ -180,6 +214,61 @@ class NormanCount(Range):
default = 4
+class LegendaryHuntCatch(Toggle):
+ """
+ Sets whether legendaries need to be caught to satisfy the Legendary Hunt win condition. Defeated legendaries can be respawned by defeating the Elite 4.
+ """
+ display_name = "Legendary Hunt Requires Catching"
+
+
+class LegendaryHuntCount(Range):
+ """
+ Sets the number of legendaries that must be caught/defeated for the Legendary Hunt goal
+ """
+ display_name = "Legendary Hunt Count"
+ range_start = 1
+ range_end = 12
+ default = 3
+
+
+class AllowedLegendaryHuntEncounters(OptionSet):
+ """
+ Sets which legendary encounters can contribute to the Legendary Hunt goal.
+
+ Latios will always be the roamer. Latias will always be at Southern Island.
+
+ Possible values are:
+ "Groudon"
+ "Kyogre"
+ "Rayquaza"
+ "Latios"
+ "Latias"
+ "Regirock"
+ "Registeel"
+ "Regice"
+ "Ho-oh"
+ "Lugia"
+ "Deoxys"
+ "Mew"
+ """
+ display_name = "Allowed Legendary Hunt Encounters"
+ valid_keys = frozenset([
+ "Groudon",
+ "Kyogre",
+ "Rayquaza",
+ "Latios",
+ "Latias",
+ "Regirock",
+ "Registeel",
+ "Regice",
+ "Ho-oh",
+ "Lugia",
+ "Deoxys",
+ "Mew",
+ ])
+ default = valid_keys.copy()
+
+
class RandomizeWildPokemon(Choice):
"""
Randomizes wild pokemon encounters (grass, caves, water, fishing)
@@ -199,11 +288,16 @@ class RandomizeWildPokemon(Choice):
option_completely_random = 4
-class AllowWildLegendaries(DefaultOnToggle):
+class WildEncounterBlacklist(OptionSet):
"""
- Wild encounters can be replaced by legendaries. Only applied if Randomize Wild Pokemon is not Vanilla.
+ Prevents listed species from appearing in the wild when wild encounters are randomized.
+
+ May be overridden if enforcing other restrictions in combination with this blacklist is impossible.
+
+ Use "_Legendaries" as a shortcut for legendary pokemon.
"""
- display_name = "Allow Wild Legendaries"
+ display_name = "Wild Encounter Blacklist"
+ valid_keys = frozenset(species.label for species in data.species.values()) | {"_Legendaries"}
class RandomizeStarters(Choice):
@@ -225,11 +319,16 @@ class RandomizeStarters(Choice):
option_completely_random = 4
-class AllowStarterLegendaries(DefaultOnToggle):
+class StarterBlacklist(OptionSet):
"""
- Starters can be replaced by legendaries. Only applied if Randomize Starters is not Vanilla.
+ Prevents listed species from appearing as starters when starters are randomized.
+
+ May be overridden if enforcing other restrictions in combination with this blacklist is impossible.
+
+ Use "_Legendaries" as a shortcut for legendary pokemon.
"""
- display_name = "Allow Starter Legendaries"
+ display_name = "Starter Blacklist"
+ valid_keys = frozenset(species.label for species in data.species.values()) | {"_Legendaries"}
class RandomizeTrainerParties(Choice):
@@ -251,25 +350,61 @@ class RandomizeTrainerParties(Choice):
option_completely_random = 4
-class AllowTrainerLegendaries(DefaultOnToggle):
+class TrainerPartyBlacklist(OptionSet):
+ """
+ Prevents listed species from appearing in opponent trainers' parties if opponent parties are randomized.
+
+ May be overridden if enforcing other restrictions in combination with this blacklist is impossible.
+
+ Use "_Legendaries" as a shortcut for legendary pokemon.
+ """
+ display_name = "Trainer Party Blacklist"
+ valid_keys = frozenset(species.label for species in data.species.values()) | {"_Legendaries"}
+
+
+class ForceFullyEvolved(Range):
+ """
+ When an opponent uses a pokemon of the specified level or higher, restricts the species to only fully evolved pokemon.
+ """
+ display_name = "Force Fully Evolved"
+ range_start = 1
+ range_end = 100
+ default = 100
+
+
+class RandomizeLegendaryEncounters(Choice):
"""
- Enemy trainer pokemon can be replaced by legendaries. Only applied if Randomize Trainer Parties is not Vanilla.
+ Randomizes legendary encounters (Rayquaza, Regice, Latias, etc.). The roamer will always be Latios during legendary hunts.
+
+ Vanilla: Legendary encounters are unchanged
+ Shuffle: Legendary encounters are shuffled between each other
+ Match Base Stats: Legendary encounters are replaced with species with approximately the same bst
+ Match Type: Legendary encounters are replaced with species that share a type with the original
+ Match Base Stats and Type: Apply both Match Base Stats and Match Type
+ Completely Random: There are no restrictions
"""
- display_name = "Allow Trainer Legendaries"
+ display_name = "Randomize Legendary Encounters"
+ default = 0
+ option_vanilla = 0
+ option_shuffle = 1
+ option_match_base_stats = 2
+ option_match_type = 3
+ option_match_base_stats_and_type = 4
+ option_completely_random = 5
-class RandomizeStaticEncounters(Choice):
+class RandomizeMiscPokemon(Choice):
"""
- Randomizes static encounters (Rayquaza, hidden Kekleons, fake Voltorb pokeballs, etc...)
+ Randomizes non-legendary static encounters. May grow to include other pokemon like trades or gifts.
- Vanilla: Static encounters are unchanged
- Shuffle: Static encounters are shuffled between each other
- Match Base Stats: Static encounters are replaced with species with approximately the same bst
- Match Type: Static encounters are replaced with species that share a type with the original
+ Vanilla: Species are unchanged
+ Shuffle: Species are shuffled between each other
+ Match Base Stats: Species are replaced with species with approximately the same bst
+ Match Type: Species are replaced with species that share a type with the original
Match Base Stats and Type: Apply both Match Base Stats and Match Type
Completely Random: There are no restrictions
"""
- display_name = "Randomize Static Encounters"
+ display_name = "Randomize Misc Pokemon"
default = 0
option_vanilla = 0
option_shuffle = 1
@@ -363,48 +498,52 @@ class MoveNormalTypeBias(Range):
default = 0
-class HmCompatibility(Choice):
+class MoveBlacklist(OptionSet):
"""
- Modifies the compatibility of HMs
+ A list of moves which should be excluded from learnsets, TMs, and move tutors.
+ """
+ display_name = "Move Blacklist"
+ valid_keys = frozenset(data.move_labels.keys())
+
- Vanilla: Compatibility is unchanged
- Fully Compatible: Every species can learn any HM
- Completely Random: Compatibility is 50/50 for every HM (does not remain consistent across evolution)
+class HmCompatibility(NamedRange):
+ """
+ Sets the percent chance that a given HM is compatible with a species
"""
display_name = "HM Compatibility"
- default = 1
- option_vanilla = 0
- option_fully_compatible = 1
- option_completely_random = 2
+ default = -1
+ range_start = 50
+ range_end = 100
+ special_range_names = {
+ "vanilla": -1
+ }
-class TmCompatibility(Choice):
+class TmTutorCompatibility(NamedRange):
"""
- Modifies the compatibility of TMs
-
- Vanilla: Compatibility is unchanged
- Fully Compatible: Every species can learn any TM
- Completely Random: Compatibility is 50/50 for every TM (does not remain consistent across evolution)
+ Sets the percent chance that a given TM or move tutor is compatible with a species
"""
- display_name = "TM Compatibility"
- default = 0
- option_vanilla = 0
- option_fully_compatible = 1
- option_completely_random = 2
+ display_name = "TM/Tutor Compatibility"
+ default = -1
+ range_start = 0
+ range_end = 100
+ special_range_names = {
+ "vanilla": -1
+ }
-class TmMoves(Toggle):
+class TmTutorMoves(Toggle):
"""
- Randomizes the moves taught by TMs
+ Randomizes the moves taught by TMs and move tutors
"""
- display_name = "TM Moves"
+ display_name = "TM/Tutor Moves"
-class ReusableTms(Toggle):
+class ReusableTmsTutors(Toggle):
"""
- Sets TMs to not break after use (they remain sellable)
+ Sets TMs to not break after use (they remain sellable). Sets move tutors to infinite use.
"""
- display_name = "Reusable TMs"
+ display_name = "Reusable TMs and Tutors"
class MinCatchRate(Range):
@@ -428,6 +567,15 @@ class GuaranteedCatch(Toggle):
display_name = "Guaranteed Catch"
+class NormalizeEncounterRates(Toggle):
+ """
+ Make every slot on an encounter table approximately equally likely.
+
+ This does NOT mean every species is equally likely. But it will make rarer encounters less rare overall.
+ """
+ display_name = "Normalize Encounter Rates"
+
+
class ExpModifier(Range):
"""
Multiplies gained experience by a percentage
@@ -435,7 +583,7 @@ class ExpModifier(Range):
100 is default
50 is half
200 is double
- etc...
+ etc.
"""
display_name = "Exp Modifier"
range_start = 0
@@ -450,6 +598,48 @@ class BlindTrainers(Toggle):
display_name = "Blind Trainers"
+class PurgeSpinners(Toggle):
+ """
+ Trainers will rotate in predictable patterns on a set interval instead of randomly and don't turn toward you when you run
+ """
+ display_name = "Purge Spinners"
+
+
+class MatchTrainerLevels(Choice):
+ """
+ When you start a battle with a trainer, your party's levels will be automatically set to match that trainer's highest level pokemon.
+
+ The experience you receive will match your party's average actual level, and will only be awarded when you win the battle.
+
+ This is a pseudo-replacement for a level cap and makes every trainer battle a fair fight while still allowing you to level up.
+
+ Off: The vanilla experience
+ Additive: The modifier you apply to your team is a flat bonus
+ Multiplicative: The modifier you apply to your team is a percent bonus
+ """
+ display_name = "Match Trainer Levels"
+ default = 0
+ option_off = 0
+ option_additive = 1
+ option_multiplicative = 2
+
+
+class MatchTrainerLevelsBonus(Range):
+ """
+ A level bonus (or penalty) to apply to your team when matching an opponent's levels.
+
+ When the match trainer levels option is "additive", this value is added to your team's levels during a battle.
+ For example, if this value is 5 (+5 levels), you'll have a level 25 team against a level 20 team, and a level 45 team against a level 40 team.
+
+ When the match trainer levels option is "multiplicative", this is a percent bonus.
+ For example, if this value is 5 (+5%), you'll have a level 21 team against a level 20 team, and a level 42 team against a level 40 team.
+ """
+ display_name = "Match Trainer Levels Modifier"
+ range_start = -100
+ range_end = 100
+ default = 0
+
+
class DoubleBattleChance(Range):
"""
The percent chance that a trainer with more than 1 pokemon will be converted into a double battle.
@@ -492,18 +682,34 @@ class RemoveRoadblocks(OptionSet):
"Safari Zone Construction Workers",
"Lilycove City Wailmer",
"Aqua Hideout Grunts",
- "Seafloor Cavern Aqua Grunt"
+ "Seafloor Cavern Aqua Grunt",
])
class ExtraBoulders(Toggle):
"""
Places strength boulders on Route 115 which block access to Meteor Falls from the beach.
- This aims to take some power away from Surf as a tool for access.
+ This aims to take some power away from Surf by restricting how much it allows you to access.
"""
display_name = "Extra Boulders"
+class ExtraBumpySlope(Toggle):
+ """
+ Adds a bumpy slope to Route 115 which allows access to Meteor Falls if you have the Acro Bike.
+ This aims to take some power away from Surf by adding a new way to exit the Rustboro area.
+ """
+ display_name = "Extra Bumpy Slope"
+
+
+class ModifyRoute118(Toggle):
+ """
+ Changes the layout of Route 118 so that it must be crossed with the Acro Bike instead of Surf.
+ This aims to take some power away from Surf by restricting how much it allows you to access.
+ """
+ display_name = "Modify Route 118"
+
+
class FreeFlyLocation(Toggle):
"""
Enables flying to one random location when Mom gives you the running shoes (excluding cities reachable with no items)
@@ -511,11 +717,14 @@ class FreeFlyLocation(Toggle):
display_name = "Free Fly Location"
-class FlyWithoutBadge(DefaultOnToggle):
+class HmRequirements(TextChoice):
"""
- Fly does not require the Feather Badge to use in the field
+ Sets the requirements to use HMs outside of battle
"""
- display_name = "Fly Without Badge"
+ display_name = "HM Requirements"
+ default = 0
+ option_vanilla = 0
+ option_fly_without_badge = 1
class TurboA(Toggle):
@@ -540,11 +749,53 @@ class ReceiveItemMessages(Choice):
option_none = 2
+class RemoteItems(Toggle):
+ """
+ Instead of placing your own items directly into the ROM, all items are received from the server, including items you find for yourself.
+
+ This enables co-op of a single slot and recovering more items after a lost save file (if you're so unlucky).
+ But it changes pickup behavior slightly and requires connection to the server to receive any items.
+ """
+ display_name = "Remote Items"
+
+
+class RandomizeMusic(Toggle):
+ """
+ Shuffles music played in any situation where it loops. Includes many FRLG tracks.
+ """
+ display_name = "Randomize Music"
+
+
+class RandomizeFanfares(Toggle):
+ """
+ Shuffles fanfares for item pickups, healing at the pokecenter, etc.
+
+ When this option is enabled, pressing B will interrupt most fanfares.
+ """
+ display_name = "Randomize Fanfares"
+
+
+class WonderTrading(DefaultOnToggle):
+ """
+ Allows participation in wonder trading with other players in your current multiworld. Speak with the center receptionist on the second floor of any pokecenter.
+
+ Wonder trading NEVER affects logic.
+
+ Certain aspects of a pokemon species are per-game, not per-pokemon.
+ As a result, some things are not retained during a trade, including type, ability, level up learnset, and so on.
+ Receiving a pokemon this way does not mark it as found in your pokedex.
+ Trade evolutions do not evolve this way; they retain their modified methods (level ups and item use).
+ """
+ display_name = "Wonder Trading"
+
+
class EasterEgg(FreeText):
"""
- ???
+ Enter certain phrases and something special might happen.
+
+ All secret phrases are something that could be a trendy phrase in Dewford Town. They are case insensitive.
"""
- default = "Example Passphrase"
+ default = "EMERALD SECRET"
@dataclass
@@ -555,10 +806,14 @@ class PokemonEmeraldOptions(PerGameCommonOptions):
hms: RandomizeHms
key_items: RandomizeKeyItems
bikes: RandomizeBikes
+ event_tickets: RandomizeEventTickets
rods: RandomizeRods
overworld_items: RandomizeOverworldItems
hidden_items: RandomizeHiddenItems
npc_gifts: RandomizeNpcGifts
+ berry_trees: RandomizeBerryTrees
+ dexsanity: Dexsanity
+ trainersanity: Trainersanity
item_pool_type: ItemPoolType
require_itemfinder: HiddenItemsRequireItemfinder
@@ -567,14 +822,19 @@ class PokemonEmeraldOptions(PerGameCommonOptions):
elite_four_count: EliteFourCount
norman_requirement: NormanRequirement
norman_count: NormanCount
+ legendary_hunt_catch: LegendaryHuntCatch
+ legendary_hunt_count: LegendaryHuntCount
+ allowed_legendary_hunt_encounters: AllowedLegendaryHuntEncounters
wild_pokemon: RandomizeWildPokemon
- allow_wild_legendaries: AllowWildLegendaries
+ wild_encounter_blacklist: WildEncounterBlacklist
starters: RandomizeStarters
- allow_starter_legendaries: AllowStarterLegendaries
+ starter_blacklist: StarterBlacklist
trainer_parties: RandomizeTrainerParties
- allow_trainer_legendaries: AllowTrainerLegendaries
- static_encounters: RandomizeStaticEncounters
+ trainer_party_blacklist: TrainerPartyBlacklist
+ force_fully_evolved: ForceFullyEvolved
+ legendary_encounters: RandomizeLegendaryEncounters
+ misc_pokemon: RandomizeMiscPokemon
types: RandomizeTypes
abilities: RandomizeAbilities
ability_blacklist: AbilityBlacklist
@@ -582,25 +842,38 @@ class PokemonEmeraldOptions(PerGameCommonOptions):
level_up_moves: LevelUpMoves
move_match_type_bias: MoveMatchTypeBias
move_normal_type_bias: MoveNormalTypeBias
- tm_compatibility: TmCompatibility
+ tm_tutor_compatibility: TmTutorCompatibility
hm_compatibility: HmCompatibility
- tm_moves: TmMoves
- reusable_tms: ReusableTms
+ tm_tutor_moves: TmTutorMoves
+ reusable_tms_tutors: ReusableTmsTutors
+ move_blacklist: MoveBlacklist
min_catch_rate: MinCatchRate
guaranteed_catch: GuaranteedCatch
+ normalize_encounter_rates: NormalizeEncounterRates
exp_modifier: ExpModifier
blind_trainers: BlindTrainers
+ purge_spinners: PurgeSpinners
+ match_trainer_levels: MatchTrainerLevels
+ match_trainer_levels_bonus: MatchTrainerLevelsBonus
double_battle_chance: DoubleBattleChance
better_shops: BetterShops
- enable_ferry: EnableFerry
remove_roadblocks: RemoveRoadblocks
extra_boulders: ExtraBoulders
+ extra_bumpy_slope: ExtraBumpySlope
+ modify_118: ModifyRoute118
free_fly_location: FreeFlyLocation
- fly_without_badge: FlyWithoutBadge
+ hm_requirements: HmRequirements
turbo_a: TurboA
receive_item_messages: ReceiveItemMessages
+ remote_items: RemoteItems
+
+ music: RandomizeMusic
+ fanfares: RandomizeFanfares
+
+ death_link: DeathLink
+ enable_wonder_trading: WonderTrading
easter_egg: EasterEgg
diff --git a/worlds/pokemon_emerald/pokemon.py b/worlds/pokemon_emerald/pokemon.py
index 13c92ddc09bd..8df15bbb2bf3 100644
--- a/worlds/pokemon_emerald/pokemon.py
+++ b/worlds/pokemon_emerald/pokemon.py
@@ -1,16 +1,23 @@
"""
Functions related to pokemon species and moves
"""
-import time
+import functools
from typing import TYPE_CHECKING, Dict, List, Set, Optional, Tuple
-from .data import SpeciesData, data
+from Options import Toggle
+
+from .data import NUM_REAL_SPECIES, POSTGAME_MAPS, EncounterTableData, LearnsetMove, MiscPokemonData, SpeciesData, data
+from .options import (Goal, HmCompatibility, LevelUpMoves, RandomizeAbilities, RandomizeLegendaryEncounters,
+ RandomizeMiscPokemon, RandomizeStarters, RandomizeTypes, RandomizeWildPokemon,
+ TmTutorCompatibility)
+from .util import bool_array_to_int, get_easter_egg, int_to_bool_array
if TYPE_CHECKING:
from random import Random
+ from . import PokemonEmeraldWorld
-_damaging_moves = frozenset({
+_DAMAGING_MOVES = frozenset({
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13,
16, 17, 20, 21, 22, 23, 24, 25, 26, 27, 29, 30,
31, 33, 34, 35, 36, 37, 38, 40, 41, 42, 44, 51,
@@ -26,10 +33,13 @@
276, 279, 280, 282, 284, 290, 292, 295, 296, 299, 301, 302,
304, 305, 306, 307, 308, 309, 310, 311, 314, 315, 317, 318,
323, 324, 325, 326, 327, 328, 330, 331, 332, 333, 337, 338,
- 340, 341, 342, 343, 344, 345, 348, 350, 351, 352, 353, 354
+ 340, 341, 342, 343, 344, 345, 348, 350, 351, 352, 353, 354,
})
+"""IDs for moves that safely deal direct damage, for avoiding putting the
+player in a situation where they can only use status moves, or are forced
+to faint themselves, or something of that nature."""
-_move_types = [
+_MOVE_TYPES = [
0, 0, 1, 0, 0, 0, 0, 10, 15, 13, 0, 0, 0, 0, 0,
0, 2, 2, 0, 2, 0, 0, 12, 0, 1, 0, 1, 1, 4, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 6, 6, 0, 17,
@@ -53,82 +63,35 @@
4, 15, 12, 0, 0, 3, 0, 10, 11, 8, 7, 0, 12, 17, 2,
10, 0, 5, 6, 8, 12, 0, 14, 11, 6, 7, 14, 1, 4, 15,
11, 12, 2, 15, 8, 0, 0, 16, 12, 1, 2, 4, 3, 0, 13,
- 12, 11, 14, 12, 16, 5, 13, 11, 8, 14
+ 12, 11, 14, 12, 16, 5, 13, 11, 8, 14,
]
+"""Maps move ids to the type of that move"""
+
+_MOVES_BY_TYPE: Dict[int, List[int]] = {}
+"""Categorizes move ids by their type"""
+for move, type in enumerate(_MOVE_TYPES):
+ _MOVES_BY_TYPE.setdefault(type, []).append(move)
+
+HM_MOVES = frozenset({
+ data.constants["MOVE_CUT"],
+ data.constants["MOVE_FLY"],
+ data.constants["MOVE_SURF"],
+ data.constants["MOVE_STRENGTH"],
+ data.constants["MOVE_FLASH"],
+ data.constants["MOVE_ROCK_SMASH"],
+ data.constants["MOVE_WATERFALL"],
+ data.constants["MOVE_DIVE"],
+})
-_moves_by_type: Dict[int, List[int]] = {}
-for move, type in enumerate(_move_types):
- _moves_by_type.setdefault(type, []).append(move)
-
-_move_blacklist = frozenset({
+_MOVE_BLACKLIST = frozenset({
0, # MOVE_NONE
165, # Struggle
- 15, # Cut
- 148, # Flash
- 249, # Rock Smash
- 70, # Strength
- 57, # Surf
- 19, # Fly
- 291, # Dive
- 127 # Waterfall
-})
+} | HM_MOVES)
-_legendary_pokemon = frozenset({
- 'Mew',
- 'Mewtwo',
- 'Articuno',
- 'Zapdos',
- 'Moltres',
- 'Lugia',
- 'Ho-oh',
- 'Raikou',
- 'Suicune',
- 'Entei',
- 'Celebi',
- 'Groudon',
- 'Kyogre',
- 'Rayquaza',
- 'Latios',
- 'Latias',
- 'Registeel',
- 'Regirock',
- 'Regice',
- 'Jirachi',
- 'Deoxys'
-})
-
-def get_random_species(
- random: "Random",
- candidates: List[Optional[SpeciesData]],
- nearby_bst: Optional[int] = None,
- species_type: Optional[int] = None,
- allow_legendaries: bool = True) -> SpeciesData:
- candidates: List[SpeciesData] = [species for species in candidates if species is not None]
-
- if species_type is not None:
- candidates = [species for species in candidates if species_type in species.types]
-
- if not allow_legendaries:
- candidates = [species for species in candidates if species.label not in _legendary_pokemon]
-
- if nearby_bst is not None:
- def has_nearby_bst(species: SpeciesData, max_percent_different: int) -> bool:
- return abs(sum(species.base_stats) - nearby_bst) < nearby_bst * (max_percent_different / 100)
-
- max_percent_different = 10
- bst_filtered_candidates = [species for species in candidates if has_nearby_bst(species, max_percent_different)]
- while len(bst_filtered_candidates) == 0:
- max_percent_different += 10
- bst_filtered_candidates = [
- species
- for species in candidates
- if has_nearby_bst(species, max_percent_different)
- ]
-
- candidates = bst_filtered_candidates
-
- return random.choice(candidates)
+@functools.lru_cache(maxsize=386)
+def get_species_id_by_label(label: str) -> int:
+ return next(species.species_id for species in data.species.values() if species.label == label)
def get_random_type(random: "Random") -> int:
@@ -145,7 +108,7 @@ def get_random_move(
type_bias: int = 0,
normal_bias: int = 0,
type_target: Optional[Tuple[int, int]] = None) -> int:
- expanded_blacklist = _move_blacklist | (blacklist if blacklist is not None else set())
+ expanded_blacklist = _MOVE_BLACKLIST | (blacklist if blacklist is not None else set())
bias = random.random() * 100
if bias < type_bias:
@@ -175,8 +138,8 @@ def get_random_move(
if type_target is None:
possible_moves = [i for i in range(data.constants["MOVES_COUNT"]) if i not in expanded_blacklist]
else:
- possible_moves = [move for move in _moves_by_type[type_target[0]] if move not in expanded_blacklist] + \
- [move for move in _moves_by_type[type_target[1]] if move not in expanded_blacklist]
+ possible_moves = [move for move in _MOVES_BY_TYPE[type_target[0]] if move not in expanded_blacklist] + \
+ [move for move in _MOVES_BY_TYPE[type_target[1]] if move not in expanded_blacklist]
if len(possible_moves) == 0:
return get_random_move(random, None, type_bias, normal_bias, type_target)
@@ -185,12 +148,549 @@ def get_random_move(
def get_random_damaging_move(random: "Random", blacklist: Optional[Set[int]] = None) -> int:
- expanded_blacklist = _move_blacklist | (blacklist if blacklist is not None else set())
-
- move_options = list(_damaging_moves)
+ expanded_blacklist = _MOVE_BLACKLIST | (blacklist if blacklist is not None else set())
+ move_options = list(_DAMAGING_MOVES)
move = random.choice(move_options)
while move in expanded_blacklist:
move = random.choice(move_options)
return move
+
+
+def filter_species_by_nearby_bst(species: List[SpeciesData], target_bst: int) -> List[SpeciesData]:
+ # Sort by difference in bst, then chop off the tail of the list that's more than
+ # 10% different. If that leaves the list empty, increase threshold to 20%, then 30%, etc.
+ species = sorted(species, key=lambda species: abs(sum(species.base_stats) - target_bst))
+ cutoff_index = 0
+ max_percent_different = 10
+ while cutoff_index == 0 and max_percent_different < 10000:
+ while cutoff_index < len(species) and abs(sum(species[cutoff_index].base_stats) - target_bst) < target_bst * (max_percent_different / 100):
+ cutoff_index += 1
+ max_percent_different += 10
+
+ return species[:cutoff_index + 1]
+
+
+def randomize_types(world: "PokemonEmeraldWorld") -> None:
+ if world.options.types == RandomizeTypes.option_shuffle:
+ type_map = list(range(18))
+ world.random.shuffle(type_map)
+
+ # We never want to map to the ??? type, so swap whatever index maps to ??? with ???
+ # which forces ??? to always map to itself. There are no pokemon which have the ??? type
+ mystery_type_index = type_map.index(9)
+ type_map[mystery_type_index], type_map[9] = type_map[9], type_map[mystery_type_index]
+
+ for species in world.modified_species.values():
+ species.types = (type_map[species.types[0]], type_map[species.types[1]])
+ elif world.options.types == RandomizeTypes.option_completely_random:
+ for species in world.modified_species.values():
+ new_type_1 = get_random_type(world.random)
+ new_type_2 = new_type_1
+ if species.types[0] != species.types[1]:
+ while new_type_1 == new_type_2:
+ new_type_2 = get_random_type(world.random)
+
+ species.types = (new_type_1, new_type_2)
+ elif world.options.types == RandomizeTypes.option_follow_evolutions:
+ already_modified: Set[int] = set()
+
+ # Similar to follow evolutions for abilities, but only needs to loop through once.
+ # For every pokemon without a pre-evolution, generates a random mapping from old types to new types
+ # and then walks through the evolution tree applying that map. This means that evolutions that share
+ # types will have those types mapped to the same new types, and evolutions with new or diverging types
+ # will still have new or diverging types.
+ # Consider:
+ # - Charmeleon (Fire/Fire) -> Charizard (Fire/Flying)
+ # - Onyx (Rock/Ground) -> Steelix (Steel/Ground)
+ # - Nincada (Bug/Ground) -> Ninjask (Bug/Flying) && Shedinja (Bug/Ghost)
+ # - Azurill (Normal/Normal) -> Marill (Water/Water)
+ for species in world.modified_species.values():
+ if species.species_id in already_modified:
+ continue
+ if species.pre_evolution is not None and species.pre_evolution not in already_modified:
+ continue
+
+ type_map = list(range(18))
+ world.random.shuffle(type_map)
+
+ # We never want to map to the ??? type, so swap whatever index maps to ??? with ???
+ # which forces ??? to always map to itself. There are no pokemon which have the ??? type
+ mystery_type_index = type_map.index(9)
+ type_map[mystery_type_index], type_map[9] = type_map[9], type_map[mystery_type_index]
+
+ evolutions = [species]
+ while len(evolutions) > 0:
+ evolution = evolutions.pop()
+ evolution.types = (type_map[evolution.types[0]], type_map[evolution.types[1]])
+ already_modified.add(evolution.species_id)
+ evolutions += [world.modified_species[evo.species_id] for evo in evolution.evolutions]
+
+
+def randomize_wild_encounters(world: "PokemonEmeraldWorld") -> None:
+ if world.options.wild_pokemon == RandomizeWildPokemon.option_vanilla:
+ return
+
+ from collections import defaultdict
+
+ should_match_bst = world.options.wild_pokemon in {
+ RandomizeWildPokemon.option_match_base_stats,
+ RandomizeWildPokemon.option_match_base_stats_and_type,
+ }
+ should_match_type = world.options.wild_pokemon in {
+ RandomizeWildPokemon.option_match_type,
+ RandomizeWildPokemon.option_match_base_stats_and_type,
+ }
+ catch_em_all = world.options.dexsanity == Toggle.option_true
+
+ catch_em_all_placed = set()
+
+ priority_species = [data.constants["SPECIES_WAILORD"], data.constants["SPECIES_RELICANTH"]]
+
+ # Loop over map data to modify their encounter slots
+ map_names = list(world.modified_maps.keys())
+ world.random.shuffle(map_names)
+ for map_name in map_names:
+ placed_priority_species = False
+ map_data = world.modified_maps[map_name]
+
+ new_encounters: List[Optional[EncounterTableData]] = [None, None, None]
+ old_encounters = [map_data.land_encounters, map_data.water_encounters, map_data.fishing_encounters]
+
+ for i, table in enumerate(old_encounters):
+ if table is not None:
+ # Create a map from the original species to new species
+ # instead of just randomizing every slot.
+ # Force area 1-to-1 mapping, in other words.
+ species_old_to_new_map: Dict[int, int] = {}
+ for species_id in table.slots:
+ if species_id not in species_old_to_new_map:
+ if not placed_priority_species and len(priority_species) > 0:
+ new_species_id = priority_species.pop()
+ placed_priority_species = True
+ else:
+ original_species = data.species[species_id]
+
+ # Construct progressive tiers of blacklists that can be peeled back if they
+ # collectively cover too much of the pokedex. A lower index in `blacklists`
+ # indicates a more important set of species to avoid. Entries at `0` will
+ # always be blacklisted.
+ blacklists: Dict[int, List[Set[int]]] = defaultdict(list)
+
+ # Blacklist pokemon already on this table
+ blacklists[0].append(set(species_old_to_new_map.values()))
+
+ # If doing legendary hunt, blacklist Latios from wild encounters so
+ # it can be tracked as the roamer. Otherwise it may be impossible
+ # to tell whether a highlighted route is the roamer or a wild
+ # encounter.
+ if world.options.goal == Goal.option_legendary_hunt:
+ blacklists[0].append({data.constants["SPECIES_LATIOS"]})
+
+ # If dexsanity/catch 'em all mode, blacklist already placed species
+ # until every species has been placed once
+ if catch_em_all and len(catch_em_all_placed) < NUM_REAL_SPECIES:
+ blacklists[1].append(catch_em_all_placed)
+
+ # Blacklist from player options
+ blacklists[2].append(world.blacklisted_wilds)
+
+ # Type matching blacklist
+ if should_match_type:
+ blacklists[3].append({
+ species.species_id
+ for species in world.modified_species.values()
+ if not bool(set(species.types) & set(original_species.types))
+ })
+
+ merged_blacklist: Set[int] = set()
+ for max_priority in reversed(sorted(blacklists.keys())):
+ merged_blacklist = set()
+ for priority in blacklists.keys():
+ if priority <= max_priority:
+ for blacklist in blacklists[priority]:
+ merged_blacklist |= blacklist
+
+ if len(merged_blacklist) < NUM_REAL_SPECIES:
+ break
+ else:
+ raise RuntimeError("This should never happen")
+
+ candidates = [
+ species
+ for species in world.modified_species.values()
+ if species.species_id not in merged_blacklist
+ ]
+
+ if should_match_bst:
+ candidates = filter_species_by_nearby_bst(candidates, sum(original_species.base_stats))
+
+ new_species_id = world.random.choice(candidates).species_id
+ species_old_to_new_map[species_id] = new_species_id
+
+ if catch_em_all and map_data.name not in POSTGAME_MAPS:
+ catch_em_all_placed.add(new_species_id)
+
+ # Actually create the new list of slots and encounter table
+ new_slots: List[int] = []
+ for species_id in table.slots:
+ new_slots.append(species_old_to_new_map[species_id])
+
+ new_encounters[i] = EncounterTableData(new_slots, table.address)
+
+ # Rename event items for the new wild pokemon species
+ slot_category: Tuple[str, List[Tuple[Optional[str], range]]] = [
+ ("LAND", [(None, range(0, 12))]),
+ ("WATER", [(None, range(0, 5))]),
+ ("FISHING", [("OLD_ROD", range(0, 2)), ("GOOD_ROD", range(2, 5)), ("SUPER_ROD", range(5, 10))]),
+ ][i]
+ for j, new_species_id in enumerate(new_slots):
+ # Get the subcategory for rods
+ subcategory = next(sc for sc in slot_category[1] if j in sc[1])
+ subcategory_species = []
+ for k in subcategory[1]:
+ if new_slots[k] not in subcategory_species:
+ subcategory_species.append(new_slots[k])
+
+ # Create the name of the location that corresponds to this encounter slot
+ # Fishing locations include the rod name
+ subcategory_str = "" if subcategory[0] is None else "_" + subcategory[0]
+ encounter_location_index = subcategory_species.index(new_species_id) + 1
+ encounter_location_name = f"{map_data.name}_{slot_category[0]}_ENCOUNTERS{subcategory_str}_{encounter_location_index}"
+ try:
+ # Get the corresponding location and change the event name to reflect the new species
+ slot_location = world.multiworld.get_location(encounter_location_name, world.player)
+ slot_location.item.name = f"CATCH_{data.species[new_species_id].name}"
+ except KeyError:
+ pass # Map probably isn't included; should be careful here about bad encounter location names
+
+ map_data.land_encounters = new_encounters[0]
+ map_data.water_encounters = new_encounters[1]
+ map_data.fishing_encounters = new_encounters[2]
+
+
+def randomize_abilities(world: "PokemonEmeraldWorld") -> None:
+ if world.options.abilities == RandomizeAbilities.option_vanilla:
+ return
+
+ # Creating list of potential abilities
+ ability_label_to_value = {ability.label.lower(): ability.ability_id for ability in data.abilities}
+
+ ability_blacklist_labels = {"cacophony"} # Cacophony is defined and has a description, but no effect
+ option_ability_blacklist = world.options.ability_blacklist.value
+ if option_ability_blacklist is not None:
+ ability_blacklist_labels |= {ability_label.lower() for ability_label in option_ability_blacklist}
+
+ ability_blacklist = {ability_label_to_value[label] for label in ability_blacklist_labels}
+ ability_whitelist = [a.ability_id for a in data.abilities if a.ability_id not in ability_blacklist]
+
+ if world.options.abilities == RandomizeAbilities.option_follow_evolutions:
+ already_modified: Set[int] = set()
+
+ # Loops through species and only tries to modify abilities if the pokemon has no pre-evolution
+ # or if the pre-evolution has already been modified. Then tries to modify all species that evolve
+ # from this one which have the same abilities.
+ #
+ # The outer while loop only runs three times for vanilla ordering: Once for a first pass, once for
+ # Hitmonlee/Hitmonchan, and once to verify that there's nothing left to do.
+ while True:
+ had_clean_pass = True
+ for species in world.modified_species.values():
+ if species.species_id in already_modified:
+ continue
+ if species.pre_evolution is not None and species.pre_evolution not in already_modified:
+ continue
+
+ had_clean_pass = False
+
+ old_abilities = species.abilities
+ # 0 is the value for "no ability"; species with only 1 ability have the other set to 0
+ new_abilities = (
+ 0 if old_abilities[0] == 0 else world.random.choice(ability_whitelist),
+ 0 if old_abilities[1] == 0 else world.random.choice(ability_whitelist)
+ )
+
+ # Recursively modify the abilities of anything that evolves from this pokemon
+ # until the evolution doesn't have a matching set of abilities
+ evolutions = [species]
+ while len(evolutions) > 0:
+ evolution = evolutions.pop()
+ if evolution.abilities == old_abilities:
+ evolution.abilities = new_abilities
+ already_modified.add(evolution.species_id)
+ evolutions += [
+ world.modified_species[evolution.species_id]
+ for evolution in evolution.evolutions
+ if evolution.species_id not in already_modified
+ ]
+
+ if had_clean_pass:
+ break
+ else: # Not following evolutions
+ for species in world.modified_species.values():
+ old_abilities = species.abilities
+ # 0 is the value for "no ability"; species with only 1 ability have the other set to 0
+ new_abilities = (
+ 0 if old_abilities[0] == 0 else world.random.choice(ability_whitelist),
+ 0 if old_abilities[1] == 0 else world.random.choice(ability_whitelist)
+ )
+
+ species.abilities = new_abilities
+
+
+def randomize_learnsets(world: "PokemonEmeraldWorld") -> None:
+ if world.options.level_up_moves == LevelUpMoves.option_vanilla:
+ return
+
+ type_bias = world.options.move_match_type_bias.value
+ normal_bias = world.options.move_normal_type_bias.value
+
+ for species in world.modified_species.values():
+ old_learnset = species.learnset
+ new_learnset: List[LearnsetMove] = []
+
+ # All species have 4 moves at level 0. Up to 3 of them are blank spaces reserved for the
+ # start with four moves option. This either replaces those moves or leaves it blank
+ # and moves the cursor.
+ cursor = 0
+ while old_learnset[cursor].move_id == 0:
+ if world.options.level_up_moves == LevelUpMoves.option_start_with_four_moves:
+ new_move = get_random_move(world.random,
+ {move.move_id for move in new_learnset} | world.blacklisted_moves,
+ type_bias, normal_bias, species.types)
+ else:
+ new_move = 0
+ new_learnset.append(LearnsetMove(old_learnset[cursor].level, new_move))
+ cursor += 1
+
+ # All moves from here onward are actual moves.
+ while cursor < len(old_learnset):
+ # Guarantees the starter has a good damaging move; i will always be <=3 when entering this loop
+ if cursor == 3:
+ new_move = get_random_damaging_move(world.random, {move.move_id for move in new_learnset})
+ else:
+ new_move = get_random_move(world.random,
+ {move.move_id for move in new_learnset} | world.blacklisted_moves,
+ type_bias, normal_bias, species.types)
+ new_learnset.append(LearnsetMove(old_learnset[cursor].level, new_move))
+ cursor += 1
+
+ species.learnset = new_learnset
+
+
+def randomize_starters(world: "PokemonEmeraldWorld") -> None:
+ if world.options.starters == RandomizeStarters.option_vanilla:
+ return
+
+ should_match_bst = world.options.starters in {
+ RandomizeStarters.option_match_base_stats,
+ RandomizeStarters.option_match_base_stats_and_type,
+ }
+ should_match_type = world.options.starters in {
+ RandomizeStarters.option_match_type,
+ RandomizeStarters.option_match_base_stats_and_type,
+ }
+
+ new_starters: List[SpeciesData] = []
+
+ easter_egg_type, easter_egg_value = get_easter_egg(world.options.easter_egg.value)
+ if easter_egg_type == 1:
+ new_starters = [
+ world.modified_species[easter_egg_value],
+ world.modified_species[easter_egg_value],
+ world.modified_species[easter_egg_value]
+ ]
+ else:
+ for i, starter_id in enumerate(data.starters):
+ original_starter = data.species[starter_id]
+ type_blacklist = {
+ species.species_id
+ for species in world.modified_species.values()
+ if not bool(set(species.types) & set(original_starter.types))
+ } if should_match_type else set()
+
+ merged_blacklist = set(s.species_id for s in new_starters) | world.blacklisted_starters | type_blacklist
+ if len(merged_blacklist) == NUM_REAL_SPECIES:
+ merged_blacklist = set(s.species_id for s in new_starters) | world.blacklisted_starters
+ if len(merged_blacklist) == NUM_REAL_SPECIES:
+ merged_blacklist = set(s.species_id for s in new_starters)
+
+ candidates = [
+ species
+ for species in world.modified_species.values()
+ if species.species_id not in merged_blacklist
+ ]
+
+ if should_match_bst:
+ candidates = filter_species_by_nearby_bst(candidates, sum(original_starter.base_stats))
+
+ new_starters.append(world.random.choice(candidates))
+
+ world.modified_starters = (
+ new_starters[0].species_id,
+ new_starters[1].species_id,
+ new_starters[2].species_id
+ )
+
+ # Putting the unchosen starter onto the rival's team
+ # (trainer name, index of starter in team, whether the starter is evolved)
+ rival_teams: List[List[Tuple[str, int, bool]]] = [
+ [
+ ("TRAINER_BRENDAN_ROUTE_103_TREECKO", 0, False),
+ ("TRAINER_BRENDAN_RUSTBORO_TREECKO", 1, False),
+ ("TRAINER_BRENDAN_ROUTE_110_TREECKO", 2, True ),
+ ("TRAINER_BRENDAN_ROUTE_119_TREECKO", 2, True ),
+ ("TRAINER_BRENDAN_LILYCOVE_TREECKO", 3, True ),
+ ("TRAINER_MAY_ROUTE_103_TREECKO", 0, False),
+ ("TRAINER_MAY_RUSTBORO_TREECKO", 1, False),
+ ("TRAINER_MAY_ROUTE_110_TREECKO", 2, True ),
+ ("TRAINER_MAY_ROUTE_119_TREECKO", 2, True ),
+ ("TRAINER_MAY_LILYCOVE_TREECKO", 3, True ),
+ ],
+ [
+ ("TRAINER_BRENDAN_ROUTE_103_TORCHIC", 0, False),
+ ("TRAINER_BRENDAN_RUSTBORO_TORCHIC", 1, False),
+ ("TRAINER_BRENDAN_ROUTE_110_TORCHIC", 2, True ),
+ ("TRAINER_BRENDAN_ROUTE_119_TORCHIC", 2, True ),
+ ("TRAINER_BRENDAN_LILYCOVE_TORCHIC", 3, True ),
+ ("TRAINER_MAY_ROUTE_103_TORCHIC", 0, False),
+ ("TRAINER_MAY_RUSTBORO_TORCHIC", 1, False),
+ ("TRAINER_MAY_ROUTE_110_TORCHIC", 2, True ),
+ ("TRAINER_MAY_ROUTE_119_TORCHIC", 2, True ),
+ ("TRAINER_MAY_LILYCOVE_TORCHIC", 3, True ),
+ ],
+ [
+ ("TRAINER_BRENDAN_ROUTE_103_MUDKIP", 0, False),
+ ("TRAINER_BRENDAN_RUSTBORO_MUDKIP", 1, False),
+ ("TRAINER_BRENDAN_ROUTE_110_MUDKIP", 2, True ),
+ ("TRAINER_BRENDAN_ROUTE_119_MUDKIP", 2, True ),
+ ("TRAINER_BRENDAN_LILYCOVE_MUDKIP", 3, True ),
+ ("TRAINER_MAY_ROUTE_103_MUDKIP", 0, False),
+ ("TRAINER_MAY_RUSTBORO_MUDKIP", 1, False),
+ ("TRAINER_MAY_ROUTE_110_MUDKIP", 2, True ),
+ ("TRAINER_MAY_ROUTE_119_MUDKIP", 2, True ),
+ ("TRAINER_MAY_LILYCOVE_MUDKIP", 3, True ),
+ ],
+ ]
+
+ for i, starter in enumerate([new_starters[1], new_starters[2], new_starters[0]]):
+ potential_evolutions = [evolution.species_id for evolution in starter.evolutions]
+ picked_evolution = starter.species_id
+ if len(potential_evolutions) > 0:
+ picked_evolution = world.random.choice(potential_evolutions)
+
+ for trainer_name, starter_position, is_evolved in rival_teams[i]:
+ trainer_data = world.modified_trainers[data.constants[trainer_name]]
+ trainer_data.party.pokemon[starter_position].species_id = picked_evolution if is_evolved else starter.species_id
+
+
+def randomize_legendary_encounters(world: "PokemonEmeraldWorld") -> None:
+ if world.options.legendary_encounters == RandomizeLegendaryEncounters.option_vanilla:
+ return
+ elif world.options.legendary_encounters == RandomizeLegendaryEncounters.option_shuffle:
+ # Just take the existing species and shuffle them
+ shuffled_species = [encounter.species_id for encounter in data.legendary_encounters]
+ world.random.shuffle(shuffled_species)
+
+ for i, encounter in enumerate(data.legendary_encounters):
+ world.modified_legendary_encounters.append(MiscPokemonData(
+ shuffled_species[i],
+ encounter.address
+ ))
+ else:
+ should_match_bst = world.options.legendary_encounters in {
+ RandomizeLegendaryEncounters.option_match_base_stats,
+ RandomizeLegendaryEncounters.option_match_base_stats_and_type
+ }
+ should_match_type = world.options.legendary_encounters in {
+ RandomizeLegendaryEncounters.option_match_type,
+ RandomizeLegendaryEncounters.option_match_base_stats_and_type
+ }
+
+ for encounter in data.legendary_encounters:
+ original_species = world.modified_species[encounter.species_id]
+
+ candidates = list(world.modified_species.values())
+ if should_match_type:
+ candidates = [
+ species
+ for species in candidates
+ if bool(set(species.types) & set(original_species.types))
+ ]
+ if should_match_bst:
+ candidates = filter_species_by_nearby_bst(candidates, sum(original_species.base_stats))
+
+ world.modified_legendary_encounters.append(MiscPokemonData(
+ world.random.choice(candidates).species_id,
+ encounter.address
+ ))
+
+
+def randomize_misc_pokemon(world: "PokemonEmeraldWorld") -> None:
+ if world.options.misc_pokemon == RandomizeMiscPokemon.option_vanilla:
+ return
+ elif world.options.misc_pokemon == RandomizeMiscPokemon.option_shuffle:
+ # Just take the existing species and shuffle them
+ shuffled_species = [encounter.species_id for encounter in data.misc_pokemon]
+ world.random.shuffle(shuffled_species)
+
+ world.modified_misc_pokemon = []
+ for i, encounter in enumerate(data.misc_pokemon):
+ world.modified_misc_pokemon.append(MiscPokemonData(
+ shuffled_species[i],
+ encounter.address
+ ))
+ else:
+ should_match_bst = world.options.misc_pokemon in {
+ RandomizeMiscPokemon.option_match_base_stats,
+ RandomizeMiscPokemon.option_match_base_stats_and_type,
+ }
+ should_match_type = world.options.misc_pokemon in {
+ RandomizeMiscPokemon.option_match_type,
+ RandomizeMiscPokemon.option_match_base_stats_and_type,
+ }
+
+ for encounter in data.misc_pokemon:
+ original_species = world.modified_species[encounter.species_id]
+
+ candidates = list(world.modified_species.values())
+ if should_match_type:
+ candidates = [
+ species
+ for species in candidates
+ if bool(set(species.types) & set(original_species.types))
+ ]
+ if should_match_bst:
+ candidates = filter_species_by_nearby_bst(candidates, sum(original_species.base_stats))
+
+ player_filtered_candidates = [
+ species
+ for species in candidates
+ if species.species_id not in world.blacklisted_wilds
+ ]
+ if len(player_filtered_candidates) > 0:
+ candidates = player_filtered_candidates
+
+ world.modified_misc_pokemon.append(MiscPokemonData(
+ world.random.choice(candidates).species_id,
+ encounter.address
+ ))
+
+
+def randomize_tm_hm_compatibility(world: "PokemonEmeraldWorld") -> None:
+ for species in world.modified_species.values():
+ # TM and HM compatibility is stored as a 64-bit bitfield
+ combatibility_array = int_to_bool_array(species.tm_hm_compatibility)
+
+ # TMs
+ if world.options.tm_tutor_compatibility != TmTutorCompatibility.special_range_names["vanilla"]:
+ for i in range(0, 50):
+ combatibility_array[i] = world.random.random() < world.options.tm_tutor_compatibility / 100
+
+ # HMs
+ if world.options.hm_compatibility != HmCompatibility.special_range_names["vanilla"]:
+ for i in range(50, 58):
+ combatibility_array[i] = world.random.random() < world.options.hm_compatibility / 100
+
+ species.tm_hm_compatibility = bool_array_to_int(combatibility_array)
diff --git a/worlds/pokemon_emerald/regions.py b/worlds/pokemon_emerald/regions.py
index e8f6d26e08ce..b74f5f5ebf76 100644
--- a/worlds/pokemon_emerald/regions.py
+++ b/worlds/pokemon_emerald/regions.py
@@ -1,9 +1,9 @@
"""
Functions related to AP regions for Pokemon Emerald (see ./data/regions for region definitions)
"""
-from typing import TYPE_CHECKING, Dict, List, Tuple
+from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple
-from BaseClasses import ItemClassification, Region
+from BaseClasses import CollectionState, ItemClassification, Region
from .data import data
from .items import PokemonEmeraldItem
@@ -18,9 +18,80 @@ def create_regions(world: "PokemonEmeraldWorld") -> Dict[str, Region]:
Iterates through regions created from JSON to create regions and adds them to the multiworld.
Also creates and places events and connects regions via warps and the exits defined in the JSON.
"""
+ # Used in connect_to_map_encounters. Splits encounter categories into "subcategories" and gives them names
+ # and rules so the rods can only access their specific slots.
+ encounter_categories: Dict[str, List[Tuple[Optional[str], range, Optional[Callable[[CollectionState], bool]]]]] = {
+ "LAND": [(None, range(0, 12), None)],
+ "WATER": [(None, range(0, 5), None)],
+ "FISHING": [
+ ("OLD_ROD", range(0, 2), lambda state: state.has("Old Rod", world.player)),
+ ("GOOD_ROD", range(2, 5), lambda state: state.has("Good Rod", world.player)),
+ ("SUPER_ROD", range(5, 10), lambda state: state.has("Super Rod", world.player)),
+ ],
+ }
+
+ def connect_to_map_encounters(region: Region, map_name: str, include_slots: Tuple[bool, bool, bool]):
+ """
+ Connects the provided region to the corresponding wild encounters for the given parent map.
+
+ Each in-game map may have a non-physical Region for encountering wild pokemon in each of the three categories
+ land, water, and fishing. Region data defines whether a given region includes places where those encounters can
+ be accessed (i.e. whether the region has tall grass, a river bank, is on water, etc.).
+
+ These regions are created lazily and dynamically so as not to bother with unused maps.
+ """
+ # For each of land, water, and fishing, connect the region if indicated by include_slots
+ for i, encounter_category in enumerate(encounter_categories.items()):
+ if include_slots[i]:
+ region_name = f"{map_name}_{encounter_category[0]}_ENCOUNTERS"
+
+ # If the region hasn't been created yet, create it now
+ try:
+ encounter_region = world.multiworld.get_region(region_name, world.player)
+ except KeyError:
+ encounter_region = Region(region_name, world.player, world.multiworld)
+ encounter_slots = getattr(data.maps[map_name], f"{encounter_category[0].lower()}_encounters").slots
+
+ # Subcategory is for splitting fishing rods; land and water only have one subcategory
+ for subcategory in encounter_category[1]:
+ # Want to create locations per species, not per slot
+ # encounter_categories includes info on which slots belong to which subcategory
+ unique_species = []
+ for j, species_id in enumerate(encounter_slots):
+ if j in subcategory[1] and not species_id in unique_species:
+ unique_species.append(species_id)
+
+ # Create a location for the species
+ for j, species_id in enumerate(unique_species):
+ encounter_location = PokemonEmeraldLocation(
+ world.player,
+ f"{region_name}{'_' + subcategory[0] if subcategory[0] is not None else ''}_{j + 1}",
+ None,
+ encounter_region
+ )
+ encounter_location.show_in_spoiler = False
+
+ # Add access rule
+ if subcategory[2] is not None:
+ encounter_location.access_rule = subcategory[2]
+
+ # Fill the location with an event for catching that species
+ encounter_location.place_locked_item(PokemonEmeraldItem(
+ f"CATCH_{data.species[species_id].name}",
+ ItemClassification.progression_skip_balancing,
+ None,
+ world.player
+ ))
+ encounter_region.locations.append(encounter_location)
+
+ # Add the new encounter region to the multiworld
+ world.multiworld.regions.append(encounter_region)
+
+ # Encounter region exists, just connect to it
+ region.connect(encounter_region, f"{region.name} -> {region_name}")
+
regions: Dict[str, Region] = {}
connections: List[Tuple[str, str, str]] = []
-
for region_name, region_data in data.regions.items():
new_region = Region(region_name, world.player, world.multiworld)
@@ -40,6 +111,9 @@ def create_regions(world: "PokemonEmeraldWorld") -> Dict[str, Region]:
regions[region_name] = new_region
+ connect_to_map_encounters(new_region, region_data.parent_map.name,
+ (region_data.has_grass, region_data.has_water, region_data.has_fishing))
+
for name, source, dest in connections:
regions[source].connect(regions[dest], name)
diff --git a/worlds/pokemon_emerald/rom.py b/worlds/pokemon_emerald/rom.py
index 156410553cf6..60318c306d82 100644
--- a/worlds/pokemon_emerald/rom.py
+++ b/worlds/pokemon_emerald/rom.py
@@ -1,24 +1,110 @@
"""
Classes and functions related to creating a ROM patch
"""
+import copy
import os
import pkgutil
-from typing import TYPE_CHECKING, List, Tuple
+from typing import TYPE_CHECKING, Dict, List, Tuple
import bsdiff4
from worlds.Files import APDeltaPatch
from settings import get_settings
-from .data import PokemonEmeraldData, TrainerPokemonDataTypeEnum, data
+from .data import TrainerPokemonDataTypeEnum, BASE_OFFSET, data
from .items import reverse_offset_item_value
-from .options import RandomizeWildPokemon, RandomizeTrainerParties, EliteFourRequirement, NormanRequirement
-from .pokemon import get_random_species
+from .options import (RandomizeWildPokemon, RandomizeTrainerParties, EliteFourRequirement, NormanRequirement,
+ MatchTrainerLevels)
+from .pokemon import HM_MOVES, get_random_move
+from .util import bool_array_to_int, encode_string, get_easter_egg
if TYPE_CHECKING:
from . import PokemonEmeraldWorld
+_LOOPING_MUSIC = [
+ "MUS_GSC_ROUTE38", "MUS_GSC_PEWTER", "MUS_ROUTE101", "MUS_ROUTE110", "MUS_ROUTE120", "MUS_ROUTE122",
+ "MUS_PETALBURG", "MUS_OLDALE", "MUS_GYM", "MUS_SURF", "MUS_PETALBURG_WOODS", "MUS_LILYCOVE_MUSEUM",
+ "MUS_OCEANIC_MUSEUM", "MUS_ENCOUNTER_GIRL", "MUS_ENCOUNTER_MALE", "MUS_ABANDONED_SHIP", "MUS_FORTREE",
+ "MUS_BIRCH_LAB", "MUS_B_TOWER_RS", "MUS_ENCOUNTER_SWIMMER", "MUS_CAVE_OF_ORIGIN", "MUS_ENCOUNTER_RICH",
+ "MUS_VERDANTURF", "MUS_RUSTBORO", "MUS_POKE_CENTER", "MUS_CAUGHT", "MUS_VICTORY_GYM_LEADER", "MUS_VICTORY_LEAGUE",
+ "MUS_VICTORY_WILD", "MUS_C_VS_LEGEND_BEAST", "MUS_ROUTE104", "MUS_ROUTE119", "MUS_CYCLING", "MUS_POKE_MART",
+ "MUS_LITTLEROOT", "MUS_MT_CHIMNEY", "MUS_ENCOUNTER_FEMALE", "MUS_LILYCOVE", "MUS_DESERT", "MUS_HELP",
+ "MUS_UNDERWATER", "MUS_VICTORY_TRAINER", "MUS_ENCOUNTER_MAY", "MUS_ENCOUNTER_INTENSE", "MUS_ENCOUNTER_COOL",
+ "MUS_ROUTE113", "MUS_ENCOUNTER_AQUA", "MUS_FOLLOW_ME", "MUS_ENCOUNTER_BRENDAN", "MUS_EVER_GRANDE",
+ "MUS_ENCOUNTER_SUSPICIOUS", "MUS_VICTORY_AQUA_MAGMA", "MUS_GAME_CORNER", "MUS_DEWFORD", "MUS_SAFARI_ZONE",
+ "MUS_VICTORY_ROAD", "MUS_AQUA_MAGMA_HIDEOUT", "MUS_SAILING", "MUS_MT_PYRE", "MUS_SLATEPORT", "MUS_MT_PYRE_EXTERIOR",
+ "MUS_SCHOOL", "MUS_HALL_OF_FAME", "MUS_FALLARBOR", "MUS_SEALED_CHAMBER", "MUS_CONTEST_WINNER", "MUS_CONTEST",
+ "MUS_ENCOUNTER_MAGMA", "MUS_ABNORMAL_WEATHER", "MUS_WEATHER_GROUDON", "MUS_SOOTOPOLIS", "MUS_HALL_OF_FAME_ROOM",
+ "MUS_TRICK_HOUSE", "MUS_ENCOUNTER_TWINS", "MUS_ENCOUNTER_ELITE_FOUR", "MUS_ENCOUNTER_HIKER", "MUS_CONTEST_LOBBY",
+ "MUS_ENCOUNTER_INTERVIEWER", "MUS_ENCOUNTER_CHAMPION", "MUS_B_FRONTIER", "MUS_B_ARENA", "MUS_B_PYRAMID",
+ "MUS_B_PYRAMID_TOP", "MUS_B_PALACE", "MUS_B_TOWER", "MUS_B_DOME", "MUS_B_PIKE", "MUS_B_FACTORY", "MUS_VS_RAYQUAZA",
+ "MUS_VS_FRONTIER_BRAIN", "MUS_VS_MEW", "MUS_B_DOME_LOBBY", "MUS_VS_WILD", "MUS_VS_AQUA_MAGMA", "MUS_VS_TRAINER",
+ "MUS_VS_GYM_LEADER", "MUS_VS_CHAMPION", "MUS_VS_REGI", "MUS_VS_KYOGRE_GROUDON", "MUS_VS_RIVAL", "MUS_VS_ELITE_FOUR",
+ "MUS_VS_AQUA_MAGMA_LEADER", "MUS_RG_FOLLOW_ME", "MUS_RG_GAME_CORNER", "MUS_RG_ROCKET_HIDEOUT", "MUS_RG_GYM",
+ "MUS_RG_CINNABAR", "MUS_RG_LAVENDER", "MUS_RG_CYCLING", "MUS_RG_ENCOUNTER_ROCKET", "MUS_RG_ENCOUNTER_GIRL",
+ "MUS_RG_ENCOUNTER_BOY", "MUS_RG_HALL_OF_FAME", "MUS_RG_VIRIDIAN_FOREST", "MUS_RG_MT_MOON", "MUS_RG_POKE_MANSION",
+ "MUS_RG_ROUTE1", "MUS_RG_ROUTE24", "MUS_RG_ROUTE3", "MUS_RG_ROUTE11", "MUS_RG_VICTORY_ROAD", "MUS_RG_VS_GYM_LEADER",
+ "MUS_RG_VS_TRAINER", "MUS_RG_VS_WILD", "MUS_RG_VS_CHAMPION", "MUS_RG_PALLET", "MUS_RG_OAK_LAB", "MUS_RG_OAK",
+ "MUS_RG_POKE_CENTER", "MUS_RG_SS_ANNE", "MUS_RG_SURF", "MUS_RG_POKE_TOWER", "MUS_RG_SILPH", "MUS_RG_FUCHSIA",
+ "MUS_RG_CELADON", "MUS_RG_VICTORY_TRAINER", "MUS_RG_VICTORY_WILD", "MUS_RG_VICTORY_GYM_LEADER", "MUS_RG_VERMILLION",
+ "MUS_RG_PEWTER", "MUS_RG_ENCOUNTER_RIVAL", "MUS_RG_RIVAL_EXIT", "MUS_RG_CAUGHT", "MUS_RG_POKE_JUMP",
+ "MUS_RG_UNION_ROOM", "MUS_RG_NET_CENTER", "MUS_RG_MYSTERY_GIFT", "MUS_RG_BERRY_PICK", "MUS_RG_SEVII_CAVE",
+ "MUS_RG_TEACHY_TV_SHOW", "MUS_RG_SEVII_ROUTE", "MUS_RG_SEVII_DUNGEON", "MUS_RG_SEVII_123", "MUS_RG_SEVII_45",
+ "MUS_RG_SEVII_67", "MUS_RG_VS_DEOXYS", "MUS_RG_VS_MEWTWO", "MUS_RG_VS_LEGEND", "MUS_RG_ENCOUNTER_GYM_LEADER",
+ "MUS_RG_ENCOUNTER_DEOXYS", "MUS_RG_TRAINER_TOWER", "MUS_RG_SLOW_PALLET", "MUS_RG_TEACHY_TV_MENU",
+]
+
+_FANFARES: Dict[str, int] = {
+ "MUS_LEVEL_UP": 80,
+ "MUS_OBTAIN_ITEM": 160,
+ "MUS_EVOLVED": 220,
+ "MUS_OBTAIN_TMHM": 220,
+ "MUS_HEAL": 160,
+ "MUS_OBTAIN_BADGE": 340,
+ "MUS_MOVE_DELETED": 180,
+ "MUS_OBTAIN_BERRY": 120,
+ "MUS_AWAKEN_LEGEND": 710,
+ "MUS_SLOTS_JACKPOT": 250,
+ "MUS_SLOTS_WIN": 150,
+ "MUS_TOO_BAD": 160,
+ "MUS_RG_POKE_FLUTE": 450,
+ "MUS_RG_OBTAIN_KEY_ITEM": 170,
+ "MUS_RG_DEX_RATING": 196,
+ "MUS_OBTAIN_B_POINTS": 313,
+ "MUS_OBTAIN_SYMBOL": 318,
+ "MUS_REGISTER_MATCH_CALL": 135,
+}
+
+CAVE_EVENT_NAME_TO_ID = {
+ "TERRA_CAVE_ROUTE_114_1": 1,
+ "TERRA_CAVE_ROUTE_114_2": 2,
+ "TERRA_CAVE_ROUTE_115_1": 3,
+ "TERRA_CAVE_ROUTE_115_2": 4,
+ "TERRA_CAVE_ROUTE_116_1": 5,
+ "TERRA_CAVE_ROUTE_116_2": 6,
+ "TERRA_CAVE_ROUTE_118_1": 7,
+ "TERRA_CAVE_ROUTE_118_2": 8,
+ "MARINE_CAVE_ROUTE_105_1": 9,
+ "MARINE_CAVE_ROUTE_105_2": 10,
+ "MARINE_CAVE_ROUTE_125_1": 11,
+ "MARINE_CAVE_ROUTE_125_2": 12,
+ "MARINE_CAVE_ROUTE_127_1": 13,
+ "MARINE_CAVE_ROUTE_127_2": 14,
+ "MARINE_CAVE_ROUTE_129_1": 15,
+ "MARINE_CAVE_ROUTE_129_2": 16,
+}
+
+
+def _set_bytes_le(byte_array: bytearray, address: int, size: int, value: int) -> None:
+ offset = 0
+ while size > 0:
+ byte_array[address + offset] = value & 0xFF
+ value = value >> 8
+ offset += 1
+ size -= 1
+
+
class PokemonEmeraldDeltaPatch(APDeltaPatch):
game = "Pokemon Emerald"
hash = "605b89b67018abcea91e693a4dd25be3"
@@ -30,60 +116,128 @@ def get_source_data(cls) -> bytes:
return get_base_rom_as_bytes()
-location_visited_event_to_id_map = {
- "EVENT_VISITED_LITTLEROOT_TOWN": 0,
- "EVENT_VISITED_OLDALE_TOWN": 1,
- "EVENT_VISITED_PETALBURG_CITY": 2,
- "EVENT_VISITED_RUSTBORO_CITY": 3,
- "EVENT_VISITED_DEWFORD_TOWN": 4,
- "EVENT_VISITED_SLATEPORT_CITY": 5,
- "EVENT_VISITED_MAUVILLE_CITY": 6,
- "EVENT_VISITED_VERDANTURF_TOWN": 7,
- "EVENT_VISITED_FALLARBOR_TOWN": 8,
- "EVENT_VISITED_LAVARIDGE_TOWN": 9,
- "EVENT_VISITED_FORTREE_CITY": 10,
- "EVENT_VISITED_LILYCOVE_CITY": 11,
- "EVENT_VISITED_MOSSDEEP_CITY": 12,
- "EVENT_VISITED_SOOTOPOLIS_CITY": 13,
- "EVENT_VISITED_PACIFIDLOG_TOWN": 14,
- "EVENT_VISITED_EVER_GRANDE_CITY": 15,
- "EVENT_VISITED_BATTLE_FRONTIER": 16,
- "EVENT_VISITED_SOUTHERN_ISLAND": 17
-}
-
-
-def generate_output(world: "PokemonEmeraldWorld", output_directory: str) -> None:
+def create_patch(world: "PokemonEmeraldWorld", output_directory: str) -> None:
base_rom = get_base_rom_as_bytes()
base_patch = pkgutil.get_data(__name__, "data/base_patch.bsdiff4")
patched_rom = bytearray(bsdiff4.patch(base_rom, base_patch))
- # Set item values
+ # Set free fly location
+ if world.options.free_fly_location:
+ _set_bytes_le(
+ patched_rom,
+ data.rom_addresses["gArchipelagoOptions"] + 0x20,
+ 1,
+ world.free_fly_location_id
+ )
+
+ location_info: List[Tuple[int, int, str]] = []
for location in world.multiworld.get_locations(world.player):
- # Set free fly location
if location.address is None:
- if world.options.free_fly_location and location.name == "EVENT_VISITED_LITTLEROOT_TOWN":
- _set_bytes_little_endian(
- patched_rom,
- data.rom_addresses["gArchipelagoOptions"] + 0x16,
- 1,
- world.free_fly_location_id
- )
continue
- if location.item and location.item.player == world.player:
- _set_bytes_little_endian(
- patched_rom,
- location.rom_address,
- 2,
- reverse_offset_item_value(location.item.code)
- )
+ if location.item is None:
+ continue
+
+ # Set local item values
+ if not world.options.remote_items and location.item.player == world.player:
+ if type(location.item_address) is int:
+ _set_bytes_le(
+ patched_rom,
+ location.item_address,
+ 2,
+ reverse_offset_item_value(location.item.code)
+ )
+ elif type(location.item_address) is list:
+ for address in location.item_address:
+ _set_bytes_le(patched_rom, address, 2, reverse_offset_item_value(location.item.code))
else:
- _set_bytes_little_endian(
- patched_rom,
- location.rom_address,
- 2,
- data.constants["ITEM_ARCHIPELAGO_PROGRESSION"]
- )
+ if type(location.item_address) is int:
+ _set_bytes_le(
+ patched_rom,
+ location.item_address,
+ 2,
+ data.constants["ITEM_ARCHIPELAGO_PROGRESSION"]
+ )
+ elif type(location.item_address) is list:
+ for address in location.item_address:
+ _set_bytes_le(patched_rom, address, 2, data.constants["ITEM_ARCHIPELAGO_PROGRESSION"])
+
+ # Creates a list of item information to store in tables later. Those tables are used to display the item and
+ # player name in a text box. In the case of not enough space, the game will default to "found an ARCHIPELAGO
+ # ITEM"
+ location_info.append((location.address - BASE_OFFSET, location.item.player, location.item.name))
+
+ if world.options.trainersanity:
+ # Duplicate entries for rival fights
+ # For each of the 5 fights, there are 6 variations that have to be accounted for (for starters * genders)
+ # The Brendan Mudkip is used as a proxy in the rest of the AP code
+ for locale in ["ROUTE_103", "ROUTE_110", "ROUTE_119", "RUSTBORO", "LILYCOVE"]:
+ location = world.multiworld.get_location(data.locations[f"TRAINER_BRENDAN_{locale}_MUDKIP_REWARD"].label, world.player)
+ alternates = [
+ f"TRAINER_BRENDAN_{locale}_TREECKO",
+ f"TRAINER_BRENDAN_{locale}_TORCHIC",
+ f"TRAINER_MAY_{locale}_MUDKIP",
+ f"TRAINER_MAY_{locale}_TREECKO",
+ f"TRAINER_MAY_{locale}_TORCHIC",
+ ]
+ location_info.extend((
+ data.constants["TRAINER_FLAGS_START"] + data.constants[trainer],
+ location.item.player,
+ location.item.name
+ ) for trainer in alternates)
+
+ player_name_ids: Dict[str, int] = {world.multiworld.player_name[world.player]: 0}
+ item_name_offsets: Dict[str, int] = {}
+ next_item_name_offset = 0
+ for i, (flag, item_player, item_name) in enumerate(sorted(location_info, key=lambda t: t[0])):
+ # The player's own items are still set in the table with the value 0 to indicate the game should not show any
+ # message (the message for receiving an item will pop up when the client eventually gives it to them).
+ # In race mode, no item location data is included, and only recieved (or own) items will show any text box.
+ if item_player == world.player or world.multiworld.is_race:
+ _set_bytes_le(patched_rom, data.rom_addresses["gArchipelagoNameTable"] + (i * 5) + 0, 2, flag)
+ _set_bytes_le(patched_rom, data.rom_addresses["gArchipelagoNameTable"] + (i * 5) + 2, 2, 0)
+ _set_bytes_le(patched_rom, data.rom_addresses["gArchipelagoNameTable"] + (i * 5) + 4, 1, 0)
+ else:
+ player_name = world.multiworld.player_name[item_player]
+
+ if player_name not in player_name_ids:
+ # Only space for 50 player names
+ if len(player_name_ids) >= 50:
+ continue
+
+ player_name_ids[player_name] = len(player_name_ids)
+ for j, b in enumerate(encode_string(player_name, 17)):
+ _set_bytes_le(
+ patched_rom,
+ data.rom_addresses["gArchipelagoPlayerNames"] + (player_name_ids[player_name] * 17) + j,
+ 1,
+ b
+ )
+
+ if item_name not in item_name_offsets:
+ if len(item_name) > 35:
+ item_name = item_name[:34] + "…"
+
+ # Only 36 * 250 bytes for item names
+ if next_item_name_offset + len(item_name) + 1 > 36 * 250:
+ continue
+
+ item_name_offsets[item_name] = next_item_name_offset
+ next_item_name_offset += len(item_name) + 1
+ for j, b in enumerate(encode_string(item_name) + b"\xFF"):
+ _set_bytes_le(
+ patched_rom,
+ data.rom_addresses["gArchipelagoItemNames"] + (item_name_offsets[item_name]) + j,
+ 1,
+ b
+ )
+
+ # There should always be enough space for one entry per location
+ _set_bytes_le(patched_rom, data.rom_addresses["gArchipelagoNameTable"] + (i * 5) + 0, 2, flag)
+ _set_bytes_le(patched_rom, data.rom_addresses["gArchipelagoNameTable"] + (i * 5) + 2, 2, item_name_offsets[item_name])
+ _set_bytes_le(patched_rom, data.rom_addresses["gArchipelagoNameTable"] + (i * 5) + 4, 1, player_name_ids[player_name])
+
+ easter_egg = get_easter_egg(world.options.easter_egg.value)
# Set start inventory
start_inventory = world.options.start_inventory.value.copy()
@@ -128,28 +282,34 @@ def generate_output(world: "PokemonEmeraldWorld", output_directory: str) -> None
for i, slot in enumerate(pc_slots):
address = data.rom_addresses["sNewGamePCItems"] + (i * 4)
item = reverse_offset_item_value(world.item_name_to_id[slot[0]])
- _set_bytes_little_endian(patched_rom, address + 0, 2, item)
- _set_bytes_little_endian(patched_rom, address + 2, 2, slot[1])
+ _set_bytes_le(patched_rom, address + 0, 2, item)
+ _set_bytes_le(patched_rom, address + 2, 2, slot[1])
# Set species data
- _set_species_info(world, patched_rom)
+ _set_species_info(world, patched_rom, easter_egg)
# Set encounter tables
if world.options.wild_pokemon != RandomizeWildPokemon.option_vanilla:
_set_encounter_tables(world, patched_rom)
# Set opponent data
- if world.options.trainer_parties != RandomizeTrainerParties.option_vanilla:
- _set_opponents(world, patched_rom)
+ if world.options.trainer_parties != RandomizeTrainerParties.option_vanilla or easter_egg[0] == 2:
+ _set_opponents(world, patched_rom, easter_egg)
- # Set static pokemon
- _set_static_encounters(world, patched_rom)
+ # Set legendary pokemon
+ _set_legendary_encounters(world, patched_rom)
+
+ # Set misc pokemon
+ _set_misc_pokemon(world, patched_rom)
# Set starters
_set_starters(world, patched_rom)
# Set TM moves
- _set_tm_moves(world, patched_rom)
+ _set_tm_moves(world, patched_rom, easter_egg)
+
+ # Randomize move tutor moves
+ _randomize_move_tutor_moves(world, patched_rom, easter_egg)
# Set TM/HM compatibility
_set_tmhm_compatibility(world, patched_rom)
@@ -160,97 +320,164 @@ def generate_output(world: "PokemonEmeraldWorld", output_directory: str) -> None
# Options
# struct ArchipelagoOptions
# {
- # /* 0x00 */ bool8 advanceTextWithHoldA;
- # /* 0x01 */ bool8 isFerryEnabled;
- # /* 0x02 */ bool8 areTrainersBlind;
- # /* 0x03 */ bool8 canFlyWithoutBadge;
- # /* 0x04 */ u16 expMultiplierNumerator;
- # /* 0x06 */ u16 expMultiplierDenominator;
- # /* 0x08 */ u16 birchPokemon;
- # /* 0x0A */ bool8 guaranteedCatch;
- # /* 0x0B */ bool8 betterShopsEnabled;
- # /* 0x0C */ bool8 eliteFourRequiresGyms;
- # /* 0x0D */ u8 eliteFourRequiredCount;
- # /* 0x0E */ bool8 normanRequiresGyms;
- # /* 0x0F */ u8 normanRequiredCount;
- # /* 0x10 */ u8 startingBadges;
- # /* 0x11 */ u8 receivedItemMessageFilter; // 0 = Show All; 1 = Show Progression Only; 2 = Show None
- # /* 0x12 */ bool8 reusableTms;
- # /* 0x14 */ u16 removedBlockers;
- # /* 0x13 */ bool8 addRoute115Boulders;
- # /* 0x14 */ u16 removedBlockers;
- # /* 0x14 */ u16 removedBlockers;
- # /* 0x16 */ u8 freeFlyLocation;
+ # /* 0x00 */ u16 birchPokemon;
+ # /* 0x02 */ bool8 advanceTextWithHoldA;
+ # /* 0x03 */ u8 receivedItemMessageFilter; // 0 = Show All; 1 = Show Progression Only; 2 = Show None
+ # /* 0x04 */ bool8 betterShopsEnabled;
+ # /* 0x05 */ bool8 reusableTms;
+ # /* 0x06 */ bool8 guaranteedCatch;
+ # /* 0x07 */ bool8 purgeSpinners;
+ # /* 0x08 */ bool8 areTrainersBlind;
+ # /* 0x09 */ u16 expMultiplierNumerator;
+ # /* 0x0B */ u16 expMultiplierDenominator;
+ # /* 0x0D */ bool8 matchTrainerLevels;
+ # /* 0x0E */ s8 matchTrainerLevelBonus;
+ # /* 0x0F */ bool8 eliteFourRequiresGyms;
+ # /* 0x10 */ u8 eliteFourRequiredCount;
+ # /* 0x11 */ bool8 normanRequiresGyms;
+ # /* 0x12 */ u8 normanRequiredCount;
+ # /* 0x13 */ u8 startingBadges;
+ # /* 0x14 */ u32 hmTotalBadgeRequirements;
+ # /* 0x18 */ u8 hmSpecificBadgeRequirements[8];
+ # /* 0x20 */ u8 freeFlyLocation;
+ # /* 0x21 */ u8 terraCaveLocationId:4;
+ # u8 marineCaveLocationId:4;
+ # /* 0x22 */ bool8 addRoute115Boulders;
+ # /* 0x23 */ bool8 addBumpySlopes;
+ # /* 0x24 */ bool8 modifyRoute118;
+ # /* 0x25 */ u16 removedBlockers;
+ # /* 0x27 */ bool8 berryTreesRandomized;
+ # /* 0x28 */ bool8 isDexsanity;
+ # /* 0x29 */ bool8 isTrainersanity;
+ # /* 0x2A */ bool8 isWarpRando;
+ # /* 0x2B */ u8 activeEasterEgg;
+ # /* 0x2C */ bool8 normalizeEncounterRates;
+ # /* 0x2D */ bool8 allowWonderTrading;
+ # /* 0x2E */ u16 matchTrainerLevelMultiplierNumerator;
+ # /* 0x30 */ u16 matchTrainerLevelMultiplierDenominator;
+ # /* 0x32 */ bool8 allowSkippingFanfares;
# };
options_address = data.rom_addresses["gArchipelagoOptions"]
+ # Set Birch pokemon
+ _set_bytes_le(
+ patched_rom,
+ options_address + 0x00,
+ 2,
+ world.random.choice(list(data.species.keys()))
+ )
+
# Set hold A to advance text
- turbo_a = 1 if world.options.turbo_a else 0
- _set_bytes_little_endian(patched_rom, options_address + 0x00, 1, turbo_a)
+ _set_bytes_le(patched_rom, options_address + 0x02, 1, 1 if world.options.turbo_a else 0)
- # Set ferry enabled
- enable_ferry = 1 if world.options.enable_ferry else 0
- _set_bytes_little_endian(patched_rom, options_address + 0x01, 1, enable_ferry)
+ # Set receive item messages type
+ _set_bytes_le(patched_rom, options_address + 0x03, 1, world.options.receive_item_messages.value)
- # Set blind trainers
- blind_trainers = 1 if world.options.blind_trainers else 0
- _set_bytes_little_endian(patched_rom, options_address + 0x02, 1, blind_trainers)
+ # Set better shops
+ _set_bytes_le(patched_rom, options_address + 0x04, 1, 1 if world.options.better_shops else 0)
- # Set fly without badge
- fly_without_badge = 1 if world.options.fly_without_badge else 0
- _set_bytes_little_endian(patched_rom, options_address + 0x03, 1, fly_without_badge)
+ # Set reusable TMs
+ _set_bytes_le(patched_rom, options_address + 0x05, 1, 1 if world.options.reusable_tms_tutors else 0)
- # Set exp modifier
- numerator = min(max(world.options.exp_modifier.value, 0), 2**16 - 1)
- _set_bytes_little_endian(patched_rom, options_address + 0x04, 2, numerator)
- _set_bytes_little_endian(patched_rom, options_address + 0x06, 2, 100)
+ # Set guaranteed catch
+ _set_bytes_le(patched_rom, options_address + 0x06, 1, 1 if world.options.guaranteed_catch else 0)
- # Set Birch pokemon
- _set_bytes_little_endian(
- patched_rom,
- options_address + 0x08,
- 2,
- get_random_species(world.random, data.species).species_id
- )
+ # Set purge spinners
+ _set_bytes_le(patched_rom, options_address + 0x07, 1, 1 if world.options.purge_spinners else 0)
- # Set guaranteed catch
- guaranteed_catch = 1 if world.options.guaranteed_catch else 0
- _set_bytes_little_endian(patched_rom, options_address + 0x0A, 1, guaranteed_catch)
+ # Set blind trainers
+ _set_bytes_le(patched_rom, options_address + 0x08, 1, 1 if world.options.blind_trainers else 0)
- # Set better shops
- better_shops = 1 if world.options.better_shops else 0
- _set_bytes_little_endian(patched_rom, options_address + 0x0B, 1, better_shops)
+ # Set exp modifier
+ _set_bytes_le(patched_rom, options_address + 0x09, 2, min(max(world.options.exp_modifier.value, 0), 2**16 - 1))
+ _set_bytes_le(patched_rom, options_address + 0x0B, 2, 100)
+
+ # Set match trainer levels
+ _set_bytes_le(patched_rom, options_address + 0x0D, 1, 1 if world.options.match_trainer_levels else 0)
+
+ # Set match trainer levels bonus
+ if world.options.match_trainer_levels == MatchTrainerLevels.option_additive:
+ match_trainer_levels_bonus = max(min(world.options.match_trainer_levels_bonus.value, 100), -100)
+ _set_bytes_le(patched_rom, options_address + 0x0E, 1, match_trainer_levels_bonus) # Works with negatives
+ elif world.options.match_trainer_levels == MatchTrainerLevels.option_multiplicative:
+ _set_bytes_le(patched_rom, options_address + 0x2E, 2, world.options.match_trainer_levels_bonus.value + 100)
+ _set_bytes_le(patched_rom, options_address + 0x30, 2, 100)
# Set elite four requirement
- elite_four_requires_gyms = 1 if world.options.elite_four_requirement == EliteFourRequirement.option_gyms else 0
- _set_bytes_little_endian(patched_rom, options_address + 0x0C, 1, elite_four_requires_gyms)
+ _set_bytes_le(
+ patched_rom,
+ options_address + 0x0F,
+ 1,
+ 1 if world.options.elite_four_requirement == EliteFourRequirement.option_gyms else 0
+ )
# Set elite four count
- elite_four_count = min(max(world.options.elite_four_count.value, 0), 8)
- _set_bytes_little_endian(patched_rom, options_address + 0x0D, 1, elite_four_count)
+ _set_bytes_le(patched_rom, options_address + 0x10, 1, min(max(world.options.elite_four_count.value, 0), 8))
# Set norman requirement
- norman_requires_gyms = 1 if world.options.norman_requirement == NormanRequirement.option_gyms else 0
- _set_bytes_little_endian(patched_rom, options_address + 0x0E, 1, norman_requires_gyms)
+ _set_bytes_le(
+ patched_rom,
+ options_address + 0x11,
+ 1,
+ 1 if world.options.norman_requirement == NormanRequirement.option_gyms else 0
+ )
# Set norman count
- norman_count = min(max(world.options.norman_count.value, 0), 8)
- _set_bytes_little_endian(patched_rom, options_address + 0x0F, 1, norman_count)
+ _set_bytes_le(patched_rom, options_address + 0x12, 1, min(max(world.options.norman_count.value, 0), 8))
# Set starting badges
- _set_bytes_little_endian(patched_rom, options_address + 0x10, 1, starting_badges)
-
- # Set receive item messages type
- receive_item_messages_type = world.options.receive_item_messages.value
- _set_bytes_little_endian(patched_rom, options_address + 0x11, 1, receive_item_messages_type)
+ _set_bytes_le(patched_rom, options_address + 0x13, 1, starting_badges)
+
+ # Set HM badge requirements
+ field_move_order = [
+ "HM01 Cut",
+ "HM05 Flash",
+ "HM06 Rock Smash",
+ "HM04 Strength",
+ "HM03 Surf",
+ "HM02 Fly",
+ "HM08 Dive",
+ "HM07 Waterfall",
+ ]
+ badge_to_bit = {
+ "Stone Badge": 1 << 0,
+ "Knuckle Badge": 1 << 1,
+ "Dynamo Badge": 1 << 2,
+ "Heat Badge": 1 << 3,
+ "Balance Badge": 1 << 4,
+ "Feather Badge": 1 << 5,
+ "Mind Badge": 1 << 6,
+ "Rain Badge": 1 << 7,
+ }
- # Set reusable TMs
- reusable_tms = 1 if world.options.reusable_tms else 0
- _set_bytes_little_endian(patched_rom, options_address + 0x12, 1, reusable_tms)
+ # Number of badges
+ # Uses 4 bits per HM. 0-8 means it's a valid requirement, otherwise use specific badges.
+ hm_badge_counts = 0
+ for i, hm in enumerate(field_move_order):
+ hm_badge_counts |= (world.hm_requirements[hm] if isinstance(world.hm_requirements[hm], int) else 0xF) << (i * 4)
+ _set_bytes_le(patched_rom, options_address + 0x14, 4, hm_badge_counts)
+
+ # Specific badges
+ for i, hm in enumerate(field_move_order):
+ if isinstance(world.hm_requirements, list):
+ bitfield = 0
+ for badge in world.hm_requirements:
+ bitfield |= badge_to_bit[badge]
+ _set_bytes_le(patched_rom, options_address + 0x18 + i, 1, bitfield)
+
+ # Set terra/marine cave locations
+ terra_cave_id = CAVE_EVENT_NAME_TO_ID[world.multiworld.get_location("TERRA_CAVE_LOCATION", world.player).item.name]
+ marine_cave_id = CAVE_EVENT_NAME_TO_ID[world.multiworld.get_location("MARINE_CAVE_LOCATION", world.player).item.name]
+ _set_bytes_le(patched_rom, options_address + 0x21, 1, terra_cave_id | (marine_cave_id << 4))
# Set route 115 boulders
- route_115_boulders = 1 if world.options.extra_boulders else 0
- _set_bytes_little_endian(patched_rom, options_address + 0x13, 1, route_115_boulders)
+ _set_bytes_le(patched_rom, options_address + 0x22, 1, 1 if world.options.extra_boulders else 0)
+
+ # Swap route 115 layout if bumpy slope enabled
+ _set_bytes_le(patched_rom, options_address + 0x23, 1, 1 if world.options.extra_bumpy_slope else 0)
+
+ # Swap route 115 layout if bumpy slope enabled
+ _set_bytes_le(patched_rom, options_address + 0x24, 1, 1 if world.options.modify_118 else 0)
# Set removed blockers
removed_roadblocks = world.options.remove_roadblocks.value
@@ -262,12 +489,76 @@ def generate_output(world: "PokemonEmeraldWorld", output_directory: str) -> None
removed_roadblocks_bitfield |= (1 << 4) if "Route 119 Aqua Grunts" in removed_roadblocks else 0
removed_roadblocks_bitfield |= (1 << 5) if "Route 112 Magma Grunts" in removed_roadblocks else 0
removed_roadblocks_bitfield |= (1 << 6) if "Seafloor Cavern Aqua Grunt" in removed_roadblocks else 0
- _set_bytes_little_endian(patched_rom, options_address + 0x14, 2, removed_roadblocks_bitfield)
+ _set_bytes_le(patched_rom, options_address + 0x25, 2, removed_roadblocks_bitfield)
+
+ # Mark berry trees as randomized
+ _set_bytes_le(patched_rom, options_address + 0x27, 1, 1 if world.options.berry_trees else 0)
+
+ # Mark dexsanity as enabled
+ _set_bytes_le(patched_rom, options_address + 0x28, 1, 1 if world.options.dexsanity else 0)
+
+ # Mark trainersanity as enabled
+ _set_bytes_le(patched_rom, options_address + 0x29, 1, 1 if world.options.trainersanity else 0)
+
+ # Set easter egg data
+ _set_bytes_le(patched_rom, options_address + 0x2B, 1, easter_egg[0])
+
+ # Set normalize encounter rates
+ _set_bytes_le(patched_rom, options_address + 0x2C, 1, 1 if world.options.normalize_encounter_rates else 0)
+
+ # Set allow wonder trading
+ _set_bytes_le(patched_rom, options_address + 0x2D, 1, 1 if world.options.enable_wonder_trading else 0)
+
+ # Set allowed to skip fanfares
+ _set_bytes_le(patched_rom, options_address + 0x32, 1, 1 if world.options.fanfares else 0)
+
+ if easter_egg[0] == 2:
+ _set_bytes_le(patched_rom, data.rom_addresses["gBattleMoves"] + (easter_egg[1] * 12) + 4, 1, 50)
+ _set_bytes_le(patched_rom, data.rom_addresses["gBattleMoves"] + (data.constants["MOVE_CUT"] * 12) + 4, 1, 1)
+ _set_bytes_le(patched_rom, data.rom_addresses["gBattleMoves"] + (data.constants["MOVE_FLY"] * 12) + 4, 1, 1)
+ _set_bytes_le(patched_rom, data.rom_addresses["gBattleMoves"] + (data.constants["MOVE_SURF"] * 12) + 4, 1, 1)
+ _set_bytes_le(patched_rom, data.rom_addresses["gBattleMoves"] + (data.constants["MOVE_STRENGTH"] * 12) + 4, 1, 1)
+ _set_bytes_le(patched_rom, data.rom_addresses["gBattleMoves"] + (data.constants["MOVE_FLASH"] * 12) + 4, 1, 1)
+ _set_bytes_le(patched_rom, data.rom_addresses["gBattleMoves"] + (data.constants["MOVE_ROCK_SMASH"] * 12) + 4, 1, 1)
+ _set_bytes_le(patched_rom, data.rom_addresses["gBattleMoves"] + (data.constants["MOVE_WATERFALL"] * 12) + 4, 1, 1)
+ _set_bytes_le(patched_rom, data.rom_addresses["gBattleMoves"] + (data.constants["MOVE_DIVE"] * 12) + 4, 1, 1)
+ _set_bytes_le(patched_rom, data.rom_addresses["gBattleMoves"] + (data.constants["MOVE_DIG"] * 12) + 4, 1, 1)
+
+ # Set slot auth
+ for i, byte in enumerate(world.auth):
+ _set_bytes_le(patched_rom, data.rom_addresses["gArchipelagoInfo"] + i, 1, byte)
+
+ # Randomize music
+ if world.options.music:
+ # The "randomized sound table" is a patchboard that redirects sounds just before they get played
+ randomized_looping_music = copy.copy(_LOOPING_MUSIC)
+ world.random.shuffle(randomized_looping_music)
+ for original_music, randomized_music in zip(_LOOPING_MUSIC, randomized_looping_music):
+ _set_bytes_le(
+ patched_rom,
+ data.rom_addresses["gRandomizedSoundTable"] + (data.constants[original_music] * 2),
+ 2,
+ data.constants[randomized_music]
+ )
- # Set slot name
- player_name = world.multiworld.get_player_name(world.player)
- for i, byte in enumerate(player_name.encode("utf-8")):
- _set_bytes_little_endian(patched_rom, data.rom_addresses["gArchipelagoInfo"] + i, 1, byte)
+ # Randomize fanfares
+ if world.options.fanfares:
+ # Shuffle the lists, pair new tracks with original tracks, set the new track ids, and set new fanfare durations
+ randomized_fanfares = [fanfare_name for fanfare_name in _FANFARES]
+ world.random.shuffle(randomized_fanfares)
+ for i, fanfare_pair in enumerate(zip(_FANFARES.keys(), randomized_fanfares)):
+ _set_bytes_le(
+ patched_rom,
+ data.rom_addresses["gRandomizedSoundTable"] + (data.constants[fanfare_pair[0]] * 2),
+ 2,
+ data.constants[fanfare_pair[1]]
+ )
+ _set_bytes_le(
+ patched_rom,
+ data.rom_addresses["sFanfares"] + (i * 4) + 2,
+ 2,
+ _FANFARES[fanfare_pair[1]]
+ )
# Write Output
out_file_name = world.multiworld.get_out_file_name_base(world.player)
@@ -275,7 +566,8 @@ def generate_output(world: "PokemonEmeraldWorld", output_directory: str) -> None
with open(output_path, "wb") as out_file:
out_file.write(patched_rom)
patch = PokemonEmeraldDeltaPatch(os.path.splitext(output_path)[0] + ".apemerald", player=world.player,
- player_name=player_name, patched_path=output_path)
+ player_name=world.multiworld.get_player_name(world.player),
+ patched_path=output_path)
patch.write()
os.unlink(output_path)
@@ -288,15 +580,6 @@ def get_base_rom_as_bytes() -> bytes:
return base_rom_bytes
-def _set_bytes_little_endian(byte_array: bytearray, address: int, size: int, value: int) -> None:
- offset = 0
- while size > 0:
- byte_array[address + offset] = value & 0xFF
- value = value >> 8
- offset += 1
- size -= 1
-
-
def _set_encounter_tables(world: "PokemonEmeraldWorld", rom: bytearray) -> None:
"""
Encounter tables are lists of
@@ -306,33 +589,38 @@ def _set_encounter_tables(world: "PokemonEmeraldWorld", rom: bytearray) -> None:
species_id: 0x02 bytes
}
"""
-
- for map_data in world.modified_maps:
+ for map_data in world.modified_maps.values():
tables = [map_data.land_encounters, map_data.water_encounters, map_data.fishing_encounters]
for table in tables:
if table is not None:
for i, species_id in enumerate(table.slots):
- address = table.rom_address + 2 + (4 * i)
- _set_bytes_little_endian(rom, address, 2, species_id)
+ address = table.address + 2 + (4 * i)
+ _set_bytes_le(rom, address, 2, species_id)
+
+def _set_species_info(world: "PokemonEmeraldWorld", rom: bytearray, easter_egg: Tuple[int, int]) -> None:
+ for species in world.modified_species.values():
+ _set_bytes_le(rom, species.address + 6, 1, species.types[0])
+ _set_bytes_le(rom, species.address + 7, 1, species.types[1])
+ _set_bytes_le(rom, species.address + 8, 1, species.catch_rate)
+ _set_bytes_le(rom, species.address + 22, 1, species.abilities[0])
+ _set_bytes_le(rom, species.address + 23, 1, species.abilities[1])
-def _set_species_info(world: "PokemonEmeraldWorld", rom: bytearray) -> None:
- for species in world.modified_species:
- if species is not None:
- _set_bytes_little_endian(rom, species.rom_address + 6, 1, species.types[0])
- _set_bytes_little_endian(rom, species.rom_address + 7, 1, species.types[1])
- _set_bytes_little_endian(rom, species.rom_address + 8, 1, species.catch_rate)
- _set_bytes_little_endian(rom, species.rom_address + 22, 1, species.abilities[0])
- _set_bytes_little_endian(rom, species.rom_address + 23, 1, species.abilities[1])
+ if easter_egg[0] == 3:
+ _set_bytes_le(rom, species.address + 22, 1, easter_egg[1])
+ _set_bytes_le(rom, species.address + 23, 1, easter_egg[1])
- for i, learnset_move in enumerate(species.learnset):
- level_move = learnset_move.level << 9 | learnset_move.move_id
- _set_bytes_little_endian(rom, species.learnset_rom_address + (i * 2), 2, level_move)
+ for i, learnset_move in enumerate(species.learnset):
+ level_move = learnset_move.level << 9 | learnset_move.move_id
+ if easter_egg[0] == 2:
+ level_move = learnset_move.level << 9 | easter_egg[1]
+ _set_bytes_le(rom, species.learnset_address + (i * 2), 2, level_move)
-def _set_opponents(world: "PokemonEmeraldWorld", rom: bytearray) -> None:
+
+def _set_opponents(world: "PokemonEmeraldWorld", rom: bytearray, easter_egg: Tuple[int, int]) -> None:
for trainer in world.modified_trainers:
- party_address = trainer.party.rom_address
+ party_address = trainer.party.address
pokemon_data_size: int
if trainer.party.pokemon_data_type in {TrainerPokemonDataTypeEnum.NO_ITEM_DEFAULT_MOVES, TrainerPokemonDataTypeEnum.ITEM_DEFAULT_MOVES}:
@@ -344,36 +632,53 @@ def _set_opponents(world: "PokemonEmeraldWorld", rom: bytearray) -> None:
pokemon_address = party_address + (i * pokemon_data_size)
# Replace species
- _set_bytes_little_endian(rom, pokemon_address + 0x04, 2, pokemon.species_id)
+ _set_bytes_le(rom, pokemon_address + 0x04, 2, pokemon.species_id)
# Replace custom moves if applicable
if trainer.party.pokemon_data_type == TrainerPokemonDataTypeEnum.NO_ITEM_CUSTOM_MOVES:
- _set_bytes_little_endian(rom, pokemon_address + 0x06, 2, pokemon.moves[0])
- _set_bytes_little_endian(rom, pokemon_address + 0x08, 2, pokemon.moves[1])
- _set_bytes_little_endian(rom, pokemon_address + 0x0A, 2, pokemon.moves[2])
- _set_bytes_little_endian(rom, pokemon_address + 0x0C, 2, pokemon.moves[3])
+ if easter_egg[0] == 2:
+ _set_bytes_le(rom, pokemon_address + 0x06, 2, easter_egg[1])
+ _set_bytes_le(rom, pokemon_address + 0x08, 2, easter_egg[1])
+ _set_bytes_le(rom, pokemon_address + 0x0A, 2, easter_egg[1])
+ _set_bytes_le(rom, pokemon_address + 0x0C, 2, easter_egg[1])
+ else:
+ _set_bytes_le(rom, pokemon_address + 0x06, 2, pokemon.moves[0])
+ _set_bytes_le(rom, pokemon_address + 0x08, 2, pokemon.moves[1])
+ _set_bytes_le(rom, pokemon_address + 0x0A, 2, pokemon.moves[2])
+ _set_bytes_le(rom, pokemon_address + 0x0C, 2, pokemon.moves[3])
elif trainer.party.pokemon_data_type == TrainerPokemonDataTypeEnum.ITEM_CUSTOM_MOVES:
- _set_bytes_little_endian(rom, pokemon_address + 0x08, 2, pokemon.moves[0])
- _set_bytes_little_endian(rom, pokemon_address + 0x0A, 2, pokemon.moves[1])
- _set_bytes_little_endian(rom, pokemon_address + 0x0C, 2, pokemon.moves[2])
- _set_bytes_little_endian(rom, pokemon_address + 0x0E, 2, pokemon.moves[3])
+ if easter_egg[0] == 2:
+ _set_bytes_le(rom, pokemon_address + 0x08, 2, easter_egg[1])
+ _set_bytes_le(rom, pokemon_address + 0x0A, 2, easter_egg[1])
+ _set_bytes_le(rom, pokemon_address + 0x0C, 2, easter_egg[1])
+ _set_bytes_le(rom, pokemon_address + 0x0E, 2, easter_egg[1])
+ else:
+ _set_bytes_le(rom, pokemon_address + 0x08, 2, pokemon.moves[0])
+ _set_bytes_le(rom, pokemon_address + 0x0A, 2, pokemon.moves[1])
+ _set_bytes_le(rom, pokemon_address + 0x0C, 2, pokemon.moves[2])
+ _set_bytes_le(rom, pokemon_address + 0x0E, 2, pokemon.moves[3])
+
+
+def _set_legendary_encounters(world: "PokemonEmeraldWorld", rom: bytearray) -> None:
+ for encounter in world.modified_legendary_encounters:
+ _set_bytes_le(rom, encounter.address, 2, encounter.species_id)
-def _set_static_encounters(world: "PokemonEmeraldWorld", rom: bytearray) -> None:
- for encounter in world.modified_static_encounters:
- _set_bytes_little_endian(rom, encounter.rom_address, 2, encounter.species_id)
+def _set_misc_pokemon(world: "PokemonEmeraldWorld", rom: bytearray) -> None:
+ for encounter in world.modified_misc_pokemon:
+ _set_bytes_le(rom, encounter.address, 2, encounter.species_id)
def _set_starters(world: "PokemonEmeraldWorld", rom: bytearray) -> None:
address = data.rom_addresses["sStarterMon"]
(starter_1, starter_2, starter_3) = world.modified_starters
- _set_bytes_little_endian(rom, address + 0, 2, starter_1)
- _set_bytes_little_endian(rom, address + 2, 2, starter_2)
- _set_bytes_little_endian(rom, address + 4, 2, starter_3)
+ _set_bytes_le(rom, address + 0, 2, starter_1)
+ _set_bytes_le(rom, address + 2, 2, starter_2)
+ _set_bytes_le(rom, address + 4, 2, starter_3)
-def _set_tm_moves(world: "PokemonEmeraldWorld", rom: bytearray) -> None:
+def _set_tm_moves(world: "PokemonEmeraldWorld", rom: bytearray, easter_egg: Tuple[int, int]) -> None:
tmhm_list_address = data.rom_addresses["sTMHMMoves"]
for i, move in enumerate(world.modified_tmhm_moves):
@@ -381,15 +686,16 @@ def _set_tm_moves(world: "PokemonEmeraldWorld", rom: bytearray) -> None:
if i >= 50:
break
- _set_bytes_little_endian(rom, tmhm_list_address + (i * 2), 2, move)
+ _set_bytes_le(rom, tmhm_list_address + (i * 2), 2, move)
+ if easter_egg[0] == 2:
+ _set_bytes_le(rom, tmhm_list_address + (i * 2), 2, easter_egg[1])
def _set_tmhm_compatibility(world: "PokemonEmeraldWorld", rom: bytearray) -> None:
learnsets_address = data.rom_addresses["gTMHMLearnsets"]
- for species in world.modified_species:
- if species is not None:
- _set_bytes_little_endian(rom, learnsets_address + (species.species_id * 8), 8, species.tm_hm_compatibility)
+ for species in world.modified_species.values():
+ _set_bytes_le(rom, learnsets_address + (species.species_id * 8), 8, species.tm_hm_compatibility)
def _randomize_opponent_battle_type(world: "PokemonEmeraldWorld", rom: bytearray) -> None:
@@ -403,18 +709,45 @@ def _randomize_opponent_battle_type(world: "PokemonEmeraldWorld", rom: bytearray
}
for trainer_data in data.trainers:
- if trainer_data.battle_script_rom_address != 0 and len(trainer_data.party.pokemon) > 1:
- if world.random.random() < probability:
- # Set the trainer to be a double battle
- _set_bytes_little_endian(rom, trainer_data.rom_address + 0x18, 1, 1)
-
- # Swap the battle type in the script for the purpose of loading the right text
- # and setting data to the right places
- original_battle_type = rom[trainer_data.battle_script_rom_address + 1]
- if original_battle_type in battle_type_map:
- _set_bytes_little_endian(
+ if trainer_data.script_address != 0 and len(trainer_data.party.pokemon) > 1:
+ original_battle_type = rom[trainer_data.script_address + 1]
+ if original_battle_type in battle_type_map: # Don't touch anything other than regular single battles
+ if world.random.random() < probability:
+ # Set the trainer to be a double battle
+ _set_bytes_le(rom, trainer_data.address + 0x18, 1, 1)
+
+ # Swap the battle type in the script for the purpose of loading the right text
+ # and setting data to the right places
+ _set_bytes_le(
rom,
- trainer_data.battle_script_rom_address + 1,
+ trainer_data.script_address + 1,
1,
battle_type_map[original_battle_type]
)
+
+
+def _randomize_move_tutor_moves(world: "PokemonEmeraldWorld", rom: bytearray, easter_egg: Tuple[int, int]) -> None:
+ if easter_egg[0] == 2:
+ for i in range(30):
+ _set_bytes_le(rom, data.rom_addresses["gTutorMoves"] + (i * 2), 2, easter_egg[1])
+ else:
+ if world.options.tm_tutor_moves:
+ new_tutor_moves = []
+ for i in range(30):
+ new_move = get_random_move(world.random, set(new_tutor_moves) | world.blacklisted_moves | HM_MOVES)
+ new_tutor_moves.append(new_move)
+
+ _set_bytes_le(rom, data.rom_addresses["gTutorMoves"] + (i * 2), 2, new_move)
+
+ # Always set Fortree move tutor to Dig
+ _set_bytes_le(rom, data.rom_addresses["gTutorMoves"] + (24 * 2), 2, data.constants["MOVE_DIG"])
+
+ # Modify compatibility
+ if world.options.tm_tutor_compatibility.value != -1:
+ for species in data.species.values():
+ _set_bytes_le(
+ rom,
+ data.rom_addresses["sTutorLearnsets"] + (species.species_id * 4),
+ 4,
+ bool_array_to_int([world.random.randrange(0, 100) < world.options.tm_tutor_compatibility.value for _ in range(32)])
+ )
diff --git a/worlds/pokemon_emerald/rules.py b/worlds/pokemon_emerald/rules.py
index 564bf5af8d16..059e21b74998 100644
--- a/worlds/pokemon_emerald/rules.py
+++ b/worlds/pokemon_emerald/rules.py
@@ -1,13 +1,13 @@
"""
Logic rule definitions for Pokemon Emerald
"""
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Callable, Dict
from BaseClasses import CollectionState
from worlds.generic.Rules import add_rule, set_rule
-from .data import data
-from .options import EliteFourRequirement, NormanRequirement, Goal
+from .data import NATIONAL_ID_TO_SPECIES_ID, NUM_REAL_SPECIES, data
+from .options import DarkCavesRequireFlash, EliteFourRequirement, NormanRequirement, Goal
if TYPE_CHECKING:
from . import PokemonEmeraldWorld
@@ -16,26 +16,14 @@
# Rules are organized by town/route/dungeon and ordered approximately
# by when you would first reach that place in a vanilla playthrough.
def set_rules(world: "PokemonEmeraldWorld") -> None:
- def can_cut(state: CollectionState):
- return state.has("HM01 Cut", world.player) and state.has("Stone Badge", world.player)
-
- def can_surf(state: CollectionState):
- return state.has("HM03 Surf", world.player) and state.has("Balance Badge", world.player)
-
- def can_strength(state: CollectionState):
- return state.has("HM04 Strength", world.player) and state.has("Heat Badge", world.player)
-
- def can_flash(state: CollectionState):
- return state.has("HM05 Flash", world.player) and state.has("Knuckle Badge", world.player)
-
- def can_rock_smash(state: CollectionState):
- return state.has("HM06 Rock Smash", world.player) and state.has("Dynamo Badge", world.player)
-
- def can_waterfall(state: CollectionState):
- return state.has("HM07 Waterfall", world.player) and state.has("Rain Badge", world.player)
-
- def can_dive(state: CollectionState):
- return state.has("HM08 Dive", world.player) and state.has("Mind Badge", world.player)
+ hm_rules: Dict[str, Callable[[CollectionState], bool]] = {}
+ for hm, badges in world.hm_requirements.items():
+ if isinstance(badges, list):
+ hm_rules[hm] = lambda state, hm=hm, badges=badges: state.has(hm, world.player) \
+ and state.has_all(badges, world.player)
+ else:
+ hm_rules[hm] = lambda state, hm=hm, badges=badges: state.has(hm, world.player) \
+ and state.has_group("Badges", world.player, badges)
def has_acro_bike(state: CollectionState):
return state.has("Acro Bike", world.player)
@@ -52,9 +40,30 @@ def defeated_n_gym_leaders(state: CollectionState, n: int) -> bool:
"EVENT_DEFEAT_NORMAN",
"EVENT_DEFEAT_WINONA",
"EVENT_DEFEAT_TATE_AND_LIZA",
- "EVENT_DEFEAT_JUAN"
+ "EVENT_DEFEAT_JUAN",
]]) >= n
+ huntable_legendary_events = [
+ f"EVENT_ENCOUNTER_{key}"
+ for name, key in {
+ "Groudon": "GROUDON",
+ "Kyogre": "KYOGRE",
+ "Rayquaza": "RAYQUAZA",
+ "Latias": "LATIAS",
+ "Latios": "LATIOS",
+ "Regirock": "REGIROCK",
+ "Regice": "REGICE",
+ "Registeel": "REGISTEEL",
+ "Mew": "MEW",
+ "Deoxys": "DEOXYS",
+ "Ho-oh": "HO_OH",
+ "Lugia": "LUGIA",
+ }.items()
+ if name in world.options.allowed_legendary_hunt_encounters.value
+ ]
+ def encountered_n_legendaries(state: CollectionState, n: int) -> bool:
+ return sum(int(state.has(event, world.player)) for event in huntable_legendary_events) >= n
+
def get_entrance(entrance: str):
return world.multiworld.get_entrance(entrance, world.player)
@@ -64,25 +73,49 @@ def get_location(location: str):
return world.multiworld.get_location(location, world.player)
- victory_event_name = "EVENT_DEFEAT_CHAMPION"
- if world.options.goal == Goal.option_steven:
- victory_event_name = "EVENT_DEFEAT_STEVEN"
+ if world.options.goal == Goal.option_champion:
+ completion_condition = lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player)
+ elif world.options.goal == Goal.option_steven:
+ completion_condition = lambda state: state.has("EVENT_DEFEAT_STEVEN", world.player)
elif world.options.goal == Goal.option_norman:
- victory_event_name = "EVENT_DEFEAT_NORMAN"
-
- world.multiworld.completion_condition[world.player] = lambda state: state.has(victory_event_name, world.player)
+ completion_condition = lambda state: state.has("EVENT_DEFEAT_NORMAN", world.player)
+ elif world.options.goal == Goal.option_legendary_hunt:
+ completion_condition = lambda state: encountered_n_legendaries(state, world.options.legendary_hunt_count.value)
+
+ world.multiworld.completion_condition[world.player] = completion_condition
+
+ if world.options.legendary_hunt_catch:
+ set_rule(get_location("EVENT_ENCOUNTER_GROUDON"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player))
+ set_rule(get_location("EVENT_ENCOUNTER_KYOGRE"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player))
+ set_rule(get_location("EVENT_ENCOUNTER_RAYQUAZA"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player))
+ set_rule(get_location("EVENT_ENCOUNTER_LATIAS"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player))
+ # Latios already only requires defeating the champion and access to Route 117
+ # set_rule(get_location("EVENT_ENCOUNTER_LATIOS"),
+ # lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player))
+ set_rule(get_location("EVENT_ENCOUNTER_REGIROCK"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player))
+ set_rule(get_location("EVENT_ENCOUNTER_REGICE"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player))
+ set_rule(get_location("EVENT_ENCOUNTER_REGISTEEL"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player))
+ set_rule(get_location("EVENT_ENCOUNTER_MEW"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player))
+ set_rule(get_location("EVENT_ENCOUNTER_DEOXYS"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player))
+ set_rule(get_location("EVENT_ENCOUNTER_HO_OH"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player))
+ set_rule(get_location("EVENT_ENCOUNTER_LUGIA"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player))
# Sky
- if world.options.fly_without_badge:
- set_rule(
- get_entrance("REGION_LITTLEROOT_TOWN/MAIN -> REGION_SKY"),
- lambda state: state.has("HM02 Fly", world.player)
- )
- else:
- set_rule(
- get_entrance("REGION_LITTLEROOT_TOWN/MAIN -> REGION_SKY"),
- lambda state: state.has("HM02 Fly", world.player) and state.has("Feather Badge", world.player)
- )
+ set_rule(
+ get_entrance("REGION_LITTLEROOT_TOWN/MAIN -> REGION_SKY"),
+ hm_rules["HM02 Fly"]
+ )
set_rule(
get_entrance("REGION_SKY -> REGION_LITTLEROOT_TOWN/MAIN"),
lambda state: state.has("EVENT_VISITED_LITTLEROOT_TOWN", world.player)
@@ -144,27 +177,59 @@ def get_location(location: str):
lambda state: state.has("EVENT_VISITED_EVER_GRANDE_CITY", world.player)
)
+ # Littleroot Town
+ set_rule(
+ get_location("NPC_GIFT_RECEIVED_SS_TICKET"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player)
+ )
+ set_rule(
+ get_location("NPC_GIFT_RECEIVED_AURORA_TICKET"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player)
+ )
+ set_rule(
+ get_location("NPC_GIFT_RECEIVED_EON_TICKET"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player)
+ )
+ set_rule(
+ get_location("NPC_GIFT_RECEIVED_MYSTIC_TICKET"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player)
+ )
+ set_rule(
+ get_location("NPC_GIFT_RECEIVED_OLD_SEA_MAP"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player)
+ )
+
+ # Route 102
+ set_rule(
+ get_entrance("REGION_ROUTE102/MAIN -> REGION_ROUTE102/POND"),
+ hm_rules["HM03 Surf"]
+ )
+
# Route 103
set_rule(
get_entrance("REGION_ROUTE103/EAST -> REGION_ROUTE103/WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_ROUTE103/WEST -> REGION_ROUTE103/WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE103/EAST -> REGION_ROUTE103/EAST_TREE_MAZE"),
+ hm_rules["HM01 Cut"]
)
# Petalburg City
set_rule(
get_entrance("REGION_PETALBURG_CITY/MAIN -> REGION_PETALBURG_CITY/SOUTH_POND"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_PETALBURG_CITY/MAIN -> REGION_PETALBURG_CITY/NORTH_POND"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
- get_location("NPC_GIFT_RECEIVED_HM03"),
+ get_location("NPC_GIFT_RECEIVED_HM_SURF"),
lambda state: state.has("EVENT_DEFEAT_NORMAN", world.player)
)
if world.options.norman_requirement == NormanRequirement.option_badges:
@@ -188,8 +253,16 @@ def get_location(location: str):
# Route 104
set_rule(
- get_entrance("REGION_ROUTE104/SOUTH -> REGION_ROUTE105/MAIN"),
- can_surf
+ get_entrance("REGION_ROUTE104/SOUTH -> REGION_ROUTE104/SOUTH_WATER"),
+ hm_rules["HM03 Surf"]
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE104/NORTH -> REGION_ROUTE104/NORTH_POND"),
+ hm_rules["HM03 Surf"]
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE104/NORTH -> REGION_ROUTE104/TREE_ALCOVE_2"),
+ hm_rules["HM01 Cut"]
)
set_rule(
get_entrance("REGION_ROUTE104_MR_BRINEYS_HOUSE/MAIN -> REGION_DEWFORD_TOWN/MAIN"),
@@ -199,7 +272,7 @@ def get_location(location: str):
# Petalburg Woods
set_rule(
get_entrance("REGION_PETALBURG_WOODS/WEST_PATH -> REGION_PETALBURG_WOODS/EAST_PATH"),
- can_cut
+ hm_rules["HM01 Cut"]
)
# Rustboro City
@@ -207,6 +280,11 @@ def get_location(location: str):
get_location("EVENT_RETURN_DEVON_GOODS"),
lambda state: state.has("EVENT_RECOVER_DEVON_GOODS", world.player)
)
+ if world.options.trainersanity:
+ set_rule(
+ get_location("TRAINER_BRENDAN_RUSTBORO_MUDKIP_REWARD"),
+ lambda state: state.has("EVENT_RETURN_DEVON_GOODS", world.player)
+ )
# Devon Corp
set_rule(
@@ -217,21 +295,33 @@ def get_location(location: str):
# Route 116
set_rule(
get_entrance("REGION_ROUTE116/WEST -> REGION_ROUTE116/WEST_ABOVE_LEDGE"),
- can_cut
+ hm_rules["HM01 Cut"]
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE116/EAST -> REGION_TERRA_CAVE_ENTRANCE/MAIN"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player) and \
+ state.has("TERRA_CAVE_ROUTE_116_1", world.player) and \
+ state.has("EVENT_DEFEAT_SHELLY", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE116/WEST -> REGION_TERRA_CAVE_ENTRANCE/MAIN"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player) and \
+ state.has("TERRA_CAVE_ROUTE_116_2", world.player) and \
+ state.has("EVENT_DEFEAT_SHELLY", world.player)
)
# Rusturf Tunnel
set_rule(
get_entrance("REGION_RUSTURF_TUNNEL/WEST -> REGION_RUSTURF_TUNNEL/EAST"),
- can_rock_smash
+ hm_rules["HM06 Rock Smash"]
)
set_rule(
get_entrance("REGION_RUSTURF_TUNNEL/EAST -> REGION_RUSTURF_TUNNEL/WEST"),
- can_rock_smash
+ hm_rules["HM06 Rock Smash"]
)
set_rule(
- get_location("NPC_GIFT_RECEIVED_HM04"),
- can_rock_smash
+ get_location("NPC_GIFT_RECEIVED_HM_STRENGTH"),
+ hm_rules["HM06 Rock Smash"]
)
set_rule(
get_location("EVENT_RECOVER_DEVON_GOODS"),
@@ -241,48 +331,86 @@ def get_location(location: str):
# Route 115
set_rule(
get_entrance("REGION_ROUTE115/SOUTH_BELOW_LEDGE -> REGION_ROUTE115/SEA"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_ROUTE115/SOUTH_BEACH_NEAR_CAVE -> REGION_ROUTE115/SEA"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_ROUTE115/SOUTH_ABOVE_LEDGE -> REGION_ROUTE115/SOUTH_BEHIND_ROCK"),
- can_rock_smash
+ hm_rules["HM06 Rock Smash"]
)
set_rule(
get_entrance("REGION_ROUTE115/NORTH_BELOW_SLOPE -> REGION_ROUTE115/SEA"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_ROUTE115/NORTH_BELOW_SLOPE -> REGION_ROUTE115/NORTH_ABOVE_SLOPE"),
lambda state: has_mach_bike(state)
)
+ set_rule(
+ get_entrance("REGION_ROUTE115/NORTH_BELOW_SLOPE -> REGION_TERRA_CAVE_ENTRANCE/MAIN"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player) and \
+ state.has("TERRA_CAVE_ROUTE_115_1", world.player) and \
+ state.has("EVENT_DEFEAT_SHELLY", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE115/NORTH_ABOVE_SLOPE -> REGION_TERRA_CAVE_ENTRANCE/MAIN"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player) and \
+ state.has("TERRA_CAVE_ROUTE_115_2", world.player) and \
+ state.has("EVENT_DEFEAT_SHELLY", world.player)
+ )
+
if world.options.extra_boulders:
set_rule(
get_entrance("REGION_ROUTE115/SOUTH_BEACH_NEAR_CAVE -> REGION_ROUTE115/SOUTH_ABOVE_LEDGE"),
- can_strength
+ hm_rules["HM04 Strength"]
)
set_rule(
get_entrance("REGION_ROUTE115/SOUTH_ABOVE_LEDGE -> REGION_ROUTE115/SOUTH_BEACH_NEAR_CAVE"),
- can_strength
+ hm_rules["HM04 Strength"]
+ )
+
+ if world.options.extra_bumpy_slope:
+ set_rule(
+ get_entrance("REGION_ROUTE115/SOUTH_BELOW_LEDGE -> REGION_ROUTE115/SOUTH_ABOVE_LEDGE"),
+ lambda state: has_acro_bike(state)
+ )
+ else:
+ set_rule(
+ get_entrance("REGION_ROUTE115/SOUTH_BELOW_LEDGE -> REGION_ROUTE115/SOUTH_ABOVE_LEDGE"),
+ lambda state: False
)
# Route 105
set_rule(
- get_entrance("REGION_ROUTE105/MAIN -> REGION_UNDERWATER_ROUTE105/MAIN"),
- can_dive
+ get_entrance("REGION_UNDERWATER_ROUTE105/MARINE_CAVE_ENTRANCE_1 -> REGION_UNDERWATER_MARINE_CAVE/MAIN"),
+ lambda state: hm_rules["HM08 Dive"](state) and \
+ state.has("EVENT_DEFEAT_CHAMPION", world.player) and \
+ state.has("MARINE_CAVE_ROUTE_105_1", world.player) and \
+ state.has("EVENT_DEFEAT_SHELLY", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_UNDERWATER_ROUTE105/MARINE_CAVE_ENTRANCE_2 -> REGION_UNDERWATER_MARINE_CAVE/MAIN"),
+ lambda state: hm_rules["HM08 Dive"](state) and \
+ state.has("EVENT_DEFEAT_CHAMPION", world.player) and \
+ state.has("MARINE_CAVE_ROUTE_105_2", world.player) and \
+ state.has("EVENT_DEFEAT_SHELLY", world.player)
+ )
+ set_rule(
+ get_entrance("MAP_ROUTE105:0/MAP_ISLAND_CAVE:0"),
+ lambda state: state.has("EVENT_UNDO_REGI_SEAL", world.player)
)
# Route 106
set_rule(
get_entrance("REGION_ROUTE106/EAST -> REGION_ROUTE106/SEA"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_ROUTE106/WEST -> REGION_ROUTE106/SEA"),
- can_surf
+ hm_rules["HM03 Surf"]
)
# Dewford Town
@@ -313,7 +441,7 @@ def get_location(location: str):
# Route 107
set_rule(
get_entrance("REGION_DEWFORD_TOWN/MAIN -> REGION_ROUTE107/MAIN"),
- can_surf
+ hm_rules["HM03 Surf"]
)
# Route 109
@@ -327,13 +455,13 @@ def get_location(location: str):
)
set_rule(
get_entrance("REGION_ROUTE109/BEACH -> REGION_ROUTE109/SEA"),
- can_surf
+ hm_rules["HM03 Surf"]
)
# Slateport City
set_rule(
get_entrance("REGION_SLATEPORT_CITY/MAIN -> REGION_ROUTE134/WEST"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_location("EVENT_TALK_TO_DOCK"),
@@ -349,17 +477,17 @@ def get_location(location: str):
)
set_rule(
get_entrance("REGION_SLATEPORT_CITY_HARBOR/MAIN -> REGION_SS_TIDAL_CORRIDOR/MAIN"),
- lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player)
+ lambda state: state.has("S.S. Ticket", world.player)
)
# Route 110
set_rule(
get_entrance("REGION_ROUTE110/MAIN -> REGION_ROUTE110/SOUTH_WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_ROUTE110/MAIN -> REGION_ROUTE110/NORTH_WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE/WEST -> REGION_ROUTE110_SEASIDE_CYCLING_ROAD_SOUTH_ENTRANCE/EAST"),
@@ -379,12 +507,64 @@ def get_location(location: str):
lambda state: state.has("EVENT_RESCUE_CAPT_STERN", world.player)
)
+ # Trick House
+ set_rule(
+ get_entrance("REGION_ROUTE110_TRICK_HOUSE_PUZZLE1/ENTRANCE -> REGION_ROUTE110_TRICK_HOUSE_PUZZLE1/REWARDS"),
+ hm_rules["HM01 Cut"]
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE110_TRICK_HOUSE_ENTRANCE/MAIN -> REGION_ROUTE110_TRICK_HOUSE_PUZZLE2/ENTRANCE"),
+ lambda state: state.has("Dynamo Badge", world.player) and state.has("EVENT_COMPLETE_TRICK_HOUSE_1", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE110_TRICK_HOUSE_ENTRANCE/MAIN -> REGION_ROUTE110_TRICK_HOUSE_PUZZLE3/ENTRANCE"),
+ lambda state: state.has("Heat Badge", world.player) and state.has("EVENT_COMPLETE_TRICK_HOUSE_2", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE110_TRICK_HOUSE_PUZZLE3/ENTRANCE -> REGION_ROUTE110_TRICK_HOUSE_PUZZLE3/REWARDS"),
+ hm_rules["HM06 Rock Smash"]
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE110_TRICK_HOUSE_ENTRANCE/MAIN -> REGION_ROUTE110_TRICK_HOUSE_PUZZLE4/ENTRANCE"),
+ lambda state: state.has("Balance Badge", world.player) and state.has("EVENT_COMPLETE_TRICK_HOUSE_3", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE110_TRICK_HOUSE_PUZZLE4/ENTRANCE -> REGION_ROUTE110_TRICK_HOUSE_PUZZLE4/REWARDS"),
+ hm_rules["HM04 Strength"]
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE110_TRICK_HOUSE_ENTRANCE/MAIN -> REGION_ROUTE110_TRICK_HOUSE_PUZZLE5/ENTRANCE"),
+ lambda state: state.has("Feather Badge", world.player) and state.has("EVENT_COMPLETE_TRICK_HOUSE_4", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE110_TRICK_HOUSE_ENTRANCE/MAIN -> REGION_ROUTE110_TRICK_HOUSE_PUZZLE6/ENTRANCE"),
+ lambda state: state.has("Mind Badge", world.player) and state.has("EVENT_COMPLETE_TRICK_HOUSE_5", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE110_TRICK_HOUSE_ENTRANCE/MAIN -> REGION_ROUTE110_TRICK_HOUSE_PUZZLE7/ENTRANCE"),
+ lambda state: state.has("Rain Badge", world.player) and state.has("EVENT_COMPLETE_TRICK_HOUSE_6", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE110_TRICK_HOUSE_ENTRANCE/MAIN -> REGION_ROUTE110_TRICK_HOUSE_PUZZLE8/ENTRANCE"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player) and state.has("EVENT_COMPLETE_TRICK_HOUSE_7", world.player)
+ )
+
# Mauville City
set_rule(
get_location("NPC_GIFT_GOT_BASEMENT_KEY_FROM_WATTSON"),
lambda state: state.has("EVENT_DEFEAT_NORMAN", world.player)
)
+ # Route 117
+ set_rule(
+ get_entrance("REGION_ROUTE117/MAIN -> REGION_ROUTE117/PONDS"),
+ hm_rules["HM03 Surf"]
+ )
+ set_rule(
+ get_location("EVENT_ENCOUNTER_LATIOS"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player)
+ )
+
# Route 111
set_rule(
get_entrance("REGION_ROUTE111/MIDDLE -> REGION_ROUTE111/DESERT"),
@@ -394,22 +574,34 @@ def get_location(location: str):
get_entrance("REGION_ROUTE111/NORTH -> REGION_ROUTE111/DESERT"),
lambda state: state.has("Go Goggles", world.player)
)
+ set_rule(
+ get_entrance("REGION_ROUTE111/NORTH -> REGION_ROUTE111/ABOVE_SLOPE"),
+ has_mach_bike
+ )
set_rule(
get_entrance("REGION_ROUTE111/MIDDLE -> REGION_ROUTE111/SOUTH"),
- can_rock_smash
+ hm_rules["HM06 Rock Smash"]
)
set_rule(
get_entrance("REGION_ROUTE111/SOUTH -> REGION_ROUTE111/SOUTH_POND"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_ROUTE111/SOUTH -> REGION_ROUTE111/MIDDLE"),
- can_rock_smash
+ hm_rules["HM06 Rock Smash"]
)
set_rule(
get_entrance("MAP_ROUTE111:4/MAP_TRAINER_HILL_ENTRANCE:0"),
lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player)
)
+ set_rule(
+ get_entrance("MAP_ROUTE111:1/MAP_DESERT_RUINS:0"),
+ lambda state: state.has("EVENT_UNDO_REGI_SEAL", world.player)
+ )
+ set_rule(
+ get_entrance("MAP_DESERT_RUINS:0/MAP_ROUTE111:1"),
+ hm_rules["HM06 Rock Smash"]
+ )
# Route 112
if "Route 112 Magma Grunts" not in world.options.remove_roadblocks.value:
@@ -425,51 +617,75 @@ def get_location(location: str):
# Fiery Path
set_rule(
get_entrance("REGION_FIERY_PATH/MAIN -> REGION_FIERY_PATH/BEHIND_BOULDER"),
- can_strength
+ hm_rules["HM04 Strength"]
)
# Route 114
set_rule(
- get_entrance("REGION_ROUTE114/MAIN -> REGION_ROUTE114/ABOVE_WATERFALL"),
- lambda state: can_surf(state) and can_waterfall(state)
+ get_entrance("REGION_ROUTE114/MAIN -> REGION_ROUTE114/WATER"),
+ hm_rules["HM03 Surf"]
)
set_rule(
- get_entrance("REGION_ROUTE114/ABOVE_WATERFALL -> REGION_ROUTE114/MAIN"),
- lambda state: can_surf(state) and can_waterfall(state)
+ get_entrance("REGION_ROUTE114/WATER -> REGION_ROUTE114/ABOVE_WATERFALL"),
+ hm_rules["HM07 Waterfall"]
)
set_rule(
get_entrance("MAP_ROUTE114_FOSSIL_MANIACS_TUNNEL:2/MAP_DESERT_UNDERPASS:0"),
lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player)
)
+ set_rule(
+ get_entrance("REGION_ROUTE114/ABOVE_WATERFALL -> REGION_TERRA_CAVE_ENTRANCE/MAIN"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player) and \
+ state.has("TERRA_CAVE_ROUTE_114_1", world.player) and \
+ state.has("EVENT_DEFEAT_SHELLY", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE114/MAIN -> REGION_TERRA_CAVE_ENTRANCE/MAIN"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player) and \
+ state.has("TERRA_CAVE_ROUTE_114_2", world.player) and \
+ state.has("EVENT_DEFEAT_SHELLY", world.player)
+ )
# Meteor Falls
set_rule(
- get_entrance("REGION_METEOR_FALLS_1F_1R/MAIN -> REGION_METEOR_FALLS_1F_1R/ABOVE_WATERFALL"),
- lambda state: can_surf(state) and can_waterfall(state)
+ get_entrance("REGION_METEOR_FALLS_1F_1R/MAIN -> REGION_METEOR_FALLS_1F_1R/WATER"),
+ hm_rules["HM03 Surf"]
)
set_rule(
- get_entrance("REGION_METEOR_FALLS_1F_1R/ABOVE_WATERFALL -> REGION_METEOR_FALLS_1F_1R/MAIN"),
- can_surf
+ get_entrance("REGION_METEOR_FALLS_1F_1R/WATER -> REGION_METEOR_FALLS_1F_1R/WATER_ABOVE_WATERFALL"),
+ hm_rules["HM07 Waterfall"]
+ )
+ set_rule(
+ get_entrance("REGION_METEOR_FALLS_1F_1R/ABOVE_WATERFALL -> REGION_METEOR_FALLS_1F_1R/WATER_ABOVE_WATERFALL"),
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("MAP_METEOR_FALLS_1F_1R:5/MAP_METEOR_FALLS_STEVENS_CAVE:0"),
lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player)
)
+ set_rule(
+ get_entrance("REGION_METEOR_FALLS_1F_2R/LEFT_SPLIT -> REGION_METEOR_FALLS_1F_2R/LEFT_SPLIT_WATER"),
+ hm_rules["HM03 Surf"]
+ )
+ set_rule(
+ get_entrance("REGION_METEOR_FALLS_1F_2R/RIGHT_SPLIT -> REGION_METEOR_FALLS_1F_2R/RIGHT_SPLIT_WATER"),
+ hm_rules["HM03 Surf"]
+ )
set_rule(
get_entrance("REGION_METEOR_FALLS_B1F_1R/HIGHEST_LADDER -> REGION_METEOR_FALLS_B1F_1R/WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_METEOR_FALLS_B1F_1R/NORTH_SHORE -> REGION_METEOR_FALLS_B1F_1R/WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_METEOR_FALLS_B1F_1R/SOUTH_SHORE -> REGION_METEOR_FALLS_B1F_1R/WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_METEOR_FALLS_B1F_2R/ENTRANCE -> REGION_METEOR_FALLS_B1F_2R/WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
# Jagged Pass
@@ -503,25 +719,25 @@ def get_location(location: str):
)
set_rule(
get_entrance("REGION_MIRAGE_TOWER_3F/TOP -> REGION_MIRAGE_TOWER_3F/BOTTOM"),
- can_rock_smash
+ hm_rules["HM06 Rock Smash"]
)
set_rule(
get_entrance("REGION_MIRAGE_TOWER_3F/BOTTOM -> REGION_MIRAGE_TOWER_3F/TOP"),
- can_rock_smash
+ hm_rules["HM06 Rock Smash"]
)
set_rule(
get_entrance("REGION_MIRAGE_TOWER_4F/MAIN -> REGION_MIRAGE_TOWER_4F/FOSSIL_PLATFORM"),
- can_rock_smash
+ hm_rules["HM06 Rock Smash"]
)
# Abandoned Ship
set_rule(
get_entrance("REGION_ABANDONED_SHIP_ROOMS_B1F/CENTER -> REGION_ABANDONED_SHIP_UNDERWATER1/MAIN"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS/MAIN -> REGION_ABANDONED_SHIP_UNDERWATER2/MAIN"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("MAP_ABANDONED_SHIP_HIDDEN_FLOOR_CORRIDORS:0/MAP_ABANDONED_SHIP_HIDDEN_FLOOR_ROOMS:0"),
@@ -551,23 +767,58 @@ def get_location(location: str):
)
# Route 118
+ if world.options.modify_118:
+ set_rule(
+ get_entrance("REGION_ROUTE118/WEST -> REGION_ROUTE118/EAST"),
+ has_acro_bike
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE118/EAST -> REGION_ROUTE118/WEST"),
+ has_acro_bike
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE118/WEST_WATER -> REGION_ROUTE118/EAST_WATER"),
+ lambda state: False
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE118/EAST_WATER -> REGION_ROUTE118/WEST_WATER"),
+ lambda state: False
+ )
+ else:
+ set_rule(
+ get_entrance("REGION_ROUTE118/WEST -> REGION_ROUTE118/EAST"),
+ lambda state: False
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE118/EAST -> REGION_ROUTE118/WEST"),
+ lambda state: False
+ )
+
set_rule(
- get_entrance("REGION_ROUTE118/WEST -> REGION_ROUTE118/WATER"),
- can_surf
+ get_entrance("REGION_ROUTE118/WEST -> REGION_ROUTE118/WEST_WATER"),
+ hm_rules["HM03 Surf"]
)
set_rule(
- get_entrance("REGION_ROUTE118/EAST -> REGION_ROUTE118/WATER"),
- can_surf
+ get_entrance("REGION_ROUTE118/EAST -> REGION_ROUTE118/EAST_WATER"),
+ hm_rules["HM03 Surf"]
)
-
- # Route 119
set_rule(
- get_entrance("REGION_ROUTE119/LOWER -> REGION_ROUTE119/LOWER_ACROSS_WATER"),
- can_surf
+ get_entrance("REGION_ROUTE118/EAST -> REGION_TERRA_CAVE_ENTRANCE/MAIN"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player) and \
+ state.has("TERRA_CAVE_ROUTE_118_1", world.player) and \
+ state.has("EVENT_DEFEAT_SHELLY", world.player)
)
set_rule(
- get_entrance("REGION_ROUTE119/LOWER_ACROSS_WATER -> REGION_ROUTE119/LOWER"),
- can_surf
+ get_entrance("REGION_ROUTE118/WEST -> REGION_TERRA_CAVE_ENTRANCE/MAIN"),
+ lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player) and \
+ state.has("TERRA_CAVE_ROUTE_118_2", world.player) and \
+ state.has("EVENT_DEFEAT_SHELLY", world.player)
+ )
+
+ # Route 119
+ set_rule(
+ get_entrance("REGION_ROUTE119/LOWER -> REGION_ROUTE119/LOWER_WATER"),
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_ROUTE119/LOWER -> REGION_ROUTE119/LOWER_ACROSS_RAILS"),
@@ -579,15 +830,15 @@ def get_location(location: str):
)
set_rule(
get_entrance("REGION_ROUTE119/UPPER -> REGION_ROUTE119/MIDDLE_RIVER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_ROUTE119/MIDDLE_RIVER -> REGION_ROUTE119/ABOVE_WATERFALL"),
- can_waterfall
+ hm_rules["HM07 Waterfall"]
)
set_rule(
get_entrance("REGION_ROUTE119/ABOVE_WATERFALL -> REGION_ROUTE119/MIDDLE_RIVER"),
- can_waterfall
+ hm_rules["HM07 Waterfall"]
)
set_rule(
get_entrance("REGION_ROUTE119/ABOVE_WATERFALL -> REGION_ROUTE119/ABOVE_WATERFALL_ACROSS_RAILS"),
@@ -624,21 +875,37 @@ def get_location(location: str):
)
set_rule(
get_entrance("REGION_ROUTE120/NORTH_POND_SHORE -> REGION_ROUTE120/NORTH_POND"),
- can_surf
+ hm_rules["HM03 Surf"]
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE120/SOUTH -> REGION_ROUTE120/SOUTH_ALCOVE"),
+ hm_rules["HM01 Cut"]
)
set_rule(
get_entrance("REGION_ROUTE120/SOUTH -> REGION_ROUTE120/SOUTH_PONDS"),
- can_surf
+ hm_rules["HM03 Surf"]
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE120/SOUTH_ALCOVE -> REGION_ROUTE120/SOUTH"),
+ hm_rules["HM01 Cut"]
+ )
+ set_rule(
+ get_entrance("MAP_ROUTE120:0/MAP_ANCIENT_TOMB:0"),
+ lambda state: state.has("EVENT_UNDO_REGI_SEAL", world.player)
+ )
+ set_rule(
+ get_entrance("MAP_ANCIENT_TOMB:1/MAP_ANCIENT_TOMB:2"),
+ hm_rules["HM05 Flash"]
)
# Route 121
set_rule(
get_entrance("REGION_ROUTE121/EAST -> REGION_ROUTE121/WEST"),
- can_cut
+ hm_rules["HM01 Cut"]
)
set_rule(
- get_entrance("REGION_ROUTE121/EAST -> REGION_ROUTE122/SEA"),
- can_surf
+ get_entrance("REGION_ROUTE121/EAST -> REGION_ROUTE121/WATER"),
+ hm_rules["HM03 Surf"]
)
# Safari Zone
@@ -646,6 +913,10 @@ def get_location(location: str):
get_entrance("MAP_ROUTE121_SAFARI_ZONE_ENTRANCE:0,1/MAP_SAFARI_ZONE_SOUTH:0"),
lambda state: state.has("Pokeblock Case", world.player)
)
+ set_rule(
+ get_entrance("REGION_SAFARI_ZONE_NORTHWEST/MAIN -> REGION_SAFARI_ZONE_NORTHWEST/POND"),
+ hm_rules["HM03 Surf"]
+ )
set_rule(
get_entrance("REGION_SAFARI_ZONE_SOUTH/MAIN -> REGION_SAFARI_ZONE_NORTH/MAIN"),
lambda state: has_acro_bike(state)
@@ -654,6 +925,14 @@ def get_location(location: str):
get_entrance("REGION_SAFARI_ZONE_SOUTHWEST/MAIN -> REGION_SAFARI_ZONE_NORTHWEST/MAIN"),
lambda state: has_mach_bike(state)
)
+ set_rule(
+ get_entrance("REGION_SAFARI_ZONE_SOUTHWEST/MAIN -> REGION_SAFARI_ZONE_SOUTHWEST/POND"),
+ hm_rules["HM03 Surf"]
+ )
+ set_rule(
+ get_entrance("REGION_SAFARI_ZONE_SOUTHEAST/MAIN -> REGION_SAFARI_ZONE_SOUTHEAST/WATER"),
+ hm_rules["HM03 Surf"]
+ )
if "Safari Zone Construction Workers" not in world.options.remove_roadblocks.value:
set_rule(
get_entrance("REGION_SAFARI_ZONE_SOUTH/MAIN -> REGION_SAFARI_ZONE_SOUTHEAST/MAIN"),
@@ -663,28 +942,49 @@ def get_location(location: str):
# Route 122
set_rule(
get_entrance("REGION_ROUTE122/MT_PYRE_ENTRANCE -> REGION_ROUTE122/SEA"),
- can_surf
+ hm_rules["HM03 Surf"]
)
# Route 123
set_rule(
get_entrance("REGION_ROUTE123/EAST -> REGION_ROUTE122/SEA"),
- can_surf
+ hm_rules["HM03 Surf"]
+ )
+ set_rule(
+ get_entrance("REGION_ROUTE123/EAST -> REGION_ROUTE123/POND"),
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_ROUTE123/EAST -> REGION_ROUTE123/EAST_BEHIND_TREE"),
- can_cut
+ hm_rules["HM01 Cut"]
)
# Lilycove City
set_rule(
get_entrance("REGION_LILYCOVE_CITY/MAIN -> REGION_LILYCOVE_CITY/SEA"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_LILYCOVE_CITY_HARBOR/MAIN -> REGION_SS_TIDAL_CORRIDOR/MAIN"),
- lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player)
+ lambda state: state.has("S.S. Ticket", world.player)
)
+ set_rule(
+ get_entrance("REGION_LILYCOVE_CITY_HARBOR/MAIN -> REGION_SOUTHERN_ISLAND_EXTERIOR/MAIN"),
+ lambda state: state.has("Eon Ticket", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_LILYCOVE_CITY_HARBOR/MAIN -> REGION_FARAWAY_ISLAND_ENTRANCE/MAIN"),
+ lambda state: state.has("Old Sea Map", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_LILYCOVE_CITY_HARBOR/MAIN -> REGION_BIRTH_ISLAND_HARBOR/MAIN"),
+ lambda state: state.has("Aurora Ticket", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_LILYCOVE_CITY_HARBOR/MAIN -> REGION_NAVEL_ROCK_HARBOR/MAIN"),
+ lambda state: state.has("Mystic Ticket", world.player)
+ )
+
if "Lilycove City Wailmer" not in world.options.remove_roadblocks.value:
set_rule(
get_entrance("REGION_LILYCOVE_CITY/SEA -> REGION_ROUTE124/MAIN"),
@@ -694,11 +994,11 @@ def get_location(location: str):
# Magma Hideout
set_rule(
get_entrance("REGION_MAGMA_HIDEOUT_1F/ENTRANCE -> REGION_MAGMA_HIDEOUT_1F/MAIN"),
- can_strength
+ hm_rules["HM04 Strength"]
)
set_rule(
get_entrance("REGION_MAGMA_HIDEOUT_1F/MAIN -> REGION_MAGMA_HIDEOUT_1F/ENTRANCE"),
- can_strength
+ hm_rules["HM04 Strength"]
)
# Aqua Hideout
@@ -709,79 +1009,83 @@ def get_location(location: str):
)
set_rule(
get_entrance("REGION_AQUA_HIDEOUT_1F/MAIN -> REGION_AQUA_HIDEOUT_1F/WATER"),
- lambda state: can_surf(state) and state.has("EVENT_AQUA_STEALS_SUBMARINE", world.player)
+ lambda state: hm_rules["HM03 Surf"](state) and state.has("EVENT_AQUA_STEALS_SUBMARINE", world.player)
)
# Route 124
set_rule(
get_entrance("REGION_ROUTE124/MAIN -> REGION_UNDERWATER_ROUTE124/BIG_AREA"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE124/MAIN -> REGION_UNDERWATER_ROUTE124/SMALL_AREA_1"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE124/MAIN -> REGION_UNDERWATER_ROUTE124/SMALL_AREA_2"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE124/MAIN -> REGION_UNDERWATER_ROUTE124/SMALL_AREA_3"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE124/MAIN -> REGION_UNDERWATER_ROUTE124/TUNNEL_1"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE124/MAIN -> REGION_UNDERWATER_ROUTE124/TUNNEL_2"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE124/MAIN -> REGION_UNDERWATER_ROUTE124/TUNNEL_3"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE124/MAIN -> REGION_UNDERWATER_ROUTE124/TUNNEL_4"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE124/NORTH_ENCLOSED_AREA_1 -> REGION_UNDERWATER_ROUTE124/TUNNEL_1"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE124/NORTH_ENCLOSED_AREA_2 -> REGION_UNDERWATER_ROUTE124/TUNNEL_1"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE124/NORTH_ENCLOSED_AREA_3 -> REGION_UNDERWATER_ROUTE124/TUNNEL_2"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE124/SOUTH_ENCLOSED_AREA_1 -> REGION_UNDERWATER_ROUTE124/TUNNEL_3"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE124/SOUTH_ENCLOSED_AREA_2 -> REGION_UNDERWATER_ROUTE124/TUNNEL_3"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE124/SOUTH_ENCLOSED_AREA_3 -> REGION_UNDERWATER_ROUTE124/TUNNEL_4"),
- can_dive
+ hm_rules["HM08 Dive"]
)
# Mossdeep City
+ set_rule(
+ get_entrance("REGION_MOSSDEEP_CITY/MAIN -> REGION_MOSSDEEP_CITY/WATER"),
+ hm_rules["HM03 Surf"]
+ )
set_rule(
get_entrance("REGION_MOSSDEEP_CITY/MAIN -> REGION_ROUTE124/MAIN"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_MOSSDEEP_CITY/MAIN -> REGION_ROUTE125/SEA"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_MOSSDEEP_CITY/MAIN -> REGION_ROUTE127/MAIN"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_location("EVENT_DEFEAT_MAXIE_AT_SPACE_STATION"),
@@ -792,94 +1096,110 @@ def get_location(location: str):
lambda state: state.has("EVENT_DEFEAT_MAXIE_AT_SPACE_STATION", world.player)
)
set_rule(
- get_location("NPC_GIFT_RECEIVED_HM08"),
+ get_location("NPC_GIFT_RECEIVED_HM_DIVE"),
lambda state: state.has("EVENT_DEFEAT_MAXIE_AT_SPACE_STATION", world.player)
)
+ # Route 125
+ set_rule(
+ get_entrance("REGION_UNDERWATER_ROUTE125/MARINE_CAVE_ENTRANCE_1 -> REGION_UNDERWATER_MARINE_CAVE/MAIN"),
+ lambda state: hm_rules["HM08 Dive"](state) and \
+ state.has("EVENT_DEFEAT_CHAMPION", world.player) and \
+ state.has("MARINE_CAVE_ROUTE_125_1", world.player) and \
+ state.has("EVENT_DEFEAT_SHELLY", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_UNDERWATER_ROUTE125/MARINE_CAVE_ENTRANCE_2 -> REGION_UNDERWATER_MARINE_CAVE/MAIN"),
+ lambda state: hm_rules["HM08 Dive"](state) and \
+ state.has("EVENT_DEFEAT_CHAMPION", world.player) and \
+ state.has("MARINE_CAVE_ROUTE_125_2", world.player) and \
+ state.has("EVENT_DEFEAT_SHELLY", world.player)
+ )
+
# Shoal Cave
set_rule(
get_entrance("REGION_SHOAL_CAVE_ENTRANCE_ROOM/SOUTH -> REGION_SHOAL_CAVE_ENTRANCE_ROOM/HIGH_TIDE_WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_SHOAL_CAVE_ENTRANCE_ROOM/NORTH_WEST_CORNER -> REGION_SHOAL_CAVE_ENTRANCE_ROOM/HIGH_TIDE_WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_SHOAL_CAVE_ENTRANCE_ROOM/NORTH_EAST_CORNER -> REGION_SHOAL_CAVE_ENTRANCE_ROOM/HIGH_TIDE_WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_SHOAL_CAVE_INNER_ROOM/HIGH_TIDE_EAST_MIDDLE_GROUND -> REGION_SHOAL_CAVE_INNER_ROOM/SOUTH_EAST_WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_SHOAL_CAVE_INNER_ROOM/HIGH_TIDE_EAST_MIDDLE_GROUND -> REGION_SHOAL_CAVE_INNER_ROOM/EAST_WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_SHOAL_CAVE_INNER_ROOM/HIGH_TIDE_EAST_MIDDLE_GROUND -> REGION_SHOAL_CAVE_INNER_ROOM/NORTH_WEST_WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_SHOAL_CAVE_INNER_ROOM/SOUTH_WEST_CORNER -> REGION_SHOAL_CAVE_INNER_ROOM/NORTH_WEST_WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_SHOAL_CAVE_INNER_ROOM/RARE_CANDY_PLATFORM -> REGION_SHOAL_CAVE_INNER_ROOM/SOUTH_EAST_WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM/NORTH_WEST -> REGION_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM/EAST"),
- can_strength
+ hm_rules["HM04 Strength"]
)
set_rule(
get_entrance("REGION_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM/EAST -> REGION_SHOAL_CAVE_LOW_TIDE_LOWER_ROOM/NORTH_WEST"),
- can_strength
+ hm_rules["HM04 Strength"]
)
# Route 126
set_rule(
get_entrance("REGION_ROUTE126/MAIN -> REGION_UNDERWATER_ROUTE126/MAIN"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE126/MAIN -> REGION_UNDERWATER_ROUTE126/SMALL_AREA_2"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE126/NEAR_ROUTE_124 -> REGION_UNDERWATER_ROUTE126/TUNNEL"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE126/NORTH_WEST_CORNER -> REGION_UNDERWATER_ROUTE126/TUNNEL"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE126/WEST -> REGION_UNDERWATER_ROUTE126/MAIN"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE126/WEST -> REGION_UNDERWATER_ROUTE126/SMALL_AREA_1"),
- can_dive
+ hm_rules["HM08 Dive"]
)
# Sootopolis City
set_rule(
get_entrance("REGION_SOOTOPOLIS_CITY/WATER -> REGION_UNDERWATER_SOOTOPOLIS_CITY/MAIN"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_SOOTOPOLIS_CITY/EAST -> REGION_SOOTOPOLIS_CITY/WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_SOOTOPOLIS_CITY/WEST -> REGION_SOOTOPOLIS_CITY/WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_SOOTOPOLIS_CITY/ISLAND -> REGION_SOOTOPOLIS_CITY/WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("MAP_SOOTOPOLIS_CITY:3/MAP_CAVE_OF_ORIGIN_ENTRANCE:0"),
@@ -887,129 +1207,151 @@ def get_location(location: str):
)
set_rule(
get_entrance("MAP_SOOTOPOLIS_CITY:2/MAP_SOOTOPOLIS_CITY_GYM_1F:0"),
- lambda state: state.has("EVENT_WAKE_RAYQUAZA", world.player)
+ lambda state: state.has("EVENT_RAYQUAZA_STOPS_FIGHT", world.player)
)
set_rule(
- get_location("NPC_GIFT_RECEIVED_HM07"),
- lambda state: state.has("EVENT_WAKE_RAYQUAZA", world.player)
+ get_location("NPC_GIFT_RECEIVED_HM_WATERFALL"),
+ lambda state: state.has("EVENT_RAYQUAZA_STOPS_FIGHT", world.player)
+ )
+ set_rule(
+ get_location("EVENT_RAYQUAZA_STOPS_FIGHT"),
+ lambda state: state.has("EVENT_RELEASE_KYOGRE", world.player)
)
# Route 127
set_rule(
get_entrance("REGION_ROUTE127/MAIN -> REGION_UNDERWATER_ROUTE127/MAIN"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE127/MAIN -> REGION_UNDERWATER_ROUTE127/TUNNEL"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE127/MAIN -> REGION_UNDERWATER_ROUTE127/AREA_1"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE127/MAIN -> REGION_UNDERWATER_ROUTE127/AREA_2"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE127/MAIN -> REGION_UNDERWATER_ROUTE127/AREA_3"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE127/ENCLOSED_AREA -> REGION_UNDERWATER_ROUTE127/TUNNEL"),
- can_dive
+ hm_rules["HM08 Dive"]
+ )
+ set_rule(
+ get_entrance("REGION_UNDERWATER_ROUTE127/MARINE_CAVE_ENTRANCE_1 -> REGION_UNDERWATER_MARINE_CAVE/MAIN"),
+ lambda state: hm_rules["HM08 Dive"](state) and \
+ state.has("EVENT_DEFEAT_CHAMPION", world.player) and \
+ state.has("MARINE_CAVE_ROUTE_127_1", world.player) and \
+ state.has("EVENT_DEFEAT_SHELLY", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_UNDERWATER_ROUTE127/MARINE_CAVE_ENTRANCE_2 -> REGION_UNDERWATER_MARINE_CAVE/MAIN"),
+ lambda state: hm_rules["HM08 Dive"](state) and \
+ state.has("EVENT_DEFEAT_CHAMPION", world.player) and \
+ state.has("MARINE_CAVE_ROUTE_127_2", world.player) and \
+ state.has("EVENT_DEFEAT_SHELLY", world.player)
)
# Route 128
set_rule(
get_entrance("REGION_ROUTE128/MAIN -> REGION_UNDERWATER_ROUTE128/MAIN"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE128/MAIN -> REGION_UNDERWATER_ROUTE128/AREA_1"),
- can_dive
+ hm_rules["HM08 Dive"]
)
set_rule(
get_entrance("REGION_ROUTE128/MAIN -> REGION_UNDERWATER_ROUTE128/AREA_2"),
- can_dive
+ hm_rules["HM08 Dive"]
)
# Seafloor Cavern
+ set_rule(
+ get_entrance("REGION_SEAFLOOR_CAVERN_ENTRANCE/MAIN -> REGION_SEAFLOOR_CAVERN_ENTRANCE/WATER"),
+ hm_rules["HM03 Surf"]
+ )
+ set_rule(
+ get_entrance("REGION_SEAFLOOR_CAVERN_ENTRANCE/WATER -> REGION_UNDERWATER_SEAFLOOR_CAVERN/MAIN"),
+ hm_rules["HM08 Dive"]
+ )
set_rule(
get_entrance("REGION_SEAFLOOR_CAVERN_ROOM1/SOUTH -> REGION_SEAFLOOR_CAVERN_ROOM1/NORTH"),
- lambda state: can_rock_smash(state) and can_strength(state)
+ lambda state: hm_rules["HM06 Rock Smash"](state) and hm_rules["HM04 Strength"](state)
)
set_rule(
get_entrance("REGION_SEAFLOOR_CAVERN_ROOM1/NORTH -> REGION_SEAFLOOR_CAVERN_ROOM1/SOUTH"),
- can_strength
+ hm_rules["HM04 Strength"]
)
set_rule(
get_entrance("REGION_SEAFLOOR_CAVERN_ROOM2/SOUTH_WEST -> REGION_SEAFLOOR_CAVERN_ROOM2/NORTH_WEST"),
- can_strength
+ hm_rules["HM04 Strength"]
)
set_rule(
get_entrance("REGION_SEAFLOOR_CAVERN_ROOM2/NORTH_WEST -> REGION_SEAFLOOR_CAVERN_ROOM2/SOUTH_WEST"),
- can_strength
+ hm_rules["HM04 Strength"]
)
set_rule(
get_entrance("REGION_SEAFLOOR_CAVERN_ROOM2/SOUTH_WEST -> REGION_SEAFLOOR_CAVERN_ROOM2/SOUTH_EAST"),
- can_rock_smash
+ hm_rules["HM06 Rock Smash"]
)
set_rule(
get_entrance("REGION_SEAFLOOR_CAVERN_ROOM2/SOUTH_EAST -> REGION_SEAFLOOR_CAVERN_ROOM2/SOUTH_WEST"),
- can_rock_smash
+ hm_rules["HM06 Rock Smash"]
)
set_rule(
get_entrance("REGION_SEAFLOOR_CAVERN_ROOM2/NORTH_WEST -> REGION_SEAFLOOR_CAVERN_ROOM2/NORTH_EAST"),
- lambda state: can_rock_smash(state) and can_strength(state)
+ lambda state: hm_rules["HM06 Rock Smash"](state) and hm_rules["HM04 Strength"](state)
)
set_rule(
get_entrance("REGION_SEAFLOOR_CAVERN_ROOM2/NORTH_WEST -> REGION_SEAFLOOR_CAVERN_ROOM2/SOUTH_EAST"),
- lambda state: can_rock_smash(state) and can_strength(state)
+ lambda state: hm_rules["HM06 Rock Smash"](state) and hm_rules["HM04 Strength"](state)
)
set_rule(
get_entrance("REGION_SEAFLOOR_CAVERN_ROOM5/NORTH_WEST -> REGION_SEAFLOOR_CAVERN_ROOM5/EAST"),
- lambda state: can_rock_smash(state) and can_strength(state)
+ lambda state: hm_rules["HM06 Rock Smash"](state) and hm_rules["HM04 Strength"](state)
)
set_rule(
get_entrance("REGION_SEAFLOOR_CAVERN_ROOM5/EAST -> REGION_SEAFLOOR_CAVERN_ROOM5/NORTH_WEST"),
- can_strength
+ hm_rules["HM04 Strength"]
)
set_rule(
get_entrance("REGION_SEAFLOOR_CAVERN_ROOM5/NORTH_WEST -> REGION_SEAFLOOR_CAVERN_ROOM5/SOUTH_WEST"),
- lambda state: can_rock_smash(state) and can_strength(state)
+ lambda state: hm_rules["HM06 Rock Smash"](state) and hm_rules["HM04 Strength"](state)
)
set_rule(
get_entrance("REGION_SEAFLOOR_CAVERN_ROOM5/SOUTH_WEST -> REGION_SEAFLOOR_CAVERN_ROOM5/NORTH_WEST"),
- lambda state: can_rock_smash(state) and can_strength(state)
- )
- set_rule(
- get_entrance("REGION_SEAFLOOR_CAVERN_ROOM6/NORTH_WEST -> REGION_SEAFLOOR_CAVERN_ROOM6/CAVE_ON_WATER"),
- can_surf
+ lambda state: hm_rules["HM06 Rock Smash"](state) and hm_rules["HM04 Strength"](state)
)
set_rule(
- get_entrance("REGION_SEAFLOOR_CAVERN_ROOM6/SOUTH -> REGION_SEAFLOOR_CAVERN_ROOM6/NORTH_WEST"),
- can_surf
+ get_entrance("REGION_SEAFLOOR_CAVERN_ROOM6/NORTH_WEST -> REGION_SEAFLOOR_CAVERN_ROOM6/WATER"),
+ hm_rules["HM03 Surf"]
)
set_rule(
- get_entrance("REGION_SEAFLOOR_CAVERN_ROOM6/SOUTH -> REGION_SEAFLOOR_CAVERN_ROOM6/CAVE_ON_WATER"),
- can_surf
+ get_entrance("REGION_SEAFLOOR_CAVERN_ROOM6/SOUTH -> REGION_SEAFLOOR_CAVERN_ROOM6/WATER"),
+ hm_rules["HM03 Surf"]
)
set_rule(
- get_entrance("REGION_SEAFLOOR_CAVERN_ROOM7/SOUTH -> REGION_SEAFLOOR_CAVERN_ROOM7/NORTH"),
- can_surf
+ get_entrance("REGION_SEAFLOOR_CAVERN_ROOM7/SOUTH -> REGION_SEAFLOOR_CAVERN_ROOM7/WATER"),
+ hm_rules["HM03 Surf"]
)
set_rule(
- get_entrance("REGION_SEAFLOOR_CAVERN_ROOM7/NORTH -> REGION_SEAFLOOR_CAVERN_ROOM7/SOUTH"),
- can_surf
+ get_entrance("REGION_SEAFLOOR_CAVERN_ROOM7/NORTH -> REGION_SEAFLOOR_CAVERN_ROOM7/WATER"),
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_SEAFLOOR_CAVERN_ROOM8/NORTH -> REGION_SEAFLOOR_CAVERN_ROOM8/SOUTH"),
- can_strength
+ hm_rules["HM04 Strength"]
)
set_rule(
get_entrance("REGION_SEAFLOOR_CAVERN_ROOM8/SOUTH -> REGION_SEAFLOOR_CAVERN_ROOM8/NORTH"),
- can_strength
+ hm_rules["HM04 Strength"]
)
if "Seafloor Cavern Aqua Grunt" not in world.options.remove_roadblocks.value:
set_rule(
@@ -1017,104 +1359,134 @@ def get_location(location: str):
lambda state: state.has("EVENT_STEVEN_GIVES_DIVE", world.player)
)
+ # Route 129
+ set_rule(
+ get_entrance("REGION_UNDERWATER_ROUTE129/MARINE_CAVE_ENTRANCE_1 -> REGION_UNDERWATER_MARINE_CAVE/MAIN"),
+ lambda state: hm_rules["HM08 Dive"](state) and \
+ state.has("EVENT_DEFEAT_CHAMPION", world.player) and \
+ state.has("MARINE_CAVE_ROUTE_129_1", world.player) and \
+ state.has("EVENT_DEFEAT_SHELLY", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_UNDERWATER_ROUTE129/MARINE_CAVE_ENTRANCE_2 -> REGION_UNDERWATER_MARINE_CAVE/MAIN"),
+ lambda state: hm_rules["HM08 Dive"](state) and \
+ state.has("EVENT_DEFEAT_CHAMPION", world.player) and \
+ state.has("MARINE_CAVE_ROUTE_129_2", world.player) and \
+ state.has("EVENT_DEFEAT_SHELLY", world.player)
+ )
+
# Pacifidlog Town
+ set_rule(
+ get_entrance("REGION_PACIFIDLOG_TOWN/MAIN -> REGION_PACIFIDLOG_TOWN/WATER"),
+ hm_rules["HM03 Surf"]
+ )
set_rule(
get_entrance("REGION_PACIFIDLOG_TOWN/MAIN -> REGION_ROUTE131/MAIN"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_PACIFIDLOG_TOWN/MAIN -> REGION_ROUTE132/EAST"),
- can_surf
+ hm_rules["HM03 Surf"]
)
# Sky Pillar
set_rule(
get_entrance("MAP_SKY_PILLAR_OUTSIDE:1/MAP_SKY_PILLAR_1F:0"),
- lambda state: state.has("EVENT_WALLACE_GOES_TO_SKY_PILLAR", world.player)
- )
- # Sky Pillar does not require the mach bike until Rayquaza returns, which means the top
- # is only logically locked behind the mach bike after the top has been reached already
- # set_rule(
- # get_entrance("REGION_SKY_PILLAR_2F/RIGHT -> REGION_SKY_PILLAR_2F/LEFT"),
- # lambda state: has_mach_bike(state)
- # )
- # set_rule(
- # get_entrance("REGION_SKY_PILLAR_2F/LEFT -> REGION_SKY_PILLAR_2F/RIGHT"),
- # lambda state: has_mach_bike(state)
- # )
- # set_rule(
- # get_entrance("REGION_SKY_PILLAR_4F/MAIN -> REGION_SKY_PILLAR_4F/ABOVE_3F_TOP_CENTER"),
- # lambda state: has_mach_bike(state)
- # )
+ lambda state: state.has("EVENT_RELEASE_KYOGRE", world.player)
+ )
+ add_rule(
+ get_location("EVENT_ENCOUNTER_RAYQUAZA"),
+ lambda state: state.has("EVENT_RAYQUAZA_STOPS_FIGHT", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_SKY_PILLAR_2F/RIGHT -> REGION_SKY_PILLAR_2F/LEFT"),
+ has_mach_bike
+ )
+ set_rule(
+ get_entrance("REGION_SKY_PILLAR_2F/LEFT -> REGION_SKY_PILLAR_2F/RIGHT"),
+ has_mach_bike
+ )
+ set_rule(
+ get_entrance("REGION_SKY_PILLAR_4F/MAIN -> REGION_SKY_PILLAR_4F/ABOVE_3F_TOP_CENTER"),
+ has_mach_bike
+ )
# Route 134
set_rule(
get_entrance("REGION_ROUTE134/MAIN -> REGION_UNDERWATER_ROUTE134/MAIN"),
- can_dive
+ hm_rules["HM08 Dive"]
+ )
+ set_rule(
+ get_location("EVENT_UNDO_REGI_SEAL"),
+ lambda state: state.has("CATCH_SPECIES_WAILORD", world.player) and state.has("CATCH_SPECIES_RELICANTH", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_SEALED_CHAMBER_OUTER_ROOM/MAIN -> REGION_SEALED_CHAMBER_OUTER_ROOM/CRUMBLED_WALL"),
+ lambda state: state.has("EVENT_MOVE_TUTOR_DIG", world.player)
)
# Ever Grande City
set_rule(
get_entrance("REGION_EVER_GRANDE_CITY/SEA -> REGION_EVER_GRANDE_CITY/SOUTH"),
- can_waterfall
+ hm_rules["HM07 Waterfall"]
)
set_rule(
get_entrance("REGION_EVER_GRANDE_CITY/SOUTH -> REGION_EVER_GRANDE_CITY/SEA"),
- can_surf
+ hm_rules["HM03 Surf"]
)
# Victory Road
set_rule(
get_entrance("REGION_VICTORY_ROAD_B1F/SOUTH_WEST_MAIN -> REGION_VICTORY_ROAD_B1F/SOUTH_WEST_LADDER_UP"),
- lambda state: can_rock_smash(state) and can_strength(state)
+ lambda state: hm_rules["HM06 Rock Smash"](state) and hm_rules["HM04 Strength"](state)
)
set_rule(
get_entrance("REGION_VICTORY_ROAD_B1F/SOUTH_WEST_LADDER_UP -> REGION_VICTORY_ROAD_B1F/SOUTH_WEST_MAIN"),
- lambda state: can_rock_smash(state) and can_strength(state)
+ lambda state: hm_rules["HM06 Rock Smash"](state) and hm_rules["HM04 Strength"](state)
)
set_rule(
get_entrance("REGION_VICTORY_ROAD_B1F/MAIN_UPPER -> REGION_VICTORY_ROAD_B1F/MAIN_LOWER_EAST"),
- lambda state: can_rock_smash(state) and can_strength(state)
+ lambda state: hm_rules["HM06 Rock Smash"](state) and hm_rules["HM04 Strength"](state)
)
set_rule(
get_entrance("REGION_VICTORY_ROAD_B1F/MAIN_LOWER_EAST -> REGION_VICTORY_ROAD_B1F/MAIN_LOWER_WEST"),
- can_rock_smash
+ hm_rules["HM06 Rock Smash"]
)
set_rule(
get_entrance("REGION_VICTORY_ROAD_B1F/MAIN_LOWER_WEST -> REGION_VICTORY_ROAD_B1F/MAIN_LOWER_EAST"),
- lambda state: can_rock_smash(state) and can_strength(state)
+ lambda state: hm_rules["HM06 Rock Smash"](state) and hm_rules["HM04 Strength"](state)
)
set_rule(
get_entrance("REGION_VICTORY_ROAD_B1F/MAIN_LOWER_WEST -> REGION_VICTORY_ROAD_B1F/MAIN_UPPER"),
- lambda state: can_rock_smash(state) and can_strength(state)
+ lambda state: hm_rules["HM06 Rock Smash"](state) and hm_rules["HM04 Strength"](state)
)
set_rule(
get_entrance("REGION_VICTORY_ROAD_B2F/LOWER_WEST -> REGION_VICTORY_ROAD_B2F/LOWER_WEST_WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_VICTORY_ROAD_B2F/LOWER_WEST_ISLAND -> REGION_VICTORY_ROAD_B2F/LOWER_WEST_WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_VICTORY_ROAD_B2F/LOWER_EAST -> REGION_VICTORY_ROAD_B2F/LOWER_EAST_WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_VICTORY_ROAD_B2F/LOWER_WEST_WATER -> REGION_VICTORY_ROAD_B2F/UPPER_WATER"),
- can_waterfall
+ hm_rules["HM07 Waterfall"]
)
set_rule(
get_entrance("REGION_VICTORY_ROAD_B2F/LOWER_EAST_WATER -> REGION_VICTORY_ROAD_B2F/UPPER_WATER"),
- can_waterfall
+ hm_rules["HM07 Waterfall"]
)
set_rule(
get_entrance("REGION_VICTORY_ROAD_B2F/UPPER -> REGION_VICTORY_ROAD_B2F/UPPER_WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
set_rule(
get_entrance("REGION_VICTORY_ROAD_B2F/UPPER -> REGION_VICTORY_ROAD_B2F/LOWER_EAST_WATER"),
- can_surf
+ hm_rules["HM03 Surf"]
)
# Pokemon League
@@ -1130,85 +1502,62 @@ def get_location(location: str):
)
# Battle Frontier
- # set_rule(
- # get_entrance("REGION_BATTLE_FRONTIER_OUTSIDE_WEST/DOCK -> REGION_LILYCOVE_CITY_HARBOR/MAIN"),
- # lambda state: state.has("S.S. Ticket", world.player) and
- # (state.has("EVENT_DEFEAT_CHAMPION", world.player) or world.options.enable_ferry.value == Toggle.option_true)
- # )
- # set_rule(
- # get_entrance("REGION_BATTLE_FRONTIER_OUTSIDE_WEST/DOCK -> REGION_SLATEPORT_CITY_HARBOR/MAIN"),
- # lambda state: state.has("S.S. Ticket", world.player) and
- # (state.has("EVENT_DEFEAT_CHAMPION", world.player) or world.options.enable_ferry.value == Toggle.option_true)
- # )
- # set_rule(
- # get_entrance("REGION_BATTLE_FRONTIER_OUTSIDE_WEST/CAVE_ENTRANCE -> REGION_BATTLE_FRONTIER_OUTSIDE_WEST/WATER"),
- # can_surf
- # )
- # set_rule(
- # get_entrance("REGION_BATTLE_FRONTIER_OUTSIDE_EAST/MAIN -> REGION_BATTLE_FRONTIER_OUTSIDE_EAST/ABOVE_WATERFALL"),
- # lambda state: state.has("Wailmer Pail", world.player) and can_surf(state)
- # )
- # set_rule(
- # get_entrance("REGION_BATTLE_FRONTIER_OUTSIDE_EAST/ABOVE_WATERFALL -> REGION_BATTLE_FRONTIER_OUTSIDE_EAST/MAIN"),
- # lambda state: state.has("ITEM_WAILMER_PAIL", world.player)
- # )
- # set_rule(
- # get_entrance("REGION_BATTLE_FRONTIER_OUTSIDE_EAST/WATER -> REGION_BATTLE_FRONTIER_OUTSIDE_EAST/ABOVE_WATERFALL"),
- # can_waterfall
- # )
+ set_rule(
+ get_entrance("REGION_BATTLE_FRONTIER_OUTSIDE_WEST/DOCK -> REGION_SS_TIDAL_CORRIDOR/MAIN"),
+ lambda state: state.has("S.S. Ticket", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_BATTLE_FRONTIER_OUTSIDE_WEST/CAVE_ENTRANCE -> REGION_BATTLE_FRONTIER_OUTSIDE_WEST/WATER"),
+ hm_rules["HM03 Surf"]
+ )
+ set_rule(
+ get_entrance("REGION_BATTLE_FRONTIER_OUTSIDE_EAST/MAIN -> REGION_BATTLE_FRONTIER_OUTSIDE_EAST/ABOVE_WATERFALL"),
+ lambda state: state.has("Wailmer Pail", world.player) and hm_rules["HM03 Surf"](state)
+ )
+ set_rule(
+ get_entrance("REGION_BATTLE_FRONTIER_OUTSIDE_EAST/ABOVE_WATERFALL -> REGION_BATTLE_FRONTIER_OUTSIDE_EAST/MAIN"),
+ lambda state: state.has("Wailmer Pail", world.player)
+ )
+ set_rule(
+ get_entrance("REGION_BATTLE_FRONTIER_OUTSIDE_EAST/WATER -> REGION_BATTLE_FRONTIER_OUTSIDE_EAST/ABOVE_WATERFALL"),
+ hm_rules["HM07 Waterfall"]
+ )
- # Overworld Items
- if world.options.overworld_items:
- # Route 103
- set_rule(
- get_location("ITEM_ROUTE_103_PP_UP"),
- can_cut
- )
- set_rule(
- get_location("ITEM_ROUTE_103_GUARD_SPEC"),
- can_cut
- )
+ # Pokedex Rewards
+ if world.options.dexsanity:
+ for i in range(NUM_REAL_SPECIES):
+ species = data.species[NATIONAL_ID_TO_SPECIES_ID[i + 1]]
+ set_rule(
+ get_location(f"Pokedex - {species.label}"),
+ lambda state, species_name=species.name: state.has(f"CATCH_{species_name}", world.player)
+ )
- # Route 104
- set_rule(
- get_location("ITEM_ROUTE_104_X_ACCURACY"),
- lambda state: can_surf(state) or can_cut(state)
- )
- set_rule(
- get_location("ITEM_ROUTE_104_PP_UP"),
- can_surf
- )
+ # Legendary hunt prevents Latios from being a wild spawn so the roamer
+ # can be tracked, and also guarantees that the roamer is a Latios.
+ if world.options.goal == Goal.option_legendary_hunt:
+ set_rule(
+ get_location(f"Pokedex - Latios"),
+ lambda state: state.has("EVENT_ENCOUNTER_LATIOS", world.player)
+ )
+ # Overworld Items
+ if world.options.overworld_items:
# Route 117
set_rule(
get_location("ITEM_ROUTE_117_REVIVE"),
- can_cut
+ hm_rules["HM01 Cut"]
)
# Route 114
set_rule(
get_location("ITEM_ROUTE_114_PROTEIN"),
- can_rock_smash
- )
-
- # Safari Zone
- set_rule(
- get_location("ITEM_SAFARI_ZONE_NORTH_WEST_TM22"),
- can_surf
- )
- set_rule(
- get_location("ITEM_SAFARI_ZONE_SOUTH_WEST_MAX_REVIVE"),
- can_surf
- )
- set_rule(
- get_location("ITEM_SAFARI_ZONE_SOUTH_EAST_BIG_PEARL"),
- can_surf
+ hm_rules["HM06 Rock Smash"]
)
# Victory Road
set_rule(
get_location("ITEM_VICTORY_ROAD_B1F_FULL_RESTORE"),
- lambda state: can_rock_smash(state) and can_strength(state)
+ lambda state: hm_rules["HM06 Rock Smash"](state) and hm_rules["HM04 Strength"](state)
)
# Hidden Items
@@ -1216,13 +1565,13 @@ def get_location(location: str):
# Route 120
set_rule(
get_location("HIDDEN_ITEM_ROUTE_120_RARE_CANDY_1"),
- can_cut
+ hm_rules["HM01 Cut"]
)
# Route 121
set_rule(
get_location("HIDDEN_ITEM_ROUTE_121_NUGGET"),
- can_cut
+ hm_rules["HM01 Cut"]
)
# NPC Gifts
@@ -1233,12 +1582,6 @@ def get_location(location: str):
lambda state: state.has("EVENT_TALK_TO_MR_STONE", world.player) and state.has("Balance Badge", world.player)
)
- # Petalburg City
- set_rule(
- get_location("NPC_GIFT_RECEIVED_TM36"),
- lambda state: state.has("EVENT_DEFEAT_NORMAN", world.player)
- )
-
# Route 104
set_rule(
get_location("NPC_GIFT_RECEIVED_WHITE_HERB"),
@@ -1251,6 +1594,18 @@ def get_location(location: str):
lambda state: state.has("EVENT_DELIVER_LETTER", world.player)
)
+ # Route 116
+ set_rule(
+ get_location("NPC_GIFT_RECEIVED_REPEAT_BALL"),
+ lambda state: state.has("EVENT_RESCUE_CAPT_STERN", world.player)
+ )
+
+ # Dewford Town
+ set_rule(
+ get_location("NPC_GIFT_RECEIVED_TM_SLUDGE_BOMB"),
+ lambda state: state.has("EVENT_DEFEAT_NORMAN", world.player)
+ )
+
# Slateport City
set_rule(
get_location("NPC_GIFT_RECEIVED_DEEP_SEA_TOOTH"),
@@ -1265,15 +1620,9 @@ def get_location(location: str):
and state.has("Mind Badge", world.player)
)
- # Route 116
- set_rule(
- get_location("NPC_GIFT_RECEIVED_REPEAT_BALL"),
- lambda state: state.has("EVENT_RESCUE_CAPT_STERN", world.player)
- )
-
# Mauville City
set_rule(
- get_location("NPC_GIFT_GOT_TM24_FROM_WATTSON"),
+ get_location("NPC_GIFT_GOT_TM_THUNDERBOLT_FROM_WATTSON"),
lambda state: state.has("EVENT_DEFEAT_NORMAN", world.player) and state.has("EVENT_TURN_OFF_GENERATOR", world.player)
)
set_rule(
@@ -1283,7 +1632,7 @@ def get_location(location: str):
# Fallarbor Town
set_rule(
- get_location("NPC_GIFT_RECEIVED_TM27"),
+ get_location("NPC_GIFT_RECEIVED_TM_RETURN"),
lambda state: state.has("EVENT_RECOVER_METEORITE", world.player) and state.has("Meteorite", world.player)
)
@@ -1293,21 +1642,6 @@ def get_location(location: str):
lambda state: state.has("EVENT_WINGULL_QUEST_2", world.player)
)
- # Ferry Items
- if world.options.enable_ferry:
- set_rule(
- get_location("NPC_GIFT_RECEIVED_SS_TICKET"),
- lambda state: state.has("EVENT_DEFEAT_CHAMPION", world.player)
- )
- set_rule(
- get_entrance("REGION_SLATEPORT_CITY_HARBOR/MAIN -> REGION_SS_TIDAL_CORRIDOR/MAIN"),
- lambda state: state.has("S.S. Ticket", world.player)
- )
- set_rule(
- get_entrance("REGION_LILYCOVE_CITY_HARBOR/MAIN -> REGION_SS_TIDAL_CORRIDOR/MAIN"),
- lambda state: state.has("S.S. Ticket", world.player)
- )
-
# Add Itemfinder requirement to hidden items
if world.options.require_itemfinder:
for location in world.multiworld.get_locations(world.player):
@@ -1318,59 +1652,60 @@ def get_location(location: str):
)
# Add Flash requirements to dark caves
- if world.options.require_flash:
- # Granite Cave
+ # Granite Cave
+ if world.options.require_flash in [DarkCavesRequireFlash.option_only_granite_cave, DarkCavesRequireFlash.option_both]:
add_rule(
get_entrance("MAP_GRANITE_CAVE_1F:2/MAP_GRANITE_CAVE_B1F:1"),
- can_flash
+ hm_rules["HM05 Flash"]
)
add_rule(
get_entrance("MAP_GRANITE_CAVE_B1F:3/MAP_GRANITE_CAVE_B2F:1"),
- can_flash
+ hm_rules["HM05 Flash"]
)
- # Victory Road
+ # Victory Road
+ if world.options.require_flash in [DarkCavesRequireFlash.option_only_victory_road, DarkCavesRequireFlash.option_both]:
add_rule(
get_entrance("MAP_VICTORY_ROAD_1F:2/MAP_VICTORY_ROAD_B1F:5"),
- can_flash
+ hm_rules["HM05 Flash"]
)
add_rule(
get_entrance("MAP_VICTORY_ROAD_1F:4/MAP_VICTORY_ROAD_B1F:4"),
- can_flash
+ hm_rules["HM05 Flash"]
)
add_rule(
get_entrance("MAP_VICTORY_ROAD_1F:3/MAP_VICTORY_ROAD_B1F:2"),
- can_flash
+ hm_rules["HM05 Flash"]
)
add_rule(
get_entrance("MAP_VICTORY_ROAD_B1F:3/MAP_VICTORY_ROAD_B2F:1"),
- can_flash
+ hm_rules["HM05 Flash"]
)
add_rule(
get_entrance("MAP_VICTORY_ROAD_B1F:1/MAP_VICTORY_ROAD_B2F:2"),
- can_flash
+ hm_rules["HM05 Flash"]
)
add_rule(
get_entrance("MAP_VICTORY_ROAD_B1F:6/MAP_VICTORY_ROAD_B2F:3"),
- can_flash
+ hm_rules["HM05 Flash"]
)
add_rule(
get_entrance("MAP_VICTORY_ROAD_B1F:0/MAP_VICTORY_ROAD_B2F:0"),
- can_flash
+ hm_rules["HM05 Flash"]
)
add_rule(
get_entrance("MAP_VICTORY_ROAD_B2F:3/MAP_VICTORY_ROAD_B1F:6"),
- can_flash
+ hm_rules["HM05 Flash"]
)
add_rule(
get_entrance("MAP_VICTORY_ROAD_B2F:2/MAP_VICTORY_ROAD_B1F:1"),
- can_flash
+ hm_rules["HM05 Flash"]
)
add_rule(
get_entrance("MAP_VICTORY_ROAD_B2F:0/MAP_VICTORY_ROAD_B1F:0"),
- can_flash
+ hm_rules["HM05 Flash"]
)
add_rule(
get_entrance("MAP_VICTORY_ROAD_B2F:1/MAP_VICTORY_ROAD_B1F:3"),
- can_flash
+ hm_rules["HM05 Flash"]
)
diff --git a/worlds/pokemon_emerald/sanity_check.py b/worlds/pokemon_emerald/sanity_check.py
index 58f9b1ef4d86..24eb768bfbc5 100644
--- a/worlds/pokemon_emerald/sanity_check.py
+++ b/worlds/pokemon_emerald/sanity_check.py
@@ -5,35 +5,41 @@
import logging
from typing import List
-from .data import data
-
-
-_ignorable_locations = {
- # Trick House
- "HIDDEN_ITEM_TRICK_HOUSE_NUGGET",
- "ITEM_TRICK_HOUSE_PUZZLE_1_ORANGE_MAIL",
- "ITEM_TRICK_HOUSE_PUZZLE_2_HARBOR_MAIL",
- "ITEM_TRICK_HOUSE_PUZZLE_2_WAVE_MAIL",
- "ITEM_TRICK_HOUSE_PUZZLE_3_SHADOW_MAIL",
- "ITEM_TRICK_HOUSE_PUZZLE_3_WOOD_MAIL",
- "ITEM_TRICK_HOUSE_PUZZLE_4_MECH_MAIL",
- "ITEM_TRICK_HOUSE_PUZZLE_6_GLITTER_MAIL",
- "ITEM_TRICK_HOUSE_PUZZLE_7_TROPIC_MAIL",
- "ITEM_TRICK_HOUSE_PUZZLE_8_BEAD_MAIL",
-
- # Battle Frontier
- "ITEM_ARTISAN_CAVE_1F_CARBOS",
- "ITEM_ARTISAN_CAVE_B1F_HP_UP",
- "HIDDEN_ITEM_ARTISAN_CAVE_B1F_CALCIUM",
- "HIDDEN_ITEM_ARTISAN_CAVE_B1F_IRON",
- "HIDDEN_ITEM_ARTISAN_CAVE_B1F_PROTEIN",
- "HIDDEN_ITEM_ARTISAN_CAVE_B1F_ZINC",
-
- # Event islands
- "HIDDEN_ITEM_NAVEL_ROCK_TOP_SACRED_ASH"
-}
-
-_ignorable_warps = {
+from .data import load_json_data, data
+
+
+_IGNORABLE_LOCATIONS = frozenset({
+ "HIDDEN_ITEM_TRICK_HOUSE_NUGGET", # Is permanently mssiable and has special behavior that sets the flag early
+
+ # Duplicate rival fights. All variations are represented by the Brandon + Mudkip version
+ "TRAINER_BRENDAN_ROUTE_103_TREECKO_REWARD",
+ "TRAINER_BRENDAN_ROUTE_103_TORCHIC_REWARD",
+ "TRAINER_MAY_ROUTE_103_MUDKIP_REWARD",
+ "TRAINER_MAY_ROUTE_103_TREECKO_REWARD",
+ "TRAINER_MAY_ROUTE_103_TORCHIC_REWARD",
+ "TRAINER_BRENDAN_ROUTE_110_TREECKO_REWARD",
+ "TRAINER_BRENDAN_ROUTE_110_TORCHIC_REWARD",
+ "TRAINER_MAY_ROUTE_110_MUDKIP_REWARD",
+ "TRAINER_MAY_ROUTE_110_TREECKO_REWARD",
+ "TRAINER_MAY_ROUTE_110_TORCHIC_REWARD",
+ "TRAINER_BRENDAN_ROUTE_119_TREECKO_REWARD",
+ "TRAINER_BRENDAN_ROUTE_119_TORCHIC_REWARD",
+ "TRAINER_MAY_ROUTE_119_MUDKIP_REWARD",
+ "TRAINER_MAY_ROUTE_119_TREECKO_REWARD",
+ "TRAINER_MAY_ROUTE_119_TORCHIC_REWARD",
+ "TRAINER_BRENDAN_RUSTBORO_TREECKO_REWARD",
+ "TRAINER_BRENDAN_RUSTBORO_TORCHIC_REWARD",
+ "TRAINER_MAY_RUSTBORO_MUDKIP_REWARD",
+ "TRAINER_MAY_RUSTBORO_TREECKO_REWARD",
+ "TRAINER_MAY_RUSTBORO_TORCHIC_REWARD",
+ "TRAINER_BRENDAN_LILYCOVE_TREECKO_REWARD",
+ "TRAINER_BRENDAN_LILYCOVE_TORCHIC_REWARD",
+ "TRAINER_MAY_LILYCOVE_MUDKIP_REWARD",
+ "TRAINER_MAY_LILYCOVE_TREECKO_REWARD",
+ "TRAINER_MAY_LILYCOVE_TORCHIC_REWARD",
+})
+
+_IGNORABLE_WARPS = frozenset({
# Trick House
"MAP_ROUTE110_TRICK_HOUSE_PUZZLE2:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!",
"MAP_ROUTE110_TRICK_HOUSE_PUZZLE2:2/MAP_ROUTE110_TRICK_HOUSE_END:0!",
@@ -72,75 +78,16 @@
"MAP_INSIDE_OF_TRUCK:0,1,2/MAP_DYNAMIC:-1!",
# Battle Frontier
- "MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:1",
"MAP_BATTLE_FRONTIER_BATTLE_DOME_CORRIDOR:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1!",
- "MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1",
"MAP_BATTLE_FRONTIER_BATTLE_DOME_PRE_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1!",
- "MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:2",
"MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2",
"MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:2",
"MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0",
"MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:3/MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0!",
- "MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:2",
"MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:0",
- "MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY:0,1,2/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:0",
- "MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:3",
- "MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:2",
- "MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:0",
- "MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:2/MAP_BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM:0",
- "MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER:0,1,2/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:6",
- "MAP_BATTLE_FRONTIER_LOUNGE1:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:5",
- "MAP_BATTLE_FRONTIER_LOUNGE2:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:3",
- "MAP_BATTLE_FRONTIER_LOUNGE3:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:9",
- "MAP_BATTLE_FRONTIER_LOUNGE4:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:6",
- "MAP_BATTLE_FRONTIER_LOUNGE5:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:7",
- "MAP_BATTLE_FRONTIER_LOUNGE6:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:8",
- "MAP_BATTLE_FRONTIER_LOUNGE7:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:7",
- "MAP_BATTLE_FRONTIER_LOUNGE8:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:10",
- "MAP_BATTLE_FRONTIER_LOUNGE9:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:11",
- "MAP_BATTLE_FRONTIER_MART:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:4",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:0/MAP_BATTLE_FRONTIER_BATTLE_TOWER_LOBBY:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:1/MAP_BATTLE_FRONTIER_BATTLE_ARENA_LOBBY:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:10/MAP_BATTLE_FRONTIER_LOUNGE8:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:11/MAP_BATTLE_FRONTIER_LOUNGE9:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:12/MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:13/MAP_ARTISAN_CAVE_1F:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:3/MAP_BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:4/MAP_BATTLE_FRONTIER_RANKING_HALL:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:5/MAP_BATTLE_FRONTIER_LOUNGE1:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:6/MAP_BATTLE_FRONTIER_EXCHANGE_SERVICE_CORNER:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:7/MAP_BATTLE_FRONTIER_LOUNGE5:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:8/MAP_BATTLE_FRONTIER_LOUNGE6:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_EAST:9/MAP_BATTLE_FRONTIER_LOUNGE3:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:0/MAP_BATTLE_FRONTIER_BATTLE_PIKE_LOBBY:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1/MAP_BATTLE_FRONTIER_BATTLE_DOME_LOBBY:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:10/MAP_ARTISAN_CAVE_B1F:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:2/MAP_BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:3/MAP_BATTLE_FRONTIER_LOUNGE2:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:4/MAP_BATTLE_FRONTIER_MART:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:5/MAP_BATTLE_FRONTIER_SCOTTS_HOUSE:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:6/MAP_BATTLE_FRONTIER_LOUNGE4:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:7/MAP_BATTLE_FRONTIER_LOUNGE7:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:8/MAP_BATTLE_FRONTIER_RECEPTION_GATE:0",
- "MAP_BATTLE_FRONTIER_OUTSIDE_WEST:9/MAP_BATTLE_FRONTIER_RECEPTION_GATE:1",
- "MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:12",
- "MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:2/MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:0",
- "MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:0/MAP_BATTLE_FRONTIER_POKEMON_CENTER_1F:2",
- "MAP_BATTLE_FRONTIER_RANKING_HALL:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:4",
- "MAP_BATTLE_FRONTIER_RECEPTION_GATE:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:8",
- "MAP_BATTLE_FRONTIER_RECEPTION_GATE:1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:9",
- "MAP_BATTLE_FRONTIER_SCOTTS_HOUSE:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:5",
-
- "MAP_ARTISAN_CAVE_1F:0/MAP_BATTLE_FRONTIER_OUTSIDE_EAST:13",
- "MAP_ARTISAN_CAVE_1F:1/MAP_ARTISAN_CAVE_B1F:1",
- "MAP_ARTISAN_CAVE_B1F:0/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:10",
- "MAP_ARTISAN_CAVE_B1F:1/MAP_ARTISAN_CAVE_1F:1",
# Terra Cave and Marine Cave
"MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!",
- "MAP_TERRA_CAVE_END:0/MAP_TERRA_CAVE_ENTRANCE:1",
- "MAP_TERRA_CAVE_ENTRANCE:1/MAP_TERRA_CAVE_END:0",
"MAP_ROUTE113:1/MAP_TERRA_CAVE_ENTRANCE:0!",
"MAP_ROUTE113:2/MAP_TERRA_CAVE_ENTRANCE:0!",
"MAP_ROUTE114:3/MAP_TERRA_CAVE_ENTRANCE:0!",
@@ -153,8 +100,6 @@
"MAP_ROUTE118:1/MAP_TERRA_CAVE_ENTRANCE:0!",
"MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!",
- "MAP_MARINE_CAVE_END:0/MAP_MARINE_CAVE_ENTRANCE:0",
- "MAP_MARINE_CAVE_ENTRANCE:0/MAP_MARINE_CAVE_END:0",
"MAP_UNDERWATER_ROUTE105:0/MAP_UNDERWATER_MARINE_CAVE:0!",
"MAP_UNDERWATER_ROUTE105:1/MAP_UNDERWATER_MARINE_CAVE:0!",
"MAP_UNDERWATER_ROUTE125:0/MAP_UNDERWATER_MARINE_CAVE:0!",
@@ -164,6 +109,10 @@
"MAP_UNDERWATER_ROUTE129:0/MAP_UNDERWATER_MARINE_CAVE:0!",
"MAP_UNDERWATER_ROUTE129:1/MAP_UNDERWATER_MARINE_CAVE:0!",
+ # Altering Cave
+ "MAP_ALTERING_CAVE:0/MAP_ROUTE103:0",
+ "MAP_ROUTE103:0/MAP_ALTERING_CAVE:0",
+
# Event islands
"MAP_BIRTH_ISLAND_EXTERIOR:0/MAP_BIRTH_ISLAND_HARBOR:0",
"MAP_BIRTH_ISLAND_HARBOR:0/MAP_BIRTH_ISLAND_EXTERIOR:0",
@@ -294,11 +243,17 @@
"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:1/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:0",
"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:0/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:1",
"MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:1/MAP_CAVE_OF_ORIGIN_B1F:0!",
- "MAP_LILYCOVE_CITY_UNUSED_MART:0,1/MAP_LILYCOVE_CITY:0!"
-}
+ "MAP_LILYCOVE_CITY_UNUSED_MART:0,1/MAP_LILYCOVE_CITY:0!",
+})
def validate_regions() -> bool:
+ """
+ Verifies that Emerald's data doesn't have duplicate or missing
+ regions/warps/locations. Meant to catch problems during development like
+ forgetting to add a new location or incorrectly splitting a region.
+ """
+ extracted_data_json = load_json_data("extracted_data.json")
error_messages: List[str] = []
warn_messages: List[str] = []
failed = False
@@ -319,7 +274,7 @@ def warn(message: str) -> None:
# Check warps
for warp_source, warp_dest in data.warp_map.items():
- if warp_source in _ignorable_warps:
+ if warp_source in _IGNORABLE_WARPS:
continue
if warp_dest is None:
@@ -335,8 +290,8 @@ def warn(message: str) -> None:
error(f"Pokemon Emerald: Location [{location_name}] was claimed by multiple regions")
claimed_locations_set.add(location_name)
- for location_name in data.locations:
- if location_name not in claimed_locations and location_name not in _ignorable_locations:
+ for location_name in extracted_data_json["locations"]:
+ if location_name not in claimed_locations and location_name not in _IGNORABLE_LOCATIONS:
warn(f"Pokemon Emerald: Location [{location_name}] was not claimed by any region")
warn_messages.sort()
diff --git a/worlds/pokemon_emerald/test/test_accessibility.py b/worlds/pokemon_emerald/test/test_accessibility.py
index 853a92ffb82c..d27301517137 100644
--- a/worlds/pokemon_emerald/test/test_accessibility.py
+++ b/worlds/pokemon_emerald/test/test_accessibility.py
@@ -21,25 +21,25 @@ def test_with_neither(self) -> None:
self.collect_by_name(["S.S. Ticket", "Letter", "Stone Badge", "HM01 Cut"])
self.assertTrue(self.can_reach_region("REGION_ROUTE120/NORTH"))
self.assertFalse(self.can_reach_location(location_name_to_label("ITEM_ROUTE_120_NEST_BALL")))
- self.assertFalse(self.can_reach_location(location_name_to_label("ITEM_SCORCHED_SLAB_TM11")))
+ self.assertFalse(self.can_reach_location(location_name_to_label("ITEM_SCORCHED_SLAB_TM_SUNNY_DAY")))
def test_with_surf(self) -> None:
self.collect_by_name(["S.S. Ticket", "Letter", "Stone Badge", "HM01 Cut", "HM03 Surf", "Balance Badge"])
self.assertTrue(self.can_reach_region("REGION_ROUTE120/NORTH"))
self.assertFalse(self.can_reach_location(location_name_to_label("ITEM_ROUTE_120_NEST_BALL")))
- self.assertFalse(self.can_reach_location(location_name_to_label("ITEM_SCORCHED_SLAB_TM11")))
+ self.assertFalse(self.can_reach_location(location_name_to_label("ITEM_SCORCHED_SLAB_TM_SUNNY_DAY")))
def test_with_scope(self) -> None:
self.collect_by_name(["S.S. Ticket", "Letter", "Stone Badge", "HM01 Cut", "Devon Scope"])
self.assertTrue(self.can_reach_region("REGION_ROUTE120/NORTH"))
self.assertTrue(self.can_reach_location(location_name_to_label("ITEM_ROUTE_120_NEST_BALL")))
- self.assertFalse(self.can_reach_location(location_name_to_label("ITEM_SCORCHED_SLAB_TM11")))
+ self.assertFalse(self.can_reach_location(location_name_to_label("ITEM_SCORCHED_SLAB_TM_SUNNY_DAY")))
def test_with_both(self) -> None:
self.collect_by_name(["S.S. Ticket", "Letter", "Stone Badge", "HM01 Cut", "Devon Scope", "HM03 Surf", "Balance Badge"])
self.assertTrue(self.can_reach_region("REGION_ROUTE120/NORTH"))
self.assertTrue(self.can_reach_location(location_name_to_label("ITEM_ROUTE_120_NEST_BALL")))
- self.assertTrue(self.can_reach_location(location_name_to_label("ITEM_SCORCHED_SLAB_TM11")))
+ self.assertTrue(self.can_reach_location(location_name_to_label("ITEM_SCORCHED_SLAB_TM_SUNNY_DAY")))
class TestSurf(PokemonEmeraldTestBase):
@@ -55,7 +55,7 @@ def test_inaccessible_with_no_surf(self) -> None:
self.assertFalse(self.can_reach_location(location_name_to_label("ITEM_LILYCOVE_CITY_MAX_REPEL")))
self.assertFalse(self.can_reach_location(location_name_to_label("HIDDEN_ITEM_ROUTE_120_RARE_CANDY_2")))
self.assertFalse(self.can_reach_location(location_name_to_label("ITEM_ROUTE_120_FULL_HEAL")))
- self.assertFalse(self.can_reach_entrance("REGION_ROUTE118/WATER -> REGION_ROUTE118/EAST"))
+ self.assertFalse(self.can_reach_entrance("REGION_ROUTE118/EAST_WATER -> REGION_ROUTE118/EAST"))
self.assertFalse(self.can_reach_entrance("REGION_ROUTE119/UPPER -> REGION_FORTREE_CITY/MAIN"))
self.assertFalse(self.can_reach_entrance("MAP_FORTREE_CITY:3/MAP_FORTREE_CITY_MART:0"))
@@ -66,7 +66,7 @@ def test_accessible_with_surf_only(self) -> None:
self.assertTrue(self.can_reach_location(location_name_to_label("ITEM_LILYCOVE_CITY_MAX_REPEL")))
self.assertTrue(self.can_reach_location(location_name_to_label("HIDDEN_ITEM_ROUTE_120_RARE_CANDY_2")))
self.assertTrue(self.can_reach_location(location_name_to_label("ITEM_ROUTE_120_FULL_HEAL")))
- self.assertTrue(self.can_reach_entrance("REGION_ROUTE118/WATER -> REGION_ROUTE118/EAST"))
+ self.assertTrue(self.can_reach_entrance("REGION_ROUTE118/EAST_WATER -> REGION_ROUTE118/EAST"))
self.assertTrue(self.can_reach_entrance("REGION_ROUTE119/UPPER -> REGION_FORTREE_CITY/MAIN"))
self.assertTrue(self.can_reach_entrance("MAP_FORTREE_CITY:3/MAP_FORTREE_CITY_MART:0"))
self.assertTrue(self.can_reach_location(location_name_to_label("BADGE_4")))
@@ -88,17 +88,16 @@ def setUp(self) -> None:
def test_sootopolis_gift_inaccessible_with_no_surf(self) -> None:
self.collect_by_name(["HM02 Fly", "Feather Badge"])
- self.assertFalse(self.can_reach_location(location_name_to_label("NPC_GIFT_RECEIVED_TM31")))
+ self.assertFalse(self.can_reach_location(location_name_to_label("NPC_GIFT_RECEIVED_TM_BRICK_BREAK")))
def test_sootopolis_gift_accessible_with_surf(self) -> None:
self.collect_by_name(["HM03 Surf", "Balance Badge", "HM02 Fly", "Feather Badge"])
- self.assertTrue(self.can_reach_location(location_name_to_label("NPC_GIFT_RECEIVED_TM31")))
+ self.assertTrue(self.can_reach_location(location_name_to_label("NPC_GIFT_RECEIVED_TM_BRICK_BREAK")))
class TestFerry(PokemonEmeraldTestBase):
options = {
- "npc_gifts": Toggle.option_true,
- "enable_ferry": Toggle.option_true
+ "npc_gifts": Toggle.option_true
}
def test_inaccessible_with_no_items(self) -> None:
diff --git a/worlds/pokemon_emerald/util.py b/worlds/pokemon_emerald/util.py
index 781cfd47bc9d..f7f02edd95d6 100644
--- a/worlds/pokemon_emerald/util.py
+++ b/worlds/pokemon_emerald/util.py
@@ -1,6 +1,115 @@
-from typing import List
+import orjson
+from typing import Any, Dict, List, Optional, Tuple, Iterable
-from .data import data
+from .data import NATIONAL_ID_TO_SPECIES_ID, data
+
+
+CHARACTER_DECODING_MAP = {
+ 0x00: " ", 0x01: "À", 0x02: "Á", 0x03: "Â", 0x04: "Ç",
+ 0x05: "È", 0x06: "É", 0x07: "Ê", 0x08: "Ë", 0x09: "Ì",
+ 0x0B: "Î", 0x0C: "Ï", 0x0D: "Ò", 0x0E: "Ó", 0x0F: "Ô",
+ 0x10: "Œ", 0x11: "Ù", 0x12: "Ú", 0x13: "Û", 0x14: "Ñ",
+ 0x15: "ß", 0x16: "à", 0x17: "á", 0x19: "ç", 0x1A: "è",
+ 0x1B: "é", 0x1C: "ê", 0x1D: "ë", 0x1E: "ì", 0x20: "î",
+ 0x21: "ï", 0x22: "ò", 0x23: "ó", 0x24: "ô", 0x25: "œ",
+ 0x26: "ù", 0x27: "ú", 0x28: "û", 0x29: "ñ", 0x2A: "°",
+ 0x2B: "ª", 0x2D: "&", 0x2E: "+", 0x35: "=", 0x36: ";",
+ 0x50: "▯", 0x51: "¿", 0x52: "¡", 0x5A: "Í", 0x5B: "%",
+ 0x5C: "(", 0x5D: ")", 0x68: "â", 0x6F: "í", 0x79: "⬆",
+ 0x7A: "⬇", 0x7B: "⬅", 0x7C: "➡", 0x7D: "*", 0x84: "ᵉ",
+ 0x85: "<", 0x86: ">", 0xA1: "0", 0xA2: "1", 0xA3: "2",
+ 0xA4: "3", 0xA5: "4", 0xA6: "5", 0xA7: "6", 0xA8: "7",
+ 0xA9: "8", 0xAA: "9", 0xAB: "!", 0xAC: "?", 0xAD: ".",
+ 0xAE: "-", 0xB0: "…", 0xB1: "“", 0xB2: "”", 0xB3: "‘",
+ 0xB4: "’", 0xB5: "♂", 0xB6: "♀", 0xB8: ",", 0xB9: "×",
+ 0xBA: "/", 0xBB: "A", 0xBC: "B", 0xBD: "C", 0xBE: "D",
+ 0xBF: "E", 0xC0: "F", 0xC1: "G", 0xC2: "H", 0xC3: "I",
+ 0xC4: "J", 0xC5: "K", 0xC6: "L", 0xC7: "M", 0xC8: "N",
+ 0xC9: "O", 0xCA: "P", 0xCB: "Q", 0xCC: "R", 0xCD: "S",
+ 0xCE: "T", 0xCF: "U", 0xD0: "V", 0xD1: "W", 0xD2: "X",
+ 0xD3: "Y", 0xD4: "Z", 0xD5: "a", 0xD6: "b", 0xD7: "c",
+ 0xD8: "d", 0xD9: "e", 0xDA: "f", 0xDB: "g", 0xDC: "h",
+ 0xDD: "i", 0xDE: "j", 0xDF: "k", 0xE0: "l", 0xE1: "m",
+ 0xE2: "n", 0xE3: "o", 0xE4: "p", 0xE5: "q", 0xE6: "r",
+ 0xE7: "s", 0xE8: "t", 0xE9: "u", 0xEA: "v", 0xEB: "w",
+ 0xEC: "x", 0xED: "y", 0xEE: "z", 0xEF: "▶", 0xF0: ":",
+}
+
+CHARACTER_ENCODING_MAP = {value: key for key, value in CHARACTER_DECODING_MAP.items()}
+CHARACTER_ENCODING_MAP.update({
+ "'": CHARACTER_ENCODING_MAP["’"],
+ "\"": CHARACTER_ENCODING_MAP["”"],
+ "_": CHARACTER_ENCODING_MAP[" "],
+})
+
+ALLOWED_TRAINER_NAME_CHARACTERS = frozenset({
+ " ", "0", "1", "2", "3", "4", "5", "6", "7", "8",
+ "9", "!", "?", ".", "-", "…", "“", "”", "‘", "’",
+ "♂", "♀", ",", "/", "A", "B", "C", "D", "E", "F",
+ "G", "H", "I", "J", "K", "L", "M", "N", "O", "P",
+ "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
+ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
+ "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
+ "u", "v", "w", "x", "y", "z",
+})
+
+
+def encode_string(string: str, length: Optional[int] = None) -> bytes:
+ arr = []
+ length = len(string) if length is None else length
+
+ for i in range(length):
+ if i >= len(string):
+ arr.append(0xFF)
+ continue
+
+ char = string[i]
+ if char in CHARACTER_ENCODING_MAP:
+ arr.append(CHARACTER_ENCODING_MAP[char])
+ else:
+ arr.append(CHARACTER_ENCODING_MAP["?"])
+
+ return bytes(arr)
+
+
+def decode_string(string_data: Iterable[int]) -> str:
+ string = ""
+ for code in string_data:
+ if code == 0xFF:
+ break
+
+ if code in CHARACTER_DECODING_MAP:
+ string += CHARACTER_DECODING_MAP[code]
+ else:
+ raise KeyError(f"The following value does not correspond to a character in Pokemon Emerald: {code}")
+
+ return string
+
+
+def get_easter_egg(easter_egg: str) -> Tuple[int, int]:
+ easter_egg = easter_egg.upper()
+ result1 = 0
+ result2 = 0
+ for c in easter_egg:
+ result1 = ((result1 << 5) - result1 + ord(c)) & 0xFFFFFFFF
+ result2 = ((result2 << 4) - result2 + ord(c)) & 0xFF
+
+ if result1 == 0x9137C17B:
+ value = (result2 + 23) & 0xFF
+ if value > 0 and (value < 252 or (value > 276 and value < 412)):
+ return (1, value)
+ elif result1 == 0x9AECC7C6:
+ value = (result2 + 64) & 0xFF
+ if value > 0 and value < 355:
+ return (2, value)
+ elif result1 == 0x506D2690:
+ value = (result2 + 169) & 0xFF
+ if value > 0 and value < 78:
+ return (3, value)
+ elif result1 == 0xA7850E45 and (result1 ^ result2) & 0xFF == 96:
+ return (4, 0)
+
+ return (0, 0)
def location_name_to_label(name: str) -> str:
@@ -8,12 +117,233 @@ def location_name_to_label(name: str) -> str:
def int_to_bool_array(num: int) -> List[bool]:
- binary_string = format(num, '064b')
- bool_array = [bit == '1' for bit in reversed(binary_string)]
+ binary_string = format(num, "064b")
+ bool_array = [bit == "1" for bit in reversed(binary_string)]
return bool_array
def bool_array_to_int(bool_array: List[bool]) -> int:
- binary_string = ''.join(['1' if bit else '0' for bit in reversed(bool_array)])
+ binary_string = "".join(["1" if bit else "0" for bit in reversed(bool_array)])
num = int(binary_string, 2)
return num
+
+
+_SUBSTRUCT_ORDERS = [
+ [0, 1, 2, 3], [0, 1, 3, 2], [0, 2, 1, 3], [0, 3, 1, 2],
+ [0, 2, 3, 1], [0, 3, 2, 1], [1, 0, 2, 3], [1, 0, 3, 2],
+ [2, 0, 1, 3], [3, 0, 1, 2], [2, 0, 3, 1], [3, 0, 2, 1],
+ [1, 2, 0, 3], [1, 3, 0, 2], [2, 1, 0, 3], [3, 1, 0, 2],
+ [2, 3, 0, 1], [3, 2, 0, 1], [1, 2, 3, 0], [1, 3, 2, 0],
+ [2, 1, 3, 0], [3, 1, 2, 0], [2, 3, 1, 0], [3, 2, 1, 0],
+]
+
+_LANGUAGE_IDS = {
+ "Japanese": 1,
+ "English": 2,
+ "French": 3,
+ "Italian": 4,
+ "German": 5,
+ "Spanish": 7,
+}
+
+_MODERN_ITEM_TO_EMERALD_ITEM = {
+ item.modern_id: item.item_id
+ for item in data.items.values()
+ if item.modern_id is not None
+}
+
+
+def _encrypt_or_decrypt_substruct(substruct_data: Iterable[int], key: int) -> bytearray:
+ modified_data = bytearray()
+ for i in range(int(len(substruct_data) / 4)):
+ modified_data.extend((int.from_bytes(substruct_data[i * 4 : (i + 1) * 4], "little") ^ key).to_bytes(4, "little"))
+
+ return modified_data
+
+
+def pokemon_data_to_json(pokemon_data: Iterable[int]) -> str:
+ personality = int.from_bytes(pokemon_data[0:4], "little")
+ tid = int.from_bytes(pokemon_data[4:8], "little")
+
+ substruct_order = _SUBSTRUCT_ORDERS[personality % 24]
+ substructs = []
+ for i in substruct_order:
+ substructs.append(pokemon_data[32 + (i * 12) : 32 + ((i + 1) * 12)])
+
+ decrypted_substructs = [_encrypt_or_decrypt_substruct(substruct, personality ^ tid) for substruct in substructs]
+
+ iv_ability_info = int.from_bytes(decrypted_substructs[3][4:8], "little")
+ met_info = int.from_bytes(decrypted_substructs[3][2:4], "little")
+
+ held_item = int.from_bytes(decrypted_substructs[0][2:4], "little")
+
+ json_object = {
+ "version": "1",
+ "personality": personality,
+ "nickname": decode_string(pokemon_data[8:18]),
+ "language": {v: k for k, v in _LANGUAGE_IDS.items()}[pokemon_data[18]],
+ "species": data.species[int.from_bytes(decrypted_substructs[0][0:2], "little")].national_dex_number,
+ "experience": int.from_bytes(decrypted_substructs[0][4:8], "little"),
+ "ability": iv_ability_info >> 31,
+ "ivs": [(iv_ability_info >> (i * 5)) & 0x1F for i in range(6)],
+ "evs": list(decrypted_substructs[2][0:6]),
+ "conditions": list(decrypted_substructs[2][6:12]),
+ "pokerus": decrypted_substructs[3][0],
+ "location_met": decrypted_substructs[3][1],
+ "level_met": met_info & 0b0000000001111111,
+ "game": (met_info & 0b0000011110000000) >> 7,
+ "ball": (met_info & 0b0111100000000000) >> 11,
+ "moves": [
+ [
+ int.from_bytes(decrypted_substructs[1][i * 2 : (i + 1) * 2], "little"),
+ decrypted_substructs[1][8 + i],
+ (decrypted_substructs[0][8] & (0b00000011 << (i * 2))) >> (i * 2)
+ ] for i in range(4)
+ ],
+ "trainer": {
+ "name": decode_string(pokemon_data[20:27]),
+ "id": tid,
+ "female": (met_info & 0b1000000000000000) != 0,
+ },
+ }
+
+ if held_item != 0:
+ json_object["item"] = data.items[held_item].modern_id
+
+ return orjson.dumps(json_object).decode("utf-8")
+
+
+def json_to_pokemon_data(json_str: str) -> bytearray:
+ pokemon_json: Dict[str, Any] = orjson.loads(json_str)
+
+ # Default values to cover for optional or accidentally missed fields
+ default_pokemon = {
+ "nickname": "A",
+ "personality": 0,
+ "species": 1,
+ "experience": 0,
+ "ability": 0,
+ "ivs": [0, 0, 0, 0, 0, 0],
+ "evs": [0, 0, 0, 0, 0, 0],
+ "conditions": [0, 0, 0, 0, 0, 0],
+ "pokerus": 0,
+ "game": 3,
+ "location_met": 0,
+ "level_met": 1,
+ "ball": 4,
+ "moves": [[33, 35, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]],
+ }
+
+ default_trainer = {
+ "name": "A",
+ "id": 0,
+ "female": False,
+ }
+
+ pokemon_json = {**default_pokemon, **{k: v for k, v in pokemon_json.items()}}
+ pokemon_json["trainer"] = {**default_trainer, **pokemon_json["trainer"]}
+
+ # Cutting string lengths to Emerald sizes
+ pokemon_json["nickname"] = pokemon_json["nickname"][0:10]
+ pokemon_json["trainer"]["name"] = pokemon_json["trainer"]["name"][0:7]
+
+ # Handle data from incompatible games
+ if pokemon_json["species"] > 387:
+ pokemon_json["species"] = 201 # Unown
+ if pokemon_json["ball"] > 12:
+ pokemon_json["ball"] = 4 # Pokeball
+ if "game" not in pokemon_json or (pokemon_json["game"] > 5 and pokemon_json["game"] != 15):
+ pokemon_json["game"] = 0 # Unknown
+ pokemon_json["location_met"] = 0 # Littleroot
+
+ substructs = [bytearray([0 for _ in range(12)]) for _ in range(4)]
+
+ # Substruct type 0
+ for i, byte in enumerate(NATIONAL_ID_TO_SPECIES_ID[pokemon_json["species"]].to_bytes(2, "little")):
+ substructs[0][0 + i] = byte
+
+ if "item" in pokemon_json:
+ if pokemon_json["item"] in _MODERN_ITEM_TO_EMERALD_ITEM:
+ for i, byte in enumerate(_MODERN_ITEM_TO_EMERALD_ITEM[pokemon_json["item"]].to_bytes(2, "little")):
+ substructs[0][2 + i] = byte
+
+ for i, byte in enumerate((pokemon_json["experience"]).to_bytes(4, "little")):
+ substructs[0][4 + i] = byte
+
+ for i, move_info in enumerate(pokemon_json["moves"]):
+ substructs[0][8] |= ((move_info[2] & 0b11) << (2 * i))
+
+ substructs[0][9] = data.species[NATIONAL_ID_TO_SPECIES_ID[pokemon_json["species"]]].friendship
+
+ # Substruct type 1
+ for i, move_info in enumerate(pokemon_json["moves"]):
+ for j, byte in enumerate(move_info[0].to_bytes(2, "little")):
+ substructs[1][(i * 2) + j] = byte
+
+ substructs[1][8 + i] = move_info[1]
+
+ # Substruct type 2
+ for i, ev in enumerate(pokemon_json["evs"]):
+ substructs[2][0 + i] = ev
+
+ for i, condition in enumerate(pokemon_json["conditions"]):
+ substructs[2][6 + i] = condition
+
+ # Substruct type 3
+ substructs[3][0] = pokemon_json["pokerus"]
+ substructs[3][1] = pokemon_json["location_met"]
+
+ origin = pokemon_json["level_met"] | (pokemon_json["game"] << 7) | (pokemon_json["ball"] << 11)
+ origin |= (1 << 15) if pokemon_json["trainer"]["female"] else 0
+ for i, byte in enumerate(origin.to_bytes(2, "little")):
+ substructs[3][2 + i] = byte
+
+ iv_ability_info = 0
+ for i, iv in enumerate(pokemon_json["ivs"]):
+ iv_ability_info |= iv << (i * 5)
+ iv_ability_info |= 1 << 31 if pokemon_json["ability"] == 1 else 0
+ for i, byte in enumerate(iv_ability_info.to_bytes(4, "little")):
+ substructs[3][4 + i] = byte
+
+ # Main data
+ pokemon_data = bytearray([0 for _ in range(80)])
+ for i, byte in enumerate(pokemon_json["personality"].to_bytes(4, "little")):
+ pokemon_data[0 + i] = byte
+
+ for i, byte in enumerate(pokemon_json["trainer"]["id"].to_bytes(4, "little")):
+ pokemon_data[4 + i] = byte
+
+ for i, byte in enumerate(encode_string(pokemon_json["nickname"], 10)):
+ pokemon_data[8 + i] = byte
+
+ pokemon_data[18] = _LANGUAGE_IDS[pokemon_json["language"]]
+ pokemon_data[19] = 0b00000010 # Flags for Bad Egg, Has Species, Is Egg, padding bits (low to high)
+
+ for i, byte in enumerate(encode_string(pokemon_json["trainer"]["name"], 7)):
+ pokemon_data[20 + i] = byte
+
+ # Markings, 1 byte
+
+ checksum = 0
+ for i in range(4):
+ for j in range(6):
+ checksum += int.from_bytes(substructs[i][j * 2 : (j + 1) * 2], "little")
+ checksum &= 0xFFFF
+ for i, byte in enumerate(checksum.to_bytes(2, "little")):
+ pokemon_data[28 + i] = byte
+
+ # Separator, 2 bytes
+
+ substruct_order = [_SUBSTRUCT_ORDERS[pokemon_json["personality"] % 24].index(n) for n in [0, 1, 2, 3]]
+ encrypted_substructs = [None for _ in range(4)]
+ encryption_key = pokemon_json["personality"] ^ pokemon_json["trainer"]["id"]
+ encrypted_substructs[0] = _encrypt_or_decrypt_substruct(substructs[substruct_order[0]], encryption_key)
+ encrypted_substructs[1] = _encrypt_or_decrypt_substruct(substructs[substruct_order[1]], encryption_key)
+ encrypted_substructs[2] = _encrypt_or_decrypt_substruct(substructs[substruct_order[2]], encryption_key)
+ encrypted_substructs[3] = _encrypt_or_decrypt_substruct(substructs[substruct_order[3]], encryption_key)
+
+ for i in range(4):
+ for j in range(12):
+ pokemon_data[32 + (i * 12) + j] = encrypted_substructs[i][j]
+
+ return pokemon_data
diff --git a/worlds/pokemon_rb/__init__.py b/worlds/pokemon_rb/__init__.py
index 5a94a8b5ff26..beb2010b58d3 100644
--- a/worlds/pokemon_rb/__init__.py
+++ b/worlds/pokemon_rb/__init__.py
@@ -46,7 +46,7 @@ class BlueRomFile(settings.UserFilePath):
class PokemonWebWorld(WebWorld):
setup_en = Tutorial(
"Multiworld Setup Guide",
- "A guide to playing Pokemon Red and Blue with Archipelago.",
+ "A guide to playing Pokémon Red and Blue with Archipelago.",
"English",
"setup_en.md",
"setup/en",
@@ -195,11 +195,11 @@ def encode_name(name, t):
normals -= subtract_amounts[2]
while super_effectives + not_very_effectives + normals > 225 - immunities:
r = self.multiworld.random.randint(0, 2)
- if r == 0:
+ if r == 0 and super_effectives:
super_effectives -= 1
- elif r == 1:
+ elif r == 1 and not_very_effectives:
not_very_effectives -= 1
- else:
+ elif normals:
normals -= 1
chart = []
for matchup_list, matchup_value in zip([immunities, normals, super_effectives, not_very_effectives],
@@ -249,14 +249,18 @@ def stage_fill_hook(cls, multiworld, progitempool, usefulitempool, filleritempoo
itempool = progitempool + usefulitempool + filleritempool
multiworld.random.shuffle(itempool)
unplaced_items = []
- for item in itempool:
+ for i, item in enumerate(itempool):
if item.player == loc.player and loc.can_fill(multiworld.state, item, False):
- if item in progitempool:
- progitempool.remove(item)
- elif item in usefulitempool:
- usefulitempool.remove(item)
- elif item in filleritempool:
- filleritempool.remove(item)
+ if item.advancement:
+ pool = progitempool
+ elif item.useful:
+ pool = usefulitempool
+ else:
+ pool = filleritempool
+ for i, check_item in enumerate(pool):
+ if item is check_item:
+ pool.pop(i)
+ break
if item.advancement:
state = sweep_from_pool(multiworld.state, progitempool + unplaced_items)
if (not item.advancement) or state.can_reach(loc, "Location", loc.player):
@@ -353,7 +357,9 @@ def pre_fill(self) -> None:
location.show_in_spoiler = False
def intervene(move, test_state):
- if self.multiworld.randomize_wild_pokemon[self.player]:
+ move_bit = pow(2, poke_data.hm_moves.index(move) + 2)
+ viable_mons = [mon for mon in self.local_poke_data if self.local_poke_data[mon]["tms"][6] & move_bit]
+ if self.multiworld.randomize_wild_pokemon[self.player] and viable_mons:
accessible_slots = [loc for loc in self.multiworld.get_reachable_locations(test_state, self.player) if
loc.type == "Wild Encounter"]
@@ -363,8 +369,6 @@ def number_of_zones(mon):
zones.add(loc.name.split(" - ")[0])
return len(zones)
- move_bit = pow(2, poke_data.hm_moves.index(move) + 2)
- viable_mons = [mon for mon in self.local_poke_data if self.local_poke_data[mon]["tms"][6] & move_bit]
placed_mons = [slot.item.name for slot in accessible_slots]
if self.multiworld.area_1_to_1_mapping[self.player]:
@@ -416,16 +420,16 @@ def number_of_zones(mon):
self.multiworld.victory_road_condition[self.player])
> 7) or (self.multiworld.door_shuffle[self.player] not in ("off", "simple")))):
intervene_move = "Cut"
- elif ((not logic.can_learn_hm(test_state, "Flash", self.player)) and self.multiworld.dark_rock_tunnel_logic[self.player]
- and (((self.multiworld.accessibility[self.player] != "minimal" or
- (self.multiworld.trainersanity[self.player] or self.multiworld.extra_key_items[self.player])) or
- self.multiworld.door_shuffle[self.player]))):
+ elif ((not logic.can_learn_hm(test_state, "Flash", self.player))
+ and self.multiworld.dark_rock_tunnel_logic[self.player]
+ and (self.multiworld.accessibility[self.player] != "minimal"
+ or self.multiworld.door_shuffle[self.player])):
intervene_move = "Flash"
# If no Pokémon can learn Fly, then during door shuffle it would simply not treat the free fly maps
# as reachable, and if on no door shuffle or simple, fly is simply never necessary.
# We only intervene if a Pokémon is able to learn fly but none are reachable, as that would have been
# considered in door shuffle.
- elif ((not logic.can_learn_hm(test_state, "Fly", self.player)) and logic.can_learn_hm(test_state, "Fly", self.player)
+ elif ((not logic.can_learn_hm(test_state, "Fly", self.player))
and self.multiworld.door_shuffle[self.player] not in
("off", "simple") and [self.fly_map, self.town_map_fly_map] != ["Pallet Town", "Pallet Town"]):
intervene_move = "Fly"
@@ -554,23 +558,21 @@ def number_of_zones(mon):
else:
raise Exception("Failed to remove corresponding item while deleting unreachable Dexsanity location")
-
- if self.multiworld.door_shuffle[self.player] == "decoupled":
- swept_state = self.multiworld.state.copy()
- swept_state.sweep_for_events(player=self.player)
- locations = [location for location in
- self.multiworld.get_reachable_locations(swept_state, self.player) if location.item is
- None]
- self.multiworld.random.shuffle(locations)
- while len(locations) > 10:
- location = locations.pop()
- location.progress_type = LocationProgressType.EXCLUDED
-
- if self.multiworld.key_items_only[self.player]:
- locations = [location for location in self.multiworld.get_unfilled_locations(self.player) if
- location.progress_type == LocationProgressType.DEFAULT]
- for location in locations:
- location.progress_type = LocationProgressType.PRIORITY
+ @classmethod
+ def stage_post_fill(cls, multiworld):
+ # Convert all but one of each instance of a wild Pokemon to useful classification.
+ # This cuts down on time spent calculating the spoiler playthrough.
+ found_mons = set()
+ for sphere in multiworld.get_spheres():
+ for location in sphere:
+ if (location.game == "Pokemon Red and Blue" and (location.item.name in poke_data.pokemon_data.keys()
+ or "Static " in location.item.name)
+ and location.item.advancement):
+ key = (location.player, location.item.name)
+ if key in found_mons:
+ location.item.classification = ItemClassification.useful
+ else:
+ found_mons.add(key)
def create_regions(self):
if (self.multiworld.old_man[self.player] == "vanilla" or
diff --git a/worlds/pokemon_rb/basepatch_blue.bsdiff4 b/worlds/pokemon_rb/basepatch_blue.bsdiff4
index 5ccf4e9bbaf8..0f65564a737b 100644
Binary files a/worlds/pokemon_rb/basepatch_blue.bsdiff4 and b/worlds/pokemon_rb/basepatch_blue.bsdiff4 differ
diff --git a/worlds/pokemon_rb/basepatch_red.bsdiff4 b/worlds/pokemon_rb/basepatch_red.bsdiff4
index 26d2eb0c2869..826b7bf8b4e5 100644
Binary files a/worlds/pokemon_rb/basepatch_red.bsdiff4 and b/worlds/pokemon_rb/basepatch_red.bsdiff4 differ
diff --git a/worlds/pokemon_rb/client.py b/worlds/pokemon_rb/client.py
index 7424cc8ddff6..8ed21443e0d4 100644
--- a/worlds/pokemon_rb/client.py
+++ b/worlds/pokemon_rb/client.py
@@ -10,7 +10,7 @@
logger = logging.getLogger("Client")
-BANK_EXCHANGE_RATE = 100000000
+BANK_EXCHANGE_RATE = 50000000
DATA_LOCATIONS = {
"ItemIndex": (0x1A6E, 0x02),
@@ -206,7 +206,7 @@ async def game_watcher(self, ctx):
money = int(original_money.hex())
if self.banking_command > money:
logger.warning(f"You do not have ${self.banking_command} to deposit!")
- elif (-self.banking_command * BANK_EXCHANGE_RATE) > ctx.stored_data[f"EnergyLink{ctx.team}"]:
+ elif (-self.banking_command * BANK_EXCHANGE_RATE) > (ctx.stored_data[f"EnergyLink{ctx.team}"] or 0):
logger.warning("Not enough money in the EnergyLink storage!")
else:
if self.banking_command + money > 999999:
@@ -258,11 +258,12 @@ def cmd_bank(self, cmd: str = "", amount: str = ""):
if self.ctx.game != "Pokemon Red and Blue":
logger.warning("This command can only be used while playing Pokémon Red and Blue")
return
- if not cmd:
- logger.info(f"Money available: {int(self.ctx.stored_data[f'EnergyLink{self.ctx.team}'] / BANK_EXCHANGE_RATE)}")
- return
- elif (not self.ctx.server) or self.ctx.server.socket.closed or not self.ctx.client_handler.game_state:
+ if (not self.ctx.server) or self.ctx.server.socket.closed or not self.ctx.client_handler.game_state:
logger.info(f"Must be connected to server and in game.")
+ return
+ elif not cmd:
+ logger.info(f"Money available: {int((self.ctx.stored_data[f'EnergyLink{self.ctx.team}'] or 0) / BANK_EXCHANGE_RATE)}")
+ return
elif not amount:
logger.warning("You must specify an amount.")
elif cmd == "withdraw":
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 b164d4b0fef6..1e5c14eb99f5 100644
--- a/worlds/pokemon_rb/docs/en_Pokemon Red and Blue.md
+++ b/worlds/pokemon_rb/docs/en_Pokemon Red and Blue.md
@@ -1,8 +1,8 @@
# Pokémon Red and Blue
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
@@ -12,7 +12,7 @@ always able to be completed, but because of the item shuffle the player may need
would in the vanilla game.
A great many things besides item placement can be randomized, such as the location of Pokémon, their stats, types, etc.,
-depending on your yaml settings.
+depending on your yaml options.
Many baseline changes are made to the game, including:
@@ -21,13 +21,13 @@ Many baseline changes are made to the game, including:
* You can hold B to run (or bike extra fast!).
* You can hold select while talking to a trainer to re-battle them.
* You can select "Pallet Warp" below the "Continue" option to warp to Pallet Town as you load your save.
-* Mew can be encountered at the S.S. Anne dock truck. This can be randomized depending on your settings.
+* Mew can be encountered at the S.S. Anne dock truck. This can be randomized depending on your options.
* The S.S. Anne will never depart.
* Seafoam Islands entrances are swapped. This means you need Strength to travel through from Cinnabar Island to Fuchsia
City. You also cannot Surf onto the water from the end of Seafoam Islands going backwards if you have not yet dropped
the boulders.
* After obtaining one of the fossil item checks in Mt Moon, the remaining item can be received from the Cinnabar Lab
-fossil scientist. This may require reviving a number of fossils, depending on your settings.
+fossil scientist. This may require reviving a number of fossils, depending on your options.
* Obedience depends on the total number of badges you have obtained instead of depending on specific badges.
* Pokémon that evolve by trading can also evolve by reaching level 35.
* Evolution stones are reusable key items.
@@ -41,6 +41,8 @@ and repeatable source of money.
* You can disable and re-enable experience gains by talking to an aide in Oak's Lab.
* You can reset static encounters (Poké Flute encounter, legendaries, and the trap Poké Ball battles in Power Plant)
for any Pokémon you have defeated but not caught, by talking to an aide in Oak's Lab.
+* Dungeons normally hidden on the Town Map are now present, and the "Sea Cottage" has been removed. This is to allow
+Simple Door Shuffle to update the locations of all of the dungeons on the Town Map.
## 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 c9344959f6b9..45b0175eac9d 100644
--- a/worlds/pokemon_rb/docs/setup_en.md
+++ b/worlds/pokemon_rb/docs/setup_en.md
@@ -47,7 +47,7 @@ an experience customized for their taste, and different players in the same mult
### Where do I get a YAML file?
-You can generate a yaml or download a template by visiting the [Pokemon Red and Blue Player Settings Page](/games/Pokemon%20Red%20and%20Blue/player-settings)
+You can generate a yaml or download a template by visiting the [Pokemon Red and Blue Player Options Page](/games/Pokemon%20Red%20and%20Blue/player-options)
It is important to note that the `game_version` option determines the ROM file that will be patched.
Both the player and the person generating (if they are generating locally) will need the corresponding ROM file.
@@ -72,7 +72,7 @@ And the following special characters (these each count as one character):
### Generating and Patching a Game
-1. Create your settings file (YAML).
+1. Create your options file (YAML).
2. Follow the general Archipelago instructions for [generating a game](../../Archipelago/setup/en#generating-a-game).
This will generate an output file for you. Your patch file will have a `.apred` or `.apblue` file extension.
3. Open `ArchipelagoLauncher.exe`
@@ -114,5 +114,5 @@ Pokémon Red and Blue has a fully functional map tracker that supports auto-trac
3. Click on the "AP" symbol at the top.
4. Enter the AP address, slot name and password.
-The rest should take care of itself! Items and checks will be marked automatically, and it even knows your settings - It
+The rest should take care of itself! Items and checks will be marked automatically, and it even knows your options - It
will hide checks & adjust logic accordingly.
diff --git a/worlds/pokemon_rb/items.py b/worlds/pokemon_rb/items.py
index b584869f41b9..24cad13252b1 100644
--- a/worlds/pokemon_rb/items.py
+++ b/worlds/pokemon_rb/items.py
@@ -42,7 +42,7 @@ def __init__(self, item_id, classification, groups):
"Repel": ItemData(30, ItemClassification.filler, ["Consumables"]),
"Old Amber": ItemData(31, ItemClassification.progression_skip_balancing, ["Unique", "Fossils", "Key Items"]),
"Fire Stone": ItemData(32, ItemClassification.progression_skip_balancing, ["Unique", "Evolution Stones", "Key Items"]),
- "Thunder Stone": ItemData(33, ItemClassification.progression_skip_balancing, ["Unique", "Evolution Stones" "Key Items"]),
+ "Thunder Stone": ItemData(33, ItemClassification.progression_skip_balancing, ["Unique", "Evolution Stones", "Key Items"]),
"Water Stone": ItemData(34, ItemClassification.progression_skip_balancing, ["Unique", "Evolution Stones", "Key Items"]),
"HP Up": ItemData(35, ItemClassification.filler, ["Consumables", "Vitamins"]),
"Protein": ItemData(36, ItemClassification.filler, ["Consumables", "Vitamins"]),
diff --git a/worlds/pokemon_rb/level_scaling.py b/worlds/pokemon_rb/level_scaling.py
index 5f3dfc1acd7c..79cda394724a 100644
--- a/worlds/pokemon_rb/level_scaling.py
+++ b/worlds/pokemon_rb/level_scaling.py
@@ -10,7 +10,9 @@ def level_scaling(multiworld):
while locations:
sphere = set()
for world in multiworld.get_game_worlds("Pokemon Red and Blue"):
- if multiworld.level_scaling[world.player] != "by_spheres_and_distance":
+ if (multiworld.level_scaling[world.player] != "by_spheres_and_distance"
+ and (multiworld.level_scaling[world.player] != "auto" or multiworld.door_shuffle[world.player]
+ in ("off", "simple"))):
continue
regions = {multiworld.get_region("Menu", world.player)}
checked_regions = set()
@@ -45,18 +47,18 @@ def reachable():
return True
if (("Rock Tunnel 1F - Wild Pokemon" in location.name
and any([multiworld.get_entrance(e, location.player).connected_region.can_reach(state)
- for e in ['Rock Tunnel 1F-NE to Route 10-N',
- 'Rock Tunnel 1F-NE to Rock Tunnel B1F-E',
- 'Rock Tunnel 1F-NW to Rock Tunnel B1F-E',
- 'Rock Tunnel 1F-NW to Rock Tunnel B1F-W',
- 'Rock Tunnel 1F-S to Route 10-S',
- 'Rock Tunnel 1F-S to Rock Tunnel B1F-W']])) or
+ for e in ['Rock Tunnel 1F-NE 1 to Route 10-N',
+ 'Rock Tunnel 1F-NE 2 to Rock Tunnel B1F-E 1',
+ 'Rock Tunnel 1F-NW 1 to Rock Tunnel B1F-E 2',
+ 'Rock Tunnel 1F-NW 2 to Rock Tunnel B1F-W 1',
+ 'Rock Tunnel 1F-S 1 to Route 10-S',
+ 'Rock Tunnel 1F-S 2 to Rock Tunnel B1F-W 2']])) or
("Rock Tunnel B1F - Wild Pokemon" in location.name and
any([multiworld.get_entrance(e, location.player).connected_region.can_reach(state)
- for e in ['Rock Tunnel B1F-E to Rock Tunnel 1F-NE',
- 'Rock Tunnel B1F-E to Rock Tunnel 1F-NW',
- 'Rock Tunnel B1F-W to Rock Tunnel 1F-NW',
- 'Rock Tunnel B1F-W to Rock Tunnel 1F-S']]))):
+ for e in ['Rock Tunnel B1F-E 1 to Rock Tunnel 1F-NE 2',
+ 'Rock Tunnel B1F-E 2 to Rock Tunnel 1F-NW 1',
+ 'Rock Tunnel B1F-W 1 to Rock Tunnel 1F-NW 2',
+ 'Rock Tunnel B1F-W 2 to Rock Tunnel 1F-S 2']]))):
# Even if checks in Rock Tunnel are out of logic due to lack of Flash, it is very easy to
# wander in the dark and encounter wild Pokémon, even unintentionally while attempting to
# leave the way you entered. We'll count the wild Pokémon as reachable as soon as the Rock
@@ -135,4 +137,3 @@ def reachable():
sphere_objects[object].level = level_list_copy.pop(0)
for world in multiworld.get_game_worlds("Pokemon Red and Blue"):
world.finished_level_scaling.set()
-
diff --git a/worlds/pokemon_rb/locations.py b/worlds/pokemon_rb/locations.py
index 3fff3b88c1ea..abaa58fcf901 100644
--- a/worlds/pokemon_rb/locations.py
+++ b/worlds/pokemon_rb/locations.py
@@ -1036,25 +1036,25 @@ def __init__(self, flag):
type="Wild Encounter", level=12),
LocationData("Mt Moon B2F-Wild", "Wild Pokemon - 10", "Clefairy", rom_addresses["Wild_MtMoonB2F"] + 19, None,
event=True, type="Wild Encounter", level=12),
- LocationData("Route 4-Grass", "Wild Pokemon - 1", "Rattata", rom_addresses["Wild_Route4"] + 1, None, event=True,
+ LocationData("Route 4-E", "Wild Pokemon - 1", "Rattata", rom_addresses["Wild_Route4"] + 1, None, event=True,
type="Wild Encounter", level=10),
- LocationData("Route 4-Grass", "Wild Pokemon - 2", "Spearow", rom_addresses["Wild_Route4"] + 3, None, event=True,
+ LocationData("Route 4-E", "Wild Pokemon - 2", "Spearow", rom_addresses["Wild_Route4"] + 3, None, event=True,
type="Wild Encounter", level=10),
- LocationData("Route 4-Grass", "Wild Pokemon - 3", "Rattata", rom_addresses["Wild_Route4"] + 5, None, event=True,
+ LocationData("Route 4-E", "Wild Pokemon - 3", "Rattata", rom_addresses["Wild_Route4"] + 5, None, event=True,
type="Wild Encounter", level=8),
- LocationData("Route 4-Grass", "Wild Pokemon - 4", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route4"] + 7, None,
+ LocationData("Route 4-E", "Wild Pokemon - 4", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route4"] + 7, None,
event=True, type="Wild Encounter", level=6),
- LocationData("Route 4-Grass", "Wild Pokemon - 5", "Spearow", rom_addresses["Wild_Route4"] + 9, None, event=True,
+ LocationData("Route 4-E", "Wild Pokemon - 5", "Spearow", rom_addresses["Wild_Route4"] + 9, None, event=True,
type="Wild Encounter", level=8),
- LocationData("Route 4-Grass", "Wild Pokemon - 6", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route4"] + 11, None,
+ LocationData("Route 4-E", "Wild Pokemon - 6", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route4"] + 11, None,
event=True, type="Wild Encounter", level=10),
- LocationData("Route 4-Grass", "Wild Pokemon - 7", "Rattata", rom_addresses["Wild_Route4"] + 13, None, event=True,
+ LocationData("Route 4-E", "Wild Pokemon - 7", "Rattata", rom_addresses["Wild_Route4"] + 13, None, event=True,
type="Wild Encounter", level=12),
- LocationData("Route 4-Grass", "Wild Pokemon - 8", "Spearow", rom_addresses["Wild_Route4"] + 15, None, event=True,
+ LocationData("Route 4-E", "Wild Pokemon - 8", "Spearow", rom_addresses["Wild_Route4"] + 15, None, event=True,
type="Wild Encounter", level=12),
- LocationData("Route 4-Grass", "Wild Pokemon - 9", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route4"] + 17, None,
+ LocationData("Route 4-E", "Wild Pokemon - 9", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route4"] + 17, None,
event=True, type="Wild Encounter", level=8),
- LocationData("Route 4-Grass", "Wild Pokemon - 10", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route4"] + 19, None,
+ LocationData("Route 4-E", "Wild Pokemon - 10", ["Ekans", "Sandshrew"], rom_addresses["Wild_Route4"] + 19, None,
event=True, type="Wild Encounter", level=12),
LocationData("Route 24", "Wild Pokemon - 1", ["Weedle", "Caterpie"], rom_addresses["Wild_Route24"] + 1, None,
event=True, type="Wild Encounter", level=7),
diff --git a/worlds/pokemon_rb/options.py b/worlds/pokemon_rb/options.py
index 8afe91b86741..bd6515913aca 100644
--- a/worlds/pokemon_rb/options.py
+++ b/worlds/pokemon_rb/options.py
@@ -228,7 +228,7 @@ class SplitCardKey(Choice):
class AllElevatorsLocked(Toggle):
"""Adds requirements to the Celadon Department Store elevator and Silph Co elevators to have the Lift Key.
- No logical implications normally, but may have a significant impact on Insanity Door Shuffle."""
+ No logical implications normally, but may have a significant impact on some Door Shuffle options."""
display_name = "All Elevators Locked"
default = 1
@@ -317,42 +317,42 @@ class TownMapFlyLocation(Toggle):
class DoorShuffle(Choice):
"""Simple: entrances are randomized together in groups: Pokemarts, Gyms, single exit dungeons, dual exit dungeons,
single exit misc interiors, dual exit misc interiors are all shuffled separately. Safari Zone is not shuffled.
- Full: Any outdoor entrance may lead to any interior.
- Insanity: All rooms in the game are shuffled."""
+ On Simple only, the Town Map will be updated to show the new locations for each dungeon.
+ Interiors: Any outdoor entrance may lead to any interior, but intra-interior doors are not shuffled. Previously
+ named Full.
+ Full: Exterior to interior entrances are shuffled, and interior to interior doors are shuffled, separately.
+ Insanity: All doors in the game are shuffled.
+ Decoupled: Doors may be decoupled from each other, so that leaving through an exit may not return you to the
+ door you entered from."""
display_name = "Door Shuffle"
option_off = 0
option_simple = 1
- option_full = 2
- option_insanity = 3
- # Disabled for now, has issues with elevators that need to be resolved
- # option_decoupled = 4
- default = 0
-
- # remove assertions that blow up checks for decoupled
- def __eq__(self, other):
- if isinstance(other, self.__class__):
- return other.value == self.value
- elif isinstance(other, str):
- return other == self.current_key
- elif isinstance(other, int):
- return other == self.value
- elif isinstance(other, bool):
- return other == bool(self.value)
- else:
- raise TypeError(f"Can't compare {self.__class__.__name__} with {other.__class__.__name__}")
-
-
-class WarpTileShuffle(Toggle):
- """Shuffle the warp tiles in Silph Co and Sabrina's Gym among themselves, separately.
- On Insanity, turning this off means they are mixed into the general door shuffle instead of only being shuffled
- among themselves."""
+ option_interiors = 2
+ option_full = 3
+ option_insanity = 4
+ option_decoupled = 5
+ default = 0
+
+
+class WarpTileShuffle(Choice):
+ """Vanilla: The warp tiles in Silph Co and Sabrina's Gym are not changed.
+ Shuffle: The warp tile destinations are shuffled among themselves.
+ Mixed: The warp tiles are mixed into the pool of available doors for Full, Insanity, and Decoupled. Same as Shuffle
+ for any other door shuffle option."""
display_name = "Warp Tile Shuffle"
default = 0
+ option_vanilla = 0
+ option_shuffle = 1
+ option_mixed = 2
+ alias_true = 1
+ alias_on = 1
+ alias_off = 0
+ alias_false = 0
class RandomizeRockTunnel(Toggle):
- """Randomize the layout of Rock Tunnel.
- If Insanity Door Shuffle is on, this will cause only the main entrances to Rock Tunnel to be shuffled."""
+ """Randomize the layout of Rock Tunnel. If Full, Insanity, or Decoupled Door Shuffle is on, this will cause only the
+ main entrances to Rock Tunnel to be shuffled."""
display_name = "Randomize Rock Tunnel"
default = 0
@@ -401,15 +401,17 @@ class Stonesanity(Toggle):
class LevelScaling(Choice):
"""Off: Encounters use vanilla game levels.
By Spheres: Levels are scaled by access sphere. Areas reachable in later spheres will have higher levels.
- Spheres and Distance: Levels are scaled by access spheres as well as distance from Pallet Town, measured by number
- of internal region connections. This is a much more severe curving of levels and may lead to much less variation in
- levels found in a particular map. However, it may make the higher door shuffle settings significantly more bearable,
- as these options more often result in a smaller number of larger access spheres."""
+ By Spheres and Distance: Levels are scaled by access spheres as well as distance from Pallet Town, measured by
+ number of internal region connections. This is a much more severe curving of levels and may lead to much less
+ variation in levels found in a particular map. However, it may make the higher door shuffle settings significantly
+ more bearable, as these options more often result in a smaller number of larger access spheres.
+ Auto: Scales by Spheres if Door Shuffle is off or on Simple, otherwise scales by Spheres and Distance"""
display_name = "Level Scaling"
option_off = 0
option_by_spheres = 1
option_by_spheres_and_distance = 2
- default = 1
+ option_auto = 3
+ default = 3
class ExpModifier(NamedRange):
diff --git a/worlds/pokemon_rb/regions.py b/worlds/pokemon_rb/regions.py
index 97e63c05573d..afeb301c9b94 100644
--- a/worlds/pokemon_rb/regions.py
+++ b/worlds/pokemon_rb/regions.py
@@ -256,6 +256,22 @@
"Indigo Plateau Agatha's Room": 0xF7,
}
+town_map_coords = {
+ "Route 2-SW": ("Viridian Forest South Gate to Route 2-SW", 2, 4, (3,), "Viridian Forest", 4), #ViridianForestName
+ "Route 2-NE": ("Diglett's Cave Route 2 to Route 2-NE", 3, 4, (48,), "Diglett's Cave", 5), #DiglettsCaveName
+ "Route 4-W": ("Mt Moon 1F to Route 4-W", 6, 2, (5,), "Mt Moon 1F", 8), #MountMoonName
+ "Cerulean City-Cave": ("Cerulean Cave 1F-SE to Cerulean City-Cave", 9, 1, (54,), "Cerulean Cave 1F", 11), #CeruleanCaveName
+ "Vermilion City-Dock": ("Vermilion Dock to Vermilion City-Dock", 9, 10, (19,), "S.S. Anne 1F", 17), #SSAnneName
+ "Route 10-N": ("Rock Tunnel 1F-NE 1 to Route 10-N", 14, 3, (13, 57), "Rock Tunnel Pokemon Center", 19), #RockTunnelName
+ "Lavender Town": ("Pokemon Tower 1F to Lavender Town", 15, 5, (27,), "Pokemon Tower 2F", 22), #PokemonTowerName
+ "Celadon Game Corner-Hidden Stairs": ("Rocket Hideout B1F to Celadon Game Corner-Hidden Stairs", 7, 5, (50,), "Rocket Hideout B1F", 26), #RocketHQName
+ "Saffron City-Silph": ("Silph Co 1F to Saffron City-Silph", 10, 5, (51, 58), "Silph Co 2F", 28), #SilphCoName
+ "Route 20-IE": ("Seafoam Islands 1F to Route 20-IE", 5, 15, (32,), "Seafoam Islands B1F", 40), #SeafoamIslandsName
+ "Cinnabar Island-M": ("Pokemon Mansion 1F to Cinnabar Island-M", 2, 15, (35, 52), "Pokemon Mansion 1F", 43), #PokemonMansionName
+ "Route 23-C": ("Victory Road 1F-S to Route 23-C", 0, 4, (20, 45, 49), "Victory Road 1F", 47), #VictoryRoadName
+ "Route 10-P": ("Power Plant to Route 10-P", 15, 4, (14,), "Power Plant", 49), #PowerPlantName
+}
+
warp_data = {'Menu': [], 'Evolution': [], 'Old Rod Fishing': [], 'Good Rod Fishing': [], 'Fossil Level': [],
'Pokedex': [], 'Fossil': [], 'Celadon City': [
{'name': 'Celadon City to Celadon Department Store 1F W', 'address': 'Warps_CeladonCity', 'id': 0,
@@ -461,15 +477,21 @@
{'address': 'Warps_PokemonMansion3F', 'id': 0, 'to': {'map': 'Pokemon Mansion 2F', 'id': 1}}],
'Pokemon Mansion B1F': [
{'address': 'Warps_PokemonMansionB1F', 'id': 0, 'to': {'map': 'Pokemon Mansion 1F-SE', 'id': 5}}],
- 'Rock Tunnel 1F-NE': [{'address': 'Warps_RockTunnel1F', 'id': 0, 'to': {'map': 'Route 10-N', 'id': 1}},
- {'address': 'Warps_RockTunnel1F', 'id': 4,
- 'to': {'map': 'Rock Tunnel B1F-E', 'id': 0}}], 'Rock Tunnel 1F-NW': [
- {'address': 'Warps_RockTunnel1F', 'id': 5, 'to': {'map': 'Rock Tunnel B1F-E', 'id': 1}},
- {'address': 'Warps_RockTunnel1F', 'id': 6, 'to': {'map': 'Rock Tunnel B1F-W', 'id': 2}}],
- 'Rock Tunnel 1F-S': [{'address': 'Warps_RockTunnel1F', 'id': 2, 'to': {'map': 'Route 10-S', 'id': 2}},
+ 'Rock Tunnel 1F-NE 1': [{'address': 'Warps_RockTunnel1F', 'id': 0, 'to': {'map': 'Route 10-N', 'id': 1}}],
+ 'Rock Tunnel 1F-NE 2':
+ [{'address': 'Warps_RockTunnel1F', 'id': 4,
+ 'to': {'map': 'Rock Tunnel B1F-E 1', 'id': 0}}], 'Rock Tunnel 1F-NW 1': [
+ {'address': 'Warps_RockTunnel1F', 'id': 5, 'to': {'map': 'Rock Tunnel B1F-E 2', 'id': 1}}],
+ 'Rock Tunnel 1F-NW 2': [
+ {'address': 'Warps_RockTunnel1F', 'id': 6, 'to': {'map': 'Rock Tunnel B1F-W 1', 'id': 2}}],
+ 'Rock Tunnel 1F-S 1': [{'address': 'Warps_RockTunnel1F', 'id': 2, 'to': {'map': 'Route 10-S', 'id': 2}}],
+ 'Rock Tunnel 1F-S 2': [
{'address': 'Warps_RockTunnel1F', 'id': 7,
- 'to': {'map': 'Rock Tunnel B1F-W', 'id': 3}}], 'Rock Tunnel 1F-Wild': [],
- 'Rock Tunnel B1F-Wild': [], 'Seafoam Islands 1F': [
+ 'to': {'map': 'Rock Tunnel B1F-W 2', 'id': 3}}], 'Rock Tunnel 1F-Wild': [],
+ 'Rock Tunnel B1F-Wild': [],
+ 'Rock Tunnel 1F-NE': [], 'Rock Tunnel 1F-NW': [], 'Rock Tunnel 1F-S': [], 'Rock Tunnel B1F-E': [],
+ 'Rock Tunnel B1F-W': [],
+ 'Seafoam Islands 1F': [
{'address': 'Warps_SeafoamIslands1F', 'id': (2, 3), 'to': {'map': 'Route 20-IE', 'id': 1}},
{'address': 'Warps_SeafoamIslands1F', 'id': 4, 'to': {'map': 'Seafoam Islands B1F', 'id': 1}},
{'address': 'Warps_SeafoamIslands1F', 'id': 5, 'to': {'map': 'Seafoam Islands B1F-NE', 'id': 6}}],
@@ -569,12 +591,14 @@
{'address': 'Warps_CeruleanCave2F', 'id': 3, 'to': {'map': 'Cerulean Cave 1F-N', 'id': 5}}],
'Cerulean Cave B1F': [
{'address': 'Warps_CeruleanCaveB1F', 'id': 0, 'to': {'map': 'Cerulean Cave 1F-NW', 'id': 8}}],
- 'Cerulean Cave B1F-E': [], 'Rock Tunnel B1F-E': [
- {'address': 'Warps_RockTunnelB1F', 'id': 0, 'to': {'map': 'Rock Tunnel 1F-NE', 'id': 4}},
- {'address': 'Warps_RockTunnelB1F', 'id': 1, 'to': {'map': 'Rock Tunnel 1F-NW', 'id': 5}}],
- 'Rock Tunnel B1F-W': [
- {'address': 'Warps_RockTunnelB1F', 'id': 2, 'to': {'map': 'Rock Tunnel 1F-NW', 'id': 6}},
- {'address': 'Warps_RockTunnelB1F', 'id': 3, 'to': {'map': 'Rock Tunnel 1F-S', 'id': 7}}],
+ 'Cerulean Cave B1F-E': [], 'Rock Tunnel B1F-E 1': [
+ {'address': 'Warps_RockTunnelB1F', 'id': 0, 'to': {'map': 'Rock Tunnel 1F-NE 2', 'id': 4}}],
+ 'Rock Tunnel B1F-E 2': [
+ {'address': 'Warps_RockTunnelB1F', 'id': 1, 'to': {'map': 'Rock Tunnel 1F-NW 1', 'id': 5}}],
+ 'Rock Tunnel B1F-W 1': [
+ {'address': 'Warps_RockTunnelB1F', 'id': 2, 'to': {'map': 'Rock Tunnel 1F-NW 2', 'id': 6}}],
+ 'Rock Tunnel B1F-W 2': [
+ {'address': 'Warps_RockTunnelB1F', 'id': 3, 'to': {'map': 'Rock Tunnel 1F-S 2', 'id': 7}}],
'Seafoam Islands B1F': [
{'address': 'Warps_SeafoamIslandsB1F', 'id': 0, 'to': {'map': 'Seafoam Islands B2F-NW', 'id': 0}},
{'address': 'Warps_SeafoamIslandsB1F', 'id': 1, 'to': {'map': 'Seafoam Islands 1F', 'id': 4}},
@@ -802,7 +826,7 @@
'Route 4-W': [{'address': 'Warps_Route4', 'id': 0, 'to': {'map': 'Route 4 Pokemon Center', 'id': 0}},
{'address': 'Warps_Route4', 'id': 1, 'to': {'map': 'Mt Moon 1F', 'id': 0}}],
'Route 4-C': [{'address': 'Warps_Route4', 'id': 2, 'to': {'map': 'Mt Moon B1F-NE', 'id': 7}}],
- 'Route 4-E': [], 'Route 4-Lass': [], 'Route 4-Grass': [],
+ 'Route 4-Lass': [], 'Route 4-E': [],
'Route 5': [{'address': 'Warps_Route5', 'id': (1, 0), 'to': {'map': 'Route 5 Gate-N', 'id': (3, 2)}},
{'address': 'Warps_Route5', 'id': 3, 'to': {'map': 'Underground Path Route 5', 'id': 0}},
{'address': 'Warps_Route5', 'id': 4, 'to': {'map': 'Daycare', 'id': 0}}], 'Route 9': [],
@@ -838,8 +862,8 @@
{'address': 'Warps_Route8', 'id': 4, 'to': {'map': 'Underground Path Route 8', 'id': 0}}],
'Route 8-Grass': [],
'Route 10-N': [{'address': 'Warps_Route10', 'id': 0, 'to': {'map': 'Rock Tunnel Pokemon Center', 'id': 0}},
- {'address': 'Warps_Route10', 'id': 1, 'to': {'map': 'Rock Tunnel 1F-NE', 'id': 0}}],
- 'Route 10-S': [{'address': 'Warps_Route10', 'id': 2, 'to': {'map': 'Rock Tunnel 1F-S', 'id': 2}}],
+ {'address': 'Warps_Route10', 'id': 1, 'to': {'map': 'Rock Tunnel 1F-NE 1', 'id': 0}}],
+ 'Route 10-S': [{'address': 'Warps_Route10', 'id': 2, 'to': {'map': 'Rock Tunnel 1F-S 1', 'id': 2}}],
'Route 10-P': [{'address': 'Warps_Route10', 'id': 3, 'to': {'map': 'Power Plant', 'id': 0}}],
'Route 10-C': [],
'Route 11': [{'address': 'Warps_Route11', 'id': 4, 'to': {'map': "Diglett's Cave Route 11", 'id': 0}}],
@@ -1293,7 +1317,7 @@ def pair(a, b):
return (f"{a} to {b}", f"{b} to {a}")
-mandatory_connections = {
+safari_zone_connections = {
pair("Safari Zone Center-S", "Safari Zone Gate-N"),
pair("Safari Zone East", "Safari Zone North"),
pair("Safari Zone East", "Safari Zone Center-S"),
@@ -1302,14 +1326,8 @@ def pair(a, b):
pair("Safari Zone North", "Safari Zone West-NW"),
pair("Safari Zone West", "Safari Zone Center-NW"),
}
-insanity_mandatory_connections = {
- # pair("Seafoam Islands B1F-NE", "Seafoam Islands 1F"),
- # pair("Seafoam Islands 1F", "Seafoam Islands B1F"),
- # pair("Seafoam Islands B2F-NW", "Seafoam Islands B1F"),
- # pair("Seafoam Islands B3F-SE", "Seafoam Islands B2F-SE"),
- # pair("Seafoam Islands B3F-NE", "Seafoam Islands B2F-NE"),
- # pair("Seafoam Islands B4F", "Seafoam Islands B3F-NE"),
- # pair("Seafoam Islands B4F", "Seafoam Islands B3F"),
+
+full_mandatory_connections = {
pair("Player's House 1F", "Player's House 2F"),
pair("Indigo Plateau Lorelei's Room", "Indigo Plateau Lobby-N"),
pair("Indigo Plateau Bruno's Room", "Indigo Plateau Lorelei's Room"),
@@ -1338,7 +1356,7 @@ def pair(a, b):
unsafe_connecting_interior_dungeons = [
["Seafoam Islands 1F to Route 20-IE", "Seafoam Islands 1F-SE to Route 20-IW"],
- ["Rock Tunnel 1F-NE to Route 10-N", "Rock Tunnel 1F-S to Route 10-S"],
+ ["Rock Tunnel 1F-NE 1 to Route 10-N", "Rock Tunnel 1F-S 1 to Route 10-S"],
["Victory Road 1F-S to Route 23-C", "Victory Road 2F-E to Route 23-N"],
]
@@ -1357,7 +1375,7 @@ def pair(a, b):
["Route 2-NE to Diglett's Cave Route 2", "Route 11 to Diglett's Cave Route 11"],
['Route 20-IE to Seafoam Islands 1F', 'Route 20-IW to Seafoam Islands 1F-SE'],
['Route 4-W to Mt Moon 1F', 'Route 4-C to Mt Moon B1F-NE'],
- ['Route 10-N to Rock Tunnel 1F-NE', 'Route 10-S to Rock Tunnel 1F-S'],
+ ['Route 10-N to Rock Tunnel 1F-NE 1', 'Route 10-S to Rock Tunnel 1F-S 1'],
['Route 23-C to Victory Road 1F-S', 'Route 23-N to Victory Road 2F-E'],
]
@@ -1454,7 +1472,6 @@ def pair(a, b):
]
unreachable_outdoor_entrances = [
- "Route 4-C to Mt Moon B1F-NE",
"Fuchsia City-Good Rod House Backyard to Fuchsia Good Rod House",
"Cerulean City-Badge House Backyard to Cerulean Badge House",
# TODO: This doesn't need to be forced if fly location is Pokemon League?
@@ -1496,7 +1513,6 @@ def create_regions(self):
start_inventory["Exp. All"] = 1
self.multiworld.push_precollected(self.create_item("Exp. All"))
- # locations = [location for location in location_data if location.type in ("Item", "Trainer Parties")]
self.item_pool = []
combined_traps = (self.multiworld.poison_trap_weight[self.player].value
+ self.multiworld.fire_trap_weight[self.player].value
@@ -1556,7 +1572,6 @@ def create_regions(self):
if event:
location_object.place_locked_item(item)
if location.type == "Trainer Parties":
- # loc.item.classification = ItemClassification.filler
location_object.party_data = deepcopy(location.party_data)
else:
self.item_pool.append(item)
@@ -1566,7 +1581,7 @@ def create_regions(self):
+ [item.name for item in self.multiworld.precollected_items[self.player] if
item.advancement]
self.total_key_items = len(
- # The stonesanity items are not checekd for here and instead just always added as the `+ 4`
+ # The stonesanity items are not checked for here and instead just always added as the `+ 4`
# They will always exist, but if stonesanity is off, then only as events.
# We don't want to just add 4 if stonesanity is off while still putting them in this list in case
# the player puts stones in their start inventory, in which case they would be double-counted here.
@@ -1619,16 +1634,15 @@ def create_regions(self):
connect(multiworld, player, "Pewter City-E", "Route 3", lambda state: logic.route_3(state, player), one_way=True)
connect(multiworld, player, "Route 3", "Pewter City-E", one_way=True)
connect(multiworld, player, "Route 4-W", "Route 3")
- connect(multiworld, player, "Route 24", "Cerulean City-Water", one_way=True)
+ connect(multiworld, player, "Route 24", "Cerulean City-Water", lambda state: logic.can_surf(state, player))
connect(multiworld, player, "Cerulean City-Water", "Route 4-Lass", lambda state: logic.can_surf(state, player), one_way=True)
connect(multiworld, player, "Mt Moon B2F", "Mt Moon B2F-Wild", one_way=True)
connect(multiworld, player, "Mt Moon B2F-NE", "Mt Moon B2F-Wild", one_way=True)
connect(multiworld, player, "Mt Moon B2F-C", "Mt Moon B2F-Wild", one_way=True)
- connect(multiworld, player, "Route 4-Lass", "Route 4-E", one_way=True)
+ connect(multiworld, player, "Route 4-Lass", "Route 4-C", one_way=True)
connect(multiworld, player, "Route 4-C", "Route 4-E", one_way=True)
- connect(multiworld, player, "Route 4-E", "Route 4-Grass", one_way=True)
- connect(multiworld, player, "Route 4-Grass", "Cerulean City", one_way=True)
- connect(multiworld, player, "Cerulean City", "Route 24", one_way=True)
+ connect(multiworld, player, "Route 4-E", "Cerulean City")
+ connect(multiworld, player, "Cerulean City", "Route 24")
connect(multiworld, player, "Cerulean City", "Cerulean City-T", lambda state: state.has("Help Bill", player))
connect(multiworld, player, "Cerulean City-Outskirts", "Cerulean City", one_way=True)
connect(multiworld, player, "Cerulean City", "Cerulean City-Outskirts", lambda state: logic.can_cut(state, player), one_way=True)
@@ -1785,7 +1799,6 @@ def create_regions(self):
connect(multiworld, player, "Seafoam Islands B3F-SE", "Seafoam Islands B3F-Wild", one_way=True)
connect(multiworld, player, "Seafoam Islands B4F", "Seafoam Islands B4F-W", lambda state: logic.can_surf(state, player), one_way=True)
connect(multiworld, player, "Seafoam Islands B4F-W", "Seafoam Islands B4F", one_way=True)
- # This really shouldn't be necessary since if the boulders are reachable you can drop, but might as well be thorough
connect(multiworld, player, "Seafoam Islands B3F", "Seafoam Islands B3F-SE", lambda state: logic.can_surf(state, player) and logic.can_strength(state, player) and state.has("Seafoam Exit Boulder", player, 6))
connect(multiworld, player, "Viridian City", "Viridian City-N", lambda state: state.has("Oak's Parcel", player) or state.multiworld.old_man[player].value == 2 or logic.can_cut(state, player))
connect(multiworld, player, "Route 11", "Route 11-C", lambda state: logic.can_strength(state, player) or not state.multiworld.extra_strength_boulders[player])
@@ -1804,6 +1817,16 @@ def create_regions(self):
connect(multiworld, player, "Pokemon Mansion 2F-E", "Pokemon Mansion 2F-Wild", one_way=True)
connect(multiworld, player, "Pokemon Mansion 1F-SE", "Pokemon Mansion 1F-Wild", one_way=True)
connect(multiworld, player, "Pokemon Mansion 1F", "Pokemon Mansion 1F-Wild", one_way=True)
+ connect(multiworld, player, "Rock Tunnel 1F-S 1", "Rock Tunnel 1F-S", lambda state: logic.rock_tunnel(state, player))
+ connect(multiworld, player, "Rock Tunnel 1F-S 2", "Rock Tunnel 1F-S", lambda state: logic.rock_tunnel(state, player))
+ connect(multiworld, player, "Rock Tunnel 1F-NW 1", "Rock Tunnel 1F-NW", lambda state: logic.rock_tunnel(state, player))
+ connect(multiworld, player, "Rock Tunnel 1F-NW 2", "Rock Tunnel 1F-NW", lambda state: logic.rock_tunnel(state, player))
+ connect(multiworld, player, "Rock Tunnel 1F-NE 1", "Rock Tunnel 1F-NE", lambda state: logic.rock_tunnel(state, player))
+ connect(multiworld, player, "Rock Tunnel 1F-NE 2", "Rock Tunnel 1F-NE", lambda state: logic.rock_tunnel(state, player))
+ connect(multiworld, player, "Rock Tunnel B1F-W 1", "Rock Tunnel B1F-W", lambda state: logic.rock_tunnel(state, player))
+ connect(multiworld, player, "Rock Tunnel B1F-W 2", "Rock Tunnel B1F-W", lambda state: logic.rock_tunnel(state, player))
+ connect(multiworld, player, "Rock Tunnel B1F-E 1", "Rock Tunnel B1F-E", lambda state: logic.rock_tunnel(state, player))
+ connect(multiworld, player, "Rock Tunnel B1F-E 2", "Rock Tunnel B1F-E", lambda state: logic.rock_tunnel(state, player))
connect(multiworld, player, "Rock Tunnel 1F-S", "Rock Tunnel 1F-Wild", lambda state: logic.rock_tunnel(state, player), one_way=True)
connect(multiworld, player, "Rock Tunnel 1F-NW", "Rock Tunnel 1F-Wild", lambda state: logic.rock_tunnel(state, player), one_way=True)
connect(multiworld, player, "Rock Tunnel 1F-NE", "Rock Tunnel 1F-Wild", lambda state: logic.rock_tunnel(state, player), one_way=True)
@@ -1860,7 +1883,6 @@ def create_regions(self):
logic.has_badges(state, self.multiworld.cerulean_cave_badges_condition[player].value, player) and
logic.has_key_items(state, self.multiworld.cerulean_cave_key_items_condition[player].total, player) and logic.can_surf(state, player))
-
# access to any part of a city will enable flying to the Pokemon Center
connect(multiworld, player, "Cerulean City-Cave", "Cerulean City", lambda state: logic.can_fly(state, player), one_way=True)
connect(multiworld, player, "Cerulean City-Badge House Backyard", "Cerulean City", lambda state: logic.can_fly(state, player), one_way=True)
@@ -1876,7 +1898,6 @@ def create_regions(self):
connect(multiworld, player, "Cinnabar Island-G", "Cinnabar Island", lambda state: logic.can_fly(state, player), one_way=True, name="Cinnabar Island-G to Cinnabar Island (Fly)")
connect(multiworld, player, "Cinnabar Island-M", "Cinnabar Island", lambda state: logic.can_fly(state, player), one_way=True, name="Cinnabar Island-M to Cinnabar Island (Fly)")
-
# drops
connect(multiworld, player, "Seafoam Islands 1F", "Seafoam Islands B1F", one_way=True, name="Seafoam Islands 1F to Seafoam Islands B1F (Drop)")
connect(multiworld, player, "Seafoam Islands 1F", "Seafoam Islands B1F-NE", one_way=True, name="Seafoam Islands 1F to Seafoam Islands B1F-NE (Drop)")
@@ -1904,14 +1925,50 @@ def create_regions(self):
lambda state: logic.can_fly(state, player) and state.has("Town Map", player), one_way=True,
name="Town Map Fly Location")
+ cache = multiworld.regions.entrance_cache[self.player].copy()
+ if multiworld.badgesanity[player] or multiworld.door_shuffle[player] in ("off", "simple"):
+ badges = None
+ badge_locs = None
+ else:
+ badges = [item for item in self.item_pool if "Badge" in item.name]
+ for badge in badges:
+ self.item_pool.remove(badge)
+ badge_locs = [multiworld.get_location(loc, player) for loc in [
+ "Pewter Gym - Brock Prize", "Cerulean Gym - Misty Prize", "Vermilion Gym - Lt. Surge Prize",
+ "Celadon Gym - Erika Prize", "Fuchsia Gym - Koga Prize", "Saffron Gym - Sabrina Prize",
+ "Cinnabar Gym - Blaine Prize", "Viridian Gym - Giovanni Prize"
+ ]]
+ for attempt in range(10):
+ try:
+ door_shuffle(self, multiworld, player, badges, badge_locs)
+ except DoorShuffleException as e:
+ if attempt == 9:
+ raise e
+ for region in self.multiworld.get_regions(player):
+ for entrance in reversed(region.exits):
+ if isinstance(entrance, PokemonRBWarp):
+ region.exits.remove(entrance)
+ multiworld.regions.entrance_cache[self.player] = cache
+ if badge_locs:
+ for loc in badge_locs:
+ loc.item = None
+ loc.locked = False
+ else:
+ break
+
+
+def door_shuffle(world, multiworld, player, badges, badge_locs):
entrances = []
+ full_interiors = []
for region_name, region_entrances in warp_data.items():
+ region = multiworld.get_region(region_name, player)
for entrance_data in region_entrances:
- region = multiworld.get_region(region_name, player)
shuffle = True
- if not outdoor_map(region.name) and not outdoor_map(entrance_data['to']['map']) and \
- multiworld.door_shuffle[player] not in ("insanity", "decoupled"):
- shuffle = False
+ interior = False
+ if not outdoor_map(region.name) and not outdoor_map(entrance_data['to']['map']):
+ if multiworld.door_shuffle[player] not in ("full", "insanity", "decoupled"):
+ shuffle = False
+ interior = True
if multiworld.door_shuffle[player] == "simple":
if sorted([entrance_data['to']['map'], region.name]) == ["Celadon Game Corner-Hidden Stairs",
"Rocket Hideout B1F"]:
@@ -1921,11 +1978,14 @@ def create_regions(self):
if (multiworld.randomize_rock_tunnel[player] and "Rock Tunnel" in region.name and "Rock Tunnel" in
entrance_data['to']['map']):
shuffle = False
- if (f"{region.name} to {entrance_data['to']['map']}" if "name" not in entrance_data else
+ elif (f"{region.name} to {entrance_data['to']['map']}" if "name" not in entrance_data else
entrance_data["name"]) in silph_co_warps + saffron_gym_warps:
- if multiworld.warp_tile_shuffle[player] or multiworld.door_shuffle[player] in ("insanity",
- "decoupled"):
+ if multiworld.warp_tile_shuffle[player]:
shuffle = True
+ if multiworld.warp_tile_shuffle[player] == "mixed" and multiworld.door_shuffle[player] == "full":
+ interior = True
+ else:
+ interior = False
else:
shuffle = False
elif not multiworld.door_shuffle[player]:
@@ -1935,33 +1995,49 @@ def create_regions(self):
entrance_data else entrance_data["name"], region, entrance_data["id"],
entrance_data["address"], entrance_data["flags"] if "flags" in
entrance_data else "")
- # if "Rock Tunnel" in region_name:
- # entrance.access_rule = lambda state: logic.rock_tunnel(state, player)
- entrances.append(entrance)
+ if interior and multiworld.door_shuffle[player] == "full":
+ full_interiors.append(entrance)
+ else:
+ entrances.append(entrance)
region.exits.append(entrance)
else:
- # connect(multiworld, player, region.name, entrance_data['to']['map'], one_way=True)
- if "Rock Tunnel" in region.name:
- connect(multiworld, player, region.name, entrance_data["to"]["map"],
- lambda state: logic.rock_tunnel(state, player), one_way=True)
- else:
- connect(multiworld, player, region.name, entrance_data["to"]["map"], one_way=True,
- name=entrance_data["name"] if "name" in entrance_data else None)
+ connect(multiworld, player, region.name, entrance_data["to"]["map"], one_way=True,
+ name=entrance_data["name"] if "name" in entrance_data else None)
forced_connections = set()
+ one_way_forced_connections = set()
if multiworld.door_shuffle[player]:
- forced_connections.update(mandatory_connections.copy())
+ if multiworld.door_shuffle[player] in ("full", "insanity", "decoupled"):
+ safari_zone_doors = [door for pair in safari_zone_connections for door in pair]
+ safari_zone_doors.sort()
+ order = ["Center", "East", "North", "West"]
+ multiworld.random.shuffle(order)
+ usable_doors = ["Safari Zone Gate-N to Safari Zone Center-S"]
+ for section in order:
+ section_doors = [door for door in safari_zone_doors if door.startswith(f"Safari Zone {section}")]
+ connect_door_a = multiworld.random.choice(usable_doors)
+ connect_door_b = multiworld.random.choice(section_doors)
+ usable_doors.remove(connect_door_a)
+ section_doors.remove(connect_door_b)
+ forced_connections.add((connect_door_a, connect_door_b))
+ usable_doors += section_doors
+ multiworld.random.shuffle(usable_doors)
+ while usable_doors:
+ forced_connections.add((usable_doors.pop(), usable_doors.pop()))
+ else:
+ forced_connections.update(safari_zone_connections)
+
usable_safe_rooms = safe_rooms.copy()
if multiworld.door_shuffle[player] == "simple":
forced_connections.update(simple_mandatory_connections)
else:
usable_safe_rooms += pokemarts
- if self.multiworld.key_items_only[self.player]:
+ if multiworld.key_items_only[player]:
usable_safe_rooms.remove("Viridian Pokemart to Viridian City")
- if multiworld.door_shuffle[player] in ("insanity", "decoupled"):
- forced_connections.update(insanity_mandatory_connections)
+ if multiworld.door_shuffle[player] in ("full", "insanity", "decoupled"):
+ forced_connections.update(full_mandatory_connections)
r = multiworld.random.randint(0, 3)
if r == 2:
forced_connections.add(("Pokemon Mansion 1F-SE to Pokemon Mansion B1F",
@@ -1969,6 +2045,9 @@ def create_regions(self):
forced_connections.add(("Pokemon Mansion 2F to Pokemon Mansion 3F",
multiworld.random.choice(mansion_stair_destinations + mansion_dead_ends
+ ["Pokemon Mansion B1F to Pokemon Mansion 1F-SE"])))
+ if multiworld.door_shuffle[player] == "full":
+ forced_connections.add(("Pokemon Mansion 1F to Pokemon Mansion 2F",
+ "Pokemon Mansion 3F to Pokemon Mansion 2F"))
elif r == 3:
dead_end = multiworld.random.randint(0, 1)
forced_connections.add(("Pokemon Mansion 3F-SE to Pokemon Mansion 2F-E",
@@ -1987,7 +2066,8 @@ def create_regions(self):
multiworld.random.choice(mansion_stair_destinations
+ ["Pokemon Mansion B1F to Pokemon Mansion 1F-SE"])))
- usable_safe_rooms += insanity_safe_rooms
+ if multiworld.door_shuffle[player] in ("insanity", "decoupled"):
+ usable_safe_rooms += insanity_safe_rooms
safe_rooms_sample = multiworld.random.sample(usable_safe_rooms, 6)
pallet_safe_room = safe_rooms_sample[-1]
@@ -1995,16 +2075,28 @@ def create_regions(self):
for a, b in zip(multiworld.random.sample(["Pallet Town to Player's House 1F", "Pallet Town to Oak's Lab",
"Pallet Town to Rival's House"], 3), ["Oak's Lab to Pallet Town",
"Player's House 1F to Pallet Town", pallet_safe_room]):
- forced_connections.add((a, b))
+ one_way_forced_connections.add((a, b))
+
+ if multiworld.door_shuffle[player] == "decoupled":
+ for a, b in zip(["Oak's Lab to Pallet Town", "Player's House 1F to Pallet Town", pallet_safe_room],
+ multiworld.random.sample(["Pallet Town to Player's House 1F", "Pallet Town to Oak's Lab",
+ "Pallet Town to Rival's House"], 3)):
+ one_way_forced_connections.add((a, b))
+
for a, b in zip(safari_zone_houses, safe_rooms_sample):
- forced_connections.add((a, b))
+ one_way_forced_connections.add((a, b))
+ if multiworld.door_shuffle[player] == "decoupled":
+ for a, b in zip(multiworld.random.sample(safe_rooms_sample[:-1], len(safe_rooms_sample) - 1),
+ safari_zone_houses):
+ one_way_forced_connections.add((a, b))
+
if multiworld.door_shuffle[player] == "simple":
# force Indigo Plateau Lobby to vanilla location on simple, otherwise shuffle with Pokemon Centers.
for a, b in zip(multiworld.random.sample(pokemon_center_entrances[0:-1], 11), pokemon_centers[0:-1]):
forced_connections.add((a, b))
forced_connections.add((pokemon_center_entrances[-1], pokemon_centers[-1]))
forced_pokemarts = multiworld.random.sample(pokemart_entrances, 8)
- if self.multiworld.key_items_only[self.player]:
+ if multiworld.key_items_only[player]:
forced_pokemarts.sort(key=lambda i: i[0] != "Viridian Pokemart to Viridian City")
for a, b in zip(forced_pokemarts, pokemarts):
forced_connections.add((a, b))
@@ -2014,15 +2106,19 @@ def create_regions(self):
# warping outside an entrance that isn't the Pokemon Center, just always put Pokemon Centers at Pokemon
# Center entrances
for a, b in zip(multiworld.random.sample(pokemon_center_entrances, 12), pokemon_centers):
- forced_connections.add((a, b))
+ one_way_forced_connections.add((a, b))
# Ensure a Pokemart is available at the beginning of the game
if multiworld.key_items_only[player]:
- forced_connections.add((multiworld.random.choice(initial_doors), "Viridian Pokemart to Viridian City"))
+ one_way_forced_connections.add((multiworld.random.choice(initial_doors),
+ "Viridian Pokemart to Viridian City"))
+
elif "Pokemart" not in pallet_safe_room:
- forced_connections.add((multiworld.random.choice(initial_doors), multiworld.random.choice(
- [mart for mart in pokemarts if mart not in safe_rooms_sample])))
+ one_way_forced_connections.add((multiworld.random.choice(initial_doors), multiworld.random.choice(
+ [mart for mart in pokemarts if mart not in safe_rooms_sample])))
- if multiworld.warp_tile_shuffle[player]:
+ if multiworld.warp_tile_shuffle[player] == "shuffle" or (multiworld.warp_tile_shuffle[player] == "mixed"
+ and multiworld.door_shuffle[player]
+ in ("off", "simple", "interiors")):
warps = multiworld.random.sample(silph_co_warps, len(silph_co_warps))
# The only warp tiles never reachable from the stairs/elevators are the two 7F-NW warps (where the rival is)
# and the final 11F-W warp. As long as the two 7F-NW warps aren't connected to each other, everything should
@@ -2055,13 +2151,38 @@ def create_regions(self):
while warps:
forced_connections.add((warps.pop(), warps.pop(),))
+ dc_destinations = None
+ if multiworld.door_shuffle[player] == "decoupled":
+ dc_destinations = entrances.copy()
+ for pair in one_way_forced_connections:
+ entrance_a = multiworld.get_entrance(pair[0], player)
+ entrance_b = multiworld.get_entrance(pair[1], player)
+ entrance_a.connect(entrance_b)
+ entrances.remove(entrance_a)
+ dc_destinations.remove(entrance_b)
+ else:
+ forced_connections.update(one_way_forced_connections)
+
for pair in forced_connections:
entrance_a = multiworld.get_entrance(pair[0], player)
entrance_b = multiworld.get_entrance(pair[1], player)
entrance_a.connect(entrance_b)
entrance_b.connect(entrance_a)
- entrances.remove(entrance_a)
- entrances.remove(entrance_b)
+ if entrance_a in entrances:
+ entrances.remove(entrance_a)
+ elif entrance_a in full_interiors:
+ full_interiors.remove(entrance_a)
+ else:
+ raise DoorShuffleException("Attempted to force connection with entrance not in any entrance pool, likely because it tried to force an entrance to connect twice.")
+ if entrance_b in entrances:
+ entrances.remove(entrance_b)
+ elif entrance_b in full_interiors:
+ full_interiors.remove(entrance_b)
+ else:
+ raise DoorShuffleException("Attempted to force connection with entrance not in any entrance pool, likely because it tried to force an entrance to connect twice.")
+ if multiworld.door_shuffle[player] == "decoupled":
+ dc_destinations.remove(entrance_a)
+ dc_destinations.remove(entrance_b)
if multiworld.door_shuffle[player] == "simple":
def connect_connecting_interiors(interior_exits, exterior_entrances):
@@ -2069,7 +2190,7 @@ def connect_connecting_interiors(interior_exits, exterior_entrances):
for a, b in zip(interior, exterior):
entrance_a = multiworld.get_entrance(a, player)
if b is None:
- #entrance_b = multiworld.get_entrance(entrances[0], player)
+ # entrance_b = multiworld.get_entrance(entrances[0], player)
# should just be able to use the entrance_b from the previous link?
pass
else:
@@ -2102,7 +2223,7 @@ def connect_interiors(interior_exits, exterior_entrances):
single_entrance_dungeon_entrances = dungeon_entrances.copy()
for i in range(2):
- if True or not multiworld.random.randint(0, 2):
+ if not multiworld.random.randint(0, 2):
placed_connecting_interior_dungeons.append(multi_purpose_dungeons[i])
interior_dungeon_entrances.append([multi_purpose_dungeon_entrances[i], None])
else:
@@ -2185,7 +2306,7 @@ def cerulean_city_problem():
and interiors[0] in connecting_interiors[13:17] # Saffron Gate at Underground Path North South
and interiors[13] in connecting_interiors[13:17] # Saffron Gate at Route 5 Saffron Gate
and multi_purpose_dungeons[0] == placed_connecting_interior_dungeons[4] # Pokémon Mansion at Rock Tunnel, which is
- and (not multiworld.tea[player]) # not traversable backwards
+ and (not multiworld.tea[player]) # not traversable backwards
and multiworld.route_3_condition[player] == "defeat_brock"
and multiworld.worlds[player].fly_map != "Cerulean City"
and multiworld.worlds[player].town_map_fly_map != "Cerulean City"):
@@ -2209,20 +2330,64 @@ def cerulean_city_problem():
entrance_b.connect(entrance_a)
elif multiworld.door_shuffle[player]:
if multiworld.door_shuffle[player] == "full":
+ multiworld.random.shuffle(full_interiors)
+
+ def search_for_exit(entrance, region, checked_regions):
+ checked_regions.add(region)
+ for exit_candidate in region.exits:
+ if ((not exit_candidate.connected_region)
+ and exit_candidate in entrances and exit_candidate is not entrance):
+ return exit_candidate
+ for entrance_candidate in region.entrances:
+ if entrance_candidate.parent_region not in checked_regions:
+ found_exit = search_for_exit(entrance, entrance_candidate.parent_region, checked_regions)
+ if found_exit is not None:
+ return found_exit
+ return None
+
+ while True:
+ for entrance_a in full_interiors:
+ if search_for_exit(entrance_a, entrance_a.parent_region, set()) is None:
+ for entrance_b in full_interiors:
+ if search_for_exit(entrance_b, entrance_b.parent_region, set()):
+ entrance_a.connect(entrance_b)
+ entrance_b.connect(entrance_a)
+ # Yes, it removes from full_interiors while iterating through it, but it immediately
+ # breaks out, from both loops.
+ full_interiors.remove(entrance_a)
+ full_interiors.remove(entrance_b)
+ break
+ else:
+ raise DoorShuffleException("No non-dead end interior sections found in Pokemon Red and Blue door shuffle.")
+ break
+ else:
+ break
+
+ loop_out_interiors = []
+ multiworld.random.shuffle(entrances)
+ for entrance in reversed(entrances):
+ if not outdoor_map(entrance.parent_region.name):
+ found_exit = search_for_exit(entrance, entrance.parent_region, set())
+ if found_exit is None:
+ continue
+ loop_out_interiors.append([found_exit, entrance])
+ entrances.remove(entrance)
+
+ if len(loop_out_interiors) == 2:
+ break
+
+ for entrance_a, entrance_b in zip(full_interiors[:len(full_interiors) // 2],
+ full_interiors[len(full_interiors) // 2:]):
+ entrance_a.connect(entrance_b)
+ entrance_b.connect(entrance_a)
+
+ elif multiworld.door_shuffle[player] == "interiors":
loop_out_interiors = [[multiworld.get_entrance(e[0], player), multiworld.get_entrance(e[1], player)] for e
in multiworld.random.sample(unsafe_connecting_interior_dungeons
+ safe_connecting_interior_dungeons, 2)]
entrances.remove(loop_out_interiors[0][1])
entrances.remove(loop_out_interiors[1][1])
if not multiworld.badgesanity[player]:
- badges = [item for item in self.item_pool if "Badge" in item.name]
- for badge in badges:
- self.item_pool.remove(badge)
- badge_locs = []
- for loc in ["Pewter Gym - Brock Prize", "Cerulean Gym - Misty Prize", "Vermilion Gym - Lt. Surge Prize",
- "Celadon Gym - Erika Prize", "Fuchsia Gym - Koga Prize", "Saffron Gym - Sabrina Prize",
- "Cinnabar Gym - Blaine Prize", "Viridian Gym - Giovanni Prize"]:
- badge_locs.append(multiworld.get_location(loc, player))
multiworld.random.shuffle(badges)
while badges[3].name == "Cascade Badge" and multiworld.badges_needed_for_hm_moves[player]:
multiworld.random.shuffle(badges)
@@ -2233,7 +2398,7 @@ def cerulean_city_problem():
for item, data in item_table.items():
if (data.id or item in poke_data.pokemon_data) and data.classification == ItemClassification.progression \
and ("Badge" not in item or multiworld.badgesanity[player]):
- state.collect(self.create_item(item))
+ state.collect(world.create_item(item))
multiworld.random.shuffle(entrances)
reachable_entrances = []
@@ -2269,22 +2434,23 @@ def cerulean_city_problem():
"Defeat Viridian Gym Giovanni",
]
- event_locations = self.multiworld.get_filled_locations(player)
+ event_locations = multiworld.get_filled_locations(player)
- def adds_reachable_entrances(entrances_copy, item, dead_end_cache):
- ret = dead_end_cache.get(item.name)
- if (ret != None):
- return ret
+ def adds_reachable_entrances(item):
state_copy = state.copy()
state_copy.collect(item, True)
state.sweep_for_events(locations=event_locations)
- ret = len([entrance for entrance in entrances_copy if entrance in reachable_entrances or
- entrance.parent_region.can_reach(state_copy)]) > len(reachable_entrances)
- dead_end_cache[item.name] = ret
- return ret
+ new_reachable_entrances = len([entrance for entrance in entrances if entrance in reachable_entrances or
+ entrance.parent_region.can_reach(state_copy)])
+ return new_reachable_entrances > len(reachable_entrances)
- def dead_end(entrances_copy, e, dead_end_cache):
+ def dead_end(e):
+ if e.can_reach(state):
+ return True
+ elif multiworld.door_shuffle[player] == "decoupled":
+ # Any unreachable exit in decoupled is not a dead end
+ return False
region = e.parent_region
check_warps = set()
checked_regions = {region}
@@ -2292,93 +2458,105 @@ def dead_end(entrances_copy, e, dead_end_cache):
check_warps.remove(e)
for location in region.locations:
if location.item and location.item.name in relevant_events and \
- adds_reachable_entrances(entrances_copy, location.item, dead_end_cache):
+ adds_reachable_entrances(location.item):
return False
while check_warps:
warp = check_warps.pop()
warp = warp
if warp not in reachable_entrances:
- if "Rock Tunnel" not in warp.name or logic.rock_tunnel(state, player):
- # confirm warp is in entrances list to ensure it's not a loop-out interior
- if warp.connected_region is None and warp in entrances_copy:
- return False
- elif (isinstance(warp, PokemonRBWarp) and ("Rock Tunnel" not in warp.name or
- logic.rock_tunnel(state, player))) or warp.access_rule(state):
- if warp.connected_region and warp.connected_region not in checked_regions:
- checked_regions.add(warp.connected_region)
- check_warps.update(warp.connected_region.exits)
- for location in warp.connected_region.locations:
- if (location.item and location.item.name in relevant_events and
- adds_reachable_entrances(entrances_copy, location.item, dead_end_cache)):
- return False
+ # confirm warp is in entrances list to ensure it's not a loop-out interior
+ if warp.connected_region is None and warp in entrances:
+ return False
+ elif isinstance(warp, PokemonRBWarp) or warp.access_rule(state):
+ if warp.connected_region and warp.connected_region not in checked_regions:
+ checked_regions.add(warp.connected_region)
+ check_warps.update(warp.connected_region.exits)
+ for location in warp.connected_region.locations:
+ if (location.item and location.item.name in relevant_events and
+ adds_reachable_entrances(location.item)):
+ return False
return True
starting_entrances = len(entrances)
- dc_connected = []
- rock_tunnel_entrances = [entrance for entrance in entrances if "Rock Tunnel" in entrance.name]
- entrances = [entrance for entrance in entrances if entrance not in rock_tunnel_entrances]
+
while entrances:
state.update_reachable_regions(player)
state.sweep_for_events(locations=event_locations)
- if rock_tunnel_entrances and logic.rock_tunnel(state, player):
- entrances += rock_tunnel_entrances
- rock_tunnel_entrances = None
+ multiworld.random.shuffle(entrances)
+
+ if multiworld.door_shuffle[player] == "decoupled":
+ multiworld.random.shuffle(dc_destinations)
+ else:
+ entrances.sort(key=lambda e: e.name not in entrance_only)
reachable_entrances = [entrance for entrance in entrances if entrance in reachable_entrances or
entrance.parent_region.can_reach(state)]
- assert reachable_entrances, \
- "Ran out of reachable entrances in Pokemon Red and Blue door shuffle"
- multiworld.random.shuffle(entrances)
- if multiworld.door_shuffle[player] == "decoupled" and len(entrances) == 1:
- entrances += dc_connected
- entrances[-1].connect(entrances[0])
- while len(entrances) > 1:
- entrances.pop(0).connect(entrances[0])
- break
- if multiworld.door_shuffle[player] == "full" or len(entrances) != len(reachable_entrances):
- entrances.sort(key=lambda e: e.name not in entrance_only)
- dead_end_cache = {}
+ entrances.sort(key=lambda e: e in reachable_entrances)
+
+ if not reachable_entrances:
+ raise DoorShuffleException("Ran out of reachable entrances in Pokemon Red and Blue door shuffle")
+
+ entrance_a = reachable_entrances.pop(0)
+ entrances.remove(entrance_a)
+
+ is_outdoor_map = outdoor_map(entrance_a.parent_region.name)
+
+ if multiworld.door_shuffle[player] in ("interiors", "full") or len(entrances) != len(reachable_entrances):
+
+ find_dead_end = False
+ if (len(reachable_entrances) >
+ (1 if multiworld.door_shuffle[player] in ("insanity", "decoupled") else 8) and len(entrances)
+ <= (starting_entrances - 3)):
+ find_dead_end = True
+
+ if (multiworld.door_shuffle[player] in ("interiors", "full") and len(entrances) < 48
+ and not is_outdoor_map):
+ # Try to prevent a situation where the only remaining outdoor entrances are ones that cannot be
+ # reached except by connecting directly to it.
+ entrances.sort(key=lambda e: e.name not in unreachable_outdoor_entrances)
+ if entrances[0].name in unreachable_outdoor_entrances and len([entrance for entrance
+ in reachable_entrances if not outdoor_map(entrance.parent_region.name)]) > 1:
+ find_dead_end = True
- # entrances list is empty while it's being sorted, must pass a copy to iterate through
- entrances_copy = entrances.copy()
if multiworld.door_shuffle[player] == "decoupled":
- entrances.sort(key=lambda e: 1 if e.connected_region is not None else 2 if e not in
- reachable_entrances else 0)
- assert entrances[0].connected_region is None,\
- "Ran out of valid reachable entrances in Pokemon Red and Blue door shuffle"
- elif len(reachable_entrances) > (1 if multiworld.door_shuffle[player] == "insanity" else 8) and len(
- entrances) <= (starting_entrances - 3):
- entrances.sort(key=lambda e: 0 if e in reachable_entrances else 2 if
- dead_end(entrances_copy, e, dead_end_cache) else 1)
+ destinations = dc_destinations
+ elif multiworld.door_shuffle[player] in ("interiors", "full"):
+ destinations = [entrance for entrance in entrances if outdoor_map(entrance.parent_region.name) is
+ not is_outdoor_map]
+ if not destinations:
+ raise DoorShuffleException("Ran out of connectable destinations in Pokemon Red and Blue door shuffle")
else:
- entrances.sort(key=lambda e: 0 if e in reachable_entrances else 1 if
- dead_end(entrances_copy, e, dead_end_cache) else 2)
- if multiworld.door_shuffle[player] == "full":
- outdoor = outdoor_map(entrances[0].parent_region.name)
- if len(entrances) < 48 and not outdoor:
- # Prevent a situation where the only remaining outdoor entrances are ones that cannot be reached
- # except by connecting directly to it.
- entrances.sort(key=lambda e: e.name in unreachable_outdoor_entrances)
-
- entrances.sort(key=lambda e: outdoor_map(e.parent_region.name) != outdoor)
- assert entrances[0] in reachable_entrances, \
- "Ran out of valid reachable entrances in Pokemon Red and Blue door shuffle"
- if (multiworld.door_shuffle[player] == "decoupled" and len(reachable_entrances) > 8 and len(entrances)
- <= (starting_entrances - 3)):
- entrance_b = entrances.pop(1)
+ destinations = entrances
+
+ destinations.sort(key=lambda e: e == entrance_a)
+ for entrance in destinations:
+ if (dead_end(entrance) is find_dead_end and (multiworld.door_shuffle[player] != "decoupled"
+ or entrance.parent_region.name.split("-")[0] !=
+ entrance_a.parent_region.name.split("-")[0])):
+ entrance_b = entrance
+ destinations.remove(entrance)
+ break
+ else:
+ entrance_b = destinations.pop(0)
+
+ if multiworld.door_shuffle[player] in ("interiors", "full"):
+ # on Interiors/Full, the destinations variable does not point to the entrances list, so we need to
+ # remove from that list here.
+ entrances.remove(entrance_b)
else:
- entrance_b = entrances.pop()
- entrance_a = entrances.pop(0)
+ # Everything is reachable. Just start connecting the rest of the doors at random.
+ if multiworld.door_shuffle[player] == "decoupled":
+ entrance_b = dc_destinations.pop(0)
+ else:
+ entrance_b = entrances.pop(0)
+
entrance_a.connect(entrance_b)
- if multiworld.door_shuffle[player] == "decoupled":
- entrances.append(entrance_b)
- dc_connected.append(entrance_a)
- else:
+ if multiworld.door_shuffle[player] != "decoupled":
entrance_b.connect(entrance_a)
- if multiworld.door_shuffle[player] == "full":
+ if multiworld.door_shuffle[player] in ("interiors", "full"):
for pair in loop_out_interiors:
pair[1].connected_region = pair[0].connected_region
pair[1].parent_region.entrances.append(pair[0])
@@ -2443,11 +2621,18 @@ def connect(self, entrance):
def access_rule(self, state):
if self.connected_region is None:
return False
- if "Rock Tunnel" in self.parent_region.name or "Rock Tunnel" in self.connected_region.name:
- return logic.rock_tunnel(state, self.player)
+ if "Elevator" in self.parent_region.name and (
+ (state.multiworld.all_elevators_locked[self.player]
+ or "Rocket Hideout" in self.parent_region.name)
+ and not state.has("Lift Key", self.player)):
+ return False
return True
+class DoorShuffleException(Exception):
+ pass
+
+
class PokemonRBRegion(Region):
def __init__(self, name, player, multiworld):
super().__init__(name, player, multiworld)
diff --git a/worlds/pokemon_rb/rom.py b/worlds/pokemon_rb/rom.py
index 81ab6648dd19..b6c1221a29f4 100644
--- a/worlds/pokemon_rb/rom.py
+++ b/worlds/pokemon_rb/rom.py
@@ -9,9 +9,10 @@
from .pokemon import set_mon_palettes
from .rock_tunnel import randomize_rock_tunnel
from .rom_addresses import rom_addresses
-from .regions import PokemonRBWarp, map_ids
+from .regions import PokemonRBWarp, map_ids, town_map_coords
from . import poke_data
+
def write_quizzes(self, data, random):
def get_quiz(q, a):
@@ -204,19 +205,21 @@ def generate_output(self, output_directory: str):
basemd5 = hashlib.md5()
basemd5.update(data)
- lab_loc = self.multiworld.get_entrance("Oak's Lab to Pallet Town", self.player).target
+ pallet_connections = {entrance: self.multiworld.get_entrance(f"Pallet Town to {entrance}",
+ self.player).connected_region.name for
+ entrance in ["Player's House 1F", "Oak's Lab",
+ "Rival's House"]}
paths = None
- if lab_loc == 0: # Player's House
+ if pallet_connections["Player's House 1F"] == "Oak's Lab":
paths = ((0x00, 4, 0x80, 5, 0x40, 1, 0xE0, 1, 0xFF), (0x40, 2, 0x20, 5, 0x80, 5, 0xFF))
- elif lab_loc == 1: # Rival's House
+ elif pallet_connections["Rival's House"] == "Oak's Lab":
paths = ((0x00, 4, 0xC0, 3, 0x40, 1, 0xE0, 1, 0xFF), (0x40, 2, 0x10, 3, 0x80, 5, 0xFF))
if paths:
write_bytes(data, paths[0], rom_addresses["Path_Pallet_Oak"])
write_bytes(data, paths[1], rom_addresses["Path_Pallet_Player"])
- home_loc = self.multiworld.get_entrance("Player's House 1F to Pallet Town", self.player).target
- if home_loc == 1: # Rival's House
+ if pallet_connections["Rival's House"] == "Player's House 1F":
write_bytes(data, [0x2F, 0xC7, 0x06, 0x0D, 0x00, 0x01], rom_addresses["Pallet_Fly_Coords"])
- elif home_loc == 2: # Oak's Lab
+ elif pallet_connections["Oak's Lab"] == "Player's House 1F":
write_bytes(data, [0x5F, 0xC7, 0x0C, 0x0C, 0x00, 0x00], rom_addresses["Pallet_Fly_Coords"])
for region in self.multiworld.get_regions(self.player):
@@ -238,6 +241,14 @@ def generate_output(self, output_directory: str):
data[address] = 0 if "Elevator" in connected_map_name else warp_to_ids[i]
data[address + 1] = map_ids[connected_map_name]
+ if self.multiworld.door_shuffle[self.player] == "simple":
+ for (entrance, _, _, map_coords_entries, map_name, _) in town_map_coords.values():
+ destination = self.multiworld.get_entrance(entrance, self.player).connected_region.name
+ (_, x, y, _, _, map_order_entry) = town_map_coords[destination]
+ for map_coord_entry in map_coords_entries:
+ data[rom_addresses["Town_Map_Coords"] + (map_coord_entry * 4) + 1] = (y << 4) | x
+ data[rom_addresses["Town_Map_Order"] + map_order_entry] = map_ids[map_name]
+
if not self.multiworld.key_items_only[self.player]:
for i, gym_leader in enumerate(("Pewter Gym - Brock TM", "Cerulean Gym - Misty TM",
"Vermilion Gym - Lt. Surge TM", "Celadon Gym - Erika TM",
diff --git a/worlds/pokemon_rb/rom_addresses.py b/worlds/pokemon_rb/rom_addresses.py
index ffb89a4dfcdf..e5c073971d5d 100644
--- a/worlds/pokemon_rb/rom_addresses.py
+++ b/worlds/pokemon_rb/rom_addresses.py
@@ -1,10 +1,10 @@
rom_addresses = {
"Option_Encounter_Minimum_Steps": 0x3c1,
- "Option_Pitch_Black_Rock_Tunnel": 0x75c,
- "Option_Blind_Trainers": 0x30c7,
- "Option_Trainersanity1": 0x3157,
- "Option_Split_Card_Key": 0x3e10,
- "Option_Fix_Combat_Bugs": 0x3e11,
+ "Option_Pitch_Black_Rock_Tunnel": 0x76a,
+ "Option_Blind_Trainers": 0x30d5,
+ "Option_Trainersanity1": 0x3165,
+ "Option_Split_Card_Key": 0x3e1e,
+ "Option_Fix_Combat_Bugs": 0x3e1f,
"Option_Lose_Money": 0x40d4,
"Base_Stats_Mew": 0x4260,
"Title_Mon_First": 0x4373,
@@ -131,49 +131,49 @@
"Starter2_K": 0x19611,
"Starter3_K": 0x19619,
"Event_Rocket_Thief": 0x19733,
- "Option_Cerulean_Cave_Badges": 0x19857,
- "Option_Cerulean_Cave_Key_Items": 0x1985e,
- "Text_Cerulean_Cave_Badges": 0x198c3,
- "Text_Cerulean_Cave_Key_Items": 0x198d1,
- "Event_Stranded_Man": 0x19b28,
- "Event_Rivals_Sister": 0x19cfb,
- "Warps_BluesHouse": 0x19d51,
- "Warps_VermilionTradeHouse": 0x19da8,
- "Require_Pokedex_D": 0x19e3f,
- "Option_Elite_Four_Key_Items": 0x19e89,
- "Option_Elite_Four_Pokedex": 0x19e90,
- "Option_Elite_Four_Badges": 0x19e97,
- "Text_Elite_Four_Badges": 0x19f33,
- "Text_Elite_Four_Key_Items": 0x19f3d,
- "Text_Elite_Four_Pokedex": 0x19f50,
- "Shop10": 0x1a004,
- "Warps_IndigoPlateauLobby": 0x1a030,
- "Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_0_ITEM": 0x1a158,
- "Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_1_ITEM": 0x1a166,
- "Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_2_ITEM": 0x1a174,
- "Event_SKC4F": 0x1a187,
- "Warps_SilphCo4F": 0x1a209,
- "Missable_Silph_Co_4F_Item_1": 0x1a249,
- "Missable_Silph_Co_4F_Item_2": 0x1a250,
- "Missable_Silph_Co_4F_Item_3": 0x1a257,
- "Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_0_ITEM": 0x1a3af,
- "Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_1_ITEM": 0x1a3bd,
- "Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_2_ITEM": 0x1a3cb,
- "Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_3_ITEM": 0x1a3d9,
- "Event_SKC5F": 0x1a3ec,
- "Warps_SilphCo5F": 0x1a496,
- "Missable_Silph_Co_5F_Item_1": 0x1a4de,
- "Missable_Silph_Co_5F_Item_2": 0x1a4e5,
- "Missable_Silph_Co_5F_Item_3": 0x1a4ec,
- "Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_0_ITEM": 0x1a61c,
- "Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_1_ITEM": 0x1a62a,
- "Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_2_ITEM": 0x1a638,
- "Event_SKC6F": 0x1a659,
- "Warps_SilphCo6F": 0x1a737,
- "Missable_Silph_Co_6F_Item_1": 0x1a787,
- "Missable_Silph_Co_6F_Item_2": 0x1a78e,
- "Path_Pallet_Oak": 0x1a914,
- "Path_Pallet_Player": 0x1a921,
+ "Option_Cerulean_Cave_Badges": 0x19861,
+ "Option_Cerulean_Cave_Key_Items": 0x19868,
+ "Text_Cerulean_Cave_Badges": 0x198d7,
+ "Text_Cerulean_Cave_Key_Items": 0x198e5,
+ "Event_Stranded_Man": 0x19b3c,
+ "Event_Rivals_Sister": 0x19d0f,
+ "Warps_BluesHouse": 0x19d65,
+ "Warps_VermilionTradeHouse": 0x19dbc,
+ "Require_Pokedex_D": 0x19e53,
+ "Option_Elite_Four_Key_Items": 0x19e9d,
+ "Option_Elite_Four_Pokedex": 0x19ea4,
+ "Option_Elite_Four_Badges": 0x19eab,
+ "Text_Elite_Four_Badges": 0x19f47,
+ "Text_Elite_Four_Key_Items": 0x19f51,
+ "Text_Elite_Four_Pokedex": 0x19f64,
+ "Shop10": 0x1a018,
+ "Warps_IndigoPlateauLobby": 0x1a044,
+ "Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_0_ITEM": 0x1a16c,
+ "Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_1_ITEM": 0x1a17a,
+ "Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_2_ITEM": 0x1a188,
+ "Event_SKC4F": 0x1a19b,
+ "Warps_SilphCo4F": 0x1a21d,
+ "Missable_Silph_Co_4F_Item_1": 0x1a25d,
+ "Missable_Silph_Co_4F_Item_2": 0x1a264,
+ "Missable_Silph_Co_4F_Item_3": 0x1a26b,
+ "Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_0_ITEM": 0x1a3c3,
+ "Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_1_ITEM": 0x1a3d1,
+ "Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_2_ITEM": 0x1a3df,
+ "Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_3_ITEM": 0x1a3ed,
+ "Event_SKC5F": 0x1a400,
+ "Warps_SilphCo5F": 0x1a4aa,
+ "Missable_Silph_Co_5F_Item_1": 0x1a4f2,
+ "Missable_Silph_Co_5F_Item_2": 0x1a4f9,
+ "Missable_Silph_Co_5F_Item_3": 0x1a500,
+ "Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_0_ITEM": 0x1a630,
+ "Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_1_ITEM": 0x1a63e,
+ "Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_2_ITEM": 0x1a64c,
+ "Event_SKC6F": 0x1a66d,
+ "Warps_SilphCo6F": 0x1a74b,
+ "Missable_Silph_Co_6F_Item_1": 0x1a79b,
+ "Missable_Silph_Co_6F_Item_2": 0x1a7a2,
+ "Path_Pallet_Oak": 0x1a928,
+ "Path_Pallet_Player": 0x1a935,
"Warps_CinnabarIsland": 0x1c026,
"Warps_Route1": 0x1c0e9,
"Option_Extra_Key_Items_B": 0x1ca46,
@@ -1074,112 +1074,112 @@
"Missable_Route_25_Item": 0x5080b,
"Warps_IndigoPlateau": 0x5093a,
"Warps_SaffronCity": 0x509e0,
- "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_0_ITEM": 0x50d63,
- "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_1_ITEM": 0x50d71,
- "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_2_ITEM": 0x50d7f,
- "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_3_ITEM": 0x50d8d,
- "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_4_ITEM": 0x50d9b,
- "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_5_ITEM": 0x50da9,
- "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_6_ITEM": 0x50db7,
- "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_7_ITEM": 0x50dc5,
- "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_8_ITEM": 0x50dd3,
- "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_9_ITEM": 0x50de1,
- "Starter2_B": 0x50ffe,
- "Starter3_B": 0x51000,
- "Starter1_B": 0x51002,
- "Starter2_A": 0x5111d,
- "Starter3_A": 0x5111f,
- "Starter1_A": 0x51121,
- "Option_Route23_Badges": 0x5126e,
- "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_0_ITEM": 0x51384,
- "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_1_ITEM": 0x51392,
- "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_2_ITEM": 0x513a0,
- "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_3_ITEM": 0x513ae,
- "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_4_ITEM": 0x513bc,
- "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_5_ITEM": 0x513ca,
- "Event_Nugget_Bridge": 0x513e1,
- "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_0_ITEM": 0x51569,
- "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_1_ITEM": 0x51577,
- "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_2_ITEM": 0x51585,
- "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_3_ITEM": 0x51593,
- "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_4_ITEM": 0x515a1,
- "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_5_ITEM": 0x515af,
- "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_6_ITEM": 0x515bd,
- "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_7_ITEM": 0x515cb,
- "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_8_ITEM": 0x515d9,
- "Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_0_ITEM": 0x51772,
- "Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_1_ITEM": 0x51780,
- "Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_2_ITEM": 0x5178e,
- "Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_3_ITEM": 0x5179c,
- "Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_4_ITEM": 0x517aa,
- "Trainersanity_EVENT_BEAT_MOLTRES_ITEM": 0x517b8,
- "Warps_VictoryRoad2F": 0x51855,
- "Static_Encounter_Moltres": 0x5189f,
- "Missable_Victory_Road_2F_Item_1": 0x518a7,
- "Missable_Victory_Road_2F_Item_2": 0x518ae,
- "Missable_Victory_Road_2F_Item_3": 0x518b5,
- "Missable_Victory_Road_2F_Item_4": 0x518bc,
- "Warps_MtMoonB1F": 0x5198d,
- "Starter2_L": 0x51beb,
- "Starter3_L": 0x51bf3,
- "Trainersanity_EVENT_BEAT_SILPH_CO_7F_TRAINER_0_ITEM": 0x51ca4,
- "Trainersanity_EVENT_BEAT_SILPH_CO_7F_TRAINER_1_ITEM": 0x51cb2,
- "Trainersanity_EVENT_BEAT_SILPH_CO_7F_TRAINER_2_ITEM": 0x51cc0,
- "Trainersanity_EVENT_BEAT_SILPH_CO_7F_TRAINER_3_ITEM": 0x51cce,
- "Gift_Lapras": 0x51cef,
- "Event_SKC7F": 0x51d7a,
- "Warps_SilphCo7F": 0x51e49,
- "Missable_Silph_Co_7F_Item_1": 0x51ea5,
- "Missable_Silph_Co_7F_Item_2": 0x51eac,
- "Trainersanity_EVENT_BEAT_MANSION_2_TRAINER_0_ITEM": 0x51fd2,
- "Warps_PokemonMansion2F": 0x52045,
- "Missable_Pokemon_Mansion_2F_Item": 0x52063,
- "Trainersanity_EVENT_BEAT_MANSION_3_TRAINER_0_ITEM": 0x52213,
- "Trainersanity_EVENT_BEAT_MANSION_3_TRAINER_1_ITEM": 0x52221,
- "Warps_PokemonMansion3F": 0x5225e,
- "Missable_Pokemon_Mansion_3F_Item_1": 0x52280,
- "Missable_Pokemon_Mansion_3F_Item_2": 0x52287,
- "Trainersanity_EVENT_BEAT_MANSION_4_TRAINER_0_ITEM": 0x523c9,
- "Trainersanity_EVENT_BEAT_MANSION_4_TRAINER_1_ITEM": 0x523d7,
- "Warps_PokemonMansionB1F": 0x52414,
- "Missable_Pokemon_Mansion_B1F_Item_1": 0x5242e,
- "Missable_Pokemon_Mansion_B1F_Item_2": 0x52435,
- "Missable_Pokemon_Mansion_B1F_Item_3": 0x5243c,
- "Missable_Pokemon_Mansion_B1F_Item_4": 0x52443,
- "Missable_Pokemon_Mansion_B1F_Item_5": 0x52450,
- "Option_Safari_Zone_Battle_Type": 0x52565,
- "Prize_Mon_A2": 0x527ef,
- "Prize_Mon_B2": 0x527f0,
- "Prize_Mon_C2": 0x527f1,
- "Prize_Mon_D2": 0x527fa,
- "Prize_Mon_E2": 0x527fb,
- "Prize_Mon_F2": 0x527fc,
- "Prize_Item_A": 0x52805,
- "Prize_Item_B": 0x52806,
- "Prize_Item_C": 0x52807,
- "Prize_Mon_A": 0x5293c,
- "Prize_Mon_B": 0x5293e,
- "Prize_Mon_C": 0x52940,
- "Prize_Mon_D": 0x52942,
- "Prize_Mon_E": 0x52944,
- "Prize_Mon_F": 0x52946,
- "Start_Inventory": 0x52a7b,
- "Map_Fly_Location": 0x52c75,
- "Reset_A": 0x52d21,
- "Reset_B": 0x52d4d,
- "Reset_C": 0x52d79,
- "Reset_D": 0x52da5,
- "Reset_E": 0x52dd1,
- "Reset_F": 0x52dfd,
- "Reset_G": 0x52e29,
- "Reset_H": 0x52e55,
- "Reset_I": 0x52e81,
- "Reset_J": 0x52ead,
- "Reset_K": 0x52ed9,
- "Reset_L": 0x52f05,
- "Reset_M": 0x52f31,
- "Reset_N": 0x52f5d,
- "Reset_O": 0x52f89,
+ "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_0_ITEM": 0x50d8b,
+ "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_1_ITEM": 0x50d99,
+ "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_2_ITEM": 0x50da7,
+ "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_3_ITEM": 0x50db5,
+ "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_4_ITEM": 0x50dc3,
+ "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_5_ITEM": 0x50dd1,
+ "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_6_ITEM": 0x50ddf,
+ "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_7_ITEM": 0x50ded,
+ "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_8_ITEM": 0x50dfb,
+ "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_9_ITEM": 0x50e09,
+ "Starter2_B": 0x51026,
+ "Starter3_B": 0x51028,
+ "Starter1_B": 0x5102a,
+ "Starter2_A": 0x51145,
+ "Starter3_A": 0x51147,
+ "Starter1_A": 0x51149,
+ "Option_Route23_Badges": 0x51296,
+ "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_0_ITEM": 0x513ac,
+ "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_1_ITEM": 0x513ba,
+ "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_2_ITEM": 0x513c8,
+ "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_3_ITEM": 0x513d6,
+ "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_4_ITEM": 0x513e4,
+ "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_5_ITEM": 0x513f2,
+ "Event_Nugget_Bridge": 0x51409,
+ "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_0_ITEM": 0x51591,
+ "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_1_ITEM": 0x5159f,
+ "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_2_ITEM": 0x515ad,
+ "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_3_ITEM": 0x515bb,
+ "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_4_ITEM": 0x515c9,
+ "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_5_ITEM": 0x515d7,
+ "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_6_ITEM": 0x515e5,
+ "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_7_ITEM": 0x515f3,
+ "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_8_ITEM": 0x51601,
+ "Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_0_ITEM": 0x5179a,
+ "Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_1_ITEM": 0x517a8,
+ "Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_2_ITEM": 0x517b6,
+ "Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_3_ITEM": 0x517c4,
+ "Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_4_ITEM": 0x517d2,
+ "Trainersanity_EVENT_BEAT_MOLTRES_ITEM": 0x517e0,
+ "Warps_VictoryRoad2F": 0x5187d,
+ "Static_Encounter_Moltres": 0x518c7,
+ "Missable_Victory_Road_2F_Item_1": 0x518cf,
+ "Missable_Victory_Road_2F_Item_2": 0x518d6,
+ "Missable_Victory_Road_2F_Item_3": 0x518dd,
+ "Missable_Victory_Road_2F_Item_4": 0x518e4,
+ "Warps_MtMoonB1F": 0x519b5,
+ "Starter2_L": 0x51c13,
+ "Starter3_L": 0x51c1b,
+ "Trainersanity_EVENT_BEAT_SILPH_CO_7F_TRAINER_0_ITEM": 0x51ccc,
+ "Trainersanity_EVENT_BEAT_SILPH_CO_7F_TRAINER_1_ITEM": 0x51cda,
+ "Trainersanity_EVENT_BEAT_SILPH_CO_7F_TRAINER_2_ITEM": 0x51ce8,
+ "Trainersanity_EVENT_BEAT_SILPH_CO_7F_TRAINER_3_ITEM": 0x51cf6,
+ "Gift_Lapras": 0x51d17,
+ "Event_SKC7F": 0x51da2,
+ "Warps_SilphCo7F": 0x51e71,
+ "Missable_Silph_Co_7F_Item_1": 0x51ecd,
+ "Missable_Silph_Co_7F_Item_2": 0x51ed4,
+ "Trainersanity_EVENT_BEAT_MANSION_2_TRAINER_0_ITEM": 0x51ffa,
+ "Warps_PokemonMansion2F": 0x5206d,
+ "Missable_Pokemon_Mansion_2F_Item": 0x5208b,
+ "Trainersanity_EVENT_BEAT_MANSION_3_TRAINER_0_ITEM": 0x5223b,
+ "Trainersanity_EVENT_BEAT_MANSION_3_TRAINER_1_ITEM": 0x52249,
+ "Warps_PokemonMansion3F": 0x52286,
+ "Missable_Pokemon_Mansion_3F_Item_1": 0x522a8,
+ "Missable_Pokemon_Mansion_3F_Item_2": 0x522af,
+ "Trainersanity_EVENT_BEAT_MANSION_4_TRAINER_0_ITEM": 0x523f1,
+ "Trainersanity_EVENT_BEAT_MANSION_4_TRAINER_1_ITEM": 0x523ff,
+ "Warps_PokemonMansionB1F": 0x5243c,
+ "Missable_Pokemon_Mansion_B1F_Item_1": 0x52456,
+ "Missable_Pokemon_Mansion_B1F_Item_2": 0x5245d,
+ "Missable_Pokemon_Mansion_B1F_Item_3": 0x52464,
+ "Missable_Pokemon_Mansion_B1F_Item_4": 0x5246b,
+ "Missable_Pokemon_Mansion_B1F_Item_5": 0x52478,
+ "Option_Safari_Zone_Battle_Type": 0x5258d,
+ "Prize_Mon_A2": 0x52817,
+ "Prize_Mon_B2": 0x52818,
+ "Prize_Mon_C2": 0x52819,
+ "Prize_Mon_D2": 0x52822,
+ "Prize_Mon_E2": 0x52823,
+ "Prize_Mon_F2": 0x52824,
+ "Prize_Item_A": 0x5282d,
+ "Prize_Item_B": 0x5282e,
+ "Prize_Item_C": 0x5282f,
+ "Prize_Mon_A": 0x52964,
+ "Prize_Mon_B": 0x52966,
+ "Prize_Mon_C": 0x52968,
+ "Prize_Mon_D": 0x5296a,
+ "Prize_Mon_E": 0x5296c,
+ "Prize_Mon_F": 0x5296e,
+ "Start_Inventory": 0x52aa3,
+ "Map_Fly_Location": 0x52c9d,
+ "Reset_A": 0x52d49,
+ "Reset_B": 0x52d75,
+ "Reset_C": 0x52da1,
+ "Reset_D": 0x52dcd,
+ "Reset_E": 0x52df9,
+ "Reset_F": 0x52e25,
+ "Reset_G": 0x52e51,
+ "Reset_H": 0x52e7d,
+ "Reset_I": 0x52ea9,
+ "Reset_J": 0x52ed5,
+ "Reset_K": 0x52f01,
+ "Reset_L": 0x52f2d,
+ "Reset_M": 0x52f59,
+ "Reset_N": 0x52f85,
+ "Reset_O": 0x52fb1,
"Warps_Route2": 0x54026,
"Missable_Route_2_Item_1": 0x5404a,
"Missable_Route_2_Item_2": 0x54051,
@@ -1539,16 +1539,18 @@
"Event_SKC11F": 0x623bd,
"Warps_SilphCo11F": 0x62446,
"Ghost_Battle4": 0x708e1,
- "Trade_Terry": 0x71b77,
- "Trade_Marcel": 0x71b85,
- "Trade_Sailor": 0x71ba1,
- "Trade_Dux": 0x71baf,
- "Trade_Marc": 0x71bbd,
- "Trade_Lola": 0x71bcb,
- "Trade_Doris": 0x71bd9,
- "Trade_Crinkles": 0x71be7,
- "Trade_Spot": 0x71bf5,
- "Mon_Palettes": 0x725d3,
+ "Town_Map_Order": 0x70f0f,
+ "Town_Map_Coords": 0x71381,
+ "Trade_Terry": 0x71b7a,
+ "Trade_Marcel": 0x71b88,
+ "Trade_Sailor": 0x71ba4,
+ "Trade_Dux": 0x71bb2,
+ "Trade_Marc": 0x71bc0,
+ "Trade_Lola": 0x71bce,
+ "Trade_Doris": 0x71bdc,
+ "Trade_Crinkles": 0x71bea,
+ "Trade_Spot": 0x71bf8,
+ "Mon_Palettes": 0x725d6,
"Badge_Viridian_Gym": 0x749d9,
"Event_Viridian_Gym": 0x749ed,
"Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_0_ITEM": 0x74a48,
diff --git a/worlds/raft/docs/en_Raft.md b/worlds/raft/docs/en_Raft.md
index 385377d45608..0c68e23d0019 100644
--- a/worlds/raft/docs/en_Raft.md
+++ b/worlds/raft/docs/en_Raft.md
@@ -1,7 +1,7 @@
# Raft
-## Where is the settings page?
-The player settings page for this game is located here. It contains all the options
+## Where is the options page?
+The player options page for this game is located here. It contains all the options
you need to configure and export a config file.
## What does randomization do to this game?
diff --git a/worlds/rogue_legacy/docs/en_Rogue Legacy.md b/worlds/rogue_legacy/docs/en_Rogue Legacy.md
index c91dc0de6f7a..dd203c73ac26 100644
--- a/worlds/rogue_legacy/docs/en_Rogue Legacy.md
+++ b/worlds/rogue_legacy/docs/en_Rogue Legacy.md
@@ -1,9 +1,9 @@
# Rogue Legacy (PC)
-## Where is the settings page?
+## Where is the options 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 in YAML, but an explanation can be found in the
+The [player options page for this game](../player-options) contains most of the options you need to
+configure and export a config file. Some options 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?
diff --git a/worlds/rogue_legacy/docs/rogue-legacy_en.md b/worlds/rogue_legacy/docs/rogue-legacy_en.md
index e513d0f0ca18..fc9f6920178d 100644
--- a/worlds/rogue_legacy/docs/rogue-legacy_en.md
+++ b/worlds/rogue_legacy/docs/rogue-legacy_en.md
@@ -21,7 +21,7 @@ an experience customized for their taste, and different players in the same mult
### Where do I get a YAML file?
-you can customize your settings by visiting the [Rogue Legacy Settings Page](/games/Rogue%20Legacy/player-settings).
+you can customize your options by visiting the [Rogue Legacy Options Page](/games/Rogue%20Legacy/player-options).
### Connect to the MultiServer
diff --git a/worlds/ror2/docs/en_Risk of Rain 2.md b/worlds/ror2/docs/en_Risk of Rain 2.md
index d30edf888944..b2210e348d50 100644
--- a/worlds/ror2/docs/en_Risk of Rain 2.md
+++ b/worlds/ror2/docs/en_Risk of Rain 2.md
@@ -1,8 +1,8 @@
# Risk of Rain 2
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
@@ -23,7 +23,7 @@ Explore Mode:
- Chests will continue to work as they did in Classic Mode, the difference being that each environment
will have a set amount of items that can be sent out. In addition, shrines, radio scanners, newt altars,
- and scavenger bags will need to be checked, depending on your settings.
+ and scavenger bags will need to be checked, depending on your options.
This mode also makes each environment an item. In order to access a particular stage, you'll need it to be
sent in the multiworld.
@@ -32,7 +32,7 @@ Explore Mode:
Just like in the original game, any way to "beat the game" counts as a win. This means beating one of the bosses
on Commencement, The Planetarium, or A Moment, Whole. Alternatively, if you are new to the game and
aren't very confident in being able to "beat the game", you can set **Final Stage Death is Win** to true
-(You can turn this on in your player settings.) This will make it so dying on either Commencement or The Planetarium,
+(You can turn this on in your player options.) This will make it so dying on either Commencement or The Planetarium,
or **obliterating yourself in A Moment, Fractured** will count as your goal.
**You do not need to complete all the location checks** to win; any item you don't collect may be released if the
server options allow.
@@ -48,12 +48,12 @@ then finish a normal mode run while keeping the items you received via the multi
## Can you play multiplayer?
Yes! You can have a single multiplayer instance as one world in the multiworld. All the players involved need to have
-the Archipelago mod, but only the host needs to configure the Archipelago settings. When someone finds an item for your
+the Archipelago mod, but only the host needs to configure the Archipelago options. When someone finds an item for your
world, all the connected players will receive a copy of the item, and the location check bar will increase whenever any
player finds an item in Risk of Rain.
You cannot have players with different player slots in the same co-op game instance. Only the host's Archipelago
-settings apply, so each Risk of Rain 2 player slot in the multiworld needs to be a separate game instance. You could,
+options apply, so each Risk of Rain 2 player slot in the multiworld needs to be a separate game instance. You could,
for example, have two players trade off hosting and making progress on each other's player slot, but a single co-op
instance can't make progress towards multiple player slots in the multiworld.
@@ -69,7 +69,7 @@ The Risk of Rain items are:
* `Legendary Item` (Red items)
* `Lunar Item` (Blue items)
* `Equipment` (Orange items)
-* `Dio's Best Friend` (Used if you set the YAML setting `total_revives_available` above `0`)
+* `Dio's Best Friend` (Used if you set the YAML option `total_revives_available` above `0`)
* `Void Item` (Purple items) (needs dlc_sotv: enabled)
Each item grants you a random in-game item from the category it belongs to.
@@ -127,7 +127,7 @@ what item you sent out. If the message does not appear, this likely means that a
## What is the item pickup step?
-The item pickup step is a setting in the YAML which allows you to set how many items you need to spawn before the _next_ item
+The item pickup step is an option in the YAML which allows you to set how many items you need to spawn before the _next_ item
that is spawned disappears (in a poof of smoke) and goes out to the multiworld. For instance, an item step of **1** means that
every other chest will send an item to the multiworld. An item step of **2** means that every third chest sends out an item
just as an item step of **0** would send an item on **each chest.**
diff --git a/worlds/ror2/docs/setup_en.md b/worlds/ror2/docs/setup_en.md
index 0fa99c071b9c..6acf2654a8b2 100644
--- a/worlds/ror2/docs/setup_en.md
+++ b/worlds/ror2/docs/setup_en.md
@@ -29,7 +29,7 @@ You can see the [basic multiworld setup guide](/tutorial/Archipelago/setup/en) h
about why Archipelago uses YAML files and what they're for.
### Where do I get a YAML?
-You can use the [game settings page](/games/Risk%20of%20Rain%202/player-settings) here on the Archipelago
+You can use the [game options page](/games/Risk%20of%20Rain%202/player-options) here on the Archipelago
website to generate a YAML using a graphical interface.
diff --git a/worlds/ror2/regions.py b/worlds/ror2/regions.py
index 13b229da9249..199fdccf80e8 100644
--- a/worlds/ror2/regions.py
+++ b/worlds/ror2/regions.py
@@ -140,11 +140,7 @@ def create_explore_region(multiworld: MultiWorld, player: int, name: str, data:
def create_connections_in_regions(multiworld: MultiWorld, player: int, name: str, data: RoRRegionData) -> None:
region = multiworld.get_region(name, player)
if data.region_exits:
- for region_exit in data.region_exits:
- r_exit_stage = Entrance(player, region_exit, region)
- exit_region = multiworld.get_region(region_exit, player)
- r_exit_stage.connect(exit_region)
- region.exits.append(r_exit_stage)
+ region.add_exits(data.region_exits)
def create_classic_regions(ror2_world: "RiskOfRainWorld") -> None:
diff --git a/worlds/ror2/rules.py b/worlds/ror2/rules.py
index 442e6c0002aa..b4d5fe68b82e 100644
--- a/worlds/ror2/rules.py
+++ b/worlds/ror2/rules.py
@@ -9,14 +9,16 @@
# Rule to see if it has access to the previous stage
-def has_entrance_access_rule(multiworld: MultiWorld, stage: str, entrance: str, player: int) -> None:
- multiworld.get_entrance(entrance, player).access_rule = \
- lambda state: state.has(entrance, player) and state.has(stage, player)
+def has_entrance_access_rule(multiworld: MultiWorld, stage: str, region: str, player: int) -> None:
+ rule = lambda state: state.has(region, player) and state.has(stage, player)
+ for entrance in multiworld.get_region(region, player).entrances:
+ entrance.access_rule = rule
-def has_all_items(multiworld: MultiWorld, items: Set[str], entrance: str, player: int) -> None:
- multiworld.get_entrance(entrance, player).access_rule = \
- lambda state: state.has_all(items, player) and state.has(entrance, player)
+def has_all_items(multiworld: MultiWorld, items: Set[str], region: str, player: int) -> None:
+ rule = lambda state: state.has_all(items, player) and state.has(region, player)
+ for entrance in multiworld.get_region(region, player).entrances:
+ entrance.access_rule = rule
# Checks to see if chest/shrine are accessible
@@ -45,8 +47,9 @@ def check_location(state, environment: str, player: int, item_number: int, item_
def get_stage_event(multiworld: MultiWorld, player: int, stage_number: int) -> None:
if stage_number == 4:
return
- multiworld.get_entrance(f"OrderedStage_{stage_number + 1}", player).access_rule = \
- lambda state: state.has(f"Stage {stage_number + 1}", player)
+ rule = lambda state: state.has(f"Stage {stage_number + 1}", player)
+ for entrance in multiworld.get_region(f"OrderedStage_{stage_number + 1}", player).entrances:
+ entrance.access_rule = rule
def set_rules(ror2_world: "RiskOfRainWorld") -> None:
diff --git a/worlds/ror2/test/test_limbo_goal.py b/worlds/ror2/test/test_limbo_goal.py
index f8757a917641..9be9cca1206a 100644
--- a/worlds/ror2/test/test_limbo_goal.py
+++ b/worlds/ror2/test/test_limbo_goal.py
@@ -8,8 +8,8 @@ class LimboGoalTest(RoR2TestBase):
def test_limbo(self) -> None:
self.collect_all_but(["Hidden Realm: A Moment, Whole", "Victory"])
- self.assertFalse(self.can_reach_entrance("Hidden Realm: A Moment, Whole"))
+ self.assertFalse(self.can_reach_region("Hidden Realm: A Moment, Whole"))
self.assertBeatable(False)
self.collect_by_name("Hidden Realm: A Moment, Whole")
- self.assertTrue(self.can_reach_entrance("Hidden Realm: A Moment, Whole"))
+ self.assertTrue(self.can_reach_region("Hidden Realm: A Moment, Whole"))
self.assertBeatable(True)
diff --git a/worlds/ror2/test/test_mithrix_goal.py b/worlds/ror2/test/test_mithrix_goal.py
index 7ed9a2cd73a2..03b82311783c 100644
--- a/worlds/ror2/test/test_mithrix_goal.py
+++ b/worlds/ror2/test/test_mithrix_goal.py
@@ -8,18 +8,18 @@ class MithrixGoalTest(RoR2TestBase):
def test_mithrix(self) -> None:
self.collect_all_but(["Commencement", "Victory"])
- self.assertFalse(self.can_reach_entrance("Commencement"))
+ self.assertFalse(self.can_reach_region("Commencement"))
self.assertBeatable(False)
self.collect_by_name("Commencement")
- self.assertTrue(self.can_reach_entrance("Commencement"))
+ self.assertTrue(self.can_reach_region("Commencement"))
self.assertBeatable(True)
def test_stage5(self) -> None:
self.collect_all_but(["Stage 4", "Sky Meadow", "Victory"])
- self.assertFalse(self.can_reach_entrance("Sky Meadow"))
+ self.assertFalse(self.can_reach_region("Sky Meadow"))
self.assertBeatable(False)
self.collect_by_name("Sky Meadow")
- self.assertFalse(self.can_reach_entrance("Sky Meadow"))
+ self.assertFalse(self.can_reach_region("Sky Meadow"))
self.collect_by_name("Stage 4")
- self.assertTrue(self.can_reach_entrance("Sky Meadow"))
+ self.assertTrue(self.can_reach_region("Sky Meadow"))
self.assertBeatable(True)
diff --git a/worlds/ror2/test/test_voidling_goal.py b/worlds/ror2/test/test_voidling_goal.py
index a7520a5c5f95..77d1349f10eb 100644
--- a/worlds/ror2/test/test_voidling_goal.py
+++ b/worlds/ror2/test/test_voidling_goal.py
@@ -9,17 +9,17 @@ class VoidlingGoalTest(RoR2TestBase):
def test_planetarium(self) -> None:
self.collect_all_but(["The Planetarium", "Victory"])
- self.assertFalse(self.can_reach_entrance("The Planetarium"))
+ self.assertFalse(self.can_reach_region("The Planetarium"))
self.assertBeatable(False)
self.collect_by_name("The Planetarium")
- self.assertTrue(self.can_reach_entrance("The Planetarium"))
+ self.assertTrue(self.can_reach_region("The Planetarium"))
self.assertBeatable(True)
def test_void_locus_to_victory(self) -> None:
self.collect_all_but(["Void Locus", "Commencement"])
self.assertFalse(self.can_reach_location("Victory"))
self.collect_by_name("Void Locus")
- self.assertTrue(self.can_reach_entrance("Victory"))
+ self.assertTrue(self.can_reach_location("Victory"))
def test_commencement_to_victory(self) -> None:
self.collect_all_but(["Void Locus", "Commencement"])
diff --git a/worlds/sa2b/CHANGELOG.md b/worlds/sa2b/CHANGELOG.md
new file mode 100644
index 000000000000..af6c4afd229f
--- /dev/null
+++ b/worlds/sa2b/CHANGELOG.md
@@ -0,0 +1,247 @@
+# Sonic Adventure 2 Battle - Changelog
+
+
+## v2.3 - The Chao Update
+
+### Features:
+
+- New goal
+ - Chaos Chao
+ - Raise a Chaos Chao to win!
+- New optional Location Checks
+ - Chao Animal Parts
+ - Each body part from each type of animal is a location
+ - Chao Stats
+ - 0-99 levels of each of the 7 Chao stats can be locations
+ - The frequency of Chao Stat locations can be set (every level, every 2nd level, etc)
+ - Kindergartensanity
+ - Classroom lessons are locations
+ - Either all lessons or any one of each category can be set as locations
+ - Shopsanity
+ - A specified number of locations can be placed in the Chao Black Market
+ - These locations are unlocked by acquiring Chao Coins
+ - Ring costs for these items can be adjusted
+ - Chao Karate can now be set to one location per fight, instead of one per tournament
+- New Items
+ - If any Chao locations are active, the following will be in the item pool:
+ - Chao Eggs
+ - Garden Seeds
+ - Garden Fruit
+ - Chao Hats
+ - Chaos Drives
+ - New Trap
+ - Reverse Trap
+- The starting eggs in the garden can be a random color
+- Chao World entrances can be shuffled
+- Chao are given default names
+
+### Quality of Life:
+
+- Chao Save Data is now separate per-slot in addition to per-seed
+ - This allows a single player to have multiple slots in the same seed, each having separate Chao progress
+- Chao Race/Karate progress is now displayed on Stage Select (when hovering over Chao World)
+- All Chao can now enter the Hero and Dark races
+- Chao Karate difficulty can be set separately from Chao Race difficulty
+- Chao Aging can be sped up at will, up to 15×
+- New mod config option to fine-tune Chao Stat multiplication
+ - Note: This does not mix well with the Mod Manager "Chao Stat Multiplier" code
+- Pong Traps can now activate in Chao World
+- Maximum range for possible number of Emblems is now 1000
+- General APWorld cleanup and optimization
+- Option access has moved to the new options system
+- An item group now exists for trap items
+
+### Bug Fixes:
+
+- Dry Lagoon now has all 11 Animals
+- `Eternal Engine - 2` (Standard and Hard Logic) now requires only `Tails - Booster`
+- `Lost Colony - 2` (Hard Logic) now requires no upgrades
+- `Lost Colony - Animal 9` (Hard Logic) now requires either `Eggman - Jet Engine` or `Eggman - Large Cannon`
+
+
+## v2.2
+
+### Features:
+
+- New goals
+ - Boss Rush
+ - Complete the Boss Rush to win!
+ - Cannon's Core Boss Rush
+ - Beat Cannon's Core, then complete the Boss Rush
+ - Boss Rush Chaos Emerald Hunt
+ - Collect the seven Chaos Emeralds, then complete the Boss Rush
+- Boss Rush Shuffle option
+- New optional Location Checks
+ - Animalsanity
+ - Collect numbers of animals per stage
+- Ring Link option
+ - Any ring amounts gained and lost by a linked player will be instantly shared with all other active linked players
+- Voice line shuffle
+ - None
+ - Shuffled
+ - Rude
+ - Chao
+ - Singularity
+- New Traps
+ - Ice Trap
+ - Slow Trap
+ - Cutscene Trap
+
+### Quality of Life:
+
+- Maximum possible number of Emblems in item pool is now a player-facing option, in the range of 50-500
+- A cause is now included for sent DeathLinks
+- Death Cause messages are now displayed in-game
+- WSS connections are now supported
+
+### Bug Fixes:
+
+- Two rare softlock scenarios related to the Chaos Control Trap should no longer occur
+- Tracking of location checks while disconnected from the server should be more consistent
+- DeathLinks can now be sent and received between two players connected to the same slot
+- 2P mode should no longer be accessible
+- Boss Stages no longer display erroneous location tracking icons
+- Boss Stages are no longer subject to mission shuffle oddities
+- Fix Logic Errors
+ - Eternal Engine - Pipe 1 (Standard and Hard Logic) now requires no upgrades
+ - Egg Quarters - 5 (Standard Logic) now requires Rouge - Iron Boots
+
+
+## v2.1
+
+### Features:
+
+- New goal
+ - Grand Prix
+ - Complete all of the Kart Races to win!
+- New optional Location Checks
+ - Omosanity (Activating Omochao)
+ - Kart Race Mode
+- Ring Loss option
+ - Classic - lose all rings on hit
+ - Modern - lose 20 rings on hit
+ - OHKO - instantly die on hit, regardless of ring count (shields still protect you)
+- New Trap
+ - Pong Trap
+
+### Quality of Life:
+
+- SA2B is now distributed as an `.apworld`
+- Maximum possible number of Emblems in item pool is increased from 180 to 250
+- An indicator now shows on the Stage Select screen when Cannon's Core is available
+- Certain traps (Exposition and Pong) are now possible to receive on Route 101 and Route 280
+- Certain traps (Confusion, Chaos Control, Exposition and Pong) are now possible to receive on FinalHazard
+
+### Bug Fixes:
+
+- Actually swap Intermediate and Expert Chao Races correctly
+- Don't always grant double score for killing Gold Beetles anymore
+- Ensure upgrades are applied properly, even when received while dying
+- Fix the Message Queue getting disordered when receiving many messages in quick succession
+- Fix Logic errors
+ - `City Escape - 3` (Hard Logic) now requires no upgrades
+ - `Mission Street - Pipe 2` (Hard Logic) now requires no upgrades
+ - `Crazy Gadget - Pipe 3` (Hard Logic) now requires no upgrades
+ - `Egg Quarters - 3` (Hard Logic) now requires only `Rouge - Mystic Melody`
+ - `Mad Space - 5` (Hard Logic) now requires no upgrades
+
+
+## v2.0
+
+### 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
+
+
+## v1.1
+
+### Features:
+
+- Unlocking each gate of levels requires beating a random boss
+- Chao Races and Karate are now available as an option for checks
+- Junk items can now be put into the itempool
+ - Five Rings
+ - Ten Rings
+ - Twenty Rings
+ - Extra Life
+ - Shield
+ - Magnetic Shield
+ - Invincibility
+- Traps can now be put into the itempool
+ - Omotrap
+ - Chaos Control Trap
+ - Confusion Trap
+ - Tiny Trap
+- The Credits now display a few stats about the run
+- An Option for the minimum required rank for mission checks is now available
+- An Option for influencing the costs of level gates is now available
+
+### Bug Fixes:
+
+- A message will display if the game loses connection to Archipelago
+- The game will gracefully reconnect to Archipelago
+- Kart Race mode is now properly hidden
+- Minor logic fixes
+
+
+## v1.0 - First Stable Release
+
+### Features:
+
+- Goal is to beat Cannon's Core and defeat the Biolizard
+- Locations included:
+ - Upgrade Pickups
+ - Mission Clears
+- Items included:
+ - Character Upgrades
+ - Emblems
+- Levels are unlocked by certain amounts of emblems
+ - An option exists to specify how many missions to include
+- Cannon's Core is unlocked by a certain percentage of existent emblems, depending on an option
+- Music Shuffle is supported
+- DeathLink is supported
diff --git a/worlds/sa2b/docs/en_Sonic Adventure 2 Battle.md b/worlds/sa2b/docs/en_Sonic Adventure 2 Battle.md
index 12ccc50ccd2c..e2f732ffe585 100644
--- a/worlds/sa2b/docs/en_Sonic Adventure 2 Battle.md
+++ b/worlds/sa2b/docs/en_Sonic Adventure 2 Battle.md
@@ -1,8 +1,8 @@
# Sonic Adventure 2: Battle
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a config file.
+The [player options page for this game](../player-options) contains all the options you need to configure and export a config file.
## What does randomization do to this game?
diff --git a/worlds/sc2/Client.py b/worlds/sc2/Client.py
new file mode 100644
index 000000000000..fe6efb9c3035
--- /dev/null
+++ b/worlds/sc2/Client.py
@@ -0,0 +1,1631 @@
+from __future__ import annotations
+
+import asyncio
+import copy
+import ctypes
+import enum
+import inspect
+import logging
+import multiprocessing
+import os.path
+import re
+import sys
+import tempfile
+import typing
+import queue
+import zipfile
+import io
+import random
+import concurrent.futures
+from pathlib import Path
+
+# CommonClient import first to trigger ModuleUpdater
+from CommonClient import CommonContext, server_loop, ClientCommandProcessor, gui_enabled, get_base_parser
+from Utils import init_logging, is_windows, async_start
+from worlds.sc2 import ItemNames
+from worlds.sc2.ItemGroups import item_name_groups, unlisted_item_name_groups
+from worlds.sc2 import Options
+from worlds.sc2.Options import (
+ MissionOrder, KerriganPrimalStatus, kerrigan_unit_available, KerriganPresence,
+ GameSpeed, GenericUpgradeItems, GenericUpgradeResearch, ColorChoice, GenericUpgradeMissions,
+ LocationInclusion, ExtraLocations, MasteryLocations, ChallengeLocations, VanillaLocations,
+ DisableForcedCamera, SkipCutscenes, GrantStoryTech, GrantStoryLevels, TakeOverAIAllies, RequiredTactics,
+ SpearOfAdunPresence, SpearOfAdunPresentInNoBuild, SpearOfAdunAutonomouslyCastAbilityPresence,
+ SpearOfAdunAutonomouslyCastPresentInNoBuild
+)
+
+
+if __name__ == "__main__":
+ init_logging("SC2Client", exception_logger="Client")
+
+logger = logging.getLogger("Client")
+sc2_logger = logging.getLogger("Starcraft2")
+
+import nest_asyncio
+from worlds._sc2common import bot
+from worlds._sc2common.bot.data import Race
+from worlds._sc2common.bot.main import run_game
+from worlds._sc2common.bot.player import Bot
+from worlds.sc2.Items import lookup_id_to_name, get_full_item_list, ItemData, type_flaggroups, upgrade_numbers, upgrade_numbers_all
+from worlds.sc2.Locations import SC2WOL_LOC_ID_OFFSET, LocationType, SC2HOTS_LOC_ID_OFFSET
+from worlds.sc2.MissionTables import lookup_id_to_mission, SC2Campaign, lookup_name_to_mission, \
+ lookup_id_to_campaign, MissionConnection, SC2Mission, campaign_mission_table, SC2Race, get_no_build_missions
+from worlds.sc2.Regions import MissionInfo
+
+import colorama
+from Options import Option
+from NetUtils import ClientStatus, NetworkItem, JSONtoTextParser, JSONMessagePart, add_json_item, add_json_location, add_json_text, JSONTypes
+from MultiServer import mark_raw
+
+pool = concurrent.futures.ThreadPoolExecutor(1)
+loop = asyncio.get_event_loop_policy().new_event_loop()
+nest_asyncio.apply(loop)
+MAX_BONUS: int = 28
+VICTORY_MODULO: int = 100
+
+# GitHub repo where the Map/mod data is hosted for /download_data command
+DATA_REPO_OWNER = "Ziktofel"
+DATA_REPO_NAME = "Archipelago-SC2-data"
+DATA_API_VERSION = "API3"
+
+# Bot controller
+CONTROLLER_HEALTH: int = 38281
+CONTROLLER2_HEALTH: int = 38282
+
+# Games
+STARCRAFT2 = "Starcraft 2"
+STARCRAFT2_WOL = "Starcraft 2 Wings of Liberty"
+
+
+# Data version file path.
+# This file is used to tell if the downloaded data are outdated
+# Associated with /download_data command
+def get_metadata_file() -> str:
+ return os.environ["SC2PATH"] + os.sep + "ArchipelagoSC2Metadata.txt"
+
+
+class ConfigurableOptionType(enum.Enum):
+ INTEGER = enum.auto()
+ ENUM = enum.auto()
+
+class ConfigurableOptionInfo(typing.NamedTuple):
+ name: str
+ variable_name: str
+ option_class: typing.Type[Option]
+ option_type: ConfigurableOptionType = ConfigurableOptionType.ENUM
+ can_break_logic: bool = False
+
+
+class ColouredMessage:
+ def __init__(self, text: str = '') -> None:
+ self.parts: typing.List[dict] = []
+ if text:
+ self(text)
+ def __call__(self, text: str) -> 'ColouredMessage':
+ add_json_text(self.parts, text)
+ return self
+ def coloured(self, text: str, colour: str) -> 'ColouredMessage':
+ add_json_text(self.parts, text, type="color", color=colour)
+ return self
+ def location(self, location_id: int, player_id: int = 0) -> 'ColouredMessage':
+ add_json_location(self.parts, location_id, player_id)
+ return self
+ def item(self, item_id: int, player_id: int = 0, flags: int = 0) -> 'ColouredMessage':
+ add_json_item(self.parts, item_id, player_id, flags)
+ return self
+ def player(self, player_id: int) -> 'ColouredMessage':
+ add_json_text(self.parts, str(player_id), type=JSONTypes.player_id)
+ return self
+ def send(self, ctx: SC2Context) -> None:
+ ctx.on_print_json({"data": self.parts, "cmd": "PrintJSON"})
+
+
+class StarcraftClientProcessor(ClientCommandProcessor):
+ ctx: SC2Context
+ echo_commands = True
+
+ def formatted_print(self, text: str) -> None:
+ """Prints with kivy formatting to the GUI, and also prints to command-line and to all logs"""
+ # Note(mm): Bold/underline can help readability, but unfortunately the CommonClient does not filter bold tags from command-line output.
+ # Regardless, using `on_print_json` to get formatted text in the GUI and output in the command-line and in the logs,
+ # without having to branch code from CommonClient
+ self.ctx.on_print_json({"data": [{"text": text}]})
+
+ def _cmd_difficulty(self, difficulty: str = "") -> bool:
+ """Overrides the current difficulty set for the world. Takes the argument casual, normal, hard, or brutal"""
+ options = difficulty.split()
+ num_options = len(options)
+
+ if num_options > 0:
+ difficulty_choice = options[0].lower()
+ if difficulty_choice == "casual":
+ self.ctx.difficulty_override = 0
+ elif difficulty_choice == "normal":
+ self.ctx.difficulty_override = 1
+ elif difficulty_choice == "hard":
+ self.ctx.difficulty_override = 2
+ elif difficulty_choice == "brutal":
+ self.ctx.difficulty_override = 3
+ else:
+ self.output("Unable to parse difficulty '" + options[0] + "'")
+ return False
+
+ self.output("Difficulty set to " + options[0])
+ return True
+
+ else:
+ if self.ctx.difficulty == -1:
+ self.output("Please connect to a seed before checking difficulty.")
+ else:
+ current_difficulty = self.ctx.difficulty
+ if self.ctx.difficulty_override >= 0:
+ current_difficulty = self.ctx.difficulty_override
+ self.output("Current difficulty: " + ["Casual", "Normal", "Hard", "Brutal"][current_difficulty])
+ self.output("To change the difficulty, add the name of the difficulty after the command.")
+ return False
+
+
+ def _cmd_game_speed(self, game_speed: str = "") -> bool:
+ """Overrides the current game speed for the world.
+ Takes the arguments default, slower, slow, normal, fast, faster"""
+ options = game_speed.split()
+ num_options = len(options)
+
+ if num_options > 0:
+ speed_choice = options[0].lower()
+ if speed_choice == "default":
+ self.ctx.game_speed_override = 0
+ elif speed_choice == "slower":
+ self.ctx.game_speed_override = 1
+ elif speed_choice == "slow":
+ self.ctx.game_speed_override = 2
+ elif speed_choice == "normal":
+ self.ctx.game_speed_override = 3
+ elif speed_choice == "fast":
+ self.ctx.game_speed_override = 4
+ elif speed_choice == "faster":
+ self.ctx.game_speed_override = 5
+ else:
+ self.output("Unable to parse game speed '" + options[0] + "'")
+ return False
+
+ self.output("Game speed set to " + options[0])
+ return True
+
+ else:
+ if self.ctx.game_speed == -1:
+ self.output("Please connect to a seed before checking game speed.")
+ else:
+ current_speed = self.ctx.game_speed
+ if self.ctx.game_speed_override >= 0:
+ current_speed = self.ctx.game_speed_override
+ self.output("Current game speed: "
+ + ["Default", "Slower", "Slow", "Normal", "Fast", "Faster"][current_speed])
+ self.output("To change the game speed, add the name of the speed after the command,"
+ " or Default to select based on difficulty.")
+ return False
+
+ @mark_raw
+ def _cmd_received(self, filter_search: str = "") -> bool:
+ """List received items.
+ Pass in a parameter to filter the search by partial item name or exact item group."""
+ # Groups must be matched case-sensitively, so we properly capitalize the search term
+ # eg. "Spear of Adun" over "Spear Of Adun" or "spear of adun"
+ # This fails a lot of item name matches, but those should be found by partial name match
+ formatted_filter_search = " ".join([(part.lower() if len(part) <= 3 else part.lower().capitalize()) for part in filter_search.split()])
+
+ def item_matches_filter(item_name: str) -> bool:
+ # The filter can be an exact group name or a partial item name
+ # Partial item name can be matched case-insensitively
+ if filter_search.lower() in item_name.lower():
+ return True
+ # The search term should already be formatted as a group name
+ if formatted_filter_search in item_name_groups and item_name in item_name_groups[formatted_filter_search]:
+ return True
+ return False
+
+ items = get_full_item_list()
+ categorized_items: typing.Dict[SC2Race, typing.List[int]] = {}
+ parent_to_child: typing.Dict[int, typing.List[int]] = {}
+ items_received: typing.Dict[int, typing.List[NetworkItem]] = {}
+ filter_match_count = 0
+ for item in self.ctx.items_received:
+ items_received.setdefault(item.item, []).append(item)
+ items_received_set = set(items_received)
+ for item_data in items.values():
+ if item_data.parent_item:
+ parent_to_child.setdefault(items[item_data.parent_item].code, []).append(item_data.code)
+ else:
+ categorized_items.setdefault(item_data.race, []).append(item_data.code)
+ for faction in SC2Race:
+ has_printed_faction_title = False
+ def print_faction_title():
+ if not has_printed_faction_title:
+ self.formatted_print(f" [u]{faction.name}[/u] ")
+
+ for item_id in categorized_items[faction]:
+ item_name = self.ctx.item_names[item_id]
+ received_child_items = items_received_set.intersection(parent_to_child.get(item_id, []))
+ matching_children = [child for child in received_child_items
+ if item_matches_filter(self.ctx.item_names[child])]
+ received_items_of_this_type = items_received.get(item_id, [])
+ item_is_match = item_matches_filter(item_name)
+ if item_is_match or len(matching_children) > 0:
+ # Print found item if it or its children match the filter
+ if item_is_match:
+ filter_match_count += len(received_items_of_this_type)
+ for item in received_items_of_this_type:
+ print_faction_title()
+ has_printed_faction_title = True
+ (ColouredMessage('* ').item(item.item, flags=item.flags)
+ (" from ").location(item.location, self.ctx.slot)
+ (" by ").player(item.player)
+ ).send(self.ctx)
+
+ if received_child_items:
+ # We have this item's children
+ if len(matching_children) == 0:
+ # ...but none of them match the filter
+ continue
+
+ if not received_items_of_this_type:
+ # We didn't receive the item itself
+ print_faction_title()
+ has_printed_faction_title = True
+ ColouredMessage("- ").coloured(item_name, "black")(" - not obtained").send(self.ctx)
+
+ for child_item in matching_children:
+ received_items_of_this_type = items_received.get(child_item, [])
+ for item in received_items_of_this_type:
+ filter_match_count += len(received_items_of_this_type)
+ (ColouredMessage(' * ').item(item.item, flags=item.flags)
+ (" from ").location(item.location, self.ctx.slot)
+ (" by ").player(item.player)
+ ).send(self.ctx)
+
+ non_matching_children = len(received_child_items) - len(matching_children)
+ if non_matching_children > 0:
+ self.formatted_print(f" + {non_matching_children} child items that don't match the filter")
+ if filter_search == "":
+ self.formatted_print(f"[b]Obtained: {len(self.ctx.items_received)} items[/b]")
+ else:
+ self.formatted_print(f"[b]Filter \"{filter_search}\" found {filter_match_count} out of {len(self.ctx.items_received)} obtained items[/b]")
+ return True
+
+ def _cmd_option(self, option_name: str = "", option_value: str = "") -> None:
+ """Sets a Starcraft game option that can be changed after generation. Use "/option list" to see all options."""
+
+ LOGIC_WARNING = f" *Note changing this may result in logically unbeatable games*\n"
+
+ options = (
+ ConfigurableOptionInfo('kerrigan_presence', 'kerrigan_presence', Options.KerriganPresence, can_break_logic=True),
+ ConfigurableOptionInfo('soa_presence', 'spear_of_adun_presence', Options.SpearOfAdunPresence, can_break_logic=True),
+ ConfigurableOptionInfo('soa_in_nobuilds', 'spear_of_adun_present_in_no_build', Options.SpearOfAdunPresentInNoBuild, can_break_logic=True),
+ ConfigurableOptionInfo('control_ally', 'take_over_ai_allies', Options.TakeOverAIAllies, can_break_logic=True),
+ ConfigurableOptionInfo('minerals_per_item', 'minerals_per_item', Options.MineralsPerItem, ConfigurableOptionType.INTEGER),
+ ConfigurableOptionInfo('gas_per_item', 'vespene_per_item', Options.VespenePerItem, ConfigurableOptionType.INTEGER),
+ ConfigurableOptionInfo('supply_per_item', 'starting_supply_per_item', Options.StartingSupplyPerItem, ConfigurableOptionType.INTEGER),
+ ConfigurableOptionInfo('no_forced_camera', 'disable_forced_camera', Options.DisableForcedCamera),
+ ConfigurableOptionInfo('skip_cutscenes', 'skip_cutscenes', Options.SkipCutscenes),
+ )
+
+ WARNING_COLOUR = "salmon"
+ CMD_COLOUR = "slateblue"
+ boolean_option_map = {
+ 'y': 'true', 'yes': 'true', 'n': 'false', 'no': 'false',
+ }
+
+ help_message = ColouredMessage(inspect.cleandoc("""
+ Options
+ --------------------
+ """))('\n')
+ for option in options:
+ option_help_text = inspect.cleandoc(option.option_class.__doc__ or "No description provided.").split('\n', 1)[0]
+ help_message.coloured(option.name, CMD_COLOUR)(": " + " | ".join(option.option_class.options)
+ + f" -- {option_help_text}\n")
+ if option.can_break_logic:
+ help_message.coloured(LOGIC_WARNING, WARNING_COLOUR)
+ help_message("--------------------\nEnter an option without arguments to see its current value.\n")
+
+ if not option_name or option_name == 'list' or option_name == 'help':
+ help_message.send(self.ctx)
+ return
+ for option in options:
+ if option_name == option.name:
+ option_value = boolean_option_map.get(option_value, option_value)
+ if not option_value:
+ pass
+ elif option.option_type == ConfigurableOptionType.ENUM and option_value in option.option_class.options:
+ self.ctx.__dict__[option.variable_name] = option.option_class.options[option_value]
+ elif option.option_type == ConfigurableOptionType.INTEGER:
+ try:
+ self.ctx.__dict__[option.variable_name] = int(option_value, base=0)
+ except:
+ self.output(f"{option_value} is not a valid integer")
+ else:
+ self.output(f"Unknown option value '{option_value}'")
+ ColouredMessage(f"{option.name} is '{option.option_class.get_option_name(self.ctx.__dict__[option.variable_name])}'").send(self.ctx)
+ break
+ else:
+ self.output(f"Unknown option '{option_name}'")
+ help_message.send(self.ctx)
+
+ def _cmd_color(self, faction: str = "", color: str = "") -> None:
+ """Changes the player color for a given faction."""
+ player_colors = [
+ "White", "Red", "Blue", "Teal",
+ "Purple", "Yellow", "Orange", "Green",
+ "LightPink", "Violet", "LightGrey", "DarkGreen",
+ "Brown", "LightGreen", "DarkGrey", "Pink",
+ "Rainbow", "Random", "Default"
+ ]
+ var_names = {
+ 'raynor': 'player_color_raynor',
+ 'kerrigan': 'player_color_zerg',
+ 'primal': 'player_color_zerg_primal',
+ 'protoss': 'player_color_protoss',
+ 'nova': 'player_color_nova',
+ }
+ faction = faction.lower()
+ if not faction:
+ for faction_name, key in var_names.items():
+ self.output(f"Current player color for {faction_name}: {player_colors[self.ctx.__dict__[key]]}")
+ self.output("To change your color, add the faction name and color after the command.")
+ self.output("Available factions: " + ', '.join(var_names))
+ self.output("Available colors: " + ', '.join(player_colors))
+ return
+ elif faction not in var_names:
+ self.output(f"Unknown faction '{faction}'.")
+ self.output("Available factions: " + ', '.join(var_names))
+ return
+ match_colors = [player_color.lower() for player_color in player_colors]
+ if not color:
+ self.output(f"Current player color for {faction}: {player_colors[self.ctx.__dict__[var_names[faction]]]}")
+ self.output("To change this faction's colors, add the name of the color after the command.")
+ self.output("Available colors: " + ', '.join(player_colors))
+ else:
+ if color.lower() not in match_colors:
+ self.output(color + " is not a valid color. Available colors: " + ', '.join(player_colors))
+ return
+ if color.lower() == "random":
+ color = random.choice(player_colors[:16])
+ self.ctx.__dict__[var_names[faction]] = match_colors.index(color.lower())
+ self.ctx.pending_color_update = True
+ self.output(f"Color for {faction} set to " + player_colors[self.ctx.__dict__[var_names[faction]]])
+
+ def _cmd_disable_mission_check(self) -> bool:
+ """Disables the check to see if a mission is available to play. Meant for co-op runs where one player can play
+ the next mission in a chain the other player is doing."""
+ self.ctx.missions_unlocked = True
+ sc2_logger.info("Mission check has been disabled")
+ return True
+
+ def _cmd_play(self, mission_id: str = "") -> bool:
+ """Start a Starcraft 2 mission"""
+
+ options = mission_id.split()
+ num_options = len(options)
+
+ if num_options > 0:
+ mission_number = int(options[0])
+
+ self.ctx.play_mission(mission_number)
+
+ else:
+ sc2_logger.info(
+ "Mission ID needs to be specified. Use /unfinished or /available to view ids for available missions.")
+ return False
+
+ return True
+
+ def _cmd_available(self) -> bool:
+ """Get what missions are currently available to play"""
+
+ request_available_missions(self.ctx)
+ return True
+
+ def _cmd_unfinished(self) -> bool:
+ """Get what missions are currently available to play and have not had all locations checked"""
+
+ request_unfinished_missions(self.ctx)
+ return True
+
+ @mark_raw
+ def _cmd_set_path(self, path: str = '') -> bool:
+ """Manually set the SC2 install directory (if the automatic detection fails)."""
+ if path:
+ os.environ["SC2PATH"] = path
+ is_mod_installed_correctly()
+ return True
+ else:
+ sc2_logger.warning("When using set_path, you must type the path to your SC2 install directory.")
+ return False
+
+ def _cmd_download_data(self) -> bool:
+ """Download the most recent release of the necessary files for playing SC2 with
+ Archipelago. Will overwrite existing files."""
+ pool.submit(self._download_data)
+ return True
+
+ @staticmethod
+ def _download_data() -> bool:
+ if "SC2PATH" not in os.environ:
+ check_game_install_path()
+
+ if os.path.exists(get_metadata_file()):
+ with open(get_metadata_file(), "r") as f:
+ metadata = f.read()
+ else:
+ metadata = None
+
+ tempzip, metadata = download_latest_release_zip(
+ DATA_REPO_OWNER, DATA_REPO_NAME, DATA_API_VERSION, metadata=metadata, force_download=True)
+
+ if tempzip:
+ try:
+ zipfile.ZipFile(tempzip).extractall(path=os.environ["SC2PATH"])
+ sc2_logger.info(f"Download complete. Package installed.")
+ if metadata is not None:
+ with open(get_metadata_file(), "w") as f:
+ f.write(metadata)
+ finally:
+ os.remove(tempzip)
+ else:
+ sc2_logger.warning("Download aborted/failed. Read the log for more information.")
+ return False
+ return True
+
+
+class SC2JSONtoTextParser(JSONtoTextParser):
+ def __init__(self, ctx) -> None:
+ self.handlers = {
+ "ItemSend": self._handle_color,
+ "ItemCheat": self._handle_color,
+ "Hint": self._handle_color,
+ }
+ super().__init__(ctx)
+
+ def _handle_color(self, node: JSONMessagePart) -> str:
+ codes = node["color"].split(";")
+ buffer = "".join(self.color_code(code) for code in codes if code in self.color_codes)
+ return buffer + self._handle_text(node) + ''
+
+ def color_code(self, code: str) -> str:
+ return ''
+
+
+class SC2Context(CommonContext):
+ command_processor = StarcraftClientProcessor
+ game = STARCRAFT2
+ items_handling = 0b111
+
+ def __init__(self, *args, **kwargs) -> None:
+ super(SC2Context, self).__init__(*args, **kwargs)
+ self.raw_text_parser = SC2JSONtoTextParser(self)
+
+ self.difficulty = -1
+ self.game_speed = -1
+ self.disable_forced_camera = 0
+ self.skip_cutscenes = 0
+ self.all_in_choice = 0
+ self.mission_order = 0
+ self.player_color_raynor = ColorChoice.option_blue
+ self.player_color_zerg = ColorChoice.option_orange
+ self.player_color_zerg_primal = ColorChoice.option_purple
+ self.player_color_protoss = ColorChoice.option_blue
+ self.player_color_nova = ColorChoice.option_dark_grey
+ self.pending_color_update = False
+ self.kerrigan_presence = 0
+ self.kerrigan_primal_status = 0
+ self.levels_per_check = 0
+ self.checks_per_level = 1
+ self.mission_req_table: typing.Dict[SC2Campaign, typing.Dict[str, MissionInfo]] = {}
+ self.final_mission: int = 29
+ self.announcements: queue.Queue = queue.Queue()
+ self.sc2_run_task: typing.Optional[asyncio.Task] = None
+ self.missions_unlocked: bool = False # allow launching missions ignoring requirements
+ self.generic_upgrade_missions = 0
+ self.generic_upgrade_research = 0
+ self.generic_upgrade_items = 0
+ self.location_inclusions: typing.Dict[LocationType, int] = {}
+ self.plando_locations: typing.List[str] = []
+ self.current_tooltip = None
+ self.last_loc_list = None
+ self.difficulty_override = -1
+ self.game_speed_override = -1
+ self.mission_id_to_location_ids: typing.Dict[int, typing.List[int]] = {}
+ self.last_bot: typing.Optional[ArchipelagoBot] = None
+ self.slot_data_version = 2
+ self.grant_story_tech = 0
+ self.required_tactics = RequiredTactics.option_standard
+ self.take_over_ai_allies = TakeOverAIAllies.option_false
+ self.spear_of_adun_presence = SpearOfAdunPresence.option_not_present
+ self.spear_of_adun_present_in_no_build = SpearOfAdunPresentInNoBuild.option_false
+ self.spear_of_adun_autonomously_cast_ability_presence = SpearOfAdunAutonomouslyCastAbilityPresence.option_not_present
+ self.spear_of_adun_autonomously_cast_present_in_no_build = SpearOfAdunAutonomouslyCastPresentInNoBuild.option_false
+ self.minerals_per_item = 15
+ self.vespene_per_item = 15
+ self.starting_supply_per_item = 2
+ self.nova_covert_ops_only = False
+ self.kerrigan_levels_per_mission_completed = 0
+
+ async def server_auth(self, password_requested: bool = False) -> None:
+ self.game = STARCRAFT2
+ if password_requested and not self.password:
+ await super(SC2Context, self).server_auth(password_requested)
+ await self.get_username()
+ await self.send_connect()
+ if self.ui:
+ self.ui.first_check = True
+
+ def is_legacy_game(self):
+ return self.game == STARCRAFT2_WOL
+
+ def event_invalid_game(self):
+ if self.is_legacy_game():
+ self.game = STARCRAFT2
+ super().event_invalid_game()
+ else:
+ self.game = STARCRAFT2_WOL
+ async_start(self.send_connect())
+
+ def on_package(self, cmd: str, args: dict) -> None:
+ if cmd == "Connected":
+ self.difficulty = args["slot_data"]["game_difficulty"]
+ self.game_speed = args["slot_data"].get("game_speed", GameSpeed.option_default)
+ self.disable_forced_camera = args["slot_data"].get("disable_forced_camera", DisableForcedCamera.default)
+ self.skip_cutscenes = args["slot_data"].get("skip_cutscenes", SkipCutscenes.default)
+ self.all_in_choice = args["slot_data"]["all_in_map"]
+ self.slot_data_version = args["slot_data"].get("version", 2)
+ slot_req_table: dict = args["slot_data"]["mission_req"]
+
+ first_item = list(slot_req_table.keys())[0]
+ # Maintaining backwards compatibility with older slot data
+ if first_item in [str(campaign.id) for campaign in SC2Campaign]:
+ # Multi-campaign
+ self.mission_req_table = {}
+ for campaign_id in slot_req_table:
+ campaign = lookup_id_to_campaign[int(campaign_id)]
+ self.mission_req_table[campaign] = {
+ mission: self.parse_mission_info(mission_info)
+ for mission, mission_info in slot_req_table[campaign_id].items()
+ }
+ else:
+ # Old format
+ self.mission_req_table = {SC2Campaign.GLOBAL: {
+ mission: self.parse_mission_info(mission_info)
+ for mission, mission_info in slot_req_table.items()
+ }
+ }
+
+ self.mission_order = args["slot_data"].get("mission_order", MissionOrder.option_vanilla)
+ self.final_mission = args["slot_data"].get("final_mission", SC2Mission.ALL_IN.id)
+ self.player_color_raynor = args["slot_data"].get("player_color_terran_raynor", ColorChoice.option_blue)
+ self.player_color_zerg = args["slot_data"].get("player_color_zerg", ColorChoice.option_orange)
+ self.player_color_zerg_primal = args["slot_data"].get("player_color_zerg_primal", ColorChoice.option_purple)
+ self.player_color_protoss = args["slot_data"].get("player_color_protoss", ColorChoice.option_blue)
+ self.player_color_nova = args["slot_data"].get("player_color_nova", ColorChoice.option_dark_grey)
+ self.generic_upgrade_missions = args["slot_data"].get("generic_upgrade_missions", GenericUpgradeMissions.default)
+ self.generic_upgrade_items = args["slot_data"].get("generic_upgrade_items", GenericUpgradeItems.option_individual_items)
+ self.generic_upgrade_research = args["slot_data"].get("generic_upgrade_research", GenericUpgradeResearch.option_vanilla)
+ self.kerrigan_presence = args["slot_data"].get("kerrigan_presence", KerriganPresence.option_vanilla)
+ self.kerrigan_primal_status = args["slot_data"].get("kerrigan_primal_status", KerriganPrimalStatus.option_vanilla)
+ self.kerrigan_levels_per_mission_completed = args["slot_data"].get("kerrigan_levels_per_mission_completed", 0)
+ self.kerrigan_levels_per_mission_completed_cap = args["slot_data"].get("kerrigan_levels_per_mission_completed_cap", -1)
+ self.kerrigan_total_level_cap = args["slot_data"].get("kerrigan_total_level_cap", -1)
+ self.grant_story_tech = args["slot_data"].get("grant_story_tech", GrantStoryTech.option_false)
+ self.grant_story_levels = args["slot_data"].get("grant_story_levels", GrantStoryLevels.option_additive)
+ self.required_tactics = args["slot_data"].get("required_tactics", RequiredTactics.option_standard)
+ self.take_over_ai_allies = args["slot_data"].get("take_over_ai_allies", TakeOverAIAllies.option_false)
+ self.spear_of_adun_presence = args["slot_data"].get("spear_of_adun_presence", SpearOfAdunPresence.option_not_present)
+ self.spear_of_adun_present_in_no_build = args["slot_data"].get("spear_of_adun_present_in_no_build", SpearOfAdunPresentInNoBuild.option_false)
+ self.spear_of_adun_autonomously_cast_ability_presence = args["slot_data"].get("spear_of_adun_autonomously_cast_ability_presence", SpearOfAdunAutonomouslyCastAbilityPresence.option_not_present)
+ self.spear_of_adun_autonomously_cast_present_in_no_build = args["slot_data"].get("spear_of_adun_autonomously_cast_present_in_no_build", SpearOfAdunAutonomouslyCastPresentInNoBuild.option_false)
+ self.minerals_per_item = args["slot_data"].get("minerals_per_item", 15)
+ self.vespene_per_item = args["slot_data"].get("vespene_per_item", 15)
+ self.starting_supply_per_item = args["slot_data"].get("starting_supply_per_item", 2)
+ self.nova_covert_ops_only = args["slot_data"].get("nova_covert_ops_only", False)
+
+ if self.required_tactics == RequiredTactics.option_no_logic:
+ # Locking Grant Story Tech/Levels if no logic
+ self.grant_story_tech = GrantStoryTech.option_true
+ self.grant_story_levels = GrantStoryLevels.option_minimum
+
+ self.location_inclusions = {
+ LocationType.VICTORY: LocationInclusion.option_enabled, # Victory checks are always enabled
+ LocationType.VANILLA: args["slot_data"].get("vanilla_locations", VanillaLocations.default),
+ LocationType.EXTRA: args["slot_data"].get("extra_locations", ExtraLocations.default),
+ LocationType.CHALLENGE: args["slot_data"].get("challenge_locations", ChallengeLocations.default),
+ LocationType.MASTERY: args["slot_data"].get("mastery_locations", MasteryLocations.default),
+ }
+ self.plando_locations = args["slot_data"].get("plando_locations", [])
+
+ self.build_location_to_mission_mapping()
+
+ # Looks for the required maps and mods for SC2. Runs check_game_install_path.
+ maps_present = is_mod_installed_correctly()
+ if os.path.exists(get_metadata_file()):
+ with open(get_metadata_file(), "r") as f:
+ current_ver = f.read()
+ sc2_logger.debug(f"Current version: {current_ver}")
+ if is_mod_update_available(DATA_REPO_OWNER, DATA_REPO_NAME, DATA_API_VERSION, current_ver):
+ sc2_logger.info("NOTICE: Update for required files found. Run /download_data to install.")
+ elif maps_present:
+ sc2_logger.warning("NOTICE: Your map files may be outdated (version number not found). "
+ "Run /download_data to update them.")
+
+ @staticmethod
+ def parse_mission_info(mission_info: dict[str, typing.Any]) -> MissionInfo:
+ if mission_info.get("id") is not None:
+ mission_info["mission"] = lookup_id_to_mission[mission_info["id"]]
+ elif isinstance(mission_info["mission"], int):
+ mission_info["mission"] = lookup_id_to_mission[mission_info["mission"]]
+
+ return MissionInfo(
+ **{field: value for field, value in mission_info.items() if field in MissionInfo._fields}
+ )
+
+ def find_campaign(self, mission_name: str) -> SC2Campaign:
+ data = self.mission_req_table
+ for campaign in data.keys():
+ if mission_name in data[campaign].keys():
+ return campaign
+ sc2_logger.info(f"Attempted to find campaign of unknown mission '{mission_name}'; defaulting to GLOBAL")
+ return SC2Campaign.GLOBAL
+
+
+
+ def on_print_json(self, args: dict) -> None:
+ # goes to this world
+ if "receiving" in args and self.slot_concerns_self(args["receiving"]):
+ relevant = True
+ # found in this world
+ elif "item" in args and self.slot_concerns_self(args["item"].player):
+ relevant = True
+ # not related
+ else:
+ relevant = False
+
+ if relevant:
+ self.announcements.put(self.raw_text_parser(copy.deepcopy(args["data"])))
+
+ super(SC2Context, self).on_print_json(args)
+
+ def run_gui(self) -> None:
+ from .ClientGui import start_gui
+ start_gui(self)
+
+
+ async def shutdown(self) -> None:
+ await super(SC2Context, self).shutdown()
+ if self.last_bot:
+ self.last_bot.want_close = True
+ if self.sc2_run_task:
+ self.sc2_run_task.cancel()
+
+ def play_mission(self, mission_id: int) -> bool:
+ if self.missions_unlocked or is_mission_available(self, mission_id):
+ if self.sc2_run_task:
+ if not self.sc2_run_task.done():
+ sc2_logger.warning("Starcraft 2 Client is still running!")
+ self.sc2_run_task.cancel() # doesn't actually close the game, just stops the python task
+ if self.slot is None:
+ sc2_logger.warning("Launching Mission without Archipelago authentication, "
+ "checks will not be registered to server.")
+ self.sc2_run_task = asyncio.create_task(starcraft_launch(self, mission_id),
+ name="Starcraft 2 Launch")
+ return True
+ else:
+ sc2_logger.info(
+ f"{lookup_id_to_mission[mission_id].mission_name} is not currently unlocked. "
+ f"Use /unfinished or /available to see what is available.")
+ return False
+
+ def build_location_to_mission_mapping(self) -> None:
+ mission_id_to_location_ids: typing.Dict[int, typing.Set[int]] = {
+ mission_info.mission.id: set() for campaign_mission in self.mission_req_table.values() for mission_info in campaign_mission.values()
+ }
+
+ for loc in self.server_locations:
+ offset = SC2WOL_LOC_ID_OFFSET if loc < SC2HOTS_LOC_ID_OFFSET \
+ else (SC2HOTS_LOC_ID_OFFSET - SC2Mission.ALL_IN.id * VICTORY_MODULO)
+ mission_id, objective = divmod(loc - offset, VICTORY_MODULO)
+ mission_id_to_location_ids[mission_id].add(objective)
+ self.mission_id_to_location_ids = {mission_id: sorted(objectives) for mission_id, objectives in
+ mission_id_to_location_ids.items()}
+
+ def locations_for_mission(self, mission_name: str):
+ mission = lookup_name_to_mission[mission_name]
+ mission_id: int = mission.id
+ objectives = self.mission_id_to_location_ids[mission_id]
+ for objective in objectives:
+ yield get_location_offset(mission_id) + mission_id * VICTORY_MODULO + objective
+
+
+class CompatItemHolder(typing.NamedTuple):
+ name: str
+ quantity: int = 1
+
+
+async def main():
+ multiprocessing.freeze_support()
+ parser = get_base_parser()
+ parser.add_argument('--name', default=None, help="Slot Name to connect as.")
+ args = parser.parse_args()
+
+ ctx = SC2Context(args.connect, args.password)
+ ctx.auth = args.name
+ if ctx.server_task is None:
+ ctx.server_task = asyncio.create_task(server_loop(ctx), name="ServerLoop")
+
+ if gui_enabled:
+ ctx.run_gui()
+ ctx.run_cli()
+
+ await ctx.exit_event.wait()
+
+ await ctx.shutdown()
+
+# These items must be given to the player if the game is generated on version 2
+API2_TO_API3_COMPAT_ITEMS: typing.Set[CompatItemHolder] = {
+ CompatItemHolder(ItemNames.PHOTON_CANNON),
+ CompatItemHolder(ItemNames.OBSERVER),
+ CompatItemHolder(ItemNames.WARP_HARMONIZATION),
+ CompatItemHolder(ItemNames.PROGRESSIVE_PROTOSS_GROUND_WEAPON, 3),
+ CompatItemHolder(ItemNames.PROGRESSIVE_PROTOSS_GROUND_ARMOR, 3),
+ CompatItemHolder(ItemNames.PROGRESSIVE_PROTOSS_SHIELDS, 3),
+ CompatItemHolder(ItemNames.PROGRESSIVE_PROTOSS_AIR_WEAPON, 3),
+ CompatItemHolder(ItemNames.PROGRESSIVE_PROTOSS_AIR_ARMOR, 3),
+ CompatItemHolder(ItemNames.PROGRESSIVE_PROTOSS_WEAPON_ARMOR_UPGRADE, 3)
+}
+
+
+def compat_item_to_network_items(compat_item: CompatItemHolder) -> typing.List[NetworkItem]:
+ item_id = get_full_item_list()[compat_item.name].code
+ network_item = NetworkItem(item_id, 0, 0, 0)
+ return compat_item.quantity * [network_item]
+
+
+def calculate_items(ctx: SC2Context) -> typing.Dict[SC2Race, typing.List[int]]:
+ items = ctx.items_received.copy()
+ # Items unlocked in API2 by default (Prophecy default items)
+ if ctx.slot_data_version < 3:
+ for compat_item in API2_TO_API3_COMPAT_ITEMS:
+ items.extend(compat_item_to_network_items(compat_item))
+
+ network_item: NetworkItem
+ accumulators: typing.Dict[SC2Race, typing.List[int]] = {race: [0 for _ in type_flaggroups[race]] for race in SC2Race}
+
+ # Protoss Shield grouped item specific logic
+ shields_from_ground_upgrade: int = 0
+ shields_from_air_upgrade: int = 0
+
+ item_list = get_full_item_list()
+ for network_item in items:
+ name: str = lookup_id_to_name[network_item.item]
+ item_data: ItemData = item_list[name]
+
+ # exists exactly once
+ if item_data.quantity == 1:
+ accumulators[item_data.race][type_flaggroups[item_data.race][item_data.type]] |= 1 << item_data.number
+
+ # exists multiple times
+ elif item_data.type in ["Upgrade", "Progressive Upgrade","Progressive Upgrade 2"]:
+ flaggroup = type_flaggroups[item_data.race][item_data.type]
+
+ # Generic upgrades apply only to Weapon / Armor upgrades
+ if item_data.type != "Upgrade" or ctx.generic_upgrade_items == 0:
+ accumulators[item_data.race][flaggroup] += 1 << item_data.number
+ else:
+ if name == ItemNames.PROGRESSIVE_PROTOSS_GROUND_UPGRADE:
+ shields_from_ground_upgrade += 1
+ if name == ItemNames.PROGRESSIVE_PROTOSS_AIR_UPGRADE:
+ shields_from_air_upgrade += 1
+ for bundled_number in upgrade_numbers[item_data.number]:
+ accumulators[item_data.race][flaggroup] += 1 << bundled_number
+
+ # Regen bio-steel nerf with API3 - undo for older games
+ if ctx.slot_data_version < 3 and name == ItemNames.PROGRESSIVE_REGENERATIVE_BIO_STEEL:
+ current_level = (accumulators[item_data.race][flaggroup] >> item_data.number) % 4
+ if current_level == 2:
+ # Switch from level 2 to level 3 for compatibility
+ accumulators[item_data.race][flaggroup] += 1 << item_data.number
+ # sum
+ else:
+ if name == ItemNames.STARTING_MINERALS:
+ accumulators[item_data.race][type_flaggroups[item_data.race][item_data.type]] += ctx.minerals_per_item
+ elif name == ItemNames.STARTING_VESPENE:
+ accumulators[item_data.race][type_flaggroups[item_data.race][item_data.type]] += ctx.vespene_per_item
+ elif name == ItemNames.STARTING_SUPPLY:
+ accumulators[item_data.race][type_flaggroups[item_data.race][item_data.type]] += ctx.starting_supply_per_item
+ else:
+ accumulators[item_data.race][type_flaggroups[item_data.race][item_data.type]] += item_data.number
+
+ # Fix Shields from generic upgrades by unit class (Maximum of ground/air upgrades)
+ if shields_from_ground_upgrade > 0 or shields_from_air_upgrade > 0:
+ shield_upgrade_level = max(shields_from_ground_upgrade, shields_from_air_upgrade)
+ shield_upgrade_item = item_list[ItemNames.PROGRESSIVE_PROTOSS_SHIELDS]
+ for _ in range(0, shield_upgrade_level):
+ accumulators[shield_upgrade_item.race][type_flaggroups[shield_upgrade_item.race][shield_upgrade_item.type]] += 1 << shield_upgrade_item.number
+
+ # Kerrigan levels per check
+ accumulators[SC2Race.ZERG][type_flaggroups[SC2Race.ZERG]["Level"]] += (len(ctx.checked_locations) // ctx.checks_per_level) * ctx.levels_per_check
+
+ # Upgrades from completed missions
+ if ctx.generic_upgrade_missions > 0:
+ total_missions = sum(len(ctx.mission_req_table[campaign]) for campaign in ctx.mission_req_table)
+ for race in SC2Race:
+ if "Upgrade" not in type_flaggroups[race]:
+ continue
+ upgrade_flaggroup = type_flaggroups[race]["Upgrade"]
+ num_missions = ctx.generic_upgrade_missions * total_missions
+ amounts = [
+ num_missions // 100,
+ 2 * num_missions // 100,
+ 3 * num_missions // 100
+ ]
+ upgrade_count = 0
+ completed = len([id for id in ctx.mission_id_to_location_ids if get_location_offset(id) + VICTORY_MODULO * id in ctx.checked_locations])
+ for amount in amounts:
+ if completed >= amount:
+ upgrade_count += 1
+ # Equivalent to "Progressive Weapon/Armor Upgrade" item
+ for bundled_number in upgrade_numbers[upgrade_numbers_all[race]]:
+ accumulators[race][upgrade_flaggroup] += upgrade_count << bundled_number
+
+ return accumulators
+
+
+def calc_difficulty(difficulty: int):
+ if difficulty == 0:
+ return 'C'
+ elif difficulty == 1:
+ return 'N'
+ elif difficulty == 2:
+ return 'H'
+ elif difficulty == 3:
+ return 'B'
+
+ return 'X'
+
+
+def get_kerrigan_level(ctx: SC2Context, items: typing.Dict[SC2Race, typing.List[int]], missions_beaten: int) -> int:
+ item_value = items[SC2Race.ZERG][type_flaggroups[SC2Race.ZERG]["Level"]]
+ mission_value = missions_beaten * ctx.kerrigan_levels_per_mission_completed
+ if ctx.kerrigan_levels_per_mission_completed_cap != -1:
+ mission_value = min(mission_value, ctx.kerrigan_levels_per_mission_completed_cap)
+ total_value = item_value + mission_value
+ if ctx.kerrigan_total_level_cap != -1:
+ total_value = min(total_value, ctx.kerrigan_total_level_cap)
+ return total_value
+
+
+def calculate_kerrigan_options(ctx: SC2Context) -> int:
+ options = 0
+
+ # Bits 0, 1
+ # Kerrigan unit available
+ if ctx.kerrigan_presence in kerrigan_unit_available:
+ options |= 1 << 0
+
+ # Bit 2
+ # Kerrigan primal status by map
+ if ctx.kerrigan_primal_status == KerriganPrimalStatus.option_vanilla:
+ options |= 1 << 2
+
+ return options
+
+
+def caclulate_soa_options(ctx: SC2Context) -> int:
+ options = 0
+
+ # Bits 0, 1
+ # SoA Calldowns available
+ soa_presence_value = 0
+ if ctx.spear_of_adun_presence == SpearOfAdunPresence.option_not_present:
+ soa_presence_value = 0
+ elif ctx.spear_of_adun_presence == SpearOfAdunPresence.option_lotv_protoss:
+ soa_presence_value = 1
+ elif ctx.spear_of_adun_presence == SpearOfAdunPresence.option_protoss:
+ soa_presence_value = 2
+ elif ctx.spear_of_adun_presence == SpearOfAdunPresence.option_everywhere:
+ soa_presence_value = 3
+ options |= soa_presence_value << 0
+
+ # Bit 2
+ # SoA Calldowns for no-builds
+ if ctx.spear_of_adun_present_in_no_build == SpearOfAdunPresentInNoBuild.option_true:
+ options |= 1 << 2
+
+ # Bits 3,4
+ # Autocasts
+ soa_autocasts_presence_value = 0
+ if ctx.spear_of_adun_autonomously_cast_ability_presence == SpearOfAdunAutonomouslyCastAbilityPresence.option_not_present:
+ soa_autocasts_presence_value = 0
+ elif ctx.spear_of_adun_autonomously_cast_ability_presence == SpearOfAdunAutonomouslyCastAbilityPresence.option_lotv_protoss:
+ soa_autocasts_presence_value = 1
+ elif ctx.spear_of_adun_autonomously_cast_ability_presence == SpearOfAdunAutonomouslyCastAbilityPresence.option_protoss:
+ soa_autocasts_presence_value = 2
+ elif ctx.spear_of_adun_autonomously_cast_ability_presence == SpearOfAdunAutonomouslyCastAbilityPresence.option_everywhere:
+ soa_autocasts_presence_value = 3
+ options |= soa_autocasts_presence_value << 3
+
+ # Bit 5
+ # Autocasts in no-builds
+ if ctx.spear_of_adun_autonomously_cast_present_in_no_build == SpearOfAdunAutonomouslyCastPresentInNoBuild.option_true:
+ options |= 1 << 5
+
+ return options
+
+def kerrigan_primal(ctx: SC2Context, items: typing.Dict[SC2Race, typing.List[int]]) -> bool:
+ if ctx.kerrigan_primal_status == KerriganPrimalStatus.option_always_zerg:
+ return True
+ elif ctx.kerrigan_primal_status == KerriganPrimalStatus.option_always_human:
+ return False
+ elif ctx.kerrigan_primal_status == KerriganPrimalStatus.option_level_35:
+ return items[SC2Race.ZERG][type_flaggroups[SC2Race.ZERG]["Level"]] >= 35
+ elif ctx.kerrigan_primal_status == KerriganPrimalStatus.option_half_completion:
+ total_missions = len(ctx.mission_id_to_location_ids)
+ completed = len([(mission_id * VICTORY_MODULO + get_location_offset(mission_id)) in ctx.checked_locations
+ for mission_id in ctx.mission_id_to_location_ids])
+ return completed >= (total_missions / 2)
+ elif ctx.kerrigan_primal_status == KerriganPrimalStatus.option_item:
+ codes = [item.item for item in ctx.items_received]
+ return get_full_item_list()[ItemNames.KERRIGAN_PRIMAL_FORM].code in codes
+ return False
+
+async def starcraft_launch(ctx: SC2Context, mission_id: int):
+ sc2_logger.info(f"Launching {lookup_id_to_mission[mission_id].mission_name}. If game does not launch check log file for errors.")
+
+ with DllDirectory(None):
+ run_game(bot.maps.get(lookup_id_to_mission[mission_id].map_file), [Bot(Race.Terran, ArchipelagoBot(ctx, mission_id),
+ name="Archipelago", fullscreen=True)], realtime=True)
+
+
+class ArchipelagoBot(bot.bot_ai.BotAI):
+ __slots__ = [
+ 'game_running',
+ 'mission_completed',
+ 'boni',
+ 'setup_done',
+ 'ctx',
+ 'mission_id',
+ 'want_close',
+ 'can_read_game',
+ 'last_received_update',
+ ]
+
+ def __init__(self, ctx: SC2Context, mission_id: int):
+ self.game_running = False
+ self.mission_completed = False
+ self.want_close = False
+ self.can_read_game = False
+ self.last_received_update: int = 0
+ self.setup_done = False
+ self.ctx = ctx
+ self.ctx.last_bot = self
+ self.mission_id = mission_id
+ self.boni = [False for _ in range(MAX_BONUS)]
+
+ super(ArchipelagoBot, self).__init__()
+
+ async def on_step(self, iteration: int):
+ if self.want_close:
+ self.want_close = False
+ await self._client.leave()
+ return
+ game_state = 0
+ if not self.setup_done:
+ self.setup_done = True
+ start_items = calculate_items(self.ctx)
+ missions_beaten = self.missions_beaten_count()
+ kerrigan_level = get_kerrigan_level(self.ctx, start_items, missions_beaten)
+ kerrigan_options = calculate_kerrigan_options(self.ctx)
+ soa_options = caclulate_soa_options(self.ctx)
+ if self.ctx.difficulty_override >= 0:
+ difficulty = calc_difficulty(self.ctx.difficulty_override)
+ else:
+ difficulty = calc_difficulty(self.ctx.difficulty)
+ if self.ctx.game_speed_override >= 0:
+ game_speed = self.ctx.game_speed_override
+ else:
+ game_speed = self.ctx.game_speed
+ await self.chat_send("?SetOptions {} {} {} {} {} {} {} {} {} {} {} {} {}".format(
+ difficulty,
+ self.ctx.generic_upgrade_research,
+ self.ctx.all_in_choice,
+ game_speed,
+ self.ctx.disable_forced_camera,
+ self.ctx.skip_cutscenes,
+ kerrigan_options,
+ self.ctx.grant_story_tech,
+ self.ctx.take_over_ai_allies,
+ soa_options,
+ self.ctx.mission_order,
+ 1 if self.ctx.nova_covert_ops_only else 0,
+ self.ctx.grant_story_levels
+ ))
+ await self.chat_send("?GiveResources {} {} {}".format(
+ start_items[SC2Race.ANY][0],
+ start_items[SC2Race.ANY][1],
+ start_items[SC2Race.ANY][2]
+ ))
+ await self.updateTerranTech(start_items)
+ await self.updateZergTech(start_items, kerrigan_level)
+ await self.updateProtossTech(start_items)
+ await self.updateColors()
+ await self.chat_send("?LoadFinished")
+ self.last_received_update = len(self.ctx.items_received)
+
+ else:
+ if self.ctx.pending_color_update:
+ await self.updateColors()
+
+ if not self.ctx.announcements.empty():
+ message = self.ctx.announcements.get(timeout=1)
+ await self.chat_send("?SendMessage " + message)
+ self.ctx.announcements.task_done()
+
+ # Archipelago reads the health
+ controller1_state = 0
+ controller2_state = 0
+ for unit in self.all_own_units():
+ if unit.health_max == CONTROLLER_HEALTH:
+ controller1_state = int(CONTROLLER_HEALTH - unit.health)
+ self.can_read_game = True
+ elif unit.health_max == CONTROLLER2_HEALTH:
+ controller2_state = int(CONTROLLER2_HEALTH - unit.health)
+ self.can_read_game = True
+ game_state = controller1_state + (controller2_state << 15)
+
+ if iteration == 160 and not game_state & 1:
+ await self.chat_send("?SendMessage Warning: Archipelago unable to connect or has lost connection to " +
+ "Starcraft 2 (This is likely a map issue)")
+
+ if self.last_received_update < len(self.ctx.items_received):
+ current_items = calculate_items(self.ctx)
+ missions_beaten = self.missions_beaten_count()
+ kerrigan_level = get_kerrigan_level(self.ctx, current_items, missions_beaten)
+ await self.updateTerranTech(current_items)
+ await self.updateZergTech(current_items, kerrigan_level)
+ await self.updateProtossTech(current_items)
+ self.last_received_update = len(self.ctx.items_received)
+
+ if game_state & 1:
+ if not self.game_running:
+ print("Archipelago Connected")
+ self.game_running = True
+
+ if self.can_read_game:
+ if game_state & (1 << 1) and not self.mission_completed:
+ if self.mission_id != self.ctx.final_mission:
+ print("Mission Completed")
+ await self.ctx.send_msgs(
+ [{"cmd": 'LocationChecks',
+ "locations": [get_location_offset(self.mission_id) + VICTORY_MODULO * self.mission_id]}])
+ self.mission_completed = True
+ else:
+ print("Game Complete")
+ await self.ctx.send_msgs([{"cmd": 'StatusUpdate', "status": ClientStatus.CLIENT_GOAL}])
+ self.mission_completed = True
+
+ for x, completed in enumerate(self.boni):
+ if not completed and game_state & (1 << (x + 2)):
+ await self.ctx.send_msgs(
+ [{"cmd": 'LocationChecks',
+ "locations": [get_location_offset(self.mission_id) + VICTORY_MODULO * self.mission_id + x + 1]}])
+ self.boni[x] = True
+ else:
+ await self.chat_send("?SendMessage LostConnection - Lost connection to game.")
+
+ def missions_beaten_count(self):
+ return len([location for location in self.ctx.checked_locations if location % VICTORY_MODULO == 0])
+
+ async def updateColors(self):
+ await self.chat_send("?SetColor rr " + str(self.ctx.player_color_raynor))
+ await self.chat_send("?SetColor ks " + str(self.ctx.player_color_zerg))
+ await self.chat_send("?SetColor pz " + str(self.ctx.player_color_zerg_primal))
+ await self.chat_send("?SetColor da " + str(self.ctx.player_color_protoss))
+ await self.chat_send("?SetColor nova " + str(self.ctx.player_color_nova))
+ self.ctx.pending_color_update = False
+
+ async def updateTerranTech(self, current_items):
+ terran_items = current_items[SC2Race.TERRAN]
+ await self.chat_send("?GiveTerranTech {} {} {} {} {} {} {} {} {} {} {} {} {} {}".format(
+ terran_items[0], terran_items[1], terran_items[2], terran_items[3], terran_items[4],
+ terran_items[5], terran_items[6], terran_items[7], terran_items[8], terran_items[9], terran_items[10],
+ terran_items[11], terran_items[12], terran_items[13]))
+
+ async def updateZergTech(self, current_items, kerrigan_level):
+ zerg_items = current_items[SC2Race.ZERG]
+ kerrigan_primal_by_items = kerrigan_primal(self.ctx, current_items)
+ kerrigan_primal_bot_value = 1 if kerrigan_primal_by_items else 0
+ await self.chat_send("?GiveZergTech {} {} {} {} {} {} {} {} {} {} {} {}".format(
+ kerrigan_level, kerrigan_primal_bot_value, zerg_items[0], zerg_items[1], zerg_items[2],
+ zerg_items[3], zerg_items[4], zerg_items[5], zerg_items[6], zerg_items[9], zerg_items[10], zerg_items[11]
+ ))
+
+ async def updateProtossTech(self, current_items):
+ protoss_items = current_items[SC2Race.PROTOSS]
+ await self.chat_send("?GiveProtossTech {} {} {} {} {} {} {} {} {} {}".format(
+ protoss_items[0], protoss_items[1], protoss_items[2], protoss_items[3], protoss_items[4],
+ protoss_items[5], protoss_items[6], protoss_items[7], protoss_items[8], protoss_items[9]
+ ))
+
+
+def request_unfinished_missions(ctx: SC2Context) -> None:
+ if ctx.mission_req_table:
+ message = "Unfinished Missions: "
+ unlocks = initialize_blank_mission_dict(ctx.mission_req_table)
+ unfinished_locations: typing.Dict[SC2Mission, typing.List[str]] = {}
+
+ _, unfinished_missions = calc_unfinished_missions(ctx, unlocks=unlocks)
+
+ for mission in unfinished_missions:
+ objectives = set(ctx.locations_for_mission(mission))
+ if objectives:
+ remaining_objectives = objectives.difference(ctx.checked_locations)
+ unfinished_locations[mission] = [ctx.location_names[location_id] for location_id in remaining_objectives]
+ else:
+ unfinished_locations[mission] = []
+
+ # Removing All-In from location pool
+ final_mission = lookup_id_to_mission[ctx.final_mission]
+ if final_mission in unfinished_missions.keys():
+ message = f"Final Mission Available: {final_mission}[{ctx.final_mission}]\n" + message
+ if unfinished_missions[final_mission] == -1:
+ unfinished_missions.pop(final_mission)
+
+ message += ", ".join(f"{mark_up_mission_name(ctx, mission, unlocks)}[{ctx.mission_req_table[ctx.find_campaign(mission)][mission].mission.id}] " +
+ mark_up_objectives(
+ f"[{len(unfinished_missions[mission])}/"
+ f"{sum(1 for _ in ctx.locations_for_mission(mission))}]",
+ ctx, unfinished_locations, mission)
+ for mission in unfinished_missions)
+
+ if ctx.ui:
+ ctx.ui.log_panels['All'].on_message_markup(message)
+ ctx.ui.log_panels['Starcraft2'].on_message_markup(message)
+ else:
+ sc2_logger.info(message)
+ else:
+ sc2_logger.warning("No mission table found, you are likely not connected to a server.")
+
+
+def calc_unfinished_missions(ctx: SC2Context, unlocks: typing.Optional[typing.Dict] = None):
+ unfinished_missions: typing.List[str] = []
+ locations_completed: typing.List[typing.Union[typing.Set[int], typing.Literal[-1]]] = []
+
+ if not unlocks:
+ unlocks = initialize_blank_mission_dict(ctx.mission_req_table)
+
+ available_missions = calc_available_missions(ctx, unlocks)
+
+ for name in available_missions:
+ objectives = set(ctx.locations_for_mission(name))
+ if objectives:
+ objectives_completed = ctx.checked_locations & objectives
+ if len(objectives_completed) < len(objectives):
+ unfinished_missions.append(name)
+ locations_completed.append(objectives_completed)
+
+ else: # infer that this is the final mission as it has no objectives
+ unfinished_missions.append(name)
+ locations_completed.append(-1)
+
+ return available_missions, dict(zip(unfinished_missions, locations_completed))
+
+
+def is_mission_available(ctx: SC2Context, mission_id_to_check: int) -> bool:
+ unfinished_missions = calc_available_missions(ctx)
+
+ return any(mission_id_to_check == ctx.mission_req_table[ctx.find_campaign(mission)][mission].mission.id for mission in unfinished_missions)
+
+
+def mark_up_mission_name(ctx: SC2Context, mission_name: str, unlock_table: typing.Dict) -> str:
+ """Checks if the mission is required for game completion and adds '*' to the name to mark that."""
+
+ campaign = ctx.find_campaign(mission_name)
+ mission_info = ctx.mission_req_table[campaign][mission_name]
+ if mission_info.completion_critical:
+ if ctx.ui:
+ message = "[color=AF99EF]" + mission_name + "[/color]"
+ else:
+ message = "*" + mission_name + "*"
+ else:
+ message = mission_name
+
+ if ctx.ui:
+ campaign_missions = list(ctx.mission_req_table[campaign].keys())
+ unlocks: typing.List[str]
+ index = campaign_missions.index(mission_name)
+ if index in unlock_table[campaign]:
+ unlocks = unlock_table[campaign][index]
+ else:
+ unlocks = []
+
+ if len(unlocks) > 0:
+ pre_message = f"[ref={mission_info.mission.id}|Unlocks: "
+ pre_message += ", ".join(f"{unlock}({ctx.mission_req_table[ctx.find_campaign(unlock)][unlock].mission.id})" for unlock in unlocks)
+ pre_message += f"]"
+ message = pre_message + message + "[/ref]"
+
+ return message
+
+
+def mark_up_objectives(message, ctx, unfinished_locations, mission):
+ formatted_message = message
+
+ if ctx.ui:
+ locations = unfinished_locations[mission]
+ campaign = ctx.find_campaign(mission)
+
+ pre_message = f"[ref={list(ctx.mission_req_table[campaign]).index(mission) + 30}|"
+ pre_message += " ".join(location for location in locations)
+ pre_message += f"]"
+ formatted_message = pre_message + message + "[/ref]"
+
+ return formatted_message
+
+
+def request_available_missions(ctx: SC2Context):
+ if ctx.mission_req_table:
+ message = "Available Missions: "
+
+ # Initialize mission unlock table
+ unlocks = initialize_blank_mission_dict(ctx.mission_req_table)
+
+ missions = calc_available_missions(ctx, unlocks)
+ message += \
+ ", ".join(f"{mark_up_mission_name(ctx, mission, unlocks)}"
+ f"[{ctx.mission_req_table[ctx.find_campaign(mission)][mission].mission.id}]"
+ for mission in missions)
+
+ if ctx.ui:
+ ctx.ui.log_panels['All'].on_message_markup(message)
+ ctx.ui.log_panels['Starcraft2'].on_message_markup(message)
+ else:
+ sc2_logger.info(message)
+ else:
+ sc2_logger.warning("No mission table found, you are likely not connected to a server.")
+
+
+def calc_available_missions(ctx: SC2Context, unlocks: typing.Optional[dict] = None) -> typing.List[str]:
+ available_missions: typing.List[str] = []
+ missions_complete = 0
+
+ # Get number of missions completed
+ for loc in ctx.checked_locations:
+ if loc % VICTORY_MODULO == 0:
+ missions_complete += 1
+
+ for campaign in ctx.mission_req_table:
+ # Go through the required missions for each mission and fill up unlock table used later for hover-over tooltips
+ for mission_name in ctx.mission_req_table[campaign]:
+ if unlocks:
+ for unlock in ctx.mission_req_table[campaign][mission_name].required_world:
+ parsed_unlock = parse_unlock(unlock)
+ # TODO prophecy-only wants to connect to WoL here
+ index = parsed_unlock.connect_to - 1
+ unlock_mission = list(ctx.mission_req_table[parsed_unlock.campaign])[index]
+ unlock_campaign = ctx.find_campaign(unlock_mission)
+ if unlock_campaign in unlocks:
+ if index not in unlocks[unlock_campaign]:
+ unlocks[unlock_campaign][index] = list()
+ unlocks[unlock_campaign][index].append(mission_name)
+
+ if mission_reqs_completed(ctx, mission_name, missions_complete):
+ available_missions.append(mission_name)
+
+ return available_missions
+
+
+def parse_unlock(unlock: typing.Union[typing.Dict[typing.Literal["connect_to", "campaign"], int], MissionConnection, int]) -> MissionConnection:
+ if isinstance(unlock, int):
+ # Legacy
+ return MissionConnection(unlock)
+ elif isinstance(unlock, MissionConnection):
+ return unlock
+ else:
+ # Multi-campaign
+ return MissionConnection(unlock["connect_to"], lookup_id_to_campaign[unlock["campaign"]])
+
+
+def mission_reqs_completed(ctx: SC2Context, mission_name: str, missions_complete: int) -> bool:
+ """Returns a bool signifying if the mission has all requirements complete and can be done
+
+ Arguments:
+ ctx -- instance of SC2Context
+ locations_to_check -- the mission string name to check
+ missions_complete -- an int of how many missions have been completed
+ mission_path -- a list of missions that have already been checked
+ """
+ campaign = ctx.find_campaign(mission_name)
+
+ if len(ctx.mission_req_table[campaign][mission_name].required_world) >= 1:
+ # A check for when the requirements are being or'd
+ or_success = False
+
+ # Loop through required missions
+ for req_mission in ctx.mission_req_table[campaign][mission_name].required_world:
+ req_success = True
+ parsed_req_mission = parse_unlock(req_mission)
+
+ # Check if required mission has been completed
+ mission_id = ctx.mission_req_table[parsed_req_mission.campaign][
+ list(ctx.mission_req_table[parsed_req_mission.campaign])[parsed_req_mission.connect_to - 1]].mission.id
+ if not (mission_id * VICTORY_MODULO + get_location_offset(mission_id)) in ctx.checked_locations:
+ if not ctx.mission_req_table[campaign][mission_name].or_requirements:
+ return False
+ else:
+ req_success = False
+
+ # Grid-specific logic (to avoid long path checks and infinite recursion)
+ if ctx.mission_order in (MissionOrder.option_grid, MissionOrder.option_mini_grid, MissionOrder.option_medium_grid):
+ if req_success:
+ return True
+ else:
+ if parsed_req_mission == ctx.mission_req_table[campaign][mission_name].required_world[-1]:
+ return False
+ else:
+ continue
+
+ # Recursively check required mission to see if it's requirements are met, in case !collect has been done
+ # Skipping recursive check on Grid settings to speed up checks and avoid infinite recursion
+ if not mission_reqs_completed(ctx, list(ctx.mission_req_table[parsed_req_mission.campaign])[parsed_req_mission.connect_to - 1], missions_complete):
+ if not ctx.mission_req_table[campaign][mission_name].or_requirements:
+ return False
+ else:
+ req_success = False
+
+ # If requirement check succeeded mark or as satisfied
+ if ctx.mission_req_table[campaign][mission_name].or_requirements and req_success:
+ or_success = True
+
+ if ctx.mission_req_table[campaign][mission_name].or_requirements:
+ # Return false if or requirements not met
+ if not or_success:
+ return False
+
+ # Check number of missions
+ if missions_complete >= ctx.mission_req_table[campaign][mission_name].number:
+ return True
+ else:
+ return False
+ else:
+ return True
+
+
+def initialize_blank_mission_dict(location_table: typing.Dict[SC2Campaign, typing.Dict[str, MissionInfo]]):
+ unlocks: typing.Dict[SC2Campaign, typing.Dict] = {}
+
+ for mission in list(location_table):
+ unlocks[mission] = {}
+
+ return unlocks
+
+
+def check_game_install_path() -> bool:
+ # First thing: go to the default location for ExecuteInfo.
+ # An exception for Windows is included because it's very difficult to find ~\Documents if the user moved it.
+ if is_windows:
+ # The next five lines of utterly inscrutable code are brought to you by copy-paste from Stack Overflow.
+ # https://stackoverflow.com/questions/6227590/finding-the-users-my-documents-path/30924555#
+ import ctypes.wintypes
+ CSIDL_PERSONAL = 5 # My Documents
+ SHGFP_TYPE_CURRENT = 0 # Get current, not default value
+
+ buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
+ ctypes.windll.shell32.SHGetFolderPathW(None, CSIDL_PERSONAL, None, SHGFP_TYPE_CURRENT, buf)
+ documentspath: str = buf.value
+ einfo = str(documentspath / Path("StarCraft II\\ExecuteInfo.txt"))
+ else:
+ einfo = str(bot.paths.get_home() / Path(bot.paths.USERPATH[bot.paths.PF]))
+
+ # Check if the file exists.
+ if os.path.isfile(einfo):
+
+ # Open the file and read it, picking out the latest executable's path.
+ with open(einfo) as f:
+ content = f.read()
+ if content:
+ search_result = re.search(r" = (.*)Versions", content)
+ if not search_result:
+ sc2_logger.warning(f"Found {einfo}, but it was empty. Run SC2 through the Blizzard launcher, "
+ "then try again.")
+ return False
+ base = search_result.group(1)
+
+ if os.path.exists(base):
+ executable = bot.paths.latest_executeble(Path(base).expanduser() / "Versions")
+
+ # Finally, check the path for an actual executable.
+ # If we find one, great. Set up the SC2PATH.
+ if os.path.isfile(executable):
+ sc2_logger.info(f"Found an SC2 install at {base}!")
+ sc2_logger.debug(f"Latest executable at {executable}.")
+ os.environ["SC2PATH"] = base
+ sc2_logger.debug(f"SC2PATH set to {base}.")
+ return True
+ else:
+ sc2_logger.warning(f"We may have found an SC2 install at {base}, but couldn't find {executable}.")
+ else:
+ sc2_logger.warning(f"{einfo} pointed to {base}, but we could not find an SC2 install there.")
+ else:
+ sc2_logger.warning(f"Couldn't find {einfo}. Run SC2 through the Blizzard launcher, then try again. "
+ f"If that fails, please run /set_path with your SC2 install directory.")
+ return False
+
+
+def is_mod_installed_correctly() -> bool:
+ """Searches for all required files."""
+ if "SC2PATH" not in os.environ:
+ check_game_install_path()
+ sc2_path: str = os.environ["SC2PATH"]
+ mapdir = sc2_path / Path('Maps/ArchipelagoCampaign')
+ mods = ["ArchipelagoCore", "ArchipelagoPlayer", "ArchipelagoPlayerSuper", "ArchipelagoPatches",
+ "ArchipelagoTriggers", "ArchipelagoPlayerWoL", "ArchipelagoPlayerHotS",
+ "ArchipelagoPlayerLotV", "ArchipelagoPlayerLotVPrologue", "ArchipelagoPlayerNCO"]
+ modfiles = [sc2_path / Path("Mods/" + mod + ".SC2Mod") for mod in mods]
+ wol_required_maps: typing.List[str] = ["WoL" + os.sep + mission.map_file + ".SC2Map" for mission in SC2Mission
+ if mission.campaign in (SC2Campaign.WOL, SC2Campaign.PROPHECY)]
+ hots_required_maps: typing.List[str] = ["HotS" + os.sep + mission.map_file + ".SC2Map" for mission in campaign_mission_table[SC2Campaign.HOTS]]
+ lotv_required_maps: typing.List[str] = ["LotV" + os.sep + mission.map_file + ".SC2Map" for mission in SC2Mission
+ if mission.campaign in (SC2Campaign.LOTV, SC2Campaign.PROLOGUE, SC2Campaign.EPILOGUE)]
+ nco_required_maps: typing.List[str] = ["NCO" + os.sep + mission.map_file + ".SC2Map" for mission in campaign_mission_table[SC2Campaign.NCO]]
+ required_maps = wol_required_maps + hots_required_maps + lotv_required_maps + nco_required_maps
+ needs_files = False
+
+ # Check for maps.
+ missing_maps: typing.List[str] = []
+ for mapfile in required_maps:
+ if not os.path.isfile(mapdir / mapfile):
+ missing_maps.append(mapfile)
+ if len(missing_maps) >= 19:
+ sc2_logger.warning(f"All map files missing from {mapdir}.")
+ needs_files = True
+ elif len(missing_maps) > 0:
+ for map in missing_maps:
+ sc2_logger.debug(f"Missing {map} from {mapdir}.")
+ sc2_logger.warning(f"Missing {len(missing_maps)} map files.")
+ needs_files = True
+ else: # Must be no maps missing
+ sc2_logger.info(f"All maps found in {mapdir}.")
+
+ # Check for mods.
+ for modfile in modfiles:
+ if os.path.isfile(modfile) or os.path.isdir(modfile):
+ sc2_logger.info(f"Archipelago mod found at {modfile}.")
+ else:
+ sc2_logger.warning(f"Archipelago mod could not be found at {modfile}.")
+ needs_files = True
+
+ # Final verdict.
+ if needs_files:
+ sc2_logger.warning(f"Required files are missing. Run /download_data to acquire them.")
+ return False
+ else:
+ sc2_logger.debug(f"All map/mod files are properly installed.")
+ return True
+
+
+class DllDirectory:
+ # Credit to Black Sliver for this code.
+ # More info: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setdlldirectoryw
+ _old: typing.Optional[str] = None
+ _new: typing.Optional[str] = None
+
+ def __init__(self, new: typing.Optional[str]):
+ self._new = new
+
+ def __enter__(self):
+ old = self.get()
+ if self.set(self._new):
+ self._old = old
+
+ def __exit__(self, *args):
+ if self._old is not None:
+ self.set(self._old)
+
+ @staticmethod
+ def get() -> typing.Optional[str]:
+ if sys.platform == "win32":
+ n = ctypes.windll.kernel32.GetDllDirectoryW(0, None)
+ buf = ctypes.create_unicode_buffer(n)
+ ctypes.windll.kernel32.GetDllDirectoryW(n, buf)
+ return buf.value
+ # NOTE: other OS may support os.environ["LD_LIBRARY_PATH"], but this fix is windows-specific
+ return None
+
+ @staticmethod
+ def set(s: typing.Optional[str]) -> bool:
+ if sys.platform == "win32":
+ return ctypes.windll.kernel32.SetDllDirectoryW(s) != 0
+ # NOTE: other OS may support os.environ["LD_LIBRARY_PATH"], but this fix is windows-specific
+ return False
+
+
+def download_latest_release_zip(
+ owner: str,
+ repo: str,
+ api_version: str,
+ metadata: typing.Optional[str] = None,
+ force_download=False
+) -> typing.Tuple[str, typing.Optional[str]]:
+ """Downloads the latest release of a GitHub repo to the current directory as a .zip file."""
+ import requests
+
+ headers = {"Accept": 'application/vnd.github.v3+json'}
+ url = f"https://api.github.com/repos/{owner}/{repo}/releases/tags/{api_version}"
+
+ r1 = requests.get(url, headers=headers)
+ if r1.status_code == 200:
+ latest_metadata = r1.json()
+ cleanup_downloaded_metadata(latest_metadata)
+ latest_metadata = str(latest_metadata)
+ # sc2_logger.info(f"Latest version: {latest_metadata}.")
+ else:
+ sc2_logger.warning(f"Status code: {r1.status_code}")
+ sc2_logger.warning(f"Failed to reach GitHub. Could not find download link.")
+ sc2_logger.warning(f"text: {r1.text}")
+ return "", metadata
+
+ if (force_download is False) and (metadata == latest_metadata):
+ sc2_logger.info("Latest version already installed.")
+ return "", metadata
+
+ sc2_logger.info(f"Attempting to download latest version of API version {api_version} of {repo}.")
+ download_url = r1.json()["assets"][0]["browser_download_url"]
+
+ r2 = requests.get(download_url, headers=headers)
+ if r2.status_code == 200 and zipfile.is_zipfile(io.BytesIO(r2.content)):
+ tempdir = tempfile.gettempdir()
+ file = tempdir + os.sep + f"{repo}.zip"
+ with open(file, "wb") as fh:
+ fh.write(r2.content)
+ sc2_logger.info(f"Successfully downloaded {repo}.zip.")
+ return file, latest_metadata
+ else:
+ sc2_logger.warning(f"Status code: {r2.status_code}")
+ sc2_logger.warning("Download failed.")
+ sc2_logger.warning(f"text: {r2.text}")
+ return "", metadata
+
+
+def cleanup_downloaded_metadata(medatada_json: dict) -> None:
+ for asset in medatada_json['assets']:
+ del asset['download_count']
+
+
+def is_mod_update_available(owner: str, repo: str, api_version: str, metadata: str) -> bool:
+ import requests
+
+ headers = {"Accept": 'application/vnd.github.v3+json'}
+ url = f"https://api.github.com/repos/{owner}/{repo}/releases/tags/{api_version}"
+
+ r1 = requests.get(url, headers=headers)
+ if r1.status_code == 200:
+ latest_metadata = r1.json()
+ cleanup_downloaded_metadata(latest_metadata)
+ latest_metadata = str(latest_metadata)
+ if metadata != latest_metadata:
+ return True
+ else:
+ return False
+
+ else:
+ sc2_logger.warning(f"Failed to reach GitHub while checking for updates.")
+ sc2_logger.warning(f"Status code: {r1.status_code}")
+ sc2_logger.warning(f"text: {r1.text}")
+ return False
+
+
+def get_location_offset(mission_id):
+ return SC2WOL_LOC_ID_OFFSET if mission_id <= SC2Mission.ALL_IN.id \
+ else (SC2HOTS_LOC_ID_OFFSET - SC2Mission.ALL_IN.id * VICTORY_MODULO)
+
+
+def launch():
+ colorama.init()
+ asyncio.run(main())
+ colorama.deinit()
diff --git a/worlds/sc2/ClientGui.py b/worlds/sc2/ClientGui.py
new file mode 100644
index 000000000000..167583fd1ecb
--- /dev/null
+++ b/worlds/sc2/ClientGui.py
@@ -0,0 +1,304 @@
+from typing import *
+import asyncio
+
+from kvui import GameManager, HoverBehavior, ServerToolTip
+from kivy.app import App
+from kivy.clock import Clock
+from kivy.uix.tabbedpanel import TabbedPanelItem
+from kivy.uix.gridlayout import GridLayout
+from kivy.lang import Builder
+from kivy.uix.label import Label
+from kivy.uix.button import Button
+from kivy.uix.floatlayout import FloatLayout
+from kivy.uix.scrollview import ScrollView
+from kivy.properties import StringProperty
+
+from worlds.sc2.Client import SC2Context, calc_unfinished_missions, parse_unlock
+from worlds.sc2.MissionTables import lookup_id_to_mission, lookup_name_to_mission, campaign_race_exceptions, \
+ SC2Mission, SC2Race, SC2Campaign
+from worlds.sc2.Locations import LocationType, lookup_location_id_to_type
+from worlds.sc2.Options import LocationInclusion
+from worlds.sc2 import SC2World, get_first_mission
+
+
+class HoverableButton(HoverBehavior, Button):
+ pass
+
+
+class MissionButton(HoverableButton):
+ tooltip_text = StringProperty("Test")
+
+ def __init__(self, *args, **kwargs):
+ super(HoverableButton, self).__init__(*args, **kwargs)
+ self.layout = FloatLayout()
+ self.popuplabel = ServerToolTip(text=self.text, markup=True)
+ self.popuplabel.padding = [5, 2, 5, 2]
+ self.layout.add_widget(self.popuplabel)
+
+ def on_enter(self):
+ self.popuplabel.text = self.tooltip_text
+
+ if self.ctx.current_tooltip:
+ App.get_running_app().root.remove_widget(self.ctx.current_tooltip)
+
+ if self.tooltip_text == "":
+ self.ctx.current_tooltip = None
+ else:
+ App.get_running_app().root.add_widget(self.layout)
+ self.ctx.current_tooltip = self.layout
+
+ def on_leave(self):
+ self.ctx.ui.clear_tooltip()
+
+ @property
+ def ctx(self) -> SC2Context:
+ return App.get_running_app().ctx
+
+class CampaignScroll(ScrollView):
+ pass
+
+class MultiCampaignLayout(GridLayout):
+ pass
+
+class CampaignLayout(GridLayout):
+ pass
+
+class MissionLayout(GridLayout):
+ pass
+
+class MissionCategory(GridLayout):
+ pass
+
+class SC2Manager(GameManager):
+ logging_pairs = [
+ ("Client", "Archipelago"),
+ ("Starcraft2", "Starcraft2"),
+ ]
+ base_title = "Archipelago Starcraft 2 Client"
+
+ campaign_panel: Optional[CampaignLayout] = None
+ last_checked_locations: Set[int] = set()
+ mission_id_to_button: Dict[int, MissionButton] = {}
+ launching: Union[bool, int] = False # if int -> mission ID
+ refresh_from_launching = True
+ first_check = True
+ first_mission = ""
+ ctx: SC2Context
+
+ def __init__(self, ctx) -> None:
+ super().__init__(ctx)
+
+ def clear_tooltip(self) -> None:
+ if self.ctx.current_tooltip:
+ App.get_running_app().root.remove_widget(self.ctx.current_tooltip)
+
+ self.ctx.current_tooltip = None
+
+ def build(self):
+ container = super().build()
+
+ panel = TabbedPanelItem(text="Starcraft 2 Launcher")
+ panel.content = CampaignScroll()
+ self.campaign_panel = MultiCampaignLayout()
+ panel.content.add_widget(self.campaign_panel)
+
+ self.tabs.add_widget(panel)
+
+ Clock.schedule_interval(self.build_mission_table, 0.5)
+
+ return container
+
+ def build_mission_table(self, dt) -> None:
+ if (not self.launching and (not self.last_checked_locations == self.ctx.checked_locations or
+ not self.refresh_from_launching)) or self.first_check:
+ assert self.campaign_panel is not None
+ self.refresh_from_launching = True
+
+ self.campaign_panel.clear_widgets()
+ if self.ctx.mission_req_table:
+ self.last_checked_locations = self.ctx.checked_locations.copy()
+ self.first_check = False
+ self.first_mission = get_first_mission(self.ctx.mission_req_table)
+
+ self.mission_id_to_button = {}
+
+ available_missions, unfinished_missions = calc_unfinished_missions(self.ctx)
+
+ multi_campaign_layout_height = 0
+
+ for campaign, missions in sorted(self.ctx.mission_req_table.items(), key=lambda item: item[0].id):
+ categories: Dict[str, List[str]] = {}
+
+ # separate missions into categories
+ for mission_index in missions:
+ mission_info = self.ctx.mission_req_table[campaign][mission_index]
+ if mission_info.category not in categories:
+ categories[mission_info.category] = []
+
+ categories[mission_info.category].append(mission_index)
+
+ max_mission_count = max(len(categories[category]) for category in categories)
+ if max_mission_count == 1:
+ campaign_layout_height = 115
+ else:
+ campaign_layout_height = (max_mission_count + 2) * 50
+ multi_campaign_layout_height += campaign_layout_height
+ campaign_layout = CampaignLayout(size_hint_y=None, height=campaign_layout_height)
+ if campaign != SC2Campaign.GLOBAL:
+ campaign_layout.add_widget(
+ Label(text=campaign.campaign_name, size_hint_y=None, height=25, outline_width=1)
+ )
+ mission_layout = MissionLayout()
+
+ for category in categories:
+ category_name_height = 0
+ category_spacing = 3
+ if category.startswith('_'):
+ category_display_name = ''
+ else:
+ category_display_name = category
+ category_name_height += 25
+ category_spacing = 10
+ category_panel = MissionCategory(padding=[category_spacing,6,category_spacing,6])
+ category_panel.add_widget(
+ Label(text=category_display_name, size_hint_y=None, height=category_name_height, outline_width=1))
+
+ for mission in categories[category]:
+ text: str = mission
+ tooltip: str = ""
+ mission_obj: SC2Mission = lookup_name_to_mission[mission]
+ mission_id: int = mission_obj.id
+ mission_data = self.ctx.mission_req_table[campaign][mission]
+ remaining_locations, plando_locations, remaining_count = self.sort_unfinished_locations(mission)
+ # Map has uncollected locations
+ if mission in unfinished_missions:
+ if self.any_valuable_locations(remaining_locations):
+ text = f"[color=6495ED]{text}[/color]"
+ else:
+ text = f"[color=A0BEF4]{text}[/color]"
+ elif mission in available_missions:
+ text = f"[color=FFFFFF]{text}[/color]"
+ # Map requirements not met
+ else:
+ text = f"[color=a9a9a9]{text}[/color]"
+ tooltip = f"Requires: "
+ if mission_data.required_world:
+ tooltip += ", ".join(list(self.ctx.mission_req_table[parse_unlock(req_mission).campaign])[parse_unlock(req_mission).connect_to - 1] for
+ req_mission in
+ mission_data.required_world)
+
+ if mission_data.number:
+ tooltip += " and "
+ if mission_data.number:
+ tooltip += f"{self.ctx.mission_req_table[campaign][mission].number} missions completed"
+
+ if mission_id == self.ctx.final_mission:
+ if mission in available_missions:
+ text = f"[color=FFBC95]{mission}[/color]"
+ else:
+ text = f"[color=D0C0BE]{mission}[/color]"
+ if tooltip:
+ tooltip += "\n"
+ tooltip += "Final Mission"
+
+ if remaining_count > 0:
+ if tooltip:
+ tooltip += "\n\n"
+ tooltip += f"-- Uncollected locations --"
+ for loctype in LocationType:
+ if len(remaining_locations[loctype]) > 0:
+ if loctype == LocationType.VICTORY:
+ tooltip += f"\n- {remaining_locations[loctype][0]}"
+ else:
+ tooltip += f"\n{self.get_location_type_title(loctype)}:\n- "
+ tooltip += "\n- ".join(remaining_locations[loctype])
+ if len(plando_locations) > 0:
+ tooltip += f"\nPlando:\n- "
+ tooltip += "\n- ".join(plando_locations)
+
+ MISSION_BUTTON_HEIGHT = 50
+ for pad in range(mission_data.ui_vertical_padding):
+ column_spacer = Label(text='', size_hint_y=None, height=MISSION_BUTTON_HEIGHT)
+ category_panel.add_widget(column_spacer)
+ mission_button = MissionButton(text=text, size_hint_y=None, height=MISSION_BUTTON_HEIGHT)
+ mission_race = mission_obj.race
+ if mission_race == SC2Race.ANY:
+ mission_race = mission_obj.campaign.race
+ race = campaign_race_exceptions.get(mission_obj, mission_race)
+ racial_colors = {
+ SC2Race.TERRAN: (0.24, 0.84, 0.68),
+ SC2Race.ZERG: (1, 0.65, 0.37),
+ SC2Race.PROTOSS: (0.55, 0.7, 1)
+ }
+ if race in racial_colors:
+ mission_button.background_color = racial_colors[race]
+ mission_button.tooltip_text = tooltip
+ mission_button.bind(on_press=self.mission_callback)
+ self.mission_id_to_button[mission_id] = mission_button
+ category_panel.add_widget(mission_button)
+
+ category_panel.add_widget(Label(text=""))
+ mission_layout.add_widget(category_panel)
+ campaign_layout.add_widget(mission_layout)
+ self.campaign_panel.add_widget(campaign_layout)
+ self.campaign_panel.height = multi_campaign_layout_height
+
+ elif self.launching:
+ assert self.campaign_panel is not None
+ self.refresh_from_launching = False
+
+ self.campaign_panel.clear_widgets()
+ self.campaign_panel.add_widget(Label(text="Launching Mission: " +
+ lookup_id_to_mission[self.launching].mission_name))
+ if self.ctx.ui:
+ self.ctx.ui.clear_tooltip()
+
+ def mission_callback(self, button: MissionButton) -> None:
+ if not self.launching:
+ mission_id: int = next(k for k, v in self.mission_id_to_button.items() if v == button)
+ if self.ctx.play_mission(mission_id):
+ self.launching = mission_id
+ Clock.schedule_once(self.finish_launching, 10)
+
+ def finish_launching(self, dt):
+ self.launching = False
+
+ def sort_unfinished_locations(self, mission_name: str) -> Tuple[Dict[LocationType, List[str]], List[str], int]:
+ locations: Dict[LocationType, List[str]] = {loctype: [] for loctype in LocationType}
+ count = 0
+ for loc in self.ctx.locations_for_mission(mission_name):
+ if loc in self.ctx.missing_locations:
+ count += 1
+ locations[lookup_location_id_to_type[loc]].append(self.ctx.location_names[loc])
+
+ plando_locations = []
+ for plando_loc in self.ctx.plando_locations:
+ for loctype in LocationType:
+ if plando_loc in locations[loctype]:
+ locations[loctype].remove(plando_loc)
+ plando_locations.append(plando_loc)
+
+ return locations, plando_locations, count
+
+ def any_valuable_locations(self, locations: Dict[LocationType, List[str]]) -> bool:
+ for loctype in LocationType:
+ if len(locations[loctype]) > 0 and self.ctx.location_inclusions[loctype] == LocationInclusion.option_enabled:
+ return True
+ return False
+
+ def get_location_type_title(self, location_type: LocationType) -> str:
+ title = location_type.name.title().replace("_", " ")
+ if self.ctx.location_inclusions[location_type] == LocationInclusion.option_disabled:
+ title += " (Nothing)"
+ elif self.ctx.location_inclusions[location_type] == LocationInclusion.option_resources:
+ title += " (Resources)"
+ else:
+ title += ""
+ return title
+
+def start_gui(context: SC2Context):
+ context.ui = SC2Manager(context)
+ context.ui_task = asyncio.create_task(context.ui.async_run(), name="UI")
+ import pkgutil
+ data = pkgutil.get_data(SC2World.__module__, "Starcraft2.kv").decode()
+ Builder.load_string(data)
diff --git a/worlds/sc2/ItemGroups.py b/worlds/sc2/ItemGroups.py
new file mode 100644
index 000000000000..a77fb920f64d
--- /dev/null
+++ b/worlds/sc2/ItemGroups.py
@@ -0,0 +1,100 @@
+import typing
+from . import Items, ItemNames
+from .MissionTables import campaign_mission_table, SC2Campaign, SC2Mission
+
+"""
+Item name groups, given to Archipelago and used in YAMLs and /received filtering.
+For non-developers the following will be useful:
+* Items with a bracket get groups named after the unbracketed part
+ * eg. "Advanced Healing AI (Medivac)" is accessible as "Advanced Healing AI"
+ * The exception to this are item names that would be ambiguous (eg. "Resource Efficiency")
+* Item flaggroups get unique groups as well as combined groups for numbered flaggroups
+ * eg. "Unit" contains all units, "Armory" contains "Armory 1" through "Armory 6"
+ * The best place to look these up is at the bottom of Items.py
+* Items that have a parent are grouped together
+ * eg. "Zergling Items" contains all items that have "Zergling" as a parent
+ * These groups do NOT contain the parent item
+ * This currently does not include items with multiple potential parents, like some LotV unit upgrades
+* All items are grouped by their race ("Terran", "Protoss", "Zerg", "Any")
+* Hand-crafted item groups can be found at the bottom of this file
+"""
+
+item_name_groups: typing.Dict[str, typing.List[str]] = {}
+
+# Groups for use in world logic
+item_name_groups["Missions"] = ["Beat " + mission.mission_name for mission in SC2Mission]
+item_name_groups["WoL Missions"] = ["Beat " + mission.mission_name for mission in campaign_mission_table[SC2Campaign.WOL]] + \
+ ["Beat " + mission.mission_name for mission in campaign_mission_table[SC2Campaign.PROPHECY]]
+
+# These item name groups should not show up in documentation
+unlisted_item_name_groups = {
+ "Missions", "WoL Missions"
+}
+
+# Some item names only differ in bracketed parts
+# These items are ambiguous for short-hand name groups
+bracketless_duplicates: typing.Set[str]
+# This is a list of names in ItemNames with bracketed parts removed, for internal use
+_shortened_names = [(name[:name.find(' (')] if '(' in name else name)
+ for name in [ItemNames.__dict__[name] for name in ItemNames.__dir__() if not name.startswith('_')]]
+# Remove the first instance of every short-name from the full item list
+bracketless_duplicates = set(_shortened_names)
+for name in bracketless_duplicates:
+ _shortened_names.remove(name)
+# The remaining short-names are the duplicates
+bracketless_duplicates = set(_shortened_names)
+del _shortened_names
+
+# All items get sorted into their data type
+for item, data in Items.get_full_item_list().items():
+ # Items get assigned to their flaggroup's type
+ item_name_groups.setdefault(data.type, []).append(item)
+ # Numbered flaggroups get sorted into an unnumbered group
+ # Currently supports numbers of one or two digits
+ if data.type[-2:].strip().isnumeric:
+ type_group = data.type[:-2].strip()
+ item_name_groups.setdefault(type_group, []).append(item)
+ # Flaggroups with numbers are unlisted
+ unlisted_item_name_groups.add(data.type)
+ # Items with a bracket get a short-hand name group for ease of use in YAMLs
+ if '(' in item:
+ short_name = item[:item.find(' (')]
+ # Ambiguous short-names are dropped
+ if short_name not in bracketless_duplicates:
+ item_name_groups[short_name] = [item]
+ # Short-name groups are unlisted
+ unlisted_item_name_groups.add(short_name)
+ # Items with a parent get assigned to their parent's group
+ if data.parent_item:
+ # The parent groups need a special name, otherwise they are ambiguous with the parent
+ parent_group = f"{data.parent_item} Items"
+ item_name_groups.setdefault(parent_group, []).append(item)
+ # Parent groups are unlisted
+ unlisted_item_name_groups.add(parent_group)
+ # All items get assigned to their race's group
+ race_group = data.race.name.capitalize()
+ item_name_groups.setdefault(race_group, []).append(item)
+
+
+# Hand-made groups
+item_name_groups["Aiur"] = [
+ ItemNames.ZEALOT, ItemNames.DRAGOON, ItemNames.SENTRY, ItemNames.AVENGER, ItemNames.HIGH_TEMPLAR,
+ ItemNames.IMMORTAL, ItemNames.REAVER,
+ ItemNames.PHOENIX, ItemNames.SCOUT, ItemNames.ARBITER, ItemNames.CARRIER,
+]
+item_name_groups["Nerazim"] = [
+ ItemNames.CENTURION, ItemNames.STALKER, ItemNames.DARK_TEMPLAR, ItemNames.SIGNIFIER, ItemNames.DARK_ARCHON,
+ ItemNames.ANNIHILATOR,
+ ItemNames.CORSAIR, ItemNames.ORACLE, ItemNames.VOID_RAY,
+]
+item_name_groups["Tal'Darim"] = [
+ ItemNames.SUPPLICANT, ItemNames.SLAYER, ItemNames.HAVOC, ItemNames.BLOOD_HUNTER, ItemNames.ASCENDANT,
+ ItemNames.VANGUARD, ItemNames.WRATHWALKER,
+ ItemNames.DESTROYER, ItemNames.MOTHERSHIP,
+ ItemNames.WARP_PRISM_PHASE_BLASTER,
+]
+item_name_groups["Purifier"] = [
+ ItemNames.SENTINEL, ItemNames.ADEPT, ItemNames.INSTIGATOR, ItemNames.ENERGIZER,
+ ItemNames.COLOSSUS, ItemNames.DISRUPTOR,
+ ItemNames.MIRAGE, ItemNames.TEMPEST,
+]
\ No newline at end of file
diff --git a/worlds/sc2/ItemNames.py b/worlds/sc2/ItemNames.py
new file mode 100644
index 000000000000..10c713910311
--- /dev/null
+++ b/worlds/sc2/ItemNames.py
@@ -0,0 +1,661 @@
+"""
+A complete collection of Starcraft 2 item names as strings.
+Users of this data may make some assumptions about the structure of a name:
+* The upgrade for a unit will end with the unit's name in parentheses
+* Weapon / armor upgrades may be grouped by a common prefix specified within this file
+"""
+
+# Terran Units
+MARINE = "Marine"
+MEDIC = "Medic"
+FIREBAT = "Firebat"
+MARAUDER = "Marauder"
+REAPER = "Reaper"
+HELLION = "Hellion"
+VULTURE = "Vulture"
+GOLIATH = "Goliath"
+DIAMONDBACK = "Diamondback"
+SIEGE_TANK = "Siege Tank"
+MEDIVAC = "Medivac"
+WRAITH = "Wraith"
+VIKING = "Viking"
+BANSHEE = "Banshee"
+BATTLECRUISER = "Battlecruiser"
+GHOST = "Ghost"
+SPECTRE = "Spectre"
+THOR = "Thor"
+RAVEN = "Raven"
+SCIENCE_VESSEL = "Science Vessel"
+PREDATOR = "Predator"
+HERCULES = "Hercules"
+# Extended units
+LIBERATOR = "Liberator"
+VALKYRIE = "Valkyrie"
+WIDOW_MINE = "Widow Mine"
+CYCLONE = "Cyclone"
+HERC = "HERC"
+WARHOUND = "Warhound"
+
+# Terran Buildings
+BUNKER = "Bunker"
+MISSILE_TURRET = "Missile Turret"
+SENSOR_TOWER = "Sensor Tower"
+PLANETARY_FORTRESS = "Planetary Fortress"
+PERDITION_TURRET = "Perdition Turret"
+HIVE_MIND_EMULATOR = "Hive Mind Emulator"
+PSI_DISRUPTER = "Psi Disrupter"
+
+# Terran Weapon / Armor Upgrades
+TERRAN_UPGRADE_PREFIX = "Progressive Terran"
+TERRAN_INFANTRY_UPGRADE_PREFIX = f"{TERRAN_UPGRADE_PREFIX} Infantry"
+TERRAN_VEHICLE_UPGRADE_PREFIX = f"{TERRAN_UPGRADE_PREFIX} Vehicle"
+TERRAN_SHIP_UPGRADE_PREFIX = f"{TERRAN_UPGRADE_PREFIX} Ship"
+
+PROGRESSIVE_TERRAN_INFANTRY_WEAPON = f"{TERRAN_INFANTRY_UPGRADE_PREFIX} Weapon"
+PROGRESSIVE_TERRAN_INFANTRY_ARMOR = f"{TERRAN_INFANTRY_UPGRADE_PREFIX} Armor"
+PROGRESSIVE_TERRAN_VEHICLE_WEAPON = f"{TERRAN_VEHICLE_UPGRADE_PREFIX} Weapon"
+PROGRESSIVE_TERRAN_VEHICLE_ARMOR = f"{TERRAN_VEHICLE_UPGRADE_PREFIX} Armor"
+PROGRESSIVE_TERRAN_SHIP_WEAPON = f"{TERRAN_SHIP_UPGRADE_PREFIX} Weapon"
+PROGRESSIVE_TERRAN_SHIP_ARMOR = f"{TERRAN_SHIP_UPGRADE_PREFIX} Armor"
+PROGRESSIVE_TERRAN_WEAPON_UPGRADE = f"{TERRAN_UPGRADE_PREFIX} Weapon Upgrade"
+PROGRESSIVE_TERRAN_ARMOR_UPGRADE = f"{TERRAN_UPGRADE_PREFIX} Armor Upgrade"
+PROGRESSIVE_TERRAN_INFANTRY_UPGRADE = f"{TERRAN_INFANTRY_UPGRADE_PREFIX} Upgrade"
+PROGRESSIVE_TERRAN_VEHICLE_UPGRADE = f"{TERRAN_VEHICLE_UPGRADE_PREFIX} Upgrade"
+PROGRESSIVE_TERRAN_SHIP_UPGRADE = f"{TERRAN_SHIP_UPGRADE_PREFIX} Upgrade"
+PROGRESSIVE_TERRAN_WEAPON_ARMOR_UPGRADE = f"{TERRAN_UPGRADE_PREFIX} Weapon/Armor Upgrade"
+
+# Mercenaries
+WAR_PIGS = "War Pigs"
+DEVIL_DOGS = "Devil Dogs"
+HAMMER_SECURITIES = "Hammer Securities"
+SPARTAN_COMPANY = "Spartan Company"
+SIEGE_BREAKERS = "Siege Breakers"
+HELS_ANGELS = "Hel's Angels"
+DUSK_WINGS = "Dusk Wings"
+JACKSONS_REVENGE = "Jackson's Revenge"
+SKIBIS_ANGELS = "Skibi's Angels"
+DEATH_HEADS = "Death Heads"
+WINGED_NIGHTMARES = "Winged Nightmares"
+MIDNIGHT_RIDERS = "Midnight Riders"
+BRYNHILDS = "Brynhilds"
+JOTUN = "Jotun"
+
+# Lab / Global
+ULTRA_CAPACITORS = "Ultra-Capacitors"
+VANADIUM_PLATING = "Vanadium Plating"
+ORBITAL_DEPOTS = "Orbital Depots"
+MICRO_FILTERING = "Micro-Filtering"
+AUTOMATED_REFINERY = "Automated Refinery"
+COMMAND_CENTER_REACTOR = "Command Center Reactor"
+TECH_REACTOR = "Tech Reactor"
+ORBITAL_STRIKE = "Orbital Strike"
+CELLULAR_REACTOR = "Cellular Reactor"
+PROGRESSIVE_REGENERATIVE_BIO_STEEL = "Progressive Regenerative Bio-Steel"
+PROGRESSIVE_FIRE_SUPPRESSION_SYSTEM = "Progressive Fire-Suppression System"
+PROGRESSIVE_ORBITAL_COMMAND = "Progressive Orbital Command"
+STRUCTURE_ARMOR = "Structure Armor"
+HI_SEC_AUTO_TRACKING = "Hi-Sec Auto Tracking"
+ADVANCED_OPTICS = "Advanced Optics"
+ROGUE_FORCES = "Rogue Forces"
+
+# Terran Unit Upgrades
+BANSHEE_HYPERFLIGHT_ROTORS = "Hyperflight Rotors (Banshee)"
+BANSHEE_INTERNAL_TECH_MODULE = "Internal Tech Module (Banshee)"
+BANSHEE_LASER_TARGETING_SYSTEM = "Laser Targeting System (Banshee)"
+BANSHEE_PROGRESSIVE_CROSS_SPECTRUM_DAMPENERS = "Progressive Cross-Spectrum Dampeners (Banshee)"
+BANSHEE_SHOCKWAVE_MISSILE_BATTERY = "Shockwave Missile Battery (Banshee)"
+BANSHEE_SHAPED_HULL = "Shaped Hull (Banshee)"
+BANSHEE_ADVANCED_TARGETING_OPTICS = "Advanced Targeting Optics (Banshee)"
+BANSHEE_DISTORTION_BLASTERS = "Distortion Blasters (Banshee)"
+BANSHEE_ROCKET_BARRAGE = "Rocket Barrage (Banshee)"
+BATTLECRUISER_ATX_LASER_BATTERY = "ATX Laser Battery (Battlecruiser)"
+BATTLECRUISER_CLOAK = "Cloak (Battlecruiser)"
+BATTLECRUISER_PROGRESSIVE_DEFENSIVE_MATRIX = "Progressive Defensive Matrix (Battlecruiser)"
+BATTLECRUISER_INTERNAL_TECH_MODULE = "Internal Tech Module (Battlecruiser)"
+BATTLECRUISER_PROGRESSIVE_MISSILE_PODS = "Progressive Missile Pods (Battlecruiser)"
+BATTLECRUISER_OPTIMIZED_LOGISTICS = "Optimized Logistics (Battlecruiser)"
+BATTLECRUISER_TACTICAL_JUMP = "Tactical Jump (Battlecruiser)"
+BATTLECRUISER_BEHEMOTH_PLATING = "Behemoth Plating (Battlecruiser)"
+BATTLECRUISER_COVERT_OPS_ENGINES = "Covert Ops Engines (Battlecruiser)"
+BUNKER_NEOSTEEL_BUNKER = "Neosteel Bunker (Bunker)"
+BUNKER_PROJECTILE_ACCELERATOR = "Projectile Accelerator (Bunker)"
+BUNKER_SHRIKE_TURRET = "Shrike Turret (Bunker)"
+BUNKER_FORTIFIED_BUNKER = "Fortified Bunker (Bunker)"
+CYCLONE_MAG_FIELD_ACCELERATORS = "Mag-Field Accelerators (Cyclone)"
+CYCLONE_MAG_FIELD_LAUNCHERS = "Mag-Field Launchers (Cyclone)"
+CYCLONE_RAPID_FIRE_LAUNCHERS = "Rapid Fire Launchers (Cyclone)"
+CYCLONE_TARGETING_OPTICS = "Targeting Optics (Cyclone)"
+CYCLONE_RESOURCE_EFFICIENCY = "Resource Efficiency (Cyclone)"
+CYCLONE_INTERNAL_TECH_MODULE = "Internal Tech Module (Cyclone)"
+DIAMONDBACK_BURST_CAPACITORS = "Burst Capacitors (Diamondback)"
+DIAMONDBACK_HYPERFLUXOR = "Hyperfluxor (Diamondback)"
+DIAMONDBACK_RESOURCE_EFFICIENCY = "Resource Efficiency (Diamondback)"
+DIAMONDBACK_SHAPED_HULL = "Shaped Hull (Diamondback)"
+DIAMONDBACK_PROGRESSIVE_TRI_LITHIUM_POWER_CELL = "Progressive Tri-Lithium Power Cell (Diamondback)"
+DIAMONDBACK_ION_THRUSTERS = "Ion Thrusters (Diamondback)"
+FIREBAT_INCINERATOR_GAUNTLETS = "Incinerator Gauntlets (Firebat)"
+FIREBAT_JUGGERNAUT_PLATING = "Juggernaut Plating (Firebat)"
+FIREBAT_RESOURCE_EFFICIENCY = "Resource Efficiency (Firebat)"
+FIREBAT_PROGRESSIVE_STIMPACK = "Progressive Stimpack (Firebat)"
+FIREBAT_INFERNAL_PRE_IGNITER = "Infernal Pre-Igniter (Firebat)"
+FIREBAT_KINETIC_FOAM = "Kinetic Foam (Firebat)"
+FIREBAT_NANO_PROJECTORS = "Nano Projectors (Firebat)"
+GHOST_CRIUS_SUIT = "Crius Suit (Ghost)"
+GHOST_EMP_ROUNDS = "EMP Rounds (Ghost)"
+GHOST_LOCKDOWN = "Lockdown (Ghost)"
+GHOST_OCULAR_IMPLANTS = "Ocular Implants (Ghost)"
+GHOST_RESOURCE_EFFICIENCY = "Resource Efficiency (Ghost)"
+GOLIATH_ARES_CLASS_TARGETING_SYSTEM = "Ares-Class Targeting System (Goliath)"
+GOLIATH_JUMP_JETS = "Jump Jets (Goliath)"
+GOLIATH_MULTI_LOCK_WEAPONS_SYSTEM = "Multi-Lock Weapons System (Goliath)"
+GOLIATH_OPTIMIZED_LOGISTICS = "Optimized Logistics (Goliath)"
+GOLIATH_SHAPED_HULL = "Shaped Hull (Goliath)"
+GOLIATH_RESOURCE_EFFICIENCY = "Resource Efficiency (Goliath)"
+GOLIATH_INTERNAL_TECH_MODULE = "Internal Tech Module (Goliath)"
+HELLION_HELLBAT_ASPECT = "Hellbat Aspect (Hellion)"
+HELLION_JUMP_JETS = "Jump Jets (Hellion)"
+HELLION_OPTIMIZED_LOGISTICS = "Optimized Logistics (Hellion)"
+HELLION_PROGRESSIVE_STIMPACK = "Progressive Stimpack (Hellion)"
+HELLION_SMART_SERVOS = "Smart Servos (Hellion)"
+HELLION_THERMITE_FILAMENTS = "Thermite Filaments (Hellion)"
+HELLION_TWIN_LINKED_FLAMETHROWER = "Twin-Linked Flamethrower (Hellion)"
+HELLION_INFERNAL_PLATING = "Infernal Plating (Hellion)"
+HERC_JUGGERNAUT_PLATING = "Juggernaut Plating (HERC)"
+HERC_KINETIC_FOAM = "Kinetic Foam (HERC)"
+HERC_RESOURCE_EFFICIENCY = "Resource Efficiency (HERC)"
+HERCULES_INTERNAL_FUSION_MODULE = "Internal Fusion Module (Hercules)"
+HERCULES_TACTICAL_JUMP = "Tactical Jump (Hercules)"
+LIBERATOR_ADVANCED_BALLISTICS = "Advanced Ballistics (Liberator)"
+LIBERATOR_CLOAK = "Cloak (Liberator)"
+LIBERATOR_LASER_TARGETING_SYSTEM = "Laser Targeting System (Liberator)"
+LIBERATOR_OPTIMIZED_LOGISTICS = "Optimized Logistics (Liberator)"
+LIBERATOR_RAID_ARTILLERY = "Raid Artillery (Liberator)"
+LIBERATOR_SMART_SERVOS = "Smart Servos (Liberator)"
+LIBERATOR_RESOURCE_EFFICIENCY = "Resource Efficiency (Liberator)"
+MARAUDER_CONCUSSIVE_SHELLS = "Concussive Shells (Marauder)"
+MARAUDER_INTERNAL_TECH_MODULE = "Internal Tech Module (Marauder)"
+MARAUDER_KINETIC_FOAM = "Kinetic Foam (Marauder)"
+MARAUDER_LASER_TARGETING_SYSTEM = "Laser Targeting System (Marauder)"
+MARAUDER_MAGRAIL_MUNITIONS = "Magrail Munitions (Marauder)"
+MARAUDER_PROGRESSIVE_STIMPACK = "Progressive Stimpack (Marauder)"
+MARAUDER_JUGGERNAUT_PLATING = "Juggernaut Plating (Marauder)"
+MARINE_COMBAT_SHIELD = "Combat Shield (Marine)"
+MARINE_LASER_TARGETING_SYSTEM = "Laser Targeting System (Marine)"
+MARINE_MAGRAIL_MUNITIONS = "Magrail Munitions (Marine)"
+MARINE_OPTIMIZED_LOGISTICS = "Optimized Logistics (Marine)"
+MARINE_PROGRESSIVE_STIMPACK = "Progressive Stimpack (Marine)"
+MEDIC_ADVANCED_MEDIC_FACILITIES = "Advanced Medic Facilities (Medic)"
+MEDIC_OPTICAL_FLARE = "Optical Flare (Medic)"
+MEDIC_RESOURCE_EFFICIENCY = "Resource Efficiency (Medic)"
+MEDIC_RESTORATION = "Restoration (Medic)"
+MEDIC_STABILIZER_MEDPACKS = "Stabilizer Medpacks (Medic)"
+MEDIC_ADAPTIVE_MEDPACKS = "Adaptive Medpacks (Medic)"
+MEDIC_NANO_PROJECTOR = "Nano Projector (Medic)"
+MEDIVAC_ADVANCED_HEALING_AI = "Advanced Healing AI (Medivac)"
+MEDIVAC_AFTERBURNERS = "Afterburners (Medivac)"
+MEDIVAC_EXPANDED_HULL = "Expanded Hull (Medivac)"
+MEDIVAC_RAPID_DEPLOYMENT_TUBE = "Rapid Deployment Tube (Medivac)"
+MEDIVAC_SCATTER_VEIL = "Scatter Veil (Medivac)"
+MEDIVAC_ADVANCED_CLOAKING_FIELD = "Advanced Cloaking Field (Medivac)"
+MISSILE_TURRET_HELLSTORM_BATTERIES = "Hellstorm Batteries (Missile Turret)"
+MISSILE_TURRET_TITANIUM_HOUSING = "Titanium Housing (Missile Turret)"
+PLANETARY_FORTRESS_PROGRESSIVE_AUGMENTED_THRUSTERS = "Progressive Augmented Thrusters (Planetary Fortress)"
+PLANETARY_FORTRESS_ADVANCED_TARGETING = "Advanced Targeting (Planetary Fortress)"
+PREDATOR_RESOURCE_EFFICIENCY = "Resource Efficiency (Predator)"
+PREDATOR_CLOAK = "Cloak (Predator)"
+PREDATOR_CHARGE = "Charge (Predator)"
+PREDATOR_PREDATOR_S_FURY = "Predator's Fury (Predator)"
+RAVEN_ANTI_ARMOR_MISSILE = "Anti-Armor Missile (Raven)"
+RAVEN_BIO_MECHANICAL_REPAIR_DRONE = "Bio Mechanical Repair Drone (Raven)"
+RAVEN_HUNTER_SEEKER_WEAPON = "Hunter-Seeker Weapon (Raven)"
+RAVEN_INTERFERENCE_MATRIX = "Interference Matrix (Raven)"
+RAVEN_INTERNAL_TECH_MODULE = "Internal Tech Module (Raven)"
+RAVEN_RAILGUN_TURRET = "Railgun Turret (Raven)"
+RAVEN_SPIDER_MINES = "Spider Mines (Raven)"
+RAVEN_RESOURCE_EFFICIENCY = "Resource Efficiency (Raven)"
+RAVEN_DURABLE_MATERIALS = "Durable Materials (Raven)"
+REAPER_ADVANCED_CLOAKING_FIELD = "Advanced Cloaking Field (Reaper)"
+REAPER_COMBAT_DRUGS = "Combat Drugs (Reaper)"
+REAPER_G4_CLUSTERBOMB = "G-4 Clusterbomb (Reaper)"
+REAPER_LASER_TARGETING_SYSTEM = "Laser Targeting System (Reaper)"
+REAPER_PROGRESSIVE_STIMPACK = "Progressive Stimpack (Reaper)"
+REAPER_SPIDER_MINES = "Spider Mines (Reaper)"
+REAPER_U238_ROUNDS = "U-238 Rounds (Reaper)"
+REAPER_JET_PACK_OVERDRIVE = "Jet Pack Overdrive (Reaper)"
+SCIENCE_VESSEL_DEFENSIVE_MATRIX = "Defensive Matrix (Science Vessel)"
+SCIENCE_VESSEL_EMP_SHOCKWAVE = "EMP Shockwave (Science Vessel)"
+SCIENCE_VESSEL_IMPROVED_NANO_REPAIR = "Improved Nano-Repair (Science Vessel)"
+SCIENCE_VESSEL_ADVANCED_AI_SYSTEMS = "Advanced AI Systems (Science Vessel)"
+SCV_ADVANCED_CONSTRUCTION = "Advanced Construction (SCV)"
+SCV_DUAL_FUSION_WELDERS = "Dual-Fusion Welders (SCV)"
+SCV_HOSTILE_ENVIRONMENT_ADAPTATION = "Hostile Environment Adaptation (SCV)"
+SIEGE_TANK_ADVANCED_SIEGE_TECH = "Advanced Siege Tech (Siege Tank)"
+SIEGE_TANK_GRADUATING_RANGE = "Graduating Range (Siege Tank)"
+SIEGE_TANK_INTERNAL_TECH_MODULE = "Internal Tech Module (Siege Tank)"
+SIEGE_TANK_JUMP_JETS = "Jump Jets (Siege Tank)"
+SIEGE_TANK_LASER_TARGETING_SYSTEM = "Laser Targeting System (Siege Tank)"
+SIEGE_TANK_MAELSTROM_ROUNDS = "Maelstrom Rounds (Siege Tank)"
+SIEGE_TANK_SHAPED_BLAST = "Shaped Blast (Siege Tank)"
+SIEGE_TANK_SMART_SERVOS = "Smart Servos (Siege Tank)"
+SIEGE_TANK_SPIDER_MINES = "Spider Mines (Siege Tank)"
+SIEGE_TANK_SHAPED_HULL = "Shaped Hull (Siege Tank)"
+SIEGE_TANK_RESOURCE_EFFICIENCY = "Resource Efficiency (Siege Tank)"
+SPECTRE_IMPALER_ROUNDS = "Impaler Rounds (Spectre)"
+SPECTRE_NYX_CLASS_CLOAKING_MODULE = "Nyx-Class Cloaking Module (Spectre)"
+SPECTRE_PSIONIC_LASH = "Psionic Lash (Spectre)"
+SPECTRE_RESOURCE_EFFICIENCY = "Resource Efficiency (Spectre)"
+SPIDER_MINE_CERBERUS_MINE = "Cerberus Mine (Spider Mine)"
+SPIDER_MINE_HIGH_EXPLOSIVE_MUNITION = "High Explosive Munition (Spider Mine)"
+THOR_330MM_BARRAGE_CANNON = "330mm Barrage Cannon (Thor)"
+THOR_PROGRESSIVE_IMMORTALITY_PROTOCOL = "Progressive Immortality Protocol (Thor)"
+THOR_PROGRESSIVE_HIGH_IMPACT_PAYLOAD = "Progressive High Impact Payload (Thor)"
+THOR_BUTTON_WITH_A_SKULL_ON_IT = "Button With a Skull on It (Thor)"
+THOR_LASER_TARGETING_SYSTEM = "Laser Targeting System (Thor)"
+THOR_LARGE_SCALE_FIELD_CONSTRUCTION = "Large Scale Field Construction (Thor)"
+VALKYRIE_AFTERBURNERS = "Afterburners (Valkyrie)"
+VALKYRIE_FLECHETTE_MISSILES = "Flechette Missiles (Valkyrie)"
+VALKYRIE_ENHANCED_CLUSTER_LAUNCHERS = "Enhanced Cluster Launchers (Valkyrie)"
+VALKYRIE_SHAPED_HULL = "Shaped Hull (Valkyrie)"
+VALKYRIE_LAUNCHING_VECTOR_COMPENSATOR = "Launching Vector Compensator (Valkyrie)"
+VALKYRIE_RESOURCE_EFFICIENCY = "Resource Efficiency (Valkyrie)"
+VIKING_ANTI_MECHANICAL_MUNITION = "Anti-Mechanical Munition (Viking)"
+VIKING_PHOBOS_CLASS_WEAPONS_SYSTEM = "Phobos-Class Weapons System (Viking)"
+VIKING_RIPWAVE_MISSILES = "Ripwave Missiles (Viking)"
+VIKING_SMART_SERVOS = "Smart Servos (Viking)"
+VIKING_SHREDDER_ROUNDS = "Shredder Rounds (Viking)"
+VIKING_WILD_MISSILES = "W.I.L.D. Missiles (Viking)"
+VULTURE_AUTO_LAUNCHERS = "Auto Launchers (Vulture)"
+VULTURE_ION_THRUSTERS = "Ion Thrusters (Vulture)"
+VULTURE_PROGRESSIVE_REPLENISHABLE_MAGAZINE = "Progressive Replenishable Magazine (Vulture)"
+VULTURE_AUTO_REPAIR = "Auto-Repair (Vulture)"
+WARHOUND_RESOURCE_EFFICIENCY = "Resource Efficiency (Warhound)"
+WARHOUND_REINFORCED_PLATING = "Reinforced Plating (Warhound)"
+WIDOW_MINE_BLACK_MARKET_LAUNCHERS = "Black Market Launchers (Widow Mine)"
+WIDOW_MINE_CONCEALMENT = "Concealment (Widow Mine)"
+WIDOW_MINE_DRILLING_CLAWS = "Drilling Claws (Widow Mine)"
+WIDOW_MINE_EXECUTIONER_MISSILES = "Executioner Missiles (Widow Mine)"
+WRAITH_ADVANCED_LASER_TECHNOLOGY = "Advanced Laser Technology (Wraith)"
+WRAITH_DISPLACEMENT_FIELD = "Displacement Field (Wraith)"
+WRAITH_PROGRESSIVE_TOMAHAWK_POWER_CELLS = "Progressive Tomahawk Power Cells (Wraith)"
+WRAITH_TRIGGER_OVERRIDE = "Trigger Override (Wraith)"
+WRAITH_INTERNAL_TECH_MODULE = "Internal Tech Module (Wraith)"
+WRAITH_RESOURCE_EFFICIENCY = "Resource Efficiency (Wraith)"
+
+# Nova
+NOVA_GHOST_VISOR = "Ghost Visor (Nova Equipment)"
+NOVA_RANGEFINDER_OCULUS = "Rangefinder Oculus (Nova Equipment)"
+NOVA_DOMINATION = "Domination (Nova Ability)"
+NOVA_BLINK = "Blink (Nova Ability)"
+NOVA_PROGRESSIVE_STEALTH_SUIT_MODULE = "Progressive Stealth Suit Module (Nova Suit Module)"
+NOVA_ENERGY_SUIT_MODULE = "Energy Suit Module (Nova Suit Module)"
+NOVA_ARMORED_SUIT_MODULE = "Armored Suit Module (Nova Suit Module)"
+NOVA_JUMP_SUIT_MODULE = "Jump Suit Module (Nova Suit Module)"
+NOVA_C20A_CANISTER_RIFLE = "C20A Canister Rifle (Nova Weapon)"
+NOVA_HELLFIRE_SHOTGUN = "Hellfire Shotgun (Nova Weapon)"
+NOVA_PLASMA_RIFLE = "Plasma Rifle (Nova Weapon)"
+NOVA_MONOMOLECULAR_BLADE = "Monomolecular Blade (Nova Weapon)"
+NOVA_BLAZEFIRE_GUNBLADE = "Blazefire Gunblade (Nova Weapon)"
+NOVA_STIM_INFUSION = "Stim Infusion (Nova Gadget)"
+NOVA_PULSE_GRENADES = "Pulse Grenades (Nova Gadget)"
+NOVA_FLASHBANG_GRENADES = "Flashbang Grenades (Nova Gadget)"
+NOVA_IONIC_FORCE_FIELD = "Ionic Force Field (Nova Gadget)"
+NOVA_HOLO_DECOY = "Holo Decoy (Nova Gadget)"
+NOVA_NUKE = "Tac Nuke Strike (Nova Ability)"
+
+# Zerg Units
+ZERGLING = "Zergling"
+SWARM_QUEEN = "Swarm Queen"
+ROACH = "Roach"
+HYDRALISK = "Hydralisk"
+ABERRATION = "Aberration"
+MUTALISK = "Mutalisk"
+SWARM_HOST = "Swarm Host"
+INFESTOR = "Infestor"
+ULTRALISK = "Ultralisk"
+CORRUPTOR = "Corruptor"
+SCOURGE = "Scourge"
+BROOD_QUEEN = "Brood Queen"
+DEFILER = "Defiler"
+
+# Zerg Buildings
+SPORE_CRAWLER = "Spore Crawler"
+SPINE_CRAWLER = "Spine Crawler"
+
+# Zerg Weapon / Armor Upgrades
+ZERG_UPGRADE_PREFIX = "Progressive Zerg"
+ZERG_FLYER_UPGRADE_PREFIX = f"{ZERG_UPGRADE_PREFIX} Flyer"
+
+PROGRESSIVE_ZERG_MELEE_ATTACK = f"{ZERG_UPGRADE_PREFIX} Melee Attack"
+PROGRESSIVE_ZERG_MISSILE_ATTACK = f"{ZERG_UPGRADE_PREFIX} Missile Attack"
+PROGRESSIVE_ZERG_GROUND_CARAPACE = f"{ZERG_UPGRADE_PREFIX} Ground Carapace"
+PROGRESSIVE_ZERG_FLYER_ATTACK = f"{ZERG_FLYER_UPGRADE_PREFIX} Attack"
+PROGRESSIVE_ZERG_FLYER_CARAPACE = f"{ZERG_FLYER_UPGRADE_PREFIX} Carapace"
+PROGRESSIVE_ZERG_WEAPON_UPGRADE = f"{ZERG_UPGRADE_PREFIX} Weapon Upgrade"
+PROGRESSIVE_ZERG_ARMOR_UPGRADE = f"{ZERG_UPGRADE_PREFIX} Armor Upgrade"
+PROGRESSIVE_ZERG_GROUND_UPGRADE = f"{ZERG_UPGRADE_PREFIX} Ground Upgrade"
+PROGRESSIVE_ZERG_FLYER_UPGRADE = f"{ZERG_FLYER_UPGRADE_PREFIX} Upgrade"
+PROGRESSIVE_ZERG_WEAPON_ARMOR_UPGRADE = f"{ZERG_UPGRADE_PREFIX} Weapon/Armor Upgrade"
+
+# Zerg Unit Upgrades
+ZERGLING_HARDENED_CARAPACE = "Hardened Carapace (Zergling)"
+ZERGLING_ADRENAL_OVERLOAD = "Adrenal Overload (Zergling)"
+ZERGLING_METABOLIC_BOOST = "Metabolic Boost (Zergling)"
+ZERGLING_SHREDDING_CLAWS = "Shredding Claws (Zergling)"
+ROACH_HYDRIODIC_BILE = "Hydriodic Bile (Roach)"
+ROACH_ADAPTIVE_PLATING = "Adaptive Plating (Roach)"
+ROACH_TUNNELING_CLAWS = "Tunneling Claws (Roach)"
+ROACH_GLIAL_RECONSTITUTION = "Glial Reconstitution (Roach)"
+ROACH_ORGANIC_CARAPACE = "Organic Carapace (Roach)"
+HYDRALISK_FRENZY = "Frenzy (Hydralisk)"
+HYDRALISK_ANCILLARY_CARAPACE = "Ancillary Carapace (Hydralisk)"
+HYDRALISK_GROOVED_SPINES = "Grooved Spines (Hydralisk)"
+HYDRALISK_MUSCULAR_AUGMENTS = "Muscular Augments (Hydralisk)"
+HYDRALISK_RESOURCE_EFFICIENCY = "Resource Efficiency (Hydralisk)"
+BANELING_CORROSIVE_ACID = "Corrosive Acid (Baneling)"
+BANELING_RUPTURE = "Rupture (Baneling)"
+BANELING_REGENERATIVE_ACID = "Regenerative Acid (Baneling)"
+BANELING_CENTRIFUGAL_HOOKS = "Centrifugal Hooks (Baneling)"
+BANELING_TUNNELING_JAWS = "Tunneling Jaws (Baneling)"
+BANELING_RAPID_METAMORPH = "Rapid Metamorph (Baneling)"
+MUTALISK_VICIOUS_GLAIVE = "Vicious Glaive (Mutalisk)"
+MUTALISK_RAPID_REGENERATION = "Rapid Regeneration (Mutalisk)"
+MUTALISK_SUNDERING_GLAIVE = "Sundering Glaive (Mutalisk)"
+MUTALISK_SEVERING_GLAIVE = "Severing Glaive (Mutalisk)"
+MUTALISK_AERODYNAMIC_GLAIVE_SHAPE = "Aerodynamic Glaive Shape (Mutalisk)"
+SWARM_HOST_BURROW = "Burrow (Swarm Host)"
+SWARM_HOST_RAPID_INCUBATION = "Rapid Incubation (Swarm Host)"
+SWARM_HOST_PRESSURIZED_GLANDS = "Pressurized Glands (Swarm Host)"
+SWARM_HOST_LOCUST_METABOLIC_BOOST = "Locust Metabolic Boost (Swarm Host)"
+SWARM_HOST_ENDURING_LOCUSTS = "Enduring Locusts (Swarm Host)"
+SWARM_HOST_ORGANIC_CARAPACE = "Organic Carapace (Swarm Host)"
+SWARM_HOST_RESOURCE_EFFICIENCY = "Resource Efficiency (Swarm Host)"
+ULTRALISK_BURROW_CHARGE = "Burrow Charge (Ultralisk)"
+ULTRALISK_TISSUE_ASSIMILATION = "Tissue Assimilation (Ultralisk)"
+ULTRALISK_MONARCH_BLADES = "Monarch Blades (Ultralisk)"
+ULTRALISK_ANABOLIC_SYNTHESIS = "Anabolic Synthesis (Ultralisk)"
+ULTRALISK_CHITINOUS_PLATING = "Chitinous Plating (Ultralisk)"
+ULTRALISK_ORGANIC_CARAPACE = "Organic Carapace (Ultralisk)"
+ULTRALISK_RESOURCE_EFFICIENCY = "Resource Efficiency (Ultralisk)"
+CORRUPTOR_CORRUPTION = "Corruption (Corruptor)"
+CORRUPTOR_CAUSTIC_SPRAY = "Caustic Spray (Corruptor)"
+SCOURGE_VIRULENT_SPORES = "Virulent Spores (Scourge)"
+SCOURGE_RESOURCE_EFFICIENCY = "Resource Efficiency (Scourge)"
+SCOURGE_SWARM_SCOURGE = "Swarm Scourge (Scourge)"
+DEVOURER_CORROSIVE_SPRAY = "Corrosive Spray (Devourer)"
+DEVOURER_GAPING_MAW = "Gaping Maw (Devourer)"
+DEVOURER_IMPROVED_OSMOSIS = "Improved Osmosis (Devourer)"
+DEVOURER_PRESCIENT_SPORES = "Prescient Spores (Devourer)"
+GUARDIAN_PROLONGED_DISPERSION = "Prolonged Dispersion (Guardian)"
+GUARDIAN_PRIMAL_ADAPTATION = "Primal Adaptation (Guardian)"
+GUARDIAN_SORONAN_ACID = "Soronan Acid (Guardian)"
+IMPALER_ADAPTIVE_TALONS = "Adaptive Talons (Impaler)"
+IMPALER_SECRETION_GLANDS = "Secretion Glands (Impaler)"
+IMPALER_HARDENED_TENTACLE_SPINES = "Hardened Tentacle Spines (Impaler)"
+LURKER_SEISMIC_SPINES = "Seismic Spines (Lurker)"
+LURKER_ADAPTED_SPINES = "Adapted Spines (Lurker)"
+RAVAGER_POTENT_BILE = "Potent Bile (Ravager)"
+RAVAGER_BLOATED_BILE_DUCTS = "Bloated Bile Ducts (Ravager)"
+RAVAGER_DEEP_TUNNEL = "Deep Tunnel (Ravager)"
+VIPER_PARASITIC_BOMB = "Parasitic Bomb (Viper)"
+VIPER_PARALYTIC_BARBS = "Paralytic Barbs (Viper)"
+VIPER_VIRULENT_MICROBES = "Virulent Microbes (Viper)"
+BROOD_LORD_POROUS_CARTILAGE = "Porous Cartilage (Brood Lord)"
+BROOD_LORD_EVOLVED_CARAPACE = "Evolved Carapace (Brood Lord)"
+BROOD_LORD_SPLITTER_MITOSIS = "Splitter Mitosis (Brood Lord)"
+BROOD_LORD_RESOURCE_EFFICIENCY = "Resource Efficiency (Brood Lord)"
+INFESTOR_INFESTED_TERRAN = "Infested Terran (Infestor)"
+INFESTOR_MICROBIAL_SHROUD = "Microbial Shroud (Infestor)"
+SWARM_QUEEN_SPAWN_LARVAE = "Spawn Larvae (Swarm Queen)"
+SWARM_QUEEN_DEEP_TUNNEL = "Deep Tunnel (Swarm Queen)"
+SWARM_QUEEN_ORGANIC_CARAPACE = "Organic Carapace (Swarm Queen)"
+SWARM_QUEEN_BIO_MECHANICAL_TRANSFUSION = "Bio-Mechanical Transfusion (Swarm Queen)"
+SWARM_QUEEN_RESOURCE_EFFICIENCY = "Resource Efficiency (Swarm Queen)"
+SWARM_QUEEN_INCUBATOR_CHAMBER = "Incubator Chamber (Swarm Queen)"
+BROOD_QUEEN_FUNGAL_GROWTH = "Fungal Growth (Brood Queen)"
+BROOD_QUEEN_ENSNARE = "Ensnare (Brood Queen)"
+BROOD_QUEEN_ENHANCED_MITOCHONDRIA = "Enhanced Mitochondria (Brood Queen)"
+
+# Zerg Strains
+ZERGLING_RAPTOR_STRAIN = "Raptor Strain (Zergling)"
+ZERGLING_SWARMLING_STRAIN = "Swarmling Strain (Zergling)"
+ROACH_VILE_STRAIN = "Vile Strain (Roach)"
+ROACH_CORPSER_STRAIN = "Corpser Strain (Roach)"
+BANELING_SPLITTER_STRAIN = "Splitter Strain (Baneling)"
+BANELING_HUNTER_STRAIN = "Hunter Strain (Baneling)"
+SWARM_HOST_CARRION_STRAIN = "Carrion Strain (Swarm Host)"
+SWARM_HOST_CREEPER_STRAIN = "Creeper Strain (Swarm Host)"
+ULTRALISK_NOXIOUS_STRAIN = "Noxious Strain (Ultralisk)"
+ULTRALISK_TORRASQUE_STRAIN = "Torrasque Strain (Ultralisk)"
+
+# Morphs
+ZERGLING_BANELING_ASPECT = "Baneling Aspect (Zergling)"
+HYDRALISK_IMPALER_ASPECT = "Impaler Aspect (Hydralisk)"
+HYDRALISK_LURKER_ASPECT = "Lurker Aspect (Hydralisk)"
+MUTALISK_CORRUPTOR_BROOD_LORD_ASPECT = "Brood Lord Aspect (Mutalisk/Corruptor)"
+MUTALISK_CORRUPTOR_VIPER_ASPECT = "Viper Aspect (Mutalisk/Corruptor)"
+MUTALISK_CORRUPTOR_GUARDIAN_ASPECT = "Guardian Aspect (Mutalisk/Corruptor)"
+MUTALISK_CORRUPTOR_DEVOURER_ASPECT = "Devourer Aspect (Mutalisk/Corruptor)"
+ROACH_RAVAGER_ASPECT = "Ravager Aspect (Roach)"
+
+# Zerg Mercs
+INFESTED_MEDICS = "Infested Medics"
+INFESTED_SIEGE_TANKS = "Infested Siege Tanks"
+INFESTED_BANSHEES = "Infested Banshees"
+
+# Kerrigan Upgrades
+KERRIGAN_KINETIC_BLAST = "Kinetic Blast (Kerrigan Tier 1)"
+KERRIGAN_HEROIC_FORTITUDE = "Heroic Fortitude (Kerrigan Tier 1)"
+KERRIGAN_LEAPING_STRIKE = "Leaping Strike (Kerrigan Tier 1)"
+KERRIGAN_CRUSHING_GRIP = "Crushing Grip (Kerrigan Tier 2)"
+KERRIGAN_CHAIN_REACTION = "Chain Reaction (Kerrigan Tier 2)"
+KERRIGAN_PSIONIC_SHIFT = "Psionic Shift (Kerrigan Tier 2)"
+KERRIGAN_WILD_MUTATION = "Wild Mutation (Kerrigan Tier 4)"
+KERRIGAN_SPAWN_BANELINGS = "Spawn Banelings (Kerrigan Tier 4)"
+KERRIGAN_MEND = "Mend (Kerrigan Tier 4)"
+KERRIGAN_INFEST_BROODLINGS = "Infest Broodlings (Kerrigan Tier 6)"
+KERRIGAN_FURY = "Fury (Kerrigan Tier 6)"
+KERRIGAN_ABILITY_EFFICIENCY = "Ability Efficiency (Kerrigan Tier 6)"
+KERRIGAN_APOCALYPSE = "Apocalypse (Kerrigan Tier 7)"
+KERRIGAN_SPAWN_LEVIATHAN = "Spawn Leviathan (Kerrigan Tier 7)"
+KERRIGAN_DROP_PODS = "Drop-Pods (Kerrigan Tier 7)"
+KERRIGAN_PRIMAL_FORM = "Primal Form (Kerrigan)"
+
+# Misc Upgrades
+KERRIGAN_ZERGLING_RECONSTITUTION = "Zergling Reconstitution (Kerrigan Tier 3)"
+KERRIGAN_IMPROVED_OVERLORDS = "Improved Overlords (Kerrigan Tier 3)"
+KERRIGAN_AUTOMATED_EXTRACTORS = "Automated Extractors (Kerrigan Tier 3)"
+KERRIGAN_TWIN_DRONES = "Twin Drones (Kerrigan Tier 5)"
+KERRIGAN_MALIGNANT_CREEP = "Malignant Creep (Kerrigan Tier 5)"
+KERRIGAN_VESPENE_EFFICIENCY = "Vespene Efficiency (Kerrigan Tier 5)"
+OVERLORD_VENTRAL_SACS = "Ventral Sacs (Overlord)"
+
+# Kerrigan Levels
+KERRIGAN_LEVELS_1 = "1 Kerrigan Level"
+KERRIGAN_LEVELS_2 = "2 Kerrigan Levels"
+KERRIGAN_LEVELS_3 = "3 Kerrigan Levels"
+KERRIGAN_LEVELS_4 = "4 Kerrigan Levels"
+KERRIGAN_LEVELS_5 = "5 Kerrigan Levels"
+KERRIGAN_LEVELS_6 = "6 Kerrigan Levels"
+KERRIGAN_LEVELS_7 = "7 Kerrigan Levels"
+KERRIGAN_LEVELS_8 = "8 Kerrigan Levels"
+KERRIGAN_LEVELS_9 = "9 Kerrigan Levels"
+KERRIGAN_LEVELS_10 = "10 Kerrigan Levels"
+KERRIGAN_LEVELS_14 = "14 Kerrigan Levels"
+KERRIGAN_LEVELS_35 = "35 Kerrigan Levels"
+KERRIGAN_LEVELS_70 = "70 Kerrigan Levels"
+
+# Protoss Units
+ZEALOT = "Zealot"
+STALKER = "Stalker"
+HIGH_TEMPLAR = "High Templar"
+DARK_TEMPLAR = "Dark Templar"
+IMMORTAL = "Immortal"
+COLOSSUS = "Colossus"
+PHOENIX = "Phoenix"
+VOID_RAY = "Void Ray"
+CARRIER = "Carrier"
+OBSERVER = "Observer"
+CENTURION = "Centurion"
+SENTINEL = "Sentinel"
+SUPPLICANT = "Supplicant"
+INSTIGATOR = "Instigator"
+SLAYER = "Slayer"
+SENTRY = "Sentry"
+ENERGIZER = "Energizer"
+HAVOC = "Havoc"
+SIGNIFIER = "Signifier"
+ASCENDANT = "Ascendant"
+AVENGER = "Avenger"
+BLOOD_HUNTER = "Blood Hunter"
+DRAGOON = "Dragoon"
+DARK_ARCHON = "Dark Archon"
+ADEPT = "Adept"
+WARP_PRISM = "Warp Prism"
+ANNIHILATOR = "Annihilator"
+VANGUARD = "Vanguard"
+WRATHWALKER = "Wrathwalker"
+REAVER = "Reaver"
+DISRUPTOR = "Disruptor"
+MIRAGE = "Mirage"
+CORSAIR = "Corsair"
+DESTROYER = "Destroyer"
+SCOUT = "Scout"
+TEMPEST = "Tempest"
+MOTHERSHIP = "Mothership"
+ARBITER = "Arbiter"
+ORACLE = "Oracle"
+
+# Upgrades
+PROTOSS_UPGRADE_PREFIX = "Progressive Protoss"
+PROTOSS_GROUND_UPGRADE_PREFIX = f"{PROTOSS_UPGRADE_PREFIX} Ground"
+PROTOSS_AIR_UPGRADE_PREFIX = f"{PROTOSS_UPGRADE_PREFIX} Air"
+PROGRESSIVE_PROTOSS_GROUND_WEAPON = f"{PROTOSS_GROUND_UPGRADE_PREFIX} Weapon"
+PROGRESSIVE_PROTOSS_GROUND_ARMOR = f"{PROTOSS_GROUND_UPGRADE_PREFIX} Armor"
+PROGRESSIVE_PROTOSS_SHIELDS = f"{PROTOSS_UPGRADE_PREFIX} Shields"
+PROGRESSIVE_PROTOSS_AIR_WEAPON = f"{PROTOSS_AIR_UPGRADE_PREFIX} Weapon"
+PROGRESSIVE_PROTOSS_AIR_ARMOR = f"{PROTOSS_AIR_UPGRADE_PREFIX} Armor"
+PROGRESSIVE_PROTOSS_WEAPON_UPGRADE = f"{PROTOSS_UPGRADE_PREFIX} Weapon Upgrade"
+PROGRESSIVE_PROTOSS_ARMOR_UPGRADE = f"{PROTOSS_UPGRADE_PREFIX} Armor Upgrade"
+PROGRESSIVE_PROTOSS_GROUND_UPGRADE = f"{PROTOSS_GROUND_UPGRADE_PREFIX} Upgrade"
+PROGRESSIVE_PROTOSS_AIR_UPGRADE = f"{PROTOSS_AIR_UPGRADE_PREFIX} Upgrade"
+PROGRESSIVE_PROTOSS_WEAPON_ARMOR_UPGRADE = f"{PROTOSS_UPGRADE_PREFIX} Weapon/Armor Upgrade"
+
+# Buildings
+PHOTON_CANNON = "Photon Cannon"
+KHAYDARIN_MONOLITH = "Khaydarin Monolith"
+SHIELD_BATTERY = "Shield Battery"
+
+# Unit Upgrades
+SUPPLICANT_BLOOD_SHIELD = "Blood Shield (Supplicant)"
+SUPPLICANT_SOUL_AUGMENTATION = "Soul Augmentation (Supplicant)"
+SUPPLICANT_SHIELD_REGENERATION = "Shield Regeneration (Supplicant)"
+ADEPT_SHOCKWAVE = "Shockwave (Adept)"
+ADEPT_RESONATING_GLAIVES = "Resonating Glaives (Adept)"
+ADEPT_PHASE_BULWARK = "Phase Bulwark (Adept)"
+STALKER_INSTIGATOR_SLAYER_DISINTEGRATING_PARTICLES = "Disintegrating Particles (Stalker/Instigator/Slayer)"
+STALKER_INSTIGATOR_SLAYER_PARTICLE_REFLECTION = "Particle Reflection (Stalker/Instigator/Slayer)"
+DRAGOON_HIGH_IMPACT_PHASE_DISRUPTORS = "High Impact Phase Disruptor (Dragoon)"
+DRAGOON_TRILLIC_COMPRESSION_SYSTEM = "Trillic Compression System (Dragoon)"
+DRAGOON_SINGULARITY_CHARGE = "Singularity Charge (Dragoon)"
+DRAGOON_ENHANCED_STRIDER_SERVOS = "Enhanced Strider Servos (Dragoon)"
+SCOUT_COMBAT_SENSOR_ARRAY = "Combat Sensor Array (Scout)"
+SCOUT_APIAL_SENSORS = "Apial Sensors (Scout)"
+SCOUT_GRAVITIC_THRUSTERS = "Gravitic Thrusters (Scout)"
+SCOUT_ADVANCED_PHOTON_BLASTERS = "Advanced Photon Blasters (Scout)"
+TEMPEST_TECTONIC_DESTABILIZERS = "Tectonic Destabilizers (Tempest)"
+TEMPEST_QUANTIC_REACTOR = "Quantic Reactor (Tempest)"
+TEMPEST_GRAVITY_SLING = "Gravity Sling (Tempest)"
+PHOENIX_MIRAGE_IONIC_WAVELENGTH_FLUX = "Ionic Wavelength Flux (Phoenix/Mirage)"
+PHOENIX_MIRAGE_ANION_PULSE_CRYSTALS = "Anion Pulse-Crystals (Phoenix/Mirage)"
+CORSAIR_STEALTH_DRIVE = "Stealth Drive (Corsair)"
+CORSAIR_ARGUS_JEWEL = "Argus Jewel (Corsair)"
+CORSAIR_SUSTAINING_DISRUPTION = "Sustaining Disruption (Corsair)"
+CORSAIR_NEUTRON_SHIELDS = "Neutron Shields (Corsair)"
+ORACLE_STEALTH_DRIVE = "Stealth Drive (Oracle)"
+ORACLE_STASIS_CALIBRATION = "Stasis Calibration (Oracle)"
+ORACLE_TEMPORAL_ACCELERATION_BEAM = "Temporal Acceleration Beam (Oracle)"
+ARBITER_CHRONOSTATIC_REINFORCEMENT = "Chronostatic Reinforcement (Arbiter)"
+ARBITER_KHAYDARIN_CORE = "Khaydarin Core (Arbiter)"
+ARBITER_SPACETIME_ANCHOR = "Spacetime Anchor (Arbiter)"
+ARBITER_RESOURCE_EFFICIENCY = "Resource Efficiency (Arbiter)"
+ARBITER_ENHANCED_CLOAK_FIELD = "Enhanced Cloak Field (Arbiter)"
+CARRIER_GRAVITON_CATAPULT = "Graviton Catapult (Carrier)"
+CARRIER_HULL_OF_PAST_GLORIES = "Hull of Past Glories (Carrier)"
+VOID_RAY_DESTROYER_FLUX_VANES = "Flux Vanes (Void Ray/Destroyer)"
+DESTROYER_REFORGED_BLOODSHARD_CORE = "Reforged Bloodshard Core (Destroyer)"
+WARP_PRISM_GRAVITIC_DRIVE = "Gravitic Drive (Warp Prism)"
+WARP_PRISM_PHASE_BLASTER = "Phase Blaster (Warp Prism)"
+WARP_PRISM_WAR_CONFIGURATION = "War Configuration (Warp Prism)"
+OBSERVER_GRAVITIC_BOOSTERS = "Gravitic Boosters (Observer)"
+OBSERVER_SENSOR_ARRAY = "Sensor Array (Observer)"
+REAVER_SCARAB_DAMAGE = "Scarab Damage (Reaver)"
+REAVER_SOLARITE_PAYLOAD = "Solarite Payload (Reaver)"
+REAVER_REAVER_CAPACITY = "Reaver Capacity (Reaver)"
+REAVER_RESOURCE_EFFICIENCY = "Resource Efficiency (Reaver)"
+VANGUARD_AGONY_LAUNCHERS = "Agony Launchers (Vanguard)"
+VANGUARD_MATTER_DISPERSION = "Matter Dispersion (Vanguard)"
+IMMORTAL_ANNIHILATOR_SINGULARITY_CHARGE = "Singularity Charge (Immortal/Annihilator)"
+IMMORTAL_ANNIHILATOR_ADVANCED_TARGETING_MECHANICS = "Advanced Targeting Mechanics (Immortal/Annihilator)"
+COLOSSUS_PACIFICATION_PROTOCOL = "Pacification Protocol (Colossus)"
+WRATHWALKER_RAPID_POWER_CYCLING = "Rapid Power Cycling (Wrathwalker)"
+WRATHWALKER_EYE_OF_WRATH = "Eye of Wrath (Wrathwalker)"
+DARK_TEMPLAR_AVENGER_BLOOD_HUNTER_SHROUD_OF_ADUN = "Shroud of Adun (Dark Templar/Avenger/Blood Hunter)"
+DARK_TEMPLAR_AVENGER_BLOOD_HUNTER_SHADOW_GUARD_TRAINING = "Shadow Guard Training (Dark Templar/Avenger/Blood Hunter)"
+DARK_TEMPLAR_AVENGER_BLOOD_HUNTER_BLINK = "Blink (Dark Templar/Avenger/Blood Hunter)"
+DARK_TEMPLAR_AVENGER_BLOOD_HUNTER_RESOURCE_EFFICIENCY = "Resource Efficiency (Dark Templar/Avenger/Blood Hunter)"
+DARK_TEMPLAR_DARK_ARCHON_MELD = "Dark Archon Meld (Dark Templar)"
+HIGH_TEMPLAR_SIGNIFIER_UNSHACKLED_PSIONIC_STORM = "Unshackled Psionic Storm (High Templar/Signifier)"
+HIGH_TEMPLAR_SIGNIFIER_HALLUCINATION = "Hallucination (High Templar/Signifier)"
+HIGH_TEMPLAR_SIGNIFIER_KHAYDARIN_AMULET = "Khaydarin Amulet (High Templar/Signifier)"
+ARCHON_HIGH_ARCHON = "High Archon (Archon)"
+DARK_ARCHON_FEEDBACK = "Feedback (Dark Archon)"
+DARK_ARCHON_MAELSTROM = "Maelstrom (Dark Archon)"
+DARK_ARCHON_ARGUS_TALISMAN = "Argus Talisman (Dark Archon)"
+ASCENDANT_POWER_OVERWHELMING = "Power Overwhelming (Ascendant)"
+ASCENDANT_CHAOTIC_ATTUNEMENT = "Chaotic Attunement (Ascendant)"
+ASCENDANT_BLOOD_AMULET = "Blood Amulet (Ascendant)"
+SENTRY_ENERGIZER_HAVOC_CLOAKING_MODULE = "Cloaking Module (Sentry/Energizer/Havoc)"
+SENTRY_ENERGIZER_HAVOC_SHIELD_BATTERY_RAPID_RECHARGING = "Rapid Recharging (Sentry/Energizer/Havoc/Shield Battery)"
+SENTRY_FORCE_FIELD = "Force Field (Sentry)"
+SENTRY_HALLUCINATION = "Hallucination (Sentry)"
+ENERGIZER_RECLAMATION = "Reclamation (Energizer)"
+ENERGIZER_FORGED_CHASSIS = "Forged Chassis (Energizer)"
+HAVOC_DETECT_WEAKNESS = "Detect Weakness (Havoc)"
+HAVOC_BLOODSHARD_RESONANCE = "Bloodshard Resonance (Havoc)"
+ZEALOT_SENTINEL_CENTURION_LEG_ENHANCEMENTS = "Leg Enhancements (Zealot/Sentinel/Centurion)"
+ZEALOT_SENTINEL_CENTURION_SHIELD_CAPACITY = "Shield Capacity (Zealot/Sentinel/Centurion)"
+
+# Spear Of Adun
+SOA_CHRONO_SURGE = "Chrono Surge (Spear of Adun Calldown)"
+SOA_PROGRESSIVE_PROXY_PYLON = "Progressive Proxy Pylon (Spear of Adun Calldown)"
+SOA_PYLON_OVERCHARGE = "Pylon Overcharge (Spear of Adun Calldown)"
+SOA_ORBITAL_STRIKE = "Orbital Strike (Spear of Adun Calldown)"
+SOA_TEMPORAL_FIELD = "Temporal Field (Spear of Adun Calldown)"
+SOA_SOLAR_LANCE = "Solar Lance (Spear of Adun Calldown)"
+SOA_MASS_RECALL = "Mass Recall (Spear of Adun Calldown)"
+SOA_SHIELD_OVERCHARGE = "Shield Overcharge (Spear of Adun Calldown)"
+SOA_DEPLOY_FENIX = "Deploy Fenix (Spear of Adun Calldown)"
+SOA_PURIFIER_BEAM = "Purifier Beam (Spear of Adun Calldown)"
+SOA_TIME_STOP = "Time Stop (Spear of Adun Calldown)"
+SOA_SOLAR_BOMBARDMENT = "Solar Bombardment (Spear of Adun Calldown)"
+
+# Generic upgrades
+MATRIX_OVERLOAD = "Matrix Overload"
+QUATRO = "Quatro"
+NEXUS_OVERCHARGE = "Nexus Overcharge"
+ORBITAL_ASSIMILATORS = "Orbital Assimilators"
+WARP_HARMONIZATION = "Warp Harmonization"
+GUARDIAN_SHELL = "Guardian Shell"
+RECONSTRUCTION_BEAM = "Reconstruction Beam (Spear of Adun Auto-Cast)"
+OVERWATCH = "Overwatch (Spear of Adun Auto-Cast)"
+SUPERIOR_WARP_GATES = "Superior Warp Gates"
+ENHANCED_TARGETING = "Enhanced Targeting"
+OPTIMIZED_ORDNANCE = "Optimized Ordnance"
+KHALAI_INGENUITY = "Khalai Ingenuity"
+AMPLIFIED_ASSIMILATORS = "Amplified Assimilators"
+
+# Filler items
+STARTING_MINERALS = "Additional Starting Minerals"
+STARTING_VESPENE = "Additional Starting Vespene"
+STARTING_SUPPLY = "Additional Starting Supply"
+NOTHING = "Nothing"
diff --git a/worlds/sc2/Items.py b/worlds/sc2/Items.py
new file mode 100644
index 000000000000..8277d0e7e13d
--- /dev/null
+++ b/worlds/sc2/Items.py
@@ -0,0 +1,2553 @@
+import inspect
+from pydoc import describe
+
+from BaseClasses import Item, ItemClassification, MultiWorld
+import typing
+
+from .Options import get_option_value, RequiredTactics
+from .MissionTables import SC2Mission, SC2Race, SC2Campaign, campaign_mission_table
+from . import ItemNames
+from worlds.AutoWorld import World
+
+
+class ItemData(typing.NamedTuple):
+ code: int
+ type: str
+ number: int # Important for bot commands to send the item into the game
+ race: SC2Race
+ classification: ItemClassification = ItemClassification.useful
+ quantity: int = 1
+ parent_item: typing.Optional[str] = None
+ origin: typing.Set[str] = {"wol"}
+ description: typing.Optional[str] = None
+ important_for_filtering: bool = False
+
+ def is_important_for_filtering(self):
+ return self.important_for_filtering \
+ or self.classification == ItemClassification.progression \
+ or self.classification == ItemClassification.progression_skip_balancing
+
+
+class StarcraftItem(Item):
+ game: str = "Starcraft 2"
+
+
+def get_full_item_list():
+ return item_table
+
+
+SC2WOL_ITEM_ID_OFFSET = 1000
+SC2HOTS_ITEM_ID_OFFSET = SC2WOL_ITEM_ID_OFFSET + 1000
+SC2LOTV_ITEM_ID_OFFSET = SC2HOTS_ITEM_ID_OFFSET + 1000
+
+# Descriptions
+WEAPON_ARMOR_UPGRADE_NOTE = inspect.cleandoc("""
+ Must be researched during the mission if the mission type isn't set to auto-unlock generic upgrades.
+""")
+LASER_TARGETING_SYSTEMS_DESCRIPTION = "Increases vision by 2 and weapon range by 1."
+STIMPACK_SMALL_COST = 10
+STIMPACK_SMALL_HEAL = 30
+STIMPACK_LARGE_COST = 20
+STIMPACK_LARGE_HEAL = 60
+STIMPACK_TEMPLATE = inspect.cleandoc("""
+ Level 1: Stimpack: Increases unit movement and attack speed for 15 seconds. Injures the unit for {} life.
+ Level 2: Super Stimpack: Instead of injuring the unit, heals the unit for {} life instead.
+""")
+STIMPACK_SMALL_DESCRIPTION = STIMPACK_TEMPLATE.format(STIMPACK_SMALL_COST, STIMPACK_SMALL_HEAL)
+STIMPACK_LARGE_DESCRIPTION = STIMPACK_TEMPLATE.format(STIMPACK_LARGE_COST, STIMPACK_LARGE_HEAL)
+SMART_SERVOS_DESCRIPTION = "Increases transformation speed between modes."
+INTERNAL_TECH_MODULE_DESCRIPTION_TEMPLATE = "{} can be trained from a {} without an attached Tech Lab."
+RESOURCE_EFFICIENCY_DESCRIPTION_TEMPLATE = "Reduces {} resource and supply cost."
+RESOURCE_EFFICIENCY_NO_SUPPLY_DESCRIPTION_TEMPLATE = "Reduces {} resource cost."
+CLOAK_DESCRIPTION_TEMPLATE = "Allows {} to use the Cloak ability."
+
+
+# The items are sorted by their IDs. The IDs shall be kept for compatibility with older games.
+item_table = {
+ # WoL
+ ItemNames.MARINE:
+ ItemData(0 + SC2WOL_ITEM_ID_OFFSET, "Unit", 0, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="General-purpose infantry."),
+ ItemNames.MEDIC:
+ ItemData(1 + SC2WOL_ITEM_ID_OFFSET, "Unit", 1, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Support trooper. Heals nearby biological units."),
+ ItemNames.FIREBAT:
+ ItemData(2 + SC2WOL_ITEM_ID_OFFSET, "Unit", 2, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Specialized anti-infantry attacker."),
+ ItemNames.MARAUDER:
+ ItemData(3 + SC2WOL_ITEM_ID_OFFSET, "Unit", 3, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Heavy assault infantry."),
+ ItemNames.REAPER:
+ ItemData(4 + SC2WOL_ITEM_ID_OFFSET, "Unit", 4, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Raider. Capable of jumping up and down cliffs. Throws explosive mines."),
+ ItemNames.HELLION:
+ ItemData(5 + SC2WOL_ITEM_ID_OFFSET, "Unit", 5, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Fast scout. Has a flame attack that damages all enemy units in its line of fire."),
+ ItemNames.VULTURE:
+ ItemData(6 + SC2WOL_ITEM_ID_OFFSET, "Unit", 6, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Fast skirmish unit. Can use the Spider Mine ability."),
+ ItemNames.GOLIATH:
+ ItemData(7 + SC2WOL_ITEM_ID_OFFSET, "Unit", 7, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Heavy-fire support unit."),
+ ItemNames.DIAMONDBACK:
+ ItemData(8 + SC2WOL_ITEM_ID_OFFSET, "Unit", 8, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Fast, high-damage hovertank. Rail Gun can fire while the Diamondback is moving."),
+ ItemNames.SIEGE_TANK:
+ ItemData(9 + SC2WOL_ITEM_ID_OFFSET, "Unit", 9, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Heavy tank. Long-range artillery in Siege Mode."),
+ ItemNames.MEDIVAC:
+ ItemData(10 + SC2WOL_ITEM_ID_OFFSET, "Unit", 10, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Air transport. Heals nearby biological units."),
+ ItemNames.WRAITH:
+ ItemData(11 + SC2WOL_ITEM_ID_OFFSET, "Unit", 11, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Highly mobile flying unit. Excellent at surgical strikes."),
+ ItemNames.VIKING:
+ ItemData(12 + SC2WOL_ITEM_ID_OFFSET, "Unit", 12, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description=inspect.cleandoc(
+ """
+ Durable support flyer. Loaded with strong anti-capital air missiles.
+ Can switch into Assault Mode to attack ground units.
+ """
+ )),
+ ItemNames.BANSHEE:
+ ItemData(13 + SC2WOL_ITEM_ID_OFFSET, "Unit", 13, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Tactical-strike aircraft."),
+ ItemNames.BATTLECRUISER:
+ ItemData(14 + SC2WOL_ITEM_ID_OFFSET, "Unit", 14, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Powerful warship."),
+ ItemNames.GHOST:
+ ItemData(15 + SC2WOL_ITEM_ID_OFFSET, "Unit", 15, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description=inspect.cleandoc(
+ """
+ Infiltration unit. Can use Snipe and Cloak abilities. Can also call down Tactical Nukes.
+ """
+ )),
+ ItemNames.SPECTRE:
+ ItemData(16 + SC2WOL_ITEM_ID_OFFSET, "Unit", 16, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description=inspect.cleandoc(
+ """
+ Infiltration unit. Can use Ultrasonic Pulse, Psionic Lash, and Cloak.
+ Can also call down Tactical Nukes.
+ """
+ )),
+ ItemNames.THOR:
+ ItemData(17 + SC2WOL_ITEM_ID_OFFSET, "Unit", 17, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Heavy assault mech."),
+ # EE units
+ ItemNames.LIBERATOR:
+ ItemData(18 + SC2WOL_ITEM_ID_OFFSET, "Unit", 18, SC2Race.TERRAN,
+ classification=ItemClassification.progression, origin={"nco", "ext"},
+ description=inspect.cleandoc(
+ """
+ Artillery fighter. Loaded with missiles that deal area damage to enemy air targets.
+ Can switch into Defender Mode to provide siege support.
+ """
+ )),
+ ItemNames.VALKYRIE:
+ ItemData(19 + SC2WOL_ITEM_ID_OFFSET, "Unit", 19, SC2Race.TERRAN,
+ classification=ItemClassification.progression, origin={"bw"},
+ description=inspect.cleandoc(
+ """
+ Advanced anti-aircraft fighter.
+ Able to use cluster missiles that deal area damage to air targets.
+ """
+ )),
+ ItemNames.WIDOW_MINE:
+ ItemData(20 + SC2WOL_ITEM_ID_OFFSET, "Unit", 20, SC2Race.TERRAN,
+ classification=ItemClassification.progression, origin={"ext"},
+ description=inspect.cleandoc(
+ """
+ Robotic mine. Launches missiles at nearby enemy units while burrowed.
+ Attacks deal splash damage in a small area around the target.
+ Widow Mine is revealed when Sentinel Missile is on cooldown.
+ """
+ )),
+ ItemNames.CYCLONE:
+ ItemData(21 + SC2WOL_ITEM_ID_OFFSET, "Unit", 21, SC2Race.TERRAN,
+ classification=ItemClassification.progression, origin={"ext"},
+ description=inspect.cleandoc(
+ """
+ Mobile assault vehicle. Can use Lock On to quickly fire while moving.
+ """
+ )),
+ ItemNames.HERC:
+ ItemData(22 + SC2WOL_ITEM_ID_OFFSET, "Unit", 26, SC2Race.TERRAN,
+ classification=ItemClassification.progression, origin={"ext"},
+ description=inspect.cleandoc(
+ """
+ Front-line infantry. Can use Grapple.
+ """
+ )),
+ ItemNames.WARHOUND:
+ ItemData(23 + SC2WOL_ITEM_ID_OFFSET, "Unit", 27, SC2Race.TERRAN,
+ classification=ItemClassification.progression, origin={"ext"},
+ description=inspect.cleandoc(
+ """
+ Anti-vehicle mech. Haywire missiles do bonus damage to mechanical units.
+ """
+ )),
+
+ # Some other items are moved to Upgrade group because of the way how the bot message is parsed
+ ItemNames.PROGRESSIVE_TERRAN_INFANTRY_WEAPON:
+ ItemData(100 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 0, SC2Race.TERRAN,
+ quantity=3,
+ description=inspect.cleandoc(
+ f"""
+ Increases damage of Terran infantry units.
+ {WEAPON_ARMOR_UPGRADE_NOTE}
+ """
+ )),
+ ItemNames.PROGRESSIVE_TERRAN_INFANTRY_ARMOR:
+ ItemData(102 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 2, SC2Race.TERRAN,
+ quantity=3,
+ description=inspect.cleandoc(
+ f"""
+ Increases armor of Terran infantry units.
+ {WEAPON_ARMOR_UPGRADE_NOTE}
+ """
+ )),
+ ItemNames.PROGRESSIVE_TERRAN_VEHICLE_WEAPON:
+ ItemData(103 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 4, SC2Race.TERRAN,
+ quantity=3,
+ description=inspect.cleandoc(
+ f"""
+ Increases damage of Terran vehicle units.
+ {WEAPON_ARMOR_UPGRADE_NOTE}
+ """
+ )),
+ ItemNames.PROGRESSIVE_TERRAN_VEHICLE_ARMOR:
+ ItemData(104 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 6, SC2Race.TERRAN,
+ quantity=3,
+ description=inspect.cleandoc(
+ f"""
+ Increases armor of Terran vehicle units.
+ {WEAPON_ARMOR_UPGRADE_NOTE}
+ """
+ )),
+ ItemNames.PROGRESSIVE_TERRAN_SHIP_WEAPON:
+ ItemData(105 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 8, SC2Race.TERRAN,
+ quantity=3,
+ description=inspect.cleandoc(
+ f"""
+ Increases damage of Terran starship units.
+ {WEAPON_ARMOR_UPGRADE_NOTE}
+ """
+ )),
+ ItemNames.PROGRESSIVE_TERRAN_SHIP_ARMOR:
+ ItemData(106 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 10, SC2Race.TERRAN,
+ quantity=3,
+ description=inspect.cleandoc(
+ f"""
+ Increases armor of Terran starship units.
+ {WEAPON_ARMOR_UPGRADE_NOTE}
+ """
+ )),
+ # Upgrade bundle 'number' values are used as indices to get affected 'number's
+ ItemNames.PROGRESSIVE_TERRAN_WEAPON_UPGRADE: ItemData(107 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 0, SC2Race.TERRAN, quantity=3),
+ ItemNames.PROGRESSIVE_TERRAN_ARMOR_UPGRADE: ItemData(108 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 1, SC2Race.TERRAN, quantity=3),
+ ItemNames.PROGRESSIVE_TERRAN_INFANTRY_UPGRADE: ItemData(109 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 2, SC2Race.TERRAN, quantity=3),
+ ItemNames.PROGRESSIVE_TERRAN_VEHICLE_UPGRADE: ItemData(110 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 3, SC2Race.TERRAN, quantity=3),
+ ItemNames.PROGRESSIVE_TERRAN_SHIP_UPGRADE: ItemData(111 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 4, SC2Race.TERRAN, quantity=3),
+ ItemNames.PROGRESSIVE_TERRAN_WEAPON_ARMOR_UPGRADE: ItemData(112 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 5, SC2Race.TERRAN, quantity=3),
+
+ # Unit and structure upgrades
+ ItemNames.BUNKER_PROJECTILE_ACCELERATOR:
+ ItemData(200 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 0, SC2Race.TERRAN,
+ parent_item=ItemNames.BUNKER,
+ description="Increases range of all units in the Bunker by 1."),
+ ItemNames.BUNKER_NEOSTEEL_BUNKER:
+ ItemData(201 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 1, SC2Race.TERRAN,
+ parent_item=ItemNames.BUNKER,
+ description="Increases the number of Bunker slots by 2."),
+ ItemNames.MISSILE_TURRET_TITANIUM_HOUSING:
+ ItemData(202 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 2, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.MISSILE_TURRET,
+ description="Increases Missile Turret life by 75."),
+ ItemNames.MISSILE_TURRET_HELLSTORM_BATTERIES:
+ ItemData(203 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 3, SC2Race.TERRAN,
+ parent_item=ItemNames.MISSILE_TURRET,
+ description="The Missile Turret unleashes an additional flurry of missiles with each attack."),
+ ItemNames.SCV_ADVANCED_CONSTRUCTION:
+ ItemData(204 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 4, SC2Race.TERRAN,
+ description="Multiple SCVs can construct a structure, reducing its construction time."),
+ ItemNames.SCV_DUAL_FUSION_WELDERS:
+ ItemData(205 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 5, SC2Race.TERRAN,
+ description="SCVs repair twice as fast."),
+ ItemNames.PROGRESSIVE_FIRE_SUPPRESSION_SYSTEM:
+ ItemData(206 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 24, SC2Race.TERRAN,
+ quantity=2,
+ description=inspect.cleandoc(
+ """
+ Level 1: While on low health, Terran structures are repaired to half health instead of burning down.
+ Level 2: Terran structures are repaired to full health instead of half health
+ """
+ )),
+ ItemNames.PROGRESSIVE_ORBITAL_COMMAND:
+ ItemData(207 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 26, SC2Race.TERRAN,
+ quantity=2, classification=ItemClassification.progression,
+ description=inspect.cleandoc(
+ """
+ Level 1: Allows Command Centers to use Scanner Sweep and Calldown: MULE abilities.
+ Level 2: Orbital Command abilities work even in Planetary Fortress mode.
+ """
+ )),
+ ItemNames.MARINE_PROGRESSIVE_STIMPACK:
+ ItemData(208 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 0, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.MARINE, quantity=2,
+ description=STIMPACK_SMALL_DESCRIPTION),
+ ItemNames.MARINE_COMBAT_SHIELD:
+ ItemData(209 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 9, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.MARINE,
+ description="Increases Marine life by 10."),
+ ItemNames.MEDIC_ADVANCED_MEDIC_FACILITIES:
+ ItemData(210 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 10, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.MEDIC,
+ description=INTERNAL_TECH_MODULE_DESCRIPTION_TEMPLATE.format("Medics", "Barracks")),
+ ItemNames.MEDIC_STABILIZER_MEDPACKS:
+ ItemData(211 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 11, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.MEDIC,
+ description="Increases Medic heal speed. Reduces the amount of energy required for each heal."),
+ ItemNames.FIREBAT_INCINERATOR_GAUNTLETS:
+ ItemData(212 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 12, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.FIREBAT,
+ description="Increases Firebat's damage radius by 40%"),
+ ItemNames.FIREBAT_JUGGERNAUT_PLATING:
+ ItemData(213 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 13, SC2Race.TERRAN,
+ parent_item=ItemNames.FIREBAT,
+ description="Increases Firebat's armor by 2."),
+ ItemNames.MARAUDER_CONCUSSIVE_SHELLS:
+ ItemData(214 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 14, SC2Race.TERRAN,
+ parent_item=ItemNames.MARAUDER,
+ description="Marauder attack temporarily slows all units in target area."),
+ ItemNames.MARAUDER_KINETIC_FOAM:
+ ItemData(215 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 15, SC2Race.TERRAN,
+ parent_item=ItemNames.MARAUDER,
+ description="Increases Marauder life by 25."),
+ ItemNames.REAPER_U238_ROUNDS:
+ ItemData(216 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 16, SC2Race.TERRAN,
+ parent_item=ItemNames.REAPER,
+ description=inspect.cleandoc(
+ """
+ Increases Reaper pistol attack range by 1.
+ Reaper pistols do additional 3 damage to Light Armor.
+ """
+ )),
+ ItemNames.REAPER_G4_CLUSTERBOMB:
+ ItemData(217 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 17, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.REAPER,
+ description="Timed explosive that does heavy area damage."),
+ ItemNames.CYCLONE_MAG_FIELD_ACCELERATORS:
+ ItemData(218 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 18, SC2Race.TERRAN,
+ parent_item=ItemNames.CYCLONE, origin={"ext"},
+ description="Increases Cyclone Lock On damage"),
+ ItemNames.CYCLONE_MAG_FIELD_LAUNCHERS:
+ ItemData(219 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 19, SC2Race.TERRAN,
+ parent_item=ItemNames.CYCLONE, origin={"ext"},
+ description="Increases Cyclone attack range by 2."),
+ ItemNames.MARINE_LASER_TARGETING_SYSTEM:
+ ItemData(220 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 8, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.MARINE, origin={"nco"},
+ description=LASER_TARGETING_SYSTEMS_DESCRIPTION),
+ ItemNames.MARINE_MAGRAIL_MUNITIONS:
+ ItemData(221 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 20, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.MARINE, origin={"nco"},
+ description="Deals 20 damage to target unit. Autocast on attack with a cooldown."),
+ ItemNames.MARINE_OPTIMIZED_LOGISTICS:
+ ItemData(222 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 21, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.MARINE, origin={"nco"},
+ description="Increases Marine training speed."),
+ ItemNames.MEDIC_RESTORATION:
+ ItemData(223 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 22, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.MEDIC, origin={"bw"},
+ description="Removes negative status effects from target allied unit."),
+ ItemNames.MEDIC_OPTICAL_FLARE:
+ ItemData(224 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 23, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.MEDIC, origin={"bw"},
+ description="Reduces vision range of target enemy unit. Disables detection."),
+ ItemNames.MEDIC_RESOURCE_EFFICIENCY:
+ ItemData(225 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 24, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.MEDIC, origin={"bw"},
+ description=RESOURCE_EFFICIENCY_DESCRIPTION_TEMPLATE.format("Medic")),
+ ItemNames.FIREBAT_PROGRESSIVE_STIMPACK:
+ ItemData(226 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 6, SC2Race.TERRAN,
+ parent_item=ItemNames.FIREBAT, quantity=2, origin={"bw"},
+ description=STIMPACK_LARGE_DESCRIPTION),
+ ItemNames.FIREBAT_RESOURCE_EFFICIENCY:
+ ItemData(227 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 25, SC2Race.TERRAN,
+ parent_item=ItemNames.FIREBAT, origin={"bw"},
+ description=RESOURCE_EFFICIENCY_DESCRIPTION_TEMPLATE.format("Firebat")),
+ ItemNames.MARAUDER_PROGRESSIVE_STIMPACK:
+ ItemData(228 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 8, SC2Race.TERRAN,
+ parent_item=ItemNames.MARAUDER, quantity=2, origin={"nco"},
+ description=STIMPACK_LARGE_DESCRIPTION),
+ ItemNames.MARAUDER_LASER_TARGETING_SYSTEM:
+ ItemData(229 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 26, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.MARAUDER, origin={"nco"},
+ description=LASER_TARGETING_SYSTEMS_DESCRIPTION),
+ ItemNames.MARAUDER_MAGRAIL_MUNITIONS:
+ ItemData(230 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 27, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.MARAUDER, origin={"nco"},
+ description="Deals 20 damage to target unit. Autocast on attack with a cooldown."),
+ ItemNames.MARAUDER_INTERNAL_TECH_MODULE:
+ ItemData(231 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 28, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.MARAUDER, origin={"nco"},
+ description=INTERNAL_TECH_MODULE_DESCRIPTION_TEMPLATE.format("Marauders", "Barracks")),
+ ItemNames.SCV_HOSTILE_ENVIRONMENT_ADAPTATION:
+ ItemData(232 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 29, SC2Race.TERRAN,
+ classification=ItemClassification.filler, origin={"bw"},
+ description="Increases SCV life by 15 and attack speed slightly."),
+ ItemNames.MEDIC_ADAPTIVE_MEDPACKS:
+ ItemData(233 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 0, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.MEDIC, origin={"ext"},
+ description="Allows Medics to heal mechanical and air units."),
+ ItemNames.MEDIC_NANO_PROJECTOR:
+ ItemData(234 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 1, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.MEDIC, origin={"ext"},
+ description="Increases Medic heal range by 2."),
+ ItemNames.FIREBAT_INFERNAL_PRE_IGNITER:
+ ItemData(235 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 2, SC2Race.TERRAN,
+ parent_item=ItemNames.FIREBAT, origin={"bw"},
+ description="Firebats do an additional 4 damage to Light Armor."),
+ ItemNames.FIREBAT_KINETIC_FOAM:
+ ItemData(236 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 3, SC2Race.TERRAN,
+ parent_item=ItemNames.FIREBAT, origin={"ext"},
+ description="Increases Firebat life by 100."),
+ ItemNames.FIREBAT_NANO_PROJECTORS:
+ ItemData(237 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 4, SC2Race.TERRAN,
+ parent_item=ItemNames.FIREBAT, origin={"ext"},
+ description="Increases Firebat attack range by 2"),
+ ItemNames.MARAUDER_JUGGERNAUT_PLATING:
+ ItemData(238 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 5, SC2Race.TERRAN,
+ parent_item=ItemNames.MARAUDER, origin={"ext"},
+ description="Increases Marauder's armor by 2."),
+ ItemNames.REAPER_JET_PACK_OVERDRIVE:
+ ItemData(239 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 6, SC2Race.TERRAN,
+ parent_item=ItemNames.REAPER, origin={"ext"},
+ description=inspect.cleandoc(
+ """
+ Allows the Reaper to fly for 10 seconds.
+ While flying, the Reaper can attack air units.
+ """
+ )),
+ ItemNames.HELLION_INFERNAL_PLATING:
+ ItemData(240 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 7, SC2Race.TERRAN,
+ parent_item=ItemNames.HELLION, origin={"ext"},
+ description="Increases Hellion and Hellbat armor by 2."),
+ ItemNames.VULTURE_AUTO_REPAIR:
+ ItemData(241 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 8, SC2Race.TERRAN,
+ parent_item=ItemNames.VULTURE, origin={"ext"},
+ description="Vultures regenerate life."),
+ ItemNames.GOLIATH_SHAPED_HULL:
+ ItemData(242 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 9, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.GOLIATH, origin={"nco", "ext"},
+ description="Increases Goliath life by 25."),
+ ItemNames.GOLIATH_RESOURCE_EFFICIENCY:
+ ItemData(243 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 10, SC2Race.TERRAN,
+ parent_item=ItemNames.GOLIATH, origin={"nco", "bw"},
+ description=RESOURCE_EFFICIENCY_DESCRIPTION_TEMPLATE.format("Goliath")),
+ ItemNames.GOLIATH_INTERNAL_TECH_MODULE:
+ ItemData(244 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 11, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.GOLIATH, origin={"nco", "bw"},
+ description=INTERNAL_TECH_MODULE_DESCRIPTION_TEMPLATE.format("Goliaths", "Factory")),
+ ItemNames.SIEGE_TANK_SHAPED_HULL:
+ ItemData(245 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 12, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.SIEGE_TANK, origin={"nco", "ext"},
+ description="Increases Siege Tank life by 25."),
+ ItemNames.SIEGE_TANK_RESOURCE_EFFICIENCY:
+ ItemData(246 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 13, SC2Race.TERRAN,
+ parent_item=ItemNames.SIEGE_TANK, origin={"bw"},
+ description=RESOURCE_EFFICIENCY_DESCRIPTION_TEMPLATE.format("Siege Tank")),
+ ItemNames.PREDATOR_CLOAK:
+ ItemData(247 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 14, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.PREDATOR, origin={"ext"},
+ description=CLOAK_DESCRIPTION_TEMPLATE.format("Predators")),
+ ItemNames.PREDATOR_CHARGE:
+ ItemData(248 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 15, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.PREDATOR, origin={"ext"},
+ description="Allows Predators to intercept enemy ground units."),
+ ItemNames.MEDIVAC_SCATTER_VEIL:
+ ItemData(249 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 16, SC2Race.TERRAN,
+ parent_item=ItemNames.MEDIVAC, origin={"ext"},
+ description="Medivacs get 100 shields."),
+ ItemNames.REAPER_PROGRESSIVE_STIMPACK:
+ ItemData(250 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 10, SC2Race.TERRAN,
+ parent_item=ItemNames.REAPER, quantity=2, origin={"nco"},
+ description=STIMPACK_SMALL_DESCRIPTION),
+ ItemNames.REAPER_LASER_TARGETING_SYSTEM:
+ ItemData(251 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 17, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.REAPER, origin={"nco"},
+ description=LASER_TARGETING_SYSTEMS_DESCRIPTION),
+ ItemNames.REAPER_ADVANCED_CLOAKING_FIELD:
+ ItemData(252 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 18, SC2Race.TERRAN,
+ parent_item=ItemNames.REAPER, origin={"nco"},
+ description="Reapers are permanently cloaked."),
+ ItemNames.REAPER_SPIDER_MINES:
+ ItemData(253 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 19, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.REAPER, origin={"nco"},
+ important_for_filtering=True,
+ description="Allows Reapers to lay Spider Mines. 3 charges per Reaper."),
+ ItemNames.REAPER_COMBAT_DRUGS:
+ ItemData(254 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 20, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.REAPER, origin={"ext"},
+ description="Reapers regenerate life while out of combat."),
+ ItemNames.HELLION_HELLBAT_ASPECT:
+ ItemData(255 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 21, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.HELLION, origin={"nco"},
+ description="Allows Hellions to transform into Hellbats."),
+ ItemNames.HELLION_SMART_SERVOS:
+ ItemData(256 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 22, SC2Race.TERRAN,
+ parent_item=ItemNames.HELLION, origin={"nco"},
+ description="Transforms faster between modes. Hellions can attack while moving."),
+ ItemNames.HELLION_OPTIMIZED_LOGISTICS:
+ ItemData(257 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 23, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.HELLION, origin={"nco"},
+ description="Increases Hellion training speed."),
+ ItemNames.HELLION_JUMP_JETS:
+ ItemData(258 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 24, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.HELLION, origin={"nco"},
+ description=inspect.cleandoc(
+ """
+ Increases movement speed in Hellion mode.
+ In Hellbat mode, launches the Hellbat toward enemy ground units and briefly stuns them.
+ """
+ )),
+ ItemNames.HELLION_PROGRESSIVE_STIMPACK:
+ ItemData(259 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 12, SC2Race.TERRAN,
+ parent_item=ItemNames.HELLION, quantity=2, origin={"nco"},
+ description=STIMPACK_LARGE_DESCRIPTION),
+ ItemNames.VULTURE_ION_THRUSTERS:
+ ItemData(260 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 25, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.VULTURE, origin={"bw"},
+ description="Increases Vulture movement speed."),
+ ItemNames.VULTURE_AUTO_LAUNCHERS:
+ ItemData(261 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 26, SC2Race.TERRAN,
+ parent_item=ItemNames.VULTURE, origin={"bw"},
+ description="Allows Vultures to attack while moving."),
+ ItemNames.SPIDER_MINE_HIGH_EXPLOSIVE_MUNITION:
+ ItemData(262 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 27, SC2Race.TERRAN,
+ origin={"bw"},
+ description="Increases Spider mine damage."),
+ ItemNames.GOLIATH_JUMP_JETS:
+ ItemData(263 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 28, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.GOLIATH, origin={"nco"},
+ description="Allows Goliaths to jump up and down cliffs."),
+ ItemNames.GOLIATH_OPTIMIZED_LOGISTICS:
+ ItemData(264 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 29, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.GOLIATH, origin={"nco"},
+ description="Increases Goliath training speed."),
+ ItemNames.DIAMONDBACK_HYPERFLUXOR:
+ ItemData(265 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 0, SC2Race.TERRAN,
+ parent_item=ItemNames.DIAMONDBACK, origin={"ext"},
+ description="Increases Diamondback attack speed."),
+ ItemNames.DIAMONDBACK_BURST_CAPACITORS:
+ ItemData(266 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 1, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.DIAMONDBACK, origin={"ext"},
+ description=inspect.cleandoc(
+ """
+ While not attacking, the Diamondback charges its weapon.
+ The next attack does 10 additional damage.
+ """
+ )),
+ ItemNames.DIAMONDBACK_RESOURCE_EFFICIENCY:
+ ItemData(267 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 2, SC2Race.TERRAN,
+ parent_item=ItemNames.DIAMONDBACK, origin={"ext"},
+ description=RESOURCE_EFFICIENCY_DESCRIPTION_TEMPLATE.format("Diamondback")),
+ ItemNames.SIEGE_TANK_JUMP_JETS:
+ ItemData(268 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 3, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.SIEGE_TANK, origin={"nco"},
+ description=inspect.cleandoc(
+ """
+ Repositions Siege Tank to a target location.
+ Can be used in either mode and to jump up and down cliffs.
+ """
+ )),
+ ItemNames.SIEGE_TANK_SPIDER_MINES:
+ ItemData(269 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 4, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.SIEGE_TANK, origin={"nco"},
+ important_for_filtering=True,
+ description=inspect.cleandoc(
+ """
+ Allows Siege Tanks to lay Spider Mines.
+ Lays 3 Spider Mines at once. 3 charges
+ """
+ )),
+ ItemNames.SIEGE_TANK_SMART_SERVOS:
+ ItemData(270 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 5, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.SIEGE_TANK, origin={"nco"},
+ description=SMART_SERVOS_DESCRIPTION),
+ ItemNames.SIEGE_TANK_GRADUATING_RANGE:
+ ItemData(271 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 6, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.SIEGE_TANK, origin={"ext"},
+ description=inspect.cleandoc(
+ """
+ Increases the Siege Tank's attack range by 1 every 3 seconds while in Siege Mode,
+ up to a maximum of 5 additional range.
+ """
+ )),
+ ItemNames.SIEGE_TANK_LASER_TARGETING_SYSTEM:
+ ItemData(272 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 7, SC2Race.TERRAN,
+ parent_item=ItemNames.SIEGE_TANK, origin={"nco"},
+ description=LASER_TARGETING_SYSTEMS_DESCRIPTION),
+ ItemNames.SIEGE_TANK_ADVANCED_SIEGE_TECH:
+ ItemData(273 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 8, SC2Race.TERRAN,
+ parent_item=ItemNames.SIEGE_TANK, origin={"ext"},
+ description="Siege Tanks gain +3 armor in Siege Mode."),
+ ItemNames.SIEGE_TANK_INTERNAL_TECH_MODULE:
+ ItemData(274 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 9, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.SIEGE_TANK, origin={"nco"},
+ description=INTERNAL_TECH_MODULE_DESCRIPTION_TEMPLATE.format("Siege Tanks", "Factory")),
+ ItemNames.PREDATOR_RESOURCE_EFFICIENCY:
+ ItemData(275 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 10, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.PREDATOR, origin={"ext"},
+ description="Decreases Predator resource and supply cost."),
+ ItemNames.MEDIVAC_EXPANDED_HULL:
+ ItemData(276 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 11, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.MEDIVAC, origin={"ext"},
+ description="Increases Medivac cargo space by 4."),
+ ItemNames.MEDIVAC_AFTERBURNERS:
+ ItemData(277 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 12, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.MEDIVAC, origin={"ext"},
+ description="Ability. Temporarily increases the Medivac's movement speed by 70%."),
+ ItemNames.WRAITH_ADVANCED_LASER_TECHNOLOGY:
+ ItemData(278 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 13, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.WRAITH, origin={"ext"},
+ description=inspect.cleandoc(
+ """
+ Burst Lasers do more damage and can hit both ground and air targets.
+ Replaces Gemini Missiles weapon.
+ """
+ )),
+ ItemNames.VIKING_SMART_SERVOS:
+ ItemData(279 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 14, SC2Race.TERRAN,
+ parent_item=ItemNames.VIKING, origin={"ext"},
+ description=SMART_SERVOS_DESCRIPTION),
+ ItemNames.VIKING_ANTI_MECHANICAL_MUNITION:
+ ItemData(280 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 15, SC2Race.TERRAN,
+ parent_item=ItemNames.VIKING, origin={"ext"},
+ description="Increases Viking damage to mechanical units while in Assault Mode."),
+ ItemNames.DIAMONDBACK_ION_THRUSTERS:
+ ItemData(281 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 21, SC2Race.TERRAN,
+ parent_item=ItemNames.DIAMONDBACK, origin={"ext"},
+ description="Increases Diamondback movement speed."),
+ ItemNames.WARHOUND_RESOURCE_EFFICIENCY:
+ ItemData(282 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 13, SC2Race.TERRAN,
+ parent_item=ItemNames.WARHOUND, origin={"ext"},
+ description=RESOURCE_EFFICIENCY_NO_SUPPLY_DESCRIPTION_TEMPLATE.format("Warhound")),
+ ItemNames.WARHOUND_REINFORCED_PLATING:
+ ItemData(283 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 14, SC2Race.TERRAN,
+ parent_item=ItemNames.WARHOUND, origin={"ext"},
+ description="Increases Warhound armor by 2."),
+ ItemNames.HERC_RESOURCE_EFFICIENCY:
+ ItemData(284 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 15, SC2Race.TERRAN,
+ parent_item=ItemNames.HERC, origin={"ext"},
+ description=RESOURCE_EFFICIENCY_DESCRIPTION_TEMPLATE.format("HERC")),
+ ItemNames.HERC_JUGGERNAUT_PLATING:
+ ItemData(285 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 16, SC2Race.TERRAN,
+ parent_item=ItemNames.HERC, origin={"ext"},
+ description="Increases HERC armor by 2."),
+ ItemNames.HERC_KINETIC_FOAM:
+ ItemData(286 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 17, SC2Race.TERRAN,
+ parent_item=ItemNames.HERC, origin={"ext"},
+ description="Increases HERC life by 50."),
+
+ ItemNames.HELLION_TWIN_LINKED_FLAMETHROWER:
+ ItemData(300 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 16, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.HELLION,
+ description="Doubles the width of the Hellion's flame attack."),
+ ItemNames.HELLION_THERMITE_FILAMENTS:
+ ItemData(301 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 17, SC2Race.TERRAN,
+ parent_item=ItemNames.HELLION,
+ description="Hellions do an additional 10 damage to Light Armor."),
+ ItemNames.SPIDER_MINE_CERBERUS_MINE:
+ ItemData(302 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 18, SC2Race.TERRAN,
+ classification=ItemClassification.filler,
+ description="Increases trigger and blast radius of Spider Mines."),
+ ItemNames.VULTURE_PROGRESSIVE_REPLENISHABLE_MAGAZINE:
+ ItemData(303 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 16, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.VULTURE, quantity=2,
+ description=inspect.cleandoc(
+ """
+ Level 1: Allows Vultures to replace used Spider Mines. Costs 15 minerals.
+ Level 2: Replacing used Spider Mines no longer costs minerals.
+ """
+ )),
+ ItemNames.GOLIATH_MULTI_LOCK_WEAPONS_SYSTEM:
+ ItemData(304 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 19, SC2Race.TERRAN,
+ parent_item=ItemNames.GOLIATH,
+ description="Goliaths can attack both ground and air targets simultaneously."),
+ ItemNames.GOLIATH_ARES_CLASS_TARGETING_SYSTEM:
+ ItemData(305 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 20, SC2Race.TERRAN,
+ parent_item=ItemNames.GOLIATH,
+ description="Increases Goliath ground attack range by 1 and air by 3."),
+ ItemNames.DIAMONDBACK_PROGRESSIVE_TRI_LITHIUM_POWER_CELL:
+ ItemData(306 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade 2", 4, SC2Race.TERRAN,
+ parent_item=ItemNames.DIAMONDBACK, quantity=2,
+ description=inspect.cleandoc(
+ """
+ Level 1: Tri-Lithium Power Cell: Increases Diamondback attack range by 1.
+ Level 2: Tungsten Spikes: Increases Diamondback attack range by 3.
+ """
+ )),
+ ItemNames.DIAMONDBACK_SHAPED_HULL:
+ ItemData(307 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 22, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.DIAMONDBACK,
+ description="Increases Diamondback life by 50."),
+ ItemNames.SIEGE_TANK_MAELSTROM_ROUNDS:
+ ItemData(308 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 23, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.SIEGE_TANK,
+ description="Siege Tanks do an additional 40 damage to the primary target in Siege Mode."),
+ ItemNames.SIEGE_TANK_SHAPED_BLAST:
+ ItemData(309 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 24, SC2Race.TERRAN,
+ parent_item=ItemNames.SIEGE_TANK,
+ description="Reduces splash damage to friendly targets while in Siege Mode by 75%."),
+ ItemNames.MEDIVAC_RAPID_DEPLOYMENT_TUBE:
+ ItemData(310 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 25, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.MEDIVAC,
+ description="Medivacs deploy loaded troops almost instantly."),
+ ItemNames.MEDIVAC_ADVANCED_HEALING_AI:
+ ItemData(311 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 26, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.MEDIVAC,
+ description="Medivacs can heal two targets at once."),
+ ItemNames.WRAITH_PROGRESSIVE_TOMAHAWK_POWER_CELLS:
+ ItemData(312 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 18, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.WRAITH, quantity=2,
+ description=inspect.cleandoc(
+ """
+ Level 1: Tomahawk Power Cells: Increases Wraith starting energy by 100.
+ Level 2: Unregistered Cloaking Module: Wraiths do not require energy to cloak and remain cloaked.
+ """
+ )),
+ ItemNames.WRAITH_DISPLACEMENT_FIELD:
+ ItemData(313 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 27, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.WRAITH,
+ description="Wraiths evade 20% of incoming attacks while cloaked."),
+ ItemNames.VIKING_RIPWAVE_MISSILES:
+ ItemData(314 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 28, SC2Race.TERRAN,
+ parent_item=ItemNames.VIKING,
+ description="Vikings do area damage while in Fighter Mode"),
+ ItemNames.VIKING_PHOBOS_CLASS_WEAPONS_SYSTEM:
+ ItemData(315 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 29, SC2Race.TERRAN,
+ parent_item=ItemNames.VIKING,
+ description="Increases Viking attack range by 1 in Assault mode and 2 in Fighter mode."),
+ ItemNames.BANSHEE_PROGRESSIVE_CROSS_SPECTRUM_DAMPENERS:
+ ItemData(316 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 2, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.BANSHEE, quantity=2,
+ description=inspect.cleandoc(
+ """
+ Level 1: Banshees can remain cloaked twice as long.
+ Level 2: Banshees do not require energy to cloak and remain cloaked.
+ """
+ )),
+ ItemNames.BANSHEE_SHOCKWAVE_MISSILE_BATTERY:
+ ItemData(317 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 0, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.BANSHEE,
+ description="Banshees do area damage in a straight line."),
+ ItemNames.BATTLECRUISER_PROGRESSIVE_MISSILE_PODS:
+ ItemData(318 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade 2", 2, SC2Race.TERRAN,
+ parent_item=ItemNames.BATTLECRUISER, quantity=2,
+ description="Spell. Missile Pods do damage to air targets in a target area."),
+ ItemNames.BATTLECRUISER_PROGRESSIVE_DEFENSIVE_MATRIX:
+ ItemData(319 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 20, SC2Race.TERRAN,
+ parent_item=ItemNames.BATTLECRUISER, quantity=2,
+ description=inspect.cleandoc(
+ """
+ Level 1: Spell. For 20 seconds the Battlecruiser gains a shield that can absorb up to 200 damage.
+ Level 2: Passive. Battlecruiser gets 200 shields.
+ """
+ )),
+ ItemNames.GHOST_OCULAR_IMPLANTS:
+ ItemData(320 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 2, SC2Race.TERRAN,
+ parent_item=ItemNames.GHOST,
+ description="Increases Ghost sight range by 3 and attack range by 2."),
+ ItemNames.GHOST_CRIUS_SUIT:
+ ItemData(321 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 3, SC2Race.TERRAN,
+ parent_item=ItemNames.GHOST,
+ description="Cloak no longer requires energy to activate or maintain."),
+ ItemNames.SPECTRE_PSIONIC_LASH:
+ ItemData(322 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 4, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.SPECTRE,
+ description="Spell. Deals 200 damage to a single target."),
+ ItemNames.SPECTRE_NYX_CLASS_CLOAKING_MODULE:
+ ItemData(323 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 5, SC2Race.TERRAN,
+ parent_item=ItemNames.SPECTRE,
+ description="Cloak no longer requires energy to activate or maintain."),
+ ItemNames.THOR_330MM_BARRAGE_CANNON:
+ ItemData(324 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 6, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.THOR,
+ description=inspect.cleandoc(
+ """
+ Improves 250mm Strike Cannons ability to deal area damage and stun units in a small area.
+ Can be also freely aimed on ground.
+ """
+ )),
+ ItemNames.THOR_PROGRESSIVE_IMMORTALITY_PROTOCOL:
+ ItemData(325 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 22, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.THOR, quantity=2,
+ description=inspect.cleandoc("""
+ Level 1: Allows destroyed Thors to be reconstructed on the field. Costs Vespene Gas.
+ Level 2: Thors are automatically reconstructed after falling for free.
+ """
+ )),
+ ItemNames.LIBERATOR_ADVANCED_BALLISTICS:
+ ItemData(326 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 7, SC2Race.TERRAN,
+ parent_item=ItemNames.LIBERATOR, origin={"ext"},
+ description="Increases Liberator range by 3 in Defender Mode."),
+ ItemNames.LIBERATOR_RAID_ARTILLERY:
+ ItemData(327 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 8, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.LIBERATOR, origin={"nco"},
+ description="Allows Liberators to attack structures while in Defender Mode."),
+ ItemNames.WIDOW_MINE_DRILLING_CLAWS:
+ ItemData(328 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 9, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.WIDOW_MINE, origin={"ext"},
+ description="Allows Widow Mines to burrow and unburrow faster."),
+ ItemNames.WIDOW_MINE_CONCEALMENT:
+ ItemData(329 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 10, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.WIDOW_MINE, origin={"ext"},
+ description="Burrowed Widow Mines are no longer revealed when the Sentinel Missile is on cooldown."),
+ ItemNames.MEDIVAC_ADVANCED_CLOAKING_FIELD:
+ ItemData(330 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 11, SC2Race.TERRAN,
+ parent_item=ItemNames.MEDIVAC, origin={"ext"},
+ description="Medivacs are permanently cloaked."),
+ ItemNames.WRAITH_TRIGGER_OVERRIDE:
+ ItemData(331 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 12, SC2Race.TERRAN,
+ parent_item=ItemNames.WRAITH, origin={"ext"},
+ description="Wraith attack speed increases by 10% with each attack, up to a maximum of 100%."),
+ ItemNames.WRAITH_INTERNAL_TECH_MODULE:
+ ItemData(332 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 13, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.WRAITH, origin={"bw"},
+ description=INTERNAL_TECH_MODULE_DESCRIPTION_TEMPLATE.format("Wraiths", "Starport")),
+ ItemNames.WRAITH_RESOURCE_EFFICIENCY:
+ ItemData(333 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 14, SC2Race.TERRAN,
+ parent_item=ItemNames.WRAITH, origin={"bw"},
+ description=RESOURCE_EFFICIENCY_NO_SUPPLY_DESCRIPTION_TEMPLATE.format("Wraith")),
+ ItemNames.VIKING_SHREDDER_ROUNDS:
+ ItemData(334 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 15, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.VIKING, origin={"ext"},
+ description="Attacks in Assault mode do line splash damage."),
+ ItemNames.VIKING_WILD_MISSILES:
+ ItemData(335 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 16, SC2Race.TERRAN,
+ parent_item=ItemNames.VIKING, origin={"ext"},
+ description="Launches 5 rockets at the target unit. Each rocket does 25 (40 vs armored) damage."),
+ ItemNames.BANSHEE_SHAPED_HULL:
+ ItemData(336 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 17, SC2Race.TERRAN,
+ parent_item=ItemNames.BANSHEE, origin={"ext"},
+ description="Increases Banshee life by 100."),
+ ItemNames.BANSHEE_ADVANCED_TARGETING_OPTICS:
+ ItemData(337 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 18, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.BANSHEE, origin={"ext"},
+ description="Increases Banshee attack range by 2 while cloaked."),
+ ItemNames.BANSHEE_DISTORTION_BLASTERS:
+ ItemData(338 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 19, SC2Race.TERRAN,
+ parent_item=ItemNames.BANSHEE, origin={"ext"},
+ description="Increases Banshee attack damage by 25% while cloaked."),
+ ItemNames.BANSHEE_ROCKET_BARRAGE:
+ ItemData(339 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 20, SC2Race.TERRAN,
+ parent_item=ItemNames.BANSHEE, origin={"ext"},
+ description="Deals 75 damage to enemy ground units in the target area."),
+ ItemNames.GHOST_RESOURCE_EFFICIENCY:
+ ItemData(340 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 21, SC2Race.TERRAN,
+ parent_item=ItemNames.GHOST, origin={"bw"},
+ description=RESOURCE_EFFICIENCY_DESCRIPTION_TEMPLATE.format("Ghost")),
+ ItemNames.SPECTRE_RESOURCE_EFFICIENCY:
+ ItemData(341 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 22, SC2Race.TERRAN,
+ parent_item=ItemNames.SPECTRE, origin={"ext"},
+ description=RESOURCE_EFFICIENCY_DESCRIPTION_TEMPLATE.format("Spectre")),
+ ItemNames.THOR_BUTTON_WITH_A_SKULL_ON_IT:
+ ItemData(342 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 23, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.THOR, origin={"ext"},
+ description="Allows Thors to launch nukes."),
+ ItemNames.THOR_LASER_TARGETING_SYSTEM:
+ ItemData(343 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 24, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.THOR, origin={"ext"},
+ description=LASER_TARGETING_SYSTEMS_DESCRIPTION),
+ ItemNames.THOR_LARGE_SCALE_FIELD_CONSTRUCTION:
+ ItemData(344 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 25, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.THOR, origin={"ext"},
+ description="Allows Thors to be built by SCVs like a structure."),
+ ItemNames.RAVEN_RESOURCE_EFFICIENCY:
+ ItemData(345 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 26, SC2Race.TERRAN,
+ parent_item=ItemNames.RAVEN, origin={"ext"},
+ description=RESOURCE_EFFICIENCY_NO_SUPPLY_DESCRIPTION_TEMPLATE.format("Raven")),
+ ItemNames.RAVEN_DURABLE_MATERIALS:
+ ItemData(346 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 27, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.RAVEN, origin={"ext"},
+ description="Extends timed life duration of Raven's summoned objects."),
+ ItemNames.SCIENCE_VESSEL_IMPROVED_NANO_REPAIR:
+ ItemData(347 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 28, SC2Race.TERRAN,
+ parent_item=ItemNames.SCIENCE_VESSEL, origin={"ext"},
+ description="Nano-Repair no longer requires energy to use."),
+ ItemNames.SCIENCE_VESSEL_ADVANCED_AI_SYSTEMS:
+ ItemData(348 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 29, SC2Race.TERRAN,
+ parent_item=ItemNames.SCIENCE_VESSEL, origin={"ext"},
+ description="Science Vessel can use Nano-Repair at two targets at once."),
+ ItemNames.CYCLONE_RESOURCE_EFFICIENCY:
+ ItemData(349 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 0, SC2Race.TERRAN,
+ parent_item=ItemNames.CYCLONE, origin={"ext"},
+ description=RESOURCE_EFFICIENCY_DESCRIPTION_TEMPLATE.format("Cyclone")),
+ ItemNames.BANSHEE_HYPERFLIGHT_ROTORS:
+ ItemData(350 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 1, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.BANSHEE, origin={"ext"},
+ description="Increases Banshee movement speed."),
+ ItemNames.BANSHEE_LASER_TARGETING_SYSTEM:
+ ItemData(351 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 2, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.BANSHEE, origin={"nco"},
+ description=LASER_TARGETING_SYSTEMS_DESCRIPTION),
+ ItemNames.BANSHEE_INTERNAL_TECH_MODULE:
+ ItemData(352 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 3, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.BANSHEE, origin={"nco"},
+ description=INTERNAL_TECH_MODULE_DESCRIPTION_TEMPLATE.format("Banshees", "Starport")),
+ ItemNames.BATTLECRUISER_TACTICAL_JUMP:
+ ItemData(353 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 4, SC2Race.TERRAN,
+ parent_item=ItemNames.BATTLECRUISER, origin={"nco", "ext"},
+ description=inspect.cleandoc(
+ """
+ Allows Battlecruisers to warp to a target location anywhere on the map.
+ """
+ )),
+ ItemNames.BATTLECRUISER_CLOAK:
+ ItemData(354 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 5, SC2Race.TERRAN,
+ parent_item=ItemNames.BATTLECRUISER, origin={"nco"},
+ description=CLOAK_DESCRIPTION_TEMPLATE.format("Battlecruisers")),
+ ItemNames.BATTLECRUISER_ATX_LASER_BATTERY:
+ ItemData(355 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 6, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.BATTLECRUISER, origin={"nco"},
+ description=inspect.cleandoc(
+ """
+ Battlecruisers can attack while moving,
+ do the same damage to both ground and air targets, and fire faster.
+ """
+ )),
+ ItemNames.BATTLECRUISER_OPTIMIZED_LOGISTICS:
+ ItemData(356 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 7, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.BATTLECRUISER, origin={"ext"},
+ description="Increases Battlecruiser training speed."),
+ ItemNames.BATTLECRUISER_INTERNAL_TECH_MODULE:
+ ItemData(357 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 8, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.BATTLECRUISER, origin={"nco"},
+ description=INTERNAL_TECH_MODULE_DESCRIPTION_TEMPLATE.format("Battlecruisers", "Starport")),
+ ItemNames.GHOST_EMP_ROUNDS:
+ ItemData(358 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 9, SC2Race.TERRAN,
+ parent_item=ItemNames.GHOST, origin={"ext"},
+ description=inspect.cleandoc(
+ """
+ Spell. Does 100 damage to shields and drains all energy from units in the targeted area.
+ Cloaked units hit by EMP are revealed for a short time.
+ """
+ )),
+ ItemNames.GHOST_LOCKDOWN:
+ ItemData(359 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 10, SC2Race.TERRAN,
+ parent_item=ItemNames.GHOST, origin={"bw"},
+ description="Spell. Stuns a target mechanical unit for a long time."),
+ ItemNames.SPECTRE_IMPALER_ROUNDS:
+ ItemData(360 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 11, SC2Race.TERRAN,
+ parent_item=ItemNames.SPECTRE, origin={"ext"},
+ description="Spectres do additional damage to armored targets."),
+ ItemNames.THOR_PROGRESSIVE_HIGH_IMPACT_PAYLOAD:
+ ItemData(361 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 14, SC2Race.TERRAN,
+ parent_item=ItemNames.THOR, quantity=2, origin={"ext"},
+ description=inspect.cleandoc(
+ f"""
+ Level 1: Allows Thors to transform in order to use an alternative air attack.
+ Level 2: {SMART_SERVOS_DESCRIPTION}
+ """
+ )),
+ ItemNames.RAVEN_BIO_MECHANICAL_REPAIR_DRONE:
+ ItemData(363 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 12, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.RAVEN, origin={"nco"},
+ description="Spell. Deploys a drone that can heal biological or mechanical units."),
+ ItemNames.RAVEN_SPIDER_MINES:
+ ItemData(364 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 13, SC2Race.TERRAN,
+ parent_item=ItemNames.RAVEN, origin={"nco"}, important_for_filtering=True,
+ description="Spell. Deploys 3 Spider Mines to a target location."),
+ ItemNames.RAVEN_RAILGUN_TURRET:
+ ItemData(365 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 14, SC2Race.TERRAN,
+ parent_item=ItemNames.RAVEN, origin={"nco"},
+ description=inspect.cleandoc(
+ """
+ Spell. Allows Ravens to deploy an advanced Auto-Turret,
+ that can attack enemy ground units in a straight line.
+ """
+ )),
+ ItemNames.RAVEN_HUNTER_SEEKER_WEAPON:
+ ItemData(366 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 15, SC2Race.TERRAN,
+ classification=ItemClassification.progression, parent_item=ItemNames.RAVEN, origin={"nco"},
+ description="Allows Ravens to attack with a Hunter-Seeker weapon."),
+ ItemNames.RAVEN_INTERFERENCE_MATRIX:
+ ItemData(367 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 16, SC2Race.TERRAN,
+ parent_item=ItemNames.RAVEN, origin={"ext"},
+ description=inspect.cleandoc(
+ """
+ Spell. Target enemy Mechanical or Psionic unit can't attack or use abilities for a short duration.
+ """
+ )),
+ ItemNames.RAVEN_ANTI_ARMOR_MISSILE:
+ ItemData(368 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 17, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.RAVEN, origin={"ext"},
+ description="Spell. Decreases target and nearby enemy units armor by 2."),
+ ItemNames.RAVEN_INTERNAL_TECH_MODULE:
+ ItemData(369 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 18, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.RAVEN, origin={"nco"},
+ description=INTERNAL_TECH_MODULE_DESCRIPTION_TEMPLATE.format("Ravens", "Starport")),
+ ItemNames.SCIENCE_VESSEL_EMP_SHOCKWAVE:
+ ItemData(370 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 19, SC2Race.TERRAN,
+ parent_item=ItemNames.SCIENCE_VESSEL, origin={"bw"},
+ description="Spell. Depletes all energy and shields of all units in a target area."),
+ ItemNames.SCIENCE_VESSEL_DEFENSIVE_MATRIX:
+ ItemData(371 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 20, SC2Race.TERRAN,
+ parent_item=ItemNames.SCIENCE_VESSEL, origin={"bw"},
+ description=inspect.cleandoc(
+ """
+ Spell. Provides a target unit with a defensive barrier that can absorb up to 250 damage
+ """
+ )),
+ ItemNames.CYCLONE_TARGETING_OPTICS:
+ ItemData(372 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 21, SC2Race.TERRAN,
+ parent_item=ItemNames.CYCLONE, origin={"ext"},
+ description="Increases Cyclone Lock On casting range and the range while Locked On."),
+ ItemNames.CYCLONE_RAPID_FIRE_LAUNCHERS:
+ ItemData(373 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 22, SC2Race.TERRAN,
+ parent_item=ItemNames.CYCLONE, origin={"ext"},
+ description="The first 12 shots of Lock On are fired more quickly."),
+ ItemNames.LIBERATOR_CLOAK:
+ ItemData(374 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 23, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.LIBERATOR, origin={"nco"},
+ description=CLOAK_DESCRIPTION_TEMPLATE.format("Liberators")),
+ ItemNames.LIBERATOR_LASER_TARGETING_SYSTEM:
+ ItemData(375 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 24, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.LIBERATOR, origin={"ext"},
+ description=LASER_TARGETING_SYSTEMS_DESCRIPTION),
+ ItemNames.LIBERATOR_OPTIMIZED_LOGISTICS:
+ ItemData(376 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 25, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.LIBERATOR, origin={"nco"},
+ description="Increases Liberator training speed."),
+ ItemNames.WIDOW_MINE_BLACK_MARKET_LAUNCHERS:
+ ItemData(377 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 26, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.WIDOW_MINE, origin={"ext"},
+ description="Increases Widow Mine Sentinel Missile range."),
+ ItemNames.WIDOW_MINE_EXECUTIONER_MISSILES:
+ ItemData(378 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 27, SC2Race.TERRAN,
+ parent_item=ItemNames.WIDOW_MINE, origin={"ext"},
+ description=inspect.cleandoc(
+ """
+ Reduces Sentinel Missile cooldown.
+ When killed, Widow Mines will launch several missiles at random enemy targets.
+ """
+ )),
+ ItemNames.VALKYRIE_ENHANCED_CLUSTER_LAUNCHERS:
+ ItemData(379 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 28,
+ SC2Race.TERRAN, parent_item=ItemNames.VALKYRIE, origin={"ext"},
+ description="Valkyries fire 2 additional rockets each volley."),
+ ItemNames.VALKYRIE_SHAPED_HULL:
+ ItemData(380 + SC2WOL_ITEM_ID_OFFSET, "Armory 5", 29, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.VALKYRIE, origin={"ext"},
+ description="Increases Valkyrie life by 50."),
+ ItemNames.VALKYRIE_FLECHETTE_MISSILES:
+ ItemData(381 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 0, SC2Race.TERRAN,
+ parent_item=ItemNames.VALKYRIE, origin={"ext"},
+ description="Equips Valkyries with Air-to-Surface missiles to attack ground units."),
+ ItemNames.VALKYRIE_AFTERBURNERS:
+ ItemData(382 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 1, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.VALKYRIE, origin={"ext"},
+ description="Ability. Temporarily increases the Valkyries's movement speed by 70%."),
+ ItemNames.CYCLONE_INTERNAL_TECH_MODULE:
+ ItemData(383 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 2, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.CYCLONE, origin={"ext"},
+ description=INTERNAL_TECH_MODULE_DESCRIPTION_TEMPLATE.format("Cyclones", "Factory")),
+ ItemNames.LIBERATOR_SMART_SERVOS:
+ ItemData(384 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 3, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.LIBERATOR, origin={"nco"},
+ description=SMART_SERVOS_DESCRIPTION),
+ ItemNames.LIBERATOR_RESOURCE_EFFICIENCY:
+ ItemData(385 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 4, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.LIBERATOR, origin={"ext"},
+ description=RESOURCE_EFFICIENCY_NO_SUPPLY_DESCRIPTION_TEMPLATE.format("Liberator")),
+ ItemNames.HERCULES_INTERNAL_FUSION_MODULE:
+ ItemData(386 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 5, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.HERCULES, origin={"ext"},
+ description="Hercules can be trained from a Starport without having a Fusion Core."),
+ ItemNames.HERCULES_TACTICAL_JUMP:
+ ItemData(387 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 6, SC2Race.TERRAN,
+ parent_item=ItemNames.HERCULES, origin={"ext"},
+ description=inspect.cleandoc(
+ """
+ Allows Hercules to warp to a target location anywhere on the map.
+ """
+ )),
+ ItemNames.PLANETARY_FORTRESS_PROGRESSIVE_AUGMENTED_THRUSTERS:
+ ItemData(388 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 28, SC2Race.TERRAN,
+ parent_item=ItemNames.PLANETARY_FORTRESS, origin={"ext"}, quantity=2,
+ description=inspect.cleandoc(
+ """
+ Level 1: Lift Off - Planetary Fortress can lift off.
+ Level 2: Armament Stabilizers - Planetary Fortress can attack while lifted off.
+ """
+ )),
+ ItemNames.PLANETARY_FORTRESS_ADVANCED_TARGETING:
+ ItemData(389 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 7, SC2Race.TERRAN,
+ parent_item=ItemNames.PLANETARY_FORTRESS, origin={"ext"},
+ description="Planetary Fortress can attack air units."),
+ ItemNames.VALKYRIE_LAUNCHING_VECTOR_COMPENSATOR:
+ ItemData(390 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 8, SC2Race.TERRAN,
+ classification=ItemClassification.filler, parent_item=ItemNames.VALKYRIE, origin={"ext"},
+ description="Allows Valkyries to shoot air while moving."),
+ ItemNames.VALKYRIE_RESOURCE_EFFICIENCY:
+ ItemData(391 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 9, SC2Race.TERRAN,
+ parent_item=ItemNames.VALKYRIE, origin={"ext"},
+ description=RESOURCE_EFFICIENCY_DESCRIPTION_TEMPLATE.format("Valkyrie")),
+ ItemNames.PREDATOR_PREDATOR_S_FURY:
+ ItemData(392 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 10, SC2Race.TERRAN,
+ parent_item=ItemNames.PREDATOR, origin={"ext"},
+ description="Predators can use an attack that jumps between targets."),
+ ItemNames.BATTLECRUISER_BEHEMOTH_PLATING:
+ ItemData(393 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 11, SC2Race.TERRAN,
+ parent_item=ItemNames.BATTLECRUISER, origin={"ext"},
+ description="Increases Battlecruiser armor by 2."),
+ ItemNames.BATTLECRUISER_COVERT_OPS_ENGINES:
+ ItemData(394 + SC2WOL_ITEM_ID_OFFSET, "Armory 6", 12, SC2Race.TERRAN,
+ parent_item=ItemNames.BATTLECRUISER, origin={"nco"},
+ description="Increases Battlecruiser movement speed."),
+
+ #Buildings
+ ItemNames.BUNKER:
+ ItemData(400 + SC2WOL_ITEM_ID_OFFSET, "Building", 0, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Defensive structure. Able to load infantry units, giving them +1 range to their attacks."),
+ ItemNames.MISSILE_TURRET:
+ ItemData(401 + SC2WOL_ITEM_ID_OFFSET, "Building", 1, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Anti-air defensive structure."),
+ ItemNames.SENSOR_TOWER:
+ ItemData(402 + SC2WOL_ITEM_ID_OFFSET, "Building", 2, SC2Race.TERRAN,
+ description="Reveals locations of enemy units at long range."),
+
+ ItemNames.WAR_PIGS:
+ ItemData(500 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 0, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Mercenary Marines"),
+ ItemNames.DEVIL_DOGS:
+ ItemData(501 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 1, SC2Race.TERRAN,
+ classification=ItemClassification.filler,
+ description="Mercenary Firebats"),
+ ItemNames.HAMMER_SECURITIES:
+ ItemData(502 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 2, SC2Race.TERRAN,
+ description="Mercenary Marauders"),
+ ItemNames.SPARTAN_COMPANY:
+ ItemData(503 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 3, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Mercenary Goliaths"),
+ ItemNames.SIEGE_BREAKERS:
+ ItemData(504 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 4, SC2Race.TERRAN,
+ description="Mercenary Siege Tanks"),
+ ItemNames.HELS_ANGELS:
+ ItemData(505 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 5, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Mercenary Vikings"),
+ ItemNames.DUSK_WINGS:
+ ItemData(506 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 6, SC2Race.TERRAN,
+ description="Mercenary Banshees"),
+ ItemNames.JACKSONS_REVENGE:
+ ItemData(507 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 7, SC2Race.TERRAN,
+ description="Mercenary Battlecruiser"),
+ ItemNames.SKIBIS_ANGELS:
+ ItemData(508 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 8, SC2Race.TERRAN,
+ origin={"ext"},
+ description="Mercenary Medics"),
+ ItemNames.DEATH_HEADS:
+ ItemData(509 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 9, SC2Race.TERRAN,
+ origin={"ext"},
+ description="Mercenary Reapers"),
+ ItemNames.WINGED_NIGHTMARES:
+ ItemData(510 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 10, SC2Race.TERRAN,
+ classification=ItemClassification.progression, origin={"ext"},
+ description="Mercenary Wraiths"),
+ ItemNames.MIDNIGHT_RIDERS:
+ ItemData(511 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 11, SC2Race.TERRAN,
+ origin={"ext"},
+ description="Mercenary Liberators"),
+ ItemNames.BRYNHILDS:
+ ItemData(512 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 12, SC2Race.TERRAN,
+ classification=ItemClassification.progression, origin={"ext"},
+ description="Mercenary Valkyries"),
+ ItemNames.JOTUN:
+ ItemData(513 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 13, SC2Race.TERRAN,
+ origin={"ext"},
+ description="Mercenary Thor"),
+
+ ItemNames.ULTRA_CAPACITORS:
+ ItemData(600 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 0, SC2Race.TERRAN,
+ description="Increases attack speed of units by 5% per weapon upgrade."),
+ ItemNames.VANADIUM_PLATING:
+ ItemData(601 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 1, SC2Race.TERRAN,
+ description="Increases the life of units by 5% per armor upgrade."),
+ ItemNames.ORBITAL_DEPOTS:
+ ItemData(602 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 2, SC2Race.TERRAN,
+ description="Supply depots are built instantly."),
+ ItemNames.MICRO_FILTERING:
+ ItemData(603 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 3, SC2Race.TERRAN,
+ description="Refineries produce Vespene gas 25% faster."),
+ ItemNames.AUTOMATED_REFINERY:
+ ItemData(604 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 4, SC2Race.TERRAN,
+ description="Eliminates the need for SCVs in vespene gas production."),
+ ItemNames.COMMAND_CENTER_REACTOR:
+ ItemData(605 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 5, SC2Race.TERRAN,
+ description="Command Centers can train two SCVs at once."),
+ ItemNames.RAVEN:
+ ItemData(606 + SC2WOL_ITEM_ID_OFFSET, "Unit", 22, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Aerial Caster unit."),
+ ItemNames.SCIENCE_VESSEL:
+ ItemData(607 + SC2WOL_ITEM_ID_OFFSET, "Unit", 23, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Aerial Caster unit. Can repair mechanical units."),
+ ItemNames.TECH_REACTOR:
+ ItemData(608 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 6, SC2Race.TERRAN,
+ description="Merges Tech Labs and Reactors into one add on structure to provide both functions."),
+ ItemNames.ORBITAL_STRIKE:
+ ItemData(609 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 7, SC2Race.TERRAN,
+ description="Trained units from Barracks are instantly deployed on rally point."),
+ ItemNames.BUNKER_SHRIKE_TURRET:
+ ItemData(610 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 6, SC2Race.TERRAN,
+ parent_item=ItemNames.BUNKER,
+ description="Adds an automated turret to Bunkers."),
+ ItemNames.BUNKER_FORTIFIED_BUNKER:
+ ItemData(611 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 7, SC2Race.TERRAN,
+ parent_item=ItemNames.BUNKER,
+ description="Bunkers have more life."),
+ ItemNames.PLANETARY_FORTRESS:
+ ItemData(612 + SC2WOL_ITEM_ID_OFFSET, "Building", 3, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description=inspect.cleandoc(
+ """
+ Allows Command Centers to upgrade into a defensive structure with a turret and additional armor.
+ Planetary Fortresses cannot Lift Off, or cast Orbital Command spells.
+ """
+ )),
+ ItemNames.PERDITION_TURRET:
+ ItemData(613 + SC2WOL_ITEM_ID_OFFSET, "Building", 4, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Automated defensive turret. Burrows down while no enemies are nearby."),
+ ItemNames.PREDATOR:
+ ItemData(614 + SC2WOL_ITEM_ID_OFFSET, "Unit", 24, SC2Race.TERRAN,
+ classification=ItemClassification.filler,
+ description="Anti-infantry specialist that deals area damage with each attack."),
+ ItemNames.HERCULES:
+ ItemData(615 + SC2WOL_ITEM_ID_OFFSET, "Unit", 25, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Massive transport ship."),
+ ItemNames.CELLULAR_REACTOR:
+ ItemData(616 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 8, SC2Race.TERRAN,
+ description="All Terran spellcasters get +100 starting and maximum energy."),
+ ItemNames.PROGRESSIVE_REGENERATIVE_BIO_STEEL:
+ ItemData(617 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 4, SC2Race.TERRAN, quantity=3,
+ classification= ItemClassification.progression,
+ description=inspect.cleandoc(
+ """
+ Allows Terran mechanical units to regenerate health while not in combat.
+ Each level increases life regeneration speed.
+ """
+ )),
+ ItemNames.HIVE_MIND_EMULATOR:
+ ItemData(618 + SC2WOL_ITEM_ID_OFFSET, "Building", 5, SC2Race.TERRAN,
+ ItemClassification.progression,
+ description="Defensive structure. Can permanently Mind Control Zerg units."),
+ ItemNames.PSI_DISRUPTER:
+ ItemData(619 + SC2WOL_ITEM_ID_OFFSET, "Building", 6, SC2Race.TERRAN,
+ classification=ItemClassification.progression,
+ description="Defensive structure. Slows the attack and movement speeds of all nearby Zerg units."),
+ ItemNames.STRUCTURE_ARMOR:
+ ItemData(620 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 9, SC2Race.TERRAN,
+ description="Increases armor of all Terran structures by 2."),
+ ItemNames.HI_SEC_AUTO_TRACKING:
+ ItemData(621 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 10, SC2Race.TERRAN,
+ description="Increases attack range of all Terran structures by 1."),
+ ItemNames.ADVANCED_OPTICS:
+ ItemData(622 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 11, SC2Race.TERRAN,
+ description="Increases attack range of all Terran mechanical units by 1."),
+ ItemNames.ROGUE_FORCES:
+ ItemData(623 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 12, SC2Race.TERRAN,
+ description="Mercenary calldowns are no longer limited by charges."),
+
+ ItemNames.ZEALOT:
+ ItemData(700 + SC2WOL_ITEM_ID_OFFSET, "Unit", 0, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"wol", "lotv"},
+ description="Powerful melee warrior. Can use the charge ability."),
+ ItemNames.STALKER:
+ ItemData(701 + SC2WOL_ITEM_ID_OFFSET, "Unit", 1, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"wol", "lotv"},
+ description="Ranged attack strider. Can use the Blink ability."),
+ ItemNames.HIGH_TEMPLAR:
+ ItemData(702 + SC2WOL_ITEM_ID_OFFSET, "Unit", 2, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"wol", "lotv"},
+ description="Potent psionic master. Can use the Feedback and Psionic Storm abilities. Can merge into an Archon."),
+ ItemNames.DARK_TEMPLAR:
+ ItemData(703 + SC2WOL_ITEM_ID_OFFSET, "Unit", 3, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"wol", "lotv"},
+ description="Deadly warrior-assassin. Permanently cloaked. Can use the Shadow Fury ability."),
+ ItemNames.IMMORTAL:
+ ItemData(704 + SC2WOL_ITEM_ID_OFFSET, "Unit", 4, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"wol", "lotv"},
+ description="Assault strider. Can use Barrier to absorb damage."),
+ ItemNames.COLOSSUS:
+ ItemData(705 + SC2WOL_ITEM_ID_OFFSET, "Unit", 5, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"wol", "lotv"},
+ description="Battle strider with a powerful area attack. Can walk up and down cliffs. Attacks set fire to the ground, dealing extra damage to enemies over time."),
+ ItemNames.PHOENIX:
+ ItemData(706 + SC2WOL_ITEM_ID_OFFSET, "Unit", 6, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"wol", "lotv"},
+ description="Air superiority starfighter. Can use Graviton Beam and Phasing Armor abilities."),
+ ItemNames.VOID_RAY:
+ ItemData(707 + SC2WOL_ITEM_ID_OFFSET, "Unit", 7, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"wol", "lotv"},
+ description="Surgical strike craft. Has the Prismatic Alignment and Prismatic Range abilities."),
+ ItemNames.CARRIER:
+ ItemData(708 + SC2WOL_ITEM_ID_OFFSET, "Unit", 8, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"wol", "lotv"},
+ description="Capital ship. Builds and launches Interceptors that attack enemy targets. Repair Drones heal nearby mechanical units."),
+
+ # Filler items to fill remaining spots
+ ItemNames.STARTING_MINERALS:
+ ItemData(800 + SC2WOL_ITEM_ID_OFFSET, "Minerals", 15, SC2Race.ANY, quantity=0,
+ classification=ItemClassification.filler,
+ description="Increases the starting minerals for all missions."),
+ ItemNames.STARTING_VESPENE:
+ ItemData(801 + SC2WOL_ITEM_ID_OFFSET, "Vespene", 15, SC2Race.ANY, quantity=0,
+ classification=ItemClassification.filler,
+ description="Increases the starting vespene for all missions."),
+ ItemNames.STARTING_SUPPLY:
+ ItemData(802 + SC2WOL_ITEM_ID_OFFSET, "Supply", 2, SC2Race.ANY, quantity=0,
+ classification=ItemClassification.filler,
+ description="Increases the starting supply for all missions."),
+ # This item is used to "remove" location from the game. Never placed unless plando'd
+ ItemNames.NOTHING:
+ ItemData(803 + SC2WOL_ITEM_ID_OFFSET, "Nothing Group", 2, SC2Race.ANY, quantity=0,
+ classification=ItemClassification.trap,
+ description="Does nothing. Used to remove a location from the game."),
+
+ # Nova gear
+ ItemNames.NOVA_GHOST_VISOR:
+ ItemData(900 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 0, SC2Race.TERRAN, origin={"nco"},
+ description="Reveals the locations of enemy units in the fog of war around Nova. Can detect cloaked units."),
+ ItemNames.NOVA_RANGEFINDER_OCULUS:
+ ItemData(901 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 1, SC2Race.TERRAN, origin={"nco"},
+ description="Increaases Nova's vision range and non-melee weapon attack range by 2. Also increases range of melee weapons by 1."),
+ ItemNames.NOVA_DOMINATION:
+ ItemData(902 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 2, SC2Race.TERRAN, origin={"nco"},
+ classification=ItemClassification.progression,
+ description="Gives Nova the ability to mind-control a target enemy unit."),
+ ItemNames.NOVA_BLINK:
+ ItemData(903 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 3, SC2Race.TERRAN, origin={"nco"},
+ classification=ItemClassification.progression,
+ description="Gives Nova the ability to teleport a short distance and cloak for 10s."),
+ ItemNames.NOVA_PROGRESSIVE_STEALTH_SUIT_MODULE:
+ ItemData(904 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade 2", 0, SC2Race.TERRAN, quantity=2, origin={"nco"},
+ classification=ItemClassification.progression,
+ description=inspect.cleandoc(
+ """
+ Level 1: Gives Nova the ability to cloak.
+ Level 2: Nova is permanently cloaked.
+ """
+ )),
+ ItemNames.NOVA_ENERGY_SUIT_MODULE:
+ ItemData(905 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 4, SC2Race.TERRAN, origin={"nco"},
+ description="Increases Nova's maximum energy and energy regeneration rate."),
+ ItemNames.NOVA_ARMORED_SUIT_MODULE:
+ ItemData(906 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 5, SC2Race.TERRAN, origin={"nco"},
+ classification=ItemClassification.progression,
+ description="Increases Nova's health by 100 and armour by 1. Nova also regenerates life quickly out of combat."),
+ ItemNames.NOVA_JUMP_SUIT_MODULE:
+ ItemData(907 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 6, SC2Race.TERRAN, origin={"nco"},
+ classification=ItemClassification.progression,
+ description="Increases Nova's movement speed and allows her to jump up and down cliffs."),
+ ItemNames.NOVA_C20A_CANISTER_RIFLE:
+ ItemData(908 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 7, SC2Race.TERRAN, origin={"nco"},
+ classification=ItemClassification.progression,
+ description="Allows Nova to equip the C20A Canister Rifle, which has a ranged attack and allows Nova to cast Snipe."),
+ ItemNames.NOVA_HELLFIRE_SHOTGUN:
+ ItemData(909 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 8, SC2Race.TERRAN, origin={"nco"},
+ classification=ItemClassification.progression,
+ description="Allows Nova to equip the Hellfire Shotgun, which has a short-range area attack in a cone and allows Nova to cast Penetrating Blast."),
+ ItemNames.NOVA_PLASMA_RIFLE:
+ ItemData(910 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 9, SC2Race.TERRAN, origin={"nco"},
+ classification=ItemClassification.progression,
+ description="Allows Nova to equip the Plasma Rifle, which has a rapidfire ranged attack and allows Nova to cast Plasma Shot."),
+ ItemNames.NOVA_MONOMOLECULAR_BLADE:
+ ItemData(911 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 10, SC2Race.TERRAN, origin={"nco"},
+ classification=ItemClassification.progression,
+ description="Allows Nova to equip the Monomolecular Blade, which has a melee attack and allows Nova to cast Dash Attack."),
+ ItemNames.NOVA_BLAZEFIRE_GUNBLADE:
+ ItemData(912 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 11, SC2Race.TERRAN, origin={"nco"},
+ classification=ItemClassification.progression,
+ description="Allows Nova to equip the Blazefire Gunblade, which has a melee attack and allows Nova to cast Fury of One."),
+ ItemNames.NOVA_STIM_INFUSION:
+ ItemData(913 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 12, SC2Race.TERRAN, origin={"nco"},
+ classification=ItemClassification.progression,
+ description="Gives Nova the ability to heal herself and temporarily increase her movement and attack speeds."),
+ ItemNames.NOVA_PULSE_GRENADES:
+ ItemData(914 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 13, SC2Race.TERRAN, origin={"nco"},
+ classification=ItemClassification.progression,
+ description="Gives Nova the ability to throw a grenade dealing large damage in an area."),
+ ItemNames.NOVA_FLASHBANG_GRENADES:
+ ItemData(915 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 14, SC2Race.TERRAN, origin={"nco"},
+ classification=ItemClassification.progression,
+ description="Gives Nova the ability to throw a grenade to stun enemies and disable detection in a large area."),
+ ItemNames.NOVA_IONIC_FORCE_FIELD:
+ ItemData(916 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 15, SC2Race.TERRAN, origin={"nco"},
+ classification=ItemClassification.progression,
+ description="Gives Nova the ability to shield herself temporarily."),
+ ItemNames.NOVA_HOLO_DECOY:
+ ItemData(917 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 16, SC2Race.TERRAN, origin={"nco"},
+ classification=ItemClassification.progression,
+ description="Gives Nova the ability to summon a decoy unit which enemies will prefer to target and takes reduced damage."),
+ ItemNames.NOVA_NUKE:
+ ItemData(918 + SC2WOL_ITEM_ID_OFFSET, "Nova Gear", 17, SC2Race.TERRAN, origin={"nco"},
+ classification=ItemClassification.progression,
+ description="Gives Nova the ability to launch tactical nukes built from the Shadow Ops."),
+
+ # HotS
+ ItemNames.ZERGLING:
+ ItemData(0 + SC2HOTS_ITEM_ID_OFFSET, "Unit", 0, SC2Race.ZERG,
+ classification=ItemClassification.progression, origin={"hots"},
+ description="Fast inexpensive melee attacker. Hatches in pairs from a single larva. Can morph into a Baneling."),
+ ItemNames.SWARM_QUEEN:
+ ItemData(1 + SC2HOTS_ITEM_ID_OFFSET, "Unit", 1, SC2Race.ZERG,
+ classification=ItemClassification.progression, origin={"hots"},
+ description="Ranged support caster. Can use the Spawn Creep Tumor and Rapid Transfusion abilities."),
+ ItemNames.ROACH:
+ ItemData(2 + SC2HOTS_ITEM_ID_OFFSET, "Unit", 2, SC2Race.ZERG,
+ classification=ItemClassification.progression, origin={"hots"},
+ description="Durable short ranged attacker. Regenerates life quickly when burrowed."),
+ ItemNames.HYDRALISK:
+ ItemData(3 + SC2HOTS_ITEM_ID_OFFSET, "Unit", 3, SC2Race.ZERG,
+ classification=ItemClassification.progression, origin={"hots"},
+ description="High-damage generalist ranged attacker."),
+ ItemNames.ZERGLING_BANELING_ASPECT:
+ ItemData(4 + SC2HOTS_ITEM_ID_OFFSET, "Morph", 5, SC2Race.ZERG,
+ classification=ItemClassification.progression, origin={"hots"},
+ description="Anti-ground suicide unit. Does damage over a small area on death."),
+ ItemNames.ABERRATION:
+ ItemData(5 + SC2HOTS_ITEM_ID_OFFSET, "Unit", 5, SC2Race.ZERG,
+ classification=ItemClassification.progression, origin={"hots"},
+ description="Durable melee attacker that deals heavy damage and can walk over other units."),
+ ItemNames.MUTALISK:
+ ItemData(6 + SC2HOTS_ITEM_ID_OFFSET, "Unit", 6, SC2Race.ZERG,
+ classification=ItemClassification.progression, origin={"hots"},
+ description="Fragile flying attacker. Attacks bounce between targets."),
+ ItemNames.SWARM_HOST:
+ ItemData(7 + SC2HOTS_ITEM_ID_OFFSET, "Unit", 7, SC2Race.ZERG,
+ classification=ItemClassification.progression, origin={"hots"},
+ description="Siege unit that attacks by rooting in place and continually spawning Locusts."),
+ ItemNames.INFESTOR:
+ ItemData(8 + SC2HOTS_ITEM_ID_OFFSET, "Unit", 8, SC2Race.ZERG,
+ classification=ItemClassification.progression, origin={"hots"},
+ description="Support caster that can move while burrowed. Can use the Fungal Growth, Parasitic Domination, and Consumption abilities."),
+ ItemNames.ULTRALISK:
+ ItemData(9 + SC2HOTS_ITEM_ID_OFFSET, "Unit", 9, SC2Race.ZERG,
+ classification=ItemClassification.progression, origin={"hots"},
+ description="Massive melee attacker. Has an area-damage cleave attack."),
+ ItemNames.SPORE_CRAWLER:
+ ItemData(10 + SC2HOTS_ITEM_ID_OFFSET, "Unit", 10, SC2Race.ZERG,
+ classification=ItemClassification.progression, origin={"hots"},
+ description="Anti-air defensive structure that can detect cloaked units."),
+ ItemNames.SPINE_CRAWLER:
+ ItemData(11 + SC2HOTS_ITEM_ID_OFFSET, "Unit", 11, SC2Race.ZERG,
+ classification=ItemClassification.progression, origin={"hots"},
+ description="Anti-ground defensive structure."),
+ ItemNames.CORRUPTOR:
+ ItemData(12 + SC2HOTS_ITEM_ID_OFFSET, "Unit", 12, SC2Race.ZERG,
+ classification=ItemClassification.progression, origin={"ext"},
+ description="Anti-air flying attacker specializing in taking down enemy capital ships."),
+ ItemNames.SCOURGE:
+ ItemData(13 + SC2HOTS_ITEM_ID_OFFSET, "Unit", 13, SC2Race.ZERG,
+ classification=ItemClassification.progression, origin={"bw", "ext"},
+ description="Flying anti-air suicide unit. Hatches in pairs from a single larva."),
+ ItemNames.BROOD_QUEEN:
+ ItemData(14 + SC2HOTS_ITEM_ID_OFFSET, "Unit", 4, SC2Race.ZERG,
+ classification=ItemClassification.progression, origin={"bw", "ext"},
+ description="Flying support caster. Can cast the Ocular Symbiote and Spawn Broodlings abilities."),
+ ItemNames.DEFILER:
+ ItemData(15 + SC2HOTS_ITEM_ID_OFFSET, "Unit", 14, SC2Race.ZERG,
+ classification=ItemClassification.progression, origin={"bw"},
+ description="Support caster. Can use the Dark Swarm, Consume, and Plague abilities."),
+
+ ItemNames.PROGRESSIVE_ZERG_MELEE_ATTACK: ItemData(100 + SC2HOTS_ITEM_ID_OFFSET, "Upgrade", 0, SC2Race.ZERG, quantity=3, origin={"hots"}),
+ ItemNames.PROGRESSIVE_ZERG_MISSILE_ATTACK: ItemData(101 + SC2HOTS_ITEM_ID_OFFSET, "Upgrade", 2, SC2Race.ZERG, quantity=3, origin={"hots"}),
+ ItemNames.PROGRESSIVE_ZERG_GROUND_CARAPACE: ItemData(102 + SC2HOTS_ITEM_ID_OFFSET, "Upgrade", 4, SC2Race.ZERG, quantity=3, origin={"hots"}),
+ ItemNames.PROGRESSIVE_ZERG_FLYER_ATTACK: ItemData(103 + SC2HOTS_ITEM_ID_OFFSET, "Upgrade", 6, SC2Race.ZERG, quantity=3, origin={"hots"}),
+ ItemNames.PROGRESSIVE_ZERG_FLYER_CARAPACE: ItemData(104 + SC2HOTS_ITEM_ID_OFFSET, "Upgrade", 8, SC2Race.ZERG, quantity=3, origin={"hots"}),
+ # Upgrade bundle 'number' values are used as indices to get affected 'number's
+ ItemNames.PROGRESSIVE_ZERG_WEAPON_UPGRADE: ItemData(105 + SC2HOTS_ITEM_ID_OFFSET, "Upgrade", 6, SC2Race.ZERG, quantity=3, origin={"hots"}),
+ ItemNames.PROGRESSIVE_ZERG_ARMOR_UPGRADE: ItemData(106 + SC2HOTS_ITEM_ID_OFFSET, "Upgrade", 7, SC2Race.ZERG, quantity=3, origin={"hots"}),
+ ItemNames.PROGRESSIVE_ZERG_GROUND_UPGRADE: ItemData(107 + SC2HOTS_ITEM_ID_OFFSET, "Upgrade", 8, SC2Race.ZERG, quantity=3, origin={"hots"}),
+ ItemNames.PROGRESSIVE_ZERG_FLYER_UPGRADE: ItemData(108 + SC2HOTS_ITEM_ID_OFFSET, "Upgrade", 9, SC2Race.ZERG, quantity=3, origin={"hots"}),
+ ItemNames.PROGRESSIVE_ZERG_WEAPON_ARMOR_UPGRADE: ItemData(109 + SC2HOTS_ITEM_ID_OFFSET, "Upgrade", 10, SC2Race.ZERG, quantity=3, origin={"hots"}),
+
+ ItemNames.ZERGLING_HARDENED_CARAPACE:
+ ItemData(200 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 0, SC2Race.ZERG, parent_item=ItemNames.ZERGLING,
+ origin={"hots"}, description="Increases Zergling health by +10."),
+ ItemNames.ZERGLING_ADRENAL_OVERLOAD:
+ ItemData(201 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 1, SC2Race.ZERG, parent_item=ItemNames.ZERGLING,
+ origin={"hots"}, description="Increases Zergling attack speed."),
+ ItemNames.ZERGLING_METABOLIC_BOOST:
+ ItemData(202 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 2, SC2Race.ZERG, parent_item=ItemNames.ZERGLING,
+ origin={"hots"}, classification=ItemClassification.filler,
+ description="Increases Zergling movement speed."),
+ ItemNames.ROACH_HYDRIODIC_BILE:
+ ItemData(203 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 3, SC2Race.ZERG, parent_item=ItemNames.ROACH,
+ origin={"hots"}, description="Roaches deal +8 damage to light targets."),
+ ItemNames.ROACH_ADAPTIVE_PLATING:
+ ItemData(204 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 4, SC2Race.ZERG, parent_item=ItemNames.ROACH,
+ origin={"hots"}, description="Roaches gain +3 armour when their life is below 50%."),
+ ItemNames.ROACH_TUNNELING_CLAWS:
+ ItemData(205 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 5, SC2Race.ZERG, parent_item=ItemNames.ROACH,
+ origin={"hots"}, classification=ItemClassification.filler,
+ description="Allows Roaches to move while burrowed."),
+ ItemNames.HYDRALISK_FRENZY:
+ ItemData(206 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 6, SC2Race.ZERG, parent_item=ItemNames.HYDRALISK,
+ origin={"hots"},
+ description="Allows Hydralisks to use the Frenzy ability, which increases their attack speed by 50%."),
+ ItemNames.HYDRALISK_ANCILLARY_CARAPACE:
+ ItemData(207 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 7, SC2Race.ZERG, parent_item=ItemNames.HYDRALISK,
+ origin={"hots"}, classification=ItemClassification.filler, description="Hydralisks gain +20 health."),
+ ItemNames.HYDRALISK_GROOVED_SPINES:
+ ItemData(208 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 8, SC2Race.ZERG, parent_item=ItemNames.HYDRALISK,
+ origin={"hots"}, description="Hydralisks gain +1 range."),
+ ItemNames.BANELING_CORROSIVE_ACID:
+ ItemData(209 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 9, SC2Race.ZERG,
+ parent_item=ItemNames.ZERGLING_BANELING_ASPECT, origin={"hots"},
+ description="Increases the damage banelings deal to their primary target. Splash damage remains the same."),
+ ItemNames.BANELING_RUPTURE:
+ ItemData(210 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 10, SC2Race.ZERG,
+ parent_item=ItemNames.ZERGLING_BANELING_ASPECT, origin={"hots"},
+ classification=ItemClassification.filler,
+ description="Increases the splash radius of baneling attacks."),
+ ItemNames.BANELING_REGENERATIVE_ACID:
+ ItemData(211 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 11, SC2Race.ZERG,
+ parent_item=ItemNames.ZERGLING_BANELING_ASPECT, origin={"hots"},
+ classification=ItemClassification.filler,
+ description="Banelings will heal nearby friendly units when they explode."),
+ ItemNames.MUTALISK_VICIOUS_GLAIVE:
+ ItemData(212 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 12, SC2Race.ZERG, parent_item=ItemNames.MUTALISK,
+ origin={"hots"}, description="Mutalisks attacks will bounce an additional 3 times."),
+ ItemNames.MUTALISK_RAPID_REGENERATION:
+ ItemData(213 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 13, SC2Race.ZERG, parent_item=ItemNames.MUTALISK,
+ origin={"hots"}, description="Mutalisks will regenerate quickly when out of combat."),
+ ItemNames.MUTALISK_SUNDERING_GLAIVE:
+ ItemData(214 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 14, SC2Race.ZERG, parent_item=ItemNames.MUTALISK,
+ origin={"hots"}, description="Mutalisks deal increased damage to their primary target."),
+ ItemNames.SWARM_HOST_BURROW:
+ ItemData(215 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 15, SC2Race.ZERG, parent_item=ItemNames.SWARM_HOST,
+ origin={"hots"}, classification=ItemClassification.filler,
+ description="Allows Swarm Hosts to burrow instead of root to spawn locusts."),
+ ItemNames.SWARM_HOST_RAPID_INCUBATION:
+ ItemData(216 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 16, SC2Race.ZERG, parent_item=ItemNames.SWARM_HOST,
+ origin={"hots"}, description="Swarm Hosts will spawn locusts 20% faster."),
+ ItemNames.SWARM_HOST_PRESSURIZED_GLANDS:
+ ItemData(217 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 17, SC2Race.ZERG, parent_item=ItemNames.SWARM_HOST,
+ origin={"hots"}, classification=ItemClassification.progression,
+ description="Allows Swarm Host Locusts to attack air targets."),
+ ItemNames.ULTRALISK_BURROW_CHARGE:
+ ItemData(218 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 18, SC2Race.ZERG, parent_item=ItemNames.ULTRALISK,
+ origin={"hots"},
+ description="Allows Ultralisks to burrow and charge at enemy units, knocking back and stunning units when it emerges."),
+ ItemNames.ULTRALISK_TISSUE_ASSIMILATION:
+ ItemData(219 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 19, SC2Race.ZERG, parent_item=ItemNames.ULTRALISK,
+ origin={"hots"}, description="Ultralisks recover health when they deal damage."),
+ ItemNames.ULTRALISK_MONARCH_BLADES:
+ ItemData(220 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 20, SC2Race.ZERG, parent_item=ItemNames.ULTRALISK,
+ origin={"hots"}, description="Ultralisks gain increased splash damage."),
+ ItemNames.CORRUPTOR_CAUSTIC_SPRAY:
+ ItemData(221 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 21, SC2Race.ZERG, parent_item=ItemNames.CORRUPTOR,
+ origin={"ext"},
+ description="Allows Corruptors to use the Caustic Spray ability, which deals ramping damage to buildings over time."),
+ ItemNames.CORRUPTOR_CORRUPTION:
+ ItemData(222 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 22, SC2Race.ZERG, parent_item=ItemNames.CORRUPTOR,
+ origin={"ext"},
+ description="Allows Corruptors to use the Corruption ability, which causes a target enemy unit to take increased damage."),
+ ItemNames.SCOURGE_VIRULENT_SPORES:
+ ItemData(223 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 23, SC2Race.ZERG, parent_item=ItemNames.SCOURGE,
+ origin={"ext"}, description="Scourge will deal splash damage."),
+ ItemNames.SCOURGE_RESOURCE_EFFICIENCY:
+ ItemData(224 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 24, SC2Race.ZERG, parent_item=ItemNames.SCOURGE,
+ origin={"ext"}, classification=ItemClassification.progression,
+ description="Reduces the cost of Scourge by 50 gas per egg."),
+ ItemNames.SCOURGE_SWARM_SCOURGE:
+ ItemData(225 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 25, SC2Race.ZERG, parent_item=ItemNames.SCOURGE,
+ origin={"ext"}, description="An extra Scourge will be built from each egg at no additional cost."),
+ ItemNames.ZERGLING_SHREDDING_CLAWS:
+ ItemData(226 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 26, SC2Race.ZERG, parent_item=ItemNames.ZERGLING,
+ origin={"ext"}, description="Zergling attacks will temporarily reduce their target's armour to 0."),
+ ItemNames.ROACH_GLIAL_RECONSTITUTION:
+ ItemData(227 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 27, SC2Race.ZERG, parent_item=ItemNames.ROACH,
+ origin={"ext"}, description="Increases Roach movement speed."),
+ ItemNames.ROACH_ORGANIC_CARAPACE:
+ ItemData(228 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 28, SC2Race.ZERG, parent_item=ItemNames.ROACH,
+ origin={"ext"}, description="Increases Roach health by +25."),
+ ItemNames.HYDRALISK_MUSCULAR_AUGMENTS:
+ ItemData(229 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 1", 29, SC2Race.ZERG, parent_item=ItemNames.HYDRALISK,
+ origin={"bw"}, description="Increases Hydralisk movement speed."),
+ ItemNames.HYDRALISK_RESOURCE_EFFICIENCY:
+ ItemData(230 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 0, SC2Race.ZERG, parent_item=ItemNames.HYDRALISK,
+ origin={"bw"}, description="Reduces Hydralisk resource cost by 25/25 and supply cost by 1."),
+ ItemNames.BANELING_CENTRIFUGAL_HOOKS:
+ ItemData(231 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 1, SC2Race.ZERG,
+ parent_item=ItemNames.ZERGLING_BANELING_ASPECT, origin={"ext"},
+ description="Increases the movement speed of Banelings."),
+ ItemNames.BANELING_TUNNELING_JAWS:
+ ItemData(232 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 2, SC2Race.ZERG,
+ parent_item=ItemNames.ZERGLING_BANELING_ASPECT, origin={"ext"},
+ description="Allows Banelings to move while burrowed."),
+ ItemNames.BANELING_RAPID_METAMORPH:
+ ItemData(233 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 3, SC2Race.ZERG,
+ parent_item=ItemNames.ZERGLING_BANELING_ASPECT, origin={"ext"}, description="Banelings morph faster."),
+ ItemNames.MUTALISK_SEVERING_GLAIVE:
+ ItemData(234 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 4, SC2Race.ZERG, parent_item=ItemNames.MUTALISK,
+ origin={"ext"}, description="Mutalisk bounce attacks will deal full damage."),
+ ItemNames.MUTALISK_AERODYNAMIC_GLAIVE_SHAPE:
+ ItemData(235 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 5, SC2Race.ZERG, parent_item=ItemNames.MUTALISK,
+ origin={"ext"}, description="Increases the attack range of Mutalisks by 2."),
+ ItemNames.SWARM_HOST_LOCUST_METABOLIC_BOOST:
+ ItemData(236 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 6, SC2Race.ZERG, parent_item=ItemNames.SWARM_HOST,
+ origin={"ext"}, classification=ItemClassification.filler,
+ description="Increases Locust movement speed."),
+ ItemNames.SWARM_HOST_ENDURING_LOCUSTS:
+ ItemData(237 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 7, SC2Race.ZERG, parent_item=ItemNames.SWARM_HOST,
+ origin={"ext"}, description="Increases the duration of Swarm Hosts' Locusts by 10s."),
+ ItemNames.SWARM_HOST_ORGANIC_CARAPACE:
+ ItemData(238 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 8, SC2Race.ZERG, parent_item=ItemNames.SWARM_HOST,
+ origin={"ext"}, description="Increases Swarm Host health by +40."),
+ ItemNames.SWARM_HOST_RESOURCE_EFFICIENCY:
+ ItemData(239 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 9, SC2Race.ZERG, parent_item=ItemNames.SWARM_HOST,
+ origin={"ext"}, description="Reduces Swarm Host resource cost by 100/25."),
+ ItemNames.ULTRALISK_ANABOLIC_SYNTHESIS:
+ ItemData(240 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 10, SC2Race.ZERG, parent_item=ItemNames.ULTRALISK,
+ origin={"bw"}, classification=ItemClassification.filler),
+ ItemNames.ULTRALISK_CHITINOUS_PLATING:
+ ItemData(241 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 11, SC2Race.ZERG, parent_item=ItemNames.ULTRALISK,
+ origin={"bw"}),
+ ItemNames.ULTRALISK_ORGANIC_CARAPACE:
+ ItemData(242 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 12, SC2Race.ZERG, parent_item=ItemNames.ULTRALISK,
+ origin={"ext"}),
+ ItemNames.ULTRALISK_RESOURCE_EFFICIENCY:
+ ItemData(243 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 13, SC2Race.ZERG, parent_item=ItemNames.ULTRALISK,
+ origin={"bw"}),
+ ItemNames.DEVOURER_CORROSIVE_SPRAY:
+ ItemData(244 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 14, SC2Race.ZERG,
+ parent_item=ItemNames.MUTALISK_CORRUPTOR_DEVOURER_ASPECT, origin={"ext"}),
+ ItemNames.DEVOURER_GAPING_MAW:
+ ItemData(245 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 15, SC2Race.ZERG,
+ parent_item=ItemNames.MUTALISK_CORRUPTOR_DEVOURER_ASPECT, origin={"ext"}),
+ ItemNames.DEVOURER_IMPROVED_OSMOSIS:
+ ItemData(246 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 16, SC2Race.ZERG,
+ parent_item=ItemNames.MUTALISK_CORRUPTOR_DEVOURER_ASPECT, origin={"ext"},
+ classification=ItemClassification.filler),
+ ItemNames.DEVOURER_PRESCIENT_SPORES:
+ ItemData(247 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 17, SC2Race.ZERG,
+ parent_item=ItemNames.MUTALISK_CORRUPTOR_DEVOURER_ASPECT, origin={"ext"}),
+ ItemNames.GUARDIAN_PROLONGED_DISPERSION:
+ ItemData(248 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 18, SC2Race.ZERG,
+ parent_item=ItemNames.MUTALISK_CORRUPTOR_GUARDIAN_ASPECT, origin={"ext"}),
+ ItemNames.GUARDIAN_PRIMAL_ADAPTATION:
+ ItemData(249 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 19, SC2Race.ZERG,
+ parent_item=ItemNames.MUTALISK_CORRUPTOR_GUARDIAN_ASPECT, origin={"ext"}),
+ ItemNames.GUARDIAN_SORONAN_ACID:
+ ItemData(250 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 20, SC2Race.ZERG,
+ parent_item=ItemNames.MUTALISK_CORRUPTOR_GUARDIAN_ASPECT, origin={"ext"}),
+ ItemNames.IMPALER_ADAPTIVE_TALONS:
+ ItemData(251 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 21, SC2Race.ZERG,
+ parent_item=ItemNames.HYDRALISK_IMPALER_ASPECT, origin={"ext"},
+ classification=ItemClassification.filler),
+ ItemNames.IMPALER_SECRETION_GLANDS:
+ ItemData(252 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 22, SC2Race.ZERG,
+ parent_item=ItemNames.HYDRALISK_IMPALER_ASPECT, origin={"ext"}),
+ ItemNames.IMPALER_HARDENED_TENTACLE_SPINES:
+ ItemData(253 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 23, SC2Race.ZERG,
+ parent_item=ItemNames.HYDRALISK_IMPALER_ASPECT, origin={"ext"}),
+ ItemNames.LURKER_SEISMIC_SPINES:
+ ItemData(254 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 24, SC2Race.ZERG,
+ parent_item=ItemNames.HYDRALISK_LURKER_ASPECT, origin={"ext"}),
+ ItemNames.LURKER_ADAPTED_SPINES:
+ ItemData(255 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 25, SC2Race.ZERG,
+ parent_item=ItemNames.HYDRALISK_LURKER_ASPECT, origin={"ext"}),
+ ItemNames.RAVAGER_POTENT_BILE:
+ ItemData(256 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 26, SC2Race.ZERG,
+ parent_item=ItemNames.ROACH_RAVAGER_ASPECT, origin={"ext"}),
+ ItemNames.RAVAGER_BLOATED_BILE_DUCTS:
+ ItemData(257 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 27, SC2Race.ZERG,
+ parent_item=ItemNames.ROACH_RAVAGER_ASPECT, origin={"ext"}),
+ ItemNames.RAVAGER_DEEP_TUNNEL:
+ ItemData(258 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 28, SC2Race.ZERG,
+ parent_item=ItemNames.ROACH_RAVAGER_ASPECT, origin={"ext"}),
+ ItemNames.VIPER_PARASITIC_BOMB:
+ ItemData(259 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 2", 29, SC2Race.ZERG,
+ parent_item=ItemNames.MUTALISK_CORRUPTOR_VIPER_ASPECT, origin={"ext"}),
+ ItemNames.VIPER_PARALYTIC_BARBS:
+ ItemData(260 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 0, SC2Race.ZERG,
+ parent_item=ItemNames.MUTALISK_CORRUPTOR_VIPER_ASPECT, origin={"ext"}),
+ ItemNames.VIPER_VIRULENT_MICROBES:
+ ItemData(261 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 1, SC2Race.ZERG,
+ parent_item=ItemNames.MUTALISK_CORRUPTOR_VIPER_ASPECT, origin={"ext"}),
+ ItemNames.BROOD_LORD_POROUS_CARTILAGE:
+ ItemData(262 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 2, SC2Race.ZERG,
+ parent_item=ItemNames.MUTALISK_CORRUPTOR_BROOD_LORD_ASPECT, origin={"ext"}),
+ ItemNames.BROOD_LORD_EVOLVED_CARAPACE:
+ ItemData(263 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 3, SC2Race.ZERG,
+ parent_item=ItemNames.MUTALISK_CORRUPTOR_BROOD_LORD_ASPECT, origin={"ext"}),
+ ItemNames.BROOD_LORD_SPLITTER_MITOSIS:
+ ItemData(264 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 4, SC2Race.ZERG,
+ parent_item=ItemNames.MUTALISK_CORRUPTOR_BROOD_LORD_ASPECT, origin={"ext"}),
+ ItemNames.BROOD_LORD_RESOURCE_EFFICIENCY:
+ ItemData(265 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 5, SC2Race.ZERG,
+ parent_item=ItemNames.MUTALISK_CORRUPTOR_BROOD_LORD_ASPECT, origin={"ext"}),
+ ItemNames.INFESTOR_INFESTED_TERRAN:
+ ItemData(266 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 6, SC2Race.ZERG, parent_item=ItemNames.INFESTOR,
+ origin={"ext"}),
+ ItemNames.INFESTOR_MICROBIAL_SHROUD:
+ ItemData(267 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 7, SC2Race.ZERG, parent_item=ItemNames.INFESTOR,
+ origin={"ext"}),
+ ItemNames.SWARM_QUEEN_SPAWN_LARVAE:
+ ItemData(268 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 8, SC2Race.ZERG, parent_item=ItemNames.SWARM_QUEEN,
+ origin={"ext"}),
+ ItemNames.SWARM_QUEEN_DEEP_TUNNEL:
+ ItemData(269 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 9, SC2Race.ZERG, parent_item=ItemNames.SWARM_QUEEN,
+ origin={"ext"}),
+ ItemNames.SWARM_QUEEN_ORGANIC_CARAPACE:
+ ItemData(270 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 10, SC2Race.ZERG, parent_item=ItemNames.SWARM_QUEEN,
+ origin={"ext"}, classification=ItemClassification.filler),
+ ItemNames.SWARM_QUEEN_BIO_MECHANICAL_TRANSFUSION:
+ ItemData(271 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 11, SC2Race.ZERG, parent_item=ItemNames.SWARM_QUEEN,
+ origin={"ext"}),
+ ItemNames.SWARM_QUEEN_RESOURCE_EFFICIENCY:
+ ItemData(272 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 12, SC2Race.ZERG, parent_item=ItemNames.SWARM_QUEEN,
+ origin={"ext"}),
+ ItemNames.SWARM_QUEEN_INCUBATOR_CHAMBER:
+ ItemData(273 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 13, SC2Race.ZERG, parent_item=ItemNames.SWARM_QUEEN,
+ origin={"ext"}),
+ ItemNames.BROOD_QUEEN_FUNGAL_GROWTH:
+ ItemData(274 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 14, SC2Race.ZERG, parent_item=ItemNames.BROOD_QUEEN,
+ origin={"ext"}),
+ ItemNames.BROOD_QUEEN_ENSNARE:
+ ItemData(275 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 15, SC2Race.ZERG, parent_item=ItemNames.BROOD_QUEEN,
+ origin={"ext"}),
+ ItemNames.BROOD_QUEEN_ENHANCED_MITOCHONDRIA:
+ ItemData(276 + SC2HOTS_ITEM_ID_OFFSET, "Mutation 3", 16, SC2Race.ZERG, parent_item=ItemNames.BROOD_QUEEN,
+ origin={"ext"}),
+
+ ItemNames.ZERGLING_RAPTOR_STRAIN:
+ ItemData(300 + SC2HOTS_ITEM_ID_OFFSET, "Strain", 0, SC2Race.ZERG, parent_item=ItemNames.ZERGLING,
+ origin={"hots"},
+ description="Allows Zerglings to jump up and down cliffs and leap onto enemies. Also increases Zergling attack damage by 2."),
+ ItemNames.ZERGLING_SWARMLING_STRAIN:
+ ItemData(301 + SC2HOTS_ITEM_ID_OFFSET, "Strain", 1, SC2Race.ZERG, parent_item=ItemNames.ZERGLING,
+ origin={"hots"},
+ description="Zerglings will spawn instantly and with an extra Zergling per egg at no additional cost."),
+ ItemNames.ROACH_VILE_STRAIN:
+ ItemData(302 + SC2HOTS_ITEM_ID_OFFSET, "Strain", 2, SC2Race.ZERG, parent_item=ItemNames.ROACH, origin={"hots"},
+ description="Roach attacks will slow the movement and attack speed of enemies."),
+ ItemNames.ROACH_CORPSER_STRAIN:
+ ItemData(303 + SC2HOTS_ITEM_ID_OFFSET, "Strain", 3, SC2Race.ZERG, parent_item=ItemNames.ROACH, origin={"hots"},
+ description="Units killed after being attacked by Roaches will spawn 2 Roachlings."),
+ ItemNames.HYDRALISK_IMPALER_ASPECT:
+ ItemData(304 + SC2HOTS_ITEM_ID_OFFSET, "Morph", 0, SC2Race.ZERG, origin={"hots"},
+ classification=ItemClassification.progression,
+ description="Allows Hydralisks to morph into Impalers."),
+ ItemNames.HYDRALISK_LURKER_ASPECT:
+ ItemData(305 + SC2HOTS_ITEM_ID_OFFSET, "Morph", 1, SC2Race.ZERG, origin={"hots"},
+ classification=ItemClassification.progression, description="Allows Hydralisks to morph into Lurkers."),
+ ItemNames.BANELING_SPLITTER_STRAIN:
+ ItemData(306 + SC2HOTS_ITEM_ID_OFFSET, "Strain", 6, SC2Race.ZERG,
+ parent_item=ItemNames.ZERGLING_BANELING_ASPECT, origin={"hots"},
+ description="Banelings will split into two smaller Splitterlings on exploding."),
+ ItemNames.BANELING_HUNTER_STRAIN:
+ ItemData(307 + SC2HOTS_ITEM_ID_OFFSET, "Strain", 7, SC2Race.ZERG,
+ parent_item=ItemNames.ZERGLING_BANELING_ASPECT, origin={"hots"},
+ description="Allows Banelings to jump up and down cliffs and leap onto enemies."),
+ ItemNames.MUTALISK_CORRUPTOR_BROOD_LORD_ASPECT:
+ ItemData(308 + SC2HOTS_ITEM_ID_OFFSET, "Morph", 2, SC2Race.ZERG, origin={"hots"},
+ classification=ItemClassification.progression,
+ description="Allows Mutalisks and Corruptors to morph into Brood Lords."),
+ ItemNames.MUTALISK_CORRUPTOR_VIPER_ASPECT:
+ ItemData(309 + SC2HOTS_ITEM_ID_OFFSET, "Morph", 3, SC2Race.ZERG, origin={"hots"},
+ classification=ItemClassification.progression,
+ description="Allows Mutalisks and Corruptors to morph into Vipers."),
+ ItemNames.SWARM_HOST_CARRION_STRAIN:
+ ItemData(310 + SC2HOTS_ITEM_ID_OFFSET, "Strain", 10, SC2Race.ZERG, parent_item=ItemNames.SWARM_HOST,
+ origin={"hots"}, description="Swarm Hosts will spawn Flying Locusts."),
+ ItemNames.SWARM_HOST_CREEPER_STRAIN:
+ ItemData(311 + SC2HOTS_ITEM_ID_OFFSET, "Strain", 11, SC2Race.ZERG, parent_item=ItemNames.SWARM_HOST,
+ origin={"hots"}, classification=ItemClassification.filler,
+ description="Allows Swarm Hosts to teleport to any creep on the map in vision. Swarm Hosts will spread creep around them when rooted or burrowed."),
+ ItemNames.ULTRALISK_NOXIOUS_STRAIN:
+ ItemData(312 + SC2HOTS_ITEM_ID_OFFSET, "Strain", 12, SC2Race.ZERG, parent_item=ItemNames.ULTRALISK,
+ origin={"hots"}, classification=ItemClassification.filler,
+ description="Ultralisks will periodically spread poison, damaging nearby biological enemies."),
+ ItemNames.ULTRALISK_TORRASQUE_STRAIN:
+ ItemData(313 + SC2HOTS_ITEM_ID_OFFSET, "Strain", 13, SC2Race.ZERG, parent_item=ItemNames.ULTRALISK,
+ origin={"hots"}, description="Ultralisks will revive after being killed."),
+
+ ItemNames.KERRIGAN_KINETIC_BLAST: ItemData(400 + SC2HOTS_ITEM_ID_OFFSET, "Ability", 0, SC2Race.ZERG, origin={"hots"}, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_HEROIC_FORTITUDE: ItemData(401 + SC2HOTS_ITEM_ID_OFFSET, "Ability", 1, SC2Race.ZERG, origin={"hots"}, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_LEAPING_STRIKE: ItemData(402 + SC2HOTS_ITEM_ID_OFFSET, "Ability", 2, SC2Race.ZERG, origin={"hots"}, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_CRUSHING_GRIP: ItemData(403 + SC2HOTS_ITEM_ID_OFFSET, "Ability", 3, SC2Race.ZERG, origin={"hots"}, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_CHAIN_REACTION: ItemData(404 + SC2HOTS_ITEM_ID_OFFSET, "Ability", 4, SC2Race.ZERG, origin={"hots"}, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_PSIONIC_SHIFT: ItemData(405 + SC2HOTS_ITEM_ID_OFFSET, "Ability", 5, SC2Race.ZERG, origin={"hots"}, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_ZERGLING_RECONSTITUTION: ItemData(406 + SC2HOTS_ITEM_ID_OFFSET, "Evolution Pit", 0, SC2Race.ZERG, origin={"hots"}, classification=ItemClassification.filler),
+ ItemNames.KERRIGAN_IMPROVED_OVERLORDS: ItemData(407 + SC2HOTS_ITEM_ID_OFFSET, "Evolution Pit", 1, SC2Race.ZERG, origin={"hots"}),
+ ItemNames.KERRIGAN_AUTOMATED_EXTRACTORS: ItemData(408 + SC2HOTS_ITEM_ID_OFFSET, "Evolution Pit", 2, SC2Race.ZERG, origin={"hots"}),
+ ItemNames.KERRIGAN_WILD_MUTATION: ItemData(409 + SC2HOTS_ITEM_ID_OFFSET, "Ability", 6, SC2Race.ZERG, origin={"hots"}, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_SPAWN_BANELINGS: ItemData(410 + SC2HOTS_ITEM_ID_OFFSET, "Ability", 7, SC2Race.ZERG, origin={"hots"}, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_MEND: ItemData(411 + SC2HOTS_ITEM_ID_OFFSET, "Ability", 8, SC2Race.ZERG, origin={"hots"}, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_TWIN_DRONES: ItemData(412 + SC2HOTS_ITEM_ID_OFFSET, "Evolution Pit", 3, SC2Race.ZERG, origin={"hots"}),
+ ItemNames.KERRIGAN_MALIGNANT_CREEP: ItemData(413 + SC2HOTS_ITEM_ID_OFFSET, "Evolution Pit", 4, SC2Race.ZERG, origin={"hots"}),
+ ItemNames.KERRIGAN_VESPENE_EFFICIENCY: ItemData(414 + SC2HOTS_ITEM_ID_OFFSET, "Evolution Pit", 5, SC2Race.ZERG, origin={"hots"}),
+ ItemNames.KERRIGAN_INFEST_BROODLINGS: ItemData(415 + SC2HOTS_ITEM_ID_OFFSET, "Ability", 9, SC2Race.ZERG, origin={"hots"}, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_FURY: ItemData(416 + SC2HOTS_ITEM_ID_OFFSET, "Ability", 10, SC2Race.ZERG, origin={"hots"}, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_ABILITY_EFFICIENCY: ItemData(417 + SC2HOTS_ITEM_ID_OFFSET, "Ability", 11, SC2Race.ZERG, origin={"hots"}),
+ ItemNames.KERRIGAN_APOCALYPSE: ItemData(418 + SC2HOTS_ITEM_ID_OFFSET, "Ability", 12, SC2Race.ZERG, origin={"hots"}, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_SPAWN_LEVIATHAN: ItemData(419 + SC2HOTS_ITEM_ID_OFFSET, "Ability", 13, SC2Race.ZERG, origin={"hots"}, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_DROP_PODS: ItemData(420 + SC2HOTS_ITEM_ID_OFFSET, "Ability", 14, SC2Race.ZERG, origin={"hots"}, classification=ItemClassification.progression),
+ # Handled separately from other abilities
+ ItemNames.KERRIGAN_PRIMAL_FORM: ItemData(421 + SC2HOTS_ITEM_ID_OFFSET, "Primal Form", 0, SC2Race.ZERG, origin={"hots"}),
+
+ ItemNames.KERRIGAN_LEVELS_10: ItemData(500 + SC2HOTS_ITEM_ID_OFFSET, "Level", 10, SC2Race.ZERG, origin={"hots"}, quantity=0, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_LEVELS_9: ItemData(501 + SC2HOTS_ITEM_ID_OFFSET, "Level", 9, SC2Race.ZERG, origin={"hots"}, quantity=0, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_LEVELS_8: ItemData(502 + SC2HOTS_ITEM_ID_OFFSET, "Level", 8, SC2Race.ZERG, origin={"hots"}, quantity=0, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_LEVELS_7: ItemData(503 + SC2HOTS_ITEM_ID_OFFSET, "Level", 7, SC2Race.ZERG, origin={"hots"}, quantity=0, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_LEVELS_6: ItemData(504 + SC2HOTS_ITEM_ID_OFFSET, "Level", 6, SC2Race.ZERG, origin={"hots"}, quantity=0, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_LEVELS_5: ItemData(505 + SC2HOTS_ITEM_ID_OFFSET, "Level", 5, SC2Race.ZERG, origin={"hots"}, quantity=0, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_LEVELS_4: ItemData(506 + SC2HOTS_ITEM_ID_OFFSET, "Level", 4, SC2Race.ZERG, origin={"hots"}, quantity=0, classification=ItemClassification.progression_skip_balancing),
+ ItemNames.KERRIGAN_LEVELS_3: ItemData(507 + SC2HOTS_ITEM_ID_OFFSET, "Level", 3, SC2Race.ZERG, origin={"hots"}, quantity=0, classification=ItemClassification.progression_skip_balancing),
+ ItemNames.KERRIGAN_LEVELS_2: ItemData(508 + SC2HOTS_ITEM_ID_OFFSET, "Level", 2, SC2Race.ZERG, origin={"hots"}, quantity=0, classification=ItemClassification.progression_skip_balancing),
+ ItemNames.KERRIGAN_LEVELS_1: ItemData(509 + SC2HOTS_ITEM_ID_OFFSET, "Level", 1, SC2Race.ZERG, origin={"hots"}, quantity=0, classification=ItemClassification.progression_skip_balancing),
+ ItemNames.KERRIGAN_LEVELS_14: ItemData(510 + SC2HOTS_ITEM_ID_OFFSET, "Level", 14, SC2Race.ZERG, origin={"hots"}, quantity=0, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_LEVELS_35: ItemData(511 + SC2HOTS_ITEM_ID_OFFSET, "Level", 35, SC2Race.ZERG, origin={"hots"}, quantity=0, classification=ItemClassification.progression),
+ ItemNames.KERRIGAN_LEVELS_70: ItemData(512 + SC2HOTS_ITEM_ID_OFFSET, "Level", 70, SC2Race.ZERG, origin={"hots"}, quantity=0, classification=ItemClassification.progression),
+
+ # Zerg Mercs
+ ItemNames.INFESTED_MEDICS: ItemData(600 + SC2HOTS_ITEM_ID_OFFSET, "Mercenary", 0, SC2Race.ZERG, origin={"ext"}),
+ ItemNames.INFESTED_SIEGE_TANKS: ItemData(601 + SC2HOTS_ITEM_ID_OFFSET, "Mercenary", 1, SC2Race.ZERG, origin={"ext"}),
+ ItemNames.INFESTED_BANSHEES: ItemData(602 + SC2HOTS_ITEM_ID_OFFSET, "Mercenary", 2, SC2Race.ZERG, origin={"ext"}),
+
+ # Misc Upgrades
+ ItemNames.OVERLORD_VENTRAL_SACS: ItemData(700 + SC2HOTS_ITEM_ID_OFFSET, "Evolution Pit", 6, SC2Race.ZERG, origin={"bw"}),
+
+ # Morphs
+ ItemNames.MUTALISK_CORRUPTOR_GUARDIAN_ASPECT: ItemData(800 + SC2HOTS_ITEM_ID_OFFSET, "Morph", 6, SC2Race.ZERG, origin={"bw"}),
+ ItemNames.MUTALISK_CORRUPTOR_DEVOURER_ASPECT: ItemData(801 + SC2HOTS_ITEM_ID_OFFSET, "Morph", 7, SC2Race.ZERG, origin={"bw"}),
+ ItemNames.ROACH_RAVAGER_ASPECT: ItemData(802 + SC2HOTS_ITEM_ID_OFFSET, "Morph", 8, SC2Race.ZERG, origin={"ext"}),
+
+
+ # Protoss Units (those that aren't as items in WoL (Prophecy))
+ ItemNames.OBSERVER: ItemData(0 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 9, SC2Race.PROTOSS,
+ classification=ItemClassification.filler, origin={"wol"},
+ description="Flying spy. Cloak renders the unit invisible to enemies without detection."),
+ ItemNames.CENTURION: ItemData(1 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 10, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Powerful melee warrior. Has the Shadow Charge and Darkcoil abilities."),
+ ItemNames.SENTINEL: ItemData(2 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 11, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Powerful melee warrior. Has the Charge and Reconstruction abilities."),
+ ItemNames.SUPPLICANT: ItemData(3 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 12, SC2Race.PROTOSS,
+ classification=ItemClassification.filler, important_for_filtering=True, origin={"ext"},
+ description="Powerful melee warrior. Has powerful damage resistant shields."),
+ ItemNames.INSTIGATOR: ItemData(4 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 13, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"ext"},
+ description="Ranged support strider. Can store multiple Blink charges."),
+ ItemNames.SLAYER: ItemData(5 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 14, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"ext"},
+ description="Ranged attack strider. Can use the Phase Blink and Phasing Armor abilities."),
+ ItemNames.SENTRY: ItemData(6 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 15, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Robotic support unit can use the Guardian Shield ability and restore the shields of nearby Protoss units."),
+ ItemNames.ENERGIZER: ItemData(7 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 16, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Robotic support unit. Can use the Chrono Beam ability and become stationary to power nearby structures."),
+ ItemNames.HAVOC: ItemData(8 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 17, SC2Race.PROTOSS,
+ origin={"lotv"}, important_for_filtering=True,
+ description="Robotic support unit. Can use the Target Lock and Force Field abilities and increase the range of nearby Protoss units."),
+ ItemNames.SIGNIFIER: ItemData(9 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 18, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"ext"},
+ description="Potent permanently cloaked psionic master. Can use the Feedback and Crippling Psionic Storm abilities. Can merge into an Archon."),
+ ItemNames.ASCENDANT: ItemData(10 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 19, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Potent psionic master. Can use the Psionic Orb, Mind Blast, and Sacrifice abilities."),
+ ItemNames.AVENGER: ItemData(11 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 20, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Deadly warrior-assassin. Permanently cloaked. Recalls to the nearest Dark Shrine upon death."),
+ ItemNames.BLOOD_HUNTER: ItemData(12 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 21, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Deadly warrior-assassin. Permanently cloaked. Can use the Void Stasis ability."),
+ ItemNames.DRAGOON: ItemData(13 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 22, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Ranged assault strider. Has enhanced health and damage."),
+ ItemNames.DARK_ARCHON: ItemData(14 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 23, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Potent psionic master. Can use the Confuse and Mind Control abilities."),
+ ItemNames.ADEPT: ItemData(15 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 24, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Ranged specialist. Can use the Psionic Transfer ability."),
+ ItemNames.WARP_PRISM: ItemData(16 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 25, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"ext"},
+ description="Flying transport. Can carry units and become stationary to deploy a power field."),
+ ItemNames.ANNIHILATOR: ItemData(17 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 26, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Assault Strider. Can use the Shadow Cannon ability to damage air and ground units."),
+ ItemNames.VANGUARD: ItemData(18 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 27, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Assault Strider. Deals splash damage around the primary target."),
+ ItemNames.WRATHWALKER: ItemData(19 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 28, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Battle strider with a powerful single target attack. Can walk up and down cliffs."),
+ ItemNames.REAVER: ItemData(20 + SC2LOTV_ITEM_ID_OFFSET, "Unit", 29, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Area damage siege unit. Builds and launches explosive Scarabs for high burst damage."),
+ ItemNames.DISRUPTOR: ItemData(21 + SC2LOTV_ITEM_ID_OFFSET, "Unit 2", 0, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"ext"},
+ description="Robotic disruption unit. Can use the Purification Nova ability to deal heavy area damage."),
+ ItemNames.MIRAGE: ItemData(22 + SC2LOTV_ITEM_ID_OFFSET, "Unit 2", 1, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Air superiority starfighter. Can use Graviton Beam and Phasing Armor abilities."),
+ ItemNames.CORSAIR: ItemData(23 + SC2LOTV_ITEM_ID_OFFSET, "Unit 2", 2, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Air superiority starfighter. Can use the Disruption Web ability."),
+ ItemNames.DESTROYER: ItemData(24 + SC2LOTV_ITEM_ID_OFFSET, "Unit 2", 3, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Area assault craft. Can use the Destruction Beam ability to attack multiple units at once."),
+ ItemNames.SCOUT: ItemData(25 + SC2LOTV_ITEM_ID_OFFSET, "Unit 2", 4, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"ext"},
+ description="Versatile high-speed fighter."),
+ ItemNames.TEMPEST: ItemData(26 + SC2LOTV_ITEM_ID_OFFSET, "Unit 2", 5, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Siege artillery craft. Attacks from long range. Can use the Disintegration ability."),
+ ItemNames.MOTHERSHIP: ItemData(27 + SC2LOTV_ITEM_ID_OFFSET, "Unit 2", 6, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Ultimate Protoss vessel, Can use the Vortex and Mass Recall abilities. Cloaks nearby units and structures."),
+ ItemNames.ARBITER: ItemData(28 + SC2LOTV_ITEM_ID_OFFSET, "Unit 2", 7, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="Army support craft. Has the Stasis Field and Recall abilities. Cloaks nearby units."),
+ ItemNames.ORACLE: ItemData(29 + SC2LOTV_ITEM_ID_OFFSET, "Unit 2", 8, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"ext"},
+ description="Flying caster. Can use the Revelation and Stasis Ward abilities."),
+
+ # Protoss Upgrades
+ ItemNames.PROGRESSIVE_PROTOSS_GROUND_WEAPON: ItemData(100 + SC2LOTV_ITEM_ID_OFFSET, "Upgrade", 0, SC2Race.PROTOSS, quantity=3, origin={"wol", "lotv"}),
+ ItemNames.PROGRESSIVE_PROTOSS_GROUND_ARMOR: ItemData(101 + SC2LOTV_ITEM_ID_OFFSET, "Upgrade", 2, SC2Race.PROTOSS, quantity=3, origin={"wol", "lotv"}),
+ ItemNames.PROGRESSIVE_PROTOSS_SHIELDS: ItemData(102 + SC2LOTV_ITEM_ID_OFFSET, "Upgrade", 4, SC2Race.PROTOSS, quantity=3, origin={"wol", "lotv"}),
+ ItemNames.PROGRESSIVE_PROTOSS_AIR_WEAPON: ItemData(103 + SC2LOTV_ITEM_ID_OFFSET, "Upgrade", 6, SC2Race.PROTOSS, quantity=3, origin={"wol", "lotv"}),
+ ItemNames.PROGRESSIVE_PROTOSS_AIR_ARMOR: ItemData(104 + SC2LOTV_ITEM_ID_OFFSET, "Upgrade", 8, SC2Race.PROTOSS, quantity=3, origin={"wol", "lotv"}),
+ # Upgrade bundle 'number' values are used as indices to get affected 'number's
+ ItemNames.PROGRESSIVE_PROTOSS_WEAPON_UPGRADE: ItemData(105 + SC2LOTV_ITEM_ID_OFFSET, "Upgrade", 11, SC2Race.PROTOSS, quantity=3, origin={"wol", "lotv"}),
+ ItemNames.PROGRESSIVE_PROTOSS_ARMOR_UPGRADE: ItemData(106 + SC2LOTV_ITEM_ID_OFFSET, "Upgrade", 12, SC2Race.PROTOSS, quantity=3, origin={"wol", "lotv"}),
+ ItemNames.PROGRESSIVE_PROTOSS_GROUND_UPGRADE: ItemData(107 + SC2LOTV_ITEM_ID_OFFSET, "Upgrade", 13, SC2Race.PROTOSS, quantity=3, origin={"wol", "lotv"}),
+ ItemNames.PROGRESSIVE_PROTOSS_AIR_UPGRADE: ItemData(108 + SC2LOTV_ITEM_ID_OFFSET, "Upgrade", 14, SC2Race.PROTOSS, quantity=3, origin={"wol", "lotv"}),
+ ItemNames.PROGRESSIVE_PROTOSS_WEAPON_ARMOR_UPGRADE: ItemData(109 + SC2LOTV_ITEM_ID_OFFSET, "Upgrade", 15, SC2Race.PROTOSS, quantity=3, origin={"wol", "lotv"}),
+
+ # Protoss Buildings
+ ItemNames.PHOTON_CANNON: ItemData(200 + SC2LOTV_ITEM_ID_OFFSET, "Building", 0, SC2Race.PROTOSS, classification=ItemClassification.progression, origin={"wol", "lotv"}),
+ ItemNames.KHAYDARIN_MONOLITH: ItemData(201 + SC2LOTV_ITEM_ID_OFFSET, "Building", 1, SC2Race.PROTOSS, classification=ItemClassification.progression, origin={"lotv"}),
+ ItemNames.SHIELD_BATTERY: ItemData(202 + SC2LOTV_ITEM_ID_OFFSET, "Building", 2, SC2Race.PROTOSS, classification=ItemClassification.progression, origin={"lotv"}),
+
+ # Protoss Unit Upgrades
+ ItemNames.SUPPLICANT_BLOOD_SHIELD: ItemData(300 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 0, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"ext"}, parent_item=ItemNames.SUPPLICANT),
+ ItemNames.SUPPLICANT_SOUL_AUGMENTATION: ItemData(301 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 1, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"ext"}, parent_item=ItemNames.SUPPLICANT),
+ ItemNames.SUPPLICANT_SHIELD_REGENERATION: ItemData(302 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 2, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"ext"}, parent_item=ItemNames.SUPPLICANT),
+ ItemNames.ADEPT_SHOCKWAVE: ItemData(303 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 3, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.ADEPT),
+ ItemNames.ADEPT_RESONATING_GLAIVES: ItemData(304 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 4, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.ADEPT),
+ ItemNames.ADEPT_PHASE_BULWARK: ItemData(305 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 5, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.ADEPT),
+ ItemNames.STALKER_INSTIGATOR_SLAYER_DISINTEGRATING_PARTICLES: ItemData(306 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 6, SC2Race.PROTOSS, origin={"ext"}, classification=ItemClassification.progression),
+ ItemNames.STALKER_INSTIGATOR_SLAYER_PARTICLE_REFLECTION: ItemData(307 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 7, SC2Race.PROTOSS, origin={"ext"}, classification=ItemClassification.progression),
+ ItemNames.DRAGOON_HIGH_IMPACT_PHASE_DISRUPTORS: ItemData(308 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 8, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.DRAGOON),
+ ItemNames.DRAGOON_TRILLIC_COMPRESSION_SYSTEM: ItemData(309 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 9, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.DRAGOON),
+ ItemNames.DRAGOON_SINGULARITY_CHARGE: ItemData(310 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 10, SC2Race.PROTOSS, origin={"bw"}, parent_item=ItemNames.DRAGOON),
+ ItemNames.DRAGOON_ENHANCED_STRIDER_SERVOS: ItemData(311 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 11, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"bw"}, parent_item=ItemNames.DRAGOON),
+ ItemNames.SCOUT_COMBAT_SENSOR_ARRAY: ItemData(312 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 12, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.SCOUT),
+ ItemNames.SCOUT_APIAL_SENSORS: ItemData(313 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 13, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"bw"}, parent_item=ItemNames.SCOUT),
+ ItemNames.SCOUT_GRAVITIC_THRUSTERS: ItemData(314 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 14, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"bw"}, parent_item=ItemNames.SCOUT),
+ ItemNames.SCOUT_ADVANCED_PHOTON_BLASTERS: ItemData(315 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 15, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.SCOUT),
+ ItemNames.TEMPEST_TECTONIC_DESTABILIZERS: ItemData(316 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 16, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"ext"}, parent_item=ItemNames.TEMPEST),
+ ItemNames.TEMPEST_QUANTIC_REACTOR: ItemData(317 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 17, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"ext"}, parent_item=ItemNames.TEMPEST),
+ ItemNames.TEMPEST_GRAVITY_SLING: ItemData(318 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 18, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.TEMPEST),
+ ItemNames.PHOENIX_MIRAGE_IONIC_WAVELENGTH_FLUX: ItemData(319 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 19, SC2Race.PROTOSS, origin={"ext"}),
+ ItemNames.PHOENIX_MIRAGE_ANION_PULSE_CRYSTALS: ItemData(320 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 20, SC2Race.PROTOSS, origin={"ext"}),
+ ItemNames.CORSAIR_STEALTH_DRIVE: ItemData(321 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 21, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.CORSAIR),
+ ItemNames.CORSAIR_ARGUS_JEWEL: ItemData(322 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 22, SC2Race.PROTOSS, origin={"bw"}, parent_item=ItemNames.CORSAIR),
+ ItemNames.CORSAIR_SUSTAINING_DISRUPTION: ItemData(323 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 23, SC2Race.PROTOSS, origin={"bw"}, parent_item=ItemNames.CORSAIR),
+ ItemNames.CORSAIR_NEUTRON_SHIELDS: ItemData(324 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 24, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"bw"}, parent_item=ItemNames.CORSAIR),
+ ItemNames.ORACLE_STEALTH_DRIVE: ItemData(325 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 25, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.ORACLE),
+ ItemNames.ORACLE_STASIS_CALIBRATION: ItemData(326 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 26, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.ORACLE),
+ ItemNames.ORACLE_TEMPORAL_ACCELERATION_BEAM: ItemData(327 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 27, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.ORACLE),
+ ItemNames.ARBITER_CHRONOSTATIC_REINFORCEMENT: ItemData(328 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 28, SC2Race.PROTOSS, origin={"bw"}, parent_item=ItemNames.ARBITER),
+ ItemNames.ARBITER_KHAYDARIN_CORE: ItemData(329 + SC2LOTV_ITEM_ID_OFFSET, "Forge 1", 29, SC2Race.PROTOSS, origin={"bw"}, parent_item=ItemNames.ARBITER),
+ ItemNames.ARBITER_SPACETIME_ANCHOR: ItemData(330 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 0, SC2Race.PROTOSS, origin={"bw"}, parent_item=ItemNames.ARBITER),
+ ItemNames.ARBITER_RESOURCE_EFFICIENCY: ItemData(331 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 1, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"bw"}, parent_item=ItemNames.ARBITER),
+ ItemNames.ARBITER_ENHANCED_CLOAK_FIELD: ItemData(332 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 2, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"bw"}, parent_item=ItemNames.ARBITER),
+ ItemNames.CARRIER_GRAVITON_CATAPULT:
+ ItemData(333 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 3, SC2Race.PROTOSS, origin={"wol"},
+ parent_item=ItemNames.CARRIER,
+ description="Carriers can launch Interceptors more quickly."),
+ ItemNames.CARRIER_HULL_OF_PAST_GLORIES:
+ ItemData(334 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 4, SC2Race.PROTOSS, origin={"bw"},
+ parent_item=ItemNames.CARRIER,
+ description="Carriers gain +2 armour."),
+ ItemNames.VOID_RAY_DESTROYER_FLUX_VANES:
+ ItemData(335 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 5, SC2Race.PROTOSS, classification=ItemClassification.filler,
+ origin={"ext"},
+ description="Increases Void Ray and Destroyer movement speed."),
+ ItemNames.DESTROYER_REFORGED_BLOODSHARD_CORE:
+ ItemData(336 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 6, SC2Race.PROTOSS, origin={"ext"},
+ parent_item=ItemNames.DESTROYER,
+ description="When fully charged, the Destroyer's Destruction Beam weapon does full damage to secondary targets."),
+ ItemNames.WARP_PRISM_GRAVITIC_DRIVE:
+ ItemData(337 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 7, SC2Race.PROTOSS, classification=ItemClassification.filler,
+ origin={"ext"}, parent_item=ItemNames.WARP_PRISM,
+ description="Increases the movement speed of Warp Prisms."),
+ ItemNames.WARP_PRISM_PHASE_BLASTER:
+ ItemData(338 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 8, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"ext"}, parent_item=ItemNames.WARP_PRISM,
+ description="Equips Warp Prisms with an auto-attack that can hit ground and air targets."),
+ ItemNames.WARP_PRISM_WAR_CONFIGURATION: ItemData(339 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 9, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.WARP_PRISM),
+ ItemNames.OBSERVER_GRAVITIC_BOOSTERS: ItemData(340 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 10, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"bw"}, parent_item=ItemNames.OBSERVER),
+ ItemNames.OBSERVER_SENSOR_ARRAY: ItemData(341 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 11, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"bw"}, parent_item=ItemNames.OBSERVER),
+ ItemNames.REAVER_SCARAB_DAMAGE: ItemData(342 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 12, SC2Race.PROTOSS, origin={"bw"}, parent_item=ItemNames.REAVER),
+ ItemNames.REAVER_SOLARITE_PAYLOAD: ItemData(343 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 13, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.REAVER),
+ ItemNames.REAVER_REAVER_CAPACITY: ItemData(344 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 14, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"bw"}, parent_item=ItemNames.REAVER),
+ ItemNames.REAVER_RESOURCE_EFFICIENCY: ItemData(345 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 15, SC2Race.PROTOSS, origin={"bw"}, parent_item=ItemNames.REAVER),
+ ItemNames.VANGUARD_AGONY_LAUNCHERS: ItemData(346 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 16, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.VANGUARD),
+ ItemNames.VANGUARD_MATTER_DISPERSION: ItemData(347 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 17, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.VANGUARD),
+ ItemNames.IMMORTAL_ANNIHILATOR_SINGULARITY_CHARGE: ItemData(348 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 18, SC2Race.PROTOSS, origin={"ext"}),
+ ItemNames.IMMORTAL_ANNIHILATOR_ADVANCED_TARGETING_MECHANICS: ItemData(349 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 19, SC2Race.PROTOSS, classification=ItemClassification.progression, origin={"ext"}),
+ ItemNames.COLOSSUS_PACIFICATION_PROTOCOL: ItemData(350 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 20, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.COLOSSUS),
+ ItemNames.WRATHWALKER_RAPID_POWER_CYCLING: ItemData(351 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 21, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.WRATHWALKER),
+ ItemNames.WRATHWALKER_EYE_OF_WRATH: ItemData(352 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 22, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"ext"}, parent_item=ItemNames.WRATHWALKER),
+ ItemNames.DARK_TEMPLAR_AVENGER_BLOOD_HUNTER_SHROUD_OF_ADUN: ItemData(353 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 23, SC2Race.PROTOSS, origin={"ext"}),
+ ItemNames.DARK_TEMPLAR_AVENGER_BLOOD_HUNTER_SHADOW_GUARD_TRAINING: ItemData(354 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 24, SC2Race.PROTOSS, origin={"bw"}),
+ ItemNames.DARK_TEMPLAR_AVENGER_BLOOD_HUNTER_BLINK: ItemData(355 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 25, SC2Race.PROTOSS, classification=ItemClassification.progression, origin={"ext"}),
+ ItemNames.DARK_TEMPLAR_AVENGER_BLOOD_HUNTER_RESOURCE_EFFICIENCY: ItemData(356 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 26, SC2Race.PROTOSS, origin={"ext"}),
+ ItemNames.DARK_TEMPLAR_DARK_ARCHON_MELD: ItemData(357 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 27, SC2Race.PROTOSS, origin={"bw"}, important_for_filtering=True ,parent_item=ItemNames.DARK_TEMPLAR),
+ ItemNames.HIGH_TEMPLAR_SIGNIFIER_UNSHACKLED_PSIONIC_STORM: ItemData(358 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 28, SC2Race.PROTOSS, origin={"bw"}),
+ ItemNames.HIGH_TEMPLAR_SIGNIFIER_HALLUCINATION: ItemData(359 + SC2LOTV_ITEM_ID_OFFSET, "Forge 2", 29, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"bw"}),
+ ItemNames.HIGH_TEMPLAR_SIGNIFIER_KHAYDARIN_AMULET: ItemData(360 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 0, SC2Race.PROTOSS, origin={"bw"}),
+ ItemNames.ARCHON_HIGH_ARCHON: ItemData(361 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 1, SC2Race.PROTOSS, origin={"ext"}, important_for_filtering=True),
+ ItemNames.DARK_ARCHON_FEEDBACK: ItemData(362 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 2, SC2Race.PROTOSS, origin={"bw"}),
+ ItemNames.DARK_ARCHON_MAELSTROM: ItemData(363 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 3, SC2Race.PROTOSS, origin={"bw"}),
+ ItemNames.DARK_ARCHON_ARGUS_TALISMAN: ItemData(364 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 4, SC2Race.PROTOSS, origin={"bw"}),
+ ItemNames.ASCENDANT_POWER_OVERWHELMING: ItemData(365 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 5, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.ASCENDANT),
+ ItemNames.ASCENDANT_CHAOTIC_ATTUNEMENT: ItemData(366 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 6, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.ASCENDANT),
+ ItemNames.ASCENDANT_BLOOD_AMULET: ItemData(367 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 7, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.ASCENDANT),
+ ItemNames.SENTRY_ENERGIZER_HAVOC_CLOAKING_MODULE: ItemData(368 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 8, SC2Race.PROTOSS, origin={"ext"}),
+ ItemNames.SENTRY_ENERGIZER_HAVOC_SHIELD_BATTERY_RAPID_RECHARGING: ItemData(369 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 9, SC2Race.PROTOSS, origin={"ext"}),
+ ItemNames.SENTRY_FORCE_FIELD: ItemData(370 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 10, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"ext"}, parent_item=ItemNames.SENTRY),
+ ItemNames.SENTRY_HALLUCINATION: ItemData(371 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 11, SC2Race.PROTOSS, classification=ItemClassification.filler, origin={"ext"}, parent_item=ItemNames.SENTRY),
+ ItemNames.ENERGIZER_RECLAMATION: ItemData(372 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 12, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.ENERGIZER),
+ ItemNames.ENERGIZER_FORGED_CHASSIS: ItemData(373 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 13, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.ENERGIZER),
+ ItemNames.HAVOC_DETECT_WEAKNESS: ItemData(374 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 14, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.HAVOC),
+ ItemNames.HAVOC_BLOODSHARD_RESONANCE: ItemData(375 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 15, SC2Race.PROTOSS, origin={"ext"}, parent_item=ItemNames.HAVOC),
+ ItemNames.ZEALOT_SENTINEL_CENTURION_LEG_ENHANCEMENTS: ItemData(376 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 16, SC2Race.PROTOSS, origin={"bw"}),
+ ItemNames.ZEALOT_SENTINEL_CENTURION_SHIELD_CAPACITY: ItemData(377 + SC2LOTV_ITEM_ID_OFFSET, "Forge 3", 17, SC2Race.PROTOSS, origin={"bw"}),
+
+ # SoA Calldown powers
+ ItemNames.SOA_CHRONO_SURGE: ItemData(700 + SC2LOTV_ITEM_ID_OFFSET, "Spear of Adun", 0, SC2Race.PROTOSS, origin={"lotv"}),
+ ItemNames.SOA_PROGRESSIVE_PROXY_PYLON: ItemData(701 + SC2LOTV_ITEM_ID_OFFSET, "Progressive Upgrade", 0, SC2Race.PROTOSS, origin={"lotv"}, quantity=2),
+ ItemNames.SOA_PYLON_OVERCHARGE: ItemData(702 + SC2LOTV_ITEM_ID_OFFSET, "Spear of Adun", 1, SC2Race.PROTOSS, origin={"ext"}),
+ ItemNames.SOA_ORBITAL_STRIKE: ItemData(703 + SC2LOTV_ITEM_ID_OFFSET, "Spear of Adun", 2, SC2Race.PROTOSS, origin={"lotv"}),
+ ItemNames.SOA_TEMPORAL_FIELD: ItemData(704 + SC2LOTV_ITEM_ID_OFFSET, "Spear of Adun", 3, SC2Race.PROTOSS, origin={"lotv"}),
+ ItemNames.SOA_SOLAR_LANCE: ItemData(705 + SC2LOTV_ITEM_ID_OFFSET, "Spear of Adun", 4, SC2Race.PROTOSS, classification=ItemClassification.progression, origin={"lotv"}),
+ ItemNames.SOA_MASS_RECALL: ItemData(706 + SC2LOTV_ITEM_ID_OFFSET, "Spear of Adun", 5, SC2Race.PROTOSS, origin={"lotv"}),
+ ItemNames.SOA_SHIELD_OVERCHARGE: ItemData(707 + SC2LOTV_ITEM_ID_OFFSET, "Spear of Adun", 6, SC2Race.PROTOSS, origin={"lotv"}),
+ ItemNames.SOA_DEPLOY_FENIX: ItemData(708 + SC2LOTV_ITEM_ID_OFFSET, "Spear of Adun", 7, SC2Race.PROTOSS, classification=ItemClassification.progression, origin={"lotv"}),
+ ItemNames.SOA_PURIFIER_BEAM: ItemData(709 + SC2LOTV_ITEM_ID_OFFSET, "Spear of Adun", 8, SC2Race.PROTOSS, origin={"lotv"}),
+ ItemNames.SOA_TIME_STOP: ItemData(710 + SC2LOTV_ITEM_ID_OFFSET, "Spear of Adun", 9, SC2Race.PROTOSS, classification=ItemClassification.progression, origin={"lotv"}),
+ ItemNames.SOA_SOLAR_BOMBARDMENT: ItemData(711 + SC2LOTV_ITEM_ID_OFFSET, "Spear of Adun", 10, SC2Race.PROTOSS, origin={"lotv"}),
+
+ # Generic Protoss Upgrades
+ ItemNames.MATRIX_OVERLOAD:
+ ItemData(800 + SC2LOTV_ITEM_ID_OFFSET, "Solarite Core", 0, SC2Race.PROTOSS, origin={"lotv"},
+ description=r"All friendly units gain 25% movement speed and 15% attack speed within a Pylon's power field and for 15 seconds after leaving it."),
+ ItemNames.QUATRO:
+ ItemData(801 + SC2LOTV_ITEM_ID_OFFSET, "Solarite Core", 1, SC2Race.PROTOSS, origin={"ext"},
+ description="All friendly Protoss units gain the equivalent of their +1 armour, attack, and shield upgrades."),
+ ItemNames.NEXUS_OVERCHARGE:
+ ItemData(802 + SC2LOTV_ITEM_ID_OFFSET, "Solarite Core", 2, SC2Race.PROTOSS, origin={"lotv"},
+ important_for_filtering=True, description="The Protoss Nexus gains a long-range auto-attack."),
+ ItemNames.ORBITAL_ASSIMILATORS:
+ ItemData(803 + SC2LOTV_ITEM_ID_OFFSET, "Solarite Core", 3, SC2Race.PROTOSS, origin={"lotv"},
+ description="Assimilators automatically harvest Vespene Gas without the need for Probes."),
+ ItemNames.WARP_HARMONIZATION:
+ ItemData(804 + SC2LOTV_ITEM_ID_OFFSET, "Solarite Core", 4, SC2Race.PROTOSS, origin={"lotv"},
+ description=r"Stargates and Robotics Facilities can transform to utilize Warp In technology. Warp In cooldowns are 20% faster than original build times."),
+ ItemNames.GUARDIAN_SHELL:
+ ItemData(805 + SC2LOTV_ITEM_ID_OFFSET, "Solarite Core", 5, SC2Race.PROTOSS, origin={"lotv"},
+ description="The Spear of Adun passively shields friendly Protoss units before death, making them invulnerable for 5 seconds. Each unit can only be shielded once every 60 seconds."),
+ ItemNames.RECONSTRUCTION_BEAM:
+ ItemData(806 + SC2LOTV_ITEM_ID_OFFSET, "Solarite Core", 6, SC2Race.PROTOSS,
+ classification=ItemClassification.progression, origin={"lotv"},
+ description="The Spear of Adun will passively heal mechanical units for 5 and non-biological structures for 10 life per second. Up to 3 targets can be repaired at once."),
+ ItemNames.OVERWATCH:
+ ItemData(807 + SC2LOTV_ITEM_ID_OFFSET, "Solarite Core", 7, SC2Race.PROTOSS, origin={"ext"},
+ description="Once per second, the Spear of Adun will last-hit a damaged enemy unit that is below 50 health."),
+ ItemNames.SUPERIOR_WARP_GATES:
+ ItemData(808 + SC2LOTV_ITEM_ID_OFFSET, "Solarite Core", 8, SC2Race.PROTOSS, origin={"ext"},
+ description="Protoss Warp Gates can hold up to 3 charges of unit warp-ins."),
+ ItemNames.ENHANCED_TARGETING:
+ ItemData(809 + SC2LOTV_ITEM_ID_OFFSET, "Solarite Core", 9, SC2Race.PROTOSS, origin={"ext"},
+ description="Protoss defensive structures gain +2 range."),
+ ItemNames.OPTIMIZED_ORDNANCE:
+ ItemData(810 + SC2LOTV_ITEM_ID_OFFSET, "Solarite Core", 10, SC2Race.PROTOSS, origin={"ext"},
+ description="Increases the attack speed of Protoss defensive structures by 25%."),
+ ItemNames.KHALAI_INGENUITY:
+ ItemData(811 + SC2LOTV_ITEM_ID_OFFSET, "Solarite Core", 11, SC2Race.PROTOSS, origin={"ext"},
+ description="Pylons, Photon Cannons, Monoliths, and Shield Batteries warp in near-instantly."),
+ ItemNames.AMPLIFIED_ASSIMILATORS:
+ ItemData(812 + SC2LOTV_ITEM_ID_OFFSET, "Solarite Core", 12, SC2Race.PROTOSS, origin={"ext"},
+ description=r"Assimilators produce Vespene gas 25% faster."),
+}
+
+
+def get_item_table():
+ return item_table
+
+
+basic_units = {
+ SC2Race.TERRAN: {
+ ItemNames.MARINE,
+ ItemNames.MARAUDER,
+ ItemNames.GOLIATH,
+ ItemNames.HELLION,
+ ItemNames.VULTURE,
+ ItemNames.WARHOUND,
+ },
+ SC2Race.ZERG: {
+ ItemNames.ZERGLING,
+ ItemNames.SWARM_QUEEN,
+ ItemNames.ROACH,
+ ItemNames.HYDRALISK,
+ },
+ SC2Race.PROTOSS: {
+ ItemNames.ZEALOT,
+ ItemNames.CENTURION,
+ ItemNames.SENTINEL,
+ ItemNames.STALKER,
+ ItemNames.INSTIGATOR,
+ ItemNames.SLAYER,
+ ItemNames.DRAGOON,
+ ItemNames.ADEPT,
+ }
+}
+
+advanced_basic_units = {
+ SC2Race.TERRAN: basic_units[SC2Race.TERRAN].union({
+ ItemNames.REAPER,
+ ItemNames.DIAMONDBACK,
+ ItemNames.VIKING,
+ ItemNames.SIEGE_TANK,
+ ItemNames.BANSHEE,
+ ItemNames.THOR,
+ ItemNames.BATTLECRUISER,
+ ItemNames.CYCLONE
+ }),
+ SC2Race.ZERG: basic_units[SC2Race.ZERG].union({
+ ItemNames.INFESTOR,
+ ItemNames.ABERRATION,
+ }),
+ SC2Race.PROTOSS: basic_units[SC2Race.PROTOSS].union({
+ ItemNames.DARK_TEMPLAR,
+ ItemNames.BLOOD_HUNTER,
+ ItemNames.AVENGER,
+ ItemNames.IMMORTAL,
+ ItemNames.ANNIHILATOR,
+ ItemNames.VANGUARD,
+ })
+}
+
+no_logic_starting_units = {
+ SC2Race.TERRAN: advanced_basic_units[SC2Race.TERRAN].union({
+ ItemNames.FIREBAT,
+ ItemNames.GHOST,
+ ItemNames.SPECTRE,
+ ItemNames.WRAITH,
+ ItemNames.RAVEN,
+ ItemNames.PREDATOR,
+ ItemNames.LIBERATOR,
+ ItemNames.HERC,
+ }),
+ SC2Race.ZERG: advanced_basic_units[SC2Race.ZERG].union({
+ ItemNames.ULTRALISK,
+ ItemNames.SWARM_HOST
+ }),
+ SC2Race.PROTOSS: advanced_basic_units[SC2Race.PROTOSS].union({
+ ItemNames.CARRIER,
+ ItemNames.TEMPEST,
+ ItemNames.VOID_RAY,
+ ItemNames.DESTROYER,
+ ItemNames.COLOSSUS,
+ ItemNames.WRATHWALKER,
+ ItemNames.SCOUT,
+ ItemNames.HIGH_TEMPLAR,
+ ItemNames.SIGNIFIER,
+ ItemNames.ASCENDANT,
+ ItemNames.DARK_ARCHON,
+ ItemNames.SUPPLICANT,
+ })
+}
+
+not_balanced_starting_units = {
+ ItemNames.SIEGE_TANK,
+ ItemNames.THOR,
+ ItemNames.BANSHEE,
+ ItemNames.BATTLECRUISER,
+ ItemNames.ULTRALISK,
+ ItemNames.CARRIER,
+ ItemNames.TEMPEST,
+}
+
+
+def get_basic_units(world: World, race: SC2Race) -> typing.Set[str]:
+ logic_level = get_option_value(world, 'required_tactics')
+ if logic_level == RequiredTactics.option_no_logic:
+ return no_logic_starting_units[race]
+ elif logic_level == RequiredTactics.option_advanced:
+ return advanced_basic_units[race]
+ else:
+ return basic_units[race]
+
+
+# Items that can be placed before resources if not already in
+# General upgrades and Mercs
+second_pass_placeable_items: typing.Tuple[str, ...] = (
+ # Global weapon/armor upgrades
+ ItemNames.PROGRESSIVE_TERRAN_ARMOR_UPGRADE,
+ ItemNames.PROGRESSIVE_TERRAN_WEAPON_UPGRADE,
+ ItemNames.PROGRESSIVE_TERRAN_WEAPON_ARMOR_UPGRADE,
+ ItemNames.PROGRESSIVE_ZERG_ARMOR_UPGRADE,
+ ItemNames.PROGRESSIVE_ZERG_WEAPON_UPGRADE,
+ ItemNames.PROGRESSIVE_ZERG_WEAPON_ARMOR_UPGRADE,
+ ItemNames.PROGRESSIVE_PROTOSS_ARMOR_UPGRADE,
+ ItemNames.PROGRESSIVE_PROTOSS_WEAPON_UPGRADE,
+ ItemNames.PROGRESSIVE_PROTOSS_WEAPON_ARMOR_UPGRADE,
+ ItemNames.PROGRESSIVE_PROTOSS_SHIELDS,
+ # Terran Buildings without upgrades
+ ItemNames.SENSOR_TOWER,
+ ItemNames.HIVE_MIND_EMULATOR,
+ ItemNames.PSI_DISRUPTER,
+ ItemNames.PERDITION_TURRET,
+ # Terran units without upgrades
+ ItemNames.HERC,
+ ItemNames.WARHOUND,
+ # General Terran upgrades without any dependencies
+ ItemNames.SCV_ADVANCED_CONSTRUCTION,
+ ItemNames.SCV_DUAL_FUSION_WELDERS,
+ ItemNames.PROGRESSIVE_FIRE_SUPPRESSION_SYSTEM,
+ ItemNames.PROGRESSIVE_ORBITAL_COMMAND,
+ ItemNames.ULTRA_CAPACITORS,
+ ItemNames.VANADIUM_PLATING,
+ ItemNames.ORBITAL_DEPOTS,
+ ItemNames.MICRO_FILTERING,
+ ItemNames.AUTOMATED_REFINERY,
+ ItemNames.COMMAND_CENTER_REACTOR,
+ ItemNames.TECH_REACTOR,
+ ItemNames.CELLULAR_REACTOR,
+ ItemNames.PROGRESSIVE_REGENERATIVE_BIO_STEEL, # Place only L1
+ ItemNames.STRUCTURE_ARMOR,
+ ItemNames.HI_SEC_AUTO_TRACKING,
+ ItemNames.ADVANCED_OPTICS,
+ ItemNames.ROGUE_FORCES,
+ # Mercenaries (All races)
+ *[item_name for item_name, item_data in get_full_item_list().items()
+ if item_data.type == "Mercenary"],
+ # Kerrigan and Nova levels, abilities and generally useful stuff
+ *[item_name for item_name, item_data in get_full_item_list().items()
+ if item_data.type in ("Level", "Ability", "Evolution Pit", "Nova Gear")],
+ ItemNames.NOVA_PROGRESSIVE_STEALTH_SUIT_MODULE,
+ # Zerg static defenses
+ ItemNames.SPORE_CRAWLER,
+ ItemNames.SPINE_CRAWLER,
+ # Defiler, Aberration (no upgrades)
+ ItemNames.DEFILER,
+ ItemNames.ABERRATION,
+ # Spear of Adun Abilities
+ ItemNames.SOA_CHRONO_SURGE,
+ ItemNames.SOA_PROGRESSIVE_PROXY_PYLON,
+ ItemNames.SOA_PYLON_OVERCHARGE,
+ ItemNames.SOA_ORBITAL_STRIKE,
+ ItemNames.SOA_TEMPORAL_FIELD,
+ ItemNames.SOA_SOLAR_LANCE,
+ ItemNames.SOA_MASS_RECALL,
+ ItemNames.SOA_SHIELD_OVERCHARGE,
+ ItemNames.SOA_DEPLOY_FENIX,
+ ItemNames.SOA_PURIFIER_BEAM,
+ ItemNames.SOA_TIME_STOP,
+ ItemNames.SOA_SOLAR_BOMBARDMENT,
+ # Protoss generic upgrades
+ ItemNames.MATRIX_OVERLOAD,
+ ItemNames.QUATRO,
+ ItemNames.NEXUS_OVERCHARGE,
+ ItemNames.ORBITAL_ASSIMILATORS,
+ ItemNames.WARP_HARMONIZATION,
+ ItemNames.GUARDIAN_SHELL,
+ ItemNames.RECONSTRUCTION_BEAM,
+ ItemNames.OVERWATCH,
+ ItemNames.SUPERIOR_WARP_GATES,
+ ItemNames.KHALAI_INGENUITY,
+ ItemNames.AMPLIFIED_ASSIMILATORS,
+ # Protoss static defenses
+ ItemNames.PHOTON_CANNON,
+ ItemNames.KHAYDARIN_MONOLITH,
+ ItemNames.SHIELD_BATTERY
+)
+
+
+filler_items: typing.Tuple[str, ...] = (
+ ItemNames.STARTING_MINERALS,
+ ItemNames.STARTING_VESPENE,
+ ItemNames.STARTING_SUPPLY,
+)
+
+# Defense rating table
+# Commented defense ratings are handled in LogicMixin
+defense_ratings = {
+ ItemNames.SIEGE_TANK: 5,
+ # "Maelstrom Rounds": 2,
+ ItemNames.PLANETARY_FORTRESS: 3,
+ # Bunker w/ Marine/Marauder: 3,
+ ItemNames.PERDITION_TURRET: 2,
+ ItemNames.VULTURE: 1,
+ ItemNames.BANSHEE: 1,
+ ItemNames.BATTLECRUISER: 1,
+ ItemNames.LIBERATOR: 4,
+ ItemNames.WIDOW_MINE: 1,
+ # "Concealment (Widow Mine)": 1
+}
+zerg_defense_ratings = {
+ ItemNames.PERDITION_TURRET: 2,
+ # Bunker w/ Firebat: 2,
+ ItemNames.LIBERATOR: -2,
+ ItemNames.HIVE_MIND_EMULATOR: 3,
+ ItemNames.PSI_DISRUPTER: 3,
+}
+air_defense_ratings = {
+ ItemNames.MISSILE_TURRET: 2,
+}
+
+kerrigan_levels = [item_name for item_name, item_data in get_full_item_list().items()
+ if item_data.type == "Level" and item_data.race == SC2Race.ZERG]
+
+spider_mine_sources = {
+ ItemNames.VULTURE,
+ ItemNames.REAPER_SPIDER_MINES,
+ ItemNames.SIEGE_TANK_SPIDER_MINES,
+ ItemNames.RAVEN_SPIDER_MINES,
+}
+
+progressive_if_nco = {
+ ItemNames.MARINE_PROGRESSIVE_STIMPACK,
+ ItemNames.FIREBAT_PROGRESSIVE_STIMPACK,
+ ItemNames.BANSHEE_PROGRESSIVE_CROSS_SPECTRUM_DAMPENERS,
+ ItemNames.PROGRESSIVE_REGENERATIVE_BIO_STEEL,
+}
+
+progressive_if_ext = {
+ ItemNames.VULTURE_PROGRESSIVE_REPLENISHABLE_MAGAZINE,
+ ItemNames.WRAITH_PROGRESSIVE_TOMAHAWK_POWER_CELLS,
+ ItemNames.BATTLECRUISER_PROGRESSIVE_DEFENSIVE_MATRIX,
+ ItemNames.BATTLECRUISER_PROGRESSIVE_MISSILE_PODS,
+ ItemNames.THOR_PROGRESSIVE_IMMORTALITY_PROTOCOL,
+ ItemNames.PROGRESSIVE_FIRE_SUPPRESSION_SYSTEM,
+ ItemNames.DIAMONDBACK_PROGRESSIVE_TRI_LITHIUM_POWER_CELL
+}
+
+kerrigan_actives: typing.List[typing.Set[str]] = [
+ {ItemNames.KERRIGAN_KINETIC_BLAST, ItemNames.KERRIGAN_LEAPING_STRIKE},
+ {ItemNames.KERRIGAN_CRUSHING_GRIP, ItemNames.KERRIGAN_PSIONIC_SHIFT},
+ set(),
+ {ItemNames.KERRIGAN_WILD_MUTATION, ItemNames.KERRIGAN_SPAWN_BANELINGS, ItemNames.KERRIGAN_MEND},
+ set(),
+ set(),
+ {ItemNames.KERRIGAN_APOCALYPSE, ItemNames.KERRIGAN_SPAWN_LEVIATHAN, ItemNames.KERRIGAN_DROP_PODS},
+]
+
+kerrigan_passives: typing.List[typing.Set[str]] = [
+ {ItemNames.KERRIGAN_HEROIC_FORTITUDE},
+ {ItemNames.KERRIGAN_CHAIN_REACTION},
+ {ItemNames.KERRIGAN_ZERGLING_RECONSTITUTION, ItemNames.KERRIGAN_IMPROVED_OVERLORDS, ItemNames.KERRIGAN_AUTOMATED_EXTRACTORS},
+ set(),
+ {ItemNames.KERRIGAN_TWIN_DRONES, ItemNames.KERRIGAN_MALIGNANT_CREEP, ItemNames.KERRIGAN_VESPENE_EFFICIENCY},
+ {ItemNames.KERRIGAN_INFEST_BROODLINGS, ItemNames.KERRIGAN_FURY, ItemNames.KERRIGAN_ABILITY_EFFICIENCY},
+ set(),
+]
+
+kerrigan_only_passives = {
+ ItemNames.KERRIGAN_HEROIC_FORTITUDE, ItemNames.KERRIGAN_CHAIN_REACTION,
+ ItemNames.KERRIGAN_INFEST_BROODLINGS, ItemNames.KERRIGAN_FURY, ItemNames.KERRIGAN_ABILITY_EFFICIENCY,
+}
+
+spear_of_adun_calldowns = {
+ ItemNames.SOA_CHRONO_SURGE,
+ ItemNames.SOA_PROGRESSIVE_PROXY_PYLON,
+ ItemNames.SOA_PYLON_OVERCHARGE,
+ ItemNames.SOA_ORBITAL_STRIKE,
+ ItemNames.SOA_TEMPORAL_FIELD,
+ ItemNames.SOA_SOLAR_LANCE,
+ ItemNames.SOA_MASS_RECALL,
+ ItemNames.SOA_SHIELD_OVERCHARGE,
+ ItemNames.SOA_DEPLOY_FENIX,
+ ItemNames.SOA_PURIFIER_BEAM,
+ ItemNames.SOA_TIME_STOP,
+ ItemNames.SOA_SOLAR_BOMBARDMENT
+}
+
+spear_of_adun_castable_passives = {
+ ItemNames.RECONSTRUCTION_BEAM,
+ ItemNames.OVERWATCH,
+}
+
+nova_equipment = {
+ *[item_name for item_name, item_data in get_full_item_list().items()
+ if item_data.type == "Nova Gear"],
+ ItemNames.NOVA_PROGRESSIVE_STEALTH_SUIT_MODULE
+}
+
+# 'number' values of upgrades for upgrade bundle items
+upgrade_numbers = [
+ # Terran
+ {0, 4, 8}, # Weapon
+ {2, 6, 10}, # Armor
+ {0, 2}, # Infantry
+ {4, 6}, # Vehicle
+ {8, 10}, # Starship
+ {0, 2, 4, 6, 8, 10}, # All
+ # Zerg
+ {0, 2, 6}, # Weapon
+ {4, 8}, # Armor
+ {0, 2, 4}, # Ground
+ {6, 8}, # Flyer
+ {0, 2, 4, 6, 8}, # All
+ # Protoss
+ {0, 6}, # Weapon
+ {2, 4, 8}, # Armor
+ {0, 2}, # Ground, Shields are handled specially
+ {6, 8}, # Air, Shields are handled specially
+ {0, 2, 4, 6, 8}, # All
+]
+# 'upgrade_numbers' indices for all upgrades
+upgrade_numbers_all = {
+ SC2Race.TERRAN: 5,
+ SC2Race.ZERG: 10,
+ SC2Race.PROTOSS: 15,
+}
+
+# Names of upgrades to be included for different options
+upgrade_included_names = [
+ { # Individual Items
+ ItemNames.PROGRESSIVE_TERRAN_INFANTRY_WEAPON,
+ ItemNames.PROGRESSIVE_TERRAN_INFANTRY_ARMOR,
+ ItemNames.PROGRESSIVE_TERRAN_VEHICLE_WEAPON,
+ ItemNames.PROGRESSIVE_TERRAN_VEHICLE_ARMOR,
+ ItemNames.PROGRESSIVE_TERRAN_SHIP_WEAPON,
+ ItemNames.PROGRESSIVE_TERRAN_SHIP_ARMOR,
+ ItemNames.PROGRESSIVE_ZERG_MELEE_ATTACK,
+ ItemNames.PROGRESSIVE_ZERG_MISSILE_ATTACK,
+ ItemNames.PROGRESSIVE_ZERG_GROUND_CARAPACE,
+ ItemNames.PROGRESSIVE_ZERG_FLYER_ATTACK,
+ ItemNames.PROGRESSIVE_ZERG_FLYER_CARAPACE,
+ ItemNames.PROGRESSIVE_PROTOSS_GROUND_WEAPON,
+ ItemNames.PROGRESSIVE_PROTOSS_GROUND_ARMOR,
+ ItemNames.PROGRESSIVE_PROTOSS_SHIELDS,
+ ItemNames.PROGRESSIVE_PROTOSS_AIR_WEAPON,
+ ItemNames.PROGRESSIVE_PROTOSS_AIR_ARMOR,
+ },
+ { # Bundle Weapon And Armor
+ ItemNames.PROGRESSIVE_TERRAN_WEAPON_UPGRADE,
+ ItemNames.PROGRESSIVE_TERRAN_ARMOR_UPGRADE,
+ ItemNames.PROGRESSIVE_ZERG_WEAPON_UPGRADE,
+ ItemNames.PROGRESSIVE_ZERG_ARMOR_UPGRADE,
+ ItemNames.PROGRESSIVE_PROTOSS_WEAPON_UPGRADE,
+ ItemNames.PROGRESSIVE_PROTOSS_ARMOR_UPGRADE,
+ },
+ { # Bundle Unit Class
+ ItemNames.PROGRESSIVE_TERRAN_INFANTRY_UPGRADE,
+ ItemNames.PROGRESSIVE_TERRAN_VEHICLE_UPGRADE,
+ ItemNames.PROGRESSIVE_TERRAN_SHIP_UPGRADE,
+ ItemNames.PROGRESSIVE_ZERG_GROUND_UPGRADE,
+ ItemNames.PROGRESSIVE_ZERG_FLYER_UPGRADE,
+ ItemNames.PROGRESSIVE_PROTOSS_GROUND_UPGRADE,
+ ItemNames.PROGRESSIVE_PROTOSS_AIR_UPGRADE,
+ },
+ { # Bundle All
+ ItemNames.PROGRESSIVE_TERRAN_WEAPON_ARMOR_UPGRADE,
+ ItemNames.PROGRESSIVE_ZERG_WEAPON_ARMOR_UPGRADE,
+ ItemNames.PROGRESSIVE_PROTOSS_WEAPON_ARMOR_UPGRADE,
+ }
+]
+
+lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in get_full_item_list().items() if
+ data.code}
+
+# Map type to expected int
+type_flaggroups: typing.Dict[SC2Race, typing.Dict[str, int]] = {
+ SC2Race.ANY: {
+ "Minerals": 0,
+ "Vespene": 1,
+ "Supply": 2,
+ "Goal": 3,
+ "Nothing Group": 4,
+ },
+ SC2Race.TERRAN: {
+ "Armory 1": 0,
+ "Armory 2": 1,
+ "Armory 3": 2,
+ "Armory 4": 3,
+ "Armory 5": 4,
+ "Armory 6": 5,
+ "Progressive Upgrade": 6, # Unit upgrades that exist multiple times (Stimpack / Super Stimpack)
+ "Laboratory": 7,
+ "Upgrade": 8, # Weapon / Armor upgrades
+ "Unit": 9,
+ "Building": 10,
+ "Mercenary": 11,
+ "Nova Gear": 12,
+ "Progressive Upgrade 2": 13,
+ },
+ SC2Race.ZERG: {
+ "Ability": 0,
+ "Mutation 1": 1,
+ "Strain": 2,
+ "Morph": 3,
+ "Upgrade": 4,
+ "Mercenary": 5,
+ "Unit": 6,
+ "Level": 7,
+ "Primal Form": 8,
+ "Evolution Pit": 9,
+ "Mutation 2": 10,
+ "Mutation 3": 11
+ },
+ SC2Race.PROTOSS: {
+ "Unit": 0,
+ "Unit 2": 1,
+ "Upgrade": 2, # Weapon / Armor upgrades
+ "Building": 3,
+ "Progressive Upgrade": 4,
+ "Spear of Adun": 5,
+ "Solarite Core": 6,
+ "Forge 1": 7,
+ "Forge 2": 8,
+ "Forge 3": 9,
+ }
+}
diff --git a/worlds/sc2/Locations.py b/worlds/sc2/Locations.py
new file mode 100644
index 000000000000..22b400a238dd
--- /dev/null
+++ b/worlds/sc2/Locations.py
@@ -0,0 +1,1638 @@
+from enum import IntEnum
+from typing import List, Tuple, Optional, Callable, NamedTuple, Set, Any
+from BaseClasses import MultiWorld
+from . import ItemNames
+from .Options import get_option_value, kerrigan_unit_available, RequiredTactics, GrantStoryTech, LocationInclusion, \
+ EnableHotsMissions
+from .Rules import SC2Logic
+
+from BaseClasses import Location
+from worlds.AutoWorld import World
+
+SC2WOL_LOC_ID_OFFSET = 1000
+SC2HOTS_LOC_ID_OFFSET = 20000000 # Avoid clashes with The Legend of Zelda
+SC2LOTV_LOC_ID_OFFSET = SC2HOTS_LOC_ID_OFFSET + 2000
+SC2NCO_LOC_ID_OFFSET = SC2LOTV_LOC_ID_OFFSET + 2500
+
+
+class SC2Location(Location):
+ game: str = "Starcraft2"
+
+
+class LocationType(IntEnum):
+ VICTORY = 0 # Winning a mission
+ VANILLA = 1 # Objectives that provided metaprogression in the original campaign, along with a few other locations for a balanced experience
+ EXTRA = 2 # Additional locations based on mission progression, collecting in-mission rewards, etc. that do not significantly increase the challenge.
+ CHALLENGE = 3 # Challenging objectives, often harder than just completing a mission, and often associated with Achievements
+ MASTERY = 4 # Extremely challenging objectives often associated with Masteries and Feats of Strength in the original campaign
+
+
+class LocationData(NamedTuple):
+ region: str
+ name: str
+ code: Optional[int]
+ type: LocationType
+ rule: Optional[Callable[[Any], bool]] = Location.access_rule
+
+
+def get_location_types(world: World, inclusion_type: LocationInclusion) -> Set[LocationType]:
+ """
+
+ :param multiworld:
+ :param player:
+ :param inclusion_type: Level of inclusion to check for
+ :return: A list of location types that match the inclusion type
+ """
+ exclusion_options = [
+ ("vanilla_locations", LocationType.VANILLA),
+ ("extra_locations", LocationType.EXTRA),
+ ("challenge_locations", LocationType.CHALLENGE),
+ ("mastery_locations", LocationType.MASTERY)
+ ]
+ excluded_location_types = set()
+ for option_name, location_type in exclusion_options:
+ if get_option_value(world, option_name) is inclusion_type:
+ excluded_location_types.add(location_type)
+ return excluded_location_types
+
+
+def get_plando_locations(world: World) -> List[str]:
+ """
+
+ :param multiworld:
+ :param player:
+ :return: A list of locations affected by a plando in a world
+ """
+ if world is None:
+ return []
+ plando_locations = []
+ for plando_setting in world.multiworld.plando_items[world.player]:
+ plando_locations += plando_setting.get("locations", [])
+ plando_setting_location = plando_setting.get("location", None)
+ if plando_setting_location is not None:
+ plando_locations.append(plando_setting_location)
+
+ return plando_locations
+
+
+def get_locations(world: Optional[World]) -> Tuple[LocationData, ...]:
+ # Note: rules which are ended with or True are rules identified as needed later when restricted units is an option
+ logic_level = get_option_value(world, 'required_tactics')
+ adv_tactics = logic_level != RequiredTactics.option_standard
+ kerriganless = get_option_value(world, 'kerrigan_presence') not in kerrigan_unit_available \
+ or get_option_value(world, "enable_hots_missions") == EnableHotsMissions.option_false
+ story_tech_granted = get_option_value(world, "grant_story_tech") == GrantStoryTech.option_true
+ logic = SC2Logic(world)
+ player = None if world is None else world.player
+ location_table: List[LocationData] = [
+ # WoL
+ LocationData("Liberation Day", "Liberation Day: Victory", SC2WOL_LOC_ID_OFFSET + 100, LocationType.VICTORY),
+ LocationData("Liberation Day", "Liberation Day: First Statue", SC2WOL_LOC_ID_OFFSET + 101, LocationType.VANILLA),
+ LocationData("Liberation Day", "Liberation Day: Second Statue", SC2WOL_LOC_ID_OFFSET + 102, LocationType.VANILLA),
+ LocationData("Liberation Day", "Liberation Day: Third Statue", SC2WOL_LOC_ID_OFFSET + 103, LocationType.VANILLA),
+ LocationData("Liberation Day", "Liberation Day: Fourth Statue", SC2WOL_LOC_ID_OFFSET + 104, LocationType.VANILLA),
+ LocationData("Liberation Day", "Liberation Day: Fifth Statue", SC2WOL_LOC_ID_OFFSET + 105, LocationType.VANILLA),
+ LocationData("Liberation Day", "Liberation Day: Sixth Statue", SC2WOL_LOC_ID_OFFSET + 106, LocationType.VANILLA),
+ LocationData("Liberation Day", "Liberation Day: Special Delivery", SC2WOL_LOC_ID_OFFSET + 107, LocationType.EXTRA),
+ LocationData("Liberation Day", "Liberation Day: Transport", SC2WOL_LOC_ID_OFFSET + 108, LocationType.EXTRA),
+ LocationData("The Outlaws", "The Outlaws: Victory", SC2WOL_LOC_ID_OFFSET + 200, LocationType.VICTORY,
+ lambda state: logic.terran_early_tech(state)),
+ LocationData("The Outlaws", "The Outlaws: Rebel Base", SC2WOL_LOC_ID_OFFSET + 201, LocationType.VANILLA,
+ lambda state: logic.terran_early_tech(state)),
+ LocationData("The Outlaws", "The Outlaws: North Resource Pickups", SC2WOL_LOC_ID_OFFSET + 202, LocationType.EXTRA,
+ lambda state: logic.terran_early_tech(state)),
+ LocationData("The Outlaws", "The Outlaws: Bunker", SC2WOL_LOC_ID_OFFSET + 203, LocationType.VANILLA,
+ lambda state: logic.terran_early_tech(state)),
+ LocationData("The Outlaws", "The Outlaws: Close Resource Pickups", SC2WOL_LOC_ID_OFFSET + 204, LocationType.EXTRA),
+ LocationData("Zero Hour", "Zero Hour: Victory", SC2WOL_LOC_ID_OFFSET + 300, LocationType.VICTORY,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_defense_rating(state, True) >= 2 and
+ (adv_tactics or logic.terran_basic_anti_air(state))),
+ LocationData("Zero Hour", "Zero Hour: First Group Rescued", SC2WOL_LOC_ID_OFFSET + 301, LocationType.VANILLA),
+ LocationData("Zero Hour", "Zero Hour: Second Group Rescued", SC2WOL_LOC_ID_OFFSET + 302, LocationType.VANILLA,
+ lambda state: logic.terran_common_unit(state)),
+ LocationData("Zero Hour", "Zero Hour: Third Group Rescued", SC2WOL_LOC_ID_OFFSET + 303, LocationType.VANILLA,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_defense_rating(state, True) >= 2),
+ LocationData("Zero Hour", "Zero Hour: First Hatchery", SC2WOL_LOC_ID_OFFSET + 304, LocationType.CHALLENGE,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Zero Hour", "Zero Hour: Second Hatchery", SC2WOL_LOC_ID_OFFSET + 305, LocationType.CHALLENGE,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Zero Hour", "Zero Hour: Third Hatchery", SC2WOL_LOC_ID_OFFSET + 306, LocationType.CHALLENGE,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Zero Hour", "Zero Hour: Fourth Hatchery", SC2WOL_LOC_ID_OFFSET + 307, LocationType.CHALLENGE,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Zero Hour", "Zero Hour: Ride's on its Way", SC2WOL_LOC_ID_OFFSET + 308, LocationType.EXTRA,
+ lambda state: logic.terran_common_unit(state)),
+ LocationData("Zero Hour", "Zero Hour: Hold Just a Little Longer", SC2WOL_LOC_ID_OFFSET + 309, LocationType.EXTRA,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_defense_rating(state, True) >= 2),
+ LocationData("Zero Hour", "Zero Hour: Cavalry's on the Way", SC2WOL_LOC_ID_OFFSET + 310, LocationType.EXTRA,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_defense_rating(state, True) >= 2),
+ LocationData("Evacuation", "Evacuation: Victory", SC2WOL_LOC_ID_OFFSET + 400, LocationType.VICTORY,
+ lambda state: logic.terran_early_tech(state) and
+ (adv_tactics and logic.terran_basic_anti_air(state)
+ or logic.terran_competent_anti_air(state))),
+ LocationData("Evacuation", "Evacuation: North Chrysalis", SC2WOL_LOC_ID_OFFSET + 401, LocationType.VANILLA),
+ LocationData("Evacuation", "Evacuation: West Chrysalis", SC2WOL_LOC_ID_OFFSET + 402, LocationType.VANILLA,
+ lambda state: logic.terran_early_tech(state)),
+ LocationData("Evacuation", "Evacuation: East Chrysalis", SC2WOL_LOC_ID_OFFSET + 403, LocationType.VANILLA,
+ lambda state: logic.terran_early_tech(state)),
+ LocationData("Evacuation", "Evacuation: Reach Hanson", SC2WOL_LOC_ID_OFFSET + 404, LocationType.EXTRA),
+ LocationData("Evacuation", "Evacuation: Secret Resource Stash", SC2WOL_LOC_ID_OFFSET + 405, LocationType.EXTRA),
+ LocationData("Evacuation", "Evacuation: Flawless", SC2WOL_LOC_ID_OFFSET + 406, LocationType.CHALLENGE,
+ lambda state: logic.terran_early_tech(state) and
+ logic.terran_defense_rating(state, True, False) >= 2 and
+ (adv_tactics and logic.terran_basic_anti_air(state)
+ or logic.terran_competent_anti_air(state))),
+ LocationData("Outbreak", "Outbreak: Victory", SC2WOL_LOC_ID_OFFSET + 500, LocationType.VICTORY,
+ lambda state: logic.terran_defense_rating(state, True, False) >= 4 and
+ (logic.terran_common_unit(state) or state.has(ItemNames.REAPER, player))),
+ LocationData("Outbreak", "Outbreak: Left Infestor", SC2WOL_LOC_ID_OFFSET + 501, LocationType.VANILLA,
+ lambda state: logic.terran_defense_rating(state, True, False) >= 2 and
+ (logic.terran_common_unit(state) or state.has(ItemNames.REAPER, player))),
+ LocationData("Outbreak", "Outbreak: Right Infestor", SC2WOL_LOC_ID_OFFSET + 502, LocationType.VANILLA,
+ lambda state: logic.terran_defense_rating(state, True, False) >= 2 and
+ (logic.terran_common_unit(state) or state.has(ItemNames.REAPER, player))),
+ LocationData("Outbreak", "Outbreak: North Infested Command Center", SC2WOL_LOC_ID_OFFSET + 503, LocationType.EXTRA,
+ lambda state: logic.terran_defense_rating(state, True, False) >= 2 and
+ (logic.terran_common_unit(state) or state.has(ItemNames.REAPER, player))),
+ LocationData("Outbreak", "Outbreak: South Infested Command Center", SC2WOL_LOC_ID_OFFSET + 504, LocationType.EXTRA,
+ lambda state: logic.terran_defense_rating(state, True, False) >= 2 and
+ (logic.terran_common_unit(state) or state.has(ItemNames.REAPER, player))),
+ LocationData("Outbreak", "Outbreak: Northwest Bar", SC2WOL_LOC_ID_OFFSET + 505, LocationType.EXTRA,
+ lambda state: logic.terran_defense_rating(state, True, False) >= 2 and
+ (logic.terran_common_unit(state) or state.has(ItemNames.REAPER, player))),
+ LocationData("Outbreak", "Outbreak: North Bar", SC2WOL_LOC_ID_OFFSET + 506, LocationType.EXTRA,
+ lambda state: logic.terran_defense_rating(state, True, False) >= 2 and
+ (logic.terran_common_unit(state) or state.has(ItemNames.REAPER, player))),
+ LocationData("Outbreak", "Outbreak: South Bar", SC2WOL_LOC_ID_OFFSET + 507, LocationType.EXTRA,
+ lambda state: logic.terran_defense_rating(state, True, False) >= 2 and
+ (logic.terran_common_unit(state) or state.has(ItemNames.REAPER, player))),
+ LocationData("Safe Haven", "Safe Haven: Victory", SC2WOL_LOC_ID_OFFSET + 600, LocationType.VICTORY,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_competent_anti_air(state)),
+ LocationData("Safe Haven", "Safe Haven: North Nexus", SC2WOL_LOC_ID_OFFSET + 601, LocationType.EXTRA,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_competent_anti_air(state)),
+ LocationData("Safe Haven", "Safe Haven: East Nexus", SC2WOL_LOC_ID_OFFSET + 602, LocationType.EXTRA,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_competent_anti_air(state)),
+ LocationData("Safe Haven", "Safe Haven: South Nexus", SC2WOL_LOC_ID_OFFSET + 603, LocationType.EXTRA,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_competent_anti_air(state)),
+ LocationData("Safe Haven", "Safe Haven: First Terror Fleet", SC2WOL_LOC_ID_OFFSET + 604, LocationType.VANILLA,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_competent_anti_air(state)),
+ LocationData("Safe Haven", "Safe Haven: Second Terror Fleet", SC2WOL_LOC_ID_OFFSET + 605, LocationType.VANILLA,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_competent_anti_air(state)),
+ LocationData("Safe Haven", "Safe Haven: Third Terror Fleet", SC2WOL_LOC_ID_OFFSET + 606, LocationType.VANILLA,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_competent_anti_air(state)),
+ LocationData("Haven's Fall", "Haven's Fall: Victory", SC2WOL_LOC_ID_OFFSET + 700, LocationType.VICTORY,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_competent_anti_air(state) and
+ logic.terran_defense_rating(state, True) >= 3),
+ LocationData("Haven's Fall", "Haven's Fall: North Hive", SC2WOL_LOC_ID_OFFSET + 701, LocationType.VANILLA,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_competent_anti_air(state) and
+ logic.terran_defense_rating(state, True) >= 3),
+ LocationData("Haven's Fall", "Haven's Fall: East Hive", SC2WOL_LOC_ID_OFFSET + 702, LocationType.VANILLA,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_competent_anti_air(state) and
+ logic.terran_defense_rating(state, True) >= 3),
+ LocationData("Haven's Fall", "Haven's Fall: South Hive", SC2WOL_LOC_ID_OFFSET + 703, LocationType.VANILLA,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_competent_anti_air(state) and
+ logic.terran_defense_rating(state, True) >= 3),
+ LocationData("Haven's Fall", "Haven's Fall: Northeast Colony Base", SC2WOL_LOC_ID_OFFSET + 704, LocationType.CHALLENGE,
+ lambda state: logic.terran_respond_to_colony_infestations(state)),
+ LocationData("Haven's Fall", "Haven's Fall: East Colony Base", SC2WOL_LOC_ID_OFFSET + 705, LocationType.CHALLENGE,
+ lambda state: logic.terran_respond_to_colony_infestations(state)),
+ LocationData("Haven's Fall", "Haven's Fall: Middle Colony Base", SC2WOL_LOC_ID_OFFSET + 706, LocationType.CHALLENGE,
+ lambda state: logic.terran_respond_to_colony_infestations(state)),
+ LocationData("Haven's Fall", "Haven's Fall: Southeast Colony Base", SC2WOL_LOC_ID_OFFSET + 707, LocationType.CHALLENGE,
+ lambda state: logic.terran_respond_to_colony_infestations(state)),
+ LocationData("Haven's Fall", "Haven's Fall: Southwest Colony Base", SC2WOL_LOC_ID_OFFSET + 708, LocationType.CHALLENGE,
+ lambda state: logic.terran_respond_to_colony_infestations(state)),
+ LocationData("Haven's Fall", "Haven's Fall: Southwest Gas Pickups", SC2WOL_LOC_ID_OFFSET + 709, LocationType.EXTRA,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_competent_anti_air(state) and
+ logic.terran_defense_rating(state, True) >= 3),
+ LocationData("Haven's Fall", "Haven's Fall: East Gas Pickups", SC2WOL_LOC_ID_OFFSET + 710, LocationType.EXTRA,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_competent_anti_air(state) and
+ logic.terran_defense_rating(state, True) >= 3),
+ LocationData("Haven's Fall", "Haven's Fall: Southeast Gas Pickups", SC2WOL_LOC_ID_OFFSET + 711, LocationType.EXTRA,
+ lambda state: logic.terran_common_unit(state) and
+ logic.terran_competent_anti_air(state) and
+ logic.terran_defense_rating(state, True) >= 3),
+ LocationData("Smash and Grab", "Smash and Grab: Victory", SC2WOL_LOC_ID_OFFSET + 800, LocationType.VICTORY,
+ lambda state: logic.terran_common_unit(state) and
+ (adv_tactics and logic.terran_basic_anti_air(state)
+ or logic.terran_competent_anti_air(state))),
+ LocationData("Smash and Grab", "Smash and Grab: First Relic", SC2WOL_LOC_ID_OFFSET + 801, LocationType.VANILLA),
+ LocationData("Smash and Grab", "Smash and Grab: Second Relic", SC2WOL_LOC_ID_OFFSET + 802, LocationType.VANILLA),
+ LocationData("Smash and Grab", "Smash and Grab: Third Relic", SC2WOL_LOC_ID_OFFSET + 803, LocationType.VANILLA,
+ lambda state: logic.terran_common_unit(state) and
+ (adv_tactics and logic.terran_basic_anti_air(state)
+ or logic.terran_competent_anti_air(state))),
+ LocationData("Smash and Grab", "Smash and Grab: Fourth Relic", SC2WOL_LOC_ID_OFFSET + 804, LocationType.VANILLA,
+ lambda state: logic.terran_common_unit(state) and
+ (adv_tactics and logic.terran_basic_anti_air(state)
+ or logic.terran_competent_anti_air(state))),
+ LocationData("Smash and Grab", "Smash and Grab: First Forcefield Area Busted", SC2WOL_LOC_ID_OFFSET + 805, LocationType.EXTRA,
+ lambda state: logic.terran_common_unit(state) and
+ (adv_tactics and logic.terran_basic_anti_air(state)
+ or logic.terran_competent_anti_air(state))),
+ LocationData("Smash and Grab", "Smash and Grab: Second Forcefield Area Busted", SC2WOL_LOC_ID_OFFSET + 806, LocationType.EXTRA,
+ lambda state: logic.terran_common_unit(state) and
+ (adv_tactics and logic.terran_basic_anti_air(state)
+ or logic.terran_competent_anti_air(state))),
+ LocationData("The Dig", "The Dig: Victory", SC2WOL_LOC_ID_OFFSET + 900, LocationType.VICTORY,
+ lambda state: logic.terran_basic_anti_air(state)
+ and logic.terran_defense_rating(state, False, True) >= 8
+ and logic.terran_defense_rating(state, False, False) >= 6
+ and logic.terran_common_unit(state)
+ and (logic.marine_medic_upgrade(state) or adv_tactics)),
+ LocationData("The Dig", "The Dig: Left Relic", SC2WOL_LOC_ID_OFFSET + 901, LocationType.VANILLA,
+ lambda state: logic.terran_defense_rating(state, False, False) >= 6
+ and logic.terran_common_unit(state)
+ and (logic.marine_medic_upgrade(state) or adv_tactics)),
+ LocationData("The Dig", "The Dig: Right Ground Relic", SC2WOL_LOC_ID_OFFSET + 902, LocationType.VANILLA,
+ lambda state: logic.terran_defense_rating(state, False, False) >= 6
+ and logic.terran_common_unit(state)
+ and (logic.marine_medic_upgrade(state) or adv_tactics)),
+ LocationData("The Dig", "The Dig: Right Cliff Relic", SC2WOL_LOC_ID_OFFSET + 903, LocationType.VANILLA,
+ lambda state: logic.terran_defense_rating(state, False, False) >= 6
+ and logic.terran_common_unit(state)
+ and (logic.marine_medic_upgrade(state) or adv_tactics)),
+ LocationData("The Dig", "The Dig: Moebius Base", SC2WOL_LOC_ID_OFFSET + 904, LocationType.EXTRA,
+ lambda state: logic.marine_medic_upgrade(state) or adv_tactics),
+ LocationData("The Dig", "The Dig: Door Outer Layer", SC2WOL_LOC_ID_OFFSET + 905, LocationType.EXTRA,
+ lambda state: logic.terran_defense_rating(state, False, False) >= 6
+ and logic.terran_common_unit(state)
+ and (logic.marine_medic_upgrade(state) or adv_tactics)),
+ LocationData("The Dig", "The Dig: Door Thermal Barrier", SC2WOL_LOC_ID_OFFSET + 906, LocationType.EXTRA,
+ lambda state: logic.terran_basic_anti_air(state)
+ and logic.terran_defense_rating(state, False, True) >= 8
+ and logic.terran_defense_rating(state, False, False) >= 6
+ and logic.terran_common_unit(state)
+ and (logic.marine_medic_upgrade(state) or adv_tactics)),
+ LocationData("The Dig", "The Dig: Cutting Through the Core", SC2WOL_LOC_ID_OFFSET + 907, LocationType.EXTRA,
+ lambda state: logic.terran_basic_anti_air(state)
+ and logic.terran_defense_rating(state, False, True) >= 8
+ and logic.terran_defense_rating(state, False, False) >= 6
+ and logic.terran_common_unit(state)
+ and (logic.marine_medic_upgrade(state) or adv_tactics)),
+ LocationData("The Dig", "The Dig: Structure Access Imminent", SC2WOL_LOC_ID_OFFSET + 908, LocationType.EXTRA,
+ lambda state: logic.terran_basic_anti_air(state)
+ and logic.terran_defense_rating(state, False, True) >= 8
+ and logic.terran_defense_rating(state, False, False) >= 6
+ and logic.terran_common_unit(state)
+ and (logic.marine_medic_upgrade(state) or adv_tactics)),
+ LocationData("The Moebius Factor", "The Moebius Factor: Victory", SC2WOL_LOC_ID_OFFSET + 1000, LocationType.VICTORY,
+ lambda state: logic.terran_basic_anti_air(state) and
+ (logic.terran_air(state)
+ or state.has_any({ItemNames.MEDIVAC, ItemNames.HERCULES}, player)
+ and logic.terran_common_unit(state))),
+ LocationData("The Moebius Factor", "The Moebius Factor: 1st Data Core", SC2WOL_LOC_ID_OFFSET + 1001, LocationType.VANILLA),
+ LocationData("The Moebius Factor", "The Moebius Factor: 2nd Data Core", SC2WOL_LOC_ID_OFFSET + 1002, LocationType.VANILLA,
+ lambda state: (logic.terran_air(state)
+ or state.has_any({ItemNames.MEDIVAC, ItemNames.HERCULES}, player)
+ and logic.terran_common_unit(state))),
+ LocationData("The Moebius Factor", "The Moebius Factor: South Rescue", SC2WOL_LOC_ID_OFFSET + 1003, LocationType.EXTRA,
+ lambda state: logic.terran_can_rescue(state)),
+ LocationData("The Moebius Factor", "The Moebius Factor: Wall Rescue", SC2WOL_LOC_ID_OFFSET + 1004, LocationType.EXTRA,
+ lambda state: logic.terran_can_rescue(state)),
+ LocationData("The Moebius Factor", "The Moebius Factor: Mid Rescue", SC2WOL_LOC_ID_OFFSET + 1005, LocationType.EXTRA,
+ lambda state: logic.terran_can_rescue(state)),
+ LocationData("The Moebius Factor", "The Moebius Factor: Nydus Roof Rescue", SC2WOL_LOC_ID_OFFSET + 1006, LocationType.EXTRA,
+ lambda state: logic.terran_can_rescue(state)),
+ LocationData("The Moebius Factor", "The Moebius Factor: Alive Inside Rescue", SC2WOL_LOC_ID_OFFSET + 1007, LocationType.EXTRA,
+ lambda state: logic.terran_can_rescue(state)),
+ LocationData("The Moebius Factor", "The Moebius Factor: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1008, LocationType.VANILLA,
+ lambda state: logic.terran_basic_anti_air(state) and
+ (logic.terran_air(state)
+ or state.has_any({ItemNames.MEDIVAC, ItemNames.HERCULES}, player)
+ and logic.terran_common_unit(state))),
+ LocationData("The Moebius Factor", "The Moebius Factor: 3rd Data Core", SC2WOL_LOC_ID_OFFSET + 1009, LocationType.VANILLA,
+ lambda state: logic.terran_basic_anti_air(state) and
+ (logic.terran_air(state)
+ or state.has_any({ItemNames.MEDIVAC, ItemNames.HERCULES}, player)
+ and logic.terran_common_unit(state))),
+ LocationData("Supernova", "Supernova: Victory", SC2WOL_LOC_ID_OFFSET + 1100, LocationType.VICTORY,
+ lambda state: logic.terran_beats_protoss_deathball(state)),
+ LocationData("Supernova", "Supernova: West Relic", SC2WOL_LOC_ID_OFFSET + 1101, LocationType.VANILLA),
+ LocationData("Supernova", "Supernova: North Relic", SC2WOL_LOC_ID_OFFSET + 1102, LocationType.VANILLA),
+ LocationData("Supernova", "Supernova: South Relic", SC2WOL_LOC_ID_OFFSET + 1103, LocationType.VANILLA,
+ lambda state: logic.terran_beats_protoss_deathball(state)),
+ LocationData("Supernova", "Supernova: East Relic", SC2WOL_LOC_ID_OFFSET + 1104, LocationType.VANILLA,
+ lambda state: logic.terran_beats_protoss_deathball(state)),
+ LocationData("Supernova", "Supernova: Landing Zone Cleared", SC2WOL_LOC_ID_OFFSET + 1105, LocationType.EXTRA),
+ LocationData("Supernova", "Supernova: Middle Base", SC2WOL_LOC_ID_OFFSET + 1106, LocationType.EXTRA,
+ lambda state: logic.terran_beats_protoss_deathball(state)),
+ LocationData("Supernova", "Supernova: Southeast Base", SC2WOL_LOC_ID_OFFSET + 1107, LocationType.EXTRA,
+ lambda state: logic.terran_beats_protoss_deathball(state)),
+ LocationData("Maw of the Void", "Maw of the Void: Victory", SC2WOL_LOC_ID_OFFSET + 1200, LocationType.VICTORY,
+ lambda state: logic.terran_survives_rip_field(state)),
+ LocationData("Maw of the Void", "Maw of the Void: Landing Zone Cleared", SC2WOL_LOC_ID_OFFSET + 1201, LocationType.EXTRA),
+ LocationData("Maw of the Void", "Maw of the Void: Expansion Prisoners", SC2WOL_LOC_ID_OFFSET + 1202, LocationType.VANILLA,
+ lambda state: adv_tactics or logic.terran_survives_rip_field(state)),
+ LocationData("Maw of the Void", "Maw of the Void: South Close Prisoners", SC2WOL_LOC_ID_OFFSET + 1203, LocationType.VANILLA,
+ lambda state: adv_tactics or logic.terran_survives_rip_field(state)),
+ LocationData("Maw of the Void", "Maw of the Void: South Far Prisoners", SC2WOL_LOC_ID_OFFSET + 1204, LocationType.VANILLA,
+ lambda state: logic.terran_survives_rip_field(state)),
+ LocationData("Maw of the Void", "Maw of the Void: North Prisoners", SC2WOL_LOC_ID_OFFSET + 1205, LocationType.VANILLA,
+ lambda state: logic.terran_survives_rip_field(state)),
+ LocationData("Maw of the Void", "Maw of the Void: Mothership", SC2WOL_LOC_ID_OFFSET + 1206, LocationType.EXTRA,
+ lambda state: logic.terran_survives_rip_field(state)),
+ LocationData("Maw of the Void", "Maw of the Void: Expansion Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1207, LocationType.EXTRA,
+ lambda state: adv_tactics or logic.terran_survives_rip_field(state)),
+ LocationData("Maw of the Void", "Maw of the Void: Middle Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1208, LocationType.EXTRA,
+ lambda state: logic.terran_survives_rip_field(state)),
+ LocationData("Maw of the Void", "Maw of the Void: Southeast Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1209, LocationType.EXTRA,
+ lambda state: logic.terran_survives_rip_field(state)),
+ LocationData("Maw of the Void", "Maw of the Void: Stargate Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1210, LocationType.EXTRA,
+ lambda state: logic.terran_survives_rip_field(state)),
+ LocationData("Maw of the Void", "Maw of the Void: Northwest Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1211, LocationType.CHALLENGE,
+ lambda state: logic.terran_survives_rip_field(state)),
+ LocationData("Maw of the Void", "Maw of the Void: West Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1212, LocationType.CHALLENGE,
+ lambda state: logic.terran_survives_rip_field(state)),
+ LocationData("Maw of the Void", "Maw of the Void: Southwest Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1213, LocationType.CHALLENGE,
+ lambda state: logic.terran_survives_rip_field(state)),
+ LocationData("Devil's Playground", "Devil's Playground: Victory", SC2WOL_LOC_ID_OFFSET + 1300, LocationType.VICTORY,
+ lambda state: adv_tactics or
+ logic.terran_basic_anti_air(state) and (
+ logic.terran_common_unit(state) or state.has(ItemNames.REAPER, player))),
+ LocationData("Devil's Playground", "Devil's Playground: Tosh's Miners", SC2WOL_LOC_ID_OFFSET + 1301, LocationType.VANILLA),
+ LocationData("Devil's Playground", "Devil's Playground: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1302, LocationType.VANILLA,
+ lambda state: adv_tactics or logic.terran_common_unit(state) or state.has(ItemNames.REAPER, player)),
+ LocationData("Devil's Playground", "Devil's Playground: North Reapers", SC2WOL_LOC_ID_OFFSET + 1303, LocationType.EXTRA),
+ LocationData("Devil's Playground", "Devil's Playground: Middle Reapers", SC2WOL_LOC_ID_OFFSET + 1304, LocationType.EXTRA,
+ lambda state: adv_tactics or logic.terran_common_unit(state) or state.has(ItemNames.REAPER, player)),
+ LocationData("Devil's Playground", "Devil's Playground: Southwest Reapers", SC2WOL_LOC_ID_OFFSET + 1305, LocationType.EXTRA,
+ lambda state: adv_tactics or logic.terran_common_unit(state) or state.has(ItemNames.REAPER, player)),
+ LocationData("Devil's Playground", "Devil's Playground: Southeast Reapers", SC2WOL_LOC_ID_OFFSET + 1306, LocationType.EXTRA,
+ lambda state: adv_tactics or
+ logic.terran_basic_anti_air(state) and (
+ logic.terran_common_unit(state) or state.has(ItemNames.REAPER, player))),
+ LocationData("Devil's Playground", "Devil's Playground: East Reapers", SC2WOL_LOC_ID_OFFSET + 1307, LocationType.CHALLENGE,
+ lambda state: logic.terran_basic_anti_air(state) and
+ (adv_tactics or
+ logic.terran_common_unit(state) or state.has(ItemNames.REAPER, player))),
+ LocationData("Devil's Playground", "Devil's Playground: Zerg Cleared", SC2WOL_LOC_ID_OFFSET + 1308, LocationType.CHALLENGE,
+ lambda state: logic.terran_competent_anti_air(state) and (
+ logic.terran_common_unit(state) or state.has(ItemNames.REAPER, player))),
+ LocationData("Welcome to the Jungle", "Welcome to the Jungle: Victory", SC2WOL_LOC_ID_OFFSET + 1400, LocationType.VICTORY,
+ lambda state: logic.welcome_to_the_jungle_requirement(state)),
+ LocationData("Welcome to the Jungle", "Welcome to the Jungle: Close Relic", SC2WOL_LOC_ID_OFFSET + 1401, LocationType.VANILLA),
+ LocationData("Welcome to the Jungle", "Welcome to the Jungle: West Relic", SC2WOL_LOC_ID_OFFSET + 1402, LocationType.VANILLA,
+ lambda state: logic.welcome_to_the_jungle_requirement(state)),
+ LocationData("Welcome to the Jungle", "Welcome to the Jungle: North-East Relic", SC2WOL_LOC_ID_OFFSET + 1403, LocationType.VANILLA,
+ lambda state: logic.welcome_to_the_jungle_requirement(state)),
+ LocationData("Welcome to the Jungle", "Welcome to the Jungle: Middle Base", SC2WOL_LOC_ID_OFFSET + 1404, LocationType.EXTRA,
+ lambda state: logic.welcome_to_the_jungle_requirement(state)),
+ LocationData("Welcome to the Jungle", "Welcome to the Jungle: Main Base", SC2WOL_LOC_ID_OFFSET + 1405,
+ LocationType.MASTERY,
+ lambda state: logic.welcome_to_the_jungle_requirement(state)
+ and logic.terran_beats_protoss_deathball(state)
+ and logic.terran_base_trasher(state)),
+ LocationData("Welcome to the Jungle", "Welcome to the Jungle: No Terrazine Nodes Sealed", SC2WOL_LOC_ID_OFFSET + 1406, LocationType.CHALLENGE,
+ lambda state: logic.welcome_to_the_jungle_requirement(state)
+ and logic.terran_competent_ground_to_air(state)
+ and logic.terran_beats_protoss_deathball(state)),
+ LocationData("Welcome to the Jungle", "Welcome to the Jungle: Up to 1 Terrazine Node Sealed", SC2WOL_LOC_ID_OFFSET + 1407, LocationType.CHALLENGE,
+ lambda state: logic.welcome_to_the_jungle_requirement(state)
+ and logic.terran_competent_ground_to_air(state)
+ and logic.terran_beats_protoss_deathball(state)),
+ LocationData("Welcome to the Jungle", "Welcome to the Jungle: Up to 2 Terrazine Nodes Sealed", SC2WOL_LOC_ID_OFFSET + 1408, LocationType.CHALLENGE,
+ lambda state: logic.welcome_to_the_jungle_requirement(state)
+ and logic.terran_beats_protoss_deathball(state)),
+ LocationData("Welcome to the Jungle", "Welcome to the Jungle: Up to 3 Terrazine Nodes Sealed", SC2WOL_LOC_ID_OFFSET + 1409, LocationType.CHALLENGE,
+ lambda state: logic.welcome_to_the_jungle_requirement(state)
+ and logic.terran_competent_comp(state)),
+ LocationData("Welcome to the Jungle", "Welcome to the Jungle: Up to 4 Terrazine Nodes Sealed", SC2WOL_LOC_ID_OFFSET + 1410, LocationType.EXTRA,
+ lambda state: logic.welcome_to_the_jungle_requirement(state)),
+ LocationData("Welcome to the Jungle", "Welcome to the Jungle: Up to 5 Terrazine Nodes Sealed", SC2WOL_LOC_ID_OFFSET + 1411, LocationType.EXTRA,
+ lambda state: logic.welcome_to_the_jungle_requirement(state)),
+ LocationData("Breakout", "Breakout: Victory", SC2WOL_LOC_ID_OFFSET + 1500, LocationType.VICTORY),
+ LocationData("Breakout", "Breakout: Diamondback Prison", SC2WOL_LOC_ID_OFFSET + 1501, LocationType.VANILLA),
+ LocationData("Breakout", "Breakout: Siege Tank Prison", SC2WOL_LOC_ID_OFFSET + 1502, LocationType.VANILLA),
+ LocationData("Breakout", "Breakout: First Checkpoint", SC2WOL_LOC_ID_OFFSET + 1503, LocationType.EXTRA),
+ LocationData("Breakout", "Breakout: Second Checkpoint", SC2WOL_LOC_ID_OFFSET + 1504, LocationType.EXTRA),
+ LocationData("Ghost of a Chance", "Ghost of a Chance: Victory", SC2WOL_LOC_ID_OFFSET + 1600, LocationType.VICTORY),
+ LocationData("Ghost of a Chance", "Ghost of a Chance: Terrazine Tank", SC2WOL_LOC_ID_OFFSET + 1601, LocationType.EXTRA),
+ LocationData("Ghost of a Chance", "Ghost of a Chance: Jorium Stockpile", SC2WOL_LOC_ID_OFFSET + 1602, LocationType.EXTRA),
+ LocationData("Ghost of a Chance", "Ghost of a Chance: First Island Spectres", SC2WOL_LOC_ID_OFFSET + 1603, LocationType.VANILLA),
+ LocationData("Ghost of a Chance", "Ghost of a Chance: Second Island Spectres", SC2WOL_LOC_ID_OFFSET + 1604, LocationType.VANILLA),
+ LocationData("Ghost of a Chance", "Ghost of a Chance: Third Island Spectres", SC2WOL_LOC_ID_OFFSET + 1605, LocationType.VANILLA),
+ LocationData("The Great Train Robbery", "The Great Train Robbery: Victory", SC2WOL_LOC_ID_OFFSET + 1700, LocationType.VICTORY,
+ lambda state: logic.great_train_robbery_train_stopper(state) and
+ logic.terran_basic_anti_air(state)),
+ LocationData("The Great Train Robbery", "The Great Train Robbery: North Defiler", SC2WOL_LOC_ID_OFFSET + 1701, LocationType.VANILLA),
+ LocationData("The Great Train Robbery", "The Great Train Robbery: Mid Defiler", SC2WOL_LOC_ID_OFFSET + 1702, LocationType.VANILLA),
+ LocationData("The Great Train Robbery", "The Great Train Robbery: South Defiler", SC2WOL_LOC_ID_OFFSET + 1703, LocationType.VANILLA),
+ LocationData("The Great Train Robbery", "The Great Train Robbery: Close Diamondback", SC2WOL_LOC_ID_OFFSET + 1704, LocationType.EXTRA),
+ LocationData("The Great Train Robbery", "The Great Train Robbery: Northwest Diamondback", SC2WOL_LOC_ID_OFFSET + 1705, LocationType.EXTRA),
+ LocationData("The Great Train Robbery", "The Great Train Robbery: North Diamondback", SC2WOL_LOC_ID_OFFSET + 1706, LocationType.EXTRA),
+ LocationData("The Great Train Robbery", "The Great Train Robbery: Northeast Diamondback", SC2WOL_LOC_ID_OFFSET + 1707, LocationType.EXTRA),
+ LocationData("The Great Train Robbery", "The Great Train Robbery: Southwest Diamondback", SC2WOL_LOC_ID_OFFSET + 1708, LocationType.EXTRA),
+ LocationData("The Great Train Robbery", "The Great Train Robbery: Southeast Diamondback", SC2WOL_LOC_ID_OFFSET + 1709, LocationType.EXTRA),
+ LocationData("The Great Train Robbery", "The Great Train Robbery: Kill Team", SC2WOL_LOC_ID_OFFSET + 1710, LocationType.CHALLENGE,
+ lambda state: (adv_tactics or logic.terran_common_unit(state)) and
+ logic.great_train_robbery_train_stopper(state) and
+ logic.terran_basic_anti_air(state)),
+ LocationData("The Great Train Robbery", "The Great Train Robbery: Flawless", SC2WOL_LOC_ID_OFFSET + 1711, LocationType.CHALLENGE,
+ lambda state: logic.great_train_robbery_train_stopper(state) and
+ logic.terran_basic_anti_air(state)),
+ LocationData("The Great Train Robbery", "The Great Train Robbery: 2 Trains Destroyed", SC2WOL_LOC_ID_OFFSET + 1712, LocationType.EXTRA,
+ lambda state: logic.great_train_robbery_train_stopper(state)),
+ LocationData("The Great Train Robbery", "The Great Train Robbery: 4 Trains Destroyed", SC2WOL_LOC_ID_OFFSET + 1713, LocationType.EXTRA,
+ lambda state: logic.great_train_robbery_train_stopper(state) and
+ logic.terran_basic_anti_air(state)),
+ LocationData("The Great Train Robbery", "The Great Train Robbery: 6 Trains Destroyed", SC2WOL_LOC_ID_OFFSET + 1714, LocationType.EXTRA,
+ lambda state: logic.great_train_robbery_train_stopper(state) and
+ logic.terran_basic_anti_air(state)),
+ LocationData("Cutthroat", "Cutthroat: Victory", SC2WOL_LOC_ID_OFFSET + 1800, LocationType.VICTORY,
+ lambda state: logic.terran_common_unit(state) and
+ (adv_tactics or logic.terran_basic_anti_air)),
+ LocationData("Cutthroat", "Cutthroat: Mira Han", SC2WOL_LOC_ID_OFFSET + 1801, LocationType.EXTRA,
+ lambda state: logic.terran_common_unit(state)),
+ LocationData("Cutthroat", "Cutthroat: North Relic", SC2WOL_LOC_ID_OFFSET + 1802, LocationType.VANILLA,
+ lambda state: logic.terran_common_unit(state)),
+ LocationData("Cutthroat", "Cutthroat: Mid Relic", SC2WOL_LOC_ID_OFFSET + 1803, LocationType.VANILLA),
+ LocationData("Cutthroat", "Cutthroat: Southwest Relic", SC2WOL_LOC_ID_OFFSET + 1804, LocationType.VANILLA,
+ lambda state: logic.terran_common_unit(state)),
+ LocationData("Cutthroat", "Cutthroat: North Command Center", SC2WOL_LOC_ID_OFFSET + 1805, LocationType.EXTRA,
+ lambda state: logic.terran_common_unit(state)),
+ LocationData("Cutthroat", "Cutthroat: South Command Center", SC2WOL_LOC_ID_OFFSET + 1806, LocationType.EXTRA,
+ lambda state: logic.terran_common_unit(state)),
+ LocationData("Cutthroat", "Cutthroat: West Command Center", SC2WOL_LOC_ID_OFFSET + 1807, LocationType.EXTRA,
+ lambda state: logic.terran_common_unit(state)),
+ LocationData("Engine of Destruction", "Engine of Destruction: Victory", SC2WOL_LOC_ID_OFFSET + 1900, LocationType.VICTORY,
+ lambda state: logic.engine_of_destruction_requirement(state)),
+ LocationData("Engine of Destruction", "Engine of Destruction: Odin", SC2WOL_LOC_ID_OFFSET + 1901, LocationType.EXTRA,
+ lambda state: logic.marine_medic_upgrade(state)),
+ LocationData("Engine of Destruction", "Engine of Destruction: Loki", SC2WOL_LOC_ID_OFFSET + 1902,
+ LocationType.CHALLENGE,
+ lambda state: logic.engine_of_destruction_requirement(state)),
+ LocationData("Engine of Destruction", "Engine of Destruction: Lab Devourer", SC2WOL_LOC_ID_OFFSET + 1903, LocationType.VANILLA,
+ lambda state: logic.marine_medic_upgrade(state)),
+ LocationData("Engine of Destruction", "Engine of Destruction: North Devourer", SC2WOL_LOC_ID_OFFSET + 1904, LocationType.VANILLA,
+ lambda state: logic.engine_of_destruction_requirement(state)),
+ LocationData("Engine of Destruction", "Engine of Destruction: Southeast Devourer", SC2WOL_LOC_ID_OFFSET + 1905, LocationType.VANILLA,
+ lambda state: logic.engine_of_destruction_requirement(state)),
+ LocationData("Engine of Destruction", "Engine of Destruction: West Base", SC2WOL_LOC_ID_OFFSET + 1906, LocationType.EXTRA,
+ lambda state: logic.engine_of_destruction_requirement(state)),
+ LocationData("Engine of Destruction", "Engine of Destruction: Northwest Base", SC2WOL_LOC_ID_OFFSET + 1907, LocationType.EXTRA,
+ lambda state: logic.engine_of_destruction_requirement(state)),
+ LocationData("Engine of Destruction", "Engine of Destruction: Northeast Base", SC2WOL_LOC_ID_OFFSET + 1908, LocationType.EXTRA,
+ lambda state: logic.engine_of_destruction_requirement(state)),
+ LocationData("Engine of Destruction", "Engine of Destruction: Southeast Base", SC2WOL_LOC_ID_OFFSET + 1909, LocationType.EXTRA,
+ lambda state: logic.engine_of_destruction_requirement(state)),
+ LocationData("Media Blitz", "Media Blitz: Victory", SC2WOL_LOC_ID_OFFSET + 2000, LocationType.VICTORY,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Media Blitz", "Media Blitz: Tower 1", SC2WOL_LOC_ID_OFFSET + 2001, LocationType.VANILLA,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Media Blitz", "Media Blitz: Tower 2", SC2WOL_LOC_ID_OFFSET + 2002, LocationType.VANILLA,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Media Blitz", "Media Blitz: Tower 3", SC2WOL_LOC_ID_OFFSET + 2003, LocationType.VANILLA,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Media Blitz", "Media Blitz: Science Facility", SC2WOL_LOC_ID_OFFSET + 2004, LocationType.VANILLA),
+ LocationData("Media Blitz", "Media Blitz: All Barracks", SC2WOL_LOC_ID_OFFSET + 2005, LocationType.EXTRA,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Media Blitz", "Media Blitz: All Factories", SC2WOL_LOC_ID_OFFSET + 2006, LocationType.EXTRA,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Media Blitz", "Media Blitz: All Starports", SC2WOL_LOC_ID_OFFSET + 2007, LocationType.EXTRA,
+ lambda state: adv_tactics or logic.terran_competent_comp(state)),
+ LocationData("Media Blitz", "Media Blitz: Odin Not Trashed", SC2WOL_LOC_ID_OFFSET + 2008, LocationType.CHALLENGE,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Media Blitz", "Media Blitz: Surprise Attack Ends", SC2WOL_LOC_ID_OFFSET + 2009, LocationType.EXTRA),
+ LocationData("Piercing the Shroud", "Piercing the Shroud: Victory", SC2WOL_LOC_ID_OFFSET + 2100, LocationType.VICTORY,
+ lambda state: logic.marine_medic_upgrade(state)),
+ LocationData("Piercing the Shroud", "Piercing the Shroud: Holding Cell Relic", SC2WOL_LOC_ID_OFFSET + 2101, LocationType.VANILLA),
+ LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk Relic", SC2WOL_LOC_ID_OFFSET + 2102, LocationType.VANILLA,
+ lambda state: logic.marine_medic_upgrade(state)),
+ LocationData("Piercing the Shroud", "Piercing the Shroud: First Escape Relic", SC2WOL_LOC_ID_OFFSET + 2103, LocationType.VANILLA,
+ lambda state: logic.marine_medic_upgrade(state)),
+ LocationData("Piercing the Shroud", "Piercing the Shroud: Second Escape Relic", SC2WOL_LOC_ID_OFFSET + 2104, LocationType.VANILLA,
+ lambda state: logic.marine_medic_upgrade(state)),
+ LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk", SC2WOL_LOC_ID_OFFSET + 2105, LocationType.VANILLA,
+ lambda state: logic.marine_medic_upgrade(state)),
+ LocationData("Piercing the Shroud", "Piercing the Shroud: Fusion Reactor", SC2WOL_LOC_ID_OFFSET + 2106, LocationType.EXTRA,
+ lambda state: logic.marine_medic_upgrade(state)),
+ LocationData("Piercing the Shroud", "Piercing the Shroud: Entrance Holding Pen", SC2WOL_LOC_ID_OFFSET + 2107, LocationType.EXTRA),
+ LocationData("Piercing the Shroud", "Piercing the Shroud: Cargo Bay Warbot", SC2WOL_LOC_ID_OFFSET + 2108, LocationType.EXTRA),
+ LocationData("Piercing the Shroud", "Piercing the Shroud: Escape Warbot", SC2WOL_LOC_ID_OFFSET + 2109, LocationType.EXTRA,
+ lambda state: logic.marine_medic_upgrade(state)),
+ LocationData("Whispers of Doom", "Whispers of Doom: Victory", SC2WOL_LOC_ID_OFFSET + 2200, LocationType.VICTORY),
+ LocationData("Whispers of Doom", "Whispers of Doom: First Hatchery", SC2WOL_LOC_ID_OFFSET + 2201, LocationType.VANILLA),
+ LocationData("Whispers of Doom", "Whispers of Doom: Second Hatchery", SC2WOL_LOC_ID_OFFSET + 2202, LocationType.VANILLA),
+ LocationData("Whispers of Doom", "Whispers of Doom: Third Hatchery", SC2WOL_LOC_ID_OFFSET + 2203, LocationType.VANILLA),
+ LocationData("Whispers of Doom", "Whispers of Doom: First Prophecy Fragment", SC2WOL_LOC_ID_OFFSET + 2204, LocationType.EXTRA),
+ LocationData("Whispers of Doom", "Whispers of Doom: Second Prophecy Fragment", SC2WOL_LOC_ID_OFFSET + 2205, LocationType.EXTRA),
+ LocationData("Whispers of Doom", "Whispers of Doom: Third Prophecy Fragment", SC2WOL_LOC_ID_OFFSET + 2206, LocationType.EXTRA),
+ LocationData("A Sinister Turn", "A Sinister Turn: Victory", SC2WOL_LOC_ID_OFFSET + 2300, LocationType.VICTORY,
+ lambda state: logic.protoss_common_unit(state) and logic.protoss_competent_anti_air(state)),
+ LocationData("A Sinister Turn", "A Sinister Turn: Robotics Facility", SC2WOL_LOC_ID_OFFSET + 2301, LocationType.VANILLA,
+ lambda state: adv_tactics or logic.protoss_common_unit(state)),
+ LocationData("A Sinister Turn", "A Sinister Turn: Dark Shrine", SC2WOL_LOC_ID_OFFSET + 2302, LocationType.VANILLA,
+ lambda state: adv_tactics or logic.protoss_common_unit(state)),
+ LocationData("A Sinister Turn", "A Sinister Turn: Templar Archives", SC2WOL_LOC_ID_OFFSET + 2303, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state) and logic.protoss_competent_anti_air(state)),
+ LocationData("A Sinister Turn", "A Sinister Turn: Northeast Base", SC2WOL_LOC_ID_OFFSET + 2304, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state) and logic.protoss_competent_anti_air(state)),
+ LocationData("A Sinister Turn", "A Sinister Turn: Southwest Base", SC2WOL_LOC_ID_OFFSET + 2305, LocationType.CHALLENGE,
+ lambda state: logic.protoss_common_unit(state) and logic.protoss_competent_anti_air(state)),
+ LocationData("A Sinister Turn", "A Sinister Turn: Maar", SC2WOL_LOC_ID_OFFSET + 2306, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)),
+ LocationData("A Sinister Turn", "A Sinister Turn: Northwest Preserver", SC2WOL_LOC_ID_OFFSET + 2307, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state) and logic.protoss_competent_anti_air(state)),
+ LocationData("A Sinister Turn", "A Sinister Turn: Southwest Preserver", SC2WOL_LOC_ID_OFFSET + 2308, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state) and logic.protoss_competent_anti_air(state)),
+ LocationData("A Sinister Turn", "A Sinister Turn: East Preserver", SC2WOL_LOC_ID_OFFSET + 2309, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state) and logic.protoss_competent_anti_air(state)),
+ LocationData("Echoes of the Future", "Echoes of the Future: Victory", SC2WOL_LOC_ID_OFFSET + 2400, LocationType.VICTORY,
+ lambda state: adv_tactics and logic.protoss_static_defense(state) or logic.protoss_common_unit(state) and logic.protoss_competent_anti_air(state)),
+ LocationData("Echoes of the Future", "Echoes of the Future: Close Obelisk", SC2WOL_LOC_ID_OFFSET + 2401, LocationType.VANILLA),
+ LocationData("Echoes of the Future", "Echoes of the Future: West Obelisk", SC2WOL_LOC_ID_OFFSET + 2402, LocationType.VANILLA,
+ lambda state: adv_tactics and logic.protoss_static_defense(state) or logic.protoss_common_unit(state)),
+ LocationData("Echoes of the Future", "Echoes of the Future: Base", SC2WOL_LOC_ID_OFFSET + 2403, LocationType.EXTRA),
+ LocationData("Echoes of the Future", "Echoes of the Future: Southwest Tendril", SC2WOL_LOC_ID_OFFSET + 2404, LocationType.EXTRA),
+ LocationData("Echoes of the Future", "Echoes of the Future: Southeast Tendril", SC2WOL_LOC_ID_OFFSET + 2405, LocationType.EXTRA,
+ lambda state: adv_tactics and logic.protoss_static_defense(state) or logic.protoss_common_unit(state)),
+ LocationData("Echoes of the Future", "Echoes of the Future: Northeast Tendril", SC2WOL_LOC_ID_OFFSET + 2406, LocationType.EXTRA,
+ lambda state: adv_tactics and logic.protoss_static_defense(state) or logic.protoss_common_unit(state)),
+ LocationData("Echoes of the Future", "Echoes of the Future: Northwest Tendril", SC2WOL_LOC_ID_OFFSET + 2407, LocationType.EXTRA,
+ lambda state: adv_tactics and logic.protoss_static_defense(state) or logic.protoss_common_unit(state)),
+ LocationData("In Utter Darkness", "In Utter Darkness: Defeat", SC2WOL_LOC_ID_OFFSET + 2500, LocationType.VICTORY),
+ LocationData("In Utter Darkness", "In Utter Darkness: Protoss Archive", SC2WOL_LOC_ID_OFFSET + 2501, LocationType.VANILLA,
+ lambda state: logic.last_stand_requirement(state)),
+ LocationData("In Utter Darkness", "In Utter Darkness: Kills", SC2WOL_LOC_ID_OFFSET + 2502, LocationType.VANILLA,
+ lambda state: logic.last_stand_requirement(state)),
+ LocationData("In Utter Darkness", "In Utter Darkness: Urun", SC2WOL_LOC_ID_OFFSET + 2503, LocationType.EXTRA),
+ LocationData("In Utter Darkness", "In Utter Darkness: Mohandar", SC2WOL_LOC_ID_OFFSET + 2504, LocationType.EXTRA,
+ lambda state: logic.last_stand_requirement(state)),
+ LocationData("In Utter Darkness", "In Utter Darkness: Selendis", SC2WOL_LOC_ID_OFFSET + 2505, LocationType.EXTRA,
+ lambda state: logic.last_stand_requirement(state)),
+ LocationData("In Utter Darkness", "In Utter Darkness: Artanis", SC2WOL_LOC_ID_OFFSET + 2506, LocationType.EXTRA,
+ lambda state: logic.last_stand_requirement(state)),
+ LocationData("Gates of Hell", "Gates of Hell: Victory", SC2WOL_LOC_ID_OFFSET + 2600, LocationType.VICTORY,
+ lambda state: logic.terran_competent_comp(state) and
+ logic.terran_defense_rating(state, True) > 6),
+ LocationData("Gates of Hell", "Gates of Hell: Large Army", SC2WOL_LOC_ID_OFFSET + 2601, LocationType.VANILLA,
+ lambda state: logic.terran_competent_comp(state) and
+ logic.terran_defense_rating(state, True) > 6),
+ LocationData("Gates of Hell", "Gates of Hell: 2 Drop Pods", SC2WOL_LOC_ID_OFFSET + 2602, LocationType.VANILLA,
+ lambda state: logic.terran_competent_comp(state) and
+ logic.terran_defense_rating(state, True) > 6),
+ LocationData("Gates of Hell", "Gates of Hell: 4 Drop Pods", SC2WOL_LOC_ID_OFFSET + 2603, LocationType.VANILLA,
+ lambda state: logic.terran_competent_comp(state) and
+ logic.terran_defense_rating(state, True) > 6),
+ LocationData("Gates of Hell", "Gates of Hell: 6 Drop Pods", SC2WOL_LOC_ID_OFFSET + 2604, LocationType.EXTRA,
+ lambda state: logic.terran_competent_comp(state) and
+ logic.terran_defense_rating(state, True) > 6),
+ LocationData("Gates of Hell", "Gates of Hell: 8 Drop Pods", SC2WOL_LOC_ID_OFFSET + 2605, LocationType.CHALLENGE,
+ lambda state: logic.terran_competent_comp(state) and
+ logic.terran_defense_rating(state, True) > 6),
+ LocationData("Gates of Hell", "Gates of Hell: Southwest Spore Cannon", SC2WOL_LOC_ID_OFFSET + 2606, LocationType.EXTRA,
+ lambda state: logic.terran_competent_comp(state) and
+ logic.terran_defense_rating(state, True) > 6),
+ LocationData("Gates of Hell", "Gates of Hell: Northwest Spore Cannon", SC2WOL_LOC_ID_OFFSET + 2607, LocationType.EXTRA,
+ lambda state: logic.terran_competent_comp(state) and
+ logic.terran_defense_rating(state, True) > 6),
+ LocationData("Gates of Hell", "Gates of Hell: Northeast Spore Cannon", SC2WOL_LOC_ID_OFFSET + 2608, LocationType.EXTRA,
+ lambda state: logic.terran_competent_comp(state) and
+ logic.terran_defense_rating(state, True) > 6),
+ LocationData("Gates of Hell", "Gates of Hell: East Spore Cannon", SC2WOL_LOC_ID_OFFSET + 2609, LocationType.EXTRA,
+ lambda state: logic.terran_competent_comp(state) and
+ logic.terran_defense_rating(state, True) > 6),
+ LocationData("Gates of Hell", "Gates of Hell: Southeast Spore Cannon", SC2WOL_LOC_ID_OFFSET + 2610, LocationType.EXTRA,
+ lambda state: logic.terran_competent_comp(state) and
+ logic.terran_defense_rating(state, True) > 6),
+ LocationData("Gates of Hell", "Gates of Hell: Expansion Spore Cannon", SC2WOL_LOC_ID_OFFSET + 2611, LocationType.EXTRA,
+ lambda state: logic.terran_competent_comp(state) and
+ logic.terran_defense_rating(state, True) > 6),
+ LocationData("Belly of the Beast", "Belly of the Beast: Victory", SC2WOL_LOC_ID_OFFSET + 2700, LocationType.VICTORY),
+ LocationData("Belly of the Beast", "Belly of the Beast: First Charge", SC2WOL_LOC_ID_OFFSET + 2701, LocationType.EXTRA),
+ LocationData("Belly of the Beast", "Belly of the Beast: Second Charge", SC2WOL_LOC_ID_OFFSET + 2702, LocationType.EXTRA),
+ LocationData("Belly of the Beast", "Belly of the Beast: Third Charge", SC2WOL_LOC_ID_OFFSET + 2703, LocationType.EXTRA),
+ LocationData("Belly of the Beast", "Belly of the Beast: First Group Rescued", SC2WOL_LOC_ID_OFFSET + 2704, LocationType.VANILLA),
+ LocationData("Belly of the Beast", "Belly of the Beast: Second Group Rescued", SC2WOL_LOC_ID_OFFSET + 2705, LocationType.VANILLA),
+ LocationData("Belly of the Beast", "Belly of the Beast: Third Group Rescued", SC2WOL_LOC_ID_OFFSET + 2706, LocationType.VANILLA),
+ LocationData("Shatter the Sky", "Shatter the Sky: Victory", SC2WOL_LOC_ID_OFFSET + 2800, LocationType.VICTORY,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Shatter the Sky", "Shatter the Sky: Close Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2801, LocationType.VANILLA,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Shatter the Sky", "Shatter the Sky: Northwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2802, LocationType.VANILLA,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Shatter the Sky", "Shatter the Sky: Southeast Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2803, LocationType.VANILLA,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Shatter the Sky", "Shatter the Sky: Southwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2804, LocationType.VANILLA,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Shatter the Sky", "Shatter the Sky: Leviathan", SC2WOL_LOC_ID_OFFSET + 2805, LocationType.VANILLA,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Shatter the Sky", "Shatter the Sky: East Hatchery", SC2WOL_LOC_ID_OFFSET + 2806, LocationType.EXTRA,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Shatter the Sky", "Shatter the Sky: North Hatchery", SC2WOL_LOC_ID_OFFSET + 2807, LocationType.EXTRA,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("Shatter the Sky", "Shatter the Sky: Mid Hatchery", SC2WOL_LOC_ID_OFFSET + 2808, LocationType.EXTRA,
+ lambda state: logic.terran_competent_comp(state)),
+ LocationData("All-In", "All-In: Victory", SC2WOL_LOC_ID_OFFSET + 2900, LocationType.VICTORY,
+ lambda state: logic.all_in_requirement(state)),
+ LocationData("All-In", "All-In: First Kerrigan Attack", SC2WOL_LOC_ID_OFFSET + 2901, LocationType.EXTRA,
+ lambda state: logic.all_in_requirement(state)),
+ LocationData("All-In", "All-In: Second Kerrigan Attack", SC2WOL_LOC_ID_OFFSET + 2902, LocationType.EXTRA,
+ lambda state: logic.all_in_requirement(state)),
+ LocationData("All-In", "All-In: Third Kerrigan Attack", SC2WOL_LOC_ID_OFFSET + 2903, LocationType.EXTRA,
+ lambda state: logic.all_in_requirement(state)),
+ LocationData("All-In", "All-In: Fourth Kerrigan Attack", SC2WOL_LOC_ID_OFFSET + 2904, LocationType.EXTRA,
+ lambda state: logic.all_in_requirement(state)),
+ LocationData("All-In", "All-In: Fifth Kerrigan Attack", SC2WOL_LOC_ID_OFFSET + 2905, LocationType.EXTRA,
+ lambda state: logic.all_in_requirement(state)),
+
+ # HotS
+ LocationData("Lab Rat", "Lab Rat: Victory", SC2HOTS_LOC_ID_OFFSET + 100, LocationType.VICTORY,
+ lambda state: logic.zerg_common_unit(state)),
+ LocationData("Lab Rat", "Lab Rat: Gather Minerals", SC2HOTS_LOC_ID_OFFSET + 101, LocationType.VANILLA),
+ LocationData("Lab Rat", "Lab Rat: South Zergling Group", SC2HOTS_LOC_ID_OFFSET + 102, LocationType.VANILLA,
+ lambda state: adv_tactics or logic.zerg_common_unit(state)),
+ LocationData("Lab Rat", "Lab Rat: East Zergling Group", SC2HOTS_LOC_ID_OFFSET + 103, LocationType.VANILLA,
+ lambda state: adv_tactics or logic.zerg_common_unit(state)),
+ LocationData("Lab Rat", "Lab Rat: West Zergling Group", SC2HOTS_LOC_ID_OFFSET + 104, LocationType.VANILLA,
+ lambda state: adv_tactics or logic.zerg_common_unit(state)),
+ LocationData("Lab Rat", "Lab Rat: Hatchery", SC2HOTS_LOC_ID_OFFSET + 105, LocationType.EXTRA),
+ LocationData("Lab Rat", "Lab Rat: Overlord", SC2HOTS_LOC_ID_OFFSET + 106, LocationType.EXTRA),
+ LocationData("Lab Rat", "Lab Rat: Gas Turrets", SC2HOTS_LOC_ID_OFFSET + 107, LocationType.EXTRA,
+ lambda state: adv_tactics or logic.zerg_common_unit(state)),
+ LocationData("Back in the Saddle", "Back in the Saddle: Victory", SC2HOTS_LOC_ID_OFFSET + 200, LocationType.VICTORY,
+ lambda state: logic.basic_kerrigan(state) or kerriganless or logic.story_tech_granted),
+ LocationData("Back in the Saddle", "Back in the Saddle: Defend the Tram", SC2HOTS_LOC_ID_OFFSET + 201, LocationType.EXTRA,
+ lambda state: logic.basic_kerrigan(state) or kerriganless or logic.story_tech_granted),
+ LocationData("Back in the Saddle", "Back in the Saddle: Kinetic Blast", SC2HOTS_LOC_ID_OFFSET + 202, LocationType.VANILLA),
+ LocationData("Back in the Saddle", "Back in the Saddle: Crushing Grip", SC2HOTS_LOC_ID_OFFSET + 203, LocationType.VANILLA),
+ LocationData("Back in the Saddle", "Back in the Saddle: Reach the Sublevel", SC2HOTS_LOC_ID_OFFSET + 204, LocationType.EXTRA),
+ LocationData("Back in the Saddle", "Back in the Saddle: Door Section Cleared", SC2HOTS_LOC_ID_OFFSET + 205, LocationType.EXTRA,
+ lambda state: logic.basic_kerrigan(state) or kerriganless or logic.story_tech_granted),
+ LocationData("Rendezvous", "Rendezvous: Victory", SC2HOTS_LOC_ID_OFFSET + 300, LocationType.VICTORY,
+ lambda state: logic.zerg_common_unit(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Rendezvous", "Rendezvous: Right Queen", SC2HOTS_LOC_ID_OFFSET + 301, LocationType.VANILLA,
+ lambda state: logic.zerg_common_unit(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Rendezvous", "Rendezvous: Center Queen", SC2HOTS_LOC_ID_OFFSET + 302, LocationType.VANILLA,
+ lambda state: logic.zerg_common_unit(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Rendezvous", "Rendezvous: Left Queen", SC2HOTS_LOC_ID_OFFSET + 303, LocationType.VANILLA,
+ lambda state: logic.zerg_common_unit(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Rendezvous", "Rendezvous: Hold Out Finished", SC2HOTS_LOC_ID_OFFSET + 304, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Harvest of Screams", "Harvest of Screams: Victory", SC2HOTS_LOC_ID_OFFSET + 400, LocationType.VICTORY,
+ lambda state: logic.zerg_common_unit(state)
+ and logic.zerg_basic_anti_air(state)),
+ LocationData("Harvest of Screams", "Harvest of Screams: First Ursadon Matriarch", SC2HOTS_LOC_ID_OFFSET + 401, LocationType.VANILLA),
+ LocationData("Harvest of Screams", "Harvest of Screams: North Ursadon Matriarch", SC2HOTS_LOC_ID_OFFSET + 402, LocationType.VANILLA,
+ lambda state: logic.zerg_common_unit(state)),
+ LocationData("Harvest of Screams", "Harvest of Screams: West Ursadon Matriarch", SC2HOTS_LOC_ID_OFFSET + 403, LocationType.VANILLA,
+ lambda state: logic.zerg_common_unit(state)),
+ LocationData("Harvest of Screams", "Harvest of Screams: Lost Brood", SC2HOTS_LOC_ID_OFFSET + 404, LocationType.EXTRA),
+ LocationData("Harvest of Screams", "Harvest of Screams: Northeast Psi-link Spire", SC2HOTS_LOC_ID_OFFSET + 405, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state)),
+ LocationData("Harvest of Screams", "Harvest of Screams: Northwest Psi-link Spire", SC2HOTS_LOC_ID_OFFSET + 406, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state)
+ and logic.zerg_basic_anti_air(state)),
+ LocationData("Harvest of Screams", "Harvest of Screams: Southwest Psi-link Spire", SC2HOTS_LOC_ID_OFFSET + 407, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state)
+ and logic.zerg_basic_anti_air(state)),
+ LocationData("Harvest of Screams", "Harvest of Screams: Nafash", SC2HOTS_LOC_ID_OFFSET + 408, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state)
+ and logic.zerg_basic_anti_air(state)),
+ LocationData("Shoot the Messenger", "Shoot the Messenger: Victory", SC2HOTS_LOC_ID_OFFSET + 500, LocationType.VICTORY,
+ lambda state: logic.zerg_common_unit(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Shoot the Messenger", "Shoot the Messenger: East Stasis Chamber", SC2HOTS_LOC_ID_OFFSET + 501, LocationType.VANILLA,
+ lambda state: logic.zerg_common_unit(state) and logic.zerg_basic_anti_air(state)),
+ LocationData("Shoot the Messenger", "Shoot the Messenger: Center Stasis Chamber", SC2HOTS_LOC_ID_OFFSET + 502, LocationType.VANILLA,
+ lambda state: logic.zerg_common_unit(state) or adv_tactics),
+ LocationData("Shoot the Messenger", "Shoot the Messenger: West Stasis Chamber", SC2HOTS_LOC_ID_OFFSET + 503, LocationType.VANILLA,
+ lambda state: logic.zerg_common_unit(state) and logic.zerg_basic_anti_air(state)),
+ LocationData("Shoot the Messenger", "Shoot the Messenger: Destroy 4 Shuttles", SC2HOTS_LOC_ID_OFFSET + 504, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state) and logic.zerg_basic_anti_air(state)),
+ LocationData("Shoot the Messenger", "Shoot the Messenger: Frozen Expansion", SC2HOTS_LOC_ID_OFFSET + 505, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state)),
+ LocationData("Shoot the Messenger", "Shoot the Messenger: Southwest Frozen Zerg", SC2HOTS_LOC_ID_OFFSET + 506, LocationType.EXTRA),
+ LocationData("Shoot the Messenger", "Shoot the Messenger: Southeast Frozen Zerg", SC2HOTS_LOC_ID_OFFSET + 507, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state) or adv_tactics),
+ LocationData("Shoot the Messenger", "Shoot the Messenger: West Frozen Zerg", SC2HOTS_LOC_ID_OFFSET + 508, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state) and logic.zerg_basic_anti_air(state)),
+ LocationData("Shoot the Messenger", "Shoot the Messenger: East Frozen Zerg", SC2HOTS_LOC_ID_OFFSET + 509, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state) and logic.zerg_competent_anti_air(state)),
+ LocationData("Enemy Within", "Enemy Within: Victory", SC2HOTS_LOC_ID_OFFSET + 600, LocationType.VICTORY,
+ lambda state: logic.zerg_pass_vents(state)
+ and (logic.story_tech_granted
+ or state.has_any({ItemNames.ZERGLING_RAPTOR_STRAIN, ItemNames.ROACH,
+ ItemNames.HYDRALISK, ItemNames.INFESTOR}, player))
+ ),
+ LocationData("Enemy Within", "Enemy Within: Infest Giant Ursadon", SC2HOTS_LOC_ID_OFFSET + 601, LocationType.VANILLA,
+ lambda state: logic.zerg_pass_vents(state)),
+ LocationData("Enemy Within", "Enemy Within: First Niadra Evolution", SC2HOTS_LOC_ID_OFFSET + 602, LocationType.VANILLA,
+ lambda state: logic.zerg_pass_vents(state)),
+ LocationData("Enemy Within", "Enemy Within: Second Niadra Evolution", SC2HOTS_LOC_ID_OFFSET + 603, LocationType.VANILLA,
+ lambda state: logic.zerg_pass_vents(state)),
+ LocationData("Enemy Within", "Enemy Within: Third Niadra Evolution", SC2HOTS_LOC_ID_OFFSET + 604, LocationType.VANILLA,
+ lambda state: logic.zerg_pass_vents(state)),
+ LocationData("Enemy Within", "Enemy Within: Warp Drive", SC2HOTS_LOC_ID_OFFSET + 605, LocationType.EXTRA,
+ lambda state: logic.zerg_pass_vents(state)),
+ LocationData("Enemy Within", "Enemy Within: Stasis Quadrant", SC2HOTS_LOC_ID_OFFSET + 606, LocationType.EXTRA,
+ lambda state: logic.zerg_pass_vents(state)),
+ LocationData("Domination", "Domination: Victory", SC2HOTS_LOC_ID_OFFSET + 700, LocationType.VICTORY,
+ lambda state: logic.zerg_common_unit(state) and logic.zerg_basic_anti_air(state)),
+ LocationData("Domination", "Domination: Center Infested Command Center", SC2HOTS_LOC_ID_OFFSET + 701, LocationType.VANILLA,
+ lambda state: logic.zerg_common_unit(state)),
+ LocationData("Domination", "Domination: North Infested Command Center", SC2HOTS_LOC_ID_OFFSET + 702, LocationType.VANILLA,
+ lambda state: logic.zerg_common_unit(state)),
+ LocationData("Domination", "Domination: Repel Zagara", SC2HOTS_LOC_ID_OFFSET + 703, LocationType.EXTRA),
+ LocationData("Domination", "Domination: Close Baneling Nest", SC2HOTS_LOC_ID_OFFSET + 704, LocationType.EXTRA),
+ LocationData("Domination", "Domination: South Baneling Nest", SC2HOTS_LOC_ID_OFFSET + 705, LocationType.EXTRA,
+ lambda state: adv_tactics or logic.zerg_common_unit(state)),
+ LocationData("Domination", "Domination: Southwest Baneling Nest", SC2HOTS_LOC_ID_OFFSET + 706, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state)),
+ LocationData("Domination", "Domination: Southeast Baneling Nest", SC2HOTS_LOC_ID_OFFSET + 707, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state) and logic.zerg_basic_anti_air(state)),
+ LocationData("Domination", "Domination: North Baneling Nest", SC2HOTS_LOC_ID_OFFSET + 708, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state)),
+ LocationData("Domination", "Domination: Northeast Baneling Nest", SC2HOTS_LOC_ID_OFFSET + 709, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state)),
+ LocationData("Fire in the Sky", "Fire in the Sky: Victory", SC2HOTS_LOC_ID_OFFSET + 800, LocationType.VICTORY,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state) and
+ logic.spread_creep(state)),
+ LocationData("Fire in the Sky", "Fire in the Sky: West Biomass", SC2HOTS_LOC_ID_OFFSET + 801, LocationType.VANILLA),
+ LocationData("Fire in the Sky", "Fire in the Sky: North Biomass", SC2HOTS_LOC_ID_OFFSET + 802, LocationType.VANILLA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state) and
+ logic.spread_creep(state)),
+ LocationData("Fire in the Sky", "Fire in the Sky: South Biomass", SC2HOTS_LOC_ID_OFFSET + 803, LocationType.VANILLA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state) and
+ logic.spread_creep(state)),
+ LocationData("Fire in the Sky", "Fire in the Sky: Destroy 3 Gorgons", SC2HOTS_LOC_ID_OFFSET + 804, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state) and
+ logic.spread_creep(state)),
+ LocationData("Fire in the Sky", "Fire in the Sky: Close Zerg Rescue", SC2HOTS_LOC_ID_OFFSET + 805, LocationType.EXTRA),
+ LocationData("Fire in the Sky", "Fire in the Sky: South Zerg Rescue", SC2HOTS_LOC_ID_OFFSET + 806, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state)),
+ LocationData("Fire in the Sky", "Fire in the Sky: North Zerg Rescue", SC2HOTS_LOC_ID_OFFSET + 807, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state) and
+ logic.spread_creep(state)),
+ LocationData("Fire in the Sky", "Fire in the Sky: West Queen Rescue", SC2HOTS_LOC_ID_OFFSET + 808, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state) and
+ logic.spread_creep(state)),
+ LocationData("Fire in the Sky", "Fire in the Sky: East Queen Rescue", SC2HOTS_LOC_ID_OFFSET + 809, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state) and
+ logic.spread_creep(state)),
+ LocationData("Old Soldiers", "Old Soldiers: Victory", SC2HOTS_LOC_ID_OFFSET + 900, LocationType.VICTORY,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Old Soldiers", "Old Soldiers: East Science Lab", SC2HOTS_LOC_ID_OFFSET + 901, LocationType.VANILLA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Old Soldiers", "Old Soldiers: North Science Lab", SC2HOTS_LOC_ID_OFFSET + 902, LocationType.VANILLA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Old Soldiers", "Old Soldiers: Get Nuked", SC2HOTS_LOC_ID_OFFSET + 903, LocationType.EXTRA),
+ LocationData("Old Soldiers", "Old Soldiers: Entrance Gate", SC2HOTS_LOC_ID_OFFSET + 904, LocationType.EXTRA),
+ LocationData("Old Soldiers", "Old Soldiers: Citadel Gate", SC2HOTS_LOC_ID_OFFSET + 905, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Old Soldiers", "Old Soldiers: South Expansion", SC2HOTS_LOC_ID_OFFSET + 906, LocationType.EXTRA),
+ LocationData("Old Soldiers", "Old Soldiers: Rich Mineral Expansion", SC2HOTS_LOC_ID_OFFSET + 907, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Waking the Ancient", "Waking the Ancient: Victory", SC2HOTS_LOC_ID_OFFSET + 1000, LocationType.VICTORY,
+ lambda state: logic.zerg_common_unit(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Waking the Ancient", "Waking the Ancient: Center Essence Pool", SC2HOTS_LOC_ID_OFFSET + 1001, LocationType.VANILLA),
+ LocationData("Waking the Ancient", "Waking the Ancient: East Essence Pool", SC2HOTS_LOC_ID_OFFSET + 1002, LocationType.VANILLA,
+ lambda state: logic.zerg_common_unit(state) and
+ (adv_tactics and logic.zerg_basic_anti_air(state)
+ or logic.zerg_competent_anti_air(state))),
+ LocationData("Waking the Ancient", "Waking the Ancient: South Essence Pool", SC2HOTS_LOC_ID_OFFSET + 1003, LocationType.VANILLA,
+ lambda state: logic.zerg_common_unit(state) and
+ (adv_tactics and logic.zerg_basic_anti_air(state)
+ or logic.zerg_competent_anti_air(state))),
+ LocationData("Waking the Ancient", "Waking the Ancient: Finish Feeding", SC2HOTS_LOC_ID_OFFSET + 1004, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Waking the Ancient", "Waking the Ancient: South Proxy Primal Hive", SC2HOTS_LOC_ID_OFFSET + 1005, LocationType.CHALLENGE,
+ lambda state: logic.zerg_common_unit(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Waking the Ancient", "Waking the Ancient: East Proxy Primal Hive", SC2HOTS_LOC_ID_OFFSET + 1006, LocationType.CHALLENGE,
+ lambda state: logic.zerg_common_unit(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Waking the Ancient", "Waking the Ancient: South Main Primal Hive", SC2HOTS_LOC_ID_OFFSET + 1007, LocationType.CHALLENGE,
+ lambda state: logic.zerg_common_unit(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Waking the Ancient", "Waking the Ancient: East Main Primal Hive", SC2HOTS_LOC_ID_OFFSET + 1008, LocationType.CHALLENGE,
+ lambda state: logic.zerg_common_unit(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("The Crucible", "The Crucible: Victory", SC2HOTS_LOC_ID_OFFSET + 1100, LocationType.VICTORY,
+ lambda state: logic.zerg_competent_defense(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("The Crucible", "The Crucible: Tyrannozor", SC2HOTS_LOC_ID_OFFSET + 1101, LocationType.VANILLA,
+ lambda state: logic.zerg_competent_defense(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("The Crucible", "The Crucible: Reach the Pool", SC2HOTS_LOC_ID_OFFSET + 1102, LocationType.VANILLA),
+ LocationData("The Crucible", "The Crucible: 15 Minutes Remaining", SC2HOTS_LOC_ID_OFFSET + 1103, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_defense(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("The Crucible", "The Crucible: 5 Minutes Remaining", SC2HOTS_LOC_ID_OFFSET + 1104, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_defense(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("The Crucible", "The Crucible: Pincer Attack", SC2HOTS_LOC_ID_OFFSET + 1105, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_defense(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("The Crucible", "The Crucible: Yagdra Claims Brakk's Pack", SC2HOTS_LOC_ID_OFFSET + 1106, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_defense(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Supreme", "Supreme: Victory", SC2HOTS_LOC_ID_OFFSET + 1200, LocationType.VICTORY,
+ lambda state: logic.supreme_requirement(state)),
+ LocationData("Supreme", "Supreme: First Relic", SC2HOTS_LOC_ID_OFFSET + 1201, LocationType.VANILLA,
+ lambda state: logic.supreme_requirement(state)),
+ LocationData("Supreme", "Supreme: Second Relic", SC2HOTS_LOC_ID_OFFSET + 1202, LocationType.VANILLA,
+ lambda state: logic.supreme_requirement(state)),
+ LocationData("Supreme", "Supreme: Third Relic", SC2HOTS_LOC_ID_OFFSET + 1203, LocationType.VANILLA,
+ lambda state: logic.supreme_requirement(state)),
+ LocationData("Supreme", "Supreme: Fourth Relic", SC2HOTS_LOC_ID_OFFSET + 1204, LocationType.VANILLA,
+ lambda state: logic.supreme_requirement(state)),
+ LocationData("Supreme", "Supreme: Yagdra", SC2HOTS_LOC_ID_OFFSET + 1205, LocationType.EXTRA,
+ lambda state: logic.supreme_requirement(state)),
+ LocationData("Supreme", "Supreme: Kraith", SC2HOTS_LOC_ID_OFFSET + 1206, LocationType.EXTRA,
+ lambda state: logic.supreme_requirement(state)),
+ LocationData("Supreme", "Supreme: Slivan", SC2HOTS_LOC_ID_OFFSET + 1207, LocationType.EXTRA,
+ lambda state: logic.supreme_requirement(state)),
+ LocationData("Infested", "Infested: Victory", SC2HOTS_LOC_ID_OFFSET + 1300, LocationType.VICTORY,
+ lambda state: logic.zerg_common_unit(state) and
+ ((logic.zerg_competent_anti_air(state) and state.has(ItemNames.INFESTOR, player)) or
+ (adv_tactics and logic.zerg_basic_anti_air(state)))),
+ LocationData("Infested", "Infested: East Science Facility", SC2HOTS_LOC_ID_OFFSET + 1301, LocationType.VANILLA,
+ lambda state: logic.zerg_common_unit(state) and
+ logic.zerg_basic_anti_air(state) and
+ logic.spread_creep(state)),
+ LocationData("Infested", "Infested: Center Science Facility", SC2HOTS_LOC_ID_OFFSET + 1302, LocationType.VANILLA,
+ lambda state: logic.zerg_common_unit(state) and
+ logic.zerg_basic_anti_air(state) and
+ logic.spread_creep(state)),
+ LocationData("Infested", "Infested: West Science Facility", SC2HOTS_LOC_ID_OFFSET + 1303, LocationType.VANILLA,
+ lambda state: logic.zerg_common_unit(state) and
+ logic.zerg_basic_anti_air(state) and
+ logic.spread_creep(state)),
+ LocationData("Infested", "Infested: First Intro Garrison", SC2HOTS_LOC_ID_OFFSET + 1304, LocationType.EXTRA),
+ LocationData("Infested", "Infested: Second Intro Garrison", SC2HOTS_LOC_ID_OFFSET + 1305, LocationType.EXTRA),
+ LocationData("Infested", "Infested: Base Garrison", SC2HOTS_LOC_ID_OFFSET + 1306, LocationType.EXTRA),
+ LocationData("Infested", "Infested: East Garrison", SC2HOTS_LOC_ID_OFFSET + 1307, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state)
+ and logic.zerg_basic_anti_air(state)
+ and (adv_tactics or state.has(ItemNames.INFESTOR, player))),
+ LocationData("Infested", "Infested: Mid Garrison", SC2HOTS_LOC_ID_OFFSET + 1308, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state)
+ and logic.zerg_basic_anti_air(state)
+ and (adv_tactics or state.has(ItemNames.INFESTOR, player))),
+ LocationData("Infested", "Infested: North Garrison", SC2HOTS_LOC_ID_OFFSET + 1309, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state)
+ and logic.zerg_basic_anti_air(state)
+ and (adv_tactics or state.has(ItemNames.INFESTOR, player))),
+ LocationData("Infested", "Infested: Close Southwest Garrison", SC2HOTS_LOC_ID_OFFSET + 1310, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state)
+ and logic.zerg_basic_anti_air(state)
+ and (adv_tactics or state.has(ItemNames.INFESTOR, player))),
+ LocationData("Infested", "Infested: Far Southwest Garrison", SC2HOTS_LOC_ID_OFFSET + 1311, LocationType.EXTRA,
+ lambda state: logic.zerg_common_unit(state)
+ and logic.zerg_basic_anti_air(state)
+ and (adv_tactics or state.has(ItemNames.INFESTOR, player))),
+ LocationData("Hand of Darkness", "Hand of Darkness: Victory", SC2HOTS_LOC_ID_OFFSET + 1400, LocationType.VICTORY,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Hand of Darkness", "Hand of Darkness: North Brutalisk", SC2HOTS_LOC_ID_OFFSET + 1401, LocationType.VANILLA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Hand of Darkness", "Hand of Darkness: South Brutalisk", SC2HOTS_LOC_ID_OFFSET + 1402, LocationType.VANILLA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Hand of Darkness", "Hand of Darkness: Kill 1 Hybrid", SC2HOTS_LOC_ID_OFFSET + 1403, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Hand of Darkness", "Hand of Darkness: Kill 2 Hybrid", SC2HOTS_LOC_ID_OFFSET + 1404, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Hand of Darkness", "Hand of Darkness: Kill 3 Hybrid", SC2HOTS_LOC_ID_OFFSET + 1405, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Hand of Darkness", "Hand of Darkness: Kill 4 Hybrid", SC2HOTS_LOC_ID_OFFSET + 1406, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Hand of Darkness", "Hand of Darkness: Kill 5 Hybrid", SC2HOTS_LOC_ID_OFFSET + 1407, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Hand of Darkness", "Hand of Darkness: Kill 6 Hybrid", SC2HOTS_LOC_ID_OFFSET + 1408, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Hand of Darkness", "Hand of Darkness: Kill 7 Hybrid", SC2HOTS_LOC_ID_OFFSET + 1409, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_basic_anti_air(state)),
+ LocationData("Phantoms of the Void", "Phantoms of the Void: Victory", SC2HOTS_LOC_ID_OFFSET + 1500, LocationType.VICTORY,
+ lambda state: logic.zerg_competent_comp(state) and
+ (logic.zerg_competent_anti_air(state) or adv_tactics)),
+ LocationData("Phantoms of the Void", "Phantoms of the Void: Northwest Crystal", SC2HOTS_LOC_ID_OFFSET + 1501, LocationType.VANILLA,
+ lambda state: logic.zerg_competent_comp(state) and
+ (logic.zerg_competent_anti_air(state) or adv_tactics)),
+ LocationData("Phantoms of the Void", "Phantoms of the Void: Northeast Crystal", SC2HOTS_LOC_ID_OFFSET + 1502, LocationType.VANILLA,
+ lambda state: logic.zerg_competent_comp(state) and
+ (logic.zerg_competent_anti_air(state) or adv_tactics)),
+ LocationData("Phantoms of the Void", "Phantoms of the Void: South Crystal", SC2HOTS_LOC_ID_OFFSET + 1503, LocationType.VANILLA),
+ LocationData("Phantoms of the Void", "Phantoms of the Void: Base Established", SC2HOTS_LOC_ID_OFFSET + 1504, LocationType.EXTRA),
+ LocationData("Phantoms of the Void", "Phantoms of the Void: Close Temple", SC2HOTS_LOC_ID_OFFSET + 1505, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ (logic.zerg_competent_anti_air(state) or adv_tactics)),
+ LocationData("Phantoms of the Void", "Phantoms of the Void: Mid Temple", SC2HOTS_LOC_ID_OFFSET + 1506, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ (logic.zerg_competent_anti_air(state) or adv_tactics)),
+ LocationData("Phantoms of the Void", "Phantoms of the Void: Southeast Temple", SC2HOTS_LOC_ID_OFFSET + 1507, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ (logic.zerg_competent_anti_air(state) or adv_tactics)),
+ LocationData("Phantoms of the Void", "Phantoms of the Void: Northeast Temple", SC2HOTS_LOC_ID_OFFSET + 1508, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ (logic.zerg_competent_anti_air(state) or adv_tactics)),
+ LocationData("Phantoms of the Void", "Phantoms of the Void: Northwest Temple", SC2HOTS_LOC_ID_OFFSET + 1509, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ (logic.zerg_competent_anti_air(state) or adv_tactics)),
+ LocationData("With Friends Like These", "With Friends Like These: Victory", SC2HOTS_LOC_ID_OFFSET + 1600, LocationType.VICTORY),
+ LocationData("With Friends Like These", "With Friends Like These: Pirate Capital Ship", SC2HOTS_LOC_ID_OFFSET + 1601, LocationType.VANILLA),
+ LocationData("With Friends Like These", "With Friends Like These: First Mineral Patch", SC2HOTS_LOC_ID_OFFSET + 1602, LocationType.VANILLA),
+ LocationData("With Friends Like These", "With Friends Like These: Second Mineral Patch", SC2HOTS_LOC_ID_OFFSET + 1603, LocationType.VANILLA),
+ LocationData("With Friends Like These", "With Friends Like These: Third Mineral Patch", SC2HOTS_LOC_ID_OFFSET + 1604, LocationType.VANILLA),
+ LocationData("Conviction", "Conviction: Victory", SC2HOTS_LOC_ID_OFFSET + 1700, LocationType.VICTORY,
+ lambda state: logic.two_kerrigan_actives(state) and
+ (logic.basic_kerrigan(state) or logic.story_tech_granted) or kerriganless),
+ LocationData("Conviction", "Conviction: First Secret Documents", SC2HOTS_LOC_ID_OFFSET + 1701, LocationType.VANILLA,
+ lambda state: logic.two_kerrigan_actives(state) or kerriganless),
+ LocationData("Conviction", "Conviction: Second Secret Documents", SC2HOTS_LOC_ID_OFFSET + 1702, LocationType.VANILLA,
+ lambda state: logic.two_kerrigan_actives(state) and
+ (logic.basic_kerrigan(state) or logic.story_tech_granted) or kerriganless),
+ LocationData("Conviction", "Conviction: Power Coupling", SC2HOTS_LOC_ID_OFFSET + 1703, LocationType.EXTRA,
+ lambda state: logic.two_kerrigan_actives(state) or kerriganless),
+ LocationData("Conviction", "Conviction: Door Blasted", SC2HOTS_LOC_ID_OFFSET + 1704, LocationType.EXTRA,
+ lambda state: logic.two_kerrigan_actives(state) or kerriganless),
+ LocationData("Planetfall", "Planetfall: Victory", SC2HOTS_LOC_ID_OFFSET + 1800, LocationType.VICTORY,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Planetfall", "Planetfall: East Gate", SC2HOTS_LOC_ID_OFFSET + 1801, LocationType.VANILLA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Planetfall", "Planetfall: Northwest Gate", SC2HOTS_LOC_ID_OFFSET + 1802, LocationType.VANILLA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Planetfall", "Planetfall: North Gate", SC2HOTS_LOC_ID_OFFSET + 1803, LocationType.VANILLA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Planetfall", "Planetfall: 1 Bile Launcher Deployed", SC2HOTS_LOC_ID_OFFSET + 1804, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Planetfall", "Planetfall: 2 Bile Launchers Deployed", SC2HOTS_LOC_ID_OFFSET + 1805, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Planetfall", "Planetfall: 3 Bile Launchers Deployed", SC2HOTS_LOC_ID_OFFSET + 1806, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Planetfall", "Planetfall: 4 Bile Launchers Deployed", SC2HOTS_LOC_ID_OFFSET + 1807, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Planetfall", "Planetfall: 5 Bile Launchers Deployed", SC2HOTS_LOC_ID_OFFSET + 1808, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Planetfall", "Planetfall: Sons of Korhal", SC2HOTS_LOC_ID_OFFSET + 1809, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Planetfall", "Planetfall: Night Wolves", SC2HOTS_LOC_ID_OFFSET + 1810, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Planetfall", "Planetfall: West Expansion", SC2HOTS_LOC_ID_OFFSET + 1811, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Planetfall", "Planetfall: Mid Expansion", SC2HOTS_LOC_ID_OFFSET + 1812, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Death From Above", "Death From Above: Victory", SC2HOTS_LOC_ID_OFFSET + 1900, LocationType.VICTORY,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Death From Above", "Death From Above: First Power Link", SC2HOTS_LOC_ID_OFFSET + 1901, LocationType.VANILLA),
+ LocationData("Death From Above", "Death From Above: Second Power Link", SC2HOTS_LOC_ID_OFFSET + 1902, LocationType.VANILLA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Death From Above", "Death From Above: Third Power Link", SC2HOTS_LOC_ID_OFFSET + 1903, LocationType.VANILLA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Death From Above", "Death From Above: Expansion Command Center", SC2HOTS_LOC_ID_OFFSET + 1904, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("Death From Above", "Death From Above: Main Path Command Center", SC2HOTS_LOC_ID_OFFSET + 1905, LocationType.EXTRA,
+ lambda state: logic.zerg_competent_comp(state) and
+ logic.zerg_competent_anti_air(state)),
+ LocationData("The Reckoning", "The Reckoning: Victory", SC2HOTS_LOC_ID_OFFSET + 2000, LocationType.VICTORY,
+ lambda state: logic.the_reckoning_requirement(state)),
+ LocationData("The Reckoning", "The Reckoning: South Lane", SC2HOTS_LOC_ID_OFFSET + 2001, LocationType.VANILLA,
+ lambda state: logic.the_reckoning_requirement(state)),
+ LocationData("The Reckoning", "The Reckoning: North Lane", SC2HOTS_LOC_ID_OFFSET + 2002, LocationType.VANILLA,
+ lambda state: logic.the_reckoning_requirement(state)),
+ LocationData("The Reckoning", "The Reckoning: East Lane", SC2HOTS_LOC_ID_OFFSET + 2003, LocationType.VANILLA,
+ lambda state: logic.the_reckoning_requirement(state)),
+ LocationData("The Reckoning", "The Reckoning: Odin", SC2HOTS_LOC_ID_OFFSET + 2004, LocationType.EXTRA,
+ lambda state: logic.the_reckoning_requirement(state)),
+
+ # LotV Prologue
+ LocationData("Dark Whispers", "Dark Whispers: Victory", SC2LOTV_LOC_ID_OFFSET + 100, LocationType.VICTORY,
+ lambda state: logic.protoss_common_unit(state) \
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("Dark Whispers", "Dark Whispers: First Prisoner Group", SC2LOTV_LOC_ID_OFFSET + 101, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state) \
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("Dark Whispers", "Dark Whispers: Second Prisoner Group", SC2LOTV_LOC_ID_OFFSET + 102, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state) \
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("Dark Whispers", "Dark Whispers: First Pylon", SC2LOTV_LOC_ID_OFFSET + 103, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state) \
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("Dark Whispers", "Dark Whispers: Second Pylon", SC2LOTV_LOC_ID_OFFSET + 104, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state) \
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("Ghosts in the Fog", "Ghosts in the Fog: Victory", SC2LOTV_LOC_ID_OFFSET + 200, LocationType.VICTORY,
+ lambda state: logic.protoss_common_unit(state) \
+ and logic.protoss_anti_armor_anti_air(state)),
+ LocationData("Ghosts in the Fog", "Ghosts in the Fog: South Rock Formation", SC2LOTV_LOC_ID_OFFSET + 201, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state) \
+ and logic.protoss_anti_armor_anti_air(state)),
+ LocationData("Ghosts in the Fog", "Ghosts in the Fog: West Rock Formation", SC2LOTV_LOC_ID_OFFSET + 202, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state) \
+ and logic.protoss_anti_armor_anti_air(state)),
+ LocationData("Ghosts in the Fog", "Ghosts in the Fog: East Rock Formation", SC2LOTV_LOC_ID_OFFSET + 203, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state) \
+ and logic.protoss_anti_armor_anti_air(state) \
+ and logic.protoss_can_attack_behind_chasm(state)),
+ LocationData("Evil Awoken", "Evil Awoken: Victory", SC2LOTV_LOC_ID_OFFSET + 300, LocationType.VICTORY,
+ lambda state: adv_tactics or logic.protoss_stalker_upgrade(state)),
+ LocationData("Evil Awoken", "Evil Awoken: Temple Investigated", SC2LOTV_LOC_ID_OFFSET + 301, LocationType.EXTRA),
+ LocationData("Evil Awoken", "Evil Awoken: Void Catalyst", SC2LOTV_LOC_ID_OFFSET + 302, LocationType.EXTRA),
+ LocationData("Evil Awoken", "Evil Awoken: First Particle Cannon", SC2LOTV_LOC_ID_OFFSET + 303, LocationType.VANILLA),
+ LocationData("Evil Awoken", "Evil Awoken: Second Particle Cannon", SC2LOTV_LOC_ID_OFFSET + 304, LocationType.VANILLA),
+ LocationData("Evil Awoken", "Evil Awoken: Third Particle Cannon", SC2LOTV_LOC_ID_OFFSET + 305, LocationType.VANILLA),
+
+
+ # LotV
+ LocationData("For Aiur!", "For Aiur!: Victory", SC2LOTV_LOC_ID_OFFSET + 400, LocationType.VICTORY),
+ LocationData("For Aiur!", "For Aiur!: Southwest Hive", SC2LOTV_LOC_ID_OFFSET + 401, LocationType.VANILLA),
+ LocationData("For Aiur!", "For Aiur!: Northwest Hive", SC2LOTV_LOC_ID_OFFSET + 402, LocationType.VANILLA),
+ LocationData("For Aiur!", "For Aiur!: Northeast Hive", SC2LOTV_LOC_ID_OFFSET + 403, LocationType.VANILLA),
+ LocationData("For Aiur!", "For Aiur!: East Hive", SC2LOTV_LOC_ID_OFFSET + 404, LocationType.VANILLA),
+ LocationData("For Aiur!", "For Aiur!: West Conduit", SC2LOTV_LOC_ID_OFFSET + 405, LocationType.EXTRA),
+ LocationData("For Aiur!", "For Aiur!: Middle Conduit", SC2LOTV_LOC_ID_OFFSET + 406, LocationType.EXTRA),
+ LocationData("For Aiur!", "For Aiur!: Northeast Conduit", SC2LOTV_LOC_ID_OFFSET + 407, LocationType.EXTRA),
+ LocationData("The Growing Shadow", "The Growing Shadow: Victory", SC2LOTV_LOC_ID_OFFSET + 500, LocationType.VICTORY,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("The Growing Shadow", "The Growing Shadow: Close Pylon", SC2LOTV_LOC_ID_OFFSET + 501, LocationType.VANILLA),
+ LocationData("The Growing Shadow", "The Growing Shadow: East Pylon", SC2LOTV_LOC_ID_OFFSET + 502, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("The Growing Shadow", "The Growing Shadow: West Pylon", SC2LOTV_LOC_ID_OFFSET + 503, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("The Growing Shadow", "The Growing Shadow: Nexus", SC2LOTV_LOC_ID_OFFSET + 504, LocationType.EXTRA),
+ LocationData("The Growing Shadow", "The Growing Shadow: Templar Base", SC2LOTV_LOC_ID_OFFSET + 505, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("The Spear of Adun", "The Spear of Adun: Victory", SC2LOTV_LOC_ID_OFFSET + 600, LocationType.VICTORY,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("The Spear of Adun", "The Spear of Adun: Close Warp Gate", SC2LOTV_LOC_ID_OFFSET + 601, LocationType.VANILLA),
+ LocationData("The Spear of Adun", "The Spear of Adun: West Warp Gate", SC2LOTV_LOC_ID_OFFSET + 602, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("The Spear of Adun", "The Spear of Adun: North Warp Gate", SC2LOTV_LOC_ID_OFFSET + 603, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("The Spear of Adun", "The Spear of Adun: North Power Cell", SC2LOTV_LOC_ID_OFFSET + 604, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("The Spear of Adun", "The Spear of Adun: East Power Cell", SC2LOTV_LOC_ID_OFFSET + 605, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("The Spear of Adun", "The Spear of Adun: South Power Cell", SC2LOTV_LOC_ID_OFFSET + 606, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("The Spear of Adun", "The Spear of Adun: Southeast Power Cell", SC2LOTV_LOC_ID_OFFSET + 607, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("Sky Shield", "Sky Shield: Victory", SC2LOTV_LOC_ID_OFFSET + 700, LocationType.VICTORY,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("Sky Shield", "Sky Shield: Mid EMP Scrambler", SC2LOTV_LOC_ID_OFFSET + 701, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("Sky Shield", "Sky Shield: Southeast EMP Scrambler", SC2LOTV_LOC_ID_OFFSET + 702, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("Sky Shield", "Sky Shield: North EMP Scrambler", SC2LOTV_LOC_ID_OFFSET + 703, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("Sky Shield", "Sky Shield: Mid Stabilizer", SC2LOTV_LOC_ID_OFFSET + 704, LocationType.EXTRA),
+ LocationData("Sky Shield", "Sky Shield: Southwest Stabilizer", SC2LOTV_LOC_ID_OFFSET + 705, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("Sky Shield", "Sky Shield: Northwest Stabilizer", SC2LOTV_LOC_ID_OFFSET + 706, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("Sky Shield", "Sky Shield: Northeast Stabilizer", SC2LOTV_LOC_ID_OFFSET + 707, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("Sky Shield", "Sky Shield: Southeast Stabilizer", SC2LOTV_LOC_ID_OFFSET + 708, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("Sky Shield", "Sky Shield: West Raynor Base", SC2LOTV_LOC_ID_OFFSET + 709, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("Sky Shield", "Sky Shield: East Raynor Base", SC2LOTV_LOC_ID_OFFSET + 710, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_basic_anti_air(state)),
+ LocationData("Brothers in Arms", "Brothers in Arms: Victory", SC2LOTV_LOC_ID_OFFSET + 800, LocationType.VICTORY,
+ lambda state: logic.brothers_in_arms_requirement(state)),
+ LocationData("Brothers in Arms", "Brothers in Arms: Mid Science Facility", SC2LOTV_LOC_ID_OFFSET + 801, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state) or logic.take_over_ai_allies),
+ LocationData("Brothers in Arms", "Brothers in Arms: North Science Facility", SC2LOTV_LOC_ID_OFFSET + 802, LocationType.VANILLA,
+ lambda state: logic.brothers_in_arms_requirement(state)
+ or logic.take_over_ai_allies
+ and logic.advanced_tactics
+ and (
+ logic.terran_common_unit(state)
+ or logic.protoss_common_unit(state)
+ )
+ ),
+ LocationData("Brothers in Arms", "Brothers in Arms: South Science Facility", SC2LOTV_LOC_ID_OFFSET + 803, LocationType.VANILLA,
+ lambda state: logic.brothers_in_arms_requirement(state)),
+ LocationData("Amon's Reach", "Amon's Reach: Victory", SC2LOTV_LOC_ID_OFFSET + 900, LocationType.VICTORY,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("Amon's Reach", "Amon's Reach: Close Solarite Reserve", SC2LOTV_LOC_ID_OFFSET + 901, LocationType.VANILLA),
+ LocationData("Amon's Reach", "Amon's Reach: North Solarite Reserve", SC2LOTV_LOC_ID_OFFSET + 902, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("Amon's Reach", "Amon's Reach: East Solarite Reserve", SC2LOTV_LOC_ID_OFFSET + 903, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("Amon's Reach", "Amon's Reach: West Launch Bay", SC2LOTV_LOC_ID_OFFSET + 904, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("Amon's Reach", "Amon's Reach: South Launch Bay", SC2LOTV_LOC_ID_OFFSET + 905, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("Amon's Reach", "Amon's Reach: Northwest Launch Bay", SC2LOTV_LOC_ID_OFFSET + 906, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("Amon's Reach", "Amon's Reach: East Launch Bay", SC2LOTV_LOC_ID_OFFSET + 907, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("Last Stand", "Last Stand: Victory", SC2LOTV_LOC_ID_OFFSET + 1000, LocationType.VICTORY,
+ lambda state: logic.last_stand_requirement(state)),
+ LocationData("Last Stand", "Last Stand: West Zenith Stone", SC2LOTV_LOC_ID_OFFSET + 1001, LocationType.VANILLA,
+ lambda state: logic.last_stand_requirement(state)),
+ LocationData("Last Stand", "Last Stand: North Zenith Stone", SC2LOTV_LOC_ID_OFFSET + 1002, LocationType.VANILLA,
+ lambda state: logic.last_stand_requirement(state)),
+ LocationData("Last Stand", "Last Stand: East Zenith Stone", SC2LOTV_LOC_ID_OFFSET + 1003, LocationType.VANILLA,
+ lambda state: logic.last_stand_requirement(state)),
+ LocationData("Last Stand", "Last Stand: 1 Billion Zerg", SC2LOTV_LOC_ID_OFFSET + 1004, LocationType.EXTRA,
+ lambda state: logic.last_stand_requirement(state)),
+ LocationData("Last Stand", "Last Stand: 1.5 Billion Zerg", SC2LOTV_LOC_ID_OFFSET + 1005, LocationType.VANILLA,
+ lambda state: logic.last_stand_requirement(state) and (
+ state.has_all({ItemNames.KHAYDARIN_MONOLITH, ItemNames.PHOTON_CANNON, ItemNames.SHIELD_BATTERY}, player)
+ or state.has_any({ItemNames.SOA_SOLAR_LANCE, ItemNames.SOA_DEPLOY_FENIX}, player)
+ )),
+ LocationData("Forbidden Weapon", "Forbidden Weapon: Victory", SC2LOTV_LOC_ID_OFFSET + 1100, LocationType.VICTORY,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_armor_anti_air(state)),
+ LocationData("Forbidden Weapon", "Forbidden Weapon: South Solarite", SC2LOTV_LOC_ID_OFFSET + 1101, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_armor_anti_air(state)),
+ LocationData("Forbidden Weapon", "Forbidden Weapon: North Solarite", SC2LOTV_LOC_ID_OFFSET + 1102, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_armor_anti_air(state)),
+ LocationData("Forbidden Weapon", "Forbidden Weapon: Northwest Solarite", SC2LOTV_LOC_ID_OFFSET + 1103, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_armor_anti_air(state)),
+ LocationData("Temple of Unification", "Temple of Unification: Victory", SC2LOTV_LOC_ID_OFFSET + 1200, LocationType.VICTORY,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_armor_anti_air(state)),
+ LocationData("Temple of Unification", "Temple of Unification: Mid Celestial Lock", SC2LOTV_LOC_ID_OFFSET + 1201, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_armor_anti_air(state)),
+ LocationData("Temple of Unification", "Temple of Unification: West Celestial Lock", SC2LOTV_LOC_ID_OFFSET + 1202, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_armor_anti_air(state)),
+ LocationData("Temple of Unification", "Temple of Unification: South Celestial Lock", SC2LOTV_LOC_ID_OFFSET + 1203, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_armor_anti_air(state)),
+ LocationData("Temple of Unification", "Temple of Unification: East Celestial Lock", SC2LOTV_LOC_ID_OFFSET + 1204, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_armor_anti_air(state)),
+ LocationData("Temple of Unification", "Temple of Unification: North Celestial Lock", SC2LOTV_LOC_ID_OFFSET + 1205, LocationType.EXTRA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_armor_anti_air(state)),
+ LocationData("Temple of Unification", "Temple of Unification: Titanic Warp Prism", SC2LOTV_LOC_ID_OFFSET + 1206, LocationType.VANILLA,
+ lambda state: logic.protoss_common_unit(state)
+ and logic.protoss_anti_armor_anti_air(state)),
+ LocationData("The Infinite Cycle", "The Infinite Cycle: Victory", SC2LOTV_LOC_ID_OFFSET + 1300, LocationType.VICTORY,
+ lambda state: logic.the_infinite_cycle_requirement(state)),
+ LocationData("The Infinite Cycle", "The Infinite Cycle: First Hall of Revelation", SC2LOTV_LOC_ID_OFFSET + 1301, LocationType.EXTRA,
+ lambda state: logic.the_infinite_cycle_requirement(state)),
+ LocationData("The Infinite Cycle", "The Infinite Cycle: Second Hall of Revelation", SC2LOTV_LOC_ID_OFFSET + 1302, LocationType.EXTRA,
+ lambda state: logic.the_infinite_cycle_requirement(state)),
+ LocationData("The Infinite Cycle", "The Infinite Cycle: First Xel'Naga Device", SC2LOTV_LOC_ID_OFFSET + 1303, LocationType.VANILLA,
+ lambda state: logic.the_infinite_cycle_requirement(state)),
+ LocationData("The Infinite Cycle", "The Infinite Cycle: Second Xel'Naga Device", SC2LOTV_LOC_ID_OFFSET + 1304, LocationType.VANILLA,
+ lambda state: logic.the_infinite_cycle_requirement(state)),
+ LocationData("The Infinite Cycle", "The Infinite Cycle: Third Xel'Naga Device", SC2LOTV_LOC_ID_OFFSET + 1305, LocationType.VANILLA,
+ lambda state: logic.the_infinite_cycle_requirement(state)),
+ LocationData("Harbinger of Oblivion", "Harbinger of Oblivion: Victory", SC2LOTV_LOC_ID_OFFSET + 1400, LocationType.VICTORY,
+ lambda state: logic.harbinger_of_oblivion_requirement(state)),
+ LocationData("Harbinger of Oblivion", "Harbinger of Oblivion: Artanis", SC2LOTV_LOC_ID_OFFSET + 1401, LocationType.EXTRA),
+ LocationData("Harbinger of Oblivion", "Harbinger of Oblivion: Northwest Void Crystal", SC2LOTV_LOC_ID_OFFSET + 1402, LocationType.EXTRA,
+ lambda state: logic.harbinger_of_oblivion_requirement(state)),
+ LocationData("Harbinger of Oblivion", "Harbinger of Oblivion: Northeast Void Crystal", SC2LOTV_LOC_ID_OFFSET + 1403, LocationType.EXTRA,
+ lambda state: logic.harbinger_of_oblivion_requirement(state)),
+ LocationData("Harbinger of Oblivion", "Harbinger of Oblivion: Southwest Void Crystal", SC2LOTV_LOC_ID_OFFSET + 1404, LocationType.EXTRA,
+ lambda state: logic.harbinger_of_oblivion_requirement(state)),
+ LocationData("Harbinger of Oblivion", "Harbinger of Oblivion: Southeast Void Crystal", SC2LOTV_LOC_ID_OFFSET + 1405, LocationType.EXTRA,
+ lambda state: logic.harbinger_of_oblivion_requirement(state)),
+ LocationData("Harbinger of Oblivion", "Harbinger of Oblivion: South Xel'Naga Vessel", SC2LOTV_LOC_ID_OFFSET + 1406, LocationType.VANILLA),
+ LocationData("Harbinger of Oblivion", "Harbinger of Oblivion: Mid Xel'Naga Vessel", SC2LOTV_LOC_ID_OFFSET + 1407, LocationType.VANILLA,
+ lambda state: logic.harbinger_of_oblivion_requirement(state)),
+ LocationData("Harbinger of Oblivion", "Harbinger of Oblivion: North Xel'Naga Vessel", SC2LOTV_LOC_ID_OFFSET + 1408, LocationType.VANILLA,
+ lambda state: logic.harbinger_of_oblivion_requirement(state)),
+ LocationData("Unsealing the Past", "Unsealing the Past: Victory", SC2LOTV_LOC_ID_OFFSET + 1500, LocationType.VICTORY,
+ lambda state: logic.protoss_basic_splash(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("Unsealing the Past", "Unsealing the Past: Zerg Cleared", SC2LOTV_LOC_ID_OFFSET + 1501, LocationType.EXTRA),
+ LocationData("Unsealing the Past", "Unsealing the Past: First Stasis Lock", SC2LOTV_LOC_ID_OFFSET + 1502, LocationType.EXTRA,
+ lambda state: logic.advanced_tactics \
+ or logic.protoss_basic_splash(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("Unsealing the Past", "Unsealing the Past: Second Stasis Lock", SC2LOTV_LOC_ID_OFFSET + 1503, LocationType.EXTRA,
+ lambda state: logic.protoss_basic_splash(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("Unsealing the Past", "Unsealing the Past: Third Stasis Lock", SC2LOTV_LOC_ID_OFFSET + 1504, LocationType.EXTRA,
+ lambda state: logic.protoss_basic_splash(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("Unsealing the Past", "Unsealing the Past: Fourth Stasis Lock", SC2LOTV_LOC_ID_OFFSET + 1505, LocationType.EXTRA,
+ lambda state: logic.protoss_basic_splash(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("Unsealing the Past", "Unsealing the Past: South Power Core", SC2LOTV_LOC_ID_OFFSET + 1506, LocationType.VANILLA,
+ lambda state: logic.protoss_basic_splash(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("Unsealing the Past", "Unsealing the Past: East Power Core", SC2LOTV_LOC_ID_OFFSET + 1507, LocationType.VANILLA,
+ lambda state: logic.protoss_basic_splash(state)
+ and logic.protoss_anti_light_anti_air(state)),
+ LocationData("Purification", "Purification: Victory", SC2LOTV_LOC_ID_OFFSET + 1600, LocationType.VICTORY,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Purification", "Purification: North Sector: West Null Circuit", SC2LOTV_LOC_ID_OFFSET + 1601, LocationType.VANILLA,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Purification", "Purification: North Sector: Northeast Null Circuit", SC2LOTV_LOC_ID_OFFSET + 1602, LocationType.EXTRA,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Purification", "Purification: North Sector: Southeast Null Circuit", SC2LOTV_LOC_ID_OFFSET + 1603, LocationType.EXTRA,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Purification", "Purification: South Sector: West Null Circuit", SC2LOTV_LOC_ID_OFFSET + 1604, LocationType.VANILLA,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Purification", "Purification: South Sector: North Null Circuit", SC2LOTV_LOC_ID_OFFSET + 1605, LocationType.EXTRA,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Purification", "Purification: South Sector: East Null Circuit", SC2LOTV_LOC_ID_OFFSET + 1606, LocationType.EXTRA,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Purification", "Purification: West Sector: West Null Circuit", SC2LOTV_LOC_ID_OFFSET + 1607, LocationType.VANILLA,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Purification", "Purification: West Sector: Mid Null Circuit", SC2LOTV_LOC_ID_OFFSET + 1608, LocationType.EXTRA,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Purification", "Purification: West Sector: East Null Circuit", SC2LOTV_LOC_ID_OFFSET + 1609, LocationType.EXTRA,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Purification", "Purification: East Sector: North Null Circuit", SC2LOTV_LOC_ID_OFFSET + 1610, LocationType.VANILLA,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Purification", "Purification: East Sector: West Null Circuit", SC2LOTV_LOC_ID_OFFSET + 1611, LocationType.EXTRA,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Purification", "Purification: East Sector: South Null Circuit", SC2LOTV_LOC_ID_OFFSET + 1612, LocationType.EXTRA,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Purification", "Purification: Purifier Warden", SC2LOTV_LOC_ID_OFFSET + 1613, LocationType.VANILLA,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Steps of the Rite", "Steps of the Rite: Victory", SC2LOTV_LOC_ID_OFFSET + 1700, LocationType.VICTORY,
+ lambda state: logic.steps_of_the_rite_requirement(state)),
+ LocationData("Steps of the Rite", "Steps of the Rite: First Terrazine Fog", SC2LOTV_LOC_ID_OFFSET + 1701, LocationType.EXTRA,
+ lambda state: logic.steps_of_the_rite_requirement(state)),
+ LocationData("Steps of the Rite", "Steps of the Rite: Southwest Guardian", SC2LOTV_LOC_ID_OFFSET + 1702, LocationType.EXTRA,
+ lambda state: logic.steps_of_the_rite_requirement(state)),
+ LocationData("Steps of the Rite", "Steps of the Rite: West Guardian", SC2LOTV_LOC_ID_OFFSET + 1703, LocationType.EXTRA,
+ lambda state: logic.steps_of_the_rite_requirement(state)),
+ LocationData("Steps of the Rite", "Steps of the Rite: Northwest Guardian", SC2LOTV_LOC_ID_OFFSET + 1704, LocationType.EXTRA,
+ lambda state: logic.steps_of_the_rite_requirement(state)),
+ LocationData("Steps of the Rite", "Steps of the Rite: Northeast Guardian", SC2LOTV_LOC_ID_OFFSET + 1705, LocationType.EXTRA,
+ lambda state: logic.steps_of_the_rite_requirement(state)),
+ LocationData("Steps of the Rite", "Steps of the Rite: North Mothership", SC2LOTV_LOC_ID_OFFSET + 1706, LocationType.VANILLA,
+ lambda state: logic.steps_of_the_rite_requirement(state)),
+ LocationData("Steps of the Rite", "Steps of the Rite: South Mothership", SC2LOTV_LOC_ID_OFFSET + 1707, LocationType.VANILLA,
+ lambda state: logic.steps_of_the_rite_requirement(state)),
+ LocationData("Rak'Shir", "Rak'Shir: Victory", SC2LOTV_LOC_ID_OFFSET + 1800, LocationType.VICTORY,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Rak'Shir", "Rak'Shir: North Slayn Elemental", SC2LOTV_LOC_ID_OFFSET + 1801, LocationType.VANILLA,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Rak'Shir", "Rak'Shir: Southwest Slayn Elemental", SC2LOTV_LOC_ID_OFFSET + 1802, LocationType.VANILLA,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Rak'Shir", "Rak'Shir: East Slayn Elemental", SC2LOTV_LOC_ID_OFFSET + 1803, LocationType.VANILLA,
+ lambda state: logic.protoss_competent_comp(state)),
+ LocationData("Templar's Charge", "Templar's Charge: Victory", SC2LOTV_LOC_ID_OFFSET + 1900, LocationType.VICTORY,
+ lambda state: logic.templars_charge_requirement(state)),
+ LocationData("Templar's Charge", "Templar's Charge: Northwest Power Core", SC2LOTV_LOC_ID_OFFSET + 1901, LocationType.EXTRA,
+ lambda state: logic.templars_charge_requirement(state)),
+ LocationData("Templar's Charge", "Templar's Charge: Northeast Power Core", SC2LOTV_LOC_ID_OFFSET + 1902, LocationType.EXTRA,
+ lambda state: logic.templars_charge_requirement(state)),
+ LocationData("Templar's Charge", "Templar's Charge: Southeast Power Core", SC2LOTV_LOC_ID_OFFSET + 1903, LocationType.EXTRA,
+ lambda state: logic.templars_charge_requirement(state)),
+ LocationData("Templar's Charge", "Templar's Charge: West Hybrid Statis Chamber", SC2LOTV_LOC_ID_OFFSET + 1904, LocationType.VANILLA,
+ lambda state: logic.templars_charge_requirement(state)),
+ LocationData("Templar's Charge", "Templar's Charge: Southeast Hybrid Statis Chamber", SC2LOTV_LOC_ID_OFFSET + 1905, LocationType.VANILLA,
+ lambda state: logic.protoss_fleet(state)),
+ LocationData("Templar's Return", "Templar's Return: Victory", SC2LOTV_LOC_ID_OFFSET + 2000, LocationType.VICTORY,
+ lambda state: logic.templars_return_requirement(state)),
+ LocationData("Templar's Return", "Templar's Return: Citadel: First Gate", SC2LOTV_LOC_ID_OFFSET + 2001, LocationType.EXTRA),
+ LocationData("Templar's Return", "Templar's Return: Citadel: Second Gate", SC2LOTV_LOC_ID_OFFSET + 2002, LocationType.EXTRA),
+ LocationData("Templar's Return", "Templar's Return: Citadel: Power Structure", SC2LOTV_LOC_ID_OFFSET + 2003, LocationType.VANILLA),
+ LocationData("Templar's Return", "Templar's Return: Temple Grounds: Gather Army", SC2LOTV_LOC_ID_OFFSET + 2004, LocationType.VANILLA,
+ lambda state: logic.templars_return_requirement(state)),
+ LocationData("Templar's Return", "Templar's Return: Temple Grounds: Power Structure", SC2LOTV_LOC_ID_OFFSET + 2005, LocationType.VANILLA,
+ lambda state: logic.templars_return_requirement(state)),
+ LocationData("Templar's Return", "Templar's Return: Caverns: Purifier", SC2LOTV_LOC_ID_OFFSET + 2006, LocationType.EXTRA,
+ lambda state: logic.templars_return_requirement(state)),
+ LocationData("Templar's Return", "Templar's Return: Caverns: Dark Templar", SC2LOTV_LOC_ID_OFFSET + 2007, LocationType.EXTRA,
+ lambda state: logic.templars_return_requirement(state)),
+ LocationData("The Host", "The Host: Victory", SC2LOTV_LOC_ID_OFFSET + 2100, LocationType.VICTORY,
+ lambda state: logic.the_host_requirement(state)),
+ LocationData("The Host", "The Host: Southeast Void Shard", SC2LOTV_LOC_ID_OFFSET + 2101, LocationType.VICTORY,
+ lambda state: logic.the_host_requirement(state)),
+ LocationData("The Host", "The Host: South Void Shard", SC2LOTV_LOC_ID_OFFSET + 2102, LocationType.EXTRA,
+ lambda state: logic.the_host_requirement(state)),
+ LocationData("The Host", "The Host: Southwest Void Shard", SC2LOTV_LOC_ID_OFFSET + 2103, LocationType.EXTRA,
+ lambda state: logic.the_host_requirement(state)),
+ LocationData("The Host", "The Host: North Void Shard", SC2LOTV_LOC_ID_OFFSET + 2104, LocationType.EXTRA,
+ lambda state: logic.the_host_requirement(state)),
+ LocationData("The Host", "The Host: Northwest Void Shard", SC2LOTV_LOC_ID_OFFSET + 2105, LocationType.EXTRA,
+ lambda state: logic.the_host_requirement(state)),
+ LocationData("The Host", "The Host: Nerazim Warp in Zone", SC2LOTV_LOC_ID_OFFSET + 2106, LocationType.VANILLA,
+ lambda state: logic.the_host_requirement(state)),
+ LocationData("The Host", "The Host: Tal'darim Warp in Zone", SC2LOTV_LOC_ID_OFFSET + 2107, LocationType.VANILLA,
+ lambda state: logic.the_host_requirement(state)),
+ LocationData("The Host", "The Host: Purifier Warp in Zone", SC2LOTV_LOC_ID_OFFSET + 2108, LocationType.VANILLA,
+ lambda state: logic.the_host_requirement(state)),
+ LocationData("Salvation", "Salvation: Victory", SC2LOTV_LOC_ID_OFFSET + 2200, LocationType.VICTORY,
+ lambda state: logic.salvation_requirement(state)),
+ LocationData("Salvation", "Salvation: Fabrication Matrix", SC2LOTV_LOC_ID_OFFSET + 2201, LocationType.EXTRA,
+ lambda state: logic.salvation_requirement(state)),
+ LocationData("Salvation", "Salvation: Assault Cluster", SC2LOTV_LOC_ID_OFFSET + 2202, LocationType.EXTRA,
+ lambda state: logic.salvation_requirement(state)),
+ LocationData("Salvation", "Salvation: Hull Breach", SC2LOTV_LOC_ID_OFFSET + 2203, LocationType.EXTRA,
+ lambda state: logic.salvation_requirement(state)),
+ LocationData("Salvation", "Salvation: Core Critical", SC2LOTV_LOC_ID_OFFSET + 2204, LocationType.EXTRA,
+ lambda state: logic.salvation_requirement(state)),
+
+ # Epilogue
+ LocationData("Into the Void", "Into the Void: Victory", SC2LOTV_LOC_ID_OFFSET + 2300, LocationType.VICTORY,
+ lambda state: logic.into_the_void_requirement(state)),
+ LocationData("Into the Void", "Into the Void: Corruption Source", SC2LOTV_LOC_ID_OFFSET + 2301, LocationType.EXTRA),
+ LocationData("Into the Void", "Into the Void: Southwest Forward Position", SC2LOTV_LOC_ID_OFFSET + 2302, LocationType.VANILLA,
+ lambda state: logic.into_the_void_requirement(state)),
+ LocationData("Into the Void", "Into the Void: Northwest Forward Position", SC2LOTV_LOC_ID_OFFSET + 2303, LocationType.VANILLA,
+ lambda state: logic.into_the_void_requirement(state)),
+ LocationData("Into the Void", "Into the Void: Southeast Forward Position", SC2LOTV_LOC_ID_OFFSET + 2304, LocationType.VANILLA,
+ lambda state: logic.into_the_void_requirement(state)),
+ LocationData("Into the Void", "Into the Void: Northeast Forward Position", SC2LOTV_LOC_ID_OFFSET + 2305, LocationType.VANILLA),
+ LocationData("The Essence of Eternity", "The Essence of Eternity: Victory", SC2LOTV_LOC_ID_OFFSET + 2400, LocationType.VICTORY,
+ lambda state: logic.essence_of_eternity_requirement(state)),
+ LocationData("The Essence of Eternity", "The Essence of Eternity: Void Trashers", SC2LOTV_LOC_ID_OFFSET + 2401, LocationType.EXTRA),
+ LocationData("Amon's Fall", "Amon's Fall: Victory", SC2LOTV_LOC_ID_OFFSET + 2500, LocationType.VICTORY,
+ lambda state: logic.amons_fall_requirement(state)),
+
+ # Nova Covert Ops
+ LocationData("The Escape", "The Escape: Victory", SC2NCO_LOC_ID_OFFSET + 100, LocationType.VICTORY,
+ lambda state: logic.the_escape_requirement(state)),
+ LocationData("The Escape", "The Escape: Rifle", SC2NCO_LOC_ID_OFFSET + 101, LocationType.VANILLA,
+ lambda state: logic.the_escape_first_stage_requirement(state)),
+ LocationData("The Escape", "The Escape: Grenades", SC2NCO_LOC_ID_OFFSET + 102, LocationType.VANILLA,
+ lambda state: logic.the_escape_first_stage_requirement(state)),
+ LocationData("The Escape", "The Escape: Agent Delta", SC2NCO_LOC_ID_OFFSET + 103, LocationType.VANILLA,
+ lambda state: logic.the_escape_requirement(state)),
+ LocationData("The Escape", "The Escape: Agent Pierce", SC2NCO_LOC_ID_OFFSET + 104, LocationType.VANILLA,
+ lambda state: logic.the_escape_requirement(state)),
+ LocationData("The Escape", "The Escape: Agent Stone", SC2NCO_LOC_ID_OFFSET + 105, LocationType.VANILLA,
+ lambda state: logic.the_escape_requirement(state)),
+ LocationData("Sudden Strike", "Sudden Strike: Victory", SC2NCO_LOC_ID_OFFSET + 200, LocationType.VICTORY,
+ lambda state: logic.sudden_strike_can_reach_objectives(state)),
+ LocationData("Sudden Strike", "Sudden Strike: Research Center", SC2NCO_LOC_ID_OFFSET + 201, LocationType.VANILLA,
+ lambda state: logic.sudden_strike_can_reach_objectives(state)),
+ LocationData("Sudden Strike", "Sudden Strike: Weaponry Labs", SC2NCO_LOC_ID_OFFSET + 202, LocationType.VANILLA,
+ lambda state: logic.sudden_strike_requirement(state)),
+ LocationData("Sudden Strike", "Sudden Strike: Brutalisk", SC2NCO_LOC_ID_OFFSET + 203, LocationType.EXTRA,
+ lambda state: logic.sudden_strike_requirement(state)),
+ LocationData("Enemy Intelligence", "Enemy Intelligence: Victory", SC2NCO_LOC_ID_OFFSET + 300, LocationType.VICTORY,
+ lambda state: logic.enemy_intelligence_third_stage_requirement(state)),
+ LocationData("Enemy Intelligence", "Enemy Intelligence: West Garrison", SC2NCO_LOC_ID_OFFSET + 301, LocationType.EXTRA,
+ lambda state: logic.enemy_intelligence_first_stage_requirement(state)),
+ LocationData("Enemy Intelligence", "Enemy Intelligence: Close Garrison", SC2NCO_LOC_ID_OFFSET + 302, LocationType.EXTRA,
+ lambda state: logic.enemy_intelligence_first_stage_requirement(state)),
+ LocationData("Enemy Intelligence", "Enemy Intelligence: Northeast Garrison", SC2NCO_LOC_ID_OFFSET + 303, LocationType.EXTRA,
+ lambda state: logic.enemy_intelligence_first_stage_requirement(state)),
+ LocationData("Enemy Intelligence", "Enemy Intelligence: Southeast Garrison", SC2NCO_LOC_ID_OFFSET + 304, LocationType.EXTRA,
+ lambda state: logic.enemy_intelligence_first_stage_requirement(state)
+ and logic.enemy_intelligence_cliff_garrison(state)),
+ LocationData("Enemy Intelligence", "Enemy Intelligence: South Garrison", SC2NCO_LOC_ID_OFFSET + 305, LocationType.EXTRA,
+ lambda state: logic.enemy_intelligence_first_stage_requirement(state)),
+ LocationData("Enemy Intelligence", "Enemy Intelligence: All Garrisons", SC2NCO_LOC_ID_OFFSET + 306, LocationType.VANILLA,
+ lambda state: logic.enemy_intelligence_first_stage_requirement(state)
+ and logic.enemy_intelligence_cliff_garrison(state)),
+ LocationData("Enemy Intelligence", "Enemy Intelligence: Forces Rescued", SC2NCO_LOC_ID_OFFSET + 307, LocationType.VANILLA,
+ lambda state: logic.enemy_intelligence_first_stage_requirement(state)),
+ LocationData("Enemy Intelligence", "Enemy Intelligence: Communications Hub", SC2NCO_LOC_ID_OFFSET + 308, LocationType.VANILLA,
+ lambda state: logic.enemy_intelligence_second_stage_requirement(state)),
+ LocationData("Trouble In Paradise", "Trouble In Paradise: Victory", SC2NCO_LOC_ID_OFFSET + 400, LocationType.VICTORY,
+ lambda state: logic.trouble_in_paradise_requirement(state)),
+ LocationData("Trouble In Paradise", "Trouble In Paradise: North Base: West Hatchery", SC2NCO_LOC_ID_OFFSET + 401, LocationType.VANILLA,
+ lambda state: logic.trouble_in_paradise_requirement(state)),
+ LocationData("Trouble In Paradise", "Trouble In Paradise: North Base: North Hatchery", SC2NCO_LOC_ID_OFFSET + 402, LocationType.VANILLA,
+ lambda state: logic.trouble_in_paradise_requirement(state)),
+ LocationData("Trouble In Paradise", "Trouble In Paradise: North Base: East Hatchery", SC2NCO_LOC_ID_OFFSET + 403, LocationType.VANILLA),
+ LocationData("Trouble In Paradise", "Trouble In Paradise: South Base: Northwest Hatchery", SC2NCO_LOC_ID_OFFSET + 404, LocationType.VANILLA,
+ lambda state: logic.trouble_in_paradise_requirement(state)),
+ LocationData("Trouble In Paradise", "Trouble In Paradise: South Base: Southwest Hatchery", SC2NCO_LOC_ID_OFFSET + 405, LocationType.VANILLA,
+ lambda state: logic.trouble_in_paradise_requirement(state)),
+ LocationData("Trouble In Paradise", "Trouble In Paradise: South Base: East Hatchery", SC2NCO_LOC_ID_OFFSET + 406, LocationType.VANILLA),
+ LocationData("Trouble In Paradise", "Trouble In Paradise: North Shield Projector", SC2NCO_LOC_ID_OFFSET + 407, LocationType.EXTRA,
+ lambda state: logic.trouble_in_paradise_requirement(state)),
+ LocationData("Trouble In Paradise", "Trouble In Paradise: East Shield Projector", SC2NCO_LOC_ID_OFFSET + 408, LocationType.EXTRA,
+ lambda state: logic.trouble_in_paradise_requirement(state)),
+ LocationData("Trouble In Paradise", "Trouble In Paradise: South Shield Projector", SC2NCO_LOC_ID_OFFSET + 409, LocationType.EXTRA,
+ lambda state: logic.trouble_in_paradise_requirement(state)),
+ LocationData("Trouble In Paradise", "Trouble In Paradise: West Shield Projector", SC2NCO_LOC_ID_OFFSET + 410, LocationType.EXTRA,
+ lambda state: logic.trouble_in_paradise_requirement(state)),
+ LocationData("Trouble In Paradise", "Trouble In Paradise: Fleet Beacon", SC2NCO_LOC_ID_OFFSET + 411, LocationType.VANILLA,
+ lambda state: logic.trouble_in_paradise_requirement(state)),
+ LocationData("Night Terrors", "Night Terrors: Victory", SC2NCO_LOC_ID_OFFSET + 500, LocationType.VICTORY,
+ lambda state: logic.night_terrors_requirement(state)),
+ LocationData("Night Terrors", "Night Terrors: 1 Terrazine Node Collected", SC2NCO_LOC_ID_OFFSET + 501, LocationType.EXTRA,
+ lambda state: logic.night_terrors_requirement(state)),
+ LocationData("Night Terrors", "Night Terrors: 2 Terrazine Nodes Collected", SC2NCO_LOC_ID_OFFSET + 502, LocationType.EXTRA,
+ lambda state: logic.night_terrors_requirement(state)),
+ LocationData("Night Terrors", "Night Terrors: 3 Terrazine Nodes Collected", SC2NCO_LOC_ID_OFFSET + 503, LocationType.EXTRA,
+ lambda state: logic.night_terrors_requirement(state)),
+ LocationData("Night Terrors", "Night Terrors: 4 Terrazine Nodes Collected", SC2NCO_LOC_ID_OFFSET + 504, LocationType.EXTRA,
+ lambda state: logic.night_terrors_requirement(state)),
+ LocationData("Night Terrors", "Night Terrors: 5 Terrazine Nodes Collected", SC2NCO_LOC_ID_OFFSET + 505, LocationType.EXTRA,
+ lambda state: logic.night_terrors_requirement(state)),
+ LocationData("Night Terrors", "Night Terrors: HERC Outpost", SC2NCO_LOC_ID_OFFSET + 506, LocationType.VANILLA,
+ lambda state: logic.night_terrors_requirement(state)),
+ LocationData("Night Terrors", "Night Terrors: Umojan Mine", SC2NCO_LOC_ID_OFFSET + 507, LocationType.EXTRA,
+ lambda state: logic.night_terrors_requirement(state)),
+ LocationData("Night Terrors", "Night Terrors: Blightbringer", SC2NCO_LOC_ID_OFFSET + 508, LocationType.VANILLA,
+ lambda state: logic.night_terrors_requirement(state)
+ and logic.nova_ranged_weapon(state)
+ and state.has_any(
+ {ItemNames.NOVA_HELLFIRE_SHOTGUN, ItemNames.NOVA_PULSE_GRENADES, ItemNames.NOVA_STIM_INFUSION,
+ ItemNames.NOVA_HOLO_DECOY}, player)),
+ LocationData("Night Terrors", "Night Terrors: Science Facility", SC2NCO_LOC_ID_OFFSET + 509, LocationType.EXTRA,
+ lambda state: logic.night_terrors_requirement(state)),
+ LocationData("Night Terrors", "Night Terrors: Eradicators", SC2NCO_LOC_ID_OFFSET + 510, LocationType.VANILLA,
+ lambda state: logic.night_terrors_requirement(state)
+ and logic.nova_any_weapon(state)),
+ LocationData("Flashpoint", "Flashpoint: Victory", SC2NCO_LOC_ID_OFFSET + 600, LocationType.VICTORY,
+ lambda state: logic.flashpoint_far_requirement(state)),
+ LocationData("Flashpoint", "Flashpoint: Close North Evidence Coordinates", SC2NCO_LOC_ID_OFFSET + 601, LocationType.EXTRA,
+ lambda state: state.has_any(
+ {ItemNames.LIBERATOR_RAID_ARTILLERY, ItemNames.RAVEN_HUNTER_SEEKER_WEAPON}, player)
+ or logic.terran_common_unit(state)),
+ LocationData("Flashpoint", "Flashpoint: Close East Evidence Coordinates", SC2NCO_LOC_ID_OFFSET + 602, LocationType.EXTRA,
+ lambda state: state.has_any(
+ {ItemNames.LIBERATOR_RAID_ARTILLERY, ItemNames.RAVEN_HUNTER_SEEKER_WEAPON}, player)
+ or logic.terran_common_unit(state)),
+ LocationData("Flashpoint", "Flashpoint: Far North Evidence Coordinates", SC2NCO_LOC_ID_OFFSET + 603, LocationType.EXTRA,
+ lambda state: logic.flashpoint_far_requirement(state)),
+ LocationData("Flashpoint", "Flashpoint: Far East Evidence Coordinates", SC2NCO_LOC_ID_OFFSET + 604, LocationType.EXTRA,
+ lambda state: logic.flashpoint_far_requirement(state)),
+ LocationData("Flashpoint", "Flashpoint: Experimental Weapon", SC2NCO_LOC_ID_OFFSET + 605, LocationType.VANILLA,
+ lambda state: logic.flashpoint_far_requirement(state)),
+ LocationData("Flashpoint", "Flashpoint: Northwest Subway Entrance", SC2NCO_LOC_ID_OFFSET + 606, LocationType.VANILLA,
+ lambda state: state.has_any(
+ {ItemNames.LIBERATOR_RAID_ARTILLERY, ItemNames.RAVEN_HUNTER_SEEKER_WEAPON}, player)
+ and logic.terran_common_unit(state)
+ or logic.flashpoint_far_requirement(state)),
+ LocationData("Flashpoint", "Flashpoint: Southeast Subway Entrance", SC2NCO_LOC_ID_OFFSET + 607, LocationType.VANILLA,
+ lambda state: state.has_any(
+ {ItemNames.LIBERATOR_RAID_ARTILLERY, ItemNames.RAVEN_HUNTER_SEEKER_WEAPON}, player)
+ and logic.terran_common_unit(state)
+ or logic.flashpoint_far_requirement(state)),
+ LocationData("Flashpoint", "Flashpoint: Northeast Subway Entrance", SC2NCO_LOC_ID_OFFSET + 608, LocationType.VANILLA,
+ lambda state: logic.flashpoint_far_requirement(state)),
+ LocationData("Flashpoint", "Flashpoint: Expansion Hatchery", SC2NCO_LOC_ID_OFFSET + 609, LocationType.EXTRA,
+ lambda state: state.has(ItemNames.LIBERATOR_RAID_ARTILLERY, player) and logic.terran_common_unit(state)
+ or logic.flashpoint_far_requirement(state)),
+ LocationData("Flashpoint", "Flashpoint: Baneling Spawns", SC2NCO_LOC_ID_OFFSET + 610, LocationType.EXTRA,
+ lambda state: logic.flashpoint_far_requirement(state)),
+ LocationData("Flashpoint", "Flashpoint: Mutalisk Spawns", SC2NCO_LOC_ID_OFFSET + 611, LocationType.EXTRA,
+ lambda state: logic.flashpoint_far_requirement(state)),
+ LocationData("Flashpoint", "Flashpoint: Nydus Worm Spawns", SC2NCO_LOC_ID_OFFSET + 612, LocationType.EXTRA,
+ lambda state: logic.flashpoint_far_requirement(state)),
+ LocationData("Flashpoint", "Flashpoint: Lurker Spawns", SC2NCO_LOC_ID_OFFSET + 613, LocationType.EXTRA,
+ lambda state: logic.flashpoint_far_requirement(state)),
+ LocationData("Flashpoint", "Flashpoint: Brood Lord Spawns", SC2NCO_LOC_ID_OFFSET + 614, LocationType.EXTRA,
+ lambda state: logic.flashpoint_far_requirement(state)),
+ LocationData("Flashpoint", "Flashpoint: Ultralisk Spawns", SC2NCO_LOC_ID_OFFSET + 615, LocationType.EXTRA,
+ lambda state: logic.flashpoint_far_requirement(state)),
+ LocationData("In the Enemy's Shadow", "In the Enemy's Shadow: Victory", SC2NCO_LOC_ID_OFFSET + 700, LocationType.VICTORY,
+ lambda state: logic.enemy_shadow_victory(state)),
+ LocationData("In the Enemy's Shadow", "In the Enemy's Shadow: Sewers: Domination Visor", SC2NCO_LOC_ID_OFFSET + 701, LocationType.VANILLA,
+ lambda state: logic.enemy_shadow_domination(state)),
+ LocationData("In the Enemy's Shadow", "In the Enemy's Shadow: Sewers: Resupply Crate", SC2NCO_LOC_ID_OFFSET + 702, LocationType.EXTRA,
+ lambda state: logic.enemy_shadow_first_stage(state)),
+ LocationData("In the Enemy's Shadow", "In the Enemy's Shadow: Sewers: Facility Access", SC2NCO_LOC_ID_OFFSET + 703, LocationType.VANILLA,
+ lambda state: logic.enemy_shadow_first_stage(state)),
+ LocationData("In the Enemy's Shadow", "In the Enemy's Shadow: Facility: Northwest Door Lock", SC2NCO_LOC_ID_OFFSET + 704, LocationType.VANILLA,
+ lambda state: logic.enemy_shadow_door_controls(state)),
+ LocationData("In the Enemy's Shadow", "In the Enemy's Shadow: Facility: Southeast Door Lock", SC2NCO_LOC_ID_OFFSET + 705, LocationType.VANILLA,
+ lambda state: logic.enemy_shadow_door_controls(state)),
+ LocationData("In the Enemy's Shadow", "In the Enemy's Shadow: Facility: Blazefire Gunblade", SC2NCO_LOC_ID_OFFSET + 706, LocationType.VANILLA,
+ lambda state: logic.enemy_shadow_second_stage(state)
+ and (story_tech_granted
+ or state.has(ItemNames.NOVA_BLINK, player)
+ or (adv_tactics and state.has_all({ItemNames.NOVA_DOMINATION, ItemNames.NOVA_HOLO_DECOY, ItemNames.NOVA_JUMP_SUIT_MODULE}, player))
+ )
+ ),
+ LocationData("In the Enemy's Shadow", "In the Enemy's Shadow: Facility: Blink Suit", SC2NCO_LOC_ID_OFFSET + 707, LocationType.VANILLA,
+ lambda state: logic.enemy_shadow_second_stage(state)),
+ LocationData("In the Enemy's Shadow", "In the Enemy's Shadow: Facility: Advanced Weaponry", SC2NCO_LOC_ID_OFFSET + 708, LocationType.VANILLA,
+ lambda state: logic.enemy_shadow_second_stage(state)),
+ LocationData("In the Enemy's Shadow", "In the Enemy's Shadow: Facility: Entrance Resupply Crate", SC2NCO_LOC_ID_OFFSET + 709, LocationType.EXTRA,
+ lambda state: logic.enemy_shadow_first_stage(state)),
+ LocationData("In the Enemy's Shadow", "In the Enemy's Shadow: Facility: West Resupply Crate", SC2NCO_LOC_ID_OFFSET + 710, LocationType.EXTRA,
+ lambda state: logic.enemy_shadow_second_stage(state)),
+ LocationData("In the Enemy's Shadow", "In the Enemy's Shadow: Facility: North Resupply Crate", SC2NCO_LOC_ID_OFFSET + 711, LocationType.EXTRA,
+ lambda state: logic.enemy_shadow_second_stage(state)),
+ LocationData("In the Enemy's Shadow", "In the Enemy's Shadow: Facility: East Resupply Crate", SC2NCO_LOC_ID_OFFSET + 712, LocationType.EXTRA,
+ lambda state: logic.enemy_shadow_second_stage(state)),
+ LocationData("In the Enemy's Shadow", "In the Enemy's Shadow: Facility: South Resupply Crate", SC2NCO_LOC_ID_OFFSET + 713, LocationType.EXTRA,
+ lambda state: logic.enemy_shadow_second_stage(state)),
+ LocationData("Dark Skies", "Dark Skies: Victory", SC2NCO_LOC_ID_OFFSET + 800, LocationType.VICTORY,
+ lambda state: logic.dark_skies_requirement(state)),
+ LocationData("Dark Skies", "Dark Skies: First Squadron of Dominion Fleet", SC2NCO_LOC_ID_OFFSET + 801, LocationType.EXTRA,
+ lambda state: logic.dark_skies_requirement(state)),
+ LocationData("Dark Skies", "Dark Skies: Remainder of Dominion Fleet", SC2NCO_LOC_ID_OFFSET + 802, LocationType.EXTRA,
+ lambda state: logic.dark_skies_requirement(state)),
+ LocationData("Dark Skies", "Dark Skies: Ji'nara", SC2NCO_LOC_ID_OFFSET + 803, LocationType.EXTRA,
+ lambda state: logic.dark_skies_requirement(state)),
+ LocationData("Dark Skies", "Dark Skies: Science Facility", SC2NCO_LOC_ID_OFFSET + 804, LocationType.VANILLA,
+ lambda state: logic.dark_skies_requirement(state)),
+ LocationData("End Game", "End Game: Victory", SC2NCO_LOC_ID_OFFSET + 900, LocationType.VICTORY,
+ lambda state: logic.end_game_requirement(state) and logic.nova_any_weapon(state)),
+ LocationData("End Game", "End Game: Xanthos", SC2NCO_LOC_ID_OFFSET + 901, LocationType.VANILLA,
+ lambda state: logic.end_game_requirement(state)),
+ ]
+
+ beat_events = []
+ # Filtering out excluded locations
+ if world is not None:
+ excluded_location_types = get_location_types(world, LocationInclusion.option_disabled)
+ plando_locations = get_plando_locations(world)
+ exclude_locations = get_option_value(world, "exclude_locations")
+ location_table = [location for location in location_table
+ if (location.type is LocationType.VICTORY or location.name not in exclude_locations)
+ and location.type not in excluded_location_types
+ or location.name in plando_locations]
+ for i, location_data in enumerate(location_table):
+ # Removing all item-based logic on No Logic
+ if logic_level == RequiredTactics.option_no_logic:
+ location_data = location_data._replace(rule=Location.access_rule)
+ location_table[i] = location_data
+ # Generating Beat event locations
+ if location_data.name.endswith((": Victory", ": Defeat")):
+ beat_events.append(
+ location_data._replace(name="Beat " + location_data.name.rsplit(": ", 1)[0], code=None)
+ )
+ return tuple(location_table + beat_events)
+
+lookup_location_id_to_type = {loc.code: loc.type for loc in get_locations(None) if loc.code is not None}
\ No newline at end of file
diff --git a/worlds/sc2/MissionTables.py b/worlds/sc2/MissionTables.py
new file mode 100644
index 000000000000..99b6448aff35
--- /dev/null
+++ b/worlds/sc2/MissionTables.py
@@ -0,0 +1,737 @@
+from typing import NamedTuple, Dict, List, Set, Union, Literal, Iterable, Callable
+from enum import IntEnum, Enum
+
+
+class SC2Race(IntEnum):
+ ANY = 0
+ TERRAN = 1
+ ZERG = 2
+ PROTOSS = 3
+
+
+class MissionPools(IntEnum):
+ STARTER = 0
+ EASY = 1
+ MEDIUM = 2
+ HARD = 3
+ VERY_HARD = 4
+ FINAL = 5
+
+
+class SC2CampaignGoalPriority(IntEnum):
+ """
+ Campaign's priority to goal election
+ """
+ NONE = 0
+ MINI_CAMPAIGN = 1 # A goal shouldn't be in a mini-campaign if there's at least one 'big' campaign
+ HARD = 2 # A campaign ending with a hard mission
+ VERY_HARD = 3 # A campaign ending with a very hard mission
+ EPILOGUE = 4 # Epilogue shall be always preferred as the goal if present
+
+
+class SC2Campaign(Enum):
+
+ def __new__(cls, *args, **kwargs):
+ value = len(cls.__members__) + 1
+ obj = object.__new__(cls)
+ obj._value_ = value
+ return obj
+
+ def __init__(self, campaign_id: int, name: str, goal_priority: SC2CampaignGoalPriority, race: SC2Race):
+ self.id = campaign_id
+ self.campaign_name = name
+ self.goal_priority = goal_priority
+ self.race = race
+
+ GLOBAL = 0, "Global", SC2CampaignGoalPriority.NONE, SC2Race.ANY
+ WOL = 1, "Wings of Liberty", SC2CampaignGoalPriority.VERY_HARD, SC2Race.TERRAN
+ PROPHECY = 2, "Prophecy", SC2CampaignGoalPriority.MINI_CAMPAIGN, SC2Race.PROTOSS
+ HOTS = 3, "Heart of the Swarm", SC2CampaignGoalPriority.HARD, SC2Race.ZERG
+ PROLOGUE = 4, "Whispers of Oblivion (Legacy of the Void: Prologue)", SC2CampaignGoalPriority.MINI_CAMPAIGN, SC2Race.PROTOSS
+ LOTV = 5, "Legacy of the Void", SC2CampaignGoalPriority.VERY_HARD, SC2Race.PROTOSS
+ EPILOGUE = 6, "Into the Void (Legacy of the Void: Epilogue)", SC2CampaignGoalPriority.EPILOGUE, SC2Race.ANY
+ NCO = 7, "Nova Covert Ops", SC2CampaignGoalPriority.HARD, SC2Race.TERRAN
+
+
+class SC2Mission(Enum):
+
+ def __new__(cls, *args, **kwargs):
+ value = len(cls.__members__) + 1
+ obj = object.__new__(cls)
+ obj._value_ = value
+ return obj
+
+ def __init__(self, mission_id: int, name: str, campaign: SC2Campaign, area: str, race: SC2Race, pool: MissionPools, map_file: str, build: bool = True):
+ self.id = mission_id
+ self.mission_name = name
+ self.campaign = campaign
+ self.area = area
+ self.race = race
+ self.pool = pool
+ self.map_file = map_file
+ self.build = build
+
+ # Wings of Liberty
+ LIBERATION_DAY = 1, "Liberation Day", SC2Campaign.WOL, "Mar Sara", SC2Race.ANY, MissionPools.STARTER, "ap_liberation_day", False
+ THE_OUTLAWS = 2, "The Outlaws", SC2Campaign.WOL, "Mar Sara", SC2Race.TERRAN, MissionPools.EASY, "ap_the_outlaws"
+ ZERO_HOUR = 3, "Zero Hour", SC2Campaign.WOL, "Mar Sara", SC2Race.TERRAN, MissionPools.EASY, "ap_zero_hour"
+ EVACUATION = 4, "Evacuation", SC2Campaign.WOL, "Colonist", SC2Race.TERRAN, MissionPools.EASY, "ap_evacuation"
+ OUTBREAK = 5, "Outbreak", SC2Campaign.WOL, "Colonist", SC2Race.TERRAN, MissionPools.EASY, "ap_outbreak"
+ SAFE_HAVEN = 6, "Safe Haven", SC2Campaign.WOL, "Colonist", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_safe_haven"
+ HAVENS_FALL = 7, "Haven's Fall", SC2Campaign.WOL, "Colonist", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_havens_fall"
+ SMASH_AND_GRAB = 8, "Smash and Grab", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.EASY, "ap_smash_and_grab"
+ THE_DIG = 9, "The Dig", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_the_dig"
+ THE_MOEBIUS_FACTOR = 10, "The Moebius Factor", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_the_moebius_factor"
+ SUPERNOVA = 11, "Supernova", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.HARD, "ap_supernova"
+ MAW_OF_THE_VOID = 12, "Maw of the Void", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.HARD, "ap_maw_of_the_void"
+ DEVILS_PLAYGROUND = 13, "Devil's Playground", SC2Campaign.WOL, "Covert", SC2Race.TERRAN, MissionPools.EASY, "ap_devils_playground"
+ WELCOME_TO_THE_JUNGLE = 14, "Welcome to the Jungle", SC2Campaign.WOL, "Covert", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_welcome_to_the_jungle"
+ BREAKOUT = 15, "Breakout", SC2Campaign.WOL, "Covert", SC2Race.ANY, MissionPools.STARTER, "ap_breakout", False
+ GHOST_OF_A_CHANCE = 16, "Ghost of a Chance", SC2Campaign.WOL, "Covert", SC2Race.ANY, MissionPools.STARTER, "ap_ghost_of_a_chance", False
+ THE_GREAT_TRAIN_ROBBERY = 17, "The Great Train Robbery", SC2Campaign.WOL, "Rebellion", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_the_great_train_robbery"
+ CUTTHROAT = 18, "Cutthroat", SC2Campaign.WOL, "Rebellion", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_cutthroat"
+ ENGINE_OF_DESTRUCTION = 19, "Engine of Destruction", SC2Campaign.WOL, "Rebellion", SC2Race.TERRAN, MissionPools.HARD, "ap_engine_of_destruction"
+ MEDIA_BLITZ = 20, "Media Blitz", SC2Campaign.WOL, "Rebellion", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_media_blitz"
+ PIERCING_OF_THE_SHROUD = 21, "Piercing the Shroud", SC2Campaign.WOL, "Rebellion", SC2Race.TERRAN, MissionPools.STARTER, "ap_piercing_the_shroud", False
+ GATES_OF_HELL = 26, "Gates of Hell", SC2Campaign.WOL, "Char", SC2Race.TERRAN, MissionPools.HARD, "ap_gates_of_hell"
+ BELLY_OF_THE_BEAST = 27, "Belly of the Beast", SC2Campaign.WOL, "Char", SC2Race.ANY, MissionPools.STARTER, "ap_belly_of_the_beast", False
+ SHATTER_THE_SKY = 28, "Shatter the Sky", SC2Campaign.WOL, "Char", SC2Race.TERRAN, MissionPools.HARD, "ap_shatter_the_sky"
+ ALL_IN = 29, "All-In", SC2Campaign.WOL, "Char", SC2Race.TERRAN, MissionPools.VERY_HARD, "ap_all_in"
+
+ # Prophecy
+ WHISPERS_OF_DOOM = 22, "Whispers of Doom", SC2Campaign.PROPHECY, "_1", SC2Race.ANY, MissionPools.STARTER, "ap_whispers_of_doom", False
+ A_SINISTER_TURN = 23, "A Sinister Turn", SC2Campaign.PROPHECY, "_2", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_a_sinister_turn"
+ ECHOES_OF_THE_FUTURE = 24, "Echoes of the Future", SC2Campaign.PROPHECY, "_3", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_echoes_of_the_future"
+ IN_UTTER_DARKNESS = 25, "In Utter Darkness", SC2Campaign.PROPHECY, "_4", SC2Race.PROTOSS, MissionPools.HARD, "ap_in_utter_darkness"
+
+ # Heart of the Swarm
+ LAB_RAT = 30, "Lab Rat", SC2Campaign.HOTS, "Umoja", SC2Race.ZERG, MissionPools.STARTER, "ap_lab_rat"
+ BACK_IN_THE_SADDLE = 31, "Back in the Saddle", SC2Campaign.HOTS, "Umoja", SC2Race.ANY, MissionPools.STARTER, "ap_back_in_the_saddle", False
+ RENDEZVOUS = 32, "Rendezvous", SC2Campaign.HOTS, "Umoja", SC2Race.ZERG, MissionPools.EASY, "ap_rendezvous"
+ HARVEST_OF_SCREAMS = 33, "Harvest of Screams", SC2Campaign.HOTS, "Kaldir", SC2Race.ZERG, MissionPools.EASY, "ap_harvest_of_screams"
+ SHOOT_THE_MESSENGER = 34, "Shoot the Messenger", SC2Campaign.HOTS, "Kaldir", SC2Race.ZERG, MissionPools.EASY, "ap_shoot_the_messenger"
+ ENEMY_WITHIN = 35, "Enemy Within", SC2Campaign.HOTS, "Kaldir", SC2Race.ANY, MissionPools.EASY, "ap_enemy_within", False
+ DOMINATION = 36, "Domination", SC2Campaign.HOTS, "Char", SC2Race.ZERG, MissionPools.EASY, "ap_domination"
+ FIRE_IN_THE_SKY = 37, "Fire in the Sky", SC2Campaign.HOTS, "Char", SC2Race.ZERG, MissionPools.MEDIUM, "ap_fire_in_the_sky"
+ OLD_SOLDIERS = 38, "Old Soldiers", SC2Campaign.HOTS, "Char", SC2Race.ZERG, MissionPools.MEDIUM, "ap_old_soldiers"
+ WAKING_THE_ANCIENT = 39, "Waking the Ancient", SC2Campaign.HOTS, "Zerus", SC2Race.ZERG, MissionPools.MEDIUM, "ap_waking_the_ancient"
+ THE_CRUCIBLE = 40, "The Crucible", SC2Campaign.HOTS, "Zerus", SC2Race.ZERG, MissionPools.MEDIUM, "ap_the_crucible"
+ SUPREME = 41, "Supreme", SC2Campaign.HOTS, "Zerus", SC2Race.ANY, MissionPools.MEDIUM, "ap_supreme", False
+ INFESTED = 42, "Infested", SC2Campaign.HOTS, "Skygeirr Station", SC2Race.ZERG, MissionPools.MEDIUM, "ap_infested"
+ HAND_OF_DARKNESS = 43, "Hand of Darkness", SC2Campaign.HOTS, "Skygeirr Station", SC2Race.ZERG, MissionPools.HARD, "ap_hand_of_darkness"
+ PHANTOMS_OF_THE_VOID = 44, "Phantoms of the Void", SC2Campaign.HOTS, "Skygeirr Station", SC2Race.ZERG, MissionPools.HARD, "ap_phantoms_of_the_void"
+ WITH_FRIENDS_LIKE_THESE = 45, "With Friends Like These", SC2Campaign.HOTS, "Dominion Space", SC2Race.ANY, MissionPools.STARTER, "ap_with_friends_like_these", False
+ CONVICTION = 46, "Conviction", SC2Campaign.HOTS, "Dominion Space", SC2Race.ANY, MissionPools.MEDIUM, "ap_conviction", False
+ PLANETFALL = 47, "Planetfall", SC2Campaign.HOTS, "Korhal", SC2Race.ZERG, MissionPools.HARD, "ap_planetfall"
+ DEATH_FROM_ABOVE = 48, "Death From Above", SC2Campaign.HOTS, "Korhal", SC2Race.ZERG, MissionPools.HARD, "ap_death_from_above"
+ THE_RECKONING = 49, "The Reckoning", SC2Campaign.HOTS, "Korhal", SC2Race.ZERG, MissionPools.HARD, "ap_the_reckoning"
+
+ # Prologue
+ DARK_WHISPERS = 50, "Dark Whispers", SC2Campaign.PROLOGUE, "_1", SC2Race.PROTOSS, MissionPools.EASY, "ap_dark_whispers"
+ GHOSTS_IN_THE_FOG = 51, "Ghosts in the Fog", SC2Campaign.PROLOGUE, "_2", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_ghosts_in_the_fog"
+ EVIL_AWOKEN = 52, "Evil Awoken", SC2Campaign.PROLOGUE, "_3", SC2Race.PROTOSS, MissionPools.STARTER, "ap_evil_awoken", False
+
+ # LotV
+ FOR_AIUR = 53, "For Aiur!", SC2Campaign.LOTV, "Aiur", SC2Race.ANY, MissionPools.STARTER, "ap_for_aiur", False
+ THE_GROWING_SHADOW = 54, "The Growing Shadow", SC2Campaign.LOTV, "Aiur", SC2Race.PROTOSS, MissionPools.EASY, "ap_the_growing_shadow"
+ THE_SPEAR_OF_ADUN = 55, "The Spear of Adun", SC2Campaign.LOTV, "Aiur", SC2Race.PROTOSS, MissionPools.EASY, "ap_the_spear_of_adun"
+ SKY_SHIELD = 56, "Sky Shield", SC2Campaign.LOTV, "Korhal", SC2Race.PROTOSS, MissionPools.EASY, "ap_sky_shield"
+ BROTHERS_IN_ARMS = 57, "Brothers in Arms", SC2Campaign.LOTV, "Korhal", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_brothers_in_arms"
+ AMON_S_REACH = 58, "Amon's Reach", SC2Campaign.LOTV, "Shakuras", SC2Race.PROTOSS, MissionPools.EASY, "ap_amon_s_reach"
+ LAST_STAND = 59, "Last Stand", SC2Campaign.LOTV, "Shakuras", SC2Race.PROTOSS, MissionPools.HARD, "ap_last_stand"
+ FORBIDDEN_WEAPON = 60, "Forbidden Weapon", SC2Campaign.LOTV, "Purifier", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_forbidden_weapon"
+ TEMPLE_OF_UNIFICATION = 61, "Temple of Unification", SC2Campaign.LOTV, "Ulnar", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_temple_of_unification"
+ THE_INFINITE_CYCLE = 62, "The Infinite Cycle", SC2Campaign.LOTV, "Ulnar", SC2Race.ANY, MissionPools.HARD, "ap_the_infinite_cycle", False
+ HARBINGER_OF_OBLIVION = 63, "Harbinger of Oblivion", SC2Campaign.LOTV, "Ulnar", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_harbinger_of_oblivion"
+ UNSEALING_THE_PAST = 64, "Unsealing the Past", SC2Campaign.LOTV, "Purifier", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_unsealing_the_past"
+ PURIFICATION = 65, "Purification", SC2Campaign.LOTV, "Purifier", SC2Race.PROTOSS, MissionPools.HARD, "ap_purification"
+ STEPS_OF_THE_RITE = 66, "Steps of the Rite", SC2Campaign.LOTV, "Tal'darim", SC2Race.PROTOSS, MissionPools.HARD, "ap_steps_of_the_rite"
+ RAK_SHIR = 67, "Rak'Shir", SC2Campaign.LOTV, "Tal'darim", SC2Race.PROTOSS, MissionPools.HARD, "ap_rak_shir"
+ TEMPLAR_S_CHARGE = 68, "Templar's Charge", SC2Campaign.LOTV, "Moebius", SC2Race.PROTOSS, MissionPools.HARD, "ap_templar_s_charge"
+ TEMPLAR_S_RETURN = 69, "Templar's Return", SC2Campaign.LOTV, "Return to Aiur", SC2Race.PROTOSS, MissionPools.EASY, "ap_templar_s_return", False
+ THE_HOST = 70, "The Host", SC2Campaign.LOTV, "Return to Aiur", SC2Race.PROTOSS, MissionPools.HARD, "ap_the_host",
+ SALVATION = 71, "Salvation", SC2Campaign.LOTV, "Return to Aiur", SC2Race.PROTOSS, MissionPools.VERY_HARD, "ap_salvation"
+
+ # Epilogue
+ INTO_THE_VOID = 72, "Into the Void", SC2Campaign.EPILOGUE, "_1", SC2Race.PROTOSS, MissionPools.VERY_HARD, "ap_into_the_void"
+ THE_ESSENCE_OF_ETERNITY = 73, "The Essence of Eternity", SC2Campaign.EPILOGUE, "_2", SC2Race.TERRAN, MissionPools.VERY_HARD, "ap_the_essence_of_eternity"
+ AMON_S_FALL = 74, "Amon's Fall", SC2Campaign.EPILOGUE, "_3", SC2Race.ZERG, MissionPools.VERY_HARD, "ap_amon_s_fall"
+
+ # Nova Covert Ops
+ THE_ESCAPE = 75, "The Escape", SC2Campaign.NCO, "_1", SC2Race.ANY, MissionPools.MEDIUM, "ap_the_escape", False
+ SUDDEN_STRIKE = 76, "Sudden Strike", SC2Campaign.NCO, "_1", SC2Race.TERRAN, MissionPools.EASY, "ap_sudden_strike"
+ ENEMY_INTELLIGENCE = 77, "Enemy Intelligence", SC2Campaign.NCO, "_1", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_enemy_intelligence"
+ TROUBLE_IN_PARADISE = 78, "Trouble In Paradise", SC2Campaign.NCO, "_2", SC2Race.TERRAN, MissionPools.HARD, "ap_trouble_in_paradise"
+ NIGHT_TERRORS = 79, "Night Terrors", SC2Campaign.NCO, "_2", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_night_terrors"
+ FLASHPOINT = 80, "Flashpoint", SC2Campaign.NCO, "_2", SC2Race.TERRAN, MissionPools.HARD, "ap_flashpoint"
+ IN_THE_ENEMY_S_SHADOW = 81, "In the Enemy's Shadow", SC2Campaign.NCO, "_3", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_in_the_enemy_s_shadow", False
+ DARK_SKIES = 82, "Dark Skies", SC2Campaign.NCO, "_3", SC2Race.TERRAN, MissionPools.HARD, "ap_dark_skies"
+ END_GAME = 83, "End Game", SC2Campaign.NCO, "_3", SC2Race.TERRAN, MissionPools.VERY_HARD, "ap_end_game"
+
+
+class MissionConnection:
+ campaign: SC2Campaign
+ connect_to: int # -1 connects to Menu
+
+ def __init__(self, connect_to, campaign = SC2Campaign.GLOBAL):
+ self.campaign = campaign
+ self.connect_to = connect_to
+
+ def _asdict(self):
+ return {
+ "campaign": self.campaign.id,
+ "connect_to": self.connect_to
+ }
+
+
+class MissionInfo(NamedTuple):
+ mission: SC2Mission
+ required_world: List[Union[MissionConnection, Dict[Literal["campaign", "connect_to"], int]]]
+ category: str
+ number: int = 0 # number of worlds need beaten
+ completion_critical: bool = False # missions needed to beat game
+ or_requirements: bool = False # true if the requirements should be or-ed instead of and-ed
+ ui_vertical_padding: int = 0
+
+
+class FillMission(NamedTuple):
+ type: MissionPools
+ connect_to: List[MissionConnection]
+ category: str
+ number: int = 0 # number of worlds need beaten
+ completion_critical: bool = False # missions needed to beat game
+ or_requirements: bool = False # true if the requirements should be or-ed instead of and-ed
+ removal_priority: int = 0 # how many missions missing from the pool required to remove this mission
+
+
+
+def vanilla_shuffle_order() -> Dict[SC2Campaign, List[FillMission]]:
+ return {
+ SC2Campaign.WOL: [
+ FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.WOL)], "Mar Sara", completion_critical=True),
+ FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.WOL)], "Mar Sara", completion_critical=True),
+ FillMission(MissionPools.EASY, [MissionConnection(1, SC2Campaign.WOL)], "Mar Sara", completion_critical=True),
+ FillMission(MissionPools.EASY, [MissionConnection(2, SC2Campaign.WOL)], "Colonist"),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(3, SC2Campaign.WOL)], "Colonist"),
+ FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.WOL)], "Colonist", number=7),
+ FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.WOL)], "Colonist", number=7, removal_priority=1),
+ FillMission(MissionPools.EASY, [MissionConnection(2, SC2Campaign.WOL)], "Artifact", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(7, SC2Campaign.WOL)], "Artifact", number=8, completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(8, SC2Campaign.WOL)], "Artifact", number=11, completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(9, SC2Campaign.WOL)], "Artifact", number=14, completion_critical=True, removal_priority=7),
+ FillMission(MissionPools.HARD, [MissionConnection(10, SC2Campaign.WOL)], "Artifact", completion_critical=True, removal_priority=6),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(2, SC2Campaign.WOL)], "Covert", number=4),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(12, SC2Campaign.WOL)], "Covert"),
+ FillMission(MissionPools.HARD, [MissionConnection(13, SC2Campaign.WOL)], "Covert", number=8, removal_priority=3),
+ FillMission(MissionPools.HARD, [MissionConnection(13, SC2Campaign.WOL)], "Covert", number=8, removal_priority=2),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(2, SC2Campaign.WOL)], "Rebellion", number=6),
+ FillMission(MissionPools.HARD, [MissionConnection(16, SC2Campaign.WOL)], "Rebellion"),
+ FillMission(MissionPools.HARD, [MissionConnection(17, SC2Campaign.WOL)], "Rebellion"),
+ FillMission(MissionPools.HARD, [MissionConnection(18, SC2Campaign.WOL)], "Rebellion", removal_priority=8),
+ FillMission(MissionPools.HARD, [MissionConnection(19, SC2Campaign.WOL)], "Rebellion", removal_priority=5),
+ FillMission(MissionPools.HARD, [MissionConnection(11, SC2Campaign.WOL)], "Char", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(21, SC2Campaign.WOL)], "Char", completion_critical=True, removal_priority=4),
+ FillMission(MissionPools.HARD, [MissionConnection(21, SC2Campaign.WOL)], "Char", completion_critical=True),
+ FillMission(MissionPools.FINAL, [MissionConnection(22, SC2Campaign.WOL), MissionConnection(23, SC2Campaign.WOL)], "Char", completion_critical=True, or_requirements=True)
+ ],
+ SC2Campaign.PROPHECY: [
+ FillMission(MissionPools.MEDIUM, [MissionConnection(8, SC2Campaign.WOL)], "_1"),
+ FillMission(MissionPools.HARD, [MissionConnection(0, SC2Campaign.PROPHECY)], "_2", removal_priority=2),
+ FillMission(MissionPools.HARD, [MissionConnection(1, SC2Campaign.PROPHECY)], "_3", removal_priority=1),
+ FillMission(MissionPools.FINAL, [MissionConnection(2, SC2Campaign.PROPHECY)], "_4"),
+ ],
+ SC2Campaign.HOTS: [
+ FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.HOTS)], "Umoja", completion_critical=True),
+ FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.HOTS)], "Umoja", completion_critical=True),
+ FillMission(MissionPools.EASY, [MissionConnection(1, SC2Campaign.HOTS)], "Umoja", completion_critical=True, removal_priority=1),
+ FillMission(MissionPools.EASY, [MissionConnection(2, SC2Campaign.HOTS)], "Kaldir", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(3, SC2Campaign.HOTS)], "Kaldir", completion_critical=True, removal_priority=2),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(4, SC2Campaign.HOTS)], "Kaldir", completion_critical=True),
+ FillMission(MissionPools.EASY, [MissionConnection(2, SC2Campaign.HOTS)], "Char", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(6, SC2Campaign.HOTS)], "Char", completion_critical=True, removal_priority=3),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(7, SC2Campaign.HOTS)], "Char", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(5, SC2Campaign.HOTS), MissionConnection(8, SC2Campaign.HOTS)], "Zerus", completion_critical=True, or_requirements=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(9, SC2Campaign.HOTS)], "Zerus", completion_critical=True, removal_priority=4),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(10, SC2Campaign.HOTS)], "Zerus", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(5, SC2Campaign.HOTS), MissionConnection(8, SC2Campaign.HOTS), MissionConnection(11, SC2Campaign.HOTS)], "Skygeirr Station", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(12, SC2Campaign.HOTS)], "Skygeirr Station", completion_critical=True, removal_priority=5),
+ FillMission(MissionPools.HARD, [MissionConnection(13, SC2Campaign.HOTS)], "Skygeirr Station", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(5, SC2Campaign.HOTS), MissionConnection(8, SC2Campaign.HOTS), MissionConnection(11, SC2Campaign.HOTS)], "Dominion Space", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(15, SC2Campaign.HOTS)], "Dominion Space", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(14, SC2Campaign.HOTS), MissionConnection(16, SC2Campaign.HOTS)], "Korhal", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(17, SC2Campaign.HOTS)], "Korhal", completion_critical=True),
+ FillMission(MissionPools.FINAL, [MissionConnection(18, SC2Campaign.HOTS)], "Korhal", completion_critical=True),
+ ],
+ SC2Campaign.PROLOGUE: [
+ FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.PROLOGUE)], "_1"),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.PROLOGUE)], "_2", removal_priority=1),
+ FillMission(MissionPools.FINAL, [MissionConnection(1, SC2Campaign.PROLOGUE)], "_3")
+ ],
+ SC2Campaign.LOTV: [
+ FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.LOTV)], "Aiur", completion_critical=True),
+ FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.LOTV)], "Aiur", completion_critical=True, removal_priority=3),
+ FillMission(MissionPools.EASY, [MissionConnection(1, SC2Campaign.LOTV)], "Aiur", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(2, SC2Campaign.LOTV)], "Korhal", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(3, SC2Campaign.LOTV)], "Korhal", completion_critical=True, removal_priority=7),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(2, SC2Campaign.LOTV)], "Shakuras", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(5, SC2Campaign.LOTV)], "Shakuras", completion_critical=True, removal_priority=6),
+ FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.LOTV), MissionConnection(6, SC2Campaign.LOTV)], "Purifier", completion_critical=True, or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.LOTV), MissionConnection(6, SC2Campaign.LOTV), MissionConnection(7, SC2Campaign.LOTV)], "Ulnar", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(8, SC2Campaign.LOTV)], "Ulnar", completion_critical=True, removal_priority=1),
+ FillMission(MissionPools.HARD, [MissionConnection(9, SC2Campaign.LOTV)], "Ulnar", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(10, SC2Campaign.LOTV)], "Purifier", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(11, SC2Campaign.LOTV)], "Purifier", completion_critical=True, removal_priority=5),
+ FillMission(MissionPools.HARD, [MissionConnection(10, SC2Campaign.LOTV)], "Tal'darim", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(13, SC2Campaign.LOTV)], "Tal'darim", completion_critical=True, removal_priority=4),
+ FillMission(MissionPools.HARD, [MissionConnection(12, SC2Campaign.LOTV), MissionConnection(14, SC2Campaign.LOTV)], "Moebius", completion_critical=True, or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(12, SC2Campaign.LOTV), MissionConnection(14, SC2Campaign.LOTV), MissionConnection(15, SC2Campaign.LOTV)], "Return to Aiur", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(16, SC2Campaign.LOTV)], "Return to Aiur", completion_critical=True, removal_priority=2),
+ FillMission(MissionPools.FINAL, [MissionConnection(17, SC2Campaign.LOTV)], "Return to Aiur", completion_critical=True),
+ ],
+ SC2Campaign.EPILOGUE: [
+ FillMission(MissionPools.VERY_HARD, [MissionConnection(24, SC2Campaign.WOL), MissionConnection(19, SC2Campaign.HOTS), MissionConnection(18, SC2Campaign.LOTV)], "_1", completion_critical=True),
+ FillMission(MissionPools.VERY_HARD, [MissionConnection(0, SC2Campaign.EPILOGUE)], "_2", completion_critical=True, removal_priority=1),
+ FillMission(MissionPools.FINAL, [MissionConnection(1, SC2Campaign.EPILOGUE)], "_3", completion_critical=True),
+ ],
+ SC2Campaign.NCO: [
+ FillMission(MissionPools.EASY, [MissionConnection(-1, SC2Campaign.NCO)], "_1", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.NCO)], "_1", completion_critical=True, removal_priority=6),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(1, SC2Campaign.NCO)], "_1", completion_critical=True, removal_priority=5),
+ FillMission(MissionPools.HARD, [MissionConnection(2, SC2Campaign.NCO)], "_2", completion_critical=True, removal_priority=7),
+ FillMission(MissionPools.HARD, [MissionConnection(3, SC2Campaign.NCO)], "_2", completion_critical=True, removal_priority=4),
+ FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.NCO)], "_2", completion_critical=True, removal_priority=3),
+ FillMission(MissionPools.HARD, [MissionConnection(5, SC2Campaign.NCO)], "_3", completion_critical=True, removal_priority=2),
+ FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.NCO)], "_3", completion_critical=True, removal_priority=1),
+ FillMission(MissionPools.FINAL, [MissionConnection(7, SC2Campaign.NCO)], "_3", completion_critical=True),
+ ]
+ }
+
+
+def mini_campaign_order() -> Dict[SC2Campaign, List[FillMission]]:
+ return {
+ SC2Campaign.WOL: [
+ FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.WOL)], "Mar Sara", completion_critical=True),
+ FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.WOL)], "Colonist"),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(1, SC2Campaign.WOL)], "Colonist"),
+ FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.WOL)], "Artifact", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(3, SC2Campaign.WOL)], "Artifact", number=4, completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.WOL)], "Artifact", number=8, completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.WOL)], "Covert", number=2),
+ FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.WOL)], "Covert"),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.WOL)], "Rebellion", number=3),
+ FillMission(MissionPools.HARD, [MissionConnection(8, SC2Campaign.WOL)], "Rebellion"),
+ FillMission(MissionPools.HARD, [MissionConnection(5, SC2Campaign.WOL)], "Char", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(5, SC2Campaign.WOL)], "Char", completion_critical=True),
+ FillMission(MissionPools.FINAL, [MissionConnection(10, SC2Campaign.WOL), MissionConnection(11, SC2Campaign.WOL)], "Char", completion_critical=True, or_requirements=True)
+ ],
+ SC2Campaign.PROPHECY: [
+ FillMission(MissionPools.MEDIUM, [MissionConnection(4, SC2Campaign.WOL)], "_1"),
+ FillMission(MissionPools.FINAL, [MissionConnection(0, SC2Campaign.PROPHECY)], "_2"),
+ ],
+ SC2Campaign.HOTS: [
+ FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.HOTS)], "Umoja", completion_critical=True),
+ FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.HOTS)], "Kaldir"),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(1, SC2Campaign.HOTS)], "Kaldir"),
+ FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.HOTS)], "Char"),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(3, SC2Campaign.HOTS)], "Char"),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.HOTS)], "Zerus", number=3),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(5, SC2Campaign.HOTS)], "Zerus"),
+ FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.HOTS)], "Skygeirr Station", number=5),
+ FillMission(MissionPools.HARD, [MissionConnection(7, SC2Campaign.HOTS)], "Skygeirr Station"),
+ FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.HOTS)], "Dominion Space", number=5),
+ FillMission(MissionPools.HARD, [MissionConnection(9, SC2Campaign.HOTS)], "Dominion Space"),
+ FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.HOTS)], "Korhal", completion_critical=True, number=8),
+ FillMission(MissionPools.FINAL, [MissionConnection(11, SC2Campaign.HOTS)], "Korhal", completion_critical=True),
+ ],
+ SC2Campaign.PROLOGUE: [
+ FillMission(MissionPools.EASY, [MissionConnection(-1, SC2Campaign.PROLOGUE)], "_1"),
+ FillMission(MissionPools.FINAL, [MissionConnection(0, SC2Campaign.PROLOGUE)], "_2")
+ ],
+ SC2Campaign.LOTV: [
+ FillMission(MissionPools.STARTER, [MissionConnection(-1, SC2Campaign.LOTV)], "Aiur",completion_critical=True),
+ FillMission(MissionPools.EASY, [MissionConnection(0, SC2Campaign.LOTV)], "Aiur", completion_critical=True),
+ FillMission(MissionPools.EASY, [MissionConnection(1, SC2Campaign.LOTV)], "Korhal", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(1, SC2Campaign.LOTV)], "Shakuras", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(2, SC2Campaign.LOTV), MissionConnection(3, SC2Campaign.LOTV)], "Purifier", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.LOTV)], "Purifier", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(4, SC2Campaign.LOTV)], "Ulnar", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(6, SC2Campaign.LOTV)], "Tal'darim", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(5, SC2Campaign.LOTV), MissionConnection(7, SC2Campaign.LOTV)], "Return to Aiur", completion_critical=True),
+ FillMission(MissionPools.FINAL, [MissionConnection(8, SC2Campaign.LOTV)], "Return to Aiur", completion_critical=True),
+ ],
+ SC2Campaign.EPILOGUE: [
+ FillMission(MissionPools.VERY_HARD, [MissionConnection(12, SC2Campaign.WOL), MissionConnection(12, SC2Campaign.HOTS), MissionConnection(9, SC2Campaign.LOTV)], "_1", completion_critical=True),
+ FillMission(MissionPools.FINAL, [MissionConnection(0, SC2Campaign.EPILOGUE)], "_2", completion_critical=True),
+ ],
+ SC2Campaign.NCO: [
+ FillMission(MissionPools.EASY, [MissionConnection(-1, SC2Campaign.NCO)], "_1", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(0, SC2Campaign.NCO)], "_1", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(1, SC2Campaign.NCO)], "_2", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(2, SC2Campaign.NCO)], "_3", completion_critical=True),
+ FillMission(MissionPools.FINAL, [MissionConnection(3, SC2Campaign.NCO)], "_3", completion_critical=True),
+ ]
+ }
+
+
+def gauntlet_order() -> Dict[SC2Campaign, List[FillMission]]:
+ return {
+ SC2Campaign.GLOBAL: [
+ FillMission(MissionPools.STARTER, [MissionConnection(-1)], "I", completion_critical=True),
+ FillMission(MissionPools.EASY, [MissionConnection(0)], "II", completion_critical=True),
+ FillMission(MissionPools.EASY, [MissionConnection(1)], "III", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(2)], "IV", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(3)], "V", completion_critical=True),
+ FillMission(MissionPools.HARD, [MissionConnection(4)], "VI", completion_critical=True),
+ FillMission(MissionPools.FINAL, [MissionConnection(5)], "Final", completion_critical=True)
+ ]
+ }
+
+
+def mini_gauntlet_order() -> Dict[SC2Campaign, List[FillMission]]:
+ return {
+ SC2Campaign.GLOBAL: [
+ FillMission(MissionPools.STARTER, [MissionConnection(-1)], "I", completion_critical=True),
+ FillMission(MissionPools.EASY, [MissionConnection(0)], "II", completion_critical=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(1)], "III", completion_critical=True),
+ FillMission(MissionPools.FINAL, [MissionConnection(2)], "Final", completion_critical=True)
+ ]
+ }
+
+
+def grid_order() -> Dict[SC2Campaign, List[FillMission]]:
+ return {
+ SC2Campaign.GLOBAL: [
+ FillMission(MissionPools.STARTER, [MissionConnection(-1)], "_1"),
+ FillMission(MissionPools.EASY, [MissionConnection(0)], "_1"),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(1), MissionConnection(6), MissionConnection( 3)], "_1", or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(2), MissionConnection(7)], "_1", or_requirements=True),
+ FillMission(MissionPools.EASY, [MissionConnection(0)], "_2"),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(1), MissionConnection(4)], "_2", or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(2), MissionConnection(5), MissionConnection(10), MissionConnection(7)], "_2", or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(3), MissionConnection(6), MissionConnection(11)], "_2", or_requirements=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(4), MissionConnection(9), MissionConnection(12)], "_3", or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(5), MissionConnection(8), MissionConnection(10), MissionConnection(13)], "_3", or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(6), MissionConnection(9), MissionConnection(11), MissionConnection(14)], "_3", or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(7), MissionConnection(10)], "_3", or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(8), MissionConnection(13)], "_4", or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(9), MissionConnection(12), MissionConnection(14)], "_4", or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(10), MissionConnection(13)], "_4", or_requirements=True),
+ FillMission(MissionPools.FINAL, [MissionConnection(11), MissionConnection(14)], "_4", or_requirements=True)
+ ]
+ }
+
+def mini_grid_order() -> Dict[SC2Campaign, List[FillMission]]:
+ return {
+ SC2Campaign.GLOBAL: [
+ FillMission(MissionPools.STARTER, [MissionConnection(-1)], "_1"),
+ FillMission(MissionPools.EASY, [MissionConnection(0)], "_1"),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(1), MissionConnection(5)], "_1", or_requirements=True),
+ FillMission(MissionPools.EASY, [MissionConnection(0)], "_2"),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(1), MissionConnection(3)], "_2", or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(2), MissionConnection(4)], "_2", or_requirements=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(3), MissionConnection(7)], "_3", or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(4), MissionConnection(6)], "_3", or_requirements=True),
+ FillMission(MissionPools.FINAL, [MissionConnection(5), MissionConnection(7)], "_3", or_requirements=True)
+ ]
+ }
+
+def tiny_grid_order() -> Dict[SC2Campaign, List[FillMission]]:
+ return {
+ SC2Campaign.GLOBAL: [
+ FillMission(MissionPools.STARTER, [MissionConnection(-1)], "_1"),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(0)], "_1"),
+ FillMission(MissionPools.EASY, [MissionConnection(0)], "_2"),
+ FillMission(MissionPools.FINAL, [MissionConnection(1), MissionConnection(2)], "_2", or_requirements=True),
+ ]
+ }
+
+def blitz_order() -> Dict[SC2Campaign, List[FillMission]]:
+ return {
+ SC2Campaign.GLOBAL: [
+ FillMission(MissionPools.STARTER, [MissionConnection(-1)], "I"),
+ FillMission(MissionPools.EASY, [MissionConnection(-1)], "I"),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(0), MissionConnection(1)], "II", number=1, or_requirements=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(0), MissionConnection(1)], "II", number=1, or_requirements=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(0), MissionConnection(1)], "III", number=2, or_requirements=True),
+ FillMission(MissionPools.MEDIUM, [MissionConnection(0), MissionConnection(1)], "III", number=2, or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(0), MissionConnection(1)], "IV", number=3, or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(0), MissionConnection(1)], "IV", number=3, or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(0), MissionConnection(1)], "V", number=4, or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(0), MissionConnection(1)], "V", number=4, or_requirements=True),
+ FillMission(MissionPools.HARD, [MissionConnection(0), MissionConnection(1)], "Final", number=5, or_requirements=True),
+ FillMission(MissionPools.FINAL, [MissionConnection(0), MissionConnection(1)], "Final", number=5, or_requirements=True)
+ ]
+ }
+
+
+mission_orders: List[Callable[[], Dict[SC2Campaign, List[FillMission]]]] = [
+ vanilla_shuffle_order,
+ vanilla_shuffle_order,
+ mini_campaign_order,
+ grid_order,
+ mini_grid_order,
+ blitz_order,
+ gauntlet_order,
+ mini_gauntlet_order,
+ tiny_grid_order
+]
+
+
+vanilla_mission_req_table: Dict[SC2Campaign, Dict[str, MissionInfo]] = {
+ SC2Campaign.WOL: {
+ SC2Mission.LIBERATION_DAY.mission_name: MissionInfo(SC2Mission.LIBERATION_DAY, [], SC2Mission.LIBERATION_DAY.area, completion_critical=True),
+ SC2Mission.THE_OUTLAWS.mission_name: MissionInfo(SC2Mission.THE_OUTLAWS, [MissionConnection(1, SC2Campaign.WOL)], SC2Mission.THE_OUTLAWS.area, completion_critical=True),
+ SC2Mission.ZERO_HOUR.mission_name: MissionInfo(SC2Mission.ZERO_HOUR, [MissionConnection(2, SC2Campaign.WOL)], SC2Mission.ZERO_HOUR.area, completion_critical=True),
+ SC2Mission.EVACUATION.mission_name: MissionInfo(SC2Mission.EVACUATION, [MissionConnection(3, SC2Campaign.WOL)], SC2Mission.EVACUATION.area),
+ SC2Mission.OUTBREAK.mission_name: MissionInfo(SC2Mission.OUTBREAK, [MissionConnection(4, SC2Campaign.WOL)], SC2Mission.OUTBREAK.area),
+ SC2Mission.SAFE_HAVEN.mission_name: MissionInfo(SC2Mission.SAFE_HAVEN, [MissionConnection(5, SC2Campaign.WOL)], SC2Mission.SAFE_HAVEN.area, number=7),
+ SC2Mission.HAVENS_FALL.mission_name: MissionInfo(SC2Mission.HAVENS_FALL, [MissionConnection(5, SC2Campaign.WOL)], SC2Mission.HAVENS_FALL.area, number=7),
+ SC2Mission.SMASH_AND_GRAB.mission_name: MissionInfo(SC2Mission.SMASH_AND_GRAB, [MissionConnection(3, SC2Campaign.WOL)], SC2Mission.SMASH_AND_GRAB.area, completion_critical=True),
+ SC2Mission.THE_DIG.mission_name: MissionInfo(SC2Mission.THE_DIG, [MissionConnection(8, SC2Campaign.WOL)], SC2Mission.THE_DIG.area, number=8, completion_critical=True),
+ SC2Mission.THE_MOEBIUS_FACTOR.mission_name: MissionInfo(SC2Mission.THE_MOEBIUS_FACTOR, [MissionConnection(9, SC2Campaign.WOL)], SC2Mission.THE_MOEBIUS_FACTOR.area, number=11, completion_critical=True),
+ SC2Mission.SUPERNOVA.mission_name: MissionInfo(SC2Mission.SUPERNOVA, [MissionConnection(10, SC2Campaign.WOL)], SC2Mission.SUPERNOVA.area, number=14, completion_critical=True),
+ SC2Mission.MAW_OF_THE_VOID.mission_name: MissionInfo(SC2Mission.MAW_OF_THE_VOID, [MissionConnection(11, SC2Campaign.WOL)], SC2Mission.MAW_OF_THE_VOID.area, completion_critical=True),
+ SC2Mission.DEVILS_PLAYGROUND.mission_name: MissionInfo(SC2Mission.DEVILS_PLAYGROUND, [MissionConnection(3, SC2Campaign.WOL)], SC2Mission.DEVILS_PLAYGROUND.area, number=4),
+ SC2Mission.WELCOME_TO_THE_JUNGLE.mission_name: MissionInfo(SC2Mission.WELCOME_TO_THE_JUNGLE, [MissionConnection(13, SC2Campaign.WOL)], SC2Mission.WELCOME_TO_THE_JUNGLE.area),
+ SC2Mission.BREAKOUT.mission_name: MissionInfo(SC2Mission.BREAKOUT, [MissionConnection(14, SC2Campaign.WOL)], SC2Mission.BREAKOUT.area, number=8),
+ SC2Mission.GHOST_OF_A_CHANCE.mission_name: MissionInfo(SC2Mission.GHOST_OF_A_CHANCE, [MissionConnection(14, SC2Campaign.WOL)], SC2Mission.GHOST_OF_A_CHANCE.area, number=8),
+ SC2Mission.THE_GREAT_TRAIN_ROBBERY.mission_name: MissionInfo(SC2Mission.THE_GREAT_TRAIN_ROBBERY, [MissionConnection(3, SC2Campaign.WOL)], SC2Mission.THE_GREAT_TRAIN_ROBBERY.area, number=6),
+ SC2Mission.CUTTHROAT.mission_name: MissionInfo(SC2Mission.CUTTHROAT, [MissionConnection(17, SC2Campaign.WOL)], SC2Mission.THE_GREAT_TRAIN_ROBBERY.area),
+ SC2Mission.ENGINE_OF_DESTRUCTION.mission_name: MissionInfo(SC2Mission.ENGINE_OF_DESTRUCTION, [MissionConnection(18, SC2Campaign.WOL)], SC2Mission.ENGINE_OF_DESTRUCTION.area),
+ SC2Mission.MEDIA_BLITZ.mission_name: MissionInfo(SC2Mission.MEDIA_BLITZ, [MissionConnection(19, SC2Campaign.WOL)], SC2Mission.MEDIA_BLITZ.area),
+ SC2Mission.PIERCING_OF_THE_SHROUD.mission_name: MissionInfo(SC2Mission.PIERCING_OF_THE_SHROUD, [MissionConnection(20, SC2Campaign.WOL)], SC2Mission.PIERCING_OF_THE_SHROUD.area),
+ SC2Mission.GATES_OF_HELL.mission_name: MissionInfo(SC2Mission.GATES_OF_HELL, [MissionConnection(12, SC2Campaign.WOL)], SC2Mission.GATES_OF_HELL.area, completion_critical=True),
+ SC2Mission.BELLY_OF_THE_BEAST.mission_name: MissionInfo(SC2Mission.BELLY_OF_THE_BEAST, [MissionConnection(22, SC2Campaign.WOL)], SC2Mission.BELLY_OF_THE_BEAST.area, completion_critical=True),
+ SC2Mission.SHATTER_THE_SKY.mission_name: MissionInfo(SC2Mission.SHATTER_THE_SKY, [MissionConnection(22, SC2Campaign.WOL)], SC2Mission.SHATTER_THE_SKY.area, completion_critical=True),
+ SC2Mission.ALL_IN.mission_name: MissionInfo(SC2Mission.ALL_IN, [MissionConnection(23, SC2Campaign.WOL), MissionConnection(24, SC2Campaign.WOL)], SC2Mission.ALL_IN.area, or_requirements=True, completion_critical=True)
+ },
+ SC2Campaign.PROPHECY: {
+ SC2Mission.WHISPERS_OF_DOOM.mission_name: MissionInfo(SC2Mission.WHISPERS_OF_DOOM, [MissionConnection(9, SC2Campaign.WOL)], SC2Mission.WHISPERS_OF_DOOM.area),
+ SC2Mission.A_SINISTER_TURN.mission_name: MissionInfo(SC2Mission.A_SINISTER_TURN, [MissionConnection(1, SC2Campaign.PROPHECY)], SC2Mission.A_SINISTER_TURN.area),
+ SC2Mission.ECHOES_OF_THE_FUTURE.mission_name: MissionInfo(SC2Mission.ECHOES_OF_THE_FUTURE, [MissionConnection(2, SC2Campaign.PROPHECY)], SC2Mission.ECHOES_OF_THE_FUTURE.area),
+ SC2Mission.IN_UTTER_DARKNESS.mission_name: MissionInfo(SC2Mission.IN_UTTER_DARKNESS, [MissionConnection(3, SC2Campaign.PROPHECY)], SC2Mission.IN_UTTER_DARKNESS.area)
+ },
+ SC2Campaign.HOTS: {
+ SC2Mission.LAB_RAT.mission_name: MissionInfo(SC2Mission.LAB_RAT, [], SC2Mission.LAB_RAT.area, completion_critical=True),
+ SC2Mission.BACK_IN_THE_SADDLE.mission_name: MissionInfo(SC2Mission.BACK_IN_THE_SADDLE, [MissionConnection(1, SC2Campaign.HOTS)], SC2Mission.BACK_IN_THE_SADDLE.area, completion_critical=True),
+ SC2Mission.RENDEZVOUS.mission_name: MissionInfo(SC2Mission.RENDEZVOUS, [MissionConnection(2, SC2Campaign.HOTS)], SC2Mission.RENDEZVOUS.area, completion_critical=True),
+ SC2Mission.HARVEST_OF_SCREAMS.mission_name: MissionInfo(SC2Mission.HARVEST_OF_SCREAMS, [MissionConnection(3, SC2Campaign.HOTS)], SC2Mission.HARVEST_OF_SCREAMS.area),
+ SC2Mission.SHOOT_THE_MESSENGER.mission_name: MissionInfo(SC2Mission.SHOOT_THE_MESSENGER, [MissionConnection(4, SC2Campaign.HOTS)], SC2Mission.SHOOT_THE_MESSENGER.area),
+ SC2Mission.ENEMY_WITHIN.mission_name: MissionInfo(SC2Mission.ENEMY_WITHIN, [MissionConnection(5, SC2Campaign.HOTS)], SC2Mission.ENEMY_WITHIN.area),
+ SC2Mission.DOMINATION.mission_name: MissionInfo(SC2Mission.DOMINATION, [MissionConnection(3, SC2Campaign.HOTS)], SC2Mission.DOMINATION.area),
+ SC2Mission.FIRE_IN_THE_SKY.mission_name: MissionInfo(SC2Mission.FIRE_IN_THE_SKY, [MissionConnection(7, SC2Campaign.HOTS)], SC2Mission.FIRE_IN_THE_SKY.area),
+ SC2Mission.OLD_SOLDIERS.mission_name: MissionInfo(SC2Mission.OLD_SOLDIERS, [MissionConnection(8, SC2Campaign.HOTS)], SC2Mission.OLD_SOLDIERS.area),
+ SC2Mission.WAKING_THE_ANCIENT.mission_name: MissionInfo(SC2Mission.WAKING_THE_ANCIENT, [MissionConnection(6, SC2Campaign.HOTS), MissionConnection(9, SC2Campaign.HOTS)], SC2Mission.WAKING_THE_ANCIENT.area, completion_critical=True, or_requirements=True),
+ SC2Mission.THE_CRUCIBLE.mission_name: MissionInfo(SC2Mission.THE_CRUCIBLE, [MissionConnection(10, SC2Campaign.HOTS)], SC2Mission.THE_CRUCIBLE.area, completion_critical=True),
+ SC2Mission.SUPREME.mission_name: MissionInfo(SC2Mission.SUPREME, [MissionConnection(11, SC2Campaign.HOTS)], SC2Mission.SUPREME.area, completion_critical=True),
+ SC2Mission.INFESTED.mission_name: MissionInfo(SC2Mission.INFESTED, [MissionConnection(6, SC2Campaign.HOTS), MissionConnection(9, SC2Campaign.HOTS), MissionConnection(12, SC2Campaign.HOTS)], SC2Mission.INFESTED.area),
+ SC2Mission.HAND_OF_DARKNESS.mission_name: MissionInfo(SC2Mission.HAND_OF_DARKNESS, [MissionConnection(13, SC2Campaign.HOTS)], SC2Mission.HAND_OF_DARKNESS.area),
+ SC2Mission.PHANTOMS_OF_THE_VOID.mission_name: MissionInfo(SC2Mission.PHANTOMS_OF_THE_VOID, [MissionConnection(14, SC2Campaign.HOTS)], SC2Mission.PHANTOMS_OF_THE_VOID.area),
+ SC2Mission.WITH_FRIENDS_LIKE_THESE.mission_name: MissionInfo(SC2Mission.WITH_FRIENDS_LIKE_THESE, [MissionConnection(6, SC2Campaign.HOTS), MissionConnection(9, SC2Campaign.HOTS), MissionConnection(12, SC2Campaign.HOTS)], SC2Mission.WITH_FRIENDS_LIKE_THESE.area),
+ SC2Mission.CONVICTION.mission_name: MissionInfo(SC2Mission.CONVICTION, [MissionConnection(16, SC2Campaign.HOTS)], SC2Mission.CONVICTION.area),
+ SC2Mission.PLANETFALL.mission_name: MissionInfo(SC2Mission.PLANETFALL, [MissionConnection(15, SC2Campaign.HOTS), MissionConnection(17, SC2Campaign.HOTS)], SC2Mission.PLANETFALL.area, completion_critical=True),
+ SC2Mission.DEATH_FROM_ABOVE.mission_name: MissionInfo(SC2Mission.DEATH_FROM_ABOVE, [MissionConnection(18, SC2Campaign.HOTS)], SC2Mission.DEATH_FROM_ABOVE.area, completion_critical=True),
+ SC2Mission.THE_RECKONING.mission_name: MissionInfo(SC2Mission.THE_RECKONING, [MissionConnection(19, SC2Campaign.HOTS)], SC2Mission.THE_RECKONING.area, completion_critical=True),
+ },
+ SC2Campaign.PROLOGUE: {
+ SC2Mission.DARK_WHISPERS.mission_name: MissionInfo(SC2Mission.DARK_WHISPERS, [], SC2Mission.DARK_WHISPERS.area),
+ SC2Mission.GHOSTS_IN_THE_FOG.mission_name: MissionInfo(SC2Mission.GHOSTS_IN_THE_FOG, [MissionConnection(1, SC2Campaign.PROLOGUE)], SC2Mission.GHOSTS_IN_THE_FOG.area),
+ SC2Mission.EVIL_AWOKEN.mission_name: MissionInfo(SC2Mission.EVIL_AWOKEN, [MissionConnection(2, SC2Campaign.PROLOGUE)], SC2Mission.EVIL_AWOKEN.area)
+ },
+ SC2Campaign.LOTV: {
+ SC2Mission.FOR_AIUR.mission_name: MissionInfo(SC2Mission.FOR_AIUR, [], SC2Mission.FOR_AIUR.area, completion_critical=True),
+ SC2Mission.THE_GROWING_SHADOW.mission_name: MissionInfo(SC2Mission.THE_GROWING_SHADOW, [MissionConnection(1, SC2Campaign.LOTV)], SC2Mission.THE_GROWING_SHADOW.area, completion_critical=True),
+ SC2Mission.THE_SPEAR_OF_ADUN.mission_name: MissionInfo(SC2Mission.THE_SPEAR_OF_ADUN, [MissionConnection(2, SC2Campaign.LOTV)], SC2Mission.THE_SPEAR_OF_ADUN.area, completion_critical=True),
+ SC2Mission.SKY_SHIELD.mission_name: MissionInfo(SC2Mission.SKY_SHIELD, [MissionConnection(3, SC2Campaign.LOTV)], SC2Mission.SKY_SHIELD.area, completion_critical=True),
+ SC2Mission.BROTHERS_IN_ARMS.mission_name: MissionInfo(SC2Mission.BROTHERS_IN_ARMS, [MissionConnection(4, SC2Campaign.LOTV)], SC2Mission.BROTHERS_IN_ARMS.area, completion_critical=True),
+ SC2Mission.AMON_S_REACH.mission_name: MissionInfo(SC2Mission.AMON_S_REACH, [MissionConnection(3, SC2Campaign.LOTV)], SC2Mission.AMON_S_REACH.area, completion_critical=True),
+ SC2Mission.LAST_STAND.mission_name: MissionInfo(SC2Mission.LAST_STAND, [MissionConnection(6, SC2Campaign.LOTV)], SC2Mission.LAST_STAND.area, completion_critical=True),
+ SC2Mission.FORBIDDEN_WEAPON.mission_name: MissionInfo(SC2Mission.FORBIDDEN_WEAPON, [MissionConnection(5, SC2Campaign.LOTV), MissionConnection(7, SC2Campaign.LOTV)], SC2Mission.FORBIDDEN_WEAPON.area, completion_critical=True, or_requirements=True),
+ SC2Mission.TEMPLE_OF_UNIFICATION.mission_name: MissionInfo(SC2Mission.TEMPLE_OF_UNIFICATION, [MissionConnection(5, SC2Campaign.LOTV), MissionConnection(7, SC2Campaign.LOTV), MissionConnection(8, SC2Campaign.LOTV)], SC2Mission.TEMPLE_OF_UNIFICATION.area, completion_critical=True),
+ SC2Mission.THE_INFINITE_CYCLE.mission_name: MissionInfo(SC2Mission.THE_INFINITE_CYCLE, [MissionConnection(9, SC2Campaign.LOTV)], SC2Mission.THE_INFINITE_CYCLE.area, completion_critical=True),
+ SC2Mission.HARBINGER_OF_OBLIVION.mission_name: MissionInfo(SC2Mission.HARBINGER_OF_OBLIVION, [MissionConnection(10, SC2Campaign.LOTV)], SC2Mission.HARBINGER_OF_OBLIVION.area, completion_critical=True),
+ SC2Mission.UNSEALING_THE_PAST.mission_name: MissionInfo(SC2Mission.UNSEALING_THE_PAST, [MissionConnection(11, SC2Campaign.LOTV)], SC2Mission.UNSEALING_THE_PAST.area, completion_critical=True),
+ SC2Mission.PURIFICATION.mission_name: MissionInfo(SC2Mission.PURIFICATION, [MissionConnection(12, SC2Campaign.LOTV)], SC2Mission.PURIFICATION.area, completion_critical=True),
+ SC2Mission.STEPS_OF_THE_RITE.mission_name: MissionInfo(SC2Mission.STEPS_OF_THE_RITE, [MissionConnection(11, SC2Campaign.LOTV)], SC2Mission.STEPS_OF_THE_RITE.area, completion_critical=True),
+ SC2Mission.RAK_SHIR.mission_name: MissionInfo(SC2Mission.RAK_SHIR, [MissionConnection(14, SC2Campaign.LOTV)], SC2Mission.RAK_SHIR.area, completion_critical=True),
+ SC2Mission.TEMPLAR_S_CHARGE.mission_name: MissionInfo(SC2Mission.TEMPLAR_S_CHARGE, [MissionConnection(13, SC2Campaign.LOTV), MissionConnection(15, SC2Campaign.LOTV)], SC2Mission.TEMPLAR_S_CHARGE.area, completion_critical=True, or_requirements=True),
+ SC2Mission.TEMPLAR_S_RETURN.mission_name: MissionInfo(SC2Mission.TEMPLAR_S_RETURN, [MissionConnection(13, SC2Campaign.LOTV), MissionConnection(15, SC2Campaign.LOTV), MissionConnection(16, SC2Campaign.LOTV)], SC2Mission.TEMPLAR_S_RETURN.area, completion_critical=True),
+ SC2Mission.THE_HOST.mission_name: MissionInfo(SC2Mission.THE_HOST, [MissionConnection(17, SC2Campaign.LOTV)], SC2Mission.THE_HOST.area, completion_critical=True),
+ SC2Mission.SALVATION.mission_name: MissionInfo(SC2Mission.SALVATION, [MissionConnection(18, SC2Campaign.LOTV)], SC2Mission.SALVATION.area, completion_critical=True),
+ },
+ SC2Campaign.EPILOGUE: {
+ SC2Mission.INTO_THE_VOID.mission_name: MissionInfo(SC2Mission.INTO_THE_VOID, [MissionConnection(25, SC2Campaign.WOL), MissionConnection(20, SC2Campaign.HOTS), MissionConnection(19, SC2Campaign.LOTV)], SC2Mission.INTO_THE_VOID.area, completion_critical=True),
+ SC2Mission.THE_ESSENCE_OF_ETERNITY.mission_name: MissionInfo(SC2Mission.THE_ESSENCE_OF_ETERNITY, [MissionConnection(1, SC2Campaign.EPILOGUE)], SC2Mission.THE_ESSENCE_OF_ETERNITY.area, completion_critical=True),
+ SC2Mission.AMON_S_FALL.mission_name: MissionInfo(SC2Mission.AMON_S_FALL, [MissionConnection(2, SC2Campaign.EPILOGUE)], SC2Mission.AMON_S_FALL.area, completion_critical=True),
+ },
+ SC2Campaign.NCO: {
+ SC2Mission.THE_ESCAPE.mission_name: MissionInfo(SC2Mission.THE_ESCAPE, [], SC2Mission.THE_ESCAPE.area, completion_critical=True),
+ SC2Mission.SUDDEN_STRIKE.mission_name: MissionInfo(SC2Mission.SUDDEN_STRIKE, [MissionConnection(1, SC2Campaign.NCO)], SC2Mission.SUDDEN_STRIKE.area, completion_critical=True),
+ SC2Mission.ENEMY_INTELLIGENCE.mission_name: MissionInfo(SC2Mission.ENEMY_INTELLIGENCE, [MissionConnection(2, SC2Campaign.NCO)], SC2Mission.ENEMY_INTELLIGENCE.area, completion_critical=True),
+ SC2Mission.TROUBLE_IN_PARADISE.mission_name: MissionInfo(SC2Mission.TROUBLE_IN_PARADISE, [MissionConnection(3, SC2Campaign.NCO)], SC2Mission.TROUBLE_IN_PARADISE.area, completion_critical=True),
+ SC2Mission.NIGHT_TERRORS.mission_name: MissionInfo(SC2Mission.NIGHT_TERRORS, [MissionConnection(4, SC2Campaign.NCO)], SC2Mission.NIGHT_TERRORS.area, completion_critical=True),
+ SC2Mission.FLASHPOINT.mission_name: MissionInfo(SC2Mission.FLASHPOINT, [MissionConnection(5, SC2Campaign.NCO)], SC2Mission.FLASHPOINT.area, completion_critical=True),
+ SC2Mission.IN_THE_ENEMY_S_SHADOW.mission_name: MissionInfo(SC2Mission.IN_THE_ENEMY_S_SHADOW, [MissionConnection(6, SC2Campaign.NCO)], SC2Mission.IN_THE_ENEMY_S_SHADOW.area, completion_critical=True),
+ SC2Mission.DARK_SKIES.mission_name: MissionInfo(SC2Mission.DARK_SKIES, [MissionConnection(7, SC2Campaign.NCO)], SC2Mission.DARK_SKIES.area, completion_critical=True),
+ SC2Mission.END_GAME.mission_name: MissionInfo(SC2Mission.END_GAME, [MissionConnection(8, SC2Campaign.NCO)], SC2Mission.END_GAME.area, completion_critical=True),
+ }
+}
+
+lookup_id_to_mission: Dict[int, SC2Mission] = {
+ mission.id: mission for mission in SC2Mission
+}
+
+lookup_name_to_mission: Dict[str, SC2Mission] = {
+ mission.mission_name: mission for mission in SC2Mission
+}
+
+lookup_id_to_campaign: Dict[int, SC2Campaign] = {
+ campaign.id: campaign for campaign in SC2Campaign
+}
+
+
+campaign_mission_table: Dict[SC2Campaign, Set[SC2Mission]] = {
+ campaign: set() for campaign in SC2Campaign
+}
+for mission in SC2Mission:
+ campaign_mission_table[mission.campaign].add(mission)
+
+
+def get_campaign_difficulty(campaign: SC2Campaign, excluded_missions: Iterable[SC2Mission] = ()) -> MissionPools:
+ """
+
+ :param campaign:
+ :param excluded_missions:
+ :return: Campaign's the most difficult non-excluded mission
+ """
+ excluded_mission_set = set(excluded_missions)
+ included_missions = campaign_mission_table[campaign].difference(excluded_mission_set)
+ return max([mission.pool for mission in included_missions])
+
+
+def get_campaign_goal_priority(campaign: SC2Campaign, excluded_missions: Iterable[SC2Mission] = ()) -> SC2CampaignGoalPriority:
+ """
+ Gets a modified campaign goal priority.
+ If all the campaign's goal missions are excluded, it's ineligible to have the goal
+ If the campaign's very hard missions are excluded, the priority is lowered to hard
+ :param campaign:
+ :param excluded_missions:
+ :return:
+ """
+ if excluded_missions is None:
+ return campaign.goal_priority
+ else:
+ goal_missions = set(get_campaign_potential_goal_missions(campaign))
+ excluded_mission_set = set(excluded_missions)
+ remaining_goals = goal_missions.difference(excluded_mission_set)
+ if remaining_goals == set():
+ # All potential goals are excluded, the campaign can't be a goal
+ return SC2CampaignGoalPriority.NONE
+ elif campaign.goal_priority == SC2CampaignGoalPriority.VERY_HARD:
+ # Check if a very hard campaign doesn't get rid of it's last very hard mission
+ difficulty = get_campaign_difficulty(campaign, excluded_missions)
+ if difficulty == MissionPools.VERY_HARD:
+ return SC2CampaignGoalPriority.VERY_HARD
+ else:
+ return SC2CampaignGoalPriority.HARD
+ else:
+ return campaign.goal_priority
+
+
+class SC2CampaignGoal(NamedTuple):
+ mission: SC2Mission
+ location: str
+
+
+campaign_final_mission_locations: Dict[SC2Campaign, SC2CampaignGoal] = {
+ SC2Campaign.WOL: SC2CampaignGoal(SC2Mission.ALL_IN, "All-In: Victory"),
+ SC2Campaign.PROPHECY: SC2CampaignGoal(SC2Mission.IN_UTTER_DARKNESS, "In Utter Darkness: Kills"),
+ SC2Campaign.HOTS: None,
+ SC2Campaign.PROLOGUE: SC2CampaignGoal(SC2Mission.EVIL_AWOKEN, "Evil Awoken: Victory"),
+ SC2Campaign.LOTV: SC2CampaignGoal(SC2Mission.SALVATION, "Salvation: Victory"),
+ SC2Campaign.EPILOGUE: None,
+ SC2Campaign.NCO: None,
+}
+
+campaign_alt_final_mission_locations: Dict[SC2Campaign, Dict[SC2Mission, str]] = {
+ SC2Campaign.WOL: {
+ SC2Mission.MAW_OF_THE_VOID: "Maw of the Void: Victory",
+ SC2Mission.ENGINE_OF_DESTRUCTION: "Engine of Destruction: Victory",
+ SC2Mission.SUPERNOVA: "Supernova: Victory",
+ SC2Mission.GATES_OF_HELL: "Gates of Hell: Victory",
+ SC2Mission.SHATTER_THE_SKY: "Shatter the Sky: Victory"
+ },
+ SC2Campaign.PROPHECY: None,
+ SC2Campaign.HOTS: {
+ SC2Mission.THE_RECKONING: "The Reckoning: Victory",
+ SC2Mission.THE_CRUCIBLE: "The Crucible: Victory",
+ SC2Mission.HAND_OF_DARKNESS: "Hand of Darkness: Victory",
+ SC2Mission.PHANTOMS_OF_THE_VOID: "Phantoms of the Void: Victory",
+ SC2Mission.PLANETFALL: "Planetfall: Victory",
+ SC2Mission.DEATH_FROM_ABOVE: "Death From Above: Victory"
+ },
+ SC2Campaign.PROLOGUE: {
+ SC2Mission.GHOSTS_IN_THE_FOG: "Ghosts in the Fog: Victory"
+ },
+ SC2Campaign.LOTV: {
+ SC2Mission.THE_HOST: "The Host: Victory",
+ SC2Mission.TEMPLAR_S_CHARGE: "Templar's Charge: Victory"
+ },
+ SC2Campaign.EPILOGUE: {
+ SC2Mission.AMON_S_FALL: "Amon's Fall: Victory",
+ SC2Mission.INTO_THE_VOID: "Into the Void: Victory",
+ SC2Mission.THE_ESSENCE_OF_ETERNITY: "The Essence of Eternity: Victory",
+ },
+ SC2Campaign.NCO: {
+ SC2Mission.END_GAME: "End Game: Victory",
+ SC2Mission.FLASHPOINT: "Flashpoint: Victory",
+ SC2Mission.DARK_SKIES: "Dark Skies: Victory",
+ SC2Mission.NIGHT_TERRORS: "Night Terrors: Victory",
+ SC2Mission.TROUBLE_IN_PARADISE: "Trouble In Paradise: Victory"
+ }
+}
+
+campaign_race_exceptions: Dict[SC2Mission, SC2Race] = {
+ SC2Mission.WITH_FRIENDS_LIKE_THESE: SC2Race.TERRAN
+}
+
+
+def get_goal_location(mission: SC2Mission) -> Union[str, None]:
+ """
+
+ :param mission:
+ :return: Goal location assigned to the goal mission
+ """
+ campaign = mission.campaign
+ primary_campaign_goal = campaign_final_mission_locations[campaign]
+ if primary_campaign_goal is not None:
+ if primary_campaign_goal.mission == mission:
+ return primary_campaign_goal.location
+
+ campaign_alt_goals = campaign_alt_final_mission_locations[campaign]
+ if campaign_alt_goals is not None:
+ return campaign_alt_goals.get(mission)
+
+ return None
+
+
+def get_campaign_potential_goal_missions(campaign: SC2Campaign) -> List[SC2Mission]:
+ """
+
+ :param campaign:
+ :return: All missions that can be the campaign's goal
+ """
+ missions: List[SC2Mission] = list()
+ primary_goal_mission = campaign_final_mission_locations[campaign]
+ if primary_goal_mission is not None:
+ missions.append(primary_goal_mission.mission)
+ alt_goal_locations = campaign_alt_final_mission_locations[campaign]
+ if alt_goal_locations is not None:
+ for mission in alt_goal_locations.keys():
+ missions.append(mission)
+
+ return missions
+
+
+def get_no_build_missions() -> List[SC2Mission]:
+ return [mission for mission in SC2Mission if not mission.build]
diff --git a/worlds/sc2/Options.py b/worlds/sc2/Options.py
new file mode 100644
index 000000000000..88febb7096ef
--- /dev/null
+++ b/worlds/sc2/Options.py
@@ -0,0 +1,908 @@
+from dataclasses import dataclass, fields, Field
+from typing import FrozenSet, Union, Set
+
+from Options import Choice, Toggle, DefaultOnToggle, ItemSet, OptionSet, Range, PerGameCommonOptions
+from .MissionTables import SC2Campaign, SC2Mission, lookup_name_to_mission, MissionPools, get_no_build_missions, \
+ campaign_mission_table
+from worlds.AutoWorld import World
+
+
+class GameDifficulty(Choice):
+ """
+ The difficulty of the campaign, affects enemy AI, starting units, and game speed.
+
+ For those unfamiliar with the Archipelago randomizer, the recommended settings are one difficulty level
+ lower than the vanilla game
+ """
+ display_name = "Game Difficulty"
+ option_casual = 0
+ option_normal = 1
+ option_hard = 2
+ option_brutal = 3
+ default = 1
+
+
+class GameSpeed(Choice):
+ """Optional setting to override difficulty-based game speed."""
+ display_name = "Game Speed"
+ option_default = 0
+ option_slower = 1
+ option_slow = 2
+ option_normal = 3
+ option_fast = 4
+ option_faster = 5
+ default = option_default
+
+
+class DisableForcedCamera(Toggle):
+ """
+ Prevents the game from moving or locking the camera without the player's consent.
+ """
+ display_name = "Disable Forced Camera Movement"
+
+
+class SkipCutscenes(Toggle):
+ """
+ Skips all cutscenes and prevents dialog from blocking progress.
+ """
+ display_name = "Skip Cutscenes"
+
+
+class AllInMap(Choice):
+ """Determines what version of All-In (WoL final map) that will be generated for the campaign."""
+ display_name = "All In Map"
+ option_ground = 0
+ option_air = 1
+
+
+class MissionOrder(Choice):
+ """
+ Determines the order the missions are played in. The last three mission orders end in a random mission.
+ Vanilla (83 total if all campaigns enabled): Keeps the standard mission order and branching from the vanilla Campaigns.
+ Vanilla Shuffled (83 total if all campaigns enabled): Keeps same branching paths from the vanilla Campaigns but randomizes the order of missions within.
+ Mini Campaign (47 total if all campaigns enabled): Shorter version of the campaign with randomized missions and optional branches.
+ Medium Grid (16): A 4x4 grid of random missions. Start at the top-left and forge a path towards bottom-right mission to win.
+ Mini Grid (9): A 3x3 version of Grid. Complete the bottom-right mission to win.
+ Blitz (12): 12 random missions that open up very quickly. Complete the bottom-right mission to win.
+ Gauntlet (7): Linear series of 7 random missions to complete the campaign.
+ Mini Gauntlet (4): Linear series of 4 random missions to complete the campaign.
+ Tiny Grid (4): A 2x2 version of Grid. Complete the bottom-right mission to win.
+ Grid (variable): A grid that will resize to use all non-excluded missions. Corners may be omitted to make the grid more square. Complete the bottom-right mission to win.
+ """
+ display_name = "Mission Order"
+ option_vanilla = 0
+ option_vanilla_shuffled = 1
+ option_mini_campaign = 2
+ option_medium_grid = 3
+ option_mini_grid = 4
+ option_blitz = 5
+ option_gauntlet = 6
+ option_mini_gauntlet = 7
+ option_tiny_grid = 8
+ option_grid = 9
+
+
+class MaximumCampaignSize(Range):
+ """
+ Sets an upper bound on how many missions to include when a variable-size mission order is selected.
+ If a set-size mission order is selected, does nothing.
+ """
+ display_name = "Maximum Campaign Size"
+ range_start = 1
+ range_end = 83
+ default = 83
+
+
+class GridTwoStartPositions(Toggle):
+ """
+ If turned on and 'grid' mission order is selected, removes a mission from the starting
+ corner sets the adjacent two missions as the starter missions.
+ """
+ display_name = "Start with two unlocked missions on grid"
+ default = Toggle.option_false
+
+
+class ColorChoice(Choice):
+ option_white = 0
+ option_red = 1
+ option_blue = 2
+ option_teal = 3
+ option_purple = 4
+ option_yellow = 5
+ option_orange = 6
+ option_green = 7
+ option_light_pink = 8
+ option_violet = 9
+ option_light_grey = 10
+ option_dark_green = 11
+ option_brown = 12
+ option_light_green = 13
+ option_dark_grey = 14
+ option_pink = 15
+ option_rainbow = 16
+ option_default = 17
+ default = option_default
+
+
+class PlayerColorTerranRaynor(ColorChoice):
+ """Determines in-game team color for playable Raynor's Raiders (Terran) factions."""
+ display_name = "Terran Player Color (Raynor)"
+
+
+class PlayerColorProtoss(ColorChoice):
+ """Determines in-game team color for playable Protoss factions."""
+ display_name = "Protoss Player Color"
+
+
+class PlayerColorZerg(ColorChoice):
+ """Determines in-game team color for playable Zerg factions before Kerrigan becomes Primal Kerrigan."""
+ display_name = "Zerg Player Color"
+
+
+class PlayerColorZergPrimal(ColorChoice):
+ """Determines in-game team color for playable Zerg factions after Kerrigan becomes Primal Kerrigan."""
+ display_name = "Zerg Player Color (Primal)"
+
+
+class EnableWolMissions(DefaultOnToggle):
+ """
+ Enables missions from main Wings of Liberty campaign.
+ """
+ display_name = "Enable Wings of Liberty missions"
+
+
+class EnableProphecyMissions(DefaultOnToggle):
+ """
+ Enables missions from Prophecy mini-campaign.
+ """
+ display_name = "Enable Prophecy missions"
+
+
+class EnableHotsMissions(DefaultOnToggle):
+ """
+ Enables missions from Heart of the Swarm campaign.
+ """
+ display_name = "Enable Heart of the Swarm missions"
+
+
+class EnableLotVPrologueMissions(DefaultOnToggle):
+ """
+ Enables missions from Prologue campaign.
+ """
+ display_name = "Enable Prologue (Legacy of the Void) missions"
+
+
+class EnableLotVMissions(DefaultOnToggle):
+ """
+ Enables missions from Legacy of the Void campaign.
+ """
+ display_name = "Enable Legacy of the Void (main campaign) missions"
+
+
+class EnableEpilogueMissions(DefaultOnToggle):
+ """
+ Enables missions from Epilogue campaign.
+ These missions are considered very hard.
+
+ Enabling Wings of Liberty, Heart of the Swarm and Legacy of the Void is strongly recommended in order to play Epilogue.
+ Not recommended for short mission orders.
+ See also: Exclude Very Hard Missions
+ """
+ display_name = "Enable Epilogue missions"
+
+
+class EnableNCOMissions(DefaultOnToggle):
+ """
+ Enables missions from Nova Covert Ops campaign.
+
+ Note: For best gameplay experience it's recommended to also enable Wings of Liberty campaign.
+ """
+ display_name = "Enable Nova Covert Ops missions"
+
+
+class ShuffleCampaigns(DefaultOnToggle):
+ """
+ Shuffles the missions between campaigns if enabled.
+ Only available for Vanilla Shuffled and Mini Campaign mission order
+ """
+ display_name = "Shuffle Campaigns"
+
+
+class ShuffleNoBuild(DefaultOnToggle):
+ """
+ Determines if the no-build missions are included in the shuffle.
+ If turned off, the no-build missions will not appear. Has no effect for Vanilla mission order.
+ """
+ display_name = "Shuffle No-Build Missions"
+
+
+class StarterUnit(Choice):
+ """
+ Unlocks a random unit at the start of the game.
+
+ Off: No units are provided, the first unit must be obtained from the randomizer
+ Balanced: A unit that doesn't give the player too much power early on is given
+ Any Starter Unit: Any starter unit can be given
+ """
+ display_name = "Starter Unit"
+ option_off = 0
+ option_balanced = 1
+ option_any_starter_unit = 2
+
+
+class RequiredTactics(Choice):
+ """
+ Determines the maximum tactical difficulty of the world (separate from mission difficulty). Higher settings
+ increase randomness.
+
+ Standard: All missions can be completed with good micro and macro.
+ Advanced: Completing missions may require relying on starting units and micro-heavy units.
+ No Logic: Units and upgrades may be placed anywhere. LIKELY TO RENDER THE RUN IMPOSSIBLE ON HARDER DIFFICULTIES!
+ Locks Grant Story Tech option to true.
+ """
+ display_name = "Required Tactics"
+ option_standard = 0
+ option_advanced = 1
+ option_no_logic = 2
+
+
+class GenericUpgradeMissions(Range):
+ """Determines the percentage of missions in the mission order that must be completed before
+ level 1 of all weapon and armor upgrades is unlocked. Level 2 upgrades require double the amount of missions,
+ and level 3 requires triple the amount. The required amounts are always rounded down.
+ If set to 0, upgrades are instead added to the item pool and must be found to be used."""
+ display_name = "Generic Upgrade Missions"
+ range_start = 0
+ range_end = 100
+ default = 0
+
+
+class GenericUpgradeResearch(Choice):
+ """Determines how weapon and armor upgrades affect missions once unlocked.
+
+ Vanilla: Upgrades must be researched as normal.
+ Auto In No-Build: In No-Build missions, upgrades are automatically researched.
+ In all other missions, upgrades must be researched as normal.
+ Auto In Build: In No-Build missions, upgrades are unavailable as normal.
+ In all other missions, upgrades are automatically researched.
+ Always Auto: Upgrades are automatically researched in all missions."""
+ display_name = "Generic Upgrade Research"
+ option_vanilla = 0
+ option_auto_in_no_build = 1
+ option_auto_in_build = 2
+ option_always_auto = 3
+
+
+class GenericUpgradeItems(Choice):
+ """Determines how weapon and armor upgrades are split into items. All options produce 3 levels of each item.
+ Does nothing if upgrades are unlocked by completed mission counts.
+
+ Individual Items: All weapon and armor upgrades are each an item,
+ resulting in 18 total upgrade items for Terran and 15 total items for Zerg and Protoss each.
+ Bundle Weapon And Armor: All types of weapon upgrades are one item per race,
+ and all types of armor upgrades are one item per race,
+ resulting in 18 total items.
+ Bundle Unit Class: Weapon and armor upgrades are merged,
+ but upgrades are bundled separately for each race:
+ Infantry, Vehicle, and Starship upgrades for Terran (9 items),
+ Ground and Flyer upgrades for Zerg (6 items),
+ Ground and Air upgrades for Protoss (6 items),
+ resulting in 21 total items.
+ Bundle All: All weapon and armor upgrades are one item per race,
+ resulting in 9 total items."""
+ display_name = "Generic Upgrade Items"
+ option_individual_items = 0
+ option_bundle_weapon_and_armor = 1
+ option_bundle_unit_class = 2
+ option_bundle_all = 3
+
+
+class NovaCovertOpsItems(Toggle):
+ """
+ If turned on, the equipment upgrades from Nova Covert Ops may be present in the world.
+
+ If Nova Covert Ops campaign is enabled, this option is locked to be turned on.
+ """
+ display_name = "Nova Covert Ops Items"
+ default = Toggle.option_true
+
+
+class BroodWarItems(Toggle):
+ """If turned on, returning items from StarCraft: Brood War may appear in the world."""
+ display_name = "Brood War Items"
+ default = Toggle.option_true
+
+
+class ExtendedItems(Toggle):
+ """If turned on, original items that did not appear in Campaign mode may appear in the world."""
+ display_name = "Extended Items"
+ default = Toggle.option_true
+
+
+# Current maximum number of upgrades for a unit
+MAX_UPGRADES_OPTION = 12
+
+
+class EnsureGenericItems(Range):
+ """
+ Specifies a minimum percentage of the generic item pool that will be present for the slot.
+ The generic item pool is the pool of all generically useful items after all exclusions.
+ Generically-useful items include: Worker upgrades, Building upgrades, economy upgrades,
+ Mercenaries, Kerrigan levels and abilities, and Spear of Adun abilities
+ Increasing this percentage will make units less common.
+ """
+ display_name = "Ensure Generic Items"
+ range_start = 0
+ range_end = 100
+ default = 25
+
+
+class MinNumberOfUpgrades(Range):
+ """
+ Set a minimum to the number of upgrades a unit/structure can have.
+ Note that most units have 4 or 6 upgrades.
+ If a unit has fewer upgrades than the minimum, it will have all of its upgrades.
+
+ Doesn't affect shared unit upgrades.
+ """
+ display_name = "Minimum number of upgrades per unit/structure"
+ range_start = 0
+ range_end = MAX_UPGRADES_OPTION
+ default = 2
+
+
+class MaxNumberOfUpgrades(Range):
+ """
+ Set a maximum to the number of upgrades a unit/structure can have. -1 is used to define unlimited.
+ Note that most unit have 4 to 6 upgrades.
+
+ Doesn't affect shared unit upgrades.
+ """
+ display_name = "Maximum number of upgrades per unit/structure"
+ range_start = -1
+ range_end = MAX_UPGRADES_OPTION
+ default = -1
+
+
+class KerriganPresence(Choice):
+ """
+ Determines whether Kerrigan is playable outside of missions that require her.
+
+ Vanilla: Kerrigan is playable as normal, appears in the same missions as in vanilla game.
+ Not Present: Kerrigan is not playable, unless the mission requires her to be present. Other hero units stay playable,
+ and locations normally requiring Kerrigan can be checked by any unit.
+ Kerrigan level items, active abilities and passive abilities affecting her will not appear.
+ In missions where the Kerrigan unit is required, story abilities are given in same way as Grant Story Tech is set to true
+ Not Present And No Passives: In addition to the above, Kerrigan's passive abilities affecting other units (such as Twin Drones) will not appear.
+
+ Note: Always set to "Not Present" if Heart of the Swarm campaign is disabled.
+ """
+ display_name = "Kerrigan Presence"
+ option_vanilla = 0
+ option_not_present = 1
+ option_not_present_and_no_passives = 2
+
+
+class KerriganLevelsPerMissionCompleted(Range):
+ """
+ Determines how many levels Kerrigan gains when a mission is beaten.
+
+ NOTE: Setting this too low can result in generation failures if The Infinite Cycle or Supreme are in the mission pool.
+ """
+ display_name = "Levels Per Mission Beaten"
+ range_start = 0
+ range_end = 20
+ default = 0
+
+
+class KerriganLevelsPerMissionCompletedCap(Range):
+ """
+ Limits how many total levels Kerrigan can gain from beating missions. This does not affect levels gained from items.
+ Set to -1 to disable this limit.
+
+ NOTE: The following missions have these level requirements:
+ Supreme: 35
+ The Infinite Cycle: 70
+ See Grant Story Levels for more details.
+ """
+ display_name = "Levels Per Mission Beaten Cap"
+ range_start = -1
+ range_end = 140
+ default = -1
+
+
+class KerriganLevelItemSum(Range):
+ """
+ Determines the sum of the level items in the world. This does not affect levels gained from beating missions.
+
+ NOTE: The following missions have these level requirements:
+ Supreme: 35
+ The Infinite Cycle: 70
+ See Grant Story Levels for more details.
+ """
+ display_name = "Kerrigan Level Item Sum"
+ range_start = 0
+ range_end = 140
+ default = 70
+
+
+class KerriganLevelItemDistribution(Choice):
+ """Determines the amount and size of Kerrigan level items.
+
+ Vanilla: Uses the distribution in the vanilla campaign.
+ This entails 32 individual levels and 6 packs of varying sizes.
+ This distribution always adds up to 70, ignoring the Level Item Sum setting.
+ Smooth: Uses a custom, condensed distribution of 10 items between sizes 4 and 10,
+ intended to fit more levels into settings with little room for filler while keeping some variance in level gains.
+ This distribution always adds up to 70, ignoring the Level Item Sum setting.
+ Size 70: Uses items worth 70 levels each.
+ Size 35: Uses items worth 35 levels each.
+ Size 14: Uses items worth 14 levels each.
+ Size 10: Uses items worth 10 levels each.
+ Size 7: Uses items worth 7 levels each.
+ Size 5: Uses items worth 5 levels each.
+ Size 2: Uses items worth 2 level eachs.
+ Size 1: Uses individual levels. As there are not enough locations in the game for this distribution,
+ this will result in a greatly reduced total level, and is likely to remove many other items."""
+ display_name = "Kerrigan Level Item Distribution"
+ option_vanilla = 0
+ option_smooth = 1
+ option_size_70 = 2
+ option_size_35 = 3
+ option_size_14 = 4
+ option_size_10 = 5
+ option_size_7 = 6
+ option_size_5 = 7
+ option_size_2 = 8
+ option_size_1 = 9
+ default = option_smooth
+
+
+class KerriganTotalLevelCap(Range):
+ """
+ Limits how many total levels Kerrigan can gain from any source. Depending on your other settings,
+ there may be more levels available in the world, but they will not affect Kerrigan.
+ Set to -1 to disable this limit.
+
+ NOTE: The following missions have these level requirements:
+ Supreme: 35
+ The Infinite Cycle: 70
+ See Grant Story Levels for more details.
+ """
+ display_name = "Total Level Cap"
+ range_start = -1
+ range_end = 140
+ default = -1
+
+
+class StartPrimaryAbilities(Range):
+ """Number of Primary Abilities (Kerrigan Tier 1, 2, and 4) to start the game with.
+ If set to 4, a Tier 7 ability is also included."""
+ display_name = "Starting Primary Abilities"
+ range_start = 0
+ range_end = 4
+ default = 0
+
+
+class KerriganPrimalStatus(Choice):
+ """Determines when Kerrigan appears in her Primal Zerg form.
+ This greatly increases her energy regeneration.
+
+ Vanilla: Kerrigan is human in missions that canonically appear before The Crucible,
+ and zerg thereafter.
+ Always Zerg: Kerrigan is always zerg.
+ Always Human: Kerrigan is always human.
+ Level 35: Kerrigan is human until reaching level 35, and zerg thereafter.
+ Half Completion: Kerrigan is human until half of the missions in the world are completed,
+ and zerg thereafter.
+ Item: Kerrigan's Primal Form is an item. She is human until it is found, and zerg thereafter."""
+ display_name = "Kerrigan Primal Status"
+ option_vanilla = 0
+ option_always_zerg = 1
+ option_always_human = 2
+ option_level_35 = 3
+ option_half_completion = 4
+ option_item = 5
+
+
+class SpearOfAdunPresence(Choice):
+ """
+ Determines in which missions Spear of Adun calldowns will be available.
+ Affects only abilities used from Spear of Adun top menu.
+
+ Not Present: Spear of Adun calldowns are unavailable.
+ LotV Protoss: Spear of Adun calldowns are only available in LotV main campaign
+ Protoss: Spear od Adun calldowns are available in any Protoss mission
+ Everywhere: Spear od Adun calldowns are available in any mission of any race
+ """
+ display_name = "Spear of Adun Presence"
+ option_not_present = 0
+ option_lotv_protoss = 1
+ option_protoss = 2
+ option_everywhere = 3
+ default = option_lotv_protoss
+
+ # Fix case
+ @classmethod
+ def get_option_name(cls, value: int) -> str:
+ if value == SpearOfAdunPresence.option_lotv_protoss:
+ return "LotV Protoss"
+ else:
+ return super().get_option_name(value)
+
+
+class SpearOfAdunPresentInNoBuild(Toggle):
+ """
+ Determines if Spear of Adun calldowns are available in no-build missions.
+
+ If turned on, Spear of Adun calldown powers are available in missions specified under "Spear of Adun Presence".
+ If turned off, Spear of Adun calldown powers are unavailable in all no-build missions
+ """
+ display_name = "Spear of Adun Present in No-Build"
+
+
+class SpearOfAdunAutonomouslyCastAbilityPresence(Choice):
+ """
+ Determines availability of Spear of Adun powers, that are autonomously cast.
+ Affects abilities like Reconstruction Beam or Overwatch
+
+ Not Presents: Autocasts are not available.
+ LotV Protoss: Spear of Adun autocasts are only available in LotV main campaign
+ Protoss: Spear od Adun autocasts are available in any Protoss mission
+ Everywhere: Spear od Adun autocasts are available in any mission of any race
+ """
+ display_name = "Spear of Adun Autonomously Cast Powers Presence"
+ option_not_present = 0
+ option_lotv_protoss = 1
+ option_protoss = 2
+ option_everywhere = 3
+ default = option_lotv_protoss
+
+ # Fix case
+ @classmethod
+ def get_option_name(cls, value: int) -> str:
+ if value == SpearOfAdunPresence.option_lotv_protoss:
+ return "LotV Protoss"
+ else:
+ return super().get_option_name(value)
+
+
+class SpearOfAdunAutonomouslyCastPresentInNoBuild(Toggle):
+ """
+ Determines if Spear of Adun autocasts are available in no-build missions.
+
+ If turned on, Spear of Adun autocasts are available in missions specified under "Spear of Adun Autonomously Cast Powers Presence".
+ If turned off, Spear of Adun autocasts are unavailable in all no-build missions
+ """
+ display_name = "Spear of Adun Autonomously Cast Powers Present in No-Build"
+
+
+class GrantStoryTech(Toggle):
+ """
+ If set true, grants special tech required for story mission completion for duration of the mission.
+ Otherwise, you need to find these tech by a normal means as items.
+ Affects story missions like Back in the Saddle and Supreme
+
+ Locked to true if Required Tactics is set to no logic.
+ """
+ display_name = "Grant Story Tech"
+
+
+class GrantStoryLevels(Choice):
+ """
+ If enabled, grants Kerrigan the required minimum levels for the following missions:
+ Supreme: 35
+ The Infinite Cycle: 70
+ The bonus levels only apply during the listed missions, and can exceed the Total Level Cap.
+
+ If disabled, either of these missions is included, and there are not enough levels in the world, generation may fail.
+ To prevent this, either increase the amount of levels in the world, or enable this option.
+
+ If disabled and Required Tactics is set to no logic, this option is forced to Minimum.
+
+ Disabled: Kerrigan does not get bonus levels for these missions,
+ instead the levels must be gained from items or beating missions.
+ Additive: Kerrigan gains bonus levels equal to the mission's required level.
+ Minimum: Kerrigan is either at her real level, or at the mission's required level,
+ depending on which is higher.
+ """
+ display_name = "Grant Story Levels"
+ option_disabled = 0
+ option_additive = 1
+ option_minimum = 2
+ default = option_minimum
+
+
+class TakeOverAIAllies(Toggle):
+ """
+ On maps supporting this feature allows you to take control over an AI Ally.
+ """
+ display_name = "Take Over AI Allies"
+
+
+class LockedItems(ItemSet):
+ """Guarantees that these items will be unlockable"""
+ display_name = "Locked Items"
+
+
+class ExcludedItems(ItemSet):
+ """Guarantees that these items will not be unlockable"""
+ display_name = "Excluded Items"
+
+
+class ExcludedMissions(OptionSet):
+ """Guarantees that these missions will not appear in the campaign
+ Doesn't apply to vanilla mission order.
+ It may be impossible to build a valid campaign if too many missions are excluded."""
+ display_name = "Excluded Missions"
+ valid_keys = {mission.mission_name for mission in SC2Mission}
+
+
+class ExcludeVeryHardMissions(Choice):
+ """
+ Excludes Very Hard missions outside of Epilogue campaign (All-In, Salvation, and all Epilogue missions are considered Very Hard).
+ Doesn't apply to "Vanilla" mission order.
+
+ Default: Not excluded for mission orders "Vanilla Shuffled" or "Grid" with Maximum Campaign Size >= 20,
+ excluded for any other order
+ Yes: Non-Epilogue Very Hard missions are excluded and won't be generated
+ No: Non-Epilogue Very Hard missions can appear normally. Not recommended for too short mission orders.
+
+ See also: Excluded Missions, Enable Epilogue Missions, Maximum Campaign Size
+ """
+ display_name = "Exclude Very Hard Missions"
+ option_default = 0
+ option_true = 1
+ option_false = 2
+
+ @classmethod
+ def get_option_name(cls, value):
+ return ["Default", "Yes", "No"][int(value)]
+
+
+class LocationInclusion(Choice):
+ option_enabled = 0
+ option_resources = 1
+ option_disabled = 2
+
+
+class VanillaLocations(LocationInclusion):
+ """
+ Enables or disables item rewards for completing vanilla objectives.
+ Vanilla objectives are bonus objectives from the vanilla game,
+ along with some additional objectives to balance the missions.
+ Enable these locations for a balanced experience.
+
+ Enabled: All locations fitting into this do their normal rewards
+ Resources: Forces these locations to contain Starting Resources
+ Disabled: Removes item rewards from these locations.
+
+ Note: Individual locations subject to plando are always enabled, so the plando can be placed properly.
+ See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando)
+ """
+ display_name = "Vanilla Locations"
+
+
+class ExtraLocations(LocationInclusion):
+ """
+ Enables or disables item rewards for mission progress and minor objectives.
+ This includes mandatory mission objectives,
+ collecting reinforcements and resource pickups,
+ destroying structures, and overcoming minor challenges.
+ Enables these locations to add more checks and items to your world.
+
+ Enabled: All locations fitting into this do their normal rewards
+ Resources: Forces these locations to contain Starting Resources
+ Disabled: Removes item rewards from these locations.
+
+ Note: Individual locations subject to plando are always enabled, so the plando can be placed properly.
+ See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando)
+ """
+ display_name = "Extra Locations"
+
+
+class ChallengeLocations(LocationInclusion):
+ """
+ Enables or disables item rewards for completing challenge tasks.
+ Challenges are tasks that are more difficult than completing the mission, and are often based on achievements.
+ You might be required to visit the same mission later after getting stronger in order to finish these tasks.
+ Enable these locations to increase the difficulty of completing the multiworld.
+
+ Enabled: All locations fitting into this do their normal rewards
+ Resources: Forces these locations to contain Starting Resources
+ Disabled: Removes item rewards from these locations.
+
+ Note: Individual locations subject to plando are always enabled, so the plando can be placed properly.
+ See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando)
+ """
+ display_name = "Challenge Locations"
+
+
+class MasteryLocations(LocationInclusion):
+ """
+ Enables or disables item rewards for overcoming especially difficult challenges.
+ These challenges are often based on Mastery achievements and Feats of Strength.
+ Enable these locations to add the most difficult checks to the world.
+
+ Enabled: All locations fitting into this do their normal rewards
+ Resources: Forces these locations to contain Starting Resources
+ Disabled: Removes item rewards from these locations.
+
+ Note: Individual locations subject to plando are always enabled, so the plando can be placed properly.
+ See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando)
+ """
+ display_name = "Mastery Locations"
+
+
+class MineralsPerItem(Range):
+ """
+ Configures how many minerals are given per resource item.
+ """
+ display_name = "Minerals Per Item"
+ range_start = 0
+ range_end = 500
+ default = 25
+
+
+class VespenePerItem(Range):
+ """
+ Configures how much vespene gas is given per resource item.
+ """
+ display_name = "Vespene Per Item"
+ range_start = 0
+ range_end = 500
+ default = 25
+
+
+class StartingSupplyPerItem(Range):
+ """
+ Configures how much starting supply per is given per item.
+ """
+ display_name = "Starting Supply Per Item"
+ range_start = 0
+ range_end = 200
+ default = 5
+
+
+@dataclass
+class Starcraft2Options(PerGameCommonOptions):
+ game_difficulty: GameDifficulty
+ game_speed: GameSpeed
+ disable_forced_camera: DisableForcedCamera
+ skip_cutscenes: SkipCutscenes
+ all_in_map: AllInMap
+ mission_order: MissionOrder
+ maximum_campaign_size: MaximumCampaignSize
+ grid_two_start_positions: GridTwoStartPositions
+ player_color_terran_raynor: PlayerColorTerranRaynor
+ player_color_protoss: PlayerColorProtoss
+ player_color_zerg: PlayerColorZerg
+ player_color_zerg_primal: PlayerColorZergPrimal
+ enable_wol_missions: EnableWolMissions
+ enable_prophecy_missions: EnableProphecyMissions
+ enable_hots_missions: EnableHotsMissions
+ enable_lotv_prologue_missions: EnableLotVPrologueMissions
+ enable_lotv_missions: EnableLotVMissions
+ enable_epilogue_missions: EnableEpilogueMissions
+ enable_nco_missions: EnableNCOMissions
+ shuffle_campaigns: ShuffleCampaigns
+ shuffle_no_build: ShuffleNoBuild
+ starter_unit: StarterUnit
+ required_tactics: RequiredTactics
+ ensure_generic_items: EnsureGenericItems
+ min_number_of_upgrades: MinNumberOfUpgrades
+ max_number_of_upgrades: MaxNumberOfUpgrades
+ generic_upgrade_missions: GenericUpgradeMissions
+ generic_upgrade_research: GenericUpgradeResearch
+ generic_upgrade_items: GenericUpgradeItems
+ kerrigan_presence: KerriganPresence
+ kerrigan_levels_per_mission_completed: KerriganLevelsPerMissionCompleted
+ kerrigan_levels_per_mission_completed_cap: KerriganLevelsPerMissionCompletedCap
+ kerrigan_level_item_sum: KerriganLevelItemSum
+ kerrigan_level_item_distribution: KerriganLevelItemDistribution
+ kerrigan_total_level_cap: KerriganTotalLevelCap
+ start_primary_abilities: StartPrimaryAbilities
+ kerrigan_primal_status: KerriganPrimalStatus
+ spear_of_adun_presence: SpearOfAdunPresence
+ spear_of_adun_present_in_no_build: SpearOfAdunPresentInNoBuild
+ spear_of_adun_autonomously_cast_ability_presence: SpearOfAdunAutonomouslyCastAbilityPresence
+ spear_of_adun_autonomously_cast_present_in_no_build: SpearOfAdunAutonomouslyCastPresentInNoBuild
+ grant_story_tech: GrantStoryTech
+ grant_story_levels: GrantStoryLevels
+ take_over_ai_allies: TakeOverAIAllies
+ locked_items: LockedItems
+ excluded_items: ExcludedItems
+ excluded_missions: ExcludedMissions
+ exclude_very_hard_missions: ExcludeVeryHardMissions
+ nco_items: NovaCovertOpsItems
+ bw_items: BroodWarItems
+ ext_items: ExtendedItems
+ vanilla_locations: VanillaLocations
+ extra_locations: ExtraLocations
+ challenge_locations: ChallengeLocations
+ mastery_locations: MasteryLocations
+ minerals_per_item: MineralsPerItem
+ vespene_per_item: VespenePerItem
+ starting_supply_per_item: StartingSupplyPerItem
+
+
+def get_option_value(world: World, name: str) -> Union[int, FrozenSet]:
+ if world is None:
+ field: Field = [class_field for class_field in fields(Starcraft2Options) if class_field.name == name][0]
+ return field.type.default
+
+ player_option = getattr(world.options, name)
+
+ return player_option.value
+
+
+def get_enabled_campaigns(world: World) -> Set[SC2Campaign]:
+ enabled_campaigns = set()
+ if get_option_value(world, "enable_wol_missions"):
+ enabled_campaigns.add(SC2Campaign.WOL)
+ if get_option_value(world, "enable_prophecy_missions"):
+ enabled_campaigns.add(SC2Campaign.PROPHECY)
+ if get_option_value(world, "enable_hots_missions"):
+ enabled_campaigns.add(SC2Campaign.HOTS)
+ if get_option_value(world, "enable_lotv_prologue_missions"):
+ enabled_campaigns.add(SC2Campaign.PROLOGUE)
+ if get_option_value(world, "enable_lotv_missions"):
+ enabled_campaigns.add(SC2Campaign.LOTV)
+ if get_option_value(world, "enable_epilogue_missions"):
+ enabled_campaigns.add(SC2Campaign.EPILOGUE)
+ if get_option_value(world, "enable_nco_missions"):
+ enabled_campaigns.add(SC2Campaign.NCO)
+ return enabled_campaigns
+
+
+def get_disabled_campaigns(world: World) -> Set[SC2Campaign]:
+ all_campaigns = set(SC2Campaign)
+ enabled_campaigns = get_enabled_campaigns(world)
+ disabled_campaigns = all_campaigns.difference(enabled_campaigns)
+ disabled_campaigns.remove(SC2Campaign.GLOBAL)
+ return disabled_campaigns
+
+
+def get_excluded_missions(world: World) -> Set[SC2Mission]:
+ mission_order_type = get_option_value(world, "mission_order")
+ excluded_mission_names = get_option_value(world, "excluded_missions")
+ shuffle_no_build = get_option_value(world, "shuffle_no_build")
+ disabled_campaigns = get_disabled_campaigns(world)
+
+ excluded_missions: Set[SC2Mission] = set([lookup_name_to_mission[name] for name in excluded_mission_names])
+
+ # Excluding Very Hard missions depending on options
+ if (get_option_value(world, "exclude_very_hard_missions") == ExcludeVeryHardMissions.option_true
+ ) or (
+ get_option_value(world, "exclude_very_hard_missions") == ExcludeVeryHardMissions.option_default
+ and (
+ mission_order_type not in [MissionOrder.option_vanilla_shuffled, MissionOrder.option_grid]
+ or (
+ mission_order_type == MissionOrder.option_grid
+ and get_option_value(world, "maximum_campaign_size") < 20
+ )
+ )
+ ):
+ excluded_missions = excluded_missions.union(
+ [mission for mission in SC2Mission if
+ mission.pool == MissionPools.VERY_HARD and mission.campaign != SC2Campaign.EPILOGUE]
+ )
+ # Omitting No-Build missions if not shuffling no-build
+ if not shuffle_no_build:
+ excluded_missions = excluded_missions.union(get_no_build_missions())
+ # Omitting missions not in enabled campaigns
+ for campaign in disabled_campaigns:
+ excluded_missions = excluded_missions.union(campaign_mission_table[campaign])
+
+ return excluded_missions
+
+
+campaign_depending_orders = [
+ MissionOrder.option_vanilla,
+ MissionOrder.option_vanilla_shuffled,
+ MissionOrder.option_mini_campaign
+]
+
+kerrigan_unit_available = [
+ KerriganPresence.option_vanilla,
+]
\ No newline at end of file
diff --git a/worlds/sc2/PoolFilter.py b/worlds/sc2/PoolFilter.py
new file mode 100644
index 000000000000..068c62314923
--- /dev/null
+++ b/worlds/sc2/PoolFilter.py
@@ -0,0 +1,595 @@
+from typing import Callable, Dict, List, Set, Union, Tuple
+from BaseClasses import Item, Location
+from .Items import get_full_item_list, spider_mine_sources, second_pass_placeable_items, progressive_if_nco, \
+ progressive_if_ext, spear_of_adun_calldowns, spear_of_adun_castable_passives, nova_equipment
+from .MissionTables import mission_orders, MissionInfo, MissionPools, \
+ get_campaign_goal_priority, campaign_final_mission_locations, campaign_alt_final_mission_locations, \
+ SC2Campaign, SC2Race, SC2CampaignGoalPriority, SC2Mission
+from .Options import get_option_value, MissionOrder, \
+ get_enabled_campaigns, get_disabled_campaigns, RequiredTactics, kerrigan_unit_available, GrantStoryTech, \
+ TakeOverAIAllies, SpearOfAdunPresence, SpearOfAdunAutonomouslyCastAbilityPresence, campaign_depending_orders, \
+ ShuffleCampaigns, get_excluded_missions, ShuffleNoBuild, ExtraLocations, GrantStoryLevels
+from . import ItemNames
+from worlds.AutoWorld import World
+
+# Items with associated upgrades
+UPGRADABLE_ITEMS = {item.parent_item for item in get_full_item_list().values() if item.parent_item}
+
+BARRACKS_UNITS = {
+ ItemNames.MARINE, ItemNames.MEDIC, ItemNames.FIREBAT, ItemNames.MARAUDER,
+ ItemNames.REAPER, ItemNames.GHOST, ItemNames.SPECTRE, ItemNames.HERC,
+}
+FACTORY_UNITS = {
+ ItemNames.HELLION, ItemNames.VULTURE, ItemNames.GOLIATH, ItemNames.DIAMONDBACK,
+ ItemNames.SIEGE_TANK, ItemNames.THOR, ItemNames.PREDATOR, ItemNames.WIDOW_MINE,
+ ItemNames.CYCLONE, ItemNames.WARHOUND,
+}
+STARPORT_UNITS = {
+ ItemNames.MEDIVAC, ItemNames.WRAITH, ItemNames.VIKING, ItemNames.BANSHEE,
+ ItemNames.BATTLECRUISER, ItemNames.HERCULES, ItemNames.SCIENCE_VESSEL, ItemNames.RAVEN,
+ ItemNames.LIBERATOR, ItemNames.VALKYRIE,
+}
+
+
+def filter_missions(world: World) -> Dict[MissionPools, List[SC2Mission]]:
+
+ """
+ Returns a semi-randomly pruned tuple of no-build, easy, medium, and hard mission sets
+ """
+ world: World = world
+ mission_order_type = get_option_value(world, "mission_order")
+ shuffle_no_build = get_option_value(world, "shuffle_no_build")
+ enabled_campaigns = get_enabled_campaigns(world)
+ grant_story_tech = get_option_value(world, "grant_story_tech") == GrantStoryTech.option_true
+ grant_story_levels = get_option_value(world, "grant_story_levels") != GrantStoryLevels.option_disabled
+ extra_locations = get_option_value(world, "extra_locations")
+ excluded_missions: Set[SC2Mission] = get_excluded_missions(world)
+ mission_pools: Dict[MissionPools, List[SC2Mission]] = {}
+ for mission in SC2Mission:
+ if not mission_pools.get(mission.pool):
+ mission_pools[mission.pool] = list()
+ mission_pools[mission.pool].append(mission)
+ # A bit of safeguard:
+ for mission_pool in MissionPools:
+ if not mission_pools.get(mission_pool):
+ mission_pools[mission_pool] = []
+
+ if mission_order_type == MissionOrder.option_vanilla:
+ # Vanilla uses the entire mission pool
+ goal_priorities: Dict[SC2Campaign, SC2CampaignGoalPriority] = {campaign: get_campaign_goal_priority(campaign) for campaign in enabled_campaigns}
+ goal_level = max(goal_priorities.values())
+ candidate_campaigns = [campaign for campaign, goal_priority in goal_priorities.items() if goal_priority == goal_level]
+ goal_campaign = world.random.choice(candidate_campaigns)
+ if campaign_final_mission_locations[goal_campaign] is not None:
+ mission_pools[MissionPools.FINAL] = [campaign_final_mission_locations[goal_campaign].mission]
+ else:
+ mission_pools[MissionPools.FINAL] = [list(campaign_alt_final_mission_locations[goal_campaign].keys())[0]]
+ remove_final_mission_from_other_pools(mission_pools)
+ return mission_pools
+
+ # Finding the goal map
+ goal_priorities = {campaign: get_campaign_goal_priority(campaign, excluded_missions) for campaign in enabled_campaigns}
+ goal_level = max(goal_priorities.values())
+ candidate_campaigns = [campaign for campaign, goal_priority in goal_priorities.items() if goal_priority == goal_level]
+ goal_campaign = world.random.choice(candidate_campaigns)
+ primary_goal = campaign_final_mission_locations[goal_campaign]
+ if primary_goal is None or primary_goal.mission in excluded_missions:
+ # No primary goal or its mission is excluded
+ candidate_missions = list(campaign_alt_final_mission_locations[goal_campaign].keys())
+ candidate_missions = [mission for mission in candidate_missions if mission not in excluded_missions]
+ if len(candidate_missions) == 0:
+ raise Exception("There are no valid goal missions. Please exclude fewer missions.")
+ goal_mission = world.random.choice(candidate_missions)
+ else:
+ goal_mission = primary_goal.mission
+
+ # Excluding missions
+ for difficulty, mission_pool in mission_pools.items():
+ mission_pools[difficulty] = [mission for mission in mission_pool if mission not in excluded_missions]
+ mission_pools[MissionPools.FINAL] = [goal_mission]
+
+ # Mission pool changes
+ adv_tactics = get_option_value(world, "required_tactics") != RequiredTactics.option_standard
+
+ def move_mission(mission: SC2Mission, current_pool, new_pool):
+ if mission in mission_pools[current_pool]:
+ mission_pools[current_pool].remove(mission)
+ mission_pools[new_pool].append(mission)
+ # WoL
+ if shuffle_no_build == ShuffleNoBuild.option_false or adv_tactics:
+ # Replacing No Build missions with Easy missions
+ # WoL
+ move_mission(SC2Mission.ZERO_HOUR, MissionPools.EASY, MissionPools.STARTER)
+ move_mission(SC2Mission.EVACUATION, MissionPools.EASY, MissionPools.STARTER)
+ move_mission(SC2Mission.DEVILS_PLAYGROUND, MissionPools.EASY, MissionPools.STARTER)
+ # LotV
+ move_mission(SC2Mission.THE_GROWING_SHADOW, MissionPools.EASY, MissionPools.STARTER)
+ move_mission(SC2Mission.THE_SPEAR_OF_ADUN, MissionPools.EASY, MissionPools.STARTER)
+ if extra_locations == ExtraLocations.option_enabled:
+ move_mission(SC2Mission.SKY_SHIELD, MissionPools.EASY, MissionPools.STARTER)
+ # Pushing this to Easy
+ move_mission(SC2Mission.THE_GREAT_TRAIN_ROBBERY, MissionPools.MEDIUM, MissionPools.EASY)
+ if shuffle_no_build == ShuffleNoBuild.option_false:
+ # Pushing Outbreak to Normal, as it cannot be placed as the second mission on Build-Only
+ move_mission(SC2Mission.OUTBREAK, MissionPools.EASY, MissionPools.MEDIUM)
+ # Pushing extra Normal missions to Easy
+ move_mission(SC2Mission.ECHOES_OF_THE_FUTURE, MissionPools.MEDIUM, MissionPools.EASY)
+ move_mission(SC2Mission.CUTTHROAT, MissionPools.MEDIUM, MissionPools.EASY)
+ # Additional changes on Advanced Tactics
+ if adv_tactics:
+ # WoL
+ move_mission(SC2Mission.THE_GREAT_TRAIN_ROBBERY, MissionPools.EASY, MissionPools.STARTER)
+ move_mission(SC2Mission.SMASH_AND_GRAB, MissionPools.EASY, MissionPools.STARTER)
+ move_mission(SC2Mission.THE_MOEBIUS_FACTOR, MissionPools.MEDIUM, MissionPools.EASY)
+ move_mission(SC2Mission.WELCOME_TO_THE_JUNGLE, MissionPools.MEDIUM, MissionPools.EASY)
+ move_mission(SC2Mission.ENGINE_OF_DESTRUCTION, MissionPools.HARD, MissionPools.MEDIUM)
+ # LotV
+ move_mission(SC2Mission.AMON_S_REACH, MissionPools.EASY, MissionPools.STARTER)
+ # Prophecy needs to be adjusted on tiny grid
+ if enabled_campaigns == {SC2Campaign.PROPHECY} and mission_order_type == MissionOrder.option_tiny_grid:
+ move_mission(SC2Mission.A_SINISTER_TURN, MissionPools.MEDIUM, MissionPools.EASY)
+ # Prologue's only valid starter is the goal mission
+ if enabled_campaigns == {SC2Campaign.PROLOGUE} \
+ or mission_order_type in campaign_depending_orders \
+ and get_option_value(world, "shuffle_campaigns") == ShuffleCampaigns.option_false:
+ move_mission(SC2Mission.DARK_WHISPERS, MissionPools.EASY, MissionPools.STARTER)
+ # HotS
+ kerriganless = get_option_value(world, "kerrigan_presence") not in kerrigan_unit_available \
+ or SC2Campaign.HOTS not in enabled_campaigns
+ if adv_tactics:
+ # Medium -> Easy
+ for mission in (SC2Mission.FIRE_IN_THE_SKY, SC2Mission.WAKING_THE_ANCIENT, SC2Mission.CONVICTION):
+ move_mission(mission, MissionPools.MEDIUM, MissionPools.EASY)
+ # Hard -> Medium
+ move_mission(SC2Mission.PHANTOMS_OF_THE_VOID, MissionPools.HARD, MissionPools.MEDIUM)
+ if not kerriganless:
+ # Additional starter mission assuming player starts with minimal anti-air
+ move_mission(SC2Mission.WAKING_THE_ANCIENT, MissionPools.EASY, MissionPools.STARTER)
+ if grant_story_tech:
+ # Additional starter mission if player is granted story tech
+ move_mission(SC2Mission.ENEMY_WITHIN, MissionPools.EASY, MissionPools.STARTER)
+ move_mission(SC2Mission.TEMPLAR_S_RETURN, MissionPools.EASY, MissionPools.STARTER)
+ move_mission(SC2Mission.THE_ESCAPE, MissionPools.MEDIUM, MissionPools.STARTER)
+ move_mission(SC2Mission.IN_THE_ENEMY_S_SHADOW, MissionPools.MEDIUM, MissionPools.STARTER)
+ if (grant_story_tech and grant_story_levels) or kerriganless:
+ # The player has, all the stuff he needs, provided under these settings
+ move_mission(SC2Mission.SUPREME, MissionPools.MEDIUM, MissionPools.STARTER)
+ move_mission(SC2Mission.THE_INFINITE_CYCLE, MissionPools.HARD, MissionPools.STARTER)
+ if get_option_value(world, "take_over_ai_allies") == TakeOverAIAllies.option_true:
+ move_mission(SC2Mission.HARBINGER_OF_OBLIVION, MissionPools.MEDIUM, MissionPools.STARTER)
+ if len(mission_pools[MissionPools.STARTER]) < 2 and not kerriganless or adv_tactics:
+ # Conditionally moving Easy missions to Starter
+ move_mission(SC2Mission.HARVEST_OF_SCREAMS, MissionPools.EASY, MissionPools.STARTER)
+ move_mission(SC2Mission.DOMINATION, MissionPools.EASY, MissionPools.STARTER)
+ if len(mission_pools[MissionPools.STARTER]) < 2:
+ move_mission(SC2Mission.TEMPLAR_S_RETURN, MissionPools.EASY, MissionPools.STARTER)
+ if len(mission_pools[MissionPools.STARTER]) + len(mission_pools[MissionPools.EASY]) < 2:
+ # Flashpoint needs just a few items at start but competent comp at the end
+ move_mission(SC2Mission.FLASHPOINT, MissionPools.HARD, MissionPools.EASY)
+
+ remove_final_mission_from_other_pools(mission_pools)
+ return mission_pools
+
+
+def remove_final_mission_from_other_pools(mission_pools: Dict[MissionPools, List[SC2Mission]]):
+ final_missions = mission_pools[MissionPools.FINAL]
+ for pool, missions in mission_pools.items():
+ if pool == MissionPools.FINAL:
+ continue
+ for final_mission in final_missions:
+ while final_mission in missions:
+ missions.remove(final_mission)
+
+
+def get_item_upgrades(inventory: List[Item], parent_item: Union[Item, str]) -> List[Item]:
+ item_name = parent_item.name if isinstance(parent_item, Item) else parent_item
+ return [
+ inv_item for inv_item in inventory
+ if get_full_item_list()[inv_item.name].parent_item == item_name
+ ]
+
+
+def get_item_quantity(item: Item, world: World):
+ if (not get_option_value(world, "nco_items")) \
+ and SC2Campaign.NCO in get_disabled_campaigns(world) \
+ and item.name in progressive_if_nco:
+ return 1
+ if (not get_option_value(world, "ext_items")) \
+ and item.name in progressive_if_ext:
+ return 1
+ return get_full_item_list()[item.name].quantity
+
+
+def copy_item(item: Item):
+ return Item(item.name, item.classification, item.code, item.player)
+
+
+def num_missions(world: World) -> int:
+ mission_order_type = get_option_value(world, "mission_order")
+ if mission_order_type != MissionOrder.option_grid:
+ mission_order = mission_orders[mission_order_type]()
+ misssions = [mission for campaign in mission_order for mission in mission_order[campaign]]
+ return len(misssions) - 1 # Menu
+ else:
+ mission_pools = filter_missions(world)
+ return sum(len(pool) for _, pool in mission_pools.items())
+
+
+class ValidInventory:
+
+ def has(self, item: str, player: int):
+ return item in self.logical_inventory
+
+ def has_any(self, items: Set[str], player: int):
+ return any(item in self.logical_inventory for item in items)
+
+ def has_all(self, items: Set[str], player: int):
+ return all(item in self.logical_inventory for item in items)
+
+ def has_group(self, item_group: str, player: int, count: int = 1):
+ return False # Deliberately fails here, as item pooling is not aware about mission layout
+
+ def count_group(self, item_name_group: str, player: int) -> int:
+ return 0 # For item filtering assume no missions are beaten
+
+ def count(self, item: str, player: int) -> int:
+ return len([inventory_item for inventory_item in self.logical_inventory if inventory_item == item])
+
+ def has_units_per_structure(self) -> bool:
+ return len(BARRACKS_UNITS.intersection(self.logical_inventory)) > self.min_units_per_structure and \
+ len(FACTORY_UNITS.intersection(self.logical_inventory)) > self.min_units_per_structure and \
+ len(STARPORT_UNITS.intersection(self.logical_inventory)) > self.min_units_per_structure
+
+ def generate_reduced_inventory(self, inventory_size: int, mission_requirements: List[Tuple[str, Callable]]) -> List[Item]:
+ """Attempts to generate a reduced inventory that can fulfill the mission requirements."""
+ inventory = list(self.item_pool)
+ locked_items = list(self.locked_items)
+ item_list = get_full_item_list()
+ self.logical_inventory = [
+ item.name for item in inventory + locked_items + self.existing_items
+ if item_list[item.name].is_important_for_filtering() # Track all Progression items and those with complex rules for filtering
+ ]
+ requirements = mission_requirements
+ parent_items = self.item_children.keys()
+ parent_lookup = {child: parent for parent, children in self.item_children.items() for child in children}
+ minimum_upgrades = get_option_value(self.world, "min_number_of_upgrades")
+
+ def attempt_removal(item: Item) -> bool:
+ inventory.remove(item)
+ # Only run logic checks when removing logic items
+ if item.name in self.logical_inventory:
+ self.logical_inventory.remove(item.name)
+ if not all(requirement(self) for (_, requirement) in mission_requirements):
+ # If item cannot be removed, lock or revert
+ self.logical_inventory.append(item.name)
+ for _ in range(get_item_quantity(item, self.world)):
+ locked_items.append(copy_item(item))
+ return False
+ return True
+
+ # Limit the maximum number of upgrades
+ maxNbUpgrade = get_option_value(self.world, "max_number_of_upgrades")
+ if maxNbUpgrade != -1:
+ unit_avail_upgrades = {}
+ # Needed to take into account locked/existing items
+ unit_nb_upgrades = {}
+ for item in inventory:
+ cItem = item_list[item.name]
+ if item.name in UPGRADABLE_ITEMS and item.name not in unit_avail_upgrades:
+ unit_avail_upgrades[item.name] = []
+ unit_nb_upgrades[item.name] = 0
+ elif cItem.parent_item is not None:
+ if cItem.parent_item not in unit_avail_upgrades:
+ unit_avail_upgrades[cItem.parent_item] = [item]
+ unit_nb_upgrades[cItem.parent_item] = 1
+ else:
+ unit_avail_upgrades[cItem.parent_item].append(item)
+ unit_nb_upgrades[cItem.parent_item] += 1
+ # For those two categories, we count them but dont include them in removal
+ for item in locked_items + self.existing_items:
+ cItem = item_list[item.name]
+ if item.name in UPGRADABLE_ITEMS and item.name not in unit_avail_upgrades:
+ unit_avail_upgrades[item.name] = []
+ unit_nb_upgrades[item.name] = 0
+ elif cItem.parent_item is not None:
+ if cItem.parent_item not in unit_avail_upgrades:
+ unit_nb_upgrades[cItem.parent_item] = 1
+ else:
+ unit_nb_upgrades[cItem.parent_item] += 1
+ # Making sure that the upgrades being removed is random
+ shuffled_unit_upgrade_list = list(unit_avail_upgrades.keys())
+ self.world.random.shuffle(shuffled_unit_upgrade_list)
+ for unit in shuffled_unit_upgrade_list:
+ while (unit_nb_upgrades[unit] > maxNbUpgrade) \
+ and (len(unit_avail_upgrades[unit]) > 0):
+ itemCandidate = self.world.random.choice(unit_avail_upgrades[unit])
+ success = attempt_removal(itemCandidate)
+ # Whatever it succeed to remove the iventory or it fails and thus
+ # lock it, the upgrade is no longer available for removal
+ unit_avail_upgrades[unit].remove(itemCandidate)
+ if success:
+ unit_nb_upgrades[unit] -= 1
+
+ # Locking minimum upgrades for items that have already been locked/placed when minimum required
+ if minimum_upgrades > 0:
+ known_items = self.existing_items + locked_items
+ known_parents = [item for item in known_items if item in parent_items]
+ for parent in known_parents:
+ child_items = self.item_children[parent]
+ removable_upgrades = [item for item in inventory if item in child_items]
+ locked_upgrade_count = sum(1 if item in child_items else 0 for item in known_items)
+ self.world.random.shuffle(removable_upgrades)
+ while len(removable_upgrades) > 0 and locked_upgrade_count < minimum_upgrades:
+ item_to_lock = removable_upgrades.pop()
+ inventory.remove(item_to_lock)
+ locked_items.append(copy_item(item_to_lock))
+ locked_upgrade_count += 1
+
+ if self.min_units_per_structure > 0 and self.has_units_per_structure():
+ requirements.append(("Minimum units per structure", lambda state: state.has_units_per_structure()))
+
+ # Determining if the full-size inventory can complete campaign
+ failed_locations: List[str] = [location for (location, requirement) in requirements if not requirement(self)]
+ if len(failed_locations) > 0:
+ raise Exception(f"Too many items excluded - couldn't satisfy access rules for the following locations:\n{failed_locations}")
+
+ # Optionally locking generic items
+ generic_items = [item for item in inventory if item.name in second_pass_placeable_items]
+ reserved_generic_percent = get_option_value(self.world, "ensure_generic_items") / 100
+ reserved_generic_amount = int(len(generic_items) * reserved_generic_percent)
+ removable_generic_items = []
+ self.world.random.shuffle(generic_items)
+ for item in generic_items[:reserved_generic_amount]:
+ locked_items.append(copy_item(item))
+ inventory.remove(item)
+ if item.name not in self.logical_inventory and item.name not in self.locked_items:
+ removable_generic_items.append(item)
+
+ # Main cull process
+ unused_items = [] # Reusable items for the second pass
+ while len(inventory) + len(locked_items) > inventory_size:
+ if len(inventory) == 0:
+ # There are more items than locations and all of them are already locked due to YAML or logic.
+ # First, drop non-logic generic items to free up space
+ while len(removable_generic_items) > 0 and len(locked_items) > inventory_size:
+ removed_item = removable_generic_items.pop()
+ locked_items.remove(removed_item)
+ # If there still isn't enough space, push locked items into start inventory
+ self.world.random.shuffle(locked_items)
+ while len(locked_items) > inventory_size:
+ item: Item = locked_items.pop()
+ self.multiworld.push_precollected(item)
+ break
+ # Select random item from removable items
+ item = self.world.random.choice(inventory)
+ # Do not remove item if it would drop upgrades below minimum
+ if minimum_upgrades > 0:
+ parent_item = parent_lookup.get(item, None)
+ if parent_item:
+ count = sum(1 if item in self.item_children[parent_item] else 0 for item in inventory + locked_items)
+ if count <= minimum_upgrades:
+ if parent_item in inventory:
+ # Attempt to remove parent instead, if possible
+ item = parent_item
+ else:
+ # Lock remaining upgrades
+ for item in self.item_children[parent_item]:
+ if item in inventory:
+ inventory.remove(item)
+ locked_items.append(copy_item(item))
+ continue
+
+ # Drop child items when removing a parent
+ if item in parent_items:
+ items_to_remove = [item for item in self.item_children[item] if item in inventory]
+ success = attempt_removal(item)
+ if success:
+ while len(items_to_remove) > 0:
+ item_to_remove = items_to_remove.pop()
+ if item_to_remove not in inventory:
+ continue
+ attempt_removal(item_to_remove)
+ else:
+ # Unimportant upgrades may be added again in the second pass
+ if attempt_removal(item):
+ unused_items.append(item.name)
+
+ # Removing extra dependencies
+ # WoL
+ logical_inventory_set = set(self.logical_inventory)
+ if not spider_mine_sources & logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.endswith("(Spider Mine)")]
+ if not BARRACKS_UNITS & logical_inventory_set:
+ inventory = [item for item in inventory if
+ not (item.name.startswith(ItemNames.TERRAN_INFANTRY_UPGRADE_PREFIX) or item.name == ItemNames.ORBITAL_STRIKE)]
+ if not FACTORY_UNITS & logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.startswith(ItemNames.TERRAN_VEHICLE_UPGRADE_PREFIX)]
+ if not STARPORT_UNITS & logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.startswith(ItemNames.TERRAN_SHIP_UPGRADE_PREFIX)]
+ # HotS
+ # Baneling without sources => remove Baneling and upgrades
+ if (ItemNames.ZERGLING_BANELING_ASPECT in self.logical_inventory
+ and ItemNames.ZERGLING not in self.logical_inventory
+ and ItemNames.KERRIGAN_SPAWN_BANELINGS not in self.logical_inventory
+ ):
+ inventory = [item for item in inventory if item.name != ItemNames.ZERGLING_BANELING_ASPECT]
+ inventory = [item for item in inventory if item_list[item.name].parent_item != ItemNames.ZERGLING_BANELING_ASPECT]
+ # Spawn Banelings without Zergling => remove Baneling unit, keep upgrades except macro ones
+ if (ItemNames.ZERGLING_BANELING_ASPECT in self.logical_inventory
+ and ItemNames.ZERGLING not in self.logical_inventory
+ and ItemNames.KERRIGAN_SPAWN_BANELINGS in self.logical_inventory
+ ):
+ inventory = [item for item in inventory if item.name != ItemNames.ZERGLING_BANELING_ASPECT]
+ inventory = [item for item in inventory if item.name != ItemNames.BANELING_RAPID_METAMORPH]
+ if not {ItemNames.MUTALISK, ItemNames.CORRUPTOR, ItemNames.SCOURGE} & logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.startswith(ItemNames.ZERG_FLYER_UPGRADE_PREFIX)]
+ locked_items = [item for item in locked_items if not item.name.startswith(ItemNames.ZERG_FLYER_UPGRADE_PREFIX)]
+ # T3 items removal rules - remove morph and its upgrades if the basic unit isn't in
+ if not {ItemNames.MUTALISK, ItemNames.CORRUPTOR} & logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.endswith("(Mutalisk/Corruptor)")]
+ inventory = [item for item in inventory if item_list[item.name].parent_item != ItemNames.MUTALISK_CORRUPTOR_GUARDIAN_ASPECT]
+ inventory = [item for item in inventory if item_list[item.name].parent_item != ItemNames.MUTALISK_CORRUPTOR_DEVOURER_ASPECT]
+ inventory = [item for item in inventory if item_list[item.name].parent_item != ItemNames.MUTALISK_CORRUPTOR_BROOD_LORD_ASPECT]
+ inventory = [item for item in inventory if item_list[item.name].parent_item != ItemNames.MUTALISK_CORRUPTOR_VIPER_ASPECT]
+ if ItemNames.ROACH not in logical_inventory_set:
+ inventory = [item for item in inventory if item.name != ItemNames.ROACH_RAVAGER_ASPECT]
+ inventory = [item for item in inventory if item_list[item.name].parent_item != ItemNames.ROACH_RAVAGER_ASPECT]
+ if ItemNames.HYDRALISK not in logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.endswith("(Hydralisk)")]
+ inventory = [item for item in inventory if item_list[item.name].parent_item != ItemNames.HYDRALISK_LURKER_ASPECT]
+ inventory = [item for item in inventory if item_list[item.name].parent_item != ItemNames.HYDRALISK_IMPALER_ASPECT]
+ # LotV
+ # Shared unit upgrades between several units
+ if not {ItemNames.STALKER, ItemNames.INSTIGATOR, ItemNames.SLAYER} & logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.endswith("(Stalker/Instigator/Slayer)")]
+ if not {ItemNames.PHOENIX, ItemNames.MIRAGE} & logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.endswith("(Phoenix/Mirage)")]
+ if not {ItemNames.VOID_RAY, ItemNames.DESTROYER} & logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.endswith("(Void Ray/Destroyer)")]
+ if not {ItemNames.IMMORTAL, ItemNames.ANNIHILATOR} & logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.endswith("(Immortal/Annihilator)")]
+ if not {ItemNames.DARK_TEMPLAR, ItemNames.AVENGER, ItemNames.BLOOD_HUNTER} & logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.endswith("(Dark Templar/Avenger/Blood Hunter)")]
+ if not {ItemNames.HIGH_TEMPLAR, ItemNames.SIGNIFIER, ItemNames.ASCENDANT, ItemNames.DARK_TEMPLAR} & logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.endswith("(Archon)")]
+ logical_inventory_set.difference_update([item_name for item_name in logical_inventory_set if item_name.endswith("(Archon)")])
+ if not {ItemNames.HIGH_TEMPLAR, ItemNames.SIGNIFIER, ItemNames.ARCHON_HIGH_ARCHON} & logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.endswith("(High Templar/Signifier)")]
+ if ItemNames.SUPPLICANT not in logical_inventory_set:
+ inventory = [item for item in inventory if item.name != ItemNames.ASCENDANT_POWER_OVERWHELMING]
+ if not {ItemNames.DARK_ARCHON, ItemNames.DARK_TEMPLAR_DARK_ARCHON_MELD} & logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.endswith("(Dark Archon)")]
+ if not {ItemNames.SENTRY, ItemNames.ENERGIZER, ItemNames.HAVOC} & logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.endswith("(Sentry/Energizer/Havoc)")]
+ if not {ItemNames.SENTRY, ItemNames.ENERGIZER, ItemNames.HAVOC, ItemNames.SHIELD_BATTERY} & logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.endswith("(Sentry/Energizer/Havoc/Shield Battery)")]
+ if not {ItemNames.ZEALOT, ItemNames.CENTURION, ItemNames.SENTINEL} & logical_inventory_set:
+ inventory = [item for item in inventory if not item.name.endswith("(Zealot/Sentinel/Centurion)")]
+ # Static defense upgrades only if static defense present
+ if not {ItemNames.PHOTON_CANNON, ItemNames.KHAYDARIN_MONOLITH, ItemNames.NEXUS_OVERCHARGE, ItemNames.SHIELD_BATTERY} & logical_inventory_set:
+ inventory = [item for item in inventory if item.name != ItemNames.ENHANCED_TARGETING]
+ if not {ItemNames.PHOTON_CANNON, ItemNames.KHAYDARIN_MONOLITH, ItemNames.NEXUS_OVERCHARGE} & logical_inventory_set:
+ inventory = [item for item in inventory if item.name != ItemNames.OPTIMIZED_ORDNANCE]
+
+ # Cull finished, adding locked items back into inventory
+ inventory += locked_items
+
+ # Replacing empty space with generically useful items
+ replacement_items = [item for item in self.item_pool
+ if (item not in inventory
+ and item not in self.locked_items
+ and (
+ item.name in second_pass_placeable_items
+ or item.name in unused_items))]
+ self.world.random.shuffle(replacement_items)
+ while len(inventory) < inventory_size and len(replacement_items) > 0:
+ item = replacement_items.pop()
+ inventory.append(item)
+
+ return inventory
+
+ def __init__(self, world: World ,
+ item_pool: List[Item], existing_items: List[Item], locked_items: List[Item],
+ used_races: Set[SC2Race], nova_equipment_used: bool):
+ self.multiworld = world.multiworld
+ self.player = world.player
+ self.world: World = world
+ self.logical_inventory = list()
+ self.locked_items = locked_items[:]
+ self.existing_items = existing_items
+ soa_presence = get_option_value(world, "spear_of_adun_presence")
+ soa_autocast_presence = get_option_value(world, "spear_of_adun_autonomously_cast_ability_presence")
+ # Initial filter of item pool
+ self.item_pool = []
+ item_quantities: dict[str, int] = dict()
+ # Inventory restrictiveness based on number of missions with checks
+ mission_count = num_missions(world)
+ self.min_units_per_structure = int(mission_count / 7)
+ min_upgrades = 1 if mission_count < 10 else 2
+ for item in item_pool:
+ item_info = get_full_item_list()[item.name]
+ if item_info.race != SC2Race.ANY and item_info.race not in used_races:
+ if soa_presence == SpearOfAdunPresence.option_everywhere \
+ and item.name in spear_of_adun_calldowns:
+ # Add SoA powers regardless of used races as it's present everywhere
+ self.item_pool.append(item)
+ if soa_autocast_presence == SpearOfAdunAutonomouslyCastAbilityPresence.option_everywhere \
+ and item.name in spear_of_adun_castable_passives:
+ self.item_pool.append(item)
+ # Drop any item belonging to a race not used in the campaign
+ continue
+ if item.name in nova_equipment and not nova_equipment_used:
+ # Drop Nova equipment if there's no NCO mission generated
+ continue
+ if item_info.type == "Upgrade":
+ # Locking upgrades based on mission duration
+ if item.name not in item_quantities:
+ item_quantities[item.name] = 0
+ item_quantities[item.name] += 1
+ if item_quantities[item.name] <= min_upgrades:
+ self.locked_items.append(item)
+ else:
+ self.item_pool.append(item)
+ elif item_info.type == "Goal":
+ self.locked_items.append(item)
+ else:
+ self.item_pool.append(item)
+ self.item_children: Dict[Item, List[Item]] = dict()
+ for item in self.item_pool + locked_items + existing_items:
+ if item.name in UPGRADABLE_ITEMS:
+ self.item_children[item] = get_item_upgrades(self.item_pool, item)
+
+
+def filter_items(world: World, mission_req_table: Dict[SC2Campaign, Dict[str, MissionInfo]], location_cache: List[Location],
+ item_pool: List[Item], existing_items: List[Item], locked_items: List[Item]) -> List[Item]:
+ """
+ Returns a semi-randomly pruned set of items based on number of available locations.
+ The returned inventory must be capable of logically accessing every location in the world.
+ """
+ open_locations = [location for location in location_cache if location.item is None]
+ inventory_size = len(open_locations)
+ used_races = get_used_races(mission_req_table, world)
+ nova_equipment_used = is_nova_equipment_used(mission_req_table)
+ mission_requirements = [(location.name, location.access_rule) for location in location_cache]
+ valid_inventory = ValidInventory(world, item_pool, existing_items, locked_items, used_races, nova_equipment_used)
+
+ valid_items = valid_inventory.generate_reduced_inventory(inventory_size, mission_requirements)
+ return valid_items
+
+
+def get_used_races(mission_req_table: Dict[SC2Campaign, Dict[str, MissionInfo]], world: World) -> Set[SC2Race]:
+ grant_story_tech = get_option_value(world, "grant_story_tech")
+ take_over_ai_allies = get_option_value(world, "take_over_ai_allies")
+ kerrigan_presence = get_option_value(world, "kerrigan_presence") in kerrigan_unit_available \
+ and SC2Campaign.HOTS in get_enabled_campaigns(world)
+ missions = missions_in_mission_table(mission_req_table)
+
+ # By missions
+ races = set([mission.race for mission in missions])
+
+ # Conditionally logic-less no-builds (They're set to SC2Race.ANY):
+ if grant_story_tech == GrantStoryTech.option_false:
+ if SC2Mission.ENEMY_WITHIN in missions:
+ # Zerg units need to be unlocked
+ races.add(SC2Race.ZERG)
+ if kerrigan_presence \
+ and not missions.isdisjoint({SC2Mission.BACK_IN_THE_SADDLE, SC2Mission.SUPREME, SC2Mission.CONVICTION, SC2Mission.THE_INFINITE_CYCLE}):
+ # You need some Kerrigan abilities (they're granted if Kerriganless or story tech granted)
+ races.add(SC2Race.ZERG)
+
+ # If you take over the AI Ally, you need to have its race stuff
+ if take_over_ai_allies == TakeOverAIAllies.option_true \
+ and not missions.isdisjoint({SC2Mission.THE_RECKONING}):
+ # Jimmy in The Reckoning
+ races.add(SC2Race.TERRAN)
+
+ return races
+
+def is_nova_equipment_used(mission_req_table: Dict[SC2Campaign, Dict[str, MissionInfo]]) -> bool:
+ missions = missions_in_mission_table(mission_req_table)
+ return any([mission.campaign == SC2Campaign.NCO for mission in missions])
+
+
+def missions_in_mission_table(mission_req_table: Dict[SC2Campaign, Dict[str, MissionInfo]]) -> Set[SC2Mission]:
+ return set([mission.mission for campaign_missions in mission_req_table.values() for mission in
+ campaign_missions.values()])
diff --git a/worlds/sc2/Regions.py b/worlds/sc2/Regions.py
new file mode 100644
index 000000000000..e6c001b186a7
--- /dev/null
+++ b/worlds/sc2/Regions.py
@@ -0,0 +1,691 @@
+from typing import List, Dict, Tuple, Optional, Callable, NamedTuple, Union
+import math
+
+from BaseClasses import MultiWorld, Region, Entrance, Location, CollectionState
+from .Locations import LocationData
+from .Options import get_option_value, MissionOrder, get_enabled_campaigns, campaign_depending_orders, \
+ GridTwoStartPositions
+from .MissionTables import MissionInfo, mission_orders, vanilla_mission_req_table, \
+ MissionPools, SC2Campaign, get_goal_location, SC2Mission, MissionConnection
+from .PoolFilter import filter_missions
+from worlds.AutoWorld import World
+
+
+class SC2MissionSlot(NamedTuple):
+ campaign: SC2Campaign
+ slot: Union[MissionPools, SC2Mission, None]
+
+
+def create_regions(
+ world: World, locations: Tuple[LocationData, ...], location_cache: List[Location]
+) -> Tuple[Dict[SC2Campaign, Dict[str, MissionInfo]], int, str]:
+ """
+ Creates region connections by calling the multiworld's `connect()` methods
+ Returns a 3-tuple containing:
+ * dict[SC2Campaign, Dict[str, MissionInfo]] mapping a campaign and mission name to its data
+ * int The number of missions in the world
+ * str The name of the goal location
+ """
+ mission_order_type: int = get_option_value(world, "mission_order")
+
+ if mission_order_type == MissionOrder.option_vanilla:
+ return create_vanilla_regions(world, locations, location_cache)
+ elif mission_order_type == MissionOrder.option_grid:
+ return create_grid_regions(world, locations, location_cache)
+ else:
+ return create_structured_regions(world, locations, location_cache, mission_order_type)
+
+def create_vanilla_regions(
+ world: World,
+ locations: Tuple[LocationData, ...],
+ location_cache: List[Location],
+) -> Tuple[Dict[SC2Campaign, Dict[str, MissionInfo]], int, str]:
+ locations_per_region = get_locations_per_region(locations)
+ regions = [create_region(world, locations_per_region, location_cache, "Menu")]
+
+ mission_pools: Dict[MissionPools, List[SC2Mission]] = filter_missions(world)
+ final_mission = mission_pools[MissionPools.FINAL][0]
+
+ enabled_campaigns = get_enabled_campaigns(world)
+ names: Dict[str, int] = {}
+
+ # Generating all regions and locations for each enabled campaign
+ for campaign in enabled_campaigns:
+ for region_name in vanilla_mission_req_table[campaign].keys():
+ regions.append(create_region(world, locations_per_region, location_cache, region_name))
+ world.multiworld.regions += regions
+ vanilla_mission_reqs = {campaign: missions for campaign, missions in vanilla_mission_req_table.items() if campaign in enabled_campaigns}
+
+ def wol_cleared_missions(state: CollectionState, mission_count: int) -> bool:
+ return state.has_group("WoL Missions", world.player, mission_count)
+
+ player: int = world.player
+ if SC2Campaign.WOL in enabled_campaigns:
+ connect(world, names, 'Menu', 'Liberation Day')
+ connect(world, names, 'Liberation Day', 'The Outlaws',
+ lambda state: state.has("Beat Liberation Day", player))
+ connect(world, names, 'The Outlaws', 'Zero Hour',
+ lambda state: state.has("Beat The Outlaws", player))
+ connect(world, names, 'Zero Hour', 'Evacuation',
+ lambda state: state.has("Beat Zero Hour", player))
+ connect(world, names, 'Evacuation', 'Outbreak',
+ lambda state: state.has("Beat Evacuation", player))
+ connect(world, names, "Outbreak", "Safe Haven",
+ lambda state: wol_cleared_missions(state, 7) and state.has("Beat Outbreak", player))
+ connect(world, names, "Outbreak", "Haven's Fall",
+ lambda state: wol_cleared_missions(state, 7) and state.has("Beat Outbreak", player))
+ connect(world, names, 'Zero Hour', 'Smash and Grab',
+ lambda state: state.has("Beat Zero Hour", player))
+ connect(world, names, 'Smash and Grab', 'The Dig',
+ lambda state: wol_cleared_missions(state, 8) and state.has("Beat Smash and Grab", player))
+ connect(world, names, 'The Dig', 'The Moebius Factor',
+ lambda state: wol_cleared_missions(state, 11) and state.has("Beat The Dig", player))
+ connect(world, names, 'The Moebius Factor', 'Supernova',
+ lambda state: wol_cleared_missions(state, 14) and state.has("Beat The Moebius Factor", player))
+ connect(world, names, 'Supernova', 'Maw of the Void',
+ lambda state: state.has("Beat Supernova", player))
+ connect(world, names, 'Zero Hour', "Devil's Playground",
+ lambda state: wol_cleared_missions(state, 4) and state.has("Beat Zero Hour", player))
+ connect(world, names, "Devil's Playground", 'Welcome to the Jungle',
+ lambda state: state.has("Beat Devil's Playground", player))
+ connect(world, names, "Welcome to the Jungle", 'Breakout',
+ lambda state: wol_cleared_missions(state, 8) and state.has("Beat Welcome to the Jungle", player))
+ connect(world, names, "Welcome to the Jungle", 'Ghost of a Chance',
+ lambda state: wol_cleared_missions(state, 8) and state.has("Beat Welcome to the Jungle", player))
+ connect(world, names, "Zero Hour", 'The Great Train Robbery',
+ lambda state: wol_cleared_missions(state, 6) and state.has("Beat Zero Hour", player))
+ connect(world, names, 'The Great Train Robbery', 'Cutthroat',
+ lambda state: state.has("Beat The Great Train Robbery", player))
+ connect(world, names, 'Cutthroat', 'Engine of Destruction',
+ lambda state: state.has("Beat Cutthroat", player))
+ connect(world, names, 'Engine of Destruction', 'Media Blitz',
+ lambda state: state.has("Beat Engine of Destruction", player))
+ connect(world, names, 'Media Blitz', 'Piercing the Shroud',
+ lambda state: state.has("Beat Media Blitz", player))
+ connect(world, names, 'Maw of the Void', 'Gates of Hell',
+ lambda state: state.has("Beat Maw of the Void", player))
+ connect(world, names, 'Gates of Hell', 'Belly of the Beast',
+ lambda state: state.has("Beat Gates of Hell", player))
+ connect(world, names, 'Gates of Hell', 'Shatter the Sky',
+ lambda state: state.has("Beat Gates of Hell", player))
+ connect(world, names, 'Gates of Hell', 'All-In',
+ lambda state: state.has('Beat Gates of Hell', player) and (
+ state.has('Beat Shatter the Sky', player) or state.has('Beat Belly of the Beast', player)))
+
+ if SC2Campaign.PROPHECY in enabled_campaigns:
+ if SC2Campaign.WOL in enabled_campaigns:
+ connect(world, names, 'The Dig', 'Whispers of Doom',
+ lambda state: state.has("Beat The Dig", player)),
+ else:
+ vanilla_mission_reqs[SC2Campaign.PROPHECY] = vanilla_mission_reqs[SC2Campaign.PROPHECY].copy()
+ vanilla_mission_reqs[SC2Campaign.PROPHECY][SC2Mission.WHISPERS_OF_DOOM.mission_name] = MissionInfo(
+ SC2Mission.WHISPERS_OF_DOOM, [], SC2Mission.WHISPERS_OF_DOOM.area)
+ connect(world, names, 'Menu', 'Whispers of Doom'),
+ connect(world, names, 'Whispers of Doom', 'A Sinister Turn',
+ lambda state: state.has("Beat Whispers of Doom", player))
+ connect(world, names, 'A Sinister Turn', 'Echoes of the Future',
+ lambda state: state.has("Beat A Sinister Turn", player))
+ connect(world, names, 'Echoes of the Future', 'In Utter Darkness',
+ lambda state: state.has("Beat Echoes of the Future", player))
+
+ if SC2Campaign.HOTS in enabled_campaigns:
+ connect(world, names, 'Menu', 'Lab Rat'),
+ connect(world, names, 'Lab Rat', 'Back in the Saddle',
+ lambda state: state.has("Beat Lab Rat", player)),
+ connect(world, names, 'Back in the Saddle', 'Rendezvous',
+ lambda state: state.has("Beat Back in the Saddle", player)),
+ connect(world, names, 'Rendezvous', 'Harvest of Screams',
+ lambda state: state.has("Beat Rendezvous", player)),
+ connect(world, names, 'Harvest of Screams', 'Shoot the Messenger',
+ lambda state: state.has("Beat Harvest of Screams", player)),
+ connect(world, names, 'Shoot the Messenger', 'Enemy Within',
+ lambda state: state.has("Beat Shoot the Messenger", player)),
+ connect(world, names, 'Rendezvous', 'Domination',
+ lambda state: state.has("Beat Rendezvous", player)),
+ connect(world, names, 'Domination', 'Fire in the Sky',
+ lambda state: state.has("Beat Domination", player)),
+ connect(world, names, 'Fire in the Sky', 'Old Soldiers',
+ lambda state: state.has("Beat Fire in the Sky", player)),
+ connect(world, names, 'Old Soldiers', 'Waking the Ancient',
+ lambda state: state.has("Beat Old Soldiers", player)),
+ connect(world, names, 'Enemy Within', 'Waking the Ancient',
+ lambda state: state.has("Beat Enemy Within", player)),
+ connect(world, names, 'Waking the Ancient', 'The Crucible',
+ lambda state: state.has("Beat Waking the Ancient", player)),
+ connect(world, names, 'The Crucible', 'Supreme',
+ lambda state: state.has("Beat The Crucible", player)),
+ connect(world, names, 'Supreme', 'Infested',
+ lambda state: state.has("Beat Supreme", player) and
+ state.has("Beat Old Soldiers", player) and
+ state.has("Beat Enemy Within", player)),
+ connect(world, names, 'Infested', 'Hand of Darkness',
+ lambda state: state.has("Beat Infested", player)),
+ connect(world, names, 'Hand of Darkness', 'Phantoms of the Void',
+ lambda state: state.has("Beat Hand of Darkness", player)),
+ connect(world, names, 'Supreme', 'With Friends Like These',
+ lambda state: state.has("Beat Supreme", player) and
+ state.has("Beat Old Soldiers", player) and
+ state.has("Beat Enemy Within", player)),
+ connect(world, names, 'With Friends Like These', 'Conviction',
+ lambda state: state.has("Beat With Friends Like These", player)),
+ connect(world, names, 'Conviction', 'Planetfall',
+ lambda state: state.has("Beat Conviction", player) and
+ state.has("Beat Phantoms of the Void", player)),
+ connect(world, names, 'Planetfall', 'Death From Above',
+ lambda state: state.has("Beat Planetfall", player)),
+ connect(world, names, 'Death From Above', 'The Reckoning',
+ lambda state: state.has("Beat Death From Above", player)),
+
+ if SC2Campaign.PROLOGUE in enabled_campaigns:
+ connect(world, names, "Menu", "Dark Whispers")
+ connect(world, names, "Dark Whispers", "Ghosts in the Fog",
+ lambda state: state.has("Beat Dark Whispers", player))
+ connect(world, names, "Dark Whispers", "Evil Awoken",
+ lambda state: state.has("Beat Ghosts in the Fog", player))
+
+ if SC2Campaign.LOTV in enabled_campaigns:
+ connect(world, names, "Menu", "For Aiur!")
+ connect(world, names, "For Aiur!", "The Growing Shadow",
+ lambda state: state.has("Beat For Aiur!", player)),
+ connect(world, names, "The Growing Shadow", "The Spear of Adun",
+ lambda state: state.has("Beat The Growing Shadow", player)),
+ connect(world, names, "The Spear of Adun", "Sky Shield",
+ lambda state: state.has("Beat The Spear of Adun", player)),
+ connect(world, names, "Sky Shield", "Brothers in Arms",
+ lambda state: state.has("Beat Sky Shield", player)),
+ connect(world, names, "Brothers in Arms", "Forbidden Weapon",
+ lambda state: state.has("Beat Brothers in Arms", player)),
+ connect(world, names, "The Spear of Adun", "Amon's Reach",
+ lambda state: state.has("Beat The Spear of Adun", player)),
+ connect(world, names, "Amon's Reach", "Last Stand",
+ lambda state: state.has("Beat Amon's Reach", player)),
+ connect(world, names, "Last Stand", "Forbidden Weapon",
+ lambda state: state.has("Beat Last Stand", player)),
+ connect(world, names, "Forbidden Weapon", "Temple of Unification",
+ lambda state: state.has("Beat Brothers in Arms", player)
+ and state.has("Beat Last Stand", player)
+ and state.has("Beat Forbidden Weapon", player)),
+ connect(world, names, "Temple of Unification", "The Infinite Cycle",
+ lambda state: state.has("Beat Temple of Unification", player)),
+ connect(world, names, "The Infinite Cycle", "Harbinger of Oblivion",
+ lambda state: state.has("Beat The Infinite Cycle", player)),
+ connect(world, names, "Harbinger of Oblivion", "Unsealing the Past",
+ lambda state: state.has("Beat Harbinger of Oblivion", player)),
+ connect(world, names, "Unsealing the Past", "Purification",
+ lambda state: state.has("Beat Unsealing the Past", player)),
+ connect(world, names, "Purification", "Templar's Charge",
+ lambda state: state.has("Beat Purification", player)),
+ connect(world, names, "Harbinger of Oblivion", "Steps of the Rite",
+ lambda state: state.has("Beat Harbinger of Oblivion", player)),
+ connect(world, names, "Steps of the Rite", "Rak'Shir",
+ lambda state: state.has("Beat Steps of the Rite", player)),
+ connect(world, names, "Rak'Shir", "Templar's Charge",
+ lambda state: state.has("Beat Rak'Shir", player)),
+ connect(world, names, "Templar's Charge", "Templar's Return",
+ lambda state: state.has("Beat Purification", player)
+ and state.has("Beat Rak'Shir", player)
+ and state.has("Beat Templar's Charge", player)),
+ connect(world, names, "Templar's Return", "The Host",
+ lambda state: state.has("Beat Templar's Return", player)),
+ connect(world, names, "The Host", "Salvation",
+ lambda state: state.has("Beat The Host", player)),
+
+ if SC2Campaign.EPILOGUE in enabled_campaigns:
+ # TODO: Make this aware about excluded campaigns
+ connect(world, names, "Salvation", "Into the Void",
+ lambda state: state.has("Beat Salvation", player)
+ and state.has("Beat The Reckoning", player)
+ and state.has("Beat All-In", player)),
+ connect(world, names, "Into the Void", "The Essence of Eternity",
+ lambda state: state.has("Beat Into the Void", player)),
+ connect(world, names, "The Essence of Eternity", "Amon's Fall",
+ lambda state: state.has("Beat The Essence of Eternity", player)),
+
+ if SC2Campaign.NCO in enabled_campaigns:
+ connect(world, names, "Menu", "The Escape")
+ connect(world, names, "The Escape", "Sudden Strike",
+ lambda state: state.has("Beat The Escape", player))
+ connect(world, names, "Sudden Strike", "Enemy Intelligence",
+ lambda state: state.has("Beat Sudden Strike", player))
+ connect(world, names, "Enemy Intelligence", "Trouble In Paradise",
+ lambda state: state.has("Beat Enemy Intelligence", player))
+ connect(world, names, "Trouble In Paradise", "Night Terrors",
+ lambda state: state.has("Beat Evacuation", player))
+ connect(world, names, "Night Terrors", "Flashpoint",
+ lambda state: state.has("Beat Night Terrors", player))
+ connect(world, names, "Flashpoint", "In the Enemy's Shadow",
+ lambda state: state.has("Beat Flashpoint", player))
+ connect(world, names, "In the Enemy's Shadow", "Dark Skies",
+ lambda state: state.has("Beat In the Enemy's Shadow", player))
+ connect(world, names, "Dark Skies", "End Game",
+ lambda state: state.has("Beat Dark Skies", player))
+
+ goal_location = get_goal_location(final_mission)
+ assert goal_location, f"Unable to find a goal location for mission {final_mission}"
+ setup_final_location(goal_location, location_cache)
+
+ return (vanilla_mission_reqs, final_mission.id, goal_location)
+
+
+def create_grid_regions(
+ world: World,
+ locations: Tuple[LocationData, ...],
+ location_cache: List[Location],
+) -> Tuple[Dict[SC2Campaign, Dict[str, MissionInfo]], int, str]:
+ locations_per_region = get_locations_per_region(locations)
+
+ mission_pools = filter_missions(world)
+ final_mission = mission_pools[MissionPools.FINAL][0]
+
+ mission_pool = [mission for mission_pool in mission_pools.values() for mission in mission_pool]
+
+ num_missions = min(len(mission_pool), get_option_value(world, "maximum_campaign_size"))
+ remove_top_left: bool = get_option_value(world, "grid_two_start_positions") == GridTwoStartPositions.option_true
+
+ regions = [create_region(world, locations_per_region, location_cache, "Menu")]
+ names: Dict[str, int] = {}
+ missions: Dict[Tuple[int, int], SC2Mission] = {}
+
+ grid_size_x, grid_size_y, num_corners_to_remove = get_grid_dimensions(num_missions + remove_top_left)
+ # pick missions in order along concentric diagonals
+ # each diagonal will have the same difficulty
+ # this keeps long sides from possibly stealing lower-difficulty missions from future columns
+ num_diagonals = grid_size_x + grid_size_y - 1
+ diagonal_difficulty = MissionPools.STARTER
+ missions_to_add = mission_pools[MissionPools.STARTER]
+ for diagonal in range(num_diagonals):
+ if diagonal == num_diagonals - 1:
+ diagonal_difficulty = MissionPools.FINAL
+ grid_coords = (grid_size_x-1, grid_size_y-1)
+ missions[grid_coords] = final_mission
+ break
+ if diagonal == 0 and remove_top_left:
+ continue
+ diagonal_length = min(diagonal + 1, num_diagonals - diagonal, grid_size_x, grid_size_y)
+ if len(missions_to_add) < diagonal_length:
+ raise Exception(f"There are not enough {diagonal_difficulty.name} missions to fill the campaign. Please exclude fewer missions.")
+ for i in range(diagonal_length):
+ # (0,0) + (0,1)*diagonal + (1,-1)*i + (1,-1)*max(diagonal - grid_size_y + 1, 0)
+ grid_coords = (i + max(diagonal - grid_size_y + 1, 0), diagonal - i - max(diagonal - grid_size_y + 1, 0))
+ if grid_coords == (grid_size_x - 1, 0) and num_corners_to_remove >= 2:
+ pass
+ elif grid_coords == (0, grid_size_y - 1) and num_corners_to_remove >= 1:
+ pass
+ else:
+ mission_index = world.random.randint(0, len(missions_to_add) - 1)
+ missions[grid_coords] = missions_to_add.pop(mission_index)
+
+ if diagonal_difficulty < MissionPools.VERY_HARD:
+ diagonal_difficulty = MissionPools(diagonal_difficulty.value + 1)
+ missions_to_add.extend(mission_pools[diagonal_difficulty])
+
+ # Generating regions and locations from selected missions
+ for x in range(grid_size_x):
+ for y in range(grid_size_y):
+ if missions.get((x, y)):
+ regions.append(create_region(world, locations_per_region, location_cache, missions[(x, y)].mission_name))
+ world.multiworld.regions += regions
+
+ # This pattern is horrifying, why are we using the dict as an ordered dict???
+ slot_map: Dict[Tuple[int, int], int] = {}
+ for index, coords in enumerate(missions):
+ slot_map[coords] = index + 1
+
+ mission_req_table: Dict[str, MissionInfo] = {}
+ for coords, mission in missions.items():
+ prepend_vertical = 0
+ if not mission:
+ continue
+ connections: List[MissionConnection] = []
+ if coords == (0, 0) or (remove_top_left and sum(coords) == 1):
+ # Connect to the "Menu" starting region
+ connect(world, names, "Menu", mission.mission_name)
+ else:
+ for dx, dy in ((-1, 0), (1, 0), (0, -1), (0, 1)):
+ connected_coords = (coords[0] + dx, coords[1] + dy)
+ if connected_coords in missions:
+ # connections.append(missions[connected_coords])
+ connections.append(MissionConnection(slot_map[connected_coords]))
+ connect(world, names, missions[connected_coords].mission_name, mission.mission_name,
+ make_grid_connect_rule(missions, connected_coords, world.player),
+ )
+ if coords[1] == 1 and not missions.get((coords[0], 0)):
+ prepend_vertical = 1
+ mission_req_table[mission.mission_name] = MissionInfo(
+ mission,
+ connections,
+ category=f'_{coords[0] + 1}',
+ or_requirements=True,
+ ui_vertical_padding=prepend_vertical,
+ )
+
+ final_mission_id = final_mission.id
+ # Changing the completion condition for alternate final missions into an event
+ final_location = get_goal_location(final_mission)
+ setup_final_location(final_location, location_cache)
+
+ return {SC2Campaign.GLOBAL: mission_req_table}, final_mission_id, final_location
+
+
+def make_grid_connect_rule(
+ missions: Dict[Tuple[int, int], SC2Mission],
+ connected_coords: Tuple[int, int],
+ player: int
+) -> Callable[[CollectionState], bool]:
+ return lambda state: state.has(f"Beat {missions[connected_coords].mission_name}", player)
+
+
+def create_structured_regions(
+ world: World,
+ locations: Tuple[LocationData, ...],
+ location_cache: List[Location],
+ mission_order_type: int,
+) -> Tuple[Dict[SC2Campaign, Dict[str, MissionInfo]], int, str]:
+ locations_per_region = get_locations_per_region(locations)
+
+ mission_order = mission_orders[mission_order_type]()
+ enabled_campaigns = get_enabled_campaigns(world)
+ shuffle_campaigns = get_option_value(world, "shuffle_campaigns")
+
+ mission_pools: Dict[MissionPools, List[SC2Mission]] = filter_missions(world)
+ final_mission = mission_pools[MissionPools.FINAL][0]
+
+ regions = [create_region(world, locations_per_region, location_cache, "Menu")]
+
+ names: Dict[str, int] = {}
+
+ mission_slots: List[SC2MissionSlot] = []
+ mission_pool = [mission for mission_pool in mission_pools.values() for mission in mission_pool]
+
+ if mission_order_type in campaign_depending_orders:
+ # Do slot removal per campaign
+ for campaign in enabled_campaigns:
+ campaign_mission_pool = [mission for mission in mission_pool if mission.campaign == campaign]
+ campaign_mission_pool_size = len(campaign_mission_pool)
+
+ removals = len(mission_order[campaign]) - campaign_mission_pool_size
+
+ for mission in mission_order[campaign]:
+ # Removing extra missions if mission pool is too small
+ if 0 < mission.removal_priority <= removals:
+ mission_slots.append(SC2MissionSlot(campaign, None))
+ elif mission.type == MissionPools.FINAL:
+ if campaign == final_mission.campaign:
+ # Campaign is elected to be goal
+ mission_slots.append(SC2MissionSlot(campaign, final_mission))
+ else:
+ # Not the goal, find the most difficult mission in the pool and set the difficulty
+ campaign_difficulty = max(mission.pool for mission in campaign_mission_pool)
+ mission_slots.append(SC2MissionSlot(campaign, campaign_difficulty))
+ else:
+ mission_slots.append(SC2MissionSlot(campaign, mission.type))
+ else:
+ order = mission_order[SC2Campaign.GLOBAL]
+ # Determining if missions must be removed
+ mission_pool_size = sum(len(mission_pool) for mission_pool in mission_pools.values())
+ removals = len(order) - mission_pool_size
+
+ # Initial fill out of mission list and marking All-In mission
+ for mission in order:
+ # Removing extra missions if mission pool is too small
+ if 0 < mission.removal_priority <= removals:
+ mission_slots.append(SC2MissionSlot(SC2Campaign.GLOBAL, None))
+ elif mission.type == MissionPools.FINAL:
+ mission_slots.append(SC2MissionSlot(SC2Campaign.GLOBAL, final_mission))
+ else:
+ mission_slots.append(SC2MissionSlot(SC2Campaign.GLOBAL, mission.type))
+
+ no_build_slots = []
+ easy_slots = []
+ medium_slots = []
+ hard_slots = []
+ very_hard_slots = []
+
+ # Search through missions to find slots needed to fill
+ for i in range(len(mission_slots)):
+ mission_slot = mission_slots[i]
+ if mission_slot is None:
+ continue
+ if isinstance(mission_slot, SC2MissionSlot):
+ if mission_slot.slot is None:
+ continue
+ if mission_slot.slot == MissionPools.STARTER:
+ no_build_slots.append(i)
+ elif mission_slot.slot == MissionPools.EASY:
+ easy_slots.append(i)
+ elif mission_slot.slot == MissionPools.MEDIUM:
+ medium_slots.append(i)
+ elif mission_slot.slot == MissionPools.HARD:
+ hard_slots.append(i)
+ elif mission_slot.slot == MissionPools.VERY_HARD:
+ very_hard_slots.append(i)
+
+ def pick_mission(slot):
+ if shuffle_campaigns or mission_order_type not in campaign_depending_orders:
+ # Pick a mission from any campaign
+ filler = world.random.randint(0, len(missions_to_add) - 1)
+ mission = missions_to_add.pop(filler)
+ slot_campaign = mission_slots[slot].campaign
+ mission_slots[slot] = SC2MissionSlot(slot_campaign, mission)
+ else:
+ # Pick a mission from required campaign
+ slot_campaign = mission_slots[slot].campaign
+ campaign_mission_candidates = [mission for mission in missions_to_add if mission.campaign == slot_campaign]
+ mission = world.random.choice(campaign_mission_candidates)
+ missions_to_add.remove(mission)
+ mission_slots[slot] = SC2MissionSlot(slot_campaign, mission)
+
+ # Add no_build missions to the pool and fill in no_build slots
+ missions_to_add: List[SC2Mission] = mission_pools[MissionPools.STARTER]
+ if len(no_build_slots) > len(missions_to_add):
+ raise Exception("There are no valid No-Build missions. Please exclude fewer missions.")
+ for slot in no_build_slots:
+ pick_mission(slot)
+
+ # Add easy missions into pool and fill in easy slots
+ missions_to_add = missions_to_add + mission_pools[MissionPools.EASY]
+ if len(easy_slots) > len(missions_to_add):
+ raise Exception("There are not enough Easy missions to fill the campaign. Please exclude fewer missions.")
+ for slot in easy_slots:
+ pick_mission(slot)
+
+ # Add medium missions into pool and fill in medium slots
+ missions_to_add = missions_to_add + mission_pools[MissionPools.MEDIUM]
+ if len(medium_slots) > len(missions_to_add):
+ raise Exception("There are not enough Easy and Medium missions to fill the campaign. Please exclude fewer missions.")
+ for slot in medium_slots:
+ pick_mission(slot)
+
+ # Add hard missions into pool and fill in hard slots
+ missions_to_add = missions_to_add + mission_pools[MissionPools.HARD]
+ if len(hard_slots) > len(missions_to_add):
+ raise Exception("There are not enough missions to fill the campaign. Please exclude fewer missions.")
+ for slot in hard_slots:
+ pick_mission(slot)
+
+ # Add very hard missions into pool and fill in very hard slots
+ missions_to_add = missions_to_add + mission_pools[MissionPools.VERY_HARD]
+ if len(very_hard_slots) > len(missions_to_add):
+ raise Exception("There are not enough missions to fill the campaign. Please exclude fewer missions.")
+ for slot in very_hard_slots:
+ pick_mission(slot)
+
+ # Generating regions and locations from selected missions
+ for mission_slot in mission_slots:
+ if isinstance(mission_slot.slot, SC2Mission):
+ regions.append(create_region(world, locations_per_region, location_cache, mission_slot.slot.mission_name))
+ world.multiworld.regions += regions
+
+ campaigns: List[SC2Campaign]
+ if mission_order_type in campaign_depending_orders:
+ campaigns = list(enabled_campaigns)
+ else:
+ campaigns = [SC2Campaign.GLOBAL]
+
+ mission_req_table: Dict[SC2Campaign, Dict[str, MissionInfo]] = {}
+ campaign_mission_slots: Dict[SC2Campaign, List[SC2MissionSlot]] = \
+ {
+ campaign: [mission_slot for mission_slot in mission_slots if campaign == mission_slot.campaign]
+ for campaign in campaigns
+ }
+
+ slot_map: Dict[SC2Campaign, List[int]] = dict()
+
+ for campaign in campaigns:
+ mission_req_table.update({campaign: dict()})
+
+ # Mapping original mission slots to shifted mission slots when missions are removed
+ slot_map[campaign] = []
+ slot_offset = 0
+ for position, mission in enumerate(campaign_mission_slots[campaign]):
+ slot_map[campaign].append(position - slot_offset + 1)
+ if mission is None or mission.slot is None:
+ slot_offset += 1
+
+ def build_connection_rule(mission_names: List[str], missions_req: int) -> Callable:
+ player = world.player
+ if len(mission_names) > 1:
+ return lambda state: state.has_all({f"Beat {name}" for name in mission_names}, player) \
+ and state.has_group("Missions", player, missions_req)
+ else:
+ return lambda state: state.has(f"Beat {mission_names[0]}", player) \
+ and state.has_group("Missions", player, missions_req)
+
+ for campaign in campaigns:
+ # Loop through missions to create requirements table and connect regions
+ for i, mission in enumerate(campaign_mission_slots[campaign]):
+ if mission is None or mission.slot is None:
+ continue
+ connections: List[MissionConnection] = []
+ all_connections: List[SC2MissionSlot] = []
+ connection: MissionConnection
+ for connection in mission_order[campaign][i].connect_to:
+ if connection.connect_to == -1:
+ continue
+ # If mission normally connects to an excluded campaign, connect to menu instead
+ if connection.campaign not in campaign_mission_slots:
+ connection.connect_to = -1
+ continue
+ while campaign_mission_slots[connection.campaign][connection.connect_to].slot is None:
+ connection.connect_to -= 1
+ all_connections.append(campaign_mission_slots[connection.campaign][connection.connect_to])
+ for connection in mission_order[campaign][i].connect_to:
+ if connection.connect_to == -1:
+ connect(world, names, "Menu", mission.slot.mission_name)
+ else:
+ required_mission = campaign_mission_slots[connection.campaign][connection.connect_to]
+ if ((required_mission is None or required_mission.slot is None)
+ and not mission_order[campaign][i].completion_critical): # Drop non-critical null slots
+ continue
+ while required_mission is None or required_mission.slot is None: # Substituting null slot with prior slot
+ connection.connect_to -= 1
+ required_mission = campaign_mission_slots[connection.campaign][connection.connect_to]
+ required_missions = [required_mission] if mission_order[campaign][i].or_requirements else all_connections
+ if isinstance(required_mission.slot, SC2Mission):
+ required_mission_name = required_mission.slot.mission_name
+ required_missions_names = [mission.slot.mission_name for mission in required_missions]
+ connect(world, names, required_mission_name, mission.slot.mission_name,
+ build_connection_rule(required_missions_names, mission_order[campaign][i].number))
+ connections.append(MissionConnection(slot_map[connection.campaign][connection.connect_to], connection.campaign))
+
+ mission_req_table[campaign].update({mission.slot.mission_name: MissionInfo(
+ mission.slot, connections, mission_order[campaign][i].category,
+ number=mission_order[campaign][i].number,
+ completion_critical=mission_order[campaign][i].completion_critical,
+ or_requirements=mission_order[campaign][i].or_requirements)})
+
+ final_mission_id = final_mission.id
+ # Changing the completion condition for alternate final missions into an event
+ final_location = get_goal_location(final_mission)
+ setup_final_location(final_location, location_cache)
+
+ return mission_req_table, final_mission_id, final_location
+
+
+def setup_final_location(final_location, location_cache):
+ # Final location should be near the end of the cache
+ for i in range(len(location_cache) - 1, -1, -1):
+ if location_cache[i].name == final_location:
+ location_cache[i].address = None
+ break
+
+
+def create_location(player: int, location_data: LocationData, region: Region,
+ location_cache: List[Location]) -> Location:
+ location = Location(player, location_data.name, location_data.code, region)
+ location.access_rule = location_data.rule
+
+ location_cache.append(location)
+
+ return location
+
+
+def create_region(world: World, locations_per_region: Dict[str, List[LocationData]],
+ location_cache: List[Location], name: str) -> Region:
+ region = Region(name, world.player, world.multiworld)
+
+ if name in locations_per_region:
+ for location_data in locations_per_region[name]:
+ location = create_location(world.player, location_data, region, location_cache)
+ region.locations.append(location)
+
+ return region
+
+
+def connect(world: World, used_names: Dict[str, int], source: str, target: str,
+ rule: Optional[Callable] = None):
+ source_region = world.get_region(source)
+ target_region = world.get_region(target)
+
+ if target not in used_names:
+ used_names[target] = 1
+ name = target
+ else:
+ used_names[target] += 1
+ name = target + (' ' * used_names[target])
+
+ connection = Entrance(world.player, name, source_region)
+
+ if rule:
+ connection.access_rule = rule
+
+ source_region.exits.append(connection)
+ connection.connect(target_region)
+
+
+def get_locations_per_region(locations: Tuple[LocationData, ...]) -> Dict[str, List[LocationData]]:
+ per_region: Dict[str, List[LocationData]] = {}
+
+ for location in locations:
+ per_region.setdefault(location.region, []).append(location)
+
+ return per_region
+
+
+def get_factors(number: int) -> Tuple[int, int]:
+ """
+ Simple factorization into pairs of numbers (x, y) using a sieve method.
+ Returns the factorization that is most square, i.e. where x + y is minimized.
+ Factor order is such that x <= y.
+ """
+ assert number > 0
+ for divisor in range(math.floor(math.sqrt(number)), 1, -1):
+ quotient = number // divisor
+ if quotient * divisor == number:
+ return divisor, quotient
+ return 1, number
+
+
+def get_grid_dimensions(size: int) -> Tuple[int, int, int]:
+ """
+ Get the dimensions of a grid mission order from the number of missions, int the format (x, y, error).
+ * Error will always be 0, 1, or 2, so the missions can be removed from the corners that aren't the start or end.
+ * Dimensions are chosen such that x <= y, as buttons in the UI are wider than they are tall.
+ * Dimensions are chosen to be maximally square. That is, x + y + error is minimized.
+ * If multiple options of the same rating are possible, the one with the larger error is chosen,
+ as it will appear more square. Compare 3x11 to 5x7-2 for an example of this.
+ """
+ dimension_candidates: List[Tuple[int, int, int]] = [(*get_factors(size + x), x) for x in (2, 1, 0)]
+ best_dimension = min(dimension_candidates, key=sum)
+ return best_dimension
+
diff --git a/worlds/sc2/Rules.py b/worlds/sc2/Rules.py
new file mode 100644
index 000000000000..8b9097ea1d78
--- /dev/null
+++ b/worlds/sc2/Rules.py
@@ -0,0 +1,952 @@
+from typing import Set
+
+from BaseClasses import CollectionState
+from .Options import get_option_value, RequiredTactics, kerrigan_unit_available, AllInMap, \
+ GrantStoryTech, GrantStoryLevels, TakeOverAIAllies, SpearOfAdunAutonomouslyCastAbilityPresence, \
+ get_enabled_campaigns, MissionOrder
+from .Items import get_basic_units, defense_ratings, zerg_defense_ratings, kerrigan_actives, air_defense_ratings, \
+ kerrigan_levels, get_full_item_list
+from .MissionTables import SC2Race, SC2Campaign
+from . import ItemNames
+from worlds.AutoWorld import World
+
+
+class SC2Logic:
+
+ def lock_any_item(self, state: CollectionState, items: Set[str]) -> bool:
+ """
+ Guarantees that at least one of these items will remain in the world. Doesn't affect placement.
+ Needed for cases when the dynamic pool filtering could remove all the item prerequisites
+ :param state:
+ :param items:
+ :return:
+ """
+ return self.is_item_placement(state) \
+ or state.has_any(items, self.player)
+
+ def is_item_placement(self, state):
+ """
+ Tells if it's item placement or item pool filter
+ :param state:
+ :return: True for item placement, False for pool filter
+ """
+ # has_group with count = 0 is always true for item placement and always false for SC2 item filtering
+ return state.has_group("Missions", self.player, 0)
+
+ # WoL
+ def terran_common_unit(self, state: CollectionState) -> bool:
+ return state.has_any(self.basic_terran_units, self.player)
+
+ def terran_early_tech(self, state: CollectionState):
+ """
+ Basic combat unit that can be deployed quickly from mission start
+ :param state
+ :return:
+ """
+ return (
+ state.has_any({ItemNames.MARINE, ItemNames.FIREBAT, ItemNames.MARAUDER, ItemNames.REAPER, ItemNames.HELLION}, self.player)
+ or (self.advanced_tactics and state.has_any({ItemNames.GOLIATH, ItemNames.DIAMONDBACK, ItemNames.VIKING, ItemNames.BANSHEE}, self.player))
+ )
+
+ def terran_air(self, state: CollectionState) -> bool:
+ """
+ Air units or drops on advanced tactics
+ :param state:
+ :return:
+ """
+ return (state.has_any({ItemNames.VIKING, ItemNames.WRAITH, ItemNames.BANSHEE, ItemNames.BATTLECRUISER}, self.player) or self.advanced_tactics
+ and state.has_any({ItemNames.HERCULES, ItemNames.MEDIVAC}, self.player) and self.terran_common_unit(state)
+ )
+
+ def terran_air_anti_air(self, state: CollectionState) -> bool:
+ """
+ Air-to-air
+ :param state:
+ :return:
+ """
+ return (
+ state.has(ItemNames.VIKING, self.player)
+ or state.has_all({ItemNames.WRAITH, ItemNames.WRAITH_ADVANCED_LASER_TECHNOLOGY}, self.player)
+ or state.has_all({ItemNames.BATTLECRUISER, ItemNames.BATTLECRUISER_ATX_LASER_BATTERY}, self.player)
+ or self.advanced_tactics and state.has_any({ItemNames.WRAITH, ItemNames.VALKYRIE, ItemNames.BATTLECRUISER}, self.player)
+ )
+
+ def terran_competent_ground_to_air(self, state: CollectionState) -> bool:
+ """
+ Ground-to-air
+ :param state:
+ :return:
+ """
+ return (
+ state.has(ItemNames.GOLIATH, self.player)
+ or state.has(ItemNames.MARINE, self.player) and self.terran_bio_heal(state)
+ or self.advanced_tactics and state.has(ItemNames.CYCLONE, self.player)
+ )
+
+ def terran_competent_anti_air(self, state: CollectionState) -> bool:
+ """
+ Good AA unit
+ :param state:
+ :return:
+ """
+ return (
+ self.terran_competent_ground_to_air(state)
+ or self.terran_air_anti_air(state)
+ )
+
+ def welcome_to_the_jungle_requirement(self, state: CollectionState) -> bool:
+ """
+ Welcome to the Jungle requirements - able to deal with Scouts, Void Rays, Zealots and Stalkers
+ :param state:
+ :return:
+ """
+ return (
+ self.terran_common_unit(state)
+ and self.terran_competent_ground_to_air(state)
+ ) or (
+ self.advanced_tactics
+ and state.has_any({ItemNames.MARINE, ItemNames.VULTURE}, self.player)
+ and self.terran_air_anti_air(state)
+ )
+
+ def terran_basic_anti_air(self, state: CollectionState) -> bool:
+ """
+ Basic AA to deal with few air units
+ :param state:
+ :return:
+ """
+ return (
+ state.has_any({
+ ItemNames.MISSILE_TURRET, ItemNames.THOR, ItemNames.WAR_PIGS, ItemNames.SPARTAN_COMPANY,
+ ItemNames.HELS_ANGELS, ItemNames.BATTLECRUISER, ItemNames.MARINE, ItemNames.WRAITH,
+ ItemNames.VALKYRIE, ItemNames.CYCLONE, ItemNames.WINGED_NIGHTMARES, ItemNames.BRYNHILDS
+ }, self.player)
+ or self.terran_competent_anti_air(state)
+ or self.advanced_tactics and state.has_any({ItemNames.GHOST, ItemNames.SPECTRE, ItemNames.WIDOW_MINE, ItemNames.LIBERATOR}, self.player)
+ )
+
+ def terran_defense_rating(self, state: CollectionState, zerg_enemy: bool, air_enemy: bool = True) -> int:
+ """
+ Ability to handle defensive missions
+ :param state:
+ :param zerg_enemy:
+ :param air_enemy:
+ :return:
+ """
+ defense_score = sum((defense_ratings[item] for item in defense_ratings if state.has(item, self.player)))
+ # Manned Bunker
+ if state.has_any({ItemNames.MARINE, ItemNames.MARAUDER}, self.player) and state.has(ItemNames.BUNKER, self.player):
+ defense_score += 3
+ elif zerg_enemy and state.has(ItemNames.FIREBAT, self.player) and state.has(ItemNames.BUNKER, self.player):
+ defense_score += 2
+ # Siege Tank upgrades
+ if state.has_all({ItemNames.SIEGE_TANK, ItemNames.SIEGE_TANK_MAELSTROM_ROUNDS}, self.player):
+ defense_score += 2
+ if state.has_all({ItemNames.SIEGE_TANK, ItemNames.SIEGE_TANK_GRADUATING_RANGE}, self.player):
+ defense_score += 1
+ # Widow Mine upgrade
+ if state.has_all({ItemNames.WIDOW_MINE, ItemNames.WIDOW_MINE_CONCEALMENT}, self.player):
+ defense_score += 1
+ # Viking with splash
+ if state.has_all({ItemNames.VIKING, ItemNames.VIKING_SHREDDER_ROUNDS}, self.player):
+ defense_score += 2
+
+ # General enemy-based rules
+ if zerg_enemy:
+ defense_score += sum((zerg_defense_ratings[item] for item in zerg_defense_ratings if state.has(item, self.player)))
+ if air_enemy:
+ defense_score += sum((air_defense_ratings[item] for item in air_defense_ratings if state.has(item, self.player)))
+ if air_enemy and zerg_enemy and state.has(ItemNames.VALKYRIE, self.player):
+ # Valkyries shred mass Mutas, most common air enemy that's massed in these cases
+ defense_score += 2
+ # Advanced Tactics bumps defense rating requirements down by 2
+ if self.advanced_tactics:
+ defense_score += 2
+ return defense_score
+
+ def terran_competent_comp(self, state: CollectionState) -> bool:
+ """
+ Ability to deal with most of hard missions
+ :param state:
+ :return:
+ """
+ return (
+ (
+ (state.has_any({ItemNames.MARINE, ItemNames.MARAUDER}, self.player) and self.terran_bio_heal(state))
+ or state.has_any({ItemNames.THOR, ItemNames.BANSHEE, ItemNames.SIEGE_TANK}, self.player)
+ or state.has_all({ItemNames.LIBERATOR, ItemNames.LIBERATOR_RAID_ARTILLERY}, self.player)
+ )
+ and self.terran_competent_anti_air(state)
+ ) or (
+ state.has(ItemNames.BATTLECRUISER, self.player) and self.terran_common_unit(state)
+ )
+
+ def great_train_robbery_train_stopper(self, state: CollectionState) -> bool:
+ """
+ Ability to deal with trains (moving target with a lot of HP)
+ :param state:
+ :return:
+ """
+ return (
+ state.has_any({ItemNames.SIEGE_TANK, ItemNames.DIAMONDBACK, ItemNames.MARAUDER, ItemNames.CYCLONE, ItemNames.BANSHEE}, self.player)
+ or self.advanced_tactics
+ and (
+ state.has_all({ItemNames.REAPER, ItemNames.REAPER_G4_CLUSTERBOMB}, self.player)
+ or state.has_all({ItemNames.SPECTRE, ItemNames.SPECTRE_PSIONIC_LASH}, self.player)
+ or state.has_any({ItemNames.VULTURE, ItemNames.LIBERATOR}, self.player)
+ )
+ )
+
+ def terran_can_rescue(self, state) -> bool:
+ """
+ Rescuing in The Moebius Factor
+ :param state:
+ :return:
+ """
+ return state.has_any({ItemNames.MEDIVAC, ItemNames.HERCULES, ItemNames.RAVEN, ItemNames.VIKING}, self.player) or self.advanced_tactics
+
+ def terran_beats_protoss_deathball(self, state: CollectionState) -> bool:
+ """
+ Ability to deal with Immortals, Colossi with some air support
+ :param state:
+ :return:
+ """
+ return (
+ (
+ state.has_any({ItemNames.BANSHEE, ItemNames.BATTLECRUISER}, self.player)
+ or state.has_all({ItemNames.LIBERATOR, ItemNames.LIBERATOR_RAID_ARTILLERY}, self.player)
+ ) and self.terran_competent_anti_air(state)
+ or self.terran_competent_comp(state) and self.terran_air_anti_air(state)
+ )
+
+ def marine_medic_upgrade(self, state: CollectionState) -> bool:
+ """
+ Infantry upgrade to infantry-only no-build segments
+ :param state:
+ :return:
+ """
+ return state.has_any({
+ ItemNames.MARINE_COMBAT_SHIELD, ItemNames.MARINE_MAGRAIL_MUNITIONS, ItemNames.MEDIC_STABILIZER_MEDPACKS
+ }, self.player) \
+ or (state.count(ItemNames.MARINE_PROGRESSIVE_STIMPACK, self.player) >= 2
+ and state.has_group("Missions", self.player, 1))
+
+ def terran_survives_rip_field(self, state: CollectionState) -> bool:
+ """
+ Ability to deal with large areas with environment damage
+ :param state:
+ :return:
+ """
+ return (state.has(ItemNames.BATTLECRUISER, self.player)
+ or self.terran_air(state) and self.terran_competent_anti_air(state) and self.terran_sustainable_mech_heal(state))
+
+ def terran_sustainable_mech_heal(self, state: CollectionState) -> bool:
+ """
+ Can heal mech units without spending resources
+ :param state:
+ :return:
+ """
+ return state.has(ItemNames.SCIENCE_VESSEL, self.player) \
+ or state.has_all({ItemNames.MEDIC, ItemNames.MEDIC_ADAPTIVE_MEDPACKS}, self.player) \
+ or state.count(ItemNames.PROGRESSIVE_REGENERATIVE_BIO_STEEL, self.player) >= 3 \
+ or (self.advanced_tactics
+ and (
+ state.has_all({ItemNames.RAVEN, ItemNames.RAVEN_BIO_MECHANICAL_REPAIR_DRONE}, self.player)
+ or state.count(ItemNames.PROGRESSIVE_REGENERATIVE_BIO_STEEL, self.player) >= 2)
+ )
+
+ def terran_bio_heal(self, state: CollectionState) -> bool:
+ """
+ Ability to heal bio units
+ :param state:
+ :return:
+ """
+ return state.has_any({ItemNames.MEDIC, ItemNames.MEDIVAC}, self.player) \
+ or self.advanced_tactics and state.has_all({ItemNames.RAVEN, ItemNames.RAVEN_BIO_MECHANICAL_REPAIR_DRONE}, self.player)
+
+ def terran_base_trasher(self, state: CollectionState) -> bool:
+ """
+ Can attack heavily defended bases
+ :param state:
+ :return:
+ """
+ return state.has(ItemNames.SIEGE_TANK, self.player) \
+ or state.has_all({ItemNames.BATTLECRUISER, ItemNames.BATTLECRUISER_ATX_LASER_BATTERY}, self.player) \
+ or state.has_all({ItemNames.LIBERATOR, ItemNames.LIBERATOR_RAID_ARTILLERY}, self.player) \
+ or (self.advanced_tactics
+ and ((state.has_all({ItemNames.RAVEN, ItemNames.RAVEN_HUNTER_SEEKER_WEAPON}, self.player)
+ or self.can_nuke(state))
+ and (
+ state.has_all({ItemNames.VIKING, ItemNames.VIKING_SHREDDER_ROUNDS}, self.player)
+ or state.has_all({ItemNames.BANSHEE, ItemNames.BANSHEE_SHOCKWAVE_MISSILE_BATTERY}, self.player))
+ )
+ )
+
+ def terran_mobile_detector(self, state: CollectionState) -> bool:
+ return state.has_any({ItemNames.RAVEN, ItemNames.SCIENCE_VESSEL, ItemNames.PROGRESSIVE_ORBITAL_COMMAND}, self.player)
+
+ def can_nuke(self, state: CollectionState) -> bool:
+ """
+ Ability to launch nukes
+ :param state:
+ :return:
+ """
+ return (self.advanced_tactics
+ and (state.has_any({ItemNames.GHOST, ItemNames.SPECTRE}, self.player)
+ or state.has_all({ItemNames.THOR, ItemNames.THOR_BUTTON_WITH_A_SKULL_ON_IT}, self.player)))
+
+ def terran_respond_to_colony_infestations(self, state: CollectionState) -> bool:
+ """
+ Can deal quickly with Brood Lords and Mutas in Haven's Fall and being able to progress the mission
+ :param state:
+ :return:
+ """
+ return (
+ self.terran_common_unit(state)
+ and self.terran_competent_anti_air(state)
+ and (
+ self.terran_air_anti_air(state)
+ or state.has_any({ItemNames.BATTLECRUISER, ItemNames.VALKYRIE}, self.player)
+ )
+ and self.terran_defense_rating(state, True) >= 3
+ )
+
+ def engine_of_destruction_requirement(self, state: CollectionState):
+ return self.marine_medic_upgrade(state) \
+ and (
+ self.terran_competent_anti_air(state)
+ and self.terran_common_unit(state) or state.has(ItemNames.WRAITH, self.player)
+ )
+
+ def all_in_requirement(self, state: CollectionState):
+ """
+ All-in
+ :param state:
+ :return:
+ """
+ beats_kerrigan = state.has_any({ItemNames.MARINE, ItemNames.BANSHEE, ItemNames.GHOST}, self.player) or self.advanced_tactics
+ if get_option_value(self.world, 'all_in_map') == AllInMap.option_ground:
+ # Ground
+ defense_rating = self.terran_defense_rating(state, True, False)
+ if state.has_any({ItemNames.BATTLECRUISER, ItemNames.BANSHEE}, self.player):
+ defense_rating += 2
+ return defense_rating >= 13 and beats_kerrigan
+ else:
+ # Air
+ defense_rating = self.terran_defense_rating(state, True, True)
+ return defense_rating >= 9 and beats_kerrigan \
+ and state.has_any({ItemNames.VIKING, ItemNames.BATTLECRUISER, ItemNames.VALKYRIE}, self.player) \
+ and state.has_any({ItemNames.HIVE_MIND_EMULATOR, ItemNames.PSI_DISRUPTER, ItemNames.MISSILE_TURRET}, self.player)
+
+ # HotS
+ def zerg_common_unit(self, state: CollectionState) -> bool:
+ return state.has_any(self.basic_zerg_units, self.player)
+
+ def zerg_competent_anti_air(self, state: CollectionState) -> bool:
+ return state.has_any({ItemNames.HYDRALISK, ItemNames.MUTALISK, ItemNames.CORRUPTOR, ItemNames.BROOD_QUEEN}, self.player) \
+ or state.has_all({ItemNames.SWARM_HOST, ItemNames.SWARM_HOST_PRESSURIZED_GLANDS}, self.player) \
+ or state.has_all({ItemNames.SCOURGE, ItemNames.SCOURGE_RESOURCE_EFFICIENCY}, self.player) \
+ or (self.advanced_tactics and state.has(ItemNames.INFESTOR, self.player))
+
+ def zerg_basic_anti_air(self, state: CollectionState) -> bool:
+ return self.zerg_competent_anti_air(state) or self.kerrigan_unit_available in kerrigan_unit_available or \
+ state.has_any({ItemNames.SWARM_QUEEN, ItemNames.SCOURGE}, self.player) or (self.advanced_tactics and state.has(ItemNames.SPORE_CRAWLER, self.player))
+
+ def morph_brood_lord(self, state: CollectionState) -> bool:
+ return state.has_any({ItemNames.MUTALISK, ItemNames.CORRUPTOR}, self.player) \
+ and state.has(ItemNames.MUTALISK_CORRUPTOR_BROOD_LORD_ASPECT, self.player)
+
+ def morph_viper(self, state: CollectionState) -> bool:
+ return state.has_any({ItemNames.MUTALISK, ItemNames.CORRUPTOR}, self.player) \
+ and state.has(ItemNames.MUTALISK_CORRUPTOR_VIPER_ASPECT, self.player)
+
+ def morph_impaler_or_lurker(self, state: CollectionState) -> bool:
+ return state.has(ItemNames.HYDRALISK, self.player) and state.has_any({ItemNames.HYDRALISK_IMPALER_ASPECT, ItemNames.HYDRALISK_LURKER_ASPECT}, self.player)
+
+ def zerg_competent_comp(self, state: CollectionState) -> bool:
+ advanced = self.advanced_tactics
+ core_unit = state.has_any({ItemNames.ROACH, ItemNames.ABERRATION, ItemNames.ZERGLING}, self.player)
+ support_unit = state.has_any({ItemNames.SWARM_QUEEN, ItemNames.HYDRALISK}, self.player) \
+ or self.morph_brood_lord(state) \
+ or advanced and (state.has_any({ItemNames.INFESTOR, ItemNames.DEFILER}, self.player) or self.morph_viper(state))
+ if core_unit and support_unit:
+ return True
+ vespene_unit = state.has_any({ItemNames.ULTRALISK, ItemNames.ABERRATION}, self.player) \
+ or advanced and self.morph_viper(state)
+ return vespene_unit and state.has_any({ItemNames.ZERGLING, ItemNames.SWARM_QUEEN}, self.player)
+
+ def spread_creep(self, state: CollectionState) -> bool:
+ return self.advanced_tactics or state.has(ItemNames.SWARM_QUEEN, self.player)
+
+ def zerg_competent_defense(self, state: CollectionState) -> bool:
+ return (
+ self.zerg_common_unit(state)
+ and (
+ (
+ state.has(ItemNames.SWARM_HOST, self.player)
+ or self.morph_brood_lord(state)
+ or self.morph_impaler_or_lurker(state)
+ ) or (
+ self.advanced_tactics
+ and (self.morph_viper(state)
+ or state.has(ItemNames.SPINE_CRAWLER, self.player))
+ )
+ )
+ )
+
+ def basic_kerrigan(self, state: CollectionState) -> bool:
+ # One active ability that can be used to defeat enemies directly on Standard
+ if not self.advanced_tactics and \
+ not state.has_any({ItemNames.KERRIGAN_KINETIC_BLAST, ItemNames.KERRIGAN_LEAPING_STRIKE,
+ ItemNames.KERRIGAN_CRUSHING_GRIP, ItemNames.KERRIGAN_PSIONIC_SHIFT,
+ ItemNames.KERRIGAN_SPAWN_BANELINGS}, self.player):
+ return False
+ # Two non-ultimate abilities
+ count = 0
+ for item in (ItemNames.KERRIGAN_KINETIC_BLAST, ItemNames.KERRIGAN_LEAPING_STRIKE, ItemNames.KERRIGAN_HEROIC_FORTITUDE,
+ ItemNames.KERRIGAN_CHAIN_REACTION, ItemNames.KERRIGAN_CRUSHING_GRIP, ItemNames.KERRIGAN_PSIONIC_SHIFT,
+ ItemNames.KERRIGAN_SPAWN_BANELINGS, ItemNames.KERRIGAN_INFEST_BROODLINGS, ItemNames.KERRIGAN_FURY):
+ if state.has(item, self.player):
+ count += 1
+ if count >= 2:
+ return True
+ return False
+
+ def two_kerrigan_actives(self, state: CollectionState) -> bool:
+ count = 0
+ for i in range(7):
+ if state.has_any(kerrigan_actives[i], self.player):
+ count += 1
+ return count >= 2
+
+ def zerg_pass_vents(self, state: CollectionState) -> bool:
+ return self.story_tech_granted \
+ or state.has_any({ItemNames.ZERGLING, ItemNames.HYDRALISK, ItemNames.ROACH}, self.player) \
+ or (self.advanced_tactics and state.has(ItemNames.INFESTOR, self.player))
+
+ def supreme_requirement(self, state: CollectionState) -> bool:
+ return self.story_tech_granted \
+ or not self.kerrigan_unit_available \
+ or (
+ state.has_all({ItemNames.KERRIGAN_LEAPING_STRIKE, ItemNames.KERRIGAN_MEND}, self.player)
+ and self.kerrigan_levels(state, 35)
+ )
+
+ def kerrigan_levels(self, state: CollectionState, target: int) -> bool:
+ if self.story_levels_granted or not self.kerrigan_unit_available:
+ return True # Levels are granted
+ if self.kerrigan_levels_per_mission_completed > 0 \
+ and self.kerrigan_levels_per_mission_completed_cap > 0 \
+ and not self.is_item_placement(state):
+ # Levels can be granted from mission completion.
+ # Item pool filtering isn't aware of missions beaten. Assume that missions beaten will fulfill this rule.
+ return True
+ # Levels from missions beaten
+ levels = self.kerrigan_levels_per_mission_completed * state.count_group("Missions", self.player)
+ if self.kerrigan_levels_per_mission_completed_cap != -1:
+ levels = min(levels, self.kerrigan_levels_per_mission_completed_cap)
+ # Levels from items
+ for kerrigan_level_item in kerrigan_levels:
+ level_amount = get_full_item_list()[kerrigan_level_item].number
+ item_count = state.count(kerrigan_level_item, self.player)
+ levels += item_count * level_amount
+ # Total level cap
+ if self.kerrigan_total_level_cap != -1:
+ levels = min(levels, self.kerrigan_total_level_cap)
+
+ return levels >= target
+
+
+ def the_reckoning_requirement(self, state: CollectionState) -> bool:
+ if self.take_over_ai_allies:
+ return self.terran_competent_comp(state) \
+ and self.zerg_competent_comp(state) \
+ and (self.zerg_competent_anti_air(state)
+ or self.terran_competent_anti_air(state))
+ else:
+ return self.zerg_competent_comp(state) \
+ and self.zerg_competent_anti_air(state)
+
+ # LotV
+
+ def protoss_common_unit(self, state: CollectionState) -> bool:
+ return state.has_any(self.basic_protoss_units, self.player)
+
+ def protoss_basic_anti_air(self, state: CollectionState) -> bool:
+ return self.protoss_competent_anti_air(state) \
+ or state.has_any({ItemNames.PHOENIX, ItemNames.MIRAGE, ItemNames.CORSAIR, ItemNames.CARRIER, ItemNames.SCOUT,
+ ItemNames.DARK_ARCHON, ItemNames.WRATHWALKER, ItemNames.MOTHERSHIP}, self.player) \
+ or state.has_all({ItemNames.WARP_PRISM, ItemNames.WARP_PRISM_PHASE_BLASTER}, self.player) \
+ or self.advanced_tactics and state.has_any(
+ {ItemNames.HIGH_TEMPLAR, ItemNames.SIGNIFIER, ItemNames.ASCENDANT, ItemNames.DARK_TEMPLAR,
+ ItemNames.SENTRY, ItemNames.ENERGIZER}, self.player)
+
+ def protoss_anti_armor_anti_air(self, state: CollectionState) -> bool:
+ return self.protoss_competent_anti_air(state) \
+ or state.has_any({ItemNames.SCOUT, ItemNames.WRATHWALKER}, self.player) \
+ or (state.has_any({ItemNames.IMMORTAL, ItemNames.ANNIHILATOR}, self.player)
+ and state.has(ItemNames.IMMORTAL_ANNIHILATOR_ADVANCED_TARGETING_MECHANICS, self.player))
+
+ def protoss_anti_light_anti_air(self, state: CollectionState) -> bool:
+ return self.protoss_competent_anti_air(state) \
+ or state.has_any({ItemNames.PHOENIX, ItemNames.MIRAGE, ItemNames.CORSAIR, ItemNames.CARRIER}, self.player)
+
+ def protoss_competent_anti_air(self, state: CollectionState) -> bool:
+ return state.has_any(
+ {ItemNames.STALKER, ItemNames.SLAYER, ItemNames.INSTIGATOR, ItemNames.DRAGOON, ItemNames.ADEPT,
+ ItemNames.VOID_RAY, ItemNames.DESTROYER, ItemNames.TEMPEST}, self.player) \
+ or (state.has_any({ItemNames.PHOENIX, ItemNames.MIRAGE, ItemNames.CORSAIR, ItemNames.CARRIER}, self.player)
+ and state.has_any({ItemNames.SCOUT, ItemNames.WRATHWALKER}, self.player)) \
+ or (self.advanced_tactics
+ and state.has_any({ItemNames.IMMORTAL, ItemNames.ANNIHILATOR}, self.player)
+ and state.has(ItemNames.IMMORTAL_ANNIHILATOR_ADVANCED_TARGETING_MECHANICS, self.player))
+
+ def protoss_has_blink(self, state: CollectionState) -> bool:
+ return state.has_any({ItemNames.STALKER, ItemNames.INSTIGATOR, ItemNames.SLAYER}, self.player) \
+ or (
+ state.has(ItemNames.DARK_TEMPLAR_AVENGER_BLOOD_HUNTER_BLINK, self.player)
+ and state.has_any({ItemNames.DARK_TEMPLAR, ItemNames.BLOOD_HUNTER, ItemNames.AVENGER}, self.player)
+ )
+
+ def protoss_can_attack_behind_chasm(self, state: CollectionState) -> bool:
+ return state.has_any(
+ {ItemNames.SCOUT, ItemNames.TEMPEST,
+ ItemNames.CARRIER, ItemNames.VOID_RAY, ItemNames.DESTROYER, ItemNames.MOTHERSHIP}, self.player) \
+ or self.protoss_has_blink(state) \
+ or (state.has(ItemNames.WARP_PRISM, self.player)
+ and (self.protoss_common_unit(state) or state.has(ItemNames.WARP_PRISM_PHASE_BLASTER, self.player))) \
+ or (self.advanced_tactics
+ and state.has_any({ItemNames.ORACLE, ItemNames.ARBITER}, self.player))
+
+ def protoss_fleet(self, state: CollectionState) -> bool:
+ return state.has_any({ItemNames.CARRIER, ItemNames.TEMPEST, ItemNames.VOID_RAY, ItemNames.DESTROYER}, self.player)
+
+ def templars_return_requirement(self, state: CollectionState) -> bool:
+ return self.story_tech_granted \
+ or (
+ state.has_any({ItemNames.IMMORTAL, ItemNames.ANNIHILATOR}, self.player)
+ and state.has_any({ItemNames.COLOSSUS, ItemNames.VANGUARD, ItemNames.REAVER, ItemNames.DARK_TEMPLAR}, self.player)
+ and state.has_any({ItemNames.SENTRY, ItemNames.HIGH_TEMPLAR}, self.player)
+ )
+
+ def brothers_in_arms_requirement(self, state: CollectionState) -> bool:
+ return (
+ self.protoss_common_unit(state)
+ and self.protoss_anti_armor_anti_air(state)
+ and self.protoss_hybrid_counter(state)
+ ) or (
+ self.take_over_ai_allies
+ and (
+ self.terran_common_unit(state)
+ or self.protoss_common_unit(state)
+ )
+ and (
+ self.terran_competent_anti_air(state)
+ or self.protoss_anti_armor_anti_air(state)
+ )
+ and (
+ self.protoss_hybrid_counter(state)
+ or state.has_any({ItemNames.BATTLECRUISER, ItemNames.LIBERATOR, ItemNames.SIEGE_TANK}, self.player)
+ or state.has_all({ItemNames.SPECTRE, ItemNames.SPECTRE_PSIONIC_LASH}, self.player)
+ or (state.has(ItemNames.IMMORTAL, self.player)
+ and state.has_any({ItemNames.MARINE, ItemNames.MARAUDER}, self.player)
+ and self.terran_bio_heal(state))
+ )
+ )
+
+ def protoss_hybrid_counter(self, state: CollectionState) -> bool:
+ """
+ Ground Hybrids
+ """
+ return state.has_any(
+ {ItemNames.ANNIHILATOR, ItemNames.ASCENDANT, ItemNames.TEMPEST, ItemNames.CARRIER, ItemNames.VOID_RAY,
+ ItemNames.WRATHWALKER, ItemNames.VANGUARD}, self.player) \
+ or (state.has(ItemNames.IMMORTAL, self.player) or self.advanced_tactics) and state.has_any(
+ {ItemNames.STALKER, ItemNames.DRAGOON, ItemNames.ADEPT, ItemNames.INSTIGATOR, ItemNames.SLAYER}, self.player)
+
+ def the_infinite_cycle_requirement(self, state: CollectionState) -> bool:
+ return self.story_tech_granted \
+ or not self.kerrigan_unit_available \
+ or (
+ self.two_kerrigan_actives(state)
+ and self.basic_kerrigan(state)
+ and self.kerrigan_levels(state, 70)
+ )
+
+ def protoss_basic_splash(self, state: CollectionState) -> bool:
+ return state.has_any(
+ {ItemNames.ZEALOT, ItemNames.COLOSSUS, ItemNames.VANGUARD, ItemNames.HIGH_TEMPLAR, ItemNames.SIGNIFIER,
+ ItemNames.DARK_TEMPLAR, ItemNames.REAVER, ItemNames.ASCENDANT}, self.player)
+
+ def protoss_static_defense(self, state: CollectionState) -> bool:
+ return state.has_any({ItemNames.PHOTON_CANNON, ItemNames.KHAYDARIN_MONOLITH}, self.player)
+
+ def last_stand_requirement(self, state: CollectionState) -> bool:
+ return self.protoss_common_unit(state) \
+ and self.protoss_competent_anti_air(state) \
+ and self.protoss_static_defense(state) \
+ and (
+ self.advanced_tactics
+ or self.protoss_basic_splash(state)
+ )
+
+ def harbinger_of_oblivion_requirement(self, state: CollectionState) -> bool:
+ return self.protoss_anti_armor_anti_air(state) and (
+ self.take_over_ai_allies
+ or (
+ self.protoss_common_unit(state)
+ and self.protoss_hybrid_counter(state)
+ )
+ )
+
+ def protoss_competent_comp(self, state: CollectionState) -> bool:
+ return self.protoss_common_unit(state) \
+ and self.protoss_competent_anti_air(state) \
+ and self.protoss_hybrid_counter(state) \
+ and self.protoss_basic_splash(state)
+
+ def protoss_stalker_upgrade(self, state: CollectionState) -> bool:
+ return (
+ state.has_any(
+ {
+ ItemNames.STALKER_INSTIGATOR_SLAYER_DISINTEGRATING_PARTICLES,
+ ItemNames.STALKER_INSTIGATOR_SLAYER_PARTICLE_REFLECTION
+ }, self.player)
+ and self.lock_any_item(state, {ItemNames.STALKER, ItemNames.INSTIGATOR, ItemNames.SLAYER})
+ )
+
+ def steps_of_the_rite_requirement(self, state: CollectionState) -> bool:
+ return self.protoss_competent_comp(state) \
+ or (
+ self.protoss_common_unit(state)
+ and self.protoss_competent_anti_air(state)
+ and self.protoss_static_defense(state)
+ )
+
+ def protoss_heal(self, state: CollectionState) -> bool:
+ return state.has_any({ItemNames.CARRIER, ItemNames.SENTRY, ItemNames.SHIELD_BATTERY, ItemNames.RECONSTRUCTION_BEAM}, self.player)
+
+ def templars_charge_requirement(self, state: CollectionState) -> bool:
+ return self.protoss_heal(state) \
+ and self.protoss_anti_armor_anti_air(state) \
+ and (
+ self.protoss_fleet(state)
+ or (self.advanced_tactics
+ and self.protoss_competent_comp(state)
+ )
+ )
+
+ def the_host_requirement(self, state: CollectionState) -> bool:
+ return (self.protoss_fleet(state)
+ and self.protoss_static_defense(state)
+ ) or (
+ self.protoss_competent_comp(state)
+ and state.has(ItemNames.SOA_TIME_STOP, self.player)
+ )
+
+ def salvation_requirement(self, state: CollectionState) -> bool:
+ return [
+ self.protoss_competent_comp(state),
+ self.protoss_fleet(state),
+ self.protoss_static_defense(state)
+ ].count(True) >= 2
+
+ def into_the_void_requirement(self, state: CollectionState) -> bool:
+ return self.protoss_competent_comp(state) \
+ or (
+ self.take_over_ai_allies
+ and (
+ state.has(ItemNames.BATTLECRUISER, self.player)
+ or (
+ state.has(ItemNames.ULTRALISK, self.player)
+ and self.protoss_competent_anti_air(state)
+ )
+ )
+ )
+
+ def essence_of_eternity_requirement(self, state: CollectionState) -> bool:
+ defense_score = self.terran_defense_rating(state, False, True)
+ if self.take_over_ai_allies and self.protoss_static_defense(state):
+ defense_score += 2
+ return defense_score >= 10 \
+ and (
+ self.terran_competent_anti_air(state)
+ or self.take_over_ai_allies
+ and self.protoss_competent_anti_air(state)
+ ) \
+ and (
+ state.has(ItemNames.BATTLECRUISER, self.player)
+ or (state.has(ItemNames.BANSHEE, self.player) and state.has_any({ItemNames.VIKING, ItemNames.VALKYRIE},
+ self.player))
+ or self.take_over_ai_allies and self.protoss_fleet(state)
+ ) \
+ and state.has_any({ItemNames.SIEGE_TANK, ItemNames.LIBERATOR}, self.player)
+
+ def amons_fall_requirement(self, state: CollectionState) -> bool:
+ if self.take_over_ai_allies:
+ return (
+ (
+ state.has_any({ItemNames.BATTLECRUISER, ItemNames.CARRIER}, self.player)
+ )
+ or (state.has(ItemNames.ULTRALISK, self.player)
+ and self.protoss_competent_anti_air(state)
+ and (
+ state.has_any({ItemNames.LIBERATOR, ItemNames.BANSHEE, ItemNames.VALKYRIE, ItemNames.VIKING}, self.player)
+ or state.has_all({ItemNames.WRAITH, ItemNames.WRAITH_ADVANCED_LASER_TECHNOLOGY}, self.player)
+ or self.protoss_fleet(state)
+ )
+ and (self.terran_sustainable_mech_heal(state)
+ or (self.spear_of_adun_autonomously_cast_presence == SpearOfAdunAutonomouslyCastAbilityPresence.option_everywhere
+ and state.has(ItemNames.RECONSTRUCTION_BEAM, self.player))
+ )
+ )
+ ) \
+ and self.terran_competent_anti_air(state) \
+ and self.protoss_competent_comp(state) \
+ and self.zerg_competent_comp(state)
+ else:
+ return state.has(ItemNames.MUTALISK, self.player) and self.zerg_competent_comp(state)
+
+ def nova_any_weapon(self, state: CollectionState) -> bool:
+ return state.has_any(
+ {ItemNames.NOVA_C20A_CANISTER_RIFLE, ItemNames.NOVA_HELLFIRE_SHOTGUN, ItemNames.NOVA_PLASMA_RIFLE,
+ ItemNames.NOVA_MONOMOLECULAR_BLADE, ItemNames.NOVA_BLAZEFIRE_GUNBLADE}, self.player)
+
+ def nova_ranged_weapon(self, state: CollectionState) -> bool:
+ return state.has_any(
+ {ItemNames.NOVA_C20A_CANISTER_RIFLE, ItemNames.NOVA_HELLFIRE_SHOTGUN, ItemNames.NOVA_PLASMA_RIFLE},
+ self.player)
+
+ def nova_splash(self, state: CollectionState) -> bool:
+ return state.has_any({
+ ItemNames.NOVA_HELLFIRE_SHOTGUN, ItemNames.NOVA_BLAZEFIRE_GUNBLADE, ItemNames.NOVA_PULSE_GRENADES
+ }, self.player) \
+ or self.advanced_tactics and state.has_any(
+ {ItemNames.NOVA_PLASMA_RIFLE, ItemNames.NOVA_MONOMOLECULAR_BLADE}, self.player)
+
+ def nova_dash(self, state: CollectionState) -> bool:
+ return state.has_any({ItemNames.NOVA_MONOMOLECULAR_BLADE, ItemNames.NOVA_BLINK}, self.player)
+
+ def nova_full_stealth(self, state: CollectionState) -> bool:
+ return state.count(ItemNames.NOVA_PROGRESSIVE_STEALTH_SUIT_MODULE, self.player) >= 2
+
+ def nova_heal(self, state: CollectionState) -> bool:
+ return state.has_any({ItemNames.NOVA_ARMORED_SUIT_MODULE, ItemNames.NOVA_STIM_INFUSION}, self.player)
+
+ def nova_escape_assist(self, state: CollectionState) -> bool:
+ return state.has_any({ItemNames.NOVA_BLINK, ItemNames.NOVA_HOLO_DECOY, ItemNames.NOVA_IONIC_FORCE_FIELD}, self.player)
+
+ def the_escape_stuff_granted(self) -> bool:
+ """
+ The NCO first mission requires having too much stuff first before actually able to do anything
+ :return:
+ """
+ return self.story_tech_granted \
+ or (self.mission_order == MissionOrder.option_vanilla and self.enabled_campaigns == {SC2Campaign.NCO})
+
+ def the_escape_first_stage_requirement(self, state: CollectionState) -> bool:
+ return self.the_escape_stuff_granted() \
+ or (self.nova_ranged_weapon(state) and (self.nova_full_stealth(state) or self.nova_heal(state)))
+
+ def the_escape_requirement(self, state: CollectionState) -> bool:
+ return self.the_escape_first_stage_requirement(state) \
+ and (self.the_escape_stuff_granted() or self.nova_splash(state))
+
+ def terran_cliffjumper(self, state: CollectionState) -> bool:
+ return state.has(ItemNames.REAPER, self.player) \
+ or state.has_all({ItemNames.GOLIATH, ItemNames.GOLIATH_JUMP_JETS}, self.player) \
+ or state.has_all({ItemNames.SIEGE_TANK, ItemNames.SIEGE_TANK_JUMP_JETS}, self.player)
+
+ def terran_able_to_snipe_defiler(self, state: CollectionState) -> bool:
+ return state.has_all({ItemNames.NOVA_JUMP_SUIT_MODULE, ItemNames.NOVA_C20A_CANISTER_RIFLE}, self.player) \
+ or state.has_all({ItemNames.SIEGE_TANK, ItemNames.SIEGE_TANK_MAELSTROM_ROUNDS, ItemNames.SIEGE_TANK_JUMP_JETS}, self.player)
+
+ def sudden_strike_requirement(self, state: CollectionState) -> bool:
+ return self.sudden_strike_can_reach_objectives(state) \
+ and self.terran_able_to_snipe_defiler(state) \
+ and state.has_any({ItemNames.SIEGE_TANK, ItemNames.VULTURE}, self.player) \
+ and self.nova_splash(state) \
+ and (self.terran_defense_rating(state, True, False) >= 2
+ or state.has(ItemNames.NOVA_JUMP_SUIT_MODULE, self.player))
+
+ def sudden_strike_can_reach_objectives(self, state: CollectionState) -> bool:
+ return self.terran_cliffjumper(state) \
+ or state.has_any({ItemNames.BANSHEE, ItemNames.VIKING}, self.player) \
+ or (
+ self.advanced_tactics
+ and state.has(ItemNames.MEDIVAC, self.player)
+ and state.has_any({ItemNames.MARINE, ItemNames.MARAUDER, ItemNames.VULTURE, ItemNames.HELLION,
+ ItemNames.GOLIATH}, self.player)
+ )
+
+ def enemy_intelligence_garrisonable_unit(self, state: CollectionState) -> bool:
+ """
+ Has unit usable as a Garrison in Enemy Intelligence
+ :param state:
+ :return:
+ """
+ return state.has_any(
+ {ItemNames.MARINE, ItemNames.REAPER, ItemNames.MARAUDER, ItemNames.GHOST, ItemNames.SPECTRE,
+ ItemNames.HELLION, ItemNames.GOLIATH, ItemNames.WARHOUND, ItemNames.DIAMONDBACK, ItemNames.VIKING},
+ self.player)
+
+ def enemy_intelligence_cliff_garrison(self, state: CollectionState) -> bool:
+ return state.has_any({ItemNames.REAPER, ItemNames.VIKING, ItemNames.MEDIVAC, ItemNames.HERCULES}, self.player) \
+ or state.has_all({ItemNames.GOLIATH, ItemNames.GOLIATH_JUMP_JETS}, self.player) \
+ or self.advanced_tactics and state.has_any({ItemNames.HELS_ANGELS, ItemNames.BRYNHILDS}, self.player)
+
+ def enemy_intelligence_first_stage_requirement(self, state: CollectionState) -> bool:
+ return self.enemy_intelligence_garrisonable_unit(state) \
+ and (self.terran_competent_comp(state)
+ or (
+ self.terran_common_unit(state)
+ and self.terran_competent_anti_air(state)
+ and state.has(ItemNames.NOVA_NUKE, self.player)
+ )
+ ) \
+ and self.terran_defense_rating(state, True, True) >= 5
+
+ def enemy_intelligence_second_stage_requirement(self, state: CollectionState) -> bool:
+ return self.enemy_intelligence_first_stage_requirement(state) \
+ and self.enemy_intelligence_cliff_garrison(state) \
+ and (
+ self.story_tech_granted
+ or (
+ self.nova_any_weapon(state)
+ and (
+ self.nova_full_stealth(state)
+ or (self.nova_heal(state)
+ and self.nova_splash(state)
+ and self.nova_ranged_weapon(state))
+ )
+ )
+ )
+
+ def enemy_intelligence_third_stage_requirement(self, state: CollectionState) -> bool:
+ return self.enemy_intelligence_second_stage_requirement(state) \
+ and (
+ self.story_tech_granted
+ or (
+ state.has(ItemNames.NOVA_PROGRESSIVE_STEALTH_SUIT_MODULE, self.player)
+ and self.nova_dash(state)
+ )
+ )
+
+ def trouble_in_paradise_requirement(self, state: CollectionState) -> bool:
+ return self.nova_any_weapon(state) \
+ and self.nova_splash(state) \
+ and self.terran_beats_protoss_deathball(state) \
+ and self.terran_defense_rating(state, True, True) >= 7
+
+ def night_terrors_requirement(self, state: CollectionState) -> bool:
+ return self.terran_common_unit(state) \
+ and self.terran_competent_anti_air(state) \
+ and (
+ # These can handle the waves of infested, even volatile ones
+ state.has(ItemNames.SIEGE_TANK, self.player)
+ or state.has_all({ItemNames.VIKING, ItemNames.VIKING_SHREDDER_ROUNDS}, self.player)
+ or (
+ (
+ # Regular infesteds
+ state.has(ItemNames.FIREBAT, self.player)
+ or state.has_all({ItemNames.HELLION, ItemNames.HELLION_HELLBAT_ASPECT}, self.player)
+ or (
+ self.advanced_tactics
+ and state.has_any({ItemNames.PERDITION_TURRET, ItemNames.PLANETARY_FORTRESS}, self.player)
+ )
+ )
+ and self.terran_bio_heal(state)
+ and (
+ # Volatile infesteds
+ state.has(ItemNames.LIBERATOR, self.player)
+ or (
+ self.advanced_tactics
+ and state.has_any({ItemNames.HERC, ItemNames.VULTURE}, self.player)
+ )
+ )
+ )
+ )
+
+ def flashpoint_far_requirement(self, state: CollectionState) -> bool:
+ return self.terran_competent_comp(state) \
+ and self.terran_mobile_detector(state) \
+ and self.terran_defense_rating(state, True, False) >= 6
+
+ def enemy_shadow_tripwires_tool(self, state: CollectionState) -> bool:
+ return state.has_any({ItemNames.NOVA_FLASHBANG_GRENADES, ItemNames.NOVA_BLINK, ItemNames.NOVA_DOMINATION},
+ self.player)
+
+ def enemy_shadow_door_unlocks_tool(self, state: CollectionState) -> bool:
+ return state.has_any({ItemNames.NOVA_DOMINATION, ItemNames.NOVA_BLINK, ItemNames.NOVA_JUMP_SUIT_MODULE},
+ self.player)
+
+ def enemy_shadow_domination(self, state: CollectionState) -> bool:
+ return self.story_tech_granted \
+ or (self.nova_ranged_weapon(state)
+ and (self.nova_full_stealth(state)
+ or state.has(ItemNames.NOVA_JUMP_SUIT_MODULE, self.player)
+ or (self.nova_heal(state) and self.nova_splash(state))
+ )
+ )
+
+ def enemy_shadow_first_stage(self, state: CollectionState) -> bool:
+ return self.enemy_shadow_domination(state) \
+ and (self.story_tech_granted
+ or ((self.nova_full_stealth(state) and self.enemy_shadow_tripwires_tool(state))
+ or (self.nova_heal(state) and self.nova_splash(state))
+ )
+ )
+
+ def enemy_shadow_second_stage(self, state: CollectionState) -> bool:
+ return self.enemy_shadow_first_stage(state) \
+ and (self.story_tech_granted
+ or self.nova_splash(state)
+ or self.nova_heal(state)
+ or self.nova_escape_assist(state)
+ )
+
+ def enemy_shadow_door_controls(self, state: CollectionState) -> bool:
+ return self.enemy_shadow_second_stage(state) \
+ and (self.story_tech_granted or self.enemy_shadow_door_unlocks_tool(state))
+
+ def enemy_shadow_victory(self, state: CollectionState) -> bool:
+ return self.enemy_shadow_door_controls(state) \
+ and (self.story_tech_granted or self.nova_heal(state))
+
+ def dark_skies_requirement(self, state: CollectionState) -> bool:
+ return self.terran_common_unit(state) \
+ and self.terran_beats_protoss_deathball(state) \
+ and self.terran_defense_rating(state, False, True) >= 8
+
+ def end_game_requirement(self, state: CollectionState) -> bool:
+ return self.terran_competent_comp(state) \
+ and self.terran_mobile_detector(state) \
+ and (
+ state.has_any({ItemNames.BATTLECRUISER, ItemNames.LIBERATOR, ItemNames.BANSHEE}, self.player)
+ or state.has_all({ItemNames.WRAITH, ItemNames.WRAITH_ADVANCED_LASER_TECHNOLOGY}, self.player)
+ ) \
+ and (state.has_any({ItemNames.BATTLECRUISER, ItemNames.VIKING, ItemNames.LIBERATOR}, self.player)
+ or (self.advanced_tactics
+ and state.has_all({ItemNames.RAVEN, ItemNames.RAVEN_HUNTER_SEEKER_WEAPON}, self.player)
+ )
+ )
+
+ def __init__(self, world: World):
+ self.world: World = world
+ self.player = None if world is None else world.player
+ self.logic_level = get_option_value(world, 'required_tactics')
+ self.advanced_tactics = self.logic_level != RequiredTactics.option_standard
+ self.take_over_ai_allies = get_option_value(world, "take_over_ai_allies") == TakeOverAIAllies.option_true
+ self.kerrigan_unit_available = get_option_value(world, 'kerrigan_presence') in kerrigan_unit_available \
+ and SC2Campaign.HOTS in get_enabled_campaigns(world)
+ self.kerrigan_levels_per_mission_completed = get_option_value(world, "kerrigan_levels_per_mission_completed")
+ self.kerrigan_levels_per_mission_completed_cap = get_option_value(world, "kerrigan_levels_per_mission_completed_cap")
+ self.kerrigan_total_level_cap = get_option_value(world, "kerrigan_total_level_cap")
+ self.story_tech_granted = get_option_value(world, "grant_story_tech") == GrantStoryTech.option_true
+ self.story_levels_granted = get_option_value(world, "grant_story_levels") != GrantStoryLevels.option_disabled
+ self.basic_terran_units = get_basic_units(world, SC2Race.TERRAN)
+ self.basic_zerg_units = get_basic_units(world, SC2Race.ZERG)
+ self.basic_protoss_units = get_basic_units(world, SC2Race.PROTOSS)
+ self.spear_of_adun_autonomously_cast_presence = get_option_value(world, "spear_of_adun_autonomously_cast_ability_presence")
+ self.enabled_campaigns = get_enabled_campaigns(world)
+ self.mission_order = get_option_value(world, "mission_order")
diff --git a/worlds/sc2/Starcraft2.kv b/worlds/sc2/Starcraft2.kv
new file mode 100644
index 000000000000..6b112c2f00a6
--- /dev/null
+++ b/worlds/sc2/Starcraft2.kv
@@ -0,0 +1,28 @@
+
+ scroll_type: ["content", "bars"]
+ bar_width: dp(12)
+ effect_cls: "ScrollEffect"
+
+
+ cols: 1
+ size_hint_y: None
+ height: self.minimum_height + 15
+ padding: [5,0,dp(12),0]
+
+:
+ cols: 1
+
+:
+ rows: 1
+
+:
+ cols: 1
+ spacing: [0,5]
+
+:
+ text_size: self.size
+ markup: True
+ halign: 'center'
+ valign: 'middle'
+ padding: [5,0,5,0]
+ outline_width: 1
diff --git a/worlds/sc2/__init__.py b/worlds/sc2/__init__.py
new file mode 100644
index 000000000000..fffa618d2694
--- /dev/null
+++ b/worlds/sc2/__init__.py
@@ -0,0 +1,482 @@
+import typing
+from dataclasses import fields
+
+from typing import List, Set, Iterable, Sequence, Dict, Callable, Union
+from math import floor, ceil
+from BaseClasses import Item, MultiWorld, Location, Tutorial, ItemClassification
+from worlds.AutoWorld import WebWorld, World
+from . import ItemNames
+from .Items import StarcraftItem, filler_items, get_item_table, get_full_item_list, \
+ get_basic_units, ItemData, upgrade_included_names, progressive_if_nco, kerrigan_actives, kerrigan_passives, \
+ kerrigan_only_passives, progressive_if_ext, not_balanced_starting_units, spear_of_adun_calldowns, \
+ spear_of_adun_castable_passives, nova_equipment
+from .ItemGroups import item_name_groups
+from .Locations import get_locations, LocationType, get_location_types, get_plando_locations
+from .Regions import create_regions
+from .Options import get_option_value, LocationInclusion, KerriganLevelItemDistribution, \
+ KerriganPresence, KerriganPrimalStatus, RequiredTactics, kerrigan_unit_available, StarterUnit, SpearOfAdunPresence, \
+ get_enabled_campaigns, SpearOfAdunAutonomouslyCastAbilityPresence, Starcraft2Options
+from .PoolFilter import filter_items, get_item_upgrades, UPGRADABLE_ITEMS, missions_in_mission_table, get_used_races
+from .MissionTables import MissionInfo, SC2Campaign, lookup_name_to_mission, SC2Mission, \
+ SC2Race
+
+
+class Starcraft2WebWorld(WebWorld):
+ setup = Tutorial(
+ "Multiworld Setup Guide",
+ "A guide to setting up the Starcraft 2 randomizer connected to an Archipelago Multiworld",
+ "English",
+ "setup_en.md",
+ "setup/en",
+ ["TheCondor", "Phaneros"]
+ )
+
+ tutorials = [setup]
+
+
+class SC2World(World):
+ """
+ StarCraft II is a science fiction real-time strategy video game developed and published by Blizzard Entertainment.
+ Play as one of three factions across four campaigns in a battle for supremacy of the Koprulu Sector.
+ """
+
+ game = "Starcraft 2"
+ web = Starcraft2WebWorld()
+ data_version = 6
+
+ item_name_to_id = {name: data.code for name, data in get_full_item_list().items()}
+ location_name_to_id = {location.name: location.code for location in get_locations(None)}
+ options_dataclass = Starcraft2Options
+ options: Starcraft2Options
+
+ item_name_groups = item_name_groups
+ locked_locations: typing.List[str]
+ location_cache: typing.List[Location]
+ mission_req_table: Dict[SC2Campaign, Dict[str, MissionInfo]] = {}
+ final_mission_id: int
+ victory_item: str
+ required_client_version = 0, 4, 5
+
+ def __init__(self, multiworld: MultiWorld, player: int):
+ super(SC2World, self).__init__(multiworld, player)
+ self.location_cache = []
+ self.locked_locations = []
+
+ def create_item(self, name: str) -> Item:
+ data = get_full_item_list()[name]
+ return StarcraftItem(name, data.classification, data.code, self.player)
+
+ def create_regions(self):
+ self.mission_req_table, self.final_mission_id, self.victory_item = create_regions(
+ self, get_locations(self), self.location_cache
+ )
+
+ def create_items(self):
+ setup_events(self.player, self.locked_locations, self.location_cache)
+
+ excluded_items = get_excluded_items(self)
+
+ starter_items = assign_starter_items(self, excluded_items, self.locked_locations, self.location_cache)
+
+ fill_resource_locations(self, self.locked_locations, self.location_cache)
+
+ pool = get_item_pool(self, self.mission_req_table, starter_items, excluded_items, self.location_cache)
+
+ fill_item_pool_with_dummy_items(self, self.locked_locations, self.location_cache, pool)
+
+ self.multiworld.itempool += pool
+
+ def set_rules(self):
+ self.multiworld.completion_condition[self.player] = lambda state: state.has(self.victory_item, self.player)
+
+ def get_filler_item_name(self) -> str:
+ return self.random.choice(filler_items)
+
+ def fill_slot_data(self):
+ slot_data = {}
+ for option_name in [field.name for field in fields(Starcraft2Options)]:
+ option = get_option_value(self, option_name)
+ if type(option) in {str, int}:
+ slot_data[option_name] = int(option)
+ slot_req_table = {}
+
+ # Serialize data
+ for campaign in self.mission_req_table:
+ slot_req_table[campaign.id] = {}
+ for mission in self.mission_req_table[campaign]:
+ slot_req_table[campaign.id][mission] = self.mission_req_table[campaign][mission]._asdict()
+ # Replace mission objects with mission IDs
+ slot_req_table[campaign.id][mission]["mission"] = slot_req_table[campaign.id][mission]["mission"].id
+
+ for index in range(len(slot_req_table[campaign.id][mission]["required_world"])):
+ # TODO this is a band-aid, sometimes the mission_req_table already contains dicts
+ # as far as I can tell it's related to having multiple vanilla mission orders
+ if not isinstance(slot_req_table[campaign.id][mission]["required_world"][index], dict):
+ slot_req_table[campaign.id][mission]["required_world"][index] = slot_req_table[campaign.id][mission]["required_world"][index]._asdict()
+
+ enabled_campaigns = get_enabled_campaigns(self)
+ slot_data["plando_locations"] = get_plando_locations(self)
+ slot_data["nova_covert_ops_only"] = (enabled_campaigns == {SC2Campaign.NCO})
+ slot_data["mission_req"] = slot_req_table
+ slot_data["final_mission"] = self.final_mission_id
+ slot_data["version"] = 3
+
+ if SC2Campaign.HOTS not in enabled_campaigns:
+ slot_data["kerrigan_presence"] = KerriganPresence.option_not_present
+ return slot_data
+
+
+def setup_events(player: int, locked_locations: typing.List[str], location_cache: typing.List[Location]):
+ for location in location_cache:
+ if location.address is None:
+ item = Item(location.name, ItemClassification.progression, None, player)
+
+ locked_locations.append(location.name)
+
+ location.place_locked_item(item)
+
+
+def get_excluded_items(world: World) -> Set[str]:
+ excluded_items: Set[str] = set(get_option_value(world, 'excluded_items'))
+ for item in world.multiworld.precollected_items[world.player]:
+ excluded_items.add(item.name)
+ locked_items: Set[str] = set(get_option_value(world, 'locked_items'))
+ # Starter items are also excluded items
+ starter_items: Set[str] = set(get_option_value(world, 'start_inventory'))
+ item_table = get_full_item_list()
+ soa_presence = get_option_value(world, "spear_of_adun_presence")
+ soa_autocast_presence = get_option_value(world, "spear_of_adun_autonomously_cast_ability_presence")
+ enabled_campaigns = get_enabled_campaigns(world)
+
+ # Ensure no item is both guaranteed and excluded
+ invalid_items = excluded_items.intersection(locked_items)
+ invalid_count = len(invalid_items)
+ # Don't count starter items that can appear multiple times
+ invalid_count -= len([item for item in starter_items.intersection(locked_items) if item_table[item].quantity != 1])
+ if invalid_count > 0:
+ raise Exception(f"{invalid_count} item{'s are' if invalid_count > 1 else ' is'} both locked and excluded from generation. Please adjust your excluded items and locked items.")
+
+ def smart_exclude(item_choices: Set[str], choices_to_keep: int):
+ expected_choices = len(item_choices)
+ if expected_choices == 0:
+ return
+ item_choices = set(item_choices)
+ starter_choices = item_choices.intersection(starter_items)
+ excluded_choices = item_choices.intersection(excluded_items)
+ item_choices.difference_update(excluded_choices)
+ item_choices.difference_update(locked_items)
+ candidates = sorted(item_choices)
+ exclude_amount = min(expected_choices - choices_to_keep - len(excluded_choices) + len(starter_choices), len(candidates))
+ if exclude_amount > 0:
+ excluded_items.update(world.random.sample(candidates, exclude_amount))
+
+ # Nova gear exclusion if NCO not in campaigns
+ if SC2Campaign.NCO not in enabled_campaigns:
+ excluded_items = excluded_items.union(nova_equipment)
+
+ kerrigan_presence = get_option_value(world, "kerrigan_presence")
+ # Exclude Primal Form item if option is not set or Kerrigan is unavailable
+ if get_option_value(world, "kerrigan_primal_status") != KerriganPrimalStatus.option_item or \
+ (kerrigan_presence in {KerriganPresence.option_not_present, KerriganPresence.option_not_present_and_no_passives}):
+ excluded_items.add(ItemNames.KERRIGAN_PRIMAL_FORM)
+
+ # no Kerrigan & remove all passives => remove all abilities
+ if kerrigan_presence == KerriganPresence.option_not_present_and_no_passives:
+ for tier in range(7):
+ smart_exclude(kerrigan_actives[tier].union(kerrigan_passives[tier]), 0)
+ else:
+ # no Kerrigan, but keep non-Kerrigan passives
+ if kerrigan_presence == KerriganPresence.option_not_present:
+ smart_exclude(kerrigan_only_passives, 0)
+ for tier in range(7):
+ smart_exclude(kerrigan_actives[tier], 0)
+
+ # SOA exclusion, other cases are handled by generic race logic
+ if (soa_presence == SpearOfAdunPresence.option_lotv_protoss and SC2Campaign.LOTV not in enabled_campaigns) \
+ or soa_presence == SpearOfAdunPresence.option_not_present:
+ excluded_items.update(spear_of_adun_calldowns)
+ if (soa_autocast_presence == SpearOfAdunAutonomouslyCastAbilityPresence.option_lotv_protoss \
+ and SC2Campaign.LOTV not in enabled_campaigns) \
+ or soa_autocast_presence == SpearOfAdunAutonomouslyCastAbilityPresence.option_not_present:
+ excluded_items.update(spear_of_adun_castable_passives)
+
+ return excluded_items
+
+
+def assign_starter_items(world: World, excluded_items: Set[str], locked_locations: List[str], location_cache: typing.List[Location]) -> List[Item]:
+ starter_items: List[Item] = []
+ non_local_items = get_option_value(world, "non_local_items")
+ starter_unit = get_option_value(world, "starter_unit")
+ enabled_campaigns = get_enabled_campaigns(world)
+ first_mission = get_first_mission(world.mission_req_table)
+ # Ensuring that first mission is completable
+ if starter_unit == StarterUnit.option_off:
+ starter_mission_locations = [location.name for location in location_cache
+ if location.parent_region.name == first_mission
+ and location.access_rule == Location.access_rule]
+ if not starter_mission_locations:
+ # Force early unit if first mission is impossible without one
+ starter_unit = StarterUnit.option_any_starter_unit
+
+ if starter_unit != StarterUnit.option_off:
+ first_race = lookup_name_to_mission[first_mission].race
+
+ if first_race == SC2Race.ANY:
+ # If the first mission is a logic-less no-build
+ mission_req_table: Dict[SC2Campaign, Dict[str, MissionInfo]] = world.mission_req_table
+ races = get_used_races(mission_req_table, world)
+ races.remove(SC2Race.ANY)
+ if lookup_name_to_mission[first_mission].race in races:
+ # The campaign's race is in (At least one mission that's not logic-less no-build exists)
+ first_race = lookup_name_to_mission[first_mission].campaign.race
+ elif len(races) > 0:
+ # The campaign only has logic-less no-build missions. Find any other valid race
+ first_race = world.random.choice(list(races))
+
+ if first_race != SC2Race.ANY:
+ # The race of the early unit has been chosen
+ basic_units = get_basic_units(world, first_race)
+ if starter_unit == StarterUnit.option_balanced:
+ basic_units = basic_units.difference(not_balanced_starting_units)
+ if first_mission == SC2Mission.DARK_WHISPERS.mission_name:
+ # Special case - you don't have a logicless location but need an AA
+ basic_units = basic_units.difference(
+ {ItemNames.ZEALOT, ItemNames.CENTURION, ItemNames.SENTINEL, ItemNames.BLOOD_HUNTER,
+ ItemNames.AVENGER, ItemNames.IMMORTAL, ItemNames.ANNIHILATOR, ItemNames.VANGUARD})
+ if first_mission == SC2Mission.SUDDEN_STRIKE.mission_name:
+ # Special case - cliffjumpers
+ basic_units = {ItemNames.REAPER, ItemNames.GOLIATH, ItemNames.SIEGE_TANK, ItemNames.VIKING, ItemNames.BANSHEE}
+ local_basic_unit = sorted(item for item in basic_units if item not in non_local_items and item not in excluded_items)
+ if not local_basic_unit:
+ # Drop non_local_items constraint
+ local_basic_unit = sorted(item for item in basic_units if item not in excluded_items)
+ if not local_basic_unit:
+ raise Exception("Early Unit: At least one basic unit must be included")
+
+ unit: Item = add_starter_item(world, excluded_items, local_basic_unit)
+ starter_items.append(unit)
+
+ # NCO-only specific rules
+ if first_mission == SC2Mission.SUDDEN_STRIKE.mission_name:
+ support_item: Union[str, None] = None
+ if unit.name == ItemNames.REAPER:
+ support_item = ItemNames.REAPER_SPIDER_MINES
+ elif unit.name == ItemNames.GOLIATH:
+ support_item = ItemNames.GOLIATH_JUMP_JETS
+ elif unit.name == ItemNames.SIEGE_TANK:
+ support_item = ItemNames.SIEGE_TANK_JUMP_JETS
+ elif unit.name == ItemNames.VIKING:
+ support_item = ItemNames.VIKING_SMART_SERVOS
+ if support_item is not None:
+ starter_items.append(add_starter_item(world, excluded_items, [support_item]))
+ starter_items.append(add_starter_item(world, excluded_items, [ItemNames.NOVA_JUMP_SUIT_MODULE]))
+ starter_items.append(
+ add_starter_item(world, excluded_items,
+ [
+ ItemNames.NOVA_HELLFIRE_SHOTGUN,
+ ItemNames.NOVA_PLASMA_RIFLE,
+ ItemNames.NOVA_PULSE_GRENADES
+ ]))
+ if enabled_campaigns == {SC2Campaign.NCO}:
+ starter_items.append(add_starter_item(world, excluded_items, [ItemNames.LIBERATOR_RAID_ARTILLERY]))
+
+ starter_abilities = get_option_value(world, 'start_primary_abilities')
+ assert isinstance(starter_abilities, int)
+ if starter_abilities:
+ ability_count = starter_abilities
+ ability_tiers = [0, 1, 3]
+ world.random.shuffle(ability_tiers)
+ if ability_count > 3:
+ ability_tiers.append(6)
+ for tier in ability_tiers:
+ abilities = kerrigan_actives[tier].union(kerrigan_passives[tier]).difference(excluded_items, non_local_items)
+ if not abilities:
+ abilities = kerrigan_actives[tier].union(kerrigan_passives[tier]).difference(excluded_items)
+ if abilities:
+ ability_count -= 1
+ starter_items.append(add_starter_item(world, excluded_items, list(abilities)))
+ if ability_count == 0:
+ break
+
+ return starter_items
+
+
+def get_first_mission(mission_req_table: Dict[SC2Campaign, Dict[str, MissionInfo]]) -> str:
+ # The first world should also be the starting world
+ campaigns = mission_req_table.keys()
+ lowest_id = min([campaign.id for campaign in campaigns])
+ first_campaign = [campaign for campaign in campaigns if campaign.id == lowest_id][0]
+ first_mission = list(mission_req_table[first_campaign])[0]
+ return first_mission
+
+
+def add_starter_item(world: World, excluded_items: Set[str], item_list: Sequence[str]) -> Item:
+
+ item_name = world.random.choice(sorted(item_list))
+
+ excluded_items.add(item_name)
+
+ item = create_item_with_correct_settings(world.player, item_name)
+
+ world.multiworld.push_precollected(item)
+
+ return item
+
+
+def get_item_pool(world: World, mission_req_table: Dict[SC2Campaign, Dict[str, MissionInfo]],
+ starter_items: List[Item], excluded_items: Set[str], location_cache: List[Location]) -> List[Item]:
+ pool: List[Item] = []
+
+ # For the future: goal items like Artifact Shards go here
+ locked_items = []
+
+ # YAML items
+ yaml_locked_items = get_option_value(world, 'locked_items')
+ assert not isinstance(yaml_locked_items, int)
+
+ # Adjust generic upgrade availability based on options
+ include_upgrades = get_option_value(world, 'generic_upgrade_missions') == 0
+ upgrade_items = get_option_value(world, 'generic_upgrade_items')
+ assert isinstance(upgrade_items, int)
+
+ # Include items from outside main campaigns
+ item_sets = {'wol', 'hots', 'lotv'}
+ if get_option_value(world, 'nco_items') \
+ or SC2Campaign.NCO in get_enabled_campaigns(world):
+ item_sets.add('nco')
+ if get_option_value(world, 'bw_items'):
+ item_sets.add('bw')
+ if get_option_value(world, 'ext_items'):
+ item_sets.add('ext')
+
+ def allowed_quantity(name: str, data: ItemData) -> int:
+ if name in excluded_items \
+ or data.type == "Upgrade" and (not include_upgrades or name not in upgrade_included_names[upgrade_items]) \
+ or not data.origin.intersection(item_sets):
+ return 0
+ elif name in progressive_if_nco and 'nco' not in item_sets:
+ return 1
+ elif name in progressive_if_ext and 'ext' not in item_sets:
+ return 1
+ else:
+ return data.quantity
+
+ for name, data in get_item_table().items():
+ for _ in range(allowed_quantity(name, data)):
+ item = create_item_with_correct_settings(world.player, name)
+ if name in yaml_locked_items:
+ locked_items.append(item)
+ else:
+ pool.append(item)
+
+ existing_items = starter_items + [item for item in world.multiworld.precollected_items[world.player] if item not in starter_items]
+ existing_names = [item.name for item in existing_items]
+
+ # Check the parent item integrity, exclude items
+ pool[:] = [item for item in pool if pool_contains_parent(item, pool + locked_items + existing_items)]
+
+ # Removing upgrades for excluded items
+ for item_name in excluded_items:
+ if item_name in existing_names:
+ continue
+ invalid_upgrades = get_item_upgrades(pool, item_name)
+ for invalid_upgrade in invalid_upgrades:
+ pool.remove(invalid_upgrade)
+
+ fill_pool_with_kerrigan_levels(world, pool)
+ filtered_pool = filter_items(world, mission_req_table, location_cache, pool, existing_items, locked_items)
+ return filtered_pool
+
+
+def fill_item_pool_with_dummy_items(self: SC2World, locked_locations: List[str],
+ location_cache: List[Location], pool: List[Item]):
+ for _ in range(len(location_cache) - len(locked_locations) - len(pool)):
+ item = create_item_with_correct_settings(self.player, self.get_filler_item_name())
+ pool.append(item)
+
+
+def create_item_with_correct_settings(player: int, name: str) -> Item:
+ data = get_full_item_list()[name]
+
+ item = Item(name, data.classification, data.code, player)
+
+ return item
+
+
+def pool_contains_parent(item: Item, pool: Iterable[Item]):
+ item_data = get_full_item_list().get(item.name)
+ if item_data.parent_item is None:
+ # The item has not associated parent, the item is valid
+ return True
+ parent_item = item_data.parent_item
+ # Check if the pool contains the parent item
+ return parent_item in [pool_item.name for pool_item in pool]
+
+
+def fill_resource_locations(world: World, locked_locations: List[str], location_cache: List[Location]):
+ """
+ Filters the locations in the world using a trash or Nothing item
+ :param multiworld:
+ :param player:
+ :param locked_locations:
+ :param location_cache:
+ :return:
+ """
+ open_locations = [location for location in location_cache if location.item is None]
+ plando_locations = get_plando_locations(world)
+ resource_location_types = get_location_types(world, LocationInclusion.option_resources)
+ location_data = {sc2_location.name: sc2_location for sc2_location in get_locations(world)}
+ for location in open_locations:
+ # Go through the locations that aren't locked yet (early unit, etc)
+ if location.name not in plando_locations:
+ # The location is not plando'd
+ sc2_location = location_data[location.name]
+ if sc2_location.type in resource_location_types:
+ item_name = world.random.choice(filler_items)
+ item = create_item_with_correct_settings(world.player, item_name)
+ location.place_locked_item(item)
+ locked_locations.append(location.name)
+
+
+def place_exclusion_item(item_name, location, locked_locations, player):
+ item = create_item_with_correct_settings(player, item_name)
+ location.place_locked_item(item)
+ locked_locations.append(location.name)
+
+
+def fill_pool_with_kerrigan_levels(world: World, item_pool: List[Item]):
+ total_levels = get_option_value(world, "kerrigan_level_item_sum")
+ if get_option_value(world, "kerrigan_presence") not in kerrigan_unit_available \
+ or total_levels == 0 \
+ or SC2Campaign.HOTS not in get_enabled_campaigns(world):
+ return
+
+ def add_kerrigan_level_items(level_amount: int, item_amount: int):
+ name = f"{level_amount} Kerrigan Level"
+ if level_amount > 1:
+ name += "s"
+ for _ in range(item_amount):
+ item_pool.append(create_item_with_correct_settings(world.player, name))
+
+ sizes = [70, 35, 14, 10, 7, 5, 2, 1]
+ option = get_option_value(world, "kerrigan_level_item_distribution")
+
+ assert isinstance(option, int)
+ assert isinstance(total_levels, int)
+
+ if option in (KerriganLevelItemDistribution.option_vanilla, KerriganLevelItemDistribution.option_smooth):
+ distribution = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+ if option == KerriganLevelItemDistribution.option_vanilla:
+ distribution = [32, 0, 0, 1, 3, 0, 0, 0, 1, 1]
+ else: # Smooth
+ distribution = [0, 0, 0, 1, 1, 2, 2, 2, 1, 1]
+ for tier in range(len(distribution)):
+ add_kerrigan_level_items(tier + 1, distribution[tier])
+ else:
+ size = sizes[option - 2]
+ round_func: Callable[[float], int] = round
+ if total_levels > 70:
+ round_func = floor
+ else:
+ round_func = ceil
+ add_kerrigan_level_items(size, round_func(float(total_levels) / size))
diff --git a/worlds/sc2/docs/contributors.md b/worlds/sc2/docs/contributors.md
new file mode 100644
index 000000000000..5b62466d7e45
--- /dev/null
+++ b/worlds/sc2/docs/contributors.md
@@ -0,0 +1,42 @@
+# Contributors
+Contibutors are listed with preferred or Discord names first, with github usernames prepended with an `@`
+
+## Update 2024.0
+### Code Changes
+* Ziktofel (@Ziktofel)
+* Salzkorn (@Salzkorn)
+* EnvyDragon (@EnvyDragon)
+* Phanerus (@MatthewMarinets)
+* Madi Sylveon (@MadiMadsen)
+* Magnemania (@Magnemania)
+* Subsourian (@Subsourian)
+* Hopop (@hopop201)
+* Alice Voltaire (@AliceVoltaire)
+* Genderdruid (@ArchonofFail)
+* CrazedCollie (@FoxOfWar)
+
+### Additional Beta testing and bug reports
+* Varcklen (@Varcklen)
+* BicolourSnake (@Bicoloursnake)
+* @NobleXenon
+* Severencir (@Severencir)
+* neocerber (@neocerber)
+* Mati (@Matiya-star)
+* Ixzine
+* sweetox
+* 8thDaughterOfFrost
+* The M8
+* Berserker (@Berserker66)
+* KaitoKid
+* Sheen
+* ProfBytes
+* IncoherentOrange
+* eudaimonistic
+* Figment
+
+## Older versions
+Not all contributors to older versions of Archipelago Starcraft 2 are known.
+
+TheCondor (@TheCondor07) is the original maintainer of the project. Other known contributors include:
+* soldieroforder
+* Berserker (@Berserker66)
diff --git a/worlds/sc2/docs/en_Starcraft 2.md b/worlds/sc2/docs/en_Starcraft 2.md
new file mode 100644
index 000000000000..43a7da89f24f
--- /dev/null
+++ b/worlds/sc2/docs/en_Starcraft 2.md
@@ -0,0 +1,64 @@
+# Starcraft 2 Wings of Liberty
+
+## What does randomization do to this game?
+
+The following unlocks are randomized as items:
+1. Your ability to build any non-worker unit.
+2. Unit specific upgrades including some combinations not available in the vanilla campaigns, such as both strain choices simultaneously for Zerg and every Spear of Adun upgrade simultaneously for Protoss!
+3. Your ability to get the generic unit upgrades, such as attack and armour upgrades.
+4. Other miscellaneous upgrades such as laboratory upgrades and mercenaries for Terran, Kerrigan levels and upgrades for Zerg, and Spear of Adun upgrades for Protoss.
+5. Small boosts to your starting mineral, vespene gas, and supply totals on each mission.
+
+You find items by making progress in these categories:
+* Completing missions
+* Completing bonus objectives (like by gathering lab research material in Wings of Liberty)
+* Reaching milestones in the mission, such as completing part of a main objective
+* Completing challenges based on achievements in the base game, such as clearing all Zerg on Devil's Playground
+
+Except for mission completion, these categories can be disabled in the game's settings. For instance, you can disable getting items for reaching required milestones.
+
+When you receive items, they will immediately become available, even during a mission, and you will be
+notified via a text box in the top-right corner of the game screen. Item unlocks are also logged in the Archipelago client.
+
+Missions are launched through the Starcraft 2 Archipelago client, through the Starcraft 2 Launcher tab. The between mission segments on the Hyperion, the Leviathan, and the Spear of Adun are not included. Additionally, metaprogression currencies such as credits and Solarite are not used.
+
+## What is the goal of this game when randomized?
+
+The goal is to beat the final mission in the mission order. The yaml configuration file controls the mission order and how missions are shuffled.
+
+## What non-randomized changes are there from vanilla Starcraft 2?
+
+1. Some missions have more vespene geysers available to allow a wider variety of units.
+2. Many new units and upgrades have been added as items, coming from co-op, melee, later campaigns, later expansions, brood war, and original ideas.
+3. Higher-tech production structures, including Factories, Starports, Robotics Facilities, and Stargates, no longer have tech requirements.
+4. Zerg missions have been adjusted to give the player a starting Lair where they would only have Hatcheries.
+5. Upgrades with a downside have had the downside removed, such as automated refineries costing more or tech reactors taking longer to build.
+6. Unit collision within the vents in Enemy Within has been adjusted to allow larger units to travel through them without getting stuck in odd places.
+7. Several vanilla bugs have been fixed.
+
+## Which of my items can be in another player's world?
+
+By default, any of StarCraft 2's items (specified above) can be in another player's world. See the
+[Advanced YAML Guide](https://archipelago.gg/tutorial/Archipelago/advanced_settings/en)
+for more information on how to change this.
+
+## Unique Local Commands
+
+The following commands are only available when using the Starcraft 2 Client to play with Archipelago. You can list them any time in the client with `/help`.
+
+* `/download_data` Download the most recent release of the necessary files for playing SC2 with Archipelago. Will overwrite existing files
+* `/difficulty [difficulty]` Overrides the difficulty set for the world.
+ * Options: casual, normal, hard, brutal
+* `/game_speed [game_speed]` Overrides the game speed for the world
+ * Options: default, slower, slow, normal, fast, faster
+* `/color [faction] [color]` Changes your color for one of your playable factions.
+ * Faction options: raynor, kerrigan, primal, protoss, nova
+ * Color options: white, red, blue, teal, purple, yellow, orange, green, lightpink, violet, lightgrey, darkgreen, brown, lightgreen, darkgrey, pink, rainbow, random, default
+* `/option [option_name] [option_value]` Sets an option normally controlled by your yaml after generation.
+ * Run without arguments to list all options.
+ * Options pertain to automatic cutscene skipping, Kerrigan presence, Spear of Adun presence, starting resource amounts, controlling AI allies, etc.
+* `/disable_mission_check` Disables the check to see if a mission is available to play. Meant for co-op runs where one player can play the next mission in a chain the other player is doing.
+* `/play [mission_id]` Starts a Starcraft 2 mission based off of the mission_id provided
+* `/available` Get what missions are currently available to play
+* `/unfinished` Get what missions are currently available to play and have not had all locations checked
+* `/set_path [path]` Manually set the SC2 install directory (if the automatic detection fails)
diff --git a/worlds/sc2wol/docs/setup_en.md b/worlds/sc2/docs/setup_en.md
similarity index 50%
rename from worlds/sc2wol/docs/setup_en.md
rename to worlds/sc2/docs/setup_en.md
index 9bfeb3d235bc..10881e149c43 100644
--- a/worlds/sc2wol/docs/setup_en.md
+++ b/worlds/sc2/docs/setup_en.md
@@ -7,12 +7,10 @@ to obtain a config file for StarCraft 2.
- [StarCraft 2](https://starcraft2.com/en-us/)
- [The most recent Archipelago release](https://github.com/ArchipelagoMW/Archipelago/releases)
-- [StarCraft 2 AP Maps and Data](https://github.com/Ziktofel/Archipelago-SC2-data/releases)
## How do I install this randomizer?
-1. Install StarCraft 2 and Archipelago using the first two links above. (The StarCraft 2 client for Archipelago is
- included by default.)
+1. Install StarCraft 2 and Archipelago using the links above. The StarCraft 2 Archipelago client is downloaded by the Archipelago installer.
- Linux users should also follow the instructions found at the bottom of this page
(["Running in Linux"](#running-in-linux)).
2. Run ArchipelagoStarcraft2Client.exe.
@@ -21,25 +19,66 @@ to obtain a config file for StarCraft 2.
## Where do I get a config file (aka "YAML") for this game?
-The [Player Settings](https://archipelago.gg/games/Starcraft%202%20Wings%20of%20Liberty/player-settings) page on this
-website allows you to choose your personal settings for the randomizer and download them into a config file. Remember
-the name you type in the `Player Name` box; that's the "slot name" the client will ask you for when you attempt to
-connect!
+Yaml files are configuration files that tell Archipelago how you'd like your game to be randomized, even if you're only using default options.
+When you're setting up a multiworld, every world needs its own yaml file.
-### And why do I need a config file?
+There are three basic ways to get a yaml:
+* You can go to the [Player Options](https://archipelago.gg/games/Starcraft%202/player-options) page, set your options in the GUI, and export the yaml.
+* You can generate a template, either by downloading it from the [Player Options](https://archipelago.gg/games/Starcraft%202/player-options) page or by generating it from the Launcher (ArchipelagoLauncher.exe). The template includes descriptions of each option, you just have to edit it in your text editor of choice.
+* You can ask someone else to share their yaml to use it for yourself or adjust it as you wish.
-Config files tell Archipelago how you'd like your game to be randomized, even if you're only using default settings.
-When you're setting up a multiworld, every world needs its own config file.
-Check out [Creating a YAML](https://archipelago.gg/tutorial/Archipelago/setup/en#creating-a-yaml) for more information.
+Remember the name you enter in the options page or in the yaml file, you'll need it to connect later!
+
+Note that the basic Player Options page doesn't allow you to change all advanced options, such as excluding particular units or upgrades. Go through the [Weighted Options](https://archipelago.gg/weighted-options) page for that.
+
+Check out [Creating a YAML](https://archipelago.gg/tutorial/Archipelago/setup/en#creating-a-yaml) for more game-agnostic information.
+
+### Common yaml questions
+#### How do I know I set my yaml up correctly?
+
+The simplest way to check is to test it out. Save your yaml to the Players/ folder within your Archipelago installation and run ArchipelagoGenerate.exe. You should see a new .zip file within the output/ folder of your Archipelago installation if things worked correctly. It's advisable to run ArchipelagoGenerate through a terminal so that you can see the printout, which will include any errors and the precise output file name if it's successful. If you don't like terminals, you can also check the log file in the logs/ folder.
+
+#### What does Progression Balancing do?
+
+For Starcraft 2, not much. It's an Archipelago-wide option meant to shift required items earlier in the playthrough, but Starcraft 2 tends to be much more open in what items you can use. As such, this adjustment isn't very noticeable. It can also increase generation times, so we generally recommend turning it off.
+
+#### How do I specify items in a list, like in excluded items?
+
+You can look up the syntax for yaml collections in the [YAML specification](https://yaml.org/spec/1.2.2/#21-collections). For lists, every item goes on its own line, started with a hyphen:
+
+```yaml
+excluded_items:
+ - Battlecruiser
+ - Drop-Pods (Kerrigan Tier 7)
+```
+
+An empty list is just a matching pair of square brackets: `[]`. That's the default value in the template, which should let you know to use this syntax.
+
+#### How do I specify items for the starting inventory?
+
+The starting inventory is a YAML mapping rather than a list, which associates an item with the amount you start with. The syntax looks like the item name, followed by a colon, then a whitespace character, and then the value:
+
+```yaml
+start_inventory:
+ Micro-Filtering: 1
+ Additional Starting Vespene: 5
+```
+
+An empty mapping is just a matching pair of curly braces: `{}`. That's the default value in the template, which should let you know to use this syntax.
+
+#### How do I know the exact names of items?
+
+You can look up a complete list if item names in the [Icon Repository](https://matthewmarinets.github.io/ap_sc2_icons/).
## How do I join a MultiWorld game?
1. Run ArchipelagoStarcraft2Client.exe.
- macOS users should instead follow the instructions found at ["Running in macOS"](#running-in-macos) for this step only.
2. Type `/connect [server ip]`.
-3. Type your slot name and the server's password when prompted.
-4. Once connected, switch to the 'StarCraft 2 Launcher' tab in the client. There, you can see every mission. By default,
- only 'Liberation Day' will be available at the beginning. Just click on a mission to start it!
+ - If you're running through the website, the server IP should be displayed near the top of the room page.
+3. Type your slot name from your YAML when prompted.
+4. If the server has a password, enter that when prompted.
+5. Once connected, switch to the 'StarCraft 2 Launcher' tab in the client. There, you can see all the missions in your world. Unreachable missions will have greyed-out text. Just click on an available mission to start it!
## The game isn't launching when I try to start a mission.
diff --git a/worlds/sc2wol/requirements.txt b/worlds/sc2/requirements.txt
similarity index 100%
rename from worlds/sc2wol/requirements.txt
rename to worlds/sc2/requirements.txt
diff --git a/worlds/sc2/test/__init__.py b/worlds/sc2/test/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/worlds/sc2/test/test_Regions.py b/worlds/sc2/test/test_Regions.py
new file mode 100644
index 000000000000..c268b65da9a8
--- /dev/null
+++ b/worlds/sc2/test/test_Regions.py
@@ -0,0 +1,41 @@
+import unittest
+from .test_base import Sc2TestBase
+from .. import Regions
+from .. import Options, MissionTables
+
+class TestGridsizes(unittest.TestCase):
+ def test_grid_sizes_meet_specs(self):
+ self.assertTupleEqual((1, 2, 0), Regions.get_grid_dimensions(2))
+ self.assertTupleEqual((1, 3, 0), Regions.get_grid_dimensions(3))
+ self.assertTupleEqual((2, 2, 0), Regions.get_grid_dimensions(4))
+ self.assertTupleEqual((2, 3, 1), Regions.get_grid_dimensions(5))
+ self.assertTupleEqual((2, 4, 1), Regions.get_grid_dimensions(7))
+ self.assertTupleEqual((2, 4, 0), Regions.get_grid_dimensions(8))
+ self.assertTupleEqual((3, 3, 0), Regions.get_grid_dimensions(9))
+ self.assertTupleEqual((2, 5, 0), Regions.get_grid_dimensions(10))
+ self.assertTupleEqual((3, 4, 1), Regions.get_grid_dimensions(11))
+ self.assertTupleEqual((3, 4, 0), Regions.get_grid_dimensions(12))
+ self.assertTupleEqual((3, 5, 0), Regions.get_grid_dimensions(15))
+ self.assertTupleEqual((4, 4, 0), Regions.get_grid_dimensions(16))
+ self.assertTupleEqual((4, 6, 0), Regions.get_grid_dimensions(24))
+ self.assertTupleEqual((5, 5, 0), Regions.get_grid_dimensions(25))
+ self.assertTupleEqual((5, 6, 1), Regions.get_grid_dimensions(29))
+ self.assertTupleEqual((5, 7, 2), Regions.get_grid_dimensions(33))
+
+
+class TestGridGeneration(Sc2TestBase):
+ options = {
+ "mission_order": Options.MissionOrder.option_grid,
+ "excluded_missions": [MissionTables.SC2Mission.ZERO_HOUR.mission_name,],
+ "enable_hots_missions": False,
+ "enable_prophecy_missions": True,
+ "enable_lotv_prologue_missions": False,
+ "enable_lotv_missions": False,
+ "enable_epilogue_missions": False,
+ "enable_nco_missions": False
+ }
+
+ def test_size_matches_exclusions(self):
+ self.assertNotIn(MissionTables.SC2Mission.ZERO_HOUR.mission_name, self.multiworld.regions)
+ # WoL has 29 missions. -1 for Zero Hour being excluded, +1 for the automatically-added menu location
+ self.assertEqual(len(self.multiworld.regions), 29)
diff --git a/worlds/sc2/test/test_base.py b/worlds/sc2/test/test_base.py
new file mode 100644
index 000000000000..28529e37edd5
--- /dev/null
+++ b/worlds/sc2/test/test_base.py
@@ -0,0 +1,11 @@
+from typing import *
+
+from test.TestBase import WorldTestBase
+from .. import SC2World
+from .. import Client
+
+class Sc2TestBase(WorldTestBase):
+ game = Client.SC2Context.game
+ world: SC2World
+ player: ClassVar[int] = 1
+ skip_long_tests: bool = True
diff --git a/worlds/sc2/test/test_options.py b/worlds/sc2/test/test_options.py
new file mode 100644
index 000000000000..30d21f39697e
--- /dev/null
+++ b/worlds/sc2/test/test_options.py
@@ -0,0 +1,7 @@
+import unittest
+from .test_base import Sc2TestBase
+from .. import Options, MissionTables
+
+class TestOptions(unittest.TestCase):
+ def test_campaign_size_option_max_matches_number_of_missions(self):
+ self.assertEqual(Options.MaximumCampaignSize.range_end, len(MissionTables.SC2Mission))
diff --git a/worlds/sc2wol/Client.py b/worlds/sc2wol/Client.py
deleted file mode 100644
index 3dbd2047debd..000000000000
--- a/worlds/sc2wol/Client.py
+++ /dev/null
@@ -1,1216 +0,0 @@
-from __future__ import annotations
-
-import asyncio
-import copy
-import ctypes
-import json
-import logging
-import multiprocessing
-import os.path
-import re
-import sys
-import tempfile
-import typing
-import queue
-import zipfile
-import io
-import random
-from pathlib import Path
-
-# CommonClient import first to trigger ModuleUpdater
-from CommonClient import CommonContext, server_loop, ClientCommandProcessor, gui_enabled, get_base_parser
-from Utils import init_logging, is_windows
-
-if __name__ == "__main__":
- init_logging("SC2Client", exception_logger="Client")
-
-logger = logging.getLogger("Client")
-sc2_logger = logging.getLogger("Starcraft2")
-
-import nest_asyncio
-from worlds._sc2common import bot
-from worlds._sc2common.bot.data import Race
-from worlds._sc2common.bot.main import run_game
-from worlds._sc2common.bot.player import Bot
-from worlds.sc2wol import SC2WoLWorld
-from worlds.sc2wol.Items import lookup_id_to_name, get_full_item_list, ItemData, type_flaggroups, upgrade_numbers
-from worlds.sc2wol.Locations import SC2WOL_LOC_ID_OFFSET
-from worlds.sc2wol.MissionTables import lookup_id_to_mission
-from worlds.sc2wol.Regions import MissionInfo
-
-import colorama
-from NetUtils import ClientStatus, NetworkItem, RawJSONtoTextParser, JSONtoTextParser, JSONMessagePart
-from MultiServer import mark_raw
-
-loop = asyncio.get_event_loop_policy().new_event_loop()
-nest_asyncio.apply(loop)
-max_bonus: int = 13
-victory_modulo: int = 100
-
-# GitHub repo where the Map/mod data is hosted for /download_data command
-DATA_REPO_OWNER = "Ziktofel"
-DATA_REPO_NAME = "Archipelago-SC2-data"
-DATA_API_VERSION = "API2"
-
-
-# Data version file path.
-# This file is used to tell if the downloaded data are outdated
-# Associated with /download_data command
-def get_metadata_file():
- return os.environ["SC2PATH"] + os.sep + "ArchipelagoSC2Metadata.txt"
-
-
-class StarcraftClientProcessor(ClientCommandProcessor):
- ctx: SC2Context
-
- def _cmd_difficulty(self, difficulty: str = "") -> bool:
- """Overrides the current difficulty set for the world. Takes the argument casual, normal, hard, or brutal"""
- options = difficulty.split()
- num_options = len(options)
-
- if num_options > 0:
- difficulty_choice = options[0].lower()
- if difficulty_choice == "casual":
- self.ctx.difficulty_override = 0
- elif difficulty_choice == "normal":
- self.ctx.difficulty_override = 1
- elif difficulty_choice == "hard":
- self.ctx.difficulty_override = 2
- elif difficulty_choice == "brutal":
- self.ctx.difficulty_override = 3
- else:
- self.output("Unable to parse difficulty '" + options[0] + "'")
- return False
-
- self.output("Difficulty set to " + options[0])
- return True
-
- else:
- if self.ctx.difficulty == -1:
- self.output("Please connect to a seed before checking difficulty.")
- else:
- current_difficulty = self.ctx.difficulty
- if self.ctx.difficulty_override >= 0:
- current_difficulty = self.ctx.difficulty_override
- self.output("Current difficulty: " + ["Casual", "Normal", "Hard", "Brutal"][current_difficulty])
- self.output("To change the difficulty, add the name of the difficulty after the command.")
- return False
-
-
- def _cmd_game_speed(self, game_speed: str = "") -> bool:
- """Overrides the current game speed for the world.
- Takes the arguments default, slower, slow, normal, fast, faster"""
- options = game_speed.split()
- num_options = len(options)
-
- if num_options > 0:
- speed_choice = options[0].lower()
- if speed_choice == "default":
- self.ctx.game_speed_override = 0
- elif speed_choice == "slower":
- self.ctx.game_speed_override = 1
- elif speed_choice == "slow":
- self.ctx.game_speed_override = 2
- elif speed_choice == "normal":
- self.ctx.game_speed_override = 3
- elif speed_choice == "fast":
- self.ctx.game_speed_override = 4
- elif speed_choice == "faster":
- self.ctx.game_speed_override = 5
- else:
- self.output("Unable to parse game speed '" + options[0] + "'")
- return False
-
- self.output("Game speed set to " + options[0])
- return True
-
- else:
- if self.ctx.game_speed == -1:
- self.output("Please connect to a seed before checking game speed.")
- else:
- current_speed = self.ctx.game_speed
- if self.ctx.game_speed_override >= 0:
- current_speed = self.ctx.game_speed_override
- self.output("Current game speed: "
- + ["Default", "Slower", "Slow", "Normal", "Fast", "Faster"][current_speed])
- self.output("To change the game speed, add the name of the speed after the command,"
- " or Default to select based on difficulty.")
- return False
-
- def _cmd_color(self, color: str = "") -> bool:
- player_colors = [
- "White", "Red", "Blue", "Teal",
- "Purple", "Yellow", "Orange", "Green",
- "LightPink", "Violet", "LightGrey", "DarkGreen",
- "Brown", "LightGreen", "DarkGrey", "Pink",
- "Rainbow", "Random", "Default"
- ]
- match_colors = [player_color.lower() for player_color in player_colors]
- if color:
- if color.lower() not in match_colors:
- self.output(color + " is not a valid color. Available colors: " + ', '.join(player_colors))
- return False
- if color.lower() == "random":
- color = random.choice(player_colors[:16])
- self.ctx.player_color = match_colors.index(color.lower())
- self.output("Color set to " + player_colors[self.ctx.player_color])
- else:
- self.output("Current player color: " + player_colors[self.ctx.player_color])
- self.output("To change your colors, add the name of the color after the command.")
- self.output("Available colors: " + ', '.join(player_colors))
-
- def _cmd_disable_mission_check(self) -> bool:
- """Disables the check to see if a mission is available to play. Meant for co-op runs where one player can play
- the next mission in a chain the other player is doing."""
- self.ctx.missions_unlocked = True
- sc2_logger.info("Mission check has been disabled")
- return True
-
- def _cmd_play(self, mission_id: str = "") -> bool:
- """Start a Starcraft 2 mission"""
-
- options = mission_id.split()
- num_options = len(options)
-
- if num_options > 0:
- mission_number = int(options[0])
-
- self.ctx.play_mission(mission_number)
-
- else:
- sc2_logger.info(
- "Mission ID needs to be specified. Use /unfinished or /available to view ids for available missions.")
- return False
-
- return True
-
- def _cmd_available(self) -> bool:
- """Get what missions are currently available to play"""
-
- request_available_missions(self.ctx)
- return True
-
- def _cmd_unfinished(self) -> bool:
- """Get what missions are currently available to play and have not had all locations checked"""
-
- request_unfinished_missions(self.ctx)
- return True
-
- @mark_raw
- def _cmd_set_path(self, path: str = '') -> bool:
- """Manually set the SC2 install directory (if the automatic detection fails)."""
- if path:
- os.environ["SC2PATH"] = path
- is_mod_installed_correctly()
- return True
- else:
- sc2_logger.warning("When using set_path, you must type the path to your SC2 install directory.")
- return False
-
- def _cmd_download_data(self) -> bool:
- """Download the most recent release of the necessary files for playing SC2 with
- Archipelago. Will overwrite existing files."""
- if "SC2PATH" not in os.environ:
- check_game_install_path()
-
- if os.path.exists(get_metadata_file()):
- with open(get_metadata_file(), "r") as f:
- metadata = f.read()
- else:
- metadata = None
-
- tempzip, metadata = download_latest_release_zip(DATA_REPO_OWNER, DATA_REPO_NAME, DATA_API_VERSION,
- metadata=metadata, force_download=True)
-
- if tempzip != '':
- try:
- zipfile.ZipFile(tempzip).extractall(path=os.environ["SC2PATH"])
- sc2_logger.info(f"Download complete. Package installed.")
- with open(get_metadata_file(), "w") as f:
- f.write(metadata)
- finally:
- os.remove(tempzip)
- else:
- sc2_logger.warning("Download aborted/failed. Read the log for more information.")
- return False
- return True
-
-
-class SC2JSONtoTextParser(JSONtoTextParser):
- def __init__(self, ctx):
- self.handlers = {
- "ItemSend": self._handle_color,
- "ItemCheat": self._handle_color,
- "Hint": self._handle_color,
- }
- super().__init__(ctx)
-
- def _handle_color(self, node: JSONMessagePart):
- codes = node["color"].split(";")
- buffer = "".join(self.color_code(code) for code in codes if code in self.color_codes)
- return buffer + self._handle_text(node) + ''
-
- def color_code(self, code: str):
- return ''
-
-
-class SC2Context(CommonContext):
- command_processor = StarcraftClientProcessor
- game = "Starcraft 2 Wings of Liberty"
- items_handling = 0b111
- difficulty = -1
- game_speed = -1
- all_in_choice = 0
- mission_order = 0
- player_color = 2
- mission_req_table: typing.Dict[str, MissionInfo] = {}
- final_mission: int = 29
- announcements = queue.Queue()
- sc2_run_task: typing.Optional[asyncio.Task] = None
- missions_unlocked: bool = False # allow launching missions ignoring requirements
- generic_upgrade_missions = 0
- generic_upgrade_research = 0
- generic_upgrade_items = 0
- current_tooltip = None
- last_loc_list = None
- difficulty_override = -1
- game_speed_override = -1
- mission_id_to_location_ids: typing.Dict[int, typing.List[int]] = {}
- last_bot: typing.Optional[ArchipelagoBot] = None
-
- def __init__(self, *args, **kwargs):
- super(SC2Context, self).__init__(*args, **kwargs)
- self.raw_text_parser = SC2JSONtoTextParser(self)
-
- async def server_auth(self, password_requested: bool = False):
- if password_requested and not self.password:
- await super(SC2Context, self).server_auth(password_requested)
- await self.get_username()
- await self.send_connect()
- if self.ui:
- self.ui.first_check = True
-
- def on_package(self, cmd: str, args: dict):
- if cmd in {"Connected"}:
- self.difficulty = args["slot_data"]["game_difficulty"]
- if "game_speed" in args["slot_data"]:
- self.game_speed = args["slot_data"]["game_speed"]
- else:
- self.game_speed = 0
- self.all_in_choice = args["slot_data"]["all_in_map"]
- slot_req_table = args["slot_data"]["mission_req"]
- # Maintaining backwards compatibility with older slot data
- self.mission_req_table = {
- mission: MissionInfo(
- **{field: value for field, value in mission_info.items() if field in MissionInfo._fields}
- )
- for mission, mission_info in slot_req_table.items()
- }
- self.mission_order = args["slot_data"].get("mission_order", 0)
- self.final_mission = args["slot_data"].get("final_mission", 29)
- self.player_color = args["slot_data"].get("player_color", 2)
- self.generic_upgrade_missions = args["slot_data"].get("generic_upgrade_missions", 0)
- self.generic_upgrade_items = args["slot_data"].get("generic_upgrade_items", 0)
- self.generic_upgrade_research = args["slot_data"].get("generic_upgrade_research", 0)
-
- self.build_location_to_mission_mapping()
-
- # Looks for the required maps and mods for SC2. Runs check_game_install_path.
- maps_present = is_mod_installed_correctly()
- if os.path.exists(get_metadata_file()):
- with open(get_metadata_file(), "r") as f:
- current_ver = f.read()
- sc2_logger.debug(f"Current version: {current_ver}")
- if is_mod_update_available(DATA_REPO_OWNER, DATA_REPO_NAME, DATA_API_VERSION, current_ver):
- sc2_logger.info("NOTICE: Update for required files found. Run /download_data to install.")
- elif maps_present:
- sc2_logger.warning("NOTICE: Your map files may be outdated (version number not found). "
- "Run /download_data to update them.")
-
-
- def on_print_json(self, args: dict):
- # goes to this world
- if "receiving" in args and self.slot_concerns_self(args["receiving"]):
- relevant = True
- # found in this world
- elif "item" in args and self.slot_concerns_self(args["item"].player):
- relevant = True
- # not related
- else:
- relevant = False
-
- if relevant:
- self.announcements.put(self.raw_text_parser(copy.deepcopy(args["data"])))
-
- super(SC2Context, self).on_print_json(args)
-
- def run_gui(self):
- from kvui import GameManager, HoverBehavior, ServerToolTip
- from kivy.app import App
- from kivy.clock import Clock
- from kivy.uix.tabbedpanel import TabbedPanelItem
- from kivy.uix.gridlayout import GridLayout
- from kivy.lang import Builder
- from kivy.uix.label import Label
- from kivy.uix.button import Button
- from kivy.uix.floatlayout import FloatLayout
- from kivy.properties import StringProperty
-
- class HoverableButton(HoverBehavior, Button):
- pass
-
- class MissionButton(HoverableButton):
- tooltip_text = StringProperty("Test")
- ctx: SC2Context
-
- def __init__(self, *args, **kwargs):
- super(HoverableButton, self).__init__(*args, **kwargs)
- self.layout = FloatLayout()
- self.popuplabel = ServerToolTip(text=self.text)
- self.layout.add_widget(self.popuplabel)
-
- def on_enter(self):
- self.popuplabel.text = self.tooltip_text
-
- if self.ctx.current_tooltip:
- App.get_running_app().root.remove_widget(self.ctx.current_tooltip)
-
- if self.tooltip_text == "":
- self.ctx.current_tooltip = None
- else:
- App.get_running_app().root.add_widget(self.layout)
- self.ctx.current_tooltip = self.layout
-
- def on_leave(self):
- self.ctx.ui.clear_tooltip()
-
- @property
- def ctx(self) -> CommonContext:
- return App.get_running_app().ctx
-
- class MissionLayout(GridLayout):
- pass
-
- class MissionCategory(GridLayout):
- pass
-
- class SC2Manager(GameManager):
- logging_pairs = [
- ("Client", "Archipelago"),
- ("Starcraft2", "Starcraft2"),
- ]
- base_title = "Archipelago Starcraft 2 Client"
-
- mission_panel = None
- last_checked_locations = {}
- mission_id_to_button = {}
- launching: typing.Union[bool, int] = False # if int -> mission ID
- refresh_from_launching = True
- first_check = True
- ctx: SC2Context
-
- def __init__(self, ctx):
- super().__init__(ctx)
-
- def clear_tooltip(self):
- if self.ctx.current_tooltip:
- App.get_running_app().root.remove_widget(self.ctx.current_tooltip)
-
- self.ctx.current_tooltip = None
-
- def build(self):
- container = super().build()
-
- panel = TabbedPanelItem(text="Starcraft 2 Launcher")
- self.mission_panel = panel.content = MissionLayout()
-
- self.tabs.add_widget(panel)
-
- Clock.schedule_interval(self.build_mission_table, 0.5)
-
- return container
-
- def build_mission_table(self, dt):
- if (not self.launching and (not self.last_checked_locations == self.ctx.checked_locations or
- not self.refresh_from_launching)) or self.first_check:
- self.refresh_from_launching = True
-
- self.mission_panel.clear_widgets()
- if self.ctx.mission_req_table:
- self.last_checked_locations = self.ctx.checked_locations.copy()
- self.first_check = False
-
- self.mission_id_to_button = {}
- categories = {}
- available_missions, unfinished_missions = calc_unfinished_missions(self.ctx)
-
- # separate missions into categories
- for mission in self.ctx.mission_req_table:
- if not self.ctx.mission_req_table[mission].category in categories:
- categories[self.ctx.mission_req_table[mission].category] = []
-
- categories[self.ctx.mission_req_table[mission].category].append(mission)
-
- for category in categories:
- category_panel = MissionCategory()
- if category.startswith('_'):
- category_display_name = ''
- else:
- category_display_name = category
- category_panel.add_widget(
- Label(text=category_display_name, size_hint_y=None, height=50, outline_width=1))
-
- for mission in categories[category]:
- text: str = mission
- tooltip: str = ""
- mission_id: int = self.ctx.mission_req_table[mission].id
- # Map has uncollected locations
- if mission in unfinished_missions:
- text = f"[color=6495ED]{text}[/color]"
- elif mission in available_missions:
- text = f"[color=FFFFFF]{text}[/color]"
- # Map requirements not met
- else:
- text = f"[color=a9a9a9]{text}[/color]"
- tooltip = f"Requires: "
- if self.ctx.mission_req_table[mission].required_world:
- tooltip += ", ".join(list(self.ctx.mission_req_table)[req_mission - 1] for
- req_mission in
- self.ctx.mission_req_table[mission].required_world)
-
- if self.ctx.mission_req_table[mission].number:
- tooltip += " and "
- if self.ctx.mission_req_table[mission].number:
- tooltip += f"{self.ctx.mission_req_table[mission].number} missions completed"
- remaining_location_names: typing.List[str] = [
- self.ctx.location_names[loc] for loc in self.ctx.locations_for_mission(mission)
- if loc in self.ctx.missing_locations]
-
- if mission_id == self.ctx.final_mission:
- if mission in available_missions:
- text = f"[color=FFBC95]{mission}[/color]"
- else:
- text = f"[color=D0C0BE]{mission}[/color]"
- if tooltip:
- tooltip += "\n"
- tooltip += "Final Mission"
-
- if remaining_location_names:
- if tooltip:
- tooltip += "\n"
- tooltip += f"Uncollected locations:\n"
- tooltip += "\n".join(remaining_location_names)
-
- mission_button = MissionButton(text=text, size_hint_y=None, height=50)
- mission_button.tooltip_text = tooltip
- mission_button.bind(on_press=self.mission_callback)
- self.mission_id_to_button[mission_id] = mission_button
- category_panel.add_widget(mission_button)
-
- category_panel.add_widget(Label(text=""))
- self.mission_panel.add_widget(category_panel)
-
- elif self.launching:
- self.refresh_from_launching = False
-
- self.mission_panel.clear_widgets()
- self.mission_panel.add_widget(Label(text="Launching Mission: " +
- lookup_id_to_mission[self.launching]))
- if self.ctx.ui:
- self.ctx.ui.clear_tooltip()
-
- def mission_callback(self, button):
- if not self.launching:
- mission_id: int = next(k for k, v in self.mission_id_to_button.items() if v == button)
- if self.ctx.play_mission(mission_id):
- self.launching = mission_id
- Clock.schedule_once(self.finish_launching, 10)
-
- def finish_launching(self, dt):
- self.launching = False
-
- self.ui = SC2Manager(self)
- self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI")
- import pkgutil
- data = pkgutil.get_data(SC2WoLWorld.__module__, "Starcraft2.kv").decode()
- Builder.load_string(data)
-
- async def shutdown(self):
- await super(SC2Context, self).shutdown()
- if self.last_bot:
- self.last_bot.want_close = True
- if self.sc2_run_task:
- self.sc2_run_task.cancel()
-
- def play_mission(self, mission_id: int) -> bool:
- if self.missions_unlocked or \
- is_mission_available(self, mission_id):
- if self.sc2_run_task:
- if not self.sc2_run_task.done():
- sc2_logger.warning("Starcraft 2 Client is still running!")
- self.sc2_run_task.cancel() # doesn't actually close the game, just stops the python task
- if self.slot is None:
- sc2_logger.warning("Launching Mission without Archipelago authentication, "
- "checks will not be registered to server.")
- self.sc2_run_task = asyncio.create_task(starcraft_launch(self, mission_id),
- name="Starcraft 2 Launch")
- return True
- else:
- sc2_logger.info(
- f"{lookup_id_to_mission[mission_id]} is not currently unlocked. "
- f"Use /unfinished or /available to see what is available.")
- return False
-
- def build_location_to_mission_mapping(self):
- mission_id_to_location_ids: typing.Dict[int, typing.Set[int]] = {
- mission_info.id: set() for mission_info in self.mission_req_table.values()
- }
-
- for loc in self.server_locations:
- mission_id, objective = divmod(loc - SC2WOL_LOC_ID_OFFSET, victory_modulo)
- mission_id_to_location_ids[mission_id].add(objective)
- self.mission_id_to_location_ids = {mission_id: sorted(objectives) for mission_id, objectives in
- mission_id_to_location_ids.items()}
-
- def locations_for_mission(self, mission: str):
- mission_id: int = self.mission_req_table[mission].id
- objectives = self.mission_id_to_location_ids[self.mission_req_table[mission].id]
- for objective in objectives:
- yield SC2WOL_LOC_ID_OFFSET + mission_id * 100 + objective
-
-
-async def main():
- multiprocessing.freeze_support()
- parser = get_base_parser()
- parser.add_argument('--name', default=None, help="Slot Name to connect as.")
- args = parser.parse_args()
-
- ctx = SC2Context(args.connect, args.password)
- ctx.auth = args.name
- if ctx.server_task is None:
- ctx.server_task = asyncio.create_task(server_loop(ctx), name="ServerLoop")
-
- if gui_enabled:
- ctx.run_gui()
- ctx.run_cli()
-
- await ctx.exit_event.wait()
-
- await ctx.shutdown()
-
-
-maps_table = [
- "ap_liberation_day", "ap_the_outlaws", "ap_zero_hour",
- "ap_evacuation", "ap_outbreak", "ap_safe_haven", "ap_havens_fall",
- "ap_smash_and_grab", "ap_the_dig", "ap_the_moebius_factor", "ap_supernova", "ap_maw_of_the_void",
- "ap_devils_playground", "ap_welcome_to_the_jungle", "ap_breakout", "ap_ghost_of_a_chance",
- "ap_the_great_train_robbery", "ap_cutthroat", "ap_engine_of_destruction", "ap_media_blitz", "ap_piercing_the_shroud",
- "ap_whispers_of_doom", "ap_a_sinister_turn", "ap_echoes_of_the_future", "ap_in_utter_darkness",
- "ap_gates_of_hell", "ap_belly_of_the_beast", "ap_shatter_the_sky", "ap_all_in"
-]
-
-wol_default_categories = [
- "Mar Sara", "Mar Sara", "Mar Sara", "Colonist", "Colonist", "Colonist", "Colonist",
- "Artifact", "Artifact", "Artifact", "Artifact", "Artifact", "Covert", "Covert", "Covert", "Covert",
- "Rebellion", "Rebellion", "Rebellion", "Rebellion", "Rebellion", "Prophecy", "Prophecy", "Prophecy", "Prophecy",
- "Char", "Char", "Char", "Char"
-]
-wol_default_category_names = [
- "Mar Sara", "Colonist", "Artifact", "Covert", "Rebellion", "Prophecy", "Char"
-]
-
-
-def calculate_items(ctx: SC2Context) -> typing.List[int]:
- items = ctx.items_received
- network_item: NetworkItem
- accumulators: typing.List[int] = [0 for _ in type_flaggroups]
-
- for network_item in items:
- name: str = lookup_id_to_name[network_item.item]
- item_data: ItemData = get_full_item_list()[name]
-
- # exists exactly once
- if item_data.quantity == 1:
- accumulators[type_flaggroups[item_data.type]] |= 1 << item_data.number
-
- # exists multiple times
- elif item_data.type == "Upgrade" or item_data.type == "Progressive Upgrade":
- flaggroup = type_flaggroups[item_data.type]
-
- # Generic upgrades apply only to Weapon / Armor upgrades
- if item_data.type != "Upgrade" or ctx.generic_upgrade_items == 0:
- accumulators[flaggroup] += 1 << item_data.number
- else:
- for bundled_number in upgrade_numbers[item_data.number]:
- accumulators[flaggroup] += 1 << bundled_number
-
- # sum
- else:
- accumulators[type_flaggroups[item_data.type]] += item_data.number
-
- # Upgrades from completed missions
- if ctx.generic_upgrade_missions > 0:
- upgrade_flaggroup = type_flaggroups["Upgrade"]
- num_missions = ctx.generic_upgrade_missions * len(ctx.mission_req_table)
- amounts = [
- num_missions // 100,
- 2 * num_missions // 100,
- 3 * num_missions // 100
- ]
- upgrade_count = 0
- completed = len([id for id in ctx.mission_id_to_location_ids if SC2WOL_LOC_ID_OFFSET + victory_modulo * id in ctx.checked_locations])
- for amount in amounts:
- if completed >= amount:
- upgrade_count += 1
- # Equivalent to "Progressive Weapon/Armor Upgrade" item
- for bundled_number in upgrade_numbers[5]:
- accumulators[upgrade_flaggroup] += upgrade_count << bundled_number
-
- return accumulators
-
-
-def calc_difficulty(difficulty):
- if difficulty == 0:
- return 'C'
- elif difficulty == 1:
- return 'N'
- elif difficulty == 2:
- return 'H'
- elif difficulty == 3:
- return 'B'
-
- return 'X'
-
-
-async def starcraft_launch(ctx: SC2Context, mission_id: int):
- sc2_logger.info(f"Launching {lookup_id_to_mission[mission_id]}. If game does not launch check log file for errors.")
-
- with DllDirectory(None):
- run_game(bot.maps.get(maps_table[mission_id - 1]), [Bot(Race.Terran, ArchipelagoBot(ctx, mission_id),
- name="Archipelago", fullscreen=True)], realtime=True)
-
-
-class ArchipelagoBot(bot.bot_ai.BotAI):
- game_running: bool = False
- mission_completed: bool = False
- boni: typing.List[bool]
- setup_done: bool
- ctx: SC2Context
- mission_id: int
- want_close: bool = False
- can_read_game = False
- last_received_update: int = 0
-
- def __init__(self, ctx: SC2Context, mission_id):
- self.setup_done = False
- self.ctx = ctx
- self.ctx.last_bot = self
- self.mission_id = mission_id
- self.boni = [False for _ in range(max_bonus)]
-
- super(ArchipelagoBot, self).__init__()
-
- async def on_step(self, iteration: int):
- if self.want_close:
- self.want_close = False
- await self._client.leave()
- return
- game_state = 0
- if not self.setup_done:
- self.setup_done = True
- start_items = calculate_items(self.ctx)
- if self.ctx.difficulty_override >= 0:
- difficulty = calc_difficulty(self.ctx.difficulty_override)
- else:
- difficulty = calc_difficulty(self.ctx.difficulty)
- if self.ctx.game_speed_override >= 0:
- game_speed = self.ctx.game_speed_override
- else:
- game_speed = self.ctx.game_speed
- await self.chat_send("?SetOptions {} {} {} {}".format(
- difficulty,
- self.ctx.generic_upgrade_research,
- self.ctx.all_in_choice,
- game_speed
- ))
- await self.chat_send("?GiveResources {} {} {}".format(
- start_items[8],
- start_items[9],
- start_items[10]
- ))
- await self.chat_send("?GiveTerranTech {} {} {} {} {} {} {} {} {} {}".format(
- start_items[0], start_items[1], start_items[2], start_items[3], start_items[4],
- start_items[5], start_items[6], start_items[12], start_items[13], start_items[14]))
- await self.chat_send("?GiveProtossTech {}".format(start_items[7]))
- await self.chat_send("?SetColor rr " + str(self.ctx.player_color)) # TODO: Add faction color options
- await self.chat_send("?LoadFinished")
- self.last_received_update = len(self.ctx.items_received)
-
- else:
- if not self.ctx.announcements.empty():
- message = self.ctx.announcements.get(timeout=1)
- await self.chat_send("?SendMessage " + message)
- self.ctx.announcements.task_done()
-
- # Archipelago reads the health
- for unit in self.all_own_units():
- if unit.health_max == 38281:
- game_state = int(38281 - unit.health)
- self.can_read_game = True
-
- if iteration == 160 and not game_state & 1:
- await self.chat_send("?SendMessage Warning: Archipelago unable to connect or has lost connection to " +
- "Starcraft 2 (This is likely a map issue)")
-
- if self.last_received_update < len(self.ctx.items_received):
- current_items = calculate_items(self.ctx)
- await self.chat_send("?GiveTerranTech {} {} {} {} {} {} {} {} {} {}".format(
- current_items[0], current_items[1], current_items[2], current_items[3], current_items[4],
- current_items[5], current_items[6], current_items[12], current_items[13], current_items[14]))
- await self.chat_send("?GiveProtossTech {}".format(current_items[7]))
- self.last_received_update = len(self.ctx.items_received)
-
- if game_state & 1:
- if not self.game_running:
- print("Archipelago Connected")
- self.game_running = True
-
- if self.can_read_game:
- if game_state & (1 << 1) and not self.mission_completed:
- if self.mission_id != self.ctx.final_mission:
- print("Mission Completed")
- await self.ctx.send_msgs(
- [{"cmd": 'LocationChecks',
- "locations": [SC2WOL_LOC_ID_OFFSET + victory_modulo * self.mission_id]}])
- self.mission_completed = True
- else:
- print("Game Complete")
- await self.ctx.send_msgs([{"cmd": 'StatusUpdate', "status": ClientStatus.CLIENT_GOAL}])
- self.mission_completed = True
-
- for x, completed in enumerate(self.boni):
- if not completed and game_state & (1 << (x + 2)):
- await self.ctx.send_msgs(
- [{"cmd": 'LocationChecks',
- "locations": [SC2WOL_LOC_ID_OFFSET + victory_modulo * self.mission_id + x + 1]}])
- self.boni[x] = True
-
- else:
- await self.chat_send("?SendMessage LostConnection - Lost connection to game.")
-
-
-def request_unfinished_missions(ctx: SC2Context):
- if ctx.mission_req_table:
- message = "Unfinished Missions: "
- unlocks = initialize_blank_mission_dict(ctx.mission_req_table)
- unfinished_locations = initialize_blank_mission_dict(ctx.mission_req_table)
-
- _, unfinished_missions = calc_unfinished_missions(ctx, unlocks=unlocks)
-
- # Removing All-In from location pool
- final_mission = lookup_id_to_mission[ctx.final_mission]
- if final_mission in unfinished_missions.keys():
- message = f"Final Mission Available: {final_mission}[{ctx.final_mission}]\n" + message
- if unfinished_missions[final_mission] == -1:
- unfinished_missions.pop(final_mission)
-
- message += ", ".join(f"{mark_up_mission_name(ctx, mission, unlocks)}[{ctx.mission_req_table[mission].id}] " +
- mark_up_objectives(
- f"[{len(unfinished_missions[mission])}/"
- f"{sum(1 for _ in ctx.locations_for_mission(mission))}]",
- ctx, unfinished_locations, mission)
- for mission in unfinished_missions)
-
- if ctx.ui:
- ctx.ui.log_panels['All'].on_message_markup(message)
- ctx.ui.log_panels['Starcraft2'].on_message_markup(message)
- else:
- sc2_logger.info(message)
- else:
- sc2_logger.warning("No mission table found, you are likely not connected to a server.")
-
-
-def calc_unfinished_missions(ctx: SC2Context, unlocks=None):
- unfinished_missions = []
- locations_completed = []
-
- if not unlocks:
- unlocks = initialize_blank_mission_dict(ctx.mission_req_table)
-
- available_missions = calc_available_missions(ctx, unlocks)
-
- for name in available_missions:
- objectives = set(ctx.locations_for_mission(name))
- if objectives:
- objectives_completed = ctx.checked_locations & objectives
- if len(objectives_completed) < len(objectives):
- unfinished_missions.append(name)
- locations_completed.append(objectives_completed)
-
- else: # infer that this is the final mission as it has no objectives
- unfinished_missions.append(name)
- locations_completed.append(-1)
-
- return available_missions, dict(zip(unfinished_missions, locations_completed))
-
-
-def is_mission_available(ctx: SC2Context, mission_id_to_check):
- unfinished_missions = calc_available_missions(ctx)
-
- return any(mission_id_to_check == ctx.mission_req_table[mission].id for mission in unfinished_missions)
-
-
-def mark_up_mission_name(ctx: SC2Context, mission, unlock_table):
- """Checks if the mission is required for game completion and adds '*' to the name to mark that."""
-
- if ctx.mission_req_table[mission].completion_critical:
- if ctx.ui:
- message = "[color=AF99EF]" + mission + "[/color]"
- else:
- message = "*" + mission + "*"
- else:
- message = mission
-
- if ctx.ui:
- unlocks = unlock_table[mission]
-
- if len(unlocks) > 0:
- pre_message = f"[ref={list(ctx.mission_req_table).index(mission)}|Unlocks: "
- pre_message += ", ".join(f"{unlock}({ctx.mission_req_table[unlock].id})" for unlock in unlocks)
- pre_message += f"]"
- message = pre_message + message + "[/ref]"
-
- return message
-
-
-def mark_up_objectives(message, ctx, unfinished_locations, mission):
- formatted_message = message
-
- if ctx.ui:
- locations = unfinished_locations[mission]
-
- pre_message = f"[ref={list(ctx.mission_req_table).index(mission) + 30}|"
- pre_message += " ".join(location for location in locations)
- pre_message += f"]"
- formatted_message = pre_message + message + "[/ref]"
-
- return formatted_message
-
-
-def request_available_missions(ctx: SC2Context):
- if ctx.mission_req_table:
- message = "Available Missions: "
-
- # Initialize mission unlock table
- unlocks = initialize_blank_mission_dict(ctx.mission_req_table)
-
- missions = calc_available_missions(ctx, unlocks)
- message += \
- ", ".join(f"{mark_up_mission_name(ctx, mission, unlocks)}"
- f"[{ctx.mission_req_table[mission].id}]"
- for mission in missions)
-
- if ctx.ui:
- ctx.ui.log_panels['All'].on_message_markup(message)
- ctx.ui.log_panels['Starcraft2'].on_message_markup(message)
- else:
- sc2_logger.info(message)
- else:
- sc2_logger.warning("No mission table found, you are likely not connected to a server.")
-
-
-def calc_available_missions(ctx: SC2Context, unlocks=None):
- available_missions = []
- missions_complete = 0
-
- # Get number of missions completed
- for loc in ctx.checked_locations:
- if loc % victory_modulo == 0:
- missions_complete += 1
-
- for name in ctx.mission_req_table:
- # Go through the required missions for each mission and fill up unlock table used later for hover-over tooltips
- if unlocks:
- for unlock in ctx.mission_req_table[name].required_world:
- unlocks[list(ctx.mission_req_table)[unlock - 1]].append(name)
-
- if mission_reqs_completed(ctx, name, missions_complete):
- available_missions.append(name)
-
- return available_missions
-
-
-def mission_reqs_completed(ctx: SC2Context, mission_name: str, missions_complete: int):
- """Returns a bool signifying if the mission has all requirements complete and can be done
-
- Arguments:
- ctx -- instance of SC2Context
- locations_to_check -- the mission string name to check
- missions_complete -- an int of how many missions have been completed
- mission_path -- a list of missions that have already been checked
-"""
- if len(ctx.mission_req_table[mission_name].required_world) >= 1:
- # A check for when the requirements are being or'd
- or_success = False
-
- # Loop through required missions
- for req_mission in ctx.mission_req_table[mission_name].required_world:
- req_success = True
-
- # Check if required mission has been completed
- if not (ctx.mission_req_table[list(ctx.mission_req_table)[req_mission - 1]].id *
- victory_modulo + SC2WOL_LOC_ID_OFFSET) in ctx.checked_locations:
- if not ctx.mission_req_table[mission_name].or_requirements:
- return False
- else:
- req_success = False
-
- # Grid-specific logic (to avoid long path checks and infinite recursion)
- if ctx.mission_order in (3, 4):
- if req_success:
- return True
- else:
- if req_mission is ctx.mission_req_table[mission_name].required_world[-1]:
- return False
- else:
- continue
-
- # Recursively check required mission to see if it's requirements are met, in case !collect has been done
- # Skipping recursive check on Grid settings to speed up checks and avoid infinite recursion
- if not mission_reqs_completed(ctx, list(ctx.mission_req_table)[req_mission - 1], missions_complete):
- if not ctx.mission_req_table[mission_name].or_requirements:
- return False
- else:
- req_success = False
-
- # If requirement check succeeded mark or as satisfied
- if ctx.mission_req_table[mission_name].or_requirements and req_success:
- or_success = True
-
- if ctx.mission_req_table[mission_name].or_requirements:
- # Return false if or requirements not met
- if not or_success:
- return False
-
- # Check number of missions
- if missions_complete >= ctx.mission_req_table[mission_name].number:
- return True
- else:
- return False
- else:
- return True
-
-
-def initialize_blank_mission_dict(location_table):
- unlocks = {}
-
- for mission in list(location_table):
- unlocks[mission] = []
-
- return unlocks
-
-
-def check_game_install_path() -> bool:
- # First thing: go to the default location for ExecuteInfo.
- # An exception for Windows is included because it's very difficult to find ~\Documents if the user moved it.
- if is_windows:
- # The next five lines of utterly inscrutable code are brought to you by copy-paste from Stack Overflow.
- # https://stackoverflow.com/questions/6227590/finding-the-users-my-documents-path/30924555#
- import ctypes.wintypes
- CSIDL_PERSONAL = 5 # My Documents
- SHGFP_TYPE_CURRENT = 0 # Get current, not default value
-
- buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
- ctypes.windll.shell32.SHGetFolderPathW(None, CSIDL_PERSONAL, None, SHGFP_TYPE_CURRENT, buf)
- documentspath = buf.value
- einfo = str(documentspath / Path("StarCraft II\\ExecuteInfo.txt"))
- else:
- einfo = str(bot.paths.get_home() / Path(bot.paths.USERPATH[bot.paths.PF]))
-
- # Check if the file exists.
- if os.path.isfile(einfo):
-
- # Open the file and read it, picking out the latest executable's path.
- with open(einfo) as f:
- content = f.read()
- if content:
- try:
- base = re.search(r" = (.*)Versions", content).group(1)
- except AttributeError:
- sc2_logger.warning(f"Found {einfo}, but it was empty. Run SC2 through the Blizzard launcher, then "
- f"try again.")
- return False
- if os.path.exists(base):
- executable = bot.paths.latest_executeble(Path(base).expanduser() / "Versions")
-
- # Finally, check the path for an actual executable.
- # If we find one, great. Set up the SC2PATH.
- if os.path.isfile(executable):
- sc2_logger.info(f"Found an SC2 install at {base}!")
- sc2_logger.debug(f"Latest executable at {executable}.")
- os.environ["SC2PATH"] = base
- sc2_logger.debug(f"SC2PATH set to {base}.")
- return True
- else:
- sc2_logger.warning(f"We may have found an SC2 install at {base}, but couldn't find {executable}.")
- else:
- sc2_logger.warning(f"{einfo} pointed to {base}, but we could not find an SC2 install there.")
- else:
- sc2_logger.warning(f"Couldn't find {einfo}. Run SC2 through the Blizzard launcher, then try again. "
- f"If that fails, please run /set_path with your SC2 install directory.")
- return False
-
-
-def is_mod_installed_correctly() -> bool:
- """Searches for all required files."""
- if "SC2PATH" not in os.environ:
- check_game_install_path()
-
- mapdir = os.environ['SC2PATH'] / Path('Maps/ArchipelagoCampaign')
- mods = ["ArchipelagoCore", "ArchipelagoPlayer", "ArchipelagoPlayerWoL", "ArchipelagoTriggers"]
- modfiles = [os.environ["SC2PATH"] / Path("Mods/" + mod + ".SC2Mod") for mod in mods]
- wol_required_maps = ["WoL" + os.sep + map_name + ".SC2Map" for map_name in maps_table]
- needs_files = False
-
- # Check for maps.
- missing_maps = []
- for mapfile in wol_required_maps:
- if not os.path.isfile(mapdir / mapfile):
- missing_maps.append(mapfile)
- if len(missing_maps) >= 19:
- sc2_logger.warning(f"All map files missing from {mapdir}.")
- needs_files = True
- elif len(missing_maps) > 0:
- for map in missing_maps:
- sc2_logger.debug(f"Missing {map} from {mapdir}.")
- sc2_logger.warning(f"Missing {len(missing_maps)} map files.")
- needs_files = True
- else: # Must be no maps missing
- sc2_logger.info(f"All maps found in {mapdir}.")
-
- # Check for mods.
- for modfile in modfiles:
- if os.path.isfile(modfile) or os.path.isdir(modfile):
- sc2_logger.info(f"Archipelago mod found at {modfile}.")
- else:
- sc2_logger.warning(f"Archipelago mod could not be found at {modfile}.")
- needs_files = True
-
- # Final verdict.
- if needs_files:
- sc2_logger.warning(f"Required files are missing. Run /download_data to acquire them.")
- return False
- else:
- sc2_logger.debug(f"All map/mod files are properly installed.")
- return True
-
-
-class DllDirectory:
- # Credit to Black Sliver for this code.
- # More info: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setdlldirectoryw
- _old: typing.Optional[str] = None
- _new: typing.Optional[str] = None
-
- def __init__(self, new: typing.Optional[str]):
- self._new = new
-
- def __enter__(self):
- old = self.get()
- if self.set(self._new):
- self._old = old
-
- def __exit__(self, *args):
- if self._old is not None:
- self.set(self._old)
-
- @staticmethod
- def get() -> typing.Optional[str]:
- if sys.platform == "win32":
- n = ctypes.windll.kernel32.GetDllDirectoryW(0, None)
- buf = ctypes.create_unicode_buffer(n)
- ctypes.windll.kernel32.GetDllDirectoryW(n, buf)
- return buf.value
- # NOTE: other OS may support os.environ["LD_LIBRARY_PATH"], but this fix is windows-specific
- return None
-
- @staticmethod
- def set(s: typing.Optional[str]) -> bool:
- if sys.platform == "win32":
- return ctypes.windll.kernel32.SetDllDirectoryW(s) != 0
- # NOTE: other OS may support os.environ["LD_LIBRARY_PATH"], but this fix is windows-specific
- return False
-
-
-def download_latest_release_zip(owner: str, repo: str, api_version: str, metadata: str = None, force_download=False) -> (str, str):
- """Downloads the latest release of a GitHub repo to the current directory as a .zip file."""
- import requests
-
- headers = {"Accept": 'application/vnd.github.v3+json'}
- url = f"https://api.github.com/repos/{owner}/{repo}/releases/tags/{api_version}"
-
- r1 = requests.get(url, headers=headers)
- if r1.status_code == 200:
- latest_metadata = r1.json()
- cleanup_downloaded_metadata(latest_metadata)
- latest_metadata = str(latest_metadata)
- # sc2_logger.info(f"Latest version: {latest_metadata}.")
- else:
- sc2_logger.warning(f"Status code: {r1.status_code}")
- sc2_logger.warning(f"Failed to reach GitHub. Could not find download link.")
- sc2_logger.warning(f"text: {r1.text}")
- return "", metadata
-
- if (force_download is False) and (metadata == latest_metadata):
- sc2_logger.info("Latest version already installed.")
- return "", metadata
-
- sc2_logger.info(f"Attempting to download latest version of API version {api_version} of {repo}.")
- download_url = r1.json()["assets"][0]["browser_download_url"]
-
- r2 = requests.get(download_url, headers=headers)
- if r2.status_code == 200 and zipfile.is_zipfile(io.BytesIO(r2.content)):
- tempdir = tempfile.gettempdir()
- file = tempdir + os.sep + f"{repo}.zip"
- with open(file, "wb") as fh:
- fh.write(r2.content)
- sc2_logger.info(f"Successfully downloaded {repo}.zip.")
- return file, latest_metadata
- else:
- sc2_logger.warning(f"Status code: {r2.status_code}")
- sc2_logger.warning("Download failed.")
- sc2_logger.warning(f"text: {r2.text}")
- return "", metadata
-
-
-def cleanup_downloaded_metadata(medatada_json):
- for asset in medatada_json['assets']:
- del asset['download_count']
-
-
-def is_mod_update_available(owner: str, repo: str, api_version: str, metadata: str) -> bool:
- import requests
-
- headers = {"Accept": 'application/vnd.github.v3+json'}
- url = f"https://api.github.com/repos/{owner}/{repo}/releases/tags/{api_version}"
-
- r1 = requests.get(url, headers=headers)
- if r1.status_code == 200:
- latest_metadata = r1.json()
- cleanup_downloaded_metadata(latest_metadata)
- latest_metadata = str(latest_metadata)
- if metadata != latest_metadata:
- return True
- else:
- return False
-
- else:
- sc2_logger.warning(f"Failed to reach GitHub while checking for updates.")
- sc2_logger.warning(f"Status code: {r1.status_code}")
- sc2_logger.warning(f"text: {r1.text}")
- return False
-
-
-def launch():
- colorama.init()
- asyncio.run(main())
- colorama.deinit()
diff --git a/worlds/sc2wol/Items.py b/worlds/sc2wol/Items.py
deleted file mode 100644
index 971a75375fe4..000000000000
--- a/worlds/sc2wol/Items.py
+++ /dev/null
@@ -1,421 +0,0 @@
-from BaseClasses import Item, ItemClassification, MultiWorld
-import typing
-
-from .Options import get_option_value
-from .MissionTables import vanilla_mission_req_table
-
-
-class ItemData(typing.NamedTuple):
- code: typing.Optional[int]
- type: typing.Optional[str]
- number: typing.Optional[int]
- classification: ItemClassification = ItemClassification.useful
- quantity: int = 1
- parent_item: str = None
- origin: typing.Set[str] = {"wol"}
-
-
-class StarcraftWoLItem(Item):
- game: str = "Starcraft 2 Wings of Liberty"
-
-
-def get_full_item_list():
- return item_table
-
-
-SC2WOL_ITEM_ID_OFFSET = 1000
-
-item_table = {
- "Marine": ItemData(0 + SC2WOL_ITEM_ID_OFFSET, "Unit", 0, classification=ItemClassification.progression),
- "Medic": ItemData(1 + SC2WOL_ITEM_ID_OFFSET, "Unit", 1, classification=ItemClassification.progression),
- "Firebat": ItemData(2 + SC2WOL_ITEM_ID_OFFSET, "Unit", 2, classification=ItemClassification.progression),
- "Marauder": ItemData(3 + SC2WOL_ITEM_ID_OFFSET, "Unit", 3, classification=ItemClassification.progression),
- "Reaper": ItemData(4 + SC2WOL_ITEM_ID_OFFSET, "Unit", 4, classification=ItemClassification.progression),
- "Hellion": ItemData(5 + SC2WOL_ITEM_ID_OFFSET, "Unit", 5, classification=ItemClassification.progression),
- "Vulture": ItemData(6 + SC2WOL_ITEM_ID_OFFSET, "Unit", 6, classification=ItemClassification.progression),
- "Goliath": ItemData(7 + SC2WOL_ITEM_ID_OFFSET, "Unit", 7, classification=ItemClassification.progression),
- "Diamondback": ItemData(8 + SC2WOL_ITEM_ID_OFFSET, "Unit", 8, classification=ItemClassification.progression),
- "Siege Tank": ItemData(9 + SC2WOL_ITEM_ID_OFFSET, "Unit", 9, classification=ItemClassification.progression),
- "Medivac": ItemData(10 + SC2WOL_ITEM_ID_OFFSET, "Unit", 10, classification=ItemClassification.progression),
- "Wraith": ItemData(11 + SC2WOL_ITEM_ID_OFFSET, "Unit", 11, classification=ItemClassification.progression),
- "Viking": ItemData(12 + SC2WOL_ITEM_ID_OFFSET, "Unit", 12, classification=ItemClassification.progression),
- "Banshee": ItemData(13 + SC2WOL_ITEM_ID_OFFSET, "Unit", 13, classification=ItemClassification.progression),
- "Battlecruiser": ItemData(14 + SC2WOL_ITEM_ID_OFFSET, "Unit", 14, classification=ItemClassification.progression),
- "Ghost": ItemData(15 + SC2WOL_ITEM_ID_OFFSET, "Unit", 15, classification=ItemClassification.progression),
- "Spectre": ItemData(16 + SC2WOL_ITEM_ID_OFFSET, "Unit", 16, classification=ItemClassification.progression),
- "Thor": ItemData(17 + SC2WOL_ITEM_ID_OFFSET, "Unit", 17, classification=ItemClassification.progression),
- # EE units
- "Liberator": ItemData(18 + SC2WOL_ITEM_ID_OFFSET, "Unit", 18, classification=ItemClassification.progression, origin={"nco", "ext"}),
- "Valkyrie": ItemData(19 + SC2WOL_ITEM_ID_OFFSET, "Unit", 19, classification=ItemClassification.progression, origin={"bw"}),
- "Widow Mine": ItemData(20 + SC2WOL_ITEM_ID_OFFSET, "Unit", 20, classification=ItemClassification.progression, origin={"ext"}),
- "Cyclone": ItemData(21 + SC2WOL_ITEM_ID_OFFSET, "Unit", 21, classification=ItemClassification.progression, origin={"ext"}),
-
- # Some other items are moved to Upgrade group because of the way how the bot message is parsed
- "Progressive Infantry Weapon": ItemData(100 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 0, quantity=3),
- "Progressive Infantry Armor": ItemData(102 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 2, quantity=3),
- "Progressive Vehicle Weapon": ItemData(103 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 4, quantity=3),
- "Progressive Vehicle Armor": ItemData(104 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 6, quantity=3),
- "Progressive Ship Weapon": ItemData(105 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 8, quantity=3),
- "Progressive Ship Armor": ItemData(106 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 10, quantity=3),
- # Upgrade bundle 'number' values are used as indices to get affected 'number's
- "Progressive Weapon Upgrade": ItemData(107 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 0, quantity=3),
- "Progressive Armor Upgrade": ItemData(108 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 1, quantity=3),
- "Progressive Infantry Upgrade": ItemData(109 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 2, quantity=3),
- "Progressive Vehicle Upgrade": ItemData(110 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 3, quantity=3),
- "Progressive Ship Upgrade": ItemData(111 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 4, quantity=3),
- "Progressive Weapon/Armor Upgrade": ItemData(112 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 5, quantity=3),
-
- "Projectile Accelerator (Bunker)": ItemData(200 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 0, parent_item="Bunker"),
- "Neosteel Bunker (Bunker)": ItemData(201 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 1, parent_item="Bunker"),
- "Titanium Housing (Missile Turret)": ItemData(202 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 2, classification=ItemClassification.filler, parent_item="Missile Turret"),
- "Hellstorm Batteries (Missile Turret)": ItemData(203 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 3, parent_item="Missile Turret"),
- "Advanced Construction (SCV)": ItemData(204 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 4),
- "Dual-Fusion Welders (SCV)": ItemData(205 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 5),
- "Fire-Suppression System (Building)": ItemData(206 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 6),
- "Orbital Command (Building)": ItemData(207 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 7),
- "Progressive Stimpack (Marine)": ItemData(208 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 0, parent_item="Marine", quantity=2),
- "Combat Shield (Marine)": ItemData(209 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 9, classification=ItemClassification.progression, parent_item="Marine"),
- "Advanced Medic Facilities (Medic)": ItemData(210 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 10, classification=ItemClassification.filler, parent_item="Medic"),
- "Stabilizer Medpacks (Medic)": ItemData(211 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 11, classification=ItemClassification.progression, parent_item="Medic"),
- "Incinerator Gauntlets (Firebat)": ItemData(212 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 12, classification=ItemClassification.filler, parent_item="Firebat"),
- "Juggernaut Plating (Firebat)": ItemData(213 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 13, parent_item="Firebat"),
- "Concussive Shells (Marauder)": ItemData(214 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 14, parent_item="Marauder"),
- "Kinetic Foam (Marauder)": ItemData(215 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 15, parent_item="Marauder"),
- "U-238 Rounds (Reaper)": ItemData(216 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 16, parent_item="Reaper"),
- "G-4 Clusterbomb (Reaper)": ItemData(217 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 17, classification=ItemClassification.progression, parent_item="Reaper"),
- # Items from EE
- "Mag-Field Accelerators (Cyclone)": ItemData(218 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 18, parent_item="Cyclone", origin={"ext"}),
- "Mag-Field Launchers (Cyclone)": ItemData(219 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 19, parent_item="Cyclone", origin={"ext"}),
- # Items from new mod
- "Laser Targeting System (Marine)": ItemData(220 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 8, classification=ItemClassification.filler, parent_item="Marine", origin={"nco"}), # Freed slot from Stimpack
- "Magrail Munitions (Marine)": ItemData(221 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 20, parent_item="Marine", origin={"nco"}),
- "Optimized Logistics (Marine)": ItemData(222 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 21, classification=ItemClassification.filler, parent_item="Marine", origin={"nco"}),
- "Restoration (Medic)": ItemData(223 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 22, classification=ItemClassification.filler, parent_item="Medic", origin={"bw"}),
- "Optical Flare (Medic)": ItemData(224 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 23, classification=ItemClassification.filler, parent_item="Medic", origin={"bw"}),
- "Optimized Logistics (Medic)": ItemData(225 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 24, classification=ItemClassification.filler, parent_item="Medic", origin={"bw"}),
- "Progressive Stimpack (Firebat)": ItemData(226 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 6, parent_item="Firebat", quantity=2, origin={"bw"}),
- "Optimized Logistics (Firebat)": ItemData(227 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 25, parent_item="Firebat", origin={"bw"}),
- "Progressive Stimpack (Marauder)": ItemData(228 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 8, parent_item="Marauder", quantity=2, origin={"nco"}),
- "Laser Targeting System (Marauder)": ItemData(229 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 26, classification=ItemClassification.filler, parent_item="Marauder", origin={"nco"}),
- "Magrail Munitions (Marauder)": ItemData(230 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 27, classification=ItemClassification.filler, parent_item="Marauder", origin={"nco"}),
- "Internal Tech Module (Marauder)": ItemData(231 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 28, classification=ItemClassification.filler, parent_item="Marauder", origin={"nco"}),
-
- # Items from new mod
- "Progressive Stimpack (Reaper)": ItemData(250 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 10, parent_item="Reaper", quantity=2, origin={"nco"}),
- "Laser Targeting System (Reaper)": ItemData(251 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 0, classification=ItemClassification.filler, parent_item="Reaper", origin={"nco"}),
- "Advanced Cloaking Field (Reaper)": ItemData(252 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 1, parent_item="Reaper", origin={"nco"}),
- "Spider Mines (Reaper)": ItemData(253 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 2, classification=ItemClassification.filler, parent_item="Reaper", origin={"nco"}),
- "Combat Drugs (Reaper)": ItemData(254 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 3, classification=ItemClassification.filler, parent_item="Reaper", origin={"ext"}),
- "Hellbat Aspect (Hellion)": ItemData(255 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 4, parent_item="Hellion", origin={"nco"}),
- "Smart Servos (Hellion)": ItemData(256 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 5, parent_item="Hellion", origin={"nco"}),
- "Optimized Logistics (Hellion)": ItemData(257 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 6, classification=ItemClassification.filler, parent_item="Hellion", origin={"nco"}),
- "Jump Jets (Hellion)": ItemData(258 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 7, classification=ItemClassification.filler, parent_item="Hellion", origin={"nco"}),
- "Progressive Stimpack (Hellion)": ItemData(259 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 12, parent_item="Hellion", quantity=2, origin={"nco"}),
- "Ion Thrusters (Vulture)": ItemData(260 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 8, classification=ItemClassification.filler, parent_item="Vulture", origin={"bw"}),
- "Auto Launchers (Vulture)": ItemData(261 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 9, parent_item="Vulture", origin={"bw"}),
- "High Explosive Munition (Spider Mine)": ItemData(262 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 10, origin={"bw"}),
- "Jump Jets (Goliath)": ItemData(263 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 11, classification=ItemClassification.filler, parent_item="Goliath", origin={"nco"}),
- "Optimized Logistics (Goliath)": ItemData(264 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 12, classification=ItemClassification.filler, parent_item="Goliath", origin={"nco"}),
- "Hyperfluxor (Diamondback)": ItemData(265 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 13, parent_item="Diamondback", origin={"ext"}),
- "Burst Capacitors (Diamondback)": ItemData(266 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 14, classification=ItemClassification.filler, parent_item="Diamondback", origin={"ext"}),
- "Optimized Logistics (Diamondback)": ItemData(267 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 15, parent_item="Diamondback", origin={"ext"}),
- "Jump Jets (Siege Tank)": ItemData(268 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 16, parent_item="Siege Tank", origin={"nco"}),
- "Spider Mines (Siege Tank)": ItemData(269 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 17, classification=ItemClassification.filler, parent_item="Siege Tank", origin={"nco"}),
- "Smart Servos (Siege Tank)": ItemData(270 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 18, classification=ItemClassification.filler, parent_item="Siege Tank", origin={"nco"}),
- "Graduating Range (Siege Tank)": ItemData(271 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 19, classification=ItemClassification.progression, parent_item="Siege Tank", origin={"ext"}),
- "Laser Targeting System (Siege Tank)": ItemData(272 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 20, parent_item="Siege Tank", origin={"nco"}),
- "Advanced Siege Tech (Siege Tank)": ItemData(273 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 21, parent_item="Siege Tank", origin={"ext"}),
- "Internal Tech Module (Siege Tank)": ItemData(274 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 22, classification=ItemClassification.filler, parent_item="Siege Tank", origin={"nco"}),
- "Optimized Logistics (Predator)": ItemData(275 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 23, classification=ItemClassification.filler, parent_item="Predator", origin={"ext"}),
- "Expanded Hull (Medivac)": ItemData(276 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 24, classification=ItemClassification.filler, parent_item="Medivac", origin={"ext"}),
- "Afterburners (Medivac)": ItemData(277 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 25, classification=ItemClassification.filler, parent_item="Medivac", origin={"ext"}),
- "Advanced Laser Technology (Wraith)": ItemData(278 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 26, classification=ItemClassification.progression, parent_item="Wraith", origin={"ext"}),
- "Smart Servos (Viking)": ItemData(279 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 27, parent_item="Viking", origin={"ext"}),
- "Magrail Munitions (Viking)": ItemData(280 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 28, parent_item="Viking", origin={"ext"}),
-
- "Twin-Linked Flamethrower (Hellion)": ItemData(300 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 0, classification=ItemClassification.filler, parent_item="Hellion"),
- "Thermite Filaments (Hellion)": ItemData(301 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 1, parent_item="Hellion"),
- "Cerberus Mine (Spider Mine)": ItemData(302 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 2, classification=ItemClassification.filler),
- "Replenishable Magazine (Vulture)": ItemData(303 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 3, classification=ItemClassification.filler, parent_item="Vulture"),
- "Multi-Lock Weapons System (Goliath)": ItemData(304 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 4, parent_item="Goliath"),
- "Ares-Class Targeting System (Goliath)": ItemData(305 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 5, parent_item="Goliath"),
- "Tri-Lithium Power Cell (Diamondback)": ItemData(306 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 6, classification=ItemClassification.filler, parent_item="Diamondback"),
- "Shaped Hull (Diamondback)": ItemData(307 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 7, classification=ItemClassification.filler, parent_item="Diamondback"),
- "Maelstrom Rounds (Siege Tank)": ItemData(308 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 8, classification=ItemClassification.progression, parent_item="Siege Tank"),
- "Shaped Blast (Siege Tank)": ItemData(309 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 9, parent_item="Siege Tank"),
- "Rapid Deployment Tube (Medivac)": ItemData(310 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 10, classification=ItemClassification.filler, parent_item="Medivac"),
- "Advanced Healing AI (Medivac)": ItemData(311 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 11, classification=ItemClassification.filler, parent_item="Medivac"),
- "Tomahawk Power Cells (Wraith)": ItemData(312 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 12, classification=ItemClassification.filler, parent_item="Wraith"),
- "Displacement Field (Wraith)": ItemData(313 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 13, classification=ItemClassification.filler, parent_item="Wraith"),
- "Ripwave Missiles (Viking)": ItemData(314 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 14, parent_item="Viking"),
- "Phobos-Class Weapons System (Viking)": ItemData(315 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 15, parent_item="Viking"),
- "Progressive Cross-Spectrum Dampeners (Banshee)": ItemData(316 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 2, classification=ItemClassification.filler, parent_item="Banshee", quantity=2),
- "Shockwave Missile Battery (Banshee)": ItemData(317 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 17, parent_item="Banshee"),
- "Missile Pods (Battlecruiser)": ItemData(318 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 18, classification=ItemClassification.filler, parent_item="Battlecruiser"),
- "Defensive Matrix (Battlecruiser)": ItemData(319 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 19, classification=ItemClassification.filler, parent_item="Battlecruiser"),
- "Ocular Implants (Ghost)": ItemData(320 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 20, parent_item="Ghost"),
- "Crius Suit (Ghost)": ItemData(321 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 21, parent_item="Ghost"),
- "Psionic Lash (Spectre)": ItemData(322 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 22, classification=ItemClassification.progression, parent_item="Spectre"),
- "Nyx-Class Cloaking Module (Spectre)": ItemData(323 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 23, parent_item="Spectre"),
- "330mm Barrage Cannon (Thor)": ItemData(324 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 24, classification=ItemClassification.filler, parent_item="Thor"),
- "Immortality Protocol (Thor)": ItemData(325 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 25, classification=ItemClassification.filler, parent_item="Thor"),
- # Items from EE
- "Advanced Ballistics (Liberator)": ItemData(326 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 26, parent_item="Liberator", origin={"ext"}),
- "Raid Artillery (Liberator)": ItemData(327 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 27, classification=ItemClassification.progression, parent_item="Liberator", origin={"nco"}),
- "Drilling Claws (Widow Mine)": ItemData(328 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 28, classification=ItemClassification.filler, parent_item="Widow Mine", origin={"ext"}),
- "Concealment (Widow Mine)": ItemData(329 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 29, classification=ItemClassification.progression, parent_item="Widow Mine", origin={"ext"}),
-
- #Items from new mod
- "Hyperflight Rotors (Banshee)": ItemData(350 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 0, classification=ItemClassification.filler, parent_item="Banshee", origin={"ext"}),
- "Laser Targeting System (Banshee)": ItemData(351 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 1, classification=ItemClassification.filler, parent_item="Banshee", origin={"nco"}),
- "Internal Tech Module (Banshee)": ItemData(352 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 2, classification=ItemClassification.filler, parent_item="Banshee", origin={"nco"}),
- "Tactical Jump (Battlecruiser)": ItemData(353 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 3, parent_item="Battlecruiser", origin={"nco", "ext"}),
- "Cloak (Battlecruiser)": ItemData(354 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 4, parent_item="Battlecruiser", origin={"nco"}),
- "ATX Laser Battery (Battlecruiser)": ItemData(355 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 5, classification=ItemClassification.progression, parent_item="Battlecruiser", origin={"nco"}),
- "Optimized Logistics (Battlecruiser)": ItemData(356 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 6, classification=ItemClassification.filler, parent_item="Battlecruiser", origin={"ext"}),
- "Internal Tech Module (Battlecruiser)": ItemData(357 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 7, classification=ItemClassification.filler, parent_item="Battlecruiser", origin={"nco"}),
- "EMP Rounds (Ghost)": ItemData(358 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 8, parent_item="Ghost", origin={"ext"}),
- "Lockdown (Ghost)": ItemData(359 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 9, parent_item="Ghost", origin={"bw"}),
- "Impaler Rounds (Spectre)": ItemData(360 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 10, parent_item="Spectre", origin={"ext"}),
- "Progressive High Impact Payload (Thor)": ItemData(361 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 14, parent_item="Thor", quantity=2, origin={"ext"}), # L2 is Smart Servos
- "Bio Mechanical Repair Drone (Raven)": ItemData(363 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 13, parent_item="Raven", origin={"nco"}),
- "Spider Mines (Raven)": ItemData(364 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 14, parent_item="Raven", origin={"nco"}),
- "Railgun Turret (Raven)": ItemData(365 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 15, parent_item="Raven", origin={"nco"}),
- "Hunter-Seeker Weapon (Raven)": ItemData(366 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 16, parent_item="Raven", origin={"nco"}),
- "Interference Matrix (Raven)": ItemData(367 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 17, parent_item="Raven", origin={"ext"}),
- "Anti-Armor Missile (Raven)": ItemData(368 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 18, classification=ItemClassification.filler, parent_item="Raven", origin={"ext"}),
- "Internal Tech Module (Raven)": ItemData(369 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 19, classification=ItemClassification.filler, parent_item="Raven", origin={"nco"}),
- "EMP Shockwave (Science Vessel)": ItemData(370 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 20, parent_item="Science Vessel", origin={"bw"}),
- "Defensive Matrix (Science Vessel)": ItemData(371 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 21, parent_item="Science Vessel", origin={"bw"}),
- "Targeting Optics (Cyclone)": ItemData(372 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 22, parent_item="Cyclone", origin={"ext"}),
- "Rapid Fire Launchers (Cyclone)": ItemData(373 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 23, parent_item="Cyclone", origin={"ext"}),
- "Cloak (Liberator)": ItemData(374 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 24, classification=ItemClassification.filler, parent_item="Liberator", origin={"nco"}),
- "Laser Targeting System (Liberator)": ItemData(375 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 25, classification=ItemClassification.filler, parent_item="Liberator", origin={"ext"}),
- "Optimized Logistics (Liberator)": ItemData(376 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 26, classification=ItemClassification.filler, parent_item="Liberator", origin={"nco"}),
- "Black Market Launchers (Widow Mine)": ItemData(377 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 27, classification=ItemClassification.filler, parent_item="Widow Mine", origin={"ext"}),
- "Executioner Missiles (Widow Mine)": ItemData(378 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 28, parent_item="Widow Mine", origin={"ext"}),
-
- # Just lazy to create a new group for one unit
- "Enhanced Cluster Launchers (Valkyrie)": ItemData(379 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 17, parent_item="Valkyrie", origin={"ext"}),
- "Shaped Hull (Valkyrie)": ItemData(380 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 20, classification=ItemClassification.filler, parent_item="Valkyrie", origin={"ext"}),
- "Burst Lasers (Valkyrie)": ItemData(381 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 21, parent_item="Valkyrie", origin={"ext"}),
- "Afterburners (Valkyrie)": ItemData(382 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 22, classification=ItemClassification.filler, parent_item="Valkyrie", origin={"ext"}),
-
- "Bunker": ItemData(400 + SC2WOL_ITEM_ID_OFFSET, "Building", 0, classification=ItemClassification.progression),
- "Missile Turret": ItemData(401 + SC2WOL_ITEM_ID_OFFSET, "Building", 1, classification=ItemClassification.progression),
- "Sensor Tower": ItemData(402 + SC2WOL_ITEM_ID_OFFSET, "Building", 2),
-
- "War Pigs": ItemData(500 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 0, classification=ItemClassification.progression),
- "Devil Dogs": ItemData(501 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 1, classification=ItemClassification.filler),
- "Hammer Securities": ItemData(502 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 2),
- "Spartan Company": ItemData(503 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 3, classification=ItemClassification.progression),
- "Siege Breakers": ItemData(504 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 4),
- "Hel's Angel": ItemData(505 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 5, classification=ItemClassification.progression),
- "Dusk Wings": ItemData(506 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 6),
- "Jackson's Revenge": ItemData(507 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 7),
-
- "Ultra-Capacitors": ItemData(600 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 0),
- "Vanadium Plating": ItemData(601 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 1),
- "Orbital Depots": ItemData(602 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 2),
- "Micro-Filtering": ItemData(603 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 3),
- "Automated Refinery": ItemData(604 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 4),
- "Command Center Reactor": ItemData(605 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 5),
- "Raven": ItemData(606 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 6),
- "Science Vessel": ItemData(607 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 7, classification=ItemClassification.progression),
- "Tech Reactor": ItemData(608 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 8),
- "Orbital Strike": ItemData(609 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 9),
- "Shrike Turret (Bunker)": ItemData(610 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 10, parent_item="Bunker"),
- "Fortified Bunker (Bunker)": ItemData(611 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 11, parent_item="Bunker"),
- "Planetary Fortress": ItemData(612 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 12, classification=ItemClassification.progression),
- "Perdition Turret": ItemData(613 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 13, classification=ItemClassification.progression),
- "Predator": ItemData(614 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 14, classification=ItemClassification.filler),
- "Hercules": ItemData(615 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 15, classification=ItemClassification.progression),
- "Cellular Reactor": ItemData(616 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 16),
- "Progressive Regenerative Bio-Steel": ItemData(617 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 4, quantity=2),
- "Hive Mind Emulator": ItemData(618 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 18, ItemClassification.progression),
- "Psi Disrupter": ItemData(619 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 19, classification=ItemClassification.progression),
-
- "Zealot": ItemData(700 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 0, classification=ItemClassification.progression),
- "Stalker": ItemData(701 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 1, classification=ItemClassification.progression),
- "High Templar": ItemData(702 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 2, classification=ItemClassification.progression),
- "Dark Templar": ItemData(703 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 3, classification=ItemClassification.progression),
- "Immortal": ItemData(704 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 4, classification=ItemClassification.progression),
- "Colossus": ItemData(705 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 5),
- "Phoenix": ItemData(706 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 6, classification=ItemClassification.filler),
- "Void Ray": ItemData(707 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 7, classification=ItemClassification.progression),
- "Carrier": ItemData(708 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 8, classification=ItemClassification.progression),
-
- # Filler items to fill remaining spots
- "+15 Starting Minerals": ItemData(800 + SC2WOL_ITEM_ID_OFFSET, "Minerals", 15, quantity=0, classification=ItemClassification.filler),
- "+15 Starting Vespene": ItemData(801 + SC2WOL_ITEM_ID_OFFSET, "Vespene", 15, quantity=0, classification=ItemClassification.filler),
- # This Filler item isn't placed by the generator yet unless plando'd
- "+2 Starting Supply": ItemData(802 + SC2WOL_ITEM_ID_OFFSET, "Supply", 2, quantity=0, classification=ItemClassification.filler),
- # This item is used to "remove" location from the game. Never placed unless plando'd
- "Nothing": ItemData(803 + SC2WOL_ITEM_ID_OFFSET, "Nothing Group", 2, quantity=0, classification=ItemClassification.trap),
-
- # "Keystone Piece": ItemData(850 + SC2WOL_ITEM_ID_OFFSET, "Goal", 0, quantity=0, classification=ItemClassification.progression_skip_balancing)
-}
-
-def get_item_table(multiworld: MultiWorld, player: int):
- return item_table
-
-basic_units = {
- 'Marine',
- 'Marauder',
- 'Goliath',
- 'Hellion',
- 'Vulture'
-}
-
-advanced_basic_units = basic_units.union({
- 'Reaper',
- 'Diamondback',
- 'Viking'
-})
-
-
-def get_basic_units(multiworld: MultiWorld, player: int) -> typing.Set[str]:
- if get_option_value(multiworld, player, 'required_tactics') > 0:
- return advanced_basic_units
- else:
- return basic_units
-
-
-item_name_groups = {}
-for item, data in get_full_item_list().items():
- item_name_groups.setdefault(data.type, []).append(item)
- if data.type in ("Armory 1", "Armory 2") and '(' in item:
- short_name = item[:item.find(' (')]
- item_name_groups[short_name] = [item]
-item_name_groups["Missions"] = ["Beat " + mission_name for mission_name in vanilla_mission_req_table]
-
-
-# Items that can be placed before resources if not already in
-# General upgrades and Mercs
-second_pass_placeable_items: typing.Tuple[str, ...] = (
- # Buildings without upgrades
- "Sensor Tower",
- "Hive Mind Emulator",
- "Psi Disrupter",
- "Perdition Turret",
- # General upgrades without any dependencies
- "Advanced Construction (SCV)",
- "Dual-Fusion Welders (SCV)",
- "Fire-Suppression System (Building)",
- "Orbital Command (Building)",
- "Ultra-Capacitors",
- "Vanadium Plating",
- "Orbital Depots",
- "Micro-Filtering",
- "Automated Refinery",
- "Command Center Reactor",
- "Tech Reactor",
- "Planetary Fortress",
- "Cellular Reactor",
- "Progressive Regenerative Bio-Steel", # Place only L1
- # Mercenaries
- "War Pigs",
- "Devil Dogs",
- "Hammer Securities",
- "Spartan Company",
- "Siege Breakers",
- "Hel's Angel",
- "Dusk Wings",
- "Jackson's Revenge"
-)
-
-
-filler_items: typing.Tuple[str, ...] = (
- '+15 Starting Minerals',
- '+15 Starting Vespene'
-)
-
-# Defense rating table
-# Commented defense ratings are handled in LogicMixin
-defense_ratings = {
- "Siege Tank": 5,
- # "Maelstrom Rounds": 2,
- "Planetary Fortress": 3,
- # Bunker w/ Marine/Marauder: 3,
- "Perdition Turret": 2,
- "Missile Turret": 2,
- "Vulture": 2,
- "Liberator": 2,
- "Widow Mine": 2
- # "Concealment (Widow Mine)": 1
-}
-zerg_defense_ratings = {
- "Perdition Turret": 2,
- # Bunker w/ Firebat: 2,
- "Hive Mind Emulator": 3,
- "Psi Disruptor": 3
-}
-
-spider_mine_sources = {
- "Vulture",
- "Spider Mines (Reaper)",
- "Spider Mines (Siege Tank)",
- "Spider Mines (Raven)"
-}
-
-progressive_if_nco = {
- "Progressive Stimpack (Marine)",
- "Progressive Stimpack (Firebat)",
- "Progressive Cross-Spectrum Dampeners (Banshee)",
- "Progressive Regenerative Bio-Steel"
-}
-
-# 'number' values of upgrades for upgrade bundle items
-upgrade_numbers = [
- {0, 4, 8}, # Weapon
- {2, 6, 10}, # Armor
- {0, 2}, # Infantry
- {4, 6}, # Vehicle
- {8, 10}, # Starship
- {0, 2, 4, 6, 8, 10} # All
-]
-# Names of upgrades to be included for different options
-upgrade_included_names = [
- { # Individual Items
- "Progressive Infantry Weapon",
- "Progressive Infantry Armor",
- "Progressive Vehicle Weapon",
- "Progressive Vehicle Armor",
- "Progressive Ship Weapon",
- "Progressive Ship Armor"
- },
- { # Bundle Weapon And Armor
- "Progressive Weapon Upgrade",
- "Progressive Armor Upgrade"
- },
- { # Bundle Unit Class
- "Progressive Infantry Upgrade",
- "Progressive Vehicle Upgrade",
- "Progressive Starship Upgrade"
- },
- { # Bundle All
- "Progressive Weapon/Armor Upgrade"
- }
-]
-
-lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in get_full_item_list().items() if
- data.code}
-# Map type to expected int
-type_flaggroups: typing.Dict[str, int] = {
- "Unit": 0,
- "Upgrade": 1, # Weapon / Armor upgrades
- "Armory 1": 2, # Unit upgrades
- "Armory 2": 3, # Unit upgrades
- "Building": 4,
- "Mercenary": 5,
- "Laboratory": 6,
- "Protoss": 7,
- "Minerals": 8,
- "Vespene": 9,
- "Supply": 10,
- "Goal": 11,
- "Armory 3": 12, # Unit upgrades
- "Armory 4": 13, # Unit upgrades
- "Progressive Upgrade": 14, # Unit upgrades that exist multiple times (Stimpack / Super Stimpack)
- "Nothing Group": 15
-}
diff --git a/worlds/sc2wol/Locations.py b/worlds/sc2wol/Locations.py
deleted file mode 100644
index fba7051337df..000000000000
--- a/worlds/sc2wol/Locations.py
+++ /dev/null
@@ -1,516 +0,0 @@
-from enum import IntEnum
-from typing import List, Tuple, Optional, Callable, NamedTuple
-from BaseClasses import MultiWorld
-from .Options import get_option_value
-
-from BaseClasses import Location
-
-SC2WOL_LOC_ID_OFFSET = 1000
-
-
-class SC2WoLLocation(Location):
- game: str = "Starcraft2WoL"
-
-
-class LocationType(IntEnum):
- VICTORY = 0 # Winning a mission
- MISSION_PROGRESS = 1 # All tasks done for progressing the mission normally towards victory. All cleaning of expansion bases falls here
- BONUS = 2 # Bonus objective, getting a campaign or mission bonus in vanilla (credits, research, bonus units or resources)
- CHALLENGE = 3 # Challenging objectives, often harder than just completing a mission
- OPTIONAL_BOSS = 4 # Any boss that's not required to win the mission. All Brutalisks, Loki, etc.
-
-class LocationData(NamedTuple):
- region: str
- name: str
- code: Optional[int]
- type: LocationType
- rule: Callable = lambda state: True
-
-
-def get_locations(multiworld: Optional[MultiWorld], player: Optional[int]) -> Tuple[LocationData, ...]:
- # Note: rules which are ended with or True are rules identified as needed later when restricted units is an option
- logic_level = get_option_value(multiworld, player, 'required_tactics')
- location_table: List[LocationData] = [
- LocationData("Liberation Day", "Liberation Day: Victory", SC2WOL_LOC_ID_OFFSET + 100, LocationType.VICTORY),
- LocationData("Liberation Day", "Liberation Day: First Statue", SC2WOL_LOC_ID_OFFSET + 101, LocationType.BONUS),
- LocationData("Liberation Day", "Liberation Day: Second Statue", SC2WOL_LOC_ID_OFFSET + 102, LocationType.BONUS),
- LocationData("Liberation Day", "Liberation Day: Third Statue", SC2WOL_LOC_ID_OFFSET + 103, LocationType.BONUS),
- LocationData("Liberation Day", "Liberation Day: Fourth Statue", SC2WOL_LOC_ID_OFFSET + 104, LocationType.BONUS),
- LocationData("Liberation Day", "Liberation Day: Fifth Statue", SC2WOL_LOC_ID_OFFSET + 105, LocationType.BONUS),
- LocationData("Liberation Day", "Liberation Day: Sixth Statue", SC2WOL_LOC_ID_OFFSET + 106, LocationType.BONUS),
- LocationData("Liberation Day", "Liberation Day: Special Delivery", SC2WOL_LOC_ID_OFFSET + 107, LocationType.MISSION_PROGRESS),
- LocationData("The Outlaws", "The Outlaws: Victory", SC2WOL_LOC_ID_OFFSET + 200, LocationType.VICTORY,
- lambda state: state._sc2wol_has_common_unit(multiworld, player)),
- LocationData("The Outlaws", "The Outlaws: Rebel Base", SC2WOL_LOC_ID_OFFSET + 201, LocationType.BONUS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player)),
- LocationData("The Outlaws", "The Outlaws: North Resource Pickups", SC2WOL_LOC_ID_OFFSET + 202, LocationType.BONUS),
- LocationData("The Outlaws", "The Outlaws: Bunker", SC2WOL_LOC_ID_OFFSET + 203, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player)),
- LocationData("Zero Hour", "Zero Hour: Victory", SC2WOL_LOC_ID_OFFSET + 300, LocationType.VICTORY,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- state._sc2wol_defense_rating(multiworld, player, True) >= 2 and
- (logic_level > 0 or state._sc2wol_has_anti_air(multiworld, player))),
- LocationData("Zero Hour", "Zero Hour: First Group Rescued", SC2WOL_LOC_ID_OFFSET + 301, LocationType.BONUS),
- LocationData("Zero Hour", "Zero Hour: Second Group Rescued", SC2WOL_LOC_ID_OFFSET + 302, LocationType.BONUS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player)),
- LocationData("Zero Hour", "Zero Hour: Third Group Rescued", SC2WOL_LOC_ID_OFFSET + 303, LocationType.BONUS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- state._sc2wol_defense_rating(multiworld, player, True) >= 2),
- LocationData("Zero Hour", "Zero Hour: First Hatchery", SC2WOL_LOC_ID_OFFSET + 304, LocationType.CHALLENGE,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Zero Hour", "Zero Hour: Second Hatchery", SC2WOL_LOC_ID_OFFSET + 305, LocationType.CHALLENGE,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Zero Hour", "Zero Hour: Third Hatchery", SC2WOL_LOC_ID_OFFSET + 306, LocationType.CHALLENGE,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Zero Hour", "Zero Hour: Fourth Hatchery", SC2WOL_LOC_ID_OFFSET + 307, LocationType.CHALLENGE,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Evacuation", "Evacuation: Victory", SC2WOL_LOC_ID_OFFSET + 400, LocationType.VICTORY,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player)
- or state._sc2wol_has_competent_anti_air(multiworld, player))),
- LocationData("Evacuation", "Evacuation: North Chrysalis", SC2WOL_LOC_ID_OFFSET + 401, LocationType.BONUS),
- LocationData("Evacuation", "Evacuation: West Chrysalis", SC2WOL_LOC_ID_OFFSET + 402, LocationType.BONUS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player)),
- LocationData("Evacuation", "Evacuation: East Chrysalis", SC2WOL_LOC_ID_OFFSET + 403, LocationType.BONUS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player)),
- LocationData("Evacuation", "Evacuation: Reach Hanson", SC2WOL_LOC_ID_OFFSET + 404, LocationType.MISSION_PROGRESS),
- LocationData("Evacuation", "Evacuation: Secret Resource Stash", SC2WOL_LOC_ID_OFFSET + 405, LocationType.BONUS),
- LocationData("Evacuation", "Evacuation: Flawless", SC2WOL_LOC_ID_OFFSET + 406, LocationType.CHALLENGE,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and
- (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player)
- or state._sc2wol_has_competent_anti_air(multiworld, player))),
- LocationData("Outbreak", "Outbreak: Victory", SC2WOL_LOC_ID_OFFSET + 500, LocationType.VICTORY,
- lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 4 and
- (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))),
- LocationData("Outbreak", "Outbreak: Left Infestor", SC2WOL_LOC_ID_OFFSET + 501, LocationType.BONUS,
- lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and
- (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))),
- LocationData("Outbreak", "Outbreak: Right Infestor", SC2WOL_LOC_ID_OFFSET + 502, LocationType.BONUS,
- lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and
- (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))),
- LocationData("Outbreak", "Outbreak: North Infested Command Center", SC2WOL_LOC_ID_OFFSET + 503, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and
- (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))),
- LocationData("Outbreak", "Outbreak: South Infested Command Center", SC2WOL_LOC_ID_OFFSET + 504, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and
- (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))),
- LocationData("Outbreak", "Outbreak: Northwest Bar", SC2WOL_LOC_ID_OFFSET + 505, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and
- (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))),
- LocationData("Outbreak", "Outbreak: North Bar", SC2WOL_LOC_ID_OFFSET + 506, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and
- (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))),
- LocationData("Outbreak", "Outbreak: South Bar", SC2WOL_LOC_ID_OFFSET + 507, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and
- (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))),
- LocationData("Safe Haven", "Safe Haven: Victory", SC2WOL_LOC_ID_OFFSET + 600, LocationType.VICTORY,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- state._sc2wol_has_competent_anti_air(multiworld, player)),
- LocationData("Safe Haven", "Safe Haven: North Nexus", SC2WOL_LOC_ID_OFFSET + 601, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- state._sc2wol_has_competent_anti_air(multiworld, player)),
- LocationData("Safe Haven", "Safe Haven: East Nexus", SC2WOL_LOC_ID_OFFSET + 602, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- state._sc2wol_has_competent_anti_air(multiworld, player)),
- LocationData("Safe Haven", "Safe Haven: South Nexus", SC2WOL_LOC_ID_OFFSET + 603, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- state._sc2wol_has_competent_anti_air(multiworld, player)),
- LocationData("Safe Haven", "Safe Haven: First Terror Fleet", SC2WOL_LOC_ID_OFFSET + 604, LocationType.BONUS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- state._sc2wol_has_competent_anti_air(multiworld, player)),
- LocationData("Safe Haven", "Safe Haven: Second Terror Fleet", SC2WOL_LOC_ID_OFFSET + 605, LocationType.BONUS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- state._sc2wol_has_competent_anti_air(multiworld, player)),
- LocationData("Safe Haven", "Safe Haven: Third Terror Fleet", SC2WOL_LOC_ID_OFFSET + 606, LocationType.BONUS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- state._sc2wol_has_competent_anti_air(multiworld, player)),
- LocationData("Haven's Fall", "Haven's Fall: Victory", SC2WOL_LOC_ID_OFFSET + 700, LocationType.VICTORY,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- state._sc2wol_has_competent_anti_air(multiworld, player) and
- state._sc2wol_defense_rating(multiworld, player, True) >= 3),
- LocationData("Haven's Fall", "Haven's Fall: North Hive", SC2WOL_LOC_ID_OFFSET + 701, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- state._sc2wol_has_competent_anti_air(multiworld, player) and
- state._sc2wol_defense_rating(multiworld, player, True) >= 3),
- LocationData("Haven's Fall", "Haven's Fall: East Hive", SC2WOL_LOC_ID_OFFSET + 702, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- state._sc2wol_has_competent_anti_air(multiworld, player) and
- state._sc2wol_defense_rating(multiworld, player, True) >= 3),
- LocationData("Haven's Fall", "Haven's Fall: South Hive", SC2WOL_LOC_ID_OFFSET + 703, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- state._sc2wol_has_competent_anti_air(multiworld, player) and
- state._sc2wol_defense_rating(multiworld, player, True) >= 3),
- LocationData("Haven's Fall", "Haven's Fall: Northeast Colony Base", SC2WOL_LOC_ID_OFFSET + 704, LocationType.CHALLENGE,
- lambda state: state._sc2wol_can_respond_to_colony_infestations),
- LocationData("Haven's Fall", "Haven's Fall: East Colony Base", SC2WOL_LOC_ID_OFFSET + 705, LocationType.CHALLENGE,
- lambda state: state._sc2wol_can_respond_to_colony_infestations),
- LocationData("Haven's Fall", "Haven's Fall: Middle Colony Base", SC2WOL_LOC_ID_OFFSET + 706, LocationType.CHALLENGE,
- lambda state: state._sc2wol_can_respond_to_colony_infestations),
- LocationData("Haven's Fall", "Haven's Fall: Southeast Colony Base", SC2WOL_LOC_ID_OFFSET + 707, LocationType.CHALLENGE,
- lambda state: state._sc2wol_can_respond_to_colony_infestations),
- LocationData("Haven's Fall", "Haven's Fall: Southwest Colony Base", SC2WOL_LOC_ID_OFFSET + 708, LocationType.CHALLENGE,
- lambda state: state._sc2wol_can_respond_to_colony_infestations),
- LocationData("Smash and Grab", "Smash and Grab: Victory", SC2WOL_LOC_ID_OFFSET + 800, LocationType.VICTORY,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player)
- or state._sc2wol_has_competent_anti_air(multiworld, player))),
- LocationData("Smash and Grab", "Smash and Grab: First Relic", SC2WOL_LOC_ID_OFFSET + 801, LocationType.BONUS),
- LocationData("Smash and Grab", "Smash and Grab: Second Relic", SC2WOL_LOC_ID_OFFSET + 802, LocationType.BONUS),
- LocationData("Smash and Grab", "Smash and Grab: Third Relic", SC2WOL_LOC_ID_OFFSET + 803, LocationType.BONUS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player)
- or state._sc2wol_has_competent_anti_air(multiworld, player))),
- LocationData("Smash and Grab", "Smash and Grab: Fourth Relic", SC2WOL_LOC_ID_OFFSET + 804, LocationType.BONUS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player)
- or state._sc2wol_has_competent_anti_air(multiworld, player))),
- LocationData("Smash and Grab", "Smash and Grab: First Forcefield Area Busted", SC2WOL_LOC_ID_OFFSET + 805, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player)
- or state._sc2wol_has_competent_anti_air(multiworld, player))),
- LocationData("Smash and Grab", "Smash and Grab: Second Forcefield Area Busted", SC2WOL_LOC_ID_OFFSET + 806, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player)
- or state._sc2wol_has_competent_anti_air(multiworld, player))),
- LocationData("The Dig", "The Dig: Victory", SC2WOL_LOC_ID_OFFSET + 900, LocationType.VICTORY,
- lambda state: state._sc2wol_has_anti_air(multiworld, player) and
- state._sc2wol_defense_rating(multiworld, player, False) >= 7),
- LocationData("The Dig", "The Dig: Left Relic", SC2WOL_LOC_ID_OFFSET + 901, LocationType.BONUS,
- lambda state: state._sc2wol_defense_rating(multiworld, player, False) >= 5),
- LocationData("The Dig", "The Dig: Right Ground Relic", SC2WOL_LOC_ID_OFFSET + 902, LocationType.BONUS,
- lambda state: state._sc2wol_defense_rating(multiworld, player, False) >= 5),
- LocationData("The Dig", "The Dig: Right Cliff Relic", SC2WOL_LOC_ID_OFFSET + 903, LocationType.BONUS,
- lambda state: state._sc2wol_defense_rating(multiworld, player, False) >= 5),
- LocationData("The Dig", "The Dig: Moebius Base", SC2WOL_LOC_ID_OFFSET + 904, LocationType.MISSION_PROGRESS),
- LocationData("The Moebius Factor", "The Moebius Factor: Victory", SC2WOL_LOC_ID_OFFSET + 1000, LocationType.VICTORY,
- lambda state: state._sc2wol_has_anti_air(multiworld, player) and
- (state._sc2wol_has_air(multiworld, player)
- or state.has_any({'Medivac', 'Hercules'}, player)
- and state._sc2wol_has_common_unit(multiworld, player))),
- LocationData("The Moebius Factor", "The Moebius Factor: 1st Data Core", SC2WOL_LOC_ID_OFFSET + 1001, LocationType.MISSION_PROGRESS),
- LocationData("The Moebius Factor", "The Moebius Factor: 2nd Data Core", SC2WOL_LOC_ID_OFFSET + 1002, LocationType.MISSION_PROGRESS,
- lambda state: (state._sc2wol_has_air(multiworld, player)
- or state.has_any({'Medivac', 'Hercules'}, player)
- and state._sc2wol_has_common_unit(multiworld, player))),
- LocationData("The Moebius Factor", "The Moebius Factor: South Rescue", SC2WOL_LOC_ID_OFFSET + 1003, LocationType.BONUS,
- lambda state: state._sc2wol_able_to_rescue(multiworld, player)),
- LocationData("The Moebius Factor", "The Moebius Factor: Wall Rescue", SC2WOL_LOC_ID_OFFSET + 1004, LocationType.BONUS,
- lambda state: state._sc2wol_able_to_rescue(multiworld, player)),
- LocationData("The Moebius Factor", "The Moebius Factor: Mid Rescue", SC2WOL_LOC_ID_OFFSET + 1005, LocationType.BONUS,
- lambda state: state._sc2wol_able_to_rescue(multiworld, player)),
- LocationData("The Moebius Factor", "The Moebius Factor: Nydus Roof Rescue", SC2WOL_LOC_ID_OFFSET + 1006, LocationType.BONUS,
- lambda state: state._sc2wol_able_to_rescue(multiworld, player)),
- LocationData("The Moebius Factor", "The Moebius Factor: Alive Inside Rescue", SC2WOL_LOC_ID_OFFSET + 1007, LocationType.BONUS,
- lambda state: state._sc2wol_able_to_rescue(multiworld, player)),
- LocationData("The Moebius Factor", "The Moebius Factor: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1008, LocationType.OPTIONAL_BOSS,
- lambda state: state._sc2wol_has_anti_air(multiworld, player) and
- (state._sc2wol_has_air(multiworld, player)
- or state.has_any({'Medivac', 'Hercules'}, player)
- and state._sc2wol_has_common_unit(multiworld, player))),
- LocationData("The Moebius Factor", "The Moebius Factor: 3rd Data Core", SC2WOL_LOC_ID_OFFSET + 1009, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_anti_air(multiworld, player) and
- (state._sc2wol_has_air(multiworld, player)
- or state.has_any({'Medivac', 'Hercules'}, player)
- and state._sc2wol_has_common_unit(multiworld, player))),
- LocationData("Supernova", "Supernova: Victory", SC2WOL_LOC_ID_OFFSET + 1100, LocationType.VICTORY,
- lambda state: state._sc2wol_beats_protoss_deathball(multiworld, player)),
- LocationData("Supernova", "Supernova: West Relic", SC2WOL_LOC_ID_OFFSET + 1101, LocationType.BONUS),
- LocationData("Supernova", "Supernova: North Relic", SC2WOL_LOC_ID_OFFSET + 1102, LocationType.BONUS),
- LocationData("Supernova", "Supernova: South Relic", SC2WOL_LOC_ID_OFFSET + 1103, LocationType.BONUS,
- lambda state: state._sc2wol_beats_protoss_deathball(multiworld, player)),
- LocationData("Supernova", "Supernova: East Relic", SC2WOL_LOC_ID_OFFSET + 1104, LocationType.BONUS,
- lambda state: state._sc2wol_beats_protoss_deathball(multiworld, player)),
- LocationData("Supernova", "Supernova: Landing Zone Cleared", SC2WOL_LOC_ID_OFFSET + 1105, LocationType.MISSION_PROGRESS),
- LocationData("Supernova", "Supernova: Middle Base", SC2WOL_LOC_ID_OFFSET + 1106, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_beats_protoss_deathball(multiworld, player)),
- LocationData("Supernova", "Supernova: Southeast Base", SC2WOL_LOC_ID_OFFSET + 1107, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_beats_protoss_deathball(multiworld, player)),
- LocationData("Maw of the Void", "Maw of the Void: Victory", SC2WOL_LOC_ID_OFFSET + 1200, LocationType.VICTORY,
- lambda state: state._sc2wol_survives_rip_field(multiworld, player)),
- LocationData("Maw of the Void", "Maw of the Void: Landing Zone Cleared", SC2WOL_LOC_ID_OFFSET + 1201, LocationType.MISSION_PROGRESS),
- LocationData("Maw of the Void", "Maw of the Void: Expansion Prisoners", SC2WOL_LOC_ID_OFFSET + 1202, LocationType.BONUS,
- lambda state: logic_level > 0 or state._sc2wol_survives_rip_field(multiworld, player)),
- LocationData("Maw of the Void", "Maw of the Void: South Close Prisoners", SC2WOL_LOC_ID_OFFSET + 1203, LocationType.BONUS,
- lambda state: logic_level > 0 or state._sc2wol_survives_rip_field(multiworld, player)),
- LocationData("Maw of the Void", "Maw of the Void: South Far Prisoners", SC2WOL_LOC_ID_OFFSET + 1204, LocationType.BONUS,
- lambda state: state._sc2wol_survives_rip_field(multiworld, player)),
- LocationData("Maw of the Void", "Maw of the Void: North Prisoners", SC2WOL_LOC_ID_OFFSET + 1205, LocationType.BONUS,
- lambda state: state._sc2wol_survives_rip_field(multiworld, player)),
- LocationData("Maw of the Void", "Maw of the Void: Mothership", SC2WOL_LOC_ID_OFFSET + 1206, LocationType.OPTIONAL_BOSS,
- lambda state: state._sc2wol_survives_rip_field(multiworld, player)),
- LocationData("Maw of the Void", "Maw of the Void: Expansion Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1207, LocationType.MISSION_PROGRESS,
- lambda state: logic_level > 0 or state._sc2wol_survives_rip_field(multiworld, player)),
- LocationData("Maw of the Void", "Maw of the Void: Middle Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1208, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_survives_rip_field(multiworld, player)),
- LocationData("Maw of the Void", "Maw of the Void: Southeast Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1209, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_survives_rip_field(multiworld, player)),
- LocationData("Maw of the Void", "Maw of the Void: Stargate Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1210, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_survives_rip_field(multiworld, player)),
- LocationData("Maw of the Void", "Maw of the Void: Northwest Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1211, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_survives_rip_field(multiworld, player)),
- LocationData("Maw of the Void", "Maw of the Void: West Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1212, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_survives_rip_field(multiworld, player)),
- LocationData("Maw of the Void", "Maw of the Void: Southwest Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1213, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_survives_rip_field(multiworld, player)),
- LocationData("Devil's Playground", "Devil's Playground: Victory", SC2WOL_LOC_ID_OFFSET + 1300, LocationType.VICTORY,
- lambda state: logic_level > 0 or
- state._sc2wol_has_anti_air(multiworld, player) and (
- state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))),
- LocationData("Devil's Playground", "Devil's Playground: Tosh's Miners", SC2WOL_LOC_ID_OFFSET + 1301, LocationType.BONUS),
- LocationData("Devil's Playground", "Devil's Playground: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1302, LocationType.OPTIONAL_BOSS,
- lambda state: logic_level > 0 or state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player)),
- LocationData("Devil's Playground", "Devil's Playground: North Reapers", SC2WOL_LOC_ID_OFFSET + 1303, LocationType.BONUS),
- LocationData("Devil's Playground", "Devil's Playground: Middle Reapers", SC2WOL_LOC_ID_OFFSET + 1304, LocationType.BONUS,
- lambda state: logic_level > 0 or state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player)),
- LocationData("Devil's Playground", "Devil's Playground: Southwest Reapers", SC2WOL_LOC_ID_OFFSET + 1305, LocationType.BONUS,
- lambda state: logic_level > 0 or state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player)),
- LocationData("Devil's Playground", "Devil's Playground: Southeast Reapers", SC2WOL_LOC_ID_OFFSET + 1306, LocationType.BONUS,
- lambda state: logic_level > 0 or
- state._sc2wol_has_anti_air(multiworld, player) and (
- state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))),
- LocationData("Devil's Playground", "Devil's Playground: East Reapers", SC2WOL_LOC_ID_OFFSET + 1307, LocationType.BONUS,
- lambda state: state._sc2wol_has_anti_air(multiworld, player) and
- (logic_level > 0 or
- state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))),
- LocationData("Welcome to the Jungle", "Welcome to the Jungle: Victory", SC2WOL_LOC_ID_OFFSET + 1400, LocationType.VICTORY,
- lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)),
- LocationData("Welcome to the Jungle", "Welcome to the Jungle: Close Relic", SC2WOL_LOC_ID_OFFSET + 1401, LocationType.BONUS),
- LocationData("Welcome to the Jungle", "Welcome to the Jungle: West Relic", SC2WOL_LOC_ID_OFFSET + 1402, LocationType.BONUS,
- lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)),
- LocationData("Welcome to the Jungle", "Welcome to the Jungle: North-East Relic", SC2WOL_LOC_ID_OFFSET + 1403, LocationType.BONUS,
- lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)),
- LocationData("Welcome to the Jungle", "Welcome to the Jungle: Middle Base", SC2WOL_LOC_ID_OFFSET + 1404, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)),
- LocationData("Welcome to the Jungle", "Welcome to the Jungle: Main Base", SC2WOL_LOC_ID_OFFSET + 1405, LocationType.CHALLENGE,
- lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)
- and state._sc2wol_beats_protoss_deathball(multiworld, player)),
- LocationData("Welcome to the Jungle", "Welcome to the Jungle: No Terrazine Nodes Sealed", SC2WOL_LOC_ID_OFFSET + 1406, LocationType.CHALLENGE,
- lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)
- and state._sc2wol_has_competent_ground_to_air(multiworld, player)
- and state._sc2wol_beats_protoss_deathball(multiworld, player)),
- LocationData("Welcome to the Jungle", "Welcome to the Jungle: Up to 1 Terrazine Node Sealed", SC2WOL_LOC_ID_OFFSET + 1407, LocationType.CHALLENGE,
- lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)
- and state._sc2wol_has_competent_ground_to_air(multiworld, player)
- and state._sc2wol_beats_protoss_deathball(multiworld, player)),
- LocationData("Welcome to the Jungle", "Welcome to the Jungle: Up to 2 Terrazine Nodes Sealed", SC2WOL_LOC_ID_OFFSET + 1408, LocationType.CHALLENGE,
- lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)
- and state._sc2wol_beats_protoss_deathball(multiworld, player)),
- LocationData("Welcome to the Jungle", "Welcome to the Jungle: Up to 3 Terrazine Nodes Sealed", SC2WOL_LOC_ID_OFFSET + 1409, LocationType.CHALLENGE,
- lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)
- and state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Welcome to the Jungle", "Welcome to the Jungle: Up to 4 Terrazine Nodes Sealed", SC2WOL_LOC_ID_OFFSET + 1410, LocationType.CHALLENGE,
- lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)),
- LocationData("Welcome to the Jungle", "Welcome to the Jungle: Up to 5 Terrazine Nodes Sealed", SC2WOL_LOC_ID_OFFSET + 1411, LocationType.CHALLENGE,
- lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)),
- LocationData("Breakout", "Breakout: Victory", SC2WOL_LOC_ID_OFFSET + 1500, LocationType.VICTORY),
- LocationData("Breakout", "Breakout: Diamondback Prison", SC2WOL_LOC_ID_OFFSET + 1501, LocationType.BONUS),
- LocationData("Breakout", "Breakout: Siege Tank Prison", SC2WOL_LOC_ID_OFFSET + 1502, LocationType.BONUS),
- LocationData("Breakout", "Breakout: First Checkpoint", SC2WOL_LOC_ID_OFFSET + 1503, LocationType.MISSION_PROGRESS),
- LocationData("Breakout", "Breakout: Second Checkpoint", SC2WOL_LOC_ID_OFFSET + 1504, LocationType.MISSION_PROGRESS),
- LocationData("Ghost of a Chance", "Ghost of a Chance: Victory", SC2WOL_LOC_ID_OFFSET + 1600, LocationType.VICTORY),
- LocationData("Ghost of a Chance", "Ghost of a Chance: Terrazine Tank", SC2WOL_LOC_ID_OFFSET + 1601, LocationType.MISSION_PROGRESS),
- LocationData("Ghost of a Chance", "Ghost of a Chance: Jorium Stockpile", SC2WOL_LOC_ID_OFFSET + 1602, LocationType.MISSION_PROGRESS),
- LocationData("Ghost of a Chance", "Ghost of a Chance: First Island Spectres", SC2WOL_LOC_ID_OFFSET + 1603, LocationType.BONUS),
- LocationData("Ghost of a Chance", "Ghost of a Chance: Second Island Spectres", SC2WOL_LOC_ID_OFFSET + 1604, LocationType.BONUS),
- LocationData("Ghost of a Chance", "Ghost of a Chance: Third Island Spectres", SC2WOL_LOC_ID_OFFSET + 1605, LocationType.BONUS),
- LocationData("The Great Train Robbery", "The Great Train Robbery: Victory", SC2WOL_LOC_ID_OFFSET + 1700, LocationType.VICTORY,
- lambda state: state._sc2wol_has_train_killers(multiworld, player) and
- state._sc2wol_has_anti_air(multiworld, player)),
- LocationData("The Great Train Robbery", "The Great Train Robbery: North Defiler", SC2WOL_LOC_ID_OFFSET + 1701, LocationType.BONUS),
- LocationData("The Great Train Robbery", "The Great Train Robbery: Mid Defiler", SC2WOL_LOC_ID_OFFSET + 1702, LocationType.BONUS),
- LocationData("The Great Train Robbery", "The Great Train Robbery: South Defiler", SC2WOL_LOC_ID_OFFSET + 1703, LocationType.BONUS),
- LocationData("The Great Train Robbery", "The Great Train Robbery: Close Diamondback", SC2WOL_LOC_ID_OFFSET + 1704, LocationType.BONUS),
- LocationData("The Great Train Robbery", "The Great Train Robbery: Northwest Diamondback", SC2WOL_LOC_ID_OFFSET + 1705, LocationType.BONUS),
- LocationData("The Great Train Robbery", "The Great Train Robbery: North Diamondback", SC2WOL_LOC_ID_OFFSET + 1706, LocationType.BONUS),
- LocationData("The Great Train Robbery", "The Great Train Robbery: Northeast Diamondback", SC2WOL_LOC_ID_OFFSET + 1707, LocationType.BONUS),
- LocationData("The Great Train Robbery", "The Great Train Robbery: Southwest Diamondback", SC2WOL_LOC_ID_OFFSET + 1708, LocationType.BONUS),
- LocationData("The Great Train Robbery", "The Great Train Robbery: Southeast Diamondback", SC2WOL_LOC_ID_OFFSET + 1709, LocationType.BONUS),
- LocationData("The Great Train Robbery", "The Great Train Robbery: Kill Team", SC2WOL_LOC_ID_OFFSET + 1710, LocationType.CHALLENGE,
- lambda state: (logic_level > 0 or state._sc2wol_has_common_unit(multiworld, player)) and
- state._sc2wol_has_train_killers(multiworld, player) and
- state._sc2wol_has_anti_air(multiworld, player)),
- LocationData("Cutthroat", "Cutthroat: Victory", SC2WOL_LOC_ID_OFFSET + 1800, LocationType.VICTORY,
- lambda state: state._sc2wol_has_common_unit(multiworld, player) and
- (logic_level > 0 or state._sc2wol_has_anti_air)),
- LocationData("Cutthroat", "Cutthroat: Mira Han", SC2WOL_LOC_ID_OFFSET + 1801, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player)),
- LocationData("Cutthroat", "Cutthroat: North Relic", SC2WOL_LOC_ID_OFFSET + 1802, LocationType.BONUS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player)),
- LocationData("Cutthroat", "Cutthroat: Mid Relic", SC2WOL_LOC_ID_OFFSET + 1803, LocationType.BONUS),
- LocationData("Cutthroat", "Cutthroat: Southwest Relic", SC2WOL_LOC_ID_OFFSET + 1804, LocationType.BONUS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player)),
- LocationData("Cutthroat", "Cutthroat: North Command Center", SC2WOL_LOC_ID_OFFSET + 1805, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player)),
- LocationData("Cutthroat", "Cutthroat: South Command Center", SC2WOL_LOC_ID_OFFSET + 1806, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player)),
- LocationData("Cutthroat", "Cutthroat: West Command Center", SC2WOL_LOC_ID_OFFSET + 1807, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_common_unit(multiworld, player)),
- LocationData("Engine of Destruction", "Engine of Destruction: Victory", SC2WOL_LOC_ID_OFFSET + 1900, LocationType.VICTORY,
- lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and
- state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)),
- LocationData("Engine of Destruction", "Engine of Destruction: Odin", SC2WOL_LOC_ID_OFFSET + 1901, LocationType.MISSION_PROGRESS),
- LocationData("Engine of Destruction", "Engine of Destruction: Loki", SC2WOL_LOC_ID_OFFSET + 1902, LocationType.OPTIONAL_BOSS,
- lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and
- state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)),
- LocationData("Engine of Destruction", "Engine of Destruction: Lab Devourer", SC2WOL_LOC_ID_OFFSET + 1903, LocationType.BONUS),
- LocationData("Engine of Destruction", "Engine of Destruction: North Devourer", SC2WOL_LOC_ID_OFFSET + 1904, LocationType.BONUS,
- lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and
- state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)),
- LocationData("Engine of Destruction", "Engine of Destruction: Southeast Devourer", SC2WOL_LOC_ID_OFFSET + 1905, LocationType.BONUS,
- lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and
- state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)),
- LocationData("Engine of Destruction", "Engine of Destruction: West Base", SC2WOL_LOC_ID_OFFSET + 1906, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and
- state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)),
- LocationData("Engine of Destruction", "Engine of Destruction: Northwest Base", SC2WOL_LOC_ID_OFFSET + 1907, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and
- state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)),
- LocationData("Engine of Destruction", "Engine of Destruction: Northeast Base", SC2WOL_LOC_ID_OFFSET + 1908, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and
- state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)),
- LocationData("Engine of Destruction", "Engine of Destruction: Southeast Base", SC2WOL_LOC_ID_OFFSET + 1909, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and
- state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)),
- LocationData("Media Blitz", "Media Blitz: Victory", SC2WOL_LOC_ID_OFFSET + 2000, LocationType.VICTORY,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Media Blitz", "Media Blitz: Tower 1", SC2WOL_LOC_ID_OFFSET + 2001, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Media Blitz", "Media Blitz: Tower 2", SC2WOL_LOC_ID_OFFSET + 2002, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Media Blitz", "Media Blitz: Tower 3", SC2WOL_LOC_ID_OFFSET + 2003, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Media Blitz", "Media Blitz: Science Facility", SC2WOL_LOC_ID_OFFSET + 2004, LocationType.BONUS),
- LocationData("Media Blitz", "Media Blitz: All Barracks", SC2WOL_LOC_ID_OFFSET + 2005, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Media Blitz", "Media Blitz: All Factories", SC2WOL_LOC_ID_OFFSET + 2006, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Media Blitz", "Media Blitz: All Starports", SC2WOL_LOC_ID_OFFSET + 2007, LocationType.MISSION_PROGRESS,
- lambda state: logic_level > 0 or state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Media Blitz", "Media Blitz: Odin Not Trashed", SC2WOL_LOC_ID_OFFSET + 2008, LocationType.CHALLENGE,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Piercing the Shroud", "Piercing the Shroud: Victory", SC2WOL_LOC_ID_OFFSET + 2100, LocationType.VICTORY,
- lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)),
- LocationData("Piercing the Shroud", "Piercing the Shroud: Holding Cell Relic", SC2WOL_LOC_ID_OFFSET + 2101, LocationType.BONUS),
- LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk Relic", SC2WOL_LOC_ID_OFFSET + 2102, LocationType.BONUS,
- lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)),
- LocationData("Piercing the Shroud", "Piercing the Shroud: First Escape Relic", SC2WOL_LOC_ID_OFFSET + 2103,LocationType.BONUS,
- lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)),
- LocationData("Piercing the Shroud", "Piercing the Shroud: Second Escape Relic", SC2WOL_LOC_ID_OFFSET + 2104, LocationType.BONUS,
- lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)),
- LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk", SC2WOL_LOC_ID_OFFSET + 2105, LocationType.OPTIONAL_BOSS,
- lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)),
- LocationData("Piercing the Shroud", "Piercing the Shroud: Fusion Reactor", SC2WOL_LOC_ID_OFFSET + 2106, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)),
- LocationData("Whispers of Doom", "Whispers of Doom: Victory", SC2WOL_LOC_ID_OFFSET + 2200, LocationType.VICTORY),
- LocationData("Whispers of Doom", "Whispers of Doom: First Hatchery", SC2WOL_LOC_ID_OFFSET + 2201, LocationType.BONUS),
- LocationData("Whispers of Doom", "Whispers of Doom: Second Hatchery", SC2WOL_LOC_ID_OFFSET + 2202, LocationType.BONUS),
- LocationData("Whispers of Doom", "Whispers of Doom: Third Hatchery", SC2WOL_LOC_ID_OFFSET + 2203, LocationType.BONUS),
- LocationData("Whispers of Doom", "Whispers of Doom: First Prophecy Fragment", SC2WOL_LOC_ID_OFFSET + 2204, LocationType.MISSION_PROGRESS),
- LocationData("Whispers of Doom", "Whispers of Doom: Second Prophecy Fragment", SC2WOL_LOC_ID_OFFSET + 2205, LocationType.MISSION_PROGRESS),
- LocationData("Whispers of Doom", "Whispers of Doom: Third Prophecy Fragment", SC2WOL_LOC_ID_OFFSET + 2206, LocationType.MISSION_PROGRESS),
- LocationData("A Sinister Turn", "A Sinister Turn: Victory", SC2WOL_LOC_ID_OFFSET + 2300, LocationType.VICTORY,
- lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)),
- LocationData("A Sinister Turn", "A Sinister Turn: Robotics Facility", SC2WOL_LOC_ID_OFFSET + 2301, LocationType.BONUS,
- lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)),
- LocationData("A Sinister Turn", "A Sinister Turn: Dark Shrine", SC2WOL_LOC_ID_OFFSET + 2302, LocationType.BONUS,
- lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)),
- LocationData("A Sinister Turn", "A Sinister Turn: Templar Archives", SC2WOL_LOC_ID_OFFSET + 2303, LocationType.BONUS,
- lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)),
- LocationData("A Sinister Turn", "A Sinister Turn: Northeast Base", SC2WOL_LOC_ID_OFFSET + 2304, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)),
- LocationData("A Sinister Turn", "A Sinister Turn: Southwest Base", SC2WOL_LOC_ID_OFFSET + 2305, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)),
- LocationData("A Sinister Turn", "A Sinister Turn: Maar", SC2WOL_LOC_ID_OFFSET + 2306, LocationType.MISSION_PROGRESS,
- lambda state: logic_level > 0 or state._sc2wol_has_protoss_medium_units(multiworld, player)),
- LocationData("A Sinister Turn", "A Sinister Turn: Northwest Preserver", SC2WOL_LOC_ID_OFFSET + 2307, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)),
- LocationData("A Sinister Turn", "A Sinister Turn: Southwest Preserver", SC2WOL_LOC_ID_OFFSET + 2308, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)),
- LocationData("A Sinister Turn", "A Sinister Turn: East Preserver", SC2WOL_LOC_ID_OFFSET + 2309, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)),
- LocationData("Echoes of the Future", "Echoes of the Future: Victory", SC2WOL_LOC_ID_OFFSET + 2400, LocationType.VICTORY,
- lambda state: logic_level > 0 or state._sc2wol_has_protoss_medium_units(multiworld, player)),
- LocationData("Echoes of the Future", "Echoes of the Future: Close Obelisk", SC2WOL_LOC_ID_OFFSET + 2401, LocationType.BONUS),
- LocationData("Echoes of the Future", "Echoes of the Future: West Obelisk", SC2WOL_LOC_ID_OFFSET + 2402, LocationType.BONUS,
- lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)),
- LocationData("Echoes of the Future", "Echoes of the Future: Base", SC2WOL_LOC_ID_OFFSET + 2403, LocationType.MISSION_PROGRESS),
- LocationData("Echoes of the Future", "Echoes of the Future: Southwest Tendril", SC2WOL_LOC_ID_OFFSET + 2404, LocationType.MISSION_PROGRESS),
- LocationData("Echoes of the Future", "Echoes of the Future: Southeast Tendril", SC2WOL_LOC_ID_OFFSET + 2405, LocationType.MISSION_PROGRESS,
- lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)),
- LocationData("Echoes of the Future", "Echoes of the Future: Northeast Tendril", SC2WOL_LOC_ID_OFFSET + 2406, LocationType.MISSION_PROGRESS,
- lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)),
- LocationData("Echoes of the Future", "Echoes of the Future: Northwest Tendril", SC2WOL_LOC_ID_OFFSET + 2407, LocationType.MISSION_PROGRESS,
- lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)),
- LocationData("In Utter Darkness", "In Utter Darkness: Defeat", SC2WOL_LOC_ID_OFFSET + 2500, LocationType.VICTORY),
- LocationData("In Utter Darkness", "In Utter Darkness: Protoss Archive", SC2WOL_LOC_ID_OFFSET + 2501, LocationType.BONUS,
- lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)),
- LocationData("In Utter Darkness", "In Utter Darkness: Kills", SC2WOL_LOC_ID_OFFSET + 2502, LocationType.CHALLENGE,
- lambda state: state._sc2wol_has_protoss_common_units(multiworld, player)),
- LocationData("In Utter Darkness", "In Utter Darkness: Urun", SC2WOL_LOC_ID_OFFSET + 2503, LocationType.MISSION_PROGRESS),
- LocationData("In Utter Darkness", "In Utter Darkness: Mohandar", SC2WOL_LOC_ID_OFFSET + 2504, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_protoss_common_units(multiworld, player)),
- LocationData("In Utter Darkness", "In Utter Darkness: Selendis", SC2WOL_LOC_ID_OFFSET + 2505, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_protoss_common_units(multiworld, player)),
- LocationData("In Utter Darkness", "In Utter Darkness: Artanis", SC2WOL_LOC_ID_OFFSET + 2506, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_protoss_common_units(multiworld, player)),
- LocationData("Gates of Hell", "Gates of Hell: Victory", SC2WOL_LOC_ID_OFFSET + 2600, LocationType.VICTORY,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player) and
- state._sc2wol_defense_rating(multiworld, player, True) > 6),
- LocationData("Gates of Hell", "Gates of Hell: Large Army", SC2WOL_LOC_ID_OFFSET + 2601, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player) and
- state._sc2wol_defense_rating(multiworld, player, True) > 6),
- LocationData("Gates of Hell", "Gates of Hell: 2 Drop Pods", SC2WOL_LOC_ID_OFFSET + 2602, LocationType.BONUS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player) and
- state._sc2wol_defense_rating(multiworld, player, True) > 6),
- LocationData("Gates of Hell", "Gates of Hell: 4 Drop Pods", SC2WOL_LOC_ID_OFFSET + 2603, LocationType.BONUS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player) and
- state._sc2wol_defense_rating(multiworld, player, True) > 6),
- LocationData("Gates of Hell", "Gates of Hell: 6 Drop Pods", SC2WOL_LOC_ID_OFFSET + 2604, LocationType.BONUS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player) and
- state._sc2wol_defense_rating(multiworld, player, True) > 6),
- LocationData("Gates of Hell", "Gates of Hell: 8 Drop Pods", SC2WOL_LOC_ID_OFFSET + 2605, LocationType.BONUS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player) and
- state._sc2wol_defense_rating(multiworld, player, True) > 6),
- LocationData("Belly of the Beast", "Belly of the Beast: Victory", SC2WOL_LOC_ID_OFFSET + 2700, LocationType.VICTORY),
- LocationData("Belly of the Beast", "Belly of the Beast: First Charge", SC2WOL_LOC_ID_OFFSET + 2701, LocationType.MISSION_PROGRESS),
- LocationData("Belly of the Beast", "Belly of the Beast: Second Charge", SC2WOL_LOC_ID_OFFSET + 2702, LocationType.MISSION_PROGRESS),
- LocationData("Belly of the Beast", "Belly of the Beast: Third Charge", SC2WOL_LOC_ID_OFFSET + 2703, LocationType.MISSION_PROGRESS),
- LocationData("Belly of the Beast", "Belly of the Beast: First Group Rescued", SC2WOL_LOC_ID_OFFSET + 2704, LocationType.BONUS),
- LocationData("Belly of the Beast", "Belly of the Beast: Second Group Rescued", SC2WOL_LOC_ID_OFFSET + 2705, LocationType.BONUS),
- LocationData("Belly of the Beast", "Belly of the Beast: Third Group Rescued", SC2WOL_LOC_ID_OFFSET + 2706, LocationType.BONUS),
- LocationData("Shatter the Sky", "Shatter the Sky: Victory", SC2WOL_LOC_ID_OFFSET + 2800, LocationType.VICTORY,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Shatter the Sky", "Shatter the Sky: Close Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2801, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Shatter the Sky", "Shatter the Sky: Northwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2802, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Shatter the Sky", "Shatter the Sky: Southeast Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2803, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Shatter the Sky", "Shatter the Sky: Southwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2804, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Shatter the Sky", "Shatter the Sky: Leviathan", SC2WOL_LOC_ID_OFFSET + 2805, LocationType.OPTIONAL_BOSS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Shatter the Sky", "Shatter the Sky: East Hatchery", SC2WOL_LOC_ID_OFFSET + 2806, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Shatter the Sky", "Shatter the Sky: North Hatchery", SC2WOL_LOC_ID_OFFSET + 2807, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("Shatter the Sky", "Shatter the Sky: Mid Hatchery", SC2WOL_LOC_ID_OFFSET + 2808, LocationType.MISSION_PROGRESS,
- lambda state: state._sc2wol_has_competent_comp(multiworld, player)),
- LocationData("All-In", "All-In: Victory", None, LocationType.VICTORY,
- lambda state: state._sc2wol_final_mission_requirements(multiworld, player))
- ]
-
- beat_events = []
-
- for i, location_data in enumerate(location_table):
- # Removing all item-based logic on No Logic
- if logic_level == 2:
- location_data = location_data._replace(rule=Location.access_rule)
- location_table[i] = location_data
- # Generating Beat event locations
- if location_data.name.endswith((": Victory", ": Defeat")):
- beat_events.append(
- location_data._replace(name="Beat " + location_data.name.rsplit(": ", 1)[0], code=None)
- )
- return tuple(location_table + beat_events)
diff --git a/worlds/sc2wol/LogicMixin.py b/worlds/sc2wol/LogicMixin.py
deleted file mode 100644
index 112302beb207..000000000000
--- a/worlds/sc2wol/LogicMixin.py
+++ /dev/null
@@ -1,148 +0,0 @@
-from BaseClasses import MultiWorld
-from worlds.AutoWorld import LogicMixin
-from .Options import get_option_value
-from .Items import get_basic_units, defense_ratings, zerg_defense_ratings
-
-
-class SC2WoLLogic(LogicMixin):
- def _sc2wol_has_common_unit(self, multiworld: MultiWorld, player: int) -> bool:
- return self.has_any(get_basic_units(multiworld, player), player)
-
- def _sc2wol_has_air(self, multiworld: MultiWorld, player: int) -> bool:
- return self.has_any({'Viking', 'Wraith', 'Banshee', 'Battlecruiser'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0 \
- and self.has_any({'Hercules', 'Medivac'}, player) and self._sc2wol_has_common_unit(multiworld, player)
-
- def _sc2wol_has_air_anti_air(self, multiworld: MultiWorld, player: int) -> bool:
- return self.has('Viking', player) \
- or self.has_all({'Wraith', 'Advanced Laser Technology (Wraith)'}, player) \
- or self.has_all({'Battlecruiser', 'ATX Laser Battery (Battlecruiser)'}, player) \
- or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'Wraith', 'Valkyrie', 'Battlecruiser'}, player)
-
- def _sc2wol_has_competent_ground_to_air(self, multiworld: MultiWorld, player: int) -> bool:
- return self.has('Goliath', player) \
- or self.has('Marine', player) and self.has_any({'Medic', 'Medivac'}, player) \
- or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has('Cyclone', player)
-
- def _sc2wol_has_competent_anti_air(self, multiworld: MultiWorld, player: int) -> bool:
- return self._sc2wol_has_competent_ground_to_air(multiworld, player) \
- or self._sc2wol_has_air_anti_air(multiworld, player)
-
- def _sc2wol_welcome_to_the_jungle_requirement(self, multiworld: MultiWorld, player: int) -> bool:
- return (
- self._sc2wol_has_common_unit(multiworld, player)
- and self._sc2wol_has_competent_ground_to_air(multiworld, player)
- ) or (
- get_option_value(multiworld, player, 'required_tactics') > 0
- and self.has_any({'Marine', 'Vulture'}, player)
- and self._sc2wol_has_air_anti_air(multiworld, player)
- )
-
- def _sc2wol_has_anti_air(self, multiworld: MultiWorld, player: int) -> bool:
- return self.has_any({'Missile Turret', 'Thor', 'War Pigs', 'Spartan Company', "Hel's Angel", 'Battlecruiser', 'Marine', 'Wraith', 'Valkyrie', 'Cyclone'}, player) \
- or self._sc2wol_has_competent_anti_air(multiworld, player) \
- or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'Ghost', 'Spectre', 'Widow Mine', 'Liberator'}, player)
-
- def _sc2wol_defense_rating(self, multiworld: MultiWorld, player: int, zerg_enemy: bool, air_enemy: bool = True) -> bool:
- defense_score = sum((defense_ratings[item] for item in defense_ratings if self.has(item, player)))
- if self.has_any({'Marine', 'Marauder'}, player) and self.has('Bunker', player):
- defense_score += 3
- if self.has_all({'Siege Tank', 'Maelstrom Rounds (Siege Tank)'}, player):
- defense_score += 2
- if self.has_all({'Siege Tank', 'Graduating Range (Siege Tank)'}, player):
- defense_score += 1
- if self.has_all({'Widow Mine', 'Concealment (Widow Mine)'}, player):
- defense_score += 1
- if zerg_enemy:
- defense_score += sum((zerg_defense_ratings[item] for item in zerg_defense_ratings if self.has(item, player)))
- if self.has('Firebat', player) and self.has('Bunker', player):
- defense_score += 2
- if not air_enemy and self.has('Missile Turret', player):
- defense_score -= defense_ratings['Missile Turret']
- # Advanced Tactics bumps defense rating requirements down by 2
- if get_option_value(multiworld, player, 'required_tactics') > 0:
- defense_score += 2
- return defense_score
-
- def _sc2wol_has_competent_comp(self, multiworld: MultiWorld, player: int) -> bool:
- return \
- (
- (
- self.has_any({'Marine', 'Marauder'}, player) and self.has_any({'Medivac', 'Medic'}, player)
- or self.has_any({'Thor', 'Banshee', 'Siege Tank'}, player)
- or self.has_all({'Liberator', 'Raid Artillery (Liberator)'}, player)
- ) and self._sc2wol_has_competent_anti_air(multiworld, player)
- ) \
- or \
- (
- self.has('Battlecruiser', player) and self._sc2wol_has_common_unit(multiworld, player)
- )
-
- def _sc2wol_has_train_killers(self, multiworld: MultiWorld, player: int) -> bool:
- return (
- self.has_any({'Siege Tank', 'Diamondback', 'Marauder', 'Cyclone'}, player)
- or get_option_value(multiworld, player, 'required_tactics') > 0
- and (
- self.has_all({'Reaper', "G-4 Clusterbomb"}, player)
- or self.has_all({'Spectre', 'Psionic Lash'}, player)
- or self.has_any({'Vulture', 'Liberator'}, player)
- )
- )
-
- def _sc2wol_able_to_rescue(self, multiworld: MultiWorld, player: int) -> bool:
- return self.has_any({'Medivac', 'Hercules', 'Raven', 'Viking'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0
-
- def _sc2wol_has_protoss_common_units(self, multiworld: MultiWorld, player: int) -> bool:
- return self.has_any({'Zealot', 'Immortal', 'Stalker', 'Dark Templar'}, player) \
- or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has('High Templar', player)
-
- def _sc2wol_has_protoss_medium_units(self, multiworld: MultiWorld, player: int) -> bool:
- return self._sc2wol_has_protoss_common_units(multiworld, player) and \
- self.has_any({'Stalker', 'Void Ray', 'Carrier'}, player) \
- or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has('Dark Templar', player)
-
- def _sc2wol_beats_protoss_deathball(self, multiworld: MultiWorld, player: int) -> bool:
- return (self.has_any({'Banshee', 'Battlecruiser'}, player) or
- self.has_all({'Liberator', 'Raid Artillery (Liberator)'}, player)) \
- and self._sc2wol_has_competent_anti_air(multiworld, player) or \
- self._sc2wol_has_competent_comp(multiworld, player) and self._sc2wol_has_air_anti_air(multiworld, player)
-
- def _sc2wol_has_mm_upgrade(self, multiworld: MultiWorld, player: int) -> bool:
- return self.has_any({"Combat Shield (Marine)", "Stabilizer Medpacks (Medic)"}, player)
-
- def _sc2wol_survives_rip_field(self, multiworld: MultiWorld, player: int) -> bool:
- return self.has("Battlecruiser", player) or \
- self._sc2wol_has_air(multiworld, player) and \
- self._sc2wol_has_competent_anti_air(multiworld, player) and \
- self.has("Science Vessel", player)
-
- def _sc2wol_has_nukes(self, multiworld: MultiWorld, player: int) -> bool:
- return get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'Ghost', 'Spectre'}, player)
-
- def _sc2wol_can_respond_to_colony_infestations(self, multiworld: MultiWorld, player: int) -> bool:
- return self._sc2wol_has_common_unit(multiworld, player) \
- and self._sc2wol_has_competent_anti_air(multiworld, player) \
- and \
- (
- self._sc2wol_has_air_anti_air(multiworld, player) or
- self.has_any({'Battlecruiser', 'Valkyrie'}), player
- ) \
- and \
- self._sc2wol_defense_rating(multiworld, player, True) >= 3
-
- def _sc2wol_final_mission_requirements(self, multiworld: MultiWorld, player: int):
- beats_kerrigan = self.has_any({'Marine', 'Banshee', 'Ghost'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0
- if get_option_value(multiworld, player, 'all_in_map') == 0:
- # Ground
- defense_rating = self._sc2wol_defense_rating(multiworld, player, True, False)
- if self.has_any({'Battlecruiser', 'Banshee'}, player):
- defense_rating += 3
- return defense_rating >= 12 and beats_kerrigan
- else:
- # Air
- defense_rating = self._sc2wol_defense_rating(multiworld, player, True, True)
- return defense_rating >= 8 and beats_kerrigan \
- and self.has_any({'Viking', 'Battlecruiser', 'Valkyrie'}, player) \
- and self.has_any({'Hive Mind Emulator', 'Psi Disruptor', 'Missile Turret'}, player)
-
- def _sc2wol_cleared_missions(self, multiworld: MultiWorld, player: int, mission_count: int) -> bool:
- return self.has_group("Missions", player, mission_count)
diff --git a/worlds/sc2wol/MissionTables.py b/worlds/sc2wol/MissionTables.py
deleted file mode 100644
index 298cd7a978a6..000000000000
--- a/worlds/sc2wol/MissionTables.py
+++ /dev/null
@@ -1,230 +0,0 @@
-from typing import NamedTuple, Dict, List
-from enum import IntEnum
-
-no_build_regions_list = ["Liberation Day", "Breakout", "Ghost of a Chance", "Piercing the Shroud", "Whispers of Doom",
- "Belly of the Beast"]
-easy_regions_list = ["The Outlaws", "Zero Hour", "Evacuation", "Outbreak", "Smash and Grab", "Devil's Playground"]
-medium_regions_list = ["Safe Haven", "Haven's Fall", "The Dig", "The Moebius Factor", "Supernova",
- "Welcome to the Jungle", "The Great Train Robbery", "Cutthroat", "Media Blitz",
- "A Sinister Turn", "Echoes of the Future"]
-hard_regions_list = ["Maw of the Void", "Engine of Destruction", "In Utter Darkness", "Gates of Hell",
- "Shatter the Sky"]
-
-
-class MissionPools(IntEnum):
- STARTER = 0
- EASY = 1
- MEDIUM = 2
- HARD = 3
- FINAL = 4
-
-
-class MissionInfo(NamedTuple):
- id: int
- required_world: List[int]
- category: str
- number: int = 0 # number of worlds need beaten
- completion_critical: bool = False # missions needed to beat game
- or_requirements: bool = False # true if the requirements should be or-ed instead of and-ed
-
-
-class FillMission(NamedTuple):
- type: int
- connect_to: List[int] # -1 connects to Menu
- category: str
- number: int = 0 # number of worlds need beaten
- completion_critical: bool = False # missions needed to beat game
- or_requirements: bool = False # true if the requirements should be or-ed instead of and-ed
- removal_priority: int = 0 # how many missions missing from the pool required to remove this mission
-
-
-vanilla_shuffle_order = [
- FillMission(MissionPools.STARTER, [-1], "Mar Sara", completion_critical=True),
- FillMission(MissionPools.EASY, [0], "Mar Sara", completion_critical=True),
- FillMission(MissionPools.EASY, [1], "Mar Sara", completion_critical=True),
- FillMission(MissionPools.EASY, [2], "Colonist"),
- FillMission(MissionPools.MEDIUM, [3], "Colonist"),
- FillMission(MissionPools.HARD, [4], "Colonist", number=7),
- FillMission(MissionPools.HARD, [4], "Colonist", number=7, removal_priority=1),
- FillMission(MissionPools.EASY, [2], "Artifact", completion_critical=True),
- FillMission(MissionPools.MEDIUM, [7], "Artifact", number=8, completion_critical=True),
- FillMission(MissionPools.HARD, [8], "Artifact", number=11, completion_critical=True),
- FillMission(MissionPools.HARD, [9], "Artifact", number=14, completion_critical=True, removal_priority=11),
- FillMission(MissionPools.HARD, [10], "Artifact", completion_critical=True, removal_priority=10),
- FillMission(MissionPools.MEDIUM, [2], "Covert", number=4),
- FillMission(MissionPools.MEDIUM, [12], "Covert"),
- FillMission(MissionPools.HARD, [13], "Covert", number=8, removal_priority=3),
- FillMission(MissionPools.HARD, [13], "Covert", number=8, removal_priority=2),
- FillMission(MissionPools.MEDIUM, [2], "Rebellion", number=6),
- FillMission(MissionPools.HARD, [16], "Rebellion"),
- FillMission(MissionPools.HARD, [17], "Rebellion"),
- FillMission(MissionPools.HARD, [18], "Rebellion", removal_priority=12),
- FillMission(MissionPools.HARD, [19], "Rebellion", removal_priority=5),
- FillMission(MissionPools.MEDIUM, [8], "Prophecy", removal_priority=9),
- FillMission(MissionPools.HARD, [21], "Prophecy", removal_priority=8),
- FillMission(MissionPools.HARD, [22], "Prophecy", removal_priority=7),
- FillMission(MissionPools.HARD, [23], "Prophecy", removal_priority=6),
- FillMission(MissionPools.HARD, [11], "Char", completion_critical=True),
- FillMission(MissionPools.HARD, [25], "Char", completion_critical=True, removal_priority=4),
- FillMission(MissionPools.HARD, [25], "Char", completion_critical=True),
- FillMission(MissionPools.FINAL, [26, 27], "Char", completion_critical=True, or_requirements=True)
-]
-
-mini_campaign_order = [
- FillMission(MissionPools.STARTER, [-1], "Mar Sara", completion_critical=True),
- FillMission(MissionPools.EASY, [0], "Colonist"),
- FillMission(MissionPools.MEDIUM, [1], "Colonist"),
- FillMission(MissionPools.EASY, [0], "Artifact", completion_critical=True),
- FillMission(MissionPools.MEDIUM, [3], "Artifact", number=4, completion_critical=True),
- FillMission(MissionPools.HARD, [4], "Artifact", number=8, completion_critical=True),
- FillMission(MissionPools.MEDIUM, [0], "Covert", number=2),
- FillMission(MissionPools.HARD, [6], "Covert"),
- FillMission(MissionPools.MEDIUM, [0], "Rebellion", number=3),
- FillMission(MissionPools.HARD, [8], "Rebellion"),
- FillMission(MissionPools.MEDIUM, [4], "Prophecy"),
- FillMission(MissionPools.HARD, [10], "Prophecy"),
- FillMission(MissionPools.HARD, [5], "Char", completion_critical=True),
- FillMission(MissionPools.HARD, [5], "Char", completion_critical=True),
- FillMission(MissionPools.FINAL, [12, 13], "Char", completion_critical=True, or_requirements=True)
-]
-
-gauntlet_order = [
- FillMission(MissionPools.STARTER, [-1], "I", completion_critical=True),
- FillMission(MissionPools.EASY, [0], "II", completion_critical=True),
- FillMission(MissionPools.EASY, [1], "III", completion_critical=True),
- FillMission(MissionPools.MEDIUM, [2], "IV", completion_critical=True),
- FillMission(MissionPools.MEDIUM, [3], "V", completion_critical=True),
- FillMission(MissionPools.HARD, [4], "VI", completion_critical=True),
- FillMission(MissionPools.FINAL, [5], "Final", completion_critical=True)
-]
-
-mini_gauntlet_order = [
- FillMission(MissionPools.STARTER, [-1], "I", completion_critical=True),
- FillMission(MissionPools.EASY, [0], "II", completion_critical=True),
- FillMission(MissionPools.MEDIUM, [1], "III", completion_critical=True),
- FillMission(MissionPools.FINAL, [2], "Final", completion_critical=True)
-]
-
-grid_order = [
- FillMission(MissionPools.STARTER, [-1], "_1"),
- FillMission(MissionPools.EASY, [0], "_1"),
- FillMission(MissionPools.MEDIUM, [1, 6, 3], "_1", or_requirements=True),
- FillMission(MissionPools.HARD, [2, 7], "_1", or_requirements=True),
- FillMission(MissionPools.EASY, [0], "_2"),
- FillMission(MissionPools.MEDIUM, [1, 4], "_2", or_requirements=True),
- FillMission(MissionPools.HARD, [2, 5, 10, 7], "_2", or_requirements=True),
- FillMission(MissionPools.HARD, [3, 6, 11], "_2", or_requirements=True),
- FillMission(MissionPools.MEDIUM, [4, 9, 12], "_3", or_requirements=True),
- FillMission(MissionPools.HARD, [5, 8, 10, 13], "_3", or_requirements=True),
- FillMission(MissionPools.HARD, [6, 9, 11, 14], "_3", or_requirements=True),
- FillMission(MissionPools.HARD, [7, 10], "_3", or_requirements=True),
- FillMission(MissionPools.HARD, [8, 13], "_4", or_requirements=True),
- FillMission(MissionPools.HARD, [9, 12, 14], "_4", or_requirements=True),
- FillMission(MissionPools.HARD, [10, 13], "_4", or_requirements=True),
- FillMission(MissionPools.FINAL, [11, 14], "_4", or_requirements=True)
-]
-
-mini_grid_order = [
- FillMission(MissionPools.STARTER, [-1], "_1"),
- FillMission(MissionPools.EASY, [0], "_1"),
- FillMission(MissionPools.MEDIUM, [1, 5], "_1", or_requirements=True),
- FillMission(MissionPools.EASY, [0], "_2"),
- FillMission(MissionPools.MEDIUM, [1, 3], "_2", or_requirements=True),
- FillMission(MissionPools.HARD, [2, 4], "_2", or_requirements=True),
- FillMission(MissionPools.MEDIUM, [3, 7], "_3", or_requirements=True),
- FillMission(MissionPools.HARD, [4, 6], "_3", or_requirements=True),
- FillMission(MissionPools.FINAL, [5, 7], "_3", or_requirements=True)
-]
-
-tiny_grid_order = [
- FillMission(MissionPools.STARTER, [-1], "_1"),
- FillMission(MissionPools.MEDIUM, [0], "_1"),
- FillMission(MissionPools.EASY, [0], "_2"),
- FillMission(MissionPools.FINAL, [1, 2], "_2", or_requirements=True),
-]
-
-blitz_order = [
- FillMission(MissionPools.STARTER, [-1], "I"),
- FillMission(MissionPools.EASY, [-1], "I"),
- FillMission(MissionPools.MEDIUM, [0, 1], "II", number=1, or_requirements=True),
- FillMission(MissionPools.MEDIUM, [0, 1], "II", number=1, or_requirements=True),
- FillMission(MissionPools.MEDIUM, [0, 1], "III", number=2, or_requirements=True),
- FillMission(MissionPools.MEDIUM, [0, 1], "III", number=2, or_requirements=True),
- FillMission(MissionPools.HARD, [0, 1], "IV", number=3, or_requirements=True),
- FillMission(MissionPools.HARD, [0, 1], "IV", number=3, or_requirements=True),
- FillMission(MissionPools.HARD, [0, 1], "V", number=4, or_requirements=True),
- FillMission(MissionPools.HARD, [0, 1], "V", number=4, or_requirements=True),
- FillMission(MissionPools.HARD, [0, 1], "Final", number=5, or_requirements=True),
- FillMission(MissionPools.FINAL, [0, 1], "Final", number=5, or_requirements=True)
-]
-
-mission_orders = [
- vanilla_shuffle_order,
- vanilla_shuffle_order,
- mini_campaign_order,
- grid_order,
- mini_grid_order,
- blitz_order,
- gauntlet_order,
- mini_gauntlet_order,
- tiny_grid_order
-]
-
-
-vanilla_mission_req_table = {
- "Liberation Day": MissionInfo(1, [], "Mar Sara", completion_critical=True),
- "The Outlaws": MissionInfo(2, [1], "Mar Sara", completion_critical=True),
- "Zero Hour": MissionInfo(3, [2], "Mar Sara", completion_critical=True),
- "Evacuation": MissionInfo(4, [3], "Colonist"),
- "Outbreak": MissionInfo(5, [4], "Colonist"),
- "Safe Haven": MissionInfo(6, [5], "Colonist", number=7),
- "Haven's Fall": MissionInfo(7, [5], "Colonist", number=7),
- "Smash and Grab": MissionInfo(8, [3], "Artifact", completion_critical=True),
- "The Dig": MissionInfo(9, [8], "Artifact", number=8, completion_critical=True),
- "The Moebius Factor": MissionInfo(10, [9], "Artifact", number=11, completion_critical=True),
- "Supernova": MissionInfo(11, [10], "Artifact", number=14, completion_critical=True),
- "Maw of the Void": MissionInfo(12, [11], "Artifact", completion_critical=True),
- "Devil's Playground": MissionInfo(13, [3], "Covert", number=4),
- "Welcome to the Jungle": MissionInfo(14, [13], "Covert"),
- "Breakout": MissionInfo(15, [14], "Covert", number=8),
- "Ghost of a Chance": MissionInfo(16, [14], "Covert", number=8),
- "The Great Train Robbery": MissionInfo(17, [3], "Rebellion", number=6),
- "Cutthroat": MissionInfo(18, [17], "Rebellion"),
- "Engine of Destruction": MissionInfo(19, [18], "Rebellion"),
- "Media Blitz": MissionInfo(20, [19], "Rebellion"),
- "Piercing the Shroud": MissionInfo(21, [20], "Rebellion"),
- "Whispers of Doom": MissionInfo(22, [9], "Prophecy"),
- "A Sinister Turn": MissionInfo(23, [22], "Prophecy"),
- "Echoes of the Future": MissionInfo(24, [23], "Prophecy"),
- "In Utter Darkness": MissionInfo(25, [24], "Prophecy"),
- "Gates of Hell": MissionInfo(26, [12], "Char", completion_critical=True),
- "Belly of the Beast": MissionInfo(27, [26], "Char", completion_critical=True),
- "Shatter the Sky": MissionInfo(28, [26], "Char", completion_critical=True),
- "All-In": MissionInfo(29, [27, 28], "Char", completion_critical=True, or_requirements=True)
-}
-
-lookup_id_to_mission: Dict[int, str] = {
- data.id: mission_name for mission_name, data in vanilla_mission_req_table.items() if data.id}
-
-starting_mission_locations = {
- "Liberation Day": "Liberation Day: Victory",
- "Breakout": "Breakout: Victory",
- "Ghost of a Chance": "Ghost of a Chance: Victory",
- "Piercing the Shroud": "Piercing the Shroud: Victory",
- "Whispers of Doom": "Whispers of Doom: Victory",
- "Belly of the Beast": "Belly of the Beast: Victory",
- "Zero Hour": "Zero Hour: First Group Rescued",
- "Evacuation": "Evacuation: Reach Hanson",
- "Devil's Playground": "Devil's Playground: Tosh's Miners",
- "Smash and Grab": "Smash and Grab: First Relic",
- "The Great Train Robbery": "The Great Train Robbery: North Defiler"
-}
-
-
-alt_final_mission_locations = {
- "Maw of the Void": "Maw of the Void: Victory",
- "Engine of Destruction": "Engine of Destruction: Victory",
- "Supernova": "Supernova: Victory",
- "Gates of Hell": "Gates of Hell: Victory",
- "Shatter the Sky": "Shatter the Sky: Victory"
-}
\ No newline at end of file
diff --git a/worlds/sc2wol/Options.py b/worlds/sc2wol/Options.py
deleted file mode 100644
index e4b6a740669a..000000000000
--- a/worlds/sc2wol/Options.py
+++ /dev/null
@@ -1,362 +0,0 @@
-from typing import Dict, FrozenSet, Union
-from BaseClasses import MultiWorld
-from Options import Choice, Option, Toggle, DefaultOnToggle, ItemSet, OptionSet, Range
-from .MissionTables import vanilla_mission_req_table
-
-ORDER_VANILLA = 0
-ORDER_VANILLA_SHUFFLED = 1
-
-class GameDifficulty(Choice):
- """
- The difficulty of the campaign, affects enemy AI, starting units, and game speed.
-
- For those unfamiliar with the Archipelago randomizer, the recommended settings are one difficulty level
- lower than the vanilla game
- """
- display_name = "Game Difficulty"
- option_casual = 0
- option_normal = 1
- option_hard = 2
- option_brutal = 3
- default = 1
-
-class GameSpeed(Choice):
- """Optional setting to override difficulty-based game speed."""
- display_name = "Game Speed"
- option_default = 0
- option_slower = 1
- option_slow = 2
- option_normal = 3
- option_fast = 4
- option_faster = 5
- default = option_default
-
-class FinalMap(Choice):
- """
- Determines if the final map and goal of the campaign.
- All in: You need to beat All-in map
- Random Hard: A random hard mission is selected as a goal.
- Beat this mission in order to complete the game.
- All-in map won't be in the campaign
-
- Vanilla mission order always ends with All in mission!
-
- Warning: Using All-in with a short mission order (7 or fewer missions) is not recommended,
- as there might not be enough locations to place all the required items,
- any excess required items will be placed into the player's starting inventory!
-
- This option is short-lived. It may be changed in the future
- """
- display_name = "Final Map"
- option_all_in = 0
- option_random_hard = 1
-
-class AllInMap(Choice):
- """Determines what version of All-In (final map) that will be generated for the campaign."""
- display_name = "All In Map"
- option_ground = 0
- option_air = 1
-
-
-class MissionOrder(Choice):
- """
- Determines the order the missions are played in. The last three mission orders end in a random mission.
- Vanilla (29): Keeps the standard mission order and branching from the WoL Campaign.
- Vanilla Shuffled (29): Keeps same branching paths from the WoL Campaign but randomizes the order of missions within.
- Mini Campaign (15): Shorter version of the campaign with randomized missions and optional branches.
- Grid (16): A 4x4 grid of random missions. Start at the top-left and forge a path towards bottom-right mission to win.
- Mini Grid (9): A 3x3 version of Grid. Complete the bottom-right mission to win.
- Blitz (12): 12 random missions that open up very quickly. Complete the bottom-right mission to win.
- Gauntlet (7): Linear series of 7 random missions to complete the campaign.
- Mini Gauntlet (4): Linear series of 4 random missions to complete the campaign.
- Tiny Grid (4): A 2x2 version of Grid. Complete the bottom-right mission to win.
- """
- display_name = "Mission Order"
- option_vanilla = 0
- option_vanilla_shuffled = 1
- option_mini_campaign = 2
- option_grid = 3
- option_mini_grid = 4
- option_blitz = 5
- option_gauntlet = 6
- option_mini_gauntlet = 7
- option_tiny_grid = 8
-
-
-class PlayerColor(Choice):
- """Determines in-game team color."""
- display_name = "Player Color"
- option_white = 0
- option_red = 1
- option_blue = 2
- option_teal = 3
- option_purple = 4
- option_yellow = 5
- option_orange = 6
- option_green = 7
- option_light_pink = 8
- option_violet = 9
- option_light_grey = 10
- option_dark_green = 11
- option_brown = 12
- option_light_green = 13
- option_dark_grey = 14
- option_pink = 15
- option_rainbow = 16
- option_default = 17
- default = option_default
-
-
-class ShuffleProtoss(DefaultOnToggle):
- """Determines if the 3 protoss missions are included in the shuffle if Vanilla mission order is not enabled.
- If turned off, the 3 protoss missions will not appear and Protoss units are removed from the pool."""
- display_name = "Shuffle Protoss Missions"
-
-
-class ShuffleNoBuild(DefaultOnToggle):
- """Determines if the 5 no-build missions are included in the shuffle if Vanilla mission order is not enabled.
- If turned off, the 5 no-build missions will not appear."""
- display_name = "Shuffle No-Build Missions"
-
-
-class EarlyUnit(DefaultOnToggle):
- """
- Guarantees that the first mission will contain a unit.
-
- Each mission available to be the first mission has a pre-defined location where the unit should spawn.
- This location gets overriden over any exclusion. It's guaranteed to be reachable with an empty inventory.
- """
- display_name = "Early Unit"
-
-
-class RequiredTactics(Choice):
- """Determines the maximum tactical difficulty of the seed (separate from mission difficulty). Higher settings
- increase randomness.
-
- Standard: All missions can be completed with good micro and macro.
- Advanced: Completing missions may require relying on starting units and micro-heavy units.
- No Logic: Units and upgrades may be placed anywhere. LIKELY TO RENDER THE RUN IMPOSSIBLE ON HARDER DIFFICULTIES!"""
- display_name = "Required Tactics"
- option_standard = 0
- option_advanced = 1
- option_no_logic = 2
-
-
-class UnitsAlwaysHaveUpgrades(DefaultOnToggle):
- """
- If turned on, all upgrades will be present for each unit and structure in the seed.
- This usually results in fewer units.
-
- See also: Max Number of Upgrades
- """
- display_name = "Units Always Have Upgrades"
-
-
-class GenericUpgradeMissions(Range):
- """Determines the percentage of missions in the mission order that must be completed before
- level 1 of all weapon and armor upgrades is unlocked. Level 2 upgrades require double the amount of missions,
- and level 3 requires triple the amount. The required amounts are always rounded down.
- If set to 0, upgrades are instead added to the item pool and must be found to be used."""
- display_name = "Generic Upgrade Missions"
- range_start = 0
- range_end = 100
- default = 0
-
-
-class GenericUpgradeResearch(Choice):
- """Determines how weapon and armor upgrades affect missions once unlocked.
-
- Vanilla: Upgrades must be researched as normal.
- Auto In No-Build: In No-Build missions, upgrades are automatically researched.
- In all other missions, upgrades must be researched as normal.
- Auto In Build: In No-Build missions, upgrades are unavailable as normal.
- In all other missions, upgrades are automatically researched.
- Always Auto: Upgrades are automatically researched in all missions."""
- display_name = "Generic Upgrade Research"
- option_vanilla = 0
- option_auto_in_no_build = 1
- option_auto_in_build = 2
- option_always_auto = 3
-
-
-class GenericUpgradeItems(Choice):
- """Determines how weapon and armor upgrades are split into items. All options produce 3 levels of each item.
- Does nothing if upgrades are unlocked by completed mission counts.
-
- Individual Items: All weapon and armor upgrades are each an item,
- resulting in 18 total upgrade items.
- Bundle Weapon And Armor: All types of weapon upgrades are one item,
- and all types of armor upgrades are one item,
- resulting in 6 total items.
- Bundle Unit Class: Weapon and armor upgrades are merged,
- but Infantry, Vehicle, and Starship upgrades are bundled separately,
- resulting in 9 total items.
- Bundle All: All weapon and armor upgrades are one item,
- resulting in 3 total items."""
- display_name = "Generic Upgrade Items"
- option_individual_items = 0
- option_bundle_weapon_and_armor = 1
- option_bundle_unit_class = 2
- option_bundle_all = 3
-
-
-class NovaCovertOpsItems(Toggle):
- """If turned on, the equipment upgrades from Nova Covert Ops may be present in the world."""
- display_name = "Nova Covert Ops Items"
- default = Toggle.option_true
-
-
-class BroodWarItems(Toggle):
- """If turned on, returning items from StarCraft: Brood War may appear in the world."""
- display_name = "Brood War Items"
- default = Toggle.option_true
-
-
-class ExtendedItems(Toggle):
- """If turned on, original items that did not appear in Campaign mode may appear in the world."""
- display_name = "Extended Items"
- default = Toggle.option_true
-
-
-class MaxNumberOfUpgrades(Range):
- """
- Set a maximum to the number of upgrades a unit/structure can have. -1 is used to define unlimited.
- Note that most unit have 4 or 6 upgrades.
-
- If used with Units Always Have Upgrades, each unit has this given amount of upgrades (if there enough upgrades exist)
-
- See also: Units Always Have Upgrades
- """
- display_name = "Maximum number of upgrades per unit/structure"
- range_start = -1
- # Do not know the maximum, but it is less than 123!
- range_end = 123
- default = -1
-
-
-class LockedItems(ItemSet):
- """Guarantees that these items will be unlockable"""
- display_name = "Locked Items"
-
-
-class ExcludedItems(ItemSet):
- """Guarantees that these items will not be unlockable"""
- display_name = "Excluded Items"
-
-
-class ExcludedMissions(OptionSet):
- """Guarantees that these missions will not appear in the campaign
- Doesn't apply to vanilla mission order.
- It may be impossible to build a valid campaign if too many missions are excluded."""
- display_name = "Excluded Missions"
- valid_keys = {mission_name for mission_name in vanilla_mission_req_table.keys() if mission_name != 'All-In'}
-
-
-class LocationInclusion(Choice):
- option_enabled = 0
- option_trash = 1
- option_nothing = 2
-
-
-class MissionProgressLocations(LocationInclusion):
- """
- Enables or disables item rewards for progressing (not finishing) a mission.
- Progressing a mission is usually a task of completing or progressing into a main objective.
- Clearing an expansion base also counts here.
-
- Enabled: All locations fitting into this do their normal rewards
- Trash: Forces a trash item in
- Nothing: No rewards for this type of tasks, effectively disabling such locations
-
- Note: Individual locations subject to plando are always enabled, so the plando can be placed properly.
- See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando)
- """
- display_name = "Mission Progress Locations"
-
-
-class BonusLocations(LocationInclusion):
- """
- Enables or disables item rewards for completing bonus tasks.
- Bonus tasks are those giving you a campaign-wide or mission-wide bonus in vanilla game:
- Research, credits, bonus units or resources, etc.
-
- Enabled: All locations fitting into this do their normal rewards
- Trash: Forces a trash item in
- Nothing: No rewards for this type of tasks, effectively disabling such locations
-
- Note: Individual locations subject to plando are always enabled, so the plando can be placed properly.
- See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando)
- """
- display_name = "Bonus Locations"
-
-
-class ChallengeLocations(LocationInclusion):
- """
- Enables or disables item rewards for completing challenge tasks.
- Challenges are tasks that have usually higher requirements to be completed
- than to complete the mission they're in successfully.
- You might be required to visit the same mission later when getting stronger in order to finish these tasks.
-
- Enabled: All locations fitting into this do their normal rewards
- Trash: Forces a trash item in
- Nothing: No rewards for this type of tasks, effectively disabling such locations
-
- Note: Individual locations subject to plando are always enabled, so the plando can be placed properly.
- See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando)
- """
- display_name = "Challenge Locations"
-
-
-class OptionalBossLocations(LocationInclusion):
- """
- Enables or disables item rewards for defeating optional bosses.
- An optional boss is any boss that's not required to kill in order to finish the mission successfully.
- All Brutalisks, Loki, etc. belongs here.
-
- Enabled: All locations fitting into this do their normal rewards
- Trash: Forces a trash item in
- Nothing: No rewards for this type of tasks, effectively disabling such locations
-
- Note: Individual locations subject to plando are always enabled, so the plando can be placed properly.
- See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando)
- """
- display_name = "Optional Boss Locations"
-
-
-# noinspection PyTypeChecker
-sc2wol_options: Dict[str, Option] = {
- "game_difficulty": GameDifficulty,
- "game_speed": GameSpeed,
- "all_in_map": AllInMap,
- "final_map": FinalMap,
- "mission_order": MissionOrder,
- "player_color": PlayerColor,
- "shuffle_protoss": ShuffleProtoss,
- "shuffle_no_build": ShuffleNoBuild,
- "early_unit": EarlyUnit,
- "required_tactics": RequiredTactics,
- "units_always_have_upgrades": UnitsAlwaysHaveUpgrades,
- "max_number_of_upgrades": MaxNumberOfUpgrades,
- "generic_upgrade_missions": GenericUpgradeMissions,
- "generic_upgrade_research": GenericUpgradeResearch,
- "generic_upgrade_items": GenericUpgradeItems,
- "locked_items": LockedItems,
- "excluded_items": ExcludedItems,
- "excluded_missions": ExcludedMissions,
- "nco_items": NovaCovertOpsItems,
- "bw_items": BroodWarItems,
- "ext_items": ExtendedItems,
- "mission_progress_locations": MissionProgressLocations,
- "bonus_locations": BonusLocations,
- "challenge_locations": ChallengeLocations,
- "optional_boss_locations": OptionalBossLocations
-}
-
-
-def get_option_value(multiworld: MultiWorld, player: int, name: str) -> Union[int, FrozenSet]:
- if multiworld is None:
- return sc2wol_options[name].default
-
- player_option = getattr(multiworld, name)[player]
-
- return player_option.value
diff --git a/worlds/sc2wol/PoolFilter.py b/worlds/sc2wol/PoolFilter.py
deleted file mode 100644
index 23422a3d1ea5..000000000000
--- a/worlds/sc2wol/PoolFilter.py
+++ /dev/null
@@ -1,367 +0,0 @@
-from typing import Callable, Dict, List, Set
-from BaseClasses import MultiWorld, ItemClassification, Item, Location
-from .Items import get_full_item_list, spider_mine_sources, second_pass_placeable_items, filler_items, \
- progressive_if_nco
-from .MissionTables import no_build_regions_list, easy_regions_list, medium_regions_list, hard_regions_list,\
- mission_orders, MissionInfo, alt_final_mission_locations, MissionPools
-from .Options import get_option_value, MissionOrder, FinalMap, MissionProgressLocations, LocationInclusion
-from .LogicMixin import SC2WoLLogic
-
-# Items with associated upgrades
-UPGRADABLE_ITEMS = [
- "Marine", "Medic", "Firebat", "Marauder", "Reaper", "Ghost", "Spectre",
- "Hellion", "Vulture", "Goliath", "Diamondback", "Siege Tank", "Thor", "Predator", "Widow Mine", "Cyclone",
- "Medivac", "Wraith", "Viking", "Banshee", "Battlecruiser", "Raven", "Science Vessel", "Liberator", "Valkyrie",
- "Bunker", "Missile Turret"
-]
-
-BARRACKS_UNITS = {"Marine", "Medic", "Firebat", "Marauder", "Reaper", "Ghost", "Spectre"}
-FACTORY_UNITS = {"Hellion", "Vulture", "Goliath", "Diamondback", "Siege Tank", "Thor", "Predator", "Widow Mine", "Cyclone"}
-STARPORT_UNITS = {"Medivac", "Wraith", "Viking", "Banshee", "Battlecruiser", "Hercules", "Science Vessel", "Raven", "Liberator", "Valkyrie"}
-
-PROTOSS_REGIONS = {"A Sinister Turn", "Echoes of the Future", "In Utter Darkness"}
-
-
-def filter_missions(multiworld: MultiWorld, player: int) -> Dict[int, List[str]]:
- """
- Returns a semi-randomly pruned tuple of no-build, easy, medium, and hard mission sets
- """
-
- mission_order_type = get_option_value(multiworld, player, "mission_order")
- shuffle_no_build = get_option_value(multiworld, player, "shuffle_no_build")
- shuffle_protoss = get_option_value(multiworld, player, "shuffle_protoss")
- excluded_missions = get_option_value(multiworld, player, "excluded_missions")
- final_map = get_option_value(multiworld, player, "final_map")
- mission_pools = {
- MissionPools.STARTER: no_build_regions_list[:],
- MissionPools.EASY: easy_regions_list[:],
- MissionPools.MEDIUM: medium_regions_list[:],
- MissionPools.HARD: hard_regions_list[:],
- MissionPools.FINAL: []
- }
- if mission_order_type == MissionOrder.option_vanilla:
- # Vanilla uses the entire mission pool
- mission_pools[MissionPools.FINAL] = ['All-In']
- return mission_pools
- # Omitting No-Build missions if not shuffling no-build
- if not shuffle_no_build:
- excluded_missions = excluded_missions.union(no_build_regions_list)
- # Omitting Protoss missions if not shuffling protoss
- if not shuffle_protoss:
- excluded_missions = excluded_missions.union(PROTOSS_REGIONS)
- # Replacing All-In with alternate ending depending on option
- if final_map == FinalMap.option_random_hard:
- final_mission = multiworld.random.choice([mission for mission in alt_final_mission_locations.keys() if mission not in excluded_missions])
- excluded_missions.add(final_mission)
- else:
- final_mission = 'All-In'
- # Excluding missions
- for difficulty, mission_pool in mission_pools.items():
- mission_pools[difficulty] = [mission for mission in mission_pool if mission not in excluded_missions]
- mission_pools[MissionPools.FINAL].append(final_mission)
- # Mission pool changes on Build-Only
- if not get_option_value(multiworld, player, 'shuffle_no_build'):
- def move_mission(mission_name, current_pool, new_pool):
- if mission_name in mission_pools[current_pool]:
- mission_pools[current_pool].remove(mission_name)
- mission_pools[new_pool].append(mission_name)
- # Replacing No Build missions with Easy missions
- move_mission("Zero Hour", MissionPools.EASY, MissionPools.STARTER)
- move_mission("Evacuation", MissionPools.EASY, MissionPools.STARTER)
- move_mission("Devil's Playground", MissionPools.EASY, MissionPools.STARTER)
- # Pushing Outbreak to Normal, as it cannot be placed as the second mission on Build-Only
- move_mission("Outbreak", MissionPools.EASY, MissionPools.MEDIUM)
- # Pushing extra Normal missions to Easy
- move_mission("The Great Train Robbery", MissionPools.MEDIUM, MissionPools.EASY)
- move_mission("Echoes of the Future", MissionPools.MEDIUM, MissionPools.EASY)
- move_mission("Cutthroat", MissionPools.MEDIUM, MissionPools.EASY)
- # Additional changes on Advanced Tactics
- if get_option_value(multiworld, player, "required_tactics") > 0:
- move_mission("The Great Train Robbery", MissionPools.EASY, MissionPools.STARTER)
- move_mission("Smash and Grab", MissionPools.EASY, MissionPools.STARTER)
- move_mission("Moebius Factor", MissionPools.MEDIUM, MissionPools.EASY)
- move_mission("Welcome to the Jungle", MissionPools.MEDIUM, MissionPools.EASY)
- move_mission("Engine of Destruction", MissionPools.HARD, MissionPools.MEDIUM)
-
- return mission_pools
-
-
-def get_item_upgrades(inventory: List[Item], parent_item: Item or str):
- item_name = parent_item.name if isinstance(parent_item, Item) else parent_item
- return [
- inv_item for inv_item in inventory
- if get_full_item_list()[inv_item.name].parent_item == item_name
- ]
-
-
-def get_item_quantity(item: Item, multiworld: MultiWorld, player: int):
- if (not get_option_value(multiworld, player, "nco_items")) \
- and item.name in progressive_if_nco:
- return 1
- return get_full_item_list()[item.name].quantity
-
-
-def copy_item(item: Item):
- return Item(item.name, item.classification, item.code, item.player)
-
-
-class ValidInventory:
-
- def has(self, item: str, player: int):
- return item in self.logical_inventory
-
- def has_any(self, items: Set[str], player: int):
- return any(item in self.logical_inventory for item in items)
-
- def has_all(self, items: Set[str], player: int):
- return all(item in self.logical_inventory for item in items)
-
- def has_units_per_structure(self) -> bool:
- return len(BARRACKS_UNITS.intersection(self.logical_inventory)) > self.min_units_per_structure and \
- len(FACTORY_UNITS.intersection(self.logical_inventory)) > self.min_units_per_structure and \
- len(STARPORT_UNITS.intersection(self.logical_inventory)) > self.min_units_per_structure
-
- def generate_reduced_inventory(self, inventory_size: int, mission_requirements: List[Callable]) -> List[Item]:
- """Attempts to generate a reduced inventory that can fulfill the mission requirements."""
- inventory = list(self.item_pool)
- locked_items = list(self.locked_items)
- self.logical_inventory = {
- item.name for item in inventory + locked_items + self.existing_items
- if item.classification in (ItemClassification.progression, ItemClassification.progression_skip_balancing)
- }
- requirements = mission_requirements
- cascade_keys = self.cascade_removal_map.keys()
- units_always_have_upgrades = get_option_value(self.multiworld, self.player, "units_always_have_upgrades")
-
- def attempt_removal(item: Item) -> bool:
- # If item can be removed and has associated items, remove them as well
- inventory.remove(item)
- # Only run logic checks when removing logic items
- if item.name in self.logical_inventory:
- self.logical_inventory.remove(item.name)
- if not all(requirement(self) for requirement in requirements):
- # If item cannot be removed, lock or revert
- self.logical_inventory.add(item.name)
- for _ in range(get_item_quantity(item, self.multiworld, self.player)):
- locked_items.append(copy_item(item))
- return False
- return True
-
- # Limit the maximum number of upgrades
- maxUpgrad = get_option_value(self.multiworld, self.player,
- "max_number_of_upgrades")
- if maxUpgrad != -1:
- unit_avail_upgrades = {}
- # Needed to take into account locked/existing items
- unit_nb_upgrades = {}
- for item in inventory:
- cItem = get_full_item_list()[item.name]
- if cItem.type in UPGRADABLE_ITEMS and item.name not in unit_avail_upgrades:
- unit_avail_upgrades[item.name] = []
- unit_nb_upgrades[item.name] = 0
- elif cItem.parent_item is not None:
- if cItem.parent_item not in unit_avail_upgrades:
- unit_avail_upgrades[cItem.parent_item] = [item]
- unit_nb_upgrades[cItem.parent_item] = 1
- else:
- unit_avail_upgrades[cItem.parent_item].append(item)
- unit_nb_upgrades[cItem.parent_item] += 1
- # For those two categories, we count them but dont include them in removal
- for item in locked_items + self.existing_items:
- cItem = get_full_item_list()[item.name]
- if cItem.type in UPGRADABLE_ITEMS and item.name not in unit_avail_upgrades:
- unit_avail_upgrades[item.name] = []
- unit_nb_upgrades[item.name] = 0
- elif cItem.parent_item is not None:
- if cItem.parent_item not in unit_avail_upgrades:
- unit_nb_upgrades[cItem.parent_item] = 1
- else:
- unit_nb_upgrades[cItem.parent_item] += 1
- # Making sure that the upgrades being removed is random
- # Currently, only for combat shield vs Stabilizer Medpacks...
- shuffled_unit_upgrade_list = list(unit_avail_upgrades.keys())
- self.multiworld.random.shuffle(shuffled_unit_upgrade_list)
- for unit in shuffled_unit_upgrade_list:
- while (unit_nb_upgrades[unit] > maxUpgrad) \
- and (len(unit_avail_upgrades[unit]) > 0):
- itemCandidate = self.multiworld.random.choice(unit_avail_upgrades[unit])
- _ = attempt_removal(itemCandidate)
- # Whatever it succeed to remove the iventory or it fails and thus
- # lock it, the upgrade is no longer available for removal
- unit_avail_upgrades[unit].remove(itemCandidate)
- unit_nb_upgrades[unit] -= 1
-
- # Locking associated items for items that have already been placed when units_always_have_upgrades is on
- if units_always_have_upgrades:
- existing_items = set(self.existing_items[:] + locked_items)
- while existing_items:
- existing_item = existing_items.pop()
- items_to_lock = self.cascade_removal_map.get(existing_item, [existing_item])
- if get_full_item_list()[existing_item.name].type != "Upgrade":
- # Don't process general upgrades, they may have been pre-locked per-level
- for item in items_to_lock:
- if item in inventory:
- item_quantity = inventory.count(item)
- # Unit upgrades, lock all levels
- for _ in range(item_quantity):
- inventory.remove(item)
- if item not in locked_items:
- # Lock all the associated items if not already locked
- for _ in range(item_quantity):
- locked_items.append(copy_item(item))
- if item in existing_items:
- existing_items.remove(item)
-
- if self.min_units_per_structure > 0 and self.has_units_per_structure():
- requirements.append(lambda state: state.has_units_per_structure())
-
- # Determining if the full-size inventory can complete campaign
- if not all(requirement(self) for requirement in requirements):
- raise Exception("Too many items excluded - campaign is impossible to complete.")
-
- while len(inventory) + len(locked_items) > inventory_size:
- if len(inventory) == 0:
- # There are more items than locations and all of them are already locked due to YAML or logic.
- # Random items from locked ones will go to starting items
- self.multiworld.random.shuffle(locked_items)
- while len(locked_items) > inventory_size:
- item: Item = locked_items.pop()
- self.multiworld.push_precollected(item)
- break
- # Select random item from removable items
- item = self.multiworld.random.choice(inventory)
- # Cascade removals to associated items
- if item in cascade_keys:
- items_to_remove = self.cascade_removal_map[item]
- transient_items = []
- cascade_failure = False
- while len(items_to_remove) > 0:
- item_to_remove = items_to_remove.pop()
- transient_items.append(item_to_remove)
- if item_to_remove not in inventory:
- if units_always_have_upgrades and item_to_remove in locked_items:
- cascade_failure = True
- break
- else:
- continue
- success = attempt_removal(item_to_remove)
- if not success and units_always_have_upgrades:
- cascade_failure = True
- transient_items += items_to_remove
- break
- # Lock all associated items if any of them cannot be removed on Units Always Have Upgrades
- if cascade_failure:
- for transient_item in transient_items:
- if transient_item in inventory:
- for _ in range(inventory.count(transient_item)):
- inventory.remove(transient_item)
- if transient_item not in locked_items:
- for _ in range(get_item_quantity(transient_item, self.multiworld, self.player)):
- locked_items.append(copy_item(transient_item))
- if transient_item.classification in (ItemClassification.progression, ItemClassification.progression_skip_balancing):
- self.logical_inventory.add(transient_item.name)
- else:
- attempt_removal(item)
-
- if not spider_mine_sources & self.logical_inventory:
- inventory = [item for item in inventory if not item.name.endswith("(Spider Mine)")]
- if not BARRACKS_UNITS & self.logical_inventory:
- inventory = [item for item in inventory if
- not (item.name.startswith("Progressive Infantry") or item.name == "Orbital Strike")]
- if not FACTORY_UNITS & self.logical_inventory:
- inventory = [item for item in inventory if not item.name.startswith("Progressive Vehicle")]
- if not STARPORT_UNITS & self.logical_inventory:
- inventory = [item for item in inventory if not item.name.startswith("Progressive Ship")]
-
- # Cull finished, adding locked items back into inventory
- inventory += locked_items
-
- # Replacing empty space with generically useful items
- replacement_items = [item for item in self.item_pool
- if (item not in inventory
- and item not in self.locked_items
- and item.name in second_pass_placeable_items)]
- self.multiworld.random.shuffle(replacement_items)
- while len(inventory) < inventory_size and len(replacement_items) > 0:
- item = replacement_items.pop()
- inventory.append(item)
-
- return inventory
-
- def _read_logic(self):
- self._sc2wol_has_common_unit = lambda world, player: SC2WoLLogic._sc2wol_has_common_unit(self, world, player)
- self._sc2wol_has_air = lambda world, player: SC2WoLLogic._sc2wol_has_air(self, world, player)
- self._sc2wol_has_air_anti_air = lambda world, player: SC2WoLLogic._sc2wol_has_air_anti_air(self, world, player)
- self._sc2wol_has_competent_anti_air = lambda world, player: SC2WoLLogic._sc2wol_has_competent_anti_air(self, world, player)
- self._sc2wol_has_competent_ground_to_air = lambda world, player: SC2WoLLogic._sc2wol_has_competent_ground_to_air(self, world, player)
- self._sc2wol_has_anti_air = lambda world, player: SC2WoLLogic._sc2wol_has_anti_air(self, world, player)
- self._sc2wol_defense_rating = lambda world, player, zerg_enemy, air_enemy=False: SC2WoLLogic._sc2wol_defense_rating(self, world, player, zerg_enemy, air_enemy)
- self._sc2wol_has_competent_comp = lambda world, player: SC2WoLLogic._sc2wol_has_competent_comp(self, world, player)
- self._sc2wol_has_train_killers = lambda world, player: SC2WoLLogic._sc2wol_has_train_killers(self, world, player)
- self._sc2wol_able_to_rescue = lambda world, player: SC2WoLLogic._sc2wol_able_to_rescue(self, world, player)
- self._sc2wol_beats_protoss_deathball = lambda world, player: SC2WoLLogic._sc2wol_beats_protoss_deathball(self, world, player)
- self._sc2wol_survives_rip_field = lambda world, player: SC2WoLLogic._sc2wol_survives_rip_field(self, world, player)
- self._sc2wol_has_protoss_common_units = lambda world, player: SC2WoLLogic._sc2wol_has_protoss_common_units(self, world, player)
- self._sc2wol_has_protoss_medium_units = lambda world, player: SC2WoLLogic._sc2wol_has_protoss_medium_units(self, world, player)
- self._sc2wol_has_mm_upgrade = lambda world, player: SC2WoLLogic._sc2wol_has_mm_upgrade(self, world, player)
- self._sc2wol_welcome_to_the_jungle_requirement = lambda world, player: SC2WoLLogic._sc2wol_welcome_to_the_jungle_requirement(self, world, player)
- self._sc2wol_can_respond_to_colony_infestations = lambda world, player: SC2WoLLogic._sc2wol_can_respond_to_colony_infestations(self, world, player)
- self._sc2wol_final_mission_requirements = lambda world, player: SC2WoLLogic._sc2wol_final_mission_requirements(self, world, player)
-
- def __init__(self, multiworld: MultiWorld, player: int,
- item_pool: List[Item], existing_items: List[Item], locked_items: List[Item],
- has_protoss: bool):
- self.multiworld = multiworld
- self.player = player
- self.logical_inventory = set()
- self.locked_items = locked_items[:]
- self.existing_items = existing_items
- self._read_logic()
- # Initial filter of item pool
- self.item_pool = []
- item_quantities: dict[str, int] = dict()
- # Inventory restrictiveness based on number of missions with checks
- mission_order_type = get_option_value(self.multiworld, self.player, "mission_order")
- mission_count = len(mission_orders[mission_order_type]) - 1
- self.min_units_per_structure = int(mission_count / 7)
- min_upgrades = 1 if mission_count < 10 else 2
- for item in item_pool:
- item_info = get_full_item_list()[item.name]
- if item_info.type == "Upgrade":
- # Locking upgrades based on mission duration
- if item.name not in item_quantities:
- item_quantities[item.name] = 0
- item_quantities[item.name] += 1
- if item_quantities[item.name] < min_upgrades:
- self.locked_items.append(item)
- else:
- self.item_pool.append(item)
- elif item_info.type == "Goal":
- locked_items.append(item)
- elif item_info.type != "Protoss" or has_protoss:
- self.item_pool.append(item)
- self.cascade_removal_map: Dict[Item, List[Item]] = dict()
- for item in self.item_pool + locked_items + existing_items:
- if item.name in UPGRADABLE_ITEMS:
- upgrades = get_item_upgrades(self.item_pool, item)
- associated_items = [*upgrades, item]
- self.cascade_removal_map[item] = associated_items
- if get_option_value(multiworld, player, "units_always_have_upgrades"):
- for upgrade in upgrades:
- self.cascade_removal_map[upgrade] = associated_items
-
-
-def filter_items(multiworld: MultiWorld, player: int, mission_req_table: Dict[str, MissionInfo], location_cache: List[Location],
- item_pool: List[Item], existing_items: List[Item], locked_items: List[Item]) -> List[Item]:
- """
- Returns a semi-randomly pruned set of items based on number of available locations.
- The returned inventory must be capable of logically accessing every location in the world.
- """
- open_locations = [location for location in location_cache if location.item is None]
- inventory_size = len(open_locations)
- has_protoss = bool(PROTOSS_REGIONS.intersection(mission_req_table.keys()))
- mission_requirements = [location.access_rule for location in location_cache]
- valid_inventory = ValidInventory(multiworld, player, item_pool, existing_items, locked_items, has_protoss)
-
- valid_items = valid_inventory.generate_reduced_inventory(inventory_size, mission_requirements)
- return valid_items
diff --git a/worlds/sc2wol/Regions.py b/worlds/sc2wol/Regions.py
deleted file mode 100644
index f588ce7e982e..000000000000
--- a/worlds/sc2wol/Regions.py
+++ /dev/null
@@ -1,313 +0,0 @@
-from typing import List, Set, Dict, Tuple, Optional, Callable
-from BaseClasses import MultiWorld, Region, Entrance, Location
-from .Locations import LocationData
-from .Options import get_option_value, MissionOrder
-from .MissionTables import MissionInfo, mission_orders, vanilla_mission_req_table, alt_final_mission_locations, \
- MissionPools, vanilla_shuffle_order
-from .PoolFilter import filter_missions
-
-PROPHECY_CHAIN_MISSION_COUNT = 4
-
-VANILLA_SHUFFLED_FIRST_PROPHECY_MISSION = 21
-
-def create_regions(multiworld: MultiWorld, player: int, locations: Tuple[LocationData, ...], location_cache: List[Location])\
- -> Tuple[Dict[str, MissionInfo], int, str]:
- locations_per_region = get_locations_per_region(locations)
-
- mission_order_type = get_option_value(multiworld, player, "mission_order")
- mission_order = mission_orders[mission_order_type]
-
- mission_pools = filter_missions(multiworld, player)
-
- regions = [create_region(multiworld, player, locations_per_region, location_cache, "Menu")]
-
- names: Dict[str, int] = {}
-
- if mission_order_type == MissionOrder.option_vanilla:
-
- # Generating all regions and locations
- for region_name in vanilla_mission_req_table.keys():
- regions.append(create_region(multiworld, player, locations_per_region, location_cache, region_name))
- multiworld.regions += regions
-
- connect(multiworld, player, names, 'Menu', 'Liberation Day'),
- connect(multiworld, player, names, 'Liberation Day', 'The Outlaws',
- lambda state: state.has("Beat Liberation Day", player)),
- connect(multiworld, player, names, 'The Outlaws', 'Zero Hour',
- lambda state: state.has("Beat The Outlaws", player)),
- connect(multiworld, player, names, 'Zero Hour', 'Evacuation',
- lambda state: state.has("Beat Zero Hour", player)),
- connect(multiworld, player, names, 'Evacuation', 'Outbreak',
- lambda state: state.has("Beat Evacuation", player)),
- connect(multiworld, player, names, "Outbreak", "Safe Haven",
- lambda state: state._sc2wol_cleared_missions(multiworld, player, 7) and
- state.has("Beat Outbreak", player)),
- connect(multiworld, player, names, "Outbreak", "Haven's Fall",
- lambda state: state._sc2wol_cleared_missions(multiworld, player, 7) and
- state.has("Beat Outbreak", player)),
- connect(multiworld, player, names, 'Zero Hour', 'Smash and Grab',
- lambda state: state.has("Beat Zero Hour", player)),
- connect(multiworld, player, names, 'Smash and Grab', 'The Dig',
- lambda state: state._sc2wol_cleared_missions(multiworld, player, 8) and
- state.has("Beat Smash and Grab", player)),
- connect(multiworld, player, names, 'The Dig', 'The Moebius Factor',
- lambda state: state._sc2wol_cleared_missions(multiworld, player, 11) and
- state.has("Beat The Dig", player)),
- connect(multiworld, player, names, 'The Moebius Factor', 'Supernova',
- lambda state: state._sc2wol_cleared_missions(multiworld, player, 14) and
- state.has("Beat The Moebius Factor", player)),
- connect(multiworld, player, names, 'Supernova', 'Maw of the Void',
- lambda state: state.has("Beat Supernova", player)),
- connect(multiworld, player, names, 'Zero Hour', "Devil's Playground",
- lambda state: state._sc2wol_cleared_missions(multiworld, player, 4) and
- state.has("Beat Zero Hour", player)),
- connect(multiworld, player, names, "Devil's Playground", 'Welcome to the Jungle',
- lambda state: state.has("Beat Devil's Playground", player)),
- connect(multiworld, player, names, "Welcome to the Jungle", 'Breakout',
- lambda state: state._sc2wol_cleared_missions(multiworld, player, 8) and
- state.has("Beat Welcome to the Jungle", player)),
- connect(multiworld, player, names, "Welcome to the Jungle", 'Ghost of a Chance',
- lambda state: state._sc2wol_cleared_missions(multiworld, player, 8) and
- state.has("Beat Welcome to the Jungle", player)),
- connect(multiworld, player, names, "Zero Hour", 'The Great Train Robbery',
- lambda state: state._sc2wol_cleared_missions(multiworld, player, 6) and
- state.has("Beat Zero Hour", player)),
- connect(multiworld, player, names, 'The Great Train Robbery', 'Cutthroat',
- lambda state: state.has("Beat The Great Train Robbery", player)),
- connect(multiworld, player, names, 'Cutthroat', 'Engine of Destruction',
- lambda state: state.has("Beat Cutthroat", player)),
- connect(multiworld, player, names, 'Engine of Destruction', 'Media Blitz',
- lambda state: state.has("Beat Engine of Destruction", player)),
- connect(multiworld, player, names, 'Media Blitz', 'Piercing the Shroud',
- lambda state: state.has("Beat Media Blitz", player)),
- connect(multiworld, player, names, 'The Dig', 'Whispers of Doom',
- lambda state: state.has("Beat The Dig", player)),
- connect(multiworld, player, names, 'Whispers of Doom', 'A Sinister Turn',
- lambda state: state.has("Beat Whispers of Doom", player)),
- connect(multiworld, player, names, 'A Sinister Turn', 'Echoes of the Future',
- lambda state: state.has("Beat A Sinister Turn", player)),
- connect(multiworld, player, names, 'Echoes of the Future', 'In Utter Darkness',
- lambda state: state.has("Beat Echoes of the Future", player)),
- connect(multiworld, player, names, 'Maw of the Void', 'Gates of Hell',
- lambda state: state.has("Beat Maw of the Void", player)),
- connect(multiworld, player, names, 'Gates of Hell', 'Belly of the Beast',
- lambda state: state.has("Beat Gates of Hell", player)),
- connect(multiworld, player, names, 'Gates of Hell', 'Shatter the Sky',
- lambda state: state.has("Beat Gates of Hell", player)),
- connect(multiworld, player, names, 'Gates of Hell', 'All-In',
- lambda state: state.has('Beat Gates of Hell', player) and (
- state.has('Beat Shatter the Sky', player) or state.has('Beat Belly of the Beast', player)))
-
- return vanilla_mission_req_table, 29, 'All-In: Victory'
-
- else:
- missions = []
-
- remove_prophecy = mission_order_type == 1 and not get_option_value(multiworld, player, "shuffle_protoss")
-
- final_mission = mission_pools[MissionPools.FINAL][0]
-
- # Determining if missions must be removed
- mission_pool_size = sum(len(mission_pool) for mission_pool in mission_pools.values())
- removals = len(mission_order) - mission_pool_size
- # Removing entire Prophecy chain on vanilla shuffled when not shuffling protoss
- if remove_prophecy:
- removals -= PROPHECY_CHAIN_MISSION_COUNT
-
- # Initial fill out of mission list and marking all-in mission
- for mission in mission_order:
- # Removing extra missions if mission pool is too small
- # Also handle lower removal priority than Prophecy
- if 0 < mission.removal_priority <= removals or mission.category == 'Prophecy' and remove_prophecy \
- or (remove_prophecy and mission_order_type == MissionOrder.option_vanilla_shuffled
- and mission.removal_priority > vanilla_shuffle_order[
- VANILLA_SHUFFLED_FIRST_PROPHECY_MISSION].removal_priority
- and 0 < mission.removal_priority <= removals + PROPHECY_CHAIN_MISSION_COUNT):
- missions.append(None)
- elif mission.type == MissionPools.FINAL:
- missions.append(final_mission)
- else:
- missions.append(mission.type)
-
- no_build_slots = []
- easy_slots = []
- medium_slots = []
- hard_slots = []
-
- # Search through missions to find slots needed to fill
- for i in range(len(missions)):
- if missions[i] is None:
- continue
- if missions[i] == MissionPools.STARTER:
- no_build_slots.append(i)
- elif missions[i] == MissionPools.EASY:
- easy_slots.append(i)
- elif missions[i] == MissionPools.MEDIUM:
- medium_slots.append(i)
- elif missions[i] == MissionPools.HARD:
- hard_slots.append(i)
-
- # Add no_build missions to the pool and fill in no_build slots
- missions_to_add = mission_pools[MissionPools.STARTER]
- if len(no_build_slots) > len(missions_to_add):
- raise Exception("There are no valid No-Build missions. Please exclude fewer missions.")
- for slot in no_build_slots:
- filler = multiworld.random.randint(0, len(missions_to_add) - 1)
-
- missions[slot] = missions_to_add.pop(filler)
-
- # Add easy missions into pool and fill in easy slots
- missions_to_add = missions_to_add + mission_pools[MissionPools.EASY]
- if len(easy_slots) > len(missions_to_add):
- raise Exception("There are not enough Easy missions to fill the campaign. Please exclude fewer missions.")
- for slot in easy_slots:
- filler = multiworld.random.randint(0, len(missions_to_add) - 1)
-
- missions[slot] = missions_to_add.pop(filler)
-
- # Add medium missions into pool and fill in medium slots
- missions_to_add = missions_to_add + mission_pools[MissionPools.MEDIUM]
- if len(medium_slots) > len(missions_to_add):
- raise Exception("There are not enough Easy and Medium missions to fill the campaign. Please exclude fewer missions.")
- for slot in medium_slots:
- filler = multiworld.random.randint(0, len(missions_to_add) - 1)
-
- missions[slot] = missions_to_add.pop(filler)
-
- # Add hard missions into pool and fill in hard slots
- missions_to_add = missions_to_add + mission_pools[MissionPools.HARD]
- if len(hard_slots) > len(missions_to_add):
- raise Exception("There are not enough missions to fill the campaign. Please exclude fewer missions.")
- for slot in hard_slots:
- filler = multiworld.random.randint(0, len(missions_to_add) - 1)
-
- missions[slot] = missions_to_add.pop(filler)
-
- # Generating regions and locations from selected missions
- for region_name in missions:
- regions.append(create_region(multiworld, player, locations_per_region, location_cache, region_name))
- multiworld.regions += regions
-
- # Mapping original mission slots to shifted mission slots when missions are removed
- slot_map = []
- slot_offset = 0
- for position, mission in enumerate(missions):
- slot_map.append(position - slot_offset + 1)
- if mission is None:
- slot_offset += 1
-
- # Loop through missions to create requirements table and connect regions
- # TODO: Handle 'and' connections
- mission_req_table = {}
-
- def build_connection_rule(mission_names: List[str], missions_req: int) -> Callable:
- if len(mission_names) > 1:
- return lambda state: state.has_all({f"Beat {name}" for name in mission_names}, player) and \
- state._sc2wol_cleared_missions(multiworld, player, missions_req)
- else:
- return lambda state: state.has(f"Beat {mission_names[0]}", player) and \
- state._sc2wol_cleared_missions(multiworld, player, missions_req)
-
- for i, mission in enumerate(missions):
- if mission is None:
- continue
- connections = []
- all_connections = []
- for connection in mission_order[i].connect_to:
- if connection == -1:
- continue
- while missions[connection] is None:
- connection -= 1
- all_connections.append(missions[connection])
- for connection in mission_order[i].connect_to:
- required_mission = missions[connection]
- if connection == -1:
- connect(multiworld, player, names, "Menu", mission)
- else:
- if required_mission is None and not mission_order[i].completion_critical: # Drop non-critical null slots
- continue
- while required_mission is None: # Substituting null slot with prior slot
- connection -= 1
- required_mission = missions[connection]
- required_missions = [required_mission] if mission_order[i].or_requirements else all_connections
- connect(multiworld, player, names, required_mission, mission,
- build_connection_rule(required_missions, mission_order[i].number))
- connections.append(slot_map[connection])
-
- mission_req_table.update({mission: MissionInfo(
- vanilla_mission_req_table[mission].id, connections, mission_order[i].category,
- number=mission_order[i].number,
- completion_critical=mission_order[i].completion_critical,
- or_requirements=mission_order[i].or_requirements)})
-
- final_mission_id = vanilla_mission_req_table[final_mission].id
-
- # Changing the completion condition for alternate final missions into an event
- if final_mission != 'All-In':
- final_location = alt_final_mission_locations[final_mission]
- # Final location should be near the end of the cache
- for i in range(len(location_cache) - 1, -1, -1):
- if location_cache[i].name == final_location:
- location_cache[i].locked = True
- location_cache[i].event = True
- location_cache[i].address = None
- break
- else:
- final_location = 'All-In: Victory'
-
- return mission_req_table, final_mission_id, final_location
-
-def create_location(player: int, location_data: LocationData, region: Region,
- location_cache: List[Location]) -> Location:
- location = Location(player, location_data.name, location_data.code, region)
- location.access_rule = location_data.rule
-
- if id is None:
- location.event = True
- location.locked = True
-
- location_cache.append(location)
-
- return location
-
-
-def create_region(multiworld: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]],
- location_cache: List[Location], name: str) -> Region:
- region = Region(name, player, multiworld)
-
- if name in locations_per_region:
- for location_data in locations_per_region[name]:
- location = create_location(player, location_data, region, location_cache)
- region.locations.append(location)
-
- return region
-
-
-def connect(world: MultiWorld, player: int, used_names: Dict[str, int], source: str, target: str,
- rule: Optional[Callable] = None):
- sourceRegion = world.get_region(source, player)
- targetRegion = world.get_region(target, player)
-
- if target not in used_names:
- used_names[target] = 1
- name = target
- else:
- used_names[target] += 1
- name = target + (' ' * used_names[target])
-
- connection = Entrance(player, name, sourceRegion)
-
- if rule:
- connection.access_rule = rule
-
- sourceRegion.exits.append(connection)
- connection.connect(targetRegion)
-
-
-def get_locations_per_region(locations: Tuple[LocationData, ...]) -> Dict[str, List[LocationData]]:
- per_region: Dict[str, List[LocationData]] = {}
-
- for location in locations:
- per_region.setdefault(location.region, []).append(location)
-
- return per_region
diff --git a/worlds/sc2wol/Starcraft2.kv b/worlds/sc2wol/Starcraft2.kv
deleted file mode 100644
index f0785b89e428..000000000000
--- a/worlds/sc2wol/Starcraft2.kv
+++ /dev/null
@@ -1,16 +0,0 @@
-:
- rows: 1
-
-:
- cols: 1
- padding: [10,5,10,5]
- spacing: [0,5]
-
-:
- text_size: self.size
- markup: True
- halign: 'center'
- valign: 'middle'
- padding: [5,0,5,0]
- markup: True
- outline_width: 1
diff --git a/worlds/sc2wol/__init__.py b/worlds/sc2wol/__init__.py
deleted file mode 100644
index 5c487f8fee09..000000000000
--- a/worlds/sc2wol/__init__.py
+++ /dev/null
@@ -1,324 +0,0 @@
-import typing
-
-from typing import List, Set, Tuple, Dict
-from BaseClasses import Item, MultiWorld, Location, Tutorial, ItemClassification
-from worlds.AutoWorld import WebWorld, World
-from .Items import StarcraftWoLItem, filler_items, item_name_groups, get_item_table, get_full_item_list, \
- get_basic_units, ItemData, upgrade_included_names, progressive_if_nco
-from .Locations import get_locations, LocationType
-from .Regions import create_regions
-from .Options import sc2wol_options, get_option_value, LocationInclusion
-from .LogicMixin import SC2WoLLogic
-from .PoolFilter import filter_missions, filter_items, get_item_upgrades
-from .MissionTables import starting_mission_locations, MissionInfo
-
-
-class Starcraft2WoLWebWorld(WebWorld):
- setup = Tutorial(
- "Multiworld Setup Guide",
- "A guide to setting up the Starcraft 2 randomizer connected to an Archipelago Multiworld",
- "English",
- "setup_en.md",
- "setup/en",
- ["TheCondor"]
- )
-
- tutorials = [setup]
-
-
-class SC2WoLWorld(World):
- """
- StarCraft II: Wings of Liberty is a science fiction real-time strategy video game developed and published by Blizzard Entertainment.
- Command Raynor's Raiders in collecting pieces of the Keystone in order to stop the zerg threat posed by the Queen of Blades.
- """
-
- game = "Starcraft 2 Wings of Liberty"
- web = Starcraft2WoLWebWorld()
- data_version = 5
-
- item_name_to_id = {name: data.code for name, data in get_full_item_list().items()}
- location_name_to_id = {location.name: location.code for location in get_locations(None, None)}
- option_definitions = sc2wol_options
-
- item_name_groups = item_name_groups
- locked_locations: typing.List[str]
- location_cache: typing.List[Location]
- mission_req_table = {}
- final_mission_id: int
- victory_item: str
- required_client_version = 0, 4, 3
-
- def __init__(self, multiworld: MultiWorld, player: int):
- super(SC2WoLWorld, self).__init__(multiworld, player)
- self.location_cache = []
- self.locked_locations = []
-
- def create_item(self, name: str) -> Item:
- data = get_full_item_list()[name]
- return StarcraftWoLItem(name, data.classification, data.code, self.player)
-
- def create_regions(self):
- self.mission_req_table, self.final_mission_id, self.victory_item = create_regions(
- self.multiworld, self.player, get_locations(self.multiworld, self.player), self.location_cache
- )
-
- def create_items(self):
- setup_events(self.player, self.locked_locations, self.location_cache)
-
- excluded_items = get_excluded_items(self.multiworld, self.player)
-
- starter_items = assign_starter_items(self.multiworld, self.player, excluded_items, self.locked_locations)
-
- filter_locations(self.multiworld, self.player, self.locked_locations, self.location_cache)
-
- pool = get_item_pool(self.multiworld, self.player, self.mission_req_table, starter_items, excluded_items, self.location_cache)
-
- fill_item_pool_with_dummy_items(self, self.multiworld, self.player, self.locked_locations, self.location_cache, pool)
-
- self.multiworld.itempool += pool
-
- def set_rules(self):
- self.multiworld.completion_condition[self.player] = lambda state: state.has(self.victory_item, self.player)
-
- def get_filler_item_name(self) -> str:
- return self.multiworld.random.choice(filler_items)
-
- def fill_slot_data(self):
- slot_data = {}
- for option_name in sc2wol_options:
- option = getattr(self.multiworld, option_name)[self.player]
- if type(option.value) in {str, int}:
- slot_data[option_name] = int(option.value)
- slot_req_table = {}
- for mission in self.mission_req_table:
- slot_req_table[mission] = self.mission_req_table[mission]._asdict()
-
- slot_data["mission_req"] = slot_req_table
- slot_data["final_mission"] = self.final_mission_id
- return slot_data
-
-
-def setup_events(player: int, locked_locations: typing.List[str], location_cache: typing.List[Location]):
- for location in location_cache:
- if location.address is None:
- item = Item(location.name, ItemClassification.progression, None, player)
-
- locked_locations.append(location.name)
-
- location.place_locked_item(item)
-
-
-def get_excluded_items(multiworld: MultiWorld, player: int) -> Set[str]:
- excluded_items: Set[str] = set()
-
- for item in multiworld.precollected_items[player]:
- excluded_items.add(item.name)
-
- excluded_items_option = getattr(multiworld, 'excluded_items', [])
-
- excluded_items.update(excluded_items_option[player].value)
-
- return excluded_items
-
-
-def assign_starter_items(multiworld: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str]) -> List[Item]:
- non_local_items = multiworld.non_local_items[player].value
- if get_option_value(multiworld, player, "early_unit"):
- local_basic_unit = sorted(item for item in get_basic_units(multiworld, player) if item not in non_local_items and item not in excluded_items)
- if not local_basic_unit:
- raise Exception("At least one basic unit must be local")
-
- # The first world should also be the starting world
- first_mission = list(multiworld.worlds[player].mission_req_table)[0]
- if first_mission in starting_mission_locations:
- first_location = starting_mission_locations[first_mission]
- elif first_mission == "In Utter Darkness":
- first_location = first_mission + ": Defeat"
- else:
- first_location = first_mission + ": Victory"
-
- return [assign_starter_item(multiworld, player, excluded_items, locked_locations, first_location, local_basic_unit)]
- else:
- return []
-
-
-def assign_starter_item(multiworld: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str],
- location: str, item_list: Tuple[str, ...]) -> Item:
-
- item_name = multiworld.random.choice(item_list)
-
- excluded_items.add(item_name)
-
- item = create_item_with_correct_settings(player, item_name)
-
- multiworld.get_location(location, player).place_locked_item(item)
-
- locked_locations.append(location)
-
- return item
-
-
-def get_item_pool(multiworld: MultiWorld, player: int, mission_req_table: Dict[str, MissionInfo],
- starter_items: List[Item], excluded_items: Set[str], location_cache: List[Location]) -> List[Item]:
- pool: List[Item] = []
-
- # For the future: goal items like Artifact Shards go here
- locked_items = []
-
- # YAML items
- yaml_locked_items = get_option_value(multiworld, player, 'locked_items')
-
- # Adjust generic upgrade availability based on options
- include_upgrades = get_option_value(multiworld, player, 'generic_upgrade_missions') == 0
- upgrade_items = get_option_value(multiworld, player, 'generic_upgrade_items')
-
- # Include items from outside Wings of Liberty
- item_sets = {'wol'}
- if get_option_value(multiworld, player, 'nco_items'):
- item_sets.add('nco')
- if get_option_value(multiworld, player, 'bw_items'):
- item_sets.add('bw')
- if get_option_value(multiworld, player, 'ext_items'):
- item_sets.add('ext')
-
- def allowed_quantity(name: str, data: ItemData) -> int:
- if name in excluded_items \
- or data.type == "Upgrade" and (not include_upgrades or name not in upgrade_included_names[upgrade_items]) \
- or not data.origin.intersection(item_sets):
- return 0
- elif name in progressive_if_nco and 'nco' not in item_sets:
- return 1
- else:
- return data.quantity
-
- for name, data in get_item_table(multiworld, player).items():
- for i in range(allowed_quantity(name, data)):
- item = create_item_with_correct_settings(player, name)
- if name in yaml_locked_items:
- locked_items.append(item)
- else:
- pool.append(item)
-
- existing_items = starter_items + [item for item in multiworld.precollected_items[player]]
- existing_names = [item.name for item in existing_items]
-
- # Check the parent item integrity, exclude items
- pool[:] = [item for item in pool if pool_contains_parent(item, pool + locked_items + existing_items)]
-
- # Removing upgrades for excluded items
- for item_name in excluded_items:
- if item_name in existing_names:
- continue
- invalid_upgrades = get_item_upgrades(pool, item_name)
- for invalid_upgrade in invalid_upgrades:
- pool.remove(invalid_upgrade)
-
- filtered_pool = filter_items(multiworld, player, mission_req_table, location_cache, pool, existing_items, locked_items)
- return filtered_pool
-
-
-def fill_item_pool_with_dummy_items(self: SC2WoLWorld, multiworld: MultiWorld, player: int, locked_locations: List[str],
- location_cache: List[Location], pool: List[Item]):
- for _ in range(len(location_cache) - len(locked_locations) - len(pool)):
- item = create_item_with_correct_settings(player, self.get_filler_item_name())
- pool.append(item)
-
-
-def create_item_with_correct_settings(player: int, name: str) -> Item:
- data = get_full_item_list()[name]
-
- item = Item(name, data.classification, data.code, player)
-
- return item
-
-
-def pool_contains_parent(item: Item, pool: [Item]):
- item_data = get_full_item_list().get(item.name)
- if item_data.parent_item is None:
- # The item has not associated parent, the item is valid
- return True
- parent_item = item_data.parent_item
- # Check if the pool contains the parent item
- return parent_item in [pool_item.name for pool_item in pool]
-
-
-def filter_locations(multiworld: MultiWorld, player, locked_locations: List[str], location_cache: List[Location]):
- """
- Filters the locations in the world using a trash or Nothing item
- :param multiworld:
- :param player:
- :param locked_locations:
- :param location_cache:
- :return:
- """
- open_locations = [location for location in location_cache if location.item is None]
- plando_locations = get_plando_locations(multiworld, player)
- mission_progress_locations = get_option_value(multiworld, player, "mission_progress_locations")
- bonus_locations = get_option_value(multiworld, player, "bonus_locations")
- challenge_locations = get_option_value(multiworld, player, "challenge_locations")
- optional_boss_locations = get_option_value(multiworld, player, "optional_boss_locations")
- location_data = get_locations(multiworld, player)
- for location in open_locations:
- # Go through the locations that aren't locked yet (early unit, etc)
- if location.name not in plando_locations:
- # The location is not plando'd
- sc2_location = [sc2_location for sc2_location in location_data if sc2_location.name == location.name][0]
- location_type = sc2_location.type
-
- if location_type == LocationType.MISSION_PROGRESS \
- and mission_progress_locations != LocationInclusion.option_enabled:
- item_name = get_exclusion_item(multiworld, mission_progress_locations)
- place_exclusion_item(item_name, location, locked_locations, player)
-
- if location_type == LocationType.BONUS \
- and bonus_locations != LocationInclusion.option_enabled:
- item_name = get_exclusion_item(multiworld, bonus_locations)
- place_exclusion_item(item_name, location, locked_locations, player)
-
- if location_type == LocationType.CHALLENGE \
- and challenge_locations != LocationInclusion.option_enabled:
- item_name = get_exclusion_item(multiworld, challenge_locations)
- place_exclusion_item(item_name, location, locked_locations, player)
-
- if location_type == LocationType.OPTIONAL_BOSS \
- and optional_boss_locations != LocationInclusion.option_enabled:
- item_name = get_exclusion_item(multiworld, optional_boss_locations)
- place_exclusion_item(item_name, location, locked_locations, player)
-
-
-def place_exclusion_item(item_name, location, locked_locations, player):
- item = create_item_with_correct_settings(player, item_name)
- location.place_locked_item(item)
- locked_locations.append(location.name)
-
-
-def get_exclusion_item(multiworld: MultiWorld, option) -> str:
- """
- Gets the exclusion item according to settings (trash/nothing)
- :param multiworld:
- :param option:
- :return: Item used for location exclusion
- """
- if option == LocationInclusion.option_nothing:
- return "Nothing"
- elif option == LocationInclusion.option_trash:
- index = multiworld.random.randint(0, len(filler_items) - 1)
- return filler_items[index]
- raise Exception(f"Unsupported option type: {option}")
-
-
-def get_plando_locations(multiworld: MultiWorld, player) -> List[str]:
- """
-
- :param multiworld:
- :param player:
- :return: A list of locations affected by a plando in a world
- """
- plando_locations = []
- for plando_setting in multiworld.plando_items[player]:
- plando_locations += plando_setting.get("locations", [])
- plando_setting_location = plando_setting.get("location", None)
- if plando_setting_location is not None:
- plando_locations.append(plando_setting_location)
-
- return plando_locations
diff --git a/worlds/sc2wol/docs/en_Starcraft 2 Wings of Liberty.md b/worlds/sc2wol/docs/en_Starcraft 2 Wings of Liberty.md
deleted file mode 100644
index 18bda6478457..000000000000
--- a/worlds/sc2wol/docs/en_Starcraft 2 Wings of Liberty.md
+++ /dev/null
@@ -1,54 +0,0 @@
-# Starcraft 2 Wings of Liberty
-
-## What does randomization do to this game?
-
-The following unlocks are randomized as items:
-1. Your ability to build any non-worker unit (including Marines!).
-2. Your ability to upgrade infantry weapons, infantry armor, vehicle weapons, etc.
-3. All armory upgrades
-4. All laboratory upgrades
-5. All mercenaries
-6. Small boosts to your starting mineral and vespene gas totals on each mission
-
-You find items by making progress in bonus objectives (like by rescuing allies in 'Zero Hour') and by completing
-missions. When you receive items, they will immediately become available, even during a mission, and you will be
-notified via a text box in the top-right corner of the game screen. (The text client for StarCraft 2 also records all
-items in all worlds.)
-
-Missions are launched only through the text client. The Hyperion is never visited. Additionally, credits are not used.
-
-## What is the goal of this game when randomized?
-
-The goal is to beat the final mission: 'All In'. The config file determines which variant you must complete.
-
-## What non-randomized changes are there from vanilla Starcraft 2?
-
-1. Some missions have more vespene geysers available to allow a wider variety of units.
-2. Starports no longer require Factories in order to be built.
-3. In 'A Sinister Turn' and 'Echoes of the Future', you can research Protoss air weapon/armor upgrades.
-
-## Which of my items can be in another player's world?
-
-By default, any of StarCraft 2's items (specified above) can be in another player's world. See the
-[Advanced YAML Guide](https://archipelago.gg/tutorial/Archipelago/advanced_settings/en)
-for more information on how to change this.
-
-## Unique Local Commands
-
-The following commands are only available when using the Starcraft 2 Client to play with Archipelago.
-
-- `/difficulty [difficulty]` Overrides the difficulty set for the world.
- - Options: casual, normal, hard, brutal
-- `/game_speed [game_speed]` Overrides the game speed for the world
- - Options: default, slower, slow, normal, fast, faster
-- `/color [color]` Changes your color (Currently has no effect)
- - Options: white, red, blue, teal, purple, yellow, orange, green, lightpink, violet, lightgrey, darkgreen, brown,
- lightgreen, darkgrey, pink, rainbow, random, default
-- `/disable_mission_check` Disables the check to see if a mission is available to play. Meant for co-op runs where one
- player can play the next mission in a chain the other player is doing.
-- `/play [mission_id]` Starts a Starcraft 2 mission based off of the mission_id provided
-- `/available` Get what missions are currently available to play
-- `/unfinished` Get what missions are currently available to play and have not had all locations checked
-- `/set_path [path]` Menually set the SC2 install directory (if the automatic detection fails)
-- `/download_data` Download the most recent release of the necassry files for playing SC2 with Archipelago. Will
- overwrite existing files
diff --git a/worlds/shivers/Items.py b/worlds/shivers/Items.py
index caf24ded2987..3b403be5cb76 100644
--- a/worlds/shivers/Items.py
+++ b/worlds/shivers/Items.py
@@ -47,7 +47,7 @@ class ItemData(typing.NamedTuple):
"Key for Generator Room": ItemData(SHIVERS_ITEM_ID_OFFSET + 29, "key"),
"Key for Egypt Room": ItemData(SHIVERS_ITEM_ID_OFFSET + 30, "key"),
"Key for Library Room": ItemData(SHIVERS_ITEM_ID_OFFSET + 31, "key"),
- "Key for Tiki Room": ItemData(SHIVERS_ITEM_ID_OFFSET + 32, "key"),
+ "Key for Shaman Room": ItemData(SHIVERS_ITEM_ID_OFFSET + 32, "key"),
"Key for UFO Room": ItemData(SHIVERS_ITEM_ID_OFFSET + 33, "key"),
"Key for Torture Room": ItemData(SHIVERS_ITEM_ID_OFFSET + 34, "key"),
"Key for Puzzle Room": ItemData(SHIVERS_ITEM_ID_OFFSET + 35, "key"),
@@ -90,7 +90,7 @@ class ItemData(typing.NamedTuple):
"Water Always Available in Lobby": ItemData(SHIVERS_ITEM_ID_OFFSET + 92, "filler2", ItemClassification.filler),
"Wax Always Available in Library": ItemData(SHIVERS_ITEM_ID_OFFSET + 93, "filler2", ItemClassification.filler),
"Wax Always Available in Anansi Room": ItemData(SHIVERS_ITEM_ID_OFFSET + 94, "filler2", ItemClassification.filler),
- "Wax Always Available in Tiki Room": ItemData(SHIVERS_ITEM_ID_OFFSET + 95, "filler2", ItemClassification.filler),
+ "Wax Always Available in Shaman Room": ItemData(SHIVERS_ITEM_ID_OFFSET + 95, "filler2", ItemClassification.filler),
"Ash Always Available in Office": ItemData(SHIVERS_ITEM_ID_OFFSET + 96, "filler2", ItemClassification.filler),
"Ash Always Available in Burial Room": ItemData(SHIVERS_ITEM_ID_OFFSET + 97, "filler2", ItemClassification.filler),
"Oil Always Available in Prehistoric Room": ItemData(SHIVERS_ITEM_ID_OFFSET + 98, "filler2", ItemClassification.filler),
diff --git a/worlds/shivers/Options.py b/worlds/shivers/Options.py
index 6d1880406910..b70882f9a545 100644
--- a/worlds/shivers/Options.py
+++ b/worlds/shivers/Options.py
@@ -13,13 +13,16 @@ class LobbyAccess(Choice):
option_local = 2
class PuzzleHintsRequired(DefaultOnToggle):
- """If turned on puzzle hints will be available before the corresponding puzzle is required. For example: The Tiki
+ """If turned on puzzle hints will be available before the corresponding puzzle is required. For example: The Shaman
Drums puzzle will be placed after access to the security cameras which give you the solution. Turning this off
allows for greater randomization."""
display_name = "Puzzle Hints Required"
class InformationPlaques(Toggle):
- """Adds Information Plaques as checks."""
+ """
+ Adds Information Plaques as checks.
+ (40 Locations)
+ """
display_name = "Include Information Plaques"
class FrontDoorUsable(Toggle):
@@ -27,7 +30,10 @@ class FrontDoorUsable(Toggle):
display_name = "Front Door Usable"
class ElevatorsStaySolved(DefaultOnToggle):
- """Adds elevators as checks and will remain open upon solving them."""
+ """
+ Adds elevators as checks and will remain open upon solving them.
+ (3 Locations)
+ """
display_name = "Elevators Stay Solved"
class EarlyBeth(DefaultOnToggle):
@@ -35,7 +41,10 @@ class EarlyBeth(DefaultOnToggle):
display_name = "Early Beth"
class EarlyLightning(Toggle):
- """Allows lightning to be captured at any point in the game. You will still need to capture all ten Ixupi for victory."""
+ """
+ Allows lightning to be captured at any point in the game. You will still need to capture all ten Ixupi for victory.
+ (1 Location)
+ """
display_name = "Early Lightning"
diff --git a/worlds/shivers/Rules.py b/worlds/shivers/Rules.py
index 4e1058fecfc8..b1abb718c275 100644
--- a/worlds/shivers/Rules.py
+++ b/worlds/shivers/Rules.py
@@ -71,6 +71,12 @@ def first_nine_ixupi_capturable(state: CollectionState, player: int) -> bool:
and metal_capturable(state, player)
+def all_skull_dials_available(state: CollectionState, player: int) -> bool:
+ return state.can_reach("Prehistoric", "Region", player) and state.can_reach("Tar River", "Region", player) \
+ and state.can_reach("Egypt", "Region", player) and state.can_reach("Burial", "Region", player) \
+ and state.can_reach("Gods Room", "Region", player) and state.can_reach("Werewolf", "Region", player)
+
+
def get_rules_lookup(player: int):
rules_lookup: Dict[str, List[Callable[[CollectionState], bool]]] = {
"entrances": {
@@ -96,8 +102,8 @@ def get_rules_lookup(player: int):
"To Lobby From Egypt": lambda state: state.has("Key for Egypt Room", player),
"To Egypt From Lobby": lambda state: state.has("Key for Egypt Room", player),
"To Janitor Closet": lambda state: state.has("Key for Janitor Closet", player),
- "To Tiki From Burial": lambda state: state.has("Key for Tiki Room", player),
- "To Burial From Tiki": lambda state: state.has("Key for Tiki Room", player),
+ "To Shaman From Burial": lambda state: state.has("Key for Shaman Room", player),
+ "To Burial From Shaman": lambda state: state.has("Key for Shaman Room", player),
"To Inventions From UFO": lambda state: state.has("Key for UFO Room", player),
"To UFO From Inventions": lambda state: state.has("Key for UFO Room", player),
"To Torture From Inventions": lambda state: state.has("Key for Torture Room", player),
@@ -116,10 +122,7 @@ def get_rules_lookup(player: int):
"To Tar River From Lobby": lambda state: (state.has("Crawling", player) and oil_capturable(state, player) and state.can_reach("Tar River", "Region", player)),
"To Burial From Egypt": lambda state: state.can_reach("Egypt", "Region", player),
"To Gods Room From Anansi": lambda state: state.can_reach("Gods Room", "Region", player),
- "To Slide Room": lambda state: (
- state.can_reach("Prehistoric", "Region", player) and state.can_reach("Tar River", "Region",player) and
- state.can_reach("Egypt", "Region", player) and state.can_reach("Burial", "Region", player) and
- state.can_reach("Gods Room", "Region", player) and state.can_reach("Werewolf", "Region", player)),
+ "To Slide Room": lambda state: all_skull_dials_available(state, player),
"To Lobby From Slide Room": lambda state: (beths_body_available(state, player))
},
"locations_required": {
@@ -141,24 +144,25 @@ def get_rules_lookup(player: int):
"Final Riddle: Norse God Stone Message": lambda state: (state.can_reach("Fortune Teller", "Region", player) and state.can_reach("UFO", "Region", player)),
"Final Riddle: Beth's Body Page 17": lambda state: beths_body_available(state, player),
"Final Riddle: Guillotine Dropped": lambda state: beths_body_available(state, player),
+ "Puzzle Solved Skull Dial Door": lambda state: all_skull_dials_available(state, player),
},
"locations_puzzle_hints": {
"Puzzle Solved Clock Tower Door": lambda state: state.can_reach("Three Floor Elevator", "Region", player),
"Puzzle Solved Clock Chains": lambda state: state.can_reach("Bedroom", "Region", player),
- "Puzzle Solved Tiki Drums": lambda state: state.can_reach("Clock Tower", "Region", player),
+ "Puzzle Solved Shaman Drums": lambda state: state.can_reach("Clock Tower", "Region", player),
"Puzzle Solved Red Door": lambda state: state.can_reach("Maintenance Tunnels", "Region", player),
"Puzzle Solved UFO Symbols": lambda state: state.can_reach("Library", "Region", player),
"Puzzle Solved Maze Door": lambda state: state.can_reach("Projector Room", "Region", player),
"Puzzle Solved Theater Door": lambda state: state.can_reach("Underground Lake", "Region", player),
"Puzzle Solved Columns of RA": lambda state: state.can_reach("Underground Lake", "Region", player),
- "Final Riddle: Guillotine Dropped": lambda state: state.can_reach("Underground Lake", "Region", player)
+ "Final Riddle: Guillotine Dropped": lambda state: (beths_body_available(state, player) and state.can_reach("Underground Lake", "Region", player))
},
"elevators": {
- "Puzzle Solved Underground Elevator": lambda state: ((state.can_reach("Underground Lake", "Region", player) or state.can_reach("Office", "Region", player)
- and state.has("Key for Office Elevator", player))),
+ "Puzzle Solved Office Elevator": lambda state: ((state.can_reach("Underground Lake", "Region", player) or state.can_reach("Office", "Region", player))
+ and state.has("Key for Office Elevator", player)),
"Puzzle Solved Bedroom Elevator": lambda state: (state.can_reach("Office", "Region", player) and state.has_all({"Key for Bedroom Elevator","Crawling"}, player)),
- "Puzzle Solved Three Floor Elevator": lambda state: (((state.can_reach("Maintenance Tunnels", "Region", player) or state.can_reach("Blue Maze", "Region", player))
- and state.has("Key for Three Floor Elevator", player)))
+ "Puzzle Solved Three Floor Elevator": lambda state: ((state.can_reach("Maintenance Tunnels", "Region", player) or state.can_reach("Blue Maze", "Region", player))
+ and state.has("Key for Three Floor Elevator", player))
},
"lightning": {
"Ixupi Captured Lightning": lambda state: lightning_capturable(state, player)
@@ -202,7 +206,7 @@ def set_rules(world: "ShiversWorld") -> None:
forbid_item(multiworld.get_location("Ixupi Captured Water", player), "Water Always Available in Lobby", player)
forbid_item(multiworld.get_location("Ixupi Captured Wax", player), "Wax Always Available in Library", player)
forbid_item(multiworld.get_location("Ixupi Captured Wax", player), "Wax Always Available in Anansi Room", player)
- forbid_item(multiworld.get_location("Ixupi Captured Wax", player), "Wax Always Available in Tiki Room", player)
+ forbid_item(multiworld.get_location("Ixupi Captured Wax", player), "Wax Always Available in Shaman Room", player)
forbid_item(multiworld.get_location("Ixupi Captured Ash", player), "Ash Always Available in Office", player)
forbid_item(multiworld.get_location("Ixupi Captured Ash", player), "Ash Always Available in Burial Room", player)
forbid_item(multiworld.get_location("Ixupi Captured Oil", player), "Oil Always Available in Prehistoric Room", player)
diff --git a/worlds/shivers/data/excluded_locations.json b/worlds/shivers/data/excluded_locations.json
index 6ed625077af8..29655d4a5024 100644
--- a/worlds/shivers/data/excluded_locations.json
+++ b/worlds/shivers/data/excluded_locations.json
@@ -1,48 +1,48 @@
{
"plaques": [
- "Information Plaque: Transforming Masks (Lobby)",
- "Information Plaque: Jade Skull (Lobby)",
- "Information Plaque: Bronze Unicorn (Prehistoric)",
- "Information Plaque: Griffin (Prehistoric)",
- "Information Plaque: Eagles Nest (Prehistoric)",
- "Information Plaque: Large Spider (Prehistoric)",
- "Information Plaque: Starfish (Prehistoric)",
- "Information Plaque: Quartz Crystal (Ocean)",
- "Information Plaque: Poseidon (Ocean)",
- "Information Plaque: Colossus of Rhodes (Ocean)",
- "Information Plaque: Poseidon's Temple (Ocean)",
- "Information Plaque: Subterranean World (Underground Maze)",
- "Information Plaque: Dero (Underground Maze)",
- "Information Plaque: Tomb of the Ixupi (Egypt)",
- "Information Plaque: The Sphinx (Egypt)",
- "Information Plaque: Curse of Anubis (Egypt)",
- "Information Plaque: Norse Burial Ship (Burial)",
- "Information Plaque: Paracas Burial Bundles (Burial)",
- "Information Plaque: Spectacular Coffins of Ghana (Burial)",
- "Information Plaque: Cremation (Burial)",
- "Information Plaque: Animal Crematorium (Burial)",
- "Information Plaque: Witch Doctors of the Congo (Tiki)",
- "Information Plaque: Sarombe doctor of Mozambique (Tiki)",
- "Information Plaque: Fisherman's Canoe God (Gods)",
- "Information Plaque: Mayan Gods (Gods)",
- "Information Plaque: Thor (Gods)",
- "Information Plaque: Celtic Janus Sculpture (Gods)",
- "Information Plaque: Sumerian Bull God - An (Gods)",
- "Information Plaque: Sumerian Lyre (Gods)",
- "Information Plaque: Chuen (Gods)",
- "Information Plaque: African Creation Myth (Anansi)",
- "Information Plaque: Apophis the Serpent (Anansi)",
- "Information Plaque: Death (Anansi)",
- "Information Plaque: Cyclops (Pegasus)",
- "Information Plaque: Lycanthropy (Werewolf)",
- "Information Plaque: Coincidence or Extraterrestrial Visits? (UFO)",
- "Information Plaque: Planets (UFO)",
- "Information Plaque: Astronomical Construction (UFO)",
- "Information Plaque: Guillotine (Torture)",
- "Information Plaque: Aliens (UFO)"
+ "Information Plaque: (Lobby) Transforming Masks",
+ "Information Plaque: (Lobby) Jade Skull",
+ "Information Plaque: (Prehistoric) Bronze Unicorn",
+ "Information Plaque: (Prehistoric) Griffin",
+ "Information Plaque: (Prehistoric) Eagles Nest",
+ "Information Plaque: (Prehistoric) Large Spider",
+ "Information Plaque: (Prehistoric) Starfish",
+ "Information Plaque: (Ocean) Quartz Crystal",
+ "Information Plaque: (Ocean) Poseidon",
+ "Information Plaque: (Ocean) Colossus of Rhodes",
+ "Information Plaque: (Ocean) Poseidon's Temple",
+ "Information Plaque: (Underground Maze) Subterranean World",
+ "Information Plaque: (Underground Maze) Dero",
+ "Information Plaque: (Egypt) Tomb of the Ixupi",
+ "Information Plaque: (Egypt) The Sphinx",
+ "Information Plaque: (Egypt) Curse of Anubis",
+ "Information Plaque: (Burial) Norse Burial Ship",
+ "Information Plaque: (Burial) Paracas Burial Bundles",
+ "Information Plaque: (Burial) Spectacular Coffins of Ghana",
+ "Information Plaque: (Burial) Cremation",
+ "Information Plaque: (Burial) Animal Crematorium",
+ "Information Plaque: (Shaman) Witch Doctors of the Congo",
+ "Information Plaque: (Shaman) Sarombe doctor of Mozambique",
+ "Information Plaque: (Gods) Fisherman's Canoe God",
+ "Information Plaque: (Gods) Mayan Gods",
+ "Information Plaque: (Gods) Thor",
+ "Information Plaque: (Gods) Celtic Janus Sculpture",
+ "Information Plaque: (Gods) Sumerian Bull God - An",
+ "Information Plaque: (Gods) Sumerian Lyre",
+ "Information Plaque: (Gods) Chuen",
+ "Information Plaque: (Anansi) African Creation Myth",
+ "Information Plaque: (Anansi) Apophis the Serpent",
+ "Information Plaque: (Anansi) Death",
+ "Information Plaque: (Pegasus) Cyclops",
+ "Information Plaque: (Werewolf) Lycanthropy",
+ "Information Plaque: (UFO) Coincidence or Extraterrestrial Visits?",
+ "Information Plaque: (UFO) Planets",
+ "Information Plaque: (UFO) Astronomical Construction",
+ "Information Plaque: (Torture) Guillotine",
+ "Information Plaque: (UFO) Aliens"
],
"elevators": [
- "Puzzle Solved Underground Elevator",
+ "Puzzle Solved Office Elevator",
"Puzzle Solved Bedroom Elevator",
"Puzzle Solved Three Floor Elevator"
],
diff --git a/worlds/shivers/data/locations.json b/worlds/shivers/data/locations.json
index 7d031b886bff..1d62f85d2d1c 100644
--- a/worlds/shivers/data/locations.json
+++ b/worlds/shivers/data/locations.json
@@ -13,7 +13,7 @@
"Puzzle Solved Columns of RA",
"Puzzle Solved Burial Door",
"Puzzle Solved Chinese Solitaire",
- "Puzzle Solved Tiki Drums",
+ "Puzzle Solved Shaman Drums",
"Puzzle Solved Lyre",
"Puzzle Solved Red Door",
"Puzzle Solved Fortune Teller Door",
@@ -38,7 +38,7 @@
"Flashback Memory Obtained Theater Movie",
"Flashback Memory Obtained Museum Blueprints",
"Flashback Memory Obtained Beth's Address Book",
- "Flashback Memory Obtained Merick's Notebook",
+ "Flashback Memory Obtained Merrick's Notebook",
"Flashback Memory Obtained Professor Windlenot's Diary",
"Ixupi Captured Water",
"Ixupi Captured Wax",
@@ -68,49 +68,49 @@
"Puzzle Hint Found: Gallows Information Plaque",
"Puzzle Hint Found: Mastermind Information Plaque",
"Puzzle Hint Found: Elevator Writing",
- "Puzzle Hint Found: Tiki Security Camera",
+ "Puzzle Hint Found: Shaman Security Camera",
"Puzzle Hint Found: Tape Recorder Heard",
- "Information Plaque: Transforming Masks (Lobby)",
- "Information Plaque: Jade Skull (Lobby)",
- "Information Plaque: Bronze Unicorn (Prehistoric)",
- "Information Plaque: Griffin (Prehistoric)",
- "Information Plaque: Eagles Nest (Prehistoric)",
- "Information Plaque: Large Spider (Prehistoric)",
- "Information Plaque: Starfish (Prehistoric)",
- "Information Plaque: Quartz Crystal (Ocean)",
- "Information Plaque: Poseidon (Ocean)",
- "Information Plaque: Colossus of Rhodes (Ocean)",
- "Information Plaque: Poseidon's Temple (Ocean)",
- "Information Plaque: Subterranean World (Underground Maze)",
- "Information Plaque: Dero (Underground Maze)",
- "Information Plaque: Tomb of the Ixupi (Egypt)",
- "Information Plaque: The Sphinx (Egypt)",
- "Information Plaque: Curse of Anubis (Egypt)",
- "Information Plaque: Norse Burial Ship (Burial)",
- "Information Plaque: Paracas Burial Bundles (Burial)",
- "Information Plaque: Spectacular Coffins of Ghana (Burial)",
- "Information Plaque: Cremation (Burial)",
- "Information Plaque: Animal Crematorium (Burial)",
- "Information Plaque: Witch Doctors of the Congo (Tiki)",
- "Information Plaque: Sarombe doctor of Mozambique (Tiki)",
- "Information Plaque: Fisherman's Canoe God (Gods)",
- "Information Plaque: Mayan Gods (Gods)",
- "Information Plaque: Thor (Gods)",
- "Information Plaque: Celtic Janus Sculpture (Gods)",
- "Information Plaque: Sumerian Bull God - An (Gods)",
- "Information Plaque: Sumerian Lyre (Gods)",
- "Information Plaque: Chuen (Gods)",
- "Information Plaque: African Creation Myth (Anansi)",
- "Information Plaque: Apophis the Serpent (Anansi)",
- "Information Plaque: Death (Anansi)",
- "Information Plaque: Cyclops (Pegasus)",
- "Information Plaque: Lycanthropy (Werewolf)",
- "Information Plaque: Coincidence or Extraterrestrial Visits? (UFO)",
- "Information Plaque: Planets (UFO)",
- "Information Plaque: Astronomical Construction (UFO)",
- "Information Plaque: Guillotine (Torture)",
- "Information Plaque: Aliens (UFO)",
- "Puzzle Solved Underground Elevator",
+ "Information Plaque: (Lobby) Transforming Masks",
+ "Information Plaque: (Lobby) Jade Skull",
+ "Information Plaque: (Prehistoric) Bronze Unicorn",
+ "Information Plaque: (Prehistoric) Griffin",
+ "Information Plaque: (Prehistoric) Eagles Nest",
+ "Information Plaque: (Prehistoric) Large Spider",
+ "Information Plaque: (Prehistoric) Starfish",
+ "Information Plaque: (Ocean) Quartz Crystal",
+ "Information Plaque: (Ocean) Poseidon",
+ "Information Plaque: (Ocean) Colossus of Rhodes",
+ "Information Plaque: (Ocean) Poseidon's Temple",
+ "Information Plaque: (Underground Maze) Subterranean World",
+ "Information Plaque: (Underground Maze) Dero",
+ "Information Plaque: (Egypt) Tomb of the Ixupi",
+ "Information Plaque: (Egypt) The Sphinx",
+ "Information Plaque: (Egypt) Curse of Anubis",
+ "Information Plaque: (Burial) Norse Burial Ship",
+ "Information Plaque: (Burial) Paracas Burial Bundles",
+ "Information Plaque: (Burial) Spectacular Coffins of Ghana",
+ "Information Plaque: (Burial) Cremation",
+ "Information Plaque: (Burial) Animal Crematorium",
+ "Information Plaque: (Shaman) Witch Doctors of the Congo",
+ "Information Plaque: (Shaman) Sarombe doctor of Mozambique",
+ "Information Plaque: (Gods) Fisherman's Canoe God",
+ "Information Plaque: (Gods) Mayan Gods",
+ "Information Plaque: (Gods) Thor",
+ "Information Plaque: (Gods) Celtic Janus Sculpture",
+ "Information Plaque: (Gods) Sumerian Bull God - An",
+ "Information Plaque: (Gods) Sumerian Lyre",
+ "Information Plaque: (Gods) Chuen",
+ "Information Plaque: (Anansi) African Creation Myth",
+ "Information Plaque: (Anansi) Apophis the Serpent",
+ "Information Plaque: (Anansi) Death",
+ "Information Plaque: (Pegasus) Cyclops",
+ "Information Plaque: (Werewolf) Lycanthropy",
+ "Information Plaque: (UFO) Coincidence or Extraterrestrial Visits?",
+ "Information Plaque: (UFO) Planets",
+ "Information Plaque: (UFO) Astronomical Construction",
+ "Information Plaque: (Torture) Guillotine",
+ "Information Plaque: (UFO) Aliens",
+ "Puzzle Solved Office Elevator",
"Puzzle Solved Bedroom Elevator",
"Puzzle Solved Three Floor Elevator",
"Ixupi Captured Lightning"
@@ -129,7 +129,7 @@
"Ixupi Captured Sand",
"Ixupi Captured Metal",
"Ixupi Captured Lightning",
- "Puzzle Solved Underground Elevator",
+ "Puzzle Solved Office Elevator",
"Puzzle Solved Three Floor Elevator",
"Puzzle Hint Found: Combo Lock in Mailbox",
"Puzzle Hint Found: Orange Symbol",
@@ -176,10 +176,10 @@
"Lobby": [
"Puzzle Solved Theater Door",
"Flashback Memory Obtained Museum Brochure",
- "Information Plaque: Jade Skull (Lobby)",
- "Information Plaque: Transforming Masks (Lobby)",
+ "Information Plaque: (Lobby) Jade Skull",
+ "Information Plaque: (Lobby) Transforming Masks",
"Accessible: Storage: Slide",
- "Accessible: Storage: Eagles Head"
+ "Accessible: Storage: Transforming Mask"
],
"Generator": [
"Final Riddle: Beth's Body Page 17"
@@ -193,7 +193,7 @@
"Clock Tower": [
"Flashback Memory Obtained Beth's Ghost",
"Accessible: Storage: Clock Tower",
- "Puzzle Hint Found: Tiki Security Camera"
+ "Puzzle Hint Found: Shaman Security Camera"
],
"Projector Room": [
"Flashback Memory Obtained Theater Movie"
@@ -204,10 +204,10 @@
"Flashback Memory Obtained Museum Blueprints",
"Accessible: Storage: Ocean",
"Puzzle Hint Found: Sirens Song Heard",
- "Information Plaque: Quartz Crystal (Ocean)",
- "Information Plaque: Poseidon (Ocean)",
- "Information Plaque: Colossus of Rhodes (Ocean)",
- "Information Plaque: Poseidon's Temple (Ocean)"
+ "Information Plaque: (Ocean) Quartz Crystal",
+ "Information Plaque: (Ocean) Poseidon",
+ "Information Plaque: (Ocean) Colossus of Rhodes",
+ "Information Plaque: (Ocean) Poseidon's Temple"
],
"Maze Staircase": [
"Puzzle Solved Maze Door"
@@ -217,38 +217,38 @@
"Puzzle Solved Burial Door",
"Accessible: Storage: Egypt",
"Puzzle Hint Found: Egyptian Sphinx Heard",
- "Information Plaque: Tomb of the Ixupi (Egypt)",
- "Information Plaque: The Sphinx (Egypt)",
- "Information Plaque: Curse of Anubis (Egypt)"
+ "Information Plaque: (Egypt) Tomb of the Ixupi",
+ "Information Plaque: (Egypt) The Sphinx",
+ "Information Plaque: (Egypt) Curse of Anubis"
],
"Burial": [
"Puzzle Solved Chinese Solitaire",
- "Flashback Memory Obtained Merick's Notebook",
+ "Flashback Memory Obtained Merrick's Notebook",
"Accessible: Storage: Chinese Solitaire",
- "Information Plaque: Norse Burial Ship (Burial)",
- "Information Plaque: Paracas Burial Bundles (Burial)",
- "Information Plaque: Spectacular Coffins of Ghana (Burial)",
- "Information Plaque: Animal Crematorium (Burial)",
- "Information Plaque: Cremation (Burial)"
- ],
- "Tiki": [
- "Puzzle Solved Tiki Drums",
- "Accessible: Storage: Tiki Hut",
- "Information Plaque: Witch Doctors of the Congo (Tiki)",
- "Information Plaque: Sarombe doctor of Mozambique (Tiki)"
+ "Information Plaque: (Burial) Norse Burial Ship",
+ "Information Plaque: (Burial) Paracas Burial Bundles",
+ "Information Plaque: (Burial) Spectacular Coffins of Ghana",
+ "Information Plaque: (Burial) Animal Crematorium",
+ "Information Plaque: (Burial) Cremation"
+ ],
+ "Shaman": [
+ "Puzzle Solved Shaman Drums",
+ "Accessible: Storage: Shaman Hut",
+ "Information Plaque: (Shaman) Witch Doctors of the Congo",
+ "Information Plaque: (Shaman) Sarombe doctor of Mozambique"
],
"Gods Room": [
"Puzzle Solved Lyre",
"Puzzle Solved Red Door",
"Accessible: Storage: Lyre",
"Final Riddle: Norse God Stone Message",
- "Information Plaque: Fisherman's Canoe God (Gods)",
- "Information Plaque: Mayan Gods (Gods)",
- "Information Plaque: Thor (Gods)",
- "Information Plaque: Celtic Janus Sculpture (Gods)",
- "Information Plaque: Sumerian Bull God - An (Gods)",
- "Information Plaque: Sumerian Lyre (Gods)",
- "Information Plaque: Chuen (Gods)"
+ "Information Plaque: (Gods) Fisherman's Canoe God",
+ "Information Plaque: (Gods) Mayan Gods",
+ "Information Plaque: (Gods) Thor",
+ "Information Plaque: (Gods) Celtic Janus Sculpture",
+ "Information Plaque: (Gods) Sumerian Bull God - An",
+ "Information Plaque: (Gods) Sumerian Lyre",
+ "Information Plaque: (Gods) Chuen"
],
"Blue Maze": [
"Puzzle Solved Fortune Teller Door"
@@ -265,28 +265,28 @@
"Puzzle Solved UFO Symbols",
"Accessible: Storage: UFO",
"Final Riddle: Planets Aligned",
- "Information Plaque: Coincidence or Extraterrestrial Visits? (UFO)",
- "Information Plaque: Planets (UFO)",
- "Information Plaque: Astronomical Construction (UFO)",
- "Information Plaque: Aliens (UFO)"
+ "Information Plaque: (UFO) Coincidence or Extraterrestrial Visits?",
+ "Information Plaque: (UFO) Planets",
+ "Information Plaque: (UFO) Astronomical Construction",
+ "Information Plaque: (UFO) Aliens"
],
"Anansi": [
"Puzzle Solved Anansi Musicbox",
"Flashback Memory Obtained Ancient Astrology",
"Accessible: Storage: Skeleton",
"Accessible: Storage: Anansi",
- "Information Plaque: African Creation Myth (Anansi)",
- "Information Plaque: Apophis the Serpent (Anansi)",
- "Information Plaque: Death (Anansi)",
- "Information Plaque: Cyclops (Pegasus)",
- "Information Plaque: Lycanthropy (Werewolf)"
+ "Information Plaque: (Anansi) African Creation Myth",
+ "Information Plaque: (Anansi) Apophis the Serpent",
+ "Information Plaque: (Anansi) Death",
+ "Information Plaque: (Pegasus) Cyclops",
+ "Information Plaque: (Werewolf) Lycanthropy"
],
"Torture": [
"Puzzle Solved Gallows",
- "Accessible: Storage: Hanging",
+ "Accessible: Storage: Gallows",
"Final Riddle: Guillotine Dropped",
"Puzzle Hint Found: Gallows Information Plaque",
- "Information Plaque: Guillotine (Torture)"
+ "Information Plaque: (Torture) Guillotine"
],
"Puzzle Room Mastermind": [
"Puzzle Solved Mastermind",
@@ -296,17 +296,17 @@
"Puzzle Solved Marble Flipper"
],
"Prehistoric": [
- "Information Plaque: Bronze Unicorn (Prehistoric)",
- "Information Plaque: Griffin (Prehistoric)",
- "Information Plaque: Eagles Nest (Prehistoric)",
- "Information Plaque: Large Spider (Prehistoric)",
- "Information Plaque: Starfish (Prehistoric)",
+ "Information Plaque: (Prehistoric) Bronze Unicorn",
+ "Information Plaque: (Prehistoric) Griffin",
+ "Information Plaque: (Prehistoric) Eagles Nest",
+ "Information Plaque: (Prehistoric) Large Spider",
+ "Information Plaque: (Prehistoric) Starfish",
"Accessible: Storage: Eagles Nest"
],
"Tar River": [
"Accessible: Storage: Tar River",
- "Information Plaque: Subterranean World (Underground Maze)",
- "Information Plaque: Dero (Underground Maze)"
+ "Information Plaque: (Underground Maze) Subterranean World",
+ "Information Plaque: (Underground Maze) Dero"
],
"Theater": [
"Accessible: Storage: Theater"
diff --git a/worlds/shivers/data/regions.json b/worlds/shivers/data/regions.json
index 3e81136c45f8..963d100faddb 100644
--- a/worlds/shivers/data/regions.json
+++ b/worlds/shivers/data/regions.json
@@ -27,9 +27,9 @@
["Maze", ["To Maze Staircase From Maze", "To Tar River"]],
["Tar River", ["To Maze From Tar River", "To Lobby From Tar River"]],
["Egypt", ["To Lobby From Egypt", "To Burial From Egypt", "To Blue Maze From Egypt"]],
- ["Burial", ["To Egypt From Burial", "To Tiki From Burial"]],
- ["Tiki", ["To Burial From Tiki", "To Gods Room"]],
- ["Gods Room", ["To Tiki From Gods Room", "To Anansi From Gods Room"]],
+ ["Burial", ["To Egypt From Burial", "To Shaman From Burial"]],
+ ["Shaman", ["To Burial From Shaman", "To Gods Room"]],
+ ["Gods Room", ["To Shaman From Gods Room", "To Anansi From Gods Room"]],
["Anansi", ["To Gods Room From Anansi", "To Werewolf From Anansi"]],
["Werewolf", ["To Anansi From Werewolf", "To Night Staircase From Werewolf"]],
["Night Staircase", ["To Werewolf From Night Staircase", "To Janitor Closet", "To UFO"]],
@@ -109,13 +109,13 @@
["To Tar River", "Tar River"],
["To Tar River From Lobby", "Tar River"],
["To Burial From Egypt", "Burial"],
- ["To Burial From Tiki", "Burial"],
+ ["To Burial From Shaman", "Burial"],
["To Blue Maze From Three Floor Elevator", "Blue Maze"],
["To Blue Maze From Fortune Teller", "Blue Maze"],
["To Blue Maze From Inventions", "Blue Maze"],
["To Blue Maze From Egypt", "Blue Maze"],
- ["To Tiki From Burial", "Tiki"],
- ["To Tiki From Gods Room", "Tiki"],
+ ["To Shaman From Burial", "Shaman"],
+ ["To Shaman From Gods Room", "Shaman"],
["To Gods Room", "Gods Room" ],
["To Gods Room From Anansi", "Gods Room"],
["To Anansi From Gods Room", "Anansi"],
diff --git a/worlds/shivers/docs/en_Shivers.md b/worlds/shivers/docs/en_Shivers.md
index 51730057b034..a92f8a6b7911 100644
--- a/worlds/shivers/docs/en_Shivers.md
+++ b/worlds/shivers/docs/en_Shivers.md
@@ -1,8 +1,8 @@
# Shivers
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
configuration file.
## What does randomization do to this game?
diff --git a/worlds/shivers/docs/setup_en.md b/worlds/shivers/docs/setup_en.md
index ee33bb70408e..187382ef643c 100644
--- a/worlds/shivers/docs/setup_en.md
+++ b/worlds/shivers/docs/setup_en.md
@@ -33,8 +33,8 @@ guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
### Where do I get a config file?
-The Player Settings page on the website allows you to configure your personal settings and export a config file from
-them. Player settings page: [Shivers Player Settings Page](/games/Shivers/player-settings)
+The Player Options page on the website allows you to configure your personal options and export a config file from
+them. Player options page: [Shivers Player Options Page](/games/Shivers/player-options)
### Verifying your config file
diff --git a/worlds/shorthike/Items.py b/worlds/shorthike/Items.py
new file mode 100644
index 000000000000..a240dcbc6a1f
--- /dev/null
+++ b/worlds/shorthike/Items.py
@@ -0,0 +1,62 @@
+from BaseClasses import ItemClassification
+from typing import TypedDict, Dict, List, Set
+
+class ItemDict(TypedDict):
+ name: str
+ id: int
+ count: int
+ classification: ItemClassification
+
+base_id = 82000
+
+item_table: List[ItemDict] = [
+ {"name": "Stick", "id": base_id + 1, "count": 8, "classification": ItemClassification.progression_skip_balancing},
+ {"name": "Seashell", "id": base_id + 2, "count": 23, "classification": ItemClassification.progression_skip_balancing},
+ {"name": "Golden Feather", "id": base_id + 3, "count": 0, "classification": ItemClassification.progression},
+ {"name": "Silver Feather", "id": base_id + 4, "count": 0, "classification": ItemClassification.useful},
+ {"name": "Bucket", "id": base_id + 5, "count": 0, "classification": ItemClassification.progression},
+ {"name": "Bait", "id": base_id + 6, "count": 2, "classification": ItemClassification.filler},
+ {"name": "Fishing Rod", "id": base_id + 7, "count": 2, "classification": ItemClassification.progression},
+ {"name": "Shovel", "id": base_id + 8, "count": 1, "classification": ItemClassification.progression},
+ {"name": "Toy Shovel", "id": base_id + 9, "count": 5, "classification": ItemClassification.progression_skip_balancing},
+ {"name": "Compass", "id": base_id + 10, "count": 1, "classification": ItemClassification.useful},
+ {"name": "Medal", "id": base_id + 11, "count": 3, "classification": ItemClassification.filler},
+ {"name": "Shell Necklace", "id": base_id + 12, "count": 1, "classification": ItemClassification.progression},
+ {"name": "Wristwatch", "id": base_id + 13, "count": 1, "classification": ItemClassification.progression},
+ {"name": "Motorboat Key", "id": base_id + 14, "count": 1, "classification": ItemClassification.progression},
+ {"name": "Pickaxe", "id": base_id + 15, "count": 3, "classification": ItemClassification.useful},
+ {"name": "Fishing Journal", "id": base_id + 16, "count": 1, "classification": ItemClassification.useful},
+ {"name": "A Stormy View Map", "id": base_id + 17, "count": 1, "classification": ItemClassification.filler},
+ {"name": "The King Map", "id": base_id + 18, "count": 1, "classification": ItemClassification.filler},
+ {"name": "The Treasure of Sid Beach Map", "id": base_id + 19, "count": 1, "classification": ItemClassification.filler},
+ {"name": "In Her Shadow Map", "id": base_id + 20, "count": 1, "classification": ItemClassification.filler},
+ {"name": "Sunhat", "id": base_id + 21, "count": 1, "classification": ItemClassification.filler},
+ {"name": "Baseball Cap", "id": base_id + 22, "count": 1, "classification": ItemClassification.filler},
+ {"name": "Provincial Park Hat", "id": base_id + 23, "count": 1, "classification": ItemClassification.filler},
+ {"name": "Headband", "id": base_id + 24, "count": 1, "classification": ItemClassification.progression},
+ {"name": "Running Shoes", "id": base_id + 25, "count": 1, "classification": ItemClassification.useful},
+ {"name": "Camping Permit", "id": base_id + 26, "count": 1, "classification": ItemClassification.progression},
+ {"name": "Walkie Talkie", "id": base_id + 27, "count": 1, "classification": ItemClassification.useful},
+
+ # Not in the item pool for now
+ #{"name": "Boating Manual", "id": base_id + ~, "count": 1, "classification": ItemClassification.filler},
+
+ # Different Coin Amounts (Fillers)
+ {"name": "7 Coins", "id": base_id + 28, "count": 3, "classification": ItemClassification.filler},
+ {"name": "15 Coins", "id": base_id + 29, "count": 1, "classification": ItemClassification.filler},
+ {"name": "18 Coins", "id": base_id + 30, "count": 1, "classification": ItemClassification.filler},
+ {"name": "21 Coins", "id": base_id + 31, "count": 2, "classification": ItemClassification.filler},
+ {"name": "25 Coins", "id": base_id + 32, "count": 7, "classification": ItemClassification.filler},
+ {"name": "27 Coins", "id": base_id + 33, "count": 1, "classification": ItemClassification.filler},
+ {"name": "32 Coins", "id": base_id + 34, "count": 1, "classification": ItemClassification.filler},
+ {"name": "33 Coins", "id": base_id + 35, "count": 6, "classification": ItemClassification.filler},
+ {"name": "50 Coins", "id": base_id + 36, "count": 1, "classification": ItemClassification.filler},
+
+ # Filler item determined by settings
+ {"name": "13 Coins", "id": base_id + 37, "count": 0, "classification": ItemClassification.filler},
+]
+
+group_table: Dict[str, Set[str]] = {
+ "Coins": {"7 Coins", "13 Coins", "15 Coins", "18 Coins", "21 Coins", "25 Coins", "27 Coins", "32 Coins", "33 Coins", "50 Coins"},
+ "Maps": {"A Stormy View Map", "The King Map", "The Treasure of Sid Beach Map", "In Her Shadow Map"},
+}
diff --git a/worlds/shorthike/Locations.py b/worlds/shorthike/Locations.py
new file mode 100644
index 000000000000..c2d316c68675
--- /dev/null
+++ b/worlds/shorthike/Locations.py
@@ -0,0 +1,709 @@
+from typing import List, TypedDict
+
+class LocationInfo(TypedDict):
+ name: str
+ id: int
+ inGameId: str
+ needsShovel: bool
+ purchase: bool
+ minGoldenFeathers: int
+ minGoldenFeathersEasy: int
+ minGoldenFeathersBucket: int
+
+base_id = 83000
+
+location_table: List[LocationInfo] = [
+ # Original Seashell Locations
+ {"name": "Start Beach Seashell",
+ "id": base_id + 1,
+ "inGameId": "PickUps.3",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Beach Hut Seashell",
+ "id": base_id + 2,
+ "inGameId": "PickUps.2",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Beach Umbrella Seashell",
+ "id": base_id + 3,
+ "inGameId": "PickUps.8",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Sid Beach Mound Seashell",
+ "id": base_id + 4,
+ "inGameId": "PickUps.12",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Sid Beach Seashell",
+ "id": base_id + 5,
+ "inGameId": "PickUps.11",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Shirley's Point Beach Seashell",
+ "id": base_id + 6,
+ "inGameId": "PickUps.18",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Shirley's Point Rock Seashell",
+ "id": base_id + 7,
+ "inGameId": "PickUps.17",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Visitor's Center Beach Seashell",
+ "id": base_id + 8,
+ "inGameId": "PickUps.19",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "West River Seashell",
+ "id": base_id + 9,
+ "inGameId": "PickUps.10",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "West Riverbank Seashell",
+ "id": base_id + 10,
+ "inGameId": "PickUps.4",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Stone Tower Riverbank Seashell",
+ "id": base_id + 11,
+ "inGameId": "PickUps.23",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "North Beach Seashell",
+ "id": base_id + 12,
+ "inGameId": "PickUps.6",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "North Coast Seashell",
+ "id": base_id + 13,
+ "inGameId": "PickUps.7",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Boat Cliff Seashell",
+ "id": base_id + 14,
+ "inGameId": "PickUps.14",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Boat Isle Mound Seashell",
+ "id": base_id + 15,
+ "inGameId": "PickUps.22",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "East Coast Seashell",
+ "id": base_id + 16,
+ "inGameId": "PickUps.21",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "House North Beach Seashell",
+ "id": base_id + 17,
+ "inGameId": "PickUps.16",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Airstream Island North Seashell",
+ "id": base_id + 18,
+ "inGameId": "PickUps.13",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Airstream Island South Seashell",
+ "id": base_id + 19,
+ "inGameId": "PickUps.15",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Secret Island Beach Seashell",
+ "id": base_id + 20,
+ "inGameId": "PickUps.1",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Meteor Lake Seashell",
+ "id": base_id + 126,
+ "inGameId": "PickUps.20",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Good Creek Path Seashell",
+ "id": base_id + 127,
+ "inGameId": "PickUps.9",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+
+ # Visitor's Center Shop
+ {"name": "Visitor's Center Shop Golden Feather 1",
+ "id": base_id + 21,
+ "inGameId": "CampRangerNPC[0]",
+ "needsShovel": False, "purchase": True,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Visitor's Center Shop Golden Feather 2",
+ "id": base_id + 22,
+ "inGameId": "CampRangerNPC[1]",
+ "needsShovel": False, "purchase": True,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Visitor's Center Shop Hat",
+ "id": base_id + 23,
+ "inGameId": "CampRangerNPC[9]",
+ "needsShovel": False, "purchase": True,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+
+ # Tough Bird Salesman
+ {"name": "Tough Bird Salesman Golden Feather 1",
+ "id": base_id + 24,
+ "inGameId": "ToughBirdNPC (1)[0]",
+ "needsShovel": False, "purchase": True,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Tough Bird Salesman Golden Feather 2",
+ "id": base_id + 25,
+ "inGameId": "ToughBirdNPC (1)[1]",
+ "needsShovel": False, "purchase": True,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Tough Bird Salesman Golden Feather 3",
+ "id": base_id + 26,
+ "inGameId": "ToughBirdNPC (1)[2]",
+ "needsShovel": False, "purchase": True,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Tough Bird Salesman Golden Feather 4",
+ "id": base_id + 27,
+ "inGameId": "ToughBirdNPC (1)[3]",
+ "needsShovel": False, "purchase": True,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Tough Bird Salesman (400 Coins)",
+ "id": base_id + 28,
+ "inGameId": "ToughBirdNPC (1)[9]",
+ "needsShovel": False, "purchase": True,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+
+ # Beachstickball
+ {"name": "Beachstickball (10 Hits)",
+ "id": base_id + 29,
+ "inGameId": "VolleyballOpponent[0]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Beachstickball (20 Hits)",
+ "id": base_id + 30,
+ "inGameId": "VolleyballOpponent[1]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Beachstickball (30 Hits)",
+ "id": base_id + 31,
+ "inGameId": "VolleyballOpponent[2]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+
+ # Misc Item Locations
+ {"name": "Shovel Kid Trade",
+ "id": base_id + 32,
+ "inGameId": "Frog_StandingNPC[0]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Compass Guy",
+ "id": base_id + 33,
+ "inGameId": "Fox_WalkingNPC[0]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Hawk Peak Bucket Rock",
+ "id": base_id + 34,
+ "inGameId": "Tools.23",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Orange Islands Bucket Rock",
+ "id": base_id + 35,
+ "inGameId": "Tools.42",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Bill the Walrus Fisherman",
+ "id": base_id + 36,
+ "inGameId": "SittingNPC (1)[0]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Catch 3 Fish Reward",
+ "id": base_id + 37,
+ "inGameId": "FishBuyer[0]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Catch All Fish Reward",
+ "id": base_id + 38,
+ "inGameId": "FishBuyer[1]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 7, "minGoldenFeathersEasy": 9, "minGoldenFeathersBucket": 7},
+ {"name": "Permit Guy Bribe",
+ "id": base_id + 39,
+ "inGameId": "CamperNPC[0]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Catch Fish with Permit",
+ "id": base_id + 129,
+ "inGameId": "Player[0]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Return Camping Permit",
+ "id": base_id + 130,
+ "inGameId": "CamperNPC[1]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+
+ # Original Pickaxe Locations
+ {"name": "Blocked Mine Pickaxe 1",
+ "id": base_id + 40,
+ "inGameId": "Tools.31",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Blocked Mine Pickaxe 2",
+ "id": base_id + 41,
+ "inGameId": "Tools.32",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Blocked Mine Pickaxe 3",
+ "id": base_id + 42,
+ "inGameId": "Tools.33",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+
+ # Original Toy Shovel Locations
+ {"name": "Blackwood Trail Lookout Toy Shovel",
+ "id": base_id + 43,
+ "inGameId": "PickUps.27",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Shirley's Point Beach Toy Shovel",
+ "id": base_id + 44,
+ "inGameId": "PickUps.30",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Visitor's Center Beach Toy Shovel",
+ "id": base_id + 45,
+ "inGameId": "PickUps.29",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Blackwood Trail Rock Toy Shovel",
+ "id": base_id + 46,
+ "inGameId": "PickUps.26",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Beach Hut Cliff Toy Shovel",
+ "id": base_id + 128,
+ "inGameId": "PickUps.28",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+
+ # Original Stick Locations
+ {"name": "Secret Island Beach Trail Stick",
+ "id": base_id + 47,
+ "inGameId": "PickUps.25",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Below Lighthouse Walkway Stick",
+ "id": base_id + 48,
+ "inGameId": "Tools.3",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Beach Hut Rocky Pool Sand Stick",
+ "id": base_id + 49,
+ "inGameId": "Tools.0",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Cliff Overlooking West River Waterfall Stick",
+ "id": base_id + 50,
+ "inGameId": "Tools.2",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 2, "minGoldenFeathersBucket": 0},
+ {"name": "Trail to Tough Bird Salesman Stick",
+ "id": base_id + 51,
+ "inGameId": "Tools.8",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "North Beach Stick",
+ "id": base_id + 52,
+ "inGameId": "Tools.4",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Beachstickball Court Stick",
+ "id": base_id + 53,
+ "inGameId": "VolleyballMinigame.4",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Stick Under Sid Beach Umbrella",
+ "id": base_id + 54,
+ "inGameId": "Tools.1",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+
+ # Boating
+ {"name": "Boat Rental",
+ "id": base_id + 55,
+ "inGameId": "DadDeer[0]",
+ "needsShovel": False, "purchase": True,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Boat Challenge Reward",
+ "id": base_id + 56,
+ "inGameId": "DeerKidBoat[0]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+
+ # Not a location for now, corresponding with the Boating Manual
+ # {"name": "Receive Boating Manual",
+ # "id": base_id + 133,
+ # "inGameId": "DadDeer[1]",
+ # "needsShovel": False, "purchase": False,
+ # "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+
+ # Original Map Locations
+ {"name": "Outlook Point Dog Gift",
+ "id": base_id + 57,
+ "inGameId": "Dog_WalkingNPC_BlueEyed[0]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+
+ # Original Clothes Locations
+ {"name": "Collect 15 Seashells",
+ "id": base_id + 58,
+ "inGameId": "LittleKidNPCVariant (1)[0]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Return to Shell Kid",
+ "id": base_id + 132,
+ "inGameId": "LittleKidNPCVariant (1)[1]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Taylor the Turtle Headband Gift",
+ "id": base_id + 59,
+ "inGameId": "Turtle_WalkingNPC[0]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Sue the Rabbit Shoes Reward",
+ "id": base_id + 60,
+ "inGameId": "Bunny_WalkingNPC (1)[0]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Purchase Sunhat",
+ "id": base_id + 61,
+ "inGameId": "SittingNPC[0]",
+ "needsShovel": False, "purchase": True,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+
+ # Original Golden Feather Locations
+ {"name": "Blackwood Forest Golden Feather",
+ "id": base_id + 62,
+ "inGameId": "Feathers.3",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Ranger May Shell Necklace Golden Feather",
+ "id": base_id + 63,
+ "inGameId": "AuntMayNPC[0]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Sand Castle Golden Feather",
+ "id": base_id + 64,
+ "inGameId": "SandProvince.3",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Artist Golden Feather",
+ "id": base_id + 65,
+ "inGameId": "StandingNPC[0]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Visitor Camp Rock Golden Feather",
+ "id": base_id + 66,
+ "inGameId": "Feathers.8",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Outlook Cliff Golden Feather",
+ "id": base_id + 67,
+ "inGameId": "Feathers.2",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Meteor Lake Cliff Golden Feather",
+ "id": base_id + 68,
+ "inGameId": "Feathers.7",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 5, "minGoldenFeathersBucket": 0},
+
+ # Original Silver Feather Locations
+ {"name": "Secret Island Peak",
+ "id": base_id + 69,
+ "inGameId": "PickUps.24",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 5, "minGoldenFeathersEasy": 7, "minGoldenFeathersBucket": 7},
+ {"name": "Wristwatch Trade",
+ "id": base_id + 70,
+ "inGameId": "Goat_StandingNPC[0]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+
+ # Golden Chests
+ {"name": "Lighthouse Golden Chest",
+ "id": base_id + 71,
+ "inGameId": "Feathers.0",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 2, "minGoldenFeathersEasy": 3, "minGoldenFeathersBucket": 0},
+ {"name": "Outlook Golden Chest",
+ "id": base_id + 72,
+ "inGameId": "Feathers.6",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Stone Tower Golden Chest",
+ "id": base_id + 73,
+ "inGameId": "Feathers.5",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "North Cliff Golden Chest",
+ "id": base_id + 74,
+ "inGameId": "Feathers.4",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 3, "minGoldenFeathersEasy": 10, "minGoldenFeathersBucket": 10},
+
+ # Chests
+ {"name": "Blackwood Cliff Chest",
+ "id": base_id + 75,
+ "inGameId": "Coins.22",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "White Coast Trail Chest",
+ "id": base_id + 76,
+ "inGameId": "Coins.6",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Sid Beach Chest",
+ "id": base_id + 77,
+ "inGameId": "Coins.7",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Sid Beach Buried Treasure Chest",
+ "id": base_id + 78,
+ "inGameId": "Coins.46",
+ "needsShovel": True, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Sid Beach Cliff Chest",
+ "id": base_id + 79,
+ "inGameId": "Coins.9",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Visitor's Center Buried Chest",
+ "id": base_id + 80,
+ "inGameId": "Coins.94",
+ "needsShovel": True, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Visitor's Center Hidden Chest",
+ "id": base_id + 81,
+ "inGameId": "Coins.42",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Shirley's Point Chest",
+ "id": base_id + 82,
+ "inGameId": "Coins.10",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 1, "minGoldenFeathersEasy": 2, "minGoldenFeathersBucket": 2},
+ {"name": "Caravan Cliff Chest",
+ "id": base_id + 83,
+ "inGameId": "Coins.12",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Caravan Arch Chest",
+ "id": base_id + 84,
+ "inGameId": "Coins.11",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "King Buried Treasure Chest",
+ "id": base_id + 85,
+ "inGameId": "Coins.41",
+ "needsShovel": True, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Good Creek Path Buried Chest",
+ "id": base_id + 86,
+ "inGameId": "Coins.48",
+ "needsShovel": True, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Good Creek Path West Chest",
+ "id": base_id + 87,
+ "inGameId": "Coins.33",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Good Creek Path East Chest",
+ "id": base_id + 88,
+ "inGameId": "Coins.62",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "West Waterfall Chest",
+ "id": base_id + 89,
+ "inGameId": "Coins.20",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Stone Tower West Cliff Chest",
+ "id": base_id + 90,
+ "inGameId": "PickUps.0",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Bucket Path Chest",
+ "id": base_id + 91,
+ "inGameId": "Coins.50",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Bucket Cliff Chest",
+ "id": base_id + 92,
+ "inGameId": "Coins.49",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 3, "minGoldenFeathersEasy": 5, "minGoldenFeathersBucket": 5},
+ {"name": "In Her Shadow Buried Treasure Chest",
+ "id": base_id + 93,
+ "inGameId": "Feathers.9",
+ "needsShovel": True, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Meteor Lake Buried Chest",
+ "id": base_id + 94,
+ "inGameId": "Coins.86",
+ "needsShovel": True, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Meteor Lake Chest",
+ "id": base_id + 95,
+ "inGameId": "Coins.64",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "House North Beach Chest",
+ "id": base_id + 96,
+ "inGameId": "Coins.65",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "East Coast Chest",
+ "id": base_id + 97,
+ "inGameId": "Coins.98",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Fisherman's Boat Chest 1",
+ "id": base_id + 99,
+ "inGameId": "Boat.0",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Fisherman's Boat Chest 2",
+ "id": base_id + 100,
+ "inGameId": "Boat.7",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Airstream Island Chest",
+ "id": base_id + 101,
+ "inGameId": "Coins.31",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "West River Waterfall Head Chest",
+ "id": base_id + 102,
+ "inGameId": "Coins.34",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Old Building Chest",
+ "id": base_id + 103,
+ "inGameId": "Coins.104",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Old Building West Chest",
+ "id": base_id + 104,
+ "inGameId": "Coins.109",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Old Building East Chest",
+ "id": base_id + 105,
+ "inGameId": "Coins.8",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Hawk Peak West Chest",
+ "id": base_id + 106,
+ "inGameId": "Coins.21",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 3, "minGoldenFeathersEasy": 5, "minGoldenFeathersBucket": 5},
+ {"name": "Hawk Peak East Buried Chest",
+ "id": base_id + 107,
+ "inGameId": "Coins.76",
+ "needsShovel": True, "purchase": False,
+ "minGoldenFeathers": 3, "minGoldenFeathersEasy": 5, "minGoldenFeathersBucket": 5},
+ {"name": "Hawk Peak Northeast Chest",
+ "id": base_id + 108,
+ "inGameId": "Coins.79",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 3, "minGoldenFeathersEasy": 5, "minGoldenFeathersBucket": 5},
+ {"name": "Northern East Coast Chest",
+ "id": base_id + 109,
+ "inGameId": "Coins.45",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 2, "minGoldenFeathersBucket": 0},
+ {"name": "North Coast Chest",
+ "id": base_id + 110,
+ "inGameId": "Coins.28",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "North Coast Buried Chest",
+ "id": base_id + 111,
+ "inGameId": "Coins.47",
+ "needsShovel": True, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Small South Island Buried Chest",
+ "id": base_id + 112,
+ "inGameId": "Coins.87",
+ "needsShovel": True, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Secret Island Bottom Chest",
+ "id": base_id + 113,
+ "inGameId": "Coins.88",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Secret Island Treehouse Chest",
+ "id": base_id + 114,
+ "inGameId": "Coins.89",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 1, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 1},
+ {"name": "Sunhat Island Buried Chest",
+ "id": base_id + 115,
+ "inGameId": "Coins.112",
+ "needsShovel": True, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Orange Islands South Buried Chest",
+ "id": base_id + 116,
+ "inGameId": "Coins.119",
+ "needsShovel": True, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Orange Islands West Chest",
+ "id": base_id + 117,
+ "inGameId": "Coins.121",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Orange Islands North Buried Chest",
+ "id": base_id + 118,
+ "inGameId": "Coins.117",
+ "needsShovel": True, "purchase": False,
+ "minGoldenFeathers": 1, "minGoldenFeathersEasy": 1, "minGoldenFeathersBucket": 0},
+ {"name": "Orange Islands East Chest",
+ "id": base_id + 119,
+ "inGameId": "Coins.120",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Orange Islands South Hidden Chest",
+ "id": base_id + 120,
+ "inGameId": "Coins.124",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "A Stormy View Buried Treasure Chest",
+ "id": base_id + 121,
+ "inGameId": "Coins.113",
+ "needsShovel": True, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+ {"name": "Orange Islands Ruins Buried Chest",
+ "id": base_id + 122,
+ "inGameId": "Coins.118",
+ "needsShovel": True, "purchase": False,
+ "minGoldenFeathers": 2, "minGoldenFeathersEasy": 4, "minGoldenFeathersBucket": 0},
+
+ # Race Rewards
+ {"name": "Lighthouse Race Reward",
+ "id": base_id + 123,
+ "inGameId": "RaceOpponent[0]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 2, "minGoldenFeathersEasy": 3, "minGoldenFeathersBucket": 1},
+ {"name": "Old Building Race Reward",
+ "id": base_id + 124,
+ "inGameId": "RaceOpponent[1]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 1, "minGoldenFeathersEasy": 5, "minGoldenFeathersBucket": 0},
+ {"name": "Hawk Peak Race Reward",
+ "id": base_id + 125,
+ "inGameId": "RaceOpponent[2]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 7, "minGoldenFeathersEasy": 9, "minGoldenFeathersBucket": 7},
+ {"name": "Lose Race Gift",
+ "id": base_id + 131,
+ "inGameId": "RaceOpponent[9]",
+ "needsShovel": False, "purchase": False,
+ "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0},
+]
diff --git a/worlds/shorthike/Options.py b/worlds/shorthike/Options.py
new file mode 100644
index 000000000000..1ac0ff52f974
--- /dev/null
+++ b/worlds/shorthike/Options.py
@@ -0,0 +1,89 @@
+from dataclasses import dataclass
+from Options import Choice, PerGameCommonOptions, Range, StartInventoryPool, Toggle
+
+class Goal(Choice):
+ """Choose the end goal.
+ Nap: Complete the climb to the top of Hawk Peak and take a nap
+ Photo: Get your picture taken at the top of Hawk Peak
+ Races: Complete all three races with Avery
+ Help Everyone: Travel around Hawk Peak and help every character with their troubles
+ Fishmonger: Catch one of every fish from around Hawk Peak"""
+ display_name = "Goal"
+ option_nap = 0
+ option_photo = 1
+ option_races = 2
+ option_help_everyone = 3
+ option_fishmonger = 4
+ default = 3
+
+class CoinsInShops(Toggle):
+ """When enabled, the randomizer can place coins into locations that are purchased, such as shops."""
+ display_name = "Coins in Purchaseable Locations"
+ default = False
+
+class GoldenFeathers(Range):
+ """Number of Golden Feathers in the item pool.
+ (Note that for the Photo and Help Everyone goals, a minimum of 12 Golden Feathers is enforced)"""
+ display_name = "Golden Feathers"
+ range_start = 0
+ range_end = 20
+ default = 20
+
+class SilverFeathers(Range):
+ """Number of Silver Feathers in the item pool."""
+ display_name = "Silver Feathers"
+ range_start = 0
+ range_end = 20
+ default = 2
+
+class Buckets(Range):
+ """Number of Buckets in the item pool."""
+ display_name = "Buckets"
+ range_start = 0
+ range_end = 2
+ default = 2
+
+class GoldenFeatherProgression(Choice):
+ """Determines which locations are considered in logic based on the required amount of golden feathers to reach them.
+ Easy: Locations will be considered inaccessible until the player has enough golden feathers to easily reach them. A minimum of 10 golden feathers is recommended for this setting.
+ Normal: Locations will be considered inaccessible until the player has the minimum possible number of golden feathers to reach them. A minimum of 7 golden feathers is recommended for this setting.
+ Hard: Removes the requirement of golden feathers for progression entirely and glitches may need to be used to progress"""
+ display_name = "Golden Feather Progression"
+ option_easy = 0
+ option_normal = 1
+ option_hard = 2
+ default = 1
+
+class CostMultiplier(Range):
+ """The percentage that all item shop costs will be of the vanilla values."""
+ display_name = "Shop Cost Multiplier"
+ range_start = 25
+ range_end = 200
+ default = 100
+
+class FillerCoinAmount(Choice):
+ """The number of coins that will be in each filler coin item."""
+ display_name = "Coins per Filler Item"
+ option_7_coins = 0
+ option_13_coins = 1
+ option_15_coins = 2
+ option_18_coins = 3
+ option_21_coins = 4
+ option_25_coins = 5
+ option_27_coins = 6
+ option_32_coins = 7
+ option_33_coins = 8
+ option_50_coins = 9
+ default = 1
+
+@dataclass
+class ShortHikeOptions(PerGameCommonOptions):
+ start_inventory_from_pool: StartInventoryPool
+ goal: Goal
+ coins_in_shops: CoinsInShops
+ golden_feathers: GoldenFeathers
+ silver_feathers: SilverFeathers
+ buckets: Buckets
+ golden_feather_progression: GoldenFeatherProgression
+ cost_multiplier: CostMultiplier
+ filler_coin_amount: FillerCoinAmount
diff --git a/worlds/shorthike/Rules.py b/worlds/shorthike/Rules.py
new file mode 100644
index 000000000000..73a16434219e
--- /dev/null
+++ b/worlds/shorthike/Rules.py
@@ -0,0 +1,73 @@
+from worlds.generic.Rules import forbid_items_for_player, add_rule
+
+def create_rules(self, location_table):
+ multiworld = self.multiworld
+ player = self.player
+ options = self.options
+
+ # Shovel Rules
+ for loc in location_table:
+ if loc["needsShovel"]:
+ forbid_items_for_player(multiworld.get_location(loc["name"], player), self.item_name_groups['Maps'], player)
+ add_rule(multiworld.get_location(loc["name"], player),
+ lambda state: state.has("Shovel", player))
+ if loc["purchase"] and not options.coins_in_shops:
+ forbid_items_for_player(multiworld.get_location(loc["name"], player), self.item_name_groups['Coins'], player)
+
+ # Minimum Feather Rules
+ if options.golden_feather_progression != 2:
+ min_feathers = get_min_feathers(self, loc["minGoldenFeathers"], loc["minGoldenFeathersEasy"])
+
+ if options.buckets > 0 and loc["minGoldenFeathersBucket"] < min_feathers:
+ add_rule(multiworld.get_location(loc["name"], player),
+ lambda state, loc=loc, min_feathers=min_feathers: state.has("Golden Feather", player, min_feathers)
+ or (state.has("Bucket", player) and state.has("Golden Feather", player, loc["minGoldenFeathersBucket"])))
+ elif min_feathers > 0:
+ add_rule(multiworld.get_location(loc["name"], player),
+ lambda state, min_feathers=min_feathers: state.has("Golden Feather", player, min_feathers))
+ add_rule(multiworld.get_location("Shovel Kid Trade", player),
+ lambda state: state.has("Toy Shovel", player))
+ add_rule(multiworld.get_location("Sand Castle Golden Feather", player),
+ lambda state: state.has("Toy Shovel", player))
+
+ # Fishing Rules
+ add_rule(multiworld.get_location("Catch 3 Fish Reward", player),
+ lambda state: state.has("Fishing Rod", player))
+ add_rule(multiworld.get_location("Catch Fish with Permit", player),
+ lambda state: state.has("Fishing Rod", player))
+ add_rule(multiworld.get_location("Catch All Fish Reward", player),
+ lambda state: state.has("Fishing Rod", player))
+
+ # Misc Rules
+ add_rule(multiworld.get_location("Return Camping Permit", player),
+ lambda state: state.has("Camping Permit", player))
+ add_rule(multiworld.get_location("Boat Challenge Reward", player),
+ lambda state: state.has("Motorboat Key", player))
+ add_rule(multiworld.get_location("Collect 15 Seashells", player),
+ lambda state: state.has("Seashell", player, 15))
+ add_rule(multiworld.get_location("Wristwatch Trade", player),
+ lambda state: state.has("Wristwatch", player))
+ add_rule(multiworld.get_location("Sue the Rabbit Shoes Reward", player),
+ lambda state: state.has("Headband", player))
+ add_rule(multiworld.get_location("Return to Shell Kid", player),
+ lambda state: state.has("Shell Necklace", player) and state.has("Seashell", player, 15))
+ add_rule(multiworld.get_location("Ranger May Shell Necklace Golden Feather", player),
+ lambda state: state.has("Shell Necklace", player))
+ add_rule(multiworld.get_location("Beachstickball (10 Hits)", player),
+ lambda state: state.has("Stick", player))
+ add_rule(multiworld.get_location("Beachstickball (20 Hits)", player),
+ lambda state: state.has("Stick", player))
+ add_rule(multiworld.get_location("Beachstickball (30 Hits)", player),
+ lambda state: state.has("Stick", player))
+
+def get_min_feathers(self, min_golden_feathers, min_golden_feathers_easy):
+ options = self.options
+
+ min_feathers = min_golden_feathers
+ if options.golden_feather_progression == 0:
+ min_feathers = min_golden_feathers_easy
+ if min_feathers > options.golden_feathers:
+ if options.goal != 1 and options.goal != 3:
+ min_feathers = options.golden_feathers
+
+ return min_feathers
diff --git a/worlds/shorthike/__init__.py b/worlds/shorthike/__init__.py
new file mode 100644
index 000000000000..3e0430f024ca
--- /dev/null
+++ b/worlds/shorthike/__init__.py
@@ -0,0 +1,133 @@
+from collections import Counter
+from typing import ClassVar, Dict, Any, Type
+from BaseClasses import Region, Location, Item, Tutorial
+from Options import PerGameCommonOptions
+from worlds.AutoWorld import World, WebWorld
+from .Items import item_table, group_table, base_id
+from .Locations import location_table
+from .Rules import create_rules, get_min_feathers
+from .Options import ShortHikeOptions
+
+class ShortHikeWeb(WebWorld):
+ theme = "ocean"
+ tutorials = [Tutorial(
+ "Multiworld Setup Guide",
+ "A guide to setting up the A Short Hike randomizer connected to an Archipelago Multiworld",
+ "English",
+ "setup_en.md",
+ "setup/en",
+ ["Chandler"]
+ )]
+
+class ShortHikeWorld(World):
+ """
+ A Short Hike is a relaxing adventure set on the islands of Hawk Peak. Fly and climb using Claire's wings and Golden Feathers
+ to make your way up to the summit. Along the way you'll meet other hikers, discover hidden treasures,
+ and take in the beautiful world around you.
+ """
+
+ game = "A Short Hike"
+ web = ShortHikeWeb()
+ data_version = 2
+
+ item_name_to_id = {item["name"]: item["id"] for item in item_table}
+ location_name_to_id = {loc["name"]: loc["id"] for loc in location_table}
+ location_name_to_game_id = {loc["name"]: loc["inGameId"] for loc in location_table}
+
+ item_name_groups = group_table
+
+ options_dataclass: ClassVar[Type[PerGameCommonOptions]] = ShortHikeOptions
+ options: ShortHikeOptions
+
+ required_client_version = (0, 4, 4)
+
+ def get_filler_item_name(self) -> str:
+ return self.options.filler_coin_amount.current_option_name
+
+ def create_item(self, name: str) -> "ShortHikeItem":
+ item_id: int = self.item_name_to_id[name]
+ id = item_id - base_id - 1
+
+ return ShortHikeItem(name, item_table[id]["classification"], item_id, player=self.player)
+
+ def create_items(self) -> None:
+ for item in item_table:
+ count = item["count"]
+
+ if count <= 0:
+ continue
+ else:
+ for i in range(count):
+ self.multiworld.itempool.append(self.create_item(item["name"]))
+
+ feather_count = self.options.golden_feathers
+ if self.options.goal == 1 or self.options.goal == 3:
+ if feather_count < 12:
+ feather_count = 12
+
+ junk = 45 - self.options.silver_feathers - feather_count - self.options.buckets
+ self.multiworld.itempool += [self.create_item(self.get_filler_item_name()) for _ in range(junk)]
+ self.multiworld.itempool += [self.create_item("Golden Feather") for _ in range(feather_count)]
+ self.multiworld.itempool += [self.create_item("Silver Feather") for _ in range(self.options.silver_feathers)]
+ self.multiworld.itempool += [self.create_item("Bucket") for _ in range(self.options.buckets)]
+
+ def create_regions(self) -> None:
+ menu_region = Region("Menu", self.player, self.multiworld)
+ self.multiworld.regions.append(menu_region)
+
+ main_region = Region("Hawk Peak", self.player, self.multiworld)
+
+ for loc in self.location_name_to_id.keys():
+ main_region.locations.append(ShortHikeLocation(self.player, loc, self.location_name_to_id[loc], main_region))
+
+ self.multiworld.regions.append(main_region)
+
+ menu_region.connect(main_region)
+
+ if self.options.goal == "nap":
+ # Nap
+ self.multiworld.completion_condition[self.player] = lambda state: (state.has("Golden Feather", self.player, get_min_feathers(self, 7, 9))
+ or (state.has("Bucket", self.player) and state.has("Golden Feather", self.player, 7)))
+ elif self.options.goal == "photo":
+ # Photo
+ self.multiworld.completion_condition[self.player] = lambda state: state.has("Golden Feather", self.player, 12)
+ elif self.options.goal == "races":
+ # Races
+ self.multiworld.completion_condition[self.player] = lambda state: (state.has("Golden Feather", self.player, get_min_feathers(self, 7, 9))
+ or (state.has("Bucket", self.player) and state.has("Golden Feather", self.player, 7)))
+ elif self.options.goal == "help_everyone":
+ # Help Everyone
+ self.multiworld.completion_condition[self.player] = lambda state: (state.has("Golden Feather", self.player, 12)
+ and state.has("Toy Shovel", self.player) and state.has("Camping Permit", self.player)
+ and state.has("Motorboat Key", self.player) and state.has("Headband", self.player)
+ and state.has("Wristwatch", self.player) and state.has("Seashell", self.player, 15)
+ and state.has("Shell Necklace", self.player))
+ elif self.options.goal == "fishmonger":
+ # Fishmonger
+ self.multiworld.completion_condition[self.player] = lambda state: (state.has("Golden Feather", self.player, get_min_feathers(self, 7, 9))
+ or (state.has("Bucket", self.player) and state.has("Golden Feather", self.player, 7))
+ and state.has("Fishing Rod", self.player))
+
+ def set_rules(self):
+ create_rules(self, location_table)
+
+ def fill_slot_data(self) -> Dict[str, Any]:
+ options = self.options
+
+ settings = {
+ "goal": int(options.goal),
+ "logicLevel": int(options.golden_feather_progression),
+ "costMultiplier": int(options.cost_multiplier),
+ }
+
+ slot_data = {
+ "settings": settings,
+ }
+
+ return slot_data
+
+class ShortHikeItem(Item):
+ game: str = "A Short Hike"
+
+class ShortHikeLocation(Location):
+ game: str = "A Short Hike"
diff --git a/worlds/shorthike/docs/en_A Short Hike.md b/worlds/shorthike/docs/en_A Short Hike.md
new file mode 100644
index 000000000000..11b5b4b8ca85
--- /dev/null
+++ b/worlds/shorthike/docs/en_A Short Hike.md
@@ -0,0 +1,29 @@
+# A Short Hike
+
+## What does randomization do to this game?
+
+All items that can be obtained from chests, the ground, and NPCs are randomized.
+
+## What does another world's item look like in A Short Hike?
+
+All items are replaced with chests that can contain items from other worlds.
+Items will appear with the Archipelago logo next to them when obtained.
+
+## Which characters need to be helped for the Help Everyone goal?
+
+To achieve the Help Everyone goal, the following characters will need to be helped:
+- Pay Tough Bird Salesman's Tuition Fee
+- Give Frog a Toy Shovel
+- Return the Camper's Camping Permit
+- Complete the Deer Kid's Boating Challenge
+- Find Sue's Headband
+- Clean Up and Purchase the Sunhat from the Deer
+- Return the Camper's Wristwatch
+- Cheer Up the Artist
+- Collect 15 Shells for the Kid
+- Give the Shell Necklace to Aunt May
+- Help the Fox Climb the Mountain
+
+## Can I have more than one save at a time?
+
+You can have up to 3 saves at a time. To switch between them, use the Save Data button in the options menu.
diff --git a/worlds/shorthike/docs/setup_en.md b/worlds/shorthike/docs/setup_en.md
new file mode 100644
index 000000000000..85d5a8f5eb16
--- /dev/null
+++ b/worlds/shorthike/docs/setup_en.md
@@ -0,0 +1,33 @@
+# A Short Hike Multiworld Setup Guide
+
+## Required Software
+
+- A Short Hike: [Steam](https://store.steampowered.com/app/1055540/A_Short_Hike/)
+ - The Epic Games Store or itch.io version of A Short Hike will also work.
+- A Short Hike Modding Tools: [GitHub](https://github.com/BrandenEK/AShortHike.ModdingTools)
+- A Short Hike Randomizer: [GitHub](https://github.com/BrandenEK/AShortHike.Randomizer)
+
+## Optional Software
+
+- [PopTracker](https://github.com/black-sliver/PopTracker/)
+ - [Chandler's A Short Hike PopTracker Pack](https://github.com/chandler05/shorthike-archipelago-poptracker/releases)
+
+## Installation
+
+1. Open the [Modding Tools GitHub page](https://github.com/BrandenEK/AShortHike.ModdingTools/), and follow
+the installation instructions. After this step, your `A Short Hike/` folder should have an empty `Modding/` subfolder.
+
+2. After the Modding Tools have been installed, download the
+[Randomizer](https://github.com/BrandenEK/AShortHike.Randomizer/releases) zip, extract it, and move the contents
+of the `Randomizer/` folder into your `Modding/` folder. After this step, your `Modding/` folder should have
+ `data/` and `plugins/` subfolders.
+
+## Connecting
+
+A Short Hike will prompt you with the server details when a new game is started or a previous one is continued.
+Enter in the Server Port, Name, and Password (optional) in the popup menu that appears and hit connect.
+
+## Tracking
+
+Install PopTracker from the link above and place the PopTracker pack into the packs folder.
+Connect to Archipelago via the AP button in the top left.
diff --git a/worlds/sm/Client.py b/worlds/sm/Client.py
index 756fd4bf36a7..ed3f2d5b3d30 100644
--- a/worlds/sm/Client.py
+++ b/worlds/sm/Client.py
@@ -37,6 +37,7 @@
class SMSNIClient(SNIClient):
game = "Super Metroid"
+ patch_suffix = [".apsm", ".apm3"]
async def deathlink_kill_player(self, ctx):
from SNIClient import DeathState, snes_buffered_write, snes_flush_writes, snes_read
diff --git a/worlds/sm/docs/en_Super Metroid.md b/worlds/sm/docs/en_Super Metroid.md
index 5c87e026f634..c8c1d0faabee 100644
--- a/worlds/sm/docs/en_Super Metroid.md
+++ b/worlds/sm/docs/en_Super Metroid.md
@@ -1,8 +1,8 @@
# Super Metroid
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
diff --git a/worlds/sm/docs/multiworld_en.md b/worlds/sm/docs/multiworld_en.md
index 0e82be769571..8f30630bc96d 100644
--- a/worlds/sm/docs/multiworld_en.md
+++ b/worlds/sm/docs/multiworld_en.md
@@ -2,16 +2,18 @@
## Required Software
-- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases).
-
-
-- Hardware or software capable of loading and playing SNES ROM files
- - An emulator capable of connecting to SNI such as:
- - snes9x-rr from: [snes9x rr](https://github.com/gocha/snes9x-rr/releases),
- - BizHawk from: [TASVideos](https://tasvideos.org/BizHawk)
- - RetroArch 1.10.1 or newer from: [RetroArch Website](https://retroarch.com?page=platforms). Or,
- - An SD2SNES, FXPak Pro ([FXPak Pro Store Page](https://krikzz.com/store/home/54-fxpak-pro.html)), or other
- compatible hardware
+- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases).
+- [SNI](https://github.com/alttpo/sni/releases). This is automatically included with your Archipelago installation above.
+- SNI is not compatible with (Q)Usb2Snes.
+- Hardware or software capable of loading and playing SNES ROM files, including:
+ - An emulator capable of connecting to SNI
+ ([snes9x-nwa](https://github.com/Skarsnik/snes9x-emunwa/releases), [snes9x-rr](https://github.com/gocha/snes9x-rr/releases),
+ [BSNES-plus](https://github.com/black-sliver/bsnes-plus),
+ [BizHawk](http://tasvideos.org/BizHawk.html), or
+ [RetroArch](https://retroarch.com?page=platforms) 1.10.1 or newer)
+ - An SD2SNES, [FXPak Pro](https://krikzz.com/store/home/54-fxpak-pro.html), or other compatible hardware. **note:
+ modded SNES minis are currently not supported by SNI. Some users have claimed success with QUsb2Snes for this system,
+ but it is not supported.**
- Your legally obtained Super Metroid ROM file, probably named `Super Metroid (Japan, USA).sfc`
## Installation Procedures
@@ -44,8 +46,8 @@ guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
### Where do I get a config file?
-The Player Settings page on the website allows you to configure your personal settings and export a config file from
-them. Player settings page: [Super Metroid Player Settings Page](/games/Super%20Metroid/player-settings)
+The Player Options page on the website allows you to configure your personal options and export a config file from
+them. Player options page: [Super Metroid Player Options Page](/games/Super%20Metroid/player-options)
### Verifying your config file
@@ -54,8 +56,8 @@ validator page: [YAML Validation page](/check)
## Generating a Single-Player Game
-1. Navigate to the Player Settings page, configure your options, and click the "Generate Game" button.
- - Player Settings page: [Super Metroid Player Settings Page](/games/Super%20Metroid/player-settings)
+1. Navigate to the Player Options page, configure your options, and click the "Generate Game" button.
+ - Player Options page: [Super Metroid Player Options Page](/games/Super%20Metroid/player-options)
2. You will be presented with a "Seed Info" page.
3. Click the "Create New Room" link.
4. You will be presented with a server page, from which you can download your patch file.
@@ -81,6 +83,11 @@ client, and will also create your ROM in the same place as your patch file.
When the client launched automatically, SNI should have also automatically launched in the background. If this is its
first time launching, you may be prompted to allow it to communicate through the Windows Firewall.
+#### snes9x-nwa
+
+1. Click on the Network Menu and check **Enable Emu Network Control**
+2. Load your ROM file if it hasn't already been loaded.
+
##### snes9x-rr
1. Load your ROM file if it hasn't already been loaded.
@@ -92,6 +99,12 @@ first time launching, you may be prompted to allow it to communicate through the
6. If you see an error while loading the script that states `socket.dll missing` or similar, navigate to the folder of
the lua you are using in your file explorer and copy the `socket.dll` to the base folder of your snes9x install.
+#### BSNES-Plus
+
+1. Load your ROM file if it hasn't already been loaded.
+2. The emulator should automatically connect while SNI is running.
+
+
##### BizHawk
1. Ensure you have the BSNES core loaded. This is done with the main menubar, under:
diff --git a/worlds/sm64ex/Items.py b/worlds/sm64ex/Items.py
index 5b429a23cdc3..546f1abd316b 100644
--- a/worlds/sm64ex/Items.py
+++ b/worlds/sm64ex/Items.py
@@ -16,6 +16,21 @@ class SM64Item(Item):
"1Up Mushroom": 3626184
}
+action_item_table = {
+ "Double Jump": 3626185,
+ "Triple Jump": 3626186,
+ "Long Jump": 3626187,
+ "Backflip": 3626188,
+ "Side Flip": 3626189,
+ "Wall Kick": 3626190,
+ "Dive": 3626191,
+ "Ground Pound": 3626192,
+ "Kick": 3626193,
+ "Climb": 3626194,
+ "Ledge Grab": 3626195
+}
+
+
cannon_item_table = {
"Cannon Unlock BoB": 3626200,
"Cannon Unlock WF": 3626201,
@@ -29,4 +44,4 @@ class SM64Item(Item):
"Cannon Unlock RR": 3626214
}
-item_table = {**generic_item_table, **cannon_item_table}
\ No newline at end of file
+item_table = {**generic_item_table, **action_item_table, **cannon_item_table}
\ No newline at end of file
diff --git a/worlds/sm64ex/Options.py b/worlds/sm64ex/Options.py
index 8a10f3edea55..60ec4bbe13c2 100644
--- a/worlds/sm64ex/Options.py
+++ b/worlds/sm64ex/Options.py
@@ -1,9 +1,11 @@
import typing
-from Options import Option, DefaultOnToggle, Range, Toggle, DeathLink, Choice
-
+from dataclasses import dataclass
+from Options import DefaultOnToggle, Range, Toggle, DeathLink, Choice, PerGameCommonOptions, OptionSet
+from .Items import action_item_table
class EnableCoinStars(DefaultOnToggle):
- """Disable to Ignore 100 Coin Stars. You can still collect them, but they don't do anything"""
+ """Disable to Ignore 100 Coin Stars. You can still collect them, but they don't do anything.
+ Removes 15 locations from the pool."""
display_name = "Enable 100 Coin Stars"
@@ -13,56 +15,63 @@ class StrictCapRequirements(DefaultOnToggle):
class StrictCannonRequirements(DefaultOnToggle):
- """If disabled, Stars that expect cannons may have to be acquired without them. Only makes a difference if Buddy
- Checks are enabled"""
+ """If disabled, Stars that expect cannons may have to be acquired without them.
+ Has no effect if Buddy Checks and Move Randomizer are disabled"""
display_name = "Strict Cannon Requirements"
class FirstBowserStarDoorCost(Range):
- """How many stars are required at the Star Door to Bowser in the Dark World"""
+ """What percent of the total stars are required at the Star Door to Bowser in the Dark World"""
+ display_name = "First Star Door Cost %"
range_start = 0
- range_end = 50
- default = 8
+ range_end = 40
+ default = 7
class BasementStarDoorCost(Range):
- """How many stars are required at the Star Door in the Basement"""
+ """What percent of the total stars are required at the Star Door in the Basement"""
+ display_name = "Basement Star Door %"
range_start = 0
- range_end = 70
- default = 30
+ range_end = 50
+ default = 25
class SecondFloorStarDoorCost(Range):
- """How many stars are required to access the third floor"""
+ """What percent of the total stars are required to access the third floor"""
+ display_name = 'Second Floor Star Door %'
range_start = 0
- range_end = 90
- default = 50
+ range_end = 70
+ default = 42
class MIPS1Cost(Range):
- """How many stars are required to spawn MIPS the first time"""
+ """What percent of the total stars are required to spawn MIPS the first time"""
+ display_name = "MIPS 1 Star %"
range_start = 0
- range_end = 40
- default = 15
+ range_end = 35
+ default = 12
class MIPS2Cost(Range):
- """How many stars are required to spawn MIPS the second time."""
+ """What percent of the total stars are required to spawn MIPS the second time."""
+ display_name = "MIPS 2 Star %"
range_start = 0
- range_end = 80
- default = 50
+ range_end = 70
+ default = 42
class StarsToFinish(Range):
- """How many stars are required at the infinite stairs"""
- display_name = "Endless Stairs Stars"
+ """What percent of the total stars are required at the infinite stairs"""
+ display_name = "Endless Stairs Star %"
range_start = 0
- range_end = 100
- default = 70
+ range_end = 90
+ default = 58
class AmountOfStars(Range):
- """How many stars exist. Disabling 100 Coin Stars removes 15 from the Pool. At least max of any Cost set"""
+ """How many stars exist.
+ If there aren't enough locations to hold the given total, the total will be reduced."""
+ display_name = "Total Power Stars"
range_start = 35
range_end = 120
default = 120
@@ -83,11 +92,13 @@ class BuddyChecks(Toggle):
class ExclamationBoxes(Choice):
- """Include 1Up Exclamation Boxes during randomization"""
+ """Include 1Up Exclamation Boxes during randomization.
+ Adds 29 locations to the pool."""
display_name = "Randomize 1Up !-Blocks"
option_Off = 0
option_1Ups_Only = 1
+
class CompletionType(Choice):
"""Set goal for game completion"""
display_name = "Completion Goal"
@@ -96,25 +107,45 @@ class CompletionType(Choice):
class ProgressiveKeys(DefaultOnToggle):
- """Keys will first grant you access to the Basement, then to the Secound Floor"""
+ """Keys will first grant you access to the Basement, then to the Second Floor"""
display_name = "Progressive Keys"
-
-sm64_options: typing.Dict[str, type(Option)] = {
- "AreaRandomizer": AreaRandomizer,
- "ProgressiveKeys": ProgressiveKeys,
- "EnableCoinStars": EnableCoinStars,
- "AmountOfStars": AmountOfStars,
- "StrictCapRequirements": StrictCapRequirements,
- "StrictCannonRequirements": StrictCannonRequirements,
- "FirstBowserStarDoorCost": FirstBowserStarDoorCost,
- "BasementStarDoorCost": BasementStarDoorCost,
- "SecondFloorStarDoorCost": SecondFloorStarDoorCost,
- "MIPS1Cost": MIPS1Cost,
- "MIPS2Cost": MIPS2Cost,
- "StarsToFinish": StarsToFinish,
- "death_link": DeathLink,
- "BuddyChecks": BuddyChecks,
- "ExclamationBoxes": ExclamationBoxes,
- "CompletionType" : CompletionType,
-}
+class StrictMoveRequirements(DefaultOnToggle):
+ """If disabled, Stars that expect certain moves may have to be acquired without them. Only makes a difference
+ if Move Randomization is enabled"""
+ display_name = "Strict Move Requirements"
+
+class EnableMoveRandomizer(Toggle):
+ """Mario is unable to perform some actions until a corresponding item is picked up.
+ This option is incompatible with builds using a 'nomoverando' branch.
+ Specific actions to randomize can be specified in the YAML."""
+ display_name = "Enable Move Randomizer"
+
+class MoveRandomizerActions(OptionSet):
+ """Which actions to randomize when Move Randomizer is enabled"""
+ display_name = "Randomized Moves"
+ # HACK: Disable randomization for double jump
+ valid_keys = [action for action in action_item_table if action != 'Double Jump']
+ default = valid_keys
+
+@dataclass
+class SM64Options(PerGameCommonOptions):
+ area_rando: AreaRandomizer
+ buddy_checks: BuddyChecks
+ exclamation_boxes: ExclamationBoxes
+ progressive_keys: ProgressiveKeys
+ enable_coin_stars: EnableCoinStars
+ enable_move_rando: EnableMoveRandomizer
+ move_rando_actions: MoveRandomizerActions
+ strict_cap_requirements: StrictCapRequirements
+ strict_cannon_requirements: StrictCannonRequirements
+ strict_move_requirements: StrictMoveRequirements
+ amount_of_stars: AmountOfStars
+ first_bowser_star_door_cost: FirstBowserStarDoorCost
+ basement_star_door_cost: BasementStarDoorCost
+ second_floor_star_door_cost: SecondFloorStarDoorCost
+ mips1_cost: MIPS1Cost
+ mips2_cost: MIPS2Cost
+ stars_to_finish: StarsToFinish
+ death_link: DeathLink
+ completion_type: CompletionType
diff --git a/worlds/sm64ex/Regions.py b/worlds/sm64ex/Regions.py
index d426804c30f3..333e2df3a97f 100644
--- a/worlds/sm64ex/Regions.py
+++ b/worlds/sm64ex/Regions.py
@@ -1,14 +1,15 @@
import typing
-
from enum import Enum
from BaseClasses import MultiWorld, Region, Entrance, Location
+from .Options import SM64Options
from .Locations import SM64Location, location_table, locBoB_table, locWhomp_table, locJRB_table, locCCM_table, \
locBBH_table, \
locHMC_table, locLLL_table, locSSL_table, locDDD_table, locSL_table, \
locWDW_table, locTTM_table, locTHI_table, locTTC_table, locRR_table, \
locPSS_table, locSA_table, locBitDW_table, locTotWC_table, locCotMC_table, \
- locVCutM_table, locBitFS_table, locWMotR_table, locBitS_table, locSS_table
+ locVCutM_table, locBitFS_table, locWMotR_table, locBitS_table, locSS_table
+
class SM64Levels(int, Enum):
BOB_OMB_BATTLEFIELD = 91
@@ -36,6 +37,11 @@ class SM64Levels(int, Enum):
BOWSER_IN_THE_FIRE_SEA = 191
WING_MARIO_OVER_THE_RAINBOW = 311
+
+class SM64Region(Region):
+ subregions: typing.List[Region] = []
+
+
# sm64paintings is a dict of entrances, format LEVEL | AREA
sm64_level_to_paintings: typing.Dict[SM64Levels, str] = {
SM64Levels.BOB_OMB_BATTLEFIELD: "Bob-omb Battlefield",
@@ -55,7 +61,7 @@ class SM64Levels(int, Enum):
SM64Levels.TICK_TOCK_CLOCK: "Tick Tock Clock",
SM64Levels.RAINBOW_RIDE: "Rainbow Ride"
}
-sm64_paintings_to_level = { painting: level for (level,painting) in sm64_level_to_paintings.items() }
+sm64_paintings_to_level = {painting: level for (level, painting) in sm64_level_to_paintings.items() }
# sm64secrets is a dict of secret areas, same format as sm64paintings
sm64_level_to_secrets: typing.Dict[SM64Levels, str] = {
@@ -68,168 +74,209 @@ class SM64Levels(int, Enum):
SM64Levels.BOWSER_IN_THE_FIRE_SEA: "Bowser in the Fire Sea",
SM64Levels.WING_MARIO_OVER_THE_RAINBOW: "Wing Mario over the Rainbow"
}
-sm64_secrets_to_level = { secret: level for (level,secret) in sm64_level_to_secrets.items() }
+sm64_secrets_to_level = {secret: level for (level,secret) in sm64_level_to_secrets.items() }
-sm64_entrances_to_level = { **sm64_paintings_to_level, **sm64_secrets_to_level }
-sm64_level_to_entrances = { **sm64_level_to_paintings, **sm64_level_to_secrets }
+sm64_entrances_to_level = {**sm64_paintings_to_level, **sm64_secrets_to_level }
+sm64_level_to_entrances = {**sm64_level_to_paintings, **sm64_level_to_secrets }
-def create_regions(world: MultiWorld, player: int):
+def create_regions(world: MultiWorld, options: SM64Options, player: int):
regSS = Region("Menu", player, world, "Castle Area")
- create_default_locs(regSS, locSS_table, player)
+ create_default_locs(regSS, locSS_table)
world.regions.append(regSS)
regBoB = create_region("Bob-omb Battlefield", player, world)
- create_default_locs(regBoB, locBoB_table, player)
- if (world.EnableCoinStars[player].value):
- regBoB.locations.append(SM64Location(player, "BoB: 100 Coins", location_table["BoB: 100 Coins"], regBoB))
- world.regions.append(regBoB)
+ create_locs(regBoB, "BoB: Big Bob-Omb on the Summit", "BoB: Footrace with Koopa The Quick",
+ "BoB: Mario Wings to the Sky", "BoB: Behind Chain Chomp's Gate", "BoB: Bob-omb Buddy")
+ bob_island = create_subregion(regBoB, "BoB: Island", "BoB: Shoot to the Island in the Sky", "BoB: Find the 8 Red Coins")
+ regBoB.subregions = [bob_island]
+ if options.enable_coin_stars:
+ create_locs(regBoB, "BoB: 100 Coins")
regWhomp = create_region("Whomp's Fortress", player, world)
- create_default_locs(regWhomp, locWhomp_table, player)
- if (world.EnableCoinStars[player].value):
- regWhomp.locations.append(SM64Location(player, "WF: 100 Coins", location_table["WF: 100 Coins"], regWhomp))
- world.regions.append(regWhomp)
+ create_locs(regWhomp, "WF: Chip Off Whomp's Block", "WF: Shoot into the Wild Blue", "WF: Red Coins on the Floating Isle",
+ "WF: Fall onto the Caged Island", "WF: Blast Away the Wall")
+ wf_tower = create_subregion(regWhomp, "WF: Tower", "WF: To the Top of the Fortress", "WF: Bob-omb Buddy")
+ regWhomp.subregions = [wf_tower]
+ if options.enable_coin_stars:
+ create_locs(regWhomp, "WF: 100 Coins")
regJRB = create_region("Jolly Roger Bay", player, world)
- create_default_locs(regJRB, locJRB_table, player)
- if (world.EnableCoinStars[player].value):
- regJRB.locations.append(SM64Location(player, "JRB: 100 Coins", location_table["JRB: 100 Coins"], regJRB))
- world.regions.append(regJRB)
+ create_locs(regJRB, "JRB: Plunder in the Sunken Ship", "JRB: Can the Eel Come Out to Play?", "JRB: Treasure of the Ocean Cave",
+ "JRB: Blast to the Stone Pillar", "JRB: Through the Jet Stream", "JRB: Bob-omb Buddy")
+ jrb_upper = create_subregion(regJRB, 'JRB: Upper', "JRB: Red Coins on the Ship Afloat")
+ regJRB.subregions = [jrb_upper]
+ if options.enable_coin_stars:
+ create_locs(jrb_upper, "JRB: 100 Coins")
regCCM = create_region("Cool, Cool Mountain", player, world)
- create_default_locs(regCCM, locCCM_table, player)
- if (world.EnableCoinStars[player].value):
- regCCM.locations.append(SM64Location(player, "CCM: 100 Coins", location_table["CCM: 100 Coins"], regCCM))
- world.regions.append(regCCM)
+ create_default_locs(regCCM, locCCM_table)
+ if options.enable_coin_stars:
+ create_locs(regCCM, "CCM: 100 Coins")
regBBH = create_region("Big Boo's Haunt", player, world)
- create_default_locs(regBBH, locBBH_table, player)
- if (world.EnableCoinStars[player].value):
- regBBH.locations.append(SM64Location(player, "BBH: 100 Coins", location_table["BBH: 100 Coins"], regBBH))
- world.regions.append(regBBH)
+ create_locs(regBBH, "BBH: Go on a Ghost Hunt", "BBH: Ride Big Boo's Merry-Go-Round",
+ "BBH: Secret of the Haunted Books", "BBH: Seek the 8 Red Coins")
+ bbh_third_floor = create_subregion(regBBH, "BBH: Third Floor", "BBH: Eye to Eye in the Secret Room")
+ bbh_roof = create_subregion(bbh_third_floor, "BBH: Roof", "BBH: Big Boo's Balcony", "BBH: 1Up Block Top of Mansion")
+ regBBH.subregions = [bbh_third_floor, bbh_roof]
+ if options.enable_coin_stars:
+ create_locs(regBBH, "BBH: 100 Coins")
regPSS = create_region("The Princess's Secret Slide", player, world)
- create_default_locs(regPSS, locPSS_table, player)
- world.regions.append(regPSS)
+ create_default_locs(regPSS, locPSS_table)
regSA = create_region("The Secret Aquarium", player, world)
- create_default_locs(regSA, locSA_table, player)
- world.regions.append(regSA)
+ create_default_locs(regSA, locSA_table)
regTotWC = create_region("Tower of the Wing Cap", player, world)
- create_default_locs(regTotWC, locTotWC_table, player)
- world.regions.append(regTotWC)
+ create_default_locs(regTotWC, locTotWC_table)
regBitDW = create_region("Bowser in the Dark World", player, world)
- create_default_locs(regBitDW, locBitDW_table, player)
- world.regions.append(regBitDW)
+ create_default_locs(regBitDW, locBitDW_table)
- regBasement = create_region("Basement", player, world)
- world.regions.append(regBasement)
+ create_region("Basement", player, world)
regHMC = create_region("Hazy Maze Cave", player, world)
- create_default_locs(regHMC, locHMC_table, player)
- if (world.EnableCoinStars[player].value):
- regHMC.locations.append(SM64Location(player, "HMC: 100 Coins", location_table["HMC: 100 Coins"], regHMC))
- world.regions.append(regHMC)
+ create_locs(regHMC, "HMC: Swimming Beast in the Cavern", "HMC: Metal-Head Mario Can Move!",
+ "HMC: Watch for Rolling Rocks", "HMC: Navigating the Toxic Maze","HMC: 1Up Block Past Rolling Rocks")
+ hmc_red_coin_area = create_subregion(regHMC, "HMC: Red Coin Area", "HMC: Elevate for 8 Red Coins")
+ hmc_pit_islands = create_subregion(regHMC, "HMC: Pit Islands", "HMC: A-Maze-Ing Emergency Exit", "HMC: 1Up Block above Pit")
+ regHMC.subregions = [hmc_red_coin_area, hmc_pit_islands]
+ if options.enable_coin_stars:
+ create_locs(hmc_red_coin_area, "HMC: 100 Coins")
regLLL = create_region("Lethal Lava Land", player, world)
- create_default_locs(regLLL, locLLL_table, player)
- if (world.EnableCoinStars[player].value):
- regLLL.locations.append(SM64Location(player, "LLL: 100 Coins", location_table["LLL: 100 Coins"], regLLL))
- world.regions.append(regLLL)
+ create_locs(regLLL, "LLL: Boil the Big Bully", "LLL: Bully the Bullies",
+ "LLL: 8-Coin Puzzle with 15 Pieces", "LLL: Red-Hot Log Rolling")
+ lll_upper_volcano = create_subregion(regLLL, "LLL: Upper Volcano", "LLL: Hot-Foot-It into the Volcano", "LLL: Elevator Tour in the Volcano")
+ regLLL.subregions = [lll_upper_volcano]
+ if options.enable_coin_stars:
+ create_locs(regLLL, "LLL: 100 Coins")
regSSL = create_region("Shifting Sand Land", player, world)
- create_default_locs(regSSL, locSSL_table, player)
- if (world.EnableCoinStars[player].value):
- regSSL.locations.append(SM64Location(player, "SSL: 100 Coins", location_table["SSL: 100 Coins"], regSSL))
- world.regions.append(regSSL)
+ create_locs(regSSL, "SSL: In the Talons of the Big Bird", "SSL: Shining Atop the Pyramid",
+ "SSL: Free Flying for 8 Red Coins", "SSL: Bob-omb Buddy",
+ "SSL: 1Up Block Outside Pyramid", "SSL: 1Up Block Pyramid Left Path", "SSL: 1Up Block Pyramid Back")
+ ssl_upper_pyramid = create_subregion(regSSL, "SSL: Upper Pyramid", "SSL: Inside the Ancient Pyramid",
+ "SSL: Stand Tall on the Four Pillars", "SSL: Pyramid Puzzle")
+ regSSL.subregions = [ssl_upper_pyramid]
+ if options.enable_coin_stars:
+ create_locs(regSSL, "SSL: 100 Coins")
regDDD = create_region("Dire, Dire Docks", player, world)
- create_default_locs(regDDD, locDDD_table, player)
- if (world.EnableCoinStars[player].value):
- regDDD.locations.append(SM64Location(player, "DDD: 100 Coins", location_table["DDD: 100 Coins"], regDDD))
- world.regions.append(regDDD)
+ create_locs(regDDD, "DDD: Board Bowser's Sub", "DDD: Chests in the Current", "DDD: Through the Jet Stream",
+ "DDD: The Manta Ray's Reward", "DDD: Collect the Caps...")
+ ddd_moving_poles = create_subregion(regDDD, "DDD: Moving Poles", "DDD: Pole-Jumping for Red Coins")
+ regDDD.subregions = [ddd_moving_poles]
+ if options.enable_coin_stars:
+ create_locs(ddd_moving_poles, "DDD: 100 Coins")
regCotMC = create_region("Cavern of the Metal Cap", player, world)
- create_default_locs(regCotMC, locCotMC_table, player)
- world.regions.append(regCotMC)
+ create_default_locs(regCotMC, locCotMC_table)
regVCutM = create_region("Vanish Cap under the Moat", player, world)
- create_default_locs(regVCutM, locVCutM_table, player)
- world.regions.append(regVCutM)
+ create_default_locs(regVCutM, locVCutM_table)
regBitFS = create_region("Bowser in the Fire Sea", player, world)
- create_default_locs(regBitFS, locBitFS_table, player)
- world.regions.append(regBitFS)
+ bitfs_upper = create_subregion(regBitFS, "BitFS: Upper", *locBitFS_table.keys())
+ regBitFS.subregions = [bitfs_upper]
- regFloor2 = create_region("Second Floor", player, world)
- world.regions.append(regFloor2)
+ create_region("Second Floor", player, world)
regSL = create_region("Snowman's Land", player, world)
- create_default_locs(regSL, locSL_table, player)
- if (world.EnableCoinStars[player].value):
- regSL.locations.append(SM64Location(player, "SL: 100 Coins", location_table["SL: 100 Coins"], regSL))
- world.regions.append(regSL)
+ create_default_locs(regSL, locSL_table)
+ if options.enable_coin_stars:
+ create_locs(regSL, "SL: 100 Coins")
regWDW = create_region("Wet-Dry World", player, world)
- create_default_locs(regWDW, locWDW_table, player)
- if (world.EnableCoinStars[player].value):
- regWDW.locations.append(SM64Location(player, "WDW: 100 Coins", location_table["WDW: 100 Coins"], regWDW))
- world.regions.append(regWDW)
+ create_locs(regWDW, "WDW: Express Elevator--Hurry Up!")
+ wdw_top = create_subregion(regWDW, "WDW: Top", "WDW: Shocking Arrow Lifts!", "WDW: Top o' the Town",
+ "WDW: Secrets in the Shallows & Sky", "WDW: Bob-omb Buddy")
+ wdw_downtown = create_subregion(regWDW, "WDW: Downtown", "WDW: Go to Town for Red Coins", "WDW: Quick Race Through Downtown!", "WDW: 1Up Block in Downtown")
+ regWDW.subregions = [wdw_top, wdw_downtown]
+ if options.enable_coin_stars:
+ create_locs(wdw_top, "WDW: 100 Coins")
regTTM = create_region("Tall, Tall Mountain", player, world)
- create_default_locs(regTTM, locTTM_table, player)
- if (world.EnableCoinStars[player].value):
- regTTM.locations.append(SM64Location(player, "TTM: 100 Coins", location_table["TTM: 100 Coins"], regTTM))
- world.regions.append(regTTM)
-
- regTHIT = create_region("Tiny-Huge Island (Tiny)", player, world)
- create_default_locs(regTHIT, locTHI_table, player)
- if (world.EnableCoinStars[player].value):
- regTHIT.locations.append(SM64Location(player, "THI: 100 Coins", location_table["THI: 100 Coins"], regTHIT))
- world.regions.append(regTHIT)
- regTHIH = create_region("Tiny-Huge Island (Huge)", player, world)
- world.regions.append(regTHIH)
+ ttm_middle = create_subregion(regTTM, "TTM: Middle", "TTM: Scary 'Shrooms, Red Coins", "TTM: Blast to the Lonely Mushroom",
+ "TTM: Bob-omb Buddy", "TTM: 1Up Block on Red Mushroom")
+ ttm_top = create_subregion(ttm_middle, "TTM: Top", "TTM: Scale the Mountain", "TTM: Mystery of the Monkey Cage",
+ "TTM: Mysterious Mountainside", "TTM: Breathtaking View from Bridge")
+ regTTM.subregions = [ttm_middle, ttm_top]
+ if options.enable_coin_stars:
+ create_locs(ttm_top, "TTM: 100 Coins")
+
+ create_region("Tiny-Huge Island (Huge)", player, world)
+ create_region("Tiny-Huge Island (Tiny)", player, world)
+ regTHI = create_region("Tiny-Huge Island", player, world)
+ create_locs(regTHI, "THI: 1Up Block THI Small near Start")
+ thi_pipes = create_subregion(regTHI, "THI: Pipes", "THI: The Tip Top of the Huge Island", "THI: Pluck the Piranha Flower", "THI: Rematch with Koopa the Quick",
+ "THI: Five Itty Bitty Secrets", "THI: Wiggler's Red Coins", "THI: Bob-omb Buddy",
+ "THI: 1Up Block THI Large near Start", "THI: 1Up Block Windy Area")
+ thi_large_top = create_subregion(thi_pipes, "THI: Large Top", "THI: Make Wiggler Squirm")
+ regTHI.subregions = [thi_pipes, thi_large_top]
+ if options.enable_coin_stars:
+ create_locs(thi_large_top, "THI: 100 Coins")
regFloor3 = create_region("Third Floor", player, world)
- world.regions.append(regFloor3)
regTTC = create_region("Tick Tock Clock", player, world)
- create_default_locs(regTTC, locTTC_table, player)
- if (world.EnableCoinStars[player].value):
- regTTC.locations.append(SM64Location(player, "TTC: 100 Coins", location_table["TTC: 100 Coins"], regTTC))
- world.regions.append(regTTC)
+ create_locs(regTTC, "TTC: Stop Time for Red Coins")
+ ttc_lower = create_subregion(regTTC, "TTC: Lower", "TTC: Roll into the Cage", "TTC: Get a Hand", "TTC: 1Up Block Midway Up")
+ ttc_upper = create_subregion(ttc_lower, "TTC: Upper", "TTC: Timed Jumps on Moving Bars", "TTC: The Pit and the Pendulums")
+ ttc_top = create_subregion(ttc_upper, "TTC: Top", "TTC: Stomp on the Thwomp", "TTC: 1Up Block at the Top")
+ regTTC.subregions = [ttc_lower, ttc_upper, ttc_top]
+ if options.enable_coin_stars:
+ create_locs(ttc_top, "TTC: 100 Coins")
regRR = create_region("Rainbow Ride", player, world)
- create_default_locs(regRR, locRR_table, player)
- if (world.EnableCoinStars[player].value):
- regRR.locations.append(SM64Location(player, "RR: 100 Coins", location_table["RR: 100 Coins"], regRR))
- world.regions.append(regRR)
+ create_locs(regRR, "RR: Swingin' in the Breeze", "RR: Tricky Triangles!",
+ "RR: 1Up Block Top of Red Coin Maze", "RR: 1Up Block Under Fly Guy", "RR: Bob-omb Buddy")
+ rr_maze = create_subregion(regRR, "RR: Maze", "RR: Coins Amassed in a Maze")
+ rr_cruiser = create_subregion(regRR, "RR: Cruiser", "RR: Cruiser Crossing the Rainbow", "RR: Somewhere Over the Rainbow")
+ rr_house = create_subregion(regRR, "RR: House", "RR: The Big House in the Sky", "RR: 1Up Block On House in the Sky")
+ regRR.subregions = [rr_maze, rr_cruiser, rr_house]
+ if options.enable_coin_stars:
+ create_locs(rr_maze, "RR: 100 Coins")
regWMotR = create_region("Wing Mario over the Rainbow", player, world)
- create_default_locs(regWMotR, locWMotR_table, player)
- world.regions.append(regWMotR)
+ create_default_locs(regWMotR, locWMotR_table)
regBitS = create_region("Bowser in the Sky", player, world)
- create_default_locs(regBitS, locBitS_table, player)
- world.regions.append(regBitS)
+ create_locs(regBitS, "Bowser in the Sky 1Up Block")
+ bits_top = create_subregion(regBitS, "BitS: Top", "Bowser in the Sky Red Coins")
+ regBitS.subregions = [bits_top]
def connect_regions(world: MultiWorld, player: int, source: str, target: str, rule=None):
sourceRegion = world.get_region(source, player)
targetRegion = world.get_region(target, player)
+ sourceRegion.connect(targetRegion, rule=rule)
+
+
+def create_region(name: str, player: int, world: MultiWorld) -> SM64Region:
+ region = SM64Region(name, player, world)
+ world.regions.append(region)
+ return region
+
+
+def create_subregion(source_region: Region, name: str, *locs: str) -> SM64Region:
+ region = SM64Region(name, source_region.player, source_region.multiworld)
+ connection = Entrance(source_region.player, name, source_region)
+ source_region.exits.append(connection)
+ connection.connect(region)
+ source_region.multiworld.regions.append(region)
+ create_locs(region, *locs)
+ return region
+
+
+def set_subregion_access_rule(world, player, region_name: str, rule):
+ world.get_entrance(world, player, region_name).access_rule = rule
- connection = Entrance(player, '', sourceRegion)
- if rule:
- connection.access_rule = rule
- sourceRegion.exits.append(connection)
- connection.connect(targetRegion)
+def create_default_locs(reg: Region, default_locs: dict):
+ create_locs(reg, *default_locs.keys())
-def create_region(name: str, player: int, world: MultiWorld) -> Region:
- return Region(name, player, world)
-def create_default_locs(reg: Region, locs, player):
- reg_names = [name for name, id in locs.items()]
- reg.locations += [SM64Location(player, loc_name, location_table[loc_name], reg) for loc_name in locs]
+def create_locs(reg: Region, *locs: str):
+ reg.locations += [SM64Location(reg.player, loc_name, location_table[loc_name], reg) for loc_name in locs]
diff --git a/worlds/sm64ex/Rules.py b/worlds/sm64ex/Rules.py
index fedd5b7a6ebd..72016b4f4014 100644
--- a/worlds/sm64ex/Rules.py
+++ b/worlds/sm64ex/Rules.py
@@ -1,36 +1,60 @@
-from ..generic.Rules import add_rule
-from .Regions import connect_regions, SM64Levels, sm64_level_to_paintings, sm64_paintings_to_level, sm64_level_to_secrets, sm64_secrets_to_level, sm64_entrances_to_level, sm64_level_to_entrances
+from typing import Callable, Union, Dict, Set
-def shuffle_dict_keys(world, obj: dict) -> dict:
- keys = list(obj.keys())
- values = list(obj.values())
+from BaseClasses import MultiWorld
+from ..generic.Rules import add_rule, set_rule
+from .Locations import location_table
+from .Options import SM64Options
+from .Regions import connect_regions, SM64Levels, sm64_level_to_paintings, sm64_paintings_to_level,\
+sm64_level_to_secrets, sm64_secrets_to_level, sm64_entrances_to_level, sm64_level_to_entrances
+from .Items import action_item_table
+
+def shuffle_dict_keys(world, dictionary: dict) -> dict:
+ keys = list(dictionary.keys())
+ values = list(dictionary.values())
world.random.shuffle(keys)
- return dict(zip(keys,values))
+ return dict(zip(keys, values))
-def fix_reg(entrance_map: dict, entrance: SM64Levels, invalid_regions: set,
- swapdict: dict, world):
+def fix_reg(entrance_map: Dict[SM64Levels, str], entrance: SM64Levels, invalid_regions: Set[str],
+ swapdict: Dict[SM64Levels, str], world):
if entrance_map[entrance] in invalid_regions: # Unlucky :C
- replacement_regions = [(rand_region, rand_entrance) for rand_region, rand_entrance in swapdict.items()
+ replacement_regions = [(rand_entrance, rand_region) for rand_entrance, rand_region in swapdict.items()
if rand_region not in invalid_regions]
- rand_region, rand_entrance = world.random.choice(replacement_regions)
+ rand_entrance, rand_region = world.random.choice(replacement_regions)
old_dest = entrance_map[entrance]
entrance_map[entrance], entrance_map[rand_entrance] = rand_region, old_dest
- swapdict[rand_region] = entrance
- swapdict.pop(entrance_map[entrance]) # Entrance now fixed to rand_region
+ swapdict[entrance], swapdict[rand_entrance] = rand_region, old_dest
+ swapdict.pop(entrance)
-def set_rules(world, player: int, area_connections: dict):
+def set_rules(world, options: SM64Options, player: int, area_connections: dict, star_costs: dict, move_rando_bitvec: int):
randomized_level_to_paintings = sm64_level_to_paintings.copy()
randomized_level_to_secrets = sm64_level_to_secrets.copy()
- if world.AreaRandomizer[player].value >= 1: # Some randomization is happening, randomize Courses
+ valid_move_randomizer_start_courses = [
+ "Bob-omb Battlefield", "Jolly Roger Bay", "Cool, Cool Mountain",
+ "Big Boo's Haunt", "Lethal Lava Land", "Shifting Sand Land",
+ "Dire, Dire Docks", "Snowman's Land"
+ ] # Excluding WF, HMC, WDW, TTM, THI, TTC, and RR
+ if options.area_rando >= 1: # Some randomization is happening, randomize Courses
randomized_level_to_paintings = shuffle_dict_keys(world,sm64_level_to_paintings)
- if world.AreaRandomizer[player].value == 2: # Randomize Secrets as well
+ # If not shuffling later, ensure a valid start course on move randomizer
+ if options.area_rando < 3 and move_rando_bitvec > 0:
+ swapdict = randomized_level_to_paintings.copy()
+ invalid_start_courses = {course for course in randomized_level_to_paintings.values() if course not in valid_move_randomizer_start_courses}
+ fix_reg(randomized_level_to_paintings, SM64Levels.BOB_OMB_BATTLEFIELD, invalid_start_courses, swapdict, world)
+ fix_reg(randomized_level_to_paintings, SM64Levels.WHOMPS_FORTRESS, invalid_start_courses, swapdict, world)
+
+ if options.area_rando == 2: # Randomize Secrets as well
randomized_level_to_secrets = shuffle_dict_keys(world,sm64_level_to_secrets)
- randomized_entrances = { **randomized_level_to_paintings, **randomized_level_to_secrets }
- if world.AreaRandomizer[player].value == 3: # Randomize Courses and Secrets in one pool
- randomized_entrances = shuffle_dict_keys(world,randomized_entrances)
- swapdict = { entrance: level for (level,entrance) in randomized_entrances.items() }
+ randomized_entrances = {**randomized_level_to_paintings, **randomized_level_to_secrets}
+ if options.area_rando == 3: # Randomize Courses and Secrets in one pool
+ randomized_entrances = shuffle_dict_keys(world, randomized_entrances)
# Guarantee first entrance is a course
- fix_reg(randomized_entrances, SM64Levels.BOB_OMB_BATTLEFIELD, sm64_secrets_to_level.keys(), swapdict, world)
+ swapdict = randomized_entrances.copy()
+ if move_rando_bitvec == 0:
+ fix_reg(randomized_entrances, SM64Levels.BOB_OMB_BATTLEFIELD, sm64_secrets_to_level.keys(), swapdict, world)
+ else:
+ invalid_start_courses = {course for course in randomized_entrances.values() if course not in valid_move_randomizer_start_courses}
+ fix_reg(randomized_entrances, SM64Levels.BOB_OMB_BATTLEFIELD, invalid_start_courses, swapdict, world)
+ fix_reg(randomized_entrances, SM64Levels.WHOMPS_FORTRESS, invalid_start_courses, swapdict, world)
# Guarantee BITFS is not mapped to DDD
fix_reg(randomized_entrances, SM64Levels.BOWSER_IN_THE_FIRE_SEA, {"Dire, Dire Docks"}, swapdict, world)
# Guarantee COTMC is not mapped to HMC, cuz thats impossible. If BitFS -> HMC, also no COTMC -> DDD.
@@ -43,27 +67,34 @@ def set_rules(world, player: int, area_connections: dict):
# Cast to int to not rely on availability of SM64Levels enum. Will cause crash in MultiServer otherwise
area_connections.update({int(entrance_lvl): int(sm64_entrances_to_level[destination]) for (entrance_lvl,destination) in randomized_entrances.items()})
randomized_entrances_s = {sm64_level_to_entrances[entrance_lvl]: destination for (entrance_lvl,destination) in randomized_entrances.items()}
-
+
+ rf = RuleFactory(world, options, player, move_rando_bitvec)
+
connect_regions(world, player, "Menu", randomized_entrances_s["Bob-omb Battlefield"])
connect_regions(world, player, "Menu", randomized_entrances_s["Whomp's Fortress"], lambda state: state.has("Power Star", player, 1))
connect_regions(world, player, "Menu", randomized_entrances_s["Jolly Roger Bay"], lambda state: state.has("Power Star", player, 3))
connect_regions(world, player, "Menu", randomized_entrances_s["Cool, Cool Mountain"], lambda state: state.has("Power Star", player, 3))
connect_regions(world, player, "Menu", randomized_entrances_s["Big Boo's Haunt"], lambda state: state.has("Power Star", player, 12))
connect_regions(world, player, "Menu", randomized_entrances_s["The Princess's Secret Slide"], lambda state: state.has("Power Star", player, 1))
- connect_regions(world, player, "Menu", randomized_entrances_s["The Secret Aquarium"], lambda state: state.has("Power Star", player, 3))
+ connect_regions(world, player, randomized_entrances_s["Jolly Roger Bay"], randomized_entrances_s["The Secret Aquarium"],
+ rf.build_rule("SF/BF | TJ & LG | MOVELESS & TJ"))
connect_regions(world, player, "Menu", randomized_entrances_s["Tower of the Wing Cap"], lambda state: state.has("Power Star", player, 10))
- connect_regions(world, player, "Menu", randomized_entrances_s["Bowser in the Dark World"], lambda state: state.has("Power Star", player, world.FirstBowserStarDoorCost[player].value))
+ connect_regions(world, player, "Menu", randomized_entrances_s["Bowser in the Dark World"],
+ lambda state: state.has("Power Star", player, star_costs["FirstBowserDoorCost"]))
connect_regions(world, player, "Menu", "Basement", lambda state: state.has("Basement Key", player) or state.has("Progressive Key", player, 1))
connect_regions(world, player, "Basement", randomized_entrances_s["Hazy Maze Cave"])
connect_regions(world, player, "Basement", randomized_entrances_s["Lethal Lava Land"])
connect_regions(world, player, "Basement", randomized_entrances_s["Shifting Sand Land"])
- connect_regions(world, player, "Basement", randomized_entrances_s["Dire, Dire Docks"], lambda state: state.has("Power Star", player, world.BasementStarDoorCost[player].value))
+ connect_regions(world, player, "Basement", randomized_entrances_s["Dire, Dire Docks"],
+ lambda state: state.has("Power Star", player, star_costs["BasementDoorCost"]))
connect_regions(world, player, "Hazy Maze Cave", randomized_entrances_s["Cavern of the Metal Cap"])
- connect_regions(world, player, "Basement", randomized_entrances_s["Vanish Cap under the Moat"])
- connect_regions(world, player, "Basement", randomized_entrances_s["Bowser in the Fire Sea"], lambda state: state.has("Power Star", player, world.BasementStarDoorCost[player].value) and
- state.can_reach("DDD: Board Bowser's Sub", 'Location', player))
+ connect_regions(world, player, "Basement", randomized_entrances_s["Vanish Cap under the Moat"],
+ rf.build_rule("GP"))
+ connect_regions(world, player, "Basement", randomized_entrances_s["Bowser in the Fire Sea"],
+ lambda state: state.has("Power Star", player, star_costs["BasementDoorCost"]) and
+ state.can_reach("DDD: Board Bowser's Sub", 'Location', player))
connect_regions(world, player, "Menu", "Second Floor", lambda state: state.has("Second Floor Key", player) or state.has("Progressive Key", player, 2))
@@ -72,70 +103,274 @@ def set_rules(world, player: int, area_connections: dict):
connect_regions(world, player, "Second Floor", randomized_entrances_s["Tall, Tall Mountain"])
connect_regions(world, player, "Second Floor", randomized_entrances_s["Tiny-Huge Island (Tiny)"])
connect_regions(world, player, "Second Floor", randomized_entrances_s["Tiny-Huge Island (Huge)"])
- connect_regions(world, player, "Tiny-Huge Island (Tiny)", "Tiny-Huge Island (Huge)")
- connect_regions(world, player, "Tiny-Huge Island (Huge)", "Tiny-Huge Island (Tiny)")
-
- connect_regions(world, player, "Second Floor", "Third Floor", lambda state: state.has("Power Star", player, world.SecondFloorStarDoorCost[player].value))
-
- connect_regions(world, player, "Third Floor", randomized_entrances_s["Tick Tock Clock"])
- connect_regions(world, player, "Third Floor", randomized_entrances_s["Rainbow Ride"])
- connect_regions(world, player, "Third Floor", randomized_entrances_s["Wing Mario over the Rainbow"])
- connect_regions(world, player, "Third Floor", "Bowser in the Sky", lambda state: state.has("Power Star", player, world.StarsToFinish[player].value))
-
- #Special Rules for some Locations
- add_rule(world.get_location("BoB: Mario Wings to the Sky", player), lambda state: state.has("Cannon Unlock BoB", player))
- add_rule(world.get_location("BBH: Eye to Eye in the Secret Room", player), lambda state: state.has("Vanish Cap", player))
- add_rule(world.get_location("DDD: Collect the Caps...", player), lambda state: state.has("Vanish Cap", player))
- add_rule(world.get_location("DDD: Pole-Jumping for Red Coins", player), lambda state: state.can_reach("Bowser in the Fire Sea", 'Region', player))
- if world.EnableCoinStars[player]:
- add_rule(world.get_location("DDD: 100 Coins", player), lambda state: state.can_reach("Bowser in the Fire Sea", 'Region', player))
- add_rule(world.get_location("SL: Into the Igloo", player), lambda state: state.has("Vanish Cap", player))
- add_rule(world.get_location("WDW: Quick Race Through Downtown!", player), lambda state: state.has("Vanish Cap", player))
- add_rule(world.get_location("RR: Somewhere Over the Rainbow", player), lambda state: state.has("Cannon Unlock RR", player))
-
- if world.AreaRandomizer[player] or world.StrictCannonRequirements[player]:
- # If area rando is on, it may not be possible to modify WDW's starting water level,
- # which would make it impossible to reach downtown area without the cannon.
- add_rule(world.get_location("WDW: Quick Race Through Downtown!", player), lambda state: state.has("Cannon Unlock WDW", player))
- add_rule(world.get_location("WDW: Go to Town for Red Coins", player), lambda state: state.has("Cannon Unlock WDW", player))
- add_rule(world.get_location("WDW: 1Up Block in Downtown", player), lambda state: state.has("Cannon Unlock WDW", player))
-
- if world.StrictCapRequirements[player]:
- add_rule(world.get_location("BoB: Mario Wings to the Sky", player), lambda state: state.has("Wing Cap", player))
- add_rule(world.get_location("HMC: Metal-Head Mario Can Move!", player), lambda state: state.has("Metal Cap", player))
- add_rule(world.get_location("JRB: Through the Jet Stream", player), lambda state: state.has("Metal Cap", player))
- add_rule(world.get_location("SSL: Free Flying for 8 Red Coins", player), lambda state: state.has("Wing Cap", player))
- add_rule(world.get_location("DDD: Through the Jet Stream", player), lambda state: state.has("Metal Cap", player))
- add_rule(world.get_location("DDD: Collect the Caps...", player), lambda state: state.has("Metal Cap", player))
- add_rule(world.get_location("Vanish Cap Under the Moat Red Coins", player), lambda state: state.has("Vanish Cap", player))
- add_rule(world.get_location("Cavern of the Metal Cap Red Coins", player), lambda state: state.has("Metal Cap", player))
- if world.StrictCannonRequirements[player]:
- add_rule(world.get_location("WF: Blast Away the Wall", player), lambda state: state.has("Cannon Unlock WF", player))
- add_rule(world.get_location("JRB: Blast to the Stone Pillar", player), lambda state: state.has("Cannon Unlock JRB", player))
- add_rule(world.get_location("CCM: Wall Kicks Will Work", player), lambda state: state.has("Cannon Unlock CCM", player))
- add_rule(world.get_location("TTM: Blast to the Lonely Mushroom", player), lambda state: state.has("Cannon Unlock TTM", player))
- if world.StrictCapRequirements[player] and world.StrictCannonRequirements[player]:
- # Ability to reach the floating island. Need some of those coins to get 100 coin star as well.
- add_rule(world.get_location("BoB: Find the 8 Red Coins", player), lambda state: state.has("Cannon Unlock BoB", player) or state.has("Wing Cap", player))
- add_rule(world.get_location("BoB: Shoot to the Island in the Sky", player), lambda state: state.has("Cannon Unlock BoB", player) or state.has("Wing Cap", player))
- if world.EnableCoinStars[player]:
- add_rule(world.get_location("BoB: 100 Coins", player), lambda state: state.has("Cannon Unlock BoB", player) or state.has("Wing Cap", player))
-
- #Rules for Secret Stars
- add_rule(world.get_location("Wing Mario Over the Rainbow Red Coins", player), lambda state: state.has("Wing Cap", player))
- add_rule(world.get_location("Wing Mario Over the Rainbow 1Up Block", player), lambda state: state.has("Wing Cap", player))
+ connect_regions(world, player, "Tiny-Huge Island (Tiny)", "Tiny-Huge Island")
+ connect_regions(world, player, "Tiny-Huge Island (Huge)", "Tiny-Huge Island")
+
+ connect_regions(world, player, "Second Floor", "Third Floor", lambda state: state.has("Power Star", player, star_costs["SecondFloorDoorCost"]))
+
+ connect_regions(world, player, "Third Floor", randomized_entrances_s["Tick Tock Clock"], rf.build_rule("LG/TJ/SF/BF/WK"))
+ connect_regions(world, player, "Third Floor", randomized_entrances_s["Rainbow Ride"], rf.build_rule("TJ/SF/BF"))
+ connect_regions(world, player, "Third Floor", randomized_entrances_s["Wing Mario over the Rainbow"], rf.build_rule("TJ/SF/BF"))
+ connect_regions(world, player, "Third Floor", "Bowser in the Sky", lambda state: state.has("Power Star", player, star_costs["StarsToFinish"]))
+
+ # Course Rules
+ # Bob-omb Battlefield
+ rf.assign_rule("BoB: Island", "CANN | CANNLESS & WC & TJ | CAPLESS & CANNLESS & LJ")
+ rf.assign_rule("BoB: Mario Wings to the Sky", "CANN & WC | CAPLESS & CANN")
+ rf.assign_rule("BoB: Behind Chain Chomp's Gate", "GP | MOVELESS")
+ # Whomp's Fortress
+ rf.assign_rule("WF: Tower", "{{WF: Chip Off Whomp's Block}}")
+ rf.assign_rule("WF: Chip Off Whomp's Block", "GP")
+ rf.assign_rule("WF: Shoot into the Wild Blue", "WK & TJ/SF | CANN")
+ rf.assign_rule("WF: Fall onto the Caged Island", "CL & {WF: Tower} | MOVELESS & TJ | MOVELESS & LJ | MOVELESS & CANN")
+ rf.assign_rule("WF: Blast Away the Wall", "CANN | CANNLESS & LG")
+ # Jolly Roger Bay
+ rf.assign_rule("JRB: Upper", "TJ/BF/SF/WK | MOVELESS & LG")
+ rf.assign_rule("JRB: Red Coins on the Ship Afloat", "CL/CANN/TJ/BF/WK")
+ rf.assign_rule("JRB: Blast to the Stone Pillar", "CANN+CL | CANNLESS & MOVELESS | CANN & MOVELESS")
+ rf.assign_rule("JRB: Through the Jet Stream", "MC | CAPLESS")
+ # Cool, Cool Mountain
+ rf.assign_rule("CCM: Wall Kicks Will Work", "TJ/WK & CANN | CANNLESS & TJ/WK | MOVELESS")
+ # Big Boo's Haunt
+ rf.assign_rule("BBH: Third Floor", "WK+LG | MOVELESS & WK")
+ rf.assign_rule("BBH: Roof", "LJ | MOVELESS")
+ rf.assign_rule("BBH: Secret of the Haunted Books", "KK | MOVELESS")
+ rf.assign_rule("BBH: Seek the 8 Red Coins", "BF/WK/TJ/SF")
+ rf.assign_rule("BBH: Eye to Eye in the Secret Room", "VC")
+ # Haze Maze Cave
+ rf.assign_rule("HMC: Red Coin Area", "CL & WK/LG/BF/SF/TJ | MOVELESS & WK")
+ rf.assign_rule("HMC: Pit Islands", "TJ+CL | MOVELESS & WK & TJ/LJ | MOVELESS & WK+SF+LG")
+ rf.assign_rule("HMC: Metal-Head Mario Can Move!", "LJ+MC | CAPLESS & LJ+TJ | CAPLESS & MOVELESS & LJ/TJ/WK")
+ rf.assign_rule("HMC: Navigating the Toxic Maze", "WK/SF/BF/TJ")
+ rf.assign_rule("HMC: Watch for Rolling Rocks", "WK")
+ # Lethal Lava Land
+ rf.assign_rule("LLL: Upper Volcano", "CL")
+ # Shifting Sand Land
+ rf.assign_rule("SSL: Upper Pyramid", "CL & TJ/BF/SF/LG | MOVELESS")
+ rf.assign_rule("SSL: Free Flying for 8 Red Coins", "TJ/SF/BF & TJ+WC | TJ/SF/BF & CAPLESS | MOVELESS & CAPLESS")
+ # Dire, Dire Docks
+ rf.assign_rule("DDD: Moving Poles", "CL & {{Bowser in the Fire Sea Key}} | TJ+DV+LG+WK & MOVELESS")
+ rf.assign_rule("DDD: Through the Jet Stream", "MC | CAPLESS")
+ rf.assign_rule("DDD: Collect the Caps...", "VC+MC | CAPLESS & VC")
+ # Snowman's Land
+ rf.assign_rule("SL: Snowman's Big Head", "BF/SF/CANN/TJ")
+ rf.assign_rule("SL: In the Deep Freeze", "WK/SF/LG/BF/CANN/TJ")
+ rf.assign_rule("SL: Into the Igloo", "VC & TJ/SF/BF/WK/LG | MOVELESS & VC")
+ # Wet-Dry World
+ rf.assign_rule("WDW: Top", "WK/TJ/SF/BF | MOVELESS")
+ rf.assign_rule("WDW: Downtown", "NAR & LG & TJ/SF/BF | CANN | MOVELESS & TJ+DV")
+ rf.assign_rule("WDW: Go to Town for Red Coins", "WK | MOVELESS & TJ")
+ rf.assign_rule("WDW: Quick Race Through Downtown!", "VC & WK/BF | VC & TJ+LG | MOVELESS & VC & TJ")
+ rf.assign_rule("WDW: Bob-omb Buddy", "TJ | SF+LG | NAR & BF/SF")
+ # Tall, Tall Mountain
+ rf.assign_rule("TTM: Top", "MOVELESS & TJ | LJ/DV & LG/KK | MOVELESS & WK & SF/LG | MOVELESS & KK/DV")
+ rf.assign_rule("TTM: Blast to the Lonely Mushroom", "CANN | CANNLESS & LJ | MOVELESS & CANNLESS")
+ # Tiny-Huge Island
+ rf.assign_rule("THI: 1Up Block THI Small near Start", "NAR | {THI: Pipes}")
+ rf.assign_rule("THI: Pipes", "NAR | LJ/TJ/DV/LG | MOVELESS & BF/SF/KK")
+ rf.assign_rule("THI: Large Top", "NAR | LJ/TJ/DV | MOVELESS")
+ rf.assign_rule("THI: Wiggler's Red Coins", "WK")
+ rf.assign_rule("THI: Make Wiggler Squirm", "GP | MOVELESS & DV")
+ # Tick Tock Clock
+ rf.assign_rule("TTC: Lower", "LG/TJ/SF/BF/WK")
+ rf.assign_rule("TTC: Upper", "CL | SF+WK")
+ rf.assign_rule("TTC: Top", "CL | SF+WK")
+ rf.assign_rule("TTC: Stomp on the Thwomp", "LG & TJ/SF/BF")
+ rf.assign_rule("TTC: Stop Time for Red Coins", "NAR | {TTC: Lower}")
+ # Rainbow Ride
+ rf.assign_rule("RR: Maze", "WK | LJ & SF/BF/TJ | MOVELESS & LG/TJ")
+ rf.assign_rule("RR: Bob-omb Buddy", "WK | MOVELESS & LG")
+ rf.assign_rule("RR: Swingin' in the Breeze", "LG/TJ/BF/SF")
+ rf.assign_rule("RR: Tricky Triangles!", "LG/TJ/BF/SF")
+ rf.assign_rule("RR: Cruiser", "WK/SF/BF/LG/TJ")
+ rf.assign_rule("RR: House", "TJ/SF/BF/LG")
+ rf.assign_rule("RR: Somewhere Over the Rainbow", "CANN")
+ # Cavern of the Metal Cap
+ rf.assign_rule("Cavern of the Metal Cap Red Coins", "MC | CAPLESS")
+ # Vanish Cap Under the Moat
+ rf.assign_rule("Vanish Cap Under the Moat Switch", "WK/TJ/BF/SF/LG | MOVELESS")
+ rf.assign_rule("Vanish Cap Under the Moat Red Coins", "TJ/BF/SF/LG/WK & VC | CAPLESS & WK")
+ # Bowser in the Fire Sea
+ rf.assign_rule("BitFS: Upper", "CL")
+ rf.assign_rule("Bowser in the Fire Sea Red Coins", "LG/WK")
+ rf.assign_rule("Bowser in the Fire Sea 1Up Block Near Poles", "LG/WK")
+ # Wing Mario Over the Rainbow
+ rf.assign_rule("Wing Mario Over the Rainbow Red Coins", "TJ+WC")
+ rf.assign_rule("Wing Mario Over the Rainbow 1Up Block", "TJ+WC")
+ # Bowser in the Sky
+ rf.assign_rule("BitS: Top", "CL+TJ | CL+SF+LG | MOVELESS & TJ+WK+LG")
+ # 100 Coin Stars
+ if options.enable_coin_stars:
+ rf.assign_rule("BoB: 100 Coins", "CANN & WC | CANNLESS & WC & TJ")
+ rf.assign_rule("WF: 100 Coins", "GP | MOVELESS")
+ rf.assign_rule("JRB: 100 Coins", "GP & {JRB: Upper}")
+ rf.assign_rule("HMC: 100 Coins", "GP")
+ rf.assign_rule("SSL: 100 Coins", "{SSL: Upper Pyramid} | GP")
+ rf.assign_rule("DDD: 100 Coins", "GP")
+ rf.assign_rule("SL: 100 Coins", "VC | MOVELESS")
+ rf.assign_rule("WDW: 100 Coins", "GP | {WDW: Downtown}")
+ rf.assign_rule("TTC: 100 Coins", "GP")
+ rf.assign_rule("THI: 100 Coins", "GP")
+ rf.assign_rule("RR: 100 Coins", "GP & WK")
+ # Castle Stars
add_rule(world.get_location("Toad (Basement)", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, 12))
add_rule(world.get_location("Toad (Second Floor)", player), lambda state: state.can_reach("Second Floor", 'Region', player) and state.has("Power Star", player, 25))
add_rule(world.get_location("Toad (Third Floor)", player), lambda state: state.can_reach("Third Floor", 'Region', player) and state.has("Power Star", player, 35))
- if world.MIPS1Cost[player].value > world.MIPS2Cost[player].value:
- (world.MIPS2Cost[player].value, world.MIPS1Cost[player].value) = (world.MIPS1Cost[player].value, world.MIPS2Cost[player].value)
- add_rule(world.get_location("MIPS 1", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, world.MIPS1Cost[player].value))
- add_rule(world.get_location("MIPS 2", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, world.MIPS2Cost[player].value))
+ if star_costs["MIPS1Cost"] > star_costs["MIPS2Cost"]:
+ (star_costs["MIPS2Cost"], star_costs["MIPS1Cost"]) = (star_costs["MIPS1Cost"], star_costs["MIPS2Cost"])
+ rf.assign_rule("MIPS 1", "DV | MOVELESS")
+ rf.assign_rule("MIPS 2", "DV | MOVELESS")
+ add_rule(world.get_location("MIPS 1", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, star_costs["MIPS1Cost"]))
+ add_rule(world.get_location("MIPS 2", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, star_costs["MIPS2Cost"]))
+
+ world.completion_condition[player] = lambda state: state.can_reach("BitS: Top", 'Region', player)
- if world.CompletionType[player] == "last_bowser_stage":
- world.completion_condition[player] = lambda state: state.can_reach("Bowser in the Sky", 'Region', player)
- elif world.CompletionType[player] == "all_bowser_stages":
+ if options.completion_type == "last_bowser_stage":
+ world.completion_condition[player] = lambda state: state.can_reach("BitS: Top", 'Region', player)
+ elif options.completion_type == "all_bowser_stages":
world.completion_condition[player] = lambda state: state.can_reach("Bowser in the Dark World", 'Region', player) and \
- state.can_reach("Bowser in the Fire Sea", 'Region', player) and \
- state.can_reach("Bowser in the Sky", 'Region', player)
+ state.can_reach("BitFS: Upper", 'Region', player) and \
+ state.can_reach("BitS: Top", 'Region', player)
+
+
+class RuleFactory:
+
+ world: MultiWorld
+ player: int
+ move_rando_bitvec: bool
+ area_randomizer: bool
+ capless: bool
+ cannonless: bool
+ moveless: bool
+
+ token_table = {
+ "TJ": "Triple Jump",
+ "LJ": "Long Jump",
+ "BF": "Backflip",
+ "SF": "Side Flip",
+ "WK": "Wall Kick",
+ "DV": "Dive",
+ "GP": "Ground Pound",
+ "KK": "Kick",
+ "CL": "Climb",
+ "LG": "Ledge Grab",
+ "WC": "Wing Cap",
+ "MC": "Metal Cap",
+ "VC": "Vanish Cap"
+ }
+
+ class SM64LogicException(Exception):
+ pass
+
+ def __init__(self, world, options: SM64Options, player: int, move_rando_bitvec: int):
+ self.world = world
+ self.player = player
+ self.move_rando_bitvec = move_rando_bitvec
+ self.area_randomizer = options.area_rando > 0
+ self.capless = not options.strict_cap_requirements
+ self.cannonless = not options.strict_cannon_requirements
+ self.moveless = not options.strict_move_requirements or not move_rando_bitvec > 0
+
+ def assign_rule(self, target_name: str, rule_expr: str):
+ target = self.world.get_location(target_name, self.player) if target_name in location_table else self.world.get_entrance(target_name, self.player)
+ cannon_name = "Cannon Unlock " + target_name.split(':')[0]
+ try:
+ rule = self.build_rule(rule_expr, cannon_name)
+ except RuleFactory.SM64LogicException as exception:
+ raise RuleFactory.SM64LogicException(
+ f"Error generating rule for {target_name} using rule expression {rule_expr}: {exception}")
+ if rule:
+ set_rule(target, rule)
+
+ def build_rule(self, rule_expr: str, cannon_name: str = '') -> Callable:
+ expressions = rule_expr.split(" | ")
+ rules = []
+ for expression in expressions:
+ or_clause = self.combine_and_clauses(expression, cannon_name)
+ if or_clause is True:
+ return None
+ if or_clause is not False:
+ rules.append(or_clause)
+ if rules:
+ if len(rules) == 1:
+ return rules[0]
+ else:
+ return lambda state: any(rule(state) for rule in rules)
+ else:
+ return None
+
+ def combine_and_clauses(self, rule_expr: str, cannon_name: str) -> Union[Callable, bool]:
+ expressions = rule_expr.split(" & ")
+ rules = []
+ for expression in expressions:
+ and_clause = self.make_lambda(expression, cannon_name)
+ if and_clause is False:
+ return False
+ if and_clause is not True:
+ rules.append(and_clause)
+ if rules:
+ if len(rules) == 1:
+ return rules[0]
+ return lambda state: all(rule(state) for rule in rules)
+ else:
+ return True
+
+ def make_lambda(self, expression: str, cannon_name: str) -> Union[Callable, bool]:
+ if '+' in expression:
+ tokens = expression.split('+')
+ items = set()
+ for token in tokens:
+ item = self.parse_token(token, cannon_name)
+ if item is True:
+ continue
+ if item is False:
+ return False
+ items.add(item)
+ if items:
+ return lambda state: state.has_all(items, self.player)
+ else:
+ return True
+ if '/' in expression:
+ tokens = expression.split('/')
+ items = set()
+ for token in tokens:
+ item = self.parse_token(token, cannon_name)
+ if item is True:
+ return True
+ if item is False:
+ continue
+ items.add(item)
+ if items:
+ return lambda state: state.has_any(items, self.player)
+ else:
+ return False
+ if '{{' in expression:
+ return lambda state: state.can_reach(expression[2:-2], "Location", self.player)
+ if '{' in expression:
+ return lambda state: state.can_reach(expression[1:-1], "Region", self.player)
+ item = self.parse_token(expression, cannon_name)
+ if item in (True, False):
+ return item
+ return lambda state: state.has(item, self.player)
+
+ def parse_token(self, token: str, cannon_name: str) -> Union[str, bool]:
+ if token == "CANN":
+ return cannon_name
+ if token == "CAPLESS":
+ return self.capless
+ if token == "CANNLESS":
+ return self.cannonless
+ if token == "MOVELESS":
+ return self.moveless
+ if token == "NAR":
+ return not self.area_randomizer
+ item = self.token_table.get(token, None)
+ if not item:
+ raise Exception(f"Invalid token: '{item}'")
+ if item in action_item_table:
+ if self.move_rando_bitvec & (1 << (action_item_table[item] - action_item_table['Double Jump'])) == 0:
+ # This action item is not randomized.
+ return True
+ return item
+
diff --git a/worlds/sm64ex/__init__.py b/worlds/sm64ex/__init__.py
index ab7409a324c3..0e944aa4ab4b 100644
--- a/worlds/sm64ex/__init__.py
+++ b/worlds/sm64ex/__init__.py
@@ -1,12 +1,12 @@
import typing
import os
import json
-from .Items import item_table, cannon_item_table, SM64Item
+from .Items import item_table, action_item_table, cannon_item_table, SM64Item
from .Locations import location_table, SM64Location
-from .Options import sm64_options
+from .Options import SM64Options
from .Rules import set_rules
-from .Regions import create_regions, sm64_level_to_entrances
-from BaseClasses import Item, Tutorial, ItemClassification
+from .Regions import create_regions, sm64_level_to_entrances, SM64Levels
+from BaseClasses import Item, Tutorial, ItemClassification, Region
from ..AutoWorld import World, WebWorld
@@ -35,22 +35,50 @@ class SM64World(World):
item_name_to_id = item_table
location_name_to_id = location_table
- data_version = 8
+ data_version = 9
required_client_version = (0, 3, 5)
area_connections: typing.Dict[int, int]
- option_definitions = sm64_options
+ options_dataclass = SM64Options
+
+ number_of_stars: int
+ move_rando_bitvec: int
+ filler_count: int
+ star_costs: typing.Dict[str, int]
def generate_early(self):
- self.topology_present = self.multiworld.AreaRandomizer[self.player].value
+ max_stars = 120
+ if (not self.options.enable_coin_stars):
+ max_stars -= 15
+ self.move_rando_bitvec = 0
+ if self.options.enable_move_rando:
+ for action in self.options.move_rando_actions.value:
+ max_stars -= 1
+ self.move_rando_bitvec |= (1 << (action_item_table[action] - action_item_table['Double Jump']))
+ if (self.options.exclamation_boxes > 0):
+ max_stars += 29
+ self.number_of_stars = min(self.options.amount_of_stars, max_stars)
+ self.filler_count = max_stars - self.number_of_stars
+ self.star_costs = {
+ 'FirstBowserDoorCost': round(self.options.first_bowser_star_door_cost * self.number_of_stars / 100),
+ 'BasementDoorCost': round(self.options.basement_star_door_cost * self.number_of_stars / 100),
+ 'SecondFloorDoorCost': round(self.options.second_floor_star_door_cost * self.number_of_stars / 100),
+ 'MIPS1Cost': round(self.options.mips1_cost * self.number_of_stars / 100),
+ 'MIPS2Cost': round(self.options.mips2_cost * self.number_of_stars / 100),
+ 'StarsToFinish': round(self.options.stars_to_finish * self.number_of_stars / 100)
+ }
+ # Nudge MIPS 1 to match vanilla on default percentage
+ if self.number_of_stars == 120 and self.options.mips1_cost == 12:
+ self.star_costs['MIPS1Cost'] = 15
+ self.topology_present = self.options.area_rando
def create_regions(self):
- create_regions(self.multiworld, self.player)
+ create_regions(self.multiworld, self.options, self.player)
def set_rules(self):
self.area_connections = {}
- set_rules(self.multiworld, self.player, self.area_connections)
+ set_rules(self.multiworld, self.options, self.player, self.area_connections, self.star_costs, self.move_rando_bitvec)
if self.topology_present:
# Write area_connections to spoiler log
for entrance, destination in self.area_connections.items():
@@ -72,31 +100,29 @@ def create_item(self, name: str) -> Item:
return item
def create_items(self):
- starcount = self.multiworld.AmountOfStars[self.player].value
- if (not self.multiworld.EnableCoinStars[self.player].value):
- starcount = max(35,self.multiworld.AmountOfStars[self.player].value-15)
- starcount = max(starcount, self.multiworld.FirstBowserStarDoorCost[self.player].value,
- self.multiworld.BasementStarDoorCost[self.player].value, self.multiworld.SecondFloorStarDoorCost[self.player].value,
- self.multiworld.MIPS1Cost[self.player].value, self.multiworld.MIPS2Cost[self.player].value,
- self.multiworld.StarsToFinish[self.player].value)
- self.multiworld.itempool += [self.create_item("Power Star") for i in range(0,starcount)]
- self.multiworld.itempool += [self.create_item("1Up Mushroom") for i in range(starcount,120 - (15 if not self.multiworld.EnableCoinStars[self.player].value else 0))]
-
- if (not self.multiworld.ProgressiveKeys[self.player].value):
+ # 1Up Mushrooms
+ self.multiworld.itempool += [self.create_item("1Up Mushroom") for i in range(0,self.filler_count)]
+ # Power Stars
+ self.multiworld.itempool += [self.create_item("Power Star") for i in range(0,self.number_of_stars)]
+ # Keys
+ if (not self.options.progressive_keys):
key1 = self.create_item("Basement Key")
key2 = self.create_item("Second Floor Key")
self.multiworld.itempool += [key1, key2]
else:
self.multiworld.itempool += [self.create_item("Progressive Key") for i in range(0,2)]
-
- wingcap = self.create_item("Wing Cap")
- metalcap = self.create_item("Metal Cap")
- vanishcap = self.create_item("Vanish Cap")
- self.multiworld.itempool += [wingcap, metalcap, vanishcap]
-
- if (self.multiworld.BuddyChecks[self.player].value):
+ # Caps
+ self.multiworld.itempool += [self.create_item(cap_name) for cap_name in ["Wing Cap", "Metal Cap", "Vanish Cap"]]
+ # Cannons
+ if (self.options.buddy_checks):
self.multiworld.itempool += [self.create_item(name) for name, id in cannon_item_table.items()]
- else:
+ # Moves
+ self.multiworld.itempool += [self.create_item(action)
+ for action, itemid in action_item_table.items()
+ if self.move_rando_bitvec & (1 << itemid - action_item_table['Double Jump'])]
+
+ def generate_basic(self):
+ if not (self.options.buddy_checks):
self.multiworld.get_location("BoB: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock BoB"))
self.multiworld.get_location("WF: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock WF"))
self.multiworld.get_location("JRB: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock JRB"))
@@ -108,9 +134,7 @@ def create_items(self):
self.multiworld.get_location("THI: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock THI"))
self.multiworld.get_location("RR: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock RR"))
- if (self.multiworld.ExclamationBoxes[self.player].value > 0):
- self.multiworld.itempool += [self.create_item("1Up Mushroom") for i in range(0,29)]
- else:
+ if (self.options.exclamation_boxes == 0):
self.multiworld.get_location("CCM: 1Up Block Near Snowman", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.multiworld.get_location("CCM: 1Up Block Ice Pillar", self.player).place_locked_item(self.create_item("1Up Mushroom"))
self.multiworld.get_location("CCM: 1Up Block Secret Slide", self.player).place_locked_item(self.create_item("1Up Mushroom"))
@@ -147,14 +171,10 @@ def get_filler_item_name(self) -> str:
def fill_slot_data(self):
return {
"AreaRando": self.area_connections,
- "FirstBowserDoorCost": self.multiworld.FirstBowserStarDoorCost[self.player].value,
- "BasementDoorCost": self.multiworld.BasementStarDoorCost[self.player].value,
- "SecondFloorDoorCost": self.multiworld.SecondFloorStarDoorCost[self.player].value,
- "MIPS1Cost": self.multiworld.MIPS1Cost[self.player].value,
- "MIPS2Cost": self.multiworld.MIPS2Cost[self.player].value,
- "StarsToFinish": self.multiworld.StarsToFinish[self.player].value,
- "DeathLink": self.multiworld.death_link[self.player].value,
- "CompletionType" : self.multiworld.CompletionType[self.player].value,
+ "MoveRandoVec": self.move_rando_bitvec,
+ "DeathLink": self.options.death_link.value,
+ "CompletionType": self.options.completion_type.value,
+ **self.star_costs
}
def generate_output(self, output_directory: str):
@@ -178,11 +198,21 @@ def generate_output(self, output_directory: str):
with open(os.path.join(output_directory, filename), 'w') as f:
json.dump(data, f)
- def modify_multidata(self, multidata):
+ def extend_hint_information(self, hint_data: typing.Dict[int, typing.Dict[int, str]]):
if self.topology_present:
er_hint_data = {}
for entrance, destination in self.area_connections.items():
- region = self.multiworld.get_region(sm64_level_to_entrances[destination], self.player)
- for location in region.locations:
- er_hint_data[location.address] = sm64_level_to_entrances[entrance]
- multidata['er_hint_data'][self.player] = er_hint_data
+ regions = [self.multiworld.get_region(sm64_level_to_entrances[destination], self.player)]
+ if regions[0].name == "Tiny-Huge Island (Huge)":
+ # Special rules for Tiny-Huge Island's dual entrances
+ reverse_area_connections = {destination: entrance for entrance, destination in self.area_connections.items()}
+ entrance_name = sm64_level_to_entrances[reverse_area_connections[SM64Levels.TINY_HUGE_ISLAND_HUGE]] \
+ + ' or ' + sm64_level_to_entrances[reverse_area_connections[SM64Levels.TINY_HUGE_ISLAND_TINY]]
+ regions[0] = self.multiworld.get_region("Tiny-Huge Island", self.player)
+ else:
+ entrance_name = sm64_level_to_entrances[entrance]
+ regions += regions[0].subregions
+ for region in regions:
+ for location in region.locations:
+ er_hint_data[location.address] = entrance_name
+ hint_data[self.player] = er_hint_data
diff --git a/worlds/sm64ex/docs/en_Super Mario 64.md b/worlds/sm64ex/docs/en_Super Mario 64.md
index def6e2a37536..3d182a422081 100644
--- a/worlds/sm64ex/docs/en_Super Mario 64.md
+++ b/worlds/sm64ex/docs/en_Super Mario 64.md
@@ -1,9 +1,9 @@
# Super Mario 64 EX
-## Where is the settings page?
+## Where is the options page?
-The player settings page for this game contains all the options you need to configure and export a config file. Player
-settings page link: [SM64EX Player Settings Page](../player-settings).
+The player options page for this game contains all the options you need to configure and export a config file. Player
+options page link: [SM64EX Player Options Page](../player-options).
## What does randomization do to this game?
All 120 Stars, the 3 Cap Switches, the Basement and Secound Floor Key are now Location Checks and may contain Items for different games as well
diff --git a/worlds/sm64ex/docs/setup_en.md b/worlds/sm64ex/docs/setup_en.md
index 2817d3c324c0..5983057f7d7a 100644
--- a/worlds/sm64ex/docs/setup_en.md
+++ b/worlds/sm64ex/docs/setup_en.md
@@ -70,7 +70,7 @@ After the compliation was successful, there will be a binary in your `sm64ex/bui
### Joining a MultiWorld Game
To join, set the following launch options: `--sm64ap_name YourName --sm64ap_ip ServerIP:Port`.
-For example, if you are hosting a game using the website, `YourName` will be the name from the Settings Page, `ServerIP` is `archipelago.gg` and `Port` the port given on the Archipelago room page.
+For example, if you are hosting a game using the website, `YourName` will be the name from the Options Page, `ServerIP` is `archipelago.gg` and `Port` the port given on the Archipelago room page.
Optionally, add `--sm64ap_passwd "YourPassword"` if the room you are using requires a password.
Should your name or password have spaces, enclose it in quotes: `"YourPassword"` and `"YourName"`.
@@ -82,7 +82,7 @@ Failing to use a new file may make some locations unavailable. However, this can
### Playing offline
-To play offline, first generate a seed on the game's settings page.
+To play offline, first generate a seed on the game's options page.
Create a room and download the `.apsm64ex` file, and start the game with the `--sm64ap_file "path/to/FileName"` launch argument.
### Optional: Using Batch Files to play offline and MultiWorld games
diff --git a/worlds/smw/Aesthetics.py b/worlds/smw/Aesthetics.py
index 73ca61650886..16b2b138f3e2 100644
--- a/worlds/smw/Aesthetics.py
+++ b/worlds/smw/Aesthetics.py
@@ -1,3 +1,82 @@
+import json
+import pkgutil
+
+from worlds.AutoWorld import World
+
+tileset_names = [
+ "grass_hills",
+ "grass_forest",
+ "grass_rocks",
+ "grass_clouds",
+ "grass_mountains",
+ "cave",
+ "cave_rocks",
+ "water",
+ "mushroom_rocks",
+ "mushroom_clouds",
+ "mushroom_forest",
+ "mushroom_hills",
+ "mushroom_stars",
+ "mushroom_cave",
+ "forest",
+ "logs",
+ "clouds",
+ "castle_pillars",
+ "castle_windows",
+ "castle_wall",
+ "castle_small_windows",
+ "ghost_house",
+ "ghost_house_exit",
+ "ship_exterior",
+ "ship_interior",
+ "switch_palace",
+ "yoshi_house"
+]
+
+map_names = [
+ "main",
+ "yoshi",
+ "vanilla",
+ "forest",
+ "valley",
+ "special",
+ "star"
+]
+
+level_palette_index = [
+ 0xFF,0x03,0x09,0x01,0x15,0x0A,0x04,0x12,0x19,0x06,0x07,0x12,0x09,0x0F,0x13,0x09, # Levels 000-00F
+ 0x03,0x07,0xFF,0x15,0x19,0x04,0x04,0xFF,0x17,0xFF,0x14,0x12,0x02,0x05,0xFF,0x11, # Levels 010-01F
+ 0x12,0x15,0x04,0x02,0x02,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 020-02F
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 030-03F
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 040-04F
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 050-05F
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 060-06F
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 070-07F
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 080-08F
+ 0xFF,0xFF,0xFF,0x12,0x12,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 090-09F
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 0A0-0AF
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x19,0x08,0x09, # Levels 0B0-0BF
+ 0x02,0x08,0x05,0x04,0x16,0x1A,0x04,0x02,0x0C,0x19,0x19,0x09,0xFF,0x02,0x02,0x02, # Levels 0C0-0CF
+ 0x04,0x04,0x05,0x12,0x14,0xFF,0x12,0x10,0x05,0xFF,0x19,0x12,0x14,0x0F,0x15,0xFF, # Levels 0D0-0DF
+ 0x12,0x12,0xFF,0x04,0x15,0xFF,0x19,0x14,0x12,0x05,0x05,0x16,0x15,0x15,0x15,0x12, # Levels 0E0-0EF
+ 0x16,0x15,0x15,0x09,0x19,0x04,0x04,0x13,0x18,0x15,0x15,0x16,0x15,0x19,0x15,0x04, # Levels 0F0-0FF
+ 0xFF,0x11,0x08,0x02,0x1A,0x00,0x01,0x15,0xFF,0x05,0x05,0x05,0xFF,0x11,0x12,0x05, # Levels 100-10F
+ 0x12,0x14,0xFF,0x0D,0x15,0x06,0x05,0x05,0x05,0x0C,0x05,0x19,0x12,0x15,0x0E,0x01, # Levels 110-11F
+ 0x07,0x19,0x0E,0x0E,0xFF,0x04,0x0E,0x02,0x02,0xFF,0x09,0x04,0x0B,0x02,0xFF,0xFF, # Levels 120-12F
+ 0x07,0xFF,0x0C,0xFF,0x05,0x0C,0x0C,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 130-13F
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 140-14F
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 150-15F
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 160-16F
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 170-17F
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 180-18F
+ 0xFF,0xFF,0xFF,0x12,0x12,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 190-19F
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, # Levels 1A0-1AF
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x19,0x19,0x12,0x02,0x05, # Levels 1B0-1BF
+ 0x02,0x07,0x05,0x05,0x03,0x03,0x00,0xFF,0x0F,0x10,0x05,0x05,0x12,0x11,0x14,0x14, # Levels 1C0-1CF
+ 0x11,0x12,0x12,0x12,0x11,0x03,0x03,0x19,0x19,0x15,0x16,0x15,0x15,0x15,0xFF,0x05, # Levels 1D0-1DF
+ 0x10,0x02,0x06,0x06,0x19,0x05,0x16,0x16,0x15,0x15,0x15,0xFF,0x06,0x05,0x05,0x06, # Levels 1E0-1EF
+ 0x05,0x05,0x12,0x14,0x12,0x05,0xFF,0x19,0x05,0x16,0x15,0x15,0x11,0x05,0x12,0x09 # Levels 1F0-1FF
+]
mario_palettes = [
[0x5F, 0x63, 0x1D, 0x58, 0x0A, 0x00, 0x1F, 0x39, 0xC4, 0x44, 0x08, 0x4E, 0x70, 0x67, 0xB6, 0x30, 0xDF, 0x35, 0xFF, 0x03], # Mario
@@ -145,36 +224,413 @@
0x2D24: [0x00, 0x02, 0x03], # Star Road
}
-def generate_shuffled_level_music(world, player):
+valid_sfxs = [
+ [0x01, 1], # Jump
+ [0x01, 0], # Hit head
+ [0x02, 0], # Contact/Spinjump on an enemy
+ [0x03, 0], # Kick item
+ [0x04, 0], # Go in pipe, get hurt
+ [0x05, 0], # Midway point
+ [0x06, 0], # Yoshi gulp
+ [0x07, 0], # Dry bones collapse
+ [0x08, 0], # Kill enemy with a spin jump
+ [0x09, 0], # Fly with cape
+ [0x0A, 0], # Get powerup
+ [0x0B, 0], # ON/OFF switch
+ [0x0C, 0], # Carry item past the goal
+ [0x0D, 0], # Get cape
+ [0x0E, 0], # Swim
+ [0x0F, 0], # Hurt while flying
+ [0x10, 0], # Magikoopa shoot magic
+ [0x13, 0], # Enemy stomp #1
+ [0x14, 0], # Enemy stomp #2
+ [0x15, 0], # Enemy stomp #3
+ [0x16, 0], # Enemy stomp #4
+ [0x17, 0], # Enemy stomp #5
+ [0x18, 0], # Enemy stomp #6
+ [0x19, 0], # Enemy stomp #7
+ [0x1C, 0], # Yoshi Coin
+ [0x1E, 0], # P-Balloon
+ [0x1F, 0], # Koopaling defeated
+ [0x20, 0], # Yoshi spit
+ [0x23, 0], # Lemmy/Wendy falling
+ [0x25, 0], # Blargg roar
+ [0x26, 0], # Firework whistle
+ [0x27, 0], # Firework bang
+ [0x2A, 0], # Peach pops up from the Clown Car
+ [0x04, 1], # Grinder
+ [0x01, 3], # Coin
+ [0x02, 3], # Hit a ? block
+ [0x03, 3], # Hit a block with a vine inside
+ [0x04, 3], # Spin jump
+ [0x05, 3], # 1up
+ [0x06, 3], # Shatter block
+ [0x07, 3], # Shoot fireball
+ [0x08, 3], # Springboard
+ [0x09, 3], # Bullet bill
+ [0x0A, 3], # Egg hatch
+ [0x0B, 3], # Item going into item box
+ [0x0C, 3], # Item falls from item box
+ [0x0E, 3], # L/R scroll
+ [0x0F, 3], # Door
+ [0x13, 3], # Lose Yoshi
+ [0x14, 3], # SMW2: New level available
+ [0x15, 3], # OW tile reveal
+ [0x16, 3], # OW castle collapse
+ [0x17, 3], # Fire spit
+ [0x18, 3], # Thunder
+ [0x19, 3], # Clap
+ [0x1A, 3], # Castle bomb
+ [0x1C, 3], # OW switch palace block ejection
+ [0x1E, 3], # Whistle
+ [0x1F, 3], # Yoshi mount
+ [0x20, 3], # Lemmy/Wendy going in lava
+ [0x21, 3], # Yoshi's tongue
+ [0x22, 3], # Message box hit
+ [0x23, 3], # Landing in a level tile
+ [0x24, 3], # P-Switch running out
+ [0x25, 3], # Yoshi defeats an enemy
+ [0x26, 3], # Swooper
+ [0x27, 3], # Podoboo
+ [0x28, 3], # Enemy hurt
+ [0x29, 3], # Correct
+ [0x2A, 3], # Wrong
+ [0x2B, 3], # Firework whistle
+ [0x2C, 3] # Firework bang
+]
+
+game_sfx_calls = [
+ 0x0565E, # Jump
+ 0x1BABD, # Spin jump
+ 0x06D41, # Hit head on ceiling
+ 0x0B4F2, # Hit head on sprite
+ 0x07EB5, # Shooting a fireball
+ 0x0507B, # Cape spin
+ 0x058A8, # Cape smash
+ 0x075F3, # Taking damage
+ 0x075E2, # Taking damage while flying
+ 0x07919, # Something during a boss fight
+ 0x05AA9, # Swim
+ 0x1BC04, # Spin jump off water
+ 0x05BA5, # Jump off a net
+ 0x05BB2, # Punching a net
+ 0x06C10, # Entering a door
+ 0x05254, # Entering a pipe #1
+ 0x07439, # Entering a pipe #2
+ 0x052A5, # Shot from a diagonal pipe
+ 0x072E8, # Hit a midway point
+ 0x07236, # Hit a wrong block
+ 0x07B7D, # Spawn a powerup during the goal tape
+ 0x1C342, # Invisible mushroom spawn
+ 0x04E3F, # Scrolling the screen with L/R
+ 0x0AAFD, # Pressing a P-Switch
+ 0x04557, # P-Switch running out
+ 0x0BAD7, # Climbing door turning
+ 0x0C109, # Break goal tape
+ 0x0C548, # Putting item in item box
+ 0x10012, # Trigger item box
+ 0x2B34D, # Collecting a coin
+ 0x07358, # Collecting a Yoshi Coin
+ 0x0C57A, # Collecting a powerup (generic)
+ 0x0C59C, # Collecting a feather
+ 0x0C309, # Collecting a P-Balloon
+ 0x0E6A9, # Bouncing off a springboard
+ 0x1117D, # Bouncing off a note block
+ 0x14DEC, # Bouncing off a wall springboard
+ 0x1067F, # Block shattering
+ 0x1081E, # Activate ON/OFF switch #1
+ 0x1118C, # Activate ON/OFF switch #2
+ 0x12045, # Fireballs hitting a block/sprite
+ 0x12124, # Fireballs converting an enemy into a coin
+ 0x12106, # Fireballs defeating a Chuck
+ 0x18D7D, # Activating a message box
+ 0x1C209, # Activating a red question block
+ 0x0A290, # Baby Yoshi swallowing an item #1
+ 0x1C037, # Baby Yoshi swallowing an item #2
+ 0x0F756, # Yoshi egg hatching
+ 0x0A2C5, # Yoshi growing #1
+ 0x1C06C, # Yoshi growing #2
+ 0x0ED5F, # Mounting Yoshi
+ 0x0F71D, # Yoshi hurt
+ 0x12481, # Yoshi hurt (projectiles)
+ 0x0EF0E, # Yoshi flying
+ 0x06F90, # Yoshi stomping an enemy
+ 0x06FB6, # Yoshi ground pound (yellow shell)
+ 0x07024, # Yoshi bounces off a triangle
+ 0x11BE9, # Yoshi stomping the ground
+ 0x0F0D3, # Yoshi swallowing a sprite
+ 0x0F0FD, # Yoshi eating a green berry
+ 0x1BA7D, # Yoshi sticking out tongue
+ 0x0F5A1, # Yoshi unable to eat
+ 0x0F2DF, # Yoshi spitting out an item
+ 0x0F28F, # Yoshi spitting out flames
+ 0x0F3EC, # Collecting Yoshi's wings (eaten)
+ 0x0F6C8, # Collecting Yoshi's wings (touched)
+ 0x7FE04, # Defeated sprite combo #1 (using Y index)
+ 0x7FE0E, # Defeated sprite combo #2 (using Y index)
+ 0x7FE18, # Defeated sprite combo #3 (using Y index)
+ 0x7FE22, # Defeated sprite combo #4 (using Y index)
+ 0x7FE2C, # Defeated sprite combo #5 (using Y index)
+ 0x7FE36, # Defeated sprite combo #6 (using Y index)
+ 0x7FE40, # Defeated sprite combo #7 (using Y index)
+ 0x7FE4B, # Defeated sprite combo #1 (using X index)
+ 0x7FE55, # Defeated sprite combo #2 (using X index)
+ 0x7FE5F, # Defeated sprite combo #3 (using X index)
+ 0x7FE69, # Defeated sprite combo #4 (using X index)
+ 0x7FE73, # Defeated sprite combo #5 (using X index)
+ 0x7FE7D, # Defeated sprite combo #6 (using X index)
+ 0x7FE87, # Defeated sprite combo #7 (using X index)
+ 0x0A728, # Kicking a carryable item
+ 0x0B12F, # Kicking a stunned and vulnerable enemy
+ 0x0A8D8, # Performing a spinjump on a immune enemy
+ 0x0A93F, # Defeating an enemy via spinjump
+ 0x0999E, # Thrown sprite hitting the ground from the side
+ 0x192B8, # Creating/Eating block moving
+ 0x195EC, # Rex stomped
+ 0x134A7, # Bullet bill blaster shooting
+ 0x13088, # Bullet bill generator #1
+ 0x130DF, # Bullet bill generator #2
+ 0x09631, # Bob-omb explosion
+ 0x15918, # Popping a bubble
+ 0x15D64, # Sumo bro stomping the ground
+ 0x15ECC, # Sumo bro lightning spawning flames
+ 0x1726B, # Bouncing off wiggler
+ 0x08390, # Banzai bill spawn
+ 0x0AF17, # Thwomp hitting the ground
+ 0x0AFFC, # Thwimp hitting the ground
+ 0x14707, # Chuck running
+ 0x14381, # Chuck whistling
+ 0x144F8, # Chuck clapping
+ 0x14536, # Chuck jumping
+ 0x145AE, # Chuck splitting
+ 0x147D2, # Chuck bounce
+ 0x147F6, # Chuck hurt
+ 0x147B8, # Chuck defeated
+ 0x19D55, # Dino torch shooting fire
+ 0x19FFA, # Blargg attacking
+ 0x188FF, # Swooper bat swooping
+ 0x08584, # Bowser statue flame spawn
+ 0x18ADA, # Bowser statue shooting a flame
+ 0x13043, # Bowser statue flame from generator
+ 0x0BF28, # Magikoopa shooting a magic spell
+ 0x0BC5F, # Magikoopa's magic spell hitting the ground
+ 0x0D745, # Line guided sprites' motor
+ 0x0DB70, # Grinder sound
+ 0x0E0A1, # Podoboo jumping
+ 0x0E5F2, # Dry bones/Bony beetle collapsing
+ 0x15474, # Giant wooden pillar hitting the ground
+ 0x2C9C1, # Spiked columns hitting the ground
+ 0x19B03, # Reznor shooting a fireball
+ 0x19A66, # Reznor: Hitting a platform
+ 0x1D752, # Reznor: Bridge collapsing
+ 0x19ABB, # Reznor: Defeated
+ 0x180E9, # Big Boo: Reappearing
+ 0x18233, # Big Boo: Hurt
+ 0x181DE, # Big Boo: Defeated
+ 0x1CEC1, # Wendy/Lemmy: Hitting a dummy
+ 0x1CECB, # Wendy/Lemmy: Hurt
+ 0x1CE33, # Wendy/Lemmy: Hurt (correct)
+ 0x1CE46, # Wendy/Lemmy: Hurt (incorrect)
+ 0x1CE24, # Wendy/Lemmy: Defeated
+ 0x1CE7E, # Wendy/Lemmy: Falling into lava
+ 0x0CF0A, # Ludwig: Jumping
+ 0x0D059, # Ludwig: Shooting a fireball
+ 0x10414, # Morton/Roy: Pillar drop
+ 0x0D299, # Morton/Roy: Ground smash
+ 0x0D3AB, # Morton/Roy/Ludwig: Hit by a fireball
+ 0x0D2FD, # Morton/Roy/Ludwig: Bouncing off
+ 0x0D31E, # Morton/Roy/Ludwig: Bouncing off (immune)
+ 0x0D334, # Morton/Roy/Ludwig: Bouncing off (immune, going up a wall)
+ 0x0CFD0, # Morton/Roy/Ludwig: Defeated
+ 0x0FCCE, # Iggy/Larry: Being hit
+ 0x0FD40, # Iggy/Larry: Being hit by a fireball
+ 0x0FB60, # Iggy/Larry: Falling in lava
+ 0x1A8B2, # Peach emerging from Clown Car
+ 0x1A8E3, # Peach throwing an item
+ 0x1B0B8, # Bumping into Clown Car
+ 0x1B129, # Bowser: Hurt
+ 0x1AB8C, # Bowser: Slamming the ground (third phase)
+ 0x1A5D0, # Bowser: Throwing a Mechakoopa
+ 0x1A603, # Bowser: Dropping a ball
+ 0x1A7F6, # Bowser: Spawning a flame
+ 0x1B1A3, # Bowser's ball slam #1
+ 0x1B1B1, # Bowser's ball slam #2
+ 0x1E016, # Bowser's arena lightning effect
+ 0x26CAA, # Map: Level tile reveal
+ 0x26763, # Map: Terrain reveal
+ 0x21170, # Map: Using a star
+ 0x2666F, # Map: Castle destruction
+ 0x272A4, # Map: Switch palace blocks spawning
+ 0x203CC, # Map: Earthquake
+ 0x27A78, # Map: Fish jumping
+ 0x27736, # Map: Valley of bowser thunder
+ 0x013C0, # Menu: Nintendo presents
+ 0x01AE3, # Menu: File menu option select
+ 0x01AF9, # Menu: File menu option change
+ 0x01BBB, # Menu: Saving game
+ 0x273FF, # Menu: Map misc menu appearing
+ 0x27567, # Menu: Something during the map
+ 0x1767A, # Cutscene: Castle door opening
+ 0x17683, # Cutscene: Castle door closing
+ 0x17765, # Cutscene: Ghost house door opening
+ 0x1776E, # Cutscene: Ghost house door closing
+ 0x04720, # Cutscene: Detonator fuse
+ 0x04732, # Cutscene: Bouncing off something
+ 0x0475F, # Cutscene: Tossing the castle
+ 0x04798, # Cutscene: Picking up the castle
+ 0x047AC, # Cutscene: Huff
+ 0x047D1, # Cutscene: Hitting a castle
+ 0x1C830, # Cutscene: Shooting a firework
+ 0x625AF, # Cutscene: Egg shattering
+ 0x64F2C, # Cutscene: Hitting a hill
+ 0x6512A, # Cutscene: Castle crashing
+ 0x65295, # Cutscene: Explosion
+ 0x652B2, # Cutscene: Castle sinking
+ 0x652BD, # Cutscene: Castle flying
+ 0x652D8, # Cutscene: Fake explosion
+ 0x653E7, # Cutscene: Castle being hit by a hammer
+ 0x657D8 # Cutscene: Castle being mopped away
+]
+
+def generate_shuffled_sfx(rom, world: World):
+ # Adjust "hitting sprites in succession" codes
+ rom.write_bytes(0x0A60B, bytearray([0x22, 0x00, 0xFE, 0x0F, 0xEA, 0xEA])) # jsl $0FFE00 : nop #2 # Thrown sprites combo #1
+ rom.write_bytes(0x0A659, bytearray([0x22, 0x47, 0xFE, 0x0F, 0xEA, 0xEA])) # jsl $0FFE47 : nop #2 # Thrown sprites combo #2
+ rom.write_bytes(0x0A865, bytearray([0x22, 0x47, 0xFE, 0x0F, 0xEA, 0xEA])) # jsl $0FFE47 : nop #2 # Star combo
+ rom.write_bytes(0x0AB57, bytearray([0x22, 0x00, 0xFE, 0x0F, 0xEA, 0xEA])) # jsl $0FFE00 : nop #2 # Bouncing off enemies
+ rom.write_bytes(0x172C0, bytearray([0x22, 0x00, 0xFE, 0x0F, 0xEA, 0xEA])) # jsl $0FFE00 : nop #2 # Star combo (wigglers)
+ rom.write_bytes(0x1961D, bytearray([0x22, 0x00, 0xFE, 0x0F, 0xEA, 0xEA])) # jsl $0FFE00 : nop #2 # Star combo (rexes)
+ rom.write_bytes(0x19639, bytearray([0x22, 0x00, 0xFE, 0x0F, 0xEA, 0xEA])) # jsl $0FFE00 : nop #2 # Bouncing off rexes
+
+ COMBO_SFX_ADDR = 0x7FE00
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0000, bytearray([0xC0, 0x01])) # COMBO_Y: CPY #$01
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0002, bytearray([0xD0, 0x06])) # BNE label_0FFE0A
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0004, bytearray([0xA9, 0x13])) # LDA #$13
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0006, bytearray([0x8D, 0xF9, 0x1D])) # STA $1DF9
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0009, bytearray([0x6B])) # RTL
+ rom.write_bytes(COMBO_SFX_ADDR + 0x000A, bytearray([0xC0, 0x02])) # label_0FFE0A: CPY #$02
+ rom.write_bytes(COMBO_SFX_ADDR + 0x000C, bytearray([0xD0, 0x06])) # BNE label_0FFE14
+ rom.write_bytes(COMBO_SFX_ADDR + 0x000E, bytearray([0xA9, 0x14])) # LDA #$14
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0010, bytearray([0x8D, 0xF9, 0x1D])) # STA $1DF9
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0013, bytearray([0x6B])) # RTL
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0014, bytearray([0xC0, 0x03])) # label_0FFE14: CPY #$03
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0016, bytearray([0xD0, 0x06])) # BNE label_0FFE1E
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0018, bytearray([0xA9, 0x15])) # LDA #$15
+ rom.write_bytes(COMBO_SFX_ADDR + 0x001A, bytearray([0x8D, 0xF9, 0x1D])) # STA $1DF9
+ rom.write_bytes(COMBO_SFX_ADDR + 0x001D, bytearray([0x6B])) # RTL
+ rom.write_bytes(COMBO_SFX_ADDR + 0x001E, bytearray([0xC0, 0x04])) # label_0FFE1E: CPY #$04
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0020, bytearray([0xD0, 0x06])) # BNE label_0FFE28
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0022, bytearray([0xA9, 0x16])) # LDA #$16
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0024, bytearray([0x8D, 0xF9, 0x1D])) # STA $1DF9
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0027, bytearray([0x6B])) # RTL
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0028, bytearray([0xC0, 0x05])) # label_0FFE28: CPY #$05
+ rom.write_bytes(COMBO_SFX_ADDR + 0x002A, bytearray([0xD0, 0x06])) # BNE label_0FFE32
+ rom.write_bytes(COMBO_SFX_ADDR + 0x002C, bytearray([0xA9, 0x17])) # LDA #$17
+ rom.write_bytes(COMBO_SFX_ADDR + 0x002E, bytearray([0x8D, 0xF9, 0x1D])) # STA $1DF9
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0031, bytearray([0x6B])) # RTL
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0032, bytearray([0xC0, 0x06])) # label_0FFE32: CPY #$06
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0034, bytearray([0xD0, 0x06])) # BNE label_0FFE3C
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0036, bytearray([0xA9, 0x18])) # LDA #$18
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0038, bytearray([0x8D, 0xF9, 0x1D])) # STA $1DF9
+ rom.write_bytes(COMBO_SFX_ADDR + 0x003B, bytearray([0x6B])) # RTL
+ rom.write_bytes(COMBO_SFX_ADDR + 0x003C, bytearray([0xC0, 0x07])) # label_0FFE3C: CPY #$07
+ rom.write_bytes(COMBO_SFX_ADDR + 0x003E, bytearray([0xD0, 0x06])) # BNE label_0FFE46
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0040, bytearray([0xA9, 0x19])) # LDA #$19
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0042, bytearray([0x8D, 0xF9, 0x1D])) # STA $1DF9
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0045, bytearray([0x6B])) # RTL
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0046, bytearray([0x6B])) # label_0FFE46: RTL
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0047, bytearray([0xE0, 0x01])) # COMBO_X: CPX #$01
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0049, bytearray([0xD0, 0x06])) # BNE label_0FFE51
+ rom.write_bytes(COMBO_SFX_ADDR + 0x004B, bytearray([0xA9, 0x13])) # LDA #$13
+ rom.write_bytes(COMBO_SFX_ADDR + 0x004D, bytearray([0x8D, 0xF9, 0x1D])) # STA $1DF9
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0050, bytearray([0x6B])) # RTL
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0051, bytearray([0xE0, 0x02])) # label_0FFE51: CPX #$02
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0053, bytearray([0xD0, 0x06])) # BNE label_0FFE5B
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0055, bytearray([0xA9, 0x14])) # LDA #$14
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0057, bytearray([0x8D, 0xF9, 0x1D])) # STA $1DF9
+ rom.write_bytes(COMBO_SFX_ADDR + 0x005A, bytearray([0x6B])) # RTL
+ rom.write_bytes(COMBO_SFX_ADDR + 0x005B, bytearray([0xE0, 0x03])) # label_0FFE5B: CPX #$03
+ rom.write_bytes(COMBO_SFX_ADDR + 0x005D, bytearray([0xD0, 0x06])) # BNE label_0FFE65
+ rom.write_bytes(COMBO_SFX_ADDR + 0x005F, bytearray([0xA9, 0x15])) # LDA #$15
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0061, bytearray([0x8D, 0xF9, 0x1D])) # STA $1DF9
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0064, bytearray([0x6B])) # RTL
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0065, bytearray([0xE0, 0x04])) # label_0FFE65: CPX #$04
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0067, bytearray([0xD0, 0x06])) # BNE label_0FFE6F
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0069, bytearray([0xA9, 0x16])) # LDA #$16
+ rom.write_bytes(COMBO_SFX_ADDR + 0x006B, bytearray([0x8D, 0xF9, 0x1D])) # STA $1DF9
+ rom.write_bytes(COMBO_SFX_ADDR + 0x006E, bytearray([0x6B])) # RTL
+ rom.write_bytes(COMBO_SFX_ADDR + 0x006F, bytearray([0xE0, 0x05])) # label_0FFE6F: CPX #$05
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0071, bytearray([0xD0, 0x06])) # BNE label_0FFE79
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0073, bytearray([0xA9, 0x17])) # LDA #$17
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0075, bytearray([0x8D, 0xF9, 0x1D])) # STA $1DF9
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0078, bytearray([0x6B])) # RTL
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0079, bytearray([0xE0, 0x06])) # label_0FFE79: CPX #$06
+ rom.write_bytes(COMBO_SFX_ADDR + 0x007B, bytearray([0xD0, 0x06])) # BNE label_0FFE83
+ rom.write_bytes(COMBO_SFX_ADDR + 0x007D, bytearray([0xA9, 0x18])) # LDA #$18
+ rom.write_bytes(COMBO_SFX_ADDR + 0x007F, bytearray([0x8D, 0xF9, 0x1D])) # STA $1DF9
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0082, bytearray([0x6B])) # RTL
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0083, bytearray([0xE0, 0x07])) # label_0FFE83: CPX #$07
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0085, bytearray([0xD0, 0x06])) # BNE label_0FFE8D
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0087, bytearray([0xA9, 0x19])) # LDA #$19
+ rom.write_bytes(COMBO_SFX_ADDR + 0x0089, bytearray([0x8D, 0xF9, 0x1D])) # STA $1DF9
+ rom.write_bytes(COMBO_SFX_ADDR + 0x008C, bytearray([0x6B])) # RTL
+ rom.write_bytes(COMBO_SFX_ADDR + 0x008D, bytearray([0x6B])) # label_0FFE8D: RTL
+
+ # Adjust "Hit head on ceiling" code
+ rom.write_bytes(0x06D41 + 0x00, bytearray([0xA9, 0x01])) # lda #$01
+ rom.write_bytes(0x06D41 + 0x02, bytearray([0x8D, 0xF9, 0x1D])) # sta $1DF9
+ rom.write_bytes(0x06D41 + 0x05, bytearray([0xEA, 0xEA, 0xEA, 0xEA])) # nop #4
+
+ # Manually add "Map: Stepping onto a level tile" random SFX
+ selected_sfx = world.random.choice(valid_sfxs)
+ rom.write_byte(0x2169F + 0x01, selected_sfx[0])
+ rom.write_byte(0x2169F + 0x04, selected_sfx[1] + 0xF9)
+
+ # Disable panning on Bowser's flames
+ rom.write_bytes(0x1A83D, bytearray([0xEA, 0xEA, 0xEA])) # nop #3
+
+ # Randomize SFX calls
+ for address in game_sfx_calls:
+ # Get random SFX
+ if world.options.sfx_shuffle != "singularity":
+ selected_sfx = world.random.choice(valid_sfxs)
+ # Write randomized SFX num
+ rom.write_byte(address + 0x01, selected_sfx[0])
+ # Write randomized SFX port
+ rom.write_byte(address + 0x03, selected_sfx[1] + 0xF9)
+
+def generate_shuffled_level_music(world: World):
shuffled_level_music = level_music_value_data.copy()
- if world.music_shuffle[player] == "consistent":
- world.per_slot_randoms[player].shuffle(shuffled_level_music)
- elif world.music_shuffle[player] == "singularity":
- single_song = world.per_slot_randoms[player].choice(shuffled_level_music)
+ if world.options.music_shuffle == "consistent":
+ world.random.shuffle(shuffled_level_music)
+ elif world.options.music_shuffle == "singularity":
+ single_song = world.random.choice(shuffled_level_music)
shuffled_level_music = [single_song for i in range(len(shuffled_level_music))]
return shuffled_level_music
-def generate_shuffled_ow_music(world, player):
+def generate_shuffled_ow_music(world: World):
shuffled_ow_music = ow_music_value_data.copy()
- if world.music_shuffle[player] == "consistent" or world.music_shuffle[player] == "full":
- world.per_slot_randoms[player].shuffle(shuffled_ow_music)
- elif world.music_shuffle[player] == "singularity":
- single_song = world.per_slot_randoms[player].choice(shuffled_ow_music)
+ if world.options.music_shuffle == "consistent" or world.options.music_shuffle == "full":
+ world.random.shuffle(shuffled_ow_music)
+ elif world.options.music_shuffle == "singularity":
+ single_song = world.random.choice(shuffled_ow_music)
shuffled_ow_music = [single_song for i in range(len(shuffled_ow_music))]
return shuffled_ow_music
-def generate_shuffled_ow_palettes(rom, world, player):
- if world.overworld_palette_shuffle[player]:
- for address, valid_palettes in valid_ow_palettes.items():
- chosen_palette = world.per_slot_randoms[player].choice(valid_palettes)
- rom.write_byte(address, chosen_palette)
+def generate_shuffled_ow_palettes(rom, world: World):
+ if world.options.overworld_palette_shuffle != "on_legacy":
+ return
+
+ for address, valid_palettes in valid_ow_palettes.items():
+ chosen_palette = world.random.choice(valid_palettes)
+ rom.write_byte(address, chosen_palette)
-def generate_shuffled_header_data(rom, world, player):
- if world.music_shuffle[player] != "full" and not world.foreground_palette_shuffle[player] and not world.background_palette_shuffle[player]:
+def generate_shuffled_header_data(rom, world: World):
+ if world.options.music_shuffle != "full" and world.options.level_palette_shuffle != "on_legacy":
return
for level_id in range(0, 0x200):
@@ -194,24 +650,425 @@ def generate_shuffled_header_data(rom, world, player):
tileset = level_header[4] & 0x0F
- if world.music_shuffle[player] == "full":
+ if world.options.music_shuffle == "full":
level_header[2] &= 0x8F
- level_header[2] |= (world.per_slot_randoms[player].randint(0, 7) << 5)
+ level_header[2] |= (world.random.randint(0, 7) << 5)
- if (world.foreground_palette_shuffle[player] and tileset in valid_foreground_palettes):
- level_header[3] &= 0xF8
- level_header[3] |= world.per_slot_randoms[player].choice(valid_foreground_palettes[tileset])
+ if world.options.level_palette_shuffle == "on_legacy":
+ if tileset in valid_foreground_palettes:
+ level_header[3] &= 0xF8
+ level_header[3] |= world.random.choice(valid_foreground_palettes[tileset])
- if world.background_palette_shuffle[player]:
layer2_ptr_list = list(rom.read_bytes(0x2E600 + level_id * 3, 3))
layer2_ptr = (layer2_ptr_list[2] << 16 | layer2_ptr_list[1] << 8 | layer2_ptr_list[0])
if layer2_ptr in valid_background_palettes:
level_header[0] &= 0x1F
- level_header[0] |= (world.per_slot_randoms[player].choice(valid_background_palettes[layer2_ptr]) << 5)
+ level_header[0] |= (world.random.choice(valid_background_palettes[layer2_ptr]) << 5)
if layer2_ptr in valid_background_colors:
level_header[1] &= 0x1F
- level_header[1] |= (world.per_slot_randoms[player].choice(valid_background_colors[layer2_ptr]) << 5)
+ level_header[1] |= (world.random.choice(valid_background_colors[layer2_ptr]) << 5)
rom.write_bytes(layer1_ptr, bytes(level_header))
+
+def generate_curated_level_palette_data(rom, world: World):
+ PALETTE_LEVEL_CODE_ADDR = 0x88000
+ PALETTE_INDEX_ADDR = 0x8F000
+ PALETTE_LEVEL_TILESET_ADDR = 0x8F200
+ PALETTE_LEVEL_PTR_ADDR = 0x92000
+ PALETTE_LEVEL_DATA_ADDR = 0xA8000
+
+ addr = pc_to_snes(PALETTE_LEVEL_PTR_ADDR)
+ snes_level_palette_pointers_1 = bytearray([0xBF, (addr)&0xFF, (addr>>8)&0xFF, (addr>>16)&0xFF])
+ snes_level_palette_pointers_2 = bytearray([0xBF, (addr+2)&0xFF, (addr>>8)&0xFF, (addr>>16)&0xFF])
+
+ # Enable curated palette loader
+ rom.write_bytes(0x02BED, bytearray([0x5C, 0x00, 0x80, 0x11])) # org $00ABED : jml custom_palettes
+ rom.write_bytes(0x02330, bytearray([0x5C, 0x02, 0x80, 0x11])) # org $00A318 : jml custom_palettes_original
+ rom.write_bytes(0x013D7, bytearray([0x20, 0x30, 0xA3])) # org $0093D7 : jmp $A330
+ rom.write_bytes(0x014DA, bytearray([0x20, 0x30, 0xA3])) # org $0094DA : jmp $A330
+ rom.write_bytes(0x015EC, bytearray([0x20, 0x30, 0xA3])) # org $0095EC : jmp $A330
+ rom.write_bytes(0x0165B, bytearray([0x20, 0x30, 0xA3])) # org $00965B : jmp $A330
+ rom.write_bytes(0x02DD9, bytearray([0x20, 0x30, 0xA3])) # org $00ADD9 : jmp $A330
+ rom.write_bytes(0x02E1F, bytearray([0x20, 0x30, 0xA3])) # org $00AE1F : jmp $A330
+
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0000, bytearray([0x80, 0x09])) # bra custom_palettes
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0002, bytearray([0xC2, 0x30])) # .original rep #$30
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0004, bytearray([0xA9, 0xDD, 0x7F])) # lda #$7FDD
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0007, bytearray([0x5C, 0xF2, 0xAB, 0x00])) # jml $00ABF2
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x000B, bytearray([0xC2, 0x30])) # custom_palettes: rep #$30
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x000D, bytearray([0xA9, 0x70, 0xB1])) # lda #$B170
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0010, bytearray([0x85, 0x0A])) # sta !_ptr
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0012, bytearray([0x64, 0x0C])) # stz !_ptr+$02
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0014, bytearray([0xA9, 0x10, 0x00])) # lda.w #$0010
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0017, bytearray([0x85, 0x04])) # sta !_index
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0019, bytearray([0xA9, 0x07, 0x00])) # lda #$0007
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x001C, bytearray([0x85, 0x06])) # sta !_x_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x001E, bytearray([0xA9, 0x01, 0x00])) # lda #$0001
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0021, bytearray([0x85, 0x08])) # sta !_y_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0023, bytearray([0x20, 0xE4, 0x80])) # jsr load_colors
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0026, bytearray([0xAE, 0x0B, 0x01])) # .get_index ldx $010B
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0029, bytearray([0xBF, 0x00, 0xF2, 0x11])) # lda.l level_tilesets,x
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x002D, bytearray([0x29, 0xFF, 0x00])) # and #$00FF
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0030, bytearray([0xEB])) # xba
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0031, bytearray([0x85, 0x00])) # sta !_tileset
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0033, bytearray([0xBF, 0x00, 0xF0, 0x11])) # lda.l level_index,x
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0037, bytearray([0x29, 0xFF, 0x00])) # and #$00FF
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x003A, bytearray([0x05, 0x00])) # ora !_tileset
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x003C, bytearray([0x85, 0x0A])) # sta !_ptr
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x003E, bytearray([0x0A])) # asl
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x003F, bytearray([0x18])) # clc
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0040, bytearray([0x65, 0x0A])) # adc !_ptr
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0042, bytearray([0x85, 0x0E])) # sta !_num
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0044, bytearray([0xAA])) # tax
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0045, snes_level_palette_pointers_1) # .back_color lda.l palette_pointers,x
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0049, bytearray([0x85, 0x0A])) # sta !_ptr
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x004B, snes_level_palette_pointers_2) # lda.l palette_pointers+$02,x
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x004F, bytearray([0x85, 0x0C])) # sta !_ptr+$02
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0051, bytearray([0xA7, 0x0A])) # lda [!_ptr]
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0053, bytearray([0x8D, 0x01, 0x07])) # sta $0701
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0056, bytearray([0xE6, 0x0A])) # inc !_ptr
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0058, bytearray([0xE6, 0x0A])) # inc !_ptr
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x005A, bytearray([0xA9, 0x02, 0x00])) # .background lda.w #$0001*$02
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x005D, bytearray([0x85, 0x04])) # sta !_index
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x005F, bytearray([0xA9, 0x06, 0x00])) # lda #$0006
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0062, bytearray([0x85, 0x06])) # sta !_x_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0064, bytearray([0xA9, 0x01, 0x00])) # lda #$0001
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0067, bytearray([0x85, 0x08])) # sta !_y_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0069, bytearray([0x20, 0xE4, 0x80])) # jsr load_colors
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x006C, bytearray([0xA9, 0x42, 0x00])) # .foreground lda.w #$0021*$02
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x006F, bytearray([0x85, 0x04])) # sta !_index
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0071, bytearray([0xA9, 0x06, 0x00])) # lda #$0006
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0074, bytearray([0x85, 0x06])) # sta !_x_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0076, bytearray([0xA9, 0x01, 0x00])) # lda #$0001
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0079, bytearray([0x85, 0x08])) # sta !_y_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x007B, bytearray([0x20, 0xE4, 0x80])) # jsr load_colors
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x007E, bytearray([0xA9, 0x52, 0x00])) # .berries lda.w #$0029*$02
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0081, bytearray([0x85, 0x04])) # sta !_index
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0083, bytearray([0xA9, 0x06, 0x00])) # lda #$0006
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0086, bytearray([0x85, 0x06])) # sta !_x_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0088, bytearray([0xA9, 0x02, 0x00])) # lda #$0002
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x008B, bytearray([0x85, 0x08])) # sta !_y_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x008D, bytearray([0xA5, 0x0A])) # lda !_ptr
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x008F, bytearray([0x48])) # pha
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0090, bytearray([0x20, 0xE4, 0x80])) # jsr load_colors
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0093, bytearray([0x68])) # pla
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0094, bytearray([0x85, 0x0A])) # sta !_ptr
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0096, bytearray([0xA9, 0x32, 0x01])) # lda.w #$0099*$02
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0099, bytearray([0x85, 0x04])) # sta !_index
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x009B, bytearray([0xA9, 0x06, 0x00])) # lda #$0006
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x009E, bytearray([0x85, 0x06])) # sta !_x_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00A0, bytearray([0xA9, 0x02, 0x00])) # lda #$0002
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00A3, bytearray([0x85, 0x08])) # sta !_y_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00A5, bytearray([0x20, 0xE4, 0x80])) # jsr load_colors
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00A8, bytearray([0xA9, 0x82, 0x00])) # .global lda.w #$0041*$02
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00AB, bytearray([0x85, 0x04])) # sta !_index
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00AD, bytearray([0xA9, 0x06, 0x00])) # lda #$0006
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00B0, bytearray([0x85, 0x06])) # sta !_x_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00B2, bytearray([0xA9, 0x0B, 0x00])) # lda #$000B
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00B5, bytearray([0x85, 0x08])) # sta !_y_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00B7, bytearray([0x20, 0xE4, 0x80])) # jsr load_colors
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00BA, bytearray([0xA5, 0x00])) # .sprite_specific lda !_tileset
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00BC, bytearray([0xC9, 0x00, 0x05])) # cmp #$0500
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00BF, bytearray([0xD0, 0x1D])) # bne .end
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00C1, bytearray([0xAD, 0x2E, 0x19])) # lda $192E
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00C4, bytearray([0x29, 0x0F, 0x00])) # and #$000F
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00C7, bytearray([0xC9, 0x02, 0x00])) # cmp #$0002
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00CA, bytearray([0xD0, 0x12])) # bne .end
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00CC, bytearray([0xA9, 0xC2, 0x01])) # lda.w #$00E1*$02
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00CF, bytearray([0x85, 0x04])) # sta !_index
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00D1, bytearray([0xA9, 0x06, 0x00])) # lda #$0006
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00D4, bytearray([0x85, 0x06])) # sta !_x_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00D6, bytearray([0xA9, 0x01, 0x00])) # lda #$0001
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00D9, bytearray([0x85, 0x08])) # sta !_y_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00DB, bytearray([0x20, 0xE4, 0x80])) # jsr load_colors
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00DE, bytearray([0xE2, 0x30])) # .end sep #$30
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00E0, bytearray([0x5C, 0xEC, 0xAC, 0x00])) # jml $00ACEC
+
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00E4, bytearray([0xA6, 0x04])) # load_colors: ldx !_index
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00E6, bytearray([0xA4, 0x06])) # ldy !_x_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00E8, bytearray([0xA7, 0x0A])) # .x_loop lda [!_ptr]
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00EA, bytearray([0x9D, 0x03, 0x07])) # sta $0703,x
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00ED, bytearray([0xE6, 0x0A])) # inc !_ptr
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00EF, bytearray([0xE6, 0x0A])) # inc !_ptr
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00F1, bytearray([0xE8])) # inx
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00F2, bytearray([0xE8])) # inx
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00F3, bytearray([0x88])) # dey
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00F4, bytearray([0x10, 0xF2])) # bpl .x_loop
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00F6, bytearray([0xA5, 0x04])) # lda !_index
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00F8, bytearray([0x18])) # clc
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00F9, bytearray([0x69, 0x20, 0x00])) # adc #$0020
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00FC, bytearray([0x85, 0x04])) # sta !_index
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00FE, bytearray([0xC6, 0x08])) # dec !_y_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0100, bytearray([0x10, 0xE2])) # bpl load_colors
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0102, bytearray([0x60])) # rts
+
+ # Load palette paths
+ data = pkgutil.get_data(__name__, f"data/palettes/level/palettes.json").decode("utf-8")
+ tilesets = json.loads(data)
+
+ # Writes the level tileset index to ROM
+ rom.write_bytes(PALETTE_LEVEL_TILESET_ADDR, bytearray(level_palette_index))
+
+ # Builds the table in ROM that holds the palette index for each level, including sublevels
+ for level_id in range(0x200):
+ tileset_num = level_palette_index[level_id]
+ if tileset_num != 0xFF:
+ tileset = tileset_names[tileset_num]
+ else:
+ tileset = tileset_names[0x19]
+ palette = world.random.randint(0, len(tilesets[tileset])-1)
+ rom.write_bytes(PALETTE_INDEX_ADDR + level_id, bytearray([palette]))
+
+ # Writes the actual level palette data and pointer to said data to the ROM
+ pal_offset = 0x0000
+ tileset_num = 0
+ bank_palette_count = 0
+ for tileset in tilesets.keys():
+ for palette in range(len(tilesets[tileset])):
+ # Handle bank crossing
+ if bank_palette_count == 110:
+ pal_offset = (pal_offset & 0xF8000) + 0x8000
+ bank_palette_count = 0
+ # Write pointer
+ data_ptr = pc_to_snes(PALETTE_LEVEL_DATA_ADDR + pal_offset)
+ rom.write_bytes(PALETTE_LEVEL_PTR_ADDR + ((tileset_num*3)<<8) + (palette*3), bytearray([data_ptr & 0xFF, (data_ptr>>8)&0xFF, (data_ptr>>16)&0xFF]))
+ # Write data
+ rom.write_bytes(PALETTE_LEVEL_DATA_ADDR + pal_offset, read_palette_file(tileset, tilesets[tileset][palette], "level"))
+ pal_offset += 0x128
+ bank_palette_count += 1
+ tileset_num += 1
+
+ # Fix eaten berry tiles
+ EATEN_BERRY_ADDR = 0x68248
+ rom.write_byte(EATEN_BERRY_ADDR + 0x01, 0x04)
+ rom.write_byte(EATEN_BERRY_ADDR + 0x03, 0x04)
+ rom.write_byte(EATEN_BERRY_ADDR + 0x05, 0x04)
+ rom.write_byte(EATEN_BERRY_ADDR + 0x07, 0x04)
+
+ # Fix title screen changing background colors
+ rom.write_bytes(0x1D30, bytearray([0xEA, 0xEA, 0xEA]))
+
+ # Skips level intros automatically
+ rom.write_byte(0x4896, 0x80)
+
+def generate_curated_map_palette_data(rom, world: World):
+ PALETTE_MAP_CODE_ADDR = 0x88200
+ PALETTE_UPLOADER_EDIT = 0x88400
+ PALETTE_MAP_INDEX_ADDR = 0x8F400
+ PALETTE_MAP_PTR_ADDR = 0x90000
+ PALETTE_MAP_DATA_ADDR = 0x98000
+
+ addr = pc_to_snes(PALETTE_MAP_PTR_ADDR)
+ snes_map_palette_pointers_1 = bytearray([0xBF, (addr)&0xFF, (addr>>8)&0xFF, (addr>>16)&0xFF])
+ snes_map_palette_pointers_2 = bytearray([0xBF, (addr+2)&0xFF, (addr>>8)&0xFF, (addr>>16)&0xFF])
+
+ rom.write_bytes(0x02D25, bytearray([0x5C, 0x09, 0x82, 0x11])) # org $00AD25 : jml map_palettes
+
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0000, bytearray([0xC2, 0x30])) # map_og_palettes: rep #$30
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0002, bytearray([0xA0, 0xD8, 0xB3])) # ldy #$B3D8
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0005, bytearray([0x5C, 0x2A, 0xAD, 0x00])) # jml $00AD2A
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0009, bytearray([0xC2, 0x30])) # map_palettes: rep #$30
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x000B, bytearray([0xAD, 0x31, 0x19])) # .prepare_index lda $1931
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x000E, bytearray([0x29, 0x0F, 0x00])) # and #$000F
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0011, bytearray([0x3A])) # dec
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0012, bytearray([0xAA])) # tax
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0013, bytearray([0xEB])) # xba
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0014, bytearray([0x85, 0x0E])) # sta !_num
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0016, bytearray([0xBF, 0x00, 0xF4, 0x11])) # lda.l map_index,x
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x001A, bytearray([0x29, 0xFF, 0x00])) # and #$00FF
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x001D, bytearray([0x05, 0x0E])) # ora !_num
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x001F, bytearray([0x85, 0x0A])) # sta !_ptr
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0021, bytearray([0x0A])) # asl
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0022, bytearray([0x18])) # clc
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0023, bytearray([0x65, 0x0A])) # adc !_ptr
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0025, bytearray([0xAA])) # tax
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0026, snes_map_palette_pointers_1) # lda.l map_palette_pointers,x
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x002A, bytearray([0x85, 0x0A])) # sta !_ptr
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x002C, snes_map_palette_pointers_2) # lda.l map_palette_pointers+$02,x
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0030, bytearray([0x85, 0x0C])) # sta !_ptr+$02
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0032, bytearray([0xA7, 0x0A])) # .load_back_color lda [!_ptr]
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0034, bytearray([0x8D, 0x01, 0x07])) # sta $0701
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0037, bytearray([0xE6, 0x0A])) # inc !_ptr
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0039, bytearray([0xE6, 0x0A])) # inc !_ptr
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x003B, bytearray([0xA9, 0x82, 0x00])) # .load_layer_2 lda.w #$0041*$02
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x003E, bytearray([0x85, 0x04])) # sta !_index
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0040, bytearray([0xA9, 0x06, 0x00])) # lda #$0006
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0043, bytearray([0x85, 0x06])) # sta !_x_span
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0045, bytearray([0xA9, 0x03, 0x00])) # lda #$0003
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0048, bytearray([0x85, 0x08])) # sta !_y_span
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x004A, bytearray([0x20, 0xE4, 0x80])) # jsr load_colors
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x004D, bytearray([0xA9, 0x52, 0x00])) # .load_layer_1 lda.w #$0029*$02
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0050, bytearray([0x85, 0x04])) # sta !_index
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0052, bytearray([0xA9, 0x06, 0x00])) # lda #$0006
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0055, bytearray([0x85, 0x06])) # sta !_x_span
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0057, bytearray([0xA9, 0x05, 0x00])) # lda #$0005
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x005A, bytearray([0x85, 0x08])) # sta !_y_span
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x005C, bytearray([0x20, 0xE4, 0x80])) # jsr load_colors
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x005F, bytearray([0xA9, 0x10, 0x00])) # .load_layer_3 lda.w #$0008*$02
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0062, bytearray([0x85, 0x04])) # sta !_index
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0064, bytearray([0xA9, 0x07, 0x00])) # lda #$0007
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0067, bytearray([0x85, 0x06])) # sta !_x_span
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0069, bytearray([0xA9, 0x01, 0x00])) # lda #$0001
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x006C, bytearray([0x85, 0x08])) # sta !_y_span
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x006E, bytearray([0x20, 0xE4, 0x80])) # jsr load_colors
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0071, bytearray([0xA9, 0x02, 0x01])) # .load_sprites lda.w #$0081*$02
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0074, bytearray([0x85, 0x04])) # sta !_index
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0076, bytearray([0xA9, 0x06, 0x00])) # lda #$0006
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0079, bytearray([0x85, 0x06])) # sta !_x_span
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x007B, bytearray([0xA9, 0x07, 0x00])) # lda #$0007
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x007E, bytearray([0x85, 0x08])) # sta !_y_span
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0080, bytearray([0x20, 0xE4, 0x80])) # jsr load_colors
+ rom.write_bytes(PALETTE_MAP_CODE_ADDR + 0x0083, bytearray([0x5C, 0xA3, 0xAD, 0x00])) # .return jml $00ADA3
+
+ rom.write_bytes(0x2488, bytearray([0x5C, 0x00, 0x84, 0x11])) # org $00A488 : jml palette_upload
+
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x0000, bytearray([0xAD, 0x00, 0x01])) # palette_upload: lda $0100
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x0003, bytearray([0xC9, 0x0E])) # cmp #$0E
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x0005, bytearray([0xF0, 0x0A])) # beq .map
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x0007, bytearray([0xAC, 0x80, 0x06])) # .regular ldy $0680
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x000A, bytearray([0xBE, 0x81, 0xA4])) # ldx.w $A47F+2,y
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x000D, bytearray([0x5C, 0x8E, 0xA4, 0x00])) # jml $00A48E
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x0011, bytearray([0xAD, 0xD9, 0x13])) # .map lda $13D9
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x0014, bytearray([0xC9, 0x0A])) # cmp #$0A
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x0016, bytearray([0xD0, 0xEF])) # bne .regular
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x0018, bytearray([0xAD, 0xE8, 0x1D])) # lda $1DE8
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x001B, bytearray([0xC9, 0x06])) # cmp #$06
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x001D, bytearray([0xD0, 0xE8])) # bne .regular
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x001F, bytearray([0x9C, 0x03, 0x07])) # stz $0703
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x0022, bytearray([0x9C, 0x04, 0x07])) # stz $0704
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x0025, bytearray([0x9C, 0x21, 0x21])) # stz $2121
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x0028, bytearray([0xA2, 0x06])) # ldx #$06
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x002A, bytearray([0xBD, 0x49, 0x92])) # .loop lda.w $9249,x
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x002D, bytearray([0x9D, 0x20, 0x43])) # sta $4320,x
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x0030, bytearray([0xCA])) # dex
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x0031, bytearray([0x10, 0xF7])) # bpl .loop
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x0033, bytearray([0xA9, 0x04])) # lda #$04
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x0035, bytearray([0x8D, 0x0B, 0x42])) # sta $420B
+ rom.write_bytes(PALETTE_UPLOADER_EDIT + 0x0038, bytearray([0x5C, 0xCF, 0xA4, 0x00])) # jml $00A4CF
+
+ # Insert this piece of ASM again in case levels are disabled
+ PALETTE_LEVEL_CODE_ADDR = 0x88000
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00E4, bytearray([0xA6, 0x04])) # load_colors: ldx !_index
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00E6, bytearray([0xA4, 0x06])) # ldy !_x_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00E8, bytearray([0xA7, 0x0A])) # .x_loop lda [!_ptr]
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00EA, bytearray([0x9D, 0x03, 0x07])) # sta $0703,x
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00ED, bytearray([0xE6, 0x0A])) # inc !_ptr
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00EF, bytearray([0xE6, 0x0A])) # inc !_ptr
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00F1, bytearray([0xE8])) # inx
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00F2, bytearray([0xE8])) # inx
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00F3, bytearray([0x88])) # dey
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00F4, bytearray([0x10, 0xF2])) # bpl .x_loop
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00F6, bytearray([0xA5, 0x04])) # lda !_index
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00F8, bytearray([0x18])) # clc
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00F9, bytearray([0x69, 0x20, 0x00])) # adc #$0020
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00FC, bytearray([0x85, 0x04])) # sta !_index
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x00FE, bytearray([0xC6, 0x08])) # dec !_y_span
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0100, bytearray([0x10, 0xE2])) # bpl load_colors
+ rom.write_bytes(PALETTE_LEVEL_CODE_ADDR + 0x0102, bytearray([0x60])) # rts
+
+ # Load palette paths
+ data = pkgutil.get_data(__name__, f"data/palettes/map/palettes.json").decode("utf-8")
+ maps = json.loads(data)
+
+ for map_id in range(0x07):
+ current_map_name = map_names[map_id]
+ palette = world.random.randint(0, len(maps[current_map_name])-1)
+ rom.write_bytes(PALETTE_MAP_INDEX_ADDR + map_id, bytearray([palette]))
+
+ # Writes the actual map palette data and pointer to said data to the ROM
+ pal_offset = 0x0000
+ map_num = 0
+ bank_palette_count = 0
+ for current_map in maps.keys():
+ for palette in range(len(maps[current_map])):
+ # Handle bank crossing
+ if bank_palette_count == 113:
+ pal_offset = (pal_offset & 0xF8000) + 0x8000
+ bank_palette_count = 0
+ # Write pointer
+ data_ptr = pc_to_snes(PALETTE_MAP_DATA_ADDR + pal_offset)
+ rom.write_bytes(PALETTE_MAP_PTR_ADDR + ((map_num*3)<<8) + (palette*3), bytearray([data_ptr & 0xFF, (data_ptr>>8)&0xFF, (data_ptr>>16)&0xFF]))
+ # Write data
+ rom.write_bytes(PALETTE_MAP_DATA_ADDR + pal_offset, read_palette_file(current_map, maps[current_map][palette], "map"))
+ # Update map mario palette
+ chosen_palette = world.options.mario_palette.value
+ rom.write_bytes(PALETTE_MAP_DATA_ADDR + pal_offset + 206, bytes(ow_mario_palettes[chosen_palette]))
+ pal_offset += 0x11C
+ bank_palette_count += 1
+ map_num += 1
+
+
+def pc_to_snes(address):
+ return ((address << 1) & 0x7F0000) | (address & 0x7FFF) | 0x8000
+
+def read_palette_file(tileset, filename, type_):
+ palette_file = pkgutil.get_data(__name__, f"data/palettes/{type_}/{tileset}/{filename}")
+ colors = bytearray([])
+
+ # Copy back colors
+ colors += bytearray([palette_file[0x200], palette_file[0x201]])
+
+ if type_ == "level":
+ # Copy background colors
+ colors += bytearray([palette_file[(0x01*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x11*2)+(i)] for i in range(14)])
+
+ # Copy foreground colors
+ colors += bytearray([palette_file[(0x21*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x31*2)+(i)] for i in range(14)])
+
+ # Copy berry colors
+ colors += bytearray([palette_file[(0x29*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x39*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x49*2)+(i)] for i in range(14)])
+
+ # Copy global colors
+ colors += bytearray([palette_file[(0x41*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x51*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x61*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x71*2)+(i)] for i in range(14)])
+
+ # Copy sprite colors
+ colors += bytearray([palette_file[(0x81*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x91*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0xA1*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0xB1*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0xC1*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0xD1*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0xE1*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0xF1*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0xE9*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0xF9*2)+(i)] for i in range(14)])
+
+ elif type_ == "map":
+ # Copy layer 2 colors
+ colors += bytearray([palette_file[(0x41*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x51*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x61*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x71*2)+(i)] for i in range(14)])
+
+ # Copy layer 1 colors
+ colors += bytearray([palette_file[(0x29*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x39*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x49*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x59*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x69*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x79*2)+(i)] for i in range(14)])
+
+ # Copy layer 3 colors
+ colors += bytearray([palette_file[(0x08*2)+(i)] for i in range(16)])
+ colors += bytearray([palette_file[(0x18*2)+(i)] for i in range(16)])
+
+ # Copy sprite colors
+ colors += bytearray([palette_file[(0x81*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0x91*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0xA1*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0xB1*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0xC1*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0xD1*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0xE1*2)+(i)] for i in range(14)])
+ colors += bytearray([palette_file[(0xF1*2)+(i)] for i in range(14)])
+
+ return colors
diff --git a/worlds/smw/CHANGELOG.md b/worlds/smw/CHANGELOG.md
new file mode 100644
index 000000000000..7f62997adcef
--- /dev/null
+++ b/worlds/smw/CHANGELOG.md
@@ -0,0 +1,118 @@
+# Super Mario World - Changelog
+
+
+## v2.0
+
+### Features:
+
+- New optional Location Checks
+ - 3-Up Moons
+ - Hidden 1-Ups
+ - Bonus Blocks
+ - Blocksanity
+ - All blocks that contain coins or items are included, with the exception of:
+ - Blocks in Top Secret Area & Front Door/Bowser Castle
+ - Blocks that are unreachable without glitches/unreasonable movement
+- New Items
+ - Special Zone Clear
+ - New Filler Items
+ - 1 Coin
+ - 5 Coins
+ - 10 Coins
+ - 50 Coins
+ - New Trap Items
+ - Reverse Trap
+ - Thwimp Trap
+- SFX Shuffle
+- Palette Shuffle Overhaul
+ - New Curated Palette can now be used for the Overworld and Level Palette Shuffle options
+ - Foreground and Background Shuffle options have been merged into a single option
+- Max possible Yoshi Egg value is 255
+ - UI in-game is updated to handle 3-digits
+ - New `Display Received Item Popups` option: `progression_minus_yoshi_eggs`
+
+### Quality of Life:
+
+- In-Game Indicators are now displayed on the map screen for location checks and received items
+- In-level sprites are displayed upon receiving certain items
+- The Camera Scroll unlocking is now only enabled on levels where it needs to be
+- SMW can now handle receiving more than 255 items
+- Significant World Code cleanup
+ - New Options API
+ - Removal of `world: MultiWorld` across the world
+- The PopTracker pack now has tabs for every level/sublevel, and can automatically swap tabs while playing if connected to the server
+
+### Bug Fixes:
+
+- Several logic tweaks/fixes
+
+
+## v1.1
+
+### Features:
+
+- New Item
+ - Timer Trap
+- `Bowser Castle Rooms` option which changes the behavior of the Front Door
+- `Boss Shuffle` option
+- `Overworld Palette Shuffle` option
+
+### Quality of Life:
+
+- `Overworld Speed` option which allows the player to move faster or slower on the map
+- `Early Climb` option which guarantees that `Climb` will be found locally in an early location
+- `Exclude Special Zone` option which fully excludes Special Zone levels from the seed
+ - Note: this option is ignored if the player chooses to fill their entire item pool with Yoshi Eggs, which are Progression Items
+- Ice and Stun Traps are now queued if received while another of its type is already active, and are then activated at the next opportunity
+
+### Bug Fixes:
+
+- Fixed `Chocolate Island 4 - Dragon Coins` requiring `Run` instead of `P-Switch`
+- Fixed a Literature Trap typo
+
+
+## v1.0 - First Stable Release
+
+### Features:
+
+- Goal
+ - Bowser
+ - Defeat bosses, reach Bowser's Castle, and defeat Bowser
+ - Yoshi Egg Hunt
+ - Find a certain number of Yoshi Eggs spread across the MultiWorld`
+- Locations included:
+ - Level exits (Normal and Secret)
+ - Dragon Coins
+ - Collect at least five Dragon Coins in a level to send a location check
+- Items included:
+ - Run
+ - Carry
+ - Swim
+ - Spin Jump
+ - Climb
+ - Yoshi
+ - P-Switch
+ - P-Balloon
+ - Progressive Powerup
+ - Unlocks the ability to use Mushrooms, Fire Flowers, and Capes, progressively
+ - Super Star Activate
+ - Yellow Switch Palace
+ - Green Switch Palace
+ - Red Switch Palace
+ - Blue Switch Palace
+ - 1-Up Mushroom
+ - Yoshi Egg
+ - Only on `Yoshi Egg Hunt` goal
+ - Traps
+ - Ice Trap
+ - Stun Trap
+ - Literature Trap
+- `Bowser Castle Doors` option
+ - Whether the Front Door and Back Door map tiles lead to the Front Door or Back Door levels
+- DeathLink is supported
+- Level Shuffle is supported
+- Autosave is supported
+- Music Shuffle is supported
+- Mario's palette can be selected
+- Level Palettes can be shuffled
+- Starting life count can be set
diff --git a/worlds/smw/Client.py b/worlds/smw/Client.py
index 92aeac4d4a2b..33a74b3dc80f 100644
--- a/worlds/smw/Client.py
+++ b/worlds/smw/Client.py
@@ -1,5 +1,4 @@
import logging
-import asyncio
import time
from NetUtils import ClientStatus, color
@@ -17,11 +16,19 @@
SMW_ROMHASH_START = 0x7FC0
ROMHASH_SIZE = 0x15
-SMW_PROGRESS_DATA = WRAM_START + 0x1F02
-SMW_DRAGON_COINS_DATA = WRAM_START + 0x1F2F
-SMW_PATH_DATA = WRAM_START + 0x1EA2
-SMW_EVENT_ROM_DATA = ROM_START + 0x2D608
-SMW_ACTIVE_LEVEL_DATA = ROM_START + 0x37F70
+SMW_PROGRESS_DATA = WRAM_START + 0x1F02
+SMW_DRAGON_COINS_DATA = WRAM_START + 0x1F2F
+SMW_PATH_DATA = WRAM_START + 0x1EA2
+SMW_EVENT_ROM_DATA = ROM_START + 0x2D608
+SMW_ACTIVE_LEVEL_DATA = ROM_START + 0x37F70
+SMW_MOON_DATA = WRAM_START + 0x1FEE
+SMW_HIDDEN_1UP_DATA = WRAM_START + 0x1F3C
+SMW_BONUS_BLOCK_DATA = WRAM_START + 0x1A000
+SMW_BLOCKSANITY_DATA = WRAM_START + 0x1A400
+SMW_BLOCKSANITY_FLAGS = WRAM_START + 0x1A010
+SMW_LEVEL_CLEAR_FLAGS = WRAM_START + 0x1A200
+SMW_SPECIAL_WORLD_CLEAR = WRAM_START + 0x1F1E
+
SMW_GOAL_DATA = ROM_START + 0x01BFA0
SMW_REQUIRED_BOSSES_DATA = ROM_START + 0x01BFA1
@@ -31,32 +38,44 @@
SMW_DEATH_LINK_ACTIVE_ADDR = ROM_START + 0x01BFA5
SMW_DRAGON_COINS_ACTIVE_ADDR = ROM_START + 0x01BFA6
SMW_SWAMP_DONUT_GH_ADDR = ROM_START + 0x01BFA7
-
-SMW_GAME_STATE_ADDR = WRAM_START + 0x100
-SMW_MARIO_STATE_ADDR = WRAM_START + 0x71
-SMW_BOSS_STATE_ADDR = WRAM_START + 0xD9B
-SMW_ACTIVE_BOSS_ADDR = WRAM_START + 0x13FC
-SMW_CURRENT_LEVEL_ADDR = WRAM_START + 0x13BF
-SMW_MESSAGE_BOX_ADDR = WRAM_START + 0x1426
-SMW_BONUS_STAR_ADDR = WRAM_START + 0xF48
-SMW_EGG_COUNT_ADDR = WRAM_START + 0x1F24
-SMW_BOSS_COUNT_ADDR = WRAM_START + 0x1F26
-SMW_NUM_EVENTS_ADDR = WRAM_START + 0x1F2E
-SMW_SFX_ADDR = WRAM_START + 0x1DFC
-SMW_PAUSE_ADDR = WRAM_START + 0x13D4
-SMW_MESSAGE_QUEUE_ADDR = WRAM_START + 0xC391
-
-SMW_RECV_PROGRESS_ADDR = WRAM_START + 0x1F2B
-
-SMW_GOAL_LEVELS = [0x28, 0x31, 0x32]
-SMW_INVALID_MARIO_STATES = [0x05, 0x06, 0x0A, 0x0C, 0x0D]
-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]
+SMW_MOON_ACTIVE_ADDR = ROM_START + 0x01BFA8
+SMW_HIDDEN_1UP_ACTIVE_ADDR = ROM_START + 0x01BFA9
+SMW_BONUS_BLOCK_ACTIVE_ADDR = ROM_START + 0x01BFAA
+SMW_BLOCKSANITY_ACTIVE_ADDR = ROM_START + 0x01BFAB
+
+
+SMW_GAME_STATE_ADDR = WRAM_START + 0x100
+SMW_MARIO_STATE_ADDR = WRAM_START + 0x71
+SMW_BOSS_STATE_ADDR = WRAM_START + 0xD9B
+SMW_ACTIVE_BOSS_ADDR = WRAM_START + 0x13FC
+SMW_CURRENT_LEVEL_ADDR = WRAM_START + 0x13BF
+SMW_CURRENT_SUBLEVEL_ADDR = WRAM_START + 0x10B
+SMW_MESSAGE_BOX_ADDR = WRAM_START + 0x1426
+SMW_BONUS_STAR_ADDR = WRAM_START + 0xF48
+SMW_EGG_COUNT_ADDR = WRAM_START + 0x1F24
+SMW_BOSS_COUNT_ADDR = WRAM_START + 0x1F26
+SMW_NUM_EVENTS_ADDR = WRAM_START + 0x1F2E
+SMW_SFX_ADDR = WRAM_START + 0x1DFC
+SMW_PAUSE_ADDR = WRAM_START + 0x13D4
+SMW_MESSAGE_QUEUE_ADDR = WRAM_START + 0xC391
+SMW_ACTIVE_THWIMP_ADDR = WRAM_START + 0x0F3C
+SMW_GOAL_ITEM_COUNT = WRAM_START + 0x1A01E
+
+SMW_RECV_PROGRESS_ADDR = WRAM_START + 0x01F2B
+
+SMW_BLOCKSANITY_BLOCK_COUNT = 582
+
+SMW_GOAL_LEVELS = [0x28, 0x31, 0x32]
+SMW_INVALID_MARIO_STATES = [0x05, 0x06, 0x0A, 0x0C, 0x0D]
+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]
+SMW_UNCOLLECTABLE_DRAGON_COINS = [0x24]
class SMWSNIClient(SNIClient):
game = "Super Mario World"
+ patch_suffix = ".apsmw"
async def deathlink_kill_player(self, ctx):
from SNIClient import DeathState, snes_buffered_write, snes_flush_writes, snes_read
@@ -114,6 +133,9 @@ async def validate_rom(self, ctx):
if death_link:
await ctx.update_death_link(bool(death_link[0] & 0b1))
+ if ctx.rom != rom_name:
+ ctx.current_sublevel_value = 0
+
ctx.rom = rom_name
return True
@@ -175,6 +197,11 @@ def add_trap_to_queue(self, trap_item, trap_msg):
self.trap_queue.append((trap_item, trap_msg))
+ def should_show_message(self, ctx, next_item):
+ return ctx.receive_option == 1 or \
+ (ctx.receive_option == 2 and ((next_item.flags & 1) != 0)) or \
+ (ctx.receive_option == 3 and ((next_item.flags & 1) != 0 and next_item.item != 0xBC0002))
+
async def handle_trap_queue(self, ctx):
from SNIClient import snes_buffered_write, snes_flush_writes, snes_read
@@ -216,6 +243,13 @@ async def handle_trap_queue(self, ctx):
self.add_trap_to_queue(next_trap, message)
return
else:
+ if next_trap.item == 0xBC001D:
+ # Special case thwimp trap
+ # Do not fire if the previous thwimp hasn't reached the player's Y pos
+ active_thwimp = await snes_read(ctx, SMW_ACTIVE_THWIMP_ADDR, 0x1)
+ if active_thwimp[0] != 0xFF:
+ self.add_trap_to_queue(next_trap, message)
+ return
verify_game_state = await snes_read(ctx, SMW_GAME_STATE_ADDR, 0x1)
if verify_game_state[0] == 0x14 and len(trap_rom_data[next_trap.item]) > 2:
snes_buffered_write(ctx, SMW_SFX_ADDR, bytes([trap_rom_data[next_trap.item][2]]))
@@ -235,13 +269,14 @@ async def handle_trap_queue(self, ctx):
if active_boss[0] != 0x00:
return
- if ctx.receive_option == 1 or (ctx.receive_option == 2 and ((next_trap.flags & 1) != 0)):
+ if self.should_show_message(ctx, next_trap):
self.add_message_to_queue(message)
async def game_watcher(self, ctx):
from SNIClient import snes_buffered_write, snes_flush_writes, snes_read
-
+
+ boss_state = await snes_read(ctx, SMW_BOSS_STATE_ADDR, 0x1)
game_state = await snes_read(ctx, SMW_GAME_STATE_ADDR, 0x1)
mario_state = await snes_read(ctx, SMW_MARIO_STATE_ADDR, 0x1)
if game_state is None:
@@ -258,6 +293,7 @@ async def game_watcher(self, ctx):
elif game_state[0] < 0x0B:
# We haven't loaded a save file
ctx.message_queue = []
+ ctx.current_sublevel_value = 0
return
elif mario_state[0] in SMW_INVALID_MARIO_STATES:
# Mario can't come to the phone right now
@@ -303,8 +339,18 @@ async def game_watcher(self, ctx):
progress_data = bytearray(await snes_read(ctx, SMW_PROGRESS_DATA, 0x0F))
dragon_coins_data = bytearray(await snes_read(ctx, SMW_DRAGON_COINS_DATA, 0x0C))
dragon_coins_active = await snes_read(ctx, SMW_DRAGON_COINS_ACTIVE_ADDR, 0x1)
- from worlds.smw.Rom import item_rom_data, ability_rom_data, trap_rom_data
- from worlds.smw.Levels import location_id_to_level_id, level_info_dict
+ moon_data = bytearray(await snes_read(ctx, SMW_MOON_DATA, 0x0C))
+ moon_active = await snes_read(ctx, SMW_MOON_ACTIVE_ADDR, 0x1)
+ hidden_1up_data = bytearray(await snes_read(ctx, SMW_HIDDEN_1UP_DATA, 0x0C))
+ hidden_1up_active = await snes_read(ctx, SMW_HIDDEN_1UP_ACTIVE_ADDR, 0x1)
+ bonus_block_data = bytearray(await snes_read(ctx, SMW_BONUS_BLOCK_DATA, 0x0C))
+ bonus_block_active = await snes_read(ctx, SMW_BONUS_BLOCK_ACTIVE_ADDR, 0x1)
+ blocksanity_data = bytearray(await snes_read(ctx, SMW_BLOCKSANITY_DATA, SMW_BLOCKSANITY_BLOCK_COUNT))
+ blocksanity_flags = bytearray(await snes_read(ctx, SMW_BLOCKSANITY_FLAGS, 0xC))
+ blocksanity_active = await snes_read(ctx, SMW_BLOCKSANITY_ACTIVE_ADDR, 0x1)
+ level_clear_flags = bytearray(await snes_read(ctx, SMW_LEVEL_CLEAR_FLAGS, 0x60))
+ from worlds.smw.Rom import item_rom_data, ability_rom_data, trap_rom_data, icon_rom_data
+ from worlds.smw.Levels import location_id_to_level_id, level_info_dict, level_blocks_data
from worlds import AutoWorldRegister
for loc_name, level_data in location_id_to_level_id.items():
loc_id = AutoWorldRegister.world_types[ctx.game].location_name_to_id[loc_name]
@@ -326,6 +372,54 @@ async def game_watcher(self, ctx):
if bit_set:
new_checks.append(loc_id)
+ elif level_data[1] == 3:
+ # Moon Check
+ if not moon_active or moon_active[0] == 0:
+ continue
+
+ progress_byte = (level_data[0] // 8)
+ progress_bit = 7 - (level_data[0] % 8)
+
+ data = moon_data[progress_byte]
+ masked_data = data & (1 << progress_bit)
+ bit_set = (masked_data != 0)
+
+ if bit_set:
+ new_checks.append(loc_id)
+ elif level_data[1] == 4:
+ # Hidden 1-Up Check
+ if not hidden_1up_active or hidden_1up_active[0] == 0:
+ continue
+
+ progress_byte = (level_data[0] // 8)
+ progress_bit = 7 - (level_data[0] % 8)
+
+ data = hidden_1up_data[progress_byte]
+ masked_data = data & (1 << progress_bit)
+ bit_set = (masked_data != 0)
+
+ if bit_set:
+ new_checks.append(loc_id)
+ elif level_data[1] == 5:
+ # Bonus Block Check
+ if not bonus_block_active or bonus_block_active[0] == 0:
+ continue
+
+ progress_byte = (level_data[0] // 8)
+ progress_bit = 7 - (level_data[0] % 8)
+
+ data = bonus_block_data[progress_byte]
+ masked_data = data & (1 << progress_bit)
+ bit_set = (masked_data != 0)
+
+ if bit_set:
+ new_checks.append(loc_id)
+ elif level_data[1] >= 100:
+ if not blocksanity_active or blocksanity_active[0] == 0:
+ continue
+ block_index = level_data[1] - 100
+ if blocksanity_data[block_index] != 0:
+ new_checks.append(loc_id)
else:
event_id_value = event_id + level_data[1]
@@ -359,12 +453,48 @@ async def game_watcher(self, ctx):
f'New Check: {location} ({len(ctx.locations_checked)}/{len(ctx.missing_locations) + len(ctx.checked_locations)})')
await ctx.send_msgs([{"cmd": 'LocationChecks', "locations": [new_check_id]}])
+ # Send Current Room for Tracker
+ current_sublevel_data = await snes_read(ctx, SMW_CURRENT_SUBLEVEL_ADDR, 2)
+ current_sublevel_value = current_sublevel_data[0] + (current_sublevel_data[1] << 8)
+
+ if game_state[0] != 0x14:
+ current_sublevel_value = 0
+
+ if ctx.current_sublevel_value != current_sublevel_value:
+ ctx.current_sublevel_value = current_sublevel_value
+
+ # Send level id data to tracker
+ await ctx.send_msgs(
+ [
+ {
+ "cmd": "Set",
+ "key": f"smw_curlevelid_{ctx.team}_{ctx.slot}",
+ "default": 0,
+ "want_reply": False,
+ "operations": [
+ {
+ "operation": "replace",
+ "value": ctx.current_sublevel_value,
+ }
+ ],
+ }
+ ]
+ )
+
if game_state[0] != 0x14:
# Don't receive items or collect locations outside of in-level mode
+ ctx.current_sublevel_value = 0
+ return
+
+ if boss_state[0] in SMW_BOSS_STATES:
+ # Don't receive items or collect locations inside boss battles
return
- recv_count = await snes_read(ctx, SMW_RECV_PROGRESS_ADDR, 1)
- recv_index = recv_count[0]
+ recv_count = await snes_read(ctx, SMW_RECV_PROGRESS_ADDR, 2)
+ if recv_count is None:
+ # Add a small failsafe in case we get a None. Other SNI games do this...
+ return
+ recv_index = recv_count[0] | (recv_count[1] << 8)
if recv_index < len(ctx.items_received):
item = ctx.items_received[recv_index]
@@ -374,7 +504,7 @@ async def game_watcher(self, ctx):
color(ctx.player_names[item.player], 'yellow'),
ctx.location_names[item.location], recv_index, len(ctx.items_received)))
- if ctx.receive_option == 1 or (ctx.receive_option == 2 and ((item.flags & 1) != 0)):
+ if self.should_show_message(ctx, item):
if item.item != 0xBC0012 and item.item not in trap_rom_data:
# Don't send messages for Boss Tokens
item_name = ctx.item_names[item.item]
@@ -383,7 +513,7 @@ async def game_watcher(self, ctx):
receive_message = generate_received_text(item_name, player_name)
self.add_message_to_queue(receive_message)
- snes_buffered_write(ctx, SMW_RECV_PROGRESS_ADDR, bytes([recv_index]))
+ snes_buffered_write(ctx, SMW_RECV_PROGRESS_ADDR, bytes([recv_index&0xFF, (recv_index>>8)&0xFF]))
if item.item in trap_rom_data:
item_name = ctx.item_names[item.item]
player_name = ctx.player_names[item.player]
@@ -404,6 +534,15 @@ async def game_watcher(self, ctx):
snes_buffered_write(ctx, SMW_SFX_ADDR, bytes([item_rom_data[item.item][2]]))
snes_buffered_write(ctx, WRAM_START + item_rom_data[item.item][0], bytes([new_item_count]))
+ elif item.item in icon_rom_data:
+ queue_addr = await snes_read(ctx, WRAM_START + icon_rom_data[item.item][0], 2)
+ queue_addr = queue_addr[0] + (queue_addr[1] << 8)
+ queue_addr += 1
+ snes_buffered_write(ctx, WRAM_START + icon_rom_data[item.item][0], bytes([queue_addr&0xFF, (queue_addr>>8)&0xFF]))
+ if (goal[0] == 0 and item.item == 0xBC0012) or (goal[0] == 1 and item.item == 0xBC0002):
+ goal_item_count = await snes_read(ctx, SMW_GOAL_ITEM_COUNT, 1)
+ snes_buffered_write(ctx, SMW_GOAL_ITEM_COUNT, bytes([goal_item_count[0] + 1]))
+
elif item.item in ability_rom_data:
# Handle Upgrades
for rom_data in ability_rom_data[item.item]:
@@ -448,6 +587,12 @@ async def game_watcher(self, ctx):
path_data = bytearray(await snes_read(ctx, SMW_PATH_DATA, 0x60))
donut_gh_swapped = await snes_read(ctx, SMW_SWAMP_DONUT_GH_ADDR, 0x1)
new_dragon_coin = False
+ new_moon = False
+ new_hidden_1up = False
+ new_bonus_block = False
+ new_blocksanity = False
+ new_blocksanity_flags = False
+
for loc_id in ctx.checked_locations:
if loc_id not in ctx.locations_checked:
ctx.locations_checked.add(loc_id)
@@ -460,6 +605,8 @@ async def game_watcher(self, ctx):
if level_data[1] == 2:
# Dragon Coins Check
+ if level_data[0] in SMW_UNCOLLECTABLE_DRAGON_COINS:
+ continue
progress_byte = (level_data[0] // 8)
progress_bit = 7 - (level_data[0] % 8)
@@ -469,10 +616,64 @@ async def game_watcher(self, ctx):
dragon_coins_data[progress_byte] = new_data
new_dragon_coin = True
+ elif level_data[1] == 3:
+ # Moon Check
+
+ progress_byte = (level_data[0] // 8)
+ progress_bit = 7 - (level_data[0] % 8)
+
+ data = moon_data[progress_byte]
+ new_data = data | (1 << progress_bit)
+ moon_data[progress_byte] = new_data
+
+ new_moon = True
+ elif level_data[1] == 4:
+ # Hidden 1-Up Check
+ progress_byte = (level_data[0] // 8)
+ progress_bit = 7 - (level_data[0] % 8)
+
+ data = hidden_1up_data[progress_byte]
+ new_data = data | (1 << progress_bit)
+ hidden_1up_data[progress_byte] = new_data
+
+ new_hidden_1up = True
+ elif level_data[1] == 5:
+ # Bonus block prize Check
+
+ progress_byte = (level_data[0] // 8)
+ progress_bit = 7 - (level_data[0] % 8)
+
+ data = bonus_block_data[progress_byte]
+ new_data = data | (1 << progress_bit)
+ bonus_block_data[progress_byte] = new_data
+
+ new_bonus_block = True
+ elif level_data[1] >= 100:
+ # Blocksanity flag Check
+ block_index = level_data[1] - 100
+ blocksanity_data[block_index] = 1
+ new_blocksanity = True
+
+ # All blocksanity blocks flag
+ new_blocksanity_flags = True
+ for block_id in level_blocks_data[level_data[0]]:
+ if blocksanity_data[block_id] != 1:
+ new_blocksanity_flags = False
+ continue
+ if new_blocksanity_flags is True:
+ progress_byte = (level_data[0] // 8)
+ progress_bit = 7 - (level_data[0] % 8)
+ data = blocksanity_flags[progress_byte]
+ new_data = data | (1 << progress_bit)
+ blocksanity_flags[progress_byte] = new_data
else:
if level_data[0] in SMW_UNCOLLECTABLE_LEVELS:
continue
+ # Handle map indicators
+ flag = 1 if level_data[1] == 0 else 2
+ level_clear_flags[level_data[0]] |= flag
+
event_id = event_data[level_data[0]]
event_id_value = event_id + level_data[1]
@@ -513,7 +714,18 @@ async def game_watcher(self, ctx):
if new_dragon_coin:
snes_buffered_write(ctx, SMW_DRAGON_COINS_DATA, bytes(dragon_coins_data))
+ if new_moon:
+ snes_buffered_write(ctx, SMW_MOON_DATA, bytes(moon_data))
+ if new_hidden_1up:
+ snes_buffered_write(ctx, SMW_HIDDEN_1UP_DATA, bytes(hidden_1up_data))
+ if new_bonus_block:
+ snes_buffered_write(ctx, SMW_BONUS_BLOCK_DATA, bytes(bonus_block_data))
+ if new_blocksanity:
+ snes_buffered_write(ctx, SMW_BLOCKSANITY_DATA, bytes(blocksanity_data))
+ if new_blocksanity_flags:
+ snes_buffered_write(ctx, SMW_BLOCKSANITY_FLAGS, bytes(blocksanity_flags))
if new_events > 0:
+ snes_buffered_write(ctx, SMW_LEVEL_CLEAR_FLAGS, bytes(level_clear_flags))
snes_buffered_write(ctx, SMW_PROGRESS_DATA, bytes(progress_data))
snes_buffered_write(ctx, SMW_PATH_DATA, bytes(path_data))
old_events = await snes_read(ctx, SMW_NUM_EVENTS_ADDR, 0x1)
diff --git a/worlds/smw/Items.py b/worlds/smw/Items.py
index 5b6cce5a7f6e..eaf58b9b8e4e 100644
--- a/worlds/smw/Items.py
+++ b/worlds/smw/Items.py
@@ -18,6 +18,10 @@ class SMWItem(Item):
# Separate tables for each type of item.
junk_table = {
+ ItemName.one_coin: ItemData(0xBC0017, False),
+ ItemName.five_coins: ItemData(0xBC0018, False),
+ ItemName.ten_coins: ItemData(0xBC0019, False),
+ ItemName.fifty_coins: ItemData(0xBC001A, False),
ItemName.one_up_mushroom: ItemData(0xBC0001, False),
}
@@ -36,6 +40,7 @@ class SMWItem(Item):
ItemName.progressive_powerup: ItemData(0xBC000A, True),
ItemName.p_balloon: ItemData(0xBC000B, True),
ItemName.super_star_active: ItemData(0xBC000D, True),
+ ItemName.special_world_clear: ItemData(0xBC001B, True),
}
switch_palace_table = {
@@ -46,10 +51,12 @@ class SMWItem(Item):
}
trap_table = {
- ItemName.ice_trap: ItemData(0xBC0013, False, True),
- ItemName.stun_trap: ItemData(0xBC0014, False, True),
- ItemName.literature_trap: ItemData(0xBC0015, False, True),
- ItemName.timer_trap: ItemData(0xBC0016, False, True),
+ ItemName.ice_trap: ItemData(0xBC0013, False, True),
+ ItemName.stun_trap: ItemData(0xBC0014, False, True),
+ ItemName.literature_trap: ItemData(0xBC0015, False, True),
+ ItemName.timer_trap: ItemData(0xBC0016, False, True),
+ ItemName.reverse_controls_trap: ItemData(0xBC001C, False, True),
+ ItemName.thwimp_trap: ItemData(0xBC001D, False, True),
}
event_table = {
diff --git a/worlds/smw/Levels.py b/worlds/smw/Levels.py
index 3940a08c7c5b..7aa9428b9110 100644
--- a/worlds/smw/Levels.py
+++ b/worlds/smw/Levels.py
@@ -1,4 +1,5 @@
+from worlds.AutoWorld import World
from .Names import LocationName
@@ -75,6 +76,103 @@ def __init__(self, name: str, exitAddress: int, roomID: int, exitAddressAlt=None
]
+level_blocks_data = {
+ 0x01: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
+ 0x02: [12, 13],
+ 0x04: [14, 15, 16, 17, 18, 19],
+ 0x05: [20, 21, 22, 23, 24, 25],
+ 0x06: [26, 27, 28, 29],
+ 0x07: [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
+ 0x09: [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
+ 0x0A: [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
+ 0x0B: [60, 61, 62],
+ 0x0C: [63, 64, 65, 66, 67, 68],
+ 0x0D: [69, 70, 71],
+ 0x0E: [72],
+ 0x0F: [73, 74, 75, 76],
+ 0x10: [77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,
+ 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
+ 109, 110, 111
+ ],
+ 0x11: [112],
+ 0x13: [113, 114, 115, 116, 117],
+ 0x15: [118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 135, 136, 137, 138, 139, 140
+ ],
+ 0x18: [141, 142],
+ 0x1A: [143, 144, 145],
+ 0x1B: [146, 147, 148, 149, 150],
+ 0x1C: [151, 152, 153, 154],
+ 0x1D: [155, 156, 157],
+ 0x1F: [158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168],
+ 0x20: [169],
+ 0x21: [170, 171, 172],
+ 0x22: [173, 174, 175, 176, 177],
+ 0x23: [178, 179, 180, 181, 182, 183, 184, 185, 186],
+ 0x24: [187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200,
+ 201, 202
+ ],
+ 0x25: [203, 204, 205, 206, 207, 208],
+ 0x26: [209, 210, 211, 212],
+ 0x27: [213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226,
+ 227, 228, 229
+ ],
+ 0x29: [230, 231, 232, 233],
+ 0x2A: [234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249
+ ],
+ 0x2B: [250, 251, 252, 253, 254],
+ 0x2D: [255, 256, 257, 258, 259, 260, 261, 262],
+ 0x2E: [263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276,
+ 277, 278, 279
+ ],
+ 0x2F: [280, 281, 282, 283, 284],
+ 0x33: [285, 286, 287, 288, 289, 290],
+ 0x34: [291, 292, 293],
+ 0x35: [294, 295],
+ 0x37: [296, 297],
+ 0x38: [298, 299, 300, 301],
+ 0x39: [302, 303, 304, 305],
+ 0x3A: [306, 307, 308, 309, 310, 311, 312, 313, 314],
+ 0x3B: [315, 316],
+ 0x3C: [317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330],
+ 0x3D: [331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341],
+ 0x3E: [342, 343, 344, 345, 346, 347, 348, 349, 350, 351],
+ 0x40: [352, 353, 354, 355, 356],
+ 0x41: [357, 358, 359, 360, 361],
+ 0x42: [362, 363, 364, 365, 366],
+ 0x43: [367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379],
+ 0x44: [380, 381, 382, 383, 384, 385, 386],
+ 0x46: [387, 388, 389],
+ 0x47: [390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403,
+ 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416
+ ],
+ 0x49: [417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430,
+ 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443,
+ 444, 445, 446
+ ],
+ 0x4A: [447, 448, 449, 450, 451],
+ 0x4B: [452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465,
+ 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478,
+ 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489
+ ],
+ 0x4C: [490],
+ 0x4E: [491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504,
+ 505, 506, 507, 508, 509, 510, 511, 512
+ ],
+ 0x4F: [513, 514, 515, 516, 517, 518, 519, 520, 521, 522],
+ 0x50: [523, 524, 525],
+ 0x51: [526, 527],
+ 0x54: [528],
+ 0x56: [529],
+ 0x59: [530, 531, 532, 533, 534, 535, 536, 537, 538],
+ 0x5A: [539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552,
+ 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565,
+ 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578,
+ 579, 580, 581
+ ]
+}
+
class SMWPath():
thisEndDirection: int
otherLevelID: int
@@ -330,12 +428,15 @@ def __init__(self, levelName: str, levelIDAddress: int, eventIDValue: int, exit1
location_id_to_level_id = {
LocationName.yoshis_island_1_exit_1: [0x29, 0],
LocationName.yoshis_island_1_dragon: [0x29, 2],
+ LocationName.yoshis_island_1_moon: [0x29, 3],
LocationName.yoshis_island_2_exit_1: [0x2A, 0],
LocationName.yoshis_island_2_dragon: [0x2A, 2],
LocationName.yoshis_island_3_exit_1: [0x27, 0],
LocationName.yoshis_island_3_dragon: [0x27, 2],
+ LocationName.yoshis_island_3_bonus_block: [0x27, 5],
LocationName.yoshis_island_4_exit_1: [0x26, 0],
LocationName.yoshis_island_4_dragon: [0x26, 2],
+ LocationName.yoshis_island_4_hidden_1up: [0x26, 4],
LocationName.yoshis_island_castle: [0x25, 0],
LocationName.yoshis_island_koopaling: [0x25, 0],
LocationName.yellow_switch_palace: [0x14, 0],
@@ -343,13 +444,17 @@ def __init__(self, levelName: str, levelIDAddress: int, eventIDValue: int, exit1
LocationName.donut_plains_1_exit_1: [0x15, 0],
LocationName.donut_plains_1_exit_2: [0x15, 1],
LocationName.donut_plains_1_dragon: [0x15, 2],
+ LocationName.donut_plains_1_hidden_1up: [0x15, 4],
LocationName.donut_plains_2_exit_1: [0x09, 0],
LocationName.donut_plains_2_exit_2: [0x09, 1],
LocationName.donut_plains_2_dragon: [0x09, 2],
LocationName.donut_plains_3_exit_1: [0x05, 0],
LocationName.donut_plains_3_dragon: [0x05, 2],
+ LocationName.donut_plains_3_bonus_block: [0x05, 5],
LocationName.donut_plains_4_exit_1: [0x06, 0],
LocationName.donut_plains_4_dragon: [0x06, 2],
+ LocationName.donut_plains_4_moon: [0x06, 3],
+ LocationName.donut_plains_4_hidden_1up: [0x06, 4],
LocationName.donut_secret_1_exit_1: [0x0A, 0],
LocationName.donut_secret_1_exit_2: [0x0A, 1],
LocationName.donut_secret_1_dragon: [0x0A, 2],
@@ -360,6 +465,7 @@ def __init__(self, levelName: str, levelIDAddress: int, eventIDValue: int, exit1
LocationName.donut_secret_house_exit_1: [0x13, 0],
LocationName.donut_secret_house_exit_2: [0x13, 1],
LocationName.donut_plains_castle: [0x07, 0],
+ LocationName.donut_plains_castle_hidden_1up: [0x07, 4],
LocationName.donut_plains_koopaling: [0x07, 0],
LocationName.green_switch_palace: [0x08, 0],
@@ -371,8 +477,10 @@ def __init__(self, levelName: str, levelIDAddress: int, eventIDValue: int, exit1
LocationName.vanilla_dome_2_dragon: [0x3C, 2],
LocationName.vanilla_dome_3_exit_1: [0x2E, 0],
LocationName.vanilla_dome_3_dragon: [0x2E, 2],
+ LocationName.vanilla_dome_3_moon: [0x2E, 3],
LocationName.vanilla_dome_4_exit_1: [0x3D, 0],
LocationName.vanilla_dome_4_dragon: [0x3D, 2],
+ LocationName.vanilla_dome_4_hidden_1up: [0x3D, 4],
LocationName.vanilla_secret_1_exit_1: [0x2D, 0],
LocationName.vanilla_secret_1_exit_2: [0x2D, 1],
LocationName.vanilla_secret_1_dragon: [0x2D, 2],
@@ -382,7 +490,9 @@ def __init__(self, levelName: str, levelIDAddress: int, eventIDValue: int, exit1
LocationName.vanilla_secret_3_dragon: [0x02, 2],
LocationName.vanilla_ghost_house_exit_1: [0x2B, 0],
LocationName.vanilla_ghost_house_dragon: [0x2B, 2],
+ LocationName.vanilla_ghost_house_hidden_1up: [0x2B, 4],
LocationName.vanilla_fortress: [0x0B, 0],
+ LocationName.vanilla_fortress_hidden_1up: [0x0B, 4],
LocationName.vanilla_reznor: [0x0B, 0],
LocationName.vanilla_dome_castle: [0x40, 0],
LocationName.vanilla_dome_koopaling: [0x40, 0],
@@ -390,13 +500,16 @@ def __init__(self, levelName: str, levelIDAddress: int, eventIDValue: int, exit1
LocationName.butter_bridge_1_exit_1: [0x0C, 0],
LocationName.butter_bridge_1_dragon: [0x0C, 2],
+ LocationName.butter_bridge_1_bonus_block: [0x0C, 5],
LocationName.butter_bridge_2_exit_1: [0x0D, 0],
LocationName.butter_bridge_2_dragon: [0x0D, 2],
LocationName.cheese_bridge_exit_1: [0x0F, 0],
LocationName.cheese_bridge_exit_2: [0x0F, 1],
LocationName.cheese_bridge_dragon: [0x0F, 2],
+ LocationName.cheese_bridge_moon: [0x0F, 3],
LocationName.cookie_mountain_exit_1: [0x10, 0],
LocationName.cookie_mountain_dragon: [0x10, 2],
+ LocationName.cookie_mountain_hidden_1up: [0x10, 4],
LocationName.soda_lake_exit_1: [0x11, 0],
LocationName.soda_lake_dragon: [0x11, 2],
LocationName.twin_bridges_castle: [0x0E, 0],
@@ -410,12 +523,14 @@ def __init__(self, levelName: str, levelIDAddress: int, eventIDValue: int, exit1
LocationName.forest_of_illusion_3_exit_1: [0x47, 0],
LocationName.forest_of_illusion_3_exit_2: [0x47, 1],
LocationName.forest_of_illusion_3_dragon: [0x47, 2],
+ LocationName.forest_of_illusion_3_hidden_1up: [0x47, 4],
LocationName.forest_of_illusion_4_exit_1: [0x43, 0],
LocationName.forest_of_illusion_4_exit_2: [0x43, 1],
LocationName.forest_of_illusion_4_dragon: [0x43, 2],
LocationName.forest_ghost_house_exit_1: [0x41, 0],
LocationName.forest_ghost_house_exit_2: [0x41, 1],
LocationName.forest_ghost_house_dragon: [0x41, 2],
+ LocationName.forest_ghost_house_moon: [0x41, 3],
LocationName.forest_secret_exit_1: [0x46, 0],
LocationName.forest_secret_dragon: [0x46, 2],
LocationName.forest_fortress: [0x1F, 0],
@@ -427,12 +542,15 @@ def __init__(self, levelName: str, levelIDAddress: int, eventIDValue: int, exit1
LocationName.chocolate_island_1_exit_1: [0x22, 0],
LocationName.chocolate_island_1_dragon: [0x22, 2],
+ LocationName.chocolate_island_1_moon: [0x22, 3],
LocationName.chocolate_island_2_exit_1: [0x24, 0],
LocationName.chocolate_island_2_exit_2: [0x24, 1],
LocationName.chocolate_island_2_dragon: [0x24, 2],
+ LocationName.chocolate_island_2_hidden_1up: [0x24, 4],
LocationName.chocolate_island_3_exit_1: [0x23, 0],
LocationName.chocolate_island_3_exit_2: [0x23, 1],
LocationName.chocolate_island_3_dragon: [0x23, 2],
+ LocationName.chocolate_island_3_bonus_block: [0x23, 5],
LocationName.chocolate_island_4_exit_1: [0x1D, 0],
LocationName.chocolate_island_4_dragon: [0x1D, 2],
LocationName.chocolate_island_5_exit_1: [0x1C, 0],
@@ -442,6 +560,7 @@ def __init__(self, levelName: str, levelIDAddress: int, eventIDValue: int, exit1
LocationName.chocolate_fortress: [0x1B, 0],
LocationName.chocolate_reznor: [0x1B, 0],
LocationName.chocolate_castle: [0x1A, 0],
+ LocationName.chocolate_castle_hidden_1up: [0x1A, 4],
LocationName.chocolate_koopaling: [0x1A, 0],
LocationName.sunken_ghost_ship: [0x18, 0],
@@ -449,9 +568,11 @@ def __init__(self, levelName: str, levelIDAddress: int, eventIDValue: int, exit1
LocationName.valley_of_bowser_1_exit_1: [0x3A, 0],
LocationName.valley_of_bowser_1_dragon: [0x3A, 2],
+ LocationName.valley_of_bowser_1_moon: [0x3A, 3],
LocationName.valley_of_bowser_2_exit_1: [0x39, 0],
LocationName.valley_of_bowser_2_exit_2: [0x39, 1],
LocationName.valley_of_bowser_2_dragon: [0x39, 2],
+ LocationName.valley_of_bowser_2_hidden_1up: [0x39, 4],
LocationName.valley_of_bowser_3_exit_1: [0x37, 0],
LocationName.valley_of_bowser_3_dragon: [0x37, 2],
LocationName.valley_of_bowser_4_exit_1: [0x33, 0],
@@ -464,6 +585,7 @@ def __init__(self, levelName: str, levelIDAddress: int, eventIDValue: int, exit1
LocationName.valley_castle: [0x34, 0],
LocationName.valley_koopaling: [0x34, 0],
LocationName.valley_castle_dragon: [0x34, 2],
+ LocationName.valley_castle_hidden_1up: [0x34, 4],
LocationName.star_road_1_exit_1: [0x58, 0],
LocationName.star_road_1_exit_2: [0x58, 1],
@@ -479,6 +601,7 @@ def __init__(self, levelName: str, levelIDAddress: int, eventIDValue: int, exit1
LocationName.special_zone_1_exit_1: [0x4E, 0],
LocationName.special_zone_1_dragon: [0x4E, 2],
+ LocationName.special_zone_1_hidden_1up: [0x4E, 4],
LocationName.special_zone_2_exit_1: [0x4F, 0],
LocationName.special_zone_2_dragon: [0x4F, 2],
LocationName.special_zone_3_exit_1: [0x50, 0],
@@ -493,19 +616,602 @@ def __init__(self, levelName: str, levelIDAddress: int, eventIDValue: int, exit1
LocationName.special_zone_7_dragon: [0x4A, 2],
LocationName.special_zone_8_exit_1: [0x49, 0],
LocationName.special_zone_8_dragon: [0x49, 2],
+
+ LocationName.vanilla_secret_2_yoshi_block_1: [0x01, 100],
+ LocationName.vanilla_secret_2_green_block_1: [0x01, 101],
+ LocationName.vanilla_secret_2_powerup_block_1: [0x01, 102],
+ LocationName.vanilla_secret_2_powerup_block_2: [0x01, 103],
+ LocationName.vanilla_secret_2_multi_coin_block_1: [0x01, 104],
+ LocationName.vanilla_secret_2_gray_pow_block_1: [0x01, 105],
+ LocationName.vanilla_secret_2_coin_block_1: [0x01, 106],
+ LocationName.vanilla_secret_2_coin_block_2: [0x01, 107],
+ LocationName.vanilla_secret_2_coin_block_3: [0x01, 108],
+ LocationName.vanilla_secret_2_coin_block_4: [0x01, 109],
+ LocationName.vanilla_secret_2_coin_block_5: [0x01, 110],
+ LocationName.vanilla_secret_2_coin_block_6: [0x01, 111],
+ LocationName.vanilla_secret_3_powerup_block_1: [0x02, 112],
+ LocationName.vanilla_secret_3_powerup_block_2: [0x02, 113],
+ LocationName.donut_ghost_house_vine_block_1: [0x04, 114],
+ LocationName.donut_ghost_house_directional_coin_block_1: [0x04, 115],
+ LocationName.donut_ghost_house_life_block_1: [0x04, 116],
+ LocationName.donut_ghost_house_life_block_2: [0x04, 117],
+ LocationName.donut_ghost_house_life_block_3: [0x04, 118],
+ LocationName.donut_ghost_house_life_block_4: [0x04, 119],
+ LocationName.donut_plains_3_green_block_1: [0x05, 120],
+ LocationName.donut_plains_3_coin_block_1: [0x05, 121],
+ LocationName.donut_plains_3_coin_block_2: [0x05, 122],
+ LocationName.donut_plains_3_vine_block_1: [0x05, 123],
+ LocationName.donut_plains_3_powerup_block_1: [0x05, 124],
+ LocationName.donut_plains_3_bonus_block_1: [0x05, 125],
+ LocationName.donut_plains_4_coin_block_1: [0x06, 126],
+ LocationName.donut_plains_4_powerup_block_1: [0x06, 127],
+ LocationName.donut_plains_4_coin_block_2: [0x06, 128],
+ LocationName.donut_plains_4_yoshi_block_1: [0x06, 129],
+ LocationName.donut_plains_castle_yellow_block_1: [0x07, 130],
+ LocationName.donut_plains_castle_coin_block_1: [0x07, 131],
+ LocationName.donut_plains_castle_powerup_block_1: [0x07, 132],
+ LocationName.donut_plains_castle_coin_block_2: [0x07, 133],
+ LocationName.donut_plains_castle_vine_block_1: [0x07, 134],
+ LocationName.donut_plains_castle_invis_life_block_1: [0x07, 135],
+ LocationName.donut_plains_castle_coin_block_3: [0x07, 136],
+ LocationName.donut_plains_castle_coin_block_4: [0x07, 137],
+ LocationName.donut_plains_castle_coin_block_5: [0x07, 138],
+ LocationName.donut_plains_castle_green_block_1: [0x07, 139],
+ LocationName.donut_plains_2_coin_block_1: [0x09, 140],
+ LocationName.donut_plains_2_coin_block_2: [0x09, 141],
+ LocationName.donut_plains_2_coin_block_3: [0x09, 142],
+ LocationName.donut_plains_2_yellow_block_1: [0x09, 143],
+ LocationName.donut_plains_2_powerup_block_1: [0x09, 144],
+ LocationName.donut_plains_2_multi_coin_block_1: [0x09, 145],
+ LocationName.donut_plains_2_flying_block_1: [0x09, 146],
+ LocationName.donut_plains_2_green_block_1: [0x09, 147],
+ LocationName.donut_plains_2_yellow_block_2: [0x09, 148],
+ LocationName.donut_plains_2_vine_block_1: [0x09, 149],
+ LocationName.donut_secret_1_coin_block_1: [0x0A, 150],
+ LocationName.donut_secret_1_coin_block_2: [0x0A, 151],
+ LocationName.donut_secret_1_powerup_block_1: [0x0A, 152],
+ LocationName.donut_secret_1_coin_block_3: [0x0A, 153],
+ LocationName.donut_secret_1_powerup_block_2: [0x0A, 154],
+ LocationName.donut_secret_1_powerup_block_3: [0x0A, 155],
+ LocationName.donut_secret_1_life_block_1: [0x0A, 156],
+ LocationName.donut_secret_1_powerup_block_4: [0x0A, 157],
+ LocationName.donut_secret_1_powerup_block_5: [0x0A, 158],
+ LocationName.donut_secret_1_key_block_1: [0x0A, 159],
+ LocationName.vanilla_fortress_powerup_block_1: [0x0B, 160],
+ LocationName.vanilla_fortress_powerup_block_2: [0x0B, 161],
+ LocationName.vanilla_fortress_yellow_block_1: [0x0B, 162],
+ LocationName.butter_bridge_1_powerup_block_1: [0x0C, 163],
+ LocationName.butter_bridge_1_multi_coin_block_1: [0x0C, 164],
+ LocationName.butter_bridge_1_multi_coin_block_2: [0x0C, 165],
+ LocationName.butter_bridge_1_multi_coin_block_3: [0x0C, 166],
+ LocationName.butter_bridge_1_life_block_1: [0x0C, 167],
+ LocationName.butter_bridge_1_bonus_block_1: [0x0C, 168],
+ LocationName.butter_bridge_2_powerup_block_1: [0x0D, 169],
+ LocationName.butter_bridge_2_green_block_1: [0x0D, 170],
+ LocationName.butter_bridge_2_yoshi_block_1: [0x0D, 171],
+ LocationName.twin_bridges_castle_powerup_block_1: [0x0E, 172],
+ LocationName.cheese_bridge_powerup_block_1: [0x0F, 173],
+ LocationName.cheese_bridge_powerup_block_2: [0x0F, 174],
+ LocationName.cheese_bridge_wings_block_1: [0x0F, 175],
+ LocationName.cheese_bridge_powerup_block_3: [0x0F, 176],
+ LocationName.cookie_mountain_coin_block_1: [0x10, 177],
+ LocationName.cookie_mountain_coin_block_2: [0x10, 178],
+ LocationName.cookie_mountain_coin_block_3: [0x10, 179],
+ LocationName.cookie_mountain_coin_block_4: [0x10, 180],
+ LocationName.cookie_mountain_coin_block_5: [0x10, 181],
+ LocationName.cookie_mountain_coin_block_6: [0x10, 182],
+ LocationName.cookie_mountain_coin_block_7: [0x10, 183],
+ LocationName.cookie_mountain_coin_block_8: [0x10, 184],
+ LocationName.cookie_mountain_coin_block_9: [0x10, 185],
+ LocationName.cookie_mountain_powerup_block_1: [0x10, 186],
+ LocationName.cookie_mountain_life_block_1: [0x10, 187],
+ LocationName.cookie_mountain_vine_block_1: [0x10, 188],
+ LocationName.cookie_mountain_yoshi_block_1: [0x10, 189],
+ LocationName.cookie_mountain_coin_block_10: [0x10, 190],
+ LocationName.cookie_mountain_coin_block_11: [0x10, 191],
+ LocationName.cookie_mountain_powerup_block_2: [0x10, 192],
+ LocationName.cookie_mountain_coin_block_12: [0x10, 193],
+ LocationName.cookie_mountain_coin_block_13: [0x10, 194],
+ LocationName.cookie_mountain_coin_block_14: [0x10, 195],
+ LocationName.cookie_mountain_coin_block_15: [0x10, 196],
+ LocationName.cookie_mountain_coin_block_16: [0x10, 197],
+ LocationName.cookie_mountain_coin_block_17: [0x10, 198],
+ LocationName.cookie_mountain_coin_block_18: [0x10, 199],
+ LocationName.cookie_mountain_coin_block_19: [0x10, 200],
+ LocationName.cookie_mountain_coin_block_20: [0x10, 201],
+ LocationName.cookie_mountain_coin_block_21: [0x10, 202],
+ LocationName.cookie_mountain_coin_block_22: [0x10, 203],
+ LocationName.cookie_mountain_coin_block_23: [0x10, 204],
+ LocationName.cookie_mountain_coin_block_24: [0x10, 205],
+ LocationName.cookie_mountain_coin_block_25: [0x10, 206],
+ LocationName.cookie_mountain_coin_block_26: [0x10, 207],
+ LocationName.cookie_mountain_coin_block_27: [0x10, 208],
+ LocationName.cookie_mountain_coin_block_28: [0x10, 209],
+ LocationName.cookie_mountain_coin_block_29: [0x10, 210],
+ LocationName.cookie_mountain_coin_block_30: [0x10, 211],
+ LocationName.soda_lake_powerup_block_1: [0x11, 212],
+ LocationName.donut_secret_house_powerup_block_1: [0x13, 213],
+ LocationName.donut_secret_house_multi_coin_block_1: [0x13, 214],
+ LocationName.donut_secret_house_life_block_1: [0x13, 215],
+ LocationName.donut_secret_house_vine_block_1: [0x13, 216],
+ LocationName.donut_secret_house_directional_coin_block_1: [0x13, 217],
+ LocationName.donut_plains_1_coin_block_1: [0x15, 218],
+ LocationName.donut_plains_1_coin_block_2: [0x15, 219],
+ LocationName.donut_plains_1_yoshi_block_1: [0x15, 220],
+ LocationName.donut_plains_1_vine_block_1: [0x15, 221],
+ LocationName.donut_plains_1_green_block_1: [0x15, 222],
+ LocationName.donut_plains_1_green_block_2: [0x15, 223],
+ LocationName.donut_plains_1_green_block_3: [0x15, 224],
+ LocationName.donut_plains_1_green_block_4: [0x15, 225],
+ LocationName.donut_plains_1_green_block_5: [0x15, 226],
+ LocationName.donut_plains_1_green_block_6: [0x15, 227],
+ LocationName.donut_plains_1_green_block_7: [0x15, 228],
+ LocationName.donut_plains_1_green_block_8: [0x15, 229],
+ LocationName.donut_plains_1_green_block_9: [0x15, 230],
+ LocationName.donut_plains_1_green_block_10: [0x15, 231],
+ LocationName.donut_plains_1_green_block_11: [0x15, 232],
+ LocationName.donut_plains_1_green_block_12: [0x15, 233],
+ LocationName.donut_plains_1_green_block_13: [0x15, 234],
+ LocationName.donut_plains_1_green_block_14: [0x15, 235],
+ LocationName.donut_plains_1_green_block_15: [0x15, 236],
+ LocationName.donut_plains_1_green_block_16: [0x15, 237],
+ LocationName.donut_plains_1_yellow_block_1: [0x15, 238],
+ LocationName.donut_plains_1_yellow_block_2: [0x15, 239],
+ LocationName.donut_plains_1_yellow_block_3: [0x15, 240],
+ LocationName.sunken_ghost_ship_powerup_block_1: [0x18, 241],
+ LocationName.sunken_ghost_ship_star_block_1: [0x18, 242],
+ LocationName.chocolate_castle_yellow_block_1: [0x1A, 243],
+ LocationName.chocolate_castle_yellow_block_2: [0x1A, 244],
+ LocationName.chocolate_castle_green_block_1: [0x1A, 245],
+ LocationName.chocolate_fortress_powerup_block_1: [0x1B, 246],
+ LocationName.chocolate_fortress_powerup_block_2: [0x1B, 247],
+ LocationName.chocolate_fortress_coin_block_1: [0x1B, 248],
+ LocationName.chocolate_fortress_coin_block_2: [0x1B, 249],
+ LocationName.chocolate_fortress_green_block_1: [0x1B, 250],
+ LocationName.chocolate_island_5_yoshi_block_1: [0x1C, 251],
+ LocationName.chocolate_island_5_powerup_block_1: [0x1C, 252],
+ LocationName.chocolate_island_5_life_block_1: [0x1C, 253],
+ LocationName.chocolate_island_5_yellow_block_1: [0x1C, 254],
+ LocationName.chocolate_island_4_yellow_block_1: [0x1D, 255],
+ LocationName.chocolate_island_4_blue_pow_block_1: [0x1D, 256],
+ LocationName.chocolate_island_4_powerup_block_1: [0x1D, 257],
+ LocationName.forest_fortress_yellow_block_1: [0x1F, 258],
+ LocationName.forest_fortress_powerup_block_1: [0x1F, 259],
+ LocationName.forest_fortress_life_block_1: [0x1F, 260],
+ LocationName.forest_fortress_life_block_2: [0x1F, 261],
+ LocationName.forest_fortress_life_block_3: [0x1F, 262],
+ LocationName.forest_fortress_life_block_4: [0x1F, 263],
+ LocationName.forest_fortress_life_block_5: [0x1F, 264],
+ LocationName.forest_fortress_life_block_6: [0x1F, 265],
+ LocationName.forest_fortress_life_block_7: [0x1F, 266],
+ LocationName.forest_fortress_life_block_8: [0x1F, 267],
+ LocationName.forest_fortress_life_block_9: [0x1F, 268],
+ LocationName.forest_castle_green_block_1: [0x20, 269],
+ LocationName.chocolate_ghost_house_powerup_block_1: [0x21, 270],
+ LocationName.chocolate_ghost_house_powerup_block_2: [0x21, 271],
+ LocationName.chocolate_ghost_house_life_block_1: [0x21, 272],
+ LocationName.chocolate_island_1_flying_block_1: [0x22, 273],
+ LocationName.chocolate_island_1_flying_block_2: [0x22, 274],
+ LocationName.chocolate_island_1_yoshi_block_1: [0x22, 275],
+ LocationName.chocolate_island_1_green_block_1: [0x22, 276],
+ LocationName.chocolate_island_1_life_block_1: [0x22, 277],
+ LocationName.chocolate_island_3_powerup_block_1: [0x23, 278],
+ LocationName.chocolate_island_3_powerup_block_2: [0x23, 279],
+ LocationName.chocolate_island_3_powerup_block_3: [0x23, 280],
+ LocationName.chocolate_island_3_green_block_1: [0x23, 281],
+ LocationName.chocolate_island_3_bonus_block_1: [0x23, 282],
+ LocationName.chocolate_island_3_vine_block_1: [0x23, 283],
+ LocationName.chocolate_island_3_life_block_1: [0x23, 284],
+ LocationName.chocolate_island_3_life_block_2: [0x23, 285],
+ LocationName.chocolate_island_3_life_block_3: [0x23, 286],
+ LocationName.chocolate_island_2_multi_coin_block_1: [0x24, 287],
+ LocationName.chocolate_island_2_invis_coin_block_1: [0x24, 288],
+ LocationName.chocolate_island_2_yoshi_block_1: [0x24, 289],
+ LocationName.chocolate_island_2_coin_block_1: [0x24, 290],
+ LocationName.chocolate_island_2_coin_block_2: [0x24, 291],
+ LocationName.chocolate_island_2_multi_coin_block_2: [0x24, 292],
+ LocationName.chocolate_island_2_powerup_block_1: [0x24, 293],
+ LocationName.chocolate_island_2_blue_pow_block_1: [0x24, 294],
+ LocationName.chocolate_island_2_yellow_block_1: [0x24, 295],
+ LocationName.chocolate_island_2_yellow_block_2: [0x24, 296],
+ LocationName.chocolate_island_2_green_block_1: [0x24, 297],
+ LocationName.chocolate_island_2_green_block_2: [0x24, 298],
+ LocationName.chocolate_island_2_green_block_3: [0x24, 299],
+ LocationName.chocolate_island_2_green_block_4: [0x24, 300],
+ LocationName.chocolate_island_2_green_block_5: [0x24, 301],
+ LocationName.chocolate_island_2_green_block_6: [0x24, 302],
+ LocationName.yoshis_island_castle_coin_block_1: [0x25, 303],
+ LocationName.yoshis_island_castle_coin_block_2: [0x25, 304],
+ LocationName.yoshis_island_castle_powerup_block_1: [0x25, 305],
+ LocationName.yoshis_island_castle_coin_block_3: [0x25, 306],
+ LocationName.yoshis_island_castle_coin_block_4: [0x25, 307],
+ LocationName.yoshis_island_castle_flying_block_1: [0x25, 308],
+ LocationName.yoshis_island_4_yellow_block_1: [0x26, 309],
+ LocationName.yoshis_island_4_powerup_block_1: [0x26, 310],
+ LocationName.yoshis_island_4_multi_coin_block_1: [0x26, 311],
+ LocationName.yoshis_island_4_star_block_1: [0x26, 312],
+ LocationName.yoshis_island_3_yellow_block_1: [0x27, 313],
+ LocationName.yoshis_island_3_yellow_block_2: [0x27, 314],
+ LocationName.yoshis_island_3_yellow_block_3: [0x27, 315],
+ LocationName.yoshis_island_3_yellow_block_4: [0x27, 316],
+ LocationName.yoshis_island_3_yellow_block_5: [0x27, 317],
+ LocationName.yoshis_island_3_yellow_block_6: [0x27, 318],
+ LocationName.yoshis_island_3_yellow_block_7: [0x27, 319],
+ LocationName.yoshis_island_3_yellow_block_8: [0x27, 320],
+ LocationName.yoshis_island_3_yellow_block_9: [0x27, 321],
+ LocationName.yoshis_island_3_coin_block_1: [0x27, 322],
+ LocationName.yoshis_island_3_yoshi_block_1: [0x27, 323],
+ LocationName.yoshis_island_3_coin_block_2: [0x27, 324],
+ LocationName.yoshis_island_3_powerup_block_1: [0x27, 325],
+ LocationName.yoshis_island_3_yellow_block_10: [0x27, 326],
+ LocationName.yoshis_island_3_yellow_block_11: [0x27, 327],
+ LocationName.yoshis_island_3_yellow_block_12: [0x27, 328],
+ LocationName.yoshis_island_3_bonus_block_1: [0x27, 329],
+ LocationName.yoshis_island_1_flying_block_1: [0x29, 330],
+ LocationName.yoshis_island_1_yellow_block_1: [0x29, 331],
+ LocationName.yoshis_island_1_life_block_1: [0x29, 332],
+ LocationName.yoshis_island_1_powerup_block_1: [0x29, 333],
+ LocationName.yoshis_island_2_flying_block_1: [0x2A, 334],
+ LocationName.yoshis_island_2_flying_block_2: [0x2A, 335],
+ LocationName.yoshis_island_2_flying_block_3: [0x2A, 336],
+ LocationName.yoshis_island_2_flying_block_4: [0x2A, 337],
+ LocationName.yoshis_island_2_flying_block_5: [0x2A, 338],
+ LocationName.yoshis_island_2_flying_block_6: [0x2A, 339],
+ LocationName.yoshis_island_2_coin_block_1: [0x2A, 340],
+ LocationName.yoshis_island_2_yellow_block_1: [0x2A, 341],
+ LocationName.yoshis_island_2_coin_block_2: [0x2A, 342],
+ LocationName.yoshis_island_2_coin_block_3: [0x2A, 343],
+ LocationName.yoshis_island_2_yoshi_block_1: [0x2A, 344],
+ LocationName.yoshis_island_2_coin_block_4: [0x2A, 345],
+ LocationName.yoshis_island_2_yoshi_block_2: [0x2A, 346],
+ LocationName.yoshis_island_2_coin_block_5: [0x2A, 347],
+ LocationName.yoshis_island_2_vine_block_1: [0x2A, 348],
+ LocationName.yoshis_island_2_yellow_block_2: [0x2A, 349],
+ LocationName.vanilla_ghost_house_powerup_block_1: [0x2B, 350],
+ LocationName.vanilla_ghost_house_vine_block_1: [0x2B, 351],
+ LocationName.vanilla_ghost_house_powerup_block_2: [0x2B, 352],
+ LocationName.vanilla_ghost_house_multi_coin_block_1: [0x2B, 353],
+ LocationName.vanilla_ghost_house_blue_pow_block_1: [0x2B, 354],
+ LocationName.vanilla_secret_1_coin_block_1: [0x2D, 355],
+ LocationName.vanilla_secret_1_powerup_block_1: [0x2D, 356],
+ LocationName.vanilla_secret_1_multi_coin_block_1: [0x2D, 357],
+ LocationName.vanilla_secret_1_vine_block_1: [0x2D, 358],
+ LocationName.vanilla_secret_1_vine_block_2: [0x2D, 359],
+ LocationName.vanilla_secret_1_coin_block_2: [0x2D, 360],
+ LocationName.vanilla_secret_1_coin_block_3: [0x2D, 361],
+ LocationName.vanilla_secret_1_powerup_block_2: [0x2D, 362],
+ LocationName.vanilla_dome_3_coin_block_1: [0x2E, 363],
+ LocationName.vanilla_dome_3_flying_block_1: [0x2E, 364],
+ LocationName.vanilla_dome_3_flying_block_2: [0x2E, 365],
+ LocationName.vanilla_dome_3_powerup_block_1: [0x2E, 366],
+ LocationName.vanilla_dome_3_flying_block_3: [0x2E, 367],
+ LocationName.vanilla_dome_3_invis_coin_block_1: [0x2E, 368],
+ LocationName.vanilla_dome_3_powerup_block_2: [0x2E, 369],
+ LocationName.vanilla_dome_3_multi_coin_block_1: [0x2E, 370],
+ LocationName.vanilla_dome_3_powerup_block_3: [0x2E, 371],
+ LocationName.vanilla_dome_3_yoshi_block_1: [0x2E, 372],
+ LocationName.vanilla_dome_3_powerup_block_4: [0x2E, 373],
+ LocationName.vanilla_dome_3_pswitch_coin_block_1: [0x2E, 374],
+ LocationName.vanilla_dome_3_pswitch_coin_block_2: [0x2E, 375],
+ LocationName.vanilla_dome_3_pswitch_coin_block_3: [0x2E, 376],
+ LocationName.vanilla_dome_3_pswitch_coin_block_4: [0x2E, 377],
+ LocationName.vanilla_dome_3_pswitch_coin_block_5: [0x2E, 378],
+ LocationName.vanilla_dome_3_pswitch_coin_block_6: [0x2E, 379],
+ LocationName.donut_secret_2_directional_coin_block_1: [0x2F, 380],
+ LocationName.donut_secret_2_vine_block_1: [0x2F, 381],
+ LocationName.donut_secret_2_star_block_1: [0x2F, 382],
+ LocationName.donut_secret_2_powerup_block_1: [0x2F, 383],
+ LocationName.donut_secret_2_star_block_2: [0x2F, 384],
+ LocationName.valley_of_bowser_4_yellow_block_1: [0x33, 385],
+ LocationName.valley_of_bowser_4_powerup_block_1: [0x33, 386],
+ LocationName.valley_of_bowser_4_vine_block_1: [0x33, 387],
+ LocationName.valley_of_bowser_4_yoshi_block_1: [0x33, 388],
+ LocationName.valley_of_bowser_4_life_block_1: [0x33, 389],
+ LocationName.valley_of_bowser_4_powerup_block_2: [0x33, 390],
+ LocationName.valley_castle_yellow_block_1: [0x34, 391],
+ LocationName.valley_castle_yellow_block_2: [0x34, 392],
+ LocationName.valley_castle_green_block_1: [0x34, 393],
+ LocationName.valley_fortress_green_block_1: [0x35, 394],
+ LocationName.valley_fortress_yellow_block_1: [0x35, 395],
+ LocationName.valley_of_bowser_3_powerup_block_1: [0x37, 396],
+ LocationName.valley_of_bowser_3_powerup_block_2: [0x37, 397],
+ LocationName.valley_ghost_house_pswitch_coin_block_1: [0x38, 398],
+ LocationName.valley_ghost_house_multi_coin_block_1: [0x38, 399],
+ LocationName.valley_ghost_house_powerup_block_1: [0x38, 400],
+ LocationName.valley_ghost_house_directional_coin_block_1: [0x38, 401],
+ LocationName.valley_of_bowser_2_powerup_block_1: [0x39, 402],
+ LocationName.valley_of_bowser_2_yellow_block_1: [0x39, 403],
+ LocationName.valley_of_bowser_2_powerup_block_2: [0x39, 404],
+ LocationName.valley_of_bowser_2_wings_block_1: [0x39, 405],
+ LocationName.valley_of_bowser_1_green_block_1: [0x3A, 406],
+ LocationName.valley_of_bowser_1_invis_coin_block_1: [0x3A, 407],
+ LocationName.valley_of_bowser_1_invis_coin_block_2: [0x3A, 408],
+ LocationName.valley_of_bowser_1_invis_coin_block_3: [0x3A, 409],
+ LocationName.valley_of_bowser_1_yellow_block_1: [0x3A, 410],
+ LocationName.valley_of_bowser_1_yellow_block_2: [0x3A, 411],
+ LocationName.valley_of_bowser_1_yellow_block_3: [0x3A, 412],
+ LocationName.valley_of_bowser_1_yellow_block_4: [0x3A, 413],
+ LocationName.valley_of_bowser_1_vine_block_1: [0x3A, 414],
+ LocationName.chocolate_secret_powerup_block_1: [0x3B, 415],
+ LocationName.chocolate_secret_powerup_block_2: [0x3B, 416],
+ LocationName.vanilla_dome_2_coin_block_1: [0x3C, 417],
+ LocationName.vanilla_dome_2_powerup_block_1: [0x3C, 418],
+ LocationName.vanilla_dome_2_coin_block_2: [0x3C, 419],
+ LocationName.vanilla_dome_2_coin_block_3: [0x3C, 420],
+ LocationName.vanilla_dome_2_vine_block_1: [0x3C, 421],
+ LocationName.vanilla_dome_2_invis_life_block_1: [0x3C, 422],
+ LocationName.vanilla_dome_2_coin_block_4: [0x3C, 423],
+ LocationName.vanilla_dome_2_coin_block_5: [0x3C, 424],
+ LocationName.vanilla_dome_2_powerup_block_2: [0x3C, 425],
+ LocationName.vanilla_dome_2_powerup_block_3: [0x3C, 426],
+ LocationName.vanilla_dome_2_powerup_block_4: [0x3C, 427],
+ LocationName.vanilla_dome_2_powerup_block_5: [0x3C, 428],
+ LocationName.vanilla_dome_2_multi_coin_block_1: [0x3C, 429],
+ LocationName.vanilla_dome_2_multi_coin_block_2: [0x3C, 430],
+ LocationName.vanilla_dome_4_powerup_block_1: [0x3D, 431],
+ LocationName.vanilla_dome_4_powerup_block_2: [0x3D, 432],
+ LocationName.vanilla_dome_4_coin_block_1: [0x3D, 433],
+ LocationName.vanilla_dome_4_coin_block_2: [0x3D, 434],
+ LocationName.vanilla_dome_4_coin_block_3: [0x3D, 435],
+ LocationName.vanilla_dome_4_life_block_1: [0x3D, 436],
+ LocationName.vanilla_dome_4_coin_block_4: [0x3D, 437],
+ LocationName.vanilla_dome_4_coin_block_5: [0x3D, 438],
+ LocationName.vanilla_dome_4_coin_block_6: [0x3D, 439],
+ LocationName.vanilla_dome_4_coin_block_7: [0x3D, 440],
+ LocationName.vanilla_dome_4_coin_block_8: [0x3D, 441],
+ LocationName.vanilla_dome_1_flying_block_1: [0x3E, 442],
+ LocationName.vanilla_dome_1_powerup_block_1: [0x3E, 443],
+ LocationName.vanilla_dome_1_powerup_block_2: [0x3E, 444],
+ LocationName.vanilla_dome_1_coin_block_1: [0x3E, 445],
+ LocationName.vanilla_dome_1_life_block_1: [0x3E, 446],
+ LocationName.vanilla_dome_1_powerup_block_3: [0x3E, 447],
+ LocationName.vanilla_dome_1_vine_block_1: [0x3E, 448],
+ LocationName.vanilla_dome_1_star_block_1: [0x3E, 449],
+ LocationName.vanilla_dome_1_powerup_block_4: [0x3E, 450],
+ LocationName.vanilla_dome_1_coin_block_2: [0x3E, 451],
+ LocationName.vanilla_dome_castle_life_block_1: [0x40, 452],
+ LocationName.vanilla_dome_castle_life_block_2: [0x40, 453],
+ LocationName.vanilla_dome_castle_powerup_block_1: [0x40, 454],
+ LocationName.vanilla_dome_castle_life_block_3: [0x40, 455],
+ LocationName.vanilla_dome_castle_green_block_1: [0x40, 456],
+ LocationName.forest_ghost_house_coin_block_1: [0x41, 457],
+ LocationName.forest_ghost_house_powerup_block_1: [0x41, 458],
+ LocationName.forest_ghost_house_flying_block_1: [0x41, 459],
+ LocationName.forest_ghost_house_powerup_block_2: [0x41, 460],
+ LocationName.forest_ghost_house_life_block_1: [0x41, 461],
+ LocationName.forest_of_illusion_1_powerup_block_1: [0x42, 462],
+ LocationName.forest_of_illusion_1_yoshi_block_1: [0x42, 463],
+ LocationName.forest_of_illusion_1_powerup_block_2: [0x42, 464],
+ LocationName.forest_of_illusion_1_key_block_1: [0x42, 465],
+ LocationName.forest_of_illusion_1_life_block_1: [0x42, 466],
+ LocationName.forest_of_illusion_4_multi_coin_block_1: [0x43, 467],
+ LocationName.forest_of_illusion_4_coin_block_1: [0x43, 468],
+ LocationName.forest_of_illusion_4_coin_block_2: [0x43, 469],
+ LocationName.forest_of_illusion_4_coin_block_3: [0x43, 470],
+ LocationName.forest_of_illusion_4_coin_block_4: [0x43, 471],
+ LocationName.forest_of_illusion_4_powerup_block_1: [0x43, 472],
+ LocationName.forest_of_illusion_4_coin_block_5: [0x43, 473],
+ LocationName.forest_of_illusion_4_coin_block_6: [0x43, 474],
+ LocationName.forest_of_illusion_4_coin_block_7: [0x43, 475],
+ LocationName.forest_of_illusion_4_powerup_block_2: [0x43, 476],
+ LocationName.forest_of_illusion_4_coin_block_8: [0x43, 477],
+ LocationName.forest_of_illusion_4_coin_block_9: [0x43, 478],
+ LocationName.forest_of_illusion_4_coin_block_10: [0x43, 479],
+ LocationName.forest_of_illusion_2_green_block_1: [0x44, 480],
+ LocationName.forest_of_illusion_2_powerup_block_1: [0x44, 481],
+ LocationName.forest_of_illusion_2_invis_coin_block_1: [0x44, 482],
+ LocationName.forest_of_illusion_2_invis_coin_block_2: [0x44, 483],
+ LocationName.forest_of_illusion_2_invis_life_block_1: [0x44, 484],
+ LocationName.forest_of_illusion_2_invis_coin_block_3: [0x44, 485],
+ LocationName.forest_of_illusion_2_yellow_block_1: [0x44, 486],
+ LocationName.forest_secret_powerup_block_1: [0x46, 487],
+ LocationName.forest_secret_powerup_block_2: [0x46, 488],
+ LocationName.forest_secret_life_block_1: [0x46, 489],
+ LocationName.forest_of_illusion_3_yoshi_block_1: [0x47, 490],
+ LocationName.forest_of_illusion_3_coin_block_1: [0x47, 491],
+ LocationName.forest_of_illusion_3_multi_coin_block_1: [0x47, 492],
+ LocationName.forest_of_illusion_3_coin_block_2: [0x47, 493],
+ LocationName.forest_of_illusion_3_multi_coin_block_2: [0x47, 494],
+ LocationName.forest_of_illusion_3_coin_block_3: [0x47, 495],
+ LocationName.forest_of_illusion_3_coin_block_4: [0x47, 496],
+ LocationName.forest_of_illusion_3_coin_block_5: [0x47, 497],
+ LocationName.forest_of_illusion_3_coin_block_6: [0x47, 498],
+ LocationName.forest_of_illusion_3_coin_block_7: [0x47, 499],
+ LocationName.forest_of_illusion_3_coin_block_8: [0x47, 500],
+ LocationName.forest_of_illusion_3_coin_block_9: [0x47, 501],
+ LocationName.forest_of_illusion_3_coin_block_10: [0x47, 502],
+ LocationName.forest_of_illusion_3_coin_block_11: [0x47, 503],
+ LocationName.forest_of_illusion_3_coin_block_12: [0x47, 504],
+ LocationName.forest_of_illusion_3_coin_block_13: [0x47, 505],
+ LocationName.forest_of_illusion_3_coin_block_14: [0x47, 506],
+ LocationName.forest_of_illusion_3_coin_block_15: [0x47, 507],
+ LocationName.forest_of_illusion_3_coin_block_16: [0x47, 508],
+ LocationName.forest_of_illusion_3_coin_block_17: [0x47, 509],
+ LocationName.forest_of_illusion_3_coin_block_18: [0x47, 510],
+ LocationName.forest_of_illusion_3_coin_block_19: [0x47, 511],
+ LocationName.forest_of_illusion_3_coin_block_20: [0x47, 512],
+ LocationName.forest_of_illusion_3_coin_block_21: [0x47, 513],
+ LocationName.forest_of_illusion_3_coin_block_22: [0x47, 514],
+ LocationName.forest_of_illusion_3_coin_block_23: [0x47, 515],
+ LocationName.forest_of_illusion_3_coin_block_24: [0x47, 516],
+ LocationName.special_zone_8_yoshi_block_1: [0x49, 517],
+ LocationName.special_zone_8_coin_block_1: [0x49, 518],
+ LocationName.special_zone_8_coin_block_2: [0x49, 519],
+ LocationName.special_zone_8_coin_block_3: [0x49, 520],
+ LocationName.special_zone_8_coin_block_4: [0x49, 521],
+ LocationName.special_zone_8_coin_block_5: [0x49, 522],
+ LocationName.special_zone_8_blue_pow_block_1: [0x49, 523],
+ LocationName.special_zone_8_powerup_block_1: [0x49, 524],
+ LocationName.special_zone_8_star_block_1: [0x49, 525],
+ LocationName.special_zone_8_coin_block_6: [0x49, 526],
+ LocationName.special_zone_8_coin_block_7: [0x49, 527],
+ LocationName.special_zone_8_coin_block_8: [0x49, 528],
+ LocationName.special_zone_8_coin_block_9: [0x49, 529],
+ LocationName.special_zone_8_coin_block_10: [0x49, 530],
+ LocationName.special_zone_8_coin_block_11: [0x49, 531],
+ LocationName.special_zone_8_coin_block_12: [0x49, 532],
+ LocationName.special_zone_8_coin_block_13: [0x49, 533],
+ LocationName.special_zone_8_coin_block_14: [0x49, 534],
+ LocationName.special_zone_8_coin_block_15: [0x49, 535],
+ LocationName.special_zone_8_coin_block_16: [0x49, 536],
+ LocationName.special_zone_8_coin_block_17: [0x49, 537],
+ LocationName.special_zone_8_coin_block_18: [0x49, 538],
+ LocationName.special_zone_8_multi_coin_block_1: [0x49, 539],
+ LocationName.special_zone_8_coin_block_19: [0x49, 540],
+ LocationName.special_zone_8_coin_block_20: [0x49, 541],
+ LocationName.special_zone_8_coin_block_21: [0x49, 542],
+ LocationName.special_zone_8_coin_block_22: [0x49, 543],
+ LocationName.special_zone_8_coin_block_23: [0x49, 544],
+ LocationName.special_zone_8_powerup_block_2: [0x49, 545],
+ LocationName.special_zone_8_flying_block_1: [0x49, 546],
+ LocationName.special_zone_7_powerup_block_1: [0x4A, 547],
+ LocationName.special_zone_7_yoshi_block_1: [0x4A, 548],
+ LocationName.special_zone_7_coin_block_1: [0x4A, 549],
+ LocationName.special_zone_7_powerup_block_2: [0x4A, 550],
+ LocationName.special_zone_7_coin_block_2: [0x4A, 551],
+ LocationName.special_zone_6_powerup_block_1: [0x4B, 552],
+ LocationName.special_zone_6_coin_block_1: [0x4B, 553],
+ LocationName.special_zone_6_coin_block_2: [0x4B, 554],
+ LocationName.special_zone_6_yoshi_block_1: [0x4B, 555],
+ LocationName.special_zone_6_life_block_1: [0x4B, 556],
+ LocationName.special_zone_6_multi_coin_block_1: [0x4B, 557],
+ LocationName.special_zone_6_coin_block_3: [0x4B, 558],
+ LocationName.special_zone_6_coin_block_4: [0x4B, 559],
+ LocationName.special_zone_6_coin_block_5: [0x4B, 560],
+ LocationName.special_zone_6_coin_block_6: [0x4B, 561],
+ LocationName.special_zone_6_coin_block_7: [0x4B, 562],
+ LocationName.special_zone_6_coin_block_8: [0x4B, 563],
+ LocationName.special_zone_6_coin_block_9: [0x4B, 564],
+ LocationName.special_zone_6_coin_block_10: [0x4B, 565],
+ LocationName.special_zone_6_coin_block_11: [0x4B, 566],
+ LocationName.special_zone_6_coin_block_12: [0x4B, 567],
+ LocationName.special_zone_6_coin_block_13: [0x4B, 568],
+ LocationName.special_zone_6_coin_block_14: [0x4B, 569],
+ LocationName.special_zone_6_coin_block_15: [0x4B, 570],
+ LocationName.special_zone_6_coin_block_16: [0x4B, 571],
+ LocationName.special_zone_6_coin_block_17: [0x4B, 572],
+ LocationName.special_zone_6_coin_block_18: [0x4B, 573],
+ LocationName.special_zone_6_coin_block_19: [0x4B, 574],
+ LocationName.special_zone_6_coin_block_20: [0x4B, 575],
+ LocationName.special_zone_6_coin_block_21: [0x4B, 576],
+ LocationName.special_zone_6_coin_block_22: [0x4B, 577],
+ LocationName.special_zone_6_coin_block_23: [0x4B, 578],
+ LocationName.special_zone_6_coin_block_24: [0x4B, 579],
+ LocationName.special_zone_6_coin_block_25: [0x4B, 580],
+ LocationName.special_zone_6_coin_block_26: [0x4B, 581],
+ LocationName.special_zone_6_coin_block_27: [0x4B, 582],
+ LocationName.special_zone_6_coin_block_28: [0x4B, 583],
+ LocationName.special_zone_6_powerup_block_2: [0x4B, 584],
+ LocationName.special_zone_6_coin_block_29: [0x4B, 585],
+ LocationName.special_zone_6_coin_block_30: [0x4B, 586],
+ LocationName.special_zone_6_coin_block_31: [0x4B, 587],
+ LocationName.special_zone_6_coin_block_32: [0x4B, 588],
+ LocationName.special_zone_6_coin_block_33: [0x4B, 589],
+ LocationName.special_zone_5_yoshi_block_1: [0x4C, 590],
+ LocationName.special_zone_1_vine_block_1: [0x4E, 591],
+ LocationName.special_zone_1_vine_block_2: [0x4E, 592],
+ LocationName.special_zone_1_vine_block_3: [0x4E, 593],
+ LocationName.special_zone_1_vine_block_4: [0x4E, 594],
+ LocationName.special_zone_1_life_block_1: [0x4E, 595],
+ LocationName.special_zone_1_vine_block_5: [0x4E, 596],
+ LocationName.special_zone_1_blue_pow_block_1: [0x4E, 597],
+ LocationName.special_zone_1_vine_block_6: [0x4E, 598],
+ LocationName.special_zone_1_powerup_block_1: [0x4E, 599],
+ LocationName.special_zone_1_pswitch_coin_block_1: [0x4E, 600],
+ LocationName.special_zone_1_pswitch_coin_block_2: [0x4E, 601],
+ LocationName.special_zone_1_pswitch_coin_block_3: [0x4E, 602],
+ LocationName.special_zone_1_pswitch_coin_block_4: [0x4E, 603],
+ LocationName.special_zone_1_pswitch_coin_block_5: [0x4E, 604],
+ LocationName.special_zone_1_pswitch_coin_block_6: [0x4E, 605],
+ LocationName.special_zone_1_pswitch_coin_block_7: [0x4E, 606],
+ LocationName.special_zone_1_pswitch_coin_block_8: [0x4E, 607],
+ LocationName.special_zone_1_pswitch_coin_block_9: [0x4E, 608],
+ LocationName.special_zone_1_pswitch_coin_block_10: [0x4E, 609],
+ LocationName.special_zone_1_pswitch_coin_block_11: [0x4E, 610],
+ LocationName.special_zone_1_pswitch_coin_block_12: [0x4E, 611],
+ LocationName.special_zone_1_pswitch_coin_block_13: [0x4E, 612],
+ LocationName.special_zone_2_powerup_block_1: [0x4F, 613],
+ LocationName.special_zone_2_coin_block_1: [0x4F, 614],
+ LocationName.special_zone_2_coin_block_2: [0x4F, 615],
+ LocationName.special_zone_2_powerup_block_2: [0x4F, 616],
+ LocationName.special_zone_2_coin_block_3: [0x4F, 617],
+ LocationName.special_zone_2_coin_block_4: [0x4F, 618],
+ LocationName.special_zone_2_powerup_block_3: [0x4F, 619],
+ LocationName.special_zone_2_multi_coin_block_1: [0x4F, 620],
+ LocationName.special_zone_2_coin_block_5: [0x4F, 621],
+ LocationName.special_zone_2_coin_block_6: [0x4F, 622],
+ LocationName.special_zone_3_powerup_block_1: [0x50, 623],
+ LocationName.special_zone_3_yoshi_block_1: [0x50, 624],
+ LocationName.special_zone_3_wings_block_1: [0x50, 625],
+ LocationName.special_zone_4_powerup_block_1: [0x51, 626],
+ LocationName.special_zone_4_star_block_1: [0x51, 627],
+ LocationName.star_road_2_star_block_1: [0x54, 628],
+ LocationName.star_road_3_key_block_1: [0x56, 629],
+ LocationName.star_road_4_powerup_block_1: [0x59, 630],
+ LocationName.star_road_4_green_block_1: [0x59, 631],
+ LocationName.star_road_4_green_block_2: [0x59, 632],
+ LocationName.star_road_4_green_block_3: [0x59, 633],
+ LocationName.star_road_4_green_block_4: [0x59, 634],
+ LocationName.star_road_4_green_block_5: [0x59, 635],
+ LocationName.star_road_4_green_block_6: [0x59, 636],
+ LocationName.star_road_4_green_block_7: [0x59, 637],
+ LocationName.star_road_4_key_block_1: [0x59, 638],
+ LocationName.star_road_5_directional_coin_block_1: [0x5A, 639],
+ LocationName.star_road_5_life_block_1: [0x5A, 640],
+ LocationName.star_road_5_vine_block_1: [0x5A, 641],
+ LocationName.star_road_5_yellow_block_1: [0x5A, 642],
+ LocationName.star_road_5_yellow_block_2: [0x5A, 643],
+ LocationName.star_road_5_yellow_block_3: [0x5A, 644],
+ LocationName.star_road_5_yellow_block_4: [0x5A, 645],
+ LocationName.star_road_5_yellow_block_5: [0x5A, 646],
+ LocationName.star_road_5_yellow_block_6: [0x5A, 647],
+ LocationName.star_road_5_yellow_block_7: [0x5A, 648],
+ LocationName.star_road_5_yellow_block_8: [0x5A, 649],
+ LocationName.star_road_5_yellow_block_9: [0x5A, 650],
+ LocationName.star_road_5_yellow_block_10: [0x5A, 651],
+ LocationName.star_road_5_yellow_block_11: [0x5A, 652],
+ LocationName.star_road_5_yellow_block_12: [0x5A, 653],
+ LocationName.star_road_5_yellow_block_13: [0x5A, 654],
+ LocationName.star_road_5_yellow_block_14: [0x5A, 655],
+ LocationName.star_road_5_yellow_block_15: [0x5A, 656],
+ LocationName.star_road_5_yellow_block_16: [0x5A, 657],
+ LocationName.star_road_5_yellow_block_17: [0x5A, 658],
+ LocationName.star_road_5_yellow_block_18: [0x5A, 659],
+ LocationName.star_road_5_yellow_block_19: [0x5A, 660],
+ LocationName.star_road_5_yellow_block_20: [0x5A, 661],
+ LocationName.star_road_5_green_block_1: [0x5A, 662],
+ LocationName.star_road_5_green_block_2: [0x5A, 663],
+ LocationName.star_road_5_green_block_3: [0x5A, 664],
+ LocationName.star_road_5_green_block_4: [0x5A, 665],
+ LocationName.star_road_5_green_block_5: [0x5A, 666],
+ LocationName.star_road_5_green_block_6: [0x5A, 667],
+ LocationName.star_road_5_green_block_7: [0x5A, 668],
+ LocationName.star_road_5_green_block_8: [0x5A, 669],
+ LocationName.star_road_5_green_block_9: [0x5A, 670],
+ LocationName.star_road_5_green_block_10: [0x5A, 671],
+ LocationName.star_road_5_green_block_11: [0x5A, 672],
+ LocationName.star_road_5_green_block_12: [0x5A, 673],
+ LocationName.star_road_5_green_block_13: [0x5A, 674],
+ LocationName.star_road_5_green_block_14: [0x5A, 675],
+ LocationName.star_road_5_green_block_15: [0x5A, 676],
+ LocationName.star_road_5_green_block_16: [0x5A, 677],
+ LocationName.star_road_5_green_block_17: [0x5A, 678],
+ LocationName.star_road_5_green_block_18: [0x5A, 679],
+ LocationName.star_road_5_green_block_19: [0x5A, 680],
+ LocationName.star_road_5_green_block_20: [0x5A, 681]
}
-def generate_level_list(world, player):
+def generate_level_list(world: World):
- if not world.level_shuffle[player]:
+ if not world.options.level_shuffle:
out_level_list = full_level_list.copy()
out_level_list[0x00] = 0x03
out_level_list[0x11] = 0x28
- if world.bowser_castle_doors[player] == "fast":
+ if world.options.bowser_castle_doors == "fast":
out_level_list[0x41] = 0x82
out_level_list[0x42] = 0x32
- elif world.bowser_castle_doors[player] == "slow":
+ elif world.options.bowser_castle_doors == "slow":
out_level_list[0x41] = 0x31
out_level_list[0x42] = 0x81
@@ -552,7 +1258,7 @@ def generate_level_list(world, player):
shuffled_level_list.append(0x16)
single_levels_copy = (easy_single_levels_copy.copy() + hard_single_levels_copy.copy())
- if not world.exclude_special_zone[player]:
+ if not world.options.exclude_special_zone:
single_levels_copy.extend(special_zone_levels_copy)
world.random.shuffle(single_levels_copy)
@@ -619,10 +1325,10 @@ def generate_level_list(world, player):
shuffled_level_list.append(castle_fortress_levels_copy.pop(0))
# Front/Back Door
- if world.bowser_castle_doors[player] == "fast":
+ if world.options.bowser_castle_doors == "fast":
shuffled_level_list.append(0x82)
shuffled_level_list.append(0x32)
- elif world.bowser_castle_doors[player] == "slow":
+ elif world.options.bowser_castle_doors == "slow":
shuffled_level_list.append(0x31)
shuffled_level_list.append(0x81)
else:
@@ -646,7 +1352,7 @@ def generate_level_list(world, player):
# Special Zone
shuffled_level_list.append(0x4D)
- if not world.exclude_special_zone[player]:
+ if not world.options.exclude_special_zone:
shuffled_level_list.append(single_levels_copy.pop(0))
shuffled_level_list.append(single_levels_copy.pop(0))
shuffled_level_list.append(single_levels_copy.pop(0))
diff --git a/worlds/smw/Locations.py b/worlds/smw/Locations.py
index a8b7f7a4ec2c..47e821fc61ff 100644
--- a/worlds/smw/Locations.py
+++ b/worlds/smw/Locations.py
@@ -1,9 +1,9 @@
import typing
from BaseClasses import Location
+from worlds.AutoWorld import World
from .Names import LocationName
-
class SMWLocation(Location):
game: str = "Super Mario World"
@@ -197,6 +197,624 @@ def __init__(self, player: int, name: str = '', address: int = None, parent=None
LocationName.special_zone_8_dragon: 0xBC0162,
}
+moon_location_table = {
+ LocationName.yoshis_island_1_moon: 0xBC0300,
+ LocationName.donut_plains_4_moon: 0xBC030B,
+ LocationName.vanilla_dome_3_moon: 0xBC0318,
+ LocationName.cheese_bridge_moon: 0xBC0325,
+ LocationName.forest_ghost_house_moon: 0xBC0332,
+ LocationName.chocolate_island_1_moon: 0xBC0338,
+ LocationName.valley_of_bowser_1_moon: 0xBC0345
+}
+
+hidden_1ups_location_table = {
+ LocationName.yoshis_island_4_hidden_1up: 0xBC0403,
+ LocationName.donut_plains_1_hidden_1up: 0xBC0406,
+ LocationName.donut_plains_4_hidden_1up: 0xBC040B,
+ LocationName.donut_plains_castle_hidden_1up: 0xBC0412,
+ LocationName.vanilla_dome_4_hidden_1up: 0xBC0419,
+ LocationName.vanilla_ghost_house_hidden_1up: 0xBC041E,
+ LocationName.vanilla_fortress_hidden_1up: 0xBC0420,
+ LocationName.cookie_mountain_hidden_1up: 0xBC0427,
+ LocationName.forest_of_illusion_3_hidden_1up: 0xBC042E,
+ LocationName.chocolate_island_2_hidden_1up: 0xBC0439,
+ LocationName.chocolate_castle_hidden_1up: 0xBC0443,
+ LocationName.valley_of_bowser_2_hidden_1up: 0xBC0446,
+ LocationName.valley_castle_hidden_1up: 0xBC044F,
+ LocationName.special_zone_1_hidden_1up: 0xBC045B
+}
+bonus_block_location_table = {
+ LocationName.yoshis_island_3_bonus_block: 0xBC0502,
+ LocationName.donut_plains_3_bonus_block: 0xBC050A,
+ LocationName.butter_bridge_1_bonus_block: 0xBC0523,
+ LocationName.chocolate_island_3_bonus_block: 0xBC053B
+}
+
+blocksanity_location_table = {
+ LocationName.vanilla_secret_2_yoshi_block_1: 0xBC0600,
+ LocationName.vanilla_secret_2_green_block_1: 0xBC0601,
+ LocationName.vanilla_secret_2_powerup_block_1: 0xBC0602,
+ LocationName.vanilla_secret_2_powerup_block_2: 0xBC0603,
+ LocationName.vanilla_secret_2_multi_coin_block_1: 0xBC0604,
+ LocationName.vanilla_secret_2_gray_pow_block_1: 0xBC0605,
+ LocationName.vanilla_secret_2_coin_block_1: 0xBC0606,
+ LocationName.vanilla_secret_2_coin_block_2: 0xBC0607,
+ LocationName.vanilla_secret_2_coin_block_3: 0xBC0608,
+ LocationName.vanilla_secret_2_coin_block_4: 0xBC0609,
+ LocationName.vanilla_secret_2_coin_block_5: 0xBC060A,
+ LocationName.vanilla_secret_2_coin_block_6: 0xBC060B,
+ LocationName.vanilla_secret_3_powerup_block_1: 0xBC060C,
+ LocationName.vanilla_secret_3_powerup_block_2: 0xBC060D,
+ LocationName.donut_ghost_house_vine_block_1: 0xBC060E,
+ LocationName.donut_ghost_house_directional_coin_block_1: 0xBC060F,
+ LocationName.donut_ghost_house_life_block_1: 0xBC0610,
+ LocationName.donut_ghost_house_life_block_2: 0xBC0611,
+ LocationName.donut_ghost_house_life_block_3: 0xBC0612,
+ LocationName.donut_ghost_house_life_block_4: 0xBC0613,
+ LocationName.donut_plains_3_green_block_1: 0xBC0614,
+ LocationName.donut_plains_3_coin_block_1: 0xBC0615,
+ LocationName.donut_plains_3_coin_block_2: 0xBC0616,
+ LocationName.donut_plains_3_vine_block_1: 0xBC0617,
+ LocationName.donut_plains_3_powerup_block_1: 0xBC0618,
+ LocationName.donut_plains_3_bonus_block_1: 0xBC0619,
+ LocationName.donut_plains_4_coin_block_1: 0xBC061A,
+ LocationName.donut_plains_4_powerup_block_1: 0xBC061B,
+ LocationName.donut_plains_4_coin_block_2: 0xBC061C,
+ LocationName.donut_plains_4_yoshi_block_1: 0xBC061D,
+ LocationName.donut_plains_castle_yellow_block_1: 0xBC061E,
+ LocationName.donut_plains_castle_coin_block_1: 0xBC061F,
+ LocationName.donut_plains_castle_powerup_block_1: 0xBC0620,
+ LocationName.donut_plains_castle_coin_block_2: 0xBC0621,
+ LocationName.donut_plains_castle_vine_block_1: 0xBC0622,
+ LocationName.donut_plains_castle_invis_life_block_1: 0xBC0623,
+ LocationName.donut_plains_castle_coin_block_3: 0xBC0624,
+ LocationName.donut_plains_castle_coin_block_4: 0xBC0625,
+ LocationName.donut_plains_castle_coin_block_5: 0xBC0626,
+ LocationName.donut_plains_castle_green_block_1: 0xBC0627,
+ LocationName.donut_plains_2_coin_block_1: 0xBC0628,
+ LocationName.donut_plains_2_coin_block_2: 0xBC0629,
+ LocationName.donut_plains_2_coin_block_3: 0xBC062A,
+ LocationName.donut_plains_2_yellow_block_1: 0xBC062B,
+ LocationName.donut_plains_2_powerup_block_1: 0xBC062C,
+ LocationName.donut_plains_2_multi_coin_block_1: 0xBC062D,
+ LocationName.donut_plains_2_flying_block_1: 0xBC062E,
+ LocationName.donut_plains_2_green_block_1: 0xBC062F,
+ LocationName.donut_plains_2_yellow_block_2: 0xBC0630,
+ LocationName.donut_plains_2_vine_block_1: 0xBC0631,
+ LocationName.donut_secret_1_coin_block_1: 0xBC0632,
+ LocationName.donut_secret_1_coin_block_2: 0xBC0633,
+ LocationName.donut_secret_1_powerup_block_1: 0xBC0634,
+ LocationName.donut_secret_1_coin_block_3: 0xBC0635,
+ LocationName.donut_secret_1_powerup_block_2: 0xBC0636,
+ LocationName.donut_secret_1_powerup_block_3: 0xBC0637,
+ LocationName.donut_secret_1_life_block_1: 0xBC0638,
+ LocationName.donut_secret_1_powerup_block_4: 0xBC0639,
+ LocationName.donut_secret_1_powerup_block_5: 0xBC063A,
+ LocationName.donut_secret_1_key_block_1: 0xBC063B,
+ LocationName.vanilla_fortress_powerup_block_1: 0xBC063C,
+ LocationName.vanilla_fortress_powerup_block_2: 0xBC063D,
+ LocationName.vanilla_fortress_yellow_block_1: 0xBC063E,
+ LocationName.butter_bridge_1_powerup_block_1: 0xBC063F,
+ LocationName.butter_bridge_1_multi_coin_block_1: 0xBC0640,
+ LocationName.butter_bridge_1_multi_coin_block_2: 0xBC0641,
+ LocationName.butter_bridge_1_multi_coin_block_3: 0xBC0642,
+ LocationName.butter_bridge_1_life_block_1: 0xBC0643,
+ LocationName.butter_bridge_1_bonus_block_1: 0xBC0644,
+ LocationName.butter_bridge_2_powerup_block_1: 0xBC0645,
+ LocationName.butter_bridge_2_green_block_1: 0xBC0646,
+ LocationName.butter_bridge_2_yoshi_block_1: 0xBC0647,
+ LocationName.twin_bridges_castle_powerup_block_1: 0xBC0648,
+ LocationName.cheese_bridge_powerup_block_1: 0xBC0649,
+ LocationName.cheese_bridge_powerup_block_2: 0xBC064A,
+ LocationName.cheese_bridge_wings_block_1: 0xBC064B,
+ LocationName.cheese_bridge_powerup_block_3: 0xBC064C,
+ LocationName.cookie_mountain_coin_block_1: 0xBC064D,
+ LocationName.cookie_mountain_coin_block_2: 0xBC064E,
+ LocationName.cookie_mountain_coin_block_3: 0xBC064F,
+ LocationName.cookie_mountain_coin_block_4: 0xBC0650,
+ LocationName.cookie_mountain_coin_block_5: 0xBC0651,
+ LocationName.cookie_mountain_coin_block_6: 0xBC0652,
+ LocationName.cookie_mountain_coin_block_7: 0xBC0653,
+ LocationName.cookie_mountain_coin_block_8: 0xBC0654,
+ LocationName.cookie_mountain_coin_block_9: 0xBC0655,
+ LocationName.cookie_mountain_powerup_block_1: 0xBC0656,
+ LocationName.cookie_mountain_life_block_1: 0xBC0657,
+ LocationName.cookie_mountain_vine_block_1: 0xBC0658,
+ LocationName.cookie_mountain_yoshi_block_1: 0xBC0659,
+ LocationName.cookie_mountain_coin_block_10: 0xBC065A,
+ LocationName.cookie_mountain_coin_block_11: 0xBC065B,
+ LocationName.cookie_mountain_powerup_block_2: 0xBC065C,
+ LocationName.cookie_mountain_coin_block_12: 0xBC065D,
+ LocationName.cookie_mountain_coin_block_13: 0xBC065E,
+ LocationName.cookie_mountain_coin_block_14: 0xBC065F,
+ LocationName.cookie_mountain_coin_block_15: 0xBC0660,
+ LocationName.cookie_mountain_coin_block_16: 0xBC0661,
+ LocationName.cookie_mountain_coin_block_17: 0xBC0662,
+ LocationName.cookie_mountain_coin_block_18: 0xBC0663,
+ LocationName.cookie_mountain_coin_block_19: 0xBC0664,
+ LocationName.cookie_mountain_coin_block_20: 0xBC0665,
+ LocationName.cookie_mountain_coin_block_21: 0xBC0666,
+ LocationName.cookie_mountain_coin_block_22: 0xBC0667,
+ LocationName.cookie_mountain_coin_block_23: 0xBC0668,
+ LocationName.cookie_mountain_coin_block_24: 0xBC0669,
+ LocationName.cookie_mountain_coin_block_25: 0xBC066A,
+ LocationName.cookie_mountain_coin_block_26: 0xBC066B,
+ LocationName.cookie_mountain_coin_block_27: 0xBC066C,
+ LocationName.cookie_mountain_coin_block_28: 0xBC066D,
+ LocationName.cookie_mountain_coin_block_29: 0xBC066E,
+ LocationName.cookie_mountain_coin_block_30: 0xBC066F,
+ LocationName.soda_lake_powerup_block_1: 0xBC0670,
+ LocationName.donut_secret_house_powerup_block_1: 0xBC0671,
+ LocationName.donut_secret_house_multi_coin_block_1: 0xBC0672,
+ LocationName.donut_secret_house_life_block_1: 0xBC0673,
+ LocationName.donut_secret_house_vine_block_1: 0xBC0674,
+ LocationName.donut_secret_house_directional_coin_block_1: 0xBC0675,
+ LocationName.donut_plains_1_coin_block_1: 0xBC0676,
+ LocationName.donut_plains_1_coin_block_2: 0xBC0677,
+ LocationName.donut_plains_1_yoshi_block_1: 0xBC0678,
+ LocationName.donut_plains_1_vine_block_1: 0xBC0679,
+ LocationName.donut_plains_1_green_block_1: 0xBC067A,
+ LocationName.donut_plains_1_green_block_2: 0xBC067B,
+ LocationName.donut_plains_1_green_block_3: 0xBC067C,
+ LocationName.donut_plains_1_green_block_4: 0xBC067D,
+ LocationName.donut_plains_1_green_block_5: 0xBC067E,
+ LocationName.donut_plains_1_green_block_6: 0xBC067F,
+ LocationName.donut_plains_1_green_block_7: 0xBC0680,
+ LocationName.donut_plains_1_green_block_8: 0xBC0681,
+ LocationName.donut_plains_1_green_block_9: 0xBC0682,
+ LocationName.donut_plains_1_green_block_10: 0xBC0683,
+ LocationName.donut_plains_1_green_block_11: 0xBC0684,
+ LocationName.donut_plains_1_green_block_12: 0xBC0685,
+ LocationName.donut_plains_1_green_block_13: 0xBC0686,
+ LocationName.donut_plains_1_green_block_14: 0xBC0687,
+ LocationName.donut_plains_1_green_block_15: 0xBC0688,
+ LocationName.donut_plains_1_green_block_16: 0xBC0689,
+ LocationName.donut_plains_1_yellow_block_1: 0xBC068A,
+ LocationName.donut_plains_1_yellow_block_2: 0xBC068B,
+ LocationName.donut_plains_1_yellow_block_3: 0xBC068C,
+ LocationName.sunken_ghost_ship_powerup_block_1: 0xBC068D,
+ LocationName.sunken_ghost_ship_star_block_1: 0xBC068E,
+ LocationName.chocolate_castle_yellow_block_1: 0xBC068F,
+ LocationName.chocolate_castle_yellow_block_2: 0xBC0690,
+ LocationName.chocolate_castle_green_block_1: 0xBC0691,
+ LocationName.chocolate_fortress_powerup_block_1: 0xBC0692,
+ LocationName.chocolate_fortress_powerup_block_2: 0xBC0693,
+ LocationName.chocolate_fortress_coin_block_1: 0xBC0694,
+ LocationName.chocolate_fortress_coin_block_2: 0xBC0695,
+ LocationName.chocolate_fortress_green_block_1: 0xBC0696,
+ LocationName.chocolate_island_5_yoshi_block_1: 0xBC0697,
+ LocationName.chocolate_island_5_powerup_block_1: 0xBC0698,
+ LocationName.chocolate_island_5_life_block_1: 0xBC0699,
+ LocationName.chocolate_island_5_yellow_block_1: 0xBC069A,
+ LocationName.chocolate_island_4_yellow_block_1: 0xBC069B,
+ LocationName.chocolate_island_4_blue_pow_block_1: 0xBC069C,
+ LocationName.chocolate_island_4_powerup_block_1: 0xBC069D,
+ LocationName.forest_fortress_yellow_block_1: 0xBC069E,
+ LocationName.forest_fortress_powerup_block_1: 0xBC069F,
+ LocationName.forest_fortress_life_block_1: 0xBC06A0,
+ LocationName.forest_fortress_life_block_2: 0xBC06A1,
+ LocationName.forest_fortress_life_block_3: 0xBC06A2,
+ LocationName.forest_fortress_life_block_4: 0xBC06A3,
+ LocationName.forest_fortress_life_block_5: 0xBC06A4,
+ LocationName.forest_fortress_life_block_6: 0xBC06A5,
+ LocationName.forest_fortress_life_block_7: 0xBC06A6,
+ LocationName.forest_fortress_life_block_8: 0xBC06A7,
+ LocationName.forest_fortress_life_block_9: 0xBC06A8,
+ LocationName.forest_castle_green_block_1: 0xBC06A9,
+ LocationName.chocolate_ghost_house_powerup_block_1: 0xBC06AA,
+ LocationName.chocolate_ghost_house_powerup_block_2: 0xBC06AB,
+ LocationName.chocolate_ghost_house_life_block_1: 0xBC06AC,
+ LocationName.chocolate_island_1_flying_block_1: 0xBC06AD,
+ LocationName.chocolate_island_1_flying_block_2: 0xBC06AE,
+ LocationName.chocolate_island_1_yoshi_block_1: 0xBC06AF,
+ LocationName.chocolate_island_1_green_block_1: 0xBC06B0,
+ LocationName.chocolate_island_1_life_block_1: 0xBC06B1,
+ LocationName.chocolate_island_3_powerup_block_1: 0xBC06B2,
+ LocationName.chocolate_island_3_powerup_block_2: 0xBC06B3,
+ LocationName.chocolate_island_3_powerup_block_3: 0xBC06B4,
+ LocationName.chocolate_island_3_green_block_1: 0xBC06B5,
+ LocationName.chocolate_island_3_bonus_block_1: 0xBC06B6,
+ LocationName.chocolate_island_3_vine_block_1: 0xBC06B7,
+ LocationName.chocolate_island_3_life_block_1: 0xBC06B8,
+ LocationName.chocolate_island_3_life_block_2: 0xBC06B9,
+ LocationName.chocolate_island_3_life_block_3: 0xBC06BA,
+ LocationName.chocolate_island_2_multi_coin_block_1: 0xBC06BB,
+ LocationName.chocolate_island_2_invis_coin_block_1: 0xBC06BC,
+ LocationName.chocolate_island_2_yoshi_block_1: 0xBC06BD,
+ LocationName.chocolate_island_2_coin_block_1: 0xBC06BE,
+ LocationName.chocolate_island_2_coin_block_2: 0xBC06BF,
+ LocationName.chocolate_island_2_multi_coin_block_2: 0xBC06C0,
+ LocationName.chocolate_island_2_powerup_block_1: 0xBC06C1,
+ LocationName.chocolate_island_2_blue_pow_block_1: 0xBC06C2,
+ LocationName.chocolate_island_2_yellow_block_1: 0xBC06C3,
+ LocationName.chocolate_island_2_yellow_block_2: 0xBC06C4,
+ LocationName.chocolate_island_2_green_block_1: 0xBC06C5,
+ LocationName.chocolate_island_2_green_block_2: 0xBC06C6,
+ LocationName.chocolate_island_2_green_block_3: 0xBC06C7,
+ LocationName.chocolate_island_2_green_block_4: 0xBC06C8,
+ LocationName.chocolate_island_2_green_block_5: 0xBC06C9,
+ LocationName.chocolate_island_2_green_block_6: 0xBC06CA,
+ LocationName.yoshis_island_castle_coin_block_1: 0xBC06CB,
+ LocationName.yoshis_island_castle_coin_block_2: 0xBC06CC,
+ LocationName.yoshis_island_castle_powerup_block_1: 0xBC06CD,
+ LocationName.yoshis_island_castle_coin_block_3: 0xBC06CE,
+ LocationName.yoshis_island_castle_coin_block_4: 0xBC06CF,
+ LocationName.yoshis_island_castle_flying_block_1: 0xBC06D0,
+ LocationName.yoshis_island_4_yellow_block_1: 0xBC06D1,
+ LocationName.yoshis_island_4_powerup_block_1: 0xBC06D2,
+ LocationName.yoshis_island_4_multi_coin_block_1: 0xBC06D3,
+ LocationName.yoshis_island_4_star_block_1: 0xBC06D4,
+ LocationName.yoshis_island_3_yellow_block_1: 0xBC06D5,
+ LocationName.yoshis_island_3_yellow_block_2: 0xBC06D6,
+ LocationName.yoshis_island_3_yellow_block_3: 0xBC06D7,
+ LocationName.yoshis_island_3_yellow_block_4: 0xBC06D8,
+ LocationName.yoshis_island_3_yellow_block_5: 0xBC06D9,
+ LocationName.yoshis_island_3_yellow_block_6: 0xBC06DA,
+ LocationName.yoshis_island_3_yellow_block_7: 0xBC06DB,
+ LocationName.yoshis_island_3_yellow_block_8: 0xBC06DC,
+ LocationName.yoshis_island_3_yellow_block_9: 0xBC06DD,
+ LocationName.yoshis_island_3_coin_block_1: 0xBC06DE,
+ LocationName.yoshis_island_3_yoshi_block_1: 0xBC06DF,
+ LocationName.yoshis_island_3_coin_block_2: 0xBC06E0,
+ LocationName.yoshis_island_3_powerup_block_1: 0xBC06E1,
+ LocationName.yoshis_island_3_yellow_block_10: 0xBC06E2,
+ LocationName.yoshis_island_3_yellow_block_11: 0xBC06E3,
+ LocationName.yoshis_island_3_yellow_block_12: 0xBC06E4,
+ LocationName.yoshis_island_3_bonus_block_1: 0xBC06E5,
+ LocationName.yoshis_island_1_flying_block_1: 0xBC06E6,
+ LocationName.yoshis_island_1_yellow_block_1: 0xBC06E7,
+ LocationName.yoshis_island_1_life_block_1: 0xBC06E8,
+ LocationName.yoshis_island_1_powerup_block_1: 0xBC06E9,
+ LocationName.yoshis_island_2_flying_block_1: 0xBC06EA,
+ LocationName.yoshis_island_2_flying_block_2: 0xBC06EB,
+ LocationName.yoshis_island_2_flying_block_3: 0xBC06EC,
+ LocationName.yoshis_island_2_flying_block_4: 0xBC06ED,
+ LocationName.yoshis_island_2_flying_block_5: 0xBC06EE,
+ LocationName.yoshis_island_2_flying_block_6: 0xBC06EF,
+ LocationName.yoshis_island_2_coin_block_1: 0xBC06F0,
+ LocationName.yoshis_island_2_yellow_block_1: 0xBC06F1,
+ LocationName.yoshis_island_2_coin_block_2: 0xBC06F2,
+ LocationName.yoshis_island_2_coin_block_3: 0xBC06F3,
+ LocationName.yoshis_island_2_yoshi_block_1: 0xBC06F4,
+ LocationName.yoshis_island_2_coin_block_4: 0xBC06F5,
+ LocationName.yoshis_island_2_yoshi_block_2: 0xBC06F6,
+ LocationName.yoshis_island_2_coin_block_5: 0xBC06F7,
+ LocationName.yoshis_island_2_vine_block_1: 0xBC06F8,
+ LocationName.yoshis_island_2_yellow_block_2: 0xBC06F9,
+ LocationName.vanilla_ghost_house_powerup_block_1: 0xBC06FA,
+ LocationName.vanilla_ghost_house_vine_block_1: 0xBC06FB,
+ LocationName.vanilla_ghost_house_powerup_block_2: 0xBC06FC,
+ LocationName.vanilla_ghost_house_multi_coin_block_1: 0xBC06FD,
+ LocationName.vanilla_ghost_house_blue_pow_block_1: 0xBC06FE,
+ LocationName.vanilla_secret_1_coin_block_1: 0xBC06FF,
+ LocationName.vanilla_secret_1_powerup_block_1: 0xBC0700,
+ LocationName.vanilla_secret_1_multi_coin_block_1: 0xBC0701,
+ LocationName.vanilla_secret_1_vine_block_1: 0xBC0702,
+ LocationName.vanilla_secret_1_vine_block_2: 0xBC0703,
+ LocationName.vanilla_secret_1_coin_block_2: 0xBC0704,
+ LocationName.vanilla_secret_1_coin_block_3: 0xBC0705,
+ LocationName.vanilla_secret_1_powerup_block_2: 0xBC0706,
+ LocationName.vanilla_dome_3_coin_block_1: 0xBC0707,
+ LocationName.vanilla_dome_3_flying_block_1: 0xBC0708,
+ LocationName.vanilla_dome_3_flying_block_2: 0xBC0709,
+ LocationName.vanilla_dome_3_powerup_block_1: 0xBC070A,
+ LocationName.vanilla_dome_3_flying_block_3: 0xBC070B,
+ LocationName.vanilla_dome_3_invis_coin_block_1: 0xBC070C,
+ LocationName.vanilla_dome_3_powerup_block_2: 0xBC070D,
+ LocationName.vanilla_dome_3_multi_coin_block_1: 0xBC070E,
+ LocationName.vanilla_dome_3_powerup_block_3: 0xBC070F,
+ LocationName.vanilla_dome_3_yoshi_block_1: 0xBC0710,
+ LocationName.vanilla_dome_3_powerup_block_4: 0xBC0711,
+ LocationName.vanilla_dome_3_pswitch_coin_block_1: 0xBC0712,
+ LocationName.vanilla_dome_3_pswitch_coin_block_2: 0xBC0713,
+ LocationName.vanilla_dome_3_pswitch_coin_block_3: 0xBC0714,
+ LocationName.vanilla_dome_3_pswitch_coin_block_4: 0xBC0715,
+ LocationName.vanilla_dome_3_pswitch_coin_block_5: 0xBC0716,
+ LocationName.vanilla_dome_3_pswitch_coin_block_6: 0xBC0717,
+ LocationName.donut_secret_2_directional_coin_block_1: 0xBC0718,
+ LocationName.donut_secret_2_vine_block_1: 0xBC0719,
+ LocationName.donut_secret_2_star_block_1: 0xBC071A,
+ LocationName.donut_secret_2_powerup_block_1: 0xBC071B,
+ LocationName.donut_secret_2_star_block_2: 0xBC071C,
+ LocationName.valley_of_bowser_4_yellow_block_1: 0xBC071D,
+ LocationName.valley_of_bowser_4_powerup_block_1: 0xBC071E,
+ LocationName.valley_of_bowser_4_vine_block_1: 0xBC071F,
+ LocationName.valley_of_bowser_4_yoshi_block_1: 0xBC0720,
+ LocationName.valley_of_bowser_4_life_block_1: 0xBC0721,
+ LocationName.valley_of_bowser_4_powerup_block_2: 0xBC0722,
+ LocationName.valley_castle_yellow_block_1: 0xBC0723,
+ LocationName.valley_castle_yellow_block_2: 0xBC0724,
+ LocationName.valley_castle_green_block_1: 0xBC0725,
+ LocationName.valley_fortress_green_block_1: 0xBC0726,
+ LocationName.valley_fortress_yellow_block_1: 0xBC0727,
+ LocationName.valley_of_bowser_3_powerup_block_1: 0xBC0728,
+ LocationName.valley_of_bowser_3_powerup_block_2: 0xBC0729,
+ LocationName.valley_ghost_house_pswitch_coin_block_1: 0xBC072A,
+ LocationName.valley_ghost_house_multi_coin_block_1: 0xBC072B,
+ LocationName.valley_ghost_house_powerup_block_1: 0xBC072C,
+ LocationName.valley_ghost_house_directional_coin_block_1: 0xBC072D,
+ LocationName.valley_of_bowser_2_powerup_block_1: 0xBC072E,
+ LocationName.valley_of_bowser_2_yellow_block_1: 0xBC072F,
+ LocationName.valley_of_bowser_2_powerup_block_2: 0xBC0730,
+ LocationName.valley_of_bowser_2_wings_block_1: 0xBC0731,
+ LocationName.valley_of_bowser_1_green_block_1: 0xBC0732,
+ LocationName.valley_of_bowser_1_invis_coin_block_1: 0xBC0733,
+ LocationName.valley_of_bowser_1_invis_coin_block_2: 0xBC0734,
+ LocationName.valley_of_bowser_1_invis_coin_block_3: 0xBC0735,
+ LocationName.valley_of_bowser_1_yellow_block_1: 0xBC0736,
+ LocationName.valley_of_bowser_1_yellow_block_2: 0xBC0737,
+ LocationName.valley_of_bowser_1_yellow_block_3: 0xBC0738,
+ LocationName.valley_of_bowser_1_yellow_block_4: 0xBC0739,
+ LocationName.valley_of_bowser_1_vine_block_1: 0xBC073A,
+ LocationName.chocolate_secret_powerup_block_1: 0xBC073B,
+ LocationName.chocolate_secret_powerup_block_2: 0xBC073C,
+ LocationName.vanilla_dome_2_coin_block_1: 0xBC073D,
+ LocationName.vanilla_dome_2_powerup_block_1: 0xBC073E,
+ LocationName.vanilla_dome_2_coin_block_2: 0xBC073F,
+ LocationName.vanilla_dome_2_coin_block_3: 0xBC0740,
+ LocationName.vanilla_dome_2_vine_block_1: 0xBC0741,
+ LocationName.vanilla_dome_2_invis_life_block_1: 0xBC0742,
+ LocationName.vanilla_dome_2_coin_block_4: 0xBC0743,
+ LocationName.vanilla_dome_2_coin_block_5: 0xBC0744,
+ LocationName.vanilla_dome_2_powerup_block_2: 0xBC0745,
+ LocationName.vanilla_dome_2_powerup_block_3: 0xBC0746,
+ LocationName.vanilla_dome_2_powerup_block_4: 0xBC0747,
+ LocationName.vanilla_dome_2_powerup_block_5: 0xBC0748,
+ LocationName.vanilla_dome_2_multi_coin_block_1: 0xBC0749,
+ LocationName.vanilla_dome_2_multi_coin_block_2: 0xBC074A,
+ LocationName.vanilla_dome_4_powerup_block_1: 0xBC074B,
+ LocationName.vanilla_dome_4_powerup_block_2: 0xBC074C,
+ LocationName.vanilla_dome_4_coin_block_1: 0xBC074D,
+ LocationName.vanilla_dome_4_coin_block_2: 0xBC074E,
+ LocationName.vanilla_dome_4_coin_block_3: 0xBC074F,
+ LocationName.vanilla_dome_4_life_block_1: 0xBC0750,
+ LocationName.vanilla_dome_4_coin_block_4: 0xBC0751,
+ LocationName.vanilla_dome_4_coin_block_5: 0xBC0752,
+ LocationName.vanilla_dome_4_coin_block_6: 0xBC0753,
+ LocationName.vanilla_dome_4_coin_block_7: 0xBC0754,
+ LocationName.vanilla_dome_4_coin_block_8: 0xBC0755,
+ LocationName.vanilla_dome_1_flying_block_1: 0xBC0756,
+ LocationName.vanilla_dome_1_powerup_block_1: 0xBC0757,
+ LocationName.vanilla_dome_1_powerup_block_2: 0xBC0758,
+ LocationName.vanilla_dome_1_coin_block_1: 0xBC0759,
+ LocationName.vanilla_dome_1_life_block_1: 0xBC075A,
+ LocationName.vanilla_dome_1_powerup_block_3: 0xBC075B,
+ LocationName.vanilla_dome_1_vine_block_1: 0xBC075C,
+ LocationName.vanilla_dome_1_star_block_1: 0xBC075D,
+ LocationName.vanilla_dome_1_powerup_block_4: 0xBC075E,
+ LocationName.vanilla_dome_1_coin_block_2: 0xBC075F,
+ LocationName.vanilla_dome_castle_life_block_1: 0xBC0760,
+ LocationName.vanilla_dome_castle_life_block_2: 0xBC0761,
+ LocationName.vanilla_dome_castle_powerup_block_1: 0xBC0762,
+ LocationName.vanilla_dome_castle_life_block_3: 0xBC0763,
+ LocationName.vanilla_dome_castle_green_block_1: 0xBC0764,
+ LocationName.forest_ghost_house_coin_block_1: 0xBC0765,
+ LocationName.forest_ghost_house_powerup_block_1: 0xBC0766,
+ LocationName.forest_ghost_house_flying_block_1: 0xBC0767,
+ LocationName.forest_ghost_house_powerup_block_2: 0xBC0768,
+ LocationName.forest_ghost_house_life_block_1: 0xBC0769,
+ LocationName.forest_of_illusion_1_powerup_block_1: 0xBC076A,
+ LocationName.forest_of_illusion_1_yoshi_block_1: 0xBC076B,
+ LocationName.forest_of_illusion_1_powerup_block_2: 0xBC076C,
+ LocationName.forest_of_illusion_1_key_block_1: 0xBC076D,
+ LocationName.forest_of_illusion_1_life_block_1: 0xBC076E,
+ LocationName.forest_of_illusion_4_multi_coin_block_1: 0xBC076F,
+ LocationName.forest_of_illusion_4_coin_block_1: 0xBC0770,
+ LocationName.forest_of_illusion_4_coin_block_2: 0xBC0771,
+ LocationName.forest_of_illusion_4_coin_block_3: 0xBC0772,
+ LocationName.forest_of_illusion_4_coin_block_4: 0xBC0773,
+ LocationName.forest_of_illusion_4_powerup_block_1: 0xBC0774,
+ LocationName.forest_of_illusion_4_coin_block_5: 0xBC0775,
+ LocationName.forest_of_illusion_4_coin_block_6: 0xBC0776,
+ LocationName.forest_of_illusion_4_coin_block_7: 0xBC0777,
+ LocationName.forest_of_illusion_4_powerup_block_2: 0xBC0778,
+ LocationName.forest_of_illusion_4_coin_block_8: 0xBC0779,
+ LocationName.forest_of_illusion_4_coin_block_9: 0xBC077A,
+ LocationName.forest_of_illusion_4_coin_block_10: 0xBC077B,
+ LocationName.forest_of_illusion_2_green_block_1: 0xBC077C,
+ LocationName.forest_of_illusion_2_powerup_block_1: 0xBC077D,
+ LocationName.forest_of_illusion_2_invis_coin_block_1: 0xBC077E,
+ LocationName.forest_of_illusion_2_invis_coin_block_2: 0xBC077F,
+ LocationName.forest_of_illusion_2_invis_life_block_1: 0xBC0780,
+ LocationName.forest_of_illusion_2_invis_coin_block_3: 0xBC0781,
+ LocationName.forest_of_illusion_2_yellow_block_1: 0xBC0782,
+ LocationName.forest_secret_powerup_block_1: 0xBC0783,
+ LocationName.forest_secret_powerup_block_2: 0xBC0784,
+ LocationName.forest_secret_life_block_1: 0xBC0785,
+ LocationName.forest_of_illusion_3_yoshi_block_1: 0xBC0786,
+ LocationName.forest_of_illusion_3_coin_block_1: 0xBC0787,
+ LocationName.forest_of_illusion_3_multi_coin_block_1: 0xBC0788,
+ LocationName.forest_of_illusion_3_coin_block_2: 0xBC0789,
+ LocationName.forest_of_illusion_3_multi_coin_block_2: 0xBC078A,
+ LocationName.forest_of_illusion_3_coin_block_3: 0xBC078B,
+ LocationName.forest_of_illusion_3_coin_block_4: 0xBC078C,
+ LocationName.forest_of_illusion_3_coin_block_5: 0xBC078D,
+ LocationName.forest_of_illusion_3_coin_block_6: 0xBC078E,
+ LocationName.forest_of_illusion_3_coin_block_7: 0xBC078F,
+ LocationName.forest_of_illusion_3_coin_block_8: 0xBC0790,
+ LocationName.forest_of_illusion_3_coin_block_9: 0xBC0791,
+ LocationName.forest_of_illusion_3_coin_block_10: 0xBC0792,
+ LocationName.forest_of_illusion_3_coin_block_11: 0xBC0793,
+ LocationName.forest_of_illusion_3_coin_block_12: 0xBC0794,
+ LocationName.forest_of_illusion_3_coin_block_13: 0xBC0795,
+ LocationName.forest_of_illusion_3_coin_block_14: 0xBC0796,
+ LocationName.forest_of_illusion_3_coin_block_15: 0xBC0797,
+ LocationName.forest_of_illusion_3_coin_block_16: 0xBC0798,
+ LocationName.forest_of_illusion_3_coin_block_17: 0xBC0799,
+ LocationName.forest_of_illusion_3_coin_block_18: 0xBC079A,
+ LocationName.forest_of_illusion_3_coin_block_19: 0xBC079B,
+ LocationName.forest_of_illusion_3_coin_block_20: 0xBC079C,
+ LocationName.forest_of_illusion_3_coin_block_21: 0xBC079D,
+ LocationName.forest_of_illusion_3_coin_block_22: 0xBC079E,
+ LocationName.forest_of_illusion_3_coin_block_23: 0xBC079F,
+ LocationName.forest_of_illusion_3_coin_block_24: 0xBC07A0,
+ LocationName.special_zone_8_yoshi_block_1: 0xBC07A1,
+ LocationName.special_zone_8_coin_block_1: 0xBC07A2,
+ LocationName.special_zone_8_coin_block_2: 0xBC07A3,
+ LocationName.special_zone_8_coin_block_3: 0xBC07A4,
+ LocationName.special_zone_8_coin_block_4: 0xBC07A5,
+ LocationName.special_zone_8_coin_block_5: 0xBC07A6,
+ LocationName.special_zone_8_blue_pow_block_1: 0xBC07A7,
+ LocationName.special_zone_8_powerup_block_1: 0xBC07A8,
+ LocationName.special_zone_8_star_block_1: 0xBC07A9,
+ LocationName.special_zone_8_coin_block_6: 0xBC07AA,
+ LocationName.special_zone_8_coin_block_7: 0xBC07AB,
+ LocationName.special_zone_8_coin_block_8: 0xBC07AC,
+ LocationName.special_zone_8_coin_block_9: 0xBC07AD,
+ LocationName.special_zone_8_coin_block_10: 0xBC07AE,
+ LocationName.special_zone_8_coin_block_11: 0xBC07AF,
+ LocationName.special_zone_8_coin_block_12: 0xBC07B0,
+ LocationName.special_zone_8_coin_block_13: 0xBC07B1,
+ LocationName.special_zone_8_coin_block_14: 0xBC07B2,
+ LocationName.special_zone_8_coin_block_15: 0xBC07B3,
+ LocationName.special_zone_8_coin_block_16: 0xBC07B4,
+ LocationName.special_zone_8_coin_block_17: 0xBC07B5,
+ LocationName.special_zone_8_coin_block_18: 0xBC07B6,
+ LocationName.special_zone_8_multi_coin_block_1: 0xBC07B7,
+ LocationName.special_zone_8_coin_block_19: 0xBC07B8,
+ LocationName.special_zone_8_coin_block_20: 0xBC07B9,
+ LocationName.special_zone_8_coin_block_21: 0xBC07BA,
+ LocationName.special_zone_8_coin_block_22: 0xBC07BB,
+ LocationName.special_zone_8_coin_block_23: 0xBC07BC,
+ LocationName.special_zone_8_powerup_block_2: 0xBC07BD,
+ LocationName.special_zone_8_flying_block_1: 0xBC07BE,
+ LocationName.special_zone_7_powerup_block_1: 0xBC07BF,
+ LocationName.special_zone_7_yoshi_block_1: 0xBC07C0,
+ LocationName.special_zone_7_coin_block_1: 0xBC07C1,
+ LocationName.special_zone_7_powerup_block_2: 0xBC07C2,
+ LocationName.special_zone_7_coin_block_2: 0xBC07C3,
+ LocationName.special_zone_6_powerup_block_1: 0xBC07C4,
+ LocationName.special_zone_6_coin_block_1: 0xBC07C5,
+ LocationName.special_zone_6_coin_block_2: 0xBC07C6,
+ LocationName.special_zone_6_yoshi_block_1: 0xBC07C7,
+ LocationName.special_zone_6_life_block_1: 0xBC07C8,
+ LocationName.special_zone_6_multi_coin_block_1: 0xBC07C9,
+ LocationName.special_zone_6_coin_block_3: 0xBC07CA,
+ LocationName.special_zone_6_coin_block_4: 0xBC07CB,
+ LocationName.special_zone_6_coin_block_5: 0xBC07CC,
+ LocationName.special_zone_6_coin_block_6: 0xBC07CD,
+ LocationName.special_zone_6_coin_block_7: 0xBC07CE,
+ LocationName.special_zone_6_coin_block_8: 0xBC07CF,
+ LocationName.special_zone_6_coin_block_9: 0xBC07D0,
+ LocationName.special_zone_6_coin_block_10: 0xBC07D1,
+ LocationName.special_zone_6_coin_block_11: 0xBC07D2,
+ LocationName.special_zone_6_coin_block_12: 0xBC07D3,
+ LocationName.special_zone_6_coin_block_13: 0xBC07D4,
+ LocationName.special_zone_6_coin_block_14: 0xBC07D5,
+ LocationName.special_zone_6_coin_block_15: 0xBC07D6,
+ LocationName.special_zone_6_coin_block_16: 0xBC07D7,
+ LocationName.special_zone_6_coin_block_17: 0xBC07D8,
+ LocationName.special_zone_6_coin_block_18: 0xBC07D9,
+ LocationName.special_zone_6_coin_block_19: 0xBC07DA,
+ LocationName.special_zone_6_coin_block_20: 0xBC07DB,
+ LocationName.special_zone_6_coin_block_21: 0xBC07DC,
+ LocationName.special_zone_6_coin_block_22: 0xBC07DD,
+ LocationName.special_zone_6_coin_block_23: 0xBC07DE,
+ LocationName.special_zone_6_coin_block_24: 0xBC07DF,
+ LocationName.special_zone_6_coin_block_25: 0xBC07E0,
+ LocationName.special_zone_6_coin_block_26: 0xBC07E1,
+ LocationName.special_zone_6_coin_block_27: 0xBC07E2,
+ LocationName.special_zone_6_coin_block_28: 0xBC07E3,
+ LocationName.special_zone_6_powerup_block_2: 0xBC07E4,
+ LocationName.special_zone_6_coin_block_29: 0xBC07E5,
+ LocationName.special_zone_6_coin_block_30: 0xBC07E6,
+ LocationName.special_zone_6_coin_block_31: 0xBC07E7,
+ LocationName.special_zone_6_coin_block_32: 0xBC07E8,
+ LocationName.special_zone_6_coin_block_33: 0xBC07E9,
+ LocationName.special_zone_5_yoshi_block_1: 0xBC07EA,
+ LocationName.special_zone_1_vine_block_1: 0xBC07EB,
+ LocationName.special_zone_1_vine_block_2: 0xBC07EC,
+ LocationName.special_zone_1_vine_block_3: 0xBC07ED,
+ LocationName.special_zone_1_vine_block_4: 0xBC07EE,
+ LocationName.special_zone_1_life_block_1: 0xBC07EF,
+ LocationName.special_zone_1_vine_block_5: 0xBC07F0,
+ LocationName.special_zone_1_blue_pow_block_1: 0xBC07F1,
+ LocationName.special_zone_1_vine_block_6: 0xBC07F2,
+ LocationName.special_zone_1_powerup_block_1: 0xBC07F3,
+ LocationName.special_zone_1_pswitch_coin_block_1: 0xBC07F4,
+ LocationName.special_zone_1_pswitch_coin_block_2: 0xBC07F5,
+ LocationName.special_zone_1_pswitch_coin_block_3: 0xBC07F6,
+ LocationName.special_zone_1_pswitch_coin_block_4: 0xBC07F7,
+ LocationName.special_zone_1_pswitch_coin_block_5: 0xBC07F8,
+ LocationName.special_zone_1_pswitch_coin_block_6: 0xBC07F9,
+ LocationName.special_zone_1_pswitch_coin_block_7: 0xBC07FA,
+ LocationName.special_zone_1_pswitch_coin_block_8: 0xBC07FB,
+ LocationName.special_zone_1_pswitch_coin_block_9: 0xBC07FC,
+ LocationName.special_zone_1_pswitch_coin_block_10: 0xBC07FD,
+ LocationName.special_zone_1_pswitch_coin_block_11: 0xBC07FE,
+ LocationName.special_zone_1_pswitch_coin_block_12: 0xBC07FF,
+ LocationName.special_zone_1_pswitch_coin_block_13: 0xBC0800,
+ LocationName.special_zone_2_powerup_block_1: 0xBC0801,
+ LocationName.special_zone_2_coin_block_1: 0xBC0802,
+ LocationName.special_zone_2_coin_block_2: 0xBC0803,
+ LocationName.special_zone_2_powerup_block_2: 0xBC0804,
+ LocationName.special_zone_2_coin_block_3: 0xBC0805,
+ LocationName.special_zone_2_coin_block_4: 0xBC0806,
+ LocationName.special_zone_2_powerup_block_3: 0xBC0807,
+ LocationName.special_zone_2_multi_coin_block_1: 0xBC0808,
+ LocationName.special_zone_2_coin_block_5: 0xBC0809,
+ LocationName.special_zone_2_coin_block_6: 0xBC080A,
+ LocationName.special_zone_3_powerup_block_1: 0xBC080B,
+ LocationName.special_zone_3_yoshi_block_1: 0xBC080C,
+ LocationName.special_zone_3_wings_block_1: 0xBC080D,
+ LocationName.special_zone_4_powerup_block_1: 0xBC080E,
+ LocationName.special_zone_4_star_block_1: 0xBC080F,
+ LocationName.star_road_2_star_block_1: 0xBC0810,
+ LocationName.star_road_3_key_block_1: 0xBC0811,
+ LocationName.star_road_4_powerup_block_1: 0xBC0812,
+ LocationName.star_road_4_green_block_1: 0xBC0813,
+ LocationName.star_road_4_green_block_2: 0xBC0814,
+ LocationName.star_road_4_green_block_3: 0xBC0815,
+ LocationName.star_road_4_green_block_4: 0xBC0816,
+ LocationName.star_road_4_green_block_5: 0xBC0817,
+ LocationName.star_road_4_green_block_6: 0xBC0818,
+ LocationName.star_road_4_green_block_7: 0xBC0819,
+ LocationName.star_road_4_key_block_1: 0xBC081A,
+ LocationName.star_road_5_directional_coin_block_1: 0xBC081B,
+ LocationName.star_road_5_life_block_1: 0xBC081C,
+ LocationName.star_road_5_vine_block_1: 0xBC081D,
+ LocationName.star_road_5_yellow_block_1: 0xBC081E,
+ LocationName.star_road_5_yellow_block_2: 0xBC081F,
+ LocationName.star_road_5_yellow_block_3: 0xBC0820,
+ LocationName.star_road_5_yellow_block_4: 0xBC0821,
+ LocationName.star_road_5_yellow_block_5: 0xBC0822,
+ LocationName.star_road_5_yellow_block_6: 0xBC0823,
+ LocationName.star_road_5_yellow_block_7: 0xBC0824,
+ LocationName.star_road_5_yellow_block_8: 0xBC0825,
+ LocationName.star_road_5_yellow_block_9: 0xBC0826,
+ LocationName.star_road_5_yellow_block_10: 0xBC0827,
+ LocationName.star_road_5_yellow_block_11: 0xBC0828,
+ LocationName.star_road_5_yellow_block_12: 0xBC0829,
+ LocationName.star_road_5_yellow_block_13: 0xBC082A,
+ LocationName.star_road_5_yellow_block_14: 0xBC082B,
+ LocationName.star_road_5_yellow_block_15: 0xBC082C,
+ LocationName.star_road_5_yellow_block_16: 0xBC082D,
+ LocationName.star_road_5_yellow_block_17: 0xBC082E,
+ LocationName.star_road_5_yellow_block_18: 0xBC082F,
+ LocationName.star_road_5_yellow_block_19: 0xBC0830,
+ LocationName.star_road_5_yellow_block_20: 0xBC0831,
+ LocationName.star_road_5_green_block_1: 0xBC0832,
+ LocationName.star_road_5_green_block_2: 0xBC0833,
+ LocationName.star_road_5_green_block_3: 0xBC0834,
+ LocationName.star_road_5_green_block_4: 0xBC0835,
+ LocationName.star_road_5_green_block_5: 0xBC0836,
+ LocationName.star_road_5_green_block_6: 0xBC0837,
+ LocationName.star_road_5_green_block_7: 0xBC0838,
+ LocationName.star_road_5_green_block_8: 0xBC0839,
+ LocationName.star_road_5_green_block_9: 0xBC083A,
+ LocationName.star_road_5_green_block_10: 0xBC083B,
+ LocationName.star_road_5_green_block_11: 0xBC083C,
+ LocationName.star_road_5_green_block_12: 0xBC083D,
+ LocationName.star_road_5_green_block_13: 0xBC083E,
+ LocationName.star_road_5_green_block_14: 0xBC083F,
+ LocationName.star_road_5_green_block_15: 0xBC0840,
+ LocationName.star_road_5_green_block_16: 0xBC0841,
+ LocationName.star_road_5_green_block_17: 0xBC0842,
+ LocationName.star_road_5_green_block_18: 0xBC0843,
+ LocationName.star_road_5_green_block_19: 0xBC0844,
+ LocationName.star_road_5_green_block_20: 0xBC0845
+}
+
bowser_location_table = {
LocationName.bowser: 0xBC0200,
}
@@ -208,6 +826,10 @@ def __init__(self, player: int, name: str = '', address: int = None, parent=None
all_locations = {
**level_location_table,
**dragon_coin_location_table,
+ **moon_location_table,
+ **hidden_1ups_location_table,
+ **bonus_block_location_table,
+ **blocksanity_location_table,
**bowser_location_table,
**yoshi_house_location_table,
}
@@ -234,20 +856,149 @@ def __init__(self, player: int, name: str = '', address: int = None, parent=None
LocationName.special_zone_8_dragon,
]
+special_zone_hidden_1up_names = [
+ LocationName.special_zone_1_hidden_1up
+]
+
+special_zone_blocksanity_names = [
+ LocationName.special_zone_8_yoshi_block_1,
+ LocationName.special_zone_8_coin_block_1,
+ LocationName.special_zone_8_coin_block_2,
+ LocationName.special_zone_8_coin_block_3,
+ LocationName.special_zone_8_coin_block_4,
+ LocationName.special_zone_8_coin_block_5,
+ LocationName.special_zone_8_blue_pow_block_1,
+ LocationName.special_zone_8_powerup_block_1,
+ LocationName.special_zone_8_star_block_1,
+ LocationName.special_zone_8_coin_block_6,
+ LocationName.special_zone_8_coin_block_7,
+ LocationName.special_zone_8_coin_block_8,
+ LocationName.special_zone_8_coin_block_9,
+ LocationName.special_zone_8_coin_block_10,
+ LocationName.special_zone_8_coin_block_11,
+ LocationName.special_zone_8_coin_block_12,
+ LocationName.special_zone_8_coin_block_13,
+ LocationName.special_zone_8_coin_block_14,
+ LocationName.special_zone_8_coin_block_15,
+ LocationName.special_zone_8_coin_block_16,
+ LocationName.special_zone_8_coin_block_17,
+ LocationName.special_zone_8_coin_block_18,
+ LocationName.special_zone_8_multi_coin_block_1,
+ LocationName.special_zone_8_coin_block_19,
+ LocationName.special_zone_8_coin_block_20,
+ LocationName.special_zone_8_coin_block_21,
+ LocationName.special_zone_8_coin_block_22,
+ LocationName.special_zone_8_coin_block_23,
+ LocationName.special_zone_8_powerup_block_2,
+ LocationName.special_zone_8_flying_block_1,
+ LocationName.special_zone_7_powerup_block_1,
+ LocationName.special_zone_7_yoshi_block_1,
+ LocationName.special_zone_7_coin_block_1,
+ LocationName.special_zone_7_powerup_block_2,
+ LocationName.special_zone_7_coin_block_2,
+ LocationName.special_zone_6_powerup_block_1,
+ LocationName.special_zone_6_coin_block_1,
+ LocationName.special_zone_6_coin_block_2,
+ LocationName.special_zone_6_yoshi_block_1,
+ LocationName.special_zone_6_life_block_1,
+ LocationName.special_zone_6_multi_coin_block_1,
+ LocationName.special_zone_6_coin_block_3,
+ LocationName.special_zone_6_coin_block_4,
+ LocationName.special_zone_6_coin_block_5,
+ LocationName.special_zone_6_coin_block_6,
+ LocationName.special_zone_6_coin_block_7,
+ LocationName.special_zone_6_coin_block_8,
+ LocationName.special_zone_6_coin_block_9,
+ LocationName.special_zone_6_coin_block_10,
+ LocationName.special_zone_6_coin_block_11,
+ LocationName.special_zone_6_coin_block_12,
+ LocationName.special_zone_6_coin_block_13,
+ LocationName.special_zone_6_coin_block_14,
+ LocationName.special_zone_6_coin_block_15,
+ LocationName.special_zone_6_coin_block_16,
+ LocationName.special_zone_6_coin_block_17,
+ LocationName.special_zone_6_coin_block_18,
+ LocationName.special_zone_6_coin_block_19,
+ LocationName.special_zone_6_coin_block_20,
+ LocationName.special_zone_6_coin_block_21,
+ LocationName.special_zone_6_coin_block_22,
+ LocationName.special_zone_6_coin_block_23,
+ LocationName.special_zone_6_coin_block_24,
+ LocationName.special_zone_6_coin_block_25,
+ LocationName.special_zone_6_coin_block_26,
+ LocationName.special_zone_6_coin_block_27,
+ LocationName.special_zone_6_coin_block_28,
+ LocationName.special_zone_6_powerup_block_2,
+ LocationName.special_zone_6_coin_block_29,
+ LocationName.special_zone_6_coin_block_30,
+ LocationName.special_zone_6_coin_block_31,
+ LocationName.special_zone_6_coin_block_32,
+ LocationName.special_zone_6_coin_block_33,
+ LocationName.special_zone_5_yoshi_block_1,
+ LocationName.special_zone_1_vine_block_1,
+ LocationName.special_zone_1_vine_block_2,
+ LocationName.special_zone_1_vine_block_3,
+ LocationName.special_zone_1_vine_block_4,
+ LocationName.special_zone_1_life_block_1,
+ LocationName.special_zone_1_vine_block_5,
+ LocationName.special_zone_1_blue_pow_block_1,
+ LocationName.special_zone_1_vine_block_6,
+ LocationName.special_zone_1_powerup_block_1,
+ LocationName.special_zone_1_pswitch_coin_block_1,
+ LocationName.special_zone_1_pswitch_coin_block_2,
+ LocationName.special_zone_1_pswitch_coin_block_3,
+ LocationName.special_zone_1_pswitch_coin_block_4,
+ LocationName.special_zone_1_pswitch_coin_block_5,
+ LocationName.special_zone_1_pswitch_coin_block_6,
+ LocationName.special_zone_1_pswitch_coin_block_7,
+ LocationName.special_zone_1_pswitch_coin_block_8,
+ LocationName.special_zone_1_pswitch_coin_block_9,
+ LocationName.special_zone_1_pswitch_coin_block_10,
+ LocationName.special_zone_1_pswitch_coin_block_11,
+ LocationName.special_zone_1_pswitch_coin_block_12,
+ LocationName.special_zone_1_pswitch_coin_block_13,
+ LocationName.special_zone_2_powerup_block_1,
+ LocationName.special_zone_2_coin_block_1,
+ LocationName.special_zone_2_coin_block_2,
+ LocationName.special_zone_2_powerup_block_2,
+ LocationName.special_zone_2_coin_block_3,
+ LocationName.special_zone_2_coin_block_4,
+ LocationName.special_zone_2_powerup_block_3,
+ LocationName.special_zone_2_multi_coin_block_1,
+ LocationName.special_zone_2_coin_block_5,
+ LocationName.special_zone_2_coin_block_6,
+ LocationName.special_zone_3_powerup_block_1,
+ LocationName.special_zone_3_yoshi_block_1,
+ LocationName.special_zone_3_wings_block_1,
+ LocationName.special_zone_4_powerup_block_1,
+ LocationName.special_zone_4_star_block_1
+]
+
location_table = {}
-def setup_locations(world, player: int):
+def setup_locations(world: World):
location_table = {**level_location_table}
- # Dragon Coins here
- if world.dragon_coin_checks[player].value:
- location_table.update({**dragon_coin_location_table})
+ if world.options.dragon_coin_checks:
+ location_table.update(dragon_coin_location_table)
+
+ if world.options.moon_checks:
+ location_table.update(moon_location_table)
+
+ if world.options.hidden_1up_checks:
+ location_table.update(hidden_1ups_location_table)
+
+ if world.options.bonus_block_checks:
+ location_table.update(bonus_block_location_table)
+
+ if world.options.blocksanity:
+ location_table.update(blocksanity_location_table)
- if world.goal[player] == "yoshi_egg_hunt":
- location_table.update({**yoshi_house_location_table})
+ if world.options.goal == "yoshi_egg_hunt":
+ location_table.update(yoshi_house_location_table)
else:
- location_table.update({**bowser_location_table})
+ location_table.update(bowser_location_table)
return location_table
diff --git a/worlds/smw/Names/ItemName.py b/worlds/smw/Names/ItemName.py
index fecb18685ef4..e6eced410059 100644
--- a/worlds/smw/Names/ItemName.py
+++ b/worlds/smw/Names/ItemName.py
@@ -1,5 +1,9 @@
# Junk Definitions
one_up_mushroom = "1-Up Mushroom"
+one_coin = "1 coin"
+five_coins = "5 coins"
+ten_coins = "10 coins"
+fifty_coins = "50 coins"
# Collectable Definitions
yoshi_egg = "Yoshi Egg"
@@ -22,11 +26,16 @@
red_switch_palace = "Red Switch Palace"
blue_switch_palace = "Blue Switch Palace"
+# Special Zone clear flag definition
+special_world_clear = "Special Zone Clear"
+
# Trap Definitions
-ice_trap = "Ice Trap"
-stun_trap = "Stun Trap"
-literature_trap = "Literature Trap"
-timer_trap = "Timer Trap"
+ice_trap = "Ice Trap"
+stun_trap = "Stun Trap"
+literature_trap = "Literature Trap"
+timer_trap = "Timer Trap"
+reverse_controls_trap = "Reverse Trap"
+thwimp_trap = "Thwimp Trap"
# Other Definitions
victory = "The Princess"
diff --git a/worlds/smw/Names/LocationName.py b/worlds/smw/Names/LocationName.py
index cc01b05ece59..847b724f7837 100644
--- a/worlds/smw/Names/LocationName.py
+++ b/worlds/smw/Names/LocationName.py
@@ -4,12 +4,15 @@
yoshis_island_1_exit_1 = "Yoshi's Island 1 - Normal Exit"
yoshis_island_1_dragon = "Yoshi's Island 1 - Dragon Coins"
+yoshis_island_1_moon = "Yoshi's Island 1 - 3-Up Moon"
yoshis_island_2_exit_1 = "Yoshi's Island 2 - Normal Exit"
yoshis_island_2_dragon = "Yoshi's Island 2 - Dragon Coins"
yoshis_island_3_exit_1 = "Yoshi's Island 3 - Normal Exit"
yoshis_island_3_dragon = "Yoshi's Island 3 - Dragon Coins"
+yoshis_island_3_bonus_block = "Yoshi's Island 3 - 1-Up from Bonus Block"
yoshis_island_4_exit_1 = "Yoshi's Island 4 - Normal Exit"
yoshis_island_4_dragon = "Yoshi's Island 4 - Dragon Coins"
+yoshis_island_4_hidden_1up = "Yoshi's Island 4 - Hidden 1-Up"
yoshis_island_castle = "#1 Iggy's Castle - Normal Exit"
yoshis_island_koopaling = "#1 Iggy's Castle - Boss"
@@ -18,13 +21,17 @@
donut_plains_1_exit_1 = "Donut Plains 1 - Normal Exit"
donut_plains_1_exit_2 = "Donut Plains 1 - Secret Exit"
donut_plains_1_dragon = "Donut Plains 1 - Dragon Coins"
+donut_plains_1_hidden_1up = "Donut Plains 1 - Hidden 1-Up"
donut_plains_2_exit_1 = "Donut Plains 2 - Normal Exit"
donut_plains_2_exit_2 = "Donut Plains 2 - Secret Exit"
donut_plains_2_dragon = "Donut Plains 2 - Dragon Coins"
donut_plains_3_exit_1 = "Donut Plains 3 - Normal Exit"
donut_plains_3_dragon = "Donut Plains 3 - Dragon Coins"
+donut_plains_3_bonus_block = "Donut Plains 3 - 1-Up from Bonus Block"
donut_plains_4_exit_1 = "Donut Plains 4 - Normal Exit"
donut_plains_4_dragon = "Donut Plains 4 - Dragon Coins"
+donut_plains_4_moon = "Donut Plains 4 - 3-Up Moon"
+donut_plains_4_hidden_1up = "Donut Plains 4 - Hidden 1-Up"
donut_secret_1_exit_1 = "Donut Secret 1 - Normal Exit"
donut_secret_1_exit_2 = "Donut Secret 1 - Secret Exit"
donut_secret_1_dragon = "Donut Secret 1 - Dragon Coins"
@@ -35,6 +42,7 @@
donut_secret_house_exit_1 = "Donut Secret House - Normal Exit"
donut_secret_house_exit_2 = "Donut Secret House - Secret Exit"
donut_plains_castle = "#2 Morton's Castle - Normal Exit"
+donut_plains_castle_hidden_1up = "#2 Morton's Castle - Hidden 1-Up"
donut_plains_koopaling = "#2 Morton's Castle - Boss"
green_switch_palace = "Green Switch Palace"
@@ -47,8 +55,10 @@
vanilla_dome_2_dragon = "Vanilla Dome 2 - Dragon Coins"
vanilla_dome_3_exit_1 = "Vanilla Dome 3 - Normal Exit"
vanilla_dome_3_dragon = "Vanilla Dome 3 - Dragon Coins"
+vanilla_dome_3_moon = "Vanilla Dome 3 - 3-Up Moon"
vanilla_dome_4_exit_1 = "Vanilla Dome 4 - Normal Exit"
vanilla_dome_4_dragon = "Vanilla Dome 4 - Dragon Coins"
+vanilla_dome_4_hidden_1up = "Vanilla Dome 4 - Hidden 1-Up"
vanilla_secret_1_exit_1 = "Vanilla Secret 1 - Normal Exit"
vanilla_secret_1_exit_2 = "Vanilla Secret 1 - Secret Exit"
vanilla_secret_1_dragon = "Vanilla Secret 1 - Dragon Coins"
@@ -58,7 +68,9 @@
vanilla_secret_3_dragon = "Vanilla Secret 3 - Dragon Coins"
vanilla_ghost_house_exit_1 = "Vanilla Ghost House - Normal Exit"
vanilla_ghost_house_dragon = "Vanilla Ghost House - Dragon Coins"
+vanilla_ghost_house_hidden_1up = "Vanilla Ghost House - Hidden 1-Up"
vanilla_fortress = "Vanilla Fortress - Normal Exit"
+vanilla_fortress_hidden_1up = "Vanilla Fortress - Hidden 1-Up"
vanilla_reznor = "Vanilla Fortress - Boss"
vanilla_dome_castle = "#3 Lemmy's Castle - Normal Exit"
vanilla_dome_koopaling = "#3 Lemmy's Castle - Boss"
@@ -67,13 +79,16 @@
butter_bridge_1_exit_1 = "Butter Bridge 1 - Normal Exit"
butter_bridge_1_dragon = "Butter Bridge 1 - Dragon Coins"
+butter_bridge_1_bonus_block = "Butter Bridge 1 - 1-Up from Bonus Block"
butter_bridge_2_exit_1 = "Butter Bridge 2 - Normal Exit"
butter_bridge_2_dragon = "Butter Bridge 2 - Dragon Coins"
cheese_bridge_exit_1 = "Cheese Bridge - Normal Exit"
cheese_bridge_exit_2 = "Cheese Bridge - Secret Exit"
cheese_bridge_dragon = "Cheese Bridge - Dragon Coins"
+cheese_bridge_moon = "Cheese Bridge - 3-Up Moon"
cookie_mountain_exit_1 = "Cookie Mountain - Normal Exit"
cookie_mountain_dragon = "Cookie Mountain - Dragon Coins"
+cookie_mountain_hidden_1up = "Cookie Mountain - Hidden 1-Up"
soda_lake_exit_1 = "Soda Lake - Normal Exit"
soda_lake_dragon = "Soda Lake - Dragon Coins"
twin_bridges_castle = "#4 Ludwig's Castle - Normal Exit"
@@ -87,12 +102,14 @@
forest_of_illusion_3_exit_1 = "Forest of Illusion 3 - Normal Exit"
forest_of_illusion_3_exit_2 = "Forest of Illusion 3 - Secret Exit"
forest_of_illusion_3_dragon = "Forest of Illusion 3 - Dragon Coins"
+forest_of_illusion_3_hidden_1up = "Forest of Illusion 3 - Hidden 1-Up"
forest_of_illusion_4_exit_1 = "Forest of Illusion 4 - Normal Exit"
forest_of_illusion_4_exit_2 = "Forest of Illusion 4 - Secret Exit"
forest_of_illusion_4_dragon = "Forest of Illusion 4 - Dragon Coins"
forest_ghost_house_exit_1 = "Forest Ghost House - Normal Exit"
forest_ghost_house_exit_2 = "Forest Ghost House - Secret Exit"
forest_ghost_house_dragon = "Forest Ghost House - Dragon Coins"
+forest_ghost_house_moon = "Forest Ghost House - 3-Up Moon"
forest_secret_exit_1 = "Forest Secret - Normal Exit"
forest_secret_dragon = "Forest Secret - Dragon Coins"
forest_fortress = "Forest Fortress - Normal Exit"
@@ -105,12 +122,15 @@
chocolate_island_1_exit_1 = "Chocolate Island 1 - Normal Exit"
chocolate_island_1_dragon = "Chocolate Island 1 - Dragon Coins"
+chocolate_island_1_moon = "Chocolate Island 1 - 3-Up Moon"
chocolate_island_2_exit_1 = "Chocolate Island 2 - Normal Exit"
chocolate_island_2_exit_2 = "Chocolate Island 2 - Secret Exit"
chocolate_island_2_dragon = "Chocolate Island 2 - Dragon Coins"
+chocolate_island_2_hidden_1up = "Chocolate Island 2 - Hidden 1-Up"
chocolate_island_3_exit_1 = "Chocolate Island 3 - Normal Exit"
chocolate_island_3_exit_2 = "Chocolate Island 3 - Secret Exit"
chocolate_island_3_dragon = "Chocolate Island 3 - Dragon Coins"
+chocolate_island_3_bonus_block = "Chocolate Island 3 - 1-Up from Bonus Block"
chocolate_island_4_exit_1 = "Chocolate Island 4 - Normal Exit"
chocolate_island_4_dragon = "Chocolate Island 4 - Dragon Coins"
chocolate_island_5_exit_1 = "Chocolate Island 5 - Normal Exit"
@@ -120,6 +140,7 @@
chocolate_fortress = "Chocolate Fortress - Normal Exit"
chocolate_reznor = "Chocolate Fortress - Boss"
chocolate_castle = "#6 Wendy's Castle - Normal Exit"
+chocolate_castle_hidden_1up = "#6 Wendy's Castle - Hidden 1-Up"
chocolate_koopaling = "#6 Wendy's Castle - Boss"
sunken_ghost_ship = "Sunken Ghost Ship - Normal Exit"
@@ -127,9 +148,11 @@
valley_of_bowser_1_exit_1 = "Valley of Bowser 1 - Normal Exit"
valley_of_bowser_1_dragon = "Valley of Bowser 1 - Dragon Coins"
+valley_of_bowser_1_moon = "Valley of Bowser 1 - 3-Up Moon"
valley_of_bowser_2_exit_1 = "Valley of Bowser 2 - Normal Exit"
valley_of_bowser_2_exit_2 = "Valley of Bowser 2 - Secret Exit"
valley_of_bowser_2_dragon = "Valley of Bowser 2 - Dragon Coins"
+valley_of_bowser_2_hidden_1up = "Valley of Bowser 2 - Hidden 1-Up"
valley_of_bowser_3_exit_1 = "Valley of Bowser 3 - Normal Exit"
valley_of_bowser_3_dragon = "Valley of Bowser 3 - Dragon Coins"
valley_of_bowser_4_exit_1 = "Valley of Bowser 4 - Normal Exit"
@@ -141,6 +164,7 @@
valley_reznor = "Valley Fortress - Boss"
valley_castle = "#7 Larry's Castle - Normal Exit"
valley_castle_dragon = "#7 Larry's Castle - Dragon Coins"
+valley_castle_hidden_1up = "#7 Larry's Castle - Hidden 1-Up"
valley_koopaling = "#7 Larry's Castle - Boss"
front_door = "Front Door"
@@ -161,6 +185,7 @@
special_zone_1_exit_1 = "Gnarly - Normal Exit"
special_zone_1_dragon = "Gnarly - Dragon Coins"
+special_zone_1_hidden_1up = "Gnarly - Hidden 1-Up"
special_zone_2_exit_1 = "Tubular - Normal Exit"
special_zone_2_dragon = "Tubular - Dragon Coins"
special_zone_3_exit_1 = "Way Cool - Normal Exit"
@@ -362,3 +387,586 @@
special_zone_8_tile = "Funky - Tile"
special_zone_8_region = "Funky"
special_complete = "Special Zone - Star Road - Complete"
+
+vanilla_secret_2_yoshi_block_1 = "Vanilla Secret 2 - Yoshi Block #1"
+vanilla_secret_2_green_block_1 = "Vanilla Secret 2 - Green Switch Palace Block #1"
+vanilla_secret_2_powerup_block_1 = "Vanilla Secret 2 - Powerup Block #1"
+vanilla_secret_2_powerup_block_2 = "Vanilla Secret 2 - Powerup Block #2"
+vanilla_secret_2_multi_coin_block_1 = "Vanilla Secret 2 - Multi Coin Block #1"
+vanilla_secret_2_gray_pow_block_1 = "Vanilla Secret 2 - Gray P-Switch Block #1"
+vanilla_secret_2_coin_block_1 = "Vanilla Secret 2 - Coin Block #1"
+vanilla_secret_2_coin_block_2 = "Vanilla Secret 2 - Coin Block #2"
+vanilla_secret_2_coin_block_3 = "Vanilla Secret 2 - Coin Block #3"
+vanilla_secret_2_coin_block_4 = "Vanilla Secret 2 - Coin Block #4"
+vanilla_secret_2_coin_block_5 = "Vanilla Secret 2 - Coin Block #5"
+vanilla_secret_2_coin_block_6 = "Vanilla Secret 2 - Coin Block #6"
+vanilla_secret_3_powerup_block_1 = "Vanilla Secret 3 - Powerup Block #1"
+vanilla_secret_3_powerup_block_2 = "Vanilla Secret 3 - Powerup Block #2"
+donut_ghost_house_vine_block_1 = "Donut Ghost House - Vine|P-Switch Block #1"
+donut_ghost_house_directional_coin_block_1 = "Donut Ghost House - Directional Coin Block #1"
+donut_ghost_house_life_block_1 = "Donut Ghost House - 1-Up Mushroom Block #1"
+donut_ghost_house_life_block_2 = "Donut Ghost House - 1-Up Mushroom Block #2"
+donut_ghost_house_life_block_3 = "Donut Ghost House - 1-Up Mushroom Block #3"
+donut_ghost_house_life_block_4 = "Donut Ghost House - 1-Up Mushroom Block #4"
+donut_plains_3_green_block_1 = "Donut Plains 3 - Green Switch Palace Block #1"
+donut_plains_3_coin_block_1 = "Donut Plains 3 - Coin Block #1"
+donut_plains_3_coin_block_2 = "Donut Plains 3 - Coin Block #2"
+donut_plains_3_vine_block_1 = "Donut Plains 3 - Vine Block #1"
+donut_plains_3_powerup_block_1 = "Donut Plains 3 - Powerup Block #1"
+donut_plains_3_bonus_block_1 = "Donut Plains 3 - Bonus Block #1"
+donut_plains_4_coin_block_1 = "Donut Plains 4 - Coin Block #1"
+donut_plains_4_powerup_block_1 = "Donut Plains 4 - Powerup Block #1"
+donut_plains_4_coin_block_2 = "Donut Plains 4 - Coin Block #2"
+donut_plains_4_yoshi_block_1 = "Donut Plains 4 - Yoshi Block #1"
+donut_plains_castle_yellow_block_1 = "#2 Morton's Castle - Yellow Switch Palace Block #1"
+donut_plains_castle_coin_block_1 = "#2 Morton's Castle - Coin Block #1"
+donut_plains_castle_powerup_block_1 = "#2 Morton's Castle - Powerup Block #1"
+donut_plains_castle_coin_block_2 = "#2 Morton's Castle - Coin Block #2"
+donut_plains_castle_vine_block_1 = "#2 Morton's Castle - Vine Block #1"
+donut_plains_castle_invis_life_block_1 = "#2 Morton's Castle - Invisible 1-Up Mushroom Block #1"
+donut_plains_castle_coin_block_3 = "#2 Morton's Castle - Coin Block #3"
+donut_plains_castle_coin_block_4 = "#2 Morton's Castle - Coin Block #4"
+donut_plains_castle_coin_block_5 = "#2 Morton's Castle - Coin Block #5"
+donut_plains_castle_green_block_1 = "#2 Morton's Castle - Green Switch Palace Block #1"
+donut_plains_2_coin_block_1 = "Donut Plains 2 - Coin Block #1"
+donut_plains_2_coin_block_2 = "Donut Plains 2 - Coin Block #2"
+donut_plains_2_coin_block_3 = "Donut Plains 2 - Coin Block #3"
+donut_plains_2_yellow_block_1 = "Donut Plains 2 - Yellow Switch Palace Block #1"
+donut_plains_2_powerup_block_1 = "Donut Plains 2 - Powerup Block #1"
+donut_plains_2_multi_coin_block_1 = "Donut Plains 2 - Multi Coin Block #1"
+donut_plains_2_flying_block_1 = "Donut Plains 2 - Flying Question Block #1"
+donut_plains_2_green_block_1 = "Donut Plains 2 - Green Switch Palace Block #1"
+donut_plains_2_yellow_block_2 = "Donut Plains 2 - Yellow Switch Palace Block #2"
+donut_plains_2_vine_block_1 = "Donut Plains 2 - Vine Block #1"
+donut_secret_1_coin_block_1 = "Donut Secret 1 - Coin Block #1"
+donut_secret_1_coin_block_2 = "Donut Secret 1 - Coin Block #2"
+donut_secret_1_powerup_block_1 = "Donut Secret 1 - Powerup Block #1"
+donut_secret_1_coin_block_3 = "Donut Secret 1 - Coin Block #3"
+donut_secret_1_powerup_block_2 = "Donut Secret 1 - Powerup Block #2"
+donut_secret_1_powerup_block_3 = "Donut Secret 1 - Powerup Block #3"
+donut_secret_1_life_block_1 = "Donut Secret 1 - 1-Up Mushroom Block #1"
+donut_secret_1_powerup_block_4 = "Donut Secret 1 - Powerup Block #4"
+donut_secret_1_powerup_block_5 = "Donut Secret 1 - Powerup Block #5"
+donut_secret_1_key_block_1 = "Donut Secret 1 - Key Block #1"
+vanilla_fortress_powerup_block_1 = "Vanilla Fortress - Powerup Block #1"
+vanilla_fortress_powerup_block_2 = "Vanilla Fortress - Powerup Block #2"
+vanilla_fortress_yellow_block_1 = "Vanilla Fortress - Yellow Switch Palace Block #1"
+butter_bridge_1_powerup_block_1 = "Butter Bridge 1 - Powerup Block #1"
+butter_bridge_1_multi_coin_block_1 = "Butter Bridge 1 - Multi Coin Block #1"
+butter_bridge_1_multi_coin_block_2 = "Butter Bridge 1 - Multi Coin Block #2"
+butter_bridge_1_multi_coin_block_3 = "Butter Bridge 1 - Multi Coin Block #3"
+butter_bridge_1_life_block_1 = "Butter Bridge 1 - 1-Up Mushroom Block #1"
+butter_bridge_1_bonus_block_1 = "Butter Bridge 1 - Bonus Block #1"
+butter_bridge_2_powerup_block_1 = "Butter Bridge 2 - Powerup Block #1"
+butter_bridge_2_green_block_1 = "Butter Bridge 2 - Green Switch Palace Block #1"
+butter_bridge_2_yoshi_block_1 = "Butter Bridge 2 - Yoshi Block #1"
+twin_bridges_castle_powerup_block_1 = "#4 Ludwig Castle - Powerup Block #1"
+cheese_bridge_powerup_block_1 = "Cheese Bridge Area - Powerup Block #1"
+cheese_bridge_powerup_block_2 = "Cheese Bridge Area - Powerup Block #2"
+cheese_bridge_wings_block_1 = "Cheese Bridge Area - Wings Block #1"
+cheese_bridge_powerup_block_3 = "Cheese Bridge Area - Powerup Block #3"
+cookie_mountain_coin_block_1 = "Cookie Mountain - Coin Block #1"
+cookie_mountain_coin_block_2 = "Cookie Mountain - Coin Block #2"
+cookie_mountain_coin_block_3 = "Cookie Mountain - Coin Block #3"
+cookie_mountain_coin_block_4 = "Cookie Mountain - Coin Block #4"
+cookie_mountain_coin_block_5 = "Cookie Mountain - Coin Block #5"
+cookie_mountain_coin_block_6 = "Cookie Mountain - Coin Block #6"
+cookie_mountain_coin_block_7 = "Cookie Mountain - Coin Block #7"
+cookie_mountain_coin_block_8 = "Cookie Mountain - Coin Block #8"
+cookie_mountain_coin_block_9 = "Cookie Mountain - Coin Block #9"
+cookie_mountain_powerup_block_1 = "Cookie Mountain - Powerup Block #1"
+cookie_mountain_life_block_1 = "Cookie Mountain - 1-Up Mushroom Block #1"
+cookie_mountain_vine_block_1 = "Cookie Mountain - Vine Block #1"
+cookie_mountain_yoshi_block_1 = "Cookie Mountain - Yoshi Block #1"
+cookie_mountain_coin_block_10 = "Cookie Mountain - Coin Block #10"
+cookie_mountain_coin_block_11 = "Cookie Mountain - Coin Block #11"
+cookie_mountain_powerup_block_2 = "Cookie Mountain - Powerup Block #2"
+cookie_mountain_coin_block_12 = "Cookie Mountain - Coin Block #12"
+cookie_mountain_coin_block_13 = "Cookie Mountain - Coin Block #13"
+cookie_mountain_coin_block_14 = "Cookie Mountain - Coin Block #14"
+cookie_mountain_coin_block_15 = "Cookie Mountain - Coin Block #15"
+cookie_mountain_coin_block_16 = "Cookie Mountain - Coin Block #16"
+cookie_mountain_coin_block_17 = "Cookie Mountain - Coin Block #17"
+cookie_mountain_coin_block_18 = "Cookie Mountain - Coin Block #18"
+cookie_mountain_coin_block_19 = "Cookie Mountain - Coin Block #19"
+cookie_mountain_coin_block_20 = "Cookie Mountain - Coin Block #20"
+cookie_mountain_coin_block_21 = "Cookie Mountain - Coin Block #21"
+cookie_mountain_coin_block_22 = "Cookie Mountain - Coin Block #22"
+cookie_mountain_coin_block_23 = "Cookie Mountain - Coin Block #23"
+cookie_mountain_coin_block_24 = "Cookie Mountain - Coin Block #24"
+cookie_mountain_coin_block_25 = "Cookie Mountain - Coin Block #25"
+cookie_mountain_coin_block_26 = "Cookie Mountain - Coin Block #26"
+cookie_mountain_coin_block_27 = "Cookie Mountain - Coin Block #27"
+cookie_mountain_coin_block_28 = "Cookie Mountain - Coin Block #28"
+cookie_mountain_coin_block_29 = "Cookie Mountain - Coin Block #29"
+cookie_mountain_coin_block_30 = "Cookie Mountain - Coin Block #30"
+soda_lake_powerup_block_1 = "Soda Lake - Powerup Block #1"
+donut_secret_house_powerup_block_1 = "Donut Secret House - Powerup Block #1"
+donut_secret_house_multi_coin_block_1 = "Donut Secret House - Multi Coin Block #1"
+donut_secret_house_life_block_1 = "Donut Secret House - 1-Up Mushroom Block #1"
+donut_secret_house_vine_block_1 = "Donut Secret House - Vine Block #1"
+donut_secret_house_directional_coin_block_1 = "Donut Secret House - Directional Coin Block #1"
+donut_plains_1_coin_block_1 = "Donut Plains 1 - Coin Block #1"
+donut_plains_1_coin_block_2 = "Donut Plains 1 - Coin Block #2"
+donut_plains_1_yoshi_block_1 = "Donut Plains 1 - Yoshi Block #1"
+donut_plains_1_vine_block_1 = "Donut Plains 1 - Vine Block #1"
+donut_plains_1_green_block_1 = "Donut Plains 1 - Green Switch Palace Block #1"
+donut_plains_1_green_block_2 = "Donut Plains 1 - Green Switch Palace Block #2"
+donut_plains_1_green_block_3 = "Donut Plains 1 - Green Switch Palace Block #3"
+donut_plains_1_green_block_4 = "Donut Plains 1 - Green Switch Palace Block #4"
+donut_plains_1_green_block_5 = "Donut Plains 1 - Green Switch Palace Block #5"
+donut_plains_1_green_block_6 = "Donut Plains 1 - Green Switch Palace Block #6"
+donut_plains_1_green_block_7 = "Donut Plains 1 - Green Switch Palace Block #7"
+donut_plains_1_green_block_8 = "Donut Plains 1 - Green Switch Palace Block #8"
+donut_plains_1_green_block_9 = "Donut Plains 1 - Green Switch Palace Block #9"
+donut_plains_1_green_block_10 = "Donut Plains 1 - Green Switch Palace Block #10"
+donut_plains_1_green_block_11 = "Donut Plains 1 - Green Switch Palace Block #11"
+donut_plains_1_green_block_12 = "Donut Plains 1 - Green Switch Palace Block #12"
+donut_plains_1_green_block_13 = "Donut Plains 1 - Green Switch Palace Block #13"
+donut_plains_1_green_block_14 = "Donut Plains 1 - Green Switch Palace Block #14"
+donut_plains_1_green_block_15 = "Donut Plains 1 - Green Switch Palace Block #15"
+donut_plains_1_green_block_16 = "Donut Plains 1 - Green Switch Palace Block #16"
+donut_plains_1_yellow_block_1 = "Donut Plains 1 - Yellow Switch Palace Block #1"
+donut_plains_1_yellow_block_2 = "Donut Plains 1 - Yellow Switch Palace Block #2"
+donut_plains_1_yellow_block_3 = "Donut Plains 1 - Yellow Switch Palace Block #3"
+sunken_ghost_ship_powerup_block_1 = "Sunken Ghost Ship - Powerup Block #1"
+sunken_ghost_ship_star_block_1 = "Sunken Ghost Ship - Star Block #1"
+chocolate_castle_yellow_block_1 = "#6 Wendy's Castle - Yellow Switch Palace Block #1"
+chocolate_castle_yellow_block_2 = "#6 Wendy's Castle - Yellow Switch Palace Block #2"
+chocolate_castle_green_block_1 = "#6 Wendy's Castle - Green Switch Palace Block #1"
+chocolate_fortress_powerup_block_1 = "Chocolate Fortress - Powerup Block #1"
+chocolate_fortress_powerup_block_2 = "Chocolate Fortress - Powerup Block #2"
+chocolate_fortress_coin_block_1 = "Chocolate Fortress - Coin Block #1"
+chocolate_fortress_coin_block_2 = "Chocolate Fortress - Coin Block #2"
+chocolate_fortress_green_block_1 = "Chocolate Fortress - Green Switch Palace Block #1"
+chocolate_island_5_yoshi_block_1 = "Chocolate Island 5 - Yoshi Block #1"
+chocolate_island_5_powerup_block_1 = "Chocolate Island 5 - Powerup Block #1"
+chocolate_island_5_life_block_1 = "Chocolate Island 5 - 1-Up Mushroom Block #1"
+chocolate_island_5_yellow_block_1 = "Chocolate Island 5 - Yellow Switch Palace Block #1"
+chocolate_island_4_yellow_block_1 = "Chocolate Island 4 - Yellow Switch Palace Block #1"
+chocolate_island_4_blue_pow_block_1 = "Chocolate Island 4 - Blue P-Switch Block #1"
+chocolate_island_4_powerup_block_1 = "Chocolate Island 4 - Powerup Block #1"
+forest_fortress_yellow_block_1 = "Forest Fortress - Yellow Switch Palace Block #1"
+forest_fortress_powerup_block_1 = "Forest Fortress - Powerup Block #1"
+forest_fortress_life_block_1 = "Forest Fortress - 1-Up Mushroom Block #1"
+forest_fortress_life_block_2 = "Forest Fortress - 1-Up Mushroom Block #2"
+forest_fortress_life_block_3 = "Forest Fortress - 1-Up Mushroom Block #3"
+forest_fortress_life_block_4 = "Forest Fortress - 1-Up Mushroom Block #4"
+forest_fortress_life_block_5 = "Forest Fortress - 1-Up Mushroom Block #5"
+forest_fortress_life_block_6 = "Forest Fortress - 1-Up Mushroom Block #6"
+forest_fortress_life_block_7 = "Forest Fortress - 1-Up Mushroom Block #7"
+forest_fortress_life_block_8 = "Forest Fortress - 1-Up Mushroom Block #8"
+forest_fortress_life_block_9 = "Forest Fortress - 1-Up Mushroom Block #9"
+forest_castle_green_block_1 = "#5 Roy's Castle - Green Switch Palace Block #1"
+chocolate_ghost_house_powerup_block_1 = "Choco Ghost House - Powerup Block #1"
+chocolate_ghost_house_powerup_block_2 = "Choco Ghost House - Powerup Block #2"
+chocolate_ghost_house_life_block_1 = "Choco Ghost House - 1-Up Mushroom Block #1"
+chocolate_island_1_flying_block_1 = "Chocolate Island 1 - Flying Question Block #1"
+chocolate_island_1_flying_block_2 = "Chocolate Island 1 - Flying Question Block #2"
+chocolate_island_1_yoshi_block_1 = "Chocolate Island 1 - Yoshi Block #1"
+chocolate_island_1_green_block_1 = "Chocolate Island 1 - Green|Yellow Switch Palace Block #1"
+chocolate_island_1_life_block_1 = "Chocolate Island 1 - 1-Up Mushroom Block #1"
+chocolate_island_3_powerup_block_1 = "Chocolate Island 3 - Powerup Block #1"
+chocolate_island_3_powerup_block_2 = "Chocolate Island 3 - Powerup Block #2"
+chocolate_island_3_powerup_block_3 = "Chocolate Island 3 - Powerup Block #3"
+chocolate_island_3_green_block_1 = "Chocolate Island 3 - Green Switch Palace Block #1"
+chocolate_island_3_bonus_block_1 = "Chocolate Island 3 - Bonus Block #1"
+chocolate_island_3_vine_block_1 = "Chocolate Island 3 - Vine Block #1"
+chocolate_island_3_life_block_1 = "Chocolate Island 3 - 1-Up Mushroom Block #1"
+chocolate_island_3_life_block_2 = "Chocolate Island 3 - 1-Up Mushroom Block #2"
+chocolate_island_3_life_block_3 = "Chocolate Island 3 - 1-Up Mushroom Block #3"
+chocolate_island_2_multi_coin_block_1 = "Chocolate Island 2 - Multi Coin Block #1"
+chocolate_island_2_invis_coin_block_1 = "Chocolate Island 2 - Invisible Coin Block #1"
+chocolate_island_2_yoshi_block_1 = "Chocolate Island 2 - Yoshi Block #1"
+chocolate_island_2_coin_block_1 = "Chocolate Island 2 - Coin Block #1"
+chocolate_island_2_coin_block_2 = "Chocolate Island 2 - Coin Block #2"
+chocolate_island_2_multi_coin_block_2 = "Chocolate Island 2 - Multi Coin Block #2"
+chocolate_island_2_powerup_block_1 = "Chocolate Island 2 - Powerup Block #1"
+chocolate_island_2_blue_pow_block_1 = "Chocolate Island 2 - Blue P-Switch Block #1"
+chocolate_island_2_yellow_block_1 = "Chocolate Island 2 - Yellow Switch Palace Block #1"
+chocolate_island_2_yellow_block_2 = "Chocolate Island 2 - Yellow Switch Palace Block #2"
+chocolate_island_2_green_block_1 = "Chocolate Island 2 - Green Switch Palace Block #1"
+chocolate_island_2_green_block_2 = "Chocolate Island 2 - Green Switch Palace Block #2"
+chocolate_island_2_green_block_3 = "Chocolate Island 2 - Green Switch Palace Block #3"
+chocolate_island_2_green_block_4 = "Chocolate Island 2 - Green Switch Palace Block #4"
+chocolate_island_2_green_block_5 = "Chocolate Island 2 - Green Switch Palace Block #5"
+chocolate_island_2_green_block_6 = "Chocolate Island 2 - Green Switch Palace Block #6"
+yoshis_island_castle_coin_block_1 = "#1 Iggy's Castle - Coin Block #1"
+yoshis_island_castle_coin_block_2 = "#1 Iggy's Castle - Coin Block #2"
+yoshis_island_castle_powerup_block_1 = "#1 Iggy's Castle - Powerup Block #1"
+yoshis_island_castle_coin_block_3 = "#1 Iggy's Castle - Coin Block #3"
+yoshis_island_castle_coin_block_4 = "#1 Iggy's Castle - Coin Block #4"
+yoshis_island_castle_flying_block_1 = "#1 Iggy's Castle - Flying Question Block #1"
+yoshis_island_4_yellow_block_1 = "Yoshi's Island 4 - Yellow Switch Palace Block #1"
+yoshis_island_4_powerup_block_1 = "Yoshi's Island 4 - Powerup Block #1"
+yoshis_island_4_multi_coin_block_1 = "Yoshi's Island 4 - Multi Coin Block #1"
+yoshis_island_4_star_block_1 = "Yoshi's Island 4 - Star Block #1"
+yoshis_island_3_yellow_block_1 = "Yoshi's Island 3 - Yellow Switch Palace Block #1"
+yoshis_island_3_yellow_block_2 = "Yoshi's Island 3 - Yellow Switch Palace Block #2"
+yoshis_island_3_yellow_block_3 = "Yoshi's Island 3 - Yellow Switch Palace Block #3"
+yoshis_island_3_yellow_block_4 = "Yoshi's Island 3 - Yellow Switch Palace Block #4"
+yoshis_island_3_yellow_block_5 = "Yoshi's Island 3 - Yellow Switch Palace Block #5"
+yoshis_island_3_yellow_block_6 = "Yoshi's Island 3 - Yellow Switch Palace Block #6"
+yoshis_island_3_yellow_block_7 = "Yoshi's Island 3 - Yellow Switch Palace Block #7"
+yoshis_island_3_yellow_block_8 = "Yoshi's Island 3 - Yellow Switch Palace Block #8"
+yoshis_island_3_yellow_block_9 = "Yoshi's Island 3 - Yellow Switch Palace Block #9"
+yoshis_island_3_coin_block_1 = "Yoshi's Island 3 - Coin Block #1"
+yoshis_island_3_yoshi_block_1 = "Yoshi's Island 3 - Yoshi Block #1"
+yoshis_island_3_coin_block_2 = "Yoshi's Island 3 - Coin Block #2"
+yoshis_island_3_powerup_block_1 = "Yoshi's Island 3 - Powerup Block #1"
+yoshis_island_3_yellow_block_10 = "Yoshi's Island 3 - Yellow Switch Palace Block #10"
+yoshis_island_3_yellow_block_11 = "Yoshi's Island 3 - Yellow Switch Palace Block #11"
+yoshis_island_3_yellow_block_12 = "Yoshi's Island 3 - Yellow Switch Palace Block #12"
+yoshis_island_3_bonus_block_1 = "Yoshi's Island 3 - Bonus Block #1"
+yoshis_island_1_flying_block_1 = "Yoshi's Island 1 - Flying Question Block #1"
+yoshis_island_1_yellow_block_1 = "Yoshi's Island 1 - Yellow Switch Palace Block #1"
+yoshis_island_1_life_block_1 = "Yoshi's Island 1 - 1-Up Mushroom Block #1"
+yoshis_island_1_powerup_block_1 = "Yoshi's Island 1 - Powerup Block #1"
+yoshis_island_2_flying_block_1 = "Yoshi's Island 2 - Flying Question Block #1"
+yoshis_island_2_flying_block_2 = "Yoshi's Island 2 - Flying Question Block #2"
+yoshis_island_2_flying_block_3 = "Yoshi's Island 2 - Flying Question Block #3"
+yoshis_island_2_flying_block_4 = "Yoshi's Island 2 - Flying Question Block #4"
+yoshis_island_2_flying_block_5 = "Yoshi's Island 2 - Flying Question Block #5"
+yoshis_island_2_flying_block_6 = "Yoshi's Island 2 - Flying Question Block #6"
+yoshis_island_2_coin_block_1 = "Yoshi's Island 2 - Coin Block #1"
+yoshis_island_2_yellow_block_1 = "Yoshi's Island 2 - Yellow Switch Palace Block #1"
+yoshis_island_2_coin_block_2 = "Yoshi's Island 2 - Coin Block #2"
+yoshis_island_2_coin_block_3 = "Yoshi's Island 2 - Coin Block #3"
+yoshis_island_2_yoshi_block_1 = "Yoshi's Island 2 - Yoshi Block #1"
+yoshis_island_2_coin_block_4 = "Yoshi's Island 2 - Coin Block #4"
+yoshis_island_2_yoshi_block_2 = "Yoshi's Island 2 - Yoshi Block #2"
+yoshis_island_2_coin_block_5 = "Yoshi's Island 2 - Coin Block #5"
+yoshis_island_2_vine_block_1 = "Yoshi's Island 2 - Vine Block #1"
+yoshis_island_2_yellow_block_2 = "Yoshi's Island 2 - Yellow Switch Palace Block #2"
+vanilla_ghost_house_powerup_block_1 = "Vanilla Ghost House - Powerup Block #1"
+vanilla_ghost_house_vine_block_1 = "Vanilla Ghost House - Vine Block #1"
+vanilla_ghost_house_powerup_block_2 = "Vanilla Ghost House - Powerup Block #2"
+vanilla_ghost_house_multi_coin_block_1 = "Vanilla Ghost House - Multi Coin Block #1"
+vanilla_ghost_house_blue_pow_block_1 = "Vanilla Ghost House - Blue P-Switch Block #1"
+vanilla_secret_1_coin_block_1 = "Vanilla Secret 1 - Coin Block #1"
+vanilla_secret_1_powerup_block_1 = "Vanilla Secret 1 - Powerup Block #1"
+vanilla_secret_1_multi_coin_block_1 = "Vanilla Secret 1 - Multi Coin Block #1"
+vanilla_secret_1_vine_block_1 = "Vanilla Secret 1 - Vine Block #1"
+vanilla_secret_1_vine_block_2 = "Vanilla Secret 1 - Vine Block #2"
+vanilla_secret_1_coin_block_2 = "Vanilla Secret 1 - Coin Block #2"
+vanilla_secret_1_coin_block_3 = "Vanilla Secret 1 - Coin Block #3"
+vanilla_secret_1_powerup_block_2 = "Vanilla Secret 1 - Powerup Block #2"
+vanilla_dome_3_coin_block_1 = "Vanilla Dome 3 - Coin Block #1"
+vanilla_dome_3_flying_block_1 = "Vanilla Dome 3 - Flying Question Block #1"
+vanilla_dome_3_flying_block_2 = "Vanilla Dome 3 - Flying Question Block #2"
+vanilla_dome_3_powerup_block_1 = "Vanilla Dome 3 - Powerup Block #1"
+vanilla_dome_3_flying_block_3 = "Vanilla Dome 3 - Flying Question Block #3"
+vanilla_dome_3_invis_coin_block_1 = "Vanilla Dome 3 - Invisible Coin Block #1"
+vanilla_dome_3_powerup_block_2 = "Vanilla Dome 3 - Powerup Block #2"
+vanilla_dome_3_multi_coin_block_1 = "Vanilla Dome 3 - Multi Coin Block #1"
+vanilla_dome_3_powerup_block_3 = "Vanilla Dome 3 - Powerup Block #3"
+vanilla_dome_3_yoshi_block_1 = "Vanilla Dome 3 - Yoshi Block #1"
+vanilla_dome_3_powerup_block_4 = "Vanilla Dome 3 - Powerup Block #4"
+vanilla_dome_3_pswitch_coin_block_1 = "Vanilla Dome 3 - P-Switch Coin Block #1"
+vanilla_dome_3_pswitch_coin_block_2 = "Vanilla Dome 3 - P-Switch Coin Block #2"
+vanilla_dome_3_pswitch_coin_block_3 = "Vanilla Dome 3 - P-Switch Coin Block #3"
+vanilla_dome_3_pswitch_coin_block_4 = "Vanilla Dome 3 - P-Switch Coin Block #4"
+vanilla_dome_3_pswitch_coin_block_5 = "Vanilla Dome 3 - P-Switch Coin Block #5"
+vanilla_dome_3_pswitch_coin_block_6 = "Vanilla Dome 3 - P-Switch Coin Block #6"
+donut_secret_2_directional_coin_block_1 = "Donut Secret 2 - Directional Coin Block #1"
+donut_secret_2_vine_block_1 = "Donut Secret 2 - Vine Block #1"
+donut_secret_2_star_block_1 = "Donut Secret 2 - Star Block #1"
+donut_secret_2_powerup_block_1 = "Donut Secret 2 - Powerup Block #1"
+donut_secret_2_star_block_2 = "Donut Secret 2 - Star Block #2"
+valley_of_bowser_4_yellow_block_1 = "Valley of Bowser 4 - Yellow Switch Palace Block #1"
+valley_of_bowser_4_powerup_block_1 = "Valley of Bowser 4 - Powerup Block #1"
+valley_of_bowser_4_vine_block_1 = "Valley of Bowser 4 - Vine Block #1"
+valley_of_bowser_4_yoshi_block_1 = "Valley of Bowser 4 - Yoshi Block #1"
+valley_of_bowser_4_life_block_1 = "Valley of Bowser 4 - 1-Up Mushroom Block #1"
+valley_of_bowser_4_powerup_block_2 = "Valley of Bowser 4 - Powerup Block #2"
+valley_castle_yellow_block_1 = "#7 Larry's Castle - Yellow Switch Palace Block #1"
+valley_castle_yellow_block_2 = "#7 Larry's Castle - Yellow Switch Palace Block #2"
+valley_castle_green_block_1 = "#7 Larry's Castle - Green Switch Palace Block #1"
+valley_fortress_green_block_1 = "Valley Fortress - Green Switch Palace Block #1"
+valley_fortress_yellow_block_1 = "Valley Fortress - Yellow Switch Palace Block #1"
+valley_of_bowser_3_powerup_block_1 = "Valley of Bowser 3 - Powerup Block #1"
+valley_of_bowser_3_powerup_block_2 = "Valley of Bowser 3 - Powerup Block #2"
+valley_ghost_house_pswitch_coin_block_1 = "Valley Ghost House - P-Switch Coin Block #1"
+valley_ghost_house_multi_coin_block_1 = "Valley Ghost House - Multi Coin Block #1"
+valley_ghost_house_powerup_block_1 = "Valley Ghost House - Powerup Block #1"
+valley_ghost_house_directional_coin_block_1 = "Valley Ghost House - Directional Coin Block #1"
+valley_of_bowser_2_powerup_block_1 = "Valley of Bowser 2 - Powerup Block #1"
+valley_of_bowser_2_yellow_block_1 = "Valley of Bowser 2 - Yellow Switch Palace Block #1"
+valley_of_bowser_2_powerup_block_2 = "Valley of Bowser 2 - Powerup Block #2"
+valley_of_bowser_2_wings_block_1 = "Valley of Bowser 2 - Wings Block #1"
+valley_of_bowser_1_green_block_1 = "Valley of Bowser 1 - Green Switch Palace Block #1"
+valley_of_bowser_1_invis_coin_block_1 = "Valley of Bowser 1 - Invisible Coin Block #1"
+valley_of_bowser_1_invis_coin_block_2 = "Valley of Bowser 1 - Invisible Coin Block #2"
+valley_of_bowser_1_invis_coin_block_3 = "Valley of Bowser 1 - Invisible Coin Block #3"
+valley_of_bowser_1_yellow_block_1 = "Valley of Bowser 1 - Yellow Switch Palace Block #1"
+valley_of_bowser_1_yellow_block_2 = "Valley of Bowser 1 - Yellow Switch Palace Block #2"
+valley_of_bowser_1_yellow_block_3 = "Valley of Bowser 1 - Yellow Switch Palace Block #3"
+valley_of_bowser_1_yellow_block_4 = "Valley of Bowser 1 - Yellow Switch Palace Block #4"
+valley_of_bowser_1_vine_block_1 = "Valley of Bowser 1 - Vine Block #1"
+chocolate_secret_powerup_block_1 = "Chocolate Secret - Powerup Block #1"
+chocolate_secret_powerup_block_2 = "Chocolate Secret - Powerup Block #2"
+vanilla_dome_2_coin_block_1 = "Vanilla Dome 2 - Coin Block #1"
+vanilla_dome_2_powerup_block_1 = "Vanilla Dome 2 - Powerup Block #1"
+vanilla_dome_2_coin_block_2 = "Vanilla Dome 2 - Coin Block #2"
+vanilla_dome_2_coin_block_3 = "Vanilla Dome 2 - Coin Block #3"
+vanilla_dome_2_vine_block_1 = "Vanilla Dome 2 - Vine Block #1"
+vanilla_dome_2_invis_life_block_1 = "Vanilla Dome 2 - Invisible 1-Up Mushroom Block #1"
+vanilla_dome_2_coin_block_4 = "Vanilla Dome 2 - Coin Block #4"
+vanilla_dome_2_coin_block_5 = "Vanilla Dome 2 - Coin Block #5"
+vanilla_dome_2_powerup_block_2 = "Vanilla Dome 2 - Powerup Block #2"
+vanilla_dome_2_powerup_block_3 = "Vanilla Dome 2 - Powerup Block #3"
+vanilla_dome_2_powerup_block_4 = "Vanilla Dome 2 - Powerup Block #4"
+vanilla_dome_2_powerup_block_5 = "Vanilla Dome 2 - Powerup Block #5"
+vanilla_dome_2_multi_coin_block_1 = "Vanilla Dome 2 - Multi Coin Block #1"
+vanilla_dome_2_multi_coin_block_2 = "Vanilla Dome 2 - Multi Coin Block #2"
+vanilla_dome_4_powerup_block_1 = "Vanilla Dome 4 - Powerup Block #1"
+vanilla_dome_4_powerup_block_2 = "Vanilla Dome 4 - Powerup Block #2"
+vanilla_dome_4_coin_block_1 = "Vanilla Dome 4 - Coin Block #1"
+vanilla_dome_4_coin_block_2 = "Vanilla Dome 4 - Coin Block #2"
+vanilla_dome_4_coin_block_3 = "Vanilla Dome 4 - Coin Block #3"
+vanilla_dome_4_life_block_1 = "Vanilla Dome 4 - 1-Up Mushroom Block #1"
+vanilla_dome_4_coin_block_4 = "Vanilla Dome 4 - Coin Block #4"
+vanilla_dome_4_coin_block_5 = "Vanilla Dome 4 - Coin Block #5"
+vanilla_dome_4_coin_block_6 = "Vanilla Dome 4 - Coin Block #6"
+vanilla_dome_4_coin_block_7 = "Vanilla Dome 4 - Coin Block #7"
+vanilla_dome_4_coin_block_8 = "Vanilla Dome 4 - Coin Block #8"
+vanilla_dome_1_flying_block_1 = "Vanilla Dome 1 - Flying Question Block #1"
+vanilla_dome_1_powerup_block_1 = "Vanilla Dome 1 - Powerup Block #1"
+vanilla_dome_1_powerup_block_2 = "Vanilla Dome 1 - Powerup Block #2"
+vanilla_dome_1_coin_block_1 = "Vanilla Dome 1 - Coin Block #1"
+vanilla_dome_1_life_block_1 = "Vanilla Dome 1 - 1-Up Mushroom Block #1"
+vanilla_dome_1_powerup_block_3 = "Vanilla Dome 1 - Powerup Block #3"
+vanilla_dome_1_vine_block_1 = "Vanilla Dome 1 - Vine Block #1"
+vanilla_dome_1_star_block_1 = "Vanilla Dome 1 - Star Block #1"
+vanilla_dome_1_powerup_block_4 = "Vanilla Dome 1 - Powerup Block #4"
+vanilla_dome_1_coin_block_2 = "Vanilla Dome 1 - Coin Block #2"
+vanilla_dome_castle_life_block_1 = "#3 Lemmy's Castle - 1-Up Mushroom Block #1"
+vanilla_dome_castle_life_block_2 = "#3 Lemmy's Castle - 1-Up Mushroom Block #2"
+vanilla_dome_castle_powerup_block_1 = "#3 Lemmy's Castle - Powerup Block #1"
+vanilla_dome_castle_life_block_3 = "#3 Lemmy's Castle - 1-Up Mushroom Block #3"
+vanilla_dome_castle_green_block_1 = "#3 Lemmy's Castle - Green Switch Palace Block #1"
+forest_ghost_house_coin_block_1 = "Forest Ghost House - Coin Block #1"
+forest_ghost_house_powerup_block_1 = "Forest Ghost House - Powerup Block #1"
+forest_ghost_house_flying_block_1 = "Forest Ghost House - Flying Question Block #1"
+forest_ghost_house_powerup_block_2 = "Forest Ghost House - Powerup Block #2"
+forest_ghost_house_life_block_1 = "Forest Ghost House - 1-Up Mushroom Block #1"
+forest_of_illusion_1_powerup_block_1 = "Forest of Illusion 1 - Powerup Block #1"
+forest_of_illusion_1_yoshi_block_1 = "Forest of Illusion 1 - Yoshi Block #1"
+forest_of_illusion_1_powerup_block_2 = "Forest of Illusion 1 - Powerup Block #2"
+forest_of_illusion_1_key_block_1 = "Forest of Illusion 1 - Key Block #1"
+forest_of_illusion_1_life_block_1 = "Forest of Illusion 1 - 1-Up Mushroom Block #1"
+forest_of_illusion_4_multi_coin_block_1 = "Forest of Illusion 4 - Multi Coin Block #1"
+forest_of_illusion_4_coin_block_1 = "Forest of Illusion 4 - Coin Block #1"
+forest_of_illusion_4_coin_block_2 = "Forest of Illusion 4 - Coin Block #2"
+forest_of_illusion_4_coin_block_3 = "Forest of Illusion 4 - Coin Block #3"
+forest_of_illusion_4_coin_block_4 = "Forest of Illusion 4 - Coin Block #4"
+forest_of_illusion_4_powerup_block_1 = "Forest of Illusion 4 - Powerup Block #1"
+forest_of_illusion_4_coin_block_5 = "Forest of Illusion 4 - Coin Block #5"
+forest_of_illusion_4_coin_block_6 = "Forest of Illusion 4 - Coin Block #6"
+forest_of_illusion_4_coin_block_7 = "Forest of Illusion 4 - Coin Block #7"
+forest_of_illusion_4_powerup_block_2 = "Forest of Illusion 4 - Powerup Block #2"
+forest_of_illusion_4_coin_block_8 = "Forest of Illusion 4 - Coin Block #8"
+forest_of_illusion_4_coin_block_9 = "Forest of Illusion 4 - Coin Block #9"
+forest_of_illusion_4_coin_block_10 = "Forest of Illusion 4 - Coin Block #10"
+forest_of_illusion_2_green_block_1 = "Forest of Illusion 2 - Green Switch Palace Block #1"
+forest_of_illusion_2_powerup_block_1 = "Forest of Illusion 2 - Powerup Block #1"
+forest_of_illusion_2_invis_coin_block_1 = "Forest of Illusion 2 - Invisible Coin Block #1"
+forest_of_illusion_2_invis_coin_block_2 = "Forest of Illusion 2 - Invisible Coin Block #2"
+forest_of_illusion_2_invis_life_block_1 = "Forest of Illusion 2 - Invisible 1-Up Mushroom Block #1"
+forest_of_illusion_2_invis_coin_block_3 = "Forest of Illusion 2 - Invisible Coin Block #3"
+forest_of_illusion_2_yellow_block_1 = "Forest of Illusion 2 - Yellow Switch Palace Block #1"
+forest_secret_powerup_block_1 = "Forest Secret Area - Powerup Block #1"
+forest_secret_powerup_block_2 = "Forest Secret Area - Powerup Block #2"
+forest_secret_life_block_1 = "Forest Secret Area - 1-Up Mushroom Block #1"
+forest_of_illusion_3_yoshi_block_1 = "Forest of Illusion 3 - Yoshi Block #1"
+forest_of_illusion_3_coin_block_1 = "Forest of Illusion 3 - Coin Block #1"
+forest_of_illusion_3_multi_coin_block_1 = "Forest of Illusion 3 - Multi Coin Block #1"
+forest_of_illusion_3_coin_block_2 = "Forest of Illusion 3 - Coin Block #2"
+forest_of_illusion_3_multi_coin_block_2 = "Forest of Illusion 3 - Multi Coin Block #2"
+forest_of_illusion_3_coin_block_3 = "Forest of Illusion 3 - Coin Block #3"
+forest_of_illusion_3_coin_block_4 = "Forest of Illusion 3 - Coin Block #4"
+forest_of_illusion_3_coin_block_5 = "Forest of Illusion 3 - Coin Block #5"
+forest_of_illusion_3_coin_block_6 = "Forest of Illusion 3 - Coin Block #6"
+forest_of_illusion_3_coin_block_7 = "Forest of Illusion 3 - Coin Block #7"
+forest_of_illusion_3_coin_block_8 = "Forest of Illusion 3 - Coin Block #8"
+forest_of_illusion_3_coin_block_9 = "Forest of Illusion 3 - Coin Block #9"
+forest_of_illusion_3_coin_block_10 = "Forest of Illusion 3 - Coin Block #10"
+forest_of_illusion_3_coin_block_11 = "Forest of Illusion 3 - Coin Block #11"
+forest_of_illusion_3_coin_block_12 = "Forest of Illusion 3 - Coin Block #12"
+forest_of_illusion_3_coin_block_13 = "Forest of Illusion 3 - Coin Block #13"
+forest_of_illusion_3_coin_block_14 = "Forest of Illusion 3 - Coin Block #14"
+forest_of_illusion_3_coin_block_15 = "Forest of Illusion 3 - Coin Block #15"
+forest_of_illusion_3_coin_block_16 = "Forest of Illusion 3 - Coin Block #16"
+forest_of_illusion_3_coin_block_17 = "Forest of Illusion 3 - Coin Block #17"
+forest_of_illusion_3_coin_block_18 = "Forest of Illusion 3 - Coin Block #18"
+forest_of_illusion_3_coin_block_19 = "Forest of Illusion 3 - Coin Block #19"
+forest_of_illusion_3_coin_block_20 = "Forest of Illusion 3 - Coin Block #20"
+forest_of_illusion_3_coin_block_21 = "Forest of Illusion 3 - Coin Block #21"
+forest_of_illusion_3_coin_block_22 = "Forest of Illusion 3 - Coin Block #22"
+forest_of_illusion_3_coin_block_23 = "Forest of Illusion 3 - Coin Block #23"
+forest_of_illusion_3_coin_block_24 = "Forest of Illusion 3 - Coin Block #24"
+special_zone_8_yoshi_block_1 = "Funky - Yoshi Block #1"
+special_zone_8_coin_block_1 = "Funky - Coin Block #1"
+special_zone_8_coin_block_2 = "Funky - Coin Block #2"
+special_zone_8_coin_block_3 = "Funky - Coin Block #3"
+special_zone_8_coin_block_4 = "Funky - Coin Block #4"
+special_zone_8_coin_block_5 = "Funky - Coin Block #5"
+special_zone_8_blue_pow_block_1 = "Funky - Blue P-Switch Block #1"
+special_zone_8_powerup_block_1 = "Funky - Powerup Block #1"
+special_zone_8_star_block_1 = "Funky - Star Block #1"
+special_zone_8_coin_block_6 = "Funky - Coin Block #6"
+special_zone_8_coin_block_7 = "Funky - Coin Block #7"
+special_zone_8_coin_block_8 = "Funky - Coin Block #8"
+special_zone_8_coin_block_9 = "Funky - Coin Block #9"
+special_zone_8_coin_block_10 = "Funky - Coin Block #10"
+special_zone_8_coin_block_11 = "Funky - Coin Block #11"
+special_zone_8_coin_block_12 = "Funky - Coin Block #12"
+special_zone_8_coin_block_13 = "Funky - Coin Block #13"
+special_zone_8_coin_block_14 = "Funky - Coin Block #14"
+special_zone_8_coin_block_15 = "Funky - Coin Block #15"
+special_zone_8_coin_block_16 = "Funky - Coin Block #16"
+special_zone_8_coin_block_17 = "Funky - Coin Block #17"
+special_zone_8_coin_block_18 = "Funky - Coin Block #18"
+special_zone_8_multi_coin_block_1 = "Funky - Multi Coin Block #1"
+special_zone_8_coin_block_19 = "Funky - Coin Block #19"
+special_zone_8_coin_block_20 = "Funky - Coin Block #20"
+special_zone_8_coin_block_21 = "Funky - Coin Block #21"
+special_zone_8_coin_block_22 = "Funky - Coin Block #22"
+special_zone_8_coin_block_23 = "Funky - Coin Block #23"
+special_zone_8_powerup_block_2 = "Funky - Powerup Block #2"
+special_zone_8_flying_block_1 = "Funky - Flying Question Block #1"
+special_zone_7_powerup_block_1 = "Outrageous - Powerup Block #1"
+special_zone_7_yoshi_block_1 = "Outrageous - Yoshi Block #1"
+special_zone_7_coin_block_1 = "Outrageous - Coin Block #1"
+special_zone_7_powerup_block_2 = "Outrageous - Powerup Block #2"
+special_zone_7_coin_block_2 = "Outrageous - Coin Block #2"
+special_zone_6_powerup_block_1 = "Mondo - Powerup Block #1"
+special_zone_6_coin_block_1 = "Mondo - Coin Block #1"
+special_zone_6_coin_block_2 = "Mondo - Coin Block #2"
+special_zone_6_yoshi_block_1 = "Mondo - Yoshi Block #1"
+special_zone_6_life_block_1 = "Mondo - 1-Up Mushroom Block #1"
+special_zone_6_multi_coin_block_1 = "Mondo - Multi Coin Block #1"
+special_zone_6_coin_block_3 = "Mondo - Coin Block #3"
+special_zone_6_coin_block_4 = "Mondo - Coin Block #4"
+special_zone_6_coin_block_5 = "Mondo - Coin Block #5"
+special_zone_6_coin_block_6 = "Mondo - Coin Block #6"
+special_zone_6_coin_block_7 = "Mondo - Coin Block #7"
+special_zone_6_coin_block_8 = "Mondo - Coin Block #8"
+special_zone_6_coin_block_9 = "Mondo - Coin Block #9"
+special_zone_6_coin_block_10 = "Mondo - Coin Block #10"
+special_zone_6_coin_block_11 = "Mondo - Coin Block #11"
+special_zone_6_coin_block_12 = "Mondo - Coin Block #12"
+special_zone_6_coin_block_13 = "Mondo - Coin Block #13"
+special_zone_6_coin_block_14 = "Mondo - Coin Block #14"
+special_zone_6_coin_block_15 = "Mondo - Coin Block #15"
+special_zone_6_coin_block_16 = "Mondo - Coin Block #16"
+special_zone_6_coin_block_17 = "Mondo - Coin Block #17"
+special_zone_6_coin_block_18 = "Mondo - Coin Block #18"
+special_zone_6_coin_block_19 = "Mondo - Coin Block #19"
+special_zone_6_coin_block_20 = "Mondo - Coin Block #20"
+special_zone_6_coin_block_21 = "Mondo - Coin Block #21"
+special_zone_6_coin_block_22 = "Mondo - Coin Block #22"
+special_zone_6_coin_block_23 = "Mondo - Coin Block #23"
+special_zone_6_coin_block_24 = "Mondo - Coin Block #24"
+special_zone_6_coin_block_25 = "Mondo - Coin Block #25"
+special_zone_6_coin_block_26 = "Mondo - Coin Block #26"
+special_zone_6_coin_block_27 = "Mondo - Coin Block #27"
+special_zone_6_coin_block_28 = "Mondo - Coin Block #28"
+special_zone_6_powerup_block_2 = "Mondo - Powerup Block #2"
+special_zone_6_coin_block_29 = "Mondo - Coin Block #29"
+special_zone_6_coin_block_30 = "Mondo - Coin Block #30"
+special_zone_6_coin_block_31 = "Mondo - Coin Block #31"
+special_zone_6_coin_block_32 = "Mondo - Coin Block #32"
+special_zone_6_coin_block_33 = "Mondo - Coin Block #33"
+special_zone_5_yoshi_block_1 = "Groovy - Yoshi Block #1"
+special_zone_1_vine_block_1 = "Gnarly - Vine Block #1"
+special_zone_1_vine_block_2 = "Gnarly - Vine Block #2"
+special_zone_1_vine_block_3 = "Gnarly - Vine Block #3"
+special_zone_1_vine_block_4 = "Gnarly - Vine Block #4"
+special_zone_1_life_block_1 = "Gnarly - 1-Up Mushroom Block #1"
+special_zone_1_vine_block_5 = "Gnarly - Vine Block #5"
+special_zone_1_blue_pow_block_1 = "Gnarly - Blue P-Switch Block #1"
+special_zone_1_vine_block_6 = "Gnarly - Vine Block #6"
+special_zone_1_powerup_block_1 = "Gnarly - Powerup Block #1"
+special_zone_1_pswitch_coin_block_1 = "Gnarly - P-Switch Coin Block #1"
+special_zone_1_pswitch_coin_block_2 = "Gnarly - P-Switch Coin Block #2"
+special_zone_1_pswitch_coin_block_3 = "Gnarly - P-Switch Coin Block #3"
+special_zone_1_pswitch_coin_block_4 = "Gnarly - P-Switch Coin Block #4"
+special_zone_1_pswitch_coin_block_5 = "Gnarly - P-Switch Coin Block #5"
+special_zone_1_pswitch_coin_block_6 = "Gnarly - P-Switch Coin Block #6"
+special_zone_1_pswitch_coin_block_7 = "Gnarly - P-Switch Coin Block #7"
+special_zone_1_pswitch_coin_block_8 = "Gnarly - P-Switch Coin Block #8"
+special_zone_1_pswitch_coin_block_9 = "Gnarly - P-Switch Coin Block #9"
+special_zone_1_pswitch_coin_block_10 = "Gnarly - P-Switch Coin Block #10"
+special_zone_1_pswitch_coin_block_11 = "Gnarly - P-Switch Coin Block #11"
+special_zone_1_pswitch_coin_block_12 = "Gnarly - P-Switch Coin Block #12"
+special_zone_1_pswitch_coin_block_13 = "Gnarly - P-Switch Coin Block #13"
+special_zone_2_powerup_block_1 = "Tubular - Powerup Block #1"
+special_zone_2_coin_block_1 = "Tubular - Coin Block #1"
+special_zone_2_coin_block_2 = "Tubular - Coin Block #2"
+special_zone_2_powerup_block_2 = "Tubular - Powerup Block #2"
+special_zone_2_coin_block_3 = "Tubular - Coin Block #3"
+special_zone_2_coin_block_4 = "Tubular - Coin Block #4"
+special_zone_2_powerup_block_3 = "Tubular - Powerup Block #3"
+special_zone_2_multi_coin_block_1 = "Tubular - Multi Coin Block #1"
+special_zone_2_coin_block_5 = "Tubular - Coin Block #5"
+special_zone_2_coin_block_6 = "Tubular - Coin Block #6"
+special_zone_3_powerup_block_1 = "Way Cool - Powerup Block #1"
+special_zone_3_yoshi_block_1 = "Way Cool - Yoshi Block #1"
+special_zone_3_wings_block_1 = "Way Cool - Wings Block #1"
+special_zone_4_powerup_block_1 = "Awesome - Powerup Block #1"
+special_zone_4_star_block_1 = "Awesome - Star Block #1"
+star_road_2_star_block_1 = "Star Road 2 - Star Block #1"
+star_road_3_key_block_1 = "Star Road 3 - Key Block #1"
+star_road_4_powerup_block_1 = "Star Road 4 - Powerup Block #1"
+star_road_4_green_block_1 = "Star Road 4 - Green Switch Palace Block #1"
+star_road_4_green_block_2 = "Star Road 4 - Green Switch Palace Block #2"
+star_road_4_green_block_3 = "Star Road 4 - Green Switch Palace Block #3"
+star_road_4_green_block_4 = "Star Road 4 - Green Switch Palace Block #4"
+star_road_4_green_block_5 = "Star Road 4 - Green Switch Palace Block #5"
+star_road_4_green_block_6 = "Star Road 4 - Green Switch Palace Block #6"
+star_road_4_green_block_7 = "Star Road 4 - Green Switch Palace Block #7"
+star_road_4_key_block_1 = "Star Road 4 - Key Block #1"
+star_road_5_directional_coin_block_1 = "Star Road 5 - Directional Coin Block #1"
+star_road_5_life_block_1 = "Star Road 5 - 1-Up Mushroom Block #1"
+star_road_5_vine_block_1 = "Star Road 5 - Vine Block #1"
+star_road_5_yellow_block_1 = "Star Road 5 - Yellow Switch Palace Block #1"
+star_road_5_yellow_block_2 = "Star Road 5 - Yellow Switch Palace Block #2"
+star_road_5_yellow_block_3 = "Star Road 5 - Yellow Switch Palace Block #3"
+star_road_5_yellow_block_4 = "Star Road 5 - Yellow Switch Palace Block #4"
+star_road_5_yellow_block_5 = "Star Road 5 - Yellow Switch Palace Block #5"
+star_road_5_yellow_block_6 = "Star Road 5 - Yellow Switch Palace Block #6"
+star_road_5_yellow_block_7 = "Star Road 5 - Yellow Switch Palace Block #7"
+star_road_5_yellow_block_8 = "Star Road 5 - Yellow Switch Palace Block #8"
+star_road_5_yellow_block_9 = "Star Road 5 - Yellow Switch Palace Block #9"
+star_road_5_yellow_block_10 = "Star Road 5 - Yellow Switch Palace Block #10"
+star_road_5_yellow_block_11 = "Star Road 5 - Yellow Switch Palace Block #11"
+star_road_5_yellow_block_12 = "Star Road 5 - Yellow Switch Palace Block #12"
+star_road_5_yellow_block_13 = "Star Road 5 - Yellow Switch Palace Block #13"
+star_road_5_yellow_block_14 = "Star Road 5 - Yellow Switch Palace Block #14"
+star_road_5_yellow_block_15 = "Star Road 5 - Yellow Switch Palace Block #15"
+star_road_5_yellow_block_16 = "Star Road 5 - Yellow Switch Palace Block #16"
+star_road_5_yellow_block_17 = "Star Road 5 - Yellow Switch Palace Block #17"
+star_road_5_yellow_block_18 = "Star Road 5 - Yellow Switch Palace Block #18"
+star_road_5_yellow_block_19 = "Star Road 5 - Yellow Switch Palace Block #19"
+star_road_5_yellow_block_20 = "Star Road 5 - Yellow Switch Palace Block #20"
+star_road_5_green_block_1 = "Star Road 5 - Green Switch Palace Block #1"
+star_road_5_green_block_2 = "Star Road 5 - Green Switch Palace Block #2"
+star_road_5_green_block_3 = "Star Road 5 - Green Switch Palace Block #3"
+star_road_5_green_block_4 = "Star Road 5 - Green Switch Palace Block #4"
+star_road_5_green_block_5 = "Star Road 5 - Green Switch Palace Block #5"
+star_road_5_green_block_6 = "Star Road 5 - Green Switch Palace Block #6"
+star_road_5_green_block_7 = "Star Road 5 - Green Switch Palace Block #7"
+star_road_5_green_block_8 = "Star Road 5 - Green Switch Palace Block #8"
+star_road_5_green_block_9 = "Star Road 5 - Green Switch Palace Block #9"
+star_road_5_green_block_10 = "Star Road 5 - Green Switch Palace Block #10"
+star_road_5_green_block_11 = "Star Road 5 - Green Switch Palace Block #11"
+star_road_5_green_block_12 = "Star Road 5 - Green Switch Palace Block #12"
+star_road_5_green_block_13 = "Star Road 5 - Green Switch Palace Block #13"
+star_road_5_green_block_14 = "Star Road 5 - Green Switch Palace Block #14"
+star_road_5_green_block_15 = "Star Road 5 - Green Switch Palace Block #15"
+star_road_5_green_block_16 = "Star Road 5 - Green Switch Palace Block #16"
+star_road_5_green_block_17 = "Star Road 5 - Green Switch Palace Block #17"
+star_road_5_green_block_18 = "Star Road 5 - Green Switch Palace Block #18"
+star_road_5_green_block_19 = "Star Road 5 - Green Switch Palace Block #19"
+star_road_5_green_block_20 = "Star Road 5 - Green Switch Palace Block #20"
diff --git a/worlds/smw/Names/TextBox.py b/worlds/smw/Names/TextBox.py
index cecf0886617c..2302a5f85fc9 100644
--- a/worlds/smw/Names/TextBox.py
+++ b/worlds/smw/Names/TextBox.py
@@ -1,5 +1,5 @@
-from BaseClasses import MultiWorld
+from worlds.AutoWorld import World
import math
@@ -63,21 +63,23 @@ def generate_text_box(input_string):
return out_bytes
-def generate_goal_text(world: MultiWorld, player: int):
+def generate_goal_text(world: World):
out_array = bytearray()
- if world.goal[player] == "yoshi_egg_hunt":
- required_yoshi_eggs = max(math.floor(
- world.number_of_yoshi_eggs[player].value * (world.percentage_of_yoshi_eggs[player].value / 100.0)), 1)
+ if world.options.goal == "yoshi_egg_hunt":
+ required_yoshi_eggs = world.required_egg_count
+ actual_yoshi_eggs = world.actual_egg_count
out_array += bytearray([0x9F, 0x9F])
out_array += string_to_bytes(" You must acquire")
out_array[-1] += 0x80
- out_array += string_to_bytes(f' {required_yoshi_eggs:02} Yoshi Eggs,')
+ out_array += string_to_bytes(f' {required_yoshi_eggs:03} of {actual_yoshi_eggs:03}')
+ out_array[-1] += 0x80
+ out_array += string_to_bytes(f' Yoshi Eggs,')
out_array[-1] += 0x80
out_array += string_to_bytes("then return here.")
out_array[-1] += 0x80
- out_array += bytearray([0x9F, 0x9F, 0x9F])
+ out_array += bytearray([0x9F, 0x9F])
else:
- bosses_required = world.bosses_required[player].value
+ bosses_required = world.options.bosses_required.value
out_array += bytearray([0x9F, 0x9F])
out_array += string_to_bytes(" You must defeat")
out_array[-1] += 0x80
diff --git a/worlds/smw/Options.py b/worlds/smw/Options.py
index 60135896c86c..ab7fcccdba5d 100644
--- a/worlds/smw/Options.py
+++ b/worlds/smw/Options.py
@@ -1,6 +1,6 @@
-import typing
+from dataclasses import dataclass
-from Options import Choice, Range, Option, Toggle, DeathLink, DefaultOnToggle, OptionList
+from Options import Choice, Range, Toggle, DeathLink, DefaultOnToggle, PerGameCommonOptions
class Goal(Choice):
@@ -27,11 +27,13 @@ class BossesRequired(Range):
class NumberOfYoshiEggs(Range):
"""
- How many Yoshi Eggs are in the pool for Yoshi Egg Hunt
+ Maximum possible number of Yoshi Eggs that will be in the item pool
+ If fewer available locations exist in the pool than this number, the number of available locations will be used instead.
+ Required Percentage of Yoshi Eggs will be calculated based off of that number.
"""
- display_name = "Total Number of Yoshi Eggs"
+ display_name = "Max Number of Yoshi Eggs"
range_start = 1
- range_end = 80
+ range_end = 255
default = 50
@@ -52,6 +54,40 @@ class DragonCoinChecks(Toggle):
display_name = "Dragon Coin Checks"
+class MoonChecks(Toggle):
+ """
+ Whether collecting a 3-Up Moon in a level will grant a check
+ """
+ display_name = "3up Moon Checks"
+
+
+class Hidden1UpChecks(Toggle):
+ """
+ Whether collecting a hidden 1-Up mushroom in a level will grant a check
+ These checks are considered cryptic as there's no actual indicator that they're in their respective places
+ Enable this option at your own risk
+ """
+ display_name = "Hidden 1-Up Checks"
+
+
+class BonusBlockChecks(Toggle):
+ """
+ Whether collecting a 1-Up mushroom from a Bonus Block in a level will grant a check
+ """
+ display_name = "Bonus Block Checks"
+
+
+class Blocksanity(Toggle):
+ """
+ Whether hitting a block with an item or coin inside will grant a check
+ Note that some blocks are excluded due to how the option and the game works!
+ Exclusion list:
+ * Blocks in Top Secret Area & Front Door/Bowser Castle
+ * Blocks that are unreachable unless you glitch your way in
+ """
+ display_name = "Blocksanity"
+
+
class BowserCastleDoors(Choice):
"""
How the doors of Bowser's Castle behave
@@ -127,16 +163,6 @@ class SwapDonutGhostHouseExits(Toggle):
display_name = "Swap Donut GH Exits"
-class DisplaySentItemPopups(Choice):
- """
- What messages to display in-game for items sent
- """
- display_name = "Display Sent Item Popups"
- option_none = 0
- option_all = 1
- default = 1
-
-
class DisplayReceivedItemPopups(Choice):
"""
What messages to display in-game for items received
@@ -145,7 +171,18 @@ class DisplayReceivedItemPopups(Choice):
option_none = 0
option_all = 1
option_progression = 2
- default = 2
+ option_progression_minus_yoshi_eggs = 3
+ default = 3
+
+
+class JunkFillPercentage(Range):
+ """
+ Replace a percentage of non-required Yoshi Eggs in the item pool with random junk items (only applicable on Yoshi Egg Hunt goal)
+ """
+ display_name = "Junk Fill Percentage"
+ range_start = 0
+ range_end = 100
+ default = 0
class TrapFillPercentage(Range):
@@ -197,6 +234,20 @@ class TimerTrapWeight(BaseTrapWeight):
display_name = "Timer Trap Weight"
+class ReverseTrapWeight(BaseTrapWeight):
+ """
+ Likelihood of a receiving a trap which causes the controls to be reversed in the current level
+ """
+ display_name = "Reverse Trap Weight"
+
+
+class ThwimpTrapWeight(BaseTrapWeight):
+ """
+ Likelihood of a receiving a trap which causes a Thwimp to spawn above the player
+ """
+ display_name = "Thwimp Trap Weight"
+
+
class Autosave(DefaultOnToggle):
"""
Whether a save prompt will appear after every level
@@ -239,6 +290,21 @@ class MusicShuffle(Choice):
default = 0
+class SFXShuffle(Choice):
+ """
+ Shuffles almost every instance of sound effect playback
+ Archipelago elements that play sound effects aren't randomized
+ None: No SFX are shuffled
+ Full: Each individual SFX call has a random SFX
+ Singularity: The entire game uses one SFX for every SFX call
+ """
+ display_name = "Sound Effect Shuffle"
+ option_none = 0
+ option_full = 1
+ option_singularity = 2
+ default = 0
+
+
class MarioPalette(Choice):
"""
Mario palette color
@@ -255,25 +321,32 @@ class MarioPalette(Choice):
default = 0
-class ForegroundPaletteShuffle(Toggle):
- """
- Whether to shuffle level foreground palettes
- """
- display_name = "Foreground Palette Shuffle"
-
-
-class BackgroundPaletteShuffle(Toggle):
+class LevelPaletteShuffle(Choice):
"""
- Whether to shuffle level background palettes
+ Whether to shuffle level palettes
+ Off: Do not shuffle palettes
+ On Legacy: Uses only the palette sets from the original game
+ On Curated: Uses custom, hand-crafted palette sets
"""
- display_name = "Background Palette Shuffle"
+ display_name = "Level Palette Shuffle"
+ option_off = 0
+ option_on_legacy = 1
+ option_on_curated = 2
+ default = 0
-class OverworldPaletteShuffle(Toggle):
+class OverworldPaletteShuffle(Choice):
"""
Whether to shuffle overworld palettes
+ Off: Do not shuffle palettes
+ On Legacy: Uses only the palette sets from the original game
+ On Curated: Uses custom, hand-crafted palette sets
"""
display_name = "Overworld Palette Shuffle"
+ option_off = 0
+ option_on_legacy = 1
+ option_on_curated = 2
+ default = 0
class StartingLifeCount(Range):
@@ -286,34 +359,39 @@ class StartingLifeCount(Range):
default = 5
-
-smw_options: typing.Dict[str, type(Option)] = {
- "death_link": DeathLink,
- "goal": Goal,
- "bosses_required": BossesRequired,
- "number_of_yoshi_eggs": NumberOfYoshiEggs,
- "percentage_of_yoshi_eggs": PercentageOfYoshiEggs,
- "dragon_coin_checks": DragonCoinChecks,
- "bowser_castle_doors": BowserCastleDoors,
- "bowser_castle_rooms": BowserCastleRooms,
- "level_shuffle": LevelShuffle,
- "exclude_special_zone": ExcludeSpecialZone,
- "boss_shuffle": BossShuffle,
- "swap_donut_gh_exits": SwapDonutGhostHouseExits,
- #"display_sent_item_popups": DisplaySentItemPopups,
- "display_received_item_popups": DisplayReceivedItemPopups,
- "trap_fill_percentage": TrapFillPercentage,
- "ice_trap_weight": IceTrapWeight,
- "stun_trap_weight": StunTrapWeight,
- "literature_trap_weight": LiteratureTrapWeight,
- "timer_trap_weight": TimerTrapWeight,
- "autosave": Autosave,
- "early_climb": EarlyClimb,
- "overworld_speed": OverworldSpeed,
- "music_shuffle": MusicShuffle,
- "mario_palette": MarioPalette,
- "foreground_palette_shuffle": ForegroundPaletteShuffle,
- "background_palette_shuffle": BackgroundPaletteShuffle,
- "overworld_palette_shuffle": OverworldPaletteShuffle,
- "starting_life_count": StartingLifeCount,
-}
+@dataclass
+class SMWOptions(PerGameCommonOptions):
+ death_link: DeathLink
+ goal: Goal
+ bosses_required: BossesRequired
+ max_yoshi_egg_cap: NumberOfYoshiEggs
+ percentage_of_yoshi_eggs: PercentageOfYoshiEggs
+ dragon_coin_checks: DragonCoinChecks
+ moon_checks: MoonChecks
+ hidden_1up_checks: Hidden1UpChecks
+ bonus_block_checks: BonusBlockChecks
+ blocksanity: Blocksanity
+ bowser_castle_doors: BowserCastleDoors
+ bowser_castle_rooms: BowserCastleRooms
+ level_shuffle: LevelShuffle
+ exclude_special_zone: ExcludeSpecialZone
+ boss_shuffle: BossShuffle
+ swap_donut_gh_exits: SwapDonutGhostHouseExits
+ display_received_item_popups: DisplayReceivedItemPopups
+ junk_fill_percentage: JunkFillPercentage
+ trap_fill_percentage: TrapFillPercentage
+ ice_trap_weight: IceTrapWeight
+ stun_trap_weight: StunTrapWeight
+ literature_trap_weight: LiteratureTrapWeight
+ timer_trap_weight: TimerTrapWeight
+ reverse_trap_weight: ReverseTrapWeight
+ thwimp_trap_weight: ThwimpTrapWeight
+ autosave: Autosave
+ early_climb: EarlyClimb
+ overworld_speed: OverworldSpeed
+ music_shuffle: MusicShuffle
+ sfx_shuffle: SFXShuffle
+ mario_palette: MarioPalette
+ level_palette_shuffle: LevelPaletteShuffle
+ overworld_palette_shuffle: OverworldPaletteShuffle
+ starting_life_count: StartingLifeCount
diff --git a/worlds/smw/Regions.py b/worlds/smw/Regions.py
index 885f209aa74c..249604987401 100644
--- a/worlds/smw/Regions.py
+++ b/worlds/smw/Regions.py
@@ -1,467 +1,470 @@
import typing
-from BaseClasses import MultiWorld, Region, Entrance
+from BaseClasses import CollectionState, MultiWorld, Region, Entrance
from .Locations import SMWLocation
from .Levels import level_info_dict
from .Names import LocationName, ItemName
from worlds.generic.Rules import add_rule, set_rule
+from worlds.AutoWorld import World
-def create_regions(world, player: int, active_locations):
- menu_region = create_region(world, player, active_locations, 'Menu', None)
+def create_regions(world: World, active_locations):
+ multiworld: MultiWorld = world.multiworld
+ player: int = world.player
- yoshis_island_region = create_region(world, player, active_locations, LocationName.yoshis_island_region, None)
+ menu_region = create_region(multiworld, player, active_locations, 'Menu', None)
+ yoshis_island_region = create_region(multiworld, player, active_locations, LocationName.yoshis_island_region, None)
- yoshis_house_tile = create_region(world, player, active_locations, LocationName.yoshis_house_tile, None)
+ yoshis_house_tile = create_region(multiworld, player, active_locations, LocationName.yoshis_house_tile, None)
yoshis_house_region_locations = []
- if world.goal[player] == "yoshi_egg_hunt":
+ if world.options.goal == "yoshi_egg_hunt":
yoshis_house_region_locations.append(LocationName.yoshis_house)
- yoshis_house_region = create_region(world, player, active_locations, LocationName.yoshis_house,
+ yoshis_house_region = create_region(multiworld, player, active_locations, LocationName.yoshis_house,
yoshis_house_region_locations)
- yoshis_island_1_tile = create_region(world, player, active_locations, LocationName.yoshis_island_1_tile, None)
- yoshis_island_1_region = create_region(world, player, active_locations, LocationName.yoshis_island_1_region, None)
- yoshis_island_1_exit_1 = create_region(world, player, active_locations, LocationName.yoshis_island_1_exit_1,
+ yoshis_island_1_tile = create_region(multiworld, player, active_locations, LocationName.yoshis_island_1_tile, None)
+ yoshis_island_1_region = create_region(multiworld, player, active_locations, LocationName.yoshis_island_1_region, None)
+ yoshis_island_1_exit_1 = create_region(multiworld, player, active_locations, LocationName.yoshis_island_1_exit_1,
[LocationName.yoshis_island_1_exit_1])
- yoshis_island_2_tile = create_region(world, player, active_locations, LocationName.yoshis_island_2_tile, None)
- yoshis_island_2_region = create_region(world, player, active_locations, LocationName.yoshis_island_2_region, None)
- yoshis_island_2_exit_1 = create_region(world, player, active_locations, LocationName.yoshis_island_2_exit_1,
+ yoshis_island_2_tile = create_region(multiworld, player, active_locations, LocationName.yoshis_island_2_tile, None)
+ yoshis_island_2_region = create_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, None)
+ yoshis_island_2_exit_1 = create_region(multiworld, player, active_locations, LocationName.yoshis_island_2_exit_1,
[LocationName.yoshis_island_2_exit_1])
- yoshis_island_3_tile = create_region(world, player, active_locations, LocationName.yoshis_island_3_tile, None)
- yoshis_island_3_region = create_region(world, player, active_locations, LocationName.yoshis_island_3_region, None)
- yoshis_island_3_exit_1 = create_region(world, player, active_locations, LocationName.yoshis_island_3_exit_1,
+ yoshis_island_3_tile = create_region(multiworld, player, active_locations, LocationName.yoshis_island_3_tile, None)
+ yoshis_island_3_region = create_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, None)
+ yoshis_island_3_exit_1 = create_region(multiworld, player, active_locations, LocationName.yoshis_island_3_exit_1,
[LocationName.yoshis_island_3_exit_1])
- yoshis_island_4_tile = create_region(world, player, active_locations, LocationName.yoshis_island_4_tile, None)
- yoshis_island_4_region = create_region(world, player, active_locations, LocationName.yoshis_island_4_region, None)
- yoshis_island_4_exit_1 = create_region(world, player, active_locations, LocationName.yoshis_island_4_exit_1,
+ yoshis_island_4_tile = create_region(multiworld, player, active_locations, LocationName.yoshis_island_4_tile, None)
+ yoshis_island_4_region = create_region(multiworld, player, active_locations, LocationName.yoshis_island_4_region, None)
+ yoshis_island_4_exit_1 = create_region(multiworld, player, active_locations, LocationName.yoshis_island_4_exit_1,
[LocationName.yoshis_island_4_exit_1])
- yoshis_island_castle_tile = create_region(world, player, active_locations, LocationName.yoshis_island_castle_tile, None)
- yoshis_island_castle_region = create_region(world, player, active_locations, LocationName.yoshis_island_castle_region, None)
- yoshis_island_castle = create_region(world, player, active_locations, LocationName.yoshis_island_castle,
+ yoshis_island_castle_tile = create_region(multiworld, player, active_locations, LocationName.yoshis_island_castle_tile, None)
+ yoshis_island_castle_region = create_region(multiworld, player, active_locations, LocationName.yoshis_island_castle_region, None)
+ yoshis_island_castle = create_region(multiworld, player, active_locations, LocationName.yoshis_island_castle,
[LocationName.yoshis_island_castle, LocationName.yoshis_island_koopaling])
- yellow_switch_palace_tile = create_region(world, player, active_locations, LocationName.yellow_switch_palace_tile, None)
- yellow_switch_palace = create_region(world, player, active_locations, LocationName.yellow_switch_palace,
+ yellow_switch_palace_tile = create_region(multiworld, player, active_locations, LocationName.yellow_switch_palace_tile, None)
+ yellow_switch_palace = create_region(multiworld, player, active_locations, LocationName.yellow_switch_palace,
[LocationName.yellow_switch_palace])
- donut_plains_1_tile = create_region(world, player, active_locations, LocationName.donut_plains_1_tile, None)
- donut_plains_1_region = create_region(world, player, active_locations, LocationName.donut_plains_1_region, None)
- donut_plains_1_exit_1 = create_region(world, player, active_locations, LocationName.donut_plains_1_exit_1,
+ donut_plains_1_tile = create_region(multiworld, player, active_locations, LocationName.donut_plains_1_tile, None)
+ donut_plains_1_region = create_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, None)
+ donut_plains_1_exit_1 = create_region(multiworld, player, active_locations, LocationName.donut_plains_1_exit_1,
[LocationName.donut_plains_1_exit_1])
- donut_plains_1_exit_2 = create_region(world, player, active_locations, LocationName.donut_plains_1_exit_2,
+ donut_plains_1_exit_2 = create_region(multiworld, player, active_locations, LocationName.donut_plains_1_exit_2,
[LocationName.donut_plains_1_exit_2])
- donut_plains_2_tile = create_region(world, player, active_locations, LocationName.donut_plains_2_tile, None)
- donut_plains_2_region = create_region(world, player, active_locations, LocationName.donut_plains_2_region, None)
- donut_plains_2_exit_1 = create_region(world, player, active_locations, LocationName.donut_plains_2_exit_1,
+ donut_plains_2_tile = create_region(multiworld, player, active_locations, LocationName.donut_plains_2_tile, None)
+ donut_plains_2_region = create_region(multiworld, player, active_locations, LocationName.donut_plains_2_region, None)
+ donut_plains_2_exit_1 = create_region(multiworld, player, active_locations, LocationName.donut_plains_2_exit_1,
[LocationName.donut_plains_2_exit_1])
- donut_plains_2_exit_2 = create_region(world, player, active_locations, LocationName.donut_plains_2_exit_2,
+ donut_plains_2_exit_2 = create_region(multiworld, player, active_locations, LocationName.donut_plains_2_exit_2,
[LocationName.donut_plains_2_exit_2])
- donut_plains_3_tile = create_region(world, player, active_locations, LocationName.donut_plains_3_tile, None)
- donut_plains_3_region = create_region(world, player, active_locations, LocationName.donut_plains_3_region, None)
- donut_plains_3_exit_1 = create_region(world, player, active_locations, LocationName.donut_plains_3_exit_1,
+ donut_plains_3_tile = create_region(multiworld, player, active_locations, LocationName.donut_plains_3_tile, None)
+ donut_plains_3_region = create_region(multiworld, player, active_locations, LocationName.donut_plains_3_region, None)
+ donut_plains_3_exit_1 = create_region(multiworld, player, active_locations, LocationName.donut_plains_3_exit_1,
[LocationName.donut_plains_3_exit_1])
- donut_plains_4_tile = create_region(world, player, active_locations, LocationName.donut_plains_4_tile, None)
- donut_plains_4_region = create_region(world, player, active_locations, LocationName.donut_plains_4_region, None)
- donut_plains_4_exit_1 = create_region(world, player, active_locations, LocationName.donut_plains_4_exit_1,
+ donut_plains_4_tile = create_region(multiworld, player, active_locations, LocationName.donut_plains_4_tile, None)
+ donut_plains_4_region = create_region(multiworld, player, active_locations, LocationName.donut_plains_4_region, None)
+ donut_plains_4_exit_1 = create_region(multiworld, player, active_locations, LocationName.donut_plains_4_exit_1,
[LocationName.donut_plains_4_exit_1])
- donut_secret_1_tile = create_region(world, player, active_locations, LocationName.donut_secret_1_tile, None)
- donut_secret_1_region = create_region(world, player, active_locations, LocationName.donut_secret_1_region, None)
- donut_secret_1_exit_1 = create_region(world, player, active_locations, LocationName.donut_secret_1_exit_1,
+ donut_secret_1_tile = create_region(multiworld, player, active_locations, LocationName.donut_secret_1_tile, None)
+ donut_secret_1_region = create_region(multiworld, player, active_locations, LocationName.donut_secret_1_region, None)
+ donut_secret_1_exit_1 = create_region(multiworld, player, active_locations, LocationName.donut_secret_1_exit_1,
[LocationName.donut_secret_1_exit_1])
- donut_secret_1_exit_2 = create_region(world, player, active_locations, LocationName.donut_secret_1_exit_2,
+ donut_secret_1_exit_2 = create_region(multiworld, player, active_locations, LocationName.donut_secret_1_exit_2,
[LocationName.donut_secret_1_exit_2])
- donut_secret_2_tile = create_region(world, player, active_locations, LocationName.donut_secret_2_tile, None)
- donut_secret_2_region = create_region(world, player, active_locations, LocationName.donut_secret_2_region, None)
- donut_secret_2_exit_1 = create_region(world, player, active_locations, LocationName.donut_secret_2_exit_1,
+ donut_secret_2_tile = create_region(multiworld, player, active_locations, LocationName.donut_secret_2_tile, None)
+ donut_secret_2_region = create_region(multiworld, player, active_locations, LocationName.donut_secret_2_region, None)
+ donut_secret_2_exit_1 = create_region(multiworld, player, active_locations, LocationName.donut_secret_2_exit_1,
[LocationName.donut_secret_2_exit_1])
- donut_ghost_house_tile = create_region(world, player, active_locations, LocationName.donut_ghost_house_tile, None)
- donut_ghost_house_region = create_region(world, player, active_locations, LocationName.donut_ghost_house_region, None)
- donut_ghost_house_exit_1 = create_region(world, player, active_locations, LocationName.donut_ghost_house_exit_1,
+ donut_ghost_house_tile = create_region(multiworld, player, active_locations, LocationName.donut_ghost_house_tile, None)
+ donut_ghost_house_region = create_region(multiworld, player, active_locations, LocationName.donut_ghost_house_region, None)
+ donut_ghost_house_exit_1 = create_region(multiworld, player, active_locations, LocationName.donut_ghost_house_exit_1,
[LocationName.donut_ghost_house_exit_1])
- donut_ghost_house_exit_2 = create_region(world, player, active_locations, LocationName.donut_ghost_house_exit_2,
+ donut_ghost_house_exit_2 = create_region(multiworld, player, active_locations, LocationName.donut_ghost_house_exit_2,
[LocationName.donut_ghost_house_exit_2])
- donut_secret_house_tile = create_region(world, player, active_locations, LocationName.donut_secret_house_tile, None)
- donut_secret_house_region = create_region(world, player, active_locations, LocationName.donut_secret_house_region, None)
- donut_secret_house_exit_1 = create_region(world, player, active_locations, LocationName.donut_secret_house_exit_1,
+ donut_secret_house_tile = create_region(multiworld, player, active_locations, LocationName.donut_secret_house_tile, None)
+ donut_secret_house_region = create_region(multiworld, player, active_locations, LocationName.donut_secret_house_region, None)
+ donut_secret_house_exit_1 = create_region(multiworld, player, active_locations, LocationName.donut_secret_house_exit_1,
[LocationName.donut_secret_house_exit_1])
- donut_secret_house_exit_2 = create_region(world, player, active_locations, LocationName.donut_secret_house_exit_2,
+ donut_secret_house_exit_2 = create_region(multiworld, player, active_locations, LocationName.donut_secret_house_exit_2,
[LocationName.donut_secret_house_exit_2])
- donut_plains_castle_tile = create_region(world, player, active_locations, LocationName.donut_plains_castle_tile, None)
- donut_plains_castle_region = create_region(world, player, active_locations, LocationName.donut_plains_castle_region, None)
- donut_plains_castle = create_region(world, player, active_locations, LocationName.donut_plains_castle,
+ donut_plains_castle_tile = create_region(multiworld, player, active_locations, LocationName.donut_plains_castle_tile, None)
+ donut_plains_castle_region = create_region(multiworld, player, active_locations, LocationName.donut_plains_castle_region, None)
+ donut_plains_castle = create_region(multiworld, player, active_locations, LocationName.donut_plains_castle,
[LocationName.donut_plains_castle, LocationName.donut_plains_koopaling])
- green_switch_palace_tile = create_region(world, player, active_locations, LocationName.green_switch_palace_tile, None)
- green_switch_palace = create_region(world, player, active_locations, LocationName.green_switch_palace,
+ green_switch_palace_tile = create_region(multiworld, player, active_locations, LocationName.green_switch_palace_tile, None)
+ green_switch_palace = create_region(multiworld, player, active_locations, LocationName.green_switch_palace,
[LocationName.green_switch_palace])
- donut_plains_top_secret_tile = create_region(world, player, active_locations, LocationName.donut_plains_top_secret_tile, None)
- donut_plains_top_secret = create_region(world, player, active_locations, LocationName.donut_plains_top_secret, None)
+ donut_plains_top_secret_tile = create_region(multiworld, player, active_locations, LocationName.donut_plains_top_secret_tile, None)
+ donut_plains_top_secret = create_region(multiworld, player, active_locations, LocationName.donut_plains_top_secret, None)
- vanilla_dome_1_tile = create_region(world, player, active_locations, LocationName.vanilla_dome_1_tile, None)
- vanilla_dome_1_region = create_region(world, player, active_locations, LocationName.vanilla_dome_1_region, None)
- vanilla_dome_1_exit_1 = create_region(world, player, active_locations, LocationName.vanilla_dome_1_exit_1,
+ vanilla_dome_1_tile = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_1_tile, None)
+ vanilla_dome_1_region = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_1_region, None)
+ vanilla_dome_1_exit_1 = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_1_exit_1,
[LocationName.vanilla_dome_1_exit_1])
- vanilla_dome_1_exit_2 = create_region(world, player, active_locations, LocationName.vanilla_dome_1_exit_2,
+ vanilla_dome_1_exit_2 = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_1_exit_2,
[LocationName.vanilla_dome_1_exit_2])
- vanilla_dome_2_tile = create_region(world, player, active_locations, LocationName.vanilla_dome_2_tile, None)
- vanilla_dome_2_region = create_region(world, player, active_locations, LocationName.vanilla_dome_2_region, None)
- vanilla_dome_2_exit_1 = create_region(world, player, active_locations, LocationName.vanilla_dome_2_exit_1,
+ vanilla_dome_2_tile = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_tile, None)
+ vanilla_dome_2_region = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_region, None)
+ vanilla_dome_2_exit_1 = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_exit_1,
[LocationName.vanilla_dome_2_exit_1])
- vanilla_dome_2_exit_2 = create_region(world, player, active_locations, LocationName.vanilla_dome_2_exit_2,
+ vanilla_dome_2_exit_2 = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_exit_2,
[LocationName.vanilla_dome_2_exit_2])
- vanilla_dome_3_tile = create_region(world, player, active_locations, LocationName.vanilla_dome_3_tile, None)
- vanilla_dome_3_region = create_region(world, player, active_locations, LocationName.vanilla_dome_3_region, None)
- vanilla_dome_3_exit_1 = create_region(world, player, active_locations, LocationName.vanilla_dome_3_exit_1,
+ vanilla_dome_3_tile = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_tile, None)
+ vanilla_dome_3_region = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, None)
+ vanilla_dome_3_exit_1 = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_exit_1,
[LocationName.vanilla_dome_3_exit_1])
- vanilla_dome_4_tile = create_region(world, player, active_locations, LocationName.vanilla_dome_4_tile, None)
- vanilla_dome_4_region = create_region(world, player, active_locations, LocationName.vanilla_dome_4_region, None)
- vanilla_dome_4_exit_1 = create_region(world, player, active_locations, LocationName.vanilla_dome_4_exit_1,
+ vanilla_dome_4_tile = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_4_tile, None)
+ vanilla_dome_4_region = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_4_region, None)
+ vanilla_dome_4_exit_1 = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_4_exit_1,
[LocationName.vanilla_dome_4_exit_1])
- vanilla_secret_1_tile = create_region(world, player, active_locations, LocationName.vanilla_secret_1_tile, None)
- vanilla_secret_1_region = create_region(world, player, active_locations, LocationName.vanilla_secret_1_region, None)
- vanilla_secret_1_exit_1 = create_region(world, player, active_locations, LocationName.vanilla_secret_1_exit_1,
+ vanilla_secret_1_tile = create_region(multiworld, player, active_locations, LocationName.vanilla_secret_1_tile, None)
+ vanilla_secret_1_region = create_region(multiworld, player, active_locations, LocationName.vanilla_secret_1_region, None)
+ vanilla_secret_1_exit_1 = create_region(multiworld, player, active_locations, LocationName.vanilla_secret_1_exit_1,
[LocationName.vanilla_secret_1_exit_1])
- vanilla_secret_1_exit_2 = create_region(world, player, active_locations, LocationName.vanilla_secret_1_exit_2,
+ vanilla_secret_1_exit_2 = create_region(multiworld, player, active_locations, LocationName.vanilla_secret_1_exit_2,
[LocationName.vanilla_secret_1_exit_2])
- vanilla_secret_2_tile = create_region(world, player, active_locations, LocationName.vanilla_secret_2_tile, None)
- vanilla_secret_2_region = create_region(world, player, active_locations, LocationName.vanilla_secret_2_region, None)
- vanilla_secret_2_exit_1 = create_region(world, player, active_locations, LocationName.vanilla_secret_2_exit_1,
+ vanilla_secret_2_tile = create_region(multiworld, player, active_locations, LocationName.vanilla_secret_2_tile, None)
+ vanilla_secret_2_region = create_region(multiworld, player, active_locations, LocationName.vanilla_secret_2_region, None)
+ vanilla_secret_2_exit_1 = create_region(multiworld, player, active_locations, LocationName.vanilla_secret_2_exit_1,
[LocationName.vanilla_secret_2_exit_1])
- vanilla_secret_3_tile = create_region(world, player, active_locations, LocationName.vanilla_secret_3_tile, None)
- vanilla_secret_3_region = create_region(world, player, active_locations, LocationName.vanilla_secret_3_region, None)
- vanilla_secret_3_exit_1 = create_region(world, player, active_locations, LocationName.vanilla_secret_3_exit_1,
+ vanilla_secret_3_tile = create_region(multiworld, player, active_locations, LocationName.vanilla_secret_3_tile, None)
+ vanilla_secret_3_region = create_region(multiworld, player, active_locations, LocationName.vanilla_secret_3_region, None)
+ vanilla_secret_3_exit_1 = create_region(multiworld, player, active_locations, LocationName.vanilla_secret_3_exit_1,
[LocationName.vanilla_secret_3_exit_1])
- vanilla_ghost_house_tile = create_region(world, player, active_locations, LocationName.vanilla_ghost_house_tile, None)
- vanilla_ghost_house_region = create_region(world, player, active_locations, LocationName.vanilla_ghost_house_region, None)
- vanilla_ghost_house_exit_1 = create_region(world, player, active_locations, LocationName.vanilla_ghost_house_exit_1,
+ vanilla_ghost_house_tile = create_region(multiworld, player, active_locations, LocationName.vanilla_ghost_house_tile, None)
+ vanilla_ghost_house_region = create_region(multiworld, player, active_locations, LocationName.vanilla_ghost_house_region, None)
+ vanilla_ghost_house_exit_1 = create_region(multiworld, player, active_locations, LocationName.vanilla_ghost_house_exit_1,
[LocationName.vanilla_ghost_house_exit_1])
- vanilla_fortress_tile = create_region(world, player, active_locations, LocationName.vanilla_fortress_tile, None)
- vanilla_fortress_region = create_region(world, player, active_locations, LocationName.vanilla_fortress_region, None)
- vanilla_fortress = create_region(world, player, active_locations, LocationName.vanilla_fortress,
+ vanilla_fortress_tile = create_region(multiworld, player, active_locations, LocationName.vanilla_fortress_tile, None)
+ vanilla_fortress_region = create_region(multiworld, player, active_locations, LocationName.vanilla_fortress_region, None)
+ vanilla_fortress = create_region(multiworld, player, active_locations, LocationName.vanilla_fortress,
[LocationName.vanilla_fortress, LocationName.vanilla_reznor])
- vanilla_dome_castle_tile = create_region(world, player, active_locations, LocationName.vanilla_dome_castle_tile, None)
- vanilla_dome_castle_region = create_region(world, player, active_locations, LocationName.vanilla_dome_castle_region, None)
- vanilla_dome_castle = create_region(world, player, active_locations, LocationName.vanilla_dome_castle,
+ vanilla_dome_castle_tile = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_castle_tile, None)
+ vanilla_dome_castle_region = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_castle_region, None)
+ vanilla_dome_castle = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_castle,
[LocationName.vanilla_dome_castle, LocationName.vanilla_dome_koopaling])
- red_switch_palace_tile = create_region(world, player, active_locations, LocationName.red_switch_palace_tile, None)
- red_switch_palace = create_region(world, player, active_locations, LocationName.red_switch_palace,
+ red_switch_palace_tile = create_region(multiworld, player, active_locations, LocationName.red_switch_palace_tile, None)
+ red_switch_palace = create_region(multiworld, player, active_locations, LocationName.red_switch_palace,
[LocationName.red_switch_palace])
- butter_bridge_1_tile = create_region(world, player, active_locations, LocationName.butter_bridge_1_tile, None)
- butter_bridge_1_region = create_region(world, player, active_locations, LocationName.butter_bridge_1_region, None)
- butter_bridge_1_exit_1 = create_region(world, player, active_locations, LocationName.butter_bridge_1_exit_1,
+ butter_bridge_1_tile = create_region(multiworld, player, active_locations, LocationName.butter_bridge_1_tile, None)
+ butter_bridge_1_region = create_region(multiworld, player, active_locations, LocationName.butter_bridge_1_region, None)
+ butter_bridge_1_exit_1 = create_region(multiworld, player, active_locations, LocationName.butter_bridge_1_exit_1,
[LocationName.butter_bridge_1_exit_1])
- butter_bridge_2_tile = create_region(world, player, active_locations, LocationName.butter_bridge_2_tile, None)
- butter_bridge_2_region = create_region(world, player, active_locations, LocationName.butter_bridge_2_region, None)
- butter_bridge_2_exit_1 = create_region(world, player, active_locations, LocationName.butter_bridge_2_exit_1,
+ butter_bridge_2_tile = create_region(multiworld, player, active_locations, LocationName.butter_bridge_2_tile, None)
+ butter_bridge_2_region = create_region(multiworld, player, active_locations, LocationName.butter_bridge_2_region, None)
+ butter_bridge_2_exit_1 = create_region(multiworld, player, active_locations, LocationName.butter_bridge_2_exit_1,
[LocationName.butter_bridge_2_exit_1])
- cheese_bridge_tile = create_region(world, player, active_locations, LocationName.cheese_bridge_tile, None)
- cheese_bridge_region = create_region(world, player, active_locations, LocationName.cheese_bridge_region, None)
- cheese_bridge_exit_1 = create_region(world, player, active_locations, LocationName.cheese_bridge_exit_1,
+ cheese_bridge_tile = create_region(multiworld, player, active_locations, LocationName.cheese_bridge_tile, None)
+ cheese_bridge_region = create_region(multiworld, player, active_locations, LocationName.cheese_bridge_region, None)
+ cheese_bridge_exit_1 = create_region(multiworld, player, active_locations, LocationName.cheese_bridge_exit_1,
[LocationName.cheese_bridge_exit_1])
- cheese_bridge_exit_2 = create_region(world, player, active_locations, LocationName.cheese_bridge_exit_2,
+ cheese_bridge_exit_2 = create_region(multiworld, player, active_locations, LocationName.cheese_bridge_exit_2,
[LocationName.cheese_bridge_exit_2])
- cookie_mountain_tile = create_region(world, player, active_locations, LocationName.cookie_mountain_tile, None)
- cookie_mountain_region = create_region(world, player, active_locations, LocationName.cookie_mountain_region, None)
- cookie_mountain_exit_1 = create_region(world, player, active_locations, LocationName.cookie_mountain_exit_1,
+ cookie_mountain_tile = create_region(multiworld, player, active_locations, LocationName.cookie_mountain_tile, None)
+ cookie_mountain_region = create_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, None)
+ cookie_mountain_exit_1 = create_region(multiworld, player, active_locations, LocationName.cookie_mountain_exit_1,
[LocationName.cookie_mountain_exit_1])
- soda_lake_tile = create_region(world, player, active_locations, LocationName.soda_lake_tile, None)
- soda_lake_region = create_region(world, player, active_locations, LocationName.soda_lake_region, None)
- soda_lake_exit_1 = create_region(world, player, active_locations, LocationName.soda_lake_exit_1,
+ soda_lake_tile = create_region(multiworld, player, active_locations, LocationName.soda_lake_tile, None)
+ soda_lake_region = create_region(multiworld, player, active_locations, LocationName.soda_lake_region, None)
+ soda_lake_exit_1 = create_region(multiworld, player, active_locations, LocationName.soda_lake_exit_1,
[LocationName.soda_lake_exit_1])
- twin_bridges_castle_tile = create_region(world, player, active_locations, LocationName.twin_bridges_castle_tile, None)
- twin_bridges_castle_region = create_region(world, player, active_locations, LocationName.twin_bridges_castle_region, None)
- twin_bridges_castle = create_region(world, player, active_locations, LocationName.twin_bridges_castle,
+ twin_bridges_castle_tile = create_region(multiworld, player, active_locations, LocationName.twin_bridges_castle_tile, None)
+ twin_bridges_castle_region = create_region(multiworld, player, active_locations, LocationName.twin_bridges_castle_region, None)
+ twin_bridges_castle = create_region(multiworld, player, active_locations, LocationName.twin_bridges_castle,
[LocationName.twin_bridges_castle, LocationName.twin_bridges_koopaling])
- forest_of_illusion_1_tile = create_region(world, player, active_locations, LocationName.forest_of_illusion_1_tile, None)
- forest_of_illusion_1_region = create_region(world, player, active_locations, LocationName.forest_of_illusion_1_region, None)
- forest_of_illusion_1_exit_1 = create_region(world, player, active_locations, LocationName.forest_of_illusion_1_exit_1,
+ forest_of_illusion_1_tile = create_region(multiworld, player, active_locations, LocationName.forest_of_illusion_1_tile, None)
+ forest_of_illusion_1_region = create_region(multiworld, player, active_locations, LocationName.forest_of_illusion_1_region, None)
+ forest_of_illusion_1_exit_1 = create_region(multiworld, player, active_locations, LocationName.forest_of_illusion_1_exit_1,
[LocationName.forest_of_illusion_1_exit_1])
- forest_of_illusion_1_exit_2 = create_region(world, player, active_locations, LocationName.forest_of_illusion_1_exit_2,
+ forest_of_illusion_1_exit_2 = create_region(multiworld, player, active_locations, LocationName.forest_of_illusion_1_exit_2,
[LocationName.forest_of_illusion_1_exit_2])
- forest_of_illusion_2_tile = create_region(world, player, active_locations, LocationName.forest_of_illusion_2_tile, None)
- forest_of_illusion_2_region = create_region(world, player, active_locations, LocationName.forest_of_illusion_2_region, None)
- forest_of_illusion_2_exit_1 = create_region(world, player, active_locations, LocationName.forest_of_illusion_2_exit_1,
+ forest_of_illusion_2_tile = create_region(multiworld, player, active_locations, LocationName.forest_of_illusion_2_tile, None)
+ forest_of_illusion_2_region = create_region(multiworld, player, active_locations, LocationName.forest_of_illusion_2_region, None)
+ forest_of_illusion_2_exit_1 = create_region(multiworld, player, active_locations, LocationName.forest_of_illusion_2_exit_1,
[LocationName.forest_of_illusion_2_exit_1])
- forest_of_illusion_2_exit_2 = create_region(world, player, active_locations, LocationName.forest_of_illusion_2_exit_2,
+ forest_of_illusion_2_exit_2 = create_region(multiworld, player, active_locations, LocationName.forest_of_illusion_2_exit_2,
[LocationName.forest_of_illusion_2_exit_2])
- forest_of_illusion_3_tile = create_region(world, player, active_locations, LocationName.forest_of_illusion_3_tile, None)
- forest_of_illusion_3_region = create_region(world, player, active_locations, LocationName.forest_of_illusion_3_region, None)
- forest_of_illusion_3_exit_1 = create_region(world, player, active_locations, LocationName.forest_of_illusion_3_exit_1,
+ forest_of_illusion_3_tile = create_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_tile, None)
+ forest_of_illusion_3_region = create_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, None)
+ forest_of_illusion_3_exit_1 = create_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_exit_1,
[LocationName.forest_of_illusion_3_exit_1])
- forest_of_illusion_3_exit_2 = create_region(world, player, active_locations, LocationName.forest_of_illusion_3_exit_2,
+ forest_of_illusion_3_exit_2 = create_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_exit_2,
[LocationName.forest_of_illusion_3_exit_2])
- forest_of_illusion_4_tile = create_region(world, player, active_locations, LocationName.forest_of_illusion_4_tile, None)
- forest_of_illusion_4_region = create_region(world, player, active_locations, LocationName.forest_of_illusion_4_region, None)
- forest_of_illusion_4_exit_1 = create_region(world, player, active_locations, LocationName.forest_of_illusion_4_exit_1,
+ forest_of_illusion_4_tile = create_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_tile, None)
+ forest_of_illusion_4_region = create_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_region, None)
+ forest_of_illusion_4_exit_1 = create_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_exit_1,
[LocationName.forest_of_illusion_4_exit_1])
- forest_of_illusion_4_exit_2 = create_region(world, player, active_locations, LocationName.forest_of_illusion_4_exit_2,
+ forest_of_illusion_4_exit_2 = create_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_exit_2,
[LocationName.forest_of_illusion_4_exit_2])
- forest_ghost_house_tile = create_region(world, player, active_locations, LocationName.forest_ghost_house_tile, None)
- forest_ghost_house_region = create_region(world, player, active_locations, LocationName.forest_ghost_house_region, None)
- forest_ghost_house_exit_1 = create_region(world, player, active_locations, LocationName.forest_ghost_house_exit_1,
+ forest_ghost_house_tile = create_region(multiworld, player, active_locations, LocationName.forest_ghost_house_tile, None)
+ forest_ghost_house_region = create_region(multiworld, player, active_locations, LocationName.forest_ghost_house_region, None)
+ forest_ghost_house_exit_1 = create_region(multiworld, player, active_locations, LocationName.forest_ghost_house_exit_1,
[LocationName.forest_ghost_house_exit_1])
- forest_ghost_house_exit_2 = create_region(world, player, active_locations, LocationName.forest_ghost_house_exit_2,
+ forest_ghost_house_exit_2 = create_region(multiworld, player, active_locations, LocationName.forest_ghost_house_exit_2,
[LocationName.forest_ghost_house_exit_2])
- forest_secret_tile = create_region(world, player, active_locations, LocationName.forest_secret_tile, None)
- forest_secret_region = create_region(world, player, active_locations, LocationName.forest_secret_region, None)
- forest_secret_exit_1 = create_region(world, player, active_locations, LocationName.forest_secret_exit_1,
+ forest_secret_tile = create_region(multiworld, player, active_locations, LocationName.forest_secret_tile, None)
+ forest_secret_region = create_region(multiworld, player, active_locations, LocationName.forest_secret_region, None)
+ forest_secret_exit_1 = create_region(multiworld, player, active_locations, LocationName.forest_secret_exit_1,
[LocationName.forest_secret_exit_1])
- forest_fortress_tile = create_region(world, player, active_locations, LocationName.forest_fortress_tile, None)
- forest_fortress_region = create_region(world, player, active_locations, LocationName.forest_fortress_region, None)
- forest_fortress = create_region(world, player, active_locations, LocationName.forest_fortress,
+ forest_fortress_tile = create_region(multiworld, player, active_locations, LocationName.forest_fortress_tile, None)
+ forest_fortress_region = create_region(multiworld, player, active_locations, LocationName.forest_fortress_region, None)
+ forest_fortress = create_region(multiworld, player, active_locations, LocationName.forest_fortress,
[LocationName.forest_fortress, LocationName.forest_reznor])
- forest_castle_tile = create_region(world, player, active_locations, LocationName.forest_castle_tile, None)
- forest_castle_region = create_region(world, player, active_locations, LocationName.forest_castle_region, None)
- forest_castle = create_region(world, player, active_locations, LocationName.forest_castle,
+ forest_castle_tile = create_region(multiworld, player, active_locations, LocationName.forest_castle_tile, None)
+ forest_castle_region = create_region(multiworld, player, active_locations, LocationName.forest_castle_region, None)
+ forest_castle = create_region(multiworld, player, active_locations, LocationName.forest_castle,
[LocationName.forest_castle, LocationName.forest_koopaling])
- blue_switch_palace_tile = create_region(world, player, active_locations, LocationName.blue_switch_palace_tile, None)
- blue_switch_palace = create_region(world, player, active_locations, LocationName.blue_switch_palace,
+ blue_switch_palace_tile = create_region(multiworld, player, active_locations, LocationName.blue_switch_palace_tile, None)
+ blue_switch_palace = create_region(multiworld, player, active_locations, LocationName.blue_switch_palace,
[LocationName.blue_switch_palace])
- chocolate_island_1_tile = create_region(world, player, active_locations, LocationName.chocolate_island_1_tile, None)
- chocolate_island_1_region = create_region(world, player, active_locations, LocationName.chocolate_island_1_region, None)
- chocolate_island_1_exit_1 = create_region(world, player, active_locations, LocationName.chocolate_island_1_exit_1,
+ chocolate_island_1_tile = create_region(multiworld, player, active_locations, LocationName.chocolate_island_1_tile, None)
+ chocolate_island_1_region = create_region(multiworld, player, active_locations, LocationName.chocolate_island_1_region, None)
+ chocolate_island_1_exit_1 = create_region(multiworld, player, active_locations, LocationName.chocolate_island_1_exit_1,
[LocationName.chocolate_island_1_exit_1])
- chocolate_island_2_tile = create_region(world, player, active_locations, LocationName.chocolate_island_2_tile, None)
- chocolate_island_2_region = create_region(world, player, active_locations, LocationName.chocolate_island_2_region, None)
- chocolate_island_2_exit_1 = create_region(world, player, active_locations, LocationName.chocolate_island_2_exit_1,
+ chocolate_island_2_tile = create_region(multiworld, player, active_locations, LocationName.chocolate_island_2_tile, None)
+ chocolate_island_2_region = create_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, None)
+ chocolate_island_2_exit_1 = create_region(multiworld, player, active_locations, LocationName.chocolate_island_2_exit_1,
[LocationName.chocolate_island_2_exit_1])
- chocolate_island_2_exit_2 = create_region(world, player, active_locations, LocationName.chocolate_island_2_exit_2,
+ chocolate_island_2_exit_2 = create_region(multiworld, player, active_locations, LocationName.chocolate_island_2_exit_2,
[LocationName.chocolate_island_2_exit_2])
- chocolate_island_3_tile = create_region(world, player, active_locations, LocationName.chocolate_island_3_tile, None)
- chocolate_island_3_region = create_region(world, player, active_locations, LocationName.chocolate_island_3_region, None)
- chocolate_island_3_exit_1 = create_region(world, player, active_locations, LocationName.chocolate_island_3_exit_1,
+ chocolate_island_3_tile = create_region(multiworld, player, active_locations, LocationName.chocolate_island_3_tile, None)
+ chocolate_island_3_region = create_region(multiworld, player, active_locations, LocationName.chocolate_island_3_region, None)
+ chocolate_island_3_exit_1 = create_region(multiworld, player, active_locations, LocationName.chocolate_island_3_exit_1,
[LocationName.chocolate_island_3_exit_1])
- chocolate_island_3_exit_2 = create_region(world, player, active_locations, LocationName.chocolate_island_3_exit_2,
+ chocolate_island_3_exit_2 = create_region(multiworld, player, active_locations, LocationName.chocolate_island_3_exit_2,
[LocationName.chocolate_island_3_exit_2])
- chocolate_island_4_tile = create_region(world, player, active_locations, LocationName.chocolate_island_4_tile, None)
- chocolate_island_4_region = create_region(world, player, active_locations, LocationName.chocolate_island_4_region, None)
- chocolate_island_4_exit_1 = create_region(world, player, active_locations, LocationName.chocolate_island_4_exit_1,
+ chocolate_island_4_tile = create_region(multiworld, player, active_locations, LocationName.chocolate_island_4_tile, None)
+ chocolate_island_4_region = create_region(multiworld, player, active_locations, LocationName.chocolate_island_4_region, None)
+ chocolate_island_4_exit_1 = create_region(multiworld, player, active_locations, LocationName.chocolate_island_4_exit_1,
[LocationName.chocolate_island_4_exit_1])
- chocolate_island_5_tile = create_region(world, player, active_locations, LocationName.chocolate_island_5_tile, None)
- chocolate_island_5_region = create_region(world, player, active_locations, LocationName.chocolate_island_5_region, None)
- chocolate_island_5_exit_1 = create_region(world, player, active_locations, LocationName.chocolate_island_5_exit_1,
+ chocolate_island_5_tile = create_region(multiworld, player, active_locations, LocationName.chocolate_island_5_tile, None)
+ chocolate_island_5_region = create_region(multiworld, player, active_locations, LocationName.chocolate_island_5_region, None)
+ chocolate_island_5_exit_1 = create_region(multiworld, player, active_locations, LocationName.chocolate_island_5_exit_1,
[LocationName.chocolate_island_5_exit_1])
- chocolate_ghost_house_tile = create_region(world, player, active_locations, LocationName.chocolate_ghost_house_tile, None)
- chocolate_ghost_house_region = create_region(world, player, active_locations, LocationName.chocolate_ghost_house_region, None)
- chocolate_ghost_house_exit_1 = create_region(world, player, active_locations, LocationName.chocolate_ghost_house_exit_1,
+ chocolate_ghost_house_tile = create_region(multiworld, player, active_locations, LocationName.chocolate_ghost_house_tile, None)
+ chocolate_ghost_house_region = create_region(multiworld, player, active_locations, LocationName.chocolate_ghost_house_region, None)
+ chocolate_ghost_house_exit_1 = create_region(multiworld, player, active_locations, LocationName.chocolate_ghost_house_exit_1,
[LocationName.chocolate_ghost_house_exit_1])
- chocolate_secret_tile = create_region(world, player, active_locations, LocationName.chocolate_secret_tile, None)
- chocolate_secret_region = create_region(world, player, active_locations, LocationName.chocolate_secret_region, None)
- chocolate_secret_exit_1 = create_region(world, player, active_locations, LocationName.chocolate_secret_exit_1,
+ chocolate_secret_tile = create_region(multiworld, player, active_locations, LocationName.chocolate_secret_tile, None)
+ chocolate_secret_region = create_region(multiworld, player, active_locations, LocationName.chocolate_secret_region, None)
+ chocolate_secret_exit_1 = create_region(multiworld, player, active_locations, LocationName.chocolate_secret_exit_1,
[LocationName.chocolate_secret_exit_1])
- chocolate_fortress_tile = create_region(world, player, active_locations, LocationName.chocolate_fortress_tile, None)
- chocolate_fortress_region = create_region(world, player, active_locations, LocationName.chocolate_fortress_region, None)
- chocolate_fortress = create_region(world, player, active_locations, LocationName.chocolate_fortress,
+ chocolate_fortress_tile = create_region(multiworld, player, active_locations, LocationName.chocolate_fortress_tile, None)
+ chocolate_fortress_region = create_region(multiworld, player, active_locations, LocationName.chocolate_fortress_region, None)
+ chocolate_fortress = create_region(multiworld, player, active_locations, LocationName.chocolate_fortress,
[LocationName.chocolate_fortress, LocationName.chocolate_reznor])
- chocolate_castle_tile = create_region(world, player, active_locations, LocationName.chocolate_castle_tile, None)
- chocolate_castle_region = create_region(world, player, active_locations, LocationName.chocolate_castle_region, None)
- chocolate_castle = create_region(world, player, active_locations, LocationName.chocolate_castle,
+ chocolate_castle_tile = create_region(multiworld, player, active_locations, LocationName.chocolate_castle_tile, None)
+ chocolate_castle_region = create_region(multiworld, player, active_locations, LocationName.chocolate_castle_region, None)
+ chocolate_castle = create_region(multiworld, player, active_locations, LocationName.chocolate_castle,
[LocationName.chocolate_castle, LocationName.chocolate_koopaling])
- sunken_ghost_ship_tile = create_region(world, player, active_locations, LocationName.sunken_ghost_ship_tile, None)
- sunken_ghost_ship_region = create_region(world, player, active_locations, LocationName.sunken_ghost_ship_region, None)
- sunken_ghost_ship = create_region(world, player, active_locations, LocationName.sunken_ghost_ship,
+ sunken_ghost_ship_tile = create_region(multiworld, player, active_locations, LocationName.sunken_ghost_ship_tile, None)
+ sunken_ghost_ship_region = create_region(multiworld, player, active_locations, LocationName.sunken_ghost_ship_region, None)
+ sunken_ghost_ship = create_region(multiworld, player, active_locations, LocationName.sunken_ghost_ship,
[LocationName.sunken_ghost_ship])
- valley_of_bowser_1_tile = create_region(world, player, active_locations, LocationName.valley_of_bowser_1_tile, None)
- valley_of_bowser_1_region = create_region(world, player, active_locations, LocationName.valley_of_bowser_1_region, None)
- valley_of_bowser_1_exit_1 = create_region(world, player, active_locations, LocationName.valley_of_bowser_1_exit_1,
+ valley_of_bowser_1_tile = create_region(multiworld, player, active_locations, LocationName.valley_of_bowser_1_tile, None)
+ valley_of_bowser_1_region = create_region(multiworld, player, active_locations, LocationName.valley_of_bowser_1_region, None)
+ valley_of_bowser_1_exit_1 = create_region(multiworld, player, active_locations, LocationName.valley_of_bowser_1_exit_1,
[LocationName.valley_of_bowser_1_exit_1])
- valley_of_bowser_2_tile = create_region(world, player, active_locations, LocationName.valley_of_bowser_2_tile, None)
- valley_of_bowser_2_region = create_region(world, player, active_locations, LocationName.valley_of_bowser_2_region, None)
- valley_of_bowser_2_exit_1 = create_region(world, player, active_locations, LocationName.valley_of_bowser_2_exit_1,
+ valley_of_bowser_2_tile = create_region(multiworld, player, active_locations, LocationName.valley_of_bowser_2_tile, None)
+ valley_of_bowser_2_region = create_region(multiworld, player, active_locations, LocationName.valley_of_bowser_2_region, None)
+ valley_of_bowser_2_exit_1 = create_region(multiworld, player, active_locations, LocationName.valley_of_bowser_2_exit_1,
[LocationName.valley_of_bowser_2_exit_1])
- valley_of_bowser_2_exit_2 = create_region(world, player, active_locations, LocationName.valley_of_bowser_2_exit_2,
+ valley_of_bowser_2_exit_2 = create_region(multiworld, player, active_locations, LocationName.valley_of_bowser_2_exit_2,
[LocationName.valley_of_bowser_2_exit_2])
- valley_of_bowser_3_tile = create_region(world, player, active_locations, LocationName.valley_of_bowser_3_tile, None)
- valley_of_bowser_3_region = create_region(world, player, active_locations, LocationName.valley_of_bowser_3_region, None)
- valley_of_bowser_3_exit_1 = create_region(world, player, active_locations, LocationName.valley_of_bowser_3_exit_1,
+ valley_of_bowser_3_tile = create_region(multiworld, player, active_locations, LocationName.valley_of_bowser_3_tile, None)
+ valley_of_bowser_3_region = create_region(multiworld, player, active_locations, LocationName.valley_of_bowser_3_region, None)
+ valley_of_bowser_3_exit_1 = create_region(multiworld, player, active_locations, LocationName.valley_of_bowser_3_exit_1,
[LocationName.valley_of_bowser_3_exit_1])
- valley_of_bowser_4_tile = create_region(world, player, active_locations, LocationName.valley_of_bowser_4_tile, None)
- valley_of_bowser_4_region = create_region(world, player, active_locations, LocationName.valley_of_bowser_4_region, None)
- valley_of_bowser_4_exit_1 = create_region(world, player, active_locations, LocationName.valley_of_bowser_4_exit_1,
+ valley_of_bowser_4_tile = create_region(multiworld, player, active_locations, LocationName.valley_of_bowser_4_tile, None)
+ valley_of_bowser_4_region = create_region(multiworld, player, active_locations, LocationName.valley_of_bowser_4_region, None)
+ valley_of_bowser_4_exit_1 = create_region(multiworld, player, active_locations, LocationName.valley_of_bowser_4_exit_1,
[LocationName.valley_of_bowser_4_exit_1])
- valley_of_bowser_4_exit_2 = create_region(world, player, active_locations, LocationName.valley_of_bowser_4_exit_2,
+ valley_of_bowser_4_exit_2 = create_region(multiworld, player, active_locations, LocationName.valley_of_bowser_4_exit_2,
[LocationName.valley_of_bowser_4_exit_2])
- valley_ghost_house_tile = create_region(world, player, active_locations, LocationName.valley_ghost_house_tile, None)
- valley_ghost_house_region = create_region(world, player, active_locations, LocationName.valley_ghost_house_region, None)
- valley_ghost_house_exit_1 = create_region(world, player, active_locations, LocationName.valley_ghost_house_exit_1,
+ valley_ghost_house_tile = create_region(multiworld, player, active_locations, LocationName.valley_ghost_house_tile, None)
+ valley_ghost_house_region = create_region(multiworld, player, active_locations, LocationName.valley_ghost_house_region, None)
+ valley_ghost_house_exit_1 = create_region(multiworld, player, active_locations, LocationName.valley_ghost_house_exit_1,
[LocationName.valley_ghost_house_exit_1])
- valley_ghost_house_exit_2 = create_region(world, player, active_locations, LocationName.valley_ghost_house_exit_2,
+ valley_ghost_house_exit_2 = create_region(multiworld, player, active_locations, LocationName.valley_ghost_house_exit_2,
[LocationName.valley_ghost_house_exit_2])
- valley_fortress_tile = create_region(world, player, active_locations, LocationName.valley_fortress_tile, None)
- valley_fortress_region = create_region(world, player, active_locations, LocationName.valley_fortress_region, None)
- valley_fortress = create_region(world, player, active_locations, LocationName.valley_fortress,
+ valley_fortress_tile = create_region(multiworld, player, active_locations, LocationName.valley_fortress_tile, None)
+ valley_fortress_region = create_region(multiworld, player, active_locations, LocationName.valley_fortress_region, None)
+ valley_fortress = create_region(multiworld, player, active_locations, LocationName.valley_fortress,
[LocationName.valley_fortress, LocationName.valley_reznor])
- valley_castle_tile = create_region(world, player, active_locations, LocationName.valley_castle_tile, None)
- valley_castle_region = create_region(world, player, active_locations, LocationName.valley_castle_region, None)
- valley_castle = create_region(world, player, active_locations, LocationName.valley_castle,
+ valley_castle_tile = create_region(multiworld, player, active_locations, LocationName.valley_castle_tile, None)
+ valley_castle_region = create_region(multiworld, player, active_locations, LocationName.valley_castle_region, None)
+ valley_castle = create_region(multiworld, player, active_locations, LocationName.valley_castle,
[LocationName.valley_castle, LocationName.valley_koopaling])
- front_door_tile = create_region(world, player, active_locations, LocationName.front_door_tile, None)
- front_door_region = create_region(world, player, active_locations, LocationName.front_door, None)
- back_door_tile = create_region(world, player, active_locations, LocationName.back_door_tile, None)
- back_door_region = create_region(world, player, active_locations, LocationName.back_door, None)
+ front_door_tile = create_region(multiworld, player, active_locations, LocationName.front_door_tile, None)
+ front_door_region = create_region(multiworld, player, active_locations, LocationName.front_door, None)
+ back_door_tile = create_region(multiworld, player, active_locations, LocationName.back_door_tile, None)
+ back_door_region = create_region(multiworld, player, active_locations, LocationName.back_door, None)
bowser_region_locations = []
- if world.goal[player] == "bowser":
+ if world.options.goal == "bowser":
bowser_region_locations += [LocationName.bowser]
- bowser_region = create_region(world, player, active_locations, LocationName.bowser_region, bowser_region_locations)
-
-
- donut_plains_star_road = create_region(world, player, active_locations, LocationName.donut_plains_star_road, None)
- vanilla_dome_star_road = create_region(world, player, active_locations, LocationName.vanilla_dome_star_road, None)
- twin_bridges_star_road = create_region(world, player, active_locations, LocationName.twin_bridges_star_road, None)
- forest_star_road = create_region(world, player, active_locations, LocationName.forest_star_road, None)
- valley_star_road = create_region(world, player, active_locations, LocationName.valley_star_road, None)
- star_road_donut = create_region(world, player, active_locations, LocationName.star_road_donut, None)
- star_road_vanilla = create_region(world, player, active_locations, LocationName.star_road_vanilla, None)
- star_road_twin_bridges = create_region(world, player, active_locations, LocationName.star_road_twin_bridges, None)
- star_road_forest = create_region(world, player, active_locations, LocationName.star_road_forest, None)
- star_road_valley = create_region(world, player, active_locations, LocationName.star_road_valley, None)
- star_road_special = create_region(world, player, active_locations, LocationName.star_road_special, None)
- special_star_road = create_region(world, player, active_locations, LocationName.special_star_road, None)
-
- star_road_1_tile = create_region(world, player, active_locations, LocationName.star_road_1_tile, None)
- star_road_1_region = create_region(world, player, active_locations, LocationName.star_road_1_region, None)
- star_road_1_exit_1 = create_region(world, player, active_locations, LocationName.star_road_1_exit_1,
+ bowser_region = create_region(multiworld, player, active_locations, LocationName.bowser_region, bowser_region_locations)
+
+
+ donut_plains_star_road = create_region(multiworld, player, active_locations, LocationName.donut_plains_star_road, None)
+ vanilla_dome_star_road = create_region(multiworld, player, active_locations, LocationName.vanilla_dome_star_road, None)
+ twin_bridges_star_road = create_region(multiworld, player, active_locations, LocationName.twin_bridges_star_road, None)
+ forest_star_road = create_region(multiworld, player, active_locations, LocationName.forest_star_road, None)
+ valley_star_road = create_region(multiworld, player, active_locations, LocationName.valley_star_road, None)
+ star_road_donut = create_region(multiworld, player, active_locations, LocationName.star_road_donut, None)
+ star_road_vanilla = create_region(multiworld, player, active_locations, LocationName.star_road_vanilla, None)
+ star_road_twin_bridges = create_region(multiworld, player, active_locations, LocationName.star_road_twin_bridges, None)
+ star_road_forest = create_region(multiworld, player, active_locations, LocationName.star_road_forest, None)
+ star_road_valley = create_region(multiworld, player, active_locations, LocationName.star_road_valley, None)
+ star_road_special = create_region(multiworld, player, active_locations, LocationName.star_road_special, None)
+ special_star_road = create_region(multiworld, player, active_locations, LocationName.special_star_road, None)
+
+ star_road_1_tile = create_region(multiworld, player, active_locations, LocationName.star_road_1_tile, None)
+ star_road_1_region = create_region(multiworld, player, active_locations, LocationName.star_road_1_region, None)
+ star_road_1_exit_1 = create_region(multiworld, player, active_locations, LocationName.star_road_1_exit_1,
[LocationName.star_road_1_exit_1])
- star_road_1_exit_2 = create_region(world, player, active_locations, LocationName.star_road_1_exit_2,
+ star_road_1_exit_2 = create_region(multiworld, player, active_locations, LocationName.star_road_1_exit_2,
[LocationName.star_road_1_exit_2])
- star_road_2_tile = create_region(world, player, active_locations, LocationName.star_road_2_tile, None)
- star_road_2_region = create_region(world, player, active_locations, LocationName.star_road_2_region, None)
- star_road_2_exit_1 = create_region(world, player, active_locations, LocationName.star_road_2_exit_1,
+ star_road_2_tile = create_region(multiworld, player, active_locations, LocationName.star_road_2_tile, None)
+ star_road_2_region = create_region(multiworld, player, active_locations, LocationName.star_road_2_region, None)
+ star_road_2_exit_1 = create_region(multiworld, player, active_locations, LocationName.star_road_2_exit_1,
[LocationName.star_road_2_exit_1])
- star_road_2_exit_2 = create_region(world, player, active_locations, LocationName.star_road_2_exit_2,
+ star_road_2_exit_2 = create_region(multiworld, player, active_locations, LocationName.star_road_2_exit_2,
[LocationName.star_road_2_exit_2])
- star_road_3_tile = create_region(world, player, active_locations, LocationName.star_road_3_tile, None)
- star_road_3_region = create_region(world, player, active_locations, LocationName.star_road_3_region, None)
- star_road_3_exit_1 = create_region(world, player, active_locations, LocationName.star_road_3_exit_1,
+ star_road_3_tile = create_region(multiworld, player, active_locations, LocationName.star_road_3_tile, None)
+ star_road_3_region = create_region(multiworld, player, active_locations, LocationName.star_road_3_region, None)
+ star_road_3_exit_1 = create_region(multiworld, player, active_locations, LocationName.star_road_3_exit_1,
[LocationName.star_road_3_exit_1])
- star_road_3_exit_2 = create_region(world, player, active_locations, LocationName.star_road_3_exit_2,
+ star_road_3_exit_2 = create_region(multiworld, player, active_locations, LocationName.star_road_3_exit_2,
[LocationName.star_road_3_exit_2])
- star_road_4_tile = create_region(world, player, active_locations, LocationName.star_road_4_tile, None)
- star_road_4_region = create_region(world, player, active_locations, LocationName.star_road_4_region, None)
- star_road_4_exit_1 = create_region(world, player, active_locations, LocationName.star_road_4_exit_1,
+ star_road_4_tile = create_region(multiworld, player, active_locations, LocationName.star_road_4_tile, None)
+ star_road_4_region = create_region(multiworld, player, active_locations, LocationName.star_road_4_region, None)
+ star_road_4_exit_1 = create_region(multiworld, player, active_locations, LocationName.star_road_4_exit_1,
[LocationName.star_road_4_exit_1])
- star_road_4_exit_2 = create_region(world, player, active_locations, LocationName.star_road_4_exit_2,
+ star_road_4_exit_2 = create_region(multiworld, player, active_locations, LocationName.star_road_4_exit_2,
[LocationName.star_road_4_exit_2])
- star_road_5_tile = create_region(world, player, active_locations, LocationName.star_road_5_tile, None)
- star_road_5_region = create_region(world, player, active_locations, LocationName.star_road_5_region, None)
- star_road_5_exit_1 = create_region(world, player, active_locations, LocationName.star_road_5_exit_1,
+ star_road_5_tile = create_region(multiworld, player, active_locations, LocationName.star_road_5_tile, None)
+ star_road_5_region = create_region(multiworld, player, active_locations, LocationName.star_road_5_region, None)
+ star_road_5_exit_1 = create_region(multiworld, player, active_locations, LocationName.star_road_5_exit_1,
[LocationName.star_road_5_exit_1])
- star_road_5_exit_2 = create_region(world, player, active_locations, LocationName.star_road_5_exit_2,
+ star_road_5_exit_2 = create_region(multiworld, player, active_locations, LocationName.star_road_5_exit_2,
[LocationName.star_road_5_exit_2])
- special_zone_1_tile = create_region(world, player, active_locations, LocationName.special_zone_1_tile, None)
- special_zone_1_region = create_region(world, player, active_locations, LocationName.special_zone_1_region, None)
- special_zone_1_exit_1 = create_region(world, player, active_locations, LocationName.special_zone_1_exit_1,
+ special_zone_1_tile = create_region(multiworld, player, active_locations, LocationName.special_zone_1_tile, None)
+ special_zone_1_region = create_region(multiworld, player, active_locations, LocationName.special_zone_1_region, None)
+ special_zone_1_exit_1 = create_region(multiworld, player, active_locations, LocationName.special_zone_1_exit_1,
[LocationName.special_zone_1_exit_1])
- special_zone_2_tile = create_region(world, player, active_locations, LocationName.special_zone_2_tile, None)
- special_zone_2_region = create_region(world, player, active_locations, LocationName.special_zone_2_region, None)
- special_zone_2_exit_1 = create_region(world, player, active_locations, LocationName.special_zone_2_exit_1,
+ special_zone_2_tile = create_region(multiworld, player, active_locations, LocationName.special_zone_2_tile, None)
+ special_zone_2_region = create_region(multiworld, player, active_locations, LocationName.special_zone_2_region, None)
+ special_zone_2_exit_1 = create_region(multiworld, player, active_locations, LocationName.special_zone_2_exit_1,
[LocationName.special_zone_2_exit_1])
- special_zone_3_tile = create_region(world, player, active_locations, LocationName.special_zone_3_tile, None)
- special_zone_3_region = create_region(world, player, active_locations, LocationName.special_zone_3_region, None)
- special_zone_3_exit_1 = create_region(world, player, active_locations, LocationName.special_zone_3_exit_1,
+ special_zone_3_tile = create_region(multiworld, player, active_locations, LocationName.special_zone_3_tile, None)
+ special_zone_3_region = create_region(multiworld, player, active_locations, LocationName.special_zone_3_region, None)
+ special_zone_3_exit_1 = create_region(multiworld, player, active_locations, LocationName.special_zone_3_exit_1,
[LocationName.special_zone_3_exit_1])
- special_zone_4_tile = create_region(world, player, active_locations, LocationName.special_zone_4_tile, None)
- special_zone_4_region = create_region(world, player, active_locations, LocationName.special_zone_4_region, None)
- special_zone_4_exit_1 = create_region(world, player, active_locations, LocationName.special_zone_4_exit_1,
+ special_zone_4_tile = create_region(multiworld, player, active_locations, LocationName.special_zone_4_tile, None)
+ special_zone_4_region = create_region(multiworld, player, active_locations, LocationName.special_zone_4_region, None)
+ special_zone_4_exit_1 = create_region(multiworld, player, active_locations, LocationName.special_zone_4_exit_1,
[LocationName.special_zone_4_exit_1])
- special_zone_5_tile = create_region(world, player, active_locations, LocationName.special_zone_5_tile, None)
- special_zone_5_region = create_region(world, player, active_locations, LocationName.special_zone_5_region, None)
- special_zone_5_exit_1 = create_region(world, player, active_locations, LocationName.special_zone_5_exit_1,
+ special_zone_5_tile = create_region(multiworld, player, active_locations, LocationName.special_zone_5_tile, None)
+ special_zone_5_region = create_region(multiworld, player, active_locations, LocationName.special_zone_5_region, None)
+ special_zone_5_exit_1 = create_region(multiworld, player, active_locations, LocationName.special_zone_5_exit_1,
[LocationName.special_zone_5_exit_1])
- special_zone_6_tile = create_region(world, player, active_locations, LocationName.special_zone_6_tile, None)
- special_zone_6_region = create_region(world, player, active_locations, LocationName.special_zone_6_region, None)
- special_zone_6_exit_1 = create_region(world, player, active_locations, LocationName.special_zone_6_exit_1,
+ special_zone_6_tile = create_region(multiworld, player, active_locations, LocationName.special_zone_6_tile, None)
+ special_zone_6_region = create_region(multiworld, player, active_locations, LocationName.special_zone_6_region, None)
+ special_zone_6_exit_1 = create_region(multiworld, player, active_locations, LocationName.special_zone_6_exit_1,
[LocationName.special_zone_6_exit_1])
- special_zone_7_tile = create_region(world, player, active_locations, LocationName.special_zone_7_tile, None)
- special_zone_7_region = create_region(world, player, active_locations, LocationName.special_zone_7_region, None)
- special_zone_7_exit_1 = create_region(world, player, active_locations, LocationName.special_zone_7_exit_1,
+ special_zone_7_tile = create_region(multiworld, player, active_locations, LocationName.special_zone_7_tile, None)
+ special_zone_7_region = create_region(multiworld, player, active_locations, LocationName.special_zone_7_region, None)
+ special_zone_7_exit_1 = create_region(multiworld, player, active_locations, LocationName.special_zone_7_exit_1,
[LocationName.special_zone_7_exit_1])
- special_zone_8_tile = create_region(world, player, active_locations, LocationName.special_zone_8_tile, None)
- special_zone_8_region = create_region(world, player, active_locations, LocationName.special_zone_8_region, None)
- special_zone_8_exit_1 = create_region(world, player, active_locations, LocationName.special_zone_8_exit_1,
+ special_zone_8_tile = create_region(multiworld, player, active_locations, LocationName.special_zone_8_tile, None)
+ special_zone_8_region = create_region(multiworld, player, active_locations, LocationName.special_zone_8_region, None)
+ special_zone_8_exit_1 = create_region(multiworld, player, active_locations, LocationName.special_zone_8_exit_1,
[LocationName.special_zone_8_exit_1])
- special_complete = create_region(world, player, active_locations, LocationName.special_complete, None)
+ special_complete = create_region(multiworld, player, active_locations, LocationName.special_complete, None)
# Set up the regions correctly.
- world.regions += [
+ multiworld.regions += [
menu_region,
yoshis_island_region,
yoshis_house_tile,
@@ -725,323 +728,1327 @@ def create_regions(world, player: int, active_locations):
]
- if world.dragon_coin_checks[player]:
- add_location_to_region(world, player, active_locations, LocationName.yoshis_island_1_region, LocationName.yoshis_island_1_dragon,
+ if world.options.dragon_coin_checks:
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_1_region, LocationName.yoshis_island_1_dragon,
lambda state: (state.has(ItemName.mario_spin_jump, player) and
state.has(ItemName.progressive_powerup, player, 1)))
- add_location_to_region(world, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_dragon,
lambda state: (state.has(ItemName.yoshi_activate, player) or
state.has(ItemName.mario_climb, player)))
- add_location_to_region(world, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_dragon,
lambda state: state.has(ItemName.p_switch, player))
- add_location_to_region(world, player, active_locations, LocationName.yoshis_island_4_region, LocationName.yoshis_island_4_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_4_region, LocationName.yoshis_island_4_dragon,
lambda state: (state.has(ItemName.yoshi_activate, player) or
state.has(ItemName.mario_swim, player) or
(state.has(ItemName.mario_carry, player) and state.has(ItemName.p_switch, player))))
- add_location_to_region(world, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_dragon,
lambda state: (state.has(ItemName.mario_climb, player) or
state.has(ItemName.yoshi_activate, player) or
(state.has(ItemName.progressive_powerup, player, 3) and state.has(ItemName.mario_run, player))))
- add_location_to_region(world, player, active_locations, LocationName.donut_plains_2_region, LocationName.donut_plains_2_dragon)
- add_location_to_region(world, player, active_locations, LocationName.donut_plains_3_region, LocationName.donut_plains_3_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_2_region, LocationName.donut_plains_2_dragon)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_3_region, LocationName.donut_plains_3_dragon,
lambda state: ((state.has(ItemName.mario_spin_jump, player) and state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_climb, player) or
state.has(ItemName.yoshi_activate, player) or
(state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))))
- add_location_to_region(world, player, active_locations, LocationName.donut_plains_4_region, LocationName.donut_plains_4_dragon)
- add_location_to_region(world, player, active_locations, LocationName.donut_secret_1_region, LocationName.donut_secret_1_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_4_region, LocationName.donut_plains_4_dragon)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_1_region, LocationName.donut_secret_1_dragon,
lambda state: state.has(ItemName.mario_swim, player))
- add_location_to_region(world, player, active_locations, LocationName.donut_secret_2_region, LocationName.donut_secret_2_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_2_region, LocationName.donut_secret_2_dragon,
lambda state: (state.has(ItemName.mario_climb, player) or state.has(ItemName.yoshi_activate, player)))
- add_location_to_region(world, player, active_locations, LocationName.vanilla_dome_1_region, LocationName.vanilla_dome_1_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_1_region, LocationName.vanilla_dome_1_dragon,
lambda state: (state.has(ItemName.mario_carry, player) and
state.has(ItemName.mario_run, player) and
(state.has(ItemName.super_star_active, player) or
state.has(ItemName.progressive_powerup, player, 1))))
- add_location_to_region(world, player, active_locations, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_dragon,
lambda state: (state.has(ItemName.mario_swim, player) and
state.has(ItemName.p_switch, player) and
(state.has(ItemName.mario_climb, player) or state.has(ItemName.yoshi_activate, player))))
- add_location_to_region(world, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_dragon)
- add_location_to_region(world, player, active_locations, LocationName.vanilla_dome_4_region, LocationName.vanilla_dome_4_dragon)
- add_location_to_region(world, player, active_locations, LocationName.vanilla_secret_1_region, LocationName.vanilla_secret_1_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_dragon)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_4_region, LocationName.vanilla_dome_4_dragon)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_1_region, LocationName.vanilla_secret_1_dragon,
lambda state: (state.has(ItemName.mario_climb, player) and
state.has(ItemName.mario_carry, player)))
- add_location_to_region(world, player, active_locations, LocationName.vanilla_secret_2_region, LocationName.vanilla_secret_2_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_2_region, LocationName.vanilla_secret_2_dragon,
lambda state: (state.has(ItemName.mario_run, player) and
state.has(ItemName.progressive_powerup, player, 3)))
- add_location_to_region(world, player, active_locations, LocationName.vanilla_secret_3_region, LocationName.vanilla_secret_3_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_3_region, LocationName.vanilla_secret_3_dragon,
lambda state: state.has(ItemName.mario_swim, player))
- add_location_to_region(world, player, active_locations, LocationName.vanilla_ghost_house_region, LocationName.vanilla_ghost_house_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_ghost_house_region, LocationName.vanilla_ghost_house_dragon,
lambda state: state.has(ItemName.mario_climb, player))
- add_location_to_region(world, player, active_locations, LocationName.butter_bridge_1_region, LocationName.butter_bridge_1_dragon)
- add_location_to_region(world, player, active_locations, LocationName.butter_bridge_2_region, LocationName.butter_bridge_2_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.butter_bridge_1_region, LocationName.butter_bridge_1_dragon)
+ add_location_to_region(multiworld, player, active_locations, LocationName.butter_bridge_2_region, LocationName.butter_bridge_2_dragon,
lambda state: (state.has(ItemName.mario_run, player) and
state.has(ItemName.progressive_powerup, player, 3)))
- add_location_to_region(world, player, active_locations, LocationName.cheese_bridge_region, LocationName.cheese_bridge_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.cheese_bridge_region, LocationName.cheese_bridge_dragon,
lambda state: (state.has(ItemName.yoshi_activate, player) or
state.has(ItemName.mario_climb, player)))
- add_location_to_region(world, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_dragon,
lambda state: (state.has(ItemName.yoshi_activate, player) or
state.has(ItemName.mario_climb, player)))
- add_location_to_region(world, player, active_locations, LocationName.soda_lake_region, LocationName.soda_lake_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.soda_lake_region, LocationName.soda_lake_dragon,
lambda state: state.has(ItemName.mario_swim, player))
- add_location_to_region(world, player, active_locations, LocationName.forest_of_illusion_2_region, LocationName.forest_of_illusion_2_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_2_region, LocationName.forest_of_illusion_2_dragon,
lambda state: state.has(ItemName.mario_swim, player))
- add_location_to_region(world, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_dragon,
lambda state: (state.has(ItemName.yoshi_activate, player) or
state.has(ItemName.mario_carry, player)))
- add_location_to_region(world, player, active_locations, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_dragon,
lambda state: (state.has(ItemName.yoshi_activate, player) or
state.has(ItemName.mario_carry, player) or
state.has(ItemName.p_switch, player) or
state.has(ItemName.progressive_powerup, player, 2)))
- add_location_to_region(world, player, active_locations, LocationName.forest_ghost_house_region, LocationName.forest_ghost_house_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_ghost_house_region, LocationName.forest_ghost_house_dragon,
lambda state: state.has(ItemName.p_switch, player))
- add_location_to_region(world, player, active_locations, LocationName.forest_secret_region, LocationName.forest_secret_dragon)
- add_location_to_region(world, player, active_locations, LocationName.forest_castle_region, LocationName.forest_castle_dragon)
- add_location_to_region(world, player, active_locations, LocationName.chocolate_island_1_region, LocationName.chocolate_island_1_dragon,
- lambda state: state.has(ItemName.mario_swim, player))
- add_location_to_region(world, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_secret_region, LocationName.forest_secret_dragon)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_castle_region, LocationName.forest_castle_dragon)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_1_region, LocationName.chocolate_island_1_dragon,
+ lambda state: state.has(ItemName.p_switch, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_dragon,
lambda state: (state.has(ItemName.blue_switch_palace, player) and
(state.has(ItemName.p_switch, player) or
state.has(ItemName.green_switch_palace, player) or
(state.has(ItemName.yellow_switch_palace, player) or state.has(ItemName.red_switch_palace, player)))))
- add_location_to_region(world, player, active_locations, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_dragon)
- add_location_to_region(world, player, active_locations, LocationName.chocolate_island_4_region, LocationName.chocolate_island_4_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_dragon)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_4_region, LocationName.chocolate_island_4_dragon,
lambda state: (state.has(ItemName.p_switch, player) and
state.has(ItemName.progressive_powerup, player, 3)))
- add_location_to_region(world, player, active_locations, LocationName.chocolate_island_5_region, LocationName.chocolate_island_5_dragon,
- lambda state: (state.has(ItemName.mario_swim, player) or
- (state.has(ItemName.mario_carry, player) and state.has(ItemName.p_switch, player))))
- add_location_to_region(world, player, active_locations, LocationName.sunken_ghost_ship_region, LocationName.sunken_ghost_ship_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_5_region, LocationName.chocolate_island_5_dragon,
+ lambda state: (state.has(ItemName.mario_carry, player) and state.has(ItemName.p_switch, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.sunken_ghost_ship_region, LocationName.sunken_ghost_ship_dragon,
lambda state: (state.has(ItemName.mario_swim, player) and
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(multiworld, player, active_locations, LocationName.valley_of_bowser_1_region, LocationName.valley_of_bowser_1_dragon)
+ add_location_to_region(multiworld, 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,
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_3_region, LocationName.valley_of_bowser_3_dragon)
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_ghost_house_region, LocationName.valley_ghost_house_dragon,
lambda state: state.has(ItemName.p_switch, player))
- add_location_to_region(world, player, active_locations, LocationName.valley_castle_region, LocationName.valley_castle_dragon)
- add_location_to_region(world, player, active_locations, LocationName.star_road_1_region, LocationName.star_road_1_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_castle_region, LocationName.valley_castle_dragon)
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_1_region, LocationName.star_road_1_dragon,
lambda state: (state.has(ItemName.mario_spin_jump, player) and
state.has(ItemName.progressive_powerup, player, 1)))
- add_location_to_region(world, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_dragon,
lambda state: state.has(ItemName.mario_climb, player))
- add_location_to_region(world, player, active_locations, LocationName.special_zone_2_region, LocationName.special_zone_2_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_2_region, LocationName.special_zone_2_dragon,
lambda state: state.has(ItemName.p_balloon, player))
- add_location_to_region(world, player, active_locations, LocationName.special_zone_3_region, LocationName.special_zone_3_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_3_region, LocationName.special_zone_3_dragon,
lambda state: state.has(ItemName.yoshi_activate, player))
- add_location_to_region(world, player, active_locations, LocationName.special_zone_4_region, LocationName.special_zone_4_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_4_region, LocationName.special_zone_4_dragon,
lambda state: state.has(ItemName.progressive_powerup, player, 1))
- add_location_to_region(world, player, active_locations, LocationName.special_zone_5_region, LocationName.special_zone_5_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_5_region, LocationName.special_zone_5_dragon,
lambda state: state.has(ItemName.progressive_powerup, player, 1))
- add_location_to_region(world, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_dragon,
lambda state: state.has(ItemName.mario_swim, player))
- add_location_to_region(world, player, active_locations, LocationName.special_zone_7_region, LocationName.special_zone_7_dragon,
- lambda state: state.has(ItemName.progressive_powerup, player, 1))
- add_location_to_region(world, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_dragon,
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_7_region, LocationName.special_zone_7_dragon,
lambda state: state.has(ItemName.progressive_powerup, player, 1))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_dragon,
+ lambda state: ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player)) or
+ state.has(ItemName.progressive_powerup, player, 3) or
+ state.has(ItemName.yoshi_activate, player) or
+ state.has(ItemName.mario_carry, player)))
+
+ if world.options.moon_checks:
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_1_region, LocationName.yoshis_island_1_moon,
+ lambda state: ((state.has(ItemName.mario_run, player) and
+ state.has(ItemName.progressive_powerup, player, 3)) or
+ state.has(ItemName.yoshi_activate, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_4_region, LocationName.donut_plains_4_moon,
+ lambda state: (state.has(ItemName.mario_run, player) and
+ state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_moon,
+ lambda state: (state.has(ItemName.mario_run, player) and
+ state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.cheese_bridge_region, LocationName.cheese_bridge_moon,
+ lambda state: (state.has(ItemName.mario_run, player) and
+ (state.has(ItemName.progressive_powerup, player, 3) or
+ state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_ghost_house_region, LocationName.forest_ghost_house_moon,
+ lambda state: state.has(ItemName.p_switch, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_1_region, LocationName.chocolate_island_1_moon,
+ lambda state: ((state.has(ItemName.mario_run, player) and
+ state.has(ItemName.progressive_powerup, player, 3)) or
+ state.has(ItemName.yoshi_activate, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_1_region, LocationName.valley_of_bowser_1_moon)
+
+ if world.options.hidden_1up_checks:
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_4_region, LocationName.yoshis_island_4_hidden_1up,
+ lambda state: (state.has(ItemName.yoshi_activate, player) or
+ (state.has(ItemName.mario_run, player, player) and
+ state.has(ItemName.progressive_powerup, player, 3))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_hidden_1up)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_4_region, LocationName.donut_plains_4_hidden_1up,
+ lambda state: (state.has(ItemName.mario_run, player) and
+ state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_castle_region, LocationName.donut_plains_castle_hidden_1up)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_ghost_house_region, LocationName.vanilla_ghost_house_hidden_1up)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_4_region, LocationName.vanilla_dome_4_hidden_1up)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_fortress_region, LocationName.vanilla_fortress_hidden_1up,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_hidden_1up,
+ lambda state: (state.has(ItemName.mario_swim, player) or
+ state.has(ItemName.yoshi_activate, player) or
+ (state.has(ItemName.mario_run, player, player) and
+ state.has(ItemName.progressive_powerup, player, 3))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_hidden_1up,
+ lambda state: (state.has(ItemName.mario_carry, player) or
+ state.has(ItemName.yoshi_activate, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_hidden_1up)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_castle_region, LocationName.chocolate_castle_hidden_1up,
+ lambda state: (state.has(ItemName.progressive_powerup, player, 1)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_2_region, LocationName.valley_of_bowser_2_hidden_1up)
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_castle_region, LocationName.valley_castle_hidden_1up)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_hidden_1up,
+ lambda state: state.has(ItemName.mario_climb, player))
+
+ if world.options.bonus_block_checks:
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_bonus_block)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_3_region, LocationName.donut_plains_3_bonus_block)
+ add_location_to_region(multiworld, player, active_locations, LocationName.butter_bridge_1_region, LocationName.butter_bridge_1_bonus_block)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_bonus_block)
+
+ if world.options.blocksanity:
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_2_region, LocationName.vanilla_secret_2_yoshi_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_2_region, LocationName.vanilla_secret_2_green_block_1,
+ lambda state:( ((state.has(ItemName.green_switch_palace, player) and state.has(ItemName.mario_carry, player))) or ((state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_2_region, LocationName.vanilla_secret_2_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_2_region, LocationName.vanilla_secret_2_powerup_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_2_region, LocationName.vanilla_secret_2_multi_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_2_region, LocationName.vanilla_secret_2_gray_pow_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_2_region, LocationName.vanilla_secret_2_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_2_region, LocationName.vanilla_secret_2_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_2_region, LocationName.vanilla_secret_2_coin_block_3)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_2_region, LocationName.vanilla_secret_2_coin_block_4)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_2_region, LocationName.vanilla_secret_2_coin_block_5)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_2_region, LocationName.vanilla_secret_2_coin_block_6)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_3_region, LocationName.vanilla_secret_3_powerup_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_3_region, LocationName.vanilla_secret_3_powerup_block_2,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_ghost_house_region, LocationName.donut_ghost_house_vine_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_ghost_house_region, LocationName.donut_ghost_house_directional_coin_block_1,
+ lambda state: state.has(ItemName.p_switch, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_ghost_house_region, LocationName.donut_ghost_house_life_block_1,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_ghost_house_region, LocationName.donut_ghost_house_life_block_2,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_ghost_house_region, LocationName.donut_ghost_house_life_block_3,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_ghost_house_region, LocationName.donut_ghost_house_life_block_4,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_3_region, LocationName.donut_plains_3_green_block_1,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_3_region, LocationName.donut_plains_3_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_3_region, LocationName.donut_plains_3_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_3_region, LocationName.donut_plains_3_vine_block_1,
+ lambda state: (state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_3_region, LocationName.donut_plains_3_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_3_region, LocationName.donut_plains_3_bonus_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_4_region, LocationName.donut_plains_4_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_4_region, LocationName.donut_plains_4_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_4_region, LocationName.donut_plains_4_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_4_region, LocationName.donut_plains_4_yoshi_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_castle_region, LocationName.donut_plains_castle_yellow_block_1,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_castle_region, LocationName.donut_plains_castle_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_castle_region, LocationName.donut_plains_castle_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_castle_region, LocationName.donut_plains_castle_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_castle_region, LocationName.donut_plains_castle_vine_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_castle_region, LocationName.donut_plains_castle_invis_life_block_1,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_castle_region, LocationName.donut_plains_castle_coin_block_3)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_castle_region, LocationName.donut_plains_castle_coin_block_4)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_castle_region, LocationName.donut_plains_castle_coin_block_5)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_castle_region, LocationName.donut_plains_castle_green_block_1,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_2_region, LocationName.donut_plains_2_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_2_region, LocationName.donut_plains_2_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_2_region, LocationName.donut_plains_2_coin_block_3)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_2_region, LocationName.donut_plains_2_yellow_block_1,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_2_region, LocationName.donut_plains_2_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_2_region, LocationName.donut_plains_2_multi_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_2_region, LocationName.donut_plains_2_flying_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_2_region, LocationName.donut_plains_2_green_block_1,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_2_region, LocationName.donut_plains_2_yellow_block_2,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_2_region, LocationName.donut_plains_2_vine_block_1,
+ lambda state:( ((state.has(ItemName.mario_carry, player) and state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_1_region, LocationName.donut_secret_1_coin_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_1_region, LocationName.donut_secret_1_coin_block_2,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_1_region, LocationName.donut_secret_1_powerup_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_1_region, LocationName.donut_secret_1_coin_block_3,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_1_region, LocationName.donut_secret_1_powerup_block_2,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_1_region, LocationName.donut_secret_1_powerup_block_3,
+ lambda state: (state.has(ItemName.mario_swim, player) and state.has(ItemName.p_balloon, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_1_region, LocationName.donut_secret_1_life_block_1,
+ lambda state: (state.has(ItemName.mario_swim, player) and state.has(ItemName.p_balloon, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_1_region, LocationName.donut_secret_1_powerup_block_4,
+ lambda state: (state.has(ItemName.mario_swim, player) and state.has(ItemName.p_balloon, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_1_region, LocationName.donut_secret_1_powerup_block_5,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_1_region, LocationName.donut_secret_1_key_block_1,
+ lambda state: (state.has(ItemName.mario_swim, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.p_switch, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_fortress_region, LocationName.vanilla_fortress_powerup_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_fortress_region, LocationName.vanilla_fortress_powerup_block_2,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_fortress_region, LocationName.vanilla_fortress_yellow_block_1,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_swim, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.butter_bridge_1_region, LocationName.butter_bridge_1_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.butter_bridge_1_region, LocationName.butter_bridge_1_multi_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.butter_bridge_1_region, LocationName.butter_bridge_1_multi_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.butter_bridge_1_region, LocationName.butter_bridge_1_multi_coin_block_3)
+ add_location_to_region(multiworld, player, active_locations, LocationName.butter_bridge_1_region, LocationName.butter_bridge_1_life_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.butter_bridge_1_region, LocationName.butter_bridge_1_bonus_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.butter_bridge_2_region, LocationName.butter_bridge_2_powerup_block_1,
+ lambda state: state.has(ItemName.mario_carry, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.butter_bridge_2_region, LocationName.butter_bridge_2_green_block_1,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.butter_bridge_2_region, LocationName.butter_bridge_2_yoshi_block_1,
+ lambda state: state.has(ItemName.mario_carry, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.twin_bridges_castle_region, LocationName.twin_bridges_castle_powerup_block_1,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.cheese_bridge_region, LocationName.cheese_bridge_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cheese_bridge_region, LocationName.cheese_bridge_powerup_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cheese_bridge_region, LocationName.cheese_bridge_wings_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cheese_bridge_region, LocationName.cheese_bridge_powerup_block_3)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_3)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_4)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_5)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_6)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_7)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_8)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_9)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_life_block_1,
+ lambda state:( (state.has(ItemName.mario_climb, player)) or (state.has(ItemName.mario_swim, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_vine_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_yoshi_block_1,
+ lambda state: state.has(ItemName.red_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_10)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_11)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_powerup_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_12)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_13)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_14)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_15)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_16)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_17)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_18)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_19)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_20)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_21)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_22)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_23)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_24)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_25)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_26)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_27)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_28)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_29)
+ add_location_to_region(multiworld, player, active_locations, LocationName.cookie_mountain_region, LocationName.cookie_mountain_coin_block_30)
+ add_location_to_region(multiworld, player, active_locations, LocationName.soda_lake_region, LocationName.soda_lake_powerup_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_house_region, LocationName.donut_secret_house_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_house_region, LocationName.donut_secret_house_multi_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_house_region, LocationName.donut_secret_house_life_block_1,
+ lambda state: state.has(ItemName.p_switch, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_house_region, LocationName.donut_secret_house_vine_block_1,
+ lambda state: state.has(ItemName.p_switch, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_house_region, LocationName.donut_secret_house_directional_coin_block_1,
+ lambda state: state.has(ItemName.p_switch, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_yoshi_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_vine_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_green_block_1,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_green_block_2,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_green_block_3,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_green_block_4,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_green_block_5,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_green_block_6,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_green_block_7,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_green_block_8,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_green_block_9,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_green_block_10,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_green_block_11,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_green_block_12,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_green_block_13,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_green_block_14,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_green_block_15,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_green_block_16,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_yellow_block_1,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_yellow_block_2,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_plains_1_region, LocationName.donut_plains_1_yellow_block_3,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.sunken_ghost_ship_region, LocationName.sunken_ghost_ship_powerup_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.sunken_ghost_ship_region, LocationName.sunken_ghost_ship_star_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_castle_region, LocationName.chocolate_castle_yellow_block_1,
+ lambda state: (state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.yellow_switch_palace, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_castle_region, LocationName.chocolate_castle_yellow_block_2,
+ lambda state: (state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.yellow_switch_palace, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_castle_region, LocationName.chocolate_castle_green_block_1,
+ lambda state: (state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.green_switch_palace, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_fortress_region, LocationName.chocolate_fortress_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_fortress_region, LocationName.chocolate_fortress_powerup_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_fortress_region, LocationName.chocolate_fortress_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_fortress_region, LocationName.chocolate_fortress_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_fortress_region, LocationName.chocolate_fortress_green_block_1,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_5_region, LocationName.chocolate_island_5_yoshi_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_5_region, LocationName.chocolate_island_5_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_5_region, LocationName.chocolate_island_5_life_block_1,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.progressive_powerup, player, 3))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_5_region, LocationName.chocolate_island_5_yellow_block_1,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.mario_carry, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_4_region, LocationName.chocolate_island_4_yellow_block_1,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.blue_switch_palace, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_4_region, LocationName.chocolate_island_4_blue_pow_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_4_region, LocationName.chocolate_island_4_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_fortress_region, LocationName.forest_fortress_yellow_block_1,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_fortress_region, LocationName.forest_fortress_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_fortress_region, LocationName.forest_fortress_life_block_1,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_fortress_region, LocationName.forest_fortress_life_block_2,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_fortress_region, LocationName.forest_fortress_life_block_3,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_fortress_region, LocationName.forest_fortress_life_block_4,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_fortress_region, LocationName.forest_fortress_life_block_5,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_fortress_region, LocationName.forest_fortress_life_block_6,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_fortress_region, LocationName.forest_fortress_life_block_7,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_fortress_region, LocationName.forest_fortress_life_block_8,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_fortress_region, LocationName.forest_fortress_life_block_9,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_castle_region, LocationName.forest_castle_green_block_1,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_ghost_house_region, LocationName.chocolate_ghost_house_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_ghost_house_region, LocationName.chocolate_ghost_house_powerup_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_ghost_house_region, LocationName.chocolate_ghost_house_life_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_1_region, LocationName.chocolate_island_1_flying_block_1,
+ lambda state: state.has(ItemName.p_switch, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_1_region, LocationName.chocolate_island_1_flying_block_2,
+ lambda state: state.has(ItemName.p_switch, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_1_region, LocationName.chocolate_island_1_yoshi_block_1,
+ lambda state: state.has(ItemName.p_switch, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_1_region, LocationName.chocolate_island_1_green_block_1,
+ lambda state:( ((state.has(ItemName.green_switch_palace, player) and state.has(ItemName.blue_switch_palace, player) and state.has(ItemName.p_switch, player))) or ((state.has(ItemName.green_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3) and state.has(ItemName.p_switch, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.blue_switch_palace, player) and state.has(ItemName.p_switch, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3) and state.has(ItemName.p_switch, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_1_region, LocationName.chocolate_island_1_life_block_1,
+ lambda state: state.has(ItemName.p_switch, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_powerup_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_powerup_block_3)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_green_block_1,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_bonus_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_vine_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_life_block_1,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_life_block_2,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_life_block_3,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_multi_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_invis_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_yoshi_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_multi_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_blue_pow_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_yellow_block_1,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_yellow_block_2,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_green_block_1,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_green_block_2,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_green_block_3,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_green_block_4,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_green_block_5,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_green_block_6,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_castle_region, LocationName.yoshis_island_castle_coin_block_1,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_castle_region, LocationName.yoshis_island_castle_coin_block_2,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_castle_region, LocationName.yoshis_island_castle_powerup_block_1,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_castle_region, LocationName.yoshis_island_castle_coin_block_3,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_castle_region, LocationName.yoshis_island_castle_coin_block_4,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_castle_region, LocationName.yoshis_island_castle_flying_block_1,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_4_region, LocationName.yoshis_island_4_yellow_block_1,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_4_region, LocationName.yoshis_island_4_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_4_region, LocationName.yoshis_island_4_multi_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_4_region, LocationName.yoshis_island_4_star_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_yellow_block_1,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_yellow_block_2,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_yellow_block_3,
+ lambda state:( ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_carry, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_yellow_block_4,
+ lambda state:( ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_carry, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_yellow_block_5,
+ lambda state:( ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_carry, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_yellow_block_6,
+ lambda state:( ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_carry, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_yellow_block_7,
+ lambda state:( ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_carry, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_yellow_block_8,
+ lambda state:( ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_carry, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_yellow_block_9,
+ lambda state:( ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_carry, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_yoshi_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_yellow_block_10,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_yellow_block_11,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_yellow_block_12,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_bonus_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_1_region, LocationName.yoshis_island_1_flying_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_1_region, LocationName.yoshis_island_1_yellow_block_1,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_1_region, LocationName.yoshis_island_1_life_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_1_region, LocationName.yoshis_island_1_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_flying_block_1,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_flying_block_2,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_flying_block_3,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_flying_block_4,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_flying_block_5,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_flying_block_6,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_yellow_block_1,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_coin_block_3)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_yoshi_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_coin_block_4)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_yoshi_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_coin_block_5)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_vine_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_yellow_block_2,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_ghost_house_region, LocationName.vanilla_ghost_house_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_ghost_house_region, LocationName.vanilla_ghost_house_vine_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_ghost_house_region, LocationName.vanilla_ghost_house_powerup_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_ghost_house_region, LocationName.vanilla_ghost_house_multi_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_ghost_house_region, LocationName.vanilla_ghost_house_blue_pow_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_1_region, LocationName.vanilla_secret_1_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_1_region, LocationName.vanilla_secret_1_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_1_region, LocationName.vanilla_secret_1_multi_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_1_region, LocationName.vanilla_secret_1_vine_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_1_region, LocationName.vanilla_secret_1_vine_block_2,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_1_region, LocationName.vanilla_secret_1_coin_block_2,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_1_region, LocationName.vanilla_secret_1_coin_block_3,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_secret_1_region, LocationName.vanilla_secret_1_powerup_block_2,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_flying_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_flying_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_flying_block_3)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_invis_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_powerup_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_multi_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_powerup_block_3)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_yoshi_block_1,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_powerup_block_4)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_pswitch_coin_block_1,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3) and state.has(ItemName.p_switch, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_pswitch_coin_block_2,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3) and state.has(ItemName.p_switch, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_pswitch_coin_block_3,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3) and state.has(ItemName.p_switch, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_pswitch_coin_block_4,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3) and state.has(ItemName.p_switch, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_pswitch_coin_block_5,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3) and state.has(ItemName.p_switch, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_pswitch_coin_block_6,
+ lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3) and state.has(ItemName.p_switch, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_2_region, LocationName.donut_secret_2_directional_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_2_region, LocationName.donut_secret_2_vine_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_2_region, LocationName.donut_secret_2_star_block_1,
+ lambda state:( (state.has(ItemName.mario_climb, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_2_region, LocationName.donut_secret_2_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.donut_secret_2_region, LocationName.donut_secret_2_star_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_4_region, LocationName.valley_of_bowser_4_yellow_block_1,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_4_region, LocationName.valley_of_bowser_4_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_4_region, LocationName.valley_of_bowser_4_vine_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_4_region, LocationName.valley_of_bowser_4_yoshi_block_1,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_4_region, LocationName.valley_of_bowser_4_life_block_1,
+ lambda state: (state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player) and state.has(ItemName.mario_climb, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_4_region, LocationName.valley_of_bowser_4_powerup_block_2,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_climb, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_castle_region, LocationName.valley_castle_yellow_block_1,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_castle_region, LocationName.valley_castle_yellow_block_2,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_castle_region, LocationName.valley_castle_green_block_1,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_fortress_region, LocationName.valley_fortress_green_block_1,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_fortress_region, LocationName.valley_fortress_yellow_block_1,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_3_region, LocationName.valley_of_bowser_3_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_3_region, LocationName.valley_of_bowser_3_powerup_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_ghost_house_region, LocationName.valley_ghost_house_pswitch_coin_block_1,
+ lambda state: state.has(ItemName.p_switch, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_ghost_house_region, LocationName.valley_ghost_house_multi_coin_block_1,
+ lambda state: state.has(ItemName.p_switch, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_ghost_house_region, LocationName.valley_ghost_house_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_ghost_house_region, LocationName.valley_ghost_house_directional_coin_block_1,
+ lambda state: state.has(ItemName.p_switch, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_2_region, LocationName.valley_of_bowser_2_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_2_region, LocationName.valley_of_bowser_2_yellow_block_1,
+ lambda state: state.has(ItemName.yellow_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_2_region, LocationName.valley_of_bowser_2_powerup_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_2_region, LocationName.valley_of_bowser_2_wings_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_1_region, LocationName.valley_of_bowser_1_green_block_1,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_1_region, LocationName.valley_of_bowser_1_invis_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_1_region, LocationName.valley_of_bowser_1_invis_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_1_region, LocationName.valley_of_bowser_1_invis_coin_block_3)
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_1_region, LocationName.valley_of_bowser_1_yellow_block_1,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_1_region, LocationName.valley_of_bowser_1_yellow_block_2,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_1_region, LocationName.valley_of_bowser_1_yellow_block_3,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_1_region, LocationName.valley_of_bowser_1_yellow_block_4,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.valley_of_bowser_1_region, LocationName.valley_of_bowser_1_vine_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_secret_region, LocationName.chocolate_secret_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.chocolate_secret_region, LocationName.chocolate_secret_powerup_block_2,
+ lambda state: state.has(ItemName.mario_run, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_coin_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_powerup_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_coin_block_2,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_coin_block_3,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_vine_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_invis_life_block_1,
+ lambda state:( ((state.has(ItemName.mario_swim, player) and state.has(ItemName.mario_climb, player))) or ((state.has(ItemName.mario_swim, player) and state.has(ItemName.yoshi_activate, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_coin_block_4,
+ lambda state:( ((state.has(ItemName.mario_swim, player) and state.has(ItemName.mario_climb, player))) or ((state.has(ItemName.mario_swim, player) and state.has(ItemName.yoshi_activate, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_coin_block_5,
+ lambda state:( ((state.has(ItemName.mario_swim, player) and state.has(ItemName.mario_climb, player))) or ((state.has(ItemName.mario_swim, player) and state.has(ItemName.yoshi_activate, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_powerup_block_2,
+ lambda state:( ((state.has(ItemName.mario_swim, player) and state.has(ItemName.mario_climb, player))) or ((state.has(ItemName.mario_swim, player) and state.has(ItemName.yoshi_activate, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_powerup_block_3,
+ lambda state:( ((state.has(ItemName.mario_swim, player) and state.has(ItemName.mario_climb, player))) or ((state.has(ItemName.mario_swim, player) and state.has(ItemName.yoshi_activate, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_powerup_block_4,
+ lambda state:( ((state.has(ItemName.mario_swim, player) and state.has(ItemName.mario_climb, player))) or ((state.has(ItemName.mario_swim, player) and state.has(ItemName.yoshi_activate, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_powerup_block_5,
+ lambda state:( ((state.has(ItemName.mario_swim, player) and state.has(ItemName.mario_climb, player))) or ((state.has(ItemName.mario_swim, player) and state.has(ItemName.yoshi_activate, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_multi_coin_block_1,
+ lambda state:( ((state.has(ItemName.mario_swim, player) and state.has(ItemName.mario_climb, player))) or ((state.has(ItemName.mario_swim, player) and state.has(ItemName.yoshi_activate, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_multi_coin_block_2,
+ lambda state:( ((state.has(ItemName.mario_swim, player) and state.has(ItemName.mario_climb, player))) or ((state.has(ItemName.mario_swim, player) and state.has(ItemName.yoshi_activate, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_4_region, LocationName.vanilla_dome_4_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_4_region, LocationName.vanilla_dome_4_powerup_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_4_region, LocationName.vanilla_dome_4_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_4_region, LocationName.vanilla_dome_4_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_4_region, LocationName.vanilla_dome_4_coin_block_3)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_4_region, LocationName.vanilla_dome_4_life_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_4_region, LocationName.vanilla_dome_4_coin_block_4)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_4_region, LocationName.vanilla_dome_4_coin_block_5)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_4_region, LocationName.vanilla_dome_4_coin_block_6)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_4_region, LocationName.vanilla_dome_4_coin_block_7)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_4_region, LocationName.vanilla_dome_4_coin_block_8,
+ lambda state: state.has(ItemName.mario_carry, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_1_region, LocationName.vanilla_dome_1_flying_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_1_region, LocationName.vanilla_dome_1_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_1_region, LocationName.vanilla_dome_1_powerup_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_1_region, LocationName.vanilla_dome_1_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_1_region, LocationName.vanilla_dome_1_life_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_1_region, LocationName.vanilla_dome_1_powerup_block_3)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_1_region, LocationName.vanilla_dome_1_vine_block_1,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.red_switch_palace, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_1_region, LocationName.vanilla_dome_1_star_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_1_region, LocationName.vanilla_dome_1_powerup_block_4,
+ lambda state:( ((state.has(ItemName.mario_run, player) and state.has(ItemName.super_star_active, player))) or ((state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 1)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_1_region, LocationName.vanilla_dome_1_coin_block_2,
+ lambda state:( ((state.has(ItemName.mario_run, player) and state.has(ItemName.super_star_active, player))) or ((state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 1)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_castle_region, LocationName.vanilla_dome_castle_life_block_1,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.progressive_powerup, player, 1))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_castle_region, LocationName.vanilla_dome_castle_life_block_2,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.progressive_powerup, player, 1))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_castle_region, LocationName.vanilla_dome_castle_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_castle_region, LocationName.vanilla_dome_castle_life_block_3,
+ lambda state: state.has(ItemName.p_switch, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.vanilla_dome_castle_region, LocationName.vanilla_dome_castle_green_block_1,
+ lambda state: state.has(ItemName.green_switch_palace, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_ghost_house_region, LocationName.forest_ghost_house_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_ghost_house_region, LocationName.forest_ghost_house_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_ghost_house_region, LocationName.forest_ghost_house_flying_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_ghost_house_region, LocationName.forest_ghost_house_powerup_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_ghost_house_region, LocationName.forest_ghost_house_life_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_1_region, LocationName.forest_of_illusion_1_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_1_region, LocationName.forest_of_illusion_1_yoshi_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_1_region, LocationName.forest_of_illusion_1_powerup_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_1_region, LocationName.forest_of_illusion_1_key_block_1,
+ lambda state: state.has(ItemName.p_balloon, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_1_region, LocationName.forest_of_illusion_1_life_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_multi_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_coin_block_3)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_coin_block_4)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_coin_block_5)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_coin_block_6)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_coin_block_7)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_powerup_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_coin_block_8)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_coin_block_9)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_coin_block_10)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_2_region, LocationName.forest_of_illusion_2_green_block_1,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.mario_swim, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_2_region, LocationName.forest_of_illusion_2_powerup_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_2_region, LocationName.forest_of_illusion_2_invis_coin_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_2_region, LocationName.forest_of_illusion_2_invis_coin_block_2,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_2_region, LocationName.forest_of_illusion_2_invis_life_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_2_region, LocationName.forest_of_illusion_2_invis_coin_block_3,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_2_region, LocationName.forest_of_illusion_2_yellow_block_1,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.mario_swim, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_secret_region, LocationName.forest_secret_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_secret_region, LocationName.forest_secret_powerup_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_secret_region, LocationName.forest_secret_life_block_1,
+ lambda state:( (state.has(ItemName.blue_switch_palace, player)) or (state.has(ItemName.mario_carry, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_yoshi_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_multi_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_2,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_multi_coin_block_2,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_3,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_4,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_5,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_6,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_7,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_8,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_9,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_10,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_11,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_12,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_13,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_14,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_15,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_16,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_17,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_18,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_19,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_20,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_21,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_22,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_23,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_coin_block_24,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_yoshi_block_1,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_3)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_4)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_5)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_blue_pow_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_star_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_6,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_7,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_8,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_9,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_10,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_11,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_12,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_13,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_14,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_15,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_16,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_17,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_18,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_multi_coin_block_1,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_19,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_20,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_21,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_22,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_coin_block_23,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_powerup_block_2,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_8_region, LocationName.special_zone_8_flying_block_1,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player))) or (state.has(ItemName.progressive_powerup, player, 3)) or (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.yoshi_activate, player))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_7_region, LocationName.special_zone_7_powerup_block_1,
+ lambda state: state.has(ItemName.progressive_powerup, player, 1))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_7_region, LocationName.special_zone_7_yoshi_block_1,
+ lambda state: state.has(ItemName.progressive_powerup, player, 1))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_7_region, LocationName.special_zone_7_coin_block_1,
+ lambda state: state.has(ItemName.progressive_powerup, player, 1))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_7_region, LocationName.special_zone_7_powerup_block_2,
+ lambda state: state.has(ItemName.progressive_powerup, player, 1))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_7_region, LocationName.special_zone_7_coin_block_2,
+ lambda state: state.has(ItemName.progressive_powerup, player, 1))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_powerup_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_2,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_yoshi_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_life_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_multi_coin_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_3,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_4,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_5,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_6,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_7,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_8,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_9,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_10,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_11,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_12,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_13,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_14,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_15,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_16,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_17,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_18,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_19,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_20,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_21,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_22,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_23,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_24,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_25,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_26,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_27,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_28,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_powerup_block_2,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_29,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_30,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_31,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_32,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_6_region, LocationName.special_zone_6_coin_block_33,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_5_region, LocationName.special_zone_5_yoshi_block_1,
+ lambda state: state.has(ItemName.progressive_powerup, player, 1))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_vine_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_vine_block_2)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_vine_block_3)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_vine_block_4)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_life_block_1,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_vine_block_5,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_blue_pow_block_1,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_vine_block_6,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_powerup_block_1,
+ lambda state: state.has(ItemName.mario_climb, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_pswitch_coin_block_1,
+ lambda state: (state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_pswitch_coin_block_2,
+ lambda state: (state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_pswitch_coin_block_3,
+ lambda state: (state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_pswitch_coin_block_4,
+ lambda state: (state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_pswitch_coin_block_5,
+ lambda state: (state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_pswitch_coin_block_6,
+ lambda state: (state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_pswitch_coin_block_7,
+ lambda state: (state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_pswitch_coin_block_8,
+ lambda state: (state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_pswitch_coin_block_9,
+ lambda state: (state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.progressive_powerup, player, 3)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_pswitch_coin_block_10,
+ lambda state:( ((state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.progressive_powerup, player, 3))) or ((state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.mario_carry, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_pswitch_coin_block_11,
+ lambda state:( ((state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.progressive_powerup, player, 3))) or ((state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.mario_carry, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_pswitch_coin_block_12,
+ lambda state:( ((state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.progressive_powerup, player, 3))) or ((state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.mario_carry, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_1_region, LocationName.special_zone_1_pswitch_coin_block_13,
+ lambda state:( ((state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.progressive_powerup, player, 3))) or ((state.has(ItemName.mario_climb, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.mario_carry, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_2_region, LocationName.special_zone_2_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_2_region, LocationName.special_zone_2_coin_block_1,
+ lambda state: state.has(ItemName.p_balloon, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_2_region, LocationName.special_zone_2_coin_block_2,
+ lambda state: state.has(ItemName.p_balloon, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_2_region, LocationName.special_zone_2_powerup_block_2,
+ lambda state: state.has(ItemName.p_balloon, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_2_region, LocationName.special_zone_2_coin_block_3,
+ lambda state: state.has(ItemName.p_balloon, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_2_region, LocationName.special_zone_2_coin_block_4,
+ lambda state: state.has(ItemName.p_balloon, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_2_region, LocationName.special_zone_2_powerup_block_3,
+ lambda state: state.has(ItemName.p_balloon, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_2_region, LocationName.special_zone_2_multi_coin_block_1,
+ lambda state: state.has(ItemName.p_balloon, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_2_region, LocationName.special_zone_2_coin_block_5,
+ lambda state: state.has(ItemName.p_balloon, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_2_region, LocationName.special_zone_2_coin_block_6,
+ lambda state: state.has(ItemName.p_balloon, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_3_region, LocationName.special_zone_3_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_3_region, LocationName.special_zone_3_yoshi_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_3_region, LocationName.special_zone_3_wings_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_4_region, LocationName.special_zone_4_powerup_block_1,
+ lambda state: state.has(ItemName.progressive_powerup, player, 2))
+ add_location_to_region(multiworld, player, active_locations, LocationName.special_zone_4_region, LocationName.special_zone_4_star_block_1,
+ lambda state:( ((state.has(ItemName.progressive_powerup, player, 2) and state.has(ItemName.mario_carry, player))) or ((state.has(ItemName.progressive_powerup, player, 2) and state.has(ItemName.p_switch, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_2_region, LocationName.star_road_2_star_block_1,
+ lambda state: state.has(ItemName.mario_swim, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_3_region, LocationName.star_road_3_key_block_1,
+ lambda state:( (state.has(ItemName.mario_carry, player)) or (state.has(ItemName.progressive_powerup, player, 2))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_4_region, LocationName.star_road_4_powerup_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_4_region, LocationName.star_road_4_green_block_1,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_4_region, LocationName.star_road_4_green_block_2,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_4_region, LocationName.star_road_4_green_block_3,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_4_region, LocationName.star_road_4_green_block_4,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_4_region, LocationName.star_road_4_green_block_5,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_4_region, LocationName.star_road_4_green_block_6,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_4_region, LocationName.star_road_4_green_block_7,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_4_region, LocationName.star_road_4_key_block_1,
+ lambda state:( ((state.has(ItemName.mario_carry, player) and state.has(ItemName.yoshi_activate, player))) or ((state.has(ItemName.green_switch_palace, player) and state.has(ItemName.red_switch_palace, player) and state.has(ItemName.mario_carry, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_directional_coin_block_1)
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_life_block_1,
+ lambda state: state.has(ItemName.p_switch, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_vine_block_1,
+ lambda state: state.has(ItemName.p_switch, player))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_1,
+ lambda state:( ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.mario_climb, player) and state.has(ItemName.mario_carry, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_2,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_3,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_4,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_5,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_6,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_7,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_8,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_9,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_10,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_11,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_12,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_13,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_14,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_15,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_16,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_17,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_18,
+ lambda state: (state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_19,
+ lambda state:( ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.green_switch_palace, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.mario_climb, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.progressive_powerup, player, 3)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_yellow_block_20,
+ lambda state:( ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player))) or ((state.has(ItemName.yellow_switch_palace, player) and state.has(ItemName.green_switch_palace, player) and state.has(ItemName.p_switch, player) and state.has(ItemName.mario_climb, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.progressive_powerup, player, 3)))))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_1,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_2,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_3,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_4,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_5,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_6,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_7,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_8,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_9,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_10,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_11,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_12,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_13,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_14,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_15,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_16,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_17,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_18,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_19,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+ add_location_to_region(multiworld, player, active_locations, LocationName.star_road_5_region, LocationName.star_road_5_green_block_20,
+ lambda state: (state.has(ItemName.green_switch_palace, player) and state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_carry, player) and state.has(ItemName.special_world_clear, player)))
+
+def connect_regions(world: World, level_to_tile_dict):
+ multiworld: MultiWorld = world.multiworld
+ player: int = world.player
-
-
-def connect_regions(world, player, level_to_tile_dict):
names: typing.Dict[str, int] = {}
- connect(world, player, names, "Menu", LocationName.yoshis_island_region)
- connect(world, player, names, LocationName.yoshis_island_region, LocationName.yoshis_house_tile)
- connect(world, player, names, LocationName.yoshis_house_tile, LocationName.donut_plains_top_secret)
- connect(world, player, names, LocationName.yoshis_island_region, LocationName.yoshis_island_1_tile)
- connect(world, player, names, LocationName.yoshis_island_region, LocationName.yoshis_island_2_tile)
+ connect(world, "Menu", LocationName.yoshis_island_region)
+ connect(world, LocationName.yoshis_island_region, LocationName.yoshis_house_tile)
+ connect(world, LocationName.yoshis_island_region, LocationName.yoshis_island_1_tile)
+ connect(world, LocationName.yoshis_island_region, LocationName.yoshis_island_2_tile)
# Connect regions within levels using rules
- connect(world, player, names, LocationName.yoshis_island_1_region, LocationName.yoshis_island_1_exit_1)
- connect(world, player, names, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_exit_1)
- connect(world, player, names, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_exit_1)
- connect(world, player, names, LocationName.yoshis_island_4_region, LocationName.yoshis_island_4_exit_1)
- connect(world, player, names, LocationName.yoshis_island_castle_region, LocationName.yoshis_island_castle,
+ connect(world, LocationName.yoshis_island_1_region, LocationName.yoshis_island_1_exit_1)
+ connect(world, LocationName.yoshis_island_2_region, LocationName.yoshis_island_2_exit_1)
+ connect(world, LocationName.yoshis_island_3_region, LocationName.yoshis_island_3_exit_1)
+ connect(world, LocationName.yoshis_island_4_region, LocationName.yoshis_island_4_exit_1)
+ connect(world, LocationName.yoshis_island_castle_region, LocationName.yoshis_island_castle,
lambda state: (state.has(ItemName.mario_climb, player)))
- connect(world, player, names, LocationName.donut_plains_1_region, LocationName.donut_plains_1_exit_1)
- connect(world, player, names, LocationName.donut_plains_1_region, LocationName.donut_plains_1_exit_2,
+ connect(world, LocationName.donut_plains_1_region, LocationName.donut_plains_1_exit_1)
+ connect(world, LocationName.donut_plains_1_region, LocationName.donut_plains_1_exit_2,
lambda state: (state.has(ItemName.mario_carry, player) and
(state.has(ItemName.yoshi_activate, player) or
state.has(ItemName.green_switch_palace, player) or
(state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))))
- connect(world, player, names, LocationName.donut_plains_2_region, LocationName.donut_plains_2_exit_1)
- connect(world, player, names, LocationName.donut_plains_2_region, LocationName.donut_plains_2_exit_2,
+ connect(world, LocationName.donut_plains_2_region, LocationName.donut_plains_2_exit_1)
+ connect(world, LocationName.donut_plains_2_region, LocationName.donut_plains_2_exit_2,
lambda state: (state.has(ItemName.mario_carry, player) and
(state.has(ItemName.yoshi_activate, player) or
(state.has(ItemName.mario_spin_jump, player) and state.has(ItemName.mario_climb, player) and state.has(ItemName.progressive_powerup, player, 1)))))
- connect(world, player, names, LocationName.donut_secret_1_region, LocationName.donut_secret_1_exit_1,
+ connect(world, LocationName.donut_secret_1_region, LocationName.donut_secret_1_exit_1,
lambda state: state.has(ItemName.mario_swim, player))
- connect(world, player, names, LocationName.donut_secret_1_region, LocationName.donut_secret_1_exit_2,
+ connect(world, LocationName.donut_secret_1_region, LocationName.donut_secret_1_exit_2,
lambda state: (state.has(ItemName.mario_carry, player) and
state.has(ItemName.mario_swim, player) and
state.has(ItemName.p_switch, player)))
- connect(world, player, names, LocationName.donut_ghost_house_region, LocationName.donut_ghost_house_exit_1,
+ connect(world, LocationName.donut_ghost_house_region, LocationName.donut_ghost_house_exit_1,
lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
- connect(world, player, names, LocationName.donut_ghost_house_region, LocationName.donut_ghost_house_exit_2,
+ connect(world, LocationName.donut_ghost_house_region, LocationName.donut_ghost_house_exit_2,
lambda state: (state.has(ItemName.mario_climb, player) or
(state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3))))
- connect(world, player, names, LocationName.donut_secret_house_region, LocationName.donut_secret_house_exit_1,
+ connect(world, LocationName.donut_secret_house_region, LocationName.donut_secret_house_exit_1,
lambda state: state.has(ItemName.p_switch, player))
- connect(world, player, names, LocationName.donut_secret_house_region, LocationName.donut_secret_house_exit_2,
+ connect(world, LocationName.donut_secret_house_region, LocationName.donut_secret_house_exit_2,
lambda state: (state.has(ItemName.p_switch, player) and state.has(ItemName.mario_carry, player) and
(state.has(ItemName.mario_climb, player) or
(state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))))
- connect(world, player, names, LocationName.donut_plains_3_region, LocationName.donut_plains_3_exit_1)
- connect(world, player, names, LocationName.donut_plains_4_region, LocationName.donut_plains_4_exit_1)
- connect(world, player, names, LocationName.donut_secret_2_region, LocationName.donut_secret_2_exit_1)
- connect(world, player, names, LocationName.donut_plains_castle_region, LocationName.donut_plains_castle)
+ connect(world, LocationName.donut_plains_3_region, LocationName.donut_plains_3_exit_1)
+ connect(world, LocationName.donut_plains_4_region, LocationName.donut_plains_4_exit_1)
+ connect(world, LocationName.donut_secret_2_region, LocationName.donut_secret_2_exit_1)
+ connect(world, LocationName.donut_plains_castle_region, LocationName.donut_plains_castle)
- connect(world, player, names, LocationName.vanilla_dome_1_region, LocationName.vanilla_dome_1_exit_1,
+ connect(world, LocationName.vanilla_dome_1_region, LocationName.vanilla_dome_1_exit_1,
lambda state: (state.has(ItemName.mario_run, player) and
(state.has(ItemName.super_star_active, player) or
state.has(ItemName.progressive_powerup, player, 1))))
- connect(world, player, names, LocationName.vanilla_dome_1_region, LocationName.vanilla_dome_1_exit_2,
+ connect(world, LocationName.vanilla_dome_1_region, LocationName.vanilla_dome_1_exit_2,
lambda state: (state.has(ItemName.mario_carry, player) and
((state.has(ItemName.yoshi_activate, player) and state.has(ItemName.mario_climb, player)) or
(state.has(ItemName.yoshi_activate, player) and state.has(ItemName.red_switch_palace, player)) or
(state.has(ItemName.red_switch_palace, player) and state.has(ItemName.mario_climb, player)))))
- connect(world, player, names, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_exit_1,
+ connect(world, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_exit_1,
lambda state: (state.has(ItemName.mario_swim, player) and
(state.has(ItemName.mario_climb, player) or state.has(ItemName.yoshi_activate, player))))
- connect(world, player, names, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_exit_2,
+ connect(world, LocationName.vanilla_dome_2_region, LocationName.vanilla_dome_2_exit_2,
lambda state: (state.has(ItemName.mario_swim, player) and
state.has(ItemName.p_switch, player) and
state.has(ItemName.mario_carry, player) and
(state.has(ItemName.mario_climb, player) or state.has(ItemName.yoshi_activate, player))))
- connect(world, player, names, LocationName.vanilla_secret_1_region, LocationName.vanilla_secret_1_exit_1,
+ connect(world, LocationName.vanilla_secret_1_region, LocationName.vanilla_secret_1_exit_1,
lambda state: state.has(ItemName.mario_climb, player))
- connect(world, player, names, LocationName.vanilla_secret_1_region, LocationName.vanilla_secret_1_exit_2,
+ connect(world, LocationName.vanilla_secret_1_region, LocationName.vanilla_secret_1_exit_2,
lambda state: (state.has(ItemName.mario_climb, player) and
(state.has(ItemName.mario_carry, player) and state.has(ItemName.blue_switch_palace, player))))
- connect(world, player, names, LocationName.vanilla_ghost_house_region, LocationName.vanilla_ghost_house_exit_1,
+ connect(world, LocationName.vanilla_ghost_house_region, LocationName.vanilla_ghost_house_exit_1,
lambda state: state.has(ItemName.p_switch, player))
- connect(world, player, names, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_exit_1)
- connect(world, player, names, LocationName.vanilla_dome_4_region, LocationName.vanilla_dome_4_exit_1)
- connect(world, player, names, LocationName.vanilla_secret_2_region, LocationName.vanilla_secret_2_exit_1)
- connect(world, player, names, LocationName.vanilla_secret_3_region, LocationName.vanilla_secret_3_exit_1,
+ connect(world, LocationName.vanilla_dome_3_region, LocationName.vanilla_dome_3_exit_1)
+ connect(world, LocationName.vanilla_dome_4_region, LocationName.vanilla_dome_4_exit_1)
+ connect(world, LocationName.vanilla_secret_2_region, LocationName.vanilla_secret_2_exit_1)
+ connect(world, LocationName.vanilla_secret_3_region, LocationName.vanilla_secret_3_exit_1,
lambda state: state.has(ItemName.mario_swim, player))
- connect(world, player, names, LocationName.vanilla_fortress_region, LocationName.vanilla_fortress,
+ connect(world, LocationName.vanilla_fortress_region, LocationName.vanilla_fortress,
lambda state: state.has(ItemName.mario_swim, player))
- connect(world, player, names, LocationName.vanilla_dome_castle_region, LocationName.vanilla_dome_castle)
+ connect(world, LocationName.vanilla_dome_castle_region, LocationName.vanilla_dome_castle)
- connect(world, player, names, LocationName.butter_bridge_1_region, LocationName.butter_bridge_1_exit_1)
- connect(world, player, names, LocationName.butter_bridge_2_region, LocationName.butter_bridge_2_exit_1)
- connect(world, player, names, LocationName.cheese_bridge_region, LocationName.cheese_bridge_exit_1,
+ connect(world, LocationName.butter_bridge_1_region, LocationName.butter_bridge_1_exit_1)
+ connect(world, LocationName.butter_bridge_2_region, LocationName.butter_bridge_2_exit_1)
+ connect(world, LocationName.cheese_bridge_region, LocationName.cheese_bridge_exit_1,
lambda state: state.has(ItemName.mario_climb, player))
- connect(world, player, names, LocationName.cheese_bridge_region, LocationName.cheese_bridge_exit_2,
- lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
- connect(world, player, names, LocationName.soda_lake_region, LocationName.soda_lake_exit_1,
+ connect(world, LocationName.cheese_bridge_region, LocationName.cheese_bridge_exit_2,
+ lambda state: (state.has(ItemName.mario_run, player) and
+ (state.has(ItemName.progressive_powerup, player, 3) or
+ state.has(ItemName.yoshi_activate, player))))
+ connect(world, LocationName.soda_lake_region, LocationName.soda_lake_exit_1,
lambda state: state.has(ItemName.mario_swim, player))
- connect(world, player, names, LocationName.cookie_mountain_region, LocationName.cookie_mountain_exit_1)
- connect(world, player, names, LocationName.twin_bridges_castle_region, LocationName.twin_bridges_castle,
+ connect(world, LocationName.cookie_mountain_region, LocationName.cookie_mountain_exit_1)
+ connect(world, LocationName.twin_bridges_castle_region, LocationName.twin_bridges_castle,
lambda state: (state.has(ItemName.mario_run, player) and
state.has(ItemName.mario_climb, player)))
- connect(world, player, names, LocationName.forest_of_illusion_1_region, LocationName.forest_of_illusion_1_exit_1)
- connect(world, player, names, LocationName.forest_of_illusion_1_region, LocationName.forest_of_illusion_1_exit_2,
+ connect(world, LocationName.forest_of_illusion_1_region, LocationName.forest_of_illusion_1_exit_1)
+ connect(world, LocationName.forest_of_illusion_1_region, LocationName.forest_of_illusion_1_exit_2,
lambda state: (state.has(ItemName.mario_carry, player) and
state.has(ItemName.p_balloon, player)))
- connect(world, player, names, LocationName.forest_of_illusion_2_region, LocationName.forest_of_illusion_2_exit_1,
+ connect(world, LocationName.forest_of_illusion_2_region, LocationName.forest_of_illusion_2_exit_1,
lambda state: state.has(ItemName.mario_swim, player))
- connect(world, player, names, LocationName.forest_of_illusion_2_region, LocationName.forest_of_illusion_2_exit_2,
+ connect(world, LocationName.forest_of_illusion_2_region, LocationName.forest_of_illusion_2_exit_2,
lambda state: (state.has(ItemName.mario_swim, player) and
state.has(ItemName.mario_carry, player)))
- connect(world, player, names, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_exit_1,
+ connect(world, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_exit_1,
lambda state: (state.has(ItemName.mario_carry, player) or
state.has(ItemName.yoshi_activate, player)))
- connect(world, player, names, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_exit_2,
+ connect(world, LocationName.forest_of_illusion_3_region, LocationName.forest_of_illusion_3_exit_2,
lambda state: (state.has(ItemName.mario_spin_jump, player) and
state.has(ItemName.mario_carry, player) and
state.has(ItemName.progressive_powerup, player, 1)))
- connect(world, player, names, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_exit_1)
- connect(world, player, names, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_exit_2,
+ connect(world, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_exit_1)
+ connect(world, LocationName.forest_of_illusion_4_region, LocationName.forest_of_illusion_4_exit_2,
lambda state: state.has(ItemName.mario_carry, player))
- connect(world, player, names, LocationName.forest_ghost_house_region, LocationName.forest_ghost_house_exit_1,
+ connect(world, LocationName.forest_ghost_house_region, LocationName.forest_ghost_house_exit_1,
lambda state: state.has(ItemName.p_switch, player))
- connect(world, player, names, LocationName.forest_ghost_house_region, LocationName.forest_ghost_house_exit_2,
+ connect(world, LocationName.forest_ghost_house_region, LocationName.forest_ghost_house_exit_2,
lambda state: state.has(ItemName.p_switch, player))
- connect(world, player, names, LocationName.forest_secret_region, LocationName.forest_secret_exit_1)
- connect(world, player, names, LocationName.forest_fortress_region, LocationName.forest_fortress)
- connect(world, player, names, LocationName.forest_castle_region, LocationName.forest_castle)
+ connect(world, LocationName.forest_secret_region, LocationName.forest_secret_exit_1)
+ connect(world, LocationName.forest_fortress_region, LocationName.forest_fortress)
+ connect(world, LocationName.forest_castle_region, LocationName.forest_castle)
- connect(world, player, names, LocationName.chocolate_island_1_region, LocationName.chocolate_island_1_exit_1,
+ connect(world, LocationName.chocolate_island_1_region, LocationName.chocolate_island_1_exit_1,
lambda state: state.has(ItemName.p_switch, player))
- connect(world, player, names, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_exit_1)
- connect(world, player, names, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_exit_2,
+ connect(world, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_exit_1)
+ connect(world, LocationName.chocolate_island_2_region, LocationName.chocolate_island_2_exit_2,
lambda state: state.has(ItemName.mario_carry, player))
- connect(world, player, names, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_exit_1,
+ connect(world, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_exit_1,
lambda state: (state.has(ItemName.mario_climb, player) or
(state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3))))
- connect(world, player, names, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_exit_2,
+ connect(world, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_exit_2,
lambda state: (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))
- connect(world, player, names, LocationName.chocolate_island_4_region, LocationName.chocolate_island_4_exit_1)
- connect(world, player, names, LocationName.chocolate_island_5_region, LocationName.chocolate_island_5_exit_1)
- connect(world, player, names, LocationName.chocolate_ghost_house_region, LocationName.chocolate_ghost_house_exit_1)
- connect(world, player, names, LocationName.chocolate_fortress_region, LocationName.chocolate_fortress)
- connect(world, player, names, LocationName.chocolate_secret_region, LocationName.chocolate_secret_exit_1,
+ connect(world, LocationName.chocolate_island_4_region, LocationName.chocolate_island_4_exit_1)
+ connect(world, LocationName.chocolate_island_5_region, LocationName.chocolate_island_5_exit_1)
+ connect(world, LocationName.chocolate_ghost_house_region, LocationName.chocolate_ghost_house_exit_1)
+ connect(world, LocationName.chocolate_fortress_region, LocationName.chocolate_fortress)
+ connect(world, LocationName.chocolate_secret_region, LocationName.chocolate_secret_exit_1,
lambda state: state.has(ItemName.mario_run, player))
- connect(world, player, names, LocationName.chocolate_castle_region, LocationName.chocolate_castle,
+ connect(world, LocationName.chocolate_castle_region, LocationName.chocolate_castle,
lambda state: (state.has(ItemName.progressive_powerup, player, 1)))
- connect(world, player, names, LocationName.sunken_ghost_ship_region, LocationName.sunken_ghost_ship,
+ connect(world, LocationName.sunken_ghost_ship_region, LocationName.sunken_ghost_ship,
lambda state: state.has(ItemName.mario_swim, player))
- connect(world, player, names, LocationName.valley_of_bowser_1_region, LocationName.valley_of_bowser_1_exit_1)
- connect(world, player, names, LocationName.valley_of_bowser_2_region, LocationName.valley_of_bowser_2_exit_1)
- connect(world, player, names, LocationName.valley_of_bowser_2_region, LocationName.valley_of_bowser_2_exit_2,
+ connect(world, LocationName.valley_of_bowser_1_region, LocationName.valley_of_bowser_1_exit_1)
+ connect(world, LocationName.valley_of_bowser_2_region, LocationName.valley_of_bowser_2_exit_1)
+ connect(world, LocationName.valley_of_bowser_2_region, LocationName.valley_of_bowser_2_exit_2,
lambda state: state.has(ItemName.mario_carry, player))
- connect(world, player, names, LocationName.valley_of_bowser_3_region, LocationName.valley_of_bowser_3_exit_1)
- connect(world, player, names, LocationName.valley_of_bowser_4_region, LocationName.valley_of_bowser_4_exit_1,
+ connect(world, LocationName.valley_of_bowser_3_region, LocationName.valley_of_bowser_3_exit_1)
+ connect(world, LocationName.valley_of_bowser_4_region, LocationName.valley_of_bowser_4_exit_1,
lambda state: state.has(ItemName.mario_climb, player))
- connect(world, player, names, LocationName.valley_of_bowser_4_region, LocationName.valley_of_bowser_4_exit_2,
+ connect(world, LocationName.valley_of_bowser_4_region, LocationName.valley_of_bowser_4_exit_2,
lambda state: (state.has(ItemName.mario_climb, player) and
state.has(ItemName.mario_carry, player) and
state.has(ItemName.yoshi_activate, player)))
- connect(world, player, names, LocationName.valley_ghost_house_region, LocationName.valley_ghost_house_exit_1,
+ connect(world, LocationName.valley_ghost_house_region, LocationName.valley_ghost_house_exit_1,
lambda state: state.has(ItemName.p_switch, player))
- connect(world, player, names, LocationName.valley_ghost_house_region, LocationName.valley_ghost_house_exit_2,
+ connect(world, LocationName.valley_ghost_house_region, LocationName.valley_ghost_house_exit_2,
lambda state: (state.has(ItemName.p_switch, player) and
state.has(ItemName.mario_carry, player) and
state.has(ItemName.mario_run, player)))
- connect(world, player, names, LocationName.valley_fortress_region, LocationName.valley_fortress,
+ connect(world, LocationName.valley_fortress_region, LocationName.valley_fortress,
lambda state: state.has(ItemName.progressive_powerup, player, 1))
- connect(world, player, names, LocationName.valley_castle_region, LocationName.valley_castle)
- connect(world, player, names, LocationName.front_door, LocationName.bowser_region,
+ connect(world, LocationName.valley_castle_region, LocationName.valley_castle)
+ connect(world, LocationName.front_door, LocationName.bowser_region,
lambda state: (state.has(ItemName.mario_climb, player) and
state.has(ItemName.mario_run, player) and
state.has(ItemName.mario_swim, player) and
state.has(ItemName.progressive_powerup, player, 1) and
- state.has(ItemName.koopaling, player, world.bosses_required[player].value)))
- connect(world, player, names, LocationName.back_door, LocationName.bowser_region,
- lambda state: state.has(ItemName.koopaling, player, world.bosses_required[player].value))
+ state.has(ItemName.koopaling, player, world.options.bosses_required.value)))
+ connect(world, LocationName.back_door, LocationName.bowser_region,
+ lambda state: state.has(ItemName.koopaling, player, world.options.bosses_required.value))
- connect(world, player, names, LocationName.star_road_1_region, LocationName.star_road_1_exit_1,
+ connect(world, LocationName.star_road_1_region, LocationName.star_road_1_exit_1,
lambda state: (state.has(ItemName.mario_spin_jump, player) and
state.has(ItemName.progressive_powerup, player, 1)))
- connect(world, player, names, LocationName.star_road_1_region, LocationName.star_road_1_exit_2,
+ connect(world, LocationName.star_road_1_region, LocationName.star_road_1_exit_2,
lambda state: (state.has(ItemName.mario_spin_jump, player) and
state.has(ItemName.mario_carry, player) and
state.has(ItemName.progressive_powerup, player, 1)))
- connect(world, player, names, LocationName.star_road_2_region, LocationName.star_road_2_exit_1,
+ connect(world, LocationName.star_road_2_region, LocationName.star_road_2_exit_1,
lambda state: state.has(ItemName.mario_swim, player))
- connect(world, player, names, LocationName.star_road_2_region, LocationName.star_road_2_exit_2,
+ connect(world, LocationName.star_road_2_region, LocationName.star_road_2_exit_2,
lambda state: (state.has(ItemName.mario_swim, player) and
state.has(ItemName.mario_carry, player)))
- connect(world, player, names, LocationName.star_road_3_region, LocationName.star_road_3_exit_1)
- connect(world, player, names, LocationName.star_road_3_region, LocationName.star_road_3_exit_2,
+ connect(world, LocationName.star_road_3_region, LocationName.star_road_3_exit_1)
+ connect(world, LocationName.star_road_3_region, LocationName.star_road_3_exit_2,
lambda state: state.has(ItemName.mario_carry, player))
- connect(world, player, names, LocationName.star_road_4_region, LocationName.star_road_4_exit_1)
- connect(world, player, names, LocationName.star_road_4_region, LocationName.star_road_4_exit_2,
+ connect(world, LocationName.star_road_4_region, LocationName.star_road_4_exit_1)
+ connect(world, LocationName.star_road_4_region, LocationName.star_road_4_exit_2,
lambda state: (state.has(ItemName.mario_carry, player) and
(state.has(ItemName.yoshi_activate, player) or
(state.has(ItemName.green_switch_palace, player) and state.has(ItemName.red_switch_palace, player)))))
- connect(world, player, names, LocationName.star_road_5_region, LocationName.star_road_5_exit_1,
+ connect(world, LocationName.star_road_5_region, LocationName.star_road_5_exit_1,
lambda state: state.has(ItemName.p_switch, player))
- connect(world, player, names, LocationName.star_road_5_region, LocationName.star_road_5_exit_2,
+ connect(world, LocationName.star_road_5_region, LocationName.star_road_5_exit_2,
lambda state: (state.has(ItemName.mario_carry, player) and
state.has(ItemName.mario_climb, player) and
state.has(ItemName.p_switch, player) and
@@ -1050,26 +2057,29 @@ def connect_regions(world, player, level_to_tile_dict):
state.has(ItemName.red_switch_palace, player) and
state.has(ItemName.blue_switch_palace, player)))
- connect(world, player, names, LocationName.special_zone_1_region, LocationName.special_zone_1_exit_1,
+ connect(world, LocationName.special_zone_1_region, LocationName.special_zone_1_exit_1,
lambda state: (state.has(ItemName.mario_climb, player) and
(state.has(ItemName.p_switch, player) or
(state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3)))))
- connect(world, player, names, LocationName.special_zone_2_region, LocationName.special_zone_2_exit_1,
+ connect(world, LocationName.special_zone_2_region, LocationName.special_zone_2_exit_1,
lambda state: state.has(ItemName.p_balloon, player))
- connect(world, player, names, LocationName.special_zone_3_region, LocationName.special_zone_3_exit_1,
+ connect(world, LocationName.special_zone_3_region, LocationName.special_zone_3_exit_1,
lambda state: (state.has(ItemName.mario_climb, player) or
- state.has(ItemName.p_switch, player) or
- (state.has(ItemName.mario_run, player) and state.has(ItemName.progressive_powerup, player, 3))))
- connect(world, player, names, LocationName.special_zone_4_region, LocationName.special_zone_4_exit_1,
- lambda state: state.has(ItemName.progressive_powerup, player, 1))
- connect(world, player, names, LocationName.special_zone_5_region, LocationName.special_zone_5_exit_1,
+ state.has(ItemName.yoshi_activate, player)))
+ connect(world, LocationName.special_zone_4_region, LocationName.special_zone_4_exit_1,
+ lambda state: (state.has(ItemName.progressive_powerup, player, 2) or
+ state.has(ItemName.super_star_active, player)))
+ connect(world, LocationName.special_zone_5_region, LocationName.special_zone_5_exit_1,
lambda state: state.has(ItemName.progressive_powerup, player, 1))
- connect(world, player, names, LocationName.special_zone_6_region, LocationName.special_zone_6_exit_1,
+ connect(world, LocationName.special_zone_6_region, LocationName.special_zone_6_exit_1,
lambda state: state.has(ItemName.mario_swim, player))
- connect(world, player, names, LocationName.special_zone_7_region, LocationName.special_zone_7_exit_1,
- lambda state: state.has(ItemName.progressive_powerup, player, 1))
- connect(world, player, names, LocationName.special_zone_8_region, LocationName.special_zone_8_exit_1,
+ connect(world, LocationName.special_zone_7_region, LocationName.special_zone_7_exit_1,
lambda state: state.has(ItemName.progressive_powerup, player, 1))
+ connect(world, LocationName.special_zone_8_region, LocationName.special_zone_8_exit_1,
+ lambda state: ((state.has(ItemName.progressive_powerup, player, 1) and state.has(ItemName.mario_spin_jump, player)) or
+ state.has(ItemName.progressive_powerup, player, 3) or
+ state.has(ItemName.yoshi_activate, player) or
+ state.has(ItemName.mario_carry, player)))
@@ -1085,52 +2095,52 @@ def connect_regions(world, player, level_to_tile_dict):
current_tile_name = current_tile_data.levelName
if ("Star Road - " not in current_tile_name) and (" - Star Road" not in current_tile_name):
current_tile_name += " - Tile"
- connect(world, player, names, current_tile_name, current_level_data.levelName)
+ connect(world, current_tile_name, current_level_data.levelName)
# Connect Exit regions to next tile regions
if current_tile_data.exit1Path:
next_tile_id = current_tile_data.exit1Path.otherLevelID
- if world.swap_donut_gh_exits[player] and current_tile_id == 0x04:
+ if world.options.swap_donut_gh_exits and current_tile_id == 0x04:
next_tile_id = current_tile_data.exit2Path.otherLevelID
next_tile_name = level_info_dict[next_tile_id].levelName
if ("Star Road - " not in next_tile_name) and (" - Star Road" not in next_tile_name):
next_tile_name += " - Tile"
current_exit_name = (current_level_data.levelName + " - Normal Exit")
- connect(world, player, names, current_exit_name, next_tile_name)
+ connect(world, current_exit_name, next_tile_name)
if current_tile_data.exit2Path:
next_tile_id = current_tile_data.exit2Path.otherLevelID
- if world.swap_donut_gh_exits[player] and current_tile_id == 0x04:
+ if world.options.swap_donut_gh_exits and current_tile_id == 0x04:
next_tile_id = current_tile_data.exit1Path.otherLevelID
next_tile_name = level_info_dict[next_tile_id].levelName
if ("Star Road - " not in next_tile_name) and (" - Star Road" not in next_tile_name):
next_tile_name += " - Tile"
current_exit_name = (current_level_data.levelName + " - Secret Exit")
- connect(world, player, names, current_exit_name, next_tile_name)
-
- connect(world, player, names, LocationName.donut_plains_star_road, LocationName.star_road_donut)
- connect(world, player, names, LocationName.star_road_donut, LocationName.donut_plains_star_road)
- connect(world, player, names, LocationName.star_road_donut, LocationName.star_road_1_tile)
- connect(world, player, names, LocationName.vanilla_dome_star_road, LocationName.star_road_vanilla)
- connect(world, player, names, LocationName.star_road_vanilla, LocationName.vanilla_dome_star_road)
- connect(world, player, names, LocationName.star_road_vanilla, LocationName.star_road_2_tile)
- connect(world, player, names, LocationName.twin_bridges_star_road, LocationName.star_road_twin_bridges)
- connect(world, player, names, LocationName.star_road_twin_bridges, LocationName.twin_bridges_star_road)
- connect(world, player, names, LocationName.star_road_twin_bridges, LocationName.star_road_3_tile)
- connect(world, player, names, LocationName.forest_star_road, LocationName.star_road_forest)
- connect(world, player, names, LocationName.star_road_forest, LocationName.forest_star_road)
- connect(world, player, names, LocationName.star_road_forest, LocationName.star_road_4_tile)
- connect(world, player, names, LocationName.valley_star_road, LocationName.star_road_valley)
- connect(world, player, names, LocationName.star_road_valley, LocationName.valley_star_road)
- connect(world, player, names, LocationName.star_road_valley, LocationName.star_road_5_tile)
- connect(world, player, names, LocationName.star_road_special, LocationName.special_star_road)
- connect(world, player, names, LocationName.special_star_road, LocationName.star_road_special)
- connect(world, player, names, LocationName.special_star_road, LocationName.special_zone_1_tile)
+ connect(world, current_exit_name, next_tile_name)
+
+ connect(world, LocationName.donut_plains_star_road, LocationName.star_road_donut)
+ connect(world, LocationName.star_road_donut, LocationName.donut_plains_star_road)
+ connect(world, LocationName.star_road_donut, LocationName.star_road_1_tile)
+ connect(world, LocationName.vanilla_dome_star_road, LocationName.star_road_vanilla)
+ connect(world, LocationName.star_road_vanilla, LocationName.vanilla_dome_star_road)
+ connect(world, LocationName.star_road_vanilla, LocationName.star_road_2_tile)
+ connect(world, LocationName.twin_bridges_star_road, LocationName.star_road_twin_bridges)
+ connect(world, LocationName.star_road_twin_bridges, LocationName.twin_bridges_star_road)
+ connect(world, LocationName.star_road_twin_bridges, LocationName.star_road_3_tile)
+ connect(world, LocationName.forest_star_road, LocationName.star_road_forest)
+ connect(world, LocationName.star_road_forest, LocationName.forest_star_road)
+ connect(world, LocationName.star_road_forest, LocationName.star_road_4_tile)
+ connect(world, LocationName.valley_star_road, LocationName.star_road_valley)
+ connect(world, LocationName.star_road_valley, LocationName.valley_star_road)
+ connect(world, LocationName.star_road_valley, LocationName.star_road_5_tile)
+ connect(world, LocationName.star_road_special, LocationName.special_star_road)
+ connect(world, LocationName.special_star_road, LocationName.star_road_special)
+ connect(world, LocationName.special_star_road, LocationName.special_zone_1_tile)
- connect(world, player, names, LocationName.star_road_valley, LocationName.front_door_tile)
+ connect(world, LocationName.star_road_valley, LocationName.front_door_tile)
-def create_region(world: MultiWorld, player: int, active_locations, name: str, locations=None):
- ret = Region(name, player, world)
+def create_region(multiworld: MultiWorld, player: int, active_locations, name: str, locations=None):
+ ret = Region(name, player, multiworld)
if locations:
for locationName in locations:
loc_id = active_locations.get(locationName, 0)
@@ -1140,9 +2150,9 @@ def create_region(world: MultiWorld, player: int, active_locations, name: str, l
return ret
-def add_location_to_region(world: MultiWorld, player: int, active_locations, region_name: str, location_name: str,
+def add_location_to_region(multiworld: MultiWorld, player: int, active_locations, region_name: str, location_name: str,
rule: typing.Optional[typing.Callable] = None):
- region = world.get_region(region_name, player)
+ region = multiworld.get_region(region_name, player)
loc_id = active_locations.get(location_name, 0)
if loc_id:
location = SMWLocation(player, location_name, loc_id, region)
@@ -1151,23 +2161,8 @@ def add_location_to_region(world: MultiWorld, player: int, active_locations, reg
add_rule(location, rule)
-
-def connect(world: MultiWorld, player: int, used_names: typing.Dict[str, int], source: str, target: str,
+def connect(world: World, source: str, target: str,
rule: typing.Optional[typing.Callable] = None):
- source_region = world.get_region(source, player)
- target_region = world.get_region(target, player)
-
- if target not in used_names:
- used_names[target] = 1
- name = target
- else:
- used_names[target] += 1
- name = target + (' ' * used_names[target])
-
- connection = Entrance(player, name, source_region)
-
- if rule:
- connection.access_rule = rule
-
- source_region.exits.append(connection)
- connection.connect(target_region)
+ source_region: Region = world.get_region(source)
+ target_region: Region = world.get_region(target)
+ source_region.connect(target_region, rule=rule)
diff --git a/worlds/smw/Rom.py b/worlds/smw/Rom.py
index 0f5ec7e4f066..36078d4622b9 100644
--- a/worlds/smw/Rom.py
+++ b/worlds/smw/Rom.py
@@ -1,6 +1,7 @@
import Utils
+from worlds.AutoWorld import World
from worlds.Files import APDeltaPatch
-from .Aesthetics import generate_shuffled_header_data, generate_shuffled_ow_palettes
+from .Aesthetics import generate_shuffled_header_data, generate_shuffled_ow_palettes, generate_curated_level_palette_data, generate_curated_map_palette_data, generate_shuffled_sfx
from .Levels import level_info_dict, full_bowser_rooms, standard_bowser_rooms, submap_boss_rooms, ow_boss_rooms
from .Names.TextBox import generate_goal_text, title_text_mapping, generate_text_box
@@ -10,38 +11,48 @@
import hashlib
import os
import math
+import pkgutil
ability_rom_data = {
- 0xBC0003: [[0x1F2C, 0x7]], # Run 0x80
- 0xBC0004: [[0x1F2C, 0x6]], # Carry 0x40
- 0xBC0005: [[0x1F2C, 0x2]], # Swim 0x04
- 0xBC0006: [[0x1F2C, 0x3]], # Spin Jump 0x08
- 0xBC0007: [[0x1F2C, 0x5]], # Climb 0x20
- 0xBC0008: [[0x1F2C, 0x1]], # Yoshi 0x02
- 0xBC0009: [[0x1F2C, 0x4]], # P-Switch 0x10
+ 0xBC0003: [[0x1F1C, 0x7]], # Run 0x80
+ 0xBC0004: [[0x1F1C, 0x6]], # Carry 0x40
+ 0xBC0005: [[0x1F1C, 0x2]], # Swim 0x04
+ 0xBC0006: [[0x1F1C, 0x3]], # Spin Jump 0x08
+ 0xBC0007: [[0x1F1C, 0x5]], # Climb 0x20
+ 0xBC0008: [[0x1F1C, 0x1]], # Yoshi 0x02
+ 0xBC0009: [[0x1F1C, 0x4]], # P-Switch 0x10
#0xBC000A: [[]]
0xBC000B: [[0x1F2D, 0x3]], # P-Balloon 0x08
- 0xBC000D: [[0x1F2D, 0x4]], # Super Star 0x10
+ 0xBC000D: [[0x1F2D, 0x4]] # Super Star 0x10
}
+icon_rom_data = {
+ 0xBC0002: [0x1B00C], # Yoshi Egg
+ 0xBC0012: [0x1B00E], # Boss Token
-item_rom_data = {
- 0xBC0001: [0x18E4, 0x1], # 1-Up Mushroom
-
- 0xBC0002: [0x1F24, 0x1, 0x1F], # Yoshi Egg
- 0xBC0012: [0x1F26, 0x1, 0x09], # Boss Token
+ 0xBC0017: [0x1B004], # 1 coin
+ 0xBC0018: [0x1B006], # 5 coins
+ 0xBC0019: [0x1B008], # 10 coins
+ 0xBC001A: [0x1B00A], # 50 coins
- 0xBC000E: [0x1F28, 0x1, 0x1C], # Yellow Switch Palace
- 0xBC000F: [0x1F27, 0x1, 0x1C], # Green Switch Palace
- 0xBC0010: [0x1F2A, 0x1, 0x1C], # Red Switch Palace
- 0xBC0011: [0x1F29, 0x1, 0x1C], # Blue Switch Palace
+ 0xBC0001: [0x1B010] # 1-Up Mushroom
+}
+
+item_rom_data = {
+ 0xBC000E: [0x1F28, 0x1, 0x1C], # Yellow Switch Palace
+ 0xBC000F: [0x1F27, 0x1, 0x1C], # Green Switch Palace
+ 0xBC0010: [0x1F2A, 0x1, 0x1C], # Red Switch Palace
+ 0xBC0011: [0x1F29, 0x1, 0x1C], # Blue Switch Palace
+ 0xBC001B: [0x1F1E, 0x80, 0x39] # Special Zone Clear
}
trap_rom_data = {
- 0xBC0013: [0x0086, 0x1, 0x0E], # Ice Trap
+ 0xBC0013: [0x0086, 0x1, 0x0E], # Ice Trap
0xBC0014: [0x18BD, 0x7F, 0x18], # Stun Trap
- 0xBC0016: [0x0F31, 0x1], # Timer Trap
+ 0xBC0016: [0x0F31, 0x1], # Timer Trap
+ 0xBC001C: [0x18B4, 0x1, 0x44], # Reverse controls trap
+ 0xBC001D: [0x18B7, 0x1], # Thwimp Trap
}
@@ -109,7 +120,7 @@ def handle_ability_code(rom):
rom.write_bytes(RUN_SUB_ADDR + 0x04, bytearray([0xC8])) # INY
rom.write_bytes(RUN_SUB_ADDR + 0x05, bytearray([0xA9, 0x70])) # LDA #70
rom.write_bytes(RUN_SUB_ADDR + 0x07, bytearray([0xAA])) # TAX
- rom.write_bytes(RUN_SUB_ADDR + 0x08, bytearray([0xAD, 0x2C, 0x1F])) # LDA $1F2C
+ rom.write_bytes(RUN_SUB_ADDR + 0x08, bytearray([0xAD, 0x1C, 0x1F])) # LDA $1F1C
rom.write_bytes(RUN_SUB_ADDR + 0x0B, bytearray([0x89, 0x80])) # BIT #80
rom.write_bytes(RUN_SUB_ADDR + 0x0D, bytearray([0xF0, 0x04])) # BEQ +0x04
rom.write_bytes(RUN_SUB_ADDR + 0x0F, bytearray([0x8A])) # TXA
@@ -126,7 +137,7 @@ def handle_ability_code(rom):
PURPLE_BLOCK_CARRY_SUB_ADDR = 0x01BA28
rom.write_bytes(PURPLE_BLOCK_CARRY_SUB_ADDR + 0x00, bytearray([0x08])) # PHP
- rom.write_bytes(PURPLE_BLOCK_CARRY_SUB_ADDR + 0x01, bytearray([0xAD, 0x2C, 0x1F])) # LDA $1F2C
+ rom.write_bytes(PURPLE_BLOCK_CARRY_SUB_ADDR + 0x01, bytearray([0xAD, 0x1C, 0x1F])) # LDA $1F1C
rom.write_bytes(PURPLE_BLOCK_CARRY_SUB_ADDR + 0x04, bytearray([0x89, 0x40])) # BIT #40
rom.write_bytes(PURPLE_BLOCK_CARRY_SUB_ADDR + 0x06, bytearray([0xF0, 0x09])) # BEQ +0x09
rom.write_bytes(PURPLE_BLOCK_CARRY_SUB_ADDR + 0x08, bytearray([0x28])) # PLP
@@ -145,7 +156,7 @@ def handle_ability_code(rom):
SPRINGBOARD_CARRY_SUB_ADDR = 0x01BA40
rom.write_bytes(SPRINGBOARD_CARRY_SUB_ADDR + 0x00, bytearray([0x48])) # PHA
rom.write_bytes(SPRINGBOARD_CARRY_SUB_ADDR + 0x01, bytearray([0x08])) # PHP
- rom.write_bytes(SPRINGBOARD_CARRY_SUB_ADDR + 0x02, bytearray([0xAD, 0x2C, 0x1F])) # LDA $1F2C
+ rom.write_bytes(SPRINGBOARD_CARRY_SUB_ADDR + 0x02, bytearray([0xAD, 0x1C, 0x1F])) # LDA $1F1C
rom.write_bytes(SPRINGBOARD_CARRY_SUB_ADDR + 0x05, bytearray([0x89, 0x40])) # BIT #40
rom.write_bytes(SPRINGBOARD_CARRY_SUB_ADDR + 0x07, bytearray([0xF0, 0x08])) # BEQ +0x08
rom.write_bytes(SPRINGBOARD_CARRY_SUB_ADDR + 0x09, bytearray([0xA9, 0x0B])) # LDA #0B
@@ -157,7 +168,7 @@ def handle_ability_code(rom):
# End Springboard Carry
# Shell Carry
- rom.write_bytes(0xAA66, bytearray([0xAD, 0x2C, 0x1F])) # LDA $1F2C
+ rom.write_bytes(0xAA66, bytearray([0xAD, 0x1C, 0x1F])) # LDA $1F1C
rom.write_bytes(0xAA69, bytearray([0x89, 0x40])) # BIT #40
rom.write_bytes(0xAA6B, bytearray([0xF0, 0x07])) # BEQ +0x07
rom.write_bytes(0xAA6D, bytearray([0x22, 0x60, 0xBA, 0x03])) # JSL $03BA60
@@ -180,7 +191,7 @@ def handle_ability_code(rom):
YOSHI_CARRY_SUB_ADDR = 0x01BA70
rom.write_bytes(YOSHI_CARRY_SUB_ADDR + 0x00, bytearray([0x08])) # PHP
- rom.write_bytes(YOSHI_CARRY_SUB_ADDR + 0x01, bytearray([0xAD, 0x2C, 0x1F])) # LDA $1F2C
+ rom.write_bytes(YOSHI_CARRY_SUB_ADDR + 0x01, bytearray([0xAD, 0x1C, 0x1F])) # LDA $1F1C
rom.write_bytes(YOSHI_CARRY_SUB_ADDR + 0x04, bytearray([0x89, 0x40])) # BIT #40
rom.write_bytes(YOSHI_CARRY_SUB_ADDR + 0x06, bytearray([0xF0, 0x0A])) # BEQ +0x0A
rom.write_bytes(YOSHI_CARRY_SUB_ADDR + 0x08, bytearray([0xA9, 0x12])) # LDA #12
@@ -197,7 +208,7 @@ def handle_ability_code(rom):
CLIMB_SUB_ADDR = 0x01BA88
rom.write_bytes(CLIMB_SUB_ADDR + 0x00, bytearray([0x08])) # PHP
- rom.write_bytes(CLIMB_SUB_ADDR + 0x01, bytearray([0xAD, 0x2C, 0x1F])) # LDA $1F2C
+ rom.write_bytes(CLIMB_SUB_ADDR + 0x01, bytearray([0xAD, 0x1C, 0x1F])) # LDA $1F1C
rom.write_bytes(CLIMB_SUB_ADDR + 0x04, bytearray([0x89, 0x20])) # BIT #20
rom.write_bytes(CLIMB_SUB_ADDR + 0x06, bytearray([0xF0, 0x09])) # BEQ +0x09
rom.write_bytes(CLIMB_SUB_ADDR + 0x08, bytearray([0xA5, 0x8B])) # LDA $8B
@@ -213,7 +224,7 @@ def handle_ability_code(rom):
CLIMB_ROPE_SUB_ADDR = 0x01BC70
rom.write_bytes(CLIMB_ROPE_SUB_ADDR + 0x00, bytearray([0x08])) # PHP
- rom.write_bytes(CLIMB_ROPE_SUB_ADDR + 0x01, bytearray([0xAD, 0x2C, 0x1F])) # LDA $1F2C
+ rom.write_bytes(CLIMB_ROPE_SUB_ADDR + 0x01, bytearray([0xAD, 0x1C, 0x1F])) # LDA $1F1C
rom.write_bytes(CLIMB_ROPE_SUB_ADDR + 0x04, bytearray([0x89, 0x20])) # BIT #20
rom.write_bytes(CLIMB_ROPE_SUB_ADDR + 0x06, bytearray([0xF0, 0x07])) # BEQ +0x07
rom.write_bytes(CLIMB_ROPE_SUB_ADDR + 0x08, bytearray([0x28])) # PLP
@@ -230,7 +241,7 @@ def handle_ability_code(rom):
P_SWITCH_SUB_ADDR = 0x01BAA0
rom.write_bytes(P_SWITCH_SUB_ADDR + 0x00, bytearray([0x08])) # PHP
- rom.write_bytes(P_SWITCH_SUB_ADDR + 0x01, bytearray([0xAD, 0x2C, 0x1F])) # LDA $1F2C
+ rom.write_bytes(P_SWITCH_SUB_ADDR + 0x01, bytearray([0xAD, 0x1C, 0x1F])) # LDA $1F1C
rom.write_bytes(P_SWITCH_SUB_ADDR + 0x04, bytearray([0x89, 0x10])) # BIT #10
rom.write_bytes(P_SWITCH_SUB_ADDR + 0x06, bytearray([0xF0, 0x04])) # BEQ +0x04
rom.write_bytes(P_SWITCH_SUB_ADDR + 0x08, bytearray([0xA9, 0xB0])) # LDA #B0
@@ -242,7 +253,7 @@ def handle_ability_code(rom):
# End P-Switch
# Spin Jump
- rom.write_bytes(0x5645, bytearray([0xAD, 0x2C, 0x1F])) # LDA $1F2C
+ rom.write_bytes(0x5645, bytearray([0xAD, 0x1C, 0x1F])) # LDA $1F1C
rom.write_bytes(0x5648, bytearray([0x89, 0x08])) # BIT #08
rom.write_bytes(0x564A, bytearray([0xF0, 0x12])) # BEQ +0x12
rom.write_bytes(0x564C, bytearray([0x22, 0xB8, 0xBA, 0x03])) # JSL $03BAB8
@@ -264,7 +275,7 @@ def handle_ability_code(rom):
SPIN_JUMP_WATER_SUB_ADDR = 0x01BBF8
rom.write_bytes(SPIN_JUMP_WATER_SUB_ADDR + 0x00, bytearray([0x08])) # PHP
- rom.write_bytes(SPIN_JUMP_WATER_SUB_ADDR + 0x01, bytearray([0xAD, 0x2C, 0x1F])) # LDA $1F2C
+ rom.write_bytes(SPIN_JUMP_WATER_SUB_ADDR + 0x01, bytearray([0xAD, 0x1C, 0x1F])) # LDA $1F1C
rom.write_bytes(SPIN_JUMP_WATER_SUB_ADDR + 0x04, bytearray([0x89, 0x08])) # BIT #08
rom.write_bytes(SPIN_JUMP_WATER_SUB_ADDR + 0x06, bytearray([0xF0, 0x09])) # BEQ +0x09
rom.write_bytes(SPIN_JUMP_WATER_SUB_ADDR + 0x08, bytearray([0x1A])) # INC
@@ -281,7 +292,7 @@ def handle_ability_code(rom):
SPIN_JUMP_SPRING_SUB_ADDR = 0x01BC0C
rom.write_bytes(SPIN_JUMP_SPRING_SUB_ADDR + 0x00, bytearray([0x08])) # PHP
- rom.write_bytes(SPIN_JUMP_SPRING_SUB_ADDR + 0x01, bytearray([0xAD, 0x2C, 0x1F])) # LDA $1F2C
+ rom.write_bytes(SPIN_JUMP_SPRING_SUB_ADDR + 0x01, bytearray([0xAD, 0x1C, 0x1F])) # LDA $1F1C
rom.write_bytes(SPIN_JUMP_SPRING_SUB_ADDR + 0x04, bytearray([0x89, 0x08])) # BIT #08
rom.write_bytes(SPIN_JUMP_SPRING_SUB_ADDR + 0x06, bytearray([0xF0, 0x05])) # BEQ +0x05
rom.write_bytes(SPIN_JUMP_SPRING_SUB_ADDR + 0x08, bytearray([0xA9, 0x01])) # LDA #01
@@ -297,7 +308,7 @@ def handle_ability_code(rom):
SWIM_SUB_ADDR = 0x01BAC8
rom.write_bytes(SWIM_SUB_ADDR + 0x00, bytearray([0x48])) # PHA
rom.write_bytes(SWIM_SUB_ADDR + 0x01, bytearray([0x08])) # PHP
- rom.write_bytes(SWIM_SUB_ADDR + 0x02, bytearray([0xAD, 0x2C, 0x1F])) # LDA $1F2C
+ rom.write_bytes(SWIM_SUB_ADDR + 0x02, bytearray([0xAD, 0x1C, 0x1F])) # LDA $1F1C
rom.write_bytes(SWIM_SUB_ADDR + 0x05, bytearray([0x89, 0x04])) # BIT #04
rom.write_bytes(SWIM_SUB_ADDR + 0x07, bytearray([0xF0, 0x0C])) # BEQ +0x0C
rom.write_bytes(SWIM_SUB_ADDR + 0x09, bytearray([0x28])) # PLP
@@ -321,7 +332,7 @@ def handle_ability_code(rom):
SWIM_SUB_ADDR = 0x01BAE8
rom.write_bytes(SWIM_SUB_ADDR + 0x00, bytearray([0x48])) # PHA
rom.write_bytes(SWIM_SUB_ADDR + 0x01, bytearray([0x08])) # PHP
- rom.write_bytes(SWIM_SUB_ADDR + 0x02, bytearray([0xAD, 0x2C, 0x1F])) # LDA $1F2C
+ rom.write_bytes(SWIM_SUB_ADDR + 0x02, bytearray([0xAD, 0x1C, 0x1F])) # LDA $1F1C
rom.write_bytes(SWIM_SUB_ADDR + 0x05, bytearray([0x89, 0x04])) # BIT #04
rom.write_bytes(SWIM_SUB_ADDR + 0x07, bytearray([0xF0, 0x0A])) # BEQ +0x0A
rom.write_bytes(SWIM_SUB_ADDR + 0x09, bytearray([0x28])) # PLP
@@ -344,7 +355,7 @@ def handle_ability_code(rom):
YOSHI_SUB_ADDR = 0x01BB08
rom.write_bytes(YOSHI_SUB_ADDR + 0x00, bytearray([0x08])) # PHP
- rom.write_bytes(YOSHI_SUB_ADDR + 0x01, bytearray([0xAD, 0x2C, 0x1F])) # LDA $1F2C
+ rom.write_bytes(YOSHI_SUB_ADDR + 0x01, bytearray([0xAD, 0x1C, 0x1F])) # LDA $1F1C
rom.write_bytes(YOSHI_SUB_ADDR + 0x04, bytearray([0x89, 0x02])) # BIT #02
rom.write_bytes(YOSHI_SUB_ADDR + 0x06, bytearray([0xF0, 0x06])) # BEQ +0x06
rom.write_bytes(YOSHI_SUB_ADDR + 0x08, bytearray([0x28])) # PLP
@@ -366,7 +377,7 @@ def handle_ability_code(rom):
YOSHI_SUB_ADDR = 0x01BB20
rom.write_bytes(YOSHI_SUB_ADDR + 0x00, bytearray([0x08])) # PHP
rom.write_bytes(YOSHI_SUB_ADDR + 0x01, bytearray([0x9C, 0x1E, 0x14])) # STZ $141E
- rom.write_bytes(YOSHI_SUB_ADDR + 0x04, bytearray([0xAD, 0x2C, 0x1F])) # LDA $1F2C
+ rom.write_bytes(YOSHI_SUB_ADDR + 0x04, bytearray([0xAD, 0x1C, 0x1F])) # LDA $1F1C
rom.write_bytes(YOSHI_SUB_ADDR + 0x07, bytearray([0x89, 0x02])) # BIT #02
rom.write_bytes(YOSHI_SUB_ADDR + 0x09, bytearray([0xF0, 0x05])) # BEQ +0x05
rom.write_bytes(YOSHI_SUB_ADDR + 0x0B, bytearray([0x28])) # PLP
@@ -576,18 +587,17 @@ def handle_yoshi_box(rom):
def handle_bowser_damage(rom):
- rom.write_bytes(0x1A509, bytearray([0x20, 0x50, 0xBC])) # JSR $03BC50
+ rom.write_bytes(0x1A509, bytearray([0x5C, 0x50, 0xBC, 0x03])) # JML $03BC50
BOWSER_BALLS_SUB_ADDR = 0x01BC50
- rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x00, bytearray([0x08])) # PHP
- rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x01, bytearray([0xAD, 0x48, 0x0F])) # LDA $F48
- rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x04, bytearray([0xCF, 0xA1, 0xBF, 0x03])) # CMP $03BFA1
- rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x08, bytearray([0x90, 0x06])) # BCC +0x06
- rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x0A, bytearray([0x28])) # PLP
- rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x0B, bytearray([0xEE, 0xB8, 0x14])) # INC $14B8
- rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x0E, bytearray([0x80, 0x01])) # BRA +0x01
- rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x10, bytearray([0x28])) # PLP
- rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x11, bytearray([0x60])) # RTS
+ rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x0000, bytearray([0xAF, 0xA0, 0xBF, 0x03])) # bowser_infinite_balls: lda.l goal_setting
+ rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x0004, bytearray([0xD0, 0x0C])) # bne .nope
+ rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x0006, bytearray([0xAD, 0x48, 0x0F])) # lda $0F48
+ rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x0009, bytearray([0xCF, 0xA1, 0xBF, 0x03])) # cmp.l required_bosses_setting
+ rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x000D, bytearray([0x90, 0x03])) # bcc .nope
+ rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x000F, bytearray([0xEE, 0xB8, 0x14])) # inc $14B8
+ rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x0012, bytearray([0xAD, 0xB8, 0x14])) # .nope lda $14B8
+ rom.write_bytes(BOWSER_BALLS_SUB_ADDR + 0x0015, bytearray([0x5C, 0x0F, 0xA5, 0x03])) # jml $03A50F
return
@@ -654,6 +664,7 @@ def handle_level_shuffle(rom, active_level_dict):
for level_id, tile_id in active_level_dict.items():
rom.write_byte(0x37F70 + level_id, tile_id)
+ rom.write_byte(0x37F00 + tile_id, level_id)
def handle_collected_paths(rom):
@@ -673,38 +684,2155 @@ def handle_collected_paths(rom):
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):
+ rom.write_bytes(0x285BA, bytearray([0x22, 0x80, 0xF4, 0x0F])) # JSL $0FF480
+
+ VERTICAL_SCROLL_SUB_ADDR = 0x7F480
+ rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x0000, bytearray([0x4A])) # vertical_scroll: lsr
+ rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x0001, bytearray([0x4A])) # lsr
+ rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x0002, bytearray([0x4A])) # lsr
+ rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x0003, bytearray([0x4A])) # lsr
+ rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x0004, bytearray([0x08])) # php
+ rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x0005, bytearray([0xC9, 0x02])) # cmp #$02
+ rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x0007, bytearray([0xD0, 0x0B])) # bne +
+ rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x0009, bytearray([0xC2, 0x10])) # rep #$10
+ rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x000B, bytearray([0xDA])) # phx
+ rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x000C, bytearray([0xAE, 0x0B, 0x01])) # ldx $010B
+ rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x000F, bytearray([0xBF, 0x00, 0xF5, 0x0F])) # lda.l vertical_scroll_levels,x
+ rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x0013, bytearray([0xFA])) # plx
+ rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x0014, bytearray([0x28])) # + plp
+ rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x0015, bytearray([0x6B])) # rtl
+
+ vertical_scroll_table = [
+ 0x02, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, # Levels 000-00F
+ 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, # Levels 010-01F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 020-02F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 030-03F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 040-04F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 050-05F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 060-06F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 070-07F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 080-08F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 090-09F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 0A0-0AF
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 0B0-0BF
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 0C0-0CF
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, # Levels 0D0-0DF
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, # Levels 0E0-0EF
+ 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, # Levels 0F0-0FF
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x01, # Levels 100-10F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 110-11F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 120-12F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 130-13F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 140-14F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 150-15F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 160-16F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 170-17F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 180-18F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 190-19F
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 1A0-1AF
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 1B0-1BF
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 1C0-1CF
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 1D0-1DF
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, # Levels 1E0-1EF
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02] # Levels 1F0-1FF
+
+ rom.write_bytes(0x7F500, bytes(vertical_scroll_table))
+
+
+def handle_bonus_block(rom):
+ rom.write_bytes(0x71A5, bytearray([0x5C, 0x19, 0x8E, 0x05])) # JML $058E19
+
+ BONUS_BLOCK_ADDR = 0x28E19
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x00, bytearray([0xA9, 0x06])) # LDA #$06
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x02, bytearray([0xAC, 0xC0, 0x0D])) # LDY $0DC0
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x05, bytearray([0xD0, 0x1E])) # BNE IGNORE
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x07, bytearray([0xDA])) # PHX
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x08, bytearray([0xAD, 0xBF, 0x13])) # LDA $13BF
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x0B, bytearray([0x4A])) # LSR
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x0C, bytearray([0x4A])) # LSR
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x0D, bytearray([0x4A])) # LSR
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x0E, bytearray([0x48])) # PHA
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x0F, bytearray([0xAD, 0xBF, 0x13])) # LDA $13BF
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x12, bytearray([0x29, 0x07])) # AND #$07
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x14, bytearray([0xAA])) # TAX
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x15, bytearray([0xBF, 0x5B, 0xB3, 0x05])) # LDA $05B35B,x
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x19, bytearray([0xFA])) # PLX
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x1A, bytearray([0x1F, 0x00, 0xA0, 0x7F])) # ORA $7FA000,x
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x1E, bytearray([0x9F, 0x00, 0xA0, 0x7F])) # STA $7FA000,x
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x22, bytearray([0xFA])) # PLX
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x23, bytearray([0xA9, 0x05])) # LDA #$05
+ rom.write_bytes(BONUS_BLOCK_ADDR + 0x25, bytearray([0x5C, 0xD0, 0xF1, 0x00])) # IGNORE: JML $00F1D0
+
+
+def handle_blocksanity(rom):
+ import json
+ blocksanity_data = pkgutil.get_data(__name__, f"data/blocksanity.json").decode("utf-8")
+ blocksanity_data = json.loads(blocksanity_data)
+ blocksanity_coords = bytearray([])
+ blocksanity_bytes = bytearray([])
+
+ block_count = 0
+ entries = 0
+ for level_name, level_data in blocksanity_data.items():
+ # Calculate blocksanity pointer
+ if level_data == []:
+ # Skip if the level doesn't have any data
+ blocksanity_bytes += bytearray([0xFF, 0xFF])
+ continue
+ level_ptr = 0x80C0 + entries
+ blocksanity_bytes += bytearray([level_ptr & 0xFF, (level_ptr >> 8) & 0xFF])
+
+ # Get block data
+ block_coords = bytearray([])
+ for x in range(len(level_data)):
+ block_coords += bytearray([
+ int(level_data[x][1], 16) & 0xFF, (int(level_data[x][1], 16) >> 8) & 0xFF,
+ int(level_data[x][2], 16) & 0xFF, (int(level_data[x][2], 16) >> 8) & 0xFF,
+ block_count & 0xFF, (block_count >> 8) & 0xFF])
+ entries += 6
+ block_count += 1
+ block_coords += bytearray([0xFF, 0xFF])
+ entries += 2
+
+ blocksanity_coords += block_coords
+
+ blocksanity_bytes += blocksanity_coords
+
+ rom.write_bytes(0x80000, blocksanity_bytes)
+ rom.write_bytes(0x071D0, bytearray([0x5C, 0x00, 0xF7, 0x0F])) # org $00F1D0 : jml blocksanity_main
+ rom.write_bytes(0x0AD59, bytearray([0x5C, 0x15, 0xF7, 0x0F])) # org $01AD5C : jml blocksanity_flying_init
+ rom.write_bytes(0x0AE16, bytearray([0x22, 0x39, 0xF7, 0x0F])) # org $01AE16 : jsl blocksanity_flying_main
+
+ BLOCKSANITY_ADDR = 0x7F700
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0000, bytearray([0x85, 0x05])) # blocksanity_main: sta $05
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0002, bytearray([0x8B])) # phb
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0003, bytearray([0xA9, 0x10])) # lda.b #blocksanity_pointers>>16
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0005, bytearray([0x48])) # pha
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0006, bytearray([0xAB])) # plb
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0007, bytearray([0x5A])) # phy
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0008, bytearray([0x20, 0x63, 0xF7])) # jsr process_block
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x000B, bytearray([0x7A])) # ply
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x000C, bytearray([0xAB])) # plb
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x000D, bytearray([0xA5, 0x05])) # lda $05
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x000F, bytearray([0xC9, 0x05])) # cmp #$05
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0011, bytearray([0x5C, 0xD4, 0xF1, 0x00])) # jml $00F1D4
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0015, bytearray([0xB5, 0xD8])) # blocksanity_flying_init: lda $D8,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0017, bytearray([0x29, 0xF0])) # and #$F0
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0019, bytearray([0x9F, 0x20, 0xB8, 0x7F])) # sta !sprite_blocksanity_y_lo,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x001D, bytearray([0xBD, 0xD4, 0x14])) # lda $14D4,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0020, bytearray([0x9F, 0x30, 0xB8, 0x7F])) # sta !sprite_blocksanity_y_hi,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0024, bytearray([0xBD, 0xE0, 0x14])) # lda $14E0,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0027, bytearray([0x9F, 0x10, 0xB8, 0x7F])) # sta !sprite_blocksanity_x_hi,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x002B, bytearray([0xB5, 0xE4])) # lda $E4,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x002D, bytearray([0x29, 0xF0])) # and #$F0
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x002F, bytearray([0x9F, 0x00, 0xB8, 0x7F])) # sta !sprite_blocksanity_x_lo,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0033, bytearray([0x4A])) # lsr
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0034, bytearray([0x4A])) # lsr
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0035, bytearray([0x5C, 0x5D, 0xAD, 0x01])) # jml $01AD5D
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0039, bytearray([0xBF, 0x20, 0xB8, 0x7F])) # blocksanity_flying_main: lda !sprite_blocksanity_y_lo,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x003D, bytearray([0x85, 0x98])) # sta $98
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x003F, bytearray([0xBF, 0x30, 0xB8, 0x7F])) # lda !sprite_blocksanity_y_hi,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0043, bytearray([0x85, 0x99])) # sta $99
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0045, bytearray([0xBF, 0x00, 0xB8, 0x7F])) # lda !sprite_blocksanity_x_lo,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0049, bytearray([0x85, 0x9A])) # sta $9A
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x004B, bytearray([0xBF, 0x10, 0xB8, 0x7F])) # lda !sprite_blocksanity_x_hi,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x004F, bytearray([0x85, 0x9B])) # sta $9B
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0051, bytearray([0x8B])) # phb
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0052, bytearray([0xA9, 0x10])) # lda.b #blocksanity_pointers>>16
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0054, bytearray([0x48])) # pha
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0055, bytearray([0xAB])) # plb
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0056, bytearray([0x5A])) # phy
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0057, bytearray([0xDA])) # phx
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0058, bytearray([0x20, 0x63, 0xF7])) # jsr process_block
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x005B, bytearray([0xFA])) # plx
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x005C, bytearray([0x7A])) # ply
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x005D, bytearray([0xAB])) # plb
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x005E, bytearray([0xB5, 0xE4])) # lda $E4,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0060, bytearray([0x85, 0x9A])) # sta $9A
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0062, bytearray([0x6B])) # rtl
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0063, bytearray([0xA9, 0x0F])) # process_block: lda #$0F
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0065, bytearray([0x14, 0x98])) # trb $98
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0067, bytearray([0x14, 0x9A])) # trb $9A
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0069, bytearray([0xC2, 0x30])) # rep #$30
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x006B, bytearray([0xA5, 0x60])) # lda $60
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x006D, bytearray([0x29, 0xFF, 0x00])) # and #$00FF
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0070, bytearray([0x0A])) # asl
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0071, bytearray([0x18])) # clc
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0072, bytearray([0x69, 0x00, 0x80])) # adc.w #blocksanity_pointers
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0075, bytearray([0x48])) # pha
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0076, bytearray([0xA0, 0x00, 0x00])) # ldy #$0000
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0079, bytearray([0xB3, 0x01])) # lda ($01,s),y
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x007B, bytearray([0x48])) # pha
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x007C, bytearray([0xB3, 0x01])) # .loop lda ($01,s),y
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x007E, bytearray([0xC9, 0xFF, 0xFF])) # cmp #$FFFF
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0081, bytearray([0xF0, 0x16])) # beq .return
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0083, bytearray([0xC5, 0x9A])) # cmp $9A
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0085, bytearray([0xD0, 0x0A])) # bne .next_block_x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0087, bytearray([0xC8])) # iny
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0088, bytearray([0xC8])) # iny
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0089, bytearray([0xB3, 0x01])) # lda ($01,s),y
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x008B, bytearray([0xC5, 0x98])) # cmp $98
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x008D, bytearray([0xF0, 0x0F])) # beq .valid_block
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x008F, bytearray([0x80, 0x02])) # bra .next_block_y
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0091, bytearray([0xC8])) # .next_block_x iny
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0092, bytearray([0xC8])) # iny
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0093, bytearray([0xC8])) # .next_block_y iny
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0094, bytearray([0xC8])) # iny
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0095, bytearray([0xC8])) # iny
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0096, bytearray([0xC8])) # iny
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0097, bytearray([0x80, 0xE3])) # bra .loop
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x0099, bytearray([0x68])) # .return pla
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x009A, bytearray([0x68])) # pla
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x009B, bytearray([0xE2, 0x30])) # sep #$30
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x009D, bytearray([0x60])) # rts
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x009E, bytearray([0xC8])) # .valid_block iny
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x009F, bytearray([0xC8])) # iny
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00A0, bytearray([0xB3, 0x01])) # lda ($01,s),y
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00A2, bytearray([0xAA])) # tax
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00A3, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00A5, bytearray([0xDA])) # phx
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00A6, bytearray([0xBF, 0x00, 0xA4, 0x7F])) # lda !blocksanity_data_flags,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00AA, bytearray([0xD0, 0x08])) # bne .processed
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00AC, bytearray([0x1A])) # inc
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00AD, bytearray([0x9F, 0x00, 0xA4, 0x7F])) # sta !blocksanity_data_flags,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00B1, bytearray([0x20, 0xBA, 0xF7])) # jsr blocksanity_check_flags
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00B4, bytearray([0xFA])) # .processed plx
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00B5, bytearray([0xFA])) # plx
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00B6, bytearray([0xFA])) # plx
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00B7, bytearray([0xE2, 0x10])) # sep #$10
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00B9, bytearray([0x60])) # rts
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00BA, bytearray([0xC2, 0x20])) # blocksanity_check_flags: rep #$20
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00BC, bytearray([0xA0, 0x00, 0x00])) # ldy #$0000
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00BF, bytearray([0xB3, 0x05])) # .loop lda ($05,s),y
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00C1, bytearray([0xC9, 0xFF, 0xFF])) # cmp #$FFFF
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00C4, bytearray([0xF0, 0x14])) # beq .check
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00C6, bytearray([0xC8])) # iny
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00C7, bytearray([0xC8])) # iny
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00C8, bytearray([0xC8])) # iny
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00C9, bytearray([0xC8])) # iny
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00CA, bytearray([0xB3, 0x05])) # lda ($05,s),y
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00CC, bytearray([0xAA])) # tax
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00CD, bytearray([0xBF, 0x00, 0xA4, 0x7F])) # lda !blocksanity_data_flags,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00D1, bytearray([0x29, 0xFF, 0x00])) # and #$00FF
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00D4, bytearray([0xF0, 0x22])) # beq .invalid
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00D6, bytearray([0xC8])) # iny
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00D7, bytearray([0xC8])) # iny
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00D8, bytearray([0x80, 0xE5])) # bra .loop
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00DA, bytearray([0xE2, 0x20])) # .check sep #$20
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00DC, bytearray([0xA9, 0x00])) # lda #$00
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00DE, bytearray([0xEB])) # xba
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00DF, bytearray([0xA5, 0x60])) # lda $60
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00E1, bytearray([0x4A])) # lsr
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00E2, bytearray([0x4A])) # lsr
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00E3, bytearray([0x4A])) # lsr
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00E4, bytearray([0xA8])) # tay
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00E5, bytearray([0xA5, 0x60])) # lda $60
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00E7, bytearray([0x29, 0x07])) # and #$07
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00E9, bytearray([0xAA])) # tax
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00EA, bytearray([0xBF, 0x5B, 0xB3, 0x05])) # lda.l $05B35B,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00EE, bytearray([0xBB])) # tyx
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00EF, bytearray([0x1F, 0x10, 0xA0, 0x7F])) # ora !blocksanity_flags,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00F3, bytearray([0x9F, 0x10, 0xA0, 0x7F])) # sta !blocksanity_flags,x
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00F7, bytearray([0x60])) # rts
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00F8, bytearray([0xE2, 0x20])) # .invalid sep #$20
+ rom.write_bytes(BLOCKSANITY_ADDR + 0x00FA, bytearray([0x60])) # rts
+
+def handle_ram(rom):
+ rom.write_byte(0x07FD8, 0x02) # Expand SRAM
+ rom.write_bytes(0x01CF5, bytearray([0x5C, 0x00, 0xF2, 0x0F])) # org $009CF5 : jml init_sram
+ rom.write_bytes(0x01C0F, bytearray([0x5C, 0x00, 0xF3, 0x0F])) # org $009C0F : jml save_sram
+ rom.write_bytes(0x013BB, bytearray([0x5C, 0xA0, 0xF0, 0x0F])) # org $0093BB : jml init_ram
+
+ INIT_SRAM_ADDR = 0x7F200
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0000, bytearray([0xD0, 0x74])) # init_sram: bne .clear
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0002, bytearray([0x9C, 0x09, 0x01])) # stz $0109
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0005, bytearray([0xDA])) # phx
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0006, bytearray([0x08])) # php
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0007, bytearray([0xE2, 0x10])) # sep #$10
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0009, bytearray([0xA2, 0x5F])) # ldx.b #$5F
+ rom.write_bytes(INIT_SRAM_ADDR + 0x000B, bytearray([0xBF, 0x00, 0x08, 0x70])) # - lda !level_clears_sram,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x000F, bytearray([0x9F, 0x00, 0xA2, 0x7F])) # sta !level_clears,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0013, bytearray([0xCA])) # dex
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0014, bytearray([0x10, 0xF5])) # bpl -
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0016, bytearray([0xA2, 0x0B])) # ldx #$0B
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0018, bytearray([0xBF, 0x40, 0x09, 0x70])) # - lda !blocksanity_sram,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x001C, bytearray([0x9F, 0x10, 0xA0, 0x7F])) # sta !blocksanity_flags,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0020, bytearray([0xBF, 0x10, 0x09, 0x70])) # lda !moons_sram,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0024, bytearray([0x9D, 0xEE, 0x1F])) # sta !moons_flags,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0027, bytearray([0xBF, 0x00, 0x09, 0x70])) # lda !yoshi_coins_sram,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x002B, bytearray([0x9D, 0x2F, 0x1F])) # sta !yoshi_coins_flags,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x002E, bytearray([0xBF, 0x30, 0x09, 0x70])) # lda !bonus_block_sram,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0032, bytearray([0x9F, 0x00, 0xA0, 0x7F])) # sta !bonus_block_flags,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0036, bytearray([0xBF, 0x20, 0x09, 0x70])) # lda !checkpoints_sram,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x003A, bytearray([0x9D, 0x3C, 0x1F])) # sta !checkpoints_flags,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x003D, bytearray([0xCA])) # dex
+ rom.write_bytes(INIT_SRAM_ADDR + 0x003E, bytearray([0x10, 0xD8])) # bpl -
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0040, bytearray([0xC2, 0x10])) # rep #$10
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0042, bytearray([0xA2, 0x45, 0x02])) # ldx.w #!blocksanity_locs-1
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0045, bytearray([0xBF, 0x00, 0x0A, 0x70])) # - lda !blocksanity_data_sram,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0049, bytearray([0x9F, 0x00, 0xA4, 0x7F])) # sta !blocksanity_data_flags,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x004D, bytearray([0xCA])) # dex
+ rom.write_bytes(INIT_SRAM_ADDR + 0x004E, bytearray([0x10, 0xF5])) # bpl -
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0050, bytearray([0xE2, 0x10])) # sep #$10
+ #rom.write_bytes(INIT_SRAM_ADDR + 0x0052, bytearray([0xAF, 0x50, 0x09, 0x70])) # lda !received_items_count_sram+$00
+ #rom.write_bytes(INIT_SRAM_ADDR + 0x0056, bytearray([0x8F, 0x0E, 0xA0, 0x7F])) # sta !received_items_count+$00
+ #rom.write_bytes(INIT_SRAM_ADDR + 0x005A, bytearray([0xAF, 0x51, 0x09, 0x70])) # lda !received_items_count_sram+$01
+ #rom.write_bytes(INIT_SRAM_ADDR + 0x005E, bytearray([0x8F, 0x0F, 0xA0, 0x7F])) # sta !received_items_count+$01
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0052, bytearray([0xEA] * 0x17)) # Ugly, will apply be better when we port everything to a Base Patch
+ #rom.write_bytes(INIT_SRAM_ADDR + 0x0062, bytearray([0xAF, 0x52, 0x09, 0x70])) # lda !special_world_clear_sram
+ #rom.write_bytes(INIT_SRAM_ADDR + 0x0066, bytearray([0x8D, 0xFF, 0x1F])) # sta !special_world_clear_flag
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0069, bytearray([0xAF, 0x54, 0x09, 0x70])) # lda !goal_item_count_sram
+ rom.write_bytes(INIT_SRAM_ADDR + 0x006D, bytearray([0x8F, 0x1E, 0xA0, 0x7F])) # sta !goal_item_count
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0071, bytearray([0x28])) # plp
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0072, bytearray([0x5C, 0xFB, 0x9C, 0x00])) # jml $009CFB
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0076, bytearray([0xDA])) # .clear phx
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0077, bytearray([0xA2, 0x5F, 0x00])) # ldx.w #$005F
+ rom.write_bytes(INIT_SRAM_ADDR + 0x007A, bytearray([0xA9, 0x00])) # lda #$00
+ rom.write_bytes(INIT_SRAM_ADDR + 0x007C, bytearray([0x9F, 0x00, 0x08, 0x70])) # - sta !level_clears_sram,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0080, bytearray([0xCA])) # dex
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0081, bytearray([0x10, 0xF9])) # bpl -
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0083, bytearray([0xA2, 0x0B, 0x00])) # ldx.w #$000B
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0086, bytearray([0x9F, 0x40, 0x09, 0x70])) # - sta !blocksanity_sram,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x008A, bytearray([0x9F, 0x00, 0x09, 0x70])) # sta !yoshi_coins_sram,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x008E, bytearray([0x9F, 0x30, 0x09, 0x70])) # sta !bonus_block_sram,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0092, bytearray([0x9F, 0x10, 0x09, 0x70])) # sta !moons_sram,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x0096, bytearray([0x9F, 0x20, 0x09, 0x70])) # sta !checkpoints_sram,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x009A, bytearray([0xCA])) # dex
+ rom.write_bytes(INIT_SRAM_ADDR + 0x009B, bytearray([0x10, 0xE9])) # bpl -
+ rom.write_bytes(INIT_SRAM_ADDR + 0x009D, bytearray([0xA2, 0x45, 0x02])) # ldx.w #!blocksanity_locs-1
+ rom.write_bytes(INIT_SRAM_ADDR + 0x00A0, bytearray([0x9F, 0x00, 0x0A, 0x70])) # - sta !blocksanity_data_sram,x
+ rom.write_bytes(INIT_SRAM_ADDR + 0x00A4, bytearray([0xCA])) # dex
+ rom.write_bytes(INIT_SRAM_ADDR + 0x00A5, bytearray([0x10, 0xF9])) # bpl -
+ rom.write_bytes(INIT_SRAM_ADDR + 0x00A7, bytearray([0x8F, 0x52, 0x09, 0x70])) # sta !special_world_clear_sram
+ rom.write_bytes(INIT_SRAM_ADDR + 0x00AB, bytearray([0x8F, 0x50, 0x09, 0x70])) # sta !received_items_count_sram+$00
+ rom.write_bytes(INIT_SRAM_ADDR + 0x00AF, bytearray([0x8F, 0x51, 0x09, 0x70])) # sta !received_items_count_sram+$01
+ rom.write_bytes(INIT_SRAM_ADDR + 0x00B3, bytearray([0x8F, 0x54, 0x09, 0x70])) # sta !goal_item_count_sram
+ rom.write_bytes(INIT_SRAM_ADDR + 0x00B7, bytearray([0xFA])) # plx
+ rom.write_bytes(INIT_SRAM_ADDR + 0x00B8, bytearray([0x5C, 0x22, 0x9D, 0x00])) # jml $009D22
+
+ SAVE_SRAM_ADDR = 0x7F300
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0000, bytearray([0xE2, 0x30])) # save_sram: sep #$30
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0002, bytearray([0xAB])) # plb
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0003, bytearray([0xA2, 0x5F])) # ldx.b #$5F
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0005, bytearray([0xBF, 0x00, 0xA2, 0x7F])) # - lda !level_clears,x
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0009, bytearray([0x9F, 0x00, 0x08, 0x70])) # sta !level_clears_sram,x
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x000D, bytearray([0xCA])) # dex
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x000E, bytearray([0x10, 0xF5])) # bpl -
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0010, bytearray([0xA2, 0x0B])) # ldx #$0B
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0012, bytearray([0xBF, 0x10, 0xA0, 0x7F])) # - lda !blocksanity_flags,x
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0016, bytearray([0x9F, 0x40, 0x09, 0x70])) # sta !blocksanity_sram,x
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x001A, bytearray([0xBD, 0x2F, 0x1F])) # lda !yoshi_coins_flags,x
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x001D, bytearray([0x9F, 0x00, 0x09, 0x70])) # sta !yoshi_coins_sram,x
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0021, bytearray([0xBD, 0xEE, 0x1F])) # lda !moons_flags,x
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0024, bytearray([0x9F, 0x10, 0x09, 0x70])) # sta !moons_sram,x
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0028, bytearray([0xBF, 0x00, 0xA0, 0x7F])) # lda !bonus_block_flags,x
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x002C, bytearray([0x9F, 0x30, 0x09, 0x70])) # sta !bonus_block_sram,x
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0030, bytearray([0xBD, 0x3C, 0x1F])) # lda !checkpoints_flags,x
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0033, bytearray([0x9F, 0x20, 0x09, 0x70])) # sta !checkpoints_sram,x
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0037, bytearray([0xCA])) # dex
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0038, bytearray([0x10, 0xD8])) # bpl -
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x003A, bytearray([0xC2, 0x10])) # rep #$10
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x003C, bytearray([0xA2, 0x45, 0x02])) # ldx.w #!blocksanity_locs-1
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x003F, bytearray([0xBF, 0x00, 0xA4, 0x7F])) # - lda !blocksanity_data_flags,x
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0043, bytearray([0x9F, 0x00, 0x0A, 0x70])) # sta !blocksanity_data_sram,x
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0047, bytearray([0xCA])) # dex
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0048, bytearray([0x10, 0xF5])) # bpl -
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x004A, bytearray([0xE2, 0x10])) # sep #$10
+ #rom.write_bytes(SAVE_SRAM_ADDR + 0x004C, bytearray([0xAD, 0xFF, 0x1F])) # lda !special_world_clear_flag
+ #rom.write_bytes(SAVE_SRAM_ADDR + 0x004F, bytearray([0x8F, 0x52, 0x09, 0x70])) # sta !special_world_clear_sram
+ #rom.write_bytes(SAVE_SRAM_ADDR + 0x0053, bytearray([0xAF, 0x0E, 0xA0, 0x7F])) # lda !received_items_count+$00
+ #rom.write_bytes(SAVE_SRAM_ADDR + 0x0057, bytearray([0x8F, 0x50, 0x09, 0x70])) # sta !received_items_count_sram+$00
+ #rom.write_bytes(SAVE_SRAM_ADDR + 0x005B, bytearray([0xAF, 0x0F, 0xA0, 0x7F])) # lda !received_items_count+$01
+ #rom.write_bytes(SAVE_SRAM_ADDR + 0x005F, bytearray([0x8F, 0x51, 0x09, 0x70])) # sta !received_items_count_sram+$01
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x004C, bytearray([0xEA] * 0x17)) # Ugly, will apply be better when we port everything to a Base Patch
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0063, bytearray([0xAF, 0x0F, 0xA0, 0x7F])) # lda !goal_item_count
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x0067, bytearray([0x8F, 0x51, 0x09, 0x70])) # sta !goal_item_count_sram
+ rom.write_bytes(SAVE_SRAM_ADDR + 0x006B, bytearray([0x6B])) # rtl
+
+ INIT_RAM_ADDR = 0x7F0A0
+ rom.write_bytes(INIT_RAM_ADDR + 0x0000, bytearray([0xA9, 0xAA])) # init_ram: lda #$AA
+ rom.write_bytes(INIT_RAM_ADDR + 0x0002, bytearray([0x8D, 0x00, 0x04])) # sta $0400
+ rom.write_bytes(INIT_RAM_ADDR + 0x0005, bytearray([0xA9, 0x00])) # clear_level_data: lda #$00
+ rom.write_bytes(INIT_RAM_ADDR + 0x0007, bytearray([0xA2, 0x5F])) # ldx #$5F
+ rom.write_bytes(INIT_RAM_ADDR + 0x0009, bytearray([0x9F, 0x00, 0xA2, 0x7F])) # .loop sta !level_clears,x
+ rom.write_bytes(INIT_RAM_ADDR + 0x000D, bytearray([0xCA])) # dex
+ rom.write_bytes(INIT_RAM_ADDR + 0x000E, bytearray([0x10, 0xF9])) # bpl .loop
+ rom.write_bytes(INIT_RAM_ADDR + 0x0010, bytearray([0xC2, 0x10])) # rep #$10
+ rom.write_bytes(INIT_RAM_ADDR + 0x0012, bytearray([0xA2, 0x0B, 0x00])) # ldx.w #$000B
+ rom.write_bytes(INIT_RAM_ADDR + 0x0015, bytearray([0x9F, 0x10, 0xA0, 0x7F])) # - sta !blocksanity_flags,x
+ rom.write_bytes(INIT_RAM_ADDR + 0x0019, bytearray([0x9D, 0x2F, 0x1F])) # sta !yoshi_coins_flags,x
+ rom.write_bytes(INIT_RAM_ADDR + 0x001C, bytearray([0x9D, 0xEE, 0x1F])) # sta !moons_flags,x
+ rom.write_bytes(INIT_RAM_ADDR + 0x001F, bytearray([0x9F, 0x00, 0xA0, 0x7F])) # sta !bonus_block_flags,x
+ rom.write_bytes(INIT_RAM_ADDR + 0x0023, bytearray([0x9D, 0x3C, 0x1F])) # sta !checkpoints_flags,x
+ rom.write_bytes(INIT_RAM_ADDR + 0x0026, bytearray([0xCA])) # dex
+ rom.write_bytes(INIT_RAM_ADDR + 0x0027, bytearray([0x10, 0xEC])) # bpl -
+ rom.write_bytes(INIT_RAM_ADDR + 0x0029, bytearray([0xA2, 0x45, 0x02])) # ldx.w #!blocksanity_locs-1
+ rom.write_bytes(INIT_RAM_ADDR + 0x002C, bytearray([0x9F, 0x00, 0xA4, 0x7F])) # - sta !blocksanity_data_flags,x
+ rom.write_bytes(INIT_RAM_ADDR + 0x0030, bytearray([0xCA])) # dex
+ rom.write_bytes(INIT_RAM_ADDR + 0x0031, bytearray([0x10, 0xF9])) # bpl -
+ rom.write_bytes(INIT_RAM_ADDR + 0x0033, bytearray([0xA2, 0x22, 0x04])) # ldx #$0422
+ rom.write_bytes(INIT_RAM_ADDR + 0x0036, bytearray([0x9F, 0x00, 0xB0, 0x7F])) # - sta !score_sprite_count,x
+ rom.write_bytes(INIT_RAM_ADDR + 0x003A, bytearray([0xCA])) # dex
+ rom.write_bytes(INIT_RAM_ADDR + 0x003B, bytearray([0x10, 0xF9])) # bpl -
+ #rom.write_bytes(INIT_RAM_ADDR + 0x003D, bytearray([0x8D, 0xFF, 0x1F])) # sta !special_world_clear_flag
+ rom.write_bytes(INIT_RAM_ADDR + 0x003D, bytearray([0xEA, 0xEA, 0xEA])) # sta !special_world_clear_flag
+ rom.write_bytes(INIT_RAM_ADDR + 0x0040, bytearray([0x8F, 0x0E, 0xA0, 0x7F])) # sta !received_items_count+$00
+ rom.write_bytes(INIT_RAM_ADDR + 0x0044, bytearray([0x8F, 0x0F, 0xA0, 0x7F])) # sta !received_items_count+$01
+ rom.write_bytes(INIT_RAM_ADDR + 0x0048, bytearray([0x8F, 0x1E, 0xA0, 0x7F])) # sta !goal_item_count
+ rom.write_bytes(INIT_RAM_ADDR + 0x004C, bytearray([0xA9, 0xFF])) # lda #$FF
+ rom.write_bytes(INIT_RAM_ADDR + 0x004E, bytearray([0x8D, 0x3C, 0x0F])) # sta !thwimp_index
+ rom.write_bytes(INIT_RAM_ADDR + 0x0051, bytearray([0xE2, 0x10])) # sep #$10
+ rom.write_bytes(INIT_RAM_ADDR + 0x0053, bytearray([0x22, 0x20, 0xF1, 0x0F])) # jsl clear_tilemap
+ rom.write_bytes(INIT_RAM_ADDR + 0x0057, bytearray([0x5C, 0xC0, 0x93, 0x00])) # jml $0093C0
+
+def handle_map_indicators(rom):
+ rom.write_bytes(0x265EE, bytearray([0x4C, 0x00, 0xA3])) # org $04E5EE : jmp check_events
+
+ GET_MAP_LEVEL_NUM_ADDR = 0x22340
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x0000, bytearray([0xC2, 0x30])) # get_translevel_num: rep #$30
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x0002, bytearray([0xAE, 0xD6, 0x0D])) # ldx $0DD6
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x0005, bytearray([0xBD, 0x1F, 0x1F])) # lda $1F1F,x
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x0008, bytearray([0x85, 0x00])) # sta $00
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x000A, bytearray([0xBD, 0x21, 0x1F])) # lda $1F21,x
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x000D, bytearray([0x85, 0x02])) # sta $02
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x000F, bytearray([0x8A])) # txa
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x0010, bytearray([0x4A])) # lsr
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x0011, bytearray([0x4A])) # lsr
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x0012, bytearray([0xAA])) # tax
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x0013, bytearray([0x20, 0x85, 0x98])) # jsr $9885
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x0016, bytearray([0xA6, 0x04])) # ldx $04
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x0018, bytearray([0xBF, 0x00, 0xD0, 0x7E])) # lda $7ED000,x
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x001C, bytearray([0xE2, 0x30])) # sep #$30
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x001E, bytearray([0x85, 0x60])) # sta $60
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x0020, bytearray([0xAA])) # tax
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x0021, bytearray([0xBF, 0x00, 0xFF, 0x06])) # lda $06FF00,x
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x0025, bytearray([0xC9, 0xFF])) # cmp #$FF
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x0027, bytearray([0xF0, 0x02])) # beq +
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x0029, bytearray([0x85, 0x60])) # sta $60
+ rom.write_bytes(GET_MAP_LEVEL_NUM_ADDR + 0x002B, bytearray([0x60])) # + rts
+
+ GET_MAP_LEVEL_BIT_ADDR = 0x22380
+ rom.write_bytes(GET_MAP_LEVEL_BIT_ADDR + 0x0000, bytearray([0xA5, 0x60])) # get_translevel_bit: lda $60
+ rom.write_bytes(GET_MAP_LEVEL_BIT_ADDR + 0x0002, bytearray([0x4A])) # lsr
+ rom.write_bytes(GET_MAP_LEVEL_BIT_ADDR + 0x0003, bytearray([0x4A])) # lsr
+ rom.write_bytes(GET_MAP_LEVEL_BIT_ADDR + 0x0004, bytearray([0x4A])) # lsr
+ rom.write_bytes(GET_MAP_LEVEL_BIT_ADDR + 0x0005, bytearray([0xA8])) # tay
+ rom.write_bytes(GET_MAP_LEVEL_BIT_ADDR + 0x0006, bytearray([0xA5, 0x60])) # lda $60
+ rom.write_bytes(GET_MAP_LEVEL_BIT_ADDR + 0x0008, bytearray([0x29, 0x07])) # and #$07
+ rom.write_bytes(GET_MAP_LEVEL_BIT_ADDR + 0x000A, bytearray([0xAA])) # tax
+ rom.write_bytes(GET_MAP_LEVEL_BIT_ADDR + 0x000B, bytearray([0x60])) # rts
+
+ UPDATE_MAP_PTRS_ADDR = 0x223C0
+ rom.write_bytes(UPDATE_MAP_PTRS_ADDR + 0x0000, bytearray([0xE6, 0x00])) # update_flag_pointers: inc $00
+ rom.write_bytes(UPDATE_MAP_PTRS_ADDR + 0x0002, bytearray([0xE6, 0x00])) # inc $00
+ rom.write_bytes(UPDATE_MAP_PTRS_ADDR + 0x0004, bytearray([0xE6, 0x03])) # inc $03
+ rom.write_bytes(UPDATE_MAP_PTRS_ADDR + 0x0006, bytearray([0xE6, 0x03])) # inc $03
+ rom.write_bytes(UPDATE_MAP_PTRS_ADDR + 0x0008, bytearray([0xE6, 0x06])) # inc $06
+ rom.write_bytes(UPDATE_MAP_PTRS_ADDR + 0x000A, bytearray([0xE6, 0x06])) # inc $06
+ rom.write_bytes(UPDATE_MAP_PTRS_ADDR + 0x000C, bytearray([0xE6, 0x62])) # inc $62
+ rom.write_bytes(UPDATE_MAP_PTRS_ADDR + 0x000E, bytearray([0xE6, 0x62])) # inc $62
+ rom.write_bytes(UPDATE_MAP_PTRS_ADDR + 0x0010, bytearray([0xE6, 0x63])) # inc $63
+ rom.write_bytes(UPDATE_MAP_PTRS_ADDR + 0x0012, bytearray([0x60])) # rts
+
+ CLEAR_TILEMAP_ADDR = 0x7F120
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x0000, bytearray([0xC2, 0x20])) # clear_tilemap: rep #$20
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x0002, bytearray([0xA9, 0x1F, 0x39])) # lda.w #$3900+!icon_disabled
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x0005, bytearray([0xA2, 0x1E])) # ldx #$1E
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x0007, bytearray([0x9F, 0x20, 0xA1, 0x7F])) # .loop sta !ow_tilemap_switches,x
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x000B, bytearray([0x9F, 0x00, 0xA1, 0x7F])) # sta !ow_tilemap_abilities,x
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x000F, bytearray([0x9F, 0x40, 0xA1, 0x7F])) # sta !ow_tilemap_flags_top,x
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x0013, bytearray([0x9F, 0x60, 0xA1, 0x7F])) # sta !ow_tilemap_flags_mid,x
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x0017, bytearray([0x9F, 0x80, 0xA1, 0x7F])) # sta !ow_tilemap_flags_bot,x
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x001B, bytearray([0xCA])) # dex
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x001C, bytearray([0xCA])) # dex
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x001D, bytearray([0x10, 0xE8])) # bpl .loop
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x001F, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x0021, bytearray([0xA9, 0x07])) # lda #$07
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x0023, bytearray([0x85, 0x63])) # sta $63
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x0025, bytearray([0x0A])) # asl
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x0026, bytearray([0x85, 0x62])) # sta $62
+ rom.write_bytes(CLEAR_TILEMAP_ADDR + 0x0028, bytearray([0x6B])) # rtl
+
+ CLEAR_TILEMAP_FLAGS_ADDR = 0x7F180
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x0000, bytearray([0xC2, 0x20])) # clear_tilemap_flags: rep #$20
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x0002, bytearray([0xA9, 0x1F, 0x39])) # lda.w #$3900+!icon_disabled
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x0005, bytearray([0xA2, 0x0C])) # ldx.b #($07*2)-2
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x0007, bytearray([0x9F, 0x40, 0xA1, 0x7F])) # .loop sta !ow_tilemap_flags_top,x
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x000B, bytearray([0x9F, 0x60, 0xA1, 0x7F])) # sta !ow_tilemap_flags_mid,x
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x000F, bytearray([0x9F, 0x80, 0xA1, 0x7F])) # sta !ow_tilemap_flags_bot,x
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x0013, bytearray([0xCA])) # dex
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x0014, bytearray([0xCA])) # dex
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x0015, bytearray([0x10, 0xF0])) # bpl .loop
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x0017, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x0019, bytearray([0xA9, 0x06])) # lda #$06
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x001B, bytearray([0x85, 0x63])) # sta $63
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x001D, bytearray([0x0A])) # asl
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x001E, bytearray([0x85, 0x62])) # sta $62
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x0020, bytearray([0xA9, 0xFF])) # lda #$FF
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x0022, bytearray([0x8D, 0x3C, 0x0F])) # sta !thwimp_index
+ rom.write_bytes(CLEAR_TILEMAP_FLAGS_ADDR + 0x0025, bytearray([0x6B])) # rtl
+
+ CHECK_EVENTS_ADDR = 0x22300
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x0000, bytearray([0xDA])) # check_events: phx
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x0001, bytearray([0x20, 0x40, 0xA3])) # jsr get_translevel_num
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x0004, bytearray([0xAD, 0xD5, 0x0D])) # lda $0DD5
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x0007, bytearray([0xF0, 0x17])) # beq .dont_sync
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x0009, bytearray([0x30, 0x15])) # bmi .dont_sync
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x000B, bytearray([0xC9, 0x05])) # cmp #$05
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x000D, bytearray([0xB0, 0x11])) # bcs .dont_sync
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x000F, bytearray([0x29, 0x07])) # and #$07
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x0011, bytearray([0xAA])) # tax
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x0012, bytearray([0xBF, 0x7D, 0x9E, 0x00])) # lda.l $009E7D,x
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x0016, bytearray([0xA6, 0x60])) # ldx $60
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x0018, bytearray([0x1F, 0x00, 0xA2, 0x7F])) # ora !level_clears,x
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x001C, bytearray([0x9F, 0x00, 0xA2, 0x7F])) # sta !level_clears,x
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x0020, bytearray([0xFA])) # .dont_sync plx
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x0021, bytearray([0xAD, 0xD5, 0x0D])) # lda $0DD5
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x0024, bytearray([0xC9, 0x02])) # cmp #$02
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x0026, bytearray([0xD0, 0x03])) # bne .no_secret
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x0028, bytearray([0xEE, 0xEA, 0x1D])) # inc $1DEA
+ rom.write_bytes(CHECK_EVENTS_ADDR + 0x002B, bytearray([0x4C, 0xF8, 0xE5])) # .no_secret jmp $E5F8
+
+ DRAW_MAP_TILEMAP_ADDR = 0x221B6
+ rom.write_bytes(0x00222, bytearray([0x5C, 0xB6, 0xA1, 0x04])) # org $008222 : jml draw_ow_tilemap
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0000, bytearray([0xAD, 0xD9, 0x13])) # draw_ow_tilemap: lda $13D9
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0003, bytearray([0xC9, 0x0A])) # cmp #$0A
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0005, bytearray([0xD0, 0x04])) # bne write_tilemap
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0007, bytearray([0x5C, 0x29, 0x82, 0x00])) # jml $008229
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x000B, bytearray([0xC2, 0x20])) # write_tilemap: rep #$20
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x000D, bytearray([0xA0, 0x80])) # ldy #$80
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x000F, bytearray([0x8C, 0x15, 0x21])) # sty $2115
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0012, bytearray([0xA9, 0x27, 0x50])) # write_abilities: lda #!vram_abilities_top
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0015, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0018, bytearray([0xA2, 0x00])) # ldx.b #$00
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x001A, bytearray([0xBF, 0xA2, 0xA2, 0x04])) # ..loop lda.l abilities_top,x
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x001E, bytearray([0x8D, 0x18, 0x21])) # sta $2118
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0021, bytearray([0xE8])) # inx
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0022, bytearray([0xE8])) # inx
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0023, bytearray([0xE0, 0x14])) # cpx.b #$0A*2
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0025, bytearray([0x90, 0xF3])) # bcc ..loop
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0027, bytearray([0xA9, 0x47, 0x50])) # .mid lda #!vram_abilities_mid
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x002A, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x002D, bytearray([0xA2, 0x00])) # ldx.b #$00
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x002F, bytearray([0xBF, 0xB6, 0xA2, 0x04])) # ..loop lda.l abilities_bottom,x
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0033, bytearray([0x8D, 0x18, 0x21])) # sta $2118
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0036, bytearray([0xE8])) # inx
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0037, bytearray([0xE8])) # inx
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0038, bytearray([0xE0, 0x14])) # cpx.b #$0A*2
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x003A, bytearray([0x90, 0xF3])) # bcc ..loop
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x003C, bytearray([0xA9, 0x67, 0x50])) # .bot lda #!vram_abilities_bot
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x003F, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0042, bytearray([0xA2, 0x00])) # ldx.b #$00
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0044, bytearray([0xBF, 0x00, 0xA1, 0x7F])) # ..loop lda !ow_tilemap_abilities,x
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0048, bytearray([0x8D, 0x18, 0x21])) # sta $2118
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x004B, bytearray([0xE8])) # inx
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x004C, bytearray([0xE8])) # inx
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x004D, bytearray([0xE0, 0x14])) # cpx.b #$0A*2
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x004F, bytearray([0x90, 0xF3])) # bcc ..loop
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0051, bytearray([0xA9, 0x32, 0x50])) # write_switches: lda #!vram_switches_top
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0054, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0057, bytearray([0xA2, 0x00])) # ldx.b #$00
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0059, bytearray([0xBF, 0xCA, 0xA2, 0x04])) # ..loop lda.l switches_top,x
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x005D, bytearray([0x8D, 0x18, 0x21])) # sta $2118
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0060, bytearray([0xE8])) # inx
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0061, bytearray([0xE8])) # inx
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0062, bytearray([0xE0, 0x0A])) # cpx.b #$05*2
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0064, bytearray([0x90, 0xF3])) # bcc ..loop
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0066, bytearray([0xA9, 0x52, 0x50])) # .mid lda #!vram_switches_mid
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0069, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x006C, bytearray([0xA2, 0x00])) # ldx.b #$00
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x006E, bytearray([0xBF, 0xD4, 0xA2, 0x04])) # ..loop lda.l switches_bottom,x
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0072, bytearray([0x8D, 0x18, 0x21])) # sta $2118
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0075, bytearray([0xE8])) # inx
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0076, bytearray([0xE8])) # inx
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0077, bytearray([0xE0, 0x0A])) # cpx.b #$05*2
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0079, bytearray([0x90, 0xF3])) # bcc ..loop
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x007B, bytearray([0xA9, 0x72, 0x50])) # .bot lda #!vram_switches_bot
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x007E, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0081, bytearray([0xA2, 0x00])) # ldx.b #$00
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0083, bytearray([0xBF, 0x20, 0xA1, 0x7F])) # ..loop lda !ow_tilemap_switches,x
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0087, bytearray([0x8D, 0x18, 0x21])) # sta $2118
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x008A, bytearray([0xE8])) # inx
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x008B, bytearray([0xE8])) # inx
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x008C, bytearray([0xE0, 0x0A])) # cpx.b #$05*2
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x008E, bytearray([0x90, 0xF3])) # bcc ..loop
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0090, bytearray([0xD4, 0x00])) # write_level_data: pei ($00)
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0092, bytearray([0xA5, 0x63])) # lda $63
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0094, bytearray([0x29, 0xFF, 0x00])) # and #$00FF
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0097, bytearray([0x85, 0x00])) # sta $00
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0099, bytearray([0xF0, 0x48])) # beq .skip_flags
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x009B, bytearray([0xA9, 0x3E, 0x50])) # .top lda.w #!vram_level_data_top+$01
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x009E, bytearray([0x38])) # sec
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x009F, bytearray([0xE5, 0x00])) # sbc $00
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00A1, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00A4, bytearray([0xA6, 0x62])) # ldx.b $62
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00A6, bytearray([0xCA])) # dex
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00A7, bytearray([0xCA])) # dex
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00A8, bytearray([0xBF, 0x40, 0xA1, 0x7F])) # ..loop lda.l !ow_tilemap_flags_top,x
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00AC, bytearray([0x8D, 0x18, 0x21])) # sta $2118
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00AF, bytearray([0xCA])) # dex
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00B0, bytearray([0xCA])) # dex
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00B1, bytearray([0x10, 0xF5])) # bpl ..loop
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00B3, bytearray([0xA9, 0x5E, 0x50])) # .mid lda.w #!vram_level_data_mid+$01
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00B6, bytearray([0x38])) # sec
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00B7, bytearray([0xE5, 0x00])) # sbc $00
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00B9, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00BC, bytearray([0xA6, 0x62])) # ldx.b $62
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00BE, bytearray([0xCA])) # dex
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00BF, bytearray([0xCA])) # dex
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00C0, bytearray([0xBF, 0x60, 0xA1, 0x7F])) # ..loop lda.l !ow_tilemap_flags_mid,x
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00C4, bytearray([0x8D, 0x18, 0x21])) # sta $2118
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00C7, bytearray([0xCA])) # dex
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00C8, bytearray([0xCA])) # dex
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00C9, bytearray([0x10, 0xF5])) # bpl ..loop
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00CB, bytearray([0xA9, 0x7E, 0x50])) # .bot lda.w #!vram_level_data_bot+$01
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00CE, bytearray([0x38])) # sec
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00CF, bytearray([0xE5, 0x00])) # sbc $00
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00D1, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00D4, bytearray([0xA6, 0x62])) # ldx.b $62
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00D6, bytearray([0xCA])) # dex
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00D7, bytearray([0xCA])) # dex
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00D8, bytearray([0xBF, 0x80, 0xA1, 0x7F])) # ..loop lda.l !ow_tilemap_flags_bot,x
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00DC, bytearray([0x8D, 0x18, 0x21])) # sta $2118
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00DF, bytearray([0xCA])) # dex
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00E0, bytearray([0xCA])) # dex
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00E1, bytearray([0x10, 0xF5])) # bpl ..loop
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00E3, bytearray([0x68])) # .skip_flags pla
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00E4, bytearray([0x85, 0x00])) # sta $00
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00E6, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00E8, bytearray([0x5C, 0x37, 0x82, 0x00])) # jml $008237
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00EC, bytearray([0x0F, 0x39, 0x12, 0x39])) # abilities_top: dw $390F,$3912
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00F0, bytearray([0x11, 0x39, 0x02, 0x39])) # dw $3911,$3902
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00F4, bytearray([0x12, 0x39, 0x02, 0x39])) # dw $3912,$3902
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00F8, bytearray([0x18, 0x39, 0x0F, 0x39])) # dw $3918,$390F
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x00FC, bytearray([0x0F, 0x39, 0x12, 0x39])) # dw $390F,$3912
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0100, bytearray([0x4E, 0x39, 0x4F, 0x39])) # abilities_bottom: dw $394E,$394F
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0104, bytearray([0x54, 0x39, 0x40, 0x39])) # dw $3954,$3940
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0108, bytearray([0x56, 0x39, 0x4B, 0x39])) # dw $3956,$394B
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x010C, bytearray([0x4E, 0x39, 0x52, 0x39])) # dw $394E,$3952
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0110, bytearray([0x41, 0x39, 0x53, 0x39])) # dw $3941,$3953
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0114, bytearray([0x18, 0x39, 0x06, 0x39])) # switches_top: dw $3918,$3906
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0118, bytearray([0x11, 0x39, 0x01, 0x39])) # dw $3911,$3901
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x011C, bytearray([0x12, 0x39])) # dw $3912
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x011E, bytearray([0x12, 0x39, 0x12, 0x39])) # switches_bottom: dw $3912,$3912
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0122, bytearray([0x12, 0x39, 0x12, 0x39])) # dw $3912,$3912
+ rom.write_bytes(DRAW_MAP_TILEMAP_ADDR + 0x0126, bytearray([0x4F, 0x39])) # dw $394F
+
+ BUILD_TILEMAP_ADDR = 0x26F3E
+ rom.write_bytes(0x021C7, bytearray([0x22, 0x3E, 0xEF, 0x04])) # org $00A1C7 : jsl prepare_dynamic_tilemap
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0000, bytearray([0x22, 0x41, 0x82, 0x04])) # prepare_dynamic_tilemap: jsl $048241
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0004, bytearray([0xA0, 0x22])) # .handle_powerup: ldy #$22
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0006, bytearray([0xAD, 0x2D, 0x1F])) # lda $1F2D
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0009, bytearray([0x4A])) # lsr
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x000A, bytearray([0x90, 0x01])) # bcc $01
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x000C, bytearray([0xC8])) # iny
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x000D, bytearray([0x4A])) # lsr
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x000E, bytearray([0x90, 0x01])) # bcc $01
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0010, bytearray([0xC8])) # iny
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0011, bytearray([0x4A])) # lsr
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0012, bytearray([0x90, 0x01])) # bcc $01
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0014, bytearray([0xC8])) # iny
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0015, bytearray([0x98])) # tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0016, bytearray([0x8F, 0x00, 0xA1, 0x7F])) # sta !ow_tilemap_abilities ; Progressive powerup
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x001A, bytearray([0xA0, 0x5E])) # .handle_spinjump: ldy #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x001C, bytearray([0xAD, 0x1C, 0x1F])) # lda $1F1C
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x001F, bytearray([0x29, 0x08])) # and #$08
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0021, bytearray([0xF0, 0x02])) # beq $02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0023, bytearray([0xA0, 0x3F])) # ldy #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0025, bytearray([0x98])) # tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0026, bytearray([0x8F, 0x02, 0xA1, 0x7F])) # sta !ow_tilemap_abilities+$02 ; Spin jump
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x002A, bytearray([0xA0, 0x5E])) # .handle_run: ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x002C, bytearray([0xAD, 0x1C, 0x1F])) # lda $1F1C
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x002F, bytearray([0x29, 0x80])) # and #$80
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0031, bytearray([0xF0, 0x02])) # beq $02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0033, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0035, bytearray([0x98])) # tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0036, bytearray([0x8F, 0x04, 0xA1, 0x7F])) # sta !ow_tilemap_abilities+$04 ; Run
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x003A, bytearray([0xA0, 0x5E])) # .handle_carry: ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x003C, bytearray([0xAD, 0x1C, 0x1F])) # lda $1F1C
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x003F, bytearray([0x29, 0x40])) # and #$40
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0041, bytearray([0xF0, 0x02])) # beq $02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0043, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0045, bytearray([0x98])) # tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0046, bytearray([0x8F, 0x06, 0xA1, 0x7F])) # sta !ow_tilemap_abilities+$06 ; Carry
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x004A, bytearray([0xA0, 0x5E])) # .handle_swim: ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x004C, bytearray([0xAD, 0x1C, 0x1F])) # lda $1F1C
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x004F, bytearray([0x29, 0x04])) # and #$04
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0051, bytearray([0xF0, 0x02])) # beq $02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0053, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0055, bytearray([0x98])) # tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0056, bytearray([0x8F, 0x08, 0xA1, 0x7F])) # sta !ow_tilemap_abilities+$08 ; Swim
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x005A, bytearray([0xA0, 0x5E])) # .handle_climb: ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x005C, bytearray([0xAD, 0x1C, 0x1F])) # lda $1F1C
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x005F, bytearray([0x29, 0x20])) # and #$20
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0061, bytearray([0xF0, 0x02])) # beq $02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0063, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0065, bytearray([0x98])) # tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0066, bytearray([0x8F, 0x0A, 0xA1, 0x7F])) # sta !ow_tilemap_abilities+$0A ; Climb
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x006A, bytearray([0xA0, 0x5E])) # .handle_yoshi: ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x006C, bytearray([0xAD, 0x1C, 0x1F])) # lda $1F1C
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x006F, bytearray([0x29, 0x02])) # and #$02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0071, bytearray([0xF0, 0x02])) # beq $02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0073, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0075, bytearray([0x98])) # tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0076, bytearray([0x8F, 0x0C, 0xA1, 0x7F])) # sta !ow_tilemap_abilities+$0C ; Yoshi
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x007A, bytearray([0xA0, 0x5E])) # .handle_pswitch: ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x007C, bytearray([0xAD, 0x1C, 0x1F])) # lda $1F1C
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x007F, bytearray([0x29, 0x10])) # and #$10
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0081, bytearray([0xF0, 0x02])) # beq $02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0083, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0085, bytearray([0x98])) # tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0086, bytearray([0x8F, 0x0E, 0xA1, 0x7F])) # sta !ow_tilemap_abilities+$0E ; P-Switch
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x008A, bytearray([0xA0, 0x5E])) # .handle_pballoon: ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x008C, bytearray([0xAD, 0x2D, 0x1F])) # lda $1F2D
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x008F, bytearray([0x29, 0x08])) # and #$08
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0091, bytearray([0xF0, 0x02])) # beq $02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0093, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0095, bytearray([0x98])) # tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0096, bytearray([0x8F, 0x10, 0xA1, 0x7F])) # sta !ow_tilemap_abilities+$10 ; P-Balloon
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x009A, bytearray([0xA0, 0x5E])) # .handle_star: ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x009C, bytearray([0xAD, 0x2D, 0x1F])) # lda $1F2D
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x009F, bytearray([0x29, 0x10])) # and #$10
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00A1, bytearray([0xF0, 0x02])) # beq $02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00A3, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00A5, bytearray([0x98])) # tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00A6, bytearray([0x8F, 0x12, 0xA1, 0x7F])) # sta !ow_tilemap_abilities+$12 ; Star
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00AA, bytearray([0xA0, 0x5E])) # .handle_yellow_switch: ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00AC, bytearray([0xAD, 0x28, 0x1F])) # lda $1F28
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00AF, bytearray([0xF0, 0x02])) # beq $02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00B1, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00B3, bytearray([0x98])) # tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00B4, bytearray([0x8F, 0x20, 0xA1, 0x7F])) # sta !ow_tilemap_switches+$00
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00B8, bytearray([0xA0, 0x5E])) # .handle_green_switch: ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00BA, bytearray([0xAD, 0x27, 0x1F])) # lda $1F27
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00BD, bytearray([0xF0, 0x02])) # beq $02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00BF, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00C1, bytearray([0x98])) # tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00C2, bytearray([0x8F, 0x22, 0xA1, 0x7F])) # sta !ow_tilemap_switches+$02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00C6, bytearray([0xA0, 0x5E])) # .handle_red_switch: ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00C8, bytearray([0xAD, 0x2A, 0x1F])) # lda $1F2A
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00CB, bytearray([0xF0, 0x02])) # beq $02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00CD, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00CF, bytearray([0x98])) # tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00D0, bytearray([0x8F, 0x24, 0xA1, 0x7F])) # sta !ow_tilemap_switches+$04
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00D4, bytearray([0xA0, 0x5E])) # .handle_blue_switch: ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00D6, bytearray([0xAD, 0x29, 0x1F])) # lda $1F29
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00D9, bytearray([0xF0, 0x02])) # beq $02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00DB, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00DD, bytearray([0x98])) # tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00DE, bytearray([0x8F, 0x26, 0xA1, 0x7F])) # sta !ow_tilemap_switches+$06
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00E2, bytearray([0xA0, 0x5E])) # .handle_special_world_clear: ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00E4, bytearray([0xAD, 0x1E, 0x1F])) # lda !special_world_clear_flag
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00E7, bytearray([0xF0, 0x02])) # beq $02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00E9, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00EB, bytearray([0x98])) # tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00EC, bytearray([0x8F, 0x28, 0xA1, 0x7F])) # sta !ow_tilemap_switches+$08
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00F0, bytearray([0x22, 0x80, 0xF1, 0x0F])) # jsl clear_tilemap_flags
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00F4, bytearray([0xAD, 0xD9, 0x13])) # lda $13D9
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00F7, bytearray([0xC9, 0x01])) # cmp #$01
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00F9, bytearray([0xF0, 0x05])) # beq process_level
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00FB, bytearray([0xC9, 0x03])) # cmp #$03
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00FD, bytearray([0xF0, 0x01])) # beq process_level
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x00FF, bytearray([0x6B])) # rtl
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0100, bytearray([0x20, 0x40, 0xA3])) # process_level: jsr get_translevel_num
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0103, bytearray([0xA6, 0x60])) # ldx $60
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0105, bytearray([0xBF, 0x00, 0xF4, 0x0F])) # lda.l level_data,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0109, bytearray([0x10, 0x01])) # bpl .handle_data
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x010B, bytearray([0x6B])) # rtl
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x010C, bytearray([0x64, 0x62])) # .handle_data stz $62
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x010E, bytearray([0x64, 0x63])) # stz $63
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0110, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0112, bytearray([0xA9, 0x40, 0xA1])) # lda.w #!ow_tilemap_flags_top
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0115, bytearray([0x85, 0x00])) # sta $00
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0117, bytearray([0xA9, 0x60, 0xA1])) # lda.w #!ow_tilemap_flags_mid
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x011A, bytearray([0x85, 0x03])) # sta $03
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x011C, bytearray([0xA9, 0x80, 0xA1])) # lda.w #!ow_tilemap_flags_bot
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x011F, bytearray([0x85, 0x06])) # sta $06
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0121, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0123, bytearray([0xA9, 0x7F])) # lda.b #!ow_tilemap_flags_top>>16
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0125, bytearray([0x85, 0x02])) # sta $02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0127, bytearray([0x85, 0x05])) # sta $05
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0129, bytearray([0x85, 0x08])) # sta $08
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x012B, bytearray([0xAF, 0xAB, 0xBF, 0x03])) # handle_blocksanity: lda.l blocksanity_enabled_flag
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x012F, bytearray([0xF0, 0x30])) # beq handle_bonus_blocks
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0131, bytearray([0xA6, 0x60])) # ldx $60
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0133, bytearray([0xA0, 0x1F])) # ldy.b #!icon_disabled
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0135, bytearray([0xBF, 0x00, 0xF4, 0x0F])) # lda.l level_data,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0139, bytearray([0x29, 0x40])) # and #$40
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x013B, bytearray([0xF0, 0x24])) # beq handle_bonus_blocks
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x013D, bytearray([0xA0, 0x5E])) # ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x013F, bytearray([0x5A])) # phy
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0140, bytearray([0x20, 0x80, 0xA3])) # jsr get_translevel_bit
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0143, bytearray([0xDA])) # phx
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0144, bytearray([0xBB])) # tyx
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0145, bytearray([0xBF, 0x10, 0xA0, 0x7F])) # lda.l !blocksanity_flags,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0149, bytearray([0xFA])) # plx
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x014A, bytearray([0x7A])) # ply
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x014B, bytearray([0x3F, 0xA6, 0xA8, 0x0D])) # and.l $0DA8A6,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x014F, bytearray([0xF0, 0x02])) # beq .write
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0151, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0153, bytearray([0x98])) # .write tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0154, bytearray([0x87, 0x06])) # sta [$06]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0156, bytearray([0xA9, 0x01])) # lda #$01
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0158, bytearray([0x87, 0x00])) # sta [$00]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x015A, bytearray([0xA9, 0x12])) # lda #$12
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x015C, bytearray([0x87, 0x03])) # sta [$03]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x015E, bytearray([0x20, 0xC0, 0xA3])) # jsr update_flag_pointers
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0161, bytearray([0xAF, 0xAA, 0xBF, 0x03])) # handle_bonus_blocks: lda.l bonus_block_enabled_flag
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0165, bytearray([0xF0, 0x30])) # beq handle_checkpoints
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0167, bytearray([0xA6, 0x60])) # ldx $60
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0169, bytearray([0xA0, 0x1F])) # ldy.b #!icon_disabled
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x016B, bytearray([0xBF, 0x00, 0xF4, 0x0F])) # lda.l level_data,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x016F, bytearray([0x29, 0x20])) # and #$20
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0171, bytearray([0xF0, 0x24])) # beq handle_checkpoints
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0173, bytearray([0xA0, 0x5E])) # ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0175, bytearray([0x5A])) # phy
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0176, bytearray([0x20, 0x80, 0xA3])) # jsr get_translevel_bit
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0179, bytearray([0xDA])) # phx
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x017A, bytearray([0xBB])) # tyx
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x017B, bytearray([0xBF, 0x00, 0xA0, 0x7F])) # lda !bonus_block_flags,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x017F, bytearray([0xFA])) # plx
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0180, bytearray([0x7A])) # ply
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0181, bytearray([0x3F, 0xA6, 0xA8, 0x0D])) # and.l $0DA8A6,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0185, bytearray([0xF0, 0x02])) # beq .write
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0187, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0189, bytearray([0x98])) # .write tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x018A, bytearray([0x87, 0x06])) # sta [$06]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x018C, bytearray([0xA9, 0x01])) # lda #$01
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x018E, bytearray([0x87, 0x00])) # sta [$00]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0190, bytearray([0xA9, 0x4E])) # lda #$4E
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0192, bytearray([0x87, 0x03])) # sta [$03]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0194, bytearray([0x20, 0xC0, 0xA3])) # jsr update_flag_pointers
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0197, bytearray([0xAF, 0xA9, 0xBF, 0x03])) # handle_checkpoints: lda.l checkpoints_enabled_flag
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x019B, bytearray([0xF0, 0x2A])) # beq handle_moons
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x019D, bytearray([0xA6, 0x60])) # ldx $60
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x019F, bytearray([0xBF, 0x00, 0xF4, 0x0F])) # lda.l level_data,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01A3, bytearray([0x29, 0x10])) # and #$10
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01A5, bytearray([0xF0, 0x20])) # beq handle_moons
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01A7, bytearray([0xA0, 0x5E])) # ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01A9, bytearray([0x5A])) # phy
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01AA, bytearray([0x20, 0x80, 0xA3])) # jsr get_translevel_bit
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01AD, bytearray([0xB9, 0x3C, 0x1F])) # lda !checkpoints_flags,y
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01B0, bytearray([0x7A])) # ply
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01B1, bytearray([0x3F, 0xA6, 0xA8, 0x0D])) # and.l $0DA8A6,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01B5, bytearray([0xF0, 0x02])) # beq .write
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01B7, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01B9, bytearray([0x98])) # .write tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01BA, bytearray([0x87, 0x06])) # sta [$06]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01BC, bytearray([0xA9, 0x07])) # lda #$07
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01BE, bytearray([0x87, 0x00])) # sta [$00]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01C0, bytearray([0xA9, 0x48])) # lda #$48
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01C2, bytearray([0x87, 0x03])) # sta [$03]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01C4, bytearray([0x20, 0xC0, 0xA3])) # jsr update_flag_pointers
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01C7, bytearray([0xAF, 0xA8, 0xBF, 0x03])) # handle_moons: lda.l moon_enabled_flag
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01CB, bytearray([0xF0, 0x2A])) # beq handle_dragon_coins
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01CD, bytearray([0xA6, 0x60])) # ldx $60
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01CF, bytearray([0xBF, 0x00, 0xF4, 0x0F])) # lda.l level_data,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01D3, bytearray([0x29, 0x08])) # and #$08
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01D5, bytearray([0xF0, 0x20])) # beq handle_dragon_coins
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01D7, bytearray([0xA0, 0x5E])) # ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01D9, bytearray([0x5A])) # phy
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01DA, bytearray([0x20, 0x80, 0xA3])) # jsr get_translevel_bit
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01DD, bytearray([0xB9, 0xEE, 0x1F])) # lda !moons_flags,y
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01E0, bytearray([0x7A])) # ply
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01E1, bytearray([0x3F, 0xA6, 0xA8, 0x0D])) # and.l $0DA8A6,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01E5, bytearray([0xF0, 0x02])) # beq .write
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01E7, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01E9, bytearray([0x98])) # .write tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01EA, bytearray([0x87, 0x06])) # sta [$06]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01EC, bytearray([0xA9, 0x0C])) # lda #$0C
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01EE, bytearray([0x87, 0x00])) # sta [$00]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01F0, bytearray([0xA9, 0x4E])) # lda #$4E
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01F2, bytearray([0x87, 0x03])) # sta [$03]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01F4, bytearray([0x20, 0xC0, 0xA3])) # jsr update_flag_pointers
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01F7, bytearray([0xAF, 0xA6, 0xBF, 0x03])) # handle_dragon_coins: lda.l dragon_coin_enabled_flag
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01FB, bytearray([0xF0, 0x2A])) # beq handle_exit_2
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01FD, bytearray([0xA6, 0x60])) # ldx $60
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x01FF, bytearray([0xBF, 0x00, 0xF4, 0x0F])) # lda.l level_data,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0203, bytearray([0x29, 0x04])) # and #$04
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0205, bytearray([0xF0, 0x20])) # beq handle_exit_2
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0207, bytearray([0xA0, 0x5E])) # ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0209, bytearray([0x5A])) # phy
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x020A, bytearray([0x20, 0x80, 0xA3])) # jsr get_translevel_bit
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x020D, bytearray([0xB9, 0x2F, 0x1F])) # lda !yoshi_coins_flags,y
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0210, bytearray([0x7A])) # ply
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0211, bytearray([0x3F, 0xA6, 0xA8, 0x0D])) # and.l $0DA8A6,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0215, bytearray([0xF0, 0x02])) # beq .write
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0217, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0219, bytearray([0x98])) # .write tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x021A, bytearray([0x87, 0x06])) # sta [$06]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x021C, bytearray([0xA9, 0x03])) # lda #$03
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x021E, bytearray([0x87, 0x00])) # sta [$00]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0220, bytearray([0xA9, 0x02])) # lda #$02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0222, bytearray([0x87, 0x03])) # sta [$03]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0224, bytearray([0x20, 0xC0, 0xA3])) # jsr update_flag_pointers
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0227, bytearray([0xA6, 0x60])) # handle_exit_2: ldx $60
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0229, bytearray([0xBF, 0x00, 0xF4, 0x0F])) # lda.l level_data,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x022D, bytearray([0x29, 0x02])) # and #$02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x022F, bytearray([0xF0, 0x1A])) # beq handle_exit_1
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0231, bytearray([0xA0, 0x5E])) # ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0233, bytearray([0xBF, 0x00, 0xA2, 0x7F])) # lda !level_clears,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0237, bytearray([0x29, 0x02])) # and #$02
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0239, bytearray([0xF0, 0x02])) # beq .write
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x023B, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x023D, bytearray([0x98])) # .write tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x023E, bytearray([0x87, 0x06])) # sta [$06]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0240, bytearray([0xA9, 0x04])) # lda #$04
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0242, bytearray([0x87, 0x00])) # sta [$00]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0244, bytearray([0xA9, 0x24])) # lda #$24
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0246, bytearray([0x87, 0x03])) # sta [$03]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0248, bytearray([0x20, 0xC0, 0xA3])) # jsr update_flag_pointers
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x024B, bytearray([0xA6, 0x60])) # handle_exit_1: ldx $60
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x024D, bytearray([0xBF, 0x00, 0xF4, 0x0F])) # lda.l level_data,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0251, bytearray([0x29, 0x01])) # and #$01
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0253, bytearray([0xF0, 0x1A])) # beq .dont_draw
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0255, bytearray([0xA0, 0x5E])) # ldy.b #!icon_not_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0257, bytearray([0xBF, 0x00, 0xA2, 0x7F])) # lda !level_clears,x
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x025B, bytearray([0x29, 0x01])) # and #$01
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x025D, bytearray([0xF0, 0x02])) # beq .write
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x025F, bytearray([0xA0, 0x3F])) # ldy.b #!icon_obtained
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0261, bytearray([0x98])) # .write tya
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0262, bytearray([0x87, 0x06])) # sta [$06]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0264, bytearray([0xA9, 0x04])) # lda #$04
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0266, bytearray([0x87, 0x00])) # sta [$00]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x0268, bytearray([0xA9, 0x23])) # lda #$23
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x026A, bytearray([0x87, 0x03])) # sta [$03]
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x026C, bytearray([0x20, 0xC0, 0xA3])) # jsr update_flag_pointers
+ rom.write_bytes(BUILD_TILEMAP_ADDR + 0x026F, bytearray([0x6B])) # .dont_draw rtl
+
+ LEVEL_INDICATOR_DATA_ADDR = 0x7F400
+ rom.write_bytes(LEVEL_INDICATOR_DATA_ADDR + 0x0000, bytearray([0x80,0x45,0x45,0x80,0x43,0x65,0x5D,0x51]))
+ rom.write_bytes(LEVEL_INDICATOR_DATA_ADDR + 0x0008, bytearray([0x01,0x47,0x47,0x51,0x65,0x45,0x41,0x4F]))
+ rom.write_bytes(LEVEL_INDICATOR_DATA_ADDR + 0x0010, bytearray([0x55,0x45,0x80,0x43,0x01,0x57,0x80,0x80]))
+ rom.write_bytes(LEVEL_INDICATOR_DATA_ADDR + 0x0018, bytearray([0x45,0x80,0x51,0x41,0x45,0x45,0x80,0x41]))
+ rom.write_bytes(LEVEL_INDICATOR_DATA_ADDR + 0x0020, bytearray([0x45,0x41,0x4D,0x67,0x57,0x41,0x55,0x65]))
+ rom.write_bytes(LEVEL_INDICATOR_DATA_ADDR + 0x0028, bytearray([0x80,0x4D,0x45,0x55,0x80,0x47,0x4D,0x45]))
+ rom.write_bytes(LEVEL_INDICATOR_DATA_ADDR + 0x0030, bytearray([0x80,0x80,0x80,0x43,0x55,0x41,0x80,0x45]))
+ rom.write_bytes(LEVEL_INDICATOR_DATA_ADDR + 0x0038, bytearray([0x47,0x57,0x4D,0x41,0x47,0x55,0x47,0x01]))
+ rom.write_bytes(LEVEL_INDICATOR_DATA_ADDR + 0x0040, bytearray([0x41,0x4F,0x43,0x47,0x47,0x01,0x45,0x57]))
+ rom.write_bytes(LEVEL_INDICATOR_DATA_ADDR + 0x0048, bytearray([0x80,0x45,0x45,0x45,0x45,0x80,0x55,0x45]))
+ rom.write_bytes(LEVEL_INDICATOR_DATA_ADDR + 0x0050, bytearray([0x45,0x45,0x80,0x80,0x43,0x80,0x43,0x80]))
+ rom.write_bytes(LEVEL_INDICATOR_DATA_ADDR + 0x0058, bytearray([0x07,0x43,0x43,0x80,0x80,0x80,0x80,0x80]))
+
+
+def handle_indicators(rom):
+ INDICATOR_QUEUE_CODE = 0x86000
+ rom.write_bytes(0x022E6, bytearray([0x22, 0x00, 0xE0, 0x10])) # org $00A2E6 : jsl gm14_hijack
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0000, bytearray([0xAD, 0x00, 0x01])) # gm14_hijack: lda $0100
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0003, bytearray([0xC9, 0x14])) # cmp #$14
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0005, bytearray([0xD0, 0x04])) # bne .invalid
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0007, bytearray([0xA5, 0x71])) # lda $71
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0009, bytearray([0xF0, 0x04])) # beq .valid
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x000B, bytearray([0x5C, 0xB1, 0x8A, 0x02])) # .invalid jml $028AB1
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x000F, bytearray([0xC2, 0x30])) # .valid rep #$30
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0011, bytearray([0xAF, 0x04, 0xB0, 0x7F])) # lda !score_sprite_add_1_coin
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0015, bytearray([0xF0, 0x03])) # beq .no_1_coin
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0017, bytearray([0x20, 0xC1, 0xE0])) # jsr add_1_coin
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x001A, bytearray([0xAF, 0x06, 0xB0, 0x7F])) # .no_1_coin lda !score_sprite_add_5_coins
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x001E, bytearray([0xF0, 0x03])) # beq .no_5_coins
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0020, bytearray([0x20, 0xDF, 0xE0])) # jsr add_5_coins
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0023, bytearray([0xAF, 0x08, 0xB0, 0x7F])) # .no_5_coins lda !score_sprite_add_10_coins
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0027, bytearray([0xF0, 0x03])) # beq .no_10_coins
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0029, bytearray([0x20, 0xFD, 0xE0])) # jsr add_10_coins
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x002C, bytearray([0xAF, 0x0A, 0xB0, 0x7F])) # .no_10_coins lda !score_sprite_add_15_coins
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0030, bytearray([0xF0, 0x03])) # beq .no_15_coins
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0032, bytearray([0x20, 0x1B, 0xE1])) # jsr add_15_coins
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0035, bytearray([0xAF, 0x10, 0xB0, 0x7F])) # .no_15_coins lda !score_sprite_add_1up
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0039, bytearray([0xF0, 0x03])) # beq .no_1up
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x003B, bytearray([0x20, 0x39, 0xE1])) # jsr add_1up
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x003E, bytearray([0xAF, 0x0C, 0xB0, 0x7F])) # .no_1up lda !score_sprite_add_yoshi_egg
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0042, bytearray([0xF0, 0x03])) # beq .no_yoshi_egg
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0044, bytearray([0x20, 0x57, 0xE1])) # jsr add_yoshi_egg
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0047, bytearray([0xAF, 0x0E, 0xB0, 0x7F])) # .no_yoshi_egg lda !score_sprite_add_boss_token
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x004B, bytearray([0xF0, 0x03])) # beq .no_boss_token
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x004D, bytearray([0x20, 0xCF, 0xE1])) # jsr add_boss_token
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0050, bytearray([0xE2, 0x30])) # .no_boss_token sep #$30
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0052, bytearray([0x20, 0xED, 0xE1])) # jsr goal_sanity_check
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0055, bytearray([0x20, 0x5C, 0xE0])) # jsr score_sprite_queue
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0058, bytearray([0x5C, 0xB1, 0x8A, 0x02])) # jml $028AB1
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x005C, bytearray([0xAF, 0x20, 0xB0, 0x7F])) # score_sprite_queue: lda !score_sprite_queue_delay
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0060, bytearray([0xF0, 0x06])) # beq .spawn
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0062, bytearray([0x3A])) # dec
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0063, bytearray([0x8F, 0x20, 0xB0, 0x7F])) # sta !score_sprite_queue_delay
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0067, bytearray([0x60])) # rts
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0068, bytearray([0xA9, 0x08])) # .spawn lda #$08
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x006A, bytearray([0x8F, 0x20, 0xB0, 0x7F])) # sta !score_sprite_queue_delay
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x006E, bytearray([0xC2, 0x30])) # rep #$30
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0070, bytearray([0xAF, 0x02, 0xB0, 0x7F])) # lda !score_sprite_index
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0074, bytearray([0xCF, 0x00, 0xB0, 0x7F])) # cmp !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0078, bytearray([0xD0, 0x03])) # bne .check_slots
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x007A, bytearray([0xE2, 0x30])) # sep #$30
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x007C, bytearray([0x60])) # rts
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x007D, bytearray([0xA0, 0x05, 0x00])) # .check_slots ldy #$0005
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0080, bytearray([0xB9, 0xE1, 0x16])) # ..loop lda !score_sprite_num,y
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0083, bytearray([0x29, 0xFF, 0x00])) # and #$00FF
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0086, bytearray([0xF0, 0x06])) # beq .found_free
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0088, bytearray([0x88])) # dey
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0089, bytearray([0x10, 0xF5])) # bpl ..loop
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x008B, bytearray([0xE2, 0x30])) # sep #$30
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x008D, bytearray([0x60])) # rts
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x008E, bytearray([0xAF, 0x02, 0xB0, 0x7F])) # .found_free lda !score_sprite_index
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0092, bytearray([0x1A])) # inc
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0093, bytearray([0xAA])) # tax
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0094, bytearray([0x8F, 0x02, 0xB0, 0x7F])) # sta !score_sprite_index
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0098, bytearray([0xBF, 0x22, 0xB0, 0x7F])) # lda !score_sprite_queue,x
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x009C, bytearray([0xE2, 0x30])) # sep #$30
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x009E, bytearray([0x99, 0xE1, 0x16])) # sta !score_sprite_num,y
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00A1, bytearray([0xA5, 0x94])) # lda $94
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00A3, bytearray([0x99, 0xED, 0x16])) # sta !score_sprite_x_lo,y
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00A6, bytearray([0xA5, 0x95])) # lda $95
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00A8, bytearray([0x99, 0xF3, 0x16])) # sta !score_sprite_x_hi,y
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00AB, bytearray([0xA5, 0x96])) # lda $96
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00AD, bytearray([0x99, 0xE7, 0x16])) # sta !score_sprite_y_lo,y
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00B0, bytearray([0xA5, 0x97])) # lda $97
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00B2, bytearray([0x99, 0xF9, 0x16])) # sta !score_sprite_y_hi,y
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00B5, bytearray([0xA9, 0x30])) # lda #$30
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00B7, bytearray([0x99, 0xFF, 0x16])) # sta !score_sprite_timer,y
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00BA, bytearray([0xAD, 0xF9, 0x13])) # lda $13F9
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00BD, bytearray([0x99, 0x05, 0x17])) # sta !score_sprite_layer,y
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00C0, bytearray([0x60])) # rts
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00C1, bytearray([0xAF, 0x04, 0xB0, 0x7F])) # add_1_coin: lda !score_sprite_add_1_coin
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00C5, bytearray([0x3A])) # dec
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00C6, bytearray([0x8F, 0x04, 0xB0, 0x7F])) # sta !score_sprite_add_1_coin
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00CA, bytearray([0xAF, 0x00, 0xB0, 0x7F])) # lda !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00CE, bytearray([0x1A])) # inc
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00CF, bytearray([0x8F, 0x00, 0xB0, 0x7F])) # sta !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00D3, bytearray([0xAA])) # tax
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00D4, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00D6, bytearray([0xA9, 0x11])) # lda #$11
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00D8, bytearray([0x9F, 0x22, 0xB0, 0x7F])) # sta !score_sprite_queue,x
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00DC, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00DE, bytearray([0x60])) # rts
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00DF, bytearray([0xAF, 0x06, 0xB0, 0x7F])) # add_5_coins: lda !score_sprite_add_5_coins
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00E3, bytearray([0x3A])) # dec
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00E4, bytearray([0x8F, 0x06, 0xB0, 0x7F])) # sta !score_sprite_add_5_coins
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00E8, bytearray([0xAF, 0x00, 0xB0, 0x7F])) # lda !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00EC, bytearray([0x1A])) # inc
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00ED, bytearray([0x8F, 0x00, 0xB0, 0x7F])) # sta !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00F1, bytearray([0xAA])) # tax
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00F2, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00F4, bytearray([0xA9, 0x12])) # lda #$12
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00F6, bytearray([0x9F, 0x22, 0xB0, 0x7F])) # sta !score_sprite_queue,x
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00FA, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00FC, bytearray([0x60])) # rts
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x00FD, bytearray([0xAF, 0x08, 0xB0, 0x7F])) # add_10_coins: lda !score_sprite_add_10_coins
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0101, bytearray([0x3A])) # dec
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0102, bytearray([0x8F, 0x08, 0xB0, 0x7F])) # sta !score_sprite_add_10_coins
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0106, bytearray([0xAF, 0x00, 0xB0, 0x7F])) # lda !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x010A, bytearray([0x1A])) # inc
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x010B, bytearray([0x8F, 0x00, 0xB0, 0x7F])) # sta !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x010F, bytearray([0xAA])) # tax
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0110, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0112, bytearray([0xA9, 0x13])) # lda #$13
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0114, bytearray([0x9F, 0x22, 0xB0, 0x7F])) # sta !score_sprite_queue,x
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0118, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x011A, bytearray([0x60])) # rts
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x011B, bytearray([0xAF, 0x0A, 0xB0, 0x7F])) # add_15_coins: lda !score_sprite_add_15_coins
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x011F, bytearray([0x3A])) # dec
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0120, bytearray([0x8F, 0x0A, 0xB0, 0x7F])) # sta !score_sprite_add_15_coins
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0124, bytearray([0xAF, 0x00, 0xB0, 0x7F])) # lda !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0128, bytearray([0x1A])) # inc
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0129, bytearray([0x8F, 0x00, 0xB0, 0x7F])) # sta !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x012D, bytearray([0xAA])) # tax
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x012E, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0130, bytearray([0xA9, 0x14])) # lda #$14
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0132, bytearray([0x9F, 0x22, 0xB0, 0x7F])) # sta !score_sprite_queue,x
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0136, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0138, bytearray([0x60])) # rts
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0139, bytearray([0xAF, 0x10, 0xB0, 0x7F])) # add_1up: lda !score_sprite_add_1up
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x013D, bytearray([0x3A])) # dec
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x013E, bytearray([0x8F, 0x10, 0xB0, 0x7F])) # sta !score_sprite_add_1up
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0142, bytearray([0xAF, 0x00, 0xB0, 0x7F])) # lda !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0146, bytearray([0x1A])) # inc
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0147, bytearray([0x8F, 0x00, 0xB0, 0x7F])) # sta !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x014B, bytearray([0xAA])) # tax
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x014C, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x014E, bytearray([0xA9, 0x16])) # lda #$16
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0150, bytearray([0x9F, 0x22, 0xB0, 0x7F])) # sta !score_sprite_queue,x
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0154, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0156, bytearray([0x60])) # rts
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0157, bytearray([0xAF, 0x0C, 0xB0, 0x7F])) # add_yoshi_egg: lda !score_sprite_add_yoshi_egg
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x015B, bytearray([0x3A])) # dec
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x015C, bytearray([0x8F, 0x0C, 0xB0, 0x7F])) # sta !score_sprite_add_yoshi_egg
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0160, bytearray([0xAF, 0x00, 0xB0, 0x7F])) # lda !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0164, bytearray([0x1A])) # inc
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0165, bytearray([0x8F, 0x00, 0xB0, 0x7F])) # sta !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0169, bytearray([0xAA])) # tax
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x016A, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x016C, bytearray([0xA9, 0x15])) # lda #$15
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x016E, bytearray([0x9F, 0x22, 0xB0, 0x7F])) # sta !score_sprite_queue,x
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0172, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0174, bytearray([0x60])) # rts
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0175, bytearray([0xAF, 0x12, 0xB0, 0x7F])) # add_mushroom: lda !score_sprite_add_mushroom
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0179, bytearray([0x3A])) # dec
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x017A, bytearray([0x8F, 0x12, 0xB0, 0x7F])) # sta !score_sprite_add_mushroom
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x017E, bytearray([0xAF, 0x00, 0xB0, 0x7F])) # lda !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0182, bytearray([0x1A])) # inc
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0183, bytearray([0x8F, 0x00, 0xB0, 0x7F])) # sta !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0187, bytearray([0xAA])) # tax
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0188, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x018A, bytearray([0xA9, 0x17])) # lda #$17
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x018C, bytearray([0x9F, 0x22, 0xB0, 0x7F])) # sta !score_sprite_queue,x
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0190, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0192, bytearray([0x60])) # rts
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0193, bytearray([0xAF, 0x14, 0xB0, 0x7F])) # add_flower: lda !score_sprite_add_flower
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0197, bytearray([0x3A])) # dec
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0198, bytearray([0x8F, 0x14, 0xB0, 0x7F])) # sta !score_sprite_add_flower
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x019C, bytearray([0xAF, 0x00, 0xB0, 0x7F])) # lda !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01A0, bytearray([0x1A])) # inc
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01A1, bytearray([0x8F, 0x00, 0xB0, 0x7F])) # sta !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01A5, bytearray([0xAA])) # tax
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01A6, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01A8, bytearray([0xA9, 0x18])) # lda #$18
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01AA, bytearray([0x9F, 0x22, 0xB0, 0x7F])) # sta !score_sprite_queue,x
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01AE, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01B0, bytearray([0x60])) # rts
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01B1, bytearray([0xAF, 0x16, 0xB0, 0x7F])) # add_feather: lda !score_sprite_add_feather
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01B5, bytearray([0x3A])) # dec
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01B6, bytearray([0x8F, 0x16, 0xB0, 0x7F])) # sta !score_sprite_add_feather
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01BA, bytearray([0xAF, 0x00, 0xB0, 0x7F])) # lda !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01BE, bytearray([0x1A])) # inc
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01BF, bytearray([0x8F, 0x00, 0xB0, 0x7F])) # sta !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01C3, bytearray([0xAA])) # tax
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01C4, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01C6, bytearray([0xA9, 0x19])) # lda #$19
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01C8, bytearray([0x9F, 0x22, 0xB0, 0x7F])) # sta !score_sprite_queue,x
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01CC, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01CE, bytearray([0x60])) # rts
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01CF, bytearray([0xAF, 0x0E, 0xB0, 0x7F])) # add_boss_token: lda !score_sprite_add_boss_token
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01D3, bytearray([0x3A])) # dec
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01D4, bytearray([0x8F, 0x0E, 0xB0, 0x7F])) # sta !score_sprite_add_boss_token
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01D8, bytearray([0xAF, 0x00, 0xB0, 0x7F])) # lda !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01DC, bytearray([0x1A])) # inc
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01DD, bytearray([0x8F, 0x00, 0xB0, 0x7F])) # sta !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01E1, bytearray([0xAA])) # tax
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01E2, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01E4, bytearray([0xA9, 0x1A])) # lda #$1A
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01E6, bytearray([0x9F, 0x22, 0xB0, 0x7F])) # sta !score_sprite_queue,x
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01EA, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01EC, bytearray([0x60])) # rts
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01ED, bytearray([0xAF, 0xA0, 0xBF, 0x03])) # goal_sanity_check: lda $03BFA0
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01F1, bytearray([0x29, 0x01])) # and #$01
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01F3, bytearray([0x49, 0x01])) # eor #$01
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01F5, bytearray([0x0A])) # asl
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01F6, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01F8, bytearray([0xBF, 0x0C, 0xB0, 0x7F])) # lda !score_sprite_add_yoshi_egg,x
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01FC, bytearray([0xD0, 0x18])) # bne .return
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x01FE, bytearray([0xAF, 0x02, 0xB0, 0x7F])) # .check_queue lda !score_sprite_index
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0202, bytearray([0xCF, 0x00, 0xB0, 0x7F])) # cmp !score_sprite_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0206, bytearray([0xD0, 0x0E])) # bne .return
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0208, bytearray([0xE2, 0x20])) # .check_count sep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x020A, bytearray([0xAF, 0x1E, 0xA0, 0x7F])) # lda !goal_item_count
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x020E, bytearray([0xDD, 0x24, 0x1F])) # cmp $1F24,x
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0211, bytearray([0xF0, 0x03])) # beq .return
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0213, bytearray([0x9D, 0x24, 0x1F])) # sta $1F24,x
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0216, bytearray([0xE2, 0x20])) # .return sep #$20
+ rom.write_bytes(INDICATOR_QUEUE_CODE + 0x0218, bytearray([0x60])) # rts
+
+ # Add code for indicators when receiving items during levels
+ INDICATOR_CODE = 0x84000
+ rom.write_bytes(0x12DBA, bytearray([0x5C, 0x00, 0xC0, 0x10])) # org $02ADBA : jsl score_sprites
+ rom.write_bytes(INDICATOR_CODE + 0x0000, bytearray([0xBD, 0xE1, 0x16])) # score_sprites: lda !score_sprite_num,x
+ rom.write_bytes(INDICATOR_CODE + 0x0003, bytearray([0xF0, 0x2D])) # beq .return
+ rom.write_bytes(INDICATOR_CODE + 0x0005, bytearray([0x8E, 0xE9, 0x15])) # stx $15E9
+ rom.write_bytes(INDICATOR_CODE + 0x0008, bytearray([0xC2, 0x30])) # rep #$30
+ rom.write_bytes(INDICATOR_CODE + 0x000A, bytearray([0x29, 0x1F, 0x00])) # and #$001F
+ rom.write_bytes(INDICATOR_CODE + 0x000D, bytearray([0x85, 0x00])) # sta $00
+ rom.write_bytes(INDICATOR_CODE + 0x000F, bytearray([0x0A])) # asl
+ rom.write_bytes(INDICATOR_CODE + 0x0010, bytearray([0x18])) # clc
+ rom.write_bytes(INDICATOR_CODE + 0x0011, bytearray([0x65, 0x00])) # adc $00
+ rom.write_bytes(INDICATOR_CODE + 0x0013, bytearray([0xAA])) # tax
+ rom.write_bytes(INDICATOR_CODE + 0x0014, bytearray([0xBF, 0x37, 0xC0, 0x10])) # lda.l .pointers-3,x
+ rom.write_bytes(INDICATOR_CODE + 0x0018, bytearray([0x85, 0x00])) # sta $00
+ rom.write_bytes(INDICATOR_CODE + 0x001A, bytearray([0xE2, 0x30])) # sep #$30
+ rom.write_bytes(INDICATOR_CODE + 0x001C, bytearray([0xBF, 0x39, 0xC0, 0x10])) # lda.l .pointers-3+2,x
+ rom.write_bytes(INDICATOR_CODE + 0x0020, bytearray([0x85, 0x02])) # sta $02
+ rom.write_bytes(INDICATOR_CODE + 0x0022, bytearray([0xE2, 0x10])) # sep #$10
+ rom.write_bytes(INDICATOR_CODE + 0x0024, bytearray([0xAE, 0xE9, 0x15])) # ldx $15E9
+ rom.write_bytes(INDICATOR_CODE + 0x0027, bytearray([0x8B])) # phb
+ rom.write_bytes(INDICATOR_CODE + 0x0028, bytearray([0x48])) # pha
+ rom.write_bytes(INDICATOR_CODE + 0x0029, bytearray([0xAB])) # plb
+ rom.write_bytes(INDICATOR_CODE + 0x002A, bytearray([0x4B])) # phk
+ rom.write_bytes(INDICATOR_CODE + 0x002B, bytearray([0xF4, 0x30, 0xC0])) # pea.w .return_code-1
+ rom.write_bytes(INDICATOR_CODE + 0x002E, bytearray([0xDC, 0x00, 0x00])) # jml [$0000]
+ rom.write_bytes(INDICATOR_CODE + 0x0031, bytearray([0xAB])) # .return_code plb
+ rom.write_bytes(INDICATOR_CODE + 0x0032, bytearray([0x5C, 0xC5, 0xAD, 0x02])) # .return jml $02ADC5
+ rom.write_bytes(INDICATOR_CODE + 0x0036, bytearray([0x9E, 0xE1, 0x16])) # .kill stz !score_sprite_num,x
+ rom.write_bytes(INDICATOR_CODE + 0x0039, bytearray([0x6B])) # rtl
+ rom.write_bytes(INDICATOR_CODE + 0x003A, bytearray([0x97, 0xC0, 0x10])) # .pointers dl original_score_sprites ; 01 - 10 points
+ rom.write_bytes(INDICATOR_CODE + 0x003D, bytearray([0x97, 0xC0, 0x10])) # dl original_score_sprites ; 02 - 20 points
+ rom.write_bytes(INDICATOR_CODE + 0x0040, bytearray([0x97, 0xC0, 0x10])) # dl original_score_sprites ; 03 - 40 points
+ rom.write_bytes(INDICATOR_CODE + 0x0043, bytearray([0x97, 0xC0, 0x10])) # dl original_score_sprites ; 04 - 80 points
+ rom.write_bytes(INDICATOR_CODE + 0x0046, bytearray([0x97, 0xC0, 0x10])) # dl original_score_sprites ; 05 - 100 points
+ rom.write_bytes(INDICATOR_CODE + 0x0049, bytearray([0x97, 0xC0, 0x10])) # dl original_score_sprites ; 06 - 200 points
+ rom.write_bytes(INDICATOR_CODE + 0x004C, bytearray([0x97, 0xC0, 0x10])) # dl original_score_sprites ; 07 - 400 points
+ rom.write_bytes(INDICATOR_CODE + 0x004F, bytearray([0x97, 0xC0, 0x10])) # dl original_score_sprites ; 08 - 800 points
+ rom.write_bytes(INDICATOR_CODE + 0x0052, bytearray([0x97, 0xC0, 0x10])) # dl original_score_sprites ; 09 - 1000 points
+ rom.write_bytes(INDICATOR_CODE + 0x0055, bytearray([0x97, 0xC0, 0x10])) # dl original_score_sprites ; 0A - 2000 points
+ rom.write_bytes(INDICATOR_CODE + 0x0058, bytearray([0x97, 0xC0, 0x10])) # dl original_score_sprites ; 0B - 4000 points
+ rom.write_bytes(INDICATOR_CODE + 0x005B, bytearray([0x97, 0xC0, 0x10])) # dl original_score_sprites ; 0C - 8000 points
+ rom.write_bytes(INDICATOR_CODE + 0x005E, bytearray([0x97, 0xC0, 0x10])) # dl original_score_sprites ; 0D - 1-up
+ rom.write_bytes(INDICATOR_CODE + 0x0061, bytearray([0x97, 0xC0, 0x10])) # dl original_score_sprites ; 0E - 2-up
+ rom.write_bytes(INDICATOR_CODE + 0x0064, bytearray([0x97, 0xC0, 0x10])) # dl original_score_sprites ; 0F - 3-up
+ rom.write_bytes(INDICATOR_CODE + 0x0067, bytearray([0x97, 0xC0, 0x10])) # dl original_score_sprites ; 10 - 5-up
+ rom.write_bytes(INDICATOR_CODE + 0x006A, bytearray([0xA7, 0xC0, 0x10])) # dl icon_score ; 11 - 1 coin
+ rom.write_bytes(INDICATOR_CODE + 0x006D, bytearray([0xA7, 0xC0, 0x10])) # dl icon_score ; 12 - 5 coins
+ rom.write_bytes(INDICATOR_CODE + 0x0070, bytearray([0xA7, 0xC0, 0x10])) # dl icon_score ; 13 - 10 coins
+ rom.write_bytes(INDICATOR_CODE + 0x0073, bytearray([0xA7, 0xC0, 0x10])) # dl icon_score ; 14 - 15 coins
+ rom.write_bytes(INDICATOR_CODE + 0x0076, bytearray([0xA7, 0xC0, 0x10])) # dl icon_score ; 15 - Yoshi Egg
+ rom.write_bytes(INDICATOR_CODE + 0x0079, bytearray([0xA7, 0xC0, 0x10])) # dl icon_score ; 16 - 1up Mushroom
+ rom.write_bytes(INDICATOR_CODE + 0x007C, bytearray([0xA7, 0xC0, 0x10])) # dl icon_score ; 17 - Mushroom
+ rom.write_bytes(INDICATOR_CODE + 0x007F, bytearray([0xA7, 0xC0, 0x10])) # dl icon_score ; 18 - Flower
+ rom.write_bytes(INDICATOR_CODE + 0x0082, bytearray([0xA7, 0xC0, 0x10])) # dl icon_score ; 19 - Feather
+ rom.write_bytes(INDICATOR_CODE + 0x0085, bytearray([0xA7, 0xC0, 0x10])) # dl icon_score ; 1A - Boss token
+ rom.write_bytes(INDICATOR_CODE + 0x0088, bytearray([0xA7, 0xC0, 0x10])) # dl icon_score ; 1B -
+ rom.write_bytes(INDICATOR_CODE + 0x008B, bytearray([0xA7, 0xC0, 0x10])) # dl icon_score ; 1C -
+ rom.write_bytes(INDICATOR_CODE + 0x008E, bytearray([0xA7, 0xC0, 0x10])) # dl icon_score ; 1D -
+ rom.write_bytes(INDICATOR_CODE + 0x0091, bytearray([0xA7, 0xC0, 0x10])) # dl icon_score ; 1E -
+ rom.write_bytes(INDICATOR_CODE + 0x0094, bytearray([0xA7, 0xC0, 0x10])) # dl icon_score ; 1F -
+ rom.write_bytes(INDICATOR_CODE + 0x0097, bytearray([0xA9, 0x02])) # original_score_sprites: lda #$02
+ rom.write_bytes(INDICATOR_CODE + 0x0099, bytearray([0x48])) # pha
+ rom.write_bytes(INDICATOR_CODE + 0x009A, bytearray([0xAB])) # plb
+ rom.write_bytes(INDICATOR_CODE + 0x009B, bytearray([0x4B])) # phk
+ rom.write_bytes(INDICATOR_CODE + 0x009C, bytearray([0xF4, 0xA5, 0xC0])) # pea.w .jslrtsreturn-1
+ rom.write_bytes(INDICATOR_CODE + 0x009F, bytearray([0xF4, 0x88, 0xB8])) # pea.w $B889-1
+ rom.write_bytes(INDICATOR_CODE + 0x00A2, bytearray([0x5C, 0xC9, 0xAD, 0x02])) # jml $02ADC9
+ rom.write_bytes(INDICATOR_CODE + 0x00A6, bytearray([0x6B])) # .jslrtsreturn rtl
+ rom.write_bytes(INDICATOR_CODE + 0x00A7, bytearray([0xBD, 0xFF, 0x16])) # icon_score: lda !score_sprite_timer,x
+ rom.write_bytes(INDICATOR_CODE + 0x00AA, bytearray([0xD0, 0x04])) # bne .active
+ rom.write_bytes(INDICATOR_CODE + 0x00AC, bytearray([0x9E, 0xE1, 0x16])) # stz !score_sprite_num,x
+ rom.write_bytes(INDICATOR_CODE + 0x00AF, bytearray([0x6B])) # rtl
+ rom.write_bytes(INDICATOR_CODE + 0x00B0, bytearray([0xDE, 0xFF, 0x16])) # .active dec !score_sprite_timer,x
+ rom.write_bytes(INDICATOR_CODE + 0x00B3, bytearray([0xC9, 0x30])) # cmp #$30
+ rom.write_bytes(INDICATOR_CODE + 0x00B5, bytearray([0xD0, 0x14])) # bne .handle_movement
+ rom.write_bytes(INDICATOR_CODE + 0x00B7, bytearray([0xBD, 0xE1, 0x16])) # lda !score_sprite_num,x
+ rom.write_bytes(INDICATOR_CODE + 0x00BA, bytearray([0x38])) # sec
+ rom.write_bytes(INDICATOR_CODE + 0x00BB, bytearray([0xE9, 0x11])) # sbc #$11
+ rom.write_bytes(INDICATOR_CODE + 0x00BD, bytearray([0x0A])) # asl
+ rom.write_bytes(INDICATOR_CODE + 0x00BE, bytearray([0xA8])) # tay
+ rom.write_bytes(INDICATOR_CODE + 0x00BF, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(INDICATOR_CODE + 0x00C1, bytearray([0xB9, 0x4B, 0xC2])) # lda .reward_ptrs,y
+ rom.write_bytes(INDICATOR_CODE + 0x00C4, bytearray([0x85, 0x00])) # sta $00
+ rom.write_bytes(INDICATOR_CODE + 0x00C6, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(INDICATOR_CODE + 0x00C8, bytearray([0x6C, 0x00, 0x00])) # jmp ($0000)
+ rom.write_bytes(INDICATOR_CODE + 0x00CB, bytearray([0xBD, 0xFF, 0x16])) # .handle_movement lda !score_sprite_timer,x
+ rom.write_bytes(INDICATOR_CODE + 0x00CE, bytearray([0x4A])) # lsr
+ rom.write_bytes(INDICATOR_CODE + 0x00CF, bytearray([0x4A])) # lsr
+ rom.write_bytes(INDICATOR_CODE + 0x00D0, bytearray([0x4A])) # lsr
+ rom.write_bytes(INDICATOR_CODE + 0x00D1, bytearray([0x4A])) # lsr
+ rom.write_bytes(INDICATOR_CODE + 0x00D2, bytearray([0xA8])) # tay
+ rom.write_bytes(INDICATOR_CODE + 0x00D3, bytearray([0xA5, 0x13])) # lda $13
+ rom.write_bytes(INDICATOR_CODE + 0x00D5, bytearray([0x39, 0xF0, 0xC0])) # and .speed,y
+ rom.write_bytes(INDICATOR_CODE + 0x00D8, bytearray([0xD0, 0x14])) # bne ..skip_update
+ rom.write_bytes(INDICATOR_CODE + 0x00DA, bytearray([0xBD, 0xE7, 0x16])) # lda !score_sprite_y_lo,x
+ rom.write_bytes(INDICATOR_CODE + 0x00DD, bytearray([0xA8])) # tay
+ rom.write_bytes(INDICATOR_CODE + 0x00DE, bytearray([0x38])) # sec
+ rom.write_bytes(INDICATOR_CODE + 0x00DF, bytearray([0xE5, 0x1C])) # sbc $1C
+ rom.write_bytes(INDICATOR_CODE + 0x00E1, bytearray([0xC9, 0x04])) # cmp #$04
+ rom.write_bytes(INDICATOR_CODE + 0x00E3, bytearray([0x90, 0x09])) # bcc ..skip_update
+ rom.write_bytes(INDICATOR_CODE + 0x00E5, bytearray([0xDE, 0xE7, 0x16])) # dec !score_sprite_y_lo,x
+ rom.write_bytes(INDICATOR_CODE + 0x00E8, bytearray([0x98])) # tya
+ rom.write_bytes(INDICATOR_CODE + 0x00E9, bytearray([0xD0, 0x03])) # bne ..skip_update
+ rom.write_bytes(INDICATOR_CODE + 0x00EB, bytearray([0xDE, 0xF9, 0x16])) # dec !score_sprite_y_hi,x
+ rom.write_bytes(INDICATOR_CODE + 0x00EE, bytearray([0x80, 0x05])) # ..skip_update bra .gfx
+ rom.write_bytes(INDICATOR_CODE + 0x00F0, bytearray([0x03, 0x01, 0x00, 0x00])) # .speed db $03,$01,$00,$00
+ rom.write_bytes(INDICATOR_CODE + 0x00F4, bytearray([0x6B])) # .return rtl
+ rom.write_bytes(INDICATOR_CODE + 0x00F5, bytearray([0xBD, 0x05, 0x17])) # .gfx lda !score_sprite_layer,x
+ rom.write_bytes(INDICATOR_CODE + 0x00F8, bytearray([0x0A])) # asl
+ rom.write_bytes(INDICATOR_CODE + 0x00F9, bytearray([0x0A])) # asl
+ rom.write_bytes(INDICATOR_CODE + 0x00FA, bytearray([0xA8])) # tay
+ rom.write_bytes(INDICATOR_CODE + 0x00FB, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(INDICATOR_CODE + 0x00FD, bytearray([0xB9, 0x1C, 0x00])) # lda $001C,y
+ rom.write_bytes(INDICATOR_CODE + 0x0100, bytearray([0x85, 0x02])) # sta $02
+ rom.write_bytes(INDICATOR_CODE + 0x0102, bytearray([0xB9, 0x1A, 0x00])) # lda $001A,y
+ rom.write_bytes(INDICATOR_CODE + 0x0105, bytearray([0x85, 0x04])) # sta $04
+ rom.write_bytes(INDICATOR_CODE + 0x0107, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(INDICATOR_CODE + 0x0109, bytearray([0xBD, 0xF3, 0x16])) # lda !score_sprite_x_hi,x
+ rom.write_bytes(INDICATOR_CODE + 0x010C, bytearray([0xEB])) # xba
+ rom.write_bytes(INDICATOR_CODE + 0x010D, bytearray([0xBD, 0xED, 0x16])) # lda !score_sprite_x_lo,x
+ rom.write_bytes(INDICATOR_CODE + 0x0110, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(INDICATOR_CODE + 0x0112, bytearray([0x38])) # sec
+ rom.write_bytes(INDICATOR_CODE + 0x0113, bytearray([0xE5, 0x04])) # sbc $04
+ rom.write_bytes(INDICATOR_CODE + 0x0115, bytearray([0x38])) # sec
+ rom.write_bytes(INDICATOR_CODE + 0x0116, bytearray([0xE9, 0x06, 0x00])) # sbc #$0006
+ rom.write_bytes(INDICATOR_CODE + 0x0119, bytearray([0xC9, 0xEA, 0x00])) # cmp #$00EA
+ rom.write_bytes(INDICATOR_CODE + 0x011C, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(INDICATOR_CODE + 0x011E, bytearray([0xB0, 0xD4])) # bcs .return
+ rom.write_bytes(INDICATOR_CODE + 0x0120, bytearray([0xBD, 0xE7, 0x16])) # lda !score_sprite_y_lo,x
+ rom.write_bytes(INDICATOR_CODE + 0x0123, bytearray([0xC5, 0x02])) # cmp $02
+ rom.write_bytes(INDICATOR_CODE + 0x0125, bytearray([0xBD, 0xF9, 0x16])) # lda !score_sprite_y_hi,x
+ rom.write_bytes(INDICATOR_CODE + 0x0128, bytearray([0xE5, 0x03])) # sbc $03
+ rom.write_bytes(INDICATOR_CODE + 0x012A, bytearray([0xD0, 0xC8])) # bne .return
+ rom.write_bytes(INDICATOR_CODE + 0x012C, bytearray([0xBF, 0x9E, 0xAD, 0x02])) # lda $02AD9E,x
+ rom.write_bytes(INDICATOR_CODE + 0x0130, bytearray([0xA8])) # tay
+ rom.write_bytes(INDICATOR_CODE + 0x0131, bytearray([0xBD, 0xE7, 0x16])) # lda !score_sprite_y_lo,x
+ rom.write_bytes(INDICATOR_CODE + 0x0134, bytearray([0x38])) # sec
+ rom.write_bytes(INDICATOR_CODE + 0x0135, bytearray([0xE5, 0x02])) # sbc $02
+ rom.write_bytes(INDICATOR_CODE + 0x0137, bytearray([0x99, 0x01, 0x02])) # sta $0201,y
+ rom.write_bytes(INDICATOR_CODE + 0x013A, bytearray([0x99, 0x05, 0x02])) # sta $0205,y
+ rom.write_bytes(INDICATOR_CODE + 0x013D, bytearray([0xBD, 0xED, 0x16])) # lda !score_sprite_x_lo,x
+ rom.write_bytes(INDICATOR_CODE + 0x0140, bytearray([0x38])) # sec
+ rom.write_bytes(INDICATOR_CODE + 0x0141, bytearray([0xE5, 0x04])) # sbc $04
+ rom.write_bytes(INDICATOR_CODE + 0x0143, bytearray([0x18])) # clc
+ rom.write_bytes(INDICATOR_CODE + 0x0144, bytearray([0x69, 0x09])) # adc #$09
+ rom.write_bytes(INDICATOR_CODE + 0x0146, bytearray([0x99, 0x00, 0x02])) # sta $0200,y
+ rom.write_bytes(INDICATOR_CODE + 0x0149, bytearray([0x18])) # clc
+ rom.write_bytes(INDICATOR_CODE + 0x014A, bytearray([0x69, 0x05])) # adc #$05
+ rom.write_bytes(INDICATOR_CODE + 0x014C, bytearray([0x99, 0x04, 0x02])) # sta $0204,y
+ rom.write_bytes(INDICATOR_CODE + 0x014F, bytearray([0xDA])) # phx
+ rom.write_bytes(INDICATOR_CODE + 0x0150, bytearray([0xBD, 0xE1, 0x16])) # lda !score_sprite_num,x
+ rom.write_bytes(INDICATOR_CODE + 0x0153, bytearray([0x38])) # sec
+ rom.write_bytes(INDICATOR_CODE + 0x0154, bytearray([0xE9, 0x11])) # sbc #$11
+ rom.write_bytes(INDICATOR_CODE + 0x0156, bytearray([0x0A])) # asl
+ rom.write_bytes(INDICATOR_CODE + 0x0157, bytearray([0xAA])) # tax
+ rom.write_bytes(INDICATOR_CODE + 0x0158, bytearray([0xBD, 0x09, 0xC2])) # lda ..num_tile+$00,x
+ rom.write_bytes(INDICATOR_CODE + 0x015B, bytearray([0x99, 0x02, 0x02])) # sta $0202,y
+ rom.write_bytes(INDICATOR_CODE + 0x015E, bytearray([0xBD, 0x0A, 0xC2])) # lda ..num_tile+$01,x
+ rom.write_bytes(INDICATOR_CODE + 0x0161, bytearray([0x99, 0x06, 0x02])) # sta $0206,y
+ rom.write_bytes(INDICATOR_CODE + 0x0164, bytearray([0xBD, 0x27, 0xC2])) # lda ..num_props+$00,x
+ rom.write_bytes(INDICATOR_CODE + 0x0167, bytearray([0x99, 0x03, 0x02])) # sta $0203,y
+ rom.write_bytes(INDICATOR_CODE + 0x016A, bytearray([0xBD, 0x28, 0xC2])) # lda ..num_props+$01,x
+ rom.write_bytes(INDICATOR_CODE + 0x016D, bytearray([0x99, 0x07, 0x02])) # sta $0207,y
+ rom.write_bytes(INDICATOR_CODE + 0x0170, bytearray([0xFA])) # plx
+ rom.write_bytes(INDICATOR_CODE + 0x0171, bytearray([0x98])) # tya
+ rom.write_bytes(INDICATOR_CODE + 0x0172, bytearray([0x4A])) # lsr
+ rom.write_bytes(INDICATOR_CODE + 0x0173, bytearray([0x4A])) # lsr
+ rom.write_bytes(INDICATOR_CODE + 0x0174, bytearray([0xA8])) # tay
+ rom.write_bytes(INDICATOR_CODE + 0x0175, bytearray([0xA9, 0x00])) # lda #$00
+ rom.write_bytes(INDICATOR_CODE + 0x0177, bytearray([0x99, 0x20, 0x04])) # sta $0420,y
+ rom.write_bytes(INDICATOR_CODE + 0x017A, bytearray([0x99, 0x21, 0x04])) # sta $0421,y
+ rom.write_bytes(INDICATOR_CODE + 0x017D, bytearray([0xBF, 0x45, 0xC2, 0x10])) # lda.l ..oam_2,x
+ rom.write_bytes(INDICATOR_CODE + 0x0181, bytearray([0xA8])) # tay
+ rom.write_bytes(INDICATOR_CODE + 0x0182, bytearray([0xBD, 0xE7, 0x16])) # lda !score_sprite_y_lo,x
+ rom.write_bytes(INDICATOR_CODE + 0x0185, bytearray([0x38])) # sec
+ rom.write_bytes(INDICATOR_CODE + 0x0186, bytearray([0xE5, 0x02])) # sbc $02
+ rom.write_bytes(INDICATOR_CODE + 0x0188, bytearray([0x99, 0x01, 0x02])) # sta $0201,y
+ rom.write_bytes(INDICATOR_CODE + 0x018B, bytearray([0x99, 0x05, 0x02])) # sta $0205,y
+ rom.write_bytes(INDICATOR_CODE + 0x018E, bytearray([0xBD, 0xED, 0x16])) # lda !score_sprite_x_lo,x
+ rom.write_bytes(INDICATOR_CODE + 0x0191, bytearray([0x38])) # sec
+ rom.write_bytes(INDICATOR_CODE + 0x0192, bytearray([0xE5, 0x04])) # sbc $04
+ rom.write_bytes(INDICATOR_CODE + 0x0194, bytearray([0xE9, 0x07])) # sbc #$07
+ rom.write_bytes(INDICATOR_CODE + 0x0196, bytearray([0x99, 0x00, 0x02])) # sta $0200,y
+ rom.write_bytes(INDICATOR_CODE + 0x0199, bytearray([0x18])) # clc
+ rom.write_bytes(INDICATOR_CODE + 0x019A, bytearray([0x69, 0x08])) # adc #$08
+ rom.write_bytes(INDICATOR_CODE + 0x019C, bytearray([0x99, 0x04, 0x02])) # sta $0204,y
+ rom.write_bytes(INDICATOR_CODE + 0x019F, bytearray([0xDA])) # phx
+ rom.write_bytes(INDICATOR_CODE + 0x01A0, bytearray([0xBD, 0xE1, 0x16])) # lda !score_sprite_num,x
+ rom.write_bytes(INDICATOR_CODE + 0x01A3, bytearray([0x38])) # sec
+ rom.write_bytes(INDICATOR_CODE + 0x01A4, bytearray([0xE9, 0x11])) # sbc #$11
+ rom.write_bytes(INDICATOR_CODE + 0x01A6, bytearray([0xAA])) # tax
+ rom.write_bytes(INDICATOR_CODE + 0x01A7, bytearray([0xBD, 0xCD, 0xC1])) # lda ..icon_tile,x
+ rom.write_bytes(INDICATOR_CODE + 0x01AA, bytearray([0x99, 0x02, 0x02])) # sta $0202,y
+ rom.write_bytes(INDICATOR_CODE + 0x01AD, bytearray([0xBD, 0xDC, 0xC1])) # lda ..icon_props,x
+ rom.write_bytes(INDICATOR_CODE + 0x01B0, bytearray([0x99, 0x03, 0x02])) # sta $0203,y
+ rom.write_bytes(INDICATOR_CODE + 0x01B3, bytearray([0xBD, 0xFA, 0xC1])) # lda ..plus_props,x
+ rom.write_bytes(INDICATOR_CODE + 0x01B6, bytearray([0x99, 0x07, 0x02])) # sta $0207,y
+ rom.write_bytes(INDICATOR_CODE + 0x01B9, bytearray([0xBD, 0xEB, 0xC1])) # lda ..plus_tile,x
+ rom.write_bytes(INDICATOR_CODE + 0x01BC, bytearray([0x99, 0x06, 0x02])) # sta $0206,y
+ rom.write_bytes(INDICATOR_CODE + 0x01BF, bytearray([0xFA])) # plx
+ rom.write_bytes(INDICATOR_CODE + 0x01C0, bytearray([0x98])) # tya
+ rom.write_bytes(INDICATOR_CODE + 0x01C1, bytearray([0x4A])) # lsr
+ rom.write_bytes(INDICATOR_CODE + 0x01C2, bytearray([0x4A])) # lsr
+ rom.write_bytes(INDICATOR_CODE + 0x01C3, bytearray([0xA8])) # tay
+ rom.write_bytes(INDICATOR_CODE + 0x01C4, bytearray([0xA9, 0x00])) # lda #$00
+ rom.write_bytes(INDICATOR_CODE + 0x01C6, bytearray([0x99, 0x20, 0x04])) # sta $0420,y
+ rom.write_bytes(INDICATOR_CODE + 0x01C9, bytearray([0x99, 0x21, 0x04])) # sta $0421,y
+ rom.write_bytes(INDICATOR_CODE + 0x01CC, bytearray([0x6B])) # rtl
+ rom.write_bytes(INDICATOR_CODE + 0x01CD, bytearray([0x1B])) # ..icon_tile db $1B ; 1 coin
+ rom.write_bytes(INDICATOR_CODE + 0x01CE, bytearray([0x1B])) # db $1B ; 5 coins
+ rom.write_bytes(INDICATOR_CODE + 0x01CF, bytearray([0x1B])) # db $1B ; 10 coins
+ rom.write_bytes(INDICATOR_CODE + 0x01D0, bytearray([0x1B])) # db $1B ; 15 coins
+ rom.write_bytes(INDICATOR_CODE + 0x01D1, bytearray([0x0A])) # db $0A ; yoshi egg
+ rom.write_bytes(INDICATOR_CODE + 0x01D2, bytearray([0x0B])) # db $0B ; 1up mushroom
+ rom.write_bytes(INDICATOR_CODE + 0x01D3, bytearray([0x0B])) # db $0B ; mushroom
+ rom.write_bytes(INDICATOR_CODE + 0x01D4, bytearray([0x7E])) # db $7E ; flower
+ rom.write_bytes(INDICATOR_CODE + 0x01D5, bytearray([0x7F])) # db $7F ; feather
+ rom.write_bytes(INDICATOR_CODE + 0x01D6, bytearray([0x38])) # db $38 ; boss token
+ rom.write_bytes(INDICATOR_CODE + 0x01D7, bytearray([0x5A])) # db $5A ;
+ rom.write_bytes(INDICATOR_CODE + 0x01D8, bytearray([0x5A])) # db $5A ;
+ rom.write_bytes(INDICATOR_CODE + 0x01D9, bytearray([0x5A])) # db $5A ;
+ rom.write_bytes(INDICATOR_CODE + 0x01DA, bytearray([0x5A])) # db $5A ;
+ rom.write_bytes(INDICATOR_CODE + 0x01DB, bytearray([0x0B])) # db $0B ;
+ rom.write_bytes(INDICATOR_CODE + 0x01DC, bytearray([0x34])) # ..icon_props db $34 ; coin
+ rom.write_bytes(INDICATOR_CODE + 0x01DD, bytearray([0x34])) # db $34 ; coin
+ rom.write_bytes(INDICATOR_CODE + 0x01DE, bytearray([0x34])) # db $34 ; coin
+ rom.write_bytes(INDICATOR_CODE + 0x01DF, bytearray([0x34])) # db $34 ; coin
+ rom.write_bytes(INDICATOR_CODE + 0x01E0, bytearray([0x3A])) # db $3A ; yoshi egg
+ rom.write_bytes(INDICATOR_CODE + 0x01E1, bytearray([0x3A])) # db $3A ; 1up mushroom
+ rom.write_bytes(INDICATOR_CODE + 0x01E2, bytearray([0x38])) # db $38 ; mushroom
+ rom.write_bytes(INDICATOR_CODE + 0x01E3, bytearray([0x3A])) # db $3A ; flower
+ rom.write_bytes(INDICATOR_CODE + 0x01E4, bytearray([0x34])) # db $34 ; feather
+ rom.write_bytes(INDICATOR_CODE + 0x01E5, bytearray([0x34])) # db $34 ; boss token
+ rom.write_bytes(INDICATOR_CODE + 0x01E6, bytearray([0x34])) # db $34 ;
+ rom.write_bytes(INDICATOR_CODE + 0x01E7, bytearray([0x3A])) # db $3A ;
+ rom.write_bytes(INDICATOR_CODE + 0x01E8, bytearray([0x38])) # db $38 ;
+ rom.write_bytes(INDICATOR_CODE + 0x01E9, bytearray([0x36])) # db $36 ;
+ rom.write_bytes(INDICATOR_CODE + 0x01EA, bytearray([0x36])) # db $36 ;
+ rom.write_bytes(INDICATOR_CODE + 0x01EB, bytearray([0x1A])) # ..plus_tile db $1A ; 1 coin
+ rom.write_bytes(INDICATOR_CODE + 0x01EC, bytearray([0x1A])) # db $1A ; 3 coins
+ rom.write_bytes(INDICATOR_CODE + 0x01ED, bytearray([0x1A])) # db $1A ; 5 coins
+ rom.write_bytes(INDICATOR_CODE + 0x01EE, bytearray([0x1A])) # db $1A ; 10 coins
+ rom.write_bytes(INDICATOR_CODE + 0x01EF, bytearray([0x1A])) # db $1A ; yoshi egg
+ rom.write_bytes(INDICATOR_CODE + 0x01F0, bytearray([0x1A])) # db $1A ; 1up mushroom
+ rom.write_bytes(INDICATOR_CODE + 0x01F1, bytearray([0x1A])) # db $1A ; mushroom
+ rom.write_bytes(INDICATOR_CODE + 0x01F2, bytearray([0x1A])) # db $1A ; flower
+ rom.write_bytes(INDICATOR_CODE + 0x01F3, bytearray([0x1A])) # db $1A ; feather
+ rom.write_bytes(INDICATOR_CODE + 0x01F4, bytearray([0x1A])) # db $1A ; boss token
+ rom.write_bytes(INDICATOR_CODE + 0x01F5, bytearray([0x1A])) # db $1A ;
+ rom.write_bytes(INDICATOR_CODE + 0x01F6, bytearray([0x1A])) # db $1A ;
+ rom.write_bytes(INDICATOR_CODE + 0x01F7, bytearray([0x1A])) # db $1A ;
+ rom.write_bytes(INDICATOR_CODE + 0x01F8, bytearray([0x1A])) # db $1A ;
+ rom.write_bytes(INDICATOR_CODE + 0x01F9, bytearray([0x1A])) # db $1A ;
+ rom.write_bytes(INDICATOR_CODE + 0x01FA, bytearray([0x32])) # ..plus_props db $32 ; 1 coin
+ rom.write_bytes(INDICATOR_CODE + 0x01FB, bytearray([0x32])) # db $32 ; 5 coins
+ rom.write_bytes(INDICATOR_CODE + 0x01FC, bytearray([0x32])) # db $32 ; 10 coins
+ rom.write_bytes(INDICATOR_CODE + 0x01FD, bytearray([0x32])) # db $32 ; 50 coins
+ rom.write_bytes(INDICATOR_CODE + 0x01FE, bytearray([0x32])) # db $32 ; yoshi egg
+ rom.write_bytes(INDICATOR_CODE + 0x01FF, bytearray([0x32])) # db $32 ; 1up mushroom
+ rom.write_bytes(INDICATOR_CODE + 0x0200, bytearray([0x32])) # db $32 ; mushroom
+ rom.write_bytes(INDICATOR_CODE + 0x0201, bytearray([0x32])) # db $32 ; flower
+ rom.write_bytes(INDICATOR_CODE + 0x0202, bytearray([0x32])) # db $32 ; feather
+ rom.write_bytes(INDICATOR_CODE + 0x0203, bytearray([0x32])) # db $32 ; boss token
+ rom.write_bytes(INDICATOR_CODE + 0x0204, bytearray([0x32])) # db $32 ;
+ rom.write_bytes(INDICATOR_CODE + 0x0205, bytearray([0x32])) # db $32 ;
+ rom.write_bytes(INDICATOR_CODE + 0x0206, bytearray([0x32])) # db $32 ;
+ rom.write_bytes(INDICATOR_CODE + 0x0207, bytearray([0x32])) # db $32 ;
+ rom.write_bytes(INDICATOR_CODE + 0x0208, bytearray([0x32])) # db $32 ;
+ rom.write_bytes(INDICATOR_CODE + 0x0209, bytearray([0x4B, 0x69])) # ..num_tile db $4B,$69 ; 1 coin
+ rom.write_bytes(INDICATOR_CODE + 0x020B, bytearray([0x5B, 0x69])) # db $5B,$69 ; 5 coins
+ rom.write_bytes(INDICATOR_CODE + 0x020D, bytearray([0x4B, 0x4A])) # db $4B,$4A ; 10 coins
+ rom.write_bytes(INDICATOR_CODE + 0x020F, bytearray([0x5B, 0x4A])) # db $4B,$5B ; 50 coins
+ rom.write_bytes(INDICATOR_CODE + 0x0211, bytearray([0x4B, 0x69])) # db $4B,$69 ; yoshi egg
+ rom.write_bytes(INDICATOR_CODE + 0x0213, bytearray([0x4B, 0x69])) # db $4B,$69 ; 1up mushroom
+ rom.write_bytes(INDICATOR_CODE + 0x0215, bytearray([0x4B, 0x69])) # db $4B,$69 ; mushroom
+ rom.write_bytes(INDICATOR_CODE + 0x0217, bytearray([0x4B, 0x69])) # db $4B,$69 ; flower
+ rom.write_bytes(INDICATOR_CODE + 0x0219, bytearray([0x4B, 0x69])) # db $4B,$69 ; feather
+ rom.write_bytes(INDICATOR_CODE + 0x021B, bytearray([0x4B, 0x69])) # db $4B,$69 ; boss token
+ rom.write_bytes(INDICATOR_CODE + 0x021D, bytearray([0x69, 0x69])) # db $69,$69 ;
+ rom.write_bytes(INDICATOR_CODE + 0x021F, bytearray([0x69, 0x69])) # db $69,$69 ;
+ rom.write_bytes(INDICATOR_CODE + 0x0221, bytearray([0x69, 0x69])) # db $69,$69 ;
+ rom.write_bytes(INDICATOR_CODE + 0x0223, bytearray([0x69, 0x69])) # db $69,$69 ;
+ rom.write_bytes(INDICATOR_CODE + 0x0225, bytearray([0x69, 0x69])) # db $69,$69 ;
+ rom.write_bytes(INDICATOR_CODE + 0x0227, bytearray([0x34, 0x34])) # ..num_props db $34,$34 ; 1 coin
+ rom.write_bytes(INDICATOR_CODE + 0x0229, bytearray([0x34, 0x34])) # db $34,$34 ; 5 coins
+ rom.write_bytes(INDICATOR_CODE + 0x022B, bytearray([0x34, 0x34])) # db $34,$34 ; 10 coins
+ rom.write_bytes(INDICATOR_CODE + 0x022D, bytearray([0x34, 0x34])) # db $34,$34 ; 50 coins
+ rom.write_bytes(INDICATOR_CODE + 0x022F, bytearray([0x34, 0x34])) # db $34,$34 ; yoshi egg
+ rom.write_bytes(INDICATOR_CODE + 0x0231, bytearray([0x34, 0x34])) # db $34,$34 ; 1up mushroom
+ rom.write_bytes(INDICATOR_CODE + 0x0233, bytearray([0x34, 0x34])) # db $34,$34 ; mushroom
+ rom.write_bytes(INDICATOR_CODE + 0x0235, bytearray([0x34, 0x34])) # db $34,$34 ; flower
+ rom.write_bytes(INDICATOR_CODE + 0x0237, bytearray([0x34, 0x34])) # db $34,$34 ; feather
+ rom.write_bytes(INDICATOR_CODE + 0x0239, bytearray([0x34, 0x34])) # db $34,$34 ; boss token
+ rom.write_bytes(INDICATOR_CODE + 0x023B, bytearray([0x34, 0x34])) # db $34,$34 ;
+ rom.write_bytes(INDICATOR_CODE + 0x023D, bytearray([0x34, 0x34])) # db $34,$34 ;
+ rom.write_bytes(INDICATOR_CODE + 0x023F, bytearray([0x34, 0x34])) # db $34,$34 ;
+ rom.write_bytes(INDICATOR_CODE + 0x0241, bytearray([0x34, 0x34])) # db $34,$34 ;
+ rom.write_bytes(INDICATOR_CODE + 0x0243, bytearray([0x34, 0x34])) # db $34,$34 ;
+ rom.write_bytes(INDICATOR_CODE + 0x0245, bytearray([0x50, 0x58, 0x60, 0x68, 0x70, 0x78]))# ..oam_2 db $50,$58,$60,$68,$70,$78
+ rom.write_bytes(INDICATOR_CODE + 0x024B, bytearray([0x69, 0xC2])) # .reward_ptrs dw .one_coin
+ rom.write_bytes(INDICATOR_CODE + 0x024D, bytearray([0x6D, 0xC2])) # dw .five_coins
+ rom.write_bytes(INDICATOR_CODE + 0x024F, bytearray([0x71, 0xC2])) # dw .ten_coins
+ rom.write_bytes(INDICATOR_CODE + 0x0251, bytearray([0x75, 0xC2])) # dw .fifty_coins
+ rom.write_bytes(INDICATOR_CODE + 0x0253, bytearray([0x8A, 0xC2])) # dw .yoshi_egg
+ rom.write_bytes(INDICATOR_CODE + 0x0255, bytearray([0xA7, 0xC2])) # dw .green_mushroom
+ rom.write_bytes(INDICATOR_CODE + 0x0257, bytearray([0xAD, 0xC2])) # dw .mushroom
+ rom.write_bytes(INDICATOR_CODE + 0x0259, bytearray([0xAF, 0xC2])) # dw .flower
+ rom.write_bytes(INDICATOR_CODE + 0x025B, bytearray([0xB1, 0xC2])) # dw .shared_item
+ rom.write_bytes(INDICATOR_CODE + 0x025D, bytearray([0x9C, 0xC2])) # dw .boss_token
+ rom.write_bytes(INDICATOR_CODE + 0x025F, bytearray([0xCB, 0xC0])) # dw .handle_movement
+ rom.write_bytes(INDICATOR_CODE + 0x0261, bytearray([0xCB, 0xC0])) # dw .handle_movement
+ rom.write_bytes(INDICATOR_CODE + 0x0263, bytearray([0xCB, 0xC0])) # dw .handle_movement
+ rom.write_bytes(INDICATOR_CODE + 0x0265, bytearray([0xCB, 0xC0])) # dw .handle_movement
+ rom.write_bytes(INDICATOR_CODE + 0x0267, bytearray([0xCB, 0xC0])) # dw .handle_movement
+ rom.write_bytes(INDICATOR_CODE + 0x0269, bytearray([0xA9, 0x01])) # .one_coin lda #$01
+ rom.write_bytes(INDICATOR_CODE + 0x026B, bytearray([0x80, 0x0A])) # bra .shared_coins
+ rom.write_bytes(INDICATOR_CODE + 0x026D, bytearray([0xA9, 0x05])) # .five_coins lda #$05
+ rom.write_bytes(INDICATOR_CODE + 0x026F, bytearray([0x80, 0x06])) # bra .shared_coins
+ rom.write_bytes(INDICATOR_CODE + 0x0271, bytearray([0xA9, 0x0A])) # .ten_coins lda #$0A
+ rom.write_bytes(INDICATOR_CODE + 0x0273, bytearray([0x80, 0x02])) # bra .shared_coins
+ rom.write_bytes(INDICATOR_CODE + 0x0275, bytearray([0xA9, 0x32])) # .fifty_coins lda #$32
+ rom.write_bytes(INDICATOR_CODE + 0x0277, bytearray([0x18])) # .shared_coins clc
+ rom.write_bytes(INDICATOR_CODE + 0x0278, bytearray([0x6D, 0xCC, 0x13])) # adc $13CC
+ rom.write_bytes(INDICATOR_CODE + 0x027B, bytearray([0x90, 0x02])) # bcc +
+ rom.write_bytes(INDICATOR_CODE + 0x027D, bytearray([0xA9, 0xFF])) # lda #$FF
+ rom.write_bytes(INDICATOR_CODE + 0x027F, bytearray([0x8D, 0xCC, 0x13])) # + sta $13CC
+ rom.write_bytes(INDICATOR_CODE + 0x0282, bytearray([0xA9, 0x01])) # lda #$01
+ rom.write_bytes(INDICATOR_CODE + 0x0284, bytearray([0x8D, 0xFC, 0x1D])) # sta $1DFC
+ rom.write_bytes(INDICATOR_CODE + 0x0287, bytearray([0x4C, 0xCB, 0xC0])) # jmp .handle_movement
+ rom.write_bytes(INDICATOR_CODE + 0x028A, bytearray([0xAD, 0x24, 0x1F])) # .yoshi_egg lda $1F24
+ rom.write_bytes(INDICATOR_CODE + 0x028D, bytearray([0xC9, 0xFF])) # cmp #$FF
+ rom.write_bytes(INDICATOR_CODE + 0x028F, bytearray([0xF0, 0x03])) # beq ..nope
+ rom.write_bytes(INDICATOR_CODE + 0x0291, bytearray([0xEE, 0x24, 0x1F])) # inc $1F24
+ rom.write_bytes(INDICATOR_CODE + 0x0294, bytearray([0xA9, 0x1F])) # ..nope lda #$1F
+ rom.write_bytes(INDICATOR_CODE + 0x0296, bytearray([0x8D, 0xFC, 0x1D])) # sta $1DFC
+ rom.write_bytes(INDICATOR_CODE + 0x0299, bytearray([0x4C, 0xCB, 0xC0])) # jmp .handle_movement
+ rom.write_bytes(INDICATOR_CODE + 0x029C, bytearray([0xEE, 0x26, 0x1F])) # .boss_token inc $1F26
+ rom.write_bytes(INDICATOR_CODE + 0x029F, bytearray([0xA9, 0x09])) # lda #$09
+ rom.write_bytes(INDICATOR_CODE + 0x02A1, bytearray([0x8D, 0xFC, 0x1D])) # sta $1DFC
+ rom.write_bytes(INDICATOR_CODE + 0x02A4, bytearray([0x4C, 0xCB, 0xC0])) # jmp .handle_movement
+ rom.write_bytes(INDICATOR_CODE + 0x02A7, bytearray([0xEE, 0xE4, 0x18])) # .green_mushroom inc $18E4
+ rom.write_bytes(INDICATOR_CODE + 0x02AA, bytearray([0x4C, 0xCB, 0xC0])) # jmp .handle_movement
+ rom.write_bytes(INDICATOR_CODE + 0x02AD, bytearray([0x80, 0x02])) # .mushroom bra .shared_item
+ rom.write_bytes(INDICATOR_CODE + 0x02AF, bytearray([0x80, 0x00])) # .flower bra .shared_item
+ rom.write_bytes(INDICATOR_CODE + 0x02B1, bytearray([0xA9, 0x0B])) # .shared_item lda #$0B
+ rom.write_bytes(INDICATOR_CODE + 0x02B3, bytearray([0x8D, 0xFC, 0x1D])) # sta $1DFC
+ rom.write_bytes(INDICATOR_CODE + 0x02B6, bytearray([0x4C, 0xCB, 0xC0])) # jmp .handle_movement
+
+def handle_traps(rom):
+ TRAPS_CODE = 0x86C00
+ rom.write_bytes(0x022D8, bytearray([0x22, 0x00, 0xEC, 0x10])) # org $00A2D8 : jsl score_sprites
+ rom.write_bytes(TRAPS_CODE + 0x0000, bytearray([0xAD, 0x00, 0x01])) # handle_traps: lda $0100
+ rom.write_bytes(TRAPS_CODE + 0x0003, bytearray([0xC9, 0x14])) # cmp #$14
+ rom.write_bytes(TRAPS_CODE + 0x0005, bytearray([0xD0, 0x04])) # bne .invalid
+ rom.write_bytes(TRAPS_CODE + 0x0007, bytearray([0xA5, 0x71])) # lda $71
+ rom.write_bytes(TRAPS_CODE + 0x0009, bytearray([0xF0, 0x09])) # beq .valid
+ rom.write_bytes(TRAPS_CODE + 0x000B, bytearray([0xA9, 0xFF])) # .invalid lda #$FF
+ rom.write_bytes(TRAPS_CODE + 0x000D, bytearray([0x8D, 0x3C, 0x0F])) # sta !thwimp_index
+ rom.write_bytes(TRAPS_CODE + 0x0010, bytearray([0x5C, 0xBD, 0xE2, 0x00])) # jml $00E2BD
+ rom.write_bytes(TRAPS_CODE + 0x0014, bytearray([0xAD, 0xB4, 0x18])) # .valid lda !reverse_controls_trap
+ rom.write_bytes(TRAPS_CODE + 0x0017, bytearray([0xF0, 0x03])) # beq .no_reverse_controls
+ rom.write_bytes(TRAPS_CODE + 0x0019, bytearray([0x20, 0x2B, 0xEC])) # jsr reverse_controls_trap
+ rom.write_bytes(TRAPS_CODE + 0x001C, bytearray([0xAD, 0xB7, 0x18])) # .no_reverse_controls lda !thwimp_trap
+ rom.write_bytes(TRAPS_CODE + 0x001F, bytearray([0xF0, 0x03])) # beq .no_thwimp
+ rom.write_bytes(TRAPS_CODE + 0x0021, bytearray([0x20, 0x86, 0xEC])) # jsr spawn_thwimp
+ rom.write_bytes(TRAPS_CODE + 0x0024, bytearray([0x20, 0xCB, 0xEC])) # .no_thwimp jsr handle_thwimp
+ rom.write_bytes(TRAPS_CODE + 0x0027, bytearray([0x5C, 0xBD, 0xE2, 0x00])) # jml $00E2BD
+ rom.write_bytes(TRAPS_CODE + 0x002B, bytearray([0xA5, 0x15])) # reverse_controls_trap: lda $15
+ rom.write_bytes(TRAPS_CODE + 0x002D, bytearray([0x89, 0x03])) # bit #$03
+ rom.write_bytes(TRAPS_CODE + 0x002F, bytearray([0xF0, 0x04])) # beq ..no_swap_hold
+ rom.write_bytes(TRAPS_CODE + 0x0031, bytearray([0x49, 0x03])) # eor #$03
+ rom.write_bytes(TRAPS_CODE + 0x0033, bytearray([0x85, 0x15])) # sta $15
+ rom.write_bytes(TRAPS_CODE + 0x0035, bytearray([0xA5, 0x16])) # ..no_swap_hold lda $16
+ rom.write_bytes(TRAPS_CODE + 0x0037, bytearray([0x89, 0x03])) # bit #$03
+ rom.write_bytes(TRAPS_CODE + 0x0039, bytearray([0xF0, 0x04])) # beq ..no_swap_press
+ rom.write_bytes(TRAPS_CODE + 0x003B, bytearray([0x49, 0x03])) # eor #$03
+ rom.write_bytes(TRAPS_CODE + 0x003D, bytearray([0x85, 0x16])) # sta $16
+ rom.write_bytes(TRAPS_CODE + 0x003F, bytearray([0xA5, 0x15])) # .swap_up_and_down lda $15
+ rom.write_bytes(TRAPS_CODE + 0x0041, bytearray([0x89, 0x0C])) # bit #$0C
+ rom.write_bytes(TRAPS_CODE + 0x0043, bytearray([0xF0, 0x04])) # beq .no_swap_hold
+ rom.write_bytes(TRAPS_CODE + 0x0045, bytearray([0x49, 0x0C])) # eor #$0C
+ rom.write_bytes(TRAPS_CODE + 0x0047, bytearray([0x85, 0x15])) # sta $15
+ rom.write_bytes(TRAPS_CODE + 0x0049, bytearray([0xA5, 0x16])) # .no_swap_hold lda $16
+ rom.write_bytes(TRAPS_CODE + 0x004B, bytearray([0x89, 0x0C])) # bit #$0C
+ rom.write_bytes(TRAPS_CODE + 0x004D, bytearray([0xF0, 0x04])) # beq ..no_swap_press
+ rom.write_bytes(TRAPS_CODE + 0x004F, bytearray([0x49, 0x0C])) # eor #$0C
+ rom.write_bytes(TRAPS_CODE + 0x0051, bytearray([0x85, 0x16])) # sta $16
+ rom.write_bytes(TRAPS_CODE + 0x0053, bytearray([0xA5, 0x16])) # .swap_a_and_b lda $16
+ rom.write_bytes(TRAPS_CODE + 0x0055, bytearray([0x10, 0x0C])) # bpl ..no_swap_b
+ rom.write_bytes(TRAPS_CODE + 0x0057, bytearray([0x49, 0x80])) # eor #$80
+ rom.write_bytes(TRAPS_CODE + 0x0059, bytearray([0x85, 0x16])) # sta $16
+ rom.write_bytes(TRAPS_CODE + 0x005B, bytearray([0xA5, 0x18])) # lda $18
+ rom.write_bytes(TRAPS_CODE + 0x005D, bytearray([0x49, 0x80])) # eor #$80
+ rom.write_bytes(TRAPS_CODE + 0x005F, bytearray([0x85, 0x18])) # sta $18
+ rom.write_bytes(TRAPS_CODE + 0x0061, bytearray([0x80, 0x0E])) # bra .swap_l_and_r
+ rom.write_bytes(TRAPS_CODE + 0x0063, bytearray([0xA5, 0x18])) # ..no_swap_b lda $18
+ rom.write_bytes(TRAPS_CODE + 0x0065, bytearray([0x10, 0x0A])) # bpl .swap_l_and_r
+ rom.write_bytes(TRAPS_CODE + 0x0067, bytearray([0x49, 0x80])) # eor #$80
+ rom.write_bytes(TRAPS_CODE + 0x0069, bytearray([0x85, 0x18])) # sta $18
+ rom.write_bytes(TRAPS_CODE + 0x006B, bytearray([0xA5, 0x16])) # lda $16
+ rom.write_bytes(TRAPS_CODE + 0x006D, bytearray([0x49, 0x80])) # eor #$80
+ rom.write_bytes(TRAPS_CODE + 0x006F, bytearray([0x85, 0x16])) # sta $16
+ rom.write_bytes(TRAPS_CODE + 0x0071, bytearray([0xA5, 0x17])) # .swap_l_and_r lda $17
+ rom.write_bytes(TRAPS_CODE + 0x0073, bytearray([0x89, 0x30])) # bit #$30
+ rom.write_bytes(TRAPS_CODE + 0x0075, bytearray([0xF0, 0x04])) # beq ..no_swap_hold
+ rom.write_bytes(TRAPS_CODE + 0x0077, bytearray([0x49, 0x30])) # eor #$30
+ rom.write_bytes(TRAPS_CODE + 0x0079, bytearray([0x85, 0x17])) # sta $17
+ rom.write_bytes(TRAPS_CODE + 0x007B, bytearray([0xA5, 0x18])) # ..no_swap_hold lda $18
+ rom.write_bytes(TRAPS_CODE + 0x007D, bytearray([0x89, 0x30])) # bit #$30
+ rom.write_bytes(TRAPS_CODE + 0x007F, bytearray([0xF0, 0x04])) # beq ..no_swap_press
+ rom.write_bytes(TRAPS_CODE + 0x0081, bytearray([0x49, 0x30])) # eor #$30
+ rom.write_bytes(TRAPS_CODE + 0x0083, bytearray([0x85, 0x18])) # sta $18
+ rom.write_bytes(TRAPS_CODE + 0x0085, bytearray([0x60])) # ..no_swap_press rts
+ rom.write_bytes(TRAPS_CODE + 0x0086, bytearray([0xAE, 0x3C, 0x0F])) # spawn_thwimp: ldx !thwimp_index
+ rom.write_bytes(TRAPS_CODE + 0x0089, bytearray([0x10, 0x06])) # bpl .return
+ rom.write_bytes(TRAPS_CODE + 0x008B, bytearray([0x22, 0xE4, 0xA9, 0x02])) # jsl $02A9E4
+ rom.write_bytes(TRAPS_CODE + 0x008F, bytearray([0x10, 0x01])) # bpl .found
+ rom.write_bytes(TRAPS_CODE + 0x0091, bytearray([0x60])) # .return rts
+ rom.write_bytes(TRAPS_CODE + 0x0092, bytearray([0xBB])) # .found tyx
+ rom.write_bytes(TRAPS_CODE + 0x0093, bytearray([0x9C, 0xB7, 0x18])) # stz !thwimp_trap
+ rom.write_bytes(TRAPS_CODE + 0x0096, bytearray([0xA9, 0x10])) # lda #$10
+ rom.write_bytes(TRAPS_CODE + 0x0098, bytearray([0x8D, 0xF9, 0x1D])) # sta $1DF9
+ rom.write_bytes(TRAPS_CODE + 0x009B, bytearray([0xA9, 0x27])) # lda #$27
+ rom.write_bytes(TRAPS_CODE + 0x009D, bytearray([0x95, 0x9E])) # sta $9E,x
+ rom.write_bytes(TRAPS_CODE + 0x009F, bytearray([0xA9, 0x08])) # lda #$08
+ rom.write_bytes(TRAPS_CODE + 0x00A1, bytearray([0x9D, 0xC8, 0x14])) # sta $14C8,x
+ rom.write_bytes(TRAPS_CODE + 0x00A4, bytearray([0x22, 0xD2, 0xF7, 0x07])) # jsl $07F7D2
+ rom.write_bytes(TRAPS_CODE + 0x00A8, bytearray([0xA5, 0x94])) # lda $94
+ rom.write_bytes(TRAPS_CODE + 0x00AA, bytearray([0x95, 0xE4])) # sta $E4,x
+ rom.write_bytes(TRAPS_CODE + 0x00AC, bytearray([0xA5, 0x95])) # lda $95
+ rom.write_bytes(TRAPS_CODE + 0x00AE, bytearray([0x9D, 0xE0, 0x14])) # sta $14E0,x
+ rom.write_bytes(TRAPS_CODE + 0x00B1, bytearray([0xA5, 0x1C])) # lda $1C
+ rom.write_bytes(TRAPS_CODE + 0x00B3, bytearray([0x38])) # sec
+ rom.write_bytes(TRAPS_CODE + 0x00B4, bytearray([0xE9, 0x0F])) # sbc #$0F
+ rom.write_bytes(TRAPS_CODE + 0x00B6, bytearray([0x95, 0xD8])) # sta $D8,x
+ rom.write_bytes(TRAPS_CODE + 0x00B8, bytearray([0xA5, 0x1D])) # lda $1D
+ rom.write_bytes(TRAPS_CODE + 0x00BA, bytearray([0xE9, 0x00])) # sbc #$00
+ rom.write_bytes(TRAPS_CODE + 0x00BC, bytearray([0x9D, 0xD4, 0x14])) # sta $14D4,x
+ rom.write_bytes(TRAPS_CODE + 0x00BF, bytearray([0xBD, 0x86, 0x16])) # lda $1686,x
+ rom.write_bytes(TRAPS_CODE + 0x00C2, bytearray([0x09, 0x80])) # ora #$80
+ rom.write_bytes(TRAPS_CODE + 0x00C4, bytearray([0x9D, 0x86, 0x16])) # sta $1686,x
+ rom.write_bytes(TRAPS_CODE + 0x00C7, bytearray([0x8E, 0x3C, 0x0F])) # stx !thwimp_index
+ rom.write_bytes(TRAPS_CODE + 0x00CA, bytearray([0x60])) # rts
+ rom.write_bytes(TRAPS_CODE + 0x00CB, bytearray([0xAE, 0x3C, 0x0F])) # handle_thwimp: ldx !thwimp_index
+ rom.write_bytes(TRAPS_CODE + 0x00CE, bytearray([0x30, 0x1C])) # bmi .return
+ rom.write_bytes(TRAPS_CODE + 0x00D0, bytearray([0xBD, 0xD4, 0x14])) # lda $14D4,x
+ rom.write_bytes(TRAPS_CODE + 0x00D3, bytearray([0xEB])) # xba
+ rom.write_bytes(TRAPS_CODE + 0x00D4, bytearray([0xB5, 0xD8])) # lda $D8,x
+ rom.write_bytes(TRAPS_CODE + 0x00D6, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(TRAPS_CODE + 0x00D8, bytearray([0x38])) # sec
+ rom.write_bytes(TRAPS_CODE + 0x00D9, bytearray([0xE5, 0x96])) # sbc $96
+ rom.write_bytes(TRAPS_CODE + 0x00DB, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(TRAPS_CODE + 0x00DD, bytearray([0x30, 0x0D])) # bmi .return
+ rom.write_bytes(TRAPS_CODE + 0x00DF, bytearray([0xA9, 0xFF])) # lda #$FF
+ rom.write_bytes(TRAPS_CODE + 0x00E1, bytearray([0x8D, 0x3C, 0x0F])) # sta !thwimp_index
+ rom.write_bytes(TRAPS_CODE + 0x00E4, bytearray([0xBD, 0x86, 0x16])) # lda $1686,x
+ rom.write_bytes(TRAPS_CODE + 0x00E7, bytearray([0x29, 0x7F])) # and #$7F
+ rom.write_bytes(TRAPS_CODE + 0x00E9, bytearray([0x9D, 0x86, 0x16])) # sta $1686,x
+ rom.write_bytes(TRAPS_CODE + 0x00EC, bytearray([0x60])) # .return rts
+
+
+
+def read_graphics_file(filename):
+ return pkgutil.get_data(__name__, f"data/graphics/{filename}")
+
+def handle_uncompressed_player_gfx(rom):
+ # Decompresses and moves into a expanded region the player, yoshi and animated graphics
+ # This should make swapping the graphics a lot easier.
+ # Maybe I should look into making a 32x32 version at some point...
+ # It also moves some 8x8 tiles in GFX00, thus making some free space for indicators and other stuff
+ # in VRAM during gameplay, will come super handy later.
+ #
+ # FOR FUTURE REFERENCE
+ # Player graphics are now located at 0xE0000
+ # Player auxiliary tiles are now located at 0xE6000
+ # Yoshi graphics are now located at 0xE8800
+ SMW_COMPRESSED_PLAYER_GFX = 0x40000
+ SMW_COMPRESSED_ANIMATED_GFX = 0x43FC0
+ SMW_COMPRESSED_GFX_00 = 0x459F9
+ SMW_COMPRESSED_GFX_10 = 0x4EF1E
+ SMW_COMPRESSED_GFX_28 = 0x5C06C
+ compressed_player_gfx = rom.read_bytes(SMW_COMPRESSED_PLAYER_GFX, 0x3FC0)
+ compressed_animated_gfx = rom.read_bytes(SMW_COMPRESSED_ANIMATED_GFX, 0x1A39)
+ compressed_gfx_00 = rom.read_bytes(SMW_COMPRESSED_GFX_00, 0x0838)
+ compressed_gfx_10 = rom.read_bytes(SMW_COMPRESSED_GFX_10, 0x0891)
+ compressed_gfx_28 = rom.read_bytes(SMW_COMPRESSED_GFX_28, 0x0637)
+ decompressed_player_gfx = decompress_gfx(compressed_player_gfx)
+ decompressed_animated_gfx = convert_3bpp(decompress_gfx(compressed_animated_gfx))
+ decompressed_gfx_00 = convert_3bpp(decompress_gfx(compressed_gfx_00))
+ decompressed_gfx_10 = convert_3bpp(decompress_gfx(compressed_gfx_10))
+ decompressed_gfx_28 = decompress_gfx(compressed_gfx_28)
+
+ # Copy berry tiles
+ order = [0x26C, 0x26D, 0x26E, 0x26F,
+ 0x27C, 0x27D, 0x27E, 0x27F,
+ 0x2E0, 0x2E1, 0x2E2, 0x2E3,
+ 0x2E4, 0x2E5, 0x2E6, 0x2E7]
+ decompressed_animated_gfx += copy_gfx_tiles(decompressed_player_gfx, order, [5, 32])
+
+ # Copy Mario's auxiliary tiles
+ order = [0x80, 0x91, 0x81, 0x90, 0x82, 0x83]
+ decompressed_gfx_00 += copy_gfx_tiles(decompressed_player_gfx, order, [5, 32])
+ order = [0x69, 0x69, 0x0C, 0x69, 0x1A, 0x1B, 0x0D, 0x69, 0x22, 0x23, 0x32, 0x33, 0x0A, 0x0B, 0x20, 0x21,
+ 0x30, 0x31, 0x7E, 0x69, 0x80, 0x4A, 0x81, 0x5B, 0x82, 0x4B, 0x83, 0x5A, 0x84, 0x69, 0x85, 0x85]
+ player_small_tiles = copy_gfx_tiles(decompressed_gfx_00, order, [5, 32])
+
+ # Copy OW mario tiles
+ order = [0x06, 0x07, 0x16, 0x17,
+ 0x08, 0x09, 0x18, 0x19,
+ 0x0A, 0x0B, 0x1A, 0x1B,
+ 0x0C, 0x0D, 0x1C, 0x1D,
+ 0x0E, 0x0F, 0x1E, 0x1F,
+ 0x20, 0x21, 0x30, 0x31,
+ 0x24, 0x25, 0x34, 0x35,
+ 0x46, 0x47, 0x56, 0x57,
+ 0x64, 0x65, 0x74, 0x75,
+ 0x66, 0x67, 0x76, 0x77,
+ 0x2E, 0x2F, 0x3E, 0x3F,
+ 0x40, 0x41, 0x50, 0x51,
+ 0x42, 0x43, 0x52, 0x53]
+ player_map_tiles = copy_gfx_tiles(decompressed_gfx_10, order, [5, 32])
+
+ # Copy HUD mario tiles
+ order = [0x30, 0x31, 0x32, 0x33, 0x34]
+ player_name_tiles = copy_gfx_tiles(decompressed_gfx_28, order, [4, 16])
+
+ rom.write_bytes(0xE0000, decompressed_player_gfx)
+ rom.write_bytes(0xE8000, decompressed_animated_gfx)
+ rom.write_bytes(0xE6000, player_small_tiles)
+ rom.write_bytes(0xE6400, player_map_tiles)
+ rom.write_bytes(0xE6C00, player_name_tiles)
+
+ # Skip Player & Animated tile decompression
+ rom.write_bytes(0x03888, bytearray([0x60])) # RTS
+
+ # Edit Mario DMA routine
+ MARIO_GFX_DMA_ADDR = 0x02300
+ rom.write_bytes(MARIO_GFX_DMA_ADDR + 0x0000, bytearray([0xA2, 0x04])) # LDX #$04
+ rom.write_bytes(MARIO_GFX_DMA_ADDR + 0x0002, bytearray([0x22, 0x00, 0xF0, 0x10])) # JSL $10F000 ; upload_score_sprite_gfx
+ rom.write_bytes(MARIO_GFX_DMA_ADDR + 0x0006, bytearray([0x22, 0x00, 0xF8, 0x0F])) # JSL $0FF800 ; player_code
+ rom.write_bytes(MARIO_GFX_DMA_ADDR + 0x000A, bytearray([0x60])) # RTS
+
+ PLAYER_UPLOAD_ADDR = 0x7F800
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0000, bytearray([0xC2, 0x20])) # player_code: rep #$20
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0002, bytearray([0xAC, 0x84, 0x0D])) # ldy $0D84
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0005, bytearray([0xD0, 0x03])) # bne .upload_player_palette
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0007, bytearray([0x4C, 0xD2, 0xF8])) # jmp .skip_everything
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x000A, bytearray([0xA0, 0x86])) # .upload_player_palette ldy #$86
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x000C, bytearray([0x8C, 0x21, 0x21])) # sty $2121
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x000F, bytearray([0xA9, 0x00, 0x22])) # lda #$2200
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0012, bytearray([0x8D, 0x20, 0x43])) # sta $4320
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0015, bytearray([0xA8])) # tay
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0016, bytearray([0xAD, 0x82, 0x0D])) # lda $0D82
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0019, bytearray([0x8D, 0x22, 0x43])) # sta $4322
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x001C, bytearray([0x8C, 0x24, 0x43])) # sty $4324
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x001F, bytearray([0xA9, 0x14, 0x00])) # lda #$0014
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0022, bytearray([0x8D, 0x25, 0x43])) # sta $4325
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0025, bytearray([0x8E, 0x0B, 0x42])) # stx $420B
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0028, bytearray([0xA0, 0x80])) # ldy #$80
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x002A, bytearray([0x8C, 0x15, 0x21])) # sty $2115
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x002D, bytearray([0xA9, 0x01, 0x18])) # lda #$1801
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0030, bytearray([0x8D, 0x20, 0x43])) # sta $4320
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0033, bytearray([0xA0, 0x1C])) # ldy.b #player_gfx>>16
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0035, bytearray([0x8C, 0x24, 0x43])) # sty $4324
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0038, bytearray([0xA9, 0x00, 0x60])) # .upload_player_top lda #$6000
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x003B, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x003E, bytearray([0xA8])) # tay
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x003F, bytearray([0xB9, 0x85, 0x0D])) # - lda $0D85,y
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0042, bytearray([0x8D, 0x22, 0x43])) # sta $4322
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0045, bytearray([0xA9, 0x40, 0x00])) # lda #$0040
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0048, bytearray([0x8D, 0x25, 0x43])) # sta $4325
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x004B, bytearray([0x8E, 0x0B, 0x42])) # stx $420B
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x004E, bytearray([0xC8])) # iny
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x004F, bytearray([0xC8])) # iny
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0050, bytearray([0xC0, 0x06])) # cpy #$06
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0052, bytearray([0xD0, 0xEB])) # bne -
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0054, bytearray([0xA9, 0x00, 0x61])) # .upload_player_bottom lda #$6100
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0057, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x005A, bytearray([0xA8])) # tay
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x005B, bytearray([0xB9, 0x8F, 0x0D])) # - lda $0D8F,y
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x005E, bytearray([0x8D, 0x22, 0x43])) # sta $4322
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0061, bytearray([0xA9, 0x40, 0x00])) # lda #$0040
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0064, bytearray([0x8D, 0x25, 0x43])) # sta $4325
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0067, bytearray([0x8E, 0x0B, 0x42])) # stx $420B
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x006A, bytearray([0xC8])) # iny
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x006B, bytearray([0xC8])) # iny
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x006C, bytearray([0xC0, 0x06])) # cpy #$06
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x006E, bytearray([0xD0, 0xEB])) # bne -
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0070, bytearray([0xAC, 0x9B, 0x0D])) # .upload_player_extended ldy $0D9B
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0073, bytearray([0xC0, 0x02])) # cpy #$02
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0075, bytearray([0xF0, 0x5B])) # beq .skip_everything
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0077, bytearray([0xA9, 0xC0, 0x60])) # lda #$60C0
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x007A, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x007D, bytearray([0xAD, 0x99, 0x0D])) # lda $0D99
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0080, bytearray([0x8D, 0x22, 0x43])) # sta $4322
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0083, bytearray([0xA9, 0x40, 0x00])) # lda #$0040
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0086, bytearray([0x8D, 0x25, 0x43])) # sta $4325
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0089, bytearray([0x8E, 0x0B, 0x42])) # stx $420B
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x008C, bytearray([0xA0, 0x1D])) # .upload_misc_tiles ldy.b #animated_tiles>>16
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x008E, bytearray([0x8C, 0x24, 0x43])) # sty $4324
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0091, bytearray([0xA9, 0x60, 0x60])) # lda #$6060
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0094, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0097, bytearray([0xA0, 0x06])) # ldy #$06
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x0099, bytearray([0xCC, 0x84, 0x0D])) # cpy $0D84
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x009C, bytearray([0xB0, 0x34])) # bcs .skip_everything
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x009E, bytearray([0xB9, 0x85, 0x0D])) # - lda $0D85,y
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00A1, bytearray([0x8D, 0x22, 0x43])) # sta $4322
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00A4, bytearray([0xA9, 0x40, 0x00])) # lda #$0040
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00A7, bytearray([0x8D, 0x25, 0x43])) # sta $4325
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00AA, bytearray([0x8E, 0x0B, 0x42])) # stx $420B
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00AD, bytearray([0xC8])) # iny
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00AE, bytearray([0xC8])) # iny
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00AF, bytearray([0xCC, 0x84, 0x0D])) # cpy $0D84
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00B2, bytearray([0x90, 0xEA])) # bcc -
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00B4, bytearray([0xA9, 0x60, 0x61])) # lda #$6160
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00B7, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00BA, bytearray([0xA0, 0x06])) # ldy #$06
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00BC, bytearray([0xB9, 0x8F, 0x0D])) # - lda $0D8F,y
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00BF, bytearray([0x8D, 0x22, 0x43])) # sta $4322
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00C2, bytearray([0xA9, 0x40, 0x00])) # lda #$0040
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00C5, bytearray([0x8D, 0x25, 0x43])) # sta $4325
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00C8, bytearray([0x8E, 0x0B, 0x42])) # stx $420B
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00CB, bytearray([0xC8])) # iny
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00CC, bytearray([0xC8])) # iny
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00CD, bytearray([0xCC, 0x84, 0x0D])) # cpy $0D84
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00D0, bytearray([0x90, 0xEA])) # bcc -
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00D2, bytearray([0xE2, 0x20])) # .skip_everything sep #$20
+ rom.write_bytes(PLAYER_UPLOAD_ADDR + 0x00D4, bytearray([0x6B])) # rtl
+
+ # Obtain data for new 8x8 tile
+ CHAR_TILE_CODE_ADDR = 0x05FE2
+ rom.write_bytes(0x063B1, bytearray([0x20, 0xE2, 0xDF])) # jsr $DFE2
+ rom.write_bytes(CHAR_TILE_CODE_ADDR + 0x0000, bytearray([0xB9, 0x1A, 0xDF])) # lda $DF1A,y
+ rom.write_bytes(CHAR_TILE_CODE_ADDR + 0x0003, bytearray([0x10, 0x06])) # bpl $06
+ rom.write_bytes(CHAR_TILE_CODE_ADDR + 0x0005, bytearray([0x29, 0x7F])) # and #$7F
+ rom.write_bytes(CHAR_TILE_CODE_ADDR + 0x0007, bytearray([0x85, 0x0D])) # sta $0D
+ rom.write_bytes(CHAR_TILE_CODE_ADDR + 0x0009, bytearray([0xA9, 0x04])) # lda #$04
+ rom.write_bytes(CHAR_TILE_CODE_ADDR + 0x000B, bytearray([0x60])) # rts
+
+ rom.write_bytes(0x0640D, bytearray([0x20, 0xEE, 0xDF])) # jsr $DFEE
+ CAPE_TILE_CODE_ADDR = 0x05FEE
+ rom.write_bytes(CAPE_TILE_CODE_ADDR + 0x0000, bytearray([0xA5, 0x0D])) # lda $0D
+ rom.write_bytes(CAPE_TILE_CODE_ADDR + 0x0002, bytearray([0xE0, 0x2B])) # cpx #$2B
+ rom.write_bytes(CAPE_TILE_CODE_ADDR + 0x0004, bytearray([0x90, 0x07])) # bcc $07
+ rom.write_bytes(CAPE_TILE_CODE_ADDR + 0x0006, bytearray([0xE0, 0x40])) # cpx #$40
+ rom.write_bytes(CAPE_TILE_CODE_ADDR + 0x0008, bytearray([0xB0, 0x03])) # bcs $03
+ rom.write_bytes(CAPE_TILE_CODE_ADDR + 0x000A, bytearray([0xBD, 0xD7, 0xE1])) # lda $E1D7,x
+ rom.write_bytes(CAPE_TILE_CODE_ADDR + 0x000D, bytearray([0x60])) # rts
+
+ # Edit Mario's 8x8 tile data
+ MARIO_AUX_TILE_DATA_ADDR = 0x05F1A
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0000, bytearray([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0008, bytearray([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0010, bytearray([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0018, bytearray([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0020, bytearray([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0028, bytearray([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0030, bytearray([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0038, bytearray([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0040, bytearray([0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0048, bytearray([0x00,0x00,0x82,0x82,0x82,0x00,0x00,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0050, bytearray([0x00,0x00,0x84,0x00,0x00,0x00,0x00,0x86]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0058, bytearray([0x86,0x86,0x00,0x00,0x88,0x88,0x8A,0x8A]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0060, bytearray([0x8C,0x8C,0x00,0x00,0x90,0x00,0x00,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0068, bytearray([0x00,0x8E,0x00,0x00,0x00,0x00,0x92,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0070, bytearray([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0078, bytearray([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0080, bytearray([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x82]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0088, bytearray([0x82,0x82,0x00,0x00,0x00,0x00,0x00,0x84]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0090, bytearray([0x00,0x00,0x00,0x00,0x86,0x86,0x86,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x0098, bytearray([0x00,0x88,0x88,0x8A,0x8A,0x8C,0x8C,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x00A0, bytearray([0x00,0x90,0x00,0x00,0x00,0x00,0x8E,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x00A8, bytearray([0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x00B0, bytearray([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]))
+ rom.write_bytes(MARIO_AUX_TILE_DATA_ADDR + 0x00B8, bytearray([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]))
+
+ MARIO_AUX_TILE_OFFSETS_ADDR = 0x05FDA # ends at $00E00C
+ rom.write_bytes(MARIO_AUX_TILE_OFFSETS_ADDR + 0x0000, bytearray([0x00,0x02,0x80,0x80,0x00,0x02,0x0C,0x0D]))
+ rom.write_bytes(MARIO_AUX_TILE_OFFSETS_ADDR + 0x0022, bytearray([0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x02]))
+ rom.write_bytes(MARIO_AUX_TILE_OFFSETS_ADDR + 0x002A, bytearray([0x02,0x80,0x04,0x0C,0x0D,0xFF,0xFF,0xFF]))
+
+ MARIO_AUX_CAPE_TILE_DATA_ADDR = 0x061FF
+ rom.write_bytes(MARIO_AUX_CAPE_TILE_DATA_ADDR + 0x0000, bytearray([0x00,0x8C,0x14,0x14,0x2E]))
+ rom.write_bytes(MARIO_AUX_CAPE_TILE_DATA_ADDR + 0x0005, bytearray([0x00,0xCA,0x16,0x16,0x2E]))
+ rom.write_bytes(MARIO_AUX_CAPE_TILE_DATA_ADDR + 0x000A, bytearray([0x00,0x8E,0x18,0x18,0x2E]))
+ rom.write_bytes(MARIO_AUX_CAPE_TILE_DATA_ADDR + 0x000F, bytearray([0x00,0xEB,0x1A,0x1A,0x2E]))
+ rom.write_bytes(MARIO_AUX_CAPE_TILE_DATA_ADDR + 0x0014, bytearray([0x04,0xED,0x1C,0x1C]))
+
+ # Edit player data offsets
+ rom.write_bytes(0x07649, bytearray([0x69, 0x00, 0x80])) # adc #$8000
+ rom.write_bytes(0x07667, bytearray([0x69, 0x00, 0x80])) # adc #$8000
+ rom.write_bytes(0x0767C, bytearray([0x69, 0x00, 0x80])) # adc #$8000
+ rom.write_bytes(0x07691, bytearray([0x69, 0x00, 0xE0])) # adc #$E000
+
+ # Fix berries
+ FIX_BERRIES_ADDR = 0x7FFE0
+ rom.write_bytes(FIX_BERRIES_ADDR + 0x0000, bytearray([0xA0, 0x1D])) # fix_berries: ldy.b #animated_tiles>>16
+ rom.write_bytes(FIX_BERRIES_ADDR + 0x0002, bytearray([0x8C, 0x24, 0x43])) # sty $4324
+ rom.write_bytes(FIX_BERRIES_ADDR + 0x0005, bytearray([0xAD, 0x76, 0x0D])) # lda $0D76
+ rom.write_bytes(FIX_BERRIES_ADDR + 0x0008, bytearray([0x8D, 0x22, 0x43])) # sta $4322
+ rom.write_bytes(FIX_BERRIES_ADDR + 0x000B, bytearray([0x6B])) # rtl
+
+ # Fix animated graphics
+ rom.write_bytes(0x018D1, bytearray([0x1D])) # db $1D
+ rom.write_bytes(0x0239E, bytearray([0x1D])) # db $1D
+
+ rom.write_bytes(0x023F0, bytearray([0x22, 0xE0, 0xFF, 0x0F])) # jsl $0FFFE0
+ rom.write_bytes(0x023F4, bytearray([0xEA])) # nop
+ rom.write_bytes(0x023F5, bytearray([0xEA])) # nop
+
+ rom.write_bytes(0x0E1A8, bytearray([0x69, 0x00, 0x88])) # adc #$8800
+ rom.write_bytes(0x0EEB4, bytearray([0x69, 0x00, 0x88])) # adc #$8800
+ rom.write_bytes(0x0EEC9, bytearray([0x69, 0x00, 0x88])) # adc #$8800
+ rom.write_bytes(0x16A3E, bytearray([0x69, 0x00, 0x88])) # adc #$8800
+
+ ANIMATED_TILE_DATA_ADDR = 0x2B999
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0000, bytearray([0x00,0x98,0x00,0x9A,0x00,0x9C,0x00,0x9E]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0008, bytearray([0x80,0x98,0x80,0x9A,0x80,0x9C,0x80,0x9E]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0010, bytearray([0x00,0x99,0x00,0x99,0x00,0x99,0x00,0x99]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0018, bytearray([0x80,0xA0,0x80,0xA2,0x80,0xA4,0x80,0xA6]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0020, bytearray([0x00,0x99,0x00,0x9B,0x00,0x9D,0x00,0x9F]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0028, bytearray([0x00,0xB0,0x80,0xB0,0x00,0xB1,0x80,0xB1]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0030, bytearray([0x20,0xAF,0x20,0xAF,0x20,0xAF,0x20,0xAF]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0038, bytearray([0x20,0xAF,0x20,0xAF,0x20,0xAF,0x20,0xAF]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0040, bytearray([0x80,0x96,0x80,0x96,0x80,0x96,0x80,0x96]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0048, bytearray([0x00,0xA7,0x80,0xA7,0x00,0xA7,0x80,0xA7]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0050, bytearray([0x20,0xAF,0x20,0xAF,0x20,0xAF,0x20,0xAF]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0058, bytearray([0x00,0xAF,0x00,0xAF,0x00,0xAF,0x00,0xAF]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0060, bytearray([0x00,0x94,0x00,0x94,0x00,0x94,0x00,0x94]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0068, bytearray([0x80,0x99,0x80,0x9B,0x80,0x9D,0x80,0x9F]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0070, bytearray([0x00,0xA0,0x00,0xA2,0x00,0xA4,0x00,0xA6]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0078, bytearray([0x80,0x91,0x80,0x93,0x80,0x95,0x80,0x97]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0080, bytearray([0x00,0x98,0x00,0x98,0x00,0x98,0x00,0x98]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0088, bytearray([0x00,0x98,0x00,0x98,0x00,0x98,0x00,0x98]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0090, bytearray([0x00,0x98,0x00,0x98,0x00,0x98,0x00,0x98]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0098, bytearray([0x00,0xA0,0x00,0xA2,0x00,0xA4,0x00,0xA6]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x00A0, bytearray([0x80,0x91,0x80,0x93,0x80,0x95,0x80,0x97]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x00A8, bytearray([0x00,0x80,0x00,0x82,0x00,0x84,0x00,0x86]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x00B0, bytearray([0x00,0x86,0x00,0x84,0x00,0x82,0x00,0x80]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x00B8, bytearray([0x00,0xA1,0x00,0xA3,0x00,0xA5,0x00,0xA3]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x00C0, bytearray([0x00,0xA0,0x00,0xA2,0x00,0xA4,0x00,0xA6]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x00C8, bytearray([0x00,0xA8,0x00,0xAA,0x00,0xAC,0x00,0xAE]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x00D0, bytearray([0x80,0xA8,0x80,0xAA,0x80,0xAC,0x80,0xAE]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x00D8, bytearray([0x80,0xAE,0x80,0xAC,0x80,0xAA,0x80,0xA8]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x00E0, bytearray([0x00,0x98,0x00,0x98,0x00,0x98,0x00,0x98]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x00E8, bytearray([0x80,0xA1,0x80,0xA3,0x80,0xA5,0x80,0xA3]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x00F0, bytearray([0x80,0x80,0x80,0x82,0x80,0x84,0x80,0x86]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x00F8, bytearray([0x00,0x81,0x00,0x83,0x00,0x85,0x00,0x87]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0100, bytearray([0x80,0x81,0x80,0x83,0x80,0x85,0x80,0x87]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0108, bytearray([0x80,0x86,0x80,0x84,0x80,0x82,0x80,0x80]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0110, bytearray([0x00,0x98,0x00,0x98,0x00,0x98,0x00,0x98]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0118, bytearray([0x80,0xA9,0x80,0xAB,0x80,0xAD,0x80,0xAB]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0120, bytearray([0x00,0x91,0x00,0x93,0x00,0x95,0x00,0x97]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0128, bytearray([0x00,0x98,0x00,0x98,0x00,0x98,0x00,0x98]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0130, bytearray([0x00,0x98,0x00,0x98,0x00,0x98,0x00,0x98]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0138, bytearray([0x80,0xA1,0x80,0xA3,0x80,0xA5,0x80,0xA3]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0140, bytearray([0x00,0xA9,0x00,0xAB,0x00,0xAD,0x00,0xAB]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0148, bytearray([0x00,0x98,0x00,0x98,0x00,0x98,0x00,0x98]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0150, bytearray([0x00,0x98,0x00,0x98,0x00,0x98,0x00,0x98]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0158, bytearray([0x00,0x98,0x00,0x98,0x00,0x98,0x00,0x98]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0160, bytearray([0x80,0x94,0x80,0x94,0x80,0x94,0x80,0x94]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0168, bytearray([0x80,0x99,0x80,0x9B,0x80,0x9D,0x80,0x9F]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0170, bytearray([0x80,0x99,0x80,0x9B,0x80,0x9D,0x80,0x9F]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0178, bytearray([0x80,0x99,0x80,0x9B,0x80,0x9D,0x80,0x9F]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0180, bytearray([0x00,0x98,0x00,0x9A,0x00,0x9C,0x00,0x9E]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0188, bytearray([0x80,0xAF,0x80,0xAF,0x80,0xAF,0x80,0xAF]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0190, bytearray([0x00,0x96,0x00,0x96,0x00,0x96,0x00,0x96]))
+ rom.write_bytes(ANIMATED_TILE_DATA_ADDR + 0x0198, bytearray([0x80,0x96,0x80,0x96,0x80,0x96,0x80,0x96]))
+
+ # Insert hand drawn graphics for in level indicators
+ rom.write_bytes(0xE7000, read_graphics_file("indicators.bin"))
+ # Upload indicator GFX
+ UPLOAD_INDICATOR_GFX = 0x87000
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0000, bytearray([0xAD, 0x00, 0x01])) # upload_score_sprite_gfx: lda $0100
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0003, bytearray([0xC9, 0x13])) # cmp #$13
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0005, bytearray([0xF0, 0x03])) # beq .check_level
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0007, bytearray([0x4C, 0x9D, 0xF0])) # jmp .check_map
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x000A, bytearray([0xA5, 0x7C])) # .check_level lda $7C
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x000C, bytearray([0xF0, 0x03])) # beq ..perform
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x000E, bytearray([0x4C, 0x9C, 0xF0])) # jmp .skip
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0011, bytearray([0xE6, 0x7C])) # ..perform inc $7C
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0013, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0015, bytearray([0xA0, 0x80])) # ldy #$80
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0017, bytearray([0x8C, 0x15, 0x21])) # sty $2115
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x001A, bytearray([0xA9, 0x01, 0x18])) # lda #$1801
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x001D, bytearray([0x8D, 0x20, 0x43])) # sta $4320
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0020, bytearray([0xA0, 0x1C])) # ldy.b #$1C
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0022, bytearray([0x8C, 0x24, 0x43])) # sty $4324
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0025, bytearray([0xA9, 0x00, 0xF0])) # lda.w #$F000
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0028, bytearray([0x8D, 0x22, 0x43])) # sta $4322
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x002B, bytearray([0xA9, 0xA0, 0x64])) # .nums_01 lda #$64A0
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x002E, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0031, bytearray([0xA9, 0x40, 0x00])) # lda #$0040
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0034, bytearray([0x8D, 0x25, 0x43])) # sta $4325
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0037, bytearray([0x8E, 0x0B, 0x42])) # stx $420B
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x003A, bytearray([0xA9, 0xA0, 0x65])) # .nums_35 lda #$65A0
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x003D, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0040, bytearray([0xA9, 0x40, 0x00])) # lda #$0040
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0043, bytearray([0x8D, 0x25, 0x43])) # sta $4325
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0046, bytearray([0x8E, 0x0B, 0x42])) # stx $420B
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0049, bytearray([0xA9, 0xA0, 0x61])) # .plus_coin lda #$61A0
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x004C, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x004F, bytearray([0xA9, 0x40, 0x00])) # lda #$0040
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0052, bytearray([0x8D, 0x25, 0x43])) # sta $4325
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0055, bytearray([0x8E, 0x0B, 0x42])) # stx $420B
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0058, bytearray([0xA9, 0xA0, 0x60])) # .egg_mushroom lda #$60A0
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x005B, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x005E, bytearray([0xA9, 0x40, 0x00])) # lda #$0040
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0061, bytearray([0x8D, 0x25, 0x43])) # sta $4325
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0064, bytearray([0x8E, 0x0B, 0x42])) # stx $420B
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0067, bytearray([0xA9, 0xE0, 0x67])) # .thwimp lda #$67E0
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x006A, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x006D, bytearray([0xA9, 0x40, 0x00])) # lda #$0040
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0070, bytearray([0x8D, 0x25, 0x43])) # sta $4325
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0073, bytearray([0x8E, 0x0B, 0x42])) # stx $420B
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0076, bytearray([0xA9, 0x80, 0x63])) # .token lda #$6380
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0079, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x007C, bytearray([0xA9, 0x20, 0x00])) # lda #$0020
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x007F, bytearray([0x8D, 0x25, 0x43])) # sta $4325
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0082, bytearray([0x8E, 0x0B, 0x42])) # stx $420B
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0085, bytearray([0xA9, 0x00, 0xEC])) # .layer_3 lda #$EC00
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0088, bytearray([0x8D, 0x22, 0x43])) # sta $4322
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x008B, bytearray([0xA9, 0x80, 0x41])) # lda #$4180
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x008E, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0091, bytearray([0xA9, 0x50, 0x00])) # lda #$0050
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0094, bytearray([0x8D, 0x25, 0x43])) # sta $4325
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0097, bytearray([0x8E, 0x0B, 0x42])) # stx $420B
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x009A, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x009C, bytearray([0x6B])) # .skip rtl
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x009D, bytearray([0xC9, 0x0E])) # .check_map cmp #$0E
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x009F, bytearray([0xF0, 0x51])) # beq .map_pal
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00A1, bytearray([0xC9, 0x0D])) # cmp #$0D
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00A3, bytearray([0xD0, 0xF7])) # bne .skip
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00A5, bytearray([0xA5, 0x7C])) # lda $7C
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00A7, bytearray([0xD0, 0xF3])) # bne .skip
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00A9, bytearray([0xE6, 0x7C])) # inc $7C
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00AB, bytearray([0xC2, 0x20])) # rep #$20
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00AD, bytearray([0xA0, 0x80])) # ldy #$80
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00AF, bytearray([0x8C, 0x15, 0x21])) # sty $2115
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00B2, bytearray([0xA9, 0x01, 0x18])) # lda #$1801
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00B5, bytearray([0x8D, 0x20, 0x43])) # sta $4320
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00B8, bytearray([0xA0, 0x1C])) # ldy.b #$1C
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00BA, bytearray([0x8C, 0x24, 0x43])) # sty $4324
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00BD, bytearray([0xA9, 0x00, 0xE4])) # lda.w #$E400
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00C0, bytearray([0x8D, 0x22, 0x43])) # sta $4322
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00C3, bytearray([0xDA])) # phx
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00C4, bytearray([0x9B])) # txy
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00C5, bytearray([0xA2, 0x18])) # ldx.b #(.map_targets_end-.map_targets-1)*2
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00C7, bytearray([0xA9, 0x40, 0x00])) # ..loop lda #$0040
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00CA, bytearray([0x8D, 0x25, 0x43])) # sta $4325
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00CD, bytearray([0xBF, 0x80, 0xFF, 0x10])) # lda.l .map_targets,x
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00D1, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00D4, bytearray([0x8C, 0x0B, 0x42])) # sty $420B
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00D7, bytearray([0xBF, 0x80, 0xFF, 0x10])) # lda.l .map_targets,x
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00DB, bytearray([0x18])) # clc
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00DC, bytearray([0x69, 0x00, 0x01])) # adc #$0100
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00DF, bytearray([0x8D, 0x16, 0x21])) # sta $2116
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00E2, bytearray([0xA9, 0x40, 0x00])) # lda #$0040
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00E5, bytearray([0x8D, 0x25, 0x43])) # sta $4325
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00E8, bytearray([0x8C, 0x0B, 0x42])) # sty $420B
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00EB, bytearray([0xCA])) # dex
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00EC, bytearray([0xCA])) # dex
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00ED, bytearray([0x10, 0xD8])) # bpl .loop
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00EF, bytearray([0xFA])) # plx
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00F0, bytearray([0xE2, 0x20])) # sep #$20
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00F2, bytearray([0xA9, 0xA3])) # .map_pal lda #$A3
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00F4, bytearray([0x8D, 0x21, 0x21])) # sta $2121
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00F7, bytearray([0xAF, 0x9C, 0xB5, 0x00])) # lda $00B59C
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00FB, bytearray([0x8D, 0x22, 0x21])) # sta $2122
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x00FE, bytearray([0xAF, 0x9D, 0xB5, 0x00])) # lda $00B59D
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0102, bytearray([0x8D, 0x22, 0x21])) # sta $2122
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0105, bytearray([0xAF, 0x9E, 0xB5, 0x00])) # lda $00B59E
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0109, bytearray([0x8D, 0x22, 0x21])) # sta $2122
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x010C, bytearray([0xAF, 0x9F, 0xB5, 0x00])) # lda $00B59F
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0110, bytearray([0x8D, 0x22, 0x21])) # sta $2122
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0113, bytearray([0xAF, 0xA0, 0xB5, 0x00])) # lda $00B5A0
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0117, bytearray([0x8D, 0x22, 0x21])) # sta $2122
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x011A, bytearray([0xAF, 0xA1, 0xB5, 0x00])) # lda $00B5A1
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x011E, bytearray([0x8D, 0x22, 0x21])) # sta $2122
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0121, bytearray([0xAF, 0xA2, 0xB5, 0x00])) # lda $00B5A2
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0125, bytearray([0x8D, 0x22, 0x21])) # sta $2122
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0128, bytearray([0xAF, 0xA3, 0xB5, 0x00])) # lda $00B5A3
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x012C, bytearray([0x8D, 0x22, 0x21])) # sta $2122
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x012F, bytearray([0xAF, 0xA4, 0xB5, 0x00])) # lda $00B5A4
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0133, bytearray([0x8D, 0x22, 0x21])) # sta $2122
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x0136, bytearray([0xAF, 0xA5, 0xB5, 0x00])) # lda $00B5A5
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x013A, bytearray([0x8D, 0x22, 0x21])) # sta $2122
+ rom.write_bytes(UPLOAD_INDICATOR_GFX + 0x013D, bytearray([0x6B])) # rtl
+
+ vram_targets = bytearray([
+ 0x20,0x64, 0x00,0x64, 0xE0,0x62,
+ 0x60,0x66, 0x40,0x66,
+ 0x60,0x64,
+ 0x40,0x62, 0x00,0x62,
+ 0xE0,0x60, 0xC0,0x60, 0xA0,0x60, 0x80,0x60, 0x60,0x60
+ ])
+ rom.write_bytes(0x87F80, vram_targets)
+
+
+def handle_chocolate_island_2(rom):
+ FIX_CHOCOISLAND2_ADDR = 0x87200
+ rom.write_bytes(0x2DB3E, bytearray([0x5C, 0x00, 0xF2, 0x10])) # jml fix_choco_island_2
+ rom.write_bytes(FIX_CHOCOISLAND2_ADDR + 0x0000, bytearray([0xAD, 0x33, 0x1F])) # fix_choco_island_2 lda $1F2F+$04
+ rom.write_bytes(FIX_CHOCOISLAND2_ADDR + 0x0003, bytearray([0x29, 0x08])) # and #$08
+ rom.write_bytes(FIX_CHOCOISLAND2_ADDR + 0x0005, bytearray([0xD0, 0x0D])) # bne .dc_room
+ rom.write_bytes(FIX_CHOCOISLAND2_ADDR + 0x0007, bytearray([0xAD, 0x22, 0x14])) # lda $1422
+ rom.write_bytes(FIX_CHOCOISLAND2_ADDR + 0x000A, bytearray([0xC9, 0x04])) # cmp #$04
+ rom.write_bytes(FIX_CHOCOISLAND2_ADDR + 0x000C, bytearray([0xF0, 0x06])) # beq .dc_room
+ rom.write_bytes(FIX_CHOCOISLAND2_ADDR + 0x000E, bytearray([0xA2, 0x02])) # .rex_room ldx #$02
+ rom.write_bytes(FIX_CHOCOISLAND2_ADDR + 0x0010, bytearray([0x5C, 0x49, 0xDB, 0x05])) # jml $05DB49
+ rom.write_bytes(FIX_CHOCOISLAND2_ADDR + 0x0014, bytearray([0xA2, 0x00])) # .dc_room ldx #$00
+ rom.write_bytes(FIX_CHOCOISLAND2_ADDR + 0x0016, bytearray([0x5C, 0x49, 0xDB, 0x05])) # jml $05DB49
+
+
+def decompress_gfx(compressed_graphics):
+ # This code decompresses graphics in LC_LZ2 format in order to be able to swap player and yoshi's graphics with ease.
+ decompressed_gfx = bytearray([])
+ i = 0
+ while True:
+ cmd = compressed_graphics[i]
+ i += 1
+ if cmd == 0xFF:
+ break
+ else:
+ if (cmd >> 5) == 0x07:
+ size = ((cmd & 0x03) << 8) + compressed_graphics[i] + 1
+ cmd = (cmd & 0x1C) >> 2
+ i += 1
+ else:
+ size = (cmd & 0x1F) + 1
+ cmd = cmd >> 5
+ if cmd == 0x00:
+ decompressed_gfx += bytearray([compressed_graphics[i+j] for j in range(size)])
+ i += size
+ elif cmd == 0x01:
+ byte_fill = compressed_graphics[i]
+ i += 1
+ decompressed_gfx += bytearray([byte_fill for j in range(size)])
+ elif cmd == 0x02:
+ byte_fill_1 = compressed_graphics[i]
+ i += 1
+ byte_fill_2 = compressed_graphics[i]
+ i += 1
+ for j in range(size):
+ if (j & 0x1) == 0x00:
+ decompressed_gfx += bytearray([byte_fill_1])
+ else:
+ decompressed_gfx += bytearray([byte_fill_2])
+ elif cmd == 0x03:
+ byte_read = compressed_graphics[i]
+ i += 1
+ decompressed_gfx += bytearray([(byte_read + j) for j in range(size)])
+ elif cmd == 0x04:
+ position = (compressed_graphics[i] << 8) + compressed_graphics[i+1]
+ i += 2
+ for j in range(size):
+ copy_byte = decompressed_gfx[position+j]
+ decompressed_gfx += bytearray([copy_byte])
+ return decompressed_gfx
+
+
+def convert_3bpp(decompressed_gfx):
+ i = 0
+ converted_gfx = bytearray([])
+ while i < len(decompressed_gfx):
+ converted_gfx += bytearray([decompressed_gfx[i+j] for j in range(16)])
+ i += 16
+ for j in range(8):
+ converted_gfx += bytearray([decompressed_gfx[i]])
+ converted_gfx += bytearray([0x00])
+ i += 1
+ return converted_gfx
+
+
+def copy_gfx_tiles(original, order, size):
+ result = bytearray([])
+ for x in range(len(order)):
+ z = order[x] << size[0]
+ result += bytearray([original[z+y] for y in range(size[1])])
+ return result
+
+
+def file_to_bytes(filename):
+ return open(os.path.dirname(__file__)+filename, "rb").read()
+
+
+def handle_music_shuffle(rom, world: World):
from .Aesthetics import generate_shuffled_level_music, generate_shuffled_ow_music, level_music_address_data, ow_music_address_data
- shuffled_level_music = generate_shuffled_level_music(world, player)
+ shuffled_level_music = generate_shuffled_level_music(world)
for i in range(len(shuffled_level_music)):
rom.write_byte(level_music_address_data[i], shuffled_level_music[i])
- shuffled_ow_music = generate_shuffled_ow_music(world, player)
+ shuffled_ow_music = generate_shuffled_ow_music(world)
for i in range(len(shuffled_ow_music)):
for addr in ow_music_address_data[i]:
rom.write_byte(addr, shuffled_ow_music[i])
-def handle_mario_palette(rom, world, player):
+def handle_mario_palette(rom, world: World):
from .Aesthetics import mario_palettes, fire_mario_palettes, ow_mario_palettes
- chosen_palette = world.mario_palette[player].value
+ chosen_palette = world.options.mario_palette.value
rom.write_bytes(0x32C8, bytes(mario_palettes[chosen_palette]))
rom.write_bytes(0x32F0, bytes(fire_mario_palettes[chosen_palette]))
@@ -723,9 +2851,9 @@ def handle_swap_donut_gh_exits(rom):
rom.write_bytes(0x26371, bytes([0x32]))
-def handle_bowser_rooms(rom, world, player: int):
- if world.bowser_castle_rooms[player] == "random_two_room":
- chosen_rooms = world.per_slot_randoms[player].sample(standard_bowser_rooms, 2)
+def handle_bowser_rooms(rom, world: World):
+ if world.options.bowser_castle_rooms == "random_two_room":
+ chosen_rooms = world.random.sample(standard_bowser_rooms, 2)
rom.write_byte(0x3A680, chosen_rooms[0].roomID)
rom.write_byte(0x3A684, chosen_rooms[0].roomID)
@@ -737,8 +2865,8 @@ def handle_bowser_rooms(rom, world, player: int):
rom.write_byte(chosen_rooms[len(chosen_rooms)-1].exitAddress, 0xBD)
- elif world.bowser_castle_rooms[player] == "random_five_room":
- chosen_rooms = world.per_slot_randoms[player].sample(standard_bowser_rooms, 5)
+ elif world.options.bowser_castle_rooms == "random_five_room":
+ chosen_rooms = world.random.sample(standard_bowser_rooms, 5)
rom.write_byte(0x3A680, chosen_rooms[0].roomID)
rom.write_byte(0x3A684, chosen_rooms[0].roomID)
@@ -750,9 +2878,9 @@ def handle_bowser_rooms(rom, world, player: int):
rom.write_byte(chosen_rooms[len(chosen_rooms)-1].exitAddress, 0xBD)
- elif world.bowser_castle_rooms[player] == "gauntlet":
+ elif world.options.bowser_castle_rooms == "gauntlet":
chosen_rooms = standard_bowser_rooms.copy()
- world.per_slot_randoms[player].shuffle(chosen_rooms)
+ world.random.shuffle(chosen_rooms)
rom.write_byte(0x3A680, chosen_rooms[0].roomID)
rom.write_byte(0x3A684, chosen_rooms[0].roomID)
@@ -763,12 +2891,12 @@ def handle_bowser_rooms(rom, world, player: int):
rom.write_byte(chosen_rooms[i-1].exitAddress, chosen_rooms[i].roomID)
rom.write_byte(chosen_rooms[len(chosen_rooms)-1].exitAddress, 0xBD)
- elif world.bowser_castle_rooms[player] == "labyrinth":
+ elif world.options.bowser_castle_rooms == "labyrinth":
bowser_rooms_copy = full_bowser_rooms.copy()
entrance_point = bowser_rooms_copy.pop(0)
- world.per_slot_randoms[player].shuffle(bowser_rooms_copy)
+ world.random.shuffle(bowser_rooms_copy)
rom.write_byte(entrance_point.exitAddress, bowser_rooms_copy[0].roomID)
for i in range(0, len(bowser_rooms_copy) - 1):
@@ -777,13 +2905,13 @@ def handle_bowser_rooms(rom, world, player: int):
rom.write_byte(bowser_rooms_copy[len(bowser_rooms_copy)-1].exitAddress, 0xBD)
-def handle_boss_shuffle(rom, world, player):
- if world.boss_shuffle[player] == "simple":
+def handle_boss_shuffle(rom, world: World):
+ if world.options.boss_shuffle == "simple":
submap_boss_rooms_copy = submap_boss_rooms.copy()
ow_boss_rooms_copy = ow_boss_rooms.copy()
- world.per_slot_randoms[player].shuffle(submap_boss_rooms_copy)
- world.per_slot_randoms[player].shuffle(ow_boss_rooms_copy)
+ world.random.shuffle(submap_boss_rooms_copy)
+ world.random.shuffle(ow_boss_rooms_copy)
for i in range(len(submap_boss_rooms_copy)):
rom.write_byte(submap_boss_rooms[i].exitAddress, submap_boss_rooms_copy[i].roomID)
@@ -794,21 +2922,21 @@ def handle_boss_shuffle(rom, world, player):
if ow_boss_rooms[i].exitAddressAlt is not None:
rom.write_byte(ow_boss_rooms[i].exitAddressAlt, ow_boss_rooms_copy[i].roomID)
- elif world.boss_shuffle[player] == "full":
+ elif world.options.boss_shuffle == "full":
for i in range(len(submap_boss_rooms)):
- chosen_boss = world.per_slot_randoms[player].choice(submap_boss_rooms)
+ chosen_boss = world.random.choice(submap_boss_rooms)
rom.write_byte(submap_boss_rooms[i].exitAddress, chosen_boss.roomID)
for i in range(len(ow_boss_rooms)):
- chosen_boss = world.per_slot_randoms[player].choice(ow_boss_rooms)
+ chosen_boss = world.random.choice(ow_boss_rooms)
rom.write_byte(ow_boss_rooms[i].exitAddress, chosen_boss.roomID)
if ow_boss_rooms[i].exitAddressAlt is not None:
rom.write_byte(ow_boss_rooms[i].exitAddressAlt, chosen_boss.roomID)
- elif world.boss_shuffle[player] == "singularity":
- chosen_submap_boss = world.per_slot_randoms[player].choice(submap_boss_rooms)
- chosen_ow_boss = world.per_slot_randoms[player].choice(ow_boss_rooms)
+ elif world.options.boss_shuffle == "singularity":
+ chosen_submap_boss = world.random.choice(submap_boss_rooms)
+ chosen_ow_boss = world.random.choice(ow_boss_rooms)
for i in range(len(submap_boss_rooms)):
rom.write_byte(submap_boss_rooms[i].exitAddress, chosen_submap_boss.roomID)
@@ -820,8 +2948,8 @@ def handle_boss_shuffle(rom, world, player):
rom.write_byte(ow_boss_rooms[i].exitAddressAlt, chosen_ow_boss.roomID)
-def patch_rom(world, rom, player, active_level_dict):
- goal_text = generate_goal_text(world, player)
+def patch_rom(world: World, rom, player, active_level_dict):
+ goal_text = generate_goal_text(world)
rom.write_bytes(0x2A6E2, goal_text)
rom.write_byte(0x2B1D8, 0x80)
@@ -829,19 +2957,23 @@ def patch_rom(world, rom, player, active_level_dict):
intro_text = generate_text_box("Bowser has stolen all of Mario's abilities. Can you help Mario travel across Dinosaur land to get them back and save the Princess from him?")
rom.write_bytes(0x2A5D9, intro_text)
- handle_bowser_rooms(rom, world, player)
- handle_boss_shuffle(rom, world, player)
+ handle_bowser_rooms(rom, world)
+ handle_boss_shuffle(rom, world)
+
+ # Handle ROM expansion
+ rom.write_bytes(0x07FD7, bytearray([0x0A]))
+ rom.write_bytes(0x80000, bytearray([0x00 for _ in range(0x80000)]))
# Prevent Title Screen Deaths
rom.write_byte(0x1C6A, 0x80)
# Title Screen Text
player_name_bytes = bytearray()
- player_name = world.get_player_name(player)
+ player_name = world.multiworld.get_player_name(player)
for i in range(16):
char = " "
if i < len(player_name):
- char = world.get_player_name(player)[i]
+ char = player_name[i]
upper_char = char.upper()
if upper_char not in title_text_mapping:
for byte in title_text_mapping["."]:
@@ -869,33 +3001,58 @@ def patch_rom(world, rom, player, active_level_dict):
rom.write_bytes(0x2B88E, bytearray([0x2C, 0x31, 0x73, 0x31, 0x75, 0x31, 0x82, 0x30, 0x30, 0x31, 0xFC, 0x38, 0x31, 0x31, 0x73, 0x31,
0x73, 0x31, 0x7C, 0x30, 0xFC, 0x38, 0xFC, 0x38, 0xFC, 0x38])) # 1 Player Game
- rom.write_bytes(0x2B6D7, bytearray([0xFC, 0x38, 0xFC, 0x38, 0x16, 0x38, 0x18, 0x38, 0x0D, 0x38, 0xFC, 0x38, 0x0B, 0x38, 0x22, 0x38,
+ rom.write_bytes(0x2B6D7, bytearray([0x16, 0x38, 0x18, 0x38, 0x0D, 0x38, 0xFC, 0x38, 0x0B, 0x38, 0x22, 0x38,
0xFC, 0x38, 0x19, 0x38, 0x18, 0x38, 0x1B, 0x38, 0x22, 0x38, 0x10, 0x38, 0x18, 0x38, 0x17, 0x38,
- 0x0E, 0x38, 0xFC, 0x38, 0xFC, 0x38])) # Mod by PoryGone
+ 0x0E, 0x38, 0xFC, 0x38, 0x15, 0x38, 0x21, 0x38, 0x05, 0x38])) # Mod by PoryGone + lx5
# Title Options
rom.write_bytes(0x1E6A, bytearray([0x01]))
rom.write_bytes(0x1E6C, bytearray([0x01]))
rom.write_bytes(0x1E6E, bytearray([0x01]))
+ # Save current level number to RAM (not translevel)
+ rom.write_bytes(0x2D8B9, bytearray([0x20, 0x46, 0xDC])) # org $05D8B9 : jsr level_num
+ rom.write_bytes(0x2DC46 + 0x0000, bytearray([0xA5, 0x0E])) # level_num: lda $0E
+ rom.write_bytes(0x2DC46 + 0x0002, bytearray([0x8D, 0x0B, 0x01])) # sta $010B
+ rom.write_bytes(0x2DC46 + 0x0005, bytearray([0x0A])) # asl
+ rom.write_bytes(0x2DC46 + 0x0006, bytearray([0x60])) # rts
+
# Always allow Start+Select
rom.write_bytes(0x2267, bytearray([0xEA, 0xEA]))
# Always bring up save prompt on beating a level
- if world.autosave[player]:
+ if world.options.autosave:
rom.write_bytes(0x20F93, bytearray([0x00]))
- if world.overworld_speed[player] == "fast":
+ if world.options.overworld_speed == "fast":
rom.write_bytes(0x21414, bytearray([0x20, 0x10]))
- elif world.overworld_speed[player] == "slow":
+ elif world.options.overworld_speed == "slow":
rom.write_bytes(0x21414, bytearray([0x05, 0x05]))
# Starting Life Count
- rom.write_bytes(0x1E25, bytearray([world.starting_life_count[player].value - 1]))
+ rom.write_bytes(0x1E25, bytearray([world.options.starting_life_count.value - 1]))
# Repurpose Bonus Stars counter for Boss Token or Yoshi Eggs
rom.write_bytes(0x3F1AA, bytearray([0x00] * 0x20))
+ # Make bonus star counter go up to 255 (999 in theory, but can't load a 16-bit addr there)
+ rom.write_bytes(0x00F5B, bytearray([0x4C, 0x73, 0x8F]))
+ rom.write_byte(0x00F95, 0x08)
+ rom.write_byte(0x00F97, 0x0C)
+ rom.write_byte(0x00FAC, 0x02)
+ rom.write_byte(0x00F9E, 0x1D)
+ rom.write_byte(0x00FA5, 0x1D)
+ rom.write_byte(0x00FA8, 0x02)
+ rom.write_byte(0x00FB0, 0x1D)
+ rom.write_byte(0x00FB8, 0x02)
+ rom.write_byte(0x00FBE, 0x1D)
+ rom.write_byte(0x00FC2, 0x03)
+
+ # Move Dragon coins one spot to the left & fix tilemap
+ rom.write_byte(0x00FF0, 0xFE)
+ rom.write_byte(0x00C94, 0x3C)
+ rom.write_byte(0x00C9C, 0x38)
+
# Delete Routine that would copy Mario position data over repurposed Luigi save data
rom.write_bytes(0x20F9F, bytearray([0xEA] * 0x3D))
@@ -904,6 +3061,12 @@ def patch_rom(world, rom, player, active_level_dict):
rom.write_bytes(0x6EB1, bytearray([0xEA, 0xEA]))
rom.write_bytes(0x6EB4, bytearray([0xEA, 0xEA, 0xEA]))
+ # Move Thwimps tilemap to another spot in VRAM in order to make them global
+ rom.write_bytes(0x09C13, bytearray([0x7E, 0x7E, 0x7F, 0x7F]))
+ rom.write_byte(0x3F425, 0x32)
+
+ handle_chocolate_island_2(rom)
+
handle_ability_code(rom)
handle_yoshi_box(rom)
@@ -913,44 +3076,97 @@ def patch_rom(world, rom, player, active_level_dict):
handle_vertical_scroll(rom)
+ handle_ram(rom)
+ handle_bonus_block(rom)
+ handle_blocksanity(rom)
+
+ handle_uncompressed_player_gfx(rom)
+
+ # Handle Special Zone Clear flag
+ rom.write_bytes(0x02A74, bytearray([0x1E, 0x1F]))
+ rom.write_bytes(0x09826, bytearray([0x1E, 0x1F]))
+ rom.write_bytes(0x0B9CD, bytearray([0x1E, 0x1F]))
+ rom.write_bytes(0x12986, bytearray([0x1E, 0x1F]))
+ rom.write_bytes(0x62E0F, bytearray([0x1E, 0x1F]))
+
+ handle_indicators(rom)
+ handle_map_indicators(rom)
+
+ # Handle extra traps
+ handle_traps(rom)
+
+ # Mario Start! -> Player Start!
+ text_data_top_tiles = bytearray([
+ 0x00,0xFF,0x4D,0x4C,0x03,0x4D,0x5D,0xFF,0x4C,0x4B,
+ 0x4A,0x03,0x4E,0x01,0x00,0x02,0x00,0x4a,0x4E,0xFF
+ ])
+ text_data_top_props = bytearray([
+ 0x34,0x30,0x34,0x34,0x34,0x34,0x34,0x30,0x34,0x34,
+ 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x30
+ ])
+ text_data_bottom_tiles = bytearray([
+ 0x10,0xFF,0x00,0x5C,0x13,0x00,0x5D,0xFF,0x5C,0x5B,
+ 0x00,0x13,0x5E,0x11,0x00,0x12,0x00,0x03,0x5E,0xFF
+ ])
+ text_data_bottom_props = bytearray([
+ 0x34,0x30,0xb4,0x34,0x34,0xb4,0xf4,0x30,0x34,0x34,
+ 0xB4,0x34,0x34,0x34,0xb4,0x34,0xb4,0xb4,0x34,0x30
+ ])
+
+ rom.write_bytes(0x010D1, text_data_top_tiles)
+ rom.write_bytes(0x01139, text_data_top_props)
+ rom.write_bytes(0x01105, text_data_bottom_tiles)
+ rom.write_bytes(0x0116A, text_data_bottom_props)
+
# Handle Level Shuffle
handle_level_shuffle(rom, active_level_dict)
# Handle Music Shuffle
- if world.music_shuffle[player] != "none":
- handle_music_shuffle(rom, world, player)
+ if world.options.music_shuffle != "none":
+ handle_music_shuffle(rom, world)
+
+ generate_shuffled_ow_palettes(rom, world)
- generate_shuffled_ow_palettes(rom, world, player)
+ generate_shuffled_header_data(rom, world)
- generate_shuffled_header_data(rom, world, player)
+ if world.options.level_palette_shuffle == "on_curated":
+ generate_curated_level_palette_data(rom, world)
- if world.swap_donut_gh_exits[player]:
+ if world.options.overworld_palette_shuffle == "on_curated":
+ generate_curated_map_palette_data(rom, world)
+
+ if world.options.sfx_shuffle != "none":
+ generate_shuffled_sfx(rom, world)
+
+ if world.options.swap_donut_gh_exits:
handle_swap_donut_gh_exits(rom)
- handle_mario_palette(rom, world, player)
+ handle_mario_palette(rom, world)
# Store all relevant option results in ROM
- rom.write_byte(0x01BFA0, world.goal[player].value)
- if world.goal[player].value == 0:
- rom.write_byte(0x01BFA1, world.bosses_required[player].value)
+ rom.write_byte(0x01BFA0, world.options.goal.value)
+ if world.options.goal.value == 0:
+ rom.write_byte(0x01BFA1, world.options.bosses_required.value)
else:
rom.write_byte(0x01BFA1, 0x7F)
- required_yoshi_eggs = max(math.floor(
- world.number_of_yoshi_eggs[player].value * (world.percentage_of_yoshi_eggs[player].value / 100.0)), 1)
+ required_yoshi_eggs = world.required_egg_count
rom.write_byte(0x01BFA2, required_yoshi_eggs)
- #rom.write_byte(0x01BFA3, world.display_sent_item_popups[player].value)
- rom.write_byte(0x01BFA4, world.display_received_item_popups[player].value)
- rom.write_byte(0x01BFA5, world.death_link[player].value)
- rom.write_byte(0x01BFA6, world.dragon_coin_checks[player].value)
- rom.write_byte(0x01BFA7, world.swap_donut_gh_exits[player].value)
+ #rom.write_byte(0x01BFA3, world.options.display_sent_item_popups.value)
+ rom.write_byte(0x01BFA4, world.options.display_received_item_popups.value)
+ rom.write_byte(0x01BFA5, world.options.death_link.value)
+ rom.write_byte(0x01BFA6, world.options.dragon_coin_checks.value)
+ rom.write_byte(0x01BFA7, world.options.swap_donut_gh_exits.value)
+ rom.write_byte(0x01BFA8, world.options.moon_checks.value)
+ rom.write_byte(0x01BFA9, world.options.hidden_1up_checks.value)
+ rom.write_byte(0x01BFAA, world.options.bonus_block_checks.value)
+ rom.write_byte(0x01BFAB, world.options.blocksanity.value)
from Utils import __version__
- rom.name = bytearray(f'SMW{__version__.replace(".", "")[0:3]}_{player}_{world.seed:11}\0', 'utf8')[:21]
+ rom.name = bytearray(f'SMW{__version__.replace(".", "")[0:3]}_{player}_{world.multiworld.seed:11}\0', 'utf8')[:21]
rom.name.extend([0] * (21 - len(rom.name)))
rom.write_bytes(0x7FC0, rom.name)
-
def get_base_rom_bytes(file_name: str = "") -> bytes:
base_rom_bytes = getattr(get_base_rom_bytes, "base_rom_bytes", None)
if not base_rom_bytes:
diff --git a/worlds/smw/Rules.py b/worlds/smw/Rules.py
index 82f22c3a34c1..a900b4fd20ec 100644
--- a/worlds/smw/Rules.py
+++ b/worlds/smw/Rules.py
@@ -2,19 +2,18 @@
from BaseClasses import MultiWorld
from .Names import LocationName, ItemName
-from worlds.AutoWorld import LogicMixin
+from worlds.AutoWorld import World
from worlds.generic.Rules import add_rule, set_rule
-def set_rules(world: MultiWorld, player: int):
+def set_rules(world: World):
- if world.goal[player] == "yoshi_egg_hunt":
- required_yoshi_eggs = max(math.floor(
- world.number_of_yoshi_eggs[player].value * (world.percentage_of_yoshi_eggs[player].value / 100.0)), 1)
+ if world.options.goal == "yoshi_egg_hunt":
+ required_yoshi_eggs = world.required_egg_count
- add_rule(world.get_location(LocationName.yoshis_house, player),
- lambda state: state.has(ItemName.yoshi_egg, player, required_yoshi_eggs))
+ add_rule(world.multiworld.get_location(LocationName.yoshis_house, world.player),
+ lambda state: state.has(ItemName.yoshi_egg, world.player, required_yoshi_eggs))
else:
- add_rule(world.get_location(LocationName.bowser, player), lambda state: state.has(ItemName.mario_carry, player))
+ add_rule(world.multiworld.get_location(LocationName.bowser, world.player), lambda state: state.has(ItemName.mario_carry, world.player))
- world.completion_condition[player] = lambda state: state.has(ItemName.victory, player)
+ world.multiworld.completion_condition[world.player] = lambda state: state.has(ItemName.victory, world.player)
diff --git a/worlds/smw/__init__.py b/worlds/smw/__init__.py
index 431287c32bef..875491a8d022 100644
--- a/worlds/smw/__init__.py
+++ b/worlds/smw/__init__.py
@@ -1,3 +1,4 @@
+import dataclasses
import os
import typing
import math
@@ -5,9 +6,9 @@
import threading
from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification
-from .Items import SMWItem, ItemData, item_table
-from .Locations import SMWLocation, all_locations, setup_locations, special_zone_level_names, special_zone_dragon_coin_names
-from .Options import smw_options
+from .Items import SMWItem, ItemData, item_table, junk_table
+from .Locations import SMWLocation, all_locations, setup_locations, special_zone_level_names, special_zone_dragon_coin_names, special_zone_hidden_1up_names, special_zone_blocksanity_names
+from .Options import SMWOptions
from .Regions import create_regions, connect_regions
from .Levels import full_level_list, generate_level_list, location_id_to_level_id
from .Rules import set_rules
@@ -50,11 +51,14 @@ class SMWWorld(World):
lost all of his abilities. Can he get them back in time to save the Princess?
"""
game: str = "Super Mario World"
- option_definitions = smw_options
+
settings: typing.ClassVar[SMWSettings]
+
+ options_dataclass = SMWOptions
+ options: SMWOptions
+
topology_present = False
- data_version = 3
- required_client_version = (0, 3, 5)
+ required_client_version = (0, 4, 5)
item_name_to_id = {name: data.code for name, data in item_table.items()}
location_name_to_id = all_locations
@@ -62,9 +66,9 @@ class SMWWorld(World):
active_level_dict: typing.Dict[int,int]
web = SMWWeb()
- def __init__(self, world: MultiWorld, player: int):
+ def __init__(self, multiworld: MultiWorld, player: int):
self.rom_name_available_event = threading.Event()
- super().__init__(world, player)
+ super().__init__(multiworld, player)
@classmethod
def stage_assert_generate(cls, multiworld: MultiWorld):
@@ -72,37 +76,34 @@ def stage_assert_generate(cls, multiworld: MultiWorld):
if not os.path.exists(rom_file):
raise FileNotFoundError(rom_file)
- def _get_slot_data(self):
- return {
- #"death_link": self.multiworld.death_link[self.player].value,
- "active_levels": self.active_level_dict,
- }
-
def fill_slot_data(self) -> dict:
- slot_data = self._get_slot_data()
- for option_name in smw_options:
- option = getattr(self.multiworld, option_name)[self.player]
- slot_data[option_name] = option.value
+ slot_data = self.options.as_dict(
+ "dragon_coin_checks",
+ "moon_checks",
+ "hidden_1up_checks",
+ "bonus_block_checks",
+ "blocksanity",
+ )
+ slot_data["active_levels"] = self.active_level_dict
return slot_data
def generate_early(self):
- if self.multiworld.early_climb[self.player]:
+ if self.options.early_climb:
self.multiworld.local_early_items[self.player][ItemName.mario_climb] = 1
-
def create_regions(self):
- location_table = setup_locations(self.multiworld, self.player)
- create_regions(self.multiworld, self.player, location_table)
+ location_table = setup_locations(self)
+ create_regions(self, location_table)
# Not generate basic
itempool: typing.List[SMWItem] = []
- self.active_level_dict = dict(zip(generate_level_list(self.multiworld, self.player), full_level_list))
- self.topology_present = self.multiworld.level_shuffle[self.player]
+ self.active_level_dict = dict(zip(generate_level_list(self), full_level_list))
+ self.topology_present = self.options.level_shuffle
+
+ connect_regions(self, self.active_level_dict)
- connect_regions(self.multiworld, self.player, self.active_level_dict)
-
# Add Boss Token amount requirements for Worlds
add_rule(self.multiworld.get_region(LocationName.donut_plains_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 1))
add_rule(self.multiworld.get_region(LocationName.vanilla_dome_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 2))
@@ -110,18 +111,29 @@ def create_regions(self):
add_rule(self.multiworld.get_region(LocationName.chocolate_island_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 5))
add_rule(self.multiworld.get_region(LocationName.valley_of_bowser_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 6))
- if self.multiworld.exclude_special_zone[self.player]:
- exclusion_pool = set()
- if self.multiworld.dragon_coin_checks[self.player]:
- exclusion_pool.update(special_zone_level_names)
+ exclusion_pool = set()
+ if self.options.exclude_special_zone:
+ exclusion_pool.update(special_zone_level_names)
+ if self.options.dragon_coin_checks:
exclusion_pool.update(special_zone_dragon_coin_names)
- elif self.multiworld.number_of_yoshi_eggs[self.player].value <= 72:
- exclusion_pool.update(special_zone_level_names)
+ if self.options.hidden_1up_checks:
+ exclusion_pool.update(special_zone_hidden_1up_names)
+ if self.options.blocksanity:
+ exclusion_pool.update(special_zone_blocksanity_names)
+
exclusion_rules(self.multiworld, self.player, exclusion_pool)
total_required_locations = 96
- if self.multiworld.dragon_coin_checks[self.player]:
+ if self.options.dragon_coin_checks:
total_required_locations += 49
+ if self.options.moon_checks:
+ total_required_locations += 7
+ if self.options.hidden_1up_checks:
+ total_required_locations += 14
+ if self.options.bonus_block_checks:
+ total_required_locations += 4
+ if self.options.blocksanity:
+ total_required_locations += 582
itempool += [self.create_item(ItemName.mario_run)]
itempool += [self.create_item(ItemName.mario_carry)]
@@ -137,31 +149,53 @@ def create_regions(self):
itempool += [self.create_item(ItemName.green_switch_palace)]
itempool += [self.create_item(ItemName.red_switch_palace)]
itempool += [self.create_item(ItemName.blue_switch_palace)]
+ itempool += [self.create_item(ItemName.special_world_clear)]
- if self.multiworld.goal[self.player] == "yoshi_egg_hunt":
- itempool += [self.create_item(ItemName.yoshi_egg)
- for _ in range(self.multiworld.number_of_yoshi_eggs[self.player])]
+ if self.options.goal == "yoshi_egg_hunt":
+ raw_egg_count = total_required_locations - len(itempool) - len(exclusion_pool)
+ total_egg_count = min(raw_egg_count, self.options.max_yoshi_egg_cap.value)
+ self.required_egg_count = max(math.floor(total_egg_count * (self.options.percentage_of_yoshi_eggs.value / 100.0)), 1)
+ extra_egg_count = total_egg_count - self.required_egg_count
+ removed_egg_count = math.floor(extra_egg_count * (self.options.junk_fill_percentage.value / 100.0))
+ self.actual_egg_count = total_egg_count - removed_egg_count
+
+ itempool += [self.create_item(ItemName.yoshi_egg) for _ in range(self.actual_egg_count)]
+
self.multiworld.get_location(LocationName.yoshis_house, self.player).place_locked_item(self.create_item(ItemName.victory))
else:
+ self.actual_egg_count = 0
+ self.required_egg_count = 0
+
self.multiworld.get_location(LocationName.bowser, self.player).place_locked_item(self.create_item(ItemName.victory))
junk_count = total_required_locations - len(itempool)
trap_weights = []
- trap_weights += ([ItemName.ice_trap] * self.multiworld.ice_trap_weight[self.player].value)
- trap_weights += ([ItemName.stun_trap] * self.multiworld.stun_trap_weight[self.player].value)
- trap_weights += ([ItemName.literature_trap] * self.multiworld.literature_trap_weight[self.player].value)
- trap_weights += ([ItemName.timer_trap] * self.multiworld.timer_trap_weight[self.player].value)
- trap_count = 0 if (len(trap_weights) == 0) else math.ceil(junk_count * (self.multiworld.trap_fill_percentage[self.player].value / 100.0))
+ trap_weights += ([ItemName.ice_trap] * self.options.ice_trap_weight.value)
+ trap_weights += ([ItemName.stun_trap] * self.options.stun_trap_weight.value)
+ trap_weights += ([ItemName.literature_trap] * self.options.literature_trap_weight.value)
+ trap_weights += ([ItemName.timer_trap] * self.options.timer_trap_weight.value)
+ trap_weights += ([ItemName.reverse_controls_trap] * self.options.reverse_trap_weight.value)
+ trap_weights += ([ItemName.thwimp_trap] * self.options.thwimp_trap_weight.value)
+ trap_count = 0 if (len(trap_weights) == 0) else math.ceil(junk_count * (self.options.trap_fill_percentage.value / 100.0))
junk_count -= trap_count
trap_pool = []
for i in range(trap_count):
- trap_item = self.multiworld.random.choice(trap_weights)
+ trap_item = self.random.choice(trap_weights)
trap_pool.append(self.create_item(trap_item))
itempool += trap_pool
- itempool += [self.create_item(ItemName.one_up_mushroom) for _ in range(junk_count)]
+ junk_weights = []
+ junk_weights += ([ItemName.one_coin] * 15)
+ junk_weights += ([ItemName.five_coins] * 15)
+ junk_weights += ([ItemName.ten_coins] * 25)
+ junk_weights += ([ItemName.fifty_coins] * 25)
+ junk_weights += ([ItemName.one_up_mushroom] * 20)
+
+ junk_pool = [self.create_item(self.random.choice(junk_weights)) for _ in range(junk_count)]
+
+ itempool += junk_pool
boss_location_names = [LocationName.yoshis_island_koopaling, LocationName.donut_plains_koopaling, LocationName.vanilla_dome_koopaling,
LocationName.twin_bridges_koopaling, LocationName.forest_koopaling, LocationName.chocolate_koopaling,
@@ -176,18 +210,18 @@ def create_regions(self):
def generate_output(self, output_directory: str):
rompath = "" # if variable is not declared finally clause may fail
try:
- world = self.multiworld
+ multiworld = self.multiworld
player = self.player
rom = LocalRom(get_base_rom_path())
- patch_rom(self.multiworld, rom, self.player, self.active_level_dict)
+ patch_rom(self, rom, self.player, self.active_level_dict)
rompath = os.path.join(output_directory, f"{self.multiworld.get_out_file_name_base(self.player)}.sfc")
rom.write_to_file(rompath)
self.rom_name = rom.name
patch = SMWDeltaPatch(os.path.splitext(rompath)[0]+SMWDeltaPatch.patch_file_ending, player=player,
- player_name=world.player_name[player], patched_path=rompath)
+ player_name=multiworld.player_name[player], patched_path=rompath)
patch.write()
except:
raise
@@ -243,7 +277,15 @@ def extend_hint_information(self, hint_data: typing.Dict[int, typing.Dict[int, s
if level_index >= world_cutoffs[i]:
continue
- if self.multiworld.dragon_coin_checks[self.player].value == 0 and "Dragon Coins" in loc_name:
+ if not self.options.dragon_coin_checks and "Dragon Coins" in loc_name:
+ continue
+ if not self.options.moon_checks and "3-Up Moon" in loc_name:
+ continue
+ if not self.options.hidden_1up_checks and "Hidden 1-Up" in loc_name:
+ continue
+ if not self.options.bonus_block_checks and "1-Up from Bonus Block" in loc_name:
+ continue
+ if not self.options.blocksanity and "Block #" in loc_name:
continue
location = self.multiworld.get_location(loc_name, self.player)
@@ -271,7 +313,7 @@ def create_item(self, name: str, force_non_progression=False) -> Item:
return created_item
def get_filler_item_name(self) -> str:
- return ItemName.one_up_mushroom
+ return self.random.choice(list(junk_table.keys()))
def set_rules(self):
- set_rules(self.multiworld, self.player)
+ set_rules(self)
diff --git a/worlds/smw/data/blocksanity.json b/worlds/smw/data/blocksanity.json
new file mode 100644
index 000000000000..e3737d25978d
--- /dev/null
+++ b/worlds/smw/data/blocksanity.json
@@ -0,0 +1,747 @@
+{
+ "000_bonus": [],
+ "001_vanilla_secret_2": [
+ ["yoshi", "0170", "0130", []],
+ ["green", "02F0", "0170", ["greenswitch carry", "greenswitch cape"]],
+ ["power", "0660", "0110", []],
+ ["power", "0B70", "0100", []],
+ ["multi", "0DC0", "0120", []],
+ ["gray", "0E70", "0120", []],
+ ["single", "1180", "0130", []],
+ ["single", "1190", "0130", []],
+ ["single", "11A0", "0130", []],
+ ["single", "11B0", "0130", []],
+ ["single", "11C0", "0130", []],
+ ["single", "11D0", "0130", []]
+ ],
+ "002_vanilla_secret_3": [
+ ["power", "0270", "00D0", ["swim"]],
+ ["power", "06E0", "00E0", ["swim"]]
+ ],
+ "003_top_secret_area": [],
+ "004_donut_ghost_house": [
+ ["vine", "0120", "0120", []],
+ ["dir", "0070", "0140", ["pswitch"]],
+ ["life", "0610", "0140", ["run cape"]],
+ ["life", "0640", "0140", ["run cape"]],
+ ["life", "0670", "0140", ["run cape"]],
+ ["life", "06A0", "0140", ["run cape"]]
+ ],
+ "005_donut_plains_3": [
+ ["green", "01B0", "00E0", ["greenswitch"]],
+ ["single", "0450", "00F0", []],
+ ["single", "0480", "00F0", []],
+ ["vine", "04E0", "0130", ["mushroom spin"]],
+ ["power", "0BD0", "0140", []],
+ ["bonus", "1250", "00F0", []]
+ ],
+ "006_donut_plains_4": [
+ ["single", "0660", "0130", []],
+ ["power", "0670", "0130", []],
+ ["single", "0680", "0130", []],
+ ["yoshi", "0AF0", "0150", []]
+ ],
+ "007_donut_plains_castle": [
+ ["yellow", "01E0", "00C0", ["yellowswitch"]],
+ ["single", "00A0", "0680", []],
+ ["power", "00B0", "0680", []],
+ ["single", "00C0", "0680", []],
+ ["vine", "0050", "0450", []],
+ ["inlife", "0030", "0320", ["climb"]],
+ ["single", "0050", "0250", []],
+ ["single", "0080", "0250", []],
+ ["single", "00B0", "0250", []],
+ ["green", "0090", "0060", ["greenswitch"]]
+ ],
+ "008_green_switch_palace": [],
+ "009_donut_plains_2": [
+ ["single", "00D0", "0120", []],
+ ["single", "00E0", "0120", []],
+ ["single", "00F0", "0120", []],
+ ["yellow", "0100", "0120", ["yellowswitch"]],
+ ["power", "0330", "00D0", []],
+ ["multi", "03C0", "00C0", []],
+ ["fly", "0820", "00E0", []],
+ ["green", "0560", "00E0", ["greenswitch"]],
+ ["yellow", "0050", "0140", ["yellowswitch"]],
+ ["vine", "02B0", "00E0", ["carry spin mushroom", "yoshi"]]
+ ],
+ "00A_donut_secret_1": [
+ ["single", "02C0", "0130", ["swim"]],
+ ["single", "02D0", "0130", ["swim"]],
+ ["power", "02E0", "0130", ["swim"]],
+ ["single", "02F0", "0130", ["swim"]],
+ ["power", "00E0", "0480", ["swim"]],
+ ["power", "0060", "0250", ["swim balloon"]],
+ ["life", "0110", "0070", ["swim balloon"]],
+ ["power", "01A0", "0250", ["swim balloon"]],
+ ["power", "0570", "0150", ["swim"]],
+ ["key", "0940", "0150", ["swim carry pswitch"]]
+ ],
+ "00B_vanilla_fortress": [
+ ["power", "04E0", "0130", ["swim"]],
+ ["power", "0220", "0130", ["swim"]],
+ ["yellow", "0780", "0110", ["yellowswitch swim"]]
+ ],
+ "00C_butter_bridge_1": [
+ ["power", "08A0", "0110", []],
+ ["multi", "08B0", "00D0", []],
+ ["multi", "0860", "0090", []],
+ ["multi", "08E0", "0050", []],
+ ["life", "0840", "0050", []],
+ ["bonus", "0BD0", "0130", []]
+ ],
+ "00D_butter_bridge_2": [
+ ["power", "0310", "0100", ["carry"]],
+ ["green", "0AC0", "0120", ["greenswitch"]],
+ ["yoshi", "0C70", "0110", ["carry"]]
+ ],
+ "00E_twin_bridges_castle": [
+ ["power", "01B0", "0370", ["climb"]]
+ ],
+ "00F_cheese_bridge": [
+ ["power", "00C0", "0140", []],
+ ["power", "0560", "00E0", []],
+ ["wings", "0A10", "0140", []],
+ ["power", "0B60", "0150", []]
+ ],
+ "010_cookie_mountain": [
+ ["single", "01C0", "0130", []],
+ ["single", "01D0", "0130", []],
+ ["single", "01E0", "0130", []],
+ ["single", "01F0", "0130", []],
+ ["single", "0200", "0130", []],
+ ["single", "0210", "0130", []],
+ ["single", "0220", "0130", []],
+ ["single", "0230", "0130", []],
+ ["single", "0240", "0130", []],
+ ["power", "0200", "00F0", []],
+ ["life", "0A40", "0070", ["climb", "swim"]],
+ ["vine", "0B20", "0140", []],
+ ["yoshi", "0C40", "0140", ["redswitch"]],
+ ["single", "11C0", "0140", []],
+ ["single", "11D0", "0140", []],
+ ["power", "11E0", "0140", []],
+ ["single", "11F0", "0140", []],
+ ["single", "1200", "0140", []],
+ ["single", "1210", "0140", []],
+ ["single", "1220", "0140", []],
+ ["single", "1230", "0140", []],
+ ["single", "1240", "0140", []],
+ ["single", "1250", "0140", []],
+ ["single", "11B0", "0100", []],
+ ["single", "11C0", "0100", []],
+ ["single", "11D0", "0100", []],
+ ["single", "11E0", "0100", []],
+ ["single", "11F0", "0100", []],
+ ["single", "1200", "0100", []],
+ ["single", "1210", "0100", []],
+ ["single", "1220", "0100", []],
+ ["single", "1230", "0100", []],
+ ["single", "1240", "0100", []],
+ ["single", "1250", "0100", []],
+ ["single", "1360", "0140", []]
+ ],
+ "011_soda_lake": [
+ ["power", "0200", "0110", ["swim"]]
+ ],
+ "012_test": [],
+ "013_donut_secret_house": [
+ ["power", "0480", "0140", []],
+ ["multi", "0310", "0140", []],
+ ["life", "04A0", "0140", ["pswitch"]],
+ ["vine", "01E0", "0110", ["pswitch"]],
+ ["dir", "0180", "0130", ["pswitch"]]
+ ],
+ "014_yellow_switch_palace": [],
+ "015_donut_plains_1": [
+ ["single", "0710", "0140", []],
+ ["single", "0720", "0140", []],
+ ["yoshi", "0D20", "00F0", []],
+ ["vine", "0DB0", "0130", []],
+ ["green", "11A0", "0070", ["greenswitch cape"]],
+ ["green", "11A0", "0080", ["greenswitch cape"]],
+ ["green", "11A0", "0090", ["greenswitch cape"]],
+ ["green", "11A0", "00A0", ["greenswitch cape"]],
+ ["green", "11A0", "00B0", ["greenswitch cape"]],
+ ["green", "11A0", "00C0", ["greenswitch cape"]],
+ ["green", "11A0", "00D0", ["greenswitch cape"]],
+ ["green", "11A0", "00E0", ["greenswitch cape"]],
+ ["green", "11A0", "00F0", ["greenswitch cape"]],
+ ["green", "11A0", "0100", ["greenswitch cape"]],
+ ["green", "11A0", "0110", ["greenswitch cape"]],
+ ["green", "11A0", "0120", ["greenswitch cape"]],
+ ["green", "11A0", "0130", ["greenswitch cape"]],
+ ["green", "11A0", "0140", ["greenswitch cape"]],
+ ["green", "11A0", "0150", ["greenswitch cape"]],
+ ["green", "11A0", "0160", ["greenswitch cape"]],
+ ["yellow", "1240", "0120", ["yellowswitch"]],
+ ["yellow", "1280", "0140", ["yellowswitch"]],
+ ["yellow", "1290", "0140", ["yellowswitch"]]
+ ],
+ "016_test": [],
+ "017_test": [],
+ "018_sunken_ghost_ship": [
+ ["power", "0110", "0070", ["swim"]],
+ ["star", "0100", "0C80", ["swim"]]
+ ],
+ "019_test": [],
+ "01A_chocolate_castle": [
+ ["yellow", "09C0", "0110", ["yellowswitch"]],
+ ["yellow", "0150", "0110", ["yellowswitch"]],
+ ["green", "0750", "0140", ["greenswitch"]]
+ ],
+ "01B_chocolate_fortress": [
+ ["power", "0380", "0140", []],
+ ["power", "0360", "0150", []],
+ ["single", "0370", "0150", []],
+ ["single", "0380", "0150", []],
+ ["green", "0AC0", "0130", ["greenswitch"]]
+ ],
+ "01C_chocolate_island_5": [
+ ["yoshi", "0170", "0130", []],
+ ["power", "0180", "0150", []],
+ ["life", "0340", "0170", ["carry", "cape"]],
+ ["yellow", "0560", "0140", ["yellowswitch pswitch"]]
+ ],
+ "01D_chocolate_island_4": [
+ ["yellow", "0700", "0140", ["yellowswitch blueswitch"]],
+ ["pow", "0920", "00A0", []],
+ ["power", "0B50", "0040", []]
+ ],
+ "01E_test": [],
+ "01F_forest_fortress": [
+ ["yellow", "02B0", "00E0", ["yellowswitch"]],
+ ["power", "0750", "00D0", []],
+ ["life", "0ED0", "0140", ["run cape"]],
+ ["life", "0F10", "0140", ["run cape"]],
+ ["life", "0F10", "0100", ["run cape"]],
+ ["life", "0F40", "0130", ["run cape"]],
+ ["life", "0F70", "0140", ["run cape"]],
+ ["life", "0F70", "00F0", ["run cape"]],
+ ["life", "0FA0", "0130", ["run cape"]],
+ ["life", "0FD0", "0140", ["run cape"]],
+ ["life", "0FD0", "0100", ["run cape"]]
+ ],
+ "020_forest_castle": [
+ ["green", "0CC0", "0120", ["greenswitch"]]
+ ],
+ "021_chocolate_ghost_house": [
+ ["power", "0910", "0140", []],
+ ["power", "0110", "0140", []],
+ ["life", "05D0", "0140", []]
+ ],
+ "022_chocolate_island_1": [
+ ["fly", "0490", "0120", ["pswitch"]],
+ ["fly", "0CD0", "0100", ["pswitch"]],
+ ["yoshi", "0E10", "0110", ["pswitch"]],
+ ["green", "0F00", "0140", ["greenswitch blueswitch", "greenswitch cape", "yellowswitch blueswitch", "yellowswitch cape"]],
+ ["life", "0070", "0120", ["pswitch"]]
+ ],
+ "023_chocolate_island_3": [
+ ["power", "0530", "0140", []],
+ ["power", "0A20", "0140", []],
+ ["power", "0F50", "00F0", []],
+ ["green", "1080", "00F0", ["greenswitch"]],
+ ["bonus", "11D0", "0140", []],
+ ["vine", "1220", "0140", []],
+ ["life", "1640", "0140", ["run cape"]],
+ ["life", "1670", "0140", ["run cape"]],
+ ["life", "16A0", "0140", ["run cape"]]
+ ],
+ "024_chocolate_island_2": [
+ ["multi", "00E0", "00A0", []],
+ ["invis", "00F0", "00D0", []],
+ ["yoshi", "0280", "0040", []],
+ ["single", "0080", "0140", []],
+ ["single", "05C0", "0140", []],
+ ["multi" , "05F0", "0120", []],
+ ["power", "0620", "0100", []],
+ ["pow", "0040", "0140", []],
+ ["yellow", "0190", "0110", ["yellowswitch"]],
+ ["yellow", "01A0", "0110", ["yellowswitch"]],
+ ["green", "0240", "0110", ["greenswitch"]],
+ ["green", "0250", "0110", ["greenswitch"]],
+ ["green", "0260", "0110", ["greenswitch"]],
+ ["green", "0270", "0110", ["greenswitch"]],
+ ["green", "0280", "0110", ["greenswitch"]],
+ ["green", "0290", "0110", ["greenswitch"]]
+ ],
+ "101_yoshis_island_castle": [
+ ["single", "0280", "00F0", ["climb"]],
+ ["single", "0290", "00F0", ["climb"]],
+ ["power", "02A0", "00F0", ["climb"]],
+ ["single", "02B0", "00F0", ["climb"]],
+ ["single", "02C0", "00F0", ["climb"]],
+ ["fly", "0250", "0150", ["climb"]]
+ ],
+ "102_yoshis_island_4": [
+ ["yellow", "00D0", "00F0", ["yellowswitch"]],
+ ["power", "0160", "0140", []],
+ ["multi", "0290", "0140", []],
+ ["star", "04E0", "0120", []]
+ ],
+ "103_yoshis_island_3": [
+ ["yellow", "0250", "00C0", ["yellowswitch"]],
+ ["yellow", "0290", "0140", ["yellowswitch"]],
+ ["yellow", "02F0", "00E0", ["yellowswitch carry", "yellowswitch yoshi", "yellowswitch run cape"]],
+ ["yellow", "0300", "00E0", ["yellowswitch carry", "yellowswitch yoshi", "yellowswitch run cape"]],
+ ["yellow", "0310", "00E0", ["yellowswitch carry", "yellowswitch yoshi", "yellowswitch run cape"]],
+ ["yellow", "0320", "00E0", ["yellowswitch carry", "yellowswitch yoshi", "yellowswitch run cape"]],
+ ["yellow", "0330", "00E0", ["yellowswitch carry", "yellowswitch yoshi", "yellowswitch run cape"]],
+ ["yellow", "0340", "00E0", ["yellowswitch carry", "yellowswitch yoshi", "yellowswitch run cape"]],
+ ["yellow", "0350", "00E0", ["yellowswitch carry", "yellowswitch yoshi", "yellowswitch run cape"]],
+ ["single", "04B0", "00A0", []],
+ ["yoshi", "04C0", "00A0", []],
+ ["single", "0AC0", "0140", []],
+ ["power", "0B00", "00C0", []],
+ ["yellow", "0CD0", "0120", ["yellowswitch"]],
+ ["yellow", "0CE0", "0120", ["yellowswitch"]],
+ ["yellow", "0DA0", "00F0", ["yellowswitch"]],
+ ["bonus", "10A0", "0080", []]
+ ],
+ "104_yoshis_house": [],
+ "105_yoshis_island_1": [
+ ["fly", "0250", "0140", []],
+ ["yellow", "09C0", "0140", ["yellowswitch"]],
+ ["life", "0D10", "00F0", []],
+ ["power", "0F30", "0110", []]
+ ],
+ "106_yoshis_island_2": [
+ ["fly", "0080", "00F0", ["carry", "yoshi"]],
+ ["fly", "00C0", "00E0", ["carry", "yoshi"]],
+ ["fly", "0130", "00F0", ["carry", "yoshi"]],
+ ["fly", "0140", "00D0", ["carry", "yoshi"]],
+ ["fly", "0180", "0100", ["carry", "yoshi"]],
+ ["fly", "01C0", "00E0", ["carry", "yoshi"]],
+ ["single", "0260", "0140", []],
+ ["yellow", "0270", "0140", ["yellowswitch"]],
+ ["single", "0280", "0140", []],
+ ["single", "0350", "0150", []],
+ ["yoshi", "0360", "0150", []],
+ ["single", "0B20", "0150", []],
+ ["yoshi", "0B30", "0150", []],
+ ["single", "0B40", "0150", []],
+ ["vine", "0C80", "0100", []],
+ ["yellow", "0DF0", "0120", ["yellowswitch"]]
+ ],
+ "107_vanilla_ghost_house": [
+ ["power", "0200", "0100", []],
+ ["vine", "0750", "0150", []],
+ ["power", "0860", "0140", []],
+ ["multi", "0A00", "0140", []],
+ ["pow", "05E0", "0120", []]
+ ],
+ "108_test": [],
+ "109_vanilla_secret_1": [
+ ["single", "0030", "0590", []],
+ ["power", "0040", "0590", []],
+ ["multi", "0110", "0490", []],
+ ["vine", "01B0", "0520", []],
+ ["vine", "0180", "0470", ["climb"]],
+ ["single", "0190", "0350", ["climb"]],
+ ["single", "01A0", "0350", ["climb"]],
+ ["power", "01B0", "0350", ["climb"]]
+ ],
+ "10A_vanilla_dome_3": [
+ ["single", "01C0", "0140", []],
+ ["fly", "0200", "0160", []],
+ ["fly", "0230", "0120", []],
+ ["power", "02D0", "0110", []],
+ ["fly", "0480", "0150", []],
+ ["invis", "0800", "0130", []],
+ ["power", "0830", "0130", []],
+ ["multi", "0D90", "0120", []],
+ ["power", "03D0", "0130", []],
+ ["yoshi", "10A0", "0100", ["carry", "yoshi"]],
+ ["power", "1A60", "0140", []],
+ ["pswitch", "1E40", "0090", ["run cape pswitch"]],
+ ["pswitch", "1E50", "00A0", ["run cape pswitch"]],
+ ["pswitch", "1E60", "00B0", ["run cape pswitch"]],
+ ["pswitch", "1E70", "00C0", ["run cape pswitch"]],
+ ["pswitch", "1E80", "00D0", ["run cape pswitch"]],
+ ["pswitch", "1E90", "00E0", ["run cape pswitch"]]
+ ],
+ "10B_donut_secret_2": [
+ ["dir", "00A0", "0170", []],
+ ["vine", "0220", "0100", []],
+ ["star", "0250", "0040", ["climb", "yoshi"]],
+ ["power", "0060", "0140", []],
+ ["star", "0B00", "0140", []]
+ ],
+ "10C_test": [],
+ "10D_front_door": [],
+ "10E_back_door": [],
+ "10F_valley_of_bowser_4": [
+ ["yellow", "0370", "0130", ["yellowswitch"]],
+ ["power", "0210", "0130", []],
+ ["vine", "05E0", "0110", []],
+ ["yoshi", "0610", "0040", ["climb"]],
+ ["life", "07A0", "00D0", ["mushroom spin climb"]],
+ ["power", "0B60", "0110", ["yellowswitch climb"]]
+ ],
+ "110_valley_castle": [
+ ["yellow", "0290", "0030", ["yellowswitch"]],
+ ["yellow", "0330", "0110", ["yellowswitch"]],
+ ["green", "0980", "0140", ["greenswitch"]]
+ ],
+ "111_valley_fortress": [
+ ["green", "0220", "0140", ["greenswitch"]],
+ ["yellow", "0940", "0100", ["yellowswitch"]]
+ ],
+ "112_test": [],
+ "113_valley_of_bowser_3": [
+ ["power", "0130", "0110", []],
+ ["power", "08A0", "00E0", []]
+ ],
+ "114_valley_ghost_house": [
+ ["pswitch", "0160", "0100", ["pswitch"]],
+ ["multi", "0570", "0110", ["pswitch"]],
+ ["power", "00E0", "0100", []],
+ ["dir", "0270", "0140", ["pswitch"]]
+ ],
+ "115_valley_of_bowser_2": [
+ ["power", "0330", "0130", []],
+ ["yellow", "0720", "0140", ["yellowswitch"]],
+ ["power", "0010", "00A0", []],
+ ["wings", "00D0", "0140", []]
+ ],
+ "116_valley_of_bowser_1": [
+ ["green", "0810", "0140", ["greenswitch"]],
+ ["invis", "0D40", "0100", []],
+ ["invis", "0D50", "0100", []],
+ ["invis", "0D60", "0100", []],
+ ["yellow", "0D60", "0080", ["yellowswitch cape"]],
+ ["yellow", "0D60", "0090", ["yellowswitch cape"]],
+ ["yellow", "0D60", "00A0", ["yellowswitch cape"]],
+ ["yellow", "0D60", "00B0", ["yellowswitch cape"]],
+ ["vine", "0F20", "0120", []]
+ ],
+ "117_chocolate_secret": [
+ ["power", "04A0", "0120", []],
+ ["power", "0960", "0140", []]
+ ],
+ "118_vanilla_dome_2": [
+ ["single", "0240", "0100", ["swim"]],
+ ["power", "0250", "0100", ["swim"]],
+ ["single", "0260", "0100", ["swim"]],
+ ["single", "0270", "0100", ["swim"]],
+ ["vine", "03B0", "0100", ["swim"]],
+ ["inlife", "0720", "00F0", ["swim climb", "swim yoshi"]],
+ ["single", "0760", "00F0", ["swim climb", "swim yoshi"]],
+ ["single", "0770", "00F0", ["swim climb", "swim yoshi"]],
+ ["power", "0780", "00F0", ["swim climb", "swim yoshi"]],
+ ["power", "0880", "0100", ["swim climb", "swim yoshi"]],
+ ["power", "0730", "0040", ["swim climb", "swim yoshi"]],
+ ["power", "0D10", "0100", ["swim climb", "swim yoshi"]],
+ ["multi", "0290", "0130", ["swim climb", "swim yoshi"]],
+ ["multi", "1150", "0140", ["swim climb", "swim yoshi"]]
+ ],
+ "119_vanilla_dome_4": [
+ ["power", "0690", "0100", []],
+ ["power", "0CB0", "0140", []],
+ ["single", "0E10", "0120", []],
+ ["single", "0E20", "0120", []],
+ ["single", "0E30", "0120", []],
+ ["life", "0E40", "0120", []],
+ ["single", "0E50", "0120", []],
+ ["single", "0E60", "0120", []],
+ ["single", "0E70", "0120", []],
+ ["single", "0E80", "0120", []],
+ ["single", "0E90", "0120", ["carry"]]
+ ],
+ "11A_vanilla_dome_1": [
+ ["fly", "0250", "0110", []],
+ ["power", "0400", "0120", []],
+ ["power", "0450", "00E0", []],
+ ["single", "0460", "0120", []],
+ ["life", "04D0", "0120", []],
+ ["power", "0640", "0180", []],
+ ["vine", "0680", "00E0", ["carry", "redswitch"]],
+ ["star", "00F0", "00E0", []],
+ ["power", "13A0", "0140", ["run star", "run mushroom"]],
+ ["single", "17D0", "0150", ["run star", "run mushroom"]]
+ ],
+ "11B_red_switch_palace": [],
+ "11C_vanilla_dome_castle": [
+ ["life", "0110", "0100", ["carry", "mushroom"]],
+ ["life", "0210", "0100", ["carry", "mushroom"]],
+ ["power", "03A0", "0130", []],
+ ["life", "0170", "0140", ["pswitch"]],
+ ["green", "0B90", "0140", ["greenswitch"]]
+ ],
+ "11D_forest_ghost_house": [
+ ["single", "0950", "0110", []],
+ ["power", "0990", "0110", []],
+ ["fly", "0190", "0150", []],
+ ["power", "0370", "0140", []],
+ ["life", "0640", "0160", []]
+ ],
+ "11E_forest_of_illusion_1": [
+ ["power", "01A0", "0110", []],
+ ["yoshi", "0360", "0130", []],
+ ["power", "0FA0", "0150", []],
+ ["key", "0E00", "0160", ["pballoon"]],
+ ["life", "0610", "0130", []]
+ ],
+ "11F_forest_of_illusion_4": [
+ ["multi", "0540", "0140", []],
+ ["single", "05E0", "0140", []],
+ ["single", "05F0", "0140", []],
+ ["single", "0600", "0140", []],
+ ["single", "0620", "0140", []],
+ ["power", "0630", "0140", []],
+ ["single", "0640", "0140", []],
+ ["single", "0E30", "0140", []],
+ ["single", "0E40", "0140", []],
+ ["power", "0E40", "0100", []],
+ ["single", "0EF0", "0140", []],
+ ["single", "0F00", "0140", []],
+ ["single", "0EF0", "0100", []]
+ ],
+ "120_forest_of_illusion_2": [
+ ["green", "0070", "0130", ["greenswitch swim"]],
+ ["power", "0480", "0040", ["swim"]],
+ ["invis", "0600", "0120", ["swim"]],
+ ["invis", "0610", "0120", ["swim"]],
+ ["inlife", "0620", "0120", ["swim"]],
+ ["invis", "0630", "0120", ["swim"]],
+ ["yellow", "0950", "0160", ["yellowswitch swim"]]
+ ],
+ "121_blue_switch_palace": [],
+ "122_forest_secret": [
+ ["power", "0330", "00A0", []],
+ ["power", "0450", "0110", []],
+ ["life", "06A0", "00B0", ["blueswitch", "carry"]]
+ ],
+ "123_forest_of_illusion_3": [
+ ["yoshi", "0350", "0150", []],
+ ["single", "04C0", "0150", []],
+ ["multi", "04D0", "0140", []],
+ ["single", "04F0", "0120", ["carry", "yoshi"]],
+ ["multi", "0D90", "0110", ["carry", "yoshi"]],
+ ["single", "0D80", "0150", ["carry", "yoshi"]],
+ ["single", "0D90", "0150", ["carry", "yoshi"]],
+ ["single", "0DA0", "0150", ["carry", "yoshi"]],
+ ["single", "0E40", "0140", ["carry", "yoshi"]],
+ ["single", "0E50", "0120", ["carry", "yoshi"]],
+ ["single", "0E70", "0150", ["carry", "yoshi"]],
+ ["single", "0E90", "0110", ["carry", "yoshi"]],
+ ["single", "0EB0", "0130", ["carry", "yoshi"]],
+ ["single", "0EE0", "0140", ["carry", "yoshi"]],
+ ["single", "0EF0", "0100", ["carry", "yoshi"]],
+ ["single", "0F10", "0120", ["carry", "yoshi"]],
+ ["single", "0F50", "0130", ["carry", "yoshi"]],
+ ["single", "0F70", "0150", ["carry", "yoshi"]],
+ ["single", "0F80", "0110", ["carry", "yoshi"]],
+ ["single", "0F90", "0130", ["carry", "yoshi"]],
+ ["single", "0FC0", "0150", ["carry", "yoshi"]],
+ ["single", "0FD0", "0120", ["carry", "yoshi"]],
+ ["single", "11A0", "0150", ["carry", "yoshi"]],
+ ["single", "11B0", "0120", ["carry", "yoshi"]],
+ ["single", "1230", "0150", ["carry", "yoshi"]],
+ ["single", "1240", "0140", ["carry", "yoshi"]],
+ ["single", "1250", "0130", ["carry", "yoshi"]]
+ ],
+ "124_test": [],
+ "125_special_zone_8": [
+ ["yoshi", "0390", "0100", ["carry", "yoshi"]],
+ ["single", "04A0", "0130", []],
+ ["single", "04B0", "0130", []],
+ ["single", "04C0", "0130", []],
+ ["single", "04D0", "0130", []],
+ ["single", "04E0", "0130", []],
+ ["pow", "0560", "0140", []],
+ ["power", "05D0", "0140", []],
+ ["star", "0750", "00F0", []],
+ ["single", "0670", "0140", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "0680", "0140", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "0690", "0140", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "06A0", "0140", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "06B0", "0140", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "06C0", "0140", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "06F0", "0140", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "0700", "0140", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "0710", "0140", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "0720", "0140", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "0730", "0140", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "0740", "0140", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "0750", "0140", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["multi", "0CA0", "0100", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "1100", "0120", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "1110", "0120", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "1120", "0120", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "1130", "0120", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["single", "1140", "0120", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["power", "13F0", "0140", ["mushroom spin", "cape", "carry", "yoshi"]],
+ ["fly", "1570", "00F0", ["mushroom spin", "cape", "carry", "yoshi"]]
+ ],
+ "126_special_zone_7": [
+ ["power", "0350", "0150", ["mushroom"]],
+ ["yoshi", "0C80", "0140", ["mushroom"]],
+ ["single", "0F90", "0140", ["mushroom"]],
+ ["power", "0FA0", "0140", ["mushroom"]],
+ ["single", "0FB0", "0140", ["mushroom"]]
+ ],
+ "127_special_zone_6": [
+ ["power", "0370", "00F0", ["swim"]],
+ ["single", "0610", "0140", ["swim"]],
+ ["single", "0630", "0120", ["swim"]],
+ ["yoshi", "0650", "0100", ["swim"]],
+ ["life", "07D0", "0140", ["swim"]],
+ ["multi", "0950", "0140", ["swim"]],
+ ["single", "0D80", "0140", ["swim"]],
+ ["single", "0D90", "0140", ["swim"]],
+ ["single", "0DA0", "0140", ["swim"]],
+ ["single", "0DB0", "0140", ["swim"]],
+ ["single", "0DC0", "0140", ["swim"]],
+ ["single", "0DD0", "0140", ["swim"]],
+ ["single", "0DE0", "0140", ["swim"]],
+ ["single", "0DF0", "0140", ["swim"]],
+ ["single", "0E00", "0140", ["swim"]],
+ ["single", "0E10", "0140", ["swim"]],
+ ["single", "0E20", "0140", ["swim"]],
+ ["single", "0E30", "0140", ["swim"]],
+ ["single", "0E40", "0140", ["swim"]],
+ ["single", "0E50", "0140", ["swim"]],
+ ["single", "0E60", "0140", ["swim"]],
+ ["single", "0E70", "0140", ["swim"]],
+ ["single", "0D80", "0100", ["swim"]],
+ ["single", "0D90", "0100", ["swim"]],
+ ["single", "0DA0", "0100", ["swim"]],
+ ["single", "0DB0", "0100", ["swim"]],
+ ["single", "0DC0", "0100", ["swim"]],
+ ["single", "0DD0", "0100", ["swim"]],
+ ["single", "0DE0", "0100", ["swim"]],
+ ["single", "0DF0", "0100", ["swim"]],
+ ["single", "0E00", "0100", ["swim"]],
+ ["single", "0E10", "0100", ["swim"]],
+ ["power", "0E20", "0100", ["swim"]],
+ ["single", "0E30", "0100", ["swim"]],
+ ["single", "0E40", "0100", ["swim"]],
+ ["single", "0E50", "0100", ["swim"]],
+ ["single", "0E60", "0100", ["swim"]],
+ ["single", "0E70", "0100", ["swim"]]
+ ],
+ "128_special_zone_5": [
+ ["yoshi", "01D0", "0160", ["mushroom"]]
+ ],
+ "129_test": [],
+ "12A_special_zone_1": [
+ ["vine", "0020", "03C0", []],
+ ["vine", "0050", "03C0", []],
+ ["vine", "0080", "03C0", []],
+ ["vine", "00B0", "03C0", []],
+ ["life", "0110", "0340", ["climb"]],
+ ["vine", "0120", "0280", ["climb"]],
+ ["pow", "0080", "01F0", ["climb"]],
+ ["vine", "00B0", "01F0", ["climb"]],
+ ["power", "00F0", "00D0", ["climb"]],
+ ["pswitch", "0190", "00C0", ["climb pswitch cape"]],
+ ["pswitch", "01C0", "0130", ["climb pswitch cape"]],
+ ["pswitch", "0180", "01A0", ["climb pswitch cape"]],
+ ["pswitch", "01D0", "01A0", ["climb pswitch cape"]],
+ ["pswitch", "01C0", "0270", ["climb pswitch cape"]],
+ ["pswitch", "01A0", "02C0", ["climb pswitch cape"]],
+ ["pswitch", "0190", "0310", ["climb pswitch cape"]],
+ ["pswitch", "01B0", "0370", ["climb pswitch cape"]],
+ ["pswitch", "0180", "03D0", ["climb pswitch cape"]],
+ ["pswitch", "0200", "0120", ["climb pswitch cape", "climb pswitch carry"]],
+ ["pswitch", "0210", "0130", ["climb pswitch cape", "climb pswitch carry"]],
+ ["pswitch", "0220", "0140", ["climb pswitch cape", "climb pswitch carry"]],
+ ["pswitch", "0230", "0150", ["climb pswitch cape", "climb pswitch carry"]]
+ ],
+ "12B_special_zone_2": [
+ ["power", "02E0", "0120", []],
+ ["single", "0380", "0110", ["pballoon"]],
+ ["single", "0450", "0140", ["pballoon"]],
+ ["power", "04A0", "00F0", ["pballoon"]],
+ ["single", "05C0", "0150", ["pballoon"]],
+ ["single", "05C0", "00F0", ["pballoon"]],
+ ["power", "0760", "0140", ["pballoon"]],
+ ["multi", "07E0", "00E0", ["pballoon"]],
+ ["single", "0850", "0100", ["pballoon"]],
+ ["single", "0920", "0140", ["pballoon"]]
+ ],
+ "12C_special_zone_3": [
+ ["power", "03F0", "0110", []],
+ ["yoshi", "0080", "0140", []],
+ ["wings", "0A50", "0140", []]
+ ],
+ "12D_special_zone_4": [
+ ["power", "0490", "0140", ["flower"]],
+ ["star", "0AF0", "00F0", ["flower carry", "flower pswitch"]]
+ ],
+ "12E_test": [],
+ "12F_test": [],
+ "130_star_road_2": [
+ ["star", "0460", "0130", ["swim"]]
+ ],
+ "131_test": [],
+ "132_star_road_3": [
+ ["key", "0080", "0030", ["carry"]]
+ ],
+ "133_test": [],
+ "134_star_road_1": [],
+ "135_star_road_4": [
+ ["power", "0540", "0090", []],
+ ["green", "0C00", "0140", ["greenswitch yoshi carry"]],
+ ["green", "0C10", "0140", ["greenswitch yoshi carry"]],
+ ["green", "0C20", "0140", ["greenswitch yoshi carry"]],
+ ["green", "0C30", "0140", ["greenswitch yoshi carry"]],
+ ["green", "0C40", "0140", ["greenswitch yoshi carry"]],
+ ["green", "0C50", "0140", ["greenswitch yoshi carry"]],
+ ["green", "0C60", "0140", ["greenswitch yoshi carry"]],
+ ["key", "0D40", "0160", ["carry yoshi", "greenswitch redswitch carry"]]
+ ],
+ "136_star_road_5": [
+ ["dir", "0510", "0140", []],
+ ["life", "07D0", "0150", ["pswitch"]],
+ ["vine", "08E0", "0100", ["pswitch"]],
+ ["yellow", "08F0", "0050", ["yellowswitch pswitch climb carry", "yellowswitch specialworld yoshi carry"]],
+ ["yellow", "0900", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "0910", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "0920", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "0930", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "0940", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "0950", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "0960", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "0970", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "0980", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "0990", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "09A0", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "09B0", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "09C0", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "09D0", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "09E0", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "09F0", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "0A00", "0050", ["yellowswitch specialworld yoshi carry"]],
+ ["yellow", "0A10", "0050", ["yellowswitch greenswitch yoshi carry", "yellowswitch greenswitch pswitch climb carry cape"]],
+ ["yellow", "0A10", "0060", ["yellowswitch greenswitch yoshi carry", "yellowswitch greenswitch pswitch climb carry cape"]],
+ ["green", "0A20", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0A30", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0A40", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0A50", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0A60", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0A70", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0A80", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0A90", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0AA0", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0AB0", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0AC0", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0AD0", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0AE0", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0AF0", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0B00", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0B10", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0B20", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0B30", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0B40", "0080", ["greenswitch specialworld yoshi carry"]],
+ ["green", "0B50", "0080", ["greenswitch specialworld yoshi carry"]]
+ ],
+ "137_test": [],
+ "138_test": [],
+ "139_test": [],
+ "13A_test": [],
+ "13B_test": []
+}
\ No newline at end of file
diff --git a/worlds/smw/data/graphics/indicators.bin b/worlds/smw/data/graphics/indicators.bin
new file mode 100644
index 000000000000..70e7036533c9
Binary files /dev/null and b/worlds/smw/data/graphics/indicators.bin differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/agnus_castle.mw3 b/worlds/smw/data/palettes/level/castle_pillars/agnus_castle.mw3
new file mode 100644
index 000000000000..99924e1facf8
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/agnus_castle.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/cheese.mw3 b/worlds/smw/data/palettes/level/castle_pillars/cheese.mw3
new file mode 100644
index 000000000000..e9b83f1bc976
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/cheese.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/chocolate_blue.mw3 b/worlds/smw/data/palettes/level/castle_pillars/chocolate_blue.mw3
new file mode 100644
index 000000000000..51073a167485
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/chocolate_blue.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/dark_aqua_marine.mw3 b/worlds/smw/data/palettes/level/castle_pillars/dark_aqua_marine.mw3
new file mode 100644
index 000000000000..a482231a048b
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/dark_aqua_marine.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/dollhouse.mw3 b/worlds/smw/data/palettes/level/castle_pillars/dollhouse.mw3
new file mode 100644
index 000000000000..06849ceaf1d5
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/dollhouse.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/gold_caslte.mw3 b/worlds/smw/data/palettes/level/castle_pillars/gold_caslte.mw3
new file mode 100644
index 000000000000..f4c03d2cb17a
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/gold_caslte.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/keves_castle.mw3 b/worlds/smw/data/palettes/level/castle_pillars/keves_castle.mw3
new file mode 100644
index 000000000000..a1ab883c34ef
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/keves_castle.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/original_gray.mw3 b/worlds/smw/data/palettes/level/castle_pillars/original_gray.mw3
new file mode 100644
index 000000000000..37143fb2b889
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/original_gray.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/original_green.mw3 b/worlds/smw/data/palettes/level/castle_pillars/original_green.mw3
new file mode 100644
index 000000000000..4124e1900166
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/original_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/original_mustard.mw3 b/worlds/smw/data/palettes/level/castle_pillars/original_mustard.mw3
new file mode 100644
index 000000000000..1ce7a0f3eac2
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/original_mustard.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/original_white.mw3 b/worlds/smw/data/palettes/level/castle_pillars/original_white.mw3
new file mode 100644
index 000000000000..f235216f1da2
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/original_white.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/pink_purple.mw3 b/worlds/smw/data/palettes/level/castle_pillars/pink_purple.mw3
new file mode 100644
index 000000000000..591ce013c9aa
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/pink_purple.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/purple_pink.mw3 b/worlds/smw/data/palettes/level/castle_pillars/purple_pink.mw3
new file mode 100644
index 000000000000..5bea72483006
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/purple_pink.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/sand_gray.mw3 b/worlds/smw/data/palettes/level/castle_pillars/sand_gray.mw3
new file mode 100644
index 000000000000..eff02a49e13f
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/sand_gray.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/sand_green.mw3 b/worlds/smw/data/palettes/level/castle_pillars/sand_green.mw3
new file mode 100644
index 000000000000..5757d8fbfaa1
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/sand_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/shenhe.mw3 b/worlds/smw/data/palettes/level/castle_pillars/shenhe.mw3
new file mode 100644
index 000000000000..93dc170a525e
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/shenhe.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_pillars/whatsapp.mw3 b/worlds/smw/data/palettes/level/castle_pillars/whatsapp.mw3
new file mode 100644
index 000000000000..198f46eca8a9
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_pillars/whatsapp.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_small_windows/dark_lava.mw3 b/worlds/smw/data/palettes/level/castle_small_windows/dark_lava.mw3
new file mode 100644
index 000000000000..477701e86a9b
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_small_windows/dark_lava.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_small_windows/dark_purple.mw3 b/worlds/smw/data/palettes/level/castle_small_windows/dark_purple.mw3
new file mode 100644
index 000000000000..29eff5aeffa3
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_small_windows/dark_purple.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_small_windows/dollhouse.mw3 b/worlds/smw/data/palettes/level/castle_small_windows/dollhouse.mw3
new file mode 100644
index 000000000000..73f69240205b
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_small_windows/dollhouse.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_small_windows/forgotten_temple.mw3 b/worlds/smw/data/palettes/level/castle_small_windows/forgotten_temple.mw3
new file mode 100644
index 000000000000..35d97033f847
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_small_windows/forgotten_temple.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_small_windows/original_gray.mw3 b/worlds/smw/data/palettes/level/castle_small_windows/original_gray.mw3
new file mode 100644
index 000000000000..37143fb2b889
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_small_windows/original_gray.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_small_windows/original_volcanic.mw3 b/worlds/smw/data/palettes/level/castle_small_windows/original_volcanic.mw3
new file mode 100644
index 000000000000..21d82d7c84a1
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_small_windows/original_volcanic.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_small_windows/original_water.mw3 b/worlds/smw/data/palettes/level/castle_small_windows/original_water.mw3
new file mode 100644
index 000000000000..20ee47e8bcba
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_small_windows/original_water.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_small_windows/sand_gray.mw3 b/worlds/smw/data/palettes/level/castle_small_windows/sand_gray.mw3
new file mode 100644
index 000000000000..5b11808ae6b9
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_small_windows/sand_gray.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_small_windows/sand_green.mw3 b/worlds/smw/data/palettes/level/castle_small_windows/sand_green.mw3
new file mode 100644
index 000000000000..2a5ff0ed8551
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_small_windows/sand_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_small_windows/shenhe.mw3 b/worlds/smw/data/palettes/level/castle_small_windows/shenhe.mw3
new file mode 100644
index 000000000000..93dc170a525e
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_small_windows/shenhe.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_small_windows/water.mw3 b/worlds/smw/data/palettes/level/castle_small_windows/water.mw3
new file mode 100644
index 000000000000..9822a3c2eaa2
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_small_windows/water.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_small_windows/whatsapp.mw3 b/worlds/smw/data/palettes/level/castle_small_windows/whatsapp.mw3
new file mode 100644
index 000000000000..198f46eca8a9
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_small_windows/whatsapp.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_wall/cheese.mw3 b/worlds/smw/data/palettes/level/castle_wall/cheese.mw3
new file mode 100644
index 000000000000..913ad3977875
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_wall/cheese.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_wall/dollhouse.mw3 b/worlds/smw/data/palettes/level/castle_wall/dollhouse.mw3
new file mode 100644
index 000000000000..73f69240205b
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_wall/dollhouse.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_wall/grand_marshall.mw3 b/worlds/smw/data/palettes/level/castle_wall/grand_marshall.mw3
new file mode 100644
index 000000000000..574d557f1ead
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_wall/grand_marshall.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_wall/hot_wall.mw3 b/worlds/smw/data/palettes/level/castle_wall/hot_wall.mw3
new file mode 100644
index 000000000000..44703fe4f681
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_wall/hot_wall.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_wall/original.mw3 b/worlds/smw/data/palettes/level/castle_wall/original.mw3
new file mode 100644
index 000000000000..395fb66a0178
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_wall/original.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_wall/sand_green.mw3 b/worlds/smw/data/palettes/level/castle_wall/sand_green.mw3
new file mode 100644
index 000000000000..5757d8fbfaa1
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_wall/sand_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_wall/shenhe.mw3 b/worlds/smw/data/palettes/level/castle_wall/shenhe.mw3
new file mode 100644
index 000000000000..93dc170a525e
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_wall/shenhe.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_wall/water.mw3 b/worlds/smw/data/palettes/level/castle_wall/water.mw3
new file mode 100644
index 000000000000..a0955e820349
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_wall/water.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_windows/brawler_pink.mw3 b/worlds/smw/data/palettes/level/castle_windows/brawler_pink.mw3
new file mode 100644
index 000000000000..69c496fc5d07
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_windows/brawler_pink.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_windows/cheese.mw3 b/worlds/smw/data/palettes/level/castle_windows/cheese.mw3
new file mode 100644
index 000000000000..d91826e8647a
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_windows/cheese.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_windows/dark_aqua_marine.mw3 b/worlds/smw/data/palettes/level/castle_windows/dark_aqua_marine.mw3
new file mode 100644
index 000000000000..501f11d1a954
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_windows/dark_aqua_marine.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_windows/dollhouse.mw3 b/worlds/smw/data/palettes/level/castle_windows/dollhouse.mw3
new file mode 100644
index 000000000000..d3e5fe4b407f
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_windows/dollhouse.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_windows/original_brown.mw3 b/worlds/smw/data/palettes/level/castle_windows/original_brown.mw3
new file mode 100644
index 000000000000..0bcc7305b7aa
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_windows/original_brown.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_windows/original_gray.mw3 b/worlds/smw/data/palettes/level/castle_windows/original_gray.mw3
new file mode 100644
index 000000000000..37143fb2b889
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_windows/original_gray.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_windows/original_water.mw3 b/worlds/smw/data/palettes/level/castle_windows/original_water.mw3
new file mode 100644
index 000000000000..d61f6dba36f6
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_windows/original_water.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_windows/red_castle.mw3 b/worlds/smw/data/palettes/level/castle_windows/red_castle.mw3
new file mode 100644
index 000000000000..666c17f66f4d
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_windows/red_castle.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_windows/shenhe.mw3 b/worlds/smw/data/palettes/level/castle_windows/shenhe.mw3
new file mode 100644
index 000000000000..93dc170a525e
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_windows/shenhe.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_windows/underwater.mw3 b/worlds/smw/data/palettes/level/castle_windows/underwater.mw3
new file mode 100644
index 000000000000..db5c1a996ccb
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_windows/underwater.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_windows/water.mw3 b/worlds/smw/data/palettes/level/castle_windows/water.mw3
new file mode 100644
index 000000000000..a0955e820349
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_windows/water.mw3 differ
diff --git a/worlds/smw/data/palettes/level/castle_windows/whatsapp.mw3 b/worlds/smw/data/palettes/level/castle_windows/whatsapp.mw3
new file mode 100644
index 000000000000..198f46eca8a9
Binary files /dev/null and b/worlds/smw/data/palettes/level/castle_windows/whatsapp.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/brawler_dark.mw3 b/worlds/smw/data/palettes/level/cave/brawler_dark.mw3
new file mode 100644
index 000000000000..9197e99e15d5
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/brawler_dark.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/brawler_purple.mw3 b/worlds/smw/data/palettes/level/cave/brawler_purple.mw3
new file mode 100644
index 000000000000..0bd5a1bf1056
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/brawler_purple.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/brawler_red.mw3 b/worlds/smw/data/palettes/level/cave/brawler_red.mw3
new file mode 100644
index 000000000000..e3f5cdfaaf02
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/brawler_red.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/brawler_teal.mw3 b/worlds/smw/data/palettes/level/cave/brawler_teal.mw3
new file mode 100644
index 000000000000..1927ac99ed87
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/brawler_teal.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/bright_magma.mw3 b/worlds/smw/data/palettes/level/cave/bright_magma.mw3
new file mode 100644
index 000000000000..92e059d863c9
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/bright_magma.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/dark_red.mw3 b/worlds/smw/data/palettes/level/cave/dark_red.mw3
new file mode 100644
index 000000000000..b2cc60680590
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/dark_red.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/glowing_mushroom.mw3 b/worlds/smw/data/palettes/level/cave/glowing_mushroom.mw3
new file mode 100644
index 000000000000..5b5531c7ef2d
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/glowing_mushroom.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/green_depths.mw3 b/worlds/smw/data/palettes/level/cave/green_depths.mw3
new file mode 100644
index 000000000000..7f7593d2e34d
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/green_depths.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/ice.mw3 b/worlds/smw/data/palettes/level/cave/ice.mw3
new file mode 100644
index 000000000000..23b0b2ef08fd
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/ice.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/magma_cave.mw3 b/worlds/smw/data/palettes/level/cave/magma_cave.mw3
new file mode 100644
index 000000000000..ca297deb2578
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/magma_cave.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/original_chocolate.mw3 b/worlds/smw/data/palettes/level/cave/original_chocolate.mw3
new file mode 100644
index 000000000000..db2693d6be98
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/original_chocolate.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/original_gray.mw3 b/worlds/smw/data/palettes/level/cave/original_gray.mw3
new file mode 100644
index 000000000000..2e01f09820c8
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/original_gray.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/original_ice.mw3 b/worlds/smw/data/palettes/level/cave/original_ice.mw3
new file mode 100644
index 000000000000..6d17d16efefb
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/original_ice.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/original_mustard.mw3 b/worlds/smw/data/palettes/level/cave/original_mustard.mw3
new file mode 100644
index 000000000000..001ed133195b
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/original_mustard.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/original_volcanic.mw3 b/worlds/smw/data/palettes/level/cave/original_volcanic.mw3
new file mode 100644
index 000000000000..96befdfa3d55
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/original_volcanic.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/snow.mw3 b/worlds/smw/data/palettes/level/cave/snow.mw3
new file mode 100644
index 000000000000..2328e1a21428
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/snow.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/toxic.mw3 b/worlds/smw/data/palettes/level/cave/toxic.mw3
new file mode 100644
index 000000000000..a78538ddba77
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/toxic.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave/toxic_moss.mw3 b/worlds/smw/data/palettes/level/cave/toxic_moss.mw3
new file mode 100644
index 000000000000..9afe61103098
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave/toxic_moss.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave_rocks/bocchi_rock_hair_cube_things.mw3 b/worlds/smw/data/palettes/level/cave_rocks/bocchi_rock_hair_cube_things.mw3
new file mode 100644
index 000000000000..0fb33b2d6a38
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave_rocks/bocchi_rock_hair_cube_things.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave_rocks/brawler_volcanic.mw3 b/worlds/smw/data/palettes/level/cave_rocks/brawler_volcanic.mw3
new file mode 100644
index 000000000000..5a3cf230f04d
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave_rocks/brawler_volcanic.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave_rocks/ice.mw3 b/worlds/smw/data/palettes/level/cave_rocks/ice.mw3
new file mode 100644
index 000000000000..baa52fbf0b8b
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave_rocks/ice.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave_rocks/layer_2.mw3 b/worlds/smw/data/palettes/level/cave_rocks/layer_2.mw3
new file mode 100644
index 000000000000..ff354e34fefa
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave_rocks/layer_2.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave_rocks/original_gray.mw3 b/worlds/smw/data/palettes/level/cave_rocks/original_gray.mw3
new file mode 100644
index 000000000000..bf50bed4088a
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave_rocks/original_gray.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave_rocks/original_mustard.mw3 b/worlds/smw/data/palettes/level/cave_rocks/original_mustard.mw3
new file mode 100644
index 000000000000..8150d4687553
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave_rocks/original_mustard.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave_rocks/pyra_mythra_ft_pneuma.mw3 b/worlds/smw/data/palettes/level/cave_rocks/pyra_mythra_ft_pneuma.mw3
new file mode 100644
index 000000000000..8f2b2817d805
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave_rocks/pyra_mythra_ft_pneuma.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave_rocks/snow.mw3 b/worlds/smw/data/palettes/level/cave_rocks/snow.mw3
new file mode 100644
index 000000000000..b36bff4fef30
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave_rocks/snow.mw3 differ
diff --git a/worlds/smw/data/palettes/level/cave_rocks/toxic.mw3 b/worlds/smw/data/palettes/level/cave_rocks/toxic.mw3
new file mode 100644
index 000000000000..4f98b11bc175
Binary files /dev/null and b/worlds/smw/data/palettes/level/cave_rocks/toxic.mw3 differ
diff --git a/worlds/smw/data/palettes/level/clouds/atardecer.mw3 b/worlds/smw/data/palettes/level/clouds/atardecer.mw3
new file mode 100644
index 000000000000..bb1754963ea6
Binary files /dev/null and b/worlds/smw/data/palettes/level/clouds/atardecer.mw3 differ
diff --git a/worlds/smw/data/palettes/level/clouds/charcoal.mw3 b/worlds/smw/data/palettes/level/clouds/charcoal.mw3
new file mode 100644
index 000000000000..755f9af87a3e
Binary files /dev/null and b/worlds/smw/data/palettes/level/clouds/charcoal.mw3 differ
diff --git a/worlds/smw/data/palettes/level/clouds/cloudy.mw3 b/worlds/smw/data/palettes/level/clouds/cloudy.mw3
new file mode 100644
index 000000000000..b7d07d348c42
Binary files /dev/null and b/worlds/smw/data/palettes/level/clouds/cloudy.mw3 differ
diff --git a/worlds/smw/data/palettes/level/clouds/cotton_candy.mw3 b/worlds/smw/data/palettes/level/clouds/cotton_candy.mw3
new file mode 100644
index 000000000000..f9ddeb89c87d
Binary files /dev/null and b/worlds/smw/data/palettes/level/clouds/cotton_candy.mw3 differ
diff --git a/worlds/smw/data/palettes/level/clouds/original_green.mw3 b/worlds/smw/data/palettes/level/clouds/original_green.mw3
new file mode 100644
index 000000000000..79af508740ad
Binary files /dev/null and b/worlds/smw/data/palettes/level/clouds/original_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/clouds/original_orange.mw3 b/worlds/smw/data/palettes/level/clouds/original_orange.mw3
new file mode 100644
index 000000000000..453b717b9038
Binary files /dev/null and b/worlds/smw/data/palettes/level/clouds/original_orange.mw3 differ
diff --git a/worlds/smw/data/palettes/level/forest/agnian_queen.mw3 b/worlds/smw/data/palettes/level/forest/agnian_queen.mw3
new file mode 100644
index 000000000000..f187d8a1abb4
Binary files /dev/null and b/worlds/smw/data/palettes/level/forest/agnian_queen.mw3 differ
diff --git a/worlds/smw/data/palettes/level/forest/atardecer.mw3 b/worlds/smw/data/palettes/level/forest/atardecer.mw3
new file mode 100644
index 000000000000..95e07701c24e
Binary files /dev/null and b/worlds/smw/data/palettes/level/forest/atardecer.mw3 differ
diff --git a/worlds/smw/data/palettes/level/forest/frozen.mw3 b/worlds/smw/data/palettes/level/forest/frozen.mw3
new file mode 100644
index 000000000000..f6dc97c10629
Binary files /dev/null and b/worlds/smw/data/palettes/level/forest/frozen.mw3 differ
diff --git a/worlds/smw/data/palettes/level/forest/halloween.mw3 b/worlds/smw/data/palettes/level/forest/halloween.mw3
new file mode 100644
index 000000000000..2e23b6dd6f95
Binary files /dev/null and b/worlds/smw/data/palettes/level/forest/halloween.mw3 differ
diff --git a/worlds/smw/data/palettes/level/forest/kevesi_queen.mw3 b/worlds/smw/data/palettes/level/forest/kevesi_queen.mw3
new file mode 100644
index 000000000000..1a4cd413d3b2
Binary files /dev/null and b/worlds/smw/data/palettes/level/forest/kevesi_queen.mw3 differ
diff --git a/worlds/smw/data/palettes/level/forest/original_dark.mw3 b/worlds/smw/data/palettes/level/forest/original_dark.mw3
new file mode 100644
index 000000000000..7c28daa0d3a6
Binary files /dev/null and b/worlds/smw/data/palettes/level/forest/original_dark.mw3 differ
diff --git a/worlds/smw/data/palettes/level/forest/original_fall.mw3 b/worlds/smw/data/palettes/level/forest/original_fall.mw3
new file mode 100644
index 000000000000..4b67c03d934a
Binary files /dev/null and b/worlds/smw/data/palettes/level/forest/original_fall.mw3 differ
diff --git a/worlds/smw/data/palettes/level/forest/original_green.mw3 b/worlds/smw/data/palettes/level/forest/original_green.mw3
new file mode 100644
index 000000000000..9139338c3158
Binary files /dev/null and b/worlds/smw/data/palettes/level/forest/original_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/forest/sakura.mw3 b/worlds/smw/data/palettes/level/forest/sakura.mw3
new file mode 100644
index 000000000000..df694e2bd34f
Binary files /dev/null and b/worlds/smw/data/palettes/level/forest/sakura.mw3 differ
diff --git a/worlds/smw/data/palettes/level/forest/snow_dark_leaves.mw3 b/worlds/smw/data/palettes/level/forest/snow_dark_leaves.mw3
new file mode 100644
index 000000000000..320256986039
Binary files /dev/null and b/worlds/smw/data/palettes/level/forest/snow_dark_leaves.mw3 differ
diff --git a/worlds/smw/data/palettes/level/forest/snow_green_leaves.mw3 b/worlds/smw/data/palettes/level/forest/snow_green_leaves.mw3
new file mode 100644
index 000000000000..a2346ca3c307
Binary files /dev/null and b/worlds/smw/data/palettes/level/forest/snow_green_leaves.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house/brawler_cyan.mw3 b/worlds/smw/data/palettes/level/ghost_house/brawler_cyan.mw3
new file mode 100644
index 000000000000..cb6590850f3f
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house/brawler_cyan.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house/brawler_orange.mw3 b/worlds/smw/data/palettes/level/ghost_house/brawler_orange.mw3
new file mode 100644
index 000000000000..750def41f99b
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house/brawler_orange.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house/brawler_purple.mw3 b/worlds/smw/data/palettes/level/ghost_house/brawler_purple.mw3
new file mode 100644
index 000000000000..60ac884bf7af
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house/brawler_purple.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house/creepypasta.mw3 b/worlds/smw/data/palettes/level/ghost_house/creepypasta.mw3
new file mode 100644
index 000000000000..92876c3c0eee
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house/creepypasta.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house/crimson_house.mw3 b/worlds/smw/data/palettes/level/ghost_house/crimson_house.mw3
new file mode 100644
index 000000000000..fc7369f7efb1
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house/crimson_house.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house/golden_house.mw3 b/worlds/smw/data/palettes/level/ghost_house/golden_house.mw3
new file mode 100644
index 000000000000..5d4e153bd1cc
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house/golden_house.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house/halloween_pallet.mw3 b/worlds/smw/data/palettes/level/ghost_house/halloween_pallet.mw3
new file mode 100644
index 000000000000..f73c16e46101
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house/halloween_pallet.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house/orange_lights.mw3 b/worlds/smw/data/palettes/level/ghost_house/orange_lights.mw3
new file mode 100644
index 000000000000..8eddf82fd34e
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house/orange_lights.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house/original_aqua.mw3 b/worlds/smw/data/palettes/level/ghost_house/original_aqua.mw3
new file mode 100644
index 000000000000..fec63947beaa
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house/original_aqua.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house/original_blue.mw3 b/worlds/smw/data/palettes/level/ghost_house/original_blue.mw3
new file mode 100644
index 000000000000..b46979edd84e
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house/original_blue.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house/original_dark.mw3 b/worlds/smw/data/palettes/level/ghost_house/original_dark.mw3
new file mode 100644
index 000000000000..eb6152098a04
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house/original_dark.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house/original_white.mw3 b/worlds/smw/data/palettes/level/ghost_house/original_white.mw3
new file mode 100644
index 000000000000..0d5c43f3b913
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house/original_white.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house_exit/evening_exit.mw3 b/worlds/smw/data/palettes/level/ghost_house_exit/evening_exit.mw3
new file mode 100644
index 000000000000..98a5f92a603a
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house_exit/evening_exit.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house_exit/golden_house.mw3 b/worlds/smw/data/palettes/level/ghost_house_exit/golden_house.mw3
new file mode 100644
index 000000000000..45f5c6701ab3
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house_exit/golden_house.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house_exit/original.mw3 b/worlds/smw/data/palettes/level/ghost_house_exit/original.mw3
new file mode 100644
index 000000000000..3856df591aad
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house_exit/original.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house_exit/original_blue_door.mw3 b/worlds/smw/data/palettes/level/ghost_house_exit/original_blue_door.mw3
new file mode 100644
index 000000000000..b43818c57827
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house_exit/original_blue_door.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ghost_house_exit/underwater.mw3 b/worlds/smw/data/palettes/level/ghost_house_exit/underwater.mw3
new file mode 100644
index 000000000000..a92bc7a1ba74
Binary files /dev/null and b/worlds/smw/data/palettes/level/ghost_house_exit/underwater.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_clouds/atardecer.mw3 b/worlds/smw/data/palettes/level/grass_clouds/atardecer.mw3
new file mode 100644
index 000000000000..2b055f2fe658
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_clouds/atardecer.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_clouds/crimson.mw3 b/worlds/smw/data/palettes/level/grass_clouds/crimson.mw3
new file mode 100644
index 000000000000..aefeb5d35680
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_clouds/crimson.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_clouds/electro.mw3 b/worlds/smw/data/palettes/level/grass_clouds/electro.mw3
new file mode 100644
index 000000000000..8a3971982011
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_clouds/electro.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_clouds/geo.mw3 b/worlds/smw/data/palettes/level/grass_clouds/geo.mw3
new file mode 100644
index 000000000000..f085c6f368eb
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_clouds/geo.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_clouds/miku.mw3 b/worlds/smw/data/palettes/level/grass_clouds/miku.mw3
new file mode 100644
index 000000000000..3b009fbc0f67
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_clouds/miku.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_clouds/original_blue.mw3 b/worlds/smw/data/palettes/level/grass_clouds/original_blue.mw3
new file mode 100644
index 000000000000..bcf57d822c71
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_clouds/original_blue.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_clouds/original_green.mw3 b/worlds/smw/data/palettes/level/grass_clouds/original_green.mw3
new file mode 100644
index 000000000000..e896e0dce38b
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_clouds/original_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_clouds/pizza.mw3 b/worlds/smw/data/palettes/level/grass_clouds/pizza.mw3
new file mode 100644
index 000000000000..2be2f2db72ee
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_clouds/pizza.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_clouds/sakura.mw3 b/worlds/smw/data/palettes/level/grass_clouds/sakura.mw3
new file mode 100644
index 000000000000..9a0d8d868733
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_clouds/sakura.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_clouds/shukfr.mw3 b/worlds/smw/data/palettes/level/grass_clouds/shukfr.mw3
new file mode 100644
index 000000000000..635d22c98abb
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_clouds/shukfr.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_clouds/snow_day.mw3 b/worlds/smw/data/palettes/level/grass_clouds/snow_day.mw3
new file mode 100644
index 000000000000..8a7c632110cf
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_clouds/snow_day.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_clouds/volcanic_rock.mw3 b/worlds/smw/data/palettes/level/grass_clouds/volcanic_rock.mw3
new file mode 100644
index 000000000000..224bfed2cb41
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_clouds/volcanic_rock.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/atardecer.mw3 b/worlds/smw/data/palettes/level/grass_forest/atardecer.mw3
new file mode 100644
index 000000000000..cf97cf05e99b
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/atardecer.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/autumn.mw3 b/worlds/smw/data/palettes/level/grass_forest/autumn.mw3
new file mode 100644
index 000000000000..89d75ed27fcf
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/autumn.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/brawler.mw3 b/worlds/smw/data/palettes/level/grass_forest/brawler.mw3
new file mode 100644
index 000000000000..85db596ea5c1
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/brawler.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/brawler_atardecer.mw3 b/worlds/smw/data/palettes/level/grass_forest/brawler_atardecer.mw3
new file mode 100644
index 000000000000..39b73646578d
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/brawler_atardecer.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/brawler_green.mw3 b/worlds/smw/data/palettes/level/grass_forest/brawler_green.mw3
new file mode 100644
index 000000000000..8ada9a25c474
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/brawler_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/crimson.mw3 b/worlds/smw/data/palettes/level/grass_forest/crimson.mw3
new file mode 100644
index 000000000000..48465a548a19
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/crimson.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/deep_forest.mw3 b/worlds/smw/data/palettes/level/grass_forest/deep_forest.mw3
new file mode 100644
index 000000000000..91517aa15e27
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/deep_forest.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/electro.mw3 b/worlds/smw/data/palettes/level/grass_forest/electro.mw3
new file mode 100644
index 000000000000..d462646a48d8
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/electro.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/geo.mw3 b/worlds/smw/data/palettes/level/grass_forest/geo.mw3
new file mode 100644
index 000000000000..f085c6f368eb
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/geo.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/miku.mw3 b/worlds/smw/data/palettes/level/grass_forest/miku.mw3
new file mode 100644
index 000000000000..5eeaf6c571d8
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/miku.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/myon.mw3 b/worlds/smw/data/palettes/level/grass_forest/myon.mw3
new file mode 100644
index 000000000000..8420ad56ec5c
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/myon.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/original_aqua.mw3 b/worlds/smw/data/palettes/level/grass_forest/original_aqua.mw3
new file mode 100644
index 000000000000..ba8ffc90ae0b
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/original_aqua.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/original_green.mw3 b/worlds/smw/data/palettes/level/grass_forest/original_green.mw3
new file mode 100644
index 000000000000..607d80cc9b92
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/original_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/pizza.mw3 b/worlds/smw/data/palettes/level/grass_forest/pizza.mw3
new file mode 100644
index 000000000000..2be2f2db72ee
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/pizza.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/sakura.mw3 b/worlds/smw/data/palettes/level/grass_forest/sakura.mw3
new file mode 100644
index 000000000000..5b7627fe74f2
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/sakura.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/snow_dark_leaves.mw3 b/worlds/smw/data/palettes/level/grass_forest/snow_dark_leaves.mw3
new file mode 100644
index 000000000000..47bbd42b97a7
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/snow_dark_leaves.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/snow_green.mw3 b/worlds/smw/data/palettes/level/grass_forest/snow_green.mw3
new file mode 100644
index 000000000000..50364a4a79c4
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/snow_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_forest/winter.mw3 b/worlds/smw/data/palettes/level/grass_forest/winter.mw3
new file mode 100644
index 000000000000..63109c24b8fa
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_forest/winter.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_hills/atardecer.mw3 b/worlds/smw/data/palettes/level/grass_hills/atardecer.mw3
new file mode 100644
index 000000000000..0c32989c5eac
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_hills/atardecer.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_hills/brawler_green.mw3 b/worlds/smw/data/palettes/level/grass_hills/brawler_green.mw3
new file mode 100644
index 000000000000..612dcce98c7e
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_hills/brawler_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_hills/crimson.mw3 b/worlds/smw/data/palettes/level/grass_hills/crimson.mw3
new file mode 100644
index 000000000000..9f56757809fe
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_hills/crimson.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_hills/electro.mw3 b/worlds/smw/data/palettes/level/grass_hills/electro.mw3
new file mode 100644
index 000000000000..c0796b530a99
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_hills/electro.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_hills/geo.mw3 b/worlds/smw/data/palettes/level/grass_hills/geo.mw3
new file mode 100644
index 000000000000..ac56278a9fc4
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_hills/geo.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_hills/miku.mw3 b/worlds/smw/data/palettes/level/grass_hills/miku.mw3
new file mode 100644
index 000000000000..49c78fadba25
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_hills/miku.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_hills/mogumogu.mw3 b/worlds/smw/data/palettes/level/grass_hills/mogumogu.mw3
new file mode 100644
index 000000000000..88af74ceafcb
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_hills/mogumogu.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_hills/nocturno.mw3 b/worlds/smw/data/palettes/level/grass_hills/nocturno.mw3
new file mode 100644
index 000000000000..f0eeaee1e62c
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_hills/nocturno.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_hills/original.mw3 b/worlds/smw/data/palettes/level/grass_hills/original.mw3
new file mode 100644
index 000000000000..705c6602f7bb
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_hills/original.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_hills/sakura.mw3 b/worlds/smw/data/palettes/level/grass_hills/sakura.mw3
new file mode 100644
index 000000000000..6c65dc4ff010
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_hills/sakura.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_hills/snow.mw3 b/worlds/smw/data/palettes/level/grass_hills/snow.mw3
new file mode 100644
index 000000000000..38d114681250
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_hills/snow.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_hills/sunsetish_grass_hills.mw3 b/worlds/smw/data/palettes/level/grass_hills/sunsetish_grass_hills.mw3
new file mode 100644
index 000000000000..c51bdfc6a7d3
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_hills/sunsetish_grass_hills.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_hills/toothpaste.mw3 b/worlds/smw/data/palettes/level/grass_hills/toothpaste.mw3
new file mode 100644
index 000000000000..7c108ae348ae
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_hills/toothpaste.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_mountains/brawler_lifeless.mw3 b/worlds/smw/data/palettes/level/grass_mountains/brawler_lifeless.mw3
new file mode 100644
index 000000000000..b9979692f09e
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_mountains/brawler_lifeless.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_mountains/classic_sm.mw3 b/worlds/smw/data/palettes/level/grass_mountains/classic_sm.mw3
new file mode 100644
index 000000000000..0e77cc017034
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_mountains/classic_sm.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_mountains/crimson.mw3 b/worlds/smw/data/palettes/level/grass_mountains/crimson.mw3
new file mode 100644
index 000000000000..9527a0a24af2
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_mountains/crimson.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_mountains/dry_hills.mw3 b/worlds/smw/data/palettes/level/grass_mountains/dry_hills.mw3
new file mode 100644
index 000000000000..61bdc7b82e90
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_mountains/dry_hills.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_mountains/electro.mw3 b/worlds/smw/data/palettes/level/grass_mountains/electro.mw3
new file mode 100644
index 000000000000..684b84a6af3f
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_mountains/electro.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_mountains/geo.mw3 b/worlds/smw/data/palettes/level/grass_mountains/geo.mw3
new file mode 100644
index 000000000000..f085c6f368eb
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_mountains/geo.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_mountains/late_sandish.mw3 b/worlds/smw/data/palettes/level/grass_mountains/late_sandish.mw3
new file mode 100644
index 000000000000..d94156adda5c
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_mountains/late_sandish.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_mountains/miku.mw3 b/worlds/smw/data/palettes/level/grass_mountains/miku.mw3
new file mode 100644
index 000000000000..5eeaf6c571d8
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_mountains/miku.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_mountains/original_aqua.mw3 b/worlds/smw/data/palettes/level/grass_mountains/original_aqua.mw3
new file mode 100644
index 000000000000..fda1d358f7f2
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_mountains/original_aqua.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_mountains/original_blue.mw3 b/worlds/smw/data/palettes/level/grass_mountains/original_blue.mw3
new file mode 100644
index 000000000000..29404f64bc8f
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_mountains/original_blue.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_mountains/original_green.mw3 b/worlds/smw/data/palettes/level/grass_mountains/original_green.mw3
new file mode 100644
index 000000000000..607d80cc9b92
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_mountains/original_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_mountains/original_white.mw3 b/worlds/smw/data/palettes/level/grass_mountains/original_white.mw3
new file mode 100644
index 000000000000..7ecd4e3ccb0b
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_mountains/original_white.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_mountains/recksfr.mw3 b/worlds/smw/data/palettes/level/grass_mountains/recksfr.mw3
new file mode 100644
index 000000000000..b5da161c2cb5
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_mountains/recksfr.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_mountains/sakura_hills.mw3 b/worlds/smw/data/palettes/level/grass_mountains/sakura_hills.mw3
new file mode 100644
index 000000000000..0f8144cd23cd
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_mountains/sakura_hills.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_mountains/snow_day.mw3 b/worlds/smw/data/palettes/level/grass_mountains/snow_day.mw3
new file mode 100644
index 000000000000..3b182c3b7b21
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_mountains/snow_day.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/atardecer.mw3 b/worlds/smw/data/palettes/level/grass_rocks/atardecer.mw3
new file mode 100644
index 000000000000..2212fc60ce5b
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/atardecer.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/crimson.mw3 b/worlds/smw/data/palettes/level/grass_rocks/crimson.mw3
new file mode 100644
index 000000000000..9527a0a24af2
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/crimson.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/dark.mw3 b/worlds/smw/data/palettes/level/grass_rocks/dark.mw3
new file mode 100644
index 000000000000..1f6aa06a19e9
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/dark.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/electro.mw3 b/worlds/smw/data/palettes/level/grass_rocks/electro.mw3
new file mode 100644
index 000000000000..684b84a6af3f
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/electro.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/geo.mw3 b/worlds/smw/data/palettes/level/grass_rocks/geo.mw3
new file mode 100644
index 000000000000..f085c6f368eb
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/geo.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/ice.mw3 b/worlds/smw/data/palettes/level/grass_rocks/ice.mw3
new file mode 100644
index 000000000000..349ce8e099d2
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/ice.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/miku.mw3 b/worlds/smw/data/palettes/level/grass_rocks/miku.mw3
new file mode 100644
index 000000000000..5eeaf6c571d8
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/miku.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/napolitano.mw3 b/worlds/smw/data/palettes/level/grass_rocks/napolitano.mw3
new file mode 100644
index 000000000000..bc6ebc6621d9
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/napolitano.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/original_aqua.mw3 b/worlds/smw/data/palettes/level/grass_rocks/original_aqua.mw3
new file mode 100644
index 000000000000..fda1d358f7f2
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/original_aqua.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/original_choco_volcanic.mw3 b/worlds/smw/data/palettes/level/grass_rocks/original_choco_volcanic.mw3
new file mode 100644
index 000000000000..4b4631e841fc
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/original_choco_volcanic.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/original_ice.mw3 b/worlds/smw/data/palettes/level/grass_rocks/original_ice.mw3
new file mode 100644
index 000000000000..041aa9edb1c3
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/original_ice.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/original_volcanic.mw3 b/worlds/smw/data/palettes/level/grass_rocks/original_volcanic.mw3
new file mode 100644
index 000000000000..d7a19dc3bcb2
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/original_volcanic.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/original_volcanic_green.mw3 b/worlds/smw/data/palettes/level/grass_rocks/original_volcanic_green.mw3
new file mode 100644
index 000000000000..86b038d6e4fd
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/original_volcanic_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/original_white.mw3 b/worlds/smw/data/palettes/level/grass_rocks/original_white.mw3
new file mode 100644
index 000000000000..53d76fe5308b
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/original_white.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/original_white_2.mw3 b/worlds/smw/data/palettes/level/grass_rocks/original_white_2.mw3
new file mode 100644
index 000000000000..596e5b93b404
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/original_white_2.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/recks.mw3 b/worlds/smw/data/palettes/level/grass_rocks/recks.mw3
new file mode 100644
index 000000000000..57ed9fcb375f
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/recks.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/sakura.mw3 b/worlds/smw/data/palettes/level/grass_rocks/sakura.mw3
new file mode 100644
index 000000000000..a339b4a62d15
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/sakura.mw3 differ
diff --git a/worlds/smw/data/palettes/level/grass_rocks/thanks_doc.mw3 b/worlds/smw/data/palettes/level/grass_rocks/thanks_doc.mw3
new file mode 100644
index 000000000000..2359c277f376
Binary files /dev/null and b/worlds/smw/data/palettes/level/grass_rocks/thanks_doc.mw3 differ
diff --git a/worlds/smw/data/palettes/level/logs/brawler.mw3 b/worlds/smw/data/palettes/level/logs/brawler.mw3
new file mode 100644
index 000000000000..ed25ef979498
Binary files /dev/null and b/worlds/smw/data/palettes/level/logs/brawler.mw3 differ
diff --git a/worlds/smw/data/palettes/level/logs/evening.mw3 b/worlds/smw/data/palettes/level/logs/evening.mw3
new file mode 100644
index 000000000000..a5cb1bf58a9c
Binary files /dev/null and b/worlds/smw/data/palettes/level/logs/evening.mw3 differ
diff --git a/worlds/smw/data/palettes/level/logs/mahogany.mw3 b/worlds/smw/data/palettes/level/logs/mahogany.mw3
new file mode 100644
index 000000000000..80d5c2c1adfc
Binary files /dev/null and b/worlds/smw/data/palettes/level/logs/mahogany.mw3 differ
diff --git a/worlds/smw/data/palettes/level/logs/not_quite_dawnbreak.mw3 b/worlds/smw/data/palettes/level/logs/not_quite_dawnbreak.mw3
new file mode 100644
index 000000000000..ecce214b80fb
Binary files /dev/null and b/worlds/smw/data/palettes/level/logs/not_quite_dawnbreak.mw3 differ
diff --git a/worlds/smw/data/palettes/level/logs/original.mw3 b/worlds/smw/data/palettes/level/logs/original.mw3
new file mode 100644
index 000000000000..a10489387001
Binary files /dev/null and b/worlds/smw/data/palettes/level/logs/original.mw3 differ
diff --git a/worlds/smw/data/palettes/level/logs/riesgo_de_chubascos.mw3 b/worlds/smw/data/palettes/level/logs/riesgo_de_chubascos.mw3
new file mode 100644
index 000000000000..0a7e877996b9
Binary files /dev/null and b/worlds/smw/data/palettes/level/logs/riesgo_de_chubascos.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_cave/argent_cave.mw3 b/worlds/smw/data/palettes/level/mushroom_cave/argent_cave.mw3
new file mode 100644
index 000000000000..872685ebc8e8
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_cave/argent_cave.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_cave/glowing_mushroom.mw3 b/worlds/smw/data/palettes/level/mushroom_cave/glowing_mushroom.mw3
new file mode 100644
index 000000000000..b3ed246dc190
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_cave/glowing_mushroom.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_cave/green_aqua.mw3 b/worlds/smw/data/palettes/level/mushroom_cave/green_aqua.mw3
new file mode 100644
index 000000000000..dcd6d0f0940e
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_cave/green_aqua.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_cave/ice.mw3 b/worlds/smw/data/palettes/level/mushroom_cave/ice.mw3
new file mode 100644
index 000000000000..ec308ed93b4a
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_cave/ice.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_cave/original.mw3 b/worlds/smw/data/palettes/level/mushroom_cave/original.mw3
new file mode 100644
index 000000000000..7d97ca364024
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_cave/original.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_cave/really_dark.mw3 b/worlds/smw/data/palettes/level/mushroom_cave/really_dark.mw3
new file mode 100644
index 000000000000..696f6df3bb76
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_cave/really_dark.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_cave/toxic.mw3 b/worlds/smw/data/palettes/level/mushroom_cave/toxic.mw3
new file mode 100644
index 000000000000..75f87d3c389a
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_cave/toxic.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_clouds/atardecer.mw3 b/worlds/smw/data/palettes/level/mushroom_clouds/atardecer.mw3
new file mode 100644
index 000000000000..8e3b464f8b53
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_clouds/atardecer.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_clouds/greenshroom.mw3 b/worlds/smw/data/palettes/level/mushroom_clouds/greenshroom.mw3
new file mode 100644
index 000000000000..4ad23210af62
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_clouds/greenshroom.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_clouds/oilshroom.mw3 b/worlds/smw/data/palettes/level/mushroom_clouds/oilshroom.mw3
new file mode 100644
index 000000000000..5013182a3e99
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_clouds/oilshroom.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_clouds/original_aqua.mw3 b/worlds/smw/data/palettes/level/mushroom_clouds/original_aqua.mw3
new file mode 100644
index 000000000000..2448aa853472
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_clouds/original_aqua.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_clouds/original_blue.mw3 b/worlds/smw/data/palettes/level/mushroom_clouds/original_blue.mw3
new file mode 100644
index 000000000000..268518d3a692
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_clouds/original_blue.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_clouds/original_yellow.mw3 b/worlds/smw/data/palettes/level/mushroom_clouds/original_yellow.mw3
new file mode 100644
index 000000000000..7241f8e9545c
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_clouds/original_yellow.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_clouds/riesgo_de_chubascos.mw3 b/worlds/smw/data/palettes/level/mushroom_clouds/riesgo_de_chubascos.mw3
new file mode 100644
index 000000000000..d515e876d315
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_clouds/riesgo_de_chubascos.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_forest/atardecer.mw3 b/worlds/smw/data/palettes/level/mushroom_forest/atardecer.mw3
new file mode 100644
index 000000000000..a78e54d23f26
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_forest/atardecer.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_forest/autumn.mw3 b/worlds/smw/data/palettes/level/mushroom_forest/autumn.mw3
new file mode 100644
index 000000000000..499e038e9e9b
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_forest/autumn.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_forest/count_shroomcula.mw3 b/worlds/smw/data/palettes/level/mushroom_forest/count_shroomcula.mw3
new file mode 100644
index 000000000000..74e336cf6f76
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_forest/count_shroomcula.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_forest/cursed_gold.mw3 b/worlds/smw/data/palettes/level/mushroom_forest/cursed_gold.mw3
new file mode 100644
index 000000000000..f0c7d6124fc6
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_forest/cursed_gold.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_forest/dark_green.mw3 b/worlds/smw/data/palettes/level/mushroom_forest/dark_green.mw3
new file mode 100644
index 000000000000..2b1f15cbb1e0
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_forest/dark_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_forest/lifeless_gray.mw3 b/worlds/smw/data/palettes/level/mushroom_forest/lifeless_gray.mw3
new file mode 100644
index 000000000000..4690266bc650
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_forest/lifeless_gray.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_forest/original.mw3 b/worlds/smw/data/palettes/level/mushroom_forest/original.mw3
new file mode 100644
index 000000000000..cab6dd134869
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_forest/original.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_forest/snow_dark.mw3 b/worlds/smw/data/palettes/level/mushroom_forest/snow_dark.mw3
new file mode 100644
index 000000000000..cf3390d5f186
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_forest/snow_dark.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_forest/snow_green.mw3 b/worlds/smw/data/palettes/level/mushroom_forest/snow_green.mw3
new file mode 100644
index 000000000000..850630f921c6
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_forest/snow_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_hills/atardecer.mw3 b/worlds/smw/data/palettes/level/mushroom_hills/atardecer.mw3
new file mode 100644
index 000000000000..19d9d3245171
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_hills/atardecer.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_hills/atardecer_naranjo.mw3 b/worlds/smw/data/palettes/level/mushroom_hills/atardecer_naranjo.mw3
new file mode 100644
index 000000000000..dfb9d3b4def1
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_hills/atardecer_naranjo.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_hills/atardecer_verde.mw3 b/worlds/smw/data/palettes/level/mushroom_hills/atardecer_verde.mw3
new file mode 100644
index 000000000000..2c59282acb07
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_hills/atardecer_verde.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_hills/future.mw3 b/worlds/smw/data/palettes/level/mushroom_hills/future.mw3
new file mode 100644
index 000000000000..4b0e3cc59f0b
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_hills/future.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_hills/original.mw3 b/worlds/smw/data/palettes/level/mushroom_hills/original.mw3
new file mode 100644
index 000000000000..3e4854584838
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_hills/original.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_hills/riesgo_de_chubascos_azul.mw3 b/worlds/smw/data/palettes/level/mushroom_hills/riesgo_de_chubascos_azul.mw3
new file mode 100644
index 000000000000..02bfc3fbfe33
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_hills/riesgo_de_chubascos_azul.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_hills/riesgo_de_chubascos_cafe.mw3 b/worlds/smw/data/palettes/level/mushroom_hills/riesgo_de_chubascos_cafe.mw3
new file mode 100644
index 000000000000..e3f6885c27e2
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_hills/riesgo_de_chubascos_cafe.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_hills/riesgo_de_chubascos_negro.mw3 b/worlds/smw/data/palettes/level/mushroom_hills/riesgo_de_chubascos_negro.mw3
new file mode 100644
index 000000000000..ce79e6ac6693
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_hills/riesgo_de_chubascos_negro.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_hills/watermelon_skies.mw3 b/worlds/smw/data/palettes/level/mushroom_hills/watermelon_skies.mw3
new file mode 100644
index 000000000000..c4b24625edca
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_hills/watermelon_skies.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_rocks/atardecer.mw3 b/worlds/smw/data/palettes/level/mushroom_rocks/atardecer.mw3
new file mode 100644
index 000000000000..4540f32bf586
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_rocks/atardecer.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_rocks/brightshroom.mw3 b/worlds/smw/data/palettes/level/mushroom_rocks/brightshroom.mw3
new file mode 100644
index 000000000000..2b0b66ac4bb0
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_rocks/brightshroom.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_rocks/original_green.mw3 b/worlds/smw/data/palettes/level/mushroom_rocks/original_green.mw3
new file mode 100644
index 000000000000..3c4ba93067ae
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_rocks/original_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_rocks/original_ice.mw3 b/worlds/smw/data/palettes/level/mushroom_rocks/original_ice.mw3
new file mode 100644
index 000000000000..82b72af52030
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_rocks/original_ice.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_rocks/original_volcanic.mw3 b/worlds/smw/data/palettes/level/mushroom_rocks/original_volcanic.mw3
new file mode 100644
index 000000000000..b366b224fadc
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_rocks/original_volcanic.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_rocks/original_white.mw3 b/worlds/smw/data/palettes/level/mushroom_rocks/original_white.mw3
new file mode 100644
index 000000000000..9ab8fe7eaa36
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_rocks/original_white.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_rocks/riesgo_de_chubascos_cafe.mw3 b/worlds/smw/data/palettes/level/mushroom_rocks/riesgo_de_chubascos_cafe.mw3
new file mode 100644
index 000000000000..c8da0fa17881
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_rocks/riesgo_de_chubascos_cafe.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_rocks/riesgo_de_chubascos_negro.mw3 b/worlds/smw/data/palettes/level/mushroom_rocks/riesgo_de_chubascos_negro.mw3
new file mode 100644
index 000000000000..868945e9f427
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_rocks/riesgo_de_chubascos_negro.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_rocks/shuk_ft_reyn.mw3 b/worlds/smw/data/palettes/level/mushroom_rocks/shuk_ft_reyn.mw3
new file mode 100644
index 000000000000..4fa3e7600a15
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_rocks/shuk_ft_reyn.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_stars/atardecer.mw3 b/worlds/smw/data/palettes/level/mushroom_stars/atardecer.mw3
new file mode 100644
index 000000000000..d64a4bb22982
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_stars/atardecer.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_stars/cool.mw3 b/worlds/smw/data/palettes/level/mushroom_stars/cool.mw3
new file mode 100644
index 000000000000..326b693e141b
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_stars/cool.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_stars/dark_night.mw3 b/worlds/smw/data/palettes/level/mushroom_stars/dark_night.mw3
new file mode 100644
index 000000000000..a4c0442e27e5
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_stars/dark_night.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_stars/halloween.mw3 b/worlds/smw/data/palettes/level/mushroom_stars/halloween.mw3
new file mode 100644
index 000000000000..1fcbd5610253
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_stars/halloween.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_stars/light_pollution.mw3 b/worlds/smw/data/palettes/level/mushroom_stars/light_pollution.mw3
new file mode 100644
index 000000000000..17c47d689170
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_stars/light_pollution.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_stars/midas.mw3 b/worlds/smw/data/palettes/level/mushroom_stars/midas.mw3
new file mode 100644
index 000000000000..9adffb69bd5f
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_stars/midas.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_stars/original_green.mw3 b/worlds/smw/data/palettes/level/mushroom_stars/original_green.mw3
new file mode 100644
index 000000000000..9abb79150620
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_stars/original_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_stars/original_night.mw3 b/worlds/smw/data/palettes/level/mushroom_stars/original_night.mw3
new file mode 100644
index 000000000000..c60a87abc87d
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_stars/original_night.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_stars/purpleish_night.mw3 b/worlds/smw/data/palettes/level/mushroom_stars/purpleish_night.mw3
new file mode 100644
index 000000000000..5757d04f552d
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_stars/purpleish_night.mw3 differ
diff --git a/worlds/smw/data/palettes/level/mushroom_stars/riesgo_de_chubascos.mw3 b/worlds/smw/data/palettes/level/mushroom_stars/riesgo_de_chubascos.mw3
new file mode 100644
index 000000000000..b17323af6555
Binary files /dev/null and b/worlds/smw/data/palettes/level/mushroom_stars/riesgo_de_chubascos.mw3 differ
diff --git a/worlds/smw/data/palettes/level/palettes.json b/worlds/smw/data/palettes/level/palettes.json
new file mode 100644
index 000000000000..9690c2069db1
--- /dev/null
+++ b/worlds/smw/data/palettes/level/palettes.json
@@ -0,0 +1,358 @@
+{
+ "grass_hills": [
+ "atardecer.mw3",
+ "brawler_green.mw3",
+ "crimson.mw3",
+ "electro.mw3",
+ "geo.mw3",
+ "miku.mw3",
+ "mogumogu.mw3",
+ "nocturno.mw3",
+ "original.mw3",
+ "sakura.mw3",
+ "snow.mw3",
+ "sunsetish_grass_hills.mw3",
+ "toothpaste.mw3"
+ ],
+ "grass_forest": [
+ "atardecer.mw3",
+ "autumn.mw3",
+ "brawler.mw3",
+ "brawler_atardecer.mw3",
+ "brawler_green.mw3",
+ "crimson.mw3",
+ "deep_forest.mw3",
+ "electro.mw3",
+ "geo.mw3",
+ "miku.mw3",
+ "myon.mw3",
+ "original_aqua.mw3",
+ "original_green.mw3",
+ "pizza.mw3",
+ "sakura.mw3",
+ "snow_dark_leaves.mw3",
+ "snow_green.mw3",
+ "winter.mw3"
+ ],
+ "grass_rocks": [
+ "atardecer.mw3",
+ "crimson.mw3",
+ "dark.mw3",
+ "electro.mw3",
+ "geo.mw3",
+ "ice.mw3",
+ "miku.mw3",
+ "napolitano.mw3",
+ "original_aqua.mw3",
+ "original_choco_volcanic.mw3",
+ "original_ice.mw3",
+ "original_volcanic.mw3",
+ "original_volcanic_green.mw3",
+ "original_white.mw3",
+ "original_white_2.mw3",
+ "recks.mw3",
+ "sakura.mw3",
+ "thanks_doc.mw3"
+ ],
+ "grass_clouds": [
+ "atardecer.mw3",
+ "crimson.mw3",
+ "electro.mw3",
+ "geo.mw3",
+ "miku.mw3",
+ "original_blue.mw3",
+ "original_green.mw3",
+ "pizza.mw3",
+ "sakura.mw3",
+ "shukfr.mw3",
+ "snow_day.mw3",
+ "volcanic_rock.mw3"
+ ],
+ "grass_mountains": [
+ "brawler_lifeless.mw3",
+ "classic_sm.mw3",
+ "crimson.mw3",
+ "dry_hills.mw3",
+ "electro.mw3",
+ "geo.mw3",
+ "late_sandish.mw3",
+ "miku.mw3",
+ "original_aqua.mw3",
+ "original_blue.mw3",
+ "original_green.mw3",
+ "original_white.mw3",
+ "recksfr.mw3",
+ "sakura_hills.mw3",
+ "snow_day.mw3"
+ ],
+ "cave": [
+ "brawler_dark.mw3",
+ "brawler_purple.mw3",
+ "brawler_red.mw3",
+ "brawler_teal.mw3",
+ "bright_magma.mw3",
+ "dark_red.mw3",
+ "glowing_mushroom.mw3",
+ "green_depths.mw3",
+ "ice.mw3",
+ "magma_cave.mw3",
+ "original_chocolate.mw3",
+ "original_gray.mw3",
+ "original_ice.mw3",
+ "original_mustard.mw3",
+ "original_volcanic.mw3",
+ "snow.mw3",
+ "toxic.mw3",
+ "toxic_moss.mw3"
+ ],
+ "cave_rocks": [
+ "bocchi_rock_hair_cube_things.mw3",
+ "brawler_volcanic.mw3",
+ "ice.mw3",
+ "layer_2.mw3",
+ "original_gray.mw3",
+ "original_mustard.mw3",
+ "pyra_mythra_ft_pneuma.mw3",
+ "snow.mw3",
+ "toxic.mw3"
+ ],
+ "water": [
+ "dark_water.mw3",
+ "deep_aqua.mw3",
+ "deep_chocolate.mw3",
+ "harmless_magma.mw3",
+ "murky.mw3",
+ "oil_spill.mw3",
+ "original_brown.mw3",
+ "original_gray.mw3",
+ "original_green.mw3",
+ "original_mustard.mw3",
+ "original_volcanic.mw3",
+ "pickle_juice.mw3"
+ ],
+ "mushroom_rocks": [
+ "atardecer.mw3",
+ "brightshroom.mw3",
+ "original_green.mw3",
+ "original_ice.mw3",
+ "original_volcanic.mw3",
+ "original_white.mw3",
+ "riesgo_de_chubascos_cafe.mw3",
+ "riesgo_de_chubascos_negro.mw3",
+ "shuk_ft_reyn.mw3"
+ ],
+ "mushroom_clouds": [
+ "atardecer.mw3",
+ "greenshroom.mw3",
+ "oilshroom.mw3",
+ "original_aqua.mw3",
+ "original_blue.mw3",
+ "original_yellow.mw3",
+ "riesgo_de_chubascos.mw3"
+ ],
+ "mushroom_forest": [
+ "atardecer.mw3",
+ "autumn.mw3",
+ "count_shroomcula.mw3",
+ "cursed_gold.mw3",
+ "dark_green.mw3",
+ "lifeless_gray.mw3",
+ "original.mw3",
+ "snow_dark.mw3",
+ "snow_green.mw3"
+ ],
+ "mushroom_hills": [
+ "atardecer.mw3",
+ "atardecer_naranjo.mw3",
+ "atardecer_verde.mw3",
+ "future.mw3",
+ "original.mw3",
+ "riesgo_de_chubascos_azul.mw3",
+ "riesgo_de_chubascos_cafe.mw3",
+ "riesgo_de_chubascos_negro.mw3",
+ "watermelon_skies.mw3"
+ ],
+ "mushroom_stars": [
+ "atardecer.mw3",
+ "cool.mw3",
+ "dark_night.mw3",
+ "halloween.mw3",
+ "light_pollution.mw3",
+ "midas.mw3",
+ "original_green.mw3",
+ "original_night.mw3",
+ "purpleish_night.mw3",
+ "riesgo_de_chubascos.mw3"
+ ],
+ "mushroom_cave": [
+ "argent_cave.mw3",
+ "glowing_mushroom.mw3",
+ "green_aqua.mw3",
+ "ice.mw3",
+ "original.mw3",
+ "really_dark.mw3",
+ "toxic.mw3"
+ ],
+ "forest": [
+ "agnian_queen.mw3",
+ "atardecer.mw3",
+ "frozen.mw3",
+ "halloween.mw3",
+ "kevesi_queen.mw3",
+ "original_dark.mw3",
+ "original_fall.mw3",
+ "original_green.mw3",
+ "sakura.mw3",
+ "snow_dark_leaves.mw3",
+ "snow_green_leaves.mw3"
+ ],
+ "logs": [
+ "brawler.mw3",
+ "evening.mw3",
+ "mahogany.mw3",
+ "not_quite_dawnbreak.mw3",
+ "original.mw3",
+ "riesgo_de_chubascos.mw3"
+ ],
+ "clouds": [
+ "atardecer.mw3",
+ "charcoal.mw3",
+ "cloudy.mw3",
+ "cotton_candy.mw3",
+ "original_green.mw3",
+ "original_orange.mw3"
+ ],
+ "castle_pillars": [
+ "agnus_castle.mw3",
+ "cheese.mw3",
+ "chocolate_blue.mw3",
+ "dark_aqua_marine.mw3",
+ "dollhouse.mw3",
+ "gold_caslte.mw3",
+ "keves_castle.mw3",
+ "original_gray.mw3",
+ "original_green.mw3",
+ "original_mustard.mw3",
+ "original_white.mw3",
+ "pink_purple.mw3",
+ "purple_pink.mw3",
+ "sand_gray.mw3",
+ "sand_green.mw3",
+ "shenhe.mw3",
+ "whatsapp.mw3"
+ ],
+ "castle_windows": [
+ "brawler_pink.mw3",
+ "cheese.mw3",
+ "dark_aqua_marine.mw3",
+ "dollhouse.mw3",
+ "original_brown.mw3",
+ "original_gray.mw3",
+ "original_water.mw3",
+ "red_castle.mw3",
+ "shenhe.mw3",
+ "underwater.mw3",
+ "water.mw3",
+ "whatsapp.mw3"
+ ],
+ "castle_wall": [
+ "cheese.mw3",
+ "dollhouse.mw3",
+ "grand_marshall.mw3",
+ "hot_wall.mw3",
+ "original.mw3",
+ "sand_green.mw3",
+ "shenhe.mw3",
+ "water.mw3"
+ ],
+ "castle_small_windows": [
+ "dark_lava.mw3",
+ "dark_purple.mw3",
+ "dollhouse.mw3",
+ "forgotten_temple.mw3",
+ "original_gray.mw3",
+ "original_volcanic.mw3",
+ "original_water.mw3",
+ "sand_gray.mw3",
+ "sand_green.mw3",
+ "shenhe.mw3",
+ "water.mw3",
+ "whatsapp.mw3"
+ ],
+ "ghost_house": [
+ "brawler_cyan.mw3",
+ "brawler_orange.mw3",
+ "brawler_purple.mw3",
+ "creepypasta.mw3",
+ "crimson_house.mw3",
+ "golden_house.mw3",
+ "halloween_pallet.mw3",
+ "orange_lights.mw3",
+ "original_aqua.mw3",
+ "original_blue.mw3",
+ "original_dark.mw3",
+ "original_white.mw3"
+ ],
+ "ghost_house_exit": [
+ "evening_exit.mw3",
+ "golden_house.mw3",
+ "original.mw3",
+ "original_blue_door.mw3",
+ "underwater.mw3"
+ ],
+ "ship_exterior": [
+ "blue_purple.mw3",
+ "doc_ship.mw3",
+ "grey_ship.mw3",
+ "original.mw3",
+ "reddish.mw3"
+ ],
+ "ship_interior": [
+ "blue_purple.mw3",
+ "bocchi_hitori.mw3",
+ "bocchi_rock.mw3",
+ "brawler.mw3",
+ "grey_ship.mw3",
+ "original.mw3"
+ ],
+ "switch_palace": [
+ "blue_grid.mw3",
+ "brawler_brown.mw3",
+ "cafe_claro.mw3",
+ "color_del_gato_2.mw3",
+ "color_de_gato.mw3",
+ "green_grid.mw3",
+ "gris.mw3",
+ "mario_pants.mw3",
+ "monado.mw3",
+ "morado.mw3",
+ "negro.mw3",
+ "onigiria.mw3",
+ "original.mw3",
+ "original_bonus.mw3",
+ "pink.mw3",
+ "red_grid.mw3",
+ "verde.mw3",
+ "verde_agua.mw3",
+ "yellow_grid.mw3",
+ "youbonus.mw3"
+ ],
+ "yoshi_house": [
+ "atardecer.mw3",
+ "brawler_green.mw3",
+ "choco.mw3",
+ "crimson.mw3",
+ "miku.mw3",
+ "mogumogu.mw3",
+ "monocromo.mw3",
+ "neon.mw3",
+ "nieve.mw3",
+ "night.mw3",
+ "nocturno.mw3",
+ "original.mw3",
+ "sakura.mw3",
+ "snow.mw3",
+ "strong_sun.mw3",
+ "sunsetish_grass_hills.mw3"
+ ]
+}
\ No newline at end of file
diff --git a/worlds/smw/data/palettes/level/ship_exterior/blue_purple.mw3 b/worlds/smw/data/palettes/level/ship_exterior/blue_purple.mw3
new file mode 100644
index 000000000000..2e098de76404
Binary files /dev/null and b/worlds/smw/data/palettes/level/ship_exterior/blue_purple.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ship_exterior/doc_ship.mw3 b/worlds/smw/data/palettes/level/ship_exterior/doc_ship.mw3
new file mode 100644
index 000000000000..8946830bb78d
Binary files /dev/null and b/worlds/smw/data/palettes/level/ship_exterior/doc_ship.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ship_exterior/grey_ship.mw3 b/worlds/smw/data/palettes/level/ship_exterior/grey_ship.mw3
new file mode 100644
index 000000000000..406656ff64e3
Binary files /dev/null and b/worlds/smw/data/palettes/level/ship_exterior/grey_ship.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ship_exterior/original.mw3 b/worlds/smw/data/palettes/level/ship_exterior/original.mw3
new file mode 100644
index 000000000000..20eec6ee80be
Binary files /dev/null and b/worlds/smw/data/palettes/level/ship_exterior/original.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ship_exterior/reddish.mw3 b/worlds/smw/data/palettes/level/ship_exterior/reddish.mw3
new file mode 100644
index 000000000000..674aafd0c0a6
Binary files /dev/null and b/worlds/smw/data/palettes/level/ship_exterior/reddish.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ship_interior/blue_purple.mw3 b/worlds/smw/data/palettes/level/ship_interior/blue_purple.mw3
new file mode 100644
index 000000000000..9ac0f022af3d
Binary files /dev/null and b/worlds/smw/data/palettes/level/ship_interior/blue_purple.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ship_interior/bocchi_hitori.mw3 b/worlds/smw/data/palettes/level/ship_interior/bocchi_hitori.mw3
new file mode 100644
index 000000000000..646c240f68e2
Binary files /dev/null and b/worlds/smw/data/palettes/level/ship_interior/bocchi_hitori.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ship_interior/bocchi_rock.mw3 b/worlds/smw/data/palettes/level/ship_interior/bocchi_rock.mw3
new file mode 100644
index 000000000000..4d45708585c8
Binary files /dev/null and b/worlds/smw/data/palettes/level/ship_interior/bocchi_rock.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ship_interior/brawler.mw3 b/worlds/smw/data/palettes/level/ship_interior/brawler.mw3
new file mode 100644
index 000000000000..d1f3f03d7e90
Binary files /dev/null and b/worlds/smw/data/palettes/level/ship_interior/brawler.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ship_interior/grey_ship.mw3 b/worlds/smw/data/palettes/level/ship_interior/grey_ship.mw3
new file mode 100644
index 000000000000..786802304f33
Binary files /dev/null and b/worlds/smw/data/palettes/level/ship_interior/grey_ship.mw3 differ
diff --git a/worlds/smw/data/palettes/level/ship_interior/original.mw3 b/worlds/smw/data/palettes/level/ship_interior/original.mw3
new file mode 100644
index 000000000000..a208db211f51
Binary files /dev/null and b/worlds/smw/data/palettes/level/ship_interior/original.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/blue_grid.mw3 b/worlds/smw/data/palettes/level/switch_palace/blue_grid.mw3
new file mode 100644
index 000000000000..d613b8061deb
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/blue_grid.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/brawler_brown.mw3 b/worlds/smw/data/palettes/level/switch_palace/brawler_brown.mw3
new file mode 100644
index 000000000000..a3073c31adca
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/brawler_brown.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/cafe_claro.mw3 b/worlds/smw/data/palettes/level/switch_palace/cafe_claro.mw3
new file mode 100644
index 000000000000..90ffc5cf73f8
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/cafe_claro.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/color_de_gato.mw3 b/worlds/smw/data/palettes/level/switch_palace/color_de_gato.mw3
new file mode 100644
index 000000000000..b5ba743bfae9
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/color_de_gato.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/color_del_gato_2.mw3 b/worlds/smw/data/palettes/level/switch_palace/color_del_gato_2.mw3
new file mode 100644
index 000000000000..9c52a56a872a
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/color_del_gato_2.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/green_grid.mw3 b/worlds/smw/data/palettes/level/switch_palace/green_grid.mw3
new file mode 100644
index 000000000000..3a8ded956b43
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/green_grid.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/gris.mw3 b/worlds/smw/data/palettes/level/switch_palace/gris.mw3
new file mode 100644
index 000000000000..8e8df8008bb4
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/gris.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/mario_pants.mw3 b/worlds/smw/data/palettes/level/switch_palace/mario_pants.mw3
new file mode 100644
index 000000000000..b18aee2e1841
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/mario_pants.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/monado.mw3 b/worlds/smw/data/palettes/level/switch_palace/monado.mw3
new file mode 100644
index 000000000000..c37acf989847
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/monado.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/morado.mw3 b/worlds/smw/data/palettes/level/switch_palace/morado.mw3
new file mode 100644
index 000000000000..ab495c8b33b6
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/morado.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/negro.mw3 b/worlds/smw/data/palettes/level/switch_palace/negro.mw3
new file mode 100644
index 000000000000..dd9db46ffdea
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/negro.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/onigiria.mw3 b/worlds/smw/data/palettes/level/switch_palace/onigiria.mw3
new file mode 100644
index 000000000000..7632dc08b226
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/onigiria.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/original.mw3 b/worlds/smw/data/palettes/level/switch_palace/original.mw3
new file mode 100644
index 000000000000..9cc5a26c042b
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/original.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/original_bonus.mw3 b/worlds/smw/data/palettes/level/switch_palace/original_bonus.mw3
new file mode 100644
index 000000000000..b793117f4a6b
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/original_bonus.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/pink.mw3 b/worlds/smw/data/palettes/level/switch_palace/pink.mw3
new file mode 100644
index 000000000000..aaa49aa32fc9
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/pink.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/red_grid.mw3 b/worlds/smw/data/palettes/level/switch_palace/red_grid.mw3
new file mode 100644
index 000000000000..1336dc834680
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/red_grid.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/verde.mw3 b/worlds/smw/data/palettes/level/switch_palace/verde.mw3
new file mode 100644
index 000000000000..92515d0c32f5
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/verde.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/verde_agua.mw3 b/worlds/smw/data/palettes/level/switch_palace/verde_agua.mw3
new file mode 100644
index 000000000000..f845850fbaf0
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/verde_agua.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/yellow_grid.mw3 b/worlds/smw/data/palettes/level/switch_palace/yellow_grid.mw3
new file mode 100644
index 000000000000..c41276492eb0
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/yellow_grid.mw3 differ
diff --git a/worlds/smw/data/palettes/level/switch_palace/youbonus.mw3 b/worlds/smw/data/palettes/level/switch_palace/youbonus.mw3
new file mode 100644
index 000000000000..b213f5660795
Binary files /dev/null and b/worlds/smw/data/palettes/level/switch_palace/youbonus.mw3 differ
diff --git a/worlds/smw/data/palettes/level/water/dark_water.mw3 b/worlds/smw/data/palettes/level/water/dark_water.mw3
new file mode 100644
index 000000000000..e0af55f5d2f8
Binary files /dev/null and b/worlds/smw/data/palettes/level/water/dark_water.mw3 differ
diff --git a/worlds/smw/data/palettes/level/water/deep_aqua.mw3 b/worlds/smw/data/palettes/level/water/deep_aqua.mw3
new file mode 100644
index 000000000000..7407102bf716
Binary files /dev/null and b/worlds/smw/data/palettes/level/water/deep_aqua.mw3 differ
diff --git a/worlds/smw/data/palettes/level/water/deep_chocolate.mw3 b/worlds/smw/data/palettes/level/water/deep_chocolate.mw3
new file mode 100644
index 000000000000..19d645e39f4c
Binary files /dev/null and b/worlds/smw/data/palettes/level/water/deep_chocolate.mw3 differ
diff --git a/worlds/smw/data/palettes/level/water/harmless_magma.mw3 b/worlds/smw/data/palettes/level/water/harmless_magma.mw3
new file mode 100644
index 000000000000..043870cf82c4
Binary files /dev/null and b/worlds/smw/data/palettes/level/water/harmless_magma.mw3 differ
diff --git a/worlds/smw/data/palettes/level/water/murky.mw3 b/worlds/smw/data/palettes/level/water/murky.mw3
new file mode 100644
index 000000000000..57ea1ce44f5e
Binary files /dev/null and b/worlds/smw/data/palettes/level/water/murky.mw3 differ
diff --git a/worlds/smw/data/palettes/level/water/oil_spill.mw3 b/worlds/smw/data/palettes/level/water/oil_spill.mw3
new file mode 100644
index 000000000000..ac1ffed27f62
Binary files /dev/null and b/worlds/smw/data/palettes/level/water/oil_spill.mw3 differ
diff --git a/worlds/smw/data/palettes/level/water/original_brown.mw3 b/worlds/smw/data/palettes/level/water/original_brown.mw3
new file mode 100644
index 000000000000..5f5366cebd11
Binary files /dev/null and b/worlds/smw/data/palettes/level/water/original_brown.mw3 differ
diff --git a/worlds/smw/data/palettes/level/water/original_gray.mw3 b/worlds/smw/data/palettes/level/water/original_gray.mw3
new file mode 100644
index 000000000000..b5087eccbed5
Binary files /dev/null and b/worlds/smw/data/palettes/level/water/original_gray.mw3 differ
diff --git a/worlds/smw/data/palettes/level/water/original_green.mw3 b/worlds/smw/data/palettes/level/water/original_green.mw3
new file mode 100644
index 000000000000..6697f2839edc
Binary files /dev/null and b/worlds/smw/data/palettes/level/water/original_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/water/original_mustard.mw3 b/worlds/smw/data/palettes/level/water/original_mustard.mw3
new file mode 100644
index 000000000000..bf14a8cb157e
Binary files /dev/null and b/worlds/smw/data/palettes/level/water/original_mustard.mw3 differ
diff --git a/worlds/smw/data/palettes/level/water/original_volcanic.mw3 b/worlds/smw/data/palettes/level/water/original_volcanic.mw3
new file mode 100644
index 000000000000..ef22bf7bf6a0
Binary files /dev/null and b/worlds/smw/data/palettes/level/water/original_volcanic.mw3 differ
diff --git a/worlds/smw/data/palettes/level/water/pickle_juice.mw3 b/worlds/smw/data/palettes/level/water/pickle_juice.mw3
new file mode 100644
index 000000000000..a27ebcfd8975
Binary files /dev/null and b/worlds/smw/data/palettes/level/water/pickle_juice.mw3 differ
diff --git a/worlds/smw/data/palettes/level/yoshi_house/atardecer.mw3 b/worlds/smw/data/palettes/level/yoshi_house/atardecer.mw3
new file mode 100644
index 000000000000..561b3f490217
Binary files /dev/null and b/worlds/smw/data/palettes/level/yoshi_house/atardecer.mw3 differ
diff --git a/worlds/smw/data/palettes/level/yoshi_house/brawler_green.mw3 b/worlds/smw/data/palettes/level/yoshi_house/brawler_green.mw3
new file mode 100644
index 000000000000..96c93099f306
Binary files /dev/null and b/worlds/smw/data/palettes/level/yoshi_house/brawler_green.mw3 differ
diff --git a/worlds/smw/data/palettes/level/yoshi_house/choco.mw3 b/worlds/smw/data/palettes/level/yoshi_house/choco.mw3
new file mode 100644
index 000000000000..cd4ebe585dd5
Binary files /dev/null and b/worlds/smw/data/palettes/level/yoshi_house/choco.mw3 differ
diff --git a/worlds/smw/data/palettes/level/yoshi_house/crimson.mw3 b/worlds/smw/data/palettes/level/yoshi_house/crimson.mw3
new file mode 100644
index 000000000000..e334904a372b
Binary files /dev/null and b/worlds/smw/data/palettes/level/yoshi_house/crimson.mw3 differ
diff --git a/worlds/smw/data/palettes/level/yoshi_house/miku.mw3 b/worlds/smw/data/palettes/level/yoshi_house/miku.mw3
new file mode 100644
index 000000000000..5d3b9e81144a
Binary files /dev/null and b/worlds/smw/data/palettes/level/yoshi_house/miku.mw3 differ
diff --git a/worlds/smw/data/palettes/level/yoshi_house/mogumogu.mw3 b/worlds/smw/data/palettes/level/yoshi_house/mogumogu.mw3
new file mode 100644
index 000000000000..cf76396c8fb0
Binary files /dev/null and b/worlds/smw/data/palettes/level/yoshi_house/mogumogu.mw3 differ
diff --git a/worlds/smw/data/palettes/level/yoshi_house/monocromo.mw3 b/worlds/smw/data/palettes/level/yoshi_house/monocromo.mw3
new file mode 100644
index 000000000000..8e1529f51498
Binary files /dev/null and b/worlds/smw/data/palettes/level/yoshi_house/monocromo.mw3 differ
diff --git a/worlds/smw/data/palettes/level/yoshi_house/neon.mw3 b/worlds/smw/data/palettes/level/yoshi_house/neon.mw3
new file mode 100644
index 000000000000..d3e183a770fd
Binary files /dev/null and b/worlds/smw/data/palettes/level/yoshi_house/neon.mw3 differ
diff --git a/worlds/smw/data/palettes/level/yoshi_house/nieve.mw3 b/worlds/smw/data/palettes/level/yoshi_house/nieve.mw3
new file mode 100644
index 000000000000..07db0b1339dd
Binary files /dev/null and b/worlds/smw/data/palettes/level/yoshi_house/nieve.mw3 differ
diff --git a/worlds/smw/data/palettes/level/yoshi_house/night.mw3 b/worlds/smw/data/palettes/level/yoshi_house/night.mw3
new file mode 100644
index 000000000000..7cc0122339b0
Binary files /dev/null and b/worlds/smw/data/palettes/level/yoshi_house/night.mw3 differ
diff --git a/worlds/smw/data/palettes/level/yoshi_house/nocturno.mw3 b/worlds/smw/data/palettes/level/yoshi_house/nocturno.mw3
new file mode 100644
index 000000000000..de764ef9e823
Binary files /dev/null and b/worlds/smw/data/palettes/level/yoshi_house/nocturno.mw3 differ
diff --git a/worlds/smw/data/palettes/level/yoshi_house/original.mw3 b/worlds/smw/data/palettes/level/yoshi_house/original.mw3
new file mode 100644
index 000000000000..051d266fbd86
Binary files /dev/null and b/worlds/smw/data/palettes/level/yoshi_house/original.mw3 differ
diff --git a/worlds/smw/data/palettes/level/yoshi_house/sakura.mw3 b/worlds/smw/data/palettes/level/yoshi_house/sakura.mw3
new file mode 100644
index 000000000000..d099ba935312
Binary files /dev/null and b/worlds/smw/data/palettes/level/yoshi_house/sakura.mw3 differ
diff --git a/worlds/smw/data/palettes/level/yoshi_house/snow.mw3 b/worlds/smw/data/palettes/level/yoshi_house/snow.mw3
new file mode 100644
index 000000000000..4fea26a690b2
Binary files /dev/null and b/worlds/smw/data/palettes/level/yoshi_house/snow.mw3 differ
diff --git a/worlds/smw/data/palettes/level/yoshi_house/strong_sun.mw3 b/worlds/smw/data/palettes/level/yoshi_house/strong_sun.mw3
new file mode 100644
index 000000000000..7c671aa368ed
Binary files /dev/null and b/worlds/smw/data/palettes/level/yoshi_house/strong_sun.mw3 differ
diff --git a/worlds/smw/data/palettes/level/yoshi_house/sunsetish_grass_hills.mw3 b/worlds/smw/data/palettes/level/yoshi_house/sunsetish_grass_hills.mw3
new file mode 100644
index 000000000000..c51bdfc6a7d3
Binary files /dev/null and b/worlds/smw/data/palettes/level/yoshi_house/sunsetish_grass_hills.mw3 differ
diff --git a/worlds/smw/data/palettes/map/forest/atardecer.mw3 b/worlds/smw/data/palettes/map/forest/atardecer.mw3
new file mode 100644
index 000000000000..5ce855399b98
Binary files /dev/null and b/worlds/smw/data/palettes/map/forest/atardecer.mw3 differ
diff --git a/worlds/smw/data/palettes/map/forest/burnt_forest.mw3 b/worlds/smw/data/palettes/map/forest/burnt_forest.mw3
new file mode 100644
index 000000000000..58b0648f18e3
Binary files /dev/null and b/worlds/smw/data/palettes/map/forest/burnt_forest.mw3 differ
diff --git a/worlds/smw/data/palettes/map/forest/dark_forest.mw3 b/worlds/smw/data/palettes/map/forest/dark_forest.mw3
new file mode 100644
index 000000000000..90b9b18809bc
Binary files /dev/null and b/worlds/smw/data/palettes/map/forest/dark_forest.mw3 differ
diff --git a/worlds/smw/data/palettes/map/forest/halloween.mw3 b/worlds/smw/data/palettes/map/forest/halloween.mw3
new file mode 100644
index 000000000000..c3899751bf31
Binary files /dev/null and b/worlds/smw/data/palettes/map/forest/halloween.mw3 differ
diff --git a/worlds/smw/data/palettes/map/forest/ice_forest.mw3 b/worlds/smw/data/palettes/map/forest/ice_forest.mw3
new file mode 100644
index 000000000000..32d62829bd5c
Binary files /dev/null and b/worlds/smw/data/palettes/map/forest/ice_forest.mw3 differ
diff --git a/worlds/smw/data/palettes/map/forest/lost_woods.mw3 b/worlds/smw/data/palettes/map/forest/lost_woods.mw3
new file mode 100644
index 000000000000..ba9486b627ba
Binary files /dev/null and b/worlds/smw/data/palettes/map/forest/lost_woods.mw3 differ
diff --git a/worlds/smw/data/palettes/map/forest/mono.mw3 b/worlds/smw/data/palettes/map/forest/mono.mw3
new file mode 100644
index 000000000000..32bce2c463ac
Binary files /dev/null and b/worlds/smw/data/palettes/map/forest/mono.mw3 differ
diff --git a/worlds/smw/data/palettes/map/forest/original.mw3 b/worlds/smw/data/palettes/map/forest/original.mw3
new file mode 100644
index 000000000000..766522e0651d
Binary files /dev/null and b/worlds/smw/data/palettes/map/forest/original.mw3 differ
diff --git a/worlds/smw/data/palettes/map/forest/original_special.mw3 b/worlds/smw/data/palettes/map/forest/original_special.mw3
new file mode 100644
index 000000000000..57fcf7dea59b
Binary files /dev/null and b/worlds/smw/data/palettes/map/forest/original_special.mw3 differ
diff --git a/worlds/smw/data/palettes/map/forest/sepia.mw3 b/worlds/smw/data/palettes/map/forest/sepia.mw3
new file mode 100644
index 000000000000..b265f978060d
Binary files /dev/null and b/worlds/smw/data/palettes/map/forest/sepia.mw3 differ
diff --git a/worlds/smw/data/palettes/map/forest/snow_day.mw3 b/worlds/smw/data/palettes/map/forest/snow_day.mw3
new file mode 100644
index 000000000000..c76540325bae
Binary files /dev/null and b/worlds/smw/data/palettes/map/forest/snow_day.mw3 differ
diff --git a/worlds/smw/data/palettes/map/main/atardecer.mw3 b/worlds/smw/data/palettes/map/main/atardecer.mw3
new file mode 100644
index 000000000000..5252db002dfe
Binary files /dev/null and b/worlds/smw/data/palettes/map/main/atardecer.mw3 differ
diff --git a/worlds/smw/data/palettes/map/main/brawler.mw3 b/worlds/smw/data/palettes/map/main/brawler.mw3
new file mode 100644
index 000000000000..731696fcfc82
Binary files /dev/null and b/worlds/smw/data/palettes/map/main/brawler.mw3 differ
diff --git a/worlds/smw/data/palettes/map/main/cake_frosting.mw3 b/worlds/smw/data/palettes/map/main/cake_frosting.mw3
new file mode 100644
index 000000000000..aec0fd7e40c9
Binary files /dev/null and b/worlds/smw/data/palettes/map/main/cake_frosting.mw3 differ
diff --git a/worlds/smw/data/palettes/map/main/invertido.mw3 b/worlds/smw/data/palettes/map/main/invertido.mw3
new file mode 100644
index 000000000000..79a7147acd17
Binary files /dev/null and b/worlds/smw/data/palettes/map/main/invertido.mw3 differ
diff --git a/worlds/smw/data/palettes/map/main/mono.mw3 b/worlds/smw/data/palettes/map/main/mono.mw3
new file mode 100644
index 000000000000..d4d0f72d2955
Binary files /dev/null and b/worlds/smw/data/palettes/map/main/mono.mw3 differ
diff --git a/worlds/smw/data/palettes/map/main/morning.mw3 b/worlds/smw/data/palettes/map/main/morning.mw3
new file mode 100644
index 000000000000..b2fe88e2cf89
Binary files /dev/null and b/worlds/smw/data/palettes/map/main/morning.mw3 differ
diff --git a/worlds/smw/data/palettes/map/main/night.mw3 b/worlds/smw/data/palettes/map/main/night.mw3
new file mode 100644
index 000000000000..a5e84ec80c00
Binary files /dev/null and b/worlds/smw/data/palettes/map/main/night.mw3 differ
diff --git a/worlds/smw/data/palettes/map/main/night_time.mw3 b/worlds/smw/data/palettes/map/main/night_time.mw3
new file mode 100644
index 000000000000..13a5af30c2e5
Binary files /dev/null and b/worlds/smw/data/palettes/map/main/night_time.mw3 differ
diff --git a/worlds/smw/data/palettes/map/main/original.mw3 b/worlds/smw/data/palettes/map/main/original.mw3
new file mode 100644
index 000000000000..cb655d882e71
Binary files /dev/null and b/worlds/smw/data/palettes/map/main/original.mw3 differ
diff --git a/worlds/smw/data/palettes/map/main/original_special.mw3 b/worlds/smw/data/palettes/map/main/original_special.mw3
new file mode 100644
index 000000000000..5c1e63e8fcd3
Binary files /dev/null and b/worlds/smw/data/palettes/map/main/original_special.mw3 differ
diff --git a/worlds/smw/data/palettes/map/main/sepia.mw3 b/worlds/smw/data/palettes/map/main/sepia.mw3
new file mode 100644
index 000000000000..3a6ece7743e9
Binary files /dev/null and b/worlds/smw/data/palettes/map/main/sepia.mw3 differ
diff --git a/worlds/smw/data/palettes/map/main/snow_day.mw3 b/worlds/smw/data/palettes/map/main/snow_day.mw3
new file mode 100644
index 000000000000..1ad307f078dc
Binary files /dev/null and b/worlds/smw/data/palettes/map/main/snow_day.mw3 differ
diff --git a/worlds/smw/data/palettes/map/palettes.json b/worlds/smw/data/palettes/map/palettes.json
new file mode 100644
index 000000000000..a926418cf934
--- /dev/null
+++ b/worlds/smw/data/palettes/map/palettes.json
@@ -0,0 +1,92 @@
+{
+ "main": [
+ "atardecer.mw3",
+ "brawler.mw3",
+ "cake_frosting.mw3",
+ "invertido.mw3",
+ "mono.mw3",
+ "morning.mw3",
+ "night.mw3",
+ "night_time.mw3",
+ "original.mw3",
+ "original_special.mw3",
+ "sepia.mw3",
+ "snow_day.mw3"
+ ],
+ "yoshi": [
+ "atardecer.mw3",
+ "gum.mw3",
+ "lava_island.mw3",
+ "mono.mw3",
+ "original.mw3",
+ "original_special.mw3",
+ "sepia.mw3",
+ "snow_day.mw3",
+ "sunset.mw3",
+ "tritanopia.mw3",
+ "yochis_ailand.mw3"
+ ],
+ "vanilla": [
+ "aqua_marine.mw3",
+ "dark cave.mw3",
+ "DOMO.mw3",
+ "fire cave.mw3",
+ "gold_mine.mw3",
+ "invertido.mw3",
+ "mono.mw3",
+ "original.mw3",
+ "original_special.mw3",
+ "purple.mw3",
+ "sepia.mw3",
+ "witches_cauldron.mw3"
+ ],
+ "forest": [
+ "atardecer.mw3",
+ "burnt_forest.mw3",
+ "dark_forest.mw3",
+ "halloween.mw3",
+ "ice_forest.mw3",
+ "lost_woods.mw3",
+ "mono.mw3",
+ "original.mw3",
+ "original_special.mw3",
+ "sepia.mw3",
+ "snow_day.mw3"
+ ],
+ "valley": [
+ "bowser.mw3",
+ "castle_colors.mw3",
+ "dark cave.mw3",
+ "dream_world.mw3",
+ "fire cave.mw3",
+ "invertido.mw3",
+ "mono.mw3",
+ "orange.mw3",
+ "original.mw3",
+ "original_special.mw3",
+ "purple_blue.mw3",
+ "sepia.mw3",
+ "snow.mw3",
+ "Tamaulipas.mw3"
+ ],
+ "special": [
+ "black_out.mw3",
+ "blood_star.mw3",
+ "brawler.mw3",
+ "green.mw3",
+ "light_pollution_map.mw3",
+ "original.mw3",
+ "purple.mw3",
+ "white_special.mw3"
+ ],
+ "star": [
+ "blood_moon.mw3",
+ "mono.mw3",
+ "mountain_top.mw3",
+ "original.mw3",
+ "original_special.mw3",
+ "pink_star.mw3",
+ "sepia.mw3",
+ "yellow_star.mw3"
+ ]
+}
\ No newline at end of file
diff --git a/worlds/smw/data/palettes/map/special/black_out.mw3 b/worlds/smw/data/palettes/map/special/black_out.mw3
new file mode 100644
index 000000000000..52d4e6d4a40f
Binary files /dev/null and b/worlds/smw/data/palettes/map/special/black_out.mw3 differ
diff --git a/worlds/smw/data/palettes/map/special/blood_star.mw3 b/worlds/smw/data/palettes/map/special/blood_star.mw3
new file mode 100644
index 000000000000..bc778b202b99
Binary files /dev/null and b/worlds/smw/data/palettes/map/special/blood_star.mw3 differ
diff --git a/worlds/smw/data/palettes/map/special/brawler.mw3 b/worlds/smw/data/palettes/map/special/brawler.mw3
new file mode 100644
index 000000000000..595edb04935b
Binary files /dev/null and b/worlds/smw/data/palettes/map/special/brawler.mw3 differ
diff --git a/worlds/smw/data/palettes/map/special/green.mw3 b/worlds/smw/data/palettes/map/special/green.mw3
new file mode 100644
index 000000000000..2bdb5fd4e075
Binary files /dev/null and b/worlds/smw/data/palettes/map/special/green.mw3 differ
diff --git a/worlds/smw/data/palettes/map/special/light_pollution_map.mw3 b/worlds/smw/data/palettes/map/special/light_pollution_map.mw3
new file mode 100644
index 000000000000..a1bc6bbcf9a1
Binary files /dev/null and b/worlds/smw/data/palettes/map/special/light_pollution_map.mw3 differ
diff --git a/worlds/smw/data/palettes/map/special/original.mw3 b/worlds/smw/data/palettes/map/special/original.mw3
new file mode 100644
index 000000000000..0a75a99a0400
Binary files /dev/null and b/worlds/smw/data/palettes/map/special/original.mw3 differ
diff --git a/worlds/smw/data/palettes/map/special/purple.mw3 b/worlds/smw/data/palettes/map/special/purple.mw3
new file mode 100644
index 000000000000..122b5c785af4
Binary files /dev/null and b/worlds/smw/data/palettes/map/special/purple.mw3 differ
diff --git a/worlds/smw/data/palettes/map/special/white_special.mw3 b/worlds/smw/data/palettes/map/special/white_special.mw3
new file mode 100644
index 000000000000..e4d2613aa964
Binary files /dev/null and b/worlds/smw/data/palettes/map/special/white_special.mw3 differ
diff --git a/worlds/smw/data/palettes/map/star/blood_moon.mw3 b/worlds/smw/data/palettes/map/star/blood_moon.mw3
new file mode 100644
index 000000000000..42f155ef33e2
Binary files /dev/null and b/worlds/smw/data/palettes/map/star/blood_moon.mw3 differ
diff --git a/worlds/smw/data/palettes/map/star/mono.mw3 b/worlds/smw/data/palettes/map/star/mono.mw3
new file mode 100644
index 000000000000..15d33bdf3a23
Binary files /dev/null and b/worlds/smw/data/palettes/map/star/mono.mw3 differ
diff --git a/worlds/smw/data/palettes/map/star/mountain_top.mw3 b/worlds/smw/data/palettes/map/star/mountain_top.mw3
new file mode 100644
index 000000000000..d2b96b0e3dd2
Binary files /dev/null and b/worlds/smw/data/palettes/map/star/mountain_top.mw3 differ
diff --git a/worlds/smw/data/palettes/map/star/original.mw3 b/worlds/smw/data/palettes/map/star/original.mw3
new file mode 100644
index 000000000000..2107a5555eba
Binary files /dev/null and b/worlds/smw/data/palettes/map/star/original.mw3 differ
diff --git a/worlds/smw/data/palettes/map/star/original_special.mw3 b/worlds/smw/data/palettes/map/star/original_special.mw3
new file mode 100644
index 000000000000..d2bd439c97b5
Binary files /dev/null and b/worlds/smw/data/palettes/map/star/original_special.mw3 differ
diff --git a/worlds/smw/data/palettes/map/star/pink_star.mw3 b/worlds/smw/data/palettes/map/star/pink_star.mw3
new file mode 100644
index 000000000000..55f68ecf6ce2
Binary files /dev/null and b/worlds/smw/data/palettes/map/star/pink_star.mw3 differ
diff --git a/worlds/smw/data/palettes/map/star/sepia.mw3 b/worlds/smw/data/palettes/map/star/sepia.mw3
new file mode 100644
index 000000000000..4c6a5f3c1df2
Binary files /dev/null and b/worlds/smw/data/palettes/map/star/sepia.mw3 differ
diff --git a/worlds/smw/data/palettes/map/star/yellow_star.mw3 b/worlds/smw/data/palettes/map/star/yellow_star.mw3
new file mode 100644
index 000000000000..96028903959a
Binary files /dev/null and b/worlds/smw/data/palettes/map/star/yellow_star.mw3 differ
diff --git a/worlds/smw/data/palettes/map/valley/Tamaulipas.mw3 b/worlds/smw/data/palettes/map/valley/Tamaulipas.mw3
new file mode 100644
index 000000000000..0cb18cc2fd3e
Binary files /dev/null and b/worlds/smw/data/palettes/map/valley/Tamaulipas.mw3 differ
diff --git a/worlds/smw/data/palettes/map/valley/bowser.mw3 b/worlds/smw/data/palettes/map/valley/bowser.mw3
new file mode 100644
index 000000000000..f3d6c23aa060
Binary files /dev/null and b/worlds/smw/data/palettes/map/valley/bowser.mw3 differ
diff --git a/worlds/smw/data/palettes/map/valley/castle_colors.mw3 b/worlds/smw/data/palettes/map/valley/castle_colors.mw3
new file mode 100644
index 000000000000..7e245cc64ecb
Binary files /dev/null and b/worlds/smw/data/palettes/map/valley/castle_colors.mw3 differ
diff --git a/worlds/smw/data/palettes/map/valley/dark cave.mw3 b/worlds/smw/data/palettes/map/valley/dark cave.mw3
new file mode 100644
index 000000000000..2b6c0f57cc61
Binary files /dev/null and b/worlds/smw/data/palettes/map/valley/dark cave.mw3 differ
diff --git a/worlds/smw/data/palettes/map/valley/dream_world.mw3 b/worlds/smw/data/palettes/map/valley/dream_world.mw3
new file mode 100644
index 000000000000..bcf8d9514213
Binary files /dev/null and b/worlds/smw/data/palettes/map/valley/dream_world.mw3 differ
diff --git a/worlds/smw/data/palettes/map/valley/fire cave.mw3 b/worlds/smw/data/palettes/map/valley/fire cave.mw3
new file mode 100644
index 000000000000..2980210dd233
Binary files /dev/null and b/worlds/smw/data/palettes/map/valley/fire cave.mw3 differ
diff --git a/worlds/smw/data/palettes/map/valley/invertido.mw3 b/worlds/smw/data/palettes/map/valley/invertido.mw3
new file mode 100644
index 000000000000..37cf2e9f50d2
Binary files /dev/null and b/worlds/smw/data/palettes/map/valley/invertido.mw3 differ
diff --git a/worlds/smw/data/palettes/map/valley/mono.mw3 b/worlds/smw/data/palettes/map/valley/mono.mw3
new file mode 100644
index 000000000000..f96409bebb0f
Binary files /dev/null and b/worlds/smw/data/palettes/map/valley/mono.mw3 differ
diff --git a/worlds/smw/data/palettes/map/valley/orange.mw3 b/worlds/smw/data/palettes/map/valley/orange.mw3
new file mode 100644
index 000000000000..c9f6ac2ff2e9
Binary files /dev/null and b/worlds/smw/data/palettes/map/valley/orange.mw3 differ
diff --git a/worlds/smw/data/palettes/map/valley/original.mw3 b/worlds/smw/data/palettes/map/valley/original.mw3
new file mode 100644
index 000000000000..c165e81b818b
Binary files /dev/null and b/worlds/smw/data/palettes/map/valley/original.mw3 differ
diff --git a/worlds/smw/data/palettes/map/valley/original_special.mw3 b/worlds/smw/data/palettes/map/valley/original_special.mw3
new file mode 100644
index 000000000000..a4a0acda9ed6
Binary files /dev/null and b/worlds/smw/data/palettes/map/valley/original_special.mw3 differ
diff --git a/worlds/smw/data/palettes/map/valley/purple_blue.mw3 b/worlds/smw/data/palettes/map/valley/purple_blue.mw3
new file mode 100644
index 000000000000..2342f0acd205
Binary files /dev/null and b/worlds/smw/data/palettes/map/valley/purple_blue.mw3 differ
diff --git a/worlds/smw/data/palettes/map/valley/sepia.mw3 b/worlds/smw/data/palettes/map/valley/sepia.mw3
new file mode 100644
index 000000000000..aa5aeb51d38b
Binary files /dev/null and b/worlds/smw/data/palettes/map/valley/sepia.mw3 differ
diff --git a/worlds/smw/data/palettes/map/valley/snow.mw3 b/worlds/smw/data/palettes/map/valley/snow.mw3
new file mode 100644
index 000000000000..185d0d42c77a
Binary files /dev/null and b/worlds/smw/data/palettes/map/valley/snow.mw3 differ
diff --git a/worlds/smw/data/palettes/map/vanilla/DOMO.mw3 b/worlds/smw/data/palettes/map/vanilla/DOMO.mw3
new file mode 100644
index 000000000000..595638077050
Binary files /dev/null and b/worlds/smw/data/palettes/map/vanilla/DOMO.mw3 differ
diff --git a/worlds/smw/data/palettes/map/vanilla/aqua_marine.mw3 b/worlds/smw/data/palettes/map/vanilla/aqua_marine.mw3
new file mode 100644
index 000000000000..b382964de02c
Binary files /dev/null and b/worlds/smw/data/palettes/map/vanilla/aqua_marine.mw3 differ
diff --git a/worlds/smw/data/palettes/map/vanilla/dark cave.mw3 b/worlds/smw/data/palettes/map/vanilla/dark cave.mw3
new file mode 100644
index 000000000000..2b6c0f57cc61
Binary files /dev/null and b/worlds/smw/data/palettes/map/vanilla/dark cave.mw3 differ
diff --git a/worlds/smw/data/palettes/map/vanilla/fire cave.mw3 b/worlds/smw/data/palettes/map/vanilla/fire cave.mw3
new file mode 100644
index 000000000000..2980210dd233
Binary files /dev/null and b/worlds/smw/data/palettes/map/vanilla/fire cave.mw3 differ
diff --git a/worlds/smw/data/palettes/map/vanilla/gold_mine.mw3 b/worlds/smw/data/palettes/map/vanilla/gold_mine.mw3
new file mode 100644
index 000000000000..ad5460a75e50
Binary files /dev/null and b/worlds/smw/data/palettes/map/vanilla/gold_mine.mw3 differ
diff --git a/worlds/smw/data/palettes/map/vanilla/invertido.mw3 b/worlds/smw/data/palettes/map/vanilla/invertido.mw3
new file mode 100644
index 000000000000..37cf2e9f50d2
Binary files /dev/null and b/worlds/smw/data/palettes/map/vanilla/invertido.mw3 differ
diff --git a/worlds/smw/data/palettes/map/vanilla/mono.mw3 b/worlds/smw/data/palettes/map/vanilla/mono.mw3
new file mode 100644
index 000000000000..f96409bebb0f
Binary files /dev/null and b/worlds/smw/data/palettes/map/vanilla/mono.mw3 differ
diff --git a/worlds/smw/data/palettes/map/vanilla/original.mw3 b/worlds/smw/data/palettes/map/vanilla/original.mw3
new file mode 100644
index 000000000000..c165e81b818b
Binary files /dev/null and b/worlds/smw/data/palettes/map/vanilla/original.mw3 differ
diff --git a/worlds/smw/data/palettes/map/vanilla/original_special.mw3 b/worlds/smw/data/palettes/map/vanilla/original_special.mw3
new file mode 100644
index 000000000000..a4a0acda9ed6
Binary files /dev/null and b/worlds/smw/data/palettes/map/vanilla/original_special.mw3 differ
diff --git a/worlds/smw/data/palettes/map/vanilla/purple.mw3 b/worlds/smw/data/palettes/map/vanilla/purple.mw3
new file mode 100644
index 000000000000..db0008bca7cd
Binary files /dev/null and b/worlds/smw/data/palettes/map/vanilla/purple.mw3 differ
diff --git a/worlds/smw/data/palettes/map/vanilla/sepia.mw3 b/worlds/smw/data/palettes/map/vanilla/sepia.mw3
new file mode 100644
index 000000000000..aa5aeb51d38b
Binary files /dev/null and b/worlds/smw/data/palettes/map/vanilla/sepia.mw3 differ
diff --git a/worlds/smw/data/palettes/map/vanilla/witches_cauldron.mw3 b/worlds/smw/data/palettes/map/vanilla/witches_cauldron.mw3
new file mode 100644
index 000000000000..ef6a81e5d49d
Binary files /dev/null and b/worlds/smw/data/palettes/map/vanilla/witches_cauldron.mw3 differ
diff --git a/worlds/smw/data/palettes/map/yoshi/atardecer.mw3 b/worlds/smw/data/palettes/map/yoshi/atardecer.mw3
new file mode 100644
index 000000000000..a75c898cee98
Binary files /dev/null and b/worlds/smw/data/palettes/map/yoshi/atardecer.mw3 differ
diff --git a/worlds/smw/data/palettes/map/yoshi/gum.mw3 b/worlds/smw/data/palettes/map/yoshi/gum.mw3
new file mode 100644
index 000000000000..cfde2f53bba4
Binary files /dev/null and b/worlds/smw/data/palettes/map/yoshi/gum.mw3 differ
diff --git a/worlds/smw/data/palettes/map/yoshi/lava_island.mw3 b/worlds/smw/data/palettes/map/yoshi/lava_island.mw3
new file mode 100644
index 000000000000..570bdee3aa9c
Binary files /dev/null and b/worlds/smw/data/palettes/map/yoshi/lava_island.mw3 differ
diff --git a/worlds/smw/data/palettes/map/yoshi/mono.mw3 b/worlds/smw/data/palettes/map/yoshi/mono.mw3
new file mode 100644
index 000000000000..62c9761b4673
Binary files /dev/null and b/worlds/smw/data/palettes/map/yoshi/mono.mw3 differ
diff --git a/worlds/smw/data/palettes/map/yoshi/original.mw3 b/worlds/smw/data/palettes/map/yoshi/original.mw3
new file mode 100644
index 000000000000..eb9451b1fe5c
Binary files /dev/null and b/worlds/smw/data/palettes/map/yoshi/original.mw3 differ
diff --git a/worlds/smw/data/palettes/map/yoshi/original_special.mw3 b/worlds/smw/data/palettes/map/yoshi/original_special.mw3
new file mode 100644
index 000000000000..269b45db6171
Binary files /dev/null and b/worlds/smw/data/palettes/map/yoshi/original_special.mw3 differ
diff --git a/worlds/smw/data/palettes/map/yoshi/sepia.mw3 b/worlds/smw/data/palettes/map/yoshi/sepia.mw3
new file mode 100644
index 000000000000..3cbf6b0390bf
Binary files /dev/null and b/worlds/smw/data/palettes/map/yoshi/sepia.mw3 differ
diff --git a/worlds/smw/data/palettes/map/yoshi/snow_day.mw3 b/worlds/smw/data/palettes/map/yoshi/snow_day.mw3
new file mode 100644
index 000000000000..464b32bad17c
Binary files /dev/null and b/worlds/smw/data/palettes/map/yoshi/snow_day.mw3 differ
diff --git a/worlds/smw/data/palettes/map/yoshi/sunset.mw3 b/worlds/smw/data/palettes/map/yoshi/sunset.mw3
new file mode 100644
index 000000000000..9477a08cb8e6
Binary files /dev/null and b/worlds/smw/data/palettes/map/yoshi/sunset.mw3 differ
diff --git a/worlds/smw/data/palettes/map/yoshi/tritanopia.mw3 b/worlds/smw/data/palettes/map/yoshi/tritanopia.mw3
new file mode 100644
index 000000000000..c90b7f9af0d2
Binary files /dev/null and b/worlds/smw/data/palettes/map/yoshi/tritanopia.mw3 differ
diff --git a/worlds/smw/data/palettes/map/yoshi/yochis_ailand.mw3 b/worlds/smw/data/palettes/map/yoshi/yochis_ailand.mw3
new file mode 100644
index 000000000000..3e1de6680d02
Binary files /dev/null and b/worlds/smw/data/palettes/map/yoshi/yochis_ailand.mw3 differ
diff --git a/worlds/smw/docs/en_Super Mario World.md b/worlds/smw/docs/en_Super Mario World.md
index 87a96e558b65..f7a12839df4d 100644
--- a/worlds/smw/docs/en_Super Mario World.md
+++ b/worlds/smw/docs/en_Super Mario World.md
@@ -1,8 +1,8 @@
# Super Mario World
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a config file.
+The [player options page for this game](../player-options) contains all the options you need to configure and export a config file.
## What does randomization do to this game?
@@ -25,10 +25,16 @@ There are two goals which can be chosen:
## What items and locations get shuffled?
-Each unique level exit awards a location check. Optionally, collecting five Dragon Coins in each level can also award a location check.
+Each unique level exit awards a location check. Additionally, the following in-level actions can be set to award a location check:
+- Collecting Five Dragon Coins
+- Collecting 3-Up Moons
+- Activating Bonus Blocks
+- Receiving Hidden 1-Ups
+- Hitting Blocks containing coins or items
+
Mario's various abilities and powerups as described above are placed into the item pool.
If the player is playing Yoshi Egg Hunt, a certain number of Yoshi Eggs will be placed into the item pool.
-Any additional items that are needed to fill out the item pool with be 1-Up Mushrooms.
+Any additional items that are needed to fill out the item pool will be 1-Up Mushrooms, bundles of coins, or, if enabled, various trap items.
## Which items can be in another player's world?
diff --git a/worlds/smw/docs/setup_en.md b/worlds/smw/docs/setup_en.md
index c8f408d6e256..825f0954c8f1 100644
--- a/worlds/smw/docs/setup_en.md
+++ b/worlds/smw/docs/setup_en.md
@@ -44,8 +44,8 @@ guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
### Where do I get a config file?
-The Player Settings page on the website allows you to configure your personal settings and export a config file from
-them. Player settings page: [Super Mario World Player Settings Page](/games/Super%20Mario%20World/player-settings)
+The Player Options page on the website allows you to configure your personal options and export a config file from
+them. Player options page: [Super Mario World Player Options Page](/games/Super%20Mario%20World/player-options)
### Verifying your config file
diff --git a/worlds/smz3/Client.py b/worlds/smz3/Client.py
index b07aa850c31d..0a248aa5d3f2 100644
--- a/worlds/smz3/Client.py
+++ b/worlds/smz3/Client.py
@@ -32,6 +32,7 @@
class SMZ3SNIClient(SNIClient):
game = "SMZ3"
+ patch_suffix = ".apsmz3"
async def validate_rom(self, ctx):
from SNIClient import snes_buffered_write, snes_flush_writes, snes_read
diff --git a/worlds/smz3/docs/en_SMZ3.md b/worlds/smz3/docs/en_SMZ3.md
index f0302d12f3a1..2116432ea7a1 100644
--- a/worlds/smz3/docs/en_SMZ3.md
+++ b/worlds/smz3/docs/en_SMZ3.md
@@ -1,8 +1,8 @@
# SMZ3
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
diff --git a/worlds/smz3/docs/multiworld_en.md b/worlds/smz3/docs/multiworld_en.md
index fadd55028fe1..38c410faee02 100644
--- a/worlds/smz3/docs/multiworld_en.md
+++ b/worlds/smz3/docs/multiworld_en.md
@@ -2,16 +2,18 @@
## Required Software
-- One of the client programs:
- - [SNIClient](https://github.com/ArchipelagoMW/Archipelago/releases), included with the main
- Archipelago install.
-- Hardware or software capable of loading and playing SNES ROM files
- - An emulator capable of connecting to SNI such as:
- - snes9x-rr from: [snes9x rr](https://github.com/gocha/snes9x-rr/releases),
- - BizHawk from: [TASVideos](https://tasvideos.org/BizHawk), or
- - RetroArch 1.10.3 or newer from: [RetroArch Website](https://retroarch.com?page=platforms). Or,
- - An SD2SNES, FXPak Pro ([FXPak Pro Store Page](https://krikzz.com/store/home/54-fxpak-pro.html)), or other
- compatible hardware
+- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases).
+- [SNI](https://github.com/alttpo/sni/releases). This is automatically included with your Archipelago installation above.
+- SNI is not compatible with (Q)Usb2Snes.
+- Hardware or software capable of loading and playing SNES ROM files, including:
+ - An emulator capable of connecting to SNI
+ ([snes9x-nwa](https://github.com/Skarsnik/snes9x-emunwa/releases), [snes9x-rr](https://github.com/gocha/snes9x-rr/releases),
+ [BSNES-plus](https://github.com/black-sliver/bsnes-plus),
+ [BizHawk](http://tasvideos.org/BizHawk.html), or
+ [RetroArch](https://retroarch.com?page=platforms) 1.10.1 or newer)
+ - An SD2SNES, [FXPak Pro](https://krikzz.com/store/home/54-fxpak-pro.html), or other compatible hardware. **note:
+ modded SNES minis are currently not supported by SNI. Some users have claimed success with QUsb2Snes for this system,
+ but it is not supported.**
- Your legally obtained Super Metroid ROM file, probably named `Super Metroid (Japan, USA).sfc` and
Your Japanese Zelda3 v1.0 ROM file, probably named `Zelda no Densetsu - Kamigami no Triforce (Japan).sfc`
@@ -41,8 +43,8 @@ guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
### Where do I get a config file?
-The Player Settings page on the website allows you to configure your personal settings and export a config file from
-them. Player settings page: [SMZ3 Player Settings Page](/games/SMZ3/player-settings)
+The Player Options page on the website allows you to configure your personal options and export a config file from
+them. Player options page: [SMZ3 Player Options Page](/games/SMZ3/player-options)
### Verifying your config file
@@ -51,8 +53,8 @@ validator page: [YAML Validation page](/check)
## Generating a Single-Player Game
-1. Navigate to the Player Settings page, configure your options, and click the "Generate Game" button.
- - Player Settings page: [SMZ3 Player Settings Page](/games/SMZ3/player-settings)
+1. Navigate to the Player Options page, configure your options, and click the "Generate Game" button.
+ - Player Options page: [SMZ3 Player Options Page](/games/SMZ3/player-options)
2. You will be presented with a "Seed Info" page.
3. Click the "Create New Room" link.
4. You will be presented with a server page, from which you can download your patch file.
@@ -78,6 +80,11 @@ client, and will also create your ROM in the same place as your patch file.
When the client launched automatically, SNI should have also automatically launched in the background. If this is its
first time launching, you may be prompted to allow it to communicate through the Windows Firewall.
+#### snes9x-nwa
+
+1. Click on the Network Menu and check **Enable Emu Network Control**
+2. Load your ROM file if it hasn't already been loaded.
+
##### snes9x-rr
1. Load your ROM file if it hasn't already been loaded.
@@ -89,6 +96,11 @@ first time launching, you may be prompted to allow it to communicate through the
6. If you see an error while loading the script that states `socket.dll missing` or similar, navigate to the folder of
the lua you are using in your file explorer and copy the `socket.dll` to the base folder of your snes9x install.
+#### BSNES-Plus
+
+1. Load your ROM file if it hasn't already been loaded.
+2. The emulator should automatically connect while SNI is running.
+
##### BizHawk
1. Ensure you have the BSNES core loaded. This is done with the main menubar, under:
diff --git a/worlds/soe/Logic.py b/worlds/soe/Logic.py
deleted file mode 100644
index fe5339c955b9..000000000000
--- a/worlds/soe/Logic.py
+++ /dev/null
@@ -1,70 +0,0 @@
-from typing import Protocol, Set
-
-from BaseClasses import MultiWorld
-from worlds.AutoWorld import LogicMixin
-from . import pyevermizer
-from .Options import EnergyCore, OutOfBounds, SequenceBreaks
-
-# TODO: Options may preset certain progress steps (i.e. P_ROCK_SKIP), set in generate_early?
-
-# TODO: resolve/flatten/expand rules to get rid of recursion below where possible
-# Logic.rules are all rules including locations, excluding those with no progress (i.e. locations that only drop items)
-rules = [rule for rule in pyevermizer.get_logic() if len(rule.provides) > 0]
-# Logic.items are all items and extra items excluding non-progression items and duplicates
-item_names: Set[str] = set()
-items = [item for item in filter(lambda item: item.progression, pyevermizer.get_items() + pyevermizer.get_extra_items())
- if item.name not in item_names and not item_names.add(item.name)]
-
-
-class LogicProtocol(Protocol):
- def has(self, name: str, player: int) -> bool: ...
- def count(self, name: str, player: int) -> int: ...
- def soe_has(self, progress: int, world: MultiWorld, player: int, count: int) -> bool: ...
- def _soe_count(self, progress: int, world: MultiWorld, player: int, max_count: int) -> int: ...
-
-
-# when this module is loaded, this mixin will extend BaseClasses.CollectionState
-class SecretOfEvermoreLogic(LogicMixin):
- def _soe_count(self: LogicProtocol, progress: int, world: MultiWorld, player: int, max_count: int = 0) -> int:
- """
- Returns reached count of one of evermizer's progress steps based on collected items.
- i.e. returns 0-3 for P_DE based on items providing CHECK_BOSS,DIAMOND_EYE_DROP
- """
- n = 0
- for item in items:
- for pvd in item.provides:
- if pvd[1] == progress:
- if self.has(item.name, player):
- n += self.count(item.name, player) * pvd[0]
- if n >= max_count > 0:
- return n
- for rule in rules:
- for pvd in rule.provides:
- if pvd[1] == progress and pvd[0] > 0:
- has = True
- for req in rule.requires:
- if not self.soe_has(req[1], world, player, req[0]):
- has = False
- break
- if has:
- n += pvd[0]
- if n >= max_count > 0:
- return n
- return n
-
- def soe_has(self: LogicProtocol, progress: int, world: MultiWorld, player: int, count: int = 1) -> bool:
- """
- Returns True if count of one of evermizer's progress steps is reached based on collected items. i.e. 2 * P_DE
- """
- if progress == pyevermizer.P_ENERGY_CORE: # logic is shared between worlds, so we override in the call
- w = world.worlds[player]
- if w.energy_core == EnergyCore.option_fragments:
- progress = pyevermizer.P_CORE_FRAGMENT
- count = w.required_fragments
- elif progress == pyevermizer.P_ALLOW_OOB:
- if world.worlds[player].out_of_bounds == OutOfBounds.option_logic:
- return True
- elif progress == pyevermizer.P_ALLOW_SEQUENCE_BREAKS:
- if world.worlds[player].sequence_breaks == SequenceBreaks.option_logic:
- return True
- return self._soe_count(progress, world, player, count) >= count
diff --git a/worlds/soe/__init__.py b/worlds/soe/__init__.py
index d02a8d02ee97..dcca722ad1fe 100644
--- a/worlds/soe/__init__.py
+++ b/worlds/soe/__init__.py
@@ -4,18 +4,23 @@
import threading
import typing
+# from . import pyevermizer # as part of the source tree
+import pyevermizer # from package
+
import settings
+from BaseClasses import Item, ItemClassification, Location, LocationProgressType, Region, Tutorial
+from Utils import output_path
from worlds.AutoWorld import WebWorld, World
from worlds.generic.Rules import add_item_rule, set_rule
-from BaseClasses import Entrance, Item, ItemClassification, Location, LocationProgressType, Region, Tutorial
-from Utils import output_path
+from .logic import SoEPlayerLogic
+from .options import Difficulty, EnergyCore, Sniffamizer, SniffIngredients, SoEOptions
+from .patch import SoEDeltaPatch, get_base_rom_path
-import pyevermizer # from package
-# from . import pyevermizer # as part of the source tree
+if typing.TYPE_CHECKING:
+ from BaseClasses import MultiWorld, CollectionState
+
+__all__ = ["pyevermizer", "SoEWorld"]
-from . import Logic # load logic mixin
-from .Options import soe_options, Difficulty, EnergyCore, RequiredFragments, AvailableFragments
-from .Patch import SoEDeltaPatch, get_base_rom_path
"""
In evermizer:
@@ -24,17 +29,17 @@
For most items this is their vanilla location (i.e. CHECK_GOURD, number).
Items have `provides`, which give the actual progression
-instead of providing multiple events per item, we iterate through them in Logic.py
+instead of providing multiple events per item, we iterate through them in logic.py
e.g. Found any weapon
Locations have `requires` and `provides`.
Requirements have to be converted to (access) rules for AP
e.g. Chest locked behind having a weapon
-Provides could be events, but instead we iterate through the entire logic in Logic.py
+Provides could be events, but instead we iterate through the entire logic in logic.py
e.g. NPC available after fighting a Boss
Rules are special locations that don't have a physical location
-instead of implementing virtual locations and virtual items, we simply use them in Logic.py
+instead of implementing virtual locations and virtual items, we simply use them in logic.py
e.g. 2DEs+Wheel+Gauge = Rocket
Rules and Locations live on the same logic tree returned by pyevermizer.get_logic()
@@ -59,24 +64,32 @@
pyevermizer.CHECK_BOSS: _id_base + 50, # bosses 64050..6499
pyevermizer.CHECK_GOURD: _id_base + 100, # gourds 64100..64399
pyevermizer.CHECK_NPC: _id_base + 400, # npc 64400..64499
- # TODO: sniff 64500..64799
+ # blank 64500..64799
pyevermizer.CHECK_EXTRA: _id_base + 800, # extra items 64800..64899
pyevermizer.CHECK_TRAP: _id_base + 900, # trap 64900..64999
+ pyevermizer.CHECK_SNIFF: _id_base + 1000 # sniff 65000..65592
}
# cache native evermizer items and locations
_items = pyevermizer.get_items()
+_sniff_items = pyevermizer.get_sniff_items() # optional, not part of the default location pool
_traps = pyevermizer.get_traps()
_extras = pyevermizer.get_extra_items() # items that are not placed by default
_locations = pyevermizer.get_locations()
+_sniff_locations = pyevermizer.get_sniff_locations() # optional, not part of the default location pool
# fix up texts for AP
for _loc in _locations:
if _loc.type == pyevermizer.CHECK_GOURD:
- _loc.name = f'{_loc.name} #{_loc.index}'
+ _loc.name = f"{_loc.name} #{_loc.index}"
+for _loc in _sniff_locations:
+ if _loc.type == pyevermizer.CHECK_SNIFF:
+ _loc.name = f"{_loc.name} Sniff #{_loc.index}"
+del _loc
+
# item helpers
_ingredients = (
'Wax', 'Water', 'Vinegar', 'Root', 'Oil', 'Mushroom', 'Mud Pepper', 'Meteorite', 'Limestone', 'Iron',
- 'Gunpowder', 'Grease', 'Feather', 'Ethanol', 'Dry Ice', 'Crystal', 'Clay', 'Brimstone', 'Bone', 'Atlas Amulet',
+ 'Gunpowder', 'Grease', 'Feather', 'Ethanol', 'Dry Ice', 'Crystal', 'Clay', 'Brimstone', 'Bone', 'Atlas Medallion',
'Ash', 'Acorn'
)
_other_items = (
@@ -84,15 +97,15 @@
)
-def _match_item_name(item, substr: str) -> bool:
- sub = item.name.split(' ', 1)[1] if item.name[0].isdigit() else item.name
+def _match_item_name(item: pyevermizer.Item, substr: str) -> bool:
+ sub: str = item.name.split(' ', 1)[1] if item.name[0].isdigit() else item.name
return sub == substr or sub == substr+'s'
def _get_location_mapping() -> typing.Tuple[typing.Dict[str, int], typing.Dict[int, pyevermizer.Location]]:
name_to_id = {}
id_to_raw = {}
- for loc in _locations:
+ for loc in itertools.chain(_locations, _sniff_locations):
ap_id = _id_offset[loc.type] + loc.index
id_to_raw[ap_id] = loc
name_to_id[loc.name] = ap_id
@@ -103,7 +116,7 @@ def _get_location_mapping() -> typing.Tuple[typing.Dict[str, int], typing.Dict[i
def _get_item_mapping() -> typing.Tuple[typing.Dict[str, int], typing.Dict[int, pyevermizer.Item]]:
name_to_id = {}
id_to_raw = {}
- for item in itertools.chain(_items, _extras, _traps):
+ for item in itertools.chain(_items, _sniff_items, _extras, _traps):
if item.name in name_to_id:
continue
ap_id = _id_offset[item.type] + item.index
@@ -156,45 +169,36 @@ class RomFile(settings.SNESRomPath):
class SoEWorld(World):
"""
Secret of Evermore is a SNES action RPG. You learn alchemy spells, fight bosses and gather rocket parts to visit a
- space station where the final boss must be defeated.
+ space station where the final boss must be defeated.
"""
- game: str = "Secret of Evermore"
- option_definitions = soe_options
+ game: typing.ClassVar[str] = "Secret of Evermore"
+ options_dataclass = SoEOptions
+ options: SoEOptions
settings: typing.ClassVar[SoESettings]
topology_present = False
- data_version = 4
+ data_version = 5
web = SoEWebWorld()
- required_client_version = (0, 3, 5)
+ required_client_version = (0, 4, 4)
item_name_to_id, item_id_to_raw = _get_item_mapping()
location_name_to_id, location_id_to_raw = _get_location_mapping()
item_name_groups = _get_item_grouping()
- trap_types = [name[12:] for name in option_definitions if name.startswith('trap_chance_')]
-
+ logic: SoEPlayerLogic
evermizer_seed: int
connect_name: str
- energy_core: int
- sequence_breaks: int
- out_of_bounds: int
- available_fragments: int
- required_fragments: int
_halls_ne_chest_names: typing.List[str] = [loc.name for loc in _locations if 'Halls NE' in loc.name]
- def __init__(self, *args, **kwargs):
+ def __init__(self, multiworld: "MultiWorld", player: int):
self.connect_name_available_event = threading.Event()
- super(SoEWorld, self).__init__(*args, **kwargs)
+ super(SoEWorld, self).__init__(multiworld, player)
def generate_early(self) -> None:
- # store option values that change logic
- self.energy_core = self.multiworld.energy_core[self.player].value
- self.sequence_breaks = self.multiworld.sequence_breaks[self.player].value
- self.out_of_bounds = self.multiworld.out_of_bounds[self.player].value
- self.required_fragments = self.multiworld.required_fragments[self.player].value
- if self.required_fragments > self.multiworld.available_fragments[self.player].value:
- self.multiworld.available_fragments[self.player].value = self.required_fragments
- self.available_fragments = self.multiworld.available_fragments[self.player].value
+ # create logic from options
+ if self.options.required_fragments.value > self.options.available_fragments.value:
+ self.options.available_fragments.value = self.options.required_fragments.value
+ self.logic = SoEPlayerLogic(self.player, self.options)
def create_event(self, event: str) -> Item:
return SoEItem(event, ItemClassification.progression, None, self.player)
@@ -214,20 +218,20 @@ def create_item(self, item: typing.Union[pyevermizer.Item, str]) -> Item:
return SoEItem(item.name, classification, self.item_name_to_id[item.name], self.player)
@classmethod
- def stage_assert_generate(cls, multiworld):
+ def stage_assert_generate(cls, _: "MultiWorld") -> None:
rom_file = get_base_rom_path()
if not os.path.exists(rom_file):
raise FileNotFoundError(rom_file)
- def create_regions(self):
+ def create_regions(self) -> None:
# exclude 'hidden' on easy
- max_difficulty = 1 if self.multiworld.difficulty[self.player] == Difficulty.option_easy else 256
+ max_difficulty = 1 if self.options.difficulty == Difficulty.option_easy else 256
# TODO: generate *some* regions from locations' requirements?
menu = Region('Menu', self.player, self.multiworld)
self.multiworld.regions += [menu]
- def get_sphere_index(evermizer_loc):
+ def get_sphere_index(evermizer_loc: pyevermizer.Location) -> int:
"""Returns 0, 1 or 2 for locations in spheres 1, 2, 3+"""
if len(evermizer_loc.requires) == 1 and evermizer_loc.requires[0][1] != pyevermizer.P_WEAPON:
return 2
@@ -242,28 +246,38 @@ def get_sphere_index(evermizer_loc):
spheres.setdefault(get_sphere_index(loc), {}).setdefault(loc.type, []).append(
SoELocation(self.player, loc.name, self.location_name_to_id[loc.name], ingame,
loc.difficulty > max_difficulty))
+ # extend pool if feature and setting enabled
+ if hasattr(Sniffamizer, "option_everywhere") and self.options.sniffamizer == Sniffamizer.option_everywhere:
+ for loc in _sniff_locations:
+ spheres.setdefault(get_sphere_index(loc), {}).setdefault(loc.type, []).append(
+ SoELocation(self.player, loc.name, self.location_name_to_id[loc.name], ingame,
+ loc.difficulty > max_difficulty))
# location balancing data
trash_fills: typing.Dict[int, typing.Dict[int, typing.Tuple[int, int, int, int]]] = {
- 0: {pyevermizer.CHECK_GOURD: (20, 40, 40, 40)}, # remove up to 40 gourds from sphere 1
- 1: {pyevermizer.CHECK_GOURD: (70, 90, 90, 90)}, # remove up to 90 gourds from sphere 2
+ 0: {pyevermizer.CHECK_GOURD: (20, 40, 40, 40), # remove up to 40 gourds from sphere 1
+ pyevermizer.CHECK_SNIFF: (100, 130, 130, 130)}, # remove up to 130 sniff spots from sphere 1
+ 1: {pyevermizer.CHECK_GOURD: (70, 90, 90, 90), # remove up to 90 gourds from sphere 2
+ pyevermizer.CHECK_SNIFF: (160, 200, 200, 200)}, # remove up to 200 sniff spots from sphere 2
}
# mark some as excluded based on numbers above
for trash_sphere, fills in trash_fills.items():
for typ, counts in fills.items():
- count = counts[self.multiworld.difficulty[self.player].value]
- for location in self.multiworld.random.sample(spheres[trash_sphere][typ], count):
+ if typ not in spheres[trash_sphere]:
+ continue # e.g. player does not have sniff locations
+ count = counts[self.options.difficulty.value]
+ for location in self.random.sample(spheres[trash_sphere][typ], count):
assert location.name != "Energy Core #285", "Error in sphere generation"
location.progress_type = LocationProgressType.EXCLUDED
- def sphere1_blocked_items_rule(item):
+ def sphere1_blocked_items_rule(item: pyevermizer.Item) -> bool:
if isinstance(item, SoEItem):
# disable certain items in sphere 1
if item.name in {"Gauge", "Wheel"}:
return False
# and some more for non-easy, non-mystery
- if self.multiworld.difficulty[item.player] not in (Difficulty.option_easy, Difficulty.option_mystery):
+ if self.options.difficulty not in (Difficulty.option_easy, Difficulty.option_mystery):
if item.name in {"Laser Lance", "Atom Smasher", "Diamond Eye"}:
return False
return True
@@ -273,13 +287,13 @@ def sphere1_blocked_items_rule(item):
add_item_rule(location, sphere1_blocked_items_rule)
# make some logically late(r) bosses priority locations to increase complexity
- if self.multiworld.difficulty[self.player] == Difficulty.option_mystery:
- late_count = self.multiworld.random.randint(0, 2)
+ if self.options.difficulty == Difficulty.option_mystery:
+ late_count = self.random.randint(0, 2)
else:
- late_count = self.multiworld.difficulty[self.player].value
+ late_count = self.options.difficulty.value
late_bosses = ("Tiny", "Aquagoth", "Megataur", "Rimsala",
"Mungola", "Lightning Storm", "Magmar", "Volcano Viper")
- late_locations = self.multiworld.random.sample(late_bosses, late_count)
+ late_locations = self.random.sample(late_bosses, late_count)
# add locations to the world
for sphere in spheres.values():
@@ -293,17 +307,26 @@ def sphere1_blocked_items_rule(item):
menu.connect(ingame, "New Game")
self.multiworld.regions += [ingame]
- def create_items(self):
+ def create_items(self) -> None:
# add regular items to the pool
exclusions: typing.List[str] = []
- if self.energy_core != EnergyCore.option_shuffle:
+ if self.options.energy_core != EnergyCore.option_shuffle:
exclusions.append("Energy Core") # will be placed in generate_basic or replaced by a fragment below
items = list(map(lambda item: self.create_item(item), (item for item in _items if item.name not in exclusions)))
# remove one pair of wings that will be placed in generate_basic
items.remove(self.create_item("Wings"))
- def is_ingredient(item):
+ # extend pool if feature and setting enabled
+ if hasattr(Sniffamizer, "option_everywhere") and self.options.sniffamizer == Sniffamizer.option_everywhere:
+ if self.options.sniff_ingredients == SniffIngredients.option_vanilla_ingredients:
+ # vanilla ingredients
+ items += list(map(lambda item: self.create_item(item), _sniff_items))
+ else:
+ # random ingredients
+ items += [self.create_item(self.get_filler_item_name()) for _ in _sniff_items]
+
+ def is_ingredient(item: pyevermizer.Item) -> bool:
for ingredient in _ingredients:
if _match_item_name(item, ingredient):
return True
@@ -311,84 +334,77 @@ def is_ingredient(item):
# add energy core fragments to the pool
ingredients = [n for n, item in enumerate(items) if is_ingredient(item)]
- if self.energy_core == EnergyCore.option_fragments:
+ if self.options.energy_core == EnergyCore.option_fragments:
items.append(self.create_item("Energy Core Fragment")) # replaces the vanilla energy core
- for _ in range(self.available_fragments - 1):
+ for _ in range(self.options.available_fragments - 1):
if len(ingredients) < 1:
break # out of ingredients to replace
- r = self.multiworld.random.choice(ingredients)
+ r = self.random.choice(ingredients)
ingredients.remove(r)
items[r] = self.create_item("Energy Core Fragment")
# add traps to the pool
- trap_count = self.multiworld.trap_count[self.player].value
- trap_chances = {}
- trap_names = {}
+ trap_count = self.options.trap_count.value
+ trap_names: typing.List[str] = []
+ trap_weights: typing.List[int] = []
if trap_count > 0:
- for trap_type in self.trap_types:
- trap_option = getattr(self.multiworld, f'trap_chance_{trap_type}')[self.player]
- trap_chances[trap_type] = trap_option.value
- trap_names[trap_type] = trap_option.item_name
- trap_chances_total = sum(trap_chances.values())
- if trap_chances_total == 0:
- for trap_type in trap_chances:
- trap_chances[trap_type] = 1
- trap_chances_total = len(trap_chances)
+ for trap_option in self.options.trap_chances:
+ trap_names.append(trap_option.item_name)
+ trap_weights.append(trap_option.value)
+ if sum(trap_weights) == 0:
+ trap_weights = [1 for _ in trap_weights]
def create_trap() -> Item:
- v = self.multiworld.random.randrange(trap_chances_total)
- for t, c in trap_chances.items():
- if v < c:
- return self.create_item(trap_names[t])
- v -= c
- assert False, "Bug in create_trap"
+ return self.create_item(self.random.choices(trap_names, trap_weights)[0])
for _ in range(trap_count):
if len(ingredients) < 1:
break # out of ingredients to replace
- r = self.multiworld.random.choice(ingredients)
+ r = self.random.choice(ingredients)
ingredients.remove(r)
items[r] = create_trap()
self.multiworld.itempool += items
- def set_rules(self):
+ def set_rules(self) -> None:
self.multiworld.completion_condition[self.player] = lambda state: state.has('Victory', self.player)
# set Done from goal option once we have multiple goals
set_rule(self.multiworld.get_location('Done', self.player),
- lambda state: state.soe_has(pyevermizer.P_FINAL_BOSS, self.multiworld, self.player))
+ lambda state: self.logic.has(state, pyevermizer.P_FINAL_BOSS))
set_rule(self.multiworld.get_entrance('New Game', self.player), lambda state: True)
- for loc in _locations:
+ locations: typing.Iterable[pyevermizer.Location]
+ if hasattr(Sniffamizer, "option_everywhere") and self.options.sniffamizer == Sniffamizer.option_everywhere:
+ locations = itertools.chain(_locations, _sniff_locations)
+ else:
+ locations = _locations
+ for loc in locations:
location = self.multiworld.get_location(loc.name, self.player)
set_rule(location, self.make_rule(loc.requires))
def make_rule(self, requires: typing.List[typing.Tuple[int, int]]) -> typing.Callable[[typing.Any], bool]:
- def rule(state) -> bool:
+ def rule(state: "CollectionState") -> bool:
for count, progress in requires:
- if not state.soe_has(progress, self.multiworld, self.player, count):
+ if not self.logic.has(state, progress, count):
return False
return True
return rule
- def make_item_type_limit_rule(self, item_type: int):
- return lambda item: item.player != self.player or self.item_id_to_raw[item.code].type == item_type
-
- def generate_basic(self):
+ def generate_basic(self) -> None:
# place Victory event
self.multiworld.get_location('Done', self.player).place_locked_item(self.create_event('Victory'))
# place wings in halls NE to avoid softlock
- wings_location = self.multiworld.random.choice(self._halls_ne_chest_names)
+ wings_location = self.random.choice(self._halls_ne_chest_names)
wings_item = self.create_item('Wings')
self.multiworld.get_location(wings_location, self.player).place_locked_item(wings_item)
# place energy core at vanilla location for vanilla mode
- if self.energy_core == EnergyCore.option_vanilla:
+ if self.options.energy_core == EnergyCore.option_vanilla:
energy_core = self.create_item('Energy Core')
self.multiworld.get_location('Energy Core #285', self.player).place_locked_item(energy_core)
# generate stuff for later
- self.evermizer_seed = self.multiworld.random.randint(0, 2 ** 16 - 1) # TODO: make this an option for "full" plando?
+ self.evermizer_seed = self.random.randint(0, 2 ** 16 - 1) # TODO: make this an option for "full" plando?
- def generate_output(self, output_directory: str):
+ def generate_output(self, output_directory: str) -> None:
player_name = self.multiworld.get_player_name(self.player)
self.connect_name = player_name[:32]
while len(self.connect_name.encode('utf-8')) > 32:
@@ -397,24 +413,21 @@ def generate_output(self, output_directory: str):
placement_file = ""
out_file = ""
try:
- money = self.multiworld.money_modifier[self.player].value
- exp = self.multiworld.exp_modifier[self.player].value
+ money = self.options.money_modifier.value
+ exp = self.options.exp_modifier.value
switches: typing.List[str] = []
- if self.multiworld.death_link[self.player].value:
+ if self.options.death_link.value:
switches.append("--death-link")
- if self.energy_core == EnergyCore.option_fragments:
- switches.extend(('--available-fragments', str(self.available_fragments),
- '--required-fragments', str(self.required_fragments)))
+ if self.options.energy_core == EnergyCore.option_fragments:
+ switches.extend(('--available-fragments', str(self.options.available_fragments.value),
+ '--required-fragments', str(self.options.required_fragments.value)))
rom_file = get_base_rom_path()
out_base = output_path(output_directory, self.multiworld.get_out_file_name_base(self.player))
out_file = out_base + '.sfc'
placement_file = out_base + '.txt'
patch_file = out_base + '.apsoe'
flags = 'l' # spoiler log
- for option_name in self.option_definitions:
- option = getattr(self.multiworld, option_name)[self.player]
- if hasattr(option, 'to_flag'):
- flags += option.to_flag()
+ flags += self.options.flags
with open(placement_file, "wb") as f: # generate placement file
for location in self.multiworld.get_locations(self.player):
@@ -448,7 +461,7 @@ def generate_output(self, output_directory: str):
except FileNotFoundError:
pass
- def modify_multidata(self, multidata: dict):
+ def modify_multidata(self, multidata: typing.Dict[str, typing.Any]) -> None:
# wait for self.connect_name to be available.
self.connect_name_available_event.wait()
# we skip in case of error, so that the original error in the output thread is the one that gets raised
@@ -457,7 +470,7 @@ def modify_multidata(self, multidata: dict):
multidata["connect_names"][self.connect_name] = payload
def get_filler_item_name(self) -> str:
- return self.multiworld.random.choice(list(self.item_name_groups["Ingredients"]))
+ return self.random.choice(list(self.item_name_groups["Ingredients"]))
class SoEItem(Item):
diff --git a/worlds/soe/docs/en_Secret of Evermore.md b/worlds/soe/docs/en_Secret of Evermore.md
index 215a5387bb9f..98882b6ff7e3 100644
--- a/worlds/soe/docs/en_Secret of Evermore.md
+++ b/worlds/soe/docs/en_Secret of Evermore.md
@@ -1,8 +1,8 @@
# Secret of Evermore
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
diff --git a/worlds/soe/docs/multiworld_en.md b/worlds/soe/docs/multiworld_en.md
index 58b9aabf6a9a..065d43fc3a1b 100644
--- a/worlds/soe/docs/multiworld_en.md
+++ b/worlds/soe/docs/multiworld_en.md
@@ -2,16 +2,18 @@
## Required Software
-- SNI from: [SNI Releases Page](https://github.com/alttpo/sni/releases)
- - v0.0.59 or newer (included in Archipelago 0.2.1 setup)
-- Hardware or software capable of loading and playing SNES ROM files
- - An emulator capable of connecting to SNI with ROM access. Any one of the following will work:
- - snes9x-rr from: [snes9x-rr Releases Page](https://github.com/gocha/snes9x-rr/releases)
- - BizHawk from: [TASVideos](https://tasvideos.org/BizHawk)
- - bsnes-plus-nwa from: [bsnes-plus GitHub](https://github.com/black-sliver/bsnes-plus)
- - RetroArch from: [RetroArch Website](https://retroarch.com?page=platforms). Or,
- - Or SD2SNES, FXPak Pro ([FXPak Pro Store Page](https://krikzz.com/store/home/54-fxpak-pro.html)), or other
- compatible hardware.
+- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases).
+- [SNI](https://github.com/alttpo/sni/releases). This is automatically included with your Archipelago installation above.
+- SNI is not compatible with (Q)Usb2Snes.
+- Hardware or software capable of loading and playing SNES ROM files, including:
+ - An emulator capable of connecting to SNI
+ ([snes9x-nwa](https://github.com/Skarsnik/snes9x-emunwa/releases), [snes9x-rr](https://github.com/gocha/snes9x-rr/releases),
+ [BSNES-plus](https://github.com/black-sliver/bsnes-plus),
+ [BizHawk](http://tasvideos.org/BizHawk.html), or
+ [RetroArch](https://retroarch.com?page=platforms) 1.10.1 or newer)
+ - An SD2SNES, [FXPak Pro](https://krikzz.com/store/home/54-fxpak-pro.html), or other compatible hardware. **note:
+ modded SNES minis are currently not supported by SNI. Some users have claimed success with QUsb2Snes for this system,
+ but it is not supported.**
- Your legally obtained Secret of Evermore US ROM file, probably named `Secret of Evermore (USA).sfc`
## Create a Config (.yaml) File
@@ -23,8 +25,8 @@ guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
### Where do I get a config file?
-The Player Settings page on the website allows you to configure your personal settings and export a config file from
-them. Player settings page: [Secret of Evermore Player Settings PAge](/games/Secret%20of%20Evermore/player-settings)
+The Player Options page on the website allows you to configure your personal options and export a config file from
+them. Player options page: [Secret of Evermore Player Options Page](/games/Secret%20of%20Evermore/player-options)
### Verifying your config file
@@ -36,8 +38,8 @@ page: [YAML Validation page](/check)
Stand-alone "Evermizer" has a way of balancing single-player games, but may not always be on par feature-wise. Head over
to the [Evermizer Website](https://evermizer.com) if you want to try the official stand-alone, otherwise read below.
-1. Navigate to the Player Settings page, configure your options, and click the "Generate Game" button.
- - Player Settings page: [Secret of Evermore Player Settings Page](/games/Secret%20of%20Evermore/player-settings)
+1. Navigate to the Player Options page, configure your options, and click the "Generate Game" button.
+ - Player Options page: [Secret of Evermore Player Options Page](/games/Secret%20of%20Evermore/player-options)
2. You will be presented with a "Seed Info" page.
3. Click the "Create New Room" link.
4. You will be presented with a server page, from which you can download your patch file.
@@ -63,6 +65,11 @@ page: [Evermizer apbpatch Page](https://evermizer.com/apbpatch)
Start SNI either from the Archipelago install folder or the stand-alone version. If this is its first time launching,
you may be prompted to allow it to communicate through the Windows Firewall.
+#### snes9x-nwa
+
+1. Click on the Network Menu and check **Enable Emu Network Control**
+2. Load your ROM file if it hasn't already been loaded.
+
##### snes9x-rr
1. Load your ROM file if it hasn't already been loaded.
diff --git a/worlds/soe/logic.py b/worlds/soe/logic.py
new file mode 100644
index 000000000000..92ffb14b3f95
--- /dev/null
+++ b/worlds/soe/logic.py
@@ -0,0 +1,88 @@
+import typing
+from itertools import chain
+from typing import Callable, Set
+
+from . import pyevermizer
+from .options import EnergyCore, OutOfBounds, SequenceBreaks, SoEOptions
+
+if typing.TYPE_CHECKING:
+ from BaseClasses import CollectionState
+
+# TODO: Options may preset certain progress steps (i.e. P_ROCK_SKIP), set in generate_early?
+
+# TODO: resolve/flatten/expand rules to get rid of recursion below where possible
+# Logic.rules are all rules including locations, excluding those with no progress (i.e. locations that only drop items)
+rules = pyevermizer.get_logic()
+# Logic.items are all items and extra items excluding non-progression items and duplicates
+# NOTE: we are skipping sniff items here because none of them is supposed to provide progression
+item_names: Set[str] = set()
+items = [item for item in filter(lambda item: item.progression, # type: ignore[arg-type]
+ chain(pyevermizer.get_items(), pyevermizer.get_extra_items()))
+ if item.name not in item_names and not item_names.add(item.name)] # type: ignore[func-returns-value]
+
+
+class SoEPlayerLogic:
+ __slots__ = "player", "out_of_bounds", "sequence_breaks", "has"
+ player: int
+ out_of_bounds: bool
+ sequence_breaks: bool
+
+ has: Callable[..., bool]
+ """
+ Returns True if count of one of evermizer's progress steps is reached based on collected items. i.e. 2 * P_DE
+ """
+
+ def __init__(self, player: int, options: "SoEOptions"):
+ self.player = player
+ self.out_of_bounds = options.out_of_bounds == OutOfBounds.option_logic
+ self.sequence_breaks = options.sequence_breaks == SequenceBreaks.option_logic
+
+ if options.energy_core == EnergyCore.option_fragments:
+ # override logic for energy core fragments
+ required_fragments = options.required_fragments.value
+
+ def fragmented_has(state: "CollectionState", progress: int, count: int = 1) -> bool:
+ if progress == pyevermizer.P_ENERGY_CORE:
+ progress = pyevermizer.P_CORE_FRAGMENT
+ count = required_fragments
+ return self._has(state, progress, count)
+
+ self.has = fragmented_has
+ else:
+ # default (energy core) logic
+ self.has = self._has
+
+ def _count(self, state: "CollectionState", progress: int, max_count: int = 0) -> int:
+ """
+ Returns reached count of one of evermizer's progress steps based on collected items.
+ i.e. returns 0-3 for P_DE based on items providing CHECK_BOSS,DIAMOND_EYE_DROP
+ """
+ n = 0
+ for item in items:
+ for pvd in item.provides:
+ if pvd[1] == progress:
+ if state.has(item.name, self.player):
+ n += state.count(item.name, self.player) * pvd[0]
+ if n >= max_count > 0:
+ return n
+ for rule in rules:
+ for pvd in rule.provides:
+ if pvd[1] == progress and pvd[0] > 0:
+ has = True
+ for req in rule.requires:
+ if not self.has(state, req[1], req[0]):
+ has = False
+ break
+ if has:
+ n += pvd[0]
+ if n >= max_count > 0:
+ return n
+ return n
+
+ def _has(self, state: "CollectionState", progress: int, count: int = 1) -> bool:
+ """Default implementation of has"""
+ if self.out_of_bounds is True and progress == pyevermizer.P_ALLOW_OOB:
+ return True
+ if self.sequence_breaks is True and progress == pyevermizer.P_ALLOW_SEQUENCE_BREAKS:
+ return True
+ return self._count(state, progress, count) >= count
diff --git a/worlds/soe/Options.py b/worlds/soe/options.py
similarity index 63%
rename from worlds/soe/Options.py
rename to worlds/soe/options.py
index 3de2de34ac67..5ecd0f9e6666 100644
--- a/worlds/soe/Options.py
+++ b/worlds/soe/options.py
@@ -1,24 +1,27 @@
-import typing
+from dataclasses import dataclass, fields
+from datetime import datetime
+from typing import Any, ClassVar, cast, Dict, Iterator, List, Tuple, Protocol
-from Options import Range, Choice, Toggle, DefaultOnToggle, AssembleOptions, DeathLink, ProgressionBalancing
+from Options import AssembleOptions, Choice, DeathLink, DefaultOnToggle, Option, PerGameCommonOptions, \
+ ProgressionBalancing, Range, Toggle
# typing boilerplate
-class FlagsProtocol(typing.Protocol):
+class FlagsProtocol(Protocol):
value: int
- default: int
- flags: typing.List[str]
+ default: ClassVar[int]
+ flags: List[str]
-class FlagProtocol(typing.Protocol):
+class FlagProtocol(Protocol):
value: int
- default: int
+ default: ClassVar[int]
flag: str
# meta options
class EvermizerFlags:
- flags: typing.List[str]
+ flags: List[str]
def to_flag(self: FlagsProtocol) -> str:
return self.flags[self.value]
@@ -156,13 +159,30 @@ class Ingredienizer(EvermizerFlags, OffOnFullChoice):
flags = ['i', '', 'I']
-class Sniffamizer(EvermizerFlags, OffOnFullChoice):
- """On Shuffles, Full randomizes drops in sniff locations"""
+class Sniffamizer(EvermizerFlags, Choice):
+ """
+ Off: all vanilla items in sniff spots
+ Shuffle: sniff items shuffled into random sniff spots
+ """
display_name = "Sniffamizer"
+ option_off = 0
+ option_shuffle = 1
+ if datetime.today().year > 2024 or datetime.today().month > 3:
+ option_everywhere = 2
+ __doc__ = __doc__ + " Everywhere: add sniff spots to multiworld pool"
+ alias_true = 1
default = 1
flags = ['s', '', 'S']
+class SniffIngredients(EvermizerFlag, Choice):
+ """Select which items should be used as sniff items"""
+ display_name = "Sniff Ingredients"
+ option_vanilla_ingredients = 0
+ option_random_ingredients = 1
+ flag = 'v'
+
+
class Callbeadamizer(EvermizerFlags, OffOnFullChoice):
"""On Shuffles call bead characters, Full shuffles individual spells"""
display_name = "Callbeadamizer"
@@ -200,13 +220,13 @@ class TrapCount(Range):
# more meta options
class ItemChanceMeta(AssembleOptions):
- def __new__(mcs, name, bases, attrs):
+ def __new__(mcs, name: str, bases: Tuple[type], attrs: Dict[Any, Any]) -> "ItemChanceMeta":
if 'item_name' in attrs:
attrs["display_name"] = f"{attrs['item_name']} Chance"
attrs["range_start"] = 0
attrs["range_end"] = 100
-
- return super(ItemChanceMeta, mcs).__new__(mcs, name, bases, attrs)
+ cls = super(ItemChanceMeta, mcs).__new__(mcs, name, bases, attrs) # type: ignore[no-untyped-call]
+ return cast(ItemChanceMeta, cls)
class TrapChance(Range, metaclass=ItemChanceMeta):
@@ -247,33 +267,53 @@ class SoEProgressionBalancing(ProgressionBalancing):
special_range_names = {**ProgressionBalancing.special_range_names, "normal": default}
-soe_options: typing.Dict[str, AssembleOptions] = {
- "difficulty": Difficulty,
- "energy_core": EnergyCore,
- "required_fragments": RequiredFragments,
- "available_fragments": AvailableFragments,
- "money_modifier": MoneyModifier,
- "exp_modifier": ExpModifier,
- "sequence_breaks": SequenceBreaks,
- "out_of_bounds": OutOfBounds,
- "fix_cheats": FixCheats,
- "fix_infinite_ammo": FixInfiniteAmmo,
- "fix_atlas_glitch": FixAtlasGlitch,
- "fix_wings_glitch": FixWingsGlitch,
- "shorter_dialogs": ShorterDialogs,
- "short_boss_rush": ShortBossRush,
- "ingredienizer": Ingredienizer,
- "sniffamizer": Sniffamizer,
- "callbeadamizer": Callbeadamizer,
- "musicmizer": Musicmizer,
- "doggomizer": Doggomizer,
- "turdo_mode": TurdoMode,
- "death_link": DeathLink,
- "trap_count": TrapCount,
- "trap_chance_quake": TrapChanceQuake,
- "trap_chance_poison": TrapChancePoison,
- "trap_chance_confound": TrapChanceConfound,
- "trap_chance_hud": TrapChanceHUD,
- "trap_chance_ohko": TrapChanceOHKO,
- "progression_balancing": SoEProgressionBalancing,
-}
+# noinspection SpellCheckingInspection
+@dataclass
+class SoEOptions(PerGameCommonOptions):
+ difficulty: Difficulty
+ energy_core: EnergyCore
+ required_fragments: RequiredFragments
+ available_fragments: AvailableFragments
+ money_modifier: MoneyModifier
+ exp_modifier: ExpModifier
+ sequence_breaks: SequenceBreaks
+ out_of_bounds: OutOfBounds
+ fix_cheats: FixCheats
+ fix_infinite_ammo: FixInfiniteAmmo
+ fix_atlas_glitch: FixAtlasGlitch
+ fix_wings_glitch: FixWingsGlitch
+ shorter_dialogs: ShorterDialogs
+ short_boss_rush: ShortBossRush
+ ingredienizer: Ingredienizer
+ sniffamizer: Sniffamizer
+ sniff_ingredients: SniffIngredients
+ callbeadamizer: Callbeadamizer
+ musicmizer: Musicmizer
+ doggomizer: Doggomizer
+ turdo_mode: TurdoMode
+ death_link: DeathLink
+ trap_count: TrapCount
+ trap_chance_quake: TrapChanceQuake
+ trap_chance_poison: TrapChancePoison
+ trap_chance_confound: TrapChanceConfound
+ trap_chance_hud: TrapChanceHUD
+ trap_chance_ohko: TrapChanceOHKO
+ progression_balancing: SoEProgressionBalancing
+
+ @property
+ def trap_chances(self) -> Iterator[TrapChance]:
+ for field in fields(self):
+ option = getattr(self, field.name)
+ if isinstance(option, TrapChance):
+ yield option
+
+ @property
+ def flags(self) -> str:
+ flags = ''
+ for field in fields(self):
+ option = getattr(self, field.name)
+ if isinstance(option, (EvermizerFlag, EvermizerFlags)):
+ assert isinstance(option, Option)
+ # noinspection PyUnresolvedReferences
+ flags += option.to_flag()
+ return flags
diff --git a/worlds/soe/Patch.py b/worlds/soe/patch.py
similarity index 86%
rename from worlds/soe/Patch.py
rename to worlds/soe/patch.py
index f4de5d06ead1..a322de2af65f 100644
--- a/worlds/soe/Patch.py
+++ b/worlds/soe/patch.py
@@ -1,5 +1,5 @@
import os
-from typing import Optional
+from typing import BinaryIO, Optional
import Utils
from worlds.Files import APDeltaPatch
@@ -30,7 +30,7 @@ def get_base_rom_path(file_name: Optional[str] = None) -> str:
return file_name
-def read_rom(stream, strip_header=True) -> bytes:
+def read_rom(stream: BinaryIO, strip_header: bool = True) -> bytes:
"""Reads rom into bytearray and optionally strips off any smc header"""
data = stream.read()
if strip_header and len(data) % 0x400 == 0x200:
@@ -40,5 +40,5 @@ def read_rom(stream, strip_header=True) -> bytes:
if __name__ == '__main__':
import sys
- print('Please use ../../Patch.py', file=sys.stderr)
+ print('Please use ../../patch.py', file=sys.stderr)
sys.exit(1)
diff --git a/worlds/soe/requirements.txt b/worlds/soe/requirements.txt
index 710f51ddb09a..4bcacb33c33c 100644
--- a/worlds/soe/requirements.txt
+++ b/worlds/soe/requirements.txt
@@ -1,36 +1,36 @@
-pyevermizer==0.46.1 \
- --hash=sha256:9fd71b5e4af26a5dd24a9cbf5320bf0111eef80320613401a1c03011b1515806 \
- --hash=sha256:23f553ed0509d9a238b2832f775e0b5abd7741b38ab60d388294ee8a7b96c5fb \
- --hash=sha256:7189b67766418a3e7e6c683f09c5e758aa1a5c24316dd9b714984bac099c4b75 \
- --hash=sha256:befa930711e63d5d5892f67fd888b2e65e746363e74599c53e71ecefb90ae16a \
- --hash=sha256:202933ce21e0f33859537bf3800d9a626c70262a9490962e3f450171758507ca \
- --hash=sha256:c20ca69311c696528e1122ebc7d33775ee971f538c0e3e05dd3bfd4de10b82d4 \
- --hash=sha256:74dc689a771ae5ffcd5257e763f571ee890e3e87bdb208233b7f451522c00d66 \
- --hash=sha256:072296baef464daeb6304cf58827dcbae441ad0803039aee1c0caa10d56e0674 \
- --hash=sha256:7921baf20d52d92d6aeb674125963c335b61abb7e1298bde4baf069d11a2d05e \
- --hash=sha256:ca098034a84007038c2bff004582e6e6ac2fa9cc8b9251301d25d7e2adcee6da \
- --hash=sha256:22ddb29823c19be9b15e1b3627db1babfe08b486aede7d5cc463a0a1ae4c75d8 \
- --hash=sha256:bf1c441b49026d9000166be6e2f63fc351a3fda170aa3fdf18d44d5e5d044640 \
- --hash=sha256:9710aa7957b4b1f14392006237eb95803acf27897377df3e85395f057f4316b9 \
- --hash=sha256:8feb676c198bee17ab991ee015828345ac3f87c27dfdb3061d92d1fe47c184b4 \
- --hash=sha256:597026dede72178ff3627a4eb3315de8444461c7f0f856f5773993c3f9790c53 \
- --hash=sha256:70f9b964bdfb5191e8f264644c5d1af3041c66fe15261df8a99b3d719dc680d6 \
- --hash=sha256:74655c0353ffb6cda30485091d0917ce703b128cd824b612b3110a85c79a93d0 \
- --hash=sha256:0e9c74d105d4ec3af12404e85bb8776931c043657add19f798ee69465f92b999 \
- --hash=sha256:d3c13446d3d482b9cce61ac73b38effd26fcdcf7f693a405868d3aaaa4d18ca6 \
- --hash=sha256:371ac3360640ef439a5920ddfe11a34e9d2e546ed886bb8c9ed312611f9f4655 \
- --hash=sha256:6e5cf63b036f24d2ae4375a88df8d0bc93208352939521d1fcac3c829ef2c363 \
- --hash=sha256:edf28f5c4d1950d17343adf6d8d40d12c7e982d1e39535d55f7915e122cd8b0e \
- --hash=sha256:b5ef6f3b4e04f677c296f60f7f4c320ac22cd5bc09c05574460116c8641c801a \
- --hash=sha256:dd651f66720af4abe2ddae29944e299a57ff91e6fca1739e6dc1f8fd7a8c2b39 \
- --hash=sha256:4e278f5f72c27f9703bce5514d2fead8c00361caac03e94b0bf9ad8a144f1eeb \
- --hash=sha256:38f36ea1f545b835c3ecd6e081685a233ac2e3cf0eec8916adc92e4d791098a6 \
- --hash=sha256:0a2e58ed6e7c42f006cc17d32cec1f432f01b3fe490e24d71471b36e0d0d8742 \
- --hash=sha256:c1b658db76240596c03571c60635abe953f36fb55b363202971831c2872ea9a0 \
- --hash=sha256:deb5a84a6a56325eb6701336cdbf70f72adaaeab33cbe953d0e551ecf2592f20 \
- --hash=sha256:b1425c793e0825f58b3726e7afebaf5a296c07cb0d28580d0ee93dbe10dcdf63 \
- --hash=sha256:11995fb4dfd14b5c359591baee2a864c5814650ba0084524d4ea0466edfaf029 \
- --hash=sha256:5d2120b5c93ae322fe2a85d48e3eab4168a19e974a880908f1ac291c0300940f \
- --hash=sha256:254912ea4bfaaffb0abe366e73bd9ecde622677d6afaf2ce8a0c330df99fefd9 \
- --hash=sha256:540d8e4525f0b5255c1554b4589089dc58e15df22f343e9545ea00f7012efa07 \
- --hash=sha256:f69b8ebded7eed181fabe30deabae89fd10c41964f38abb26b19664bbe55c1ae
+pyevermizer==0.48.0 \
+ --hash=sha256:069ce348e480e04fd6208cfd0f789c600b18d7c34b5272375b95823be191ed57 \
+ --hash=sha256:58164dddaba2f340b0a8b4f39605e9dac46d8b0ffb16120e2e57bef2bfc1d683 \
+ --hash=sha256:115dd09d38a10f11d4629b340dfd75e2ba4089a1ff9e9748a11619829e02c876 \
+ --hash=sha256:b5e79cfe721e75cd7dec306b5eecd6385ce059e31ef7523ba7f677e22161ec6f \
+ --hash=sha256:382882fa9d641b9969a6c3ed89449a814bdabcb6b17b558872d95008a6cc908b \
+ --hash=sha256:92f67700e9132064a90858d391dd0b8fb111aff6dfd472befed57772d89ae567 \
+ --hash=sha256:fe4c453b7dbd5aa834b81f9a7aedb949a605455650b938b8b304d8e5a7edcbf7 \
+ --hash=sha256:c6bdbc45daf73818f763ed59ad079f16494593395d806f772dd62605c722b3e9 \
+ --hash=sha256:bb09f45448fdfd28566ae6fcc38c35a6632f4c31a9de2483848f6ce17b2359b5 \
+ --hash=sha256:00a8b9014744bd1528d0d39c33ede7c0d1713ad797a331cebb33d377a5bc1064 \
+ --hash=sha256:64ee69edc0a7d3b3caded78f2e46975f9beaff1ff8feaf29b87da44c45f38d7d \
+ --hash=sha256:9211bdb1313e9f4869ed5bdc61f3831d39679bd08bb4087f1c1e5475d9e3018b \
+ --hash=sha256:4a57821e422a1d75fe3307931a78db7a65e76955f8e401c4b347db6570390d09 \
+ --hash=sha256:04670cee0a0b913f24d2b9a1e771781560e2485bda31e6cd372a08421cf85cfa \
+ --hash=sha256:971fe77d0a20a1db984020ad253b613d0983f5e23ff22cba60ee5ac00d8128de \
+ --hash=sha256:127265fdb49f718f54706bf15604af1cec23590afd00d423089dea4331dcfc61 \
+ --hash=sha256:d47576360337c1a23f424cd49944a8d68fc4f3338e00719c9f89972c84604bef \
+ --hash=sha256:879659603e51130a0de8d9885d815a2fa1df8bd6cebe6d520d1c6002302adfdb \
+ --hash=sha256:6a91bfc53dd130db6424adf8ac97a1133e97b4157ed00f889d8cbd26a2a4b340 \
+ --hash=sha256:f3bf35fc5eef4cda49d2de77339fc201dd3206660a3dc15db005625b15bb806c \
+ --hash=sha256:e7c8d5bf59a3c16db20411bc5d8e9c9087a30b6b4edf1b5ed9f4c013291427e4 \
+ --hash=sha256:054a4d84ffe75448d41e88e1e0642ef719eb6111be5fe608e71e27a558c59069 \
+ --hash=sha256:e6f141ca367469c69ba7fbf65836c479ec6672c598cfcb6b39e8098c60d346bc \
+ --hash=sha256:6e65eb88f0c1ff4acde1c13b24ce649b0fe3d1d3916d02d96836c781a5022571 \
+ --hash=sha256:e61e8f476b6da809cf38912755ed8bb009665f589e913eb8df877e9fa763024b \
+ --hash=sha256:7e7c5484c0a2e3da6064de3f73d8d988d6703db58ab0be4730cbbf1a82319237 \
+ --hash=sha256:9033b954e5f4878fd94af6d2056c78e3316115521fb1c24a4416d5cbf2ad66ad \
+ --hash=sha256:824c623fff8ae4da176306c458ad63ad16a06a495a16db700665eca3c115924f \
+ --hash=sha256:8e31031409a8386c6a63b79d480393481badb3ba29f32ff7a0db2b4abed20ac8 \
+ --hash=sha256:7dbb7bb13e1e94f69f7ccdbcf4d35776424555fce5af1ca29d0256f91fdf087a \
+ --hash=sha256:3a24e331b259407b6912d6e0738aa8a675831db3b7493fcf54dc17cb0cb80d37 \
+ --hash=sha256:fdda06662a994271e96633cba100dd92b2fcd524acef8b2f664d1aaa14503cbd \
+ --hash=sha256:0f0fc81bef3dbb78ba6a7622dd4296f23c59825968a0bb0448beb16eb3397cc2 \
+ --hash=sha256:e07cbef776a7468669211546887357cc88e9afcf1578b23a4a4f2480517b15d9 \
+ --hash=sha256:e442212695bdf60e455673b7b9dd83a5d4b830d714376477093d2c9054d92832
diff --git a/worlds/soe/test/__init__.py b/worlds/soe/test/__init__.py
index 27d38605aae4..e84d7e669d2c 100644
--- a/worlds/soe/test/__init__.py
+++ b/worlds/soe/test/__init__.py
@@ -1,12 +1,14 @@
-from test.TestBase import WorldTestBase
+from test.bases import WorldTestBase
from typing import Iterable
+from .. import SoEWorld
class SoETestBase(WorldTestBase):
game = "Secret of Evermore"
+ world: SoEWorld
def assertLocationReachability(self, reachable: Iterable[str] = (), unreachable: Iterable[str] = (),
- satisfied=True) -> None:
+ satisfied: bool = True) -> None:
"""
Tests that unreachable can't be reached. Tests that reachable can be reached if satisfied=True.
Usage: test with satisfied=False, collect requirements into state, test again with satisfied=True
@@ -18,3 +20,14 @@ def assertLocationReachability(self, reachable: Iterable[str] = (), unreachable:
for location in unreachable:
self.assertFalse(self.can_reach_location(location),
f"{location} is reachable but shouldn't be")
+
+ def testRocketPartsExist(self) -> None:
+ """Tests that rocket parts exist and are unique"""
+ self.assertEqual(len(self.get_items_by_name("Gauge")), 1)
+ self.assertEqual(len(self.get_items_by_name("Wheel")), 1)
+ diamond_eyes = self.get_items_by_name("Diamond Eye")
+ self.assertEqual(len(diamond_eyes), 3)
+ # verify diamond eyes are individual items
+ self.assertFalse(diamond_eyes[0] is diamond_eyes[1])
+ self.assertFalse(diamond_eyes[0] is diamond_eyes[2])
+ self.assertFalse(diamond_eyes[1] is diamond_eyes[2])
diff --git a/worlds/soe/test/test_access.py b/worlds/soe/test/test_access.py
index c7da7b889627..f1d6ee993b34 100644
--- a/worlds/soe/test/test_access.py
+++ b/worlds/soe/test/test_access.py
@@ -4,10 +4,10 @@
class AccessTest(SoETestBase):
@staticmethod
- def _resolveGourds(gourds: typing.Dict[str, typing.Iterable[int]]):
+ def _resolveGourds(gourds: typing.Mapping[str, typing.Iterable[int]]) -> typing.List[str]:
return [f"{name} #{number}" for name, numbers in gourds.items() for number in numbers]
- def testBronzeAxe(self):
+ def test_bronze_axe(self) -> None:
gourds = {
"Pyramid bottom": (118, 121, 122, 123, 124, 125),
"Pyramid top": (140,)
@@ -16,7 +16,7 @@ def testBronzeAxe(self):
items = [["Bronze Axe"]]
self.assertAccessDependency(locations, items)
- def testBronzeSpearPlus(self):
+ def test_bronze_spear_plus(self) -> None:
locations = ["Megataur"]
items = [["Bronze Spear"], ["Lance (Weapon)"], ["Laser Lance"]]
self.assertAccessDependency(locations, items)
diff --git a/worlds/soe/test/test_goal.py b/worlds/soe/test/test_goal.py
index d127d3899869..bb64b8eca759 100644
--- a/worlds/soe/test/test_goal.py
+++ b/worlds/soe/test/test_goal.py
@@ -8,7 +8,7 @@ class TestFragmentGoal(SoETestBase):
"required_fragments": 20,
}
- def testFragments(self):
+ def test_fragments(self) -> None:
self.collect_by_name(["Gladiator Sword", "Diamond Eye", "Wheel", "Gauge"])
self.assertBeatable(False) # 0 fragments
fragments = self.get_items_by_name("Energy Core Fragment")
@@ -24,11 +24,11 @@ def testFragments(self):
self.assertEqual(self.count("Energy Core Fragment"), 21)
self.assertBeatable(True)
- def testNoWeapon(self):
+ def test_no_weapon(self) -> None:
self.collect_by_name(["Diamond Eye", "Wheel", "Gauge", "Energy Core Fragment"])
self.assertBeatable(False)
- def testNoRocket(self):
+ def test_no_rocket(self) -> None:
self.collect_by_name(["Gladiator Sword", "Diamond Eye", "Wheel", "Energy Core Fragment"])
self.assertBeatable(False)
@@ -38,16 +38,16 @@ class TestShuffleGoal(SoETestBase):
"energy_core": "shuffle",
}
- def testCore(self):
+ def test_core(self) -> None:
self.collect_by_name(["Gladiator Sword", "Diamond Eye", "Wheel", "Gauge"])
self.assertBeatable(False)
self.collect_by_name(["Energy Core"])
self.assertBeatable(True)
- def testNoWeapon(self):
+ def test_no_weapon(self) -> None:
self.collect_by_name(["Diamond Eye", "Wheel", "Gauge", "Energy Core"])
self.assertBeatable(False)
- def testNoRocket(self):
+ def test_no_rocket(self) -> None:
self.collect_by_name(["Gladiator Sword", "Diamond Eye", "Wheel", "Energy Core"])
self.assertBeatable(False)
diff --git a/worlds/soe/test/test_item_mapping.py b/worlds/soe/test/test_item_mapping.py
new file mode 100644
index 000000000000..7df05837c78b
--- /dev/null
+++ b/worlds/soe/test/test_item_mapping.py
@@ -0,0 +1,21 @@
+from unittest import TestCase
+from .. import SoEWorld
+
+
+class TestMapping(TestCase):
+ def test_atlas_medallion_name_group(self) -> None:
+ """
+ Test that we used the pyevermizer name for Atlas Medallion (not Amulet) in item groups.
+ """
+ self.assertIn("Any Atlas Medallion", SoEWorld.item_name_groups)
+
+ def test_atlas_medallion_name_items(self) -> None:
+ """
+ Test that we used the pyevermizer name for Atlas Medallion (not Amulet) in items.
+ """
+ found_medallion = False
+ for name in SoEWorld.item_name_to_id:
+ self.assertNotIn("Atlas Amulet", name, "Expected Atlas Medallion, not Amulet")
+ if "Atlas Medallion" in name:
+ found_medallion = True
+ self.assertTrue(found_medallion, "Did not find Atlas Medallion in items")
diff --git a/worlds/soe/test/test_oob.py b/worlds/soe/test/test_oob.py
index 27e00cd3e764..3c1a2829de8e 100644
--- a/worlds/soe/test/test_oob.py
+++ b/worlds/soe/test/test_oob.py
@@ -6,7 +6,7 @@ class OoBTest(SoETestBase):
"""Tests that 'on' doesn't put out-of-bounds in logic. This is also the test base for OoB in logic."""
options: typing.Dict[str, typing.Any] = {"out_of_bounds": "on"}
- def testOoBAccess(self):
+ def test_oob_access(self) -> None:
in_logic = self.options["out_of_bounds"] == "logic"
# some locations that just need a weapon + OoB
@@ -37,7 +37,7 @@ def testOoBAccess(self):
self.collect_by_name("Diamond Eye")
self.assertLocationReachability(reachable=de_reachable, unreachable=de_unreachable, satisfied=in_logic)
- def testOoBGoal(self):
+ def test_oob_goal(self) -> None:
# still need Energy Core with OoB if sequence breaks are not in logic
for item in ["Gladiator Sword", "Diamond Eye", "Wheel", "Gauge"]:
self.collect_by_name(item)
diff --git a/worlds/soe/test/test_sequence_breaks.py b/worlds/soe/test/test_sequence_breaks.py
index 4248f9b47d97..2da8c9242cb9 100644
--- a/worlds/soe/test/test_sequence_breaks.py
+++ b/worlds/soe/test/test_sequence_breaks.py
@@ -6,7 +6,7 @@ class SequenceBreaksTest(SoETestBase):
"""Tests that 'on' doesn't put sequence breaks in logic. This is also the test base for in-logic."""
options: typing.Dict[str, typing.Any] = {"sequence_breaks": "on"}
- def testSequenceBreaksAccess(self):
+ def test_sequence_breaks_access(self) -> None:
in_logic = self.options["sequence_breaks"] == "logic"
# some locations that just need any weapon + sequence break
@@ -30,7 +30,7 @@ def testSequenceBreaksAccess(self):
self.collect_by_name("Bronze Spear") # Escape now just needs either Megataur or Rimsala dead
self.assertEqual(self.can_reach_location("Escape"), in_logic)
- def testSequenceBreaksGoal(self):
+ def test_sequence_breaks_goal(self) -> None:
in_logic = self.options["sequence_breaks"] == "logic"
# don't need Energy Core with sequence breaks in logic
diff --git a/worlds/soe/test/test_sniffamizer.py b/worlds/soe/test/test_sniffamizer.py
new file mode 100644
index 000000000000..45aff1fa4d6d
--- /dev/null
+++ b/worlds/soe/test/test_sniffamizer.py
@@ -0,0 +1,130 @@
+import typing
+from unittest import TestCase, skipUnless
+
+from . import SoETestBase
+from .. import pyevermizer
+from ..options import Sniffamizer
+
+
+class TestCount(TestCase):
+ """
+ Test that counts line up for sniff spots
+ """
+
+ def test_compare_counts(self) -> None:
+ self.assertEqual(len(pyevermizer.get_sniff_locations()), len(pyevermizer.get_sniff_items()),
+ "Sniff locations and sniff items don't line up")
+
+
+class Bases:
+ # class in class to avoid running tests for helper class
+ class TestSniffamizerLocal(SoETestBase):
+ """
+ Test that provided options do not add sniff items or locations
+ """
+ def test_no_sniff_items(self) -> None:
+ self.assertLess(len(self.multiworld.itempool), 500,
+ "Unexpected number of items")
+ for item in self.multiworld.itempool:
+ if item.code is not None:
+ self.assertLess(item.code, 65000,
+ "Unexpected item type")
+
+ def test_no_sniff_locations(self) -> None:
+ location_count = sum(1 for location in self.multiworld.get_locations(self.player) if location.item is None)
+ self.assertLess(location_count, 500,
+ "Unexpected number of locations")
+ for location in self.multiworld.get_locations(self.player):
+ if location.address is not None:
+ self.assertLess(location.address, 65000,
+ "Unexpected location type")
+ self.assertEqual(location_count, len(self.multiworld.itempool),
+ "Locations and item counts do not line up")
+
+ class TestSniffamizerPool(SoETestBase):
+ """
+ Test that provided options add sniff items and locations
+ """
+ def test_sniff_items(self) -> None:
+ self.assertGreater(len(self.multiworld.itempool), 500,
+ "Unexpected number of items")
+
+ def test_sniff_locations(self) -> None:
+ location_count = sum(1 for location in self.multiworld.get_locations(self.player) if location.item is None)
+ self.assertGreater(location_count, 500,
+ "Unexpected number of locations")
+ self.assertTrue(any(location.address is not None and location.address >= 65000
+ for location in self.multiworld.get_locations(self.player)),
+ "No sniff locations")
+ self.assertEqual(location_count, len(self.multiworld.itempool),
+ "Locations and item counts do not line up")
+
+
+class TestSniffamizerShuffle(Bases.TestSniffamizerLocal):
+ """
+ Test that shuffle does not add extra items or locations
+ """
+ options: typing.Dict[str, typing.Any] = {
+ "sniffamizer": "shuffle"
+ }
+
+ def test_flags(self) -> None:
+ # default -> no flags
+ flags = self.world.options.flags
+ self.assertNotIn("s", flags)
+ self.assertNotIn("S", flags)
+ self.assertNotIn("v", flags)
+
+
+@skipUnless(hasattr(Sniffamizer, "option_everywhere"), "Feature disabled")
+class TestSniffamizerEverywhereVanilla(Bases.TestSniffamizerPool):
+ """
+ Test that everywhere + vanilla ingredients does add extra items and locations
+ """
+ options: typing.Dict[str, typing.Any] = {
+ "sniffamizer": "everywhere",
+ "sniff_ingredients": "vanilla_ingredients",
+ }
+
+ def test_flags(self) -> None:
+ flags = self.world.options.flags
+ self.assertIn("S", flags)
+ self.assertNotIn("v", flags)
+
+
+@skipUnless(hasattr(Sniffamizer, "option_everywhere"), "Feature disabled")
+class TestSniffamizerEverywhereRandom(Bases.TestSniffamizerPool):
+ """
+ Test that everywhere + random ingredients also adds extra items and locations
+ """
+ options: typing.Dict[str, typing.Any] = {
+ "sniffamizer": "everywhere",
+ "sniff_ingredients": "random_ingredients",
+ }
+
+ def test_flags(self) -> None:
+ flags = self.world.options.flags
+ self.assertIn("S", flags)
+ self.assertIn("v", flags)
+
+
+@skipUnless(hasattr(Sniffamizer, "option_everywhere"), "Feature disabled")
+class EverywhereAccessTest(SoETestBase):
+ """
+ Test that everywhere has certain rules
+ """
+ options: typing.Dict[str, typing.Any] = {
+ "sniffamizer": "everywhere",
+ }
+
+ @staticmethod
+ def _resolve_numbers(spots: typing.Mapping[str, typing.Iterable[int]]) -> typing.List[str]:
+ return [f"{name} #{number}" for name, numbers in spots.items() for number in numbers]
+
+ def test_knight_basher(self) -> None:
+ locations = ["Mungola", "Lightning Storm"] + self._resolve_numbers({
+ "Gomi's Tower Sniff": range(473, 491),
+ "Gomi's Tower": range(195, 199),
+ })
+ items = [["Knight Basher"]]
+ self.assertAccessDependency(locations, items)
diff --git a/worlds/soe/test/test_traps.py b/worlds/soe/test/test_traps.py
new file mode 100644
index 000000000000..7babd4522b30
--- /dev/null
+++ b/worlds/soe/test/test_traps.py
@@ -0,0 +1,56 @@
+import typing
+from dataclasses import fields
+
+from . import SoETestBase
+from ..options import SoEOptions
+
+if typing.TYPE_CHECKING:
+ from .. import SoEWorld
+
+
+class Bases:
+ # class in class to avoid running tests for TrapTest class
+ class TrapTestBase(SoETestBase):
+ """Test base for trap tests"""
+ option_name_to_item_name = {
+ # filtering by name here validates that there is no confusion between name and type
+ field.name: field.type.item_name for field in fields(SoEOptions) if field.name.startswith("trap_chance_")
+ }
+
+ def test_dataclass(self) -> None:
+ """Test that the dataclass helper property returns the expected sequence"""
+ self.assertGreater(len(self.option_name_to_item_name), 0, "Expected more than 0 trap types")
+ world: "SoEWorld" = typing.cast("SoEWorld", self.multiworld.worlds[1])
+ item_name_to_rolled_option = {option.item_name: option for option in world.options.trap_chances}
+ # compare that all fields are present - that is property in dataclass and selector code in test line up
+ self.assertEqual(sorted(self.option_name_to_item_name.values()), sorted(item_name_to_rolled_option),
+ "field names probably do not match field types")
+ # sanity check that chances are correctly set and returned by property
+ for option_name, item_name in self.option_name_to_item_name.items():
+ self.assertEqual(item_name_to_rolled_option[item_name].value,
+ self.options.get(option_name, item_name_to_rolled_option[item_name].default))
+
+ def test_trap_count(self) -> None:
+ """Test that total trap count is correct"""
+ self.assertEqual(self.options["trap_count"],
+ len(self.get_items_by_name(self.option_name_to_item_name.values())))
+
+
+class TestTrapAllZeroChance(Bases.TrapTestBase):
+ """Tests all zero chances still gives traps if trap_count is set."""
+ options: typing.Dict[str, typing.Any] = {
+ "trap_count": 1,
+ **{name: 0 for name in Bases.TrapTestBase.option_name_to_item_name}
+ }
+
+
+class TestTrapNoConfound(Bases.TrapTestBase):
+ """Tests that one zero chance does not give that trap."""
+ options: typing.Dict[str, typing.Any] = {
+ "trap_count": 99,
+ "trap_chance_confound": 0,
+ }
+
+ def test_no_confound_trap(self) -> None:
+ self.assertEqual(self.option_name_to_item_name["trap_chance_confound"], "Confound Trap")
+ self.assertEqual(len(self.get_items_by_name("Confound Trap")), 0)
diff --git a/worlds/spire/docs/en_Slay the Spire.md b/worlds/spire/docs/en_Slay the Spire.md
index f4519455fa83..4591db58dc51 100644
--- a/worlds/spire/docs/en_Slay the Spire.md
+++ b/worlds/spire/docs/en_Slay the Spire.md
@@ -1,8 +1,8 @@
# Slay the Spire (PC)
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
diff --git a/worlds/spire/docs/slay-the-spire_en.md b/worlds/spire/docs/slay-the-spire_en.md
index d85a17d987b2..daeb65415196 100644
--- a/worlds/spire/docs/slay-the-spire_en.md
+++ b/worlds/spire/docs/slay-the-spire_en.md
@@ -43,8 +43,8 @@ an experience customized for their taste, and different players in the same mult
### Where do I get a YAML file?
-you can customize your settings by visiting
-the [Slay the Spire Settings Page](/games/Slay%20the%20Spire/player-settings).
+you can customize your options by visiting
+the [Slay the Spire Options Page](/games/Slay%20the%20Spire/player-options).
### Connect to the MultiServer
diff --git a/worlds/stardew_valley/__init__.py b/worlds/stardew_valley/__init__.py
index aa825af302eb..e25fd8eb9a58 100644
--- a/worlds/stardew_valley/__init__.py
+++ b/worlds/stardew_valley/__init__.py
@@ -1,21 +1,28 @@
import logging
-from typing import Dict, Any, Iterable, Optional, Union, Set, List
+from typing import Dict, Any, Iterable, Optional, Union, List, TextIO
-from BaseClasses import Region, Entrance, Location, Item, Tutorial, CollectionState, ItemClassification, MultiWorld, Group as ItemLinkGroup
+from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification, MultiWorld
from Options import PerGameCommonOptions
from worlds.AutoWorld import World, WebWorld
from . import rules
-from .bundles import get_all_bundles, Bundle
+from .bundles.bundle_room import BundleRoom
+from .bundles.bundles import get_all_bundles
+from .early_items import setup_early_items
from .items import item_table, create_items, ItemData, Group, items_by_group, get_all_filler_items, remove_limited_amount_packs
-from .locations import location_table, create_locations, LocationData
-from .logic import StardewLogic, StardewRule, True_, MAX_MONTHS
+from .locations import location_table, create_locations, LocationData, locations_by_tag
+from .logic.bundle_logic import BundleLogic
+from .logic.logic import StardewLogic
+from .logic.time_logic import MAX_MONTHS
from .options import StardewValleyOptions, SeasonRandomization, Goal, BundleRandomization, BundlePrice, NumberOfLuckBuffs, NumberOfMovementBuffs, \
- BackpackProgression, BuildingProgression, ExcludeGingerIsland, TrapItems
+ BackpackProgression, BuildingProgression, ExcludeGingerIsland, TrapItems, EntranceRandomization
from .presets import sv_options_presets
from .regions import create_regions
from .rules import set_rules
-from worlds.generic.Rules import set_rule
+from .stardew_rule import True_, StardewRule, HasProgressionPercent
+from .strings.ap_names.event_names import Event
+from .strings.entrance_names import Entrance as EntranceName
from .strings.goal_names import Goal as GoalName
+from .strings.region_names import Region as RegionName
client_version = 0
@@ -59,6 +66,15 @@ class StardewValleyWorld(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_name_groups = {
+ group.name.replace("_", " ").title() + (" Group" if group.name.replace("_", " ").title() in item_table else ""):
+ [item.name for item in items] for group, items in items_by_group.items()
+ }
+ location_name_groups = {
+ group.name.replace("_", " ").title() + (" Group" if group.name.replace("_", " ").title() in locations_by_tag else ""):
+ [location.name for location in locations] for group, locations in locations_by_tag.items()
+ }
+
data_version = 3
required_client_version = (0, 4, 0)
@@ -67,24 +83,21 @@ class StardewValleyWorld(World):
logic: StardewLogic
web = StardewWebWorld()
- modified_bundles: Dict[str, Bundle]
+ modified_bundles: List[BundleRoom]
randomized_entrances: Dict[str, str]
- all_progression_items: Set[str]
+ total_progression_items: int
+
+ # all_progression_items: Dict[str, int] # If you need to debug total_progression_items, uncommenting this will help tremendously
- def __init__(self, world: MultiWorld, player: int):
- super().__init__(world, player)
- self.all_progression_items = set()
+ def __init__(self, multiworld: MultiWorld, player: int):
+ super().__init__(multiworld, player)
self.filler_item_pool_names = []
+ self.total_progression_items = 0
+ # self.all_progression_items = dict()
def generate_early(self):
self.force_change_options_if_incompatible()
- self.logic = StardewLogic(self.player, self.options)
- self.modified_bundles = get_all_bundles(self.multiworld.random,
- self.logic,
- self.options.bundle_randomization,
- self.options.bundle_price)
-
def force_change_options_if_incompatible(self):
goal_is_walnut_hunter = self.options.goal == Goal.option_greatest_walnut_hunter
goal_is_perfection = self.options.goal == Goal.option_perfection
@@ -94,7 +107,8 @@ def force_change_options_if_incompatible(self):
self.options.exclude_ginger_island.value = ExcludeGingerIsland.option_false
goal_name = self.options.goal.current_key
player_name = self.multiworld.player_name[self.player]
- logging.warning(f"Goal '{goal_name}' requires Ginger Island. Exclude Ginger Island setting forced to 'False' for player {self.player} ({player_name})")
+ logging.warning(
+ f"Goal '{goal_name}' requires Ginger Island. Exclude Ginger Island setting forced to 'False' for player {self.player} ({player_name})")
def create_regions(self):
def create_region(name: str, exits: Iterable[str]) -> Region:
@@ -102,15 +116,19 @@ def create_region(name: str, exits: Iterable[str]) -> Region:
region.exits = [Entrance(self.player, exit_name, region) for exit_name in exits]
return region
- world_regions, self.randomized_entrances = create_regions(create_region, self.multiworld.random, self.options)
+ world_regions, world_entrances, self.randomized_entrances = create_regions(create_region, self.random, self.options)
+
+ self.logic = StardewLogic(self.player, self.options, world_regions.keys())
+ self.modified_bundles = get_all_bundles(self.random,
+ self.logic,
+ self.options)
def add_location(name: str, code: Optional[int], region: str):
region = world_regions[region]
location = StardewLocation(self.player, name, code, region)
- location.access_rule = lambda _: True
region.locations.append(location)
- create_locations(add_location, self.options, self.multiworld.random)
+ create_locations(add_location, self.modified_bundles, self.options, self.random)
self.multiworld.regions.extend(world_regions.values())
def create_items(self):
@@ -128,16 +146,16 @@ def create_items(self):
for location in self.multiworld.get_locations(self.player)
if not location.event])
- created_items = create_items(self.create_item, locations_count, items_to_exclude, self.options,
- self.multiworld.random)
+ created_items = create_items(self.create_item, self.delete_item, locations_count, items_to_exclude, self.options,
+ self.random)
self.multiworld.itempool += created_items
- self.setup_early_items()
- self.setup_month_events()
+ setup_early_items(self.multiworld, self.options, self.player, self.random)
+ self.setup_player_events()
self.setup_victory()
- def precollect_starting_season(self) -> Optional[StardewItem]:
+ def precollect_starting_season(self):
if self.options.season_randomization == SeasonRandomization.option_progressive:
return
@@ -145,7 +163,7 @@ def precollect_starting_season(self) -> Optional[StardewItem]:
if self.options.season_randomization == SeasonRandomization.option_disabled:
for season in season_pool:
- self.multiworld.push_precollected(self.create_item(season))
+ self.multiworld.push_precollected(self.create_starting_item(season))
return
if [item for item in self.multiworld.precollected_items[self.player]
@@ -155,75 +173,128 @@ def precollect_starting_season(self) -> Optional[StardewItem]:
if self.options.season_randomization == SeasonRandomization.option_randomized_not_winter:
season_pool = [season for season in season_pool if season.name != "Winter"]
- starting_season = self.create_item(self.multiworld.random.choice(season_pool))
+ starting_season = self.create_starting_item(self.random.choice(season_pool))
self.multiworld.push_precollected(starting_season)
- def setup_early_items(self):
- if (self.options.building_progression ==
- BuildingProgression.option_progressive_early_shipping_bin):
- self.multiworld.early_items[self.player]["Shipping Bin"] = 1
+ def setup_player_events(self):
+ self.setup_construction_events()
+ self.setup_quest_events()
+ self.setup_action_events()
- if self.options.backpack_progression == BackpackProgression.option_early_progressive:
- self.multiworld.early_items[self.player]["Progressive Backpack"] = 1
+ def setup_construction_events(self):
+ can_construct_buildings = LocationData(None, RegionName.carpenter, Event.can_construct_buildings)
+ self.create_event_location(can_construct_buildings, True_(), Event.can_construct_buildings)
- def setup_month_events(self):
- for i in range(0, MAX_MONTHS):
- month_end = LocationData(None, "Stardew Valley", f"Month End {i + 1}")
- if i == 0:
- self.create_event_location(month_end, True_(), "Month End")
- continue
+ def setup_quest_events(self):
+ start_dark_talisman_quest = LocationData(None, RegionName.railroad, Event.start_dark_talisman_quest)
+ self.create_event_location(start_dark_talisman_quest, self.logic.wallet.has_rusty_key(), Event.start_dark_talisman_quest)
- self.create_event_location(month_end, self.logic.received("Month End", i).simplify(), "Month End")
+ def setup_action_events(self):
+ can_ship_event = LocationData(None, RegionName.shipping, Event.can_ship_items)
+ self.create_event_location(can_ship_event, True_(), Event.can_ship_items)
+ can_shop_pierre_event = LocationData(None, RegionName.pierre_store, Event.can_shop_at_pierre)
+ self.create_event_location(can_shop_pierre_event, True_(), Event.can_shop_at_pierre)
def setup_victory(self):
if self.options.goal == Goal.option_community_center:
self.create_event_location(location_table[GoalName.community_center],
- self.logic.can_complete_community_center().simplify(),
- "Victory")
+ self.logic.bundle.can_complete_community_center,
+ Event.victory)
elif self.options.goal == Goal.option_grandpa_evaluation:
self.create_event_location(location_table[GoalName.grandpa_evaluation],
- self.logic.can_finish_grandpa_evaluation().simplify(),
- "Victory")
+ self.logic.can_finish_grandpa_evaluation(),
+ Event.victory)
elif self.options.goal == Goal.option_bottom_of_the_mines:
self.create_event_location(location_table[GoalName.bottom_of_the_mines],
- self.logic.can_mine_to_floor(120).simplify(),
- "Victory")
+ True_(),
+ Event.victory)
elif self.options.goal == Goal.option_cryptic_note:
self.create_event_location(location_table[GoalName.cryptic_note],
- self.logic.can_complete_quest("Cryptic Note").simplify(),
- "Victory")
+ self.logic.quest.can_complete_quest("Cryptic Note"),
+ Event.victory)
elif self.options.goal == Goal.option_master_angler:
self.create_event_location(location_table[GoalName.master_angler],
- self.logic.can_catch_every_fish().simplify(),
- "Victory")
+ self.logic.fishing.can_catch_every_fish_in_slot(self.get_all_location_names()),
+ Event.victory)
elif self.options.goal == Goal.option_complete_collection:
self.create_event_location(location_table[GoalName.complete_museum],
- self.logic.can_complete_museum().simplify(),
- "Victory")
+ self.logic.museum.can_complete_museum(),
+ Event.victory)
elif self.options.goal == Goal.option_full_house:
self.create_event_location(location_table[GoalName.full_house],
- (self.logic.has_children(2) & self.logic.can_reproduce()).simplify(),
- "Victory")
+ (self.logic.relationship.has_children(2) & self.logic.relationship.can_reproduce()),
+ Event.victory)
elif self.options.goal == Goal.option_greatest_walnut_hunter:
self.create_event_location(location_table[GoalName.greatest_walnut_hunter],
- self.logic.has_walnut(130).simplify(),
- "Victory")
+ self.logic.has_walnut(130),
+ Event.victory)
+ elif self.options.goal == Goal.option_protector_of_the_valley:
+ self.create_event_location(location_table[GoalName.protector_of_the_valley],
+ self.logic.monster.can_complete_all_monster_slaying_goals(),
+ Event.victory)
+ elif self.options.goal == Goal.option_full_shipment:
+ self.create_event_location(location_table[GoalName.full_shipment],
+ self.logic.shipping.can_ship_everything_in_slot(self.get_all_location_names()),
+ Event.victory)
+ elif self.options.goal == Goal.option_gourmet_chef:
+ self.create_event_location(location_table[GoalName.gourmet_chef],
+ self.logic.cooking.can_cook_everything,
+ Event.victory)
+ elif self.options.goal == Goal.option_craft_master:
+ self.create_event_location(location_table[GoalName.craft_master],
+ self.logic.crafting.can_craft_everything,
+ Event.victory)
+ elif self.options.goal == Goal.option_legend:
+ self.create_event_location(location_table[GoalName.legend],
+ self.logic.money.can_have_earned_total(10_000_000),
+ Event.victory)
+ elif self.options.goal == Goal.option_mystery_of_the_stardrops:
+ self.create_event_location(location_table[GoalName.mystery_of_the_stardrops],
+ self.logic.has_all_stardrops(),
+ Event.victory)
+ elif self.options.goal == Goal.option_allsanity:
+ self.create_event_location(location_table[GoalName.allsanity],
+ HasProgressionPercent(self.player, 100),
+ Event.victory)
elif self.options.goal == Goal.option_perfection:
self.create_event_location(location_table[GoalName.perfection],
- self.logic.has_everything(self.all_progression_items).simplify(),
- "Victory")
+ HasProgressionPercent(self.player, 100),
+ Event.victory)
+
+ self.multiworld.completion_condition[self.player] = lambda state: state.has(Event.victory, self.player)
+
+ def get_all_location_names(self) -> List[str]:
+ return list(location.name for location in self.multiworld.get_locations(self.player))
+
+ def create_item(self, item: Union[str, ItemData], override_classification: ItemClassification = None) -> StardewItem:
+ if isinstance(item, str):
+ item = item_table[item]
+
+ if override_classification is None:
+ override_classification = item.classification
+
+ if override_classification == ItemClassification.progression and item.name != Event.victory:
+ self.total_progression_items += 1
+ # if item.name not in self.all_progression_items:
+ # self.all_progression_items[item.name] = 0
+ # self.all_progression_items[item.name] += 1
+ return StardewItem(item.name, override_classification, item.code, self.player)
- self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
+ def delete_item(self, item: Item):
+ if item.classification & ItemClassification.progression:
+ self.total_progression_items -= 1
+ # if item.name in self.all_progression_items:
+ # self.all_progression_items[item.name] -= 1
- def create_item(self, item: Union[str, ItemData]) -> StardewItem:
+ def create_starting_item(self, item: Union[str, ItemData]) -> StardewItem:
if isinstance(item, str):
item = item_table[item]
- if item.classification == ItemClassification.progression:
- self.all_progression_items.add(item.name)
return StardewItem(item.name, item.classification, item.code, self.player)
- def create_event_location(self, location_data: LocationData, rule: StardewRule, item: Optional[str] = None):
+ def create_event_location(self, location_data: LocationData, rule: StardewRule = None, item: Optional[str] = None):
+ if rule is None:
+ rule = True_()
if item is None:
item = location_data.name
@@ -235,37 +306,6 @@ def create_event_location(self, location_data: LocationData, rule: StardewRule,
def set_rules(self):
set_rules(self)
- self.force_first_month_once_all_early_items_are_found()
-
- def force_first_month_once_all_early_items_are_found(self):
- """
- The Fill algorithm sweeps all event when calculating the early location. This causes an issue where
- location only locked behind event are considered early, which they are not really...
-
- This patches the issue, by adding a dependency to the first month end on all early items, so all the locations
- that depends on it will not be considered early. This requires at least one early item to be progression, or
- it just won't work...
- """
-
- early_items = []
- for player, item_count in self.multiworld.early_items.items():
- for item, count in item_count.items():
- if self.multiworld.worlds[player].create_item(item).advancement:
- early_items.append((player, item, count))
-
- for item, count in self.multiworld.local_early_items[self.player].items():
- if self.create_item(item).advancement:
- early_items.append((self.player, item, count))
-
- def first_month_require_all_early_items(state: CollectionState) -> bool:
- for player, item, count in early_items:
- if not state.has(item, player, count):
- return False
-
- return True
-
- first_month_end = self.multiworld.get_location("Month End 1", self.player)
- set_rule(first_month_end, first_month_require_all_early_items)
def generate_basic(self):
pass
@@ -283,13 +323,12 @@ def generate_filler_item_pool_names(self):
def get_filler_item_rules(self):
if self.player in self.multiworld.groups:
- link_group: ItemLinkGroup = self.multiworld.groups[self.player]
+ link_group = self.multiworld.groups[self.player]
include_traps = True
exclude_island = False
for player in link_group["players"]:
player_options = self.multiworld.worlds[player].options
if self.multiworld.game[player] != self.game:
-
continue
if player_options.trap_items == TrapItems.option_no_traps:
include_traps = False
@@ -299,24 +338,57 @@ def get_filler_item_rules(self):
else:
return self.options.trap_items != TrapItems.option_no_traps, self.options.exclude_ginger_island == ExcludeGingerIsland.option_true
- def fill_slot_data(self) -> Dict[str, Any]:
+ def write_spoiler_header(self, spoiler_handle: TextIO) -> None:
+ """Write to the spoiler header. If individual it's right at the end of that player's options,
+ if as stage it's right under the common header before per-player options."""
+ self.add_entrances_to_spoiler_log()
+
+ def write_spoiler(self, spoiler_handle: TextIO) -> None:
+ """Write to the spoiler "middle", this is after the per-player options and before locations,
+ meant for useful or interesting info."""
+ self.add_bundles_to_spoiler_log(spoiler_handle)
- modified_bundles = {}
- for bundle_key in self.modified_bundles:
- key, value = self.modified_bundles[bundle_key].to_pair()
- modified_bundles[key] = value
+ def add_bundles_to_spoiler_log(self, spoiler_handle: TextIO):
+ if self.options.bundle_randomization == BundleRandomization.option_vanilla:
+ return
+ player_name = self.multiworld.get_player_name(self.player)
+ spoiler_handle.write(f"\n\nCommunity Center ({player_name}):\n")
+ for room in self.modified_bundles:
+ for bundle in room.bundles:
+ spoiler_handle.write(f"\t[{room.name}] {bundle.name} ({bundle.number_required} required):\n")
+ for i, item in enumerate(bundle.items):
+ if "Basic" in item.quality:
+ quality = ""
+ else:
+ quality = f" ({item.quality.split(' ')[0]})"
+ spoiler_handle.write(f"\t\t{item.amount}x {item.item_name}{quality}\n")
+
+ def add_entrances_to_spoiler_log(self):
+ if self.options.entrance_randomization == EntranceRandomization.option_disabled:
+ return
+ for original_entrance, replaced_entrance in self.randomized_entrances.items():
+ self.multiworld.spoiler.set_entrance(original_entrance, replaced_entrance, "entrance", self.player)
- excluded_options = [BundleRandomization, BundlePrice, NumberOfMovementBuffs, NumberOfLuckBuffs]
+ def fill_slot_data(self) -> Dict[str, Any]:
+ bundles = dict()
+ for room in self.modified_bundles:
+ bundles[room.name] = dict()
+ for bundle in room.bundles:
+ bundles[room.name][bundle.name] = {"number_required": bundle.number_required}
+ for i, item in enumerate(bundle.items):
+ bundles[room.name][bundle.name][i] = f"{item.item_name}|{item.amount}|{item.quality}"
+
+ excluded_options = [BundleRandomization, NumberOfMovementBuffs, NumberOfLuckBuffs]
excluded_option_names = [option.internal_name for option in excluded_options]
generic_option_names = [option_name for option_name in PerGameCommonOptions.type_hints]
excluded_option_names.extend(generic_option_names)
included_option_names: List[str] = [option_name for option_name in self.options_dataclass.type_hints if option_name not in excluded_option_names]
slot_data = self.options.as_dict(*included_option_names)
slot_data.update({
- "seed": self.multiworld.per_slot_randoms[self.player].randrange(1000000000), # Seed should be max 9 digits
+ "seed": self.random.randrange(1000000000), # Seed should be max 9 digits
"randomized_entrances": self.randomized_entrances,
- "modified_bundles": modified_bundles,
- "client_version": "4.0.0",
+ "modified_bundles": bundles,
+ "client_version": "5.0.0",
})
return slot_data
diff --git a/worlds/stardew_valley/bundles.py b/worlds/stardew_valley/bundles.py
deleted file mode 100644
index 4af21542a4ec..000000000000
--- a/worlds/stardew_valley/bundles.py
+++ /dev/null
@@ -1,254 +0,0 @@
-from random import Random
-from typing import List, Dict, Union
-
-from .data.bundle_data import *
-from .logic import StardewLogic
-from .options import BundleRandomization, BundlePrice
-
-vanilla_bundles = {
- "Pantry/0": "Spring Crops/O 465 20/24 1 0 188 1 0 190 1 0 192 1 0/0",
- "Pantry/1": "Summer Crops/O 621 1/256 1 0 260 1 0 258 1 0 254 1 0/3",
- "Pantry/2": "Fall Crops/BO 10 1/270 1 0 272 1 0 276 1 0 280 1 0/2",
- "Pantry/3": "Quality Crops/BO 15 1/24 5 2 254 5 2 276 5 2 270 5 2/6/3",
- "Pantry/4": "Animal/BO 16 1/186 1 0 182 1 0 174 1 0 438 1 0 440 1 0 442 1 0/4/5",
- # 639 1 0 640 1 0 641 1 0 642 1 0 643 1 0
- "Pantry/5": "Artisan/BO 12 1/432 1 0 428 1 0 426 1 0 424 1 0 340 1 0 344 1 0 613 1 0 634 1 0 635 1 0 636 1 0 637 1 0 638 1 0/1/6",
- "Crafts Room/13": "Spring Foraging/O 495 30/16 1 0 18 1 0 20 1 0 22 1 0/0",
- "Crafts Room/14": "Summer Foraging/O 496 30/396 1 0 398 1 0 402 1 0/3",
- "Crafts Room/15": "Fall Foraging/O 497 30/404 1 0 406 1 0 408 1 0 410 1 0/2",
- "Crafts Room/16": "Winter Foraging/O 498 30/412 1 0 414 1 0 416 1 0 418 1 0/6",
- "Crafts Room/17": "Construction/BO 114 1/388 99 0 388 99 0 390 99 0 709 10 0/4",
- "Crafts Room/19": "Exotic Foraging/O 235 5/88 1 0 90 1 0 78 1 0 420 1 0 422 1 0 724 1 0 725 1 0 726 1 0 257 1 0/1/5",
- "Fish Tank/6": "River Fish/O 685 30/145 1 0 143 1 0 706 1 0 699 1 0/6",
- "Fish Tank/7": "Lake Fish/O 687 1/136 1 0 142 1 0 700 1 0 698 1 0/0",
- "Fish Tank/8": "Ocean Fish/O 690 5/131 1 0 130 1 0 150 1 0 701 1 0/5",
- "Fish Tank/9": "Night Fishing/R 516 1/140 1 0 132 1 0 148 1 0/1",
- "Fish Tank/10": "Specialty Fish/O 242 5/128 1 0 156 1 0 164 1 0 734 1 0/4",
- "Fish Tank/11": "Crab Pot/O 710 3/715 1 0 716 1 0 717 1 0 718 1 0 719 1 0 720 1 0 721 1 0 722 1 0 723 1 0 372 1 0/1/5",
- "Boiler Room/20": "Blacksmith's/BO 13 1/334 1 0 335 1 0 336 1 0/2",
- "Boiler Room/21": "Geologist's/O 749 5/80 1 0 86 1 0 84 1 0 82 1 0/1",
- "Boiler Room/22": "Adventurer's/R 518 1/766 99 0 767 10 0 768 1 0 769 1 0/1/2",
- "Vault/23": "2,500g/O 220 3/-1 2500 2500/4",
- "Vault/24": "5,000g/O 369 30/-1 5000 5000/2",
- "Vault/25": "10,000g/BO 9 1/-1 10000 10000/3",
- "Vault/26": "25,000g/BO 21 1/-1 25000 25000/1",
- "Bulletin Board/31": "Chef's/O 221 3/724 1 0 259 1 0 430 1 0 376 1 0 228 1 0 194 1 0/4",
- "Bulletin Board/32": "Field Research/BO 20 1/422 1 0 392 1 0 702 1 0 536 1 0/5",
- "Bulletin Board/33": "Enchanter's/O 336 5/725 1 0 348 1 0 446 1 0 637 1 0/1",
- "Bulletin Board/34": "Dye/BO 25 1/420 1 0 397 1 0 421 1 0 444 1 0 62 1 0 266 1 0/6",
- "Bulletin Board/35": "Fodder/BO 104 1/262 10 0 178 10 0 613 3 0/3",
- # "Abandoned Joja Mart/36": "The Missing//348 1 1 807 1 0 74 1 0 454 5 2 795 1 2 445 1 0/1/5"
-}
-
-
-class Bundle:
- room: str
- sprite: str
- original_name: str
- name: str
- rewards: List[str]
- requirements: List[BundleItem]
- color: str
- number_required: int
-
- def __init__(self, key: str, value: str):
- key_parts = key.split("/")
- self.room = key_parts[0]
- self.sprite = key_parts[1]
-
- value_parts = value.split("/")
- self.original_name = value_parts[0]
- self.name = value_parts[0]
- self.rewards = self.parse_stardew_objects(value_parts[1])
- self.requirements = self.parse_stardew_bundle_items(value_parts[2])
- self.color = value_parts[3]
- if len(value_parts) > 4:
- self.number_required = int(value_parts[4])
- else:
- self.number_required = len(self.requirements)
-
- def __repr__(self):
- return f"{self.original_name} -> {repr(self.requirements)}"
-
- def get_name_with_bundle(self) -> str:
- return f"{self.original_name} Bundle"
-
- def to_pair(self) -> (str, str):
- key = f"{self.room}/{self.sprite}"
- str_rewards = ""
- for reward in self.rewards:
- str_rewards += f" {reward}"
- str_rewards = str_rewards.strip()
- str_requirements = ""
- for requirement in self.requirements:
- str_requirements += f" {requirement.item.item_id} {requirement.amount} {requirement.quality}"
- str_requirements = str_requirements.strip()
- value = f"{self.name}/{str_rewards}/{str_requirements}/{self.color}/{self.number_required}"
- return key, value
-
- def remove_rewards(self):
- self.rewards = []
-
- def change_number_required(self, difference: int):
- self.number_required = min(len(self.requirements), max(1, self.number_required + difference))
- if len(self.requirements) == 1 and self.requirements[0].item.item_id == -1:
- one_fifth = self.requirements[0].amount / 5
- new_amount = int(self.requirements[0].amount + (difference * one_fifth))
- self.requirements[0] = BundleItem.money_bundle(new_amount)
- thousand_amount = int(new_amount / 1000)
- dollar_amount = str(new_amount % 1000)
- while len(dollar_amount) < 3:
- dollar_amount = f"0{dollar_amount}"
- self.name = f"{thousand_amount},{dollar_amount}g"
-
- def randomize_requirements(self, random: Random,
- potential_requirements: Union[List[BundleItem], List[List[BundleItem]]]):
- if not potential_requirements:
- return
-
- number_to_generate = len(self.requirements)
- self.requirements.clear()
- if number_to_generate > len(potential_requirements):
- choices: Union[BundleItem, List[BundleItem]] = random.choices(potential_requirements, k=number_to_generate)
- else:
- choices: Union[BundleItem, List[BundleItem]] = random.sample(potential_requirements, number_to_generate)
- for choice in choices:
- if isinstance(choice, BundleItem):
- self.requirements.append(choice)
- else:
- self.requirements.append(random.choice(choice))
-
- def assign_requirements(self, new_requirements: List[BundleItem]) -> List[BundleItem]:
- number_to_generate = len(self.requirements)
- self.requirements.clear()
- for requirement in new_requirements:
- self.requirements.append(requirement)
- if len(self.requirements) >= number_to_generate:
- return new_requirements[number_to_generate:]
-
- @staticmethod
- def parse_stardew_objects(string_objects: str) -> List[str]:
- objects = []
- if len(string_objects) < 5:
- return objects
- rewards_parts = string_objects.split(" ")
- for index in range(0, len(rewards_parts), 3):
- objects.append(f"{rewards_parts[index]} {rewards_parts[index + 1]} {rewards_parts[index + 2]}")
- return objects
-
- @staticmethod
- def parse_stardew_bundle_items(string_objects: str) -> List[BundleItem]:
- bundle_items = []
- parts = string_objects.split(" ")
- for index in range(0, len(parts), 3):
- item_id = int(parts[index])
- bundle_item = BundleItem(all_bundle_items_by_id[item_id].item,
- int(parts[index + 1]),
- int(parts[index + 2]))
- bundle_items.append(bundle_item)
- return bundle_items
-
- # Shuffling the Vault doesn't really work with the stardew system in place
- # shuffle_vault_amongst_themselves(random, bundles)
-
-
-def get_all_bundles(random: Random, logic: StardewLogic, randomization: BundleRandomization, price: BundlePrice) -> Dict[str, Bundle]:
- bundles = {}
- for bundle_key in vanilla_bundles:
- bundle_value = vanilla_bundles[bundle_key]
- bundle = Bundle(bundle_key, bundle_value)
- bundles[bundle.get_name_with_bundle()] = bundle
-
- if randomization == BundleRandomization.option_thematic:
- shuffle_bundles_thematically(random, bundles)
- elif randomization == BundleRandomization.option_shuffled:
- shuffle_bundles_completely(random, logic, bundles)
-
- price_difference = 0
- if price == BundlePrice.option_very_cheap:
- price_difference = -2
- elif price == BundlePrice.option_cheap:
- price_difference = -1
- elif price == BundlePrice.option_expensive:
- price_difference = 1
-
- for bundle_key in bundles:
- bundles[bundle_key].remove_rewards()
- bundles[bundle_key].change_number_required(price_difference)
-
- return bundles
-
-
-def shuffle_bundles_completely(random: Random, logic: StardewLogic, bundles: Dict[str, Bundle]):
- total_required_item_number = sum(len(bundle.requirements) for bundle in bundles.values())
- quality_crops_items_set = set(quality_crops_items)
- all_bundle_items_without_quality_and_money = [item
- for item in all_bundle_items_except_money
- if item not in quality_crops_items_set] + \
- random.sample(quality_crops_items, 10)
- choices = random.sample(all_bundle_items_without_quality_and_money, total_required_item_number - 4)
-
- items_sorted = sorted(choices, key=lambda x: logic.item_rules[x.item.name].get_difficulty())
-
- keys = sorted(bundles.keys())
- random.shuffle(keys)
-
- for key in keys:
- if not bundles[key].original_name.endswith("00g"):
- items_sorted = bundles[key].assign_requirements(items_sorted)
-
-
-def shuffle_bundles_thematically(random: Random, bundles: Dict[str, Bundle]):
- shuffle_crafts_room_bundle_thematically(random, bundles)
- shuffle_pantry_bundle_thematically(random, bundles)
- shuffle_fish_tank_thematically(random, bundles)
- shuffle_boiler_room_thematically(random, bundles)
- shuffle_bulletin_board_thematically(random, bundles)
-
-
-def shuffle_crafts_room_bundle_thematically(random: Random, bundles: Dict[str, Bundle]):
- bundles["Spring Foraging Bundle"].randomize_requirements(random, spring_foraging_items)
- bundles["Summer Foraging Bundle"].randomize_requirements(random, summer_foraging_items)
- bundles["Fall Foraging Bundle"].randomize_requirements(random, fall_foraging_items)
- bundles["Winter Foraging Bundle"].randomize_requirements(random, winter_foraging_items)
- bundles["Exotic Foraging Bundle"].randomize_requirements(random, exotic_foraging_items)
- bundles["Construction Bundle"].randomize_requirements(random, construction_items)
-
-
-def shuffle_pantry_bundle_thematically(random: Random, bundles: Dict[str, Bundle]):
- bundles["Spring Crops Bundle"].randomize_requirements(random, spring_crop_items)
- bundles["Summer Crops Bundle"].randomize_requirements(random, summer_crops_items)
- bundles["Fall Crops Bundle"].randomize_requirements(random, fall_crops_items)
- bundles["Quality Crops Bundle"].randomize_requirements(random, quality_crops_items)
- bundles["Animal Bundle"].randomize_requirements(random, animal_product_items)
- bundles["Artisan Bundle"].randomize_requirements(random, artisan_goods_items)
-
-
-def shuffle_fish_tank_thematically(random: Random, bundles: Dict[str, Bundle]):
- bundles["River Fish Bundle"].randomize_requirements(random, river_fish_items)
- bundles["Lake Fish Bundle"].randomize_requirements(random, lake_fish_items)
- bundles["Ocean Fish Bundle"].randomize_requirements(random, ocean_fish_items)
- bundles["Night Fishing Bundle"].randomize_requirements(random, night_fish_items)
- bundles["Crab Pot Bundle"].randomize_requirements(random, crab_pot_items)
- bundles["Specialty Fish Bundle"].randomize_requirements(random, specialty_fish_items)
-
-
-def shuffle_boiler_room_thematically(random: Random, bundles: Dict[str, Bundle]):
- bundles["Blacksmith's Bundle"].randomize_requirements(random, blacksmith_items)
- bundles["Geologist's Bundle"].randomize_requirements(random, geologist_items)
- bundles["Adventurer's Bundle"].randomize_requirements(random, adventurer_items)
-
-
-def shuffle_bulletin_board_thematically(random: Random, bundles: Dict[str, Bundle]):
- bundles["Chef's Bundle"].randomize_requirements(random, chef_items)
- bundles["Dye Bundle"].randomize_requirements(random, dye_items)
- bundles["Field Research Bundle"].randomize_requirements(random, field_research_items)
- bundles["Fodder Bundle"].randomize_requirements(random, fodder_items)
- bundles["Enchanter's Bundle"].randomize_requirements(random, enchanter_items)
-
-
-def shuffle_vault_amongst_themselves(random: Random, bundles: Dict[str, Bundle]):
- bundles["2,500g Bundle"].randomize_requirements(random, vault_bundle_items)
- bundles["5,000g Bundle"].randomize_requirements(random, vault_bundle_items)
- bundles["10,000g Bundle"].randomize_requirements(random, vault_bundle_items)
- bundles["25,000g Bundle"].randomize_requirements(random, vault_bundle_items)
diff --git a/worlds/stardew_valley/bundles/__init__.py b/worlds/stardew_valley/bundles/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/worlds/stardew_valley/bundles/bundle.py b/worlds/stardew_valley/bundles/bundle.py
new file mode 100644
index 000000000000..199826b96bc8
--- /dev/null
+++ b/worlds/stardew_valley/bundles/bundle.py
@@ -0,0 +1,163 @@
+from dataclasses import dataclass
+from random import Random
+from typing import List
+
+from .bundle_item import BundleItem
+from ..options import BundlePrice, StardewValleyOptions, ExcludeGingerIsland, FestivalLocations
+from ..strings.currency_names import Currency
+
+
+@dataclass
+class Bundle:
+ room: str
+ name: str
+ items: List[BundleItem]
+ number_required: int
+
+ def __repr__(self):
+ return f"{self.name} -> {self.number_required} from {repr(self.items)}"
+
+
+@dataclass
+class BundleTemplate:
+ room: str
+ name: str
+ items: List[BundleItem]
+ number_possible_items: int
+ number_required_items: int
+
+ def __init__(self, room: str, name: str, items: List[BundleItem], number_possible_items: int, number_required_items: int):
+ self.room = room
+ self.name = name
+ self.items = items
+ self.number_possible_items = number_possible_items
+ self.number_required_items = number_required_items
+
+ @staticmethod
+ def extend_from(template, items: List[BundleItem]):
+ return BundleTemplate(template.room, template.name, items, template.number_possible_items, template.number_required_items)
+
+ def create_bundle(self, bundle_price_option: BundlePrice, random: Random, options: StardewValleyOptions) -> Bundle:
+ if bundle_price_option == BundlePrice.option_minimum:
+ number_required = 1
+ elif bundle_price_option == BundlePrice.option_maximum:
+ number_required = 8
+ else:
+ number_required = self.number_required_items + bundle_price_option.value
+ number_required = max(1, number_required)
+ filtered_items = [item for item in self.items if item.can_appear(options)]
+ number_items = len(filtered_items)
+ number_chosen_items = self.number_possible_items
+ if number_chosen_items < number_required:
+ number_chosen_items = number_required
+
+ if number_chosen_items > number_items:
+ chosen_items = filtered_items + random.choices(filtered_items, k=number_chosen_items - number_items)
+ else:
+ chosen_items = random.sample(filtered_items, number_chosen_items)
+ return Bundle(self.room, self.name, chosen_items, number_required)
+
+ def can_appear(self, options: StardewValleyOptions) -> bool:
+ return True
+
+
+class CurrencyBundleTemplate(BundleTemplate):
+ item: BundleItem
+
+ def __init__(self, room: str, name: str, item: BundleItem):
+ super().__init__(room, name, [item], 1, 1)
+ self.item = item
+
+ def create_bundle(self, bundle_price_option: BundlePrice, random: Random, options: StardewValleyOptions) -> Bundle:
+ currency_amount = self.get_currency_amount(bundle_price_option)
+ return Bundle(self.room, self.name, [BundleItem(self.item.item_name, currency_amount)], 1)
+
+ def get_currency_amount(self, bundle_price_option: BundlePrice):
+ if bundle_price_option == BundlePrice.option_minimum:
+ price_multiplier = 0.1
+ elif bundle_price_option == BundlePrice.option_maximum:
+ price_multiplier = 4
+ else:
+ price_multiplier = round(1 + (bundle_price_option.value * 0.4), 2)
+
+ currency_amount = int(self.item.amount * price_multiplier)
+ return currency_amount
+
+ def can_appear(self, options: StardewValleyOptions) -> bool:
+ if options.exclude_ginger_island == ExcludeGingerIsland.option_true:
+ if self.item.item_name == Currency.qi_gem or self.item.item_name == Currency.golden_walnut or self.item.item_name == Currency.cinder_shard:
+ return False
+ if options.festival_locations == FestivalLocations.option_disabled:
+ if self.item.item_name == Currency.star_token:
+ return False
+ return True
+
+
+class MoneyBundleTemplate(CurrencyBundleTemplate):
+
+ def __init__(self, room: str, item: BundleItem):
+ super().__init__(room, "", item)
+
+ def create_bundle(self, bundle_price_option: BundlePrice, random: Random, options: StardewValleyOptions) -> Bundle:
+ currency_amount = self.get_currency_amount(bundle_price_option)
+ currency_name = "g"
+ if currency_amount >= 1000:
+ unit_amount = currency_amount % 1000
+ unit_amount = "000" if unit_amount == 0 else unit_amount
+ currency_display = f"{currency_amount // 1000},{unit_amount}"
+ else:
+ currency_display = f"{currency_amount}"
+ name = f"{currency_display}{currency_name} Bundle"
+ return Bundle(self.room, name, [BundleItem(self.item.item_name, currency_amount)], 1)
+
+ def get_currency_amount(self, bundle_price_option: BundlePrice):
+ if bundle_price_option == BundlePrice.option_minimum:
+ price_multiplier = 0.1
+ elif bundle_price_option == BundlePrice.option_maximum:
+ price_multiplier = 4
+ else:
+ price_multiplier = round(1 + (bundle_price_option.value * 0.4), 2)
+ currency_amount = int(self.item.amount * price_multiplier)
+ return currency_amount
+
+
+class IslandBundleTemplate(BundleTemplate):
+ def can_appear(self, options: StardewValleyOptions) -> bool:
+ return options.exclude_ginger_island == ExcludeGingerIsland.option_false
+
+
+class FestivalBundleTemplate(BundleTemplate):
+ def can_appear(self, options: StardewValleyOptions) -> bool:
+ return options.festival_locations != FestivalLocations.option_disabled
+
+
+class DeepBundleTemplate(BundleTemplate):
+ categories: List[List[BundleItem]]
+
+ def __init__(self, room: str, name: str, categories: List[List[BundleItem]], number_possible_items: int, number_required_items: int):
+ super().__init__(room, name, [], number_possible_items, number_required_items)
+ self.categories = categories
+
+ def create_bundle(self, bundle_price_option: BundlePrice, random: Random, options: StardewValleyOptions) -> Bundle:
+ if bundle_price_option == BundlePrice.option_minimum:
+ number_required = 1
+ elif bundle_price_option == BundlePrice.option_maximum:
+ number_required = 8
+ else:
+ number_required = self.number_required_items + bundle_price_option.value
+ number_categories = len(self.categories)
+ number_chosen_categories = self.number_possible_items
+ if number_chosen_categories < number_required:
+ number_chosen_categories = number_required
+
+ if number_chosen_categories > number_categories:
+ chosen_categories = self.categories + random.choices(self.categories, k=number_chosen_categories - number_categories)
+ else:
+ chosen_categories = random.sample(self.categories, number_chosen_categories)
+
+ chosen_items = []
+ for category in chosen_categories:
+ filtered_items = [item for item in category if item.can_appear(options)]
+ chosen_items.append(random.choice(filtered_items))
+
+ return Bundle(self.room, self.name, chosen_items, number_required)
diff --git a/worlds/stardew_valley/bundles/bundle_item.py b/worlds/stardew_valley/bundles/bundle_item.py
new file mode 100644
index 000000000000..8aaa67c5f242
--- /dev/null
+++ b/worlds/stardew_valley/bundles/bundle_item.py
@@ -0,0 +1,73 @@
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from dataclasses import dataclass
+
+from ..options import StardewValleyOptions, ExcludeGingerIsland, FestivalLocations
+from ..strings.crop_names import Fruit
+from ..strings.currency_names import Currency
+from ..strings.quality_names import CropQuality, FishQuality, ForageQuality
+
+
+class BundleItemSource(ABC):
+ @abstractmethod
+ def can_appear(self, options: StardewValleyOptions) -> bool:
+ ...
+
+
+class VanillaItemSource(BundleItemSource):
+ def can_appear(self, options: StardewValleyOptions) -> bool:
+ return True
+
+
+class IslandItemSource(BundleItemSource):
+ def can_appear(self, options: StardewValleyOptions) -> bool:
+ return options.exclude_ginger_island == ExcludeGingerIsland.option_false
+
+
+class FestivalItemSource(BundleItemSource):
+ def can_appear(self, options: StardewValleyOptions) -> bool:
+ return options.festival_locations != FestivalLocations.option_disabled
+
+
+@dataclass(frozen=True, order=True)
+class BundleItem:
+ class Sources:
+ vanilla = VanillaItemSource()
+ island = IslandItemSource()
+ festival = FestivalItemSource()
+
+ item_name: str
+ amount: int = 1
+ quality: str = CropQuality.basic
+ source: BundleItemSource = Sources.vanilla
+
+ @staticmethod
+ def money_bundle(amount: int) -> BundleItem:
+ return BundleItem(Currency.money, amount)
+
+ def as_amount(self, amount: int) -> BundleItem:
+ return BundleItem(self.item_name, amount, self.quality, self.source)
+
+ def as_quality(self, quality: str) -> BundleItem:
+ return BundleItem(self.item_name, self.amount, quality, self.source)
+
+ def as_quality_crop(self) -> BundleItem:
+ amount = 5
+ difficult_crops = [Fruit.sweet_gem_berry, Fruit.ancient_fruit]
+ if self.item_name in difficult_crops:
+ amount = 1
+ return self.as_quality(CropQuality.gold).as_amount(amount)
+
+ def as_quality_fish(self) -> BundleItem:
+ return self.as_quality(FishQuality.gold)
+
+ def as_quality_forage(self) -> BundleItem:
+ return self.as_quality(ForageQuality.gold)
+
+ def __repr__(self):
+ quality = "" if self.quality == CropQuality.basic else self.quality
+ return f"{self.amount} {quality} {self.item_name}"
+
+ def can_appear(self, options: StardewValleyOptions) -> bool:
+ return self.source.can_appear(options)
diff --git a/worlds/stardew_valley/bundles/bundle_room.py b/worlds/stardew_valley/bundles/bundle_room.py
new file mode 100644
index 000000000000..a5cdb89144f5
--- /dev/null
+++ b/worlds/stardew_valley/bundles/bundle_room.py
@@ -0,0 +1,24 @@
+from dataclasses import dataclass
+from random import Random
+from typing import List
+
+from .bundle import Bundle, BundleTemplate
+from ..options import BundlePrice, StardewValleyOptions
+
+
+@dataclass
+class BundleRoom:
+ name: str
+ bundles: List[Bundle]
+
+
+@dataclass
+class BundleRoomTemplate:
+ name: str
+ bundles: List[BundleTemplate]
+ number_bundles: int
+
+ def create_bundle_room(self, bundle_price_option: BundlePrice, random: Random, options: StardewValleyOptions):
+ filtered_bundles = [bundle for bundle in self.bundles if bundle.can_appear(options)]
+ chosen_bundles = random.sample(filtered_bundles, self.number_bundles)
+ return BundleRoom(self.name, [bundle.create_bundle(bundle_price_option, random, options) for bundle in chosen_bundles])
diff --git a/worlds/stardew_valley/bundles/bundles.py b/worlds/stardew_valley/bundles/bundles.py
new file mode 100644
index 000000000000..260ee17cbe82
--- /dev/null
+++ b/worlds/stardew_valley/bundles/bundles.py
@@ -0,0 +1,80 @@
+from random import Random
+from typing import List
+
+from .bundle_room import BundleRoom
+from ..data.bundle_data import pantry_vanilla, crafts_room_vanilla, fish_tank_vanilla, boiler_room_vanilla, bulletin_board_vanilla, vault_vanilla, \
+ pantry_thematic, crafts_room_thematic, fish_tank_thematic, boiler_room_thematic, bulletin_board_thematic, vault_thematic, pantry_remixed, \
+ crafts_room_remixed, fish_tank_remixed, boiler_room_remixed, bulletin_board_remixed, vault_remixed, all_bundle_items_except_money, \
+ abandoned_joja_mart_thematic, abandoned_joja_mart_vanilla, abandoned_joja_mart_remixed
+from ..logic.logic import StardewLogic
+from ..options import BundleRandomization, StardewValleyOptions, ExcludeGingerIsland
+
+
+def get_all_bundles(random: Random, logic: StardewLogic, options: StardewValleyOptions) -> List[BundleRoom]:
+ if options.bundle_randomization == BundleRandomization.option_vanilla:
+ return get_vanilla_bundles(random, options)
+ elif options.bundle_randomization == BundleRandomization.option_thematic:
+ return get_thematic_bundles(random, options)
+ elif options.bundle_randomization == BundleRandomization.option_remixed:
+ return get_remixed_bundles(random, options)
+ elif options.bundle_randomization == BundleRandomization.option_shuffled:
+ return get_shuffled_bundles(random, logic, options)
+
+ raise NotImplementedError
+
+
+def get_vanilla_bundles(random: Random, options: StardewValleyOptions) -> List[BundleRoom]:
+ pantry = pantry_vanilla.create_bundle_room(options.bundle_price, random, options)
+ crafts_room = crafts_room_vanilla.create_bundle_room(options.bundle_price, random, options)
+ fish_tank = fish_tank_vanilla.create_bundle_room(options.bundle_price, random, options)
+ boiler_room = boiler_room_vanilla.create_bundle_room(options.bundle_price, random, options)
+ bulletin_board = bulletin_board_vanilla.create_bundle_room(options.bundle_price, random, options)
+ vault = vault_vanilla.create_bundle_room(options.bundle_price, random, options)
+ abandoned_joja_mart = abandoned_joja_mart_vanilla.create_bundle_room(options.bundle_price, random, options)
+ return [pantry, crafts_room, fish_tank, boiler_room, bulletin_board, vault, abandoned_joja_mart]
+
+
+def get_thematic_bundles(random: Random, options: StardewValleyOptions) -> List[BundleRoom]:
+ pantry = pantry_thematic.create_bundle_room(options.bundle_price, random, options)
+ crafts_room = crafts_room_thematic.create_bundle_room(options.bundle_price, random, options)
+ fish_tank = fish_tank_thematic.create_bundle_room(options.bundle_price, random, options)
+ boiler_room = boiler_room_thematic.create_bundle_room(options.bundle_price, random, options)
+ bulletin_board = bulletin_board_thematic.create_bundle_room(options.bundle_price, random, options)
+ vault = vault_thematic.create_bundle_room(options.bundle_price, random, options)
+ abandoned_joja_mart = abandoned_joja_mart_thematic.create_bundle_room(options.bundle_price, random, options)
+ return [pantry, crafts_room, fish_tank, boiler_room, bulletin_board, vault, abandoned_joja_mart]
+
+
+def get_remixed_bundles(random: Random, options: StardewValleyOptions) -> List[BundleRoom]:
+ pantry = pantry_remixed.create_bundle_room(options.bundle_price, random, options)
+ crafts_room = crafts_room_remixed.create_bundle_room(options.bundle_price, random, options)
+ fish_tank = fish_tank_remixed.create_bundle_room(options.bundle_price, random, options)
+ boiler_room = boiler_room_remixed.create_bundle_room(options.bundle_price, random, options)
+ bulletin_board = bulletin_board_remixed.create_bundle_room(options.bundle_price, random, options)
+ vault = vault_remixed.create_bundle_room(options.bundle_price, random, options)
+ abandoned_joja_mart = abandoned_joja_mart_remixed.create_bundle_room(options.bundle_price, random, options)
+ return [pantry, crafts_room, fish_tank, boiler_room, bulletin_board, vault, abandoned_joja_mart]
+
+
+def get_shuffled_bundles(random: Random, logic: StardewLogic, options: StardewValleyOptions) -> List[BundleRoom]:
+ valid_bundle_items = [bundle_item for bundle_item in all_bundle_items_except_money if bundle_item.can_appear(options)]
+
+ rooms = [room for room in get_remixed_bundles(random, options) if room.name != "Vault"]
+ required_items = 0
+ for room in rooms:
+ for bundle in room.bundles:
+ required_items += len(bundle.items)
+ random.shuffle(room.bundles)
+ random.shuffle(rooms)
+
+ chosen_bundle_items = random.sample(valid_bundle_items, required_items)
+ sorted_bundle_items = sorted(chosen_bundle_items, key=lambda x: logic.has(x.item_name).get_difficulty())
+ for room in rooms:
+ for bundle in room.bundles:
+ num_items = len(bundle.items)
+ bundle.items = sorted_bundle_items[:num_items]
+ sorted_bundle_items = sorted_bundle_items[num_items:]
+
+ vault = vault_remixed.create_bundle_room(options.bundle_price, random, options)
+ return [*rooms, vault]
+
diff --git a/worlds/stardew_valley/data/bundle_data.py b/worlds/stardew_valley/data/bundle_data.py
index 183383ccbf3a..7e7a08c16b37 100644
--- a/worlds/stardew_valley/data/bundle_data.py
+++ b/worlds/stardew_valley/data/bundle_data.py
@@ -1,419 +1,777 @@
-from dataclasses import dataclass
-
-from . import fish_data
-from .common_data import quality_dict
-from .game_item import GameItem
-from .museum_data import Mineral
-
-@dataclass(frozen=True)
-class BundleItem:
- item: GameItem
- amount: int
- quality: int
-
- @staticmethod
- def item_bundle(name: str, item_id: int, amount: int, quality: int):
- return BundleItem(GameItem(name, item_id), amount, quality)
-
- @staticmethod
- def money_bundle(amount: int):
- return BundleItem.item_bundle("Money", -1, amount, amount)
-
- def as_amount(self, amount: int):
- return BundleItem.item_bundle(self.item.name, self.item.item_id, amount, self.quality)
-
- def as_quality(self, quality: int):
- return BundleItem.item_bundle(self.item.name, self.item.item_id, self.amount, quality)
-
- def as_gold_quality(self):
- return self.as_quality(2)
-
- def as_quality_crop(self):
- amount = 5
- difficult_crops = ["Sweet Gem Berry", "Ancient Fruit"]
- if self.item.name in difficult_crops:
- amount = 1
- return self.as_gold_quality().as_amount(amount)
-
- def is_gold_quality(self) -> bool:
- return self.quality >= 2
-
- def __repr__(self):
- return f"{self.amount} {quality_dict[self.quality]} {self.item.name}"
-
- def __lt__(self, other):
- return self.item < other.item
-
-
-wild_horseradish = BundleItem.item_bundle("Wild Horseradish", 16, 1, 0)
-daffodil = BundleItem.item_bundle("Daffodil", 18, 1, 0)
-leek = BundleItem.item_bundle("Leek", 20, 1, 0)
-dandelion = BundleItem.item_bundle("Dandelion", 22, 1, 0)
-morel = BundleItem.item_bundle("Morel", 257, 1, 0)
-common_mushroom = BundleItem.item_bundle("Common Mushroom", 404, 1, 0)
-salmonberry = BundleItem.item_bundle("Salmonberry", 296, 1, 0)
-spring_onion = BundleItem.item_bundle("Spring Onion", 399, 1, 0)
-
-grape = BundleItem.item_bundle("Grape", 398, 1, 0)
-spice_berry = BundleItem.item_bundle("Spice Berry", 396, 1, 0)
-sweet_pea = BundleItem.item_bundle("Sweet Pea", 402, 1, 0)
-red_mushroom = BundleItem.item_bundle("Red Mushroom", 420, 1, 0)
-fiddlehead_fern = BundleItem.item_bundle("Fiddlehead Fern", 259, 1, 0)
-
-wild_plum = BundleItem.item_bundle("Wild Plum", 406, 1, 0)
-hazelnut = BundleItem.item_bundle("Hazelnut", 408, 1, 0)
-blackberry = BundleItem.item_bundle("Blackberry", 410, 1, 0)
-chanterelle = BundleItem.item_bundle("Chanterelle", 281, 1, 0)
-
-winter_root = BundleItem.item_bundle("Winter Root", 412, 1, 0)
-crystal_fruit = BundleItem.item_bundle("Crystal Fruit", 414, 1, 0)
-snow_yam = BundleItem.item_bundle("Snow Yam", 416, 1, 0)
-crocus = BundleItem.item_bundle("Crocus", 418, 1, 0)
-holly = BundleItem.item_bundle("Holly", 283, 1, 0)
-
-coconut = BundleItem.item_bundle("Coconut", 88, 1, 0)
-cactus_fruit = BundleItem.item_bundle("Cactus Fruit", 90, 1, 0)
-cave_carrot = BundleItem.item_bundle("Cave Carrot", 78, 1, 0)
-purple_mushroom = BundleItem.item_bundle("Purple Mushroom", 422, 1, 0)
-maple_syrup = BundleItem.item_bundle("Maple Syrup", 724, 1, 0)
-oak_resin = BundleItem.item_bundle("Oak Resin", 725, 1, 0)
-pine_tar = BundleItem.item_bundle("Pine Tar", 726, 1, 0)
-nautilus_shell = BundleItem.item_bundle("Nautilus Shell", 392, 1, 0)
-coral = BundleItem.item_bundle("Coral", 393, 1, 0)
-sea_urchin = BundleItem.item_bundle("Sea Urchin", 397, 1, 0)
-rainbow_shell = BundleItem.item_bundle("Rainbow Shell", 394, 1, 0)
-clam = BundleItem(fish_data.clam, 1, 0)
-cockle = BundleItem(fish_data.cockle, 1, 0)
-mussel = BundleItem(fish_data.mussel, 1, 0)
-oyster = BundleItem(fish_data.oyster, 1, 0)
-seaweed = BundleItem.item_bundle("Seaweed", 152, 1, 0)
-
-wood = BundleItem.item_bundle("Wood", 388, 99, 0)
-stone = BundleItem.item_bundle("Stone", 390, 99, 0)
-hardwood = BundleItem.item_bundle("Hardwood", 709, 10, 0)
-clay = BundleItem.item_bundle("Clay", 330, 10, 0)
-fiber = BundleItem.item_bundle("Fiber", 771, 99, 0)
-
-blue_jazz = BundleItem.item_bundle("Blue Jazz", 597, 1, 0)
-cauliflower = BundleItem.item_bundle("Cauliflower", 190, 1, 0)
-green_bean = BundleItem.item_bundle("Green Bean", 188, 1, 0)
-kale = BundleItem.item_bundle("Kale", 250, 1, 0)
-parsnip = BundleItem.item_bundle("Parsnip", 24, 1, 0)
-potato = BundleItem.item_bundle("Potato", 192, 1, 0)
-strawberry = BundleItem.item_bundle("Strawberry", 400, 1, 0)
-tulip = BundleItem.item_bundle("Tulip", 591, 1, 0)
-unmilled_rice = BundleItem.item_bundle("Unmilled Rice", 271, 1, 0)
-blueberry = BundleItem.item_bundle("Blueberry", 258, 1, 0)
-corn = BundleItem.item_bundle("Corn", 270, 1, 0)
-hops = BundleItem.item_bundle("Hops", 304, 1, 0)
-hot_pepper = BundleItem.item_bundle("Hot Pepper", 260, 1, 0)
-melon = BundleItem.item_bundle("Melon", 254, 1, 0)
-poppy = BundleItem.item_bundle("Poppy", 376, 1, 0)
-radish = BundleItem.item_bundle("Radish", 264, 1, 0)
-summer_spangle = BundleItem.item_bundle("Summer Spangle", 593, 1, 0)
-sunflower = BundleItem.item_bundle("Sunflower", 421, 1, 0)
-tomato = BundleItem.item_bundle("Tomato", 256, 1, 0)
-wheat = BundleItem.item_bundle("Wheat", 262, 1, 0)
-hay = BundleItem.item_bundle("Hay", 178, 1, 0)
-amaranth = BundleItem.item_bundle("Amaranth", 300, 1, 0)
-bok_choy = BundleItem.item_bundle("Bok Choy", 278, 1, 0)
-cranberries = BundleItem.item_bundle("Cranberries", 282, 1, 0)
-eggplant = BundleItem.item_bundle("Eggplant", 272, 1, 0)
-fairy_rose = BundleItem.item_bundle("Fairy Rose", 595, 1, 0)
-pumpkin = BundleItem.item_bundle("Pumpkin", 276, 1, 0)
-yam = BundleItem.item_bundle("Yam", 280, 1, 0)
-sweet_gem_berry = BundleItem.item_bundle("Sweet Gem Berry", 417, 1, 0)
-rhubarb = BundleItem.item_bundle("Rhubarb", 252, 1, 0)
-beet = BundleItem.item_bundle("Beet", 284, 1, 0)
-red_cabbage = BundleItem.item_bundle("Red Cabbage", 266, 1, 0)
-artichoke = BundleItem.item_bundle("Artichoke", 274, 1, 0)
-
-egg = BundleItem.item_bundle("Egg", 176, 1, 0)
-large_egg = BundleItem.item_bundle("Large Egg", 174, 1, 0)
-brown_egg = BundleItem.item_bundle("Egg (Brown)", 180, 1, 0)
-large_brown_egg = BundleItem.item_bundle("Large Egg (Brown)", 182, 1, 0)
-wool = BundleItem.item_bundle("Wool", 440, 1, 0)
-milk = BundleItem.item_bundle("Milk", 184, 1, 0)
-large_milk = BundleItem.item_bundle("Large Milk", 186, 1, 0)
-goat_milk = BundleItem.item_bundle("Goat Milk", 436, 1, 0)
-large_goat_milk = BundleItem.item_bundle("Large Goat Milk", 438, 1, 0)
-truffle = BundleItem.item_bundle("Truffle", 430, 1, 0)
-duck_feather = BundleItem.item_bundle("Duck Feather", 444, 1, 0)
-duck_egg = BundleItem.item_bundle("Duck Egg", 442, 1, 0)
-rabbit_foot = BundleItem.item_bundle("Rabbit's Foot", 446, 1, 0)
-
-truffle_oil = BundleItem.item_bundle("Truffle Oil", 432, 1, 0)
-cloth = BundleItem.item_bundle("Cloth", 428, 1, 0)
-goat_cheese = BundleItem.item_bundle("Goat Cheese", 426, 1, 0)
-cheese = BundleItem.item_bundle("Cheese", 424, 1, 0)
-honey = BundleItem.item_bundle("Honey", 340, 1, 0)
-beer = BundleItem.item_bundle("Beer", 346, 1, 0)
-juice = BundleItem.item_bundle("Juice", 350, 1, 0)
-mead = BundleItem.item_bundle("Mead", 459, 1, 0)
-pale_ale = BundleItem.item_bundle("Pale Ale", 303, 1, 0)
-wine = BundleItem.item_bundle("Wine", 348, 1, 0)
-jelly = BundleItem.item_bundle("Jelly", 344, 1, 0)
-pickles = BundleItem.item_bundle("Pickles", 342, 1, 0)
-caviar = BundleItem.item_bundle("Caviar", 445, 1, 0)
-aged_roe = BundleItem.item_bundle("Aged Roe", 447, 1, 0)
-apple = BundleItem.item_bundle("Apple", 613, 1, 0)
-apricot = BundleItem.item_bundle("Apricot", 634, 1, 0)
-orange = BundleItem.item_bundle("Orange", 635, 1, 0)
-peach = BundleItem.item_bundle("Peach", 636, 1, 0)
-pomegranate = BundleItem.item_bundle("Pomegranate", 637, 1, 0)
-cherry = BundleItem.item_bundle("Cherry", 638, 1, 0)
-lobster = BundleItem(fish_data.lobster, 1, 0)
-crab = BundleItem(fish_data.crab, 1, 0)
-shrimp = BundleItem(fish_data.shrimp, 1, 0)
-crayfish = BundleItem(fish_data.crayfish, 1, 0)
-snail = BundleItem(fish_data.snail, 1, 0)
-periwinkle = BundleItem(fish_data.periwinkle, 1, 0)
-trash = BundleItem.item_bundle("Trash", 168, 1, 0)
-driftwood = BundleItem.item_bundle("Driftwood", 169, 1, 0)
-soggy_newspaper = BundleItem.item_bundle("Soggy Newspaper", 172, 1, 0)
-broken_cd = BundleItem.item_bundle("Broken CD", 171, 1, 0)
-broken_glasses = BundleItem.item_bundle("Broken Glasses", 170, 1, 0)
-
-chub = BundleItem(fish_data.chub, 1, 0)
-catfish = BundleItem(fish_data.catfish, 1, 0)
-rainbow_trout = BundleItem(fish_data.rainbow_trout, 1, 0)
-lingcod = BundleItem(fish_data.lingcod, 1, 0)
-walleye = BundleItem(fish_data.walleye, 1, 0)
-perch = BundleItem(fish_data.perch, 1, 0)
-pike = BundleItem(fish_data.pike, 1, 0)
-bream = BundleItem(fish_data.bream, 1, 0)
-salmon = BundleItem(fish_data.salmon, 1, 0)
-sunfish = BundleItem(fish_data.sunfish, 1, 0)
-tiger_trout = BundleItem(fish_data.tiger_trout, 1, 0)
-shad = BundleItem(fish_data.shad, 1, 0)
-smallmouth_bass = BundleItem(fish_data.smallmouth_bass, 1, 0)
-dorado = BundleItem(fish_data.dorado, 1, 0)
-carp = BundleItem(fish_data.carp, 1, 0)
-midnight_carp = BundleItem(fish_data.midnight_carp, 1, 0)
-largemouth_bass = BundleItem(fish_data.largemouth_bass, 1, 0)
-sturgeon = BundleItem(fish_data.sturgeon, 1, 0)
-bullhead = BundleItem(fish_data.bullhead, 1, 0)
-tilapia = BundleItem(fish_data.tilapia, 1, 0)
-pufferfish = BundleItem(fish_data.pufferfish, 1, 0)
-tuna = BundleItem(fish_data.tuna, 1, 0)
-super_cucumber = BundleItem(fish_data.super_cucumber, 1, 0)
-flounder = BundleItem(fish_data.flounder, 1, 0)
-anchovy = BundleItem(fish_data.anchovy, 1, 0)
-sardine = BundleItem(fish_data.sardine, 1, 0)
-red_mullet = BundleItem(fish_data.red_mullet, 1, 0)
-herring = BundleItem(fish_data.herring, 1, 0)
-eel = BundleItem(fish_data.eel, 1, 0)
-octopus = BundleItem(fish_data.octopus, 1, 0)
-red_snapper = BundleItem(fish_data.red_snapper, 1, 0)
-squid = BundleItem(fish_data.squid, 1, 0)
-sea_cucumber = BundleItem(fish_data.sea_cucumber, 1, 0)
-albacore = BundleItem(fish_data.albacore, 1, 0)
-halibut = BundleItem(fish_data.halibut, 1, 0)
-scorpion_carp = BundleItem(fish_data.scorpion_carp, 1, 0)
-sandfish = BundleItem(fish_data.sandfish, 1, 0)
-woodskip = BundleItem(fish_data.woodskip, 1, 0)
-lava_eel = BundleItem(fish_data.lava_eel, 1, 0)
-ice_pip = BundleItem(fish_data.ice_pip, 1, 0)
-stonefish = BundleItem(fish_data.stonefish, 1, 0)
-ghostfish = BundleItem(fish_data.ghostfish, 1, 0)
-
-wilted_bouquet = BundleItem.item_bundle("Wilted Bouquet", 277, 1, 0)
-copper_bar = BundleItem.item_bundle("Copper Bar", 334, 2, 0)
-iron_Bar = BundleItem.item_bundle("Iron Bar", 335, 2, 0)
-gold_bar = BundleItem.item_bundle("Gold Bar", 336, 1, 0)
-iridium_bar = BundleItem.item_bundle("Iridium Bar", 337, 1, 0)
-refined_quartz = BundleItem.item_bundle("Refined Quartz", 338, 2, 0)
-coal = BundleItem.item_bundle("Coal", 382, 5, 0)
-
-quartz = BundleItem(Mineral.quartz, 1, 0)
-fire_quartz = BundleItem(Mineral.fire_quartz, 1, 0)
-frozen_tear = BundleItem(Mineral.frozen_tear, 1, 0)
-earth_crystal = BundleItem(Mineral.earth_crystal, 1, 0)
-emerald = BundleItem(Mineral.emerald, 1, 0)
-aquamarine = BundleItem(Mineral.aquamarine, 1, 0)
-ruby = BundleItem(Mineral.ruby, 1, 0)
-amethyst = BundleItem(Mineral.amethyst, 1, 0)
-topaz = BundleItem(Mineral.topaz, 1, 0)
-jade = BundleItem(Mineral.jade, 1, 0)
-
-slime = BundleItem.item_bundle("Slime", 766, 99, 0)
-bug_meat = BundleItem.item_bundle("Bug Meat", 684, 10, 0)
-bat_wing = BundleItem.item_bundle("Bat Wing", 767, 10, 0)
-solar_essence = BundleItem.item_bundle("Solar Essence", 768, 1, 0)
-void_essence = BundleItem.item_bundle("Void Essence", 769, 1, 0)
-
-maki_roll = BundleItem.item_bundle("Maki Roll", 228, 1, 0)
-fried_egg = BundleItem.item_bundle("Fried Egg", 194, 1, 0)
-omelet = BundleItem.item_bundle("Omelet", 195, 1, 0)
-pizza = BundleItem.item_bundle("Pizza", 206, 1, 0)
-hashbrowns = BundleItem.item_bundle("Hashbrowns", 210, 1, 0)
-pancakes = BundleItem.item_bundle("Pancakes", 211, 1, 0)
-bread = BundleItem.item_bundle("Bread", 216, 1, 0)
-tortilla = BundleItem.item_bundle("Tortilla", 229, 1, 0)
-triple_shot_espresso = BundleItem.item_bundle("Triple Shot Espresso", 253, 1, 0)
-farmer_s_lunch = BundleItem.item_bundle("Farmer's Lunch", 240, 1, 0)
-survival_burger = BundleItem.item_bundle("Survival Burger", 241, 1, 0)
-dish_o_the_sea = BundleItem.item_bundle("Dish O' The Sea", 242, 1, 0)
-miner_s_treat = BundleItem.item_bundle("Miner's Treat", 243, 1, 0)
-roots_platter = BundleItem.item_bundle("Roots Platter", 244, 1, 0)
-salad = BundleItem.item_bundle("Salad", 196, 1, 0)
-cheese_cauliflower = BundleItem.item_bundle("Cheese Cauliflower", 197, 1, 0)
-parsnip_soup = BundleItem.item_bundle("Parsnip Soup", 199, 1, 0)
-fried_mushroom = BundleItem.item_bundle("Fried Mushroom", 205, 1, 0)
-salmon_dinner = BundleItem.item_bundle("Salmon Dinner", 212, 1, 0)
-pepper_poppers = BundleItem.item_bundle("Pepper Poppers", 215, 1, 0)
-spaghetti = BundleItem.item_bundle("Spaghetti", 224, 1, 0)
-sashimi = BundleItem.item_bundle("Sashimi", 227, 1, 0)
-blueberry_tart = BundleItem.item_bundle("Blueberry Tart", 234, 1, 0)
-algae_soup = BundleItem.item_bundle("Algae Soup", 456, 1, 0)
-pale_broth = BundleItem.item_bundle("Pale Broth", 457, 1, 0)
-chowder = BundleItem.item_bundle("Chowder", 727, 1, 0)
-green_algae = BundleItem.item_bundle("Green Algae", 153, 1, 0)
-white_algae = BundleItem.item_bundle("White Algae", 157, 1, 0)
-geode = BundleItem.item_bundle("Geode", 535, 1, 0)
-frozen_geode = BundleItem.item_bundle("Frozen Geode", 536, 1, 0)
-magma_geode = BundleItem.item_bundle("Magma Geode", 537, 1, 0)
-omni_geode = BundleItem.item_bundle("Omni Geode", 749, 1, 0)
-
-spring_foraging_items = [wild_horseradish, daffodil, leek, dandelion, salmonberry, spring_onion]
-summer_foraging_items = [grape, spice_berry, sweet_pea, fiddlehead_fern, rainbow_shell]
-fall_foraging_items = [common_mushroom, wild_plum, hazelnut, blackberry]
-winter_foraging_items = [winter_root, crystal_fruit, snow_yam, crocus, holly, nautilus_shell]
-exotic_foraging_items = [coconut, cactus_fruit, cave_carrot, red_mushroom, purple_mushroom,
- maple_syrup, oak_resin, pine_tar, morel, coral,
- sea_urchin, clam, cockle, mussel, oyster, seaweed]
-construction_items = [wood, stone, hardwood, clay, fiber]
-
-# TODO coffee_bean, garlic, rhubarb, tea_leaves
-spring_crop_items = [blue_jazz, cauliflower, green_bean, kale, parsnip, potato, strawberry, tulip, unmilled_rice]
-# TODO red_cabbage, starfruit, ancient_fruit, pineapple, taro_root
-summer_crops_items = [blueberry, corn, hops, hot_pepper, melon, poppy,
- radish, summer_spangle, sunflower, tomato, wheat]
-# TODO artichoke, beet
-fall_crops_items = [corn, sunflower, wheat, amaranth, bok_choy, cranberries,
- eggplant, fairy_rose, grape, pumpkin, yam, sweet_gem_berry]
-all_crops_items = sorted({*spring_crop_items, *summer_crops_items, *fall_crops_items})
-quality_crops_items = [item.as_quality_crop() for item in all_crops_items]
-# TODO void_egg, dinosaur_egg, ostrich_egg, golden_egg
-animal_product_items = [egg, large_egg, brown_egg, large_brown_egg, wool, milk, large_milk,
- goat_milk, large_goat_milk, truffle, duck_feather, duck_egg, rabbit_foot]
-# TODO coffee, green_tea
-artisan_goods_items = [truffle_oil, cloth, goat_cheese, cheese, honey, beer, juice, mead, pale_ale, wine, jelly,
- pickles, caviar, aged_roe, apple, apricot, orange, peach, pomegranate, cherry]
-
-river_fish_items = [chub, catfish, rainbow_trout, lingcod, walleye, perch, pike, bream,
- salmon, sunfish, tiger_trout, shad, smallmouth_bass, dorado]
-lake_fish_items = [chub, rainbow_trout, lingcod, walleye, perch, carp, midnight_carp, largemouth_bass, sturgeon, bullhead]
-ocean_fish_items = [tilapia, pufferfish, tuna, super_cucumber, flounder, anchovy, sardine, red_mullet,
- herring, eel, octopus, red_snapper, squid, sea_cucumber, albacore, halibut]
-night_fish_items = [walleye, bream, super_cucumber, eel, squid, midnight_carp]
-# TODO void_salmon
-specialty_fish_items = [scorpion_carp, sandfish, woodskip, pufferfish, eel, octopus,
- squid, lava_eel, ice_pip, stonefish, ghostfish, dorado]
-crab_pot_items = [lobster, clam, crab, cockle, mussel, shrimp, oyster, crayfish, snail,
- periwinkle, trash, driftwood, soggy_newspaper, broken_cd, broken_glasses]
-
-# TODO radioactive_bar
-blacksmith_items = [wilted_bouquet, copper_bar, iron_Bar, gold_bar, iridium_bar, refined_quartz, coal]
-geologist_items = [quartz, earth_crystal, frozen_tear, fire_quartz, emerald, aquamarine, ruby, amethyst, topaz, jade]
-adventurer_items = [slime, bug_meat, bat_wing, solar_essence, void_essence, coal]
-
-chef_items = [maki_roll, fried_egg, omelet, pizza, hashbrowns, pancakes, bread, tortilla, triple_shot_espresso,
- farmer_s_lunch, survival_burger, dish_o_the_sea, miner_s_treat, roots_platter, salad,
- cheese_cauliflower, parsnip_soup, fried_mushroom, salmon_dinner, pepper_poppers, spaghetti,
- sashimi, blueberry_tart, algae_soup, pale_broth, chowder]
-
-dwarf_scroll_1 = BundleItem.item_bundle("Dwarf Scroll I", 96, 1, 0)
-dwarf_scroll_2 = BundleItem.item_bundle("Dwarf Scroll II", 97, 1, 0)
-dwarf_scroll_3 = BundleItem.item_bundle("Dwarf Scroll III", 98, 1, 0)
-dwarf_scroll_4 = BundleItem.item_bundle("Dwarf Scroll IV", 99, 1, 0)
-elvish_jewelry = BundleItem.item_bundle("Elvish Jewelry", 104, 1, 0)
-ancient_drum = BundleItem.item_bundle("Ancient Drum", 123, 1, 0)
-dried_starfish = BundleItem.item_bundle("Dried Starfish", 116, 1, 0)
-
+from ..bundles.bundle import BundleTemplate, IslandBundleTemplate, DeepBundleTemplate, CurrencyBundleTemplate, MoneyBundleTemplate, FestivalBundleTemplate
+from ..bundles.bundle_item import BundleItem
+from ..bundles.bundle_room import BundleRoomTemplate
+from ..strings.animal_product_names import AnimalProduct
+from ..strings.artisan_good_names import ArtisanGood
+from ..strings.bundle_names import CCRoom, BundleName
+from ..strings.craftable_names import Fishing, Craftable, Bomb
+from ..strings.crop_names import Fruit, Vegetable
+from ..strings.currency_names import Currency
+from ..strings.fertilizer_names import Fertilizer, RetainingSoil, SpeedGro
+from ..strings.fish_names import Fish, WaterItem, Trash
+from ..strings.flower_names import Flower
+from ..strings.food_names import Beverage, Meal
+from ..strings.forageable_names import Forageable
+from ..strings.geode_names import Geode
+from ..strings.gift_names import Gift
+from ..strings.ingredient_names import Ingredient
+from ..strings.material_names import Material
+from ..strings.metal_names import MetalBar, Artifact, Fossil, Ore, Mineral
+from ..strings.monster_drop_names import Loot
+from ..strings.quality_names import ForageQuality, ArtisanQuality, FishQuality
+from ..strings.seed_names import Seed
+
+wild_horseradish = BundleItem(Forageable.wild_horseradish)
+daffodil = BundleItem(Forageable.daffodil)
+leek = BundleItem(Forageable.leek)
+dandelion = BundleItem(Forageable.dandelion)
+morel = BundleItem(Forageable.morel)
+common_mushroom = BundleItem(Forageable.common_mushroom)
+salmonberry = BundleItem(Forageable.salmonberry)
+spring_onion = BundleItem(Forageable.spring_onion)
+
+grape = BundleItem(Fruit.grape)
+spice_berry = BundleItem(Forageable.spice_berry)
+sweet_pea = BundleItem(Forageable.sweet_pea)
+red_mushroom = BundleItem(Forageable.red_mushroom)
+fiddlehead_fern = BundleItem(Forageable.fiddlehead_fern)
+
+wild_plum = BundleItem(Forageable.wild_plum)
+hazelnut = BundleItem(Forageable.hazelnut)
+blackberry = BundleItem(Forageable.blackberry)
+chanterelle = BundleItem(Forageable.chanterelle)
+
+winter_root = BundleItem(Forageable.winter_root)
+crystal_fruit = BundleItem(Forageable.crystal_fruit)
+snow_yam = BundleItem(Forageable.snow_yam)
+crocus = BundleItem(Forageable.crocus)
+holly = BundleItem(Forageable.holly)
+
+coconut = BundleItem(Forageable.coconut)
+cactus_fruit = BundleItem(Forageable.cactus_fruit)
+cave_carrot = BundleItem(Forageable.cave_carrot)
+purple_mushroom = BundleItem(Forageable.purple_mushroom)
+maple_syrup = BundleItem(ArtisanGood.maple_syrup)
+oak_resin = BundleItem(ArtisanGood.oak_resin)
+pine_tar = BundleItem(ArtisanGood.pine_tar)
+nautilus_shell = BundleItem(WaterItem.nautilus_shell)
+coral = BundleItem(WaterItem.coral)
+sea_urchin = BundleItem(WaterItem.sea_urchin)
+rainbow_shell = BundleItem(Forageable.rainbow_shell)
+clam = BundleItem(Fish.clam)
+cockle = BundleItem(Fish.cockle)
+mussel = BundleItem(Fish.mussel)
+oyster = BundleItem(Fish.oyster)
+seaweed = BundleItem(WaterItem.seaweed)
+
+wood = BundleItem(Material.wood, 99)
+stone = BundleItem(Material.stone, 99)
+hardwood = BundleItem(Material.hardwood, 10)
+clay = BundleItem(Material.clay, 10)
+fiber = BundleItem(Material.fiber, 99)
+
+blue_jazz = BundleItem(Flower.blue_jazz)
+cauliflower = BundleItem(Vegetable.cauliflower)
+green_bean = BundleItem(Vegetable.green_bean)
+kale = BundleItem(Vegetable.kale)
+parsnip = BundleItem(Vegetable.parsnip)
+potato = BundleItem(Vegetable.potato)
+strawberry = BundleItem(Fruit.strawberry, source=BundleItem.Sources.festival)
+tulip = BundleItem(Flower.tulip)
+unmilled_rice = BundleItem(Vegetable.unmilled_rice)
+coffee_bean = BundleItem(Seed.coffee)
+garlic = BundleItem(Vegetable.garlic)
+blueberry = BundleItem(Fruit.blueberry)
+corn = BundleItem(Vegetable.corn)
+hops = BundleItem(Vegetable.hops)
+hot_pepper = BundleItem(Fruit.hot_pepper)
+melon = BundleItem(Fruit.melon)
+poppy = BundleItem(Flower.poppy)
+radish = BundleItem(Vegetable.radish)
+summer_spangle = BundleItem(Flower.summer_spangle)
+sunflower = BundleItem(Flower.sunflower)
+tomato = BundleItem(Vegetable.tomato)
+wheat = BundleItem(Vegetable.wheat)
+hay = BundleItem(Forageable.hay)
+amaranth = BundleItem(Vegetable.amaranth)
+bok_choy = BundleItem(Vegetable.bok_choy)
+cranberries = BundleItem(Fruit.cranberries)
+eggplant = BundleItem(Vegetable.eggplant)
+fairy_rose = BundleItem(Flower.fairy_rose)
+pumpkin = BundleItem(Vegetable.pumpkin)
+yam = BundleItem(Vegetable.yam)
+sweet_gem_berry = BundleItem(Fruit.sweet_gem_berry)
+rhubarb = BundleItem(Fruit.rhubarb)
+beet = BundleItem(Vegetable.beet)
+red_cabbage = BundleItem(Vegetable.red_cabbage)
+starfruit = BundleItem(Fruit.starfruit)
+artichoke = BundleItem(Vegetable.artichoke)
+pineapple = BundleItem(Fruit.pineapple, source=BundleItem.Sources.island)
+taro_root = BundleItem(Vegetable.taro_root, source=BundleItem.Sources.island, )
+
+egg = BundleItem(AnimalProduct.egg)
+large_egg = BundleItem(AnimalProduct.large_egg)
+brown_egg = BundleItem(AnimalProduct.brown_egg)
+large_brown_egg = BundleItem(AnimalProduct.large_brown_egg)
+wool = BundleItem(AnimalProduct.wool)
+milk = BundleItem(AnimalProduct.milk)
+large_milk = BundleItem(AnimalProduct.large_milk)
+goat_milk = BundleItem(AnimalProduct.goat_milk)
+large_goat_milk = BundleItem(AnimalProduct.large_goat_milk)
+truffle = BundleItem(AnimalProduct.truffle)
+duck_feather = BundleItem(AnimalProduct.duck_feather)
+duck_egg = BundleItem(AnimalProduct.duck_egg)
+rabbit_foot = BundleItem(AnimalProduct.rabbit_foot)
+dinosaur_egg = BundleItem(AnimalProduct.dinosaur_egg)
+void_egg = BundleItem(AnimalProduct.void_egg)
+ostrich_egg = BundleItem(AnimalProduct.ostrich_egg, source=BundleItem.Sources.island, )
+golden_egg = BundleItem(AnimalProduct.golden_egg)
+
+truffle_oil = BundleItem(ArtisanGood.truffle_oil)
+cloth = BundleItem(ArtisanGood.cloth)
+goat_cheese = BundleItem(ArtisanGood.goat_cheese)
+cheese = BundleItem(ArtisanGood.cheese)
+honey = BundleItem(ArtisanGood.honey)
+beer = BundleItem(Beverage.beer)
+juice = BundleItem(ArtisanGood.juice)
+mead = BundleItem(ArtisanGood.mead)
+pale_ale = BundleItem(ArtisanGood.pale_ale)
+wine = BundleItem(ArtisanGood.wine)
+jelly = BundleItem(ArtisanGood.jelly)
+pickles = BundleItem(ArtisanGood.pickles)
+caviar = BundleItem(ArtisanGood.caviar)
+aged_roe = BundleItem(ArtisanGood.aged_roe)
+roe = BundleItem(AnimalProduct.roe)
+squid_ink = BundleItem(AnimalProduct.squid_ink)
+coffee = BundleItem(Beverage.coffee)
+green_tea = BundleItem(ArtisanGood.green_tea)
+apple = BundleItem(Fruit.apple)
+apricot = BundleItem(Fruit.apricot)
+orange = BundleItem(Fruit.orange)
+peach = BundleItem(Fruit.peach)
+pomegranate = BundleItem(Fruit.pomegranate)
+cherry = BundleItem(Fruit.cherry)
+banana = BundleItem(Fruit.banana, source=BundleItem.Sources.island)
+mango = BundleItem(Fruit.mango, source=BundleItem.Sources.island)
+
+basic_fertilizer = BundleItem(Fertilizer.basic, 100)
+quality_fertilizer = BundleItem(Fertilizer.quality, 20)
+deluxe_fertilizer = BundleItem(Fertilizer.deluxe, 5, source=BundleItem.Sources.island)
+basic_retaining_soil = BundleItem(RetainingSoil.basic, 80)
+quality_retaining_soil = BundleItem(RetainingSoil.quality, 50)
+deluxe_retaining_soil = BundleItem(RetainingSoil.deluxe, 20, source=BundleItem.Sources.island)
+speed_gro = BundleItem(SpeedGro.basic, 40)
+deluxe_speed_gro = BundleItem(SpeedGro.deluxe, 20)
+hyper_speed_gro = BundleItem(SpeedGro.hyper, 5, source=BundleItem.Sources.island)
+tree_fertilizer = BundleItem(Fertilizer.tree, 20)
+
+lobster = BundleItem(Fish.lobster)
+crab = BundleItem(Fish.crab)
+shrimp = BundleItem(Fish.shrimp)
+crayfish = BundleItem(Fish.crayfish)
+snail = BundleItem(Fish.snail)
+periwinkle = BundleItem(Fish.periwinkle)
+trash = BundleItem(Trash.trash)
+driftwood = BundleItem(Trash.driftwood)
+soggy_newspaper = BundleItem(Trash.soggy_newspaper)
+broken_cd = BundleItem(Trash.broken_cd)
+broken_glasses = BundleItem(Trash.broken_glasses)
+
+chub = BundleItem(Fish.chub)
+catfish = BundleItem(Fish.catfish)
+rainbow_trout = BundleItem(Fish.rainbow_trout)
+lingcod = BundleItem(Fish.lingcod)
+walleye = BundleItem(Fish.walleye)
+perch = BundleItem(Fish.perch)
+pike = BundleItem(Fish.pike)
+bream = BundleItem(Fish.bream)
+salmon = BundleItem(Fish.salmon)
+sunfish = BundleItem(Fish.sunfish)
+tiger_trout = BundleItem(Fish.tiger_trout)
+shad = BundleItem(Fish.shad)
+smallmouth_bass = BundleItem(Fish.smallmouth_bass)
+dorado = BundleItem(Fish.dorado)
+carp = BundleItem(Fish.carp)
+midnight_carp = BundleItem(Fish.midnight_carp)
+largemouth_bass = BundleItem(Fish.largemouth_bass)
+sturgeon = BundleItem(Fish.sturgeon)
+bullhead = BundleItem(Fish.bullhead)
+tilapia = BundleItem(Fish.tilapia)
+pufferfish = BundleItem(Fish.pufferfish)
+tuna = BundleItem(Fish.tuna)
+super_cucumber = BundleItem(Fish.super_cucumber)
+flounder = BundleItem(Fish.flounder)
+anchovy = BundleItem(Fish.anchovy)
+sardine = BundleItem(Fish.sardine)
+red_mullet = BundleItem(Fish.red_mullet)
+herring = BundleItem(Fish.herring)
+eel = BundleItem(Fish.eel)
+octopus = BundleItem(Fish.octopus)
+red_snapper = BundleItem(Fish.red_snapper)
+squid = BundleItem(Fish.squid)
+sea_cucumber = BundleItem(Fish.sea_cucumber)
+albacore = BundleItem(Fish.albacore)
+halibut = BundleItem(Fish.halibut)
+scorpion_carp = BundleItem(Fish.scorpion_carp)
+sandfish = BundleItem(Fish.sandfish)
+woodskip = BundleItem(Fish.woodskip)
+lava_eel = BundleItem(Fish.lava_eel)
+ice_pip = BundleItem(Fish.ice_pip)
+stonefish = BundleItem(Fish.stonefish)
+ghostfish = BundleItem(Fish.ghostfish)
+
+bouquet = BundleItem(Gift.bouquet)
+wilted_bouquet = BundleItem(Gift.wilted_bouquet)
+copper_bar = BundleItem(MetalBar.copper)
+iron_Bar = BundleItem(MetalBar.iron)
+gold_bar = BundleItem(MetalBar.gold)
+iridium_bar = BundleItem(MetalBar.iridium)
+refined_quartz = BundleItem(MetalBar.quartz)
+coal = BundleItem(Material.coal, 5)
+iridium_ore = BundleItem(Ore.iridium)
+gold_ore = BundleItem(Ore.gold)
+iron_ore = BundleItem(Ore.iron)
+copper_ore = BundleItem(Ore.copper)
+battery_pack = BundleItem(ArtisanGood.battery_pack)
+
+quartz = BundleItem(Mineral.quartz)
+fire_quartz = BundleItem(Mineral.fire_quartz)
+frozen_tear = BundleItem(Mineral.frozen_tear)
+earth_crystal = BundleItem(Mineral.earth_crystal)
+emerald = BundleItem(Mineral.emerald)
+aquamarine = BundleItem(Mineral.aquamarine)
+ruby = BundleItem(Mineral.ruby)
+amethyst = BundleItem(Mineral.amethyst)
+topaz = BundleItem(Mineral.topaz)
+jade = BundleItem(Mineral.jade)
+
+slime = BundleItem(Loot.slime, 99)
+bug_meat = BundleItem(Loot.bug_meat, 10)
+bat_wing = BundleItem(Loot.bat_wing, 10)
+solar_essence = BundleItem(Loot.solar_essence)
+void_essence = BundleItem(Loot.void_essence)
+
+petrified_slime = BundleItem(Mineral.petrified_slime)
+blue_slime_egg = BundleItem(Loot.blue_slime_egg)
+red_slime_egg = BundleItem(Loot.red_slime_egg)
+purple_slime_egg = BundleItem(Loot.purple_slime_egg)
+green_slime_egg = BundleItem(Loot.green_slime_egg)
+tiger_slime_egg = BundleItem(Loot.tiger_slime_egg, source=BundleItem.Sources.island)
+
+cherry_bomb = BundleItem(Bomb.cherry_bomb, 5)
+bomb = BundleItem(Bomb.bomb, 2)
+mega_bomb = BundleItem(Bomb.mega_bomb)
+explosive_ammo = BundleItem(Craftable.explosive_ammo, 5)
+
+maki_roll = BundleItem(Meal.maki_roll)
+fried_egg = BundleItem(Meal.fried_egg)
+omelet = BundleItem(Meal.omelet)
+pizza = BundleItem(Meal.pizza)
+hashbrowns = BundleItem(Meal.hashbrowns)
+pancakes = BundleItem(Meal.pancakes)
+bread = BundleItem(Meal.bread)
+tortilla = BundleItem(Meal.tortilla)
+triple_shot_espresso = BundleItem(Beverage.triple_shot_espresso)
+farmer_s_lunch = BundleItem(Meal.farmer_lunch)
+survival_burger = BundleItem(Meal.survival_burger)
+dish_o_the_sea = BundleItem(Meal.dish_o_the_sea)
+miner_s_treat = BundleItem(Meal.miners_treat)
+roots_platter = BundleItem(Meal.roots_platter)
+salad = BundleItem(Meal.salad)
+cheese_cauliflower = BundleItem(Meal.cheese_cauliflower)
+parsnip_soup = BundleItem(Meal.parsnip_soup)
+fried_mushroom = BundleItem(Meal.fried_mushroom)
+salmon_dinner = BundleItem(Meal.salmon_dinner)
+pepper_poppers = BundleItem(Meal.pepper_poppers)
+spaghetti = BundleItem(Meal.spaghetti)
+sashimi = BundleItem(Meal.sashimi)
+blueberry_tart = BundleItem(Meal.blueberry_tart)
+algae_soup = BundleItem(Meal.algae_soup)
+pale_broth = BundleItem(Meal.pale_broth)
+chowder = BundleItem(Meal.chowder)
+cookie = BundleItem(Meal.cookie)
+ancient_doll = BundleItem(Artifact.ancient_doll)
+ice_cream = BundleItem(Meal.ice_cream)
+cranberry_candy = BundleItem(Meal.cranberry_candy)
+ginger_ale = BundleItem(Beverage.ginger_ale, source=BundleItem.Sources.island)
+pink_cake = BundleItem(Meal.pink_cake)
+plum_pudding = BundleItem(Meal.plum_pudding)
+chocolate_cake = BundleItem(Meal.chocolate_cake)
+rhubarb_pie = BundleItem(Meal.rhubarb_pie)
+shrimp_cocktail = BundleItem(Meal.shrimp_cocktail)
+pina_colada = BundleItem(Beverage.pina_colada, source=BundleItem.Sources.island)
+
+green_algae = BundleItem(WaterItem.green_algae)
+white_algae = BundleItem(WaterItem.white_algae)
+geode = BundleItem(Geode.geode)
+frozen_geode = BundleItem(Geode.frozen)
+magma_geode = BundleItem(Geode.magma)
+omni_geode = BundleItem(Geode.omni)
+sap = BundleItem(Material.sap)
+
+dwarf_scroll_1 = BundleItem(Artifact.dwarf_scroll_i)
+dwarf_scroll_2 = BundleItem(Artifact.dwarf_scroll_ii)
+dwarf_scroll_3 = BundleItem(Artifact.dwarf_scroll_iii)
+dwarf_scroll_4 = BundleItem(Artifact.dwarf_scroll_iv)
+elvish_jewelry = BundleItem(Artifact.elvish_jewelry)
+ancient_drum = BundleItem(Artifact.ancient_drum)
+dried_starfish = BundleItem(Fossil.dried_starfish)
+bone_fragment = BundleItem(Fossil.bone_fragment)
+
+golden_mask = BundleItem(Artifact.golden_mask)
+golden_relic = BundleItem(Artifact.golden_relic)
+dwarf_gadget = BundleItem(Artifact.dwarf_gadget)
+dwarvish_helm = BundleItem(Artifact.dwarvish_helm)
+prehistoric_handaxe = BundleItem(Artifact.prehistoric_handaxe)
+bone_flute = BundleItem(Artifact.bone_flute)
+anchor = BundleItem(Artifact.anchor)
+prehistoric_tool = BundleItem(Artifact.prehistoric_tool)
+chicken_statue = BundleItem(Artifact.chicken_statue)
+rusty_cog = BundleItem(Artifact.rusty_cog)
+rusty_spur = BundleItem(Artifact.rusty_spur)
+rusty_spoon = BundleItem(Artifact.rusty_spoon)
+ancient_sword = BundleItem(Artifact.ancient_sword)
+ornamental_fan = BundleItem(Artifact.ornamental_fan)
+chipped_amphora = BundleItem(Artifact.chipped_amphora)
+
+prehistoric_scapula = BundleItem(Fossil.prehistoric_scapula)
+prehistoric_tibia = BundleItem(Fossil.prehistoric_tibia)
+prehistoric_skull = BundleItem(Fossil.prehistoric_skull)
+skeletal_hand = BundleItem(Fossil.skeletal_hand)
+prehistoric_rib = BundleItem(Fossil.prehistoric_rib)
+prehistoric_vertebra = BundleItem(Fossil.prehistoric_vertebra)
+skeletal_tail = BundleItem(Fossil.skeletal_tail)
+nautilus_fossil = BundleItem(Fossil.nautilus_fossil)
+amphibian_fossil = BundleItem(Fossil.amphibian_fossil)
+palm_fossil = BundleItem(Fossil.palm_fossil)
+trilobite = BundleItem(Fossil.trilobite)
+
+dinosaur_mayo = BundleItem(ArtisanGood.dinosaur_mayonnaise)
+void_mayo = BundleItem(ArtisanGood.void_mayonnaise)
+prismatic_shard = BundleItem(Mineral.prismatic_shard)
+diamond = BundleItem(Mineral.diamond)
+ancient_fruit = BundleItem(Fruit.ancient_fruit)
+void_salmon = BundleItem(Fish.void_salmon)
+tea_leaves = BundleItem(Vegetable.tea_leaves)
+blobfish = BundleItem(Fish.blobfish)
+spook_fish = BundleItem(Fish.spook_fish)
+lionfish = BundleItem(Fish.lionfish, source=BundleItem.Sources.island)
+blue_discus = BundleItem(Fish.blue_discus, source=BundleItem.Sources.island)
+stingray = BundleItem(Fish.stingray, source=BundleItem.Sources.island)
+spookfish = BundleItem(Fish.spookfish)
+midnight_squid = BundleItem(Fish.midnight_squid)
+
+angler = BundleItem(Fish.angler)
+crimsonfish = BundleItem(Fish.crimsonfish)
+mutant_carp = BundleItem(Fish.mutant_carp)
+glacierfish = BundleItem(Fish.glacierfish)
+legend = BundleItem(Fish.legend)
+
+spinner = BundleItem(Fishing.spinner)
+dressed_spinner = BundleItem(Fishing.dressed_spinner)
+trap_bobber = BundleItem(Fishing.trap_bobber)
+cork_bobber = BundleItem(Fishing.cork_bobber)
+lead_bobber = BundleItem(Fishing.lead_bobber)
+treasure_hunter = BundleItem(Fishing.treasure_hunter)
+barbed_hook = BundleItem(Fishing.barbed_hook)
+curiosity_lure = BundleItem(Fishing.curiosity_lure)
+quality_bobber = BundleItem(Fishing.quality_bobber)
+bait = BundleItem(Fishing.bait, 100)
+magnet = BundleItem(Fishing.magnet)
+wild_bait = BundleItem(Fishing.wild_bait, 10)
+magic_bait = BundleItem(Fishing.magic_bait, 5, source=BundleItem.Sources.island)
+pearl = BundleItem(Gift.pearl)
+
+ginger = BundleItem(Forageable.ginger, source=BundleItem.Sources.island)
+magma_cap = BundleItem(Forageable.magma_cap, source=BundleItem.Sources.island)
+
+wheat_flour = BundleItem(Ingredient.wheat_flour)
+sugar = BundleItem(Ingredient.sugar)
+vinegar = BundleItem(Ingredient.vinegar)
+
+# Crafts Room
+spring_foraging_items_vanilla = [wild_horseradish, daffodil, leek, dandelion]
+spring_foraging_items_thematic = [*spring_foraging_items_vanilla, spring_onion, salmonberry, morel]
+spring_foraging_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.spring_foraging, spring_foraging_items_vanilla, 4, 4)
+spring_foraging_bundle_thematic = BundleTemplate.extend_from(spring_foraging_bundle_vanilla, spring_foraging_items_thematic)
+
+summer_foraging_items_vanilla = [grape, spice_berry, sweet_pea]
+summer_foraging_items_thematic = [*summer_foraging_items_vanilla, fiddlehead_fern, red_mushroom, rainbow_shell]
+summer_foraging_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.summer_foraging, summer_foraging_items_vanilla, 3, 3)
+summer_foraging_bundle_thematic = BundleTemplate.extend_from(summer_foraging_bundle_vanilla, summer_foraging_items_thematic)
+
+fall_foraging_items_vanilla = [common_mushroom, wild_plum, hazelnut, blackberry]
+fall_foraging_items_thematic = [*fall_foraging_items_vanilla, chanterelle]
+fall_foraging_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.fall_foraging, fall_foraging_items_vanilla, 4, 4)
+fall_foraging_bundle_thematic = BundleTemplate.extend_from(fall_foraging_bundle_vanilla, fall_foraging_items_thematic)
+
+winter_foraging_items_vanilla = [winter_root, crystal_fruit, snow_yam, crocus]
+winter_foraging_items_thematic = [*winter_foraging_items_vanilla, holly, nautilus_shell]
+winter_foraging_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.winter_foraging, winter_foraging_items_vanilla, 4, 4)
+winter_foraging_bundle_thematic = BundleTemplate.extend_from(winter_foraging_bundle_vanilla, winter_foraging_items_thematic)
+
+construction_items_vanilla = [wood, stone, hardwood]
+construction_items_thematic = [*construction_items_vanilla, clay, fiber, sap.as_amount(50)]
+construction_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.construction, construction_items_vanilla, 4, 4)
+construction_bundle_thematic = BundleTemplate.extend_from(construction_bundle_vanilla, construction_items_thematic)
+
+exotic_foraging_items_vanilla = [coconut, cactus_fruit, cave_carrot, red_mushroom, purple_mushroom, maple_syrup, oak_resin, pine_tar, morel]
+exotic_foraging_items_thematic = [*exotic_foraging_items_vanilla, coral, sea_urchin, clam, cockle, mussel, oyster, seaweed]
+exotic_foraging_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.exotic_foraging, exotic_foraging_items_vanilla, 9, 5)
+exotic_foraging_bundle_thematic = BundleTemplate.extend_from(exotic_foraging_bundle_vanilla, exotic_foraging_items_thematic)
+
+beach_foraging_items = [nautilus_shell, coral, sea_urchin, rainbow_shell, clam, cockle, mussel, oyster, seaweed]
+beach_foraging_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.beach_foraging, beach_foraging_items, 4, 4)
+
+mines_foraging_items = [quartz, earth_crystal, frozen_tear, fire_quartz, red_mushroom, purple_mushroom, cave_carrot]
+mines_foraging_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.mines_foraging, mines_foraging_items, 4, 4)
+
+desert_foraging_items = [cactus_fruit.as_quality(ForageQuality.gold), cactus_fruit.as_amount(5), coconut.as_quality(ForageQuality.gold), coconut.as_amount(5)]
+desert_foraging_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.desert_foraging, desert_foraging_items, 2, 2)
+
+island_foraging_items = [ginger.as_amount(5), magma_cap.as_quality(ForageQuality.gold), magma_cap.as_amount(5),
+ fiddlehead_fern.as_quality(ForageQuality.gold), fiddlehead_fern.as_amount(5)]
+island_foraging_bundle = IslandBundleTemplate(CCRoom.crafts_room, BundleName.island_foraging, island_foraging_items, 2, 2)
+
+sticky_items = [sap.as_amount(500), sap.as_amount(500)]
+sticky_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.sticky, sticky_items, 1, 1)
+
+wild_medicine_items = [item.as_amount(5) for item in [purple_mushroom, fiddlehead_fern, white_algae, hops, blackberry, dandelion]]
+wild_medicine_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.wild_medicine, wild_medicine_items, 4, 3)
+
+quality_foraging_items = sorted({item.as_quality(ForageQuality.gold).as_amount(1)
+ for item in
+ [*spring_foraging_items_thematic, *summer_foraging_items_thematic, *fall_foraging_items_thematic,
+ *winter_foraging_items_thematic, *beach_foraging_items, *desert_foraging_items, magma_cap]})
+quality_foraging_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.quality_foraging, quality_foraging_items, 4, 3)
+
+crafts_room_bundles_vanilla = [spring_foraging_bundle_vanilla, summer_foraging_bundle_vanilla, fall_foraging_bundle_vanilla,
+ winter_foraging_bundle_vanilla, construction_bundle_vanilla, exotic_foraging_bundle_vanilla]
+crafts_room_bundles_thematic = [spring_foraging_bundle_thematic, summer_foraging_bundle_thematic, fall_foraging_bundle_thematic,
+ winter_foraging_bundle_thematic, construction_bundle_thematic, exotic_foraging_bundle_thematic]
+crafts_room_bundles_remixed = [*crafts_room_bundles_thematic, beach_foraging_bundle, mines_foraging_bundle, desert_foraging_bundle,
+ island_foraging_bundle, sticky_bundle, wild_medicine_bundle, quality_foraging_bundle]
+crafts_room_vanilla = BundleRoomTemplate(CCRoom.crafts_room, crafts_room_bundles_vanilla, 6)
+crafts_room_thematic = BundleRoomTemplate(CCRoom.crafts_room, crafts_room_bundles_thematic, 6)
+crafts_room_remixed = BundleRoomTemplate(CCRoom.crafts_room, crafts_room_bundles_remixed, 6)
+
+# Pantry
+spring_crops_items_vanilla = [parsnip, green_bean, cauliflower, potato]
+spring_crops_items_thematic = [*spring_crops_items_vanilla, blue_jazz, coffee_bean, garlic, kale, rhubarb, strawberry, tulip, unmilled_rice]
+spring_crops_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.spring_crops, spring_crops_items_vanilla, 4, 4)
+spring_crops_bundle_thematic = BundleTemplate.extend_from(spring_crops_bundle_vanilla, spring_crops_items_thematic)
+
+summer_crops_items_vanilla = [tomato, hot_pepper, blueberry, melon]
+summer_crops_items_thematic = [*summer_crops_items_vanilla, corn, hops, poppy, radish, red_cabbage, starfruit, summer_spangle, sunflower, wheat]
+summer_crops_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.summer_crops, summer_crops_items_vanilla, 4, 4)
+summer_crops_bundle_thematic = BundleTemplate.extend_from(summer_crops_bundle_vanilla, summer_crops_items_thematic)
+
+fall_crops_items_vanilla = [corn, eggplant, pumpkin, yam]
+fall_crops_items_thematic = [*fall_crops_items_vanilla, amaranth, artichoke, beet, bok_choy, cranberries, fairy_rose, grape, sunflower, wheat, sweet_gem_berry]
+fall_crops_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.fall_crops, fall_crops_items_vanilla, 4, 4)
+fall_crops_bundle_thematic = BundleTemplate.extend_from(fall_crops_bundle_vanilla, fall_crops_items_thematic)
+
+all_crops_items = sorted({*spring_crops_items_thematic, *summer_crops_items_thematic, *fall_crops_items_thematic})
+
+quality_crops_items_vanilla = [item.as_quality_crop() for item in [parsnip, melon, pumpkin, corn]]
+quality_crops_items_thematic = [item.as_quality_crop() for item in all_crops_items]
+quality_crops_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.quality_crops, quality_crops_items_vanilla, 4, 3)
+quality_crops_bundle_thematic = BundleTemplate.extend_from(quality_crops_bundle_vanilla, quality_crops_items_thematic)
+
+animal_items_vanilla = [large_milk, large_brown_egg, large_egg, large_goat_milk, wool, duck_egg]
+animal_items_thematic = [*animal_items_vanilla, egg, brown_egg, milk, goat_milk, truffle,
+ duck_feather, rabbit_foot, dinosaur_egg, void_egg, golden_egg, ostrich_egg]
+animal_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.animal, animal_items_vanilla, 6, 5)
+animal_bundle_thematic = BundleTemplate.extend_from(animal_bundle_vanilla, animal_items_thematic)
+
+artisan_items_vanilla = [truffle_oil, cloth, goat_cheese, cheese, honey, jelly, apple, apricot, orange, peach, pomegranate, cherry]
+artisan_items_thematic = [*artisan_items_vanilla, beer, juice, mead, pale_ale, wine, pickles, caviar, aged_roe, coffee, green_tea, banana, mango]
+artisan_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.artisan, artisan_items_vanilla, 12, 6)
+artisan_bundle_thematic = BundleTemplate.extend_from(artisan_bundle_vanilla, artisan_items_thematic)
+
+rare_crops_items = [ancient_fruit, sweet_gem_berry]
+rare_crops_bundle = BundleTemplate(CCRoom.pantry, BundleName.rare_crops, rare_crops_items, 2, 2)
+
+fish_farmer_items = [roe.as_amount(15), aged_roe.as_amount(15), squid_ink]
+fish_farmer_bundle = BundleTemplate(CCRoom.pantry, BundleName.fish_farmer, fish_farmer_items, 3, 2)
+
+garden_items = [tulip, blue_jazz, summer_spangle, sunflower, fairy_rose, poppy, bouquet]
+garden_bundle = BundleTemplate(CCRoom.pantry, BundleName.garden, garden_items, 5, 4)
+
+brewer_items = [mead, pale_ale, wine, juice, green_tea, beer]
+brewer_bundle = BundleTemplate(CCRoom.pantry, BundleName.brewer, brewer_items, 5, 4)
+
+orchard_items = [apple, apricot, orange, peach, pomegranate, cherry, banana, mango]
+orchard_bundle = BundleTemplate(CCRoom.pantry, BundleName.orchard, orchard_items, 6, 4)
+
+island_crops_items = [pineapple, taro_root, banana, mango]
+island_crops_bundle = IslandBundleTemplate(CCRoom.pantry, BundleName.island_crops, island_crops_items, 3, 3)
+
+agronomist_items = [basic_fertilizer, quality_fertilizer, deluxe_fertilizer,
+ basic_retaining_soil, quality_retaining_soil, deluxe_retaining_soil,
+ speed_gro, deluxe_speed_gro, hyper_speed_gro, tree_fertilizer]
+agronomist_bundle = BundleTemplate(CCRoom.pantry, BundleName.agronomist, agronomist_items, 4, 3)
+
+slime_farmer_items = [slime.as_amount(99), petrified_slime.as_amount(10), blue_slime_egg, red_slime_egg,
+ purple_slime_egg, green_slime_egg, tiger_slime_egg]
+slime_farmer_bundle = BundleTemplate(CCRoom.pantry, BundleName.slime_farmer, slime_farmer_items, 4, 3)
+
+pantry_bundles_vanilla = [spring_crops_bundle_vanilla, summer_crops_bundle_vanilla, fall_crops_bundle_vanilla,
+ quality_crops_bundle_vanilla, animal_bundle_vanilla, artisan_bundle_vanilla]
+pantry_bundles_thematic = [spring_crops_bundle_thematic, summer_crops_bundle_thematic, fall_crops_bundle_thematic,
+ quality_crops_bundle_thematic, animal_bundle_thematic, artisan_bundle_thematic]
+pantry_bundles_remixed = [*pantry_bundles_thematic, rare_crops_bundle, fish_farmer_bundle, garden_bundle,
+ brewer_bundle, orchard_bundle, island_crops_bundle, agronomist_bundle, slime_farmer_bundle]
+pantry_vanilla = BundleRoomTemplate(CCRoom.pantry, pantry_bundles_vanilla, 6)
+pantry_thematic = BundleRoomTemplate(CCRoom.pantry, pantry_bundles_thematic, 6)
+pantry_remixed = BundleRoomTemplate(CCRoom.pantry, pantry_bundles_remixed, 6)
+
+# Fish Tank
+river_fish_items_vanilla = [sunfish, catfish, shad, tiger_trout]
+river_fish_items_thematic = [*river_fish_items_vanilla, chub, rainbow_trout, lingcod, walleye, perch, pike, bream, salmon, smallmouth_bass, dorado]
+river_fish_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.river_fish, river_fish_items_vanilla, 4, 4)
+river_fish_bundle_thematic = BundleTemplate.extend_from(river_fish_bundle_vanilla, river_fish_items_thematic)
+
+lake_fish_items_vanilla = [largemouth_bass, carp, bullhead, sturgeon]
+lake_fish_items_thematic = [*lake_fish_items_vanilla, chub, rainbow_trout, lingcod, walleye, perch, midnight_carp]
+lake_fish_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.lake_fish, lake_fish_items_vanilla, 4, 4)
+lake_fish_bundle_thematic = BundleTemplate.extend_from(lake_fish_bundle_vanilla, lake_fish_items_thematic)
+
+ocean_fish_items_vanilla = [sardine, tuna, red_snapper, tilapia]
+ocean_fish_items_thematic = [*ocean_fish_items_vanilla, pufferfish, super_cucumber, flounder, anchovy, red_mullet,
+ herring, eel, octopus, squid, sea_cucumber, albacore, halibut]
+ocean_fish_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.ocean_fish, ocean_fish_items_vanilla, 4, 4)
+ocean_fish_bundle_thematic = BundleTemplate.extend_from(ocean_fish_bundle_vanilla, ocean_fish_items_thematic)
+
+night_fish_items_vanilla = [walleye, bream, eel]
+night_fish_items_thematic = [*night_fish_items_vanilla, super_cucumber, squid, midnight_carp, midnight_squid]
+night_fish_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.night_fish, night_fish_items_vanilla, 3, 3)
+night_fish_bundle_thematic = BundleTemplate.extend_from(night_fish_bundle_vanilla, night_fish_items_thematic)
+
+crab_pot_items_vanilla = [lobster, crayfish, crab, cockle, mussel, shrimp, snail, periwinkle, oyster, clam]
+crab_pot_trash_items = [trash, driftwood, soggy_newspaper, broken_cd, broken_glasses]
+crab_pot_items_thematic = [*crab_pot_items_vanilla, *crab_pot_trash_items]
+crab_pot_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.crab_pot, crab_pot_items_vanilla, 10, 5)
+crab_pot_bundle_thematic = BundleTemplate.extend_from(crab_pot_bundle_vanilla, crab_pot_items_thematic)
+trash_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.trash, crab_pot_trash_items, 4, 4)
+
+specialty_fish_items_vanilla = [pufferfish, ghostfish, sandfish, woodskip]
+specialty_fish_items_thematic = [*specialty_fish_items_vanilla, scorpion_carp, eel, octopus, lava_eel, ice_pip,
+ stonefish, void_salmon, stingray, spookfish, midnight_squid]
+specialty_fish_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.specialty_fish, specialty_fish_items_vanilla, 4, 4)
+specialty_fish_bundle_thematic = BundleTemplate.extend_from(specialty_fish_bundle_vanilla, specialty_fish_items_thematic)
+
+spring_fish_items = [herring, halibut, shad, flounder, sunfish, sardine, catfish, anchovy, smallmouth_bass, eel, legend]
+spring_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.spring_fish, spring_fish_items, 4, 4)
+
+summer_fish_items = [tuna, pike, red_mullet, sturgeon, red_snapper, super_cucumber, tilapia, pufferfish, rainbow_trout,
+ octopus, dorado, halibut, shad, flounder, sunfish, crimsonfish]
+summer_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.summer_fish, summer_fish_items, 4, 4)
+
+fall_fish_items = [red_snapper, super_cucumber, tilapia, shad, sardine, catfish, anchovy, smallmouth_bass, eel, midnight_carp,
+ walleye, sea_cucumber, tiger_trout, albacore, salmon, angler]
+fall_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.fall_fish, fall_fish_items, 4, 4)
+
+winter_fish_items = [perch, squid, lingcod, tuna, pike, red_mullet, sturgeon, red_snapper, herring, halibut, sardine,
+ midnight_carp, sea_cucumber, tiger_trout, albacore, glacierfish]
+winter_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.winter_fish, winter_fish_items, 4, 4)
+
+rain_fish_items = [red_snapper, shad, catfish, eel, walleye]
+rain_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.rain_fish, rain_fish_items, 3, 3)
+
+quality_fish_items = sorted({item.as_quality(FishQuality.gold) for item in [*river_fish_items_thematic, *lake_fish_items_thematic, *ocean_fish_items_thematic]})
+quality_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.quality_fish, quality_fish_items, 4, 4)
+
+master_fisher_items = [lava_eel, scorpion_carp, octopus, blobfish, lingcod, ice_pip, super_cucumber, stingray, void_salmon, pufferfish]
+master_fisher_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.master_fisher, master_fisher_items, 4, 2)
+
+legendary_fish_items = [angler, legend, mutant_carp, crimsonfish, glacierfish]
+legendary_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.legendary_fish, legendary_fish_items, 4, 2)
+
+island_fish_items = [lionfish, blue_discus, stingray]
+island_fish_bundle = IslandBundleTemplate(CCRoom.fish_tank, BundleName.island_fish, island_fish_items, 3, 3)
+
+tackle_items = [spinner, dressed_spinner, trap_bobber, cork_bobber, lead_bobber, treasure_hunter, barbed_hook, curiosity_lure, quality_bobber]
+tackle_bundle = IslandBundleTemplate(CCRoom.fish_tank, BundleName.tackle, tackle_items, 3, 2)
+
+bait_items = [bait, magnet, wild_bait, magic_bait]
+bait_bundle = IslandBundleTemplate(CCRoom.fish_tank, BundleName.bait, bait_items, 2, 2)
+
+deep_fishing_items = [blobfish, spook_fish, midnight_squid, sea_cucumber, super_cucumber, octopus, pearl, seaweed]
+deep_fishing_bundle = FestivalBundleTemplate(CCRoom.fish_tank, BundleName.deep_fishing, deep_fishing_items, 4, 3)
+
+fish_tank_bundles_vanilla = [river_fish_bundle_vanilla, lake_fish_bundle_vanilla, ocean_fish_bundle_vanilla,
+ night_fish_bundle_vanilla, crab_pot_bundle_vanilla, specialty_fish_bundle_vanilla]
+fish_tank_bundles_thematic = [river_fish_bundle_thematic, lake_fish_bundle_thematic, ocean_fish_bundle_thematic,
+ night_fish_bundle_thematic, crab_pot_bundle_thematic, specialty_fish_bundle_thematic]
+fish_tank_bundles_remixed = [*fish_tank_bundles_thematic, spring_fish_bundle, summer_fish_bundle, fall_fish_bundle, winter_fish_bundle, trash_bundle,
+ rain_fish_bundle, quality_fish_bundle, master_fisher_bundle, legendary_fish_bundle, tackle_bundle, bait_bundle]
+
+# In Remixed, the trash items are in the recycling bundle, so we don't use the thematic version of the crab pot bundle that added trash items to it
+fish_tank_bundles_remixed.remove(crab_pot_bundle_thematic)
+fish_tank_bundles_remixed.append(crab_pot_bundle_vanilla)
+fish_tank_vanilla = BundleRoomTemplate(CCRoom.fish_tank, fish_tank_bundles_vanilla, 6)
+fish_tank_thematic = BundleRoomTemplate(CCRoom.fish_tank, fish_tank_bundles_thematic, 6)
+fish_tank_remixed = BundleRoomTemplate(CCRoom.fish_tank, fish_tank_bundles_remixed, 6)
+
+# Boiler Room
+blacksmith_items_vanilla = [copper_bar, iron_Bar, gold_bar]
+blacksmith_items_thematic = [*blacksmith_items_vanilla, iridium_bar, refined_quartz.as_amount(3), wilted_bouquet]
+blacksmith_bundle_vanilla = BundleTemplate(CCRoom.boiler_room, BundleName.blacksmith, blacksmith_items_vanilla, 3, 3)
+blacksmith_bundle_thematic = BundleTemplate.extend_from(blacksmith_bundle_vanilla, blacksmith_items_thematic)
+
+geologist_items_vanilla = [quartz, earth_crystal, frozen_tear, fire_quartz]
+geologist_items_thematic = [*geologist_items_vanilla, emerald, aquamarine, ruby, amethyst, topaz, jade, diamond]
+geologist_bundle_vanilla = BundleTemplate(CCRoom.boiler_room, BundleName.geologist, geologist_items_vanilla, 4, 4)
+geologist_bundle_thematic = BundleTemplate.extend_from(geologist_bundle_vanilla, geologist_items_thematic)
+
+adventurer_items_vanilla = [slime, bat_wing, solar_essence, void_essence]
+adventurer_items_thematic = [*adventurer_items_vanilla, bug_meat, coal, bone_fragment.as_amount(10)]
+adventurer_bundle_vanilla = BundleTemplate(CCRoom.boiler_room, BundleName.adventurer, adventurer_items_vanilla, 4, 2)
+adventurer_bundle_thematic = BundleTemplate.extend_from(adventurer_bundle_vanilla, adventurer_items_thematic)
+
+# Where to put radioactive bar?
+treasure_hunter_items = [emerald, aquamarine, ruby, amethyst, topaz, jade, diamond]
+treasure_hunter_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.treasure_hunter, treasure_hunter_items, 6, 5)
+
+engineer_items = [iridium_ore.as_amount(5), battery_pack, refined_quartz.as_amount(5), diamond]
+engineer_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.engineer, engineer_items, 3, 3)
+
+demolition_items = [cherry_bomb, bomb, mega_bomb, explosive_ammo]
+demolition_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.demolition, demolition_items, 3, 3)
+
+recycling_items = [stone, coal, iron_ore, wood, cloth, refined_quartz]
+recycling_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.recycling, recycling_items, 4, 4)
+
+archaeologist_items = [golden_mask, golden_relic, ancient_drum, dwarf_gadget, dwarvish_helm, prehistoric_handaxe, bone_flute, anchor, prehistoric_tool,
+ chicken_statue, rusty_cog, rusty_spur, rusty_spoon, ancient_sword, ornamental_fan, elvish_jewelry, ancient_doll, chipped_amphora]
+archaeologist_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.archaeologist, archaeologist_items, 6, 3)
+
+paleontologist_items = [prehistoric_scapula, prehistoric_tibia, prehistoric_skull, skeletal_hand, prehistoric_rib, prehistoric_vertebra, skeletal_tail,
+ nautilus_fossil, amphibian_fossil, palm_fossil, trilobite]
+paleontologist_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.paleontologist, paleontologist_items, 6, 3)
+
+boiler_room_bundles_vanilla = [blacksmith_bundle_vanilla, geologist_bundle_vanilla, adventurer_bundle_vanilla]
+boiler_room_bundles_thematic = [blacksmith_bundle_thematic, geologist_bundle_thematic, adventurer_bundle_thematic]
+boiler_room_bundles_remixed = [*boiler_room_bundles_thematic, treasure_hunter_bundle, engineer_bundle,
+ demolition_bundle, recycling_bundle, archaeologist_bundle, paleontologist_bundle]
+boiler_room_vanilla = BundleRoomTemplate(CCRoom.boiler_room, boiler_room_bundles_vanilla, 3)
+boiler_room_thematic = BundleRoomTemplate(CCRoom.boiler_room, boiler_room_bundles_thematic, 3)
+boiler_room_remixed = BundleRoomTemplate(CCRoom.boiler_room, boiler_room_bundles_remixed, 3)
+
+# Bulletin Board
+chef_items_vanilla = [maple_syrup, fiddlehead_fern, truffle, poppy, maki_roll, fried_egg]
+# More recipes?
+chef_items_thematic = [maki_roll, fried_egg, omelet, pizza, hashbrowns, pancakes, bread, tortilla,
+ farmer_s_lunch, survival_burger, dish_o_the_sea, miner_s_treat, roots_platter, salad,
+ cheese_cauliflower, parsnip_soup, fried_mushroom, salmon_dinner, pepper_poppers, spaghetti,
+ sashimi, blueberry_tart, algae_soup, pale_broth, chowder]
+chef_bundle_vanilla = BundleTemplate(CCRoom.bulletin_board, BundleName.chef, chef_items_vanilla, 6, 6)
+chef_bundle_thematic = BundleTemplate.extend_from(chef_bundle_vanilla, chef_items_thematic)
+
+dye_items_vanilla = [red_mushroom, sea_urchin, sunflower, duck_feather, aquamarine, red_cabbage]
dye_red_items = [cranberries, hot_pepper, radish, rhubarb, spaghetti, strawberry, tomato, tulip]
dye_orange_items = [poppy, pumpkin, apricot, orange, spice_berry, winter_root]
dye_yellow_items = [corn, parsnip, summer_spangle, sunflower]
dye_green_items = [fiddlehead_fern, kale, artichoke, bok_choy, green_bean]
dye_blue_items = [blueberry, blue_jazz, blackberry, crystal_fruit]
dye_purple_items = [beet, crocus, eggplant, red_cabbage, sweet_pea]
-dye_items = [dye_red_items, dye_orange_items, dye_yellow_items, dye_green_items, dye_blue_items, dye_purple_items]
-field_research_items = [purple_mushroom, nautilus_shell, chub, geode, frozen_geode, magma_geode, omni_geode,
- rainbow_shell, amethyst, bream, carp]
-fodder_items = [wheat.as_amount(10), hay.as_amount(10), apple.as_amount(3), kale.as_amount(3), corn.as_amount(3),
- green_bean.as_amount(3), potato.as_amount(3), green_algae.as_amount(5), white_algae.as_amount(3)]
-enchanter_items = [oak_resin, wine, rabbit_foot, pomegranate, purple_mushroom, solar_essence,
- super_cucumber, void_essence, fire_quartz, frozen_tear, jade]
-
-vault_2500_items = [BundleItem.money_bundle(2500)]
-vault_5000_items = [BundleItem.money_bundle(5000)]
-vault_10000_items = [BundleItem.money_bundle(10000)]
-vault_25000_items = [BundleItem.money_bundle(25000)]
-
-crafts_room_bundle_items = [
- *spring_foraging_items,
- *summer_foraging_items,
- *fall_foraging_items,
- *winter_foraging_items,
- *exotic_foraging_items,
- *construction_items,
-]
-
-pantry_bundle_items = sorted({
- *spring_crop_items,
- *summer_crops_items,
- *fall_crops_items,
- *quality_crops_items,
- *animal_product_items,
- *artisan_goods_items,
-})
-
-fish_tank_bundle_items = sorted({
- *river_fish_items,
- *lake_fish_items,
- *ocean_fish_items,
- *night_fish_items,
- *crab_pot_items,
- *specialty_fish_items,
-})
-
-boiler_room_bundle_items = sorted({
- *blacksmith_items,
- *geologist_items,
- *adventurer_items,
-})
-
-bulletin_board_bundle_items = sorted({
- *chef_items,
- *[item for dye_color_items in dye_items for item in dye_color_items],
- *field_research_items,
- *fodder_items,
- *enchanter_items
-})
-
-vault_bundle_items = [
- *vault_2500_items,
- *vault_5000_items,
- *vault_10000_items,
- *vault_25000_items,
-]
-
-all_bundle_items_except_money = sorted({
- *crafts_room_bundle_items,
- *pantry_bundle_items,
- *fish_tank_bundle_items,
- *boiler_room_bundle_items,
- *bulletin_board_bundle_items,
-}, key=lambda x: x.item.name)
-
-all_bundle_items = sorted({
- *crafts_room_bundle_items,
- *pantry_bundle_items,
- *fish_tank_bundle_items,
- *boiler_room_bundle_items,
- *bulletin_board_bundle_items,
- *vault_bundle_items,
-}, key=lambda x: x.item.name)
-
-all_bundle_items_by_name = {item.item.name: item for item in all_bundle_items}
-all_bundle_items_by_id = {item.item.item_id: item for item in all_bundle_items}
+dye_items_thematic = [dye_red_items, dye_orange_items, dye_yellow_items, dye_green_items, dye_blue_items, dye_purple_items]
+dye_bundle_vanilla = BundleTemplate(CCRoom.bulletin_board, BundleName.dye, dye_items_vanilla, 6, 6)
+dye_bundle_thematic = DeepBundleTemplate(CCRoom.bulletin_board, BundleName.dye, dye_items_thematic, 6, 6)
+
+field_research_items_vanilla = [purple_mushroom, nautilus_shell, chub, frozen_geode]
+field_research_items_thematic = [*field_research_items_vanilla, geode, magma_geode, omni_geode,
+ rainbow_shell, amethyst, bream, carp]
+field_research_bundle_vanilla = BundleTemplate(CCRoom.bulletin_board, BundleName.field_research, field_research_items_vanilla, 4, 4)
+field_research_bundle_thematic = BundleTemplate.extend_from(field_research_bundle_vanilla, field_research_items_thematic)
+
+fodder_items_vanilla = [wheat.as_amount(10), hay.as_amount(10), apple.as_amount(3)]
+fodder_items_thematic = [*fodder_items_vanilla, kale.as_amount(3), corn.as_amount(3), green_bean.as_amount(3),
+ potato.as_amount(3), green_algae.as_amount(5), white_algae.as_amount(3)]
+fodder_bundle_vanilla = BundleTemplate(CCRoom.bulletin_board, BundleName.fodder, fodder_items_vanilla, 3, 3)
+fodder_bundle_thematic = BundleTemplate.extend_from(fodder_bundle_vanilla, fodder_items_thematic)
+
+enchanter_items_vanilla = [oak_resin, wine, rabbit_foot, pomegranate]
+enchanter_items_thematic = [*enchanter_items_vanilla, purple_mushroom, solar_essence,
+ super_cucumber, void_essence, fire_quartz, frozen_tear, jade]
+enchanter_bundle_vanilla = BundleTemplate(CCRoom.bulletin_board, BundleName.enchanter, enchanter_items_vanilla, 4, 4)
+enchanter_bundle_thematic = BundleTemplate.extend_from(enchanter_bundle_vanilla, enchanter_items_thematic)
+
+children_items = [salmonberry.as_amount(10), cookie, ancient_doll, ice_cream, cranberry_candy, ginger_ale,
+ grape.as_amount(10), pink_cake, snail, fairy_rose, plum_pudding]
+children_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.children, children_items, 4, 3)
+
+forager_items = [salmonberry.as_amount(50), blackberry.as_amount(50), wild_plum.as_amount(20), snow_yam.as_amount(20),
+ common_mushroom.as_amount(20), grape.as_amount(20), spring_onion.as_amount(20)]
+forager_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.forager, forager_items, 3, 2)
+
+home_cook_items = [egg.as_amount(10), milk.as_amount(10), wheat_flour.as_amount(100), sugar.as_amount(100), vinegar.as_amount(100),
+ chocolate_cake, pancakes, rhubarb_pie]
+home_cook_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.home_cook, home_cook_items, 3, 3)
+
+bartender_items = [shrimp_cocktail, triple_shot_espresso, ginger_ale, cranberry_candy, beer, pale_ale, pina_colada]
+bartender_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.bartender, bartender_items, 3, 3)
+
+bulletin_board_bundles_vanilla = [chef_bundle_vanilla, dye_bundle_vanilla, field_research_bundle_vanilla, fodder_bundle_vanilla, enchanter_bundle_vanilla]
+bulletin_board_bundles_thematic = [chef_bundle_thematic, dye_bundle_thematic, field_research_bundle_thematic, fodder_bundle_thematic, enchanter_bundle_thematic]
+bulletin_board_bundles_remixed = [*bulletin_board_bundles_thematic, children_bundle, forager_bundle, home_cook_bundle, bartender_bundle]
+bulletin_board_vanilla = BundleRoomTemplate(CCRoom.bulletin_board, bulletin_board_bundles_vanilla, 5)
+bulletin_board_thematic = BundleRoomTemplate(CCRoom.bulletin_board, bulletin_board_bundles_thematic, 5)
+bulletin_board_remixed = BundleRoomTemplate(CCRoom.bulletin_board, bulletin_board_bundles_remixed, 5)
+
+missing_bundle_items_vanilla = [wine.as_quality(ArtisanQuality.silver), dinosaur_mayo, prismatic_shard, caviar,
+ ancient_fruit.as_quality_crop(), void_salmon.as_quality(FishQuality.gold)]
+missing_bundle_items_thematic = [*missing_bundle_items_vanilla, pale_ale.as_quality(ArtisanQuality.silver), beer.as_quality(ArtisanQuality.silver),
+ mead.as_quality(ArtisanQuality.silver),
+ cheese.as_quality(ArtisanQuality.silver), goat_cheese.as_quality(ArtisanQuality.silver), void_mayo, cloth, green_tea,
+ truffle_oil, diamond,
+ sweet_gem_berry.as_quality_crop(), starfruit.as_quality_crop(),
+ tea_leaves.as_amount(5), lava_eel.as_quality(FishQuality.gold), scorpion_carp.as_quality(FishQuality.gold),
+ blobfish.as_quality(FishQuality.gold)]
+missing_bundle_vanilla = BundleTemplate(CCRoom.abandoned_joja_mart, BundleName.missing_bundle, missing_bundle_items_vanilla, 6, 5)
+missing_bundle_thematic = BundleTemplate.extend_from(missing_bundle_vanilla, missing_bundle_items_thematic)
+
+abandoned_joja_mart_bundles_vanilla = [missing_bundle_vanilla]
+abandoned_joja_mart_bundles_thematic = [missing_bundle_thematic]
+abandoned_joja_mart_vanilla = BundleRoomTemplate(CCRoom.abandoned_joja_mart, abandoned_joja_mart_bundles_vanilla, 1)
+abandoned_joja_mart_thematic = BundleRoomTemplate(CCRoom.abandoned_joja_mart, abandoned_joja_mart_bundles_thematic, 1)
+abandoned_joja_mart_remixed = abandoned_joja_mart_thematic
+
+# Make thematic with other currencies
+vault_2500_gold = BundleItem.money_bundle(2500)
+vault_5000_gold = BundleItem.money_bundle(5000)
+vault_10000_gold = BundleItem.money_bundle(10000)
+vault_25000_gold = BundleItem.money_bundle(25000)
+
+vault_2500_bundle = MoneyBundleTemplate(CCRoom.vault, vault_2500_gold)
+vault_5000_bundle = MoneyBundleTemplate(CCRoom.vault, vault_5000_gold)
+vault_10000_bundle = MoneyBundleTemplate(CCRoom.vault, vault_10000_gold)
+vault_25000_bundle = MoneyBundleTemplate(CCRoom.vault, vault_25000_gold)
+
+vault_gambler_items = BundleItem(Currency.qi_coin, 10000)
+vault_gambler_bundle = CurrencyBundleTemplate(CCRoom.vault, BundleName.gambler, vault_gambler_items)
+
+vault_carnival_items = BundleItem(Currency.star_token, 2500, source=BundleItem.Sources.festival)
+vault_carnival_bundle = CurrencyBundleTemplate(CCRoom.vault, BundleName.carnival, vault_carnival_items)
+
+vault_walnut_hunter_items = BundleItem(Currency.golden_walnut, 25)
+vault_walnut_hunter_bundle = CurrencyBundleTemplate(CCRoom.vault, BundleName.walnut_hunter, vault_walnut_hunter_items)
+
+vault_qi_helper_items = BundleItem(Currency.qi_gem, 25, source=BundleItem.Sources.island)
+vault_qi_helper_bundle = CurrencyBundleTemplate(CCRoom.vault, BundleName.qi_helper, vault_qi_helper_items)
+
+vault_bundles_vanilla = [vault_2500_bundle, vault_5000_bundle, vault_10000_bundle, vault_25000_bundle]
+vault_bundles_thematic = vault_bundles_vanilla
+vault_bundles_remixed = [*vault_bundles_vanilla, vault_gambler_bundle, vault_qi_helper_bundle, vault_carnival_bundle] # , vault_walnut_hunter_bundle
+vault_vanilla = BundleRoomTemplate(CCRoom.vault, vault_bundles_vanilla, 4)
+vault_thematic = BundleRoomTemplate(CCRoom.vault, vault_bundles_thematic, 4)
+vault_remixed = BundleRoomTemplate(CCRoom.vault, vault_bundles_remixed, 4)
+
+all_bundle_items_except_money = []
+all_remixed_bundles = [*crafts_room_bundles_remixed, *pantry_bundles_remixed, *fish_tank_bundles_remixed,
+ *boiler_room_bundles_remixed, *bulletin_board_bundles_remixed, missing_bundle_thematic]
+for bundle in all_remixed_bundles:
+ all_bundle_items_except_money.extend(bundle.items)
+
+all_bundle_items_by_name = {item.item_name: item for item in all_bundle_items_except_money}
diff --git a/worlds/stardew_valley/data/common_data.py b/worlds/stardew_valley/data/common_data.py
deleted file mode 100644
index 8a2d0f5eecfc..000000000000
--- a/worlds/stardew_valley/data/common_data.py
+++ /dev/null
@@ -1,9 +0,0 @@
-fishing_chest = "Fishing Chest"
-secret_note = "Secret Note"
-
-quality_dict = {
- 0: "",
- 1: "Silver",
- 2: "Gold",
- 3: "Iridium"
-}
diff --git a/worlds/stardew_valley/data/craftable_data.py b/worlds/stardew_valley/data/craftable_data.py
new file mode 100644
index 000000000000..bfb2d25ec6b8
--- /dev/null
+++ b/worlds/stardew_valley/data/craftable_data.py
@@ -0,0 +1,313 @@
+from typing import Dict, List, Optional
+
+from ..mods.mod_data import ModNames
+from .recipe_source import RecipeSource, StarterSource, QueenOfSauceSource, ShopSource, SkillSource, FriendshipSource, ShopTradeSource, CutsceneSource, \
+ ArchipelagoSource, LogicSource, SpecialOrderSource, FestivalShopSource, QuestSource
+from ..strings.artisan_good_names import ArtisanGood
+from ..strings.craftable_names import Bomb, Fence, Sprinkler, WildSeeds, Floor, Fishing, Ring, Consumable, Edible, Lighting, Storage, Furniture, Sign, Craftable, \
+ ModEdible, ModCraftable, ModMachine, ModFloor, ModConsumable
+from ..strings.crop_names import Fruit, Vegetable
+from ..strings.currency_names import Currency
+from ..strings.fertilizer_names import Fertilizer, RetainingSoil, SpeedGro
+from ..strings.fish_names import Fish, WaterItem
+from ..strings.flower_names import Flower
+from ..strings.food_names import Meal
+from ..strings.forageable_names import Forageable, SVEForage, DistantLandsForageable
+from ..strings.ingredient_names import Ingredient
+from ..strings.machine_names import Machine
+from ..strings.material_names import Material
+from ..strings.metal_names import Ore, MetalBar, Fossil, Artifact, Mineral, ModFossil
+from ..strings.monster_drop_names import Loot
+from ..strings.quest_names import Quest
+from ..strings.region_names import Region, SVERegion
+from ..strings.seed_names import Seed, TreeSeed
+from ..strings.skill_names import Skill, ModSkill
+from ..strings.special_order_names import SpecialOrder
+from ..strings.villager_names import NPC, ModNPC
+
+
+class CraftingRecipe:
+ item: str
+ ingredients: Dict[str, int]
+ source: RecipeSource
+ mod_name: Optional[str]
+
+ def __init__(self, item: str, ingredients: Dict[str, int], source: RecipeSource, mod_name: Optional[str] = None):
+ self.item = item
+ self.ingredients = ingredients
+ self.source = source
+ self.mod_name = mod_name
+
+ def __repr__(self):
+ return f"{self.item} (Source: {self.source} |" \
+ f" Ingredients: {self.ingredients})"
+
+
+all_crafting_recipes: List[CraftingRecipe] = []
+
+
+def friendship_recipe(name: str, friend: str, hearts: int, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CraftingRecipe:
+ source = FriendshipSource(friend, hearts)
+ return create_recipe(name, ingredients, source, mod_name)
+
+
+def cutscene_recipe(name: str, region: str, friend: str, hearts: int, ingredients: Dict[str, int]) -> CraftingRecipe:
+ source = CutsceneSource(region, friend, hearts)
+ return create_recipe(name, ingredients, source)
+
+
+def skill_recipe(name: str, skill: str, level: int, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CraftingRecipe:
+ source = SkillSource(skill, level)
+ return create_recipe(name, ingredients, source, mod_name)
+
+
+def shop_recipe(name: str, region: str, price: int, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CraftingRecipe:
+ source = ShopSource(region, price)
+ return create_recipe(name, ingredients, source, mod_name)
+
+
+def festival_shop_recipe(name: str, region: str, price: int, ingredients: Dict[str, int]) -> CraftingRecipe:
+ source = FestivalShopSource(region, price)
+ return create_recipe(name, ingredients, source)
+
+
+def shop_trade_recipe(name: str, region: str, currency: str, price: int, ingredients: Dict[str, int]) -> CraftingRecipe:
+ source = ShopTradeSource(region, currency, price)
+ return create_recipe(name, ingredients, source)
+
+
+def queen_of_sauce_recipe(name: str, year: int, season: str, day: int, ingredients: Dict[str, int]) -> CraftingRecipe:
+ source = QueenOfSauceSource(year, season, day)
+ return create_recipe(name, ingredients, source)
+
+
+def quest_recipe(name: str, quest: str, ingredients: Dict[str, int]) -> CraftingRecipe:
+ source = QuestSource(quest)
+ return create_recipe(name, ingredients, source)
+
+
+def special_order_recipe(name: str, special_order: str, ingredients: Dict[str, int]) -> CraftingRecipe:
+ source = SpecialOrderSource(special_order)
+ return create_recipe(name, ingredients, source)
+
+
+def starter_recipe(name: str, ingredients: Dict[str, int]) -> CraftingRecipe:
+ source = StarterSource()
+ return create_recipe(name, ingredients, source)
+
+
+def ap_recipe(name: str, ingredients: Dict[str, int], ap_item: str = None) -> CraftingRecipe:
+ if ap_item is None:
+ ap_item = f"{name} Recipe"
+ source = ArchipelagoSource(ap_item)
+ return create_recipe(name, ingredients, source)
+
+
+def cellar_recipe(name: str, ingredients: Dict[str, int]) -> CraftingRecipe:
+ source = LogicSource("Cellar")
+ return create_recipe(name, ingredients, source)
+
+
+def create_recipe(name: str, ingredients: Dict[str, int], source: RecipeSource, mod_name: Optional[str] = None) -> CraftingRecipe:
+ recipe = CraftingRecipe(name, ingredients, source, mod_name)
+ all_crafting_recipes.append(recipe)
+ return recipe
+
+
+cherry_bomb = skill_recipe(Bomb.cherry_bomb, Skill.mining, 1, {Ore.copper: 4, Material.coal: 1})
+bomb = skill_recipe(Bomb.bomb, Skill.mining, 6, {Ore.iron: 4, Material.coal: 1})
+mega_bomb = skill_recipe(Bomb.mega_bomb, Skill.mining, 8, {Ore.gold: 4, Loot.solar_essence: 1, Loot.void_essence: 1})
+
+gate = starter_recipe(Fence.gate, {Material.wood: 10})
+wood_fence = starter_recipe(Fence.wood, {Material.wood: 2})
+stone_fence = skill_recipe(Fence.stone, Skill.farming, 2, {Material.stone: 2})
+iron_fence = skill_recipe(Fence.iron, Skill.farming, 4, {MetalBar.iron: 2})
+hardwood_fence = skill_recipe(Fence.hardwood, Skill.farming, 6, {Material.hardwood: 2})
+
+sprinkler = skill_recipe(Sprinkler.basic, Skill.farming, 2, {MetalBar.copper: 1, MetalBar.iron: 1})
+quality_sprinkler = skill_recipe(Sprinkler.quality, Skill.farming, 6, {MetalBar.iron: 1, MetalBar.gold: 1, MetalBar.quartz: 1})
+iridium_sprinkler = skill_recipe(Sprinkler.iridium, Skill.farming, 9, {MetalBar.gold: 1, MetalBar.iridium: 1, ArtisanGood.battery_pack: 1})
+
+bee_house = skill_recipe(Machine.bee_house, Skill.farming, 3, {Material.wood: 40, Material.coal: 8, MetalBar.iron: 1, ArtisanGood.maple_syrup: 1})
+cask = cellar_recipe(Machine.cask, {Material.wood: 40, Material.hardwood: 1})
+cheese_press = skill_recipe(Machine.cheese_press, Skill.farming, 6, {Material.wood: 45, Material.stone: 45, Material.hardwood: 10, MetalBar.copper: 1})
+keg = skill_recipe(Machine.keg, Skill.farming, 8, {Material.wood: 30, MetalBar.copper: 1, MetalBar.iron: 1, ArtisanGood.oak_resin: 1})
+loom = skill_recipe(Machine.loom, Skill.farming, 7, {Material.wood: 60, Material.fiber: 30, ArtisanGood.pine_tar: 1})
+mayonnaise_machine = skill_recipe(Machine.mayonnaise_machine, Skill.farming, 2, {Material.wood: 15, Material.stone: 15, Mineral.earth_crystal: 10, MetalBar.copper: 1})
+oil_maker = skill_recipe(Machine.oil_maker, Skill.farming, 8, {Loot.slime: 50, Material.hardwood: 20, MetalBar.gold: 1})
+preserves_jar = skill_recipe(Machine.preserves_jar, Skill.farming, 4, {Material.wood: 50, Material.stone: 40, Material.coal: 8})
+
+basic_fertilizer = skill_recipe(Fertilizer.basic, Skill.farming, 1, {Material.sap: 2})
+quality_fertilizer = skill_recipe(Fertilizer.quality, Skill.farming, 9, {Material.sap: 2, Fish.any: 1})
+deluxe_fertilizer = ap_recipe(Fertilizer.deluxe, {MetalBar.iridium: 1, Material.sap: 40})
+basic_speed_gro = skill_recipe(SpeedGro.basic, Skill.farming, 3, {ArtisanGood.pine_tar: 1, Fish.clam: 1})
+deluxe_speed_gro = skill_recipe(SpeedGro.deluxe, Skill.farming, 8, {ArtisanGood.oak_resin: 1, WaterItem.coral: 1})
+hyper_speed_gro = ap_recipe(SpeedGro.hyper, {Ore.radioactive: 1, Fossil.bone_fragment: 3, Loot.solar_essence: 1})
+basic_retaining_soil = skill_recipe(RetainingSoil.basic, Skill.farming, 4, {Material.stone: 2})
+quality_retaining_soil = skill_recipe(RetainingSoil.quality, Skill.farming, 7, {Material.stone: 3, Material.clay: 1})
+deluxe_retaining_soil = shop_trade_recipe(RetainingSoil.deluxe, Region.island_trader, Currency.cinder_shard, 50, {Material.stone: 5, Material.fiber: 3, Material.clay: 1})
+tree_fertilizer = skill_recipe(Fertilizer.tree, Skill.foraging, 7, {Material.fiber: 5, Material.stone: 5})
+
+spring_seeds = skill_recipe(WildSeeds.spring, Skill.foraging, 1, {Forageable.wild_horseradish: 1, Forageable.daffodil: 1, Forageable.leek: 1, Forageable.dandelion: 1})
+summer_seeds = skill_recipe(WildSeeds.summer, Skill.foraging, 4, {Forageable.spice_berry: 1, Fruit.grape: 1, Forageable.sweet_pea: 1})
+fall_seeds = skill_recipe(WildSeeds.fall, Skill.foraging, 6, {Forageable.common_mushroom: 1, Forageable.wild_plum: 1, Forageable.hazelnut: 1, Forageable.blackberry: 1})
+winter_seeds = skill_recipe(WildSeeds.winter, Skill.foraging, 7, {Forageable.winter_root: 1, Forageable.crystal_fruit: 1, Forageable.snow_yam: 1, Forageable.crocus: 1})
+ancient_seeds = ap_recipe(WildSeeds.ancient, {Artifact.ancient_seed: 1})
+grass_starter = shop_recipe(WildSeeds.grass_starter, Region.pierre_store, 1000, {Material.fiber: 10})
+for wild_seeds in [WildSeeds.spring, WildSeeds.summer, WildSeeds.fall, WildSeeds.winter]:
+ tea_sapling = cutscene_recipe(WildSeeds.tea_sapling, Region.sunroom, NPC.caroline, 2, {wild_seeds: 2, Material.fiber: 5, Material.wood: 5})
+fiber_seeds = special_order_recipe(WildSeeds.fiber, SpecialOrder.community_cleanup, {Seed.mixed: 1, Material.sap: 5, Material.clay: 1})
+
+wood_floor = shop_recipe(Floor.wood, Region.carpenter, 100, {Material.wood: 1})
+rustic_floor = shop_recipe(Floor.rustic, Region.carpenter, 200, {Material.wood: 1})
+straw_floor = shop_recipe(Floor.straw, Region.carpenter, 200, {Material.wood: 1, Material.fiber: 1})
+weathered_floor = shop_recipe(Floor.weathered, Region.mines_dwarf_shop, 500, {Material.wood: 1})
+crystal_floor = shop_recipe(Floor.crystal, Region.sewer, 500, {MetalBar.quartz: 1})
+stone_floor = shop_recipe(Floor.stone, Region.carpenter, 100, {Material.stone: 1})
+stone_walkway_floor = shop_recipe(Floor.stone_walkway, Region.carpenter, 200, {Material.stone: 1})
+brick_floor = shop_recipe(Floor.brick, Region.carpenter, 500, {Material.clay: 2, Material.stone: 5})
+wood_path = starter_recipe(Floor.wood_path, {Material.wood: 1})
+gravel_path = starter_recipe(Floor.gravel_path, {Material.stone: 1})
+cobblestone_path = starter_recipe(Floor.cobblestone_path, {Material.stone: 1})
+stepping_stone_path = shop_recipe(Floor.stepping_stone_path, Region.carpenter, 100, {Material.stone: 1})
+crystal_path = shop_recipe(Floor.crystal_path, Region.carpenter, 200, {MetalBar.quartz: 1})
+
+spinner = skill_recipe(Fishing.spinner, Skill.fishing, 6, {MetalBar.iron: 2})
+trap_bobber = skill_recipe(Fishing.trap_bobber, Skill.fishing, 6, {MetalBar.copper: 1, Material.sap: 10})
+cork_bobber = skill_recipe(Fishing.cork_bobber, Skill.fishing, 7, {Material.wood: 10, Material.hardwood: 5, Loot.slime: 10})
+quality_bobber = special_order_recipe(Fishing.quality_bobber, SpecialOrder.juicy_bugs_wanted, {MetalBar.copper: 1, Material.sap: 20, Loot.solar_essence: 5})
+treasure_hunter = skill_recipe(Fishing.treasure_hunter, Skill.fishing, 7, {MetalBar.gold: 2})
+dressed_spinner = skill_recipe(Fishing.dressed_spinner, Skill.fishing, 8, {MetalBar.iron: 2, ArtisanGood.cloth: 1})
+barbed_hook = skill_recipe(Fishing.barbed_hook, Skill.fishing, 8, {MetalBar.copper: 1, MetalBar.iron: 1, MetalBar.gold: 1})
+magnet = skill_recipe(Fishing.magnet, Skill.fishing, 9, {MetalBar.iron: 1})
+bait = skill_recipe(Fishing.bait, Skill.fishing, 2, {Loot.bug_meat: 1})
+wild_bait = cutscene_recipe(Fishing.wild_bait, Region.tent, NPC.linus, 4, {Material.fiber: 10, Loot.bug_meat: 5, Loot.slime: 5})
+magic_bait = ap_recipe(Fishing.magic_bait, {Ore.radioactive: 1, Loot.bug_meat: 3})
+crab_pot = skill_recipe(Machine.crab_pot, Skill.fishing, 3, {Material.wood: 40, MetalBar.iron: 3})
+
+sturdy_ring = skill_recipe(Ring.sturdy_ring, Skill.combat, 1, {MetalBar.copper: 2, Loot.bug_meat: 25, Loot.slime: 25})
+warrior_ring = skill_recipe(Ring.warrior_ring, Skill.combat, 4, {MetalBar.iron: 10, Material.coal: 25, Mineral.frozen_tear: 10})
+ring_of_yoba = skill_recipe(Ring.ring_of_yoba, Skill.combat, 7, {MetalBar.gold: 5, MetalBar.iron: 5, Mineral.diamond: 1})
+thorns_ring = skill_recipe(Ring.thorns_ring, Skill.combat, 7, {Fossil.bone_fragment: 50, Material.stone: 50, MetalBar.gold: 1})
+glowstone_ring = skill_recipe(Ring.glowstone_ring, Skill.mining, 4, {Loot.solar_essence: 5, MetalBar.iron: 5})
+iridium_band = skill_recipe(Ring.iridium_band, Skill.combat, 9, {MetalBar.iridium: 5, Loot.solar_essence: 50, Loot.void_essence: 50})
+wedding_ring = shop_recipe(Ring.wedding_ring, Region.traveling_cart, 500, {MetalBar.iridium: 5, Mineral.prismatic_shard: 1})
+
+field_snack = skill_recipe(Edible.field_snack, Skill.foraging, 1, {TreeSeed.acorn: 1, TreeSeed.maple: 1, TreeSeed.pine: 1})
+bug_steak = skill_recipe(Edible.bug_steak, Skill.combat, 1, {Loot.bug_meat: 10})
+life_elixir = skill_recipe(Edible.life_elixir, Skill.combat, 2, {Forageable.red_mushroom: 1, Forageable.purple_mushroom: 1, Forageable.morel: 1, Forageable.chanterelle: 1})
+oil_of_garlic = skill_recipe(Edible.oil_of_garlic, Skill.combat, 6, {Vegetable.garlic: 10, Ingredient.oil: 1})
+
+monster_musk = special_order_recipe(Consumable.monster_musk, SpecialOrder.prismatic_jelly, {Loot.bat_wing: 30, Loot.slime: 30})
+fairy_dust = quest_recipe(Consumable.fairy_dust, Quest.the_pirates_wife, {Mineral.diamond: 1, Flower.fairy_rose: 1})
+warp_totem_beach = skill_recipe(Consumable.warp_totem_beach, Skill.foraging, 6, {Material.hardwood: 1, WaterItem.coral: 2, Material.fiber: 10})
+warp_totem_mountains = skill_recipe(Consumable.warp_totem_mountains, Skill.foraging, 7, {Material.hardwood: 1, MetalBar.iron: 1, Material.stone: 25})
+warp_totem_farm = skill_recipe(Consumable.warp_totem_farm, Skill.foraging, 8, {Material.hardwood: 1, ArtisanGood.honey: 1, Material.fiber: 20})
+warp_totem_desert = shop_trade_recipe(Consumable.warp_totem_desert, Region.desert, MetalBar.iridium, 10, {Material.hardwood: 2, Forageable.coconut: 1, Ore.iridium: 4})
+warp_totem_island = shop_recipe(Consumable.warp_totem_island, Region.volcano_dwarf_shop, 10000, {Material.hardwood: 5, Forageable.dragon_tooth: 1, Forageable.ginger: 1})
+rain_totem = skill_recipe(Consumable.rain_totem, Skill.foraging, 9, {Material.hardwood: 1, ArtisanGood.truffle_oil: 1, ArtisanGood.pine_tar: 5})
+
+torch = starter_recipe(Lighting.torch, {Material.wood: 1, Material.sap: 2})
+campfire = starter_recipe(Lighting.campfire, {Material.stone: 10, Material.wood: 10, Material.fiber: 10})
+wooden_brazier = shop_recipe(Lighting.wooden_brazier, Region.carpenter, 250, {Material.wood: 10, Material.coal: 1, Material.fiber: 5})
+stone_brazier = shop_recipe(Lighting.stone_brazier, Region.carpenter, 400, {Material.stone: 10, Material.coal: 1, Material.fiber: 5})
+gold_brazier = shop_recipe(Lighting.gold_brazier, Region.carpenter, 1000, {MetalBar.gold: 1, Material.coal: 1, Material.fiber: 5})
+carved_brazier = shop_recipe(Lighting.carved_brazier, Region.carpenter, 2000, {Material.hardwood: 10, Material.coal: 1})
+stump_brazier = shop_recipe(Lighting.stump_brazier, Region.carpenter, 800, {Material.hardwood: 5, Material.coal: 1})
+barrel_brazier = shop_recipe(Lighting.barrel_brazier, Region.carpenter, 800, {Material.wood: 50, Loot.solar_essence: 1, Material.coal: 1})
+skull_brazier = shop_recipe(Lighting.skull_brazier, Region.carpenter, 3000, {Fossil.bone_fragment: 10})
+marble_brazier = shop_recipe(Lighting.marble_brazier, Region.carpenter, 5000, {Mineral.marble: 1, Mineral.aquamarine: 1, Material.stone: 100})
+wood_lamp_post = shop_recipe(Lighting.wood_lamp_post, Region.carpenter, 500, {Material.wood: 50, ArtisanGood.battery_pack: 1})
+iron_lamp_post = shop_recipe(Lighting.iron_lamp_post, Region.carpenter, 1000, {MetalBar.iron: 1, ArtisanGood.battery_pack: 1})
+jack_o_lantern = festival_shop_recipe(Lighting.jack_o_lantern, Region.spirit_eve, 2000, {Vegetable.pumpkin: 1, Lighting.torch: 1})
+
+bone_mill = special_order_recipe(Machine.bone_mill, SpecialOrder.fragments_of_the_past, {Fossil.bone_fragment: 10, Material.clay: 3, Material.stone: 20})
+charcoal_kiln = skill_recipe(Machine.charcoal_kiln, Skill.foraging, 4, {Material.wood: 20, MetalBar.copper: 2})
+crystalarium = skill_recipe(Machine.crystalarium, Skill.mining, 9, {Material.stone: 99, MetalBar.gold: 5, MetalBar.iridium: 2, ArtisanGood.battery_pack: 1})
+furnace = skill_recipe(Machine.furnace, Skill.mining, 1, {Ore.copper: 20, Material.stone: 25})
+geode_crusher = special_order_recipe(Machine.geode_crusher, SpecialOrder.cave_patrol, {MetalBar.gold: 2, Material.stone: 50, Mineral.diamond: 1})
+heavy_tapper = ap_recipe(Machine.heavy_tapper, {Material.hardwood: 30, MetalBar.radioactive: 1})
+lightning_rod = skill_recipe(Machine.lightning_rod, Skill.foraging, 6, {MetalBar.iron: 1, MetalBar.quartz: 1, Loot.bat_wing: 5})
+ostrich_incubator = ap_recipe(Machine.ostrich_incubator, {Fossil.bone_fragment: 50, Material.hardwood: 50, Currency.cinder_shard: 20})
+recycling_machine = skill_recipe(Machine.recycling_machine, Skill.fishing, 4, {Material.wood: 25, Material.stone: 25, MetalBar.iron: 1})
+seed_maker = skill_recipe(Machine.seed_maker, Skill.farming, 9, {Material.wood: 25, Material.coal: 10, MetalBar.gold: 1})
+slime_egg_press = skill_recipe(Machine.slime_egg_press, Skill.combat, 6, {Material.coal: 25, Mineral.fire_quartz: 1, ArtisanGood.battery_pack: 1})
+slime_incubator = skill_recipe(Machine.slime_incubator, Skill.combat, 8, {MetalBar.iridium: 2, Loot.slime: 100})
+solar_panel = special_order_recipe(Machine.solar_panel, SpecialOrder.island_ingredients, {MetalBar.quartz: 10, MetalBar.iron: 5, MetalBar.gold: 5})
+tapper = skill_recipe(Machine.tapper, Skill.foraging, 3, {Material.wood: 40, MetalBar.copper: 2})
+worm_bin = skill_recipe(Machine.worm_bin, Skill.fishing, 8, {Material.hardwood: 25, MetalBar.gold: 1, MetalBar.iron: 1, Material.fiber: 50})
+
+tub_o_flowers = festival_shop_recipe(Furniture.tub_o_flowers, Region.flower_dance, 2000, {Material.wood: 15, Seed.tulip: 1, Seed.jazz: 1, Seed.poppy: 1, Seed.spangle: 1})
+wicked_statue = shop_recipe(Furniture.wicked_statue, Region.sewer, 1000, {Material.stone: 25, Material.coal: 5})
+flute_block = cutscene_recipe(Furniture.flute_block, Region.carpenter, NPC.robin, 6, {Material.wood: 10, Ore.copper: 2, Material.fiber: 20})
+drum_block = cutscene_recipe(Furniture.drum_block, Region.carpenter, NPC.robin, 6, {Material.stone: 10, Ore.copper: 2, Material.fiber: 20})
+
+chest = starter_recipe(Storage.chest, {Material.wood: 50})
+stone_chest = special_order_recipe(Storage.stone_chest, SpecialOrder.robins_resource_rush, {Material.stone: 50})
+
+wood_sign = starter_recipe(Sign.wood, {Material.wood: 25})
+stone_sign = starter_recipe(Sign.stone, {Material.stone: 25})
+dark_sign = friendship_recipe(Sign.dark, NPC.krobus, 3, {Loot.bat_wing: 5, Fossil.bone_fragment: 5})
+
+garden_pot = ap_recipe(Craftable.garden_pot, {Material.clay: 1, Material.stone: 10, MetalBar.quartz: 1}, "Greenhouse")
+scarecrow = skill_recipe(Craftable.scarecrow, Skill.farming, 1, {Material.wood: 50, Material.coal: 1, Material.fiber: 20})
+deluxe_scarecrow = ap_recipe(Craftable.deluxe_scarecrow, {Material.wood: 50, Material.fiber: 40, Ore.iridium: 1})
+staircase = skill_recipe(Craftable.staircase, Skill.mining, 2, {Material.stone: 99})
+explosive_ammo = skill_recipe(Craftable.explosive_ammo, Skill.combat, 8, {MetalBar.iron: 1, Material.coal: 2})
+transmute_fe = skill_recipe(Craftable.transmute_fe, Skill.mining, 4, {MetalBar.copper: 3})
+transmute_au = skill_recipe(Craftable.transmute_au, Skill.mining, 7, {MetalBar.iron: 2})
+mini_jukebox = cutscene_recipe(Craftable.mini_jukebox, Region.saloon, NPC.gus, 5, {MetalBar.iron: 2, ArtisanGood.battery_pack: 1})
+mini_obelisk = special_order_recipe(Craftable.mini_obelisk, SpecialOrder.a_curious_substance, {Material.hardwood: 30, Loot.solar_essence: 20, MetalBar.gold: 3})
+farm_computer = special_order_recipe(Craftable.farm_computer, SpecialOrder.aquatic_overpopulation, {Artifact.dwarf_gadget: 1, ArtisanGood.battery_pack: 1, MetalBar.quartz: 10})
+hopper = ap_recipe(Craftable.hopper, {Material.hardwood: 10, MetalBar.iridium: 1, MetalBar.radioactive: 1})
+cookout_kit = skill_recipe(Craftable.cookout_kit, Skill.foraging, 9, {Material.wood: 15, Material.fiber: 10, Material.coal: 3})
+
+travel_charm = shop_recipe(ModCraftable.travel_core, Region.adventurer_guild, 250, {Loot.solar_essence: 1, Loot.void_essence: 1}, ModNames.magic)
+preservation_chamber = skill_recipe(ModMachine.preservation_chamber, ModSkill.archaeology, 2, {MetalBar.copper: 1, Material.wood: 15, ArtisanGood.oak_resin: 30},
+ ModNames.archaeology)
+preservation_chamber_h = skill_recipe(ModMachine.hardwood_preservation_chamber, ModSkill.archaeology, 7, {MetalBar.copper: 1, Material.hardwood: 15,
+ ArtisanGood.oak_resin: 30}, ModNames.archaeology)
+grinder = skill_recipe(ModMachine.grinder, ModSkill.archaeology, 8, {Artifact.rusty_cog: 10, MetalBar.iron: 5, ArtisanGood.battery_pack: 1}, ModNames.archaeology)
+ancient_battery = skill_recipe(ModMachine.ancient_battery, ModSkill.archaeology, 6, {Material.stone: 40, MetalBar.copper: 10, MetalBar.iron: 5},
+ ModNames.archaeology)
+glass_bazier = skill_recipe(ModCraftable.glass_bazier, ModSkill.archaeology, 1, {Artifact.glass_shards: 10}, ModNames.archaeology)
+glass_path = skill_recipe(ModFloor.glass_path, ModSkill.archaeology, 1, {Artifact.glass_shards: 1}, ModNames.archaeology)
+glass_fence = skill_recipe(ModCraftable.glass_fence, ModSkill.archaeology, 1, {Artifact.glass_shards: 5}, ModNames.archaeology)
+bone_path = skill_recipe(ModFloor.bone_path, ModSkill.archaeology, 3, {Fossil.bone_fragment: 1}, ModNames.archaeology)
+water_shifter = skill_recipe(ModCraftable.water_shifter, ModSkill.archaeology, 4, {Material.wood: 40, MetalBar.copper: 4}, ModNames.archaeology)
+wooden_display = skill_recipe(ModCraftable.wooden_display, ModSkill.archaeology, 2, {Material.wood: 25}, ModNames.archaeology)
+hardwood_display = skill_recipe(ModCraftable.hardwood_display, ModSkill.archaeology, 7, {Material.hardwood: 10}, ModNames.archaeology)
+volcano_totem = skill_recipe(ModConsumable.volcano_totem, ModSkill.archaeology, 9, {Material.cinder_shard: 5, Artifact.rare_disc: 1, Artifact.dwarf_gadget: 1},
+ ModNames.archaeology)
+haste_elixir = shop_recipe(ModEdible.haste_elixir, SVERegion.alesia_shop, 35000, {Loot.void_essence: 35, SVEForage.void_soul: 5, Ingredient.sugar: 1,
+ Meal.spicy_eel: 1}, ModNames.sve)
+hero_elixir = shop_recipe(ModEdible.hero_elixir, SVERegion.isaac_shop, 65000, {SVEForage.void_pebble: 3, SVEForage.void_soul: 5, Ingredient.oil: 1,
+ Loot.slime: 10}, ModNames.sve)
+armor_elixir = shop_recipe(ModEdible.armor_elixir, SVERegion.alesia_shop, 50000, {Loot.solar_essence: 30, SVEForage.void_soul: 5, Ingredient.vinegar: 5,
+ Fossil.bone_fragment: 5}, ModNames.sve)
+ginger_tincture = friendship_recipe(ModConsumable.ginger_tincture, ModNPC.goblin, 4, {DistantLandsForageable.brown_amanita: 1, Forageable.ginger: 5,
+ Material.cinder_shard: 1, DistantLandsForageable.swamp_herb: 1}, ModNames.distant_lands)
+
+neanderthal_skeleton = shop_recipe(ModCraftable.neanderthal_skeleton, Region.mines_dwarf_shop, 5000,
+ {ModFossil.neanderthal_skull: 1, ModFossil.neanderthal_ribs: 1, ModFossil.neanderthal_pelvis: 1, ModFossil.neanderthal_limb_bones: 1,
+ MetalBar.iron: 5, Material.hardwood: 10}, ModNames.boarding_house)
+pterodactyl_skeleton_l = shop_recipe(ModCraftable.pterodactyl_skeleton_l, Region.mines_dwarf_shop, 5000,
+ {ModFossil.pterodactyl_phalange: 1, ModFossil.pterodactyl_skull: 1, ModFossil.pterodactyl_l_wing_bone: 1,
+ MetalBar.iron: 10, Material.hardwood: 15}, ModNames.boarding_house)
+pterodactyl_skeleton_m = shop_recipe(ModCraftable.pterodactyl_skeleton_m, Region.mines_dwarf_shop, 5000,
+ {ModFossil.pterodactyl_phalange: 1, ModFossil.pterodactyl_vertebra: 1, ModFossil.pterodactyl_ribs: 1,
+ MetalBar.iron: 10, Material.hardwood: 15}, ModNames.boarding_house)
+pterodactyl_skeleton_r = shop_recipe(ModCraftable.pterodactyl_skeleton_r, Region.mines_dwarf_shop, 5000,
+ {ModFossil.pterodactyl_phalange: 1, ModFossil.pterodactyl_claw: 1, ModFossil.pterodactyl_r_wing_bone: 1,
+ MetalBar.iron: 10, Material.hardwood: 15}, ModNames.boarding_house)
+trex_skeleton_l = shop_recipe(ModCraftable.trex_skeleton_l, Region.mines_dwarf_shop, 5000,
+ {ModFossil.dinosaur_vertebra: 1, ModFossil.dinosaur_tooth: 1, ModFossil.dinosaur_skull: 1,
+ MetalBar.iron: 10, Material.hardwood: 15}, ModNames.boarding_house)
+trex_skeleton_m = shop_recipe(ModCraftable.trex_skeleton_m, Region.mines_dwarf_shop, 5000,
+ {ModFossil.dinosaur_vertebra: 1, ModFossil.dinosaur_ribs: 1, ModFossil.dinosaur_claw: 1,
+ MetalBar.iron: 10, Material.hardwood: 15}, ModNames.boarding_house)
+trex_skeleton_r = shop_recipe(ModCraftable.trex_skeleton_r, Region.mines_dwarf_shop, 5000,
+ {ModFossil.dinosaur_vertebra: 1, ModFossil.dinosaur_femur: 1, ModFossil.dinosaur_pelvis: 1,
+ MetalBar.iron: 10, Material.hardwood: 15}, ModNames.boarding_house)
+
+all_crafting_recipes_by_name = {recipe.item: recipe for recipe in all_crafting_recipes}
diff --git a/worlds/stardew_valley/data/crops.csv b/worlds/stardew_valley/data/crops.csv
index e3d2dc8256db..0bf43a76764e 100644
--- a/worlds/stardew_valley/data/crops.csv
+++ b/worlds/stardew_valley/data/crops.csv
@@ -1,40 +1,41 @@
-crop,farm_growth_seasons,seed,seed_seasons,seed_regions
-Amaranth,Fall,Amaranth Seeds,Fall,"Pierre's General Store,JojaMart"
-Artichoke,Fall,Artichoke Seeds,Fall,"Pierre's General Store,JojaMart"
-Beet,Fall,Beet Seeds,Fall,Oasis
-Blue Jazz,Spring,Jazz Seeds,Spring,"Pierre's General Store,JojaMart"
-Blueberry,Summer,Blueberry Seeds,Summer,"Pierre's General Store,JojaMart"
-Bok Choy,Fall,Bok Choy Seeds,Fall,"Pierre's General Store,JojaMart"
-Cactus Fruit,,Cactus Seeds,,Oasis
-Cauliflower,Spring,Cauliflower Seeds,Spring,"Pierre's General Store,JojaMart"
-Coffee Bean,"Spring,Summer",Coffee Bean,"Summer,Fall","Traveling Cart"
-Corn,"Summer,Fall",Corn Seeds,"Summer,Fall","Pierre's General Store,JojaMart"
-Cranberries,Fall,Cranberry Seeds,Fall,"Pierre's General Store,JojaMart"
-Eggplant,Fall,Eggplant Seeds,Fall,"Pierre's General Store,JojaMart"
-Fairy Rose,Fall,Fairy Seeds,Fall,"Pierre's General Store,JojaMart"
-Garlic,Spring,Garlic Seeds,Spring,"Pierre's General Store,JojaMart"
-Grape,Fall,Grape Starter,Fall,"Pierre's General Store,JojaMart"
-Green Bean,Spring,Bean Starter,Spring,"Pierre's General Store,JojaMart"
-Hops,Summer,Hops Starter,Summer,"Pierre's General Store,JojaMart"
-Hot Pepper,Summer,Pepper Seeds,Summer,"Pierre's General Store,JojaMart"
-Kale,Spring,Kale Seeds,Spring,"Pierre's General Store,JojaMart"
-Melon,Summer,Melon Seeds,Summer,"Pierre's General Store,JojaMart"
-Parsnip,Spring,Parsnip Seeds,Spring,"Pierre's General Store,JojaMart"
-Pineapple,Summer,Pineapple Seeds,Summer,"Island Trader"
-Poppy,Summer,Poppy Seeds,Summer,"Pierre's General Store,JojaMart"
-Potato,Spring,Potato Seeds,Spring,"Pierre's General Store,JojaMart"
-Pumpkin,Fall,Pumpkin Seeds,Fall,"Pierre's General Store,JojaMart"
-Radish,Summer,Radish Seeds,Summer,"Pierre's General Store,JojaMart"
-Red Cabbage,Summer,Red Cabbage Seeds,Summer,"Pierre's General Store,JojaMart"
-Rhubarb,Spring,Rhubarb Seeds,Spring,Oasis
-Starfruit,Summer,Starfruit Seeds,Summer,Oasis
-Strawberry,Spring,Strawberry Seeds,Spring,"Pierre's General Store,JojaMart"
-Summer Spangle,Summer,Spangle Seeds,Summer,"Pierre's General Store,JojaMart"
-Sunflower,"Summer,Fall",Sunflower Seeds,"Summer,Fall","Pierre's General Store,JojaMart"
-Sweet Gem Berry,Fall,Rare Seed,"Spring,Summer",Traveling Cart
-Taro Root,Summer,Taro Tuber,Summer,"Island Trader"
-Tomato,Summer,Tomato Seeds,Summer,"Pierre's General Store,JojaMart"
-Tulip,Spring,Tulip Bulb,Spring,"Pierre's General Store,JojaMart"
-Unmilled Rice,Spring,Rice Shoot,Spring,"Pierre's General Store,JojaMart"
-Wheat,"Summer,Fall",Wheat Seeds,"Summer,Fall","Pierre's General Store,JojaMart"
-Yam,Fall,Yam Seeds,Fall,"Pierre's General Store,JojaMart"
+crop,farm_growth_seasons,seed,seed_seasons,seed_regions,requires_island
+Amaranth,Fall,Amaranth Seeds,Fall,"Pierre's General Store",False
+Artichoke,Fall,Artichoke Seeds,Fall,"Pierre's General Store",False
+Beet,Fall,Beet Seeds,Fall,Oasis,False
+Blue Jazz,Spring,Jazz Seeds,Spring,"Pierre's General Store",False
+Blueberry,Summer,Blueberry Seeds,Summer,"Pierre's General Store",False
+Bok Choy,Fall,Bok Choy Seeds,Fall,"Pierre's General Store",False
+Cactus Fruit,,Cactus Seeds,,Oasis,False
+Cauliflower,Spring,Cauliflower Seeds,Spring,"Pierre's General Store",False
+Coffee Bean,"Spring,Summer",Coffee Bean,"Summer,Fall","Traveling Cart",False
+Corn,"Summer,Fall",Corn Seeds,"Summer,Fall","Pierre's General Store",False
+Cranberries,Fall,Cranberry Seeds,Fall,"Pierre's General Store",False
+Eggplant,Fall,Eggplant Seeds,Fall,"Pierre's General Store",False
+Fairy Rose,Fall,Fairy Seeds,Fall,"Pierre's General Store",False
+Garlic,Spring,Garlic Seeds,Spring,"Pierre's General Store",False
+Grape,Fall,Grape Starter,Fall,"Pierre's General Store",False
+Green Bean,Spring,Bean Starter,Spring,"Pierre's General Store",False
+Hops,Summer,Hops Starter,Summer,"Pierre's General Store",False
+Hot Pepper,Summer,Pepper Seeds,Summer,"Pierre's General Store",False
+Kale,Spring,Kale Seeds,Spring,"Pierre's General Store",False
+Melon,Summer,Melon Seeds,Summer,"Pierre's General Store",False
+Parsnip,Spring,Parsnip Seeds,Spring,"Pierre's General Store",False
+Pineapple,Summer,Pineapple Seeds,Summer,"Island Trader",True
+Poppy,Summer,Poppy Seeds,Summer,"Pierre's General Store",False
+Potato,Spring,Potato Seeds,Spring,"Pierre's General Store",False
+Qi Fruit,"Spring,Summer,Fall,Winter",Qi Bean,"Spring,Summer,Fall,Winter","Qi's Walnut Room",True
+Pumpkin,Fall,Pumpkin Seeds,Fall,"Pierre's General Store",False
+Radish,Summer,Radish Seeds,Summer,"Pierre's General Store",False
+Red Cabbage,Summer,Red Cabbage Seeds,Summer,"Pierre's General Store",False
+Rhubarb,Spring,Rhubarb Seeds,Spring,Oasis,False
+Starfruit,Summer,Starfruit Seeds,Summer,Oasis,False
+Strawberry,Spring,Strawberry Seeds,Spring,"Pierre's General Store",False
+Summer Spangle,Summer,Spangle Seeds,Summer,"Pierre's General Store",False
+Sunflower,"Summer,Fall",Sunflower Seeds,"Summer,Fall","Pierre's General Store",False
+Sweet Gem Berry,Fall,Rare Seed,"Spring,Summer",Traveling Cart,False
+Taro Root,Summer,Taro Tuber,Summer,"Island Trader",True
+Tomato,Summer,Tomato Seeds,Summer,"Pierre's General Store",False
+Tulip,Spring,Tulip Bulb,Spring,"Pierre's General Store",False
+Unmilled Rice,Spring,Rice Shoot,Spring,"Pierre's General Store",False
+Wheat,"Summer,Fall",Wheat Seeds,"Summer,Fall","Pierre's General Store",False
+Yam,Fall,Yam Seeds,Fall,"Pierre's General Store",False
diff --git a/worlds/stardew_valley/data/crops_data.py b/worlds/stardew_valley/data/crops_data.py
index e798235060f2..7144ccfbcf9b 100644
--- a/worlds/stardew_valley/data/crops_data.py
+++ b/worlds/stardew_valley/data/crops_data.py
@@ -1,5 +1,5 @@
from dataclasses import dataclass
-from typing import List
+from typing import Tuple
from .. import data
@@ -7,14 +7,15 @@
@dataclass(frozen=True)
class SeedItem:
name: str
- seasons: List[str]
- regions: List[str]
+ seasons: Tuple[str]
+ regions: Tuple[str]
+ requires_island: bool
@dataclass(frozen=True)
class CropItem:
name: str
- farm_growth_seasons: List[str]
+ farm_growth_seasons: Tuple[str]
seed: SeedItem
@@ -32,13 +33,14 @@ def load_crop_csv():
for item in reader:
seeds.append(SeedItem(item["seed"],
- [season for season in item["seed_seasons"].split(",")]
- if item["seed_seasons"] else [],
- [region for region in item["seed_regions"].split(",")]
- if item["seed_regions"] else []))
+ tuple(season for season in item["seed_seasons"].split(","))
+ if item["seed_seasons"] else tuple(),
+ tuple(region for region in item["seed_regions"].split(","))
+ if item["seed_regions"] else tuple(),
+ item["requires_island"] == "True"))
crops.append(CropItem(item["crop"],
- [season for season in item["farm_growth_seasons"].split(",")]
- if item["farm_growth_seasons"] else [],
+ tuple(season for season in item["farm_growth_seasons"].split(","))
+ if item["farm_growth_seasons"] else tuple(),
seeds[-1]))
return crops, seeds
diff --git a/worlds/stardew_valley/data/fish_data.py b/worlds/stardew_valley/data/fish_data.py
index 91a4431c6552..aeb416733950 100644
--- a/worlds/stardew_valley/data/fish_data.py
+++ b/worlds/stardew_valley/data/fish_data.py
@@ -1,20 +1,24 @@
from dataclasses import dataclass
-from typing import List, Tuple, Union, Optional
+from typing import List, Tuple, Union, Optional, Set
from . import season_data as season
-from .game_item import GameItem
-from ..strings.region_names import Region
+from ..strings.fish_names import Fish, SVEFish, DistantLandsFish
+from ..strings.region_names import Region, SVERegion
+from ..mods.mod_data import ModNames
@dataclass(frozen=True)
-class FishItem(GameItem):
+class FishItem:
+ name: str
locations: Tuple[str]
seasons: Tuple[str]
difficulty: int
- mod_name: Optional[str]
+ legendary: bool
+ extended_family: bool
+ mod_name: Optional[str] = None
def __repr__(self):
- return f"{self.name} [{self.item_id}] (Locations: {self.locations} |" \
+ return f"{self.name} (Locations: {self.locations} |" \
f" Seasons: {self.seasons} |" \
f" Difficulty: {self.difficulty}) |" \
f"Mod: {self.mod_name}"
@@ -39,92 +43,149 @@ def __repr__(self):
ginger_island_river = (Region.island_west,)
pirate_cove = (Region.pirate_cove,)
+crimson_badlands = (SVERegion.crimson_badlands,)
+shearwater = (SVERegion.shearwater,)
+highlands = (SVERegion.highlands_outside,)
+sprite_spring = (SVERegion.sprite_spring,)
+fable_reef = (SVERegion.fable_reef,)
+vineyard = (SVERegion.blue_moon_vineyard,)
+
all_fish: List[FishItem] = []
-def create_fish(name: str, item_id: int, locations: Tuple[str, ...], seasons: Union[str, Tuple[str, ...]],
- difficulty: int, mod_name: Optional[str] = None) -> FishItem:
+def create_fish(name: str, locations: Tuple[str, ...], seasons: Union[str, Tuple[str, ...]],
+ difficulty: int, legendary: bool = False, extended_family: bool = False, mod_name: Optional[str] = None) -> FishItem:
if isinstance(seasons, str):
seasons = (seasons,)
- fish_item = FishItem(name, item_id, locations, seasons, difficulty, mod_name)
+ fish_item = FishItem(name, locations, seasons, difficulty, legendary, extended_family, mod_name)
all_fish.append(fish_item)
return fish_item
-albacore = create_fish("Albacore", 705, ocean, (season.fall, season.winter), 60)
-anchovy = create_fish("Anchovy", 129, ocean, (season.spring, season.fall), 30)
-blue_discus = create_fish("Blue Discus", 838, ginger_island_river, season.all_seasons, 60)
-bream = create_fish("Bream", 132, town_river + forest_river, season.all_seasons, 35)
-bullhead = create_fish("Bullhead", 700, mountain_lake, season.all_seasons, 46)
-carp = create_fish("Carp", 142, mountain_lake + secret_woods + sewers + mutant_bug_lair, season.not_winter, 15)
-catfish = create_fish("Catfish", 143, town_river + forest_river + secret_woods, (season.spring, season.fall), 75)
-chub = create_fish("Chub", 702, forest_river + mountain_lake, season.all_seasons, 35)
-dorado = create_fish("Dorado", 704, forest_river, season.summer, 78)
-eel = create_fish("Eel", 148, ocean, (season.spring, season.fall), 70)
-flounder = create_fish("Flounder", 267, ocean, (season.spring, season.summer), 50)
-ghostfish = create_fish("Ghostfish", 156, mines_floor_20 + mines_floor_60, season.all_seasons, 50)
-halibut = create_fish("Halibut", 708, ocean, season.not_fall, 50)
-herring = create_fish("Herring", 147, ocean, (season.spring, season.winter), 25)
-ice_pip = create_fish("Ice Pip", 161, mines_floor_60, season.all_seasons, 85)
-largemouth_bass = create_fish("Largemouth Bass", 136, mountain_lake, season.all_seasons, 50)
-lava_eel = create_fish("Lava Eel", 162, mines_floor_100, season.all_seasons, 90)
-lingcod = create_fish("Lingcod", 707, town_river + forest_river + mountain_lake, season.winter, 85)
-lionfish = create_fish("Lionfish", 837, ginger_island_ocean, season.all_seasons, 50)
-midnight_carp = create_fish("Midnight Carp", 269, mountain_lake + forest_pond + ginger_island_river,
+albacore = create_fish("Albacore", ocean, (season.fall, season.winter), 60)
+anchovy = create_fish("Anchovy", ocean, (season.spring, season.fall), 30)
+blue_discus = create_fish("Blue Discus", ginger_island_river, season.all_seasons, 60)
+bream = create_fish("Bream", town_river + forest_river, season.all_seasons, 35)
+bullhead = create_fish("Bullhead", mountain_lake, season.all_seasons, 46)
+carp = create_fish(Fish.carp, mountain_lake + secret_woods + sewers + mutant_bug_lair, season.not_winter, 15)
+catfish = create_fish("Catfish", town_river + forest_river + secret_woods, (season.spring, season.fall), 75)
+chub = create_fish("Chub", forest_river + mountain_lake, season.all_seasons, 35)
+dorado = create_fish("Dorado", forest_river, season.summer, 78)
+eel = create_fish("Eel", ocean, (season.spring, season.fall), 70)
+flounder = create_fish("Flounder", ocean, (season.spring, season.summer), 50)
+ghostfish = create_fish("Ghostfish", mines_floor_20 + mines_floor_60, season.all_seasons, 50)
+halibut = create_fish("Halibut", ocean, season.not_fall, 50)
+herring = create_fish("Herring", ocean, (season.spring, season.winter), 25)
+ice_pip = create_fish("Ice Pip", mines_floor_60, season.all_seasons, 85)
+largemouth_bass = create_fish("Largemouth Bass", mountain_lake, season.all_seasons, 50)
+lava_eel = create_fish("Lava Eel", mines_floor_100, season.all_seasons, 90)
+lingcod = create_fish("Lingcod", town_river + forest_river + mountain_lake, season.winter, 85)
+lionfish = create_fish("Lionfish", ginger_island_ocean, season.all_seasons, 50)
+midnight_carp = create_fish("Midnight Carp", mountain_lake + forest_pond + ginger_island_river,
(season.fall, season.winter), 55)
-octopus = create_fish("Octopus", 149, ocean, season.summer, 95)
-perch = create_fish("Perch", 141, town_river + forest_river + forest_pond + mountain_lake, season.winter, 35)
-pike = create_fish("Pike", 144, town_river + forest_river + forest_pond, (season.summer, season.winter), 60)
-pufferfish = create_fish("Pufferfish", 128, ocean + ginger_island_ocean, season.summer, 80)
-rainbow_trout = create_fish("Rainbow Trout", 138, town_river + forest_river + mountain_lake, season.summer, 45)
-red_mullet = create_fish("Red Mullet", 146, ocean, (season.summer, season.winter), 55)
-red_snapper = create_fish("Red Snapper", 150, ocean, (season.summer, season.fall), 40)
-salmon = create_fish("Salmon", 139, town_river + forest_river, season.fall, 50)
-sandfish = create_fish("Sandfish", 164, desert, season.all_seasons, 65)
-sardine = create_fish("Sardine", 131, ocean, (season.spring, season.fall, season.winter), 30)
-scorpion_carp = create_fish("Scorpion Carp", 165, desert, season.all_seasons, 90)
-sea_cucumber = create_fish("Sea Cucumber", 154, ocean, (season.fall, season.winter), 40)
-shad = create_fish("Shad", 706, town_river + forest_river, season.not_winter, 45)
-slimejack = create_fish("Slimejack", 796, mutant_bug_lair, season.all_seasons, 55)
-smallmouth_bass = create_fish("Smallmouth Bass", 137, town_river + forest_river, (season.spring, season.fall), 28)
-squid = create_fish("Squid", 151, ocean, season.winter, 75)
-stingray = create_fish("Stingray", 836, pirate_cove, season.all_seasons, 80)
-stonefish = create_fish("Stonefish", 158, mines_floor_20, season.all_seasons, 65)
-sturgeon = create_fish("Sturgeon", 698, mountain_lake, (season.summer, season.winter), 78)
-sunfish = create_fish("Sunfish", 145, town_river + forest_river, (season.spring, season.summer), 30)
-super_cucumber = create_fish("Super Cucumber", 155, ocean + ginger_island_ocean, (season.summer, season.fall), 80)
-tiger_trout = create_fish("Tiger Trout", 699, town_river + forest_river, (season.fall, season.winter), 60)
-tilapia = create_fish("Tilapia", 701, ocean + ginger_island_ocean, (season.summer, season.fall), 50)
+octopus = create_fish("Octopus", ocean, season.summer, 95)
+perch = create_fish("Perch", town_river + forest_river + forest_pond + mountain_lake, season.winter, 35)
+pike = create_fish("Pike", town_river + forest_river + forest_pond, (season.summer, season.winter), 60)
+pufferfish = create_fish("Pufferfish", ocean + ginger_island_ocean, season.summer, 80)
+rainbow_trout = create_fish("Rainbow Trout", town_river + forest_river + mountain_lake, season.summer, 45)
+red_mullet = create_fish("Red Mullet", ocean, (season.summer, season.winter), 55)
+red_snapper = create_fish("Red Snapper", ocean, (season.summer, season.fall), 40)
+salmon = create_fish("Salmon", town_river + forest_river, season.fall, 50)
+sandfish = create_fish("Sandfish", desert, season.all_seasons, 65)
+sardine = create_fish("Sardine", ocean, (season.spring, season.fall, season.winter), 30)
+scorpion_carp = create_fish("Scorpion Carp", desert, season.all_seasons, 90)
+sea_cucumber = create_fish("Sea Cucumber", ocean, (season.fall, season.winter), 40)
+shad = create_fish("Shad", town_river + forest_river, season.not_winter, 45)
+slimejack = create_fish("Slimejack", mutant_bug_lair, season.all_seasons, 55)
+smallmouth_bass = create_fish("Smallmouth Bass", town_river + forest_river, (season.spring, season.fall), 28)
+squid = create_fish("Squid", ocean, season.winter, 75)
+stingray = create_fish("Stingray", pirate_cove, season.all_seasons, 80)
+stonefish = create_fish("Stonefish", mines_floor_20, season.all_seasons, 65)
+sturgeon = create_fish("Sturgeon", mountain_lake, (season.summer, season.winter), 78)
+sunfish = create_fish("Sunfish", town_river + forest_river, (season.spring, season.summer), 30)
+super_cucumber = create_fish("Super Cucumber", ocean + ginger_island_ocean, (season.summer, season.fall), 80)
+tiger_trout = create_fish("Tiger Trout", town_river + forest_river, (season.fall, season.winter), 60)
+tilapia = create_fish("Tilapia", ocean + ginger_island_ocean, (season.summer, season.fall), 50)
# Tuna has different seasons on ginger island. Should be changed when the whole fish thing is refactored
-tuna = create_fish("Tuna", 130, ocean + ginger_island_ocean, (season.summer, season.winter), 70)
-void_salmon = create_fish("Void Salmon", 795, witch_swamp, season.all_seasons, 80)
-walleye = create_fish("Walleye", 140, town_river + forest_river + forest_pond + mountain_lake, season.fall, 45)
-woodskip = create_fish("Woodskip", 734, secret_woods, season.all_seasons, 50)
-
-blob_fish = create_fish("Blobfish", 800, night_market, season.winter, 75)
-midnight_squid = create_fish("Midnight Squid", 798, night_market, season.winter, 55)
-spook_fish = create_fish("Spook Fish", 799, night_market, season.winter, 60)
-
-angler = create_fish("Angler", 160, town_river, season.fall, 85)
-crimsonfish = create_fish("Crimsonfish", 159, ocean, season.summer, 95)
-glacierfish = create_fish("Glacierfish", 775, forest_river, season.winter, 100)
-legend = create_fish("Legend", 163, mountain_lake, season.spring, 110)
-mutant_carp = create_fish("Mutant Carp", 682, sewers, season.all_seasons, 80)
-
-clam = create_fish("Clam", 372, ocean, season.all_seasons, -1)
-cockle = create_fish("Cockle", 718, ocean, season.all_seasons, -1)
-crab = create_fish("Crab", 717, ocean, season.all_seasons, -1)
-crayfish = create_fish("Crayfish", 716, fresh_water, season.all_seasons, -1)
-lobster = create_fish("Lobster", 715, ocean, season.all_seasons, -1)
-mussel = create_fish("Mussel", 719, ocean, season.all_seasons, -1)
-oyster = create_fish("Oyster", 723, ocean, season.all_seasons, -1)
-periwinkle = create_fish("Periwinkle", 722, fresh_water, season.all_seasons, -1)
-shrimp = create_fish("Shrimp", 720, ocean, season.all_seasons, -1)
-snail = create_fish("Snail", 721, fresh_water, season.all_seasons, -1)
-
-legendary_fish = [crimsonfish, angler, legend, glacierfish, mutant_carp]
+tuna = create_fish("Tuna", ocean + ginger_island_ocean, (season.summer, season.winter), 70)
+void_salmon = create_fish("Void Salmon", witch_swamp, season.all_seasons, 80)
+walleye = create_fish("Walleye", town_river + forest_river + forest_pond + mountain_lake, season.fall, 45)
+woodskip = create_fish("Woodskip", secret_woods, season.all_seasons, 50)
+
+blob_fish = create_fish("Blobfish", night_market, season.winter, 75)
+midnight_squid = create_fish("Midnight Squid", night_market, season.winter, 55)
+spook_fish = create_fish("Spook Fish", night_market, season.winter, 60)
+
+angler = create_fish(Fish.angler, town_river, season.fall, 85, True, False)
+crimsonfish = create_fish(Fish.crimsonfish, ocean, season.summer, 95, True, False)
+glacierfish = create_fish(Fish.glacierfish, forest_river, season.winter, 100, True, False)
+legend = create_fish(Fish.legend, mountain_lake, season.spring, 110, True, False)
+mutant_carp = create_fish(Fish.mutant_carp, sewers, season.all_seasons, 80, True, False)
+
+ms_angler = create_fish(Fish.ms_angler, town_river, season.fall, 85, True, True)
+son_of_crimsonfish = create_fish(Fish.son_of_crimsonfish, ocean, season.summer, 95, True, True)
+glacierfish_jr = create_fish(Fish.glacierfish_jr, forest_river, season.winter, 100, True, True)
+legend_ii = create_fish(Fish.legend_ii, mountain_lake, season.spring, 110, True, True)
+radioactive_carp = create_fish(Fish.radioactive_carp, sewers, season.all_seasons, 80, True, True)
+
+baby_lunaloo = create_fish(SVEFish.baby_lunaloo, ginger_island_ocean, season.all_seasons, 15, mod_name=ModNames.sve)
+bonefish = create_fish(SVEFish.bonefish, crimson_badlands, season.all_seasons, 70, mod_name=ModNames.sve)
+bull_trout = create_fish(SVEFish.bull_trout, forest_river, season.not_spring, 45, mod_name=ModNames.sve)
+butterfish = create_fish(SVEFish.butterfish, shearwater, season.not_winter, 75, mod_name=ModNames.sve)
+clownfish = create_fish(SVEFish.clownfish, ginger_island_ocean, season.all_seasons, 45, mod_name=ModNames.sve)
+daggerfish = create_fish(SVEFish.daggerfish, highlands, season.all_seasons, 50, mod_name=ModNames.sve)
+frog = create_fish(SVEFish.frog, mountain_lake, (season.spring, season.summer), 70, mod_name=ModNames.sve)
+gemfish = create_fish(SVEFish.gemfish, highlands, season.all_seasons, 100, mod_name=ModNames.sve)
+goldenfish = create_fish(SVEFish.goldenfish, sprite_spring, season.all_seasons, 60, mod_name=ModNames.sve)
+grass_carp = create_fish(SVEFish.grass_carp, secret_woods, (season.spring, season.summer), 85, mod_name=ModNames.sve)
+king_salmon = create_fish(SVEFish.king_salmon, forest_river, (season.spring, season.summer), 80, mod_name=ModNames.sve)
+kittyfish = create_fish(SVEFish.kittyfish, shearwater, (season.fall, season.winter), 85, mod_name=ModNames.sve)
+lunaloo = create_fish(SVEFish.lunaloo, ginger_island_ocean, season.all_seasons, 70, mod_name=ModNames.sve)
+meteor_carp = create_fish(SVEFish.meteor_carp, sprite_spring, season.all_seasons, 80, mod_name=ModNames.sve)
+minnow = create_fish(SVEFish.minnow, town_river, season.all_seasons, 1, mod_name=ModNames.sve)
+puppyfish = create_fish(SVEFish.puppyfish, shearwater, season.not_winter, 85, mod_name=ModNames.sve)
+radioactive_bass = create_fish(SVEFish.radioactive_bass, sewers, season.all_seasons, 90, mod_name=ModNames.sve)
+seahorse = create_fish(SVEFish.seahorse, ginger_island_ocean, season.all_seasons, 25, mod_name=ModNames.sve)
+shiny_lunaloo = create_fish(SVEFish.shiny_lunaloo, ginger_island_ocean, season.all_seasons, 110, mod_name=ModNames.sve)
+snatcher_worm = create_fish(SVEFish.snatcher_worm, mutant_bug_lair, season.all_seasons, 75, mod_name=ModNames.sve)
+starfish = create_fish(SVEFish.starfish, ginger_island_ocean, season.all_seasons, 75, mod_name=ModNames.sve)
+torpedo_trout = create_fish(SVEFish.torpedo_trout, fable_reef, season.all_seasons, 70, mod_name=ModNames.sve)
+undeadfish = create_fish(SVEFish.undeadfish, crimson_badlands, season.all_seasons, 80, mod_name=ModNames.sve)
+void_eel = create_fish(SVEFish.void_eel, witch_swamp, season.all_seasons, 100, mod_name=ModNames.sve)
+water_grub = create_fish(SVEFish.water_grub, mutant_bug_lair, season.all_seasons, 60, mod_name=ModNames.sve)
+sea_sponge = create_fish(SVEFish.sea_sponge, ginger_island_ocean, season.all_seasons, 40, mod_name=ModNames.sve)
+dulse_seaweed = create_fish(SVEFish.dulse_seaweed, vineyard, season.all_seasons, 50, mod_name=ModNames.sve)
+
+void_minnow = create_fish(DistantLandsFish.void_minnow, witch_swamp, season.all_seasons, 15, mod_name=ModNames.distant_lands)
+purple_algae = create_fish(DistantLandsFish.purple_algae, witch_swamp, season.all_seasons, 15, mod_name=ModNames.distant_lands)
+swamp_leech = create_fish(DistantLandsFish.swamp_leech, witch_swamp, season.all_seasons, 15, mod_name=ModNames.distant_lands)
+giant_horsehoe_crab = create_fish(DistantLandsFish.giant_horsehoe_crab, witch_swamp, season.all_seasons, 90, mod_name=ModNames.distant_lands)
+
+
+clam = create_fish("Clam", ocean, season.all_seasons, -1)
+cockle = create_fish("Cockle", ocean, season.all_seasons, -1)
+crab = create_fish("Crab", ocean, season.all_seasons, -1)
+crayfish = create_fish("Crayfish", fresh_water, season.all_seasons, -1)
+lobster = create_fish("Lobster", ocean, season.all_seasons, -1)
+mussel = create_fish("Mussel", ocean, season.all_seasons, -1)
+oyster = create_fish("Oyster", ocean, season.all_seasons, -1)
+periwinkle = create_fish("Periwinkle", fresh_water, season.all_seasons, -1)
+shrimp = create_fish("Shrimp", ocean, season.all_seasons, -1)
+snail = create_fish("Snail", fresh_water, season.all_seasons, -1)
+
+legendary_fish = [angler, crimsonfish, glacierfish, legend, mutant_carp]
+extended_family = [ms_angler, son_of_crimsonfish, glacierfish_jr, legend_ii, radioactive_carp]
special_fish = [*legendary_fish, blob_fish, lava_eel, octopus, scorpion_carp, ice_pip, super_cucumber, dorado]
-island_fish = [lionfish, blue_discus, stingray]
+island_fish = [lionfish, blue_discus, stingray, *extended_family]
all_fish_by_name = {fish.name: fish for fish in all_fish}
+
+
+def get_fish_for_mods(mods: Set[str]) -> List[FishItem]:
+ fish_for_mods = []
+ for fish in all_fish:
+ if fish.mod_name and fish.mod_name not in mods:
+ continue
+ fish_for_mods.append(fish)
+ return fish_for_mods
diff --git a/worlds/stardew_valley/data/game_item.py b/worlds/stardew_valley/data/game_item.py
deleted file mode 100644
index cac86d527d86..000000000000
--- a/worlds/stardew_valley/data/game_item.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from dataclasses import dataclass
-
-
-@dataclass(frozen=True)
-class GameItem:
- name: str
- item_id: int
-
- def __repr__(self):
- return f"{self.name} [{self.item_id}]"
-
- def __lt__(self, other):
- return self.name < other.name
diff --git a/worlds/stardew_valley/data/items.csv b/worlds/stardew_valley/data/items.csv
index 3c4ddb84156b..a3096cf789df 100644
--- a/worlds/stardew_valley/data/items.csv
+++ b/worlds/stardew_valley/data/items.csv
@@ -7,48 +7,46 @@ id,name,classification,groups,mod_name
19,Glittering Boulder Removed,progression,COMMUNITY_REWARD,
20,Minecarts Repair,useful,COMMUNITY_REWARD,
21,Bus Repair,progression,COMMUNITY_REWARD,
-22,Movie Theater,useful,,
+22,Progressive Movie Theater,progression,COMMUNITY_REWARD,
23,Stardrop,progression,,
24,Progressive Backpack,progression,,
-25,Rusty Sword,progression,WEAPON,
-26,Leather Boots,progression,"FOOTWEAR,MINES_FLOOR_10",
-27,Work Boots,useful,"FOOTWEAR,MINES_FLOOR_10",
-28,Wooden Blade,progression,"MINES_FLOOR_10,WEAPON",
-29,Iron Dirk,progression,"MINES_FLOOR_10,WEAPON",
-30,Wind Spire,progression,"MINES_FLOOR_10,WEAPON",
-31,Femur,progression,"MINES_FLOOR_10,WEAPON",
-32,Steel Smallsword,progression,"MINES_FLOOR_20,WEAPON",
-33,Wood Club,progression,"MINES_FLOOR_20,WEAPON",
-34,Elf Blade,progression,"MINES_FLOOR_20,WEAPON",
-35,Glow Ring,useful,"MINES_FLOOR_20,RING",
-36,Magnet Ring,useful,"MINES_FLOOR_20,RING",
-37,Slingshot,progression,WEAPON,
-38,Tundra Boots,useful,"FOOTWEAR,MINES_FLOOR_50",
-39,Thermal Boots,useful,"FOOTWEAR,MINES_FLOOR_50",
-40,Combat Boots,useful,"FOOTWEAR,MINES_FLOOR_50",
-41,Silver Saber,progression,"MINES_FLOOR_50,WEAPON",
-42,Pirate's Sword,progression,"MINES_FLOOR_50,WEAPON",
-43,Crystal Dagger,progression,"MINES_FLOOR_60,WEAPON",
-44,Cutlass,progression,"MINES_FLOOR_60,WEAPON",
-45,Iron Edge,progression,"MINES_FLOOR_60,WEAPON",
-46,Burglar's Shank,progression,"MINES_FLOOR_60,WEAPON",
-47,Wood Mallet,progression,"MINES_FLOOR_60,WEAPON",
-48,Master Slingshot,progression,WEAPON,
-49,Firewalker Boots,useful,"FOOTWEAR,MINES_FLOOR_80",
-50,Dark Boots,useful,"FOOTWEAR,MINES_FLOOR_80",
-51,Claymore,progression,"MINES_FLOOR_80,WEAPON",
-52,Templar's Blade,progression,"MINES_FLOOR_80,WEAPON",
-53,Kudgel,progression,"MINES_FLOOR_80,WEAPON",
-54,Shadow Dagger,progression,"MINES_FLOOR_80,WEAPON",
-55,Obsidian Edge,progression,"MINES_FLOOR_90,WEAPON",
-56,Tempered Broadsword,progression,"MINES_FLOOR_90,WEAPON",
-57,Wicked Kris,progression,"MINES_FLOOR_90,WEAPON",
-58,Bone Sword,progression,"MINES_FLOOR_90,WEAPON",
-59,Ossified Blade,progression,"MINES_FLOOR_90,WEAPON",
-60,Space Boots,useful,"FOOTWEAR,MINES_FLOOR_110",
-61,Crystal Shoes,useful,"FOOTWEAR,MINES_FLOOR_110",
-62,Steel Falchion,progression,"MINES_FLOOR_110,WEAPON",
-63,The Slammer,progression,"MINES_FLOOR_110,WEAPON",
+25,Rusty Sword,filler,"WEAPON,DEPRECATED",
+26,Leather Boots,filler,"FOOTWEAR,DEPRECATED",
+27,Work Boots,filler,"FOOTWEAR,DEPRECATED",
+28,Wooden Blade,filler,"WEAPON,DEPRECATED",
+29,Iron Dirk,filler,"WEAPON,DEPRECATED",
+30,Wind Spire,filler,"WEAPON,DEPRECATED",
+31,Femur,filler,"WEAPON,DEPRECATED",
+32,Steel Smallsword,filler,"WEAPON,DEPRECATED",
+33,Wood Club,filler,"WEAPON,DEPRECATED",
+34,Elf Blade,filler,"WEAPON,DEPRECATED",
+37,Slingshot,filler,"WEAPON,DEPRECATED",
+38,Tundra Boots,filler,"FOOTWEAR,DEPRECATED",
+39,Thermal Boots,filler,"FOOTWEAR,DEPRECATED",
+40,Combat Boots,filler,"FOOTWEAR,DEPRECATED",
+41,Silver Saber,filler,"WEAPON,DEPRECATED",
+42,Pirate's Sword,filler,"WEAPON,DEPRECATED",
+43,Crystal Dagger,filler,"WEAPON,DEPRECATED",
+44,Cutlass,filler,"WEAPON,DEPRECATED",
+45,Iron Edge,filler,"WEAPON,DEPRECATED",
+46,Burglar's Shank,filler,"WEAPON,DEPRECATED",
+47,Wood Mallet,filler,"WEAPON,DEPRECATED",
+48,Master Slingshot,filler,"WEAPON,DEPRECATED",
+49,Firewalker Boots,filler,"FOOTWEAR,DEPRECATED",
+50,Dark Boots,filler,"FOOTWEAR,DEPRECATED",
+51,Claymore,filler,"WEAPON,DEPRECATED",
+52,Templar's Blade,filler,"WEAPON,DEPRECATED",
+53,Kudgel,filler,"WEAPON,DEPRECATED",
+54,Shadow Dagger,filler,"WEAPON,DEPRECATED",
+55,Obsidian Edge,filler,"WEAPON,DEPRECATED",
+56,Tempered Broadsword,filler,"WEAPON,DEPRECATED",
+57,Wicked Kris,filler,"WEAPON,DEPRECATED",
+58,Bone Sword,filler,"WEAPON,DEPRECATED",
+59,Ossified Blade,filler,"WEAPON,DEPRECATED",
+60,Space Boots,filler,"FOOTWEAR,DEPRECATED",
+61,Crystal Shoes,filler,"FOOTWEAR,DEPRECATED",
+62,Steel Falchion,filler,"WEAPON,DEPRECATED",
+63,The Slammer,filler,"WEAPON,DEPRECATED",
64,Skull Key,progression,,
65,Progressive Hoe,progression,PROGRESSIVE_TOOLS,
66,Progressive Pickaxe,progression,PROGRESSIVE_TOOLS,
@@ -63,40 +61,40 @@ id,name,classification,groups,mod_name
75,Foraging Level,progression,SKILL_LEVEL_UP,
76,Mining Level,progression,SKILL_LEVEL_UP,
77,Combat Level,progression,SKILL_LEVEL_UP,
-78,Earth Obelisk,progression,,
-79,Water Obelisk,progression,,
-80,Desert Obelisk,progression,,
-81,Island Obelisk,progression,GINGER_ISLAND,
-82,Junimo Hut,useful,,
-83,Gold Clock,progression,,
-84,Progressive Coop,progression,,
-85,Progressive Barn,progression,,
-86,Well,useful,,
-87,Silo,progression,,
-88,Mill,progression,,
-89,Progressive Shed,progression,,
-90,Fish Pond,progression,,
-91,Stable,useful,,
-92,Slime Hutch,useful,,
-93,Shipping Bin,progression,,
+78,Earth Obelisk,progression,WIZARD_BUILDING,
+79,Water Obelisk,progression,WIZARD_BUILDING,
+80,Desert Obelisk,progression,WIZARD_BUILDING,
+81,Island Obelisk,progression,"WIZARD_BUILDING,GINGER_ISLAND",
+82,Junimo Hut,useful,WIZARD_BUILDING,
+83,Gold Clock,progression,WIZARD_BUILDING,
+84,Progressive Coop,progression,BUILDING,
+85,Progressive Barn,progression,BUILDING,
+86,Well,useful,BUILDING,
+87,Silo,progression,BUILDING,
+88,Mill,progression,BUILDING,
+89,Progressive Shed,progression,BUILDING,
+90,Fish Pond,progression,BUILDING,
+91,Stable,useful,BUILDING,
+92,Slime Hutch,progression,BUILDING,
+93,Shipping Bin,progression,BUILDING,
94,Beach Bridge,progression,,
-95,Adventurer's Guild,progression,,
+95,Adventurer's Guild,progression,DEPRECATED,
96,Club Card,progression,,
97,Magnifying Glass,progression,,
98,Bear's Knowledge,progression,,
-99,Iridium Snake Milk,progression,,
+99,Iridium Snake Milk,useful,,
100,JotPK: Progressive Boots,progression,ARCADE_MACHINE_BUFFS,
101,JotPK: Progressive Gun,progression,ARCADE_MACHINE_BUFFS,
102,JotPK: Progressive Ammo,progression,ARCADE_MACHINE_BUFFS,
103,JotPK: Extra Life,progression,ARCADE_MACHINE_BUFFS,
104,JotPK: Increased Drop Rate,progression,ARCADE_MACHINE_BUFFS,
105,Junimo Kart: Extra Life,progression,ARCADE_MACHINE_BUFFS,
-106,Galaxy Sword,progression,"GALAXY_WEAPONS,WEAPON",
-107,Galaxy Dagger,progression,"GALAXY_WEAPONS,WEAPON",
-108,Galaxy Hammer,progression,"GALAXY_WEAPONS,WEAPON",
+106,Galaxy Sword,filler,"WEAPON,DEPRECATED",
+107,Galaxy Dagger,filler,"WEAPON,DEPRECATED",
+108,Galaxy Hammer,filler,"WEAPON,DEPRECATED",
109,Movement Speed Bonus,progression,,
110,Luck Bonus,progression,,
-111,Lava Katana,progression,"MINES_FLOOR_110,WEAPON",
+111,Lava Katana,filler,"WEAPON,DEPRECATED",
112,Progressive House,progression,,
113,Traveling Merchant: Sunday,progression,TRAVELING_MERCHANT_DAY,
114,Traveling Merchant: Monday,progression,TRAVELING_MERCHANT_DAY,
@@ -105,8 +103,8 @@ id,name,classification,groups,mod_name
117,Traveling Merchant: Thursday,progression,TRAVELING_MERCHANT_DAY,
118,Traveling Merchant: Friday,progression,TRAVELING_MERCHANT_DAY,
119,Traveling Merchant: Saturday,progression,TRAVELING_MERCHANT_DAY,
-120,Traveling Merchant Stock Size,progression,,
-121,Traveling Merchant Discount,progression,,
+120,Traveling Merchant Stock Size,useful,,
+121,Traveling Merchant Discount,useful,,
122,Return Scepter,useful,,
123,Progressive Season,progression,,
124,Spring,progression,SEASON,
@@ -223,7 +221,7 @@ id,name,classification,groups,mod_name
235,Quality Bobber Recipe,progression,SPECIAL_ORDER_BOARD,
236,Mini-Obelisk Recipe,progression,SPECIAL_ORDER_BOARD,
237,Monster Musk Recipe,progression,SPECIAL_ORDER_BOARD,
-239,Sewing Machine,progression,"RESOURCE_PACK_USEFUL,SPECIAL_ORDER_BOARD",
+239,Sewing Machine,useful,"RESOURCE_PACK_USEFUL,SPECIAL_ORDER_BOARD",
240,Coffee Maker,progression,"RESOURCE_PACK_USEFUL,SPECIAL_ORDER_BOARD",
241,Mini-Fridge,useful,"RESOURCE_PACK_USEFUL,SPECIAL_ORDER_BOARD",
242,Mini-Shipping Bin,progression,"RESOURCE_PACK_USEFUL,SPECIAL_ORDER_BOARD",
@@ -245,7 +243,7 @@ id,name,classification,groups,mod_name
258,Banana Sapling,progression,"GINGER_ISLAND,RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY",
259,Mango Sapling,progression,"GINGER_ISLAND,RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY",
260,Boat Repair,progression,GINGER_ISLAND,
-261,Open Professor Snail Cave,progression,"GINGER_ISLAND",
+261,Open Professor Snail Cave,progression,GINGER_ISLAND,
262,Island North Turtle,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
263,Island West Turtle,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
264,Island Farmhouse,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
@@ -254,42 +252,218 @@ id,name,classification,groups,mod_name
267,Dig Site Bridge,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
268,Island Trader,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
269,Volcano Bridge,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
-270,Volcano Exit Shortcut,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
+270,Volcano Exit Shortcut,useful,"GINGER_ISLAND,WALNUT_PURCHASE",
271,Island Resort,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
272,Parrot Express,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
273,Qi Walnut Room,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
274,Pineapple Seeds,progression,"GINGER_ISLAND,CROPSANITY",
275,Taro Tuber,progression,"GINGER_ISLAND,CROPSANITY",
-276,Weather Report,useful,"TV_CHANNEL",
-277,Fortune Teller,useful,"TV_CHANNEL",
-278,Livin' Off The Land,useful,"TV_CHANNEL",
-279,The Queen of Sauce,progression,"TV_CHANNEL",
-280,Fishing Information Broadcasting Service,useful,"TV_CHANNEL",
-281,Sinister Signal,useful,"TV_CHANNEL",
+276,Weather Report,useful,TV_CHANNEL,
+277,Fortune Teller,useful,TV_CHANNEL,
+278,Livin' Off The Land,useful,TV_CHANNEL,
+279,The Queen of Sauce,progression,TV_CHANNEL,
+280,Fishing Information Broadcasting Service,useful,TV_CHANNEL,
+281,Sinister Signal,filler,TV_CHANNEL,
282,Dark Talisman,progression,,
-283,Ostrich Incubator Recipe,progression,"GINGER_ISLAND",
-284,Cute Baby,progression,"BABY",
-285,Ugly Baby,progression,"BABY",
-286,Deluxe Scarecrow Recipe,progression,"FESTIVAL,RARECROW",
-287,Treehouse,progression,"GINGER_ISLAND",
+283,Ostrich Incubator Recipe,progression,GINGER_ISLAND,
+284,Cute Baby,progression,BABY,
+285,Ugly Baby,progression,BABY,
+286,Deluxe Scarecrow Recipe,progression,RARECROW,
+287,Treehouse,progression,GINGER_ISLAND,
288,Coffee Bean,progression,CROPSANITY,
-4001,Burnt,trap,TRAP,
-4002,Darkness,trap,TRAP,
-4003,Frozen,trap,TRAP,
-4004,Jinxed,trap,TRAP,
-4005,Nauseated,trap,TRAP,
-4006,Slimed,trap,TRAP,
-4007,Weakness,trap,TRAP,
-4008,Taxes,trap,TRAP,
-4009,Random Teleport,trap,TRAP,
-4010,The Crows,trap,TRAP,
-4011,Monsters,trap,TRAP,
-4012,Entrance Reshuffle,trap,"TRAP,DEPRECATED",
-4013,Debris,trap,TRAP,
-4014,Shuffle,trap,TRAP,
-4015,Temporary Winter,trap,"TRAP,DEPRECATED",
-4016,Pariah,trap,TRAP,
-4017,Drought,trap,TRAP,
+289,Progressive Weapon,progression,"WEAPON,WEAPON_GENERIC",
+290,Progressive Sword,progression,"WEAPON,WEAPON_SWORD",
+291,Progressive Club,progression,"WEAPON,WEAPON_CLUB",
+292,Progressive Dagger,progression,"WEAPON,WEAPON_DAGGER",
+293,Progressive Slingshot,progression,"WEAPON,WEAPON_SLINGSHOT",
+294,Progressive Footwear,useful,FOOTWEAR,
+295,Small Glow Ring,filler,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+296,Glow Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+297,Small Magnet Ring,filler,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+298,Magnet Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+299,Slime Charmer Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+300,Warrior Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+301,Vampire Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+302,Savage Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+303,Ring of Yoba,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+304,Sturdy Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+305,Burglar's Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+306,Iridium Band,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+307,Jukebox Ring,filler,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+308,Amethyst Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+309,Topaz Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+310,Aquamarine Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+311,Jade Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+312,Emerald Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+313,Ruby Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+314,Wedding Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+315,Crabshell Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+316,Napalm Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+317,Thorns Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+318,Lucky Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+319,Hot Java Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+320,Protection Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+321,Soul Sapper Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+322,Phoenix Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+323,Immunity Band,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+324,Glowstone Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE",
+325,Fairy Dust Recipe,progression,,
+326,Heavy Tapper Recipe,progression,"QI_CRAFTING_RECIPE,GINGER_ISLAND",
+327,Hyper Speed-Gro Recipe,progression,"QI_CRAFTING_RECIPE,GINGER_ISLAND",
+328,Deluxe Fertilizer Recipe,progression,QI_CRAFTING_RECIPE,
+329,Hopper Recipe,progression,"QI_CRAFTING_RECIPE,GINGER_ISLAND",
+330,Magic Bait Recipe,progression,"QI_CRAFTING_RECIPE,GINGER_ISLAND",
+331,Jack-O-Lantern Recipe,progression,FESTIVAL,
+333,Tub o' Flowers Recipe,progression,FESTIVAL,
+335,Moonlight Jellies Banner,filler,FESTIVAL,
+336,Starport Decal,filler,FESTIVAL,
+337,Golden Egg,progression,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
+340,Algae Soup Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+341,Artichoke Dip Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+342,Autumn's Bounty Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+343,Baked Fish Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+344,Banana Pudding Recipe,progression,"CHEFSANITY,GINGER_ISLAND,CHEFSANITY_PURCHASE",
+345,Bean Hotpot Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+346,Blackberry Cobbler Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+347,Blueberry Tart Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+348,Bread Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS,CHEFSANITY_FRIENDSHIP,CHEFSANITY_PURCHASE",
+349,Bruschetta Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+350,Carp Surprise Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+351,Cheese Cauliflower Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+352,Chocolate Cake Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+353,Chowder Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+354,Coleslaw Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+355,Complete Breakfast Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+356,Cookies Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+357,Crab Cakes Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+358,Cranberry Candy Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+359,Cranberry Sauce Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+360,Crispy Bass Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+361,Dish O' The Sea Recipe,progression,"CHEFSANITY,CHEFSANITY_SKILL",
+362,Eggplant Parmesan Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+363,Escargot Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+364,Farmer's Lunch Recipe,progression,"CHEFSANITY,CHEFSANITY_SKILL",
+365,Fiddlehead Risotto Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+366,Fish Stew Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+367,Fish Taco Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+368,Fried Calamari Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+369,Fried Eel Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+370,Fried Egg Recipe,progression,CHEFSANITY_STARTER,
+371,Fried Mushroom Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+372,Fruit Salad Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+373,Ginger Ale Recipe,progression,"CHEFSANITY,GINGER_ISLAND,CHEFSANITY_PURCHASE",
+374,Glazed Yams Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+375,Hashbrowns Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS,CHEFSANITY_PURCHASE",
+376,Ice Cream Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+377,Lobster Bisque Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS,CHEFSANITY_FRIENDSHIP",
+378,Lucky Lunch Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+379,Maki Roll Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS,CHEFSANITY_PURCHASE",
+380,Mango Sticky Rice Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP,GINGER_ISLAND",
+381,Maple Bar Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+382,Miner's Treat Recipe,progression,"CHEFSANITY,CHEFSANITY_SKILL",
+383,Omelet Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS,CHEFSANITY_PURCHASE",
+384,Pale Broth Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+385,Pancakes Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS,CHEFSANITY_PURCHASE",
+386,Parsnip Soup Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+387,Pepper Poppers Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+388,Pink Cake Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+389,Pizza Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS,CHEFSANITY_PURCHASE",
+390,Plum Pudding Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+391,Poi Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP,GINGER_ISLAND",
+392,Poppyseed Muffin Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+393,Pumpkin Pie Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+394,Pumpkin Soup Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+395,Radish Salad Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+396,Red Plate Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+397,Rhubarb Pie Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+398,Rice Pudding Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+399,Roasted Hazelnuts Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+400,Roots Platter Recipe,progression,"CHEFSANITY,CHEFSANITY_SKILL",
+401,Salad Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+402,Salmon Dinner Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+403,Sashimi Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+404,Seafoam Pudding Recipe,progression,"CHEFSANITY,CHEFSANITY_SKILL",
+405,Shrimp Cocktail Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+406,Spaghetti Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+407,Spicy Eel Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+408,Squid Ink Ravioli Recipe,progression,"CHEFSANITY,CHEFSANITY_SKILL",
+409,Stir Fry Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+410,Strange Bun Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+411,Stuffing Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+412,Super Meal Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+413,Survival Burger Recipe,progression,"CHEFSANITY,CHEFSANITY_SKILL",
+414,Tom Kha Soup Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+415,Tortilla Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS,CHEFSANITY_PURCHASE",
+416,Triple Shot Espresso Recipe,progression,"CHEFSANITY,CHEFSANITY_PURCHASE",
+417,Tropical Curry Recipe,progression,"CHEFSANITY,GINGER_ISLAND,CHEFSANITY_PURCHASE",
+418,Trout Soup Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
+419,Vegetable Medley Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+425,Gate Recipe,progression,CRAFTSANITY,
+426,Wood Fence Recipe,progression,CRAFTSANITY,
+427,Deluxe Retaining Soil Recipe,progression,"CRAFTSANITY,GINGER_ISLAND",
+428,Grass Starter Recipe,progression,CRAFTSANITY,
+429,Wood Floor Recipe,progression,CRAFTSANITY,
+430,Rustic Plank Floor Recipe,progression,CRAFTSANITY,
+431,Straw Floor Recipe,progression,CRAFTSANITY,
+432,Weathered Floor Recipe,progression,CRAFTSANITY,
+433,Crystal Floor Recipe,progression,CRAFTSANITY,
+434,Stone Floor Recipe,progression,CRAFTSANITY,
+435,Stone Walkway Floor Recipe,progression,CRAFTSANITY,
+436,Brick Floor Recipe,progression,CRAFTSANITY,
+437,Wood Path Recipe,progression,CRAFTSANITY,
+438,Gravel Path Recipe,progression,CRAFTSANITY,
+439,Cobblestone Path Recipe,progression,CRAFTSANITY,
+440,Stepping Stone Path Recipe,progression,CRAFTSANITY,
+441,Crystal Path Recipe,progression,CRAFTSANITY,
+442,Wedding Ring Recipe,progression,CRAFTSANITY,
+443,Warp Totem: Desert Recipe,progression,CRAFTSANITY,
+444,Warp Totem: Island Recipe,progression,"CRAFTSANITY,GINGER_ISLAND",
+445,Torch Recipe,progression,CRAFTSANITY,
+446,Campfire Recipe,progression,CRAFTSANITY,
+447,Wooden Brazier Recipe,progression,CRAFTSANITY,
+448,Stone Brazier Recipe,progression,CRAFTSANITY,
+449,Gold Brazier Recipe,progression,CRAFTSANITY,
+450,Carved Brazier Recipe,progression,CRAFTSANITY,
+451,Stump Brazier Recipe,progression,CRAFTSANITY,
+452,Barrel Brazier Recipe,progression,CRAFTSANITY,
+453,Skull Brazier Recipe,progression,CRAFTSANITY,
+454,Marble Brazier Recipe,progression,CRAFTSANITY,
+455,Wood Lamp-post Recipe,progression,CRAFTSANITY,
+456,Iron Lamp-post Recipe,progression,CRAFTSANITY,
+457,Furnace Recipe,progression,CRAFTSANITY,
+458,Wicked Statue Recipe,progression,CRAFTSANITY,
+459,Chest Recipe,progression,CRAFTSANITY,
+460,Wood Sign Recipe,progression,CRAFTSANITY,
+461,Stone Sign Recipe,progression,CRAFTSANITY,
+469,Railroad Boulder Removed,progression,,
+470,Fruit Bats,progression,,
+471,Mushroom Boxes,progression,,
+475,The Gateway Gazette,progression,TV_CHANNEL,
+4001,Burnt Trap,trap,TRAP,
+4002,Darkness Trap,trap,TRAP,
+4003,Frozen Trap,trap,TRAP,
+4004,Jinxed Trap,trap,TRAP,
+4005,Nauseated Trap,trap,TRAP,
+4006,Slimed Trap,trap,TRAP,
+4007,Weakness Trap,trap,TRAP,
+4008,Taxes Trap,trap,TRAP,
+4009,Random Teleport Trap,trap,TRAP,
+4010,The Crows Trap,trap,TRAP,
+4011,Monsters Trap,trap,TRAP,
+4012,Entrance Reshuffle Trap,trap,"TRAP,DEPRECATED",
+4013,Debris Trap,trap,TRAP,
+4014,Shuffle Trap,trap,TRAP,
+4015,Temporary Winter Trap,trap,"TRAP,DEPRECATED",
+4016,Pariah Trap,trap,TRAP,
+4017,Drought Trap,trap,TRAP,
+4018,Time Flies Trap,trap,TRAP,
+4019,Babies Trap,trap,TRAP,
+4020,Meow Trap,trap,TRAP,
+4021,Bark Trap,trap,TRAP,
+4022,Depression Trap,trap,"TRAP,DEPRECATED",
+4023,Benjamin Budton Trap,trap,TRAP,
+4024,Inflation Trap,trap,TRAP,
+4025,Bomb Trap,trap,TRAP,
5000,Resource Pack: 500 Money,useful,"BASE_RESOURCE,RESOURCE_PACK",
5001,Resource Pack: 1000 Money,useful,"BASE_RESOURCE,RESOURCE_PACK",
5002,Resource Pack: 1500 Money,useful,"BASE_RESOURCE,RESOURCE_PACK",
@@ -336,12 +510,12 @@ id,name,classification,groups,mod_name
5043,Resource Pack: 7 Warp Totem: Farm,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5044,Resource Pack: 9 Warp Totem: Farm,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5045,Resource Pack: 10 Warp Totem: Farm,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
-5046,Resource Pack: 1 Warp Totem: Island,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
-5047,Resource Pack: 3 Warp Totem: Island,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
-5048,Resource Pack: 5 Warp Totem: Island,filler,"RESOURCE_PACK,WARP_TOTEM",
-5049,Resource Pack: 7 Warp Totem: Island,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
-5050,Resource Pack: 9 Warp Totem: Island,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
-5051,Resource Pack: 10 Warp Totem: Island,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
+5046,Resource Pack: 1 Warp Totem: Island,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM,GINGER_ISLAND",
+5047,Resource Pack: 3 Warp Totem: Island,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM,GINGER_ISLAND",
+5048,Resource Pack: 5 Warp Totem: Island,filler,"RESOURCE_PACK,WARP_TOTEM,GINGER_ISLAND",
+5049,Resource Pack: 7 Warp Totem: Island,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM,GINGER_ISLAND",
+5050,Resource Pack: 9 Warp Totem: Island,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM,GINGER_ISLAND",
+5051,Resource Pack: 10 Warp Totem: Island,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM,GINGER_ISLAND",
5052,Resource Pack: 1 Warp Totem: Mountains,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5053,Resource Pack: 3 Warp Totem: Mountains,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5054,Resource Pack: 5 Warp Totem: Mountains,filler,"RESOURCE_PACK,WARP_TOTEM",
@@ -492,7 +666,7 @@ id,name,classification,groups,mod_name
5199,Friendship Bonus (2 <3),useful,"FRIENDSHIP_PACK,COMMUNITY_REWARD",
5200,Friendship Bonus (3 <3),useful,"DEPRECATED,FRIENDSHIP_PACK,RESOURCE_PACK",
5201,Friendship Bonus (4 <3),useful,"DEPRECATED,FRIENDSHIP_PACK,RESOURCE_PACK",
-5202,30 Qi Gems,useful,"GINGER_ISLAND,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
+5202,15 Qi Gems,progression,"GINGER_ISLAND,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5203,Solar Panel,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5204,Geode Crusher,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5205,Farm Computer,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
@@ -507,36 +681,29 @@ id,name,classification,groups,mod_name
5214,Quality Sprinkler,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5215,Iridium Sprinkler,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5216,Scarecrow,filler,RESOURCE_PACK,
-5217,Deluxe Scarecrow,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
+5217,Deluxe Scarecrow,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5218,Furnace,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5219,Charcoal Kiln,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5220,Lightning Rod,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5221,Resource Pack: 5000 Money,useful,"BASE_RESOURCE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5222,Resource Pack: 10000 Money,useful,"BASE_RESOURCE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5223,Junimo Chest,filler,"EXACTLY_TWO,RESOURCE_PACK",
-5224,Horse Flute,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
-5225,Pierre's Missing Stocklist,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
+5224,Horse Flute,useful,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
+5225,Pierre's Missing Stocklist,useful,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5226,Hopper,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5227,Enricher,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5228,Pressure Nozzle,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5229,Deconstructor,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
-5230,Key To The Town,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
+5230,Key To The Town,useful,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5231,Galaxy Soul,filler,"GINGER_ISLAND,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5232,Mushroom Tree Seed,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5233,Resource Pack: 20 Magic Bait,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5234,Resource Pack: 10 Qi Seasoning,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5235,Mr. Qi's Hat,filler,"MAXIMUM_ONE,RESOURCE_PACK",
5236,Aquatic Sanctuary,filler,RESOURCE_PACK,
-5237,Heavy Tapper Recipe,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
-5238,Hyper Speed-Gro Recipe,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
-5239,Deluxe Fertilizer Recipe,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
-5240,Hopper Recipe,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
-5241,Magic Bait Recipe,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5242,Exotic Double Bed,filler,RESOURCE_PACK,
-5243,Resource Pack: 2 Qi Gem,filler,"GINGER_ISLAND,RESOURCE_PACK",
-5244,Golden Egg,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
+5243,Resource Pack: 2 Qi Gem,filler,"GINGER_ISLAND,RESOURCE_PACK,DEPRECATED",
5245,Golden Walnut,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL,GINGER_ISLAND",
-5246,Fairy Dust Recipe,useful,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5247,Fairy Dust,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5248,Seed Maker,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5249,Keg,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
@@ -552,10 +719,13 @@ id,name,classification,groups,mod_name
5259,Worm Bin,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5260,Tapper,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5261,Heavy Tapper,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
-5262,Slime Incubator,useful,"RESOURCE_PACK",
-5263,Slime Egg-Press,useful,"RESOURCE_PACK",
+5262,Slime Incubator,useful,RESOURCE_PACK,
+5263,Slime Egg-Press,useful,RESOURCE_PACK,
5264,Crystalarium,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5265,Ostrich Incubator,useful,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
+5266,Resource Pack: 5 Staircase,filler,"RESOURCE_PACK",
+5267,Auto-Petter,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
+5268,Auto-Grabber,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
10001,Luck Level,progression,SKILL_LEVEL_UP,Luck Skill
10002,Magic Level,progression,SKILL_LEVEL_UP,Magic
10003,Socializing Level,progression,SKILL_LEVEL_UP,Socializing Skill
@@ -569,14 +739,14 @@ id,name,classification,groups,mod_name
10011,Spell: Water,progression,MAGIC_SPELL,Magic
10012,Spell: Blink,progression,MAGIC_SPELL,Magic
10013,Spell: Evac,useful,MAGIC_SPELL,Magic
-10014,Spell: Haste,filler,MAGIC_SPELL,Magic
+10014,Spell: Haste,useful,MAGIC_SPELL,Magic
10015,Spell: Heal,progression,MAGIC_SPELL,Magic
10016,Spell: Buff,useful,MAGIC_SPELL,Magic
10017,Spell: Shockwave,progression,MAGIC_SPELL,Magic
10018,Spell: Fireball,progression,MAGIC_SPELL,Magic
-10019,Spell: Frostbite,progression,MAGIC_SPELL,Magic
+10019,Spell: Frostbolt,progression,MAGIC_SPELL,Magic
10020,Spell: Teleport,progression,MAGIC_SPELL,Magic
-10021,Spell: Lantern,filler,MAGIC_SPELL,Magic
+10021,Spell: Lantern,useful,MAGIC_SPELL,Magic
10022,Spell: Tendrils,progression,MAGIC_SPELL,Magic
10023,Spell: Photosynthesis,useful,MAGIC_SPELL,Magic
10024,Spell: Descend,progression,MAGIC_SPELL,Magic
@@ -585,6 +755,9 @@ id,name,classification,groups,mod_name
10027,Spell: Lucksteal,useful,MAGIC_SPELL,Magic
10028,Spell: Spirit,progression,MAGIC_SPELL,Magic
10029,Spell: Rewind,useful,MAGIC_SPELL,Magic
+10030,Pendant of Community,progression,,DeepWoods
+10031,Pendant of Elders,progression,,DeepWoods
+10032,Pendant of Depths,progression,,DeepWoods
10101,Juna <3,progression,FRIENDSANITY,Juna - Roommate NPC
10102,Jasper <3,progression,FRIENDSANITY,Professor Jasper Thomas
10103,Alec <3,progression,FRIENDSANITY,Alec Revisited
@@ -596,5 +769,91 @@ id,name,classification,groups,mod_name
10109,Delores <3,progression,FRIENDSANITY,Delores - Custom NPC
10110,Ayeisha <3,progression,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
10111,Riley <3,progression,FRIENDSANITY,Custom NPC - Riley
+10112,Claire <3,progression,FRIENDSANITY,Stardew Valley Expanded
+10113,Lance <3,progression,"FRIENDSANITY,GINGER_ISLAND",Stardew Valley Expanded
+10114,Olivia <3,progression,FRIENDSANITY,Stardew Valley Expanded
+10115,Sophia <3,progression,FRIENDSANITY,Stardew Valley Expanded
+10116,Victor <3,progression,FRIENDSANITY,Stardew Valley Expanded
+10117,Andy <3,progression,FRIENDSANITY,Stardew Valley Expanded
+10118,Apples <3,progression,FRIENDSANITY,Stardew Valley Expanded
+10119,Gunther <3,progression,FRIENDSANITY,Stardew Valley Expanded
+10120,Martin <3,progression,FRIENDSANITY,Stardew Valley Expanded
+10121,Marlon <3,progression,FRIENDSANITY,Stardew Valley Expanded
+10122,Morgan <3,progression,FRIENDSANITY,Stardew Valley Expanded
+10123,Scarlett <3,progression,FRIENDSANITY,Stardew Valley Expanded
+10124,Susan <3,progression,FRIENDSANITY,Stardew Valley Expanded
+10125,Morris <3,progression,FRIENDSANITY,Stardew Valley Expanded
+10126,Alecto <3,progression,FRIENDSANITY,Alecto the Witch
+10127,Zic <3,progression,FRIENDSANITY,Distant Lands - Witch Swamp Overhaul
+10128,Lacey <3,progression,FRIENDSANITY,Hat Mouse Lacey
+10129,Gregory <3,progression,FRIENDSANITY,Boarding House and Bus Stop Extension
+10130,Sheila <3,progression,FRIENDSANITY,Boarding House and Bus Stop Extension
+10131,Joel <3,progression,FRIENDSANITY,Boarding House and Bus Stop Extension
10301,Progressive Woods Obelisk Sigils,progression,,DeepWoods
10302,Progressive Skull Cavern Elevator,progression,,Skull Cavern Elevator
+10401,Baked Berry Oatmeal Recipe,progression,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
+10402,Big Bark Burger Recipe,progression,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
+10403,Flower Cookie Recipe,progression,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
+10404,Frog Legs Recipe,progression,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
+10405,Glazed Butterfish Recipe,progression,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
+10406,Mixed Berry Pie Recipe,progression,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
+10407,Mushroom Berry Rice Recipe,progression,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
+10408,Seaweed Salad Recipe,progression,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
+10409,Void Delight Recipe,progression,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
+10410,Void Salmon Sushi Recipe,progression,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
+10411,Mushroom Kebab Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",Distant Lands - Witch Swamp Overhaul
+10412,Crayfish Soup Recipe,progression,,Distant Lands - Witch Swamp Overhaul
+10413,Pemmican Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",Distant Lands - Witch Swamp Overhaul
+10414,Void Mint Tea Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",Distant Lands - Witch Swamp Overhaul
+10415,Ginger Tincture Recipe,progression,GINGER_ISLAND,Distant Lands - Witch Swamp Overhaul
+10416,Special Pumpkin Soup Recipe,progression,,Boarding House and Bus Stop Extension
+10450,Void Mint Seeds,progression,DEPRECATED,Distant Lands - Witch Swamp Overhaul
+10451,Vile Ancient Fruit Seeds,progression,DEPRECATED,Distant Lands - Witch Swamp Overhaul
+10501,Marlon's Boat Paddle,progression,GINGER_ISLAND,Stardew Valley Expanded
+10502,Diamond Wand,filler,"WEAPON,DEPRECATED",Stardew Valley Expanded
+10503,Iridium Bomb,progression,,Stardew Valley Expanded
+10504,Void Spirit Peace Agreement,useful,GINGER_ISLAND,Stardew Valley Expanded
+10505,Kittyfish Spell,progression,,Stardew Valley Expanded
+10506,Nexus: Adventurer's Guild Runes,progression,MOD_WARP,Stardew Valley Expanded
+10507,Nexus: Junimo Woods Runes,progression,MOD_WARP,Stardew Valley Expanded
+10508,Nexus: Aurora Vineyard Runes,progression,MOD_WARP,Stardew Valley Expanded
+10509,Nexus: Sprite Spring Runes,progression,MOD_WARP,Stardew Valley Expanded
+10510,Nexus: Outpost Runes,progression,MOD_WARP,Stardew Valley Expanded
+10511,Nexus: Farm Runes,progression,MOD_WARP,Stardew Valley Expanded
+10512,Nexus: Wizard Runes,progression,MOD_WARP,Stardew Valley Expanded
+10513,Fable Reef Portal,progression,GINGER_ISLAND,Stardew Valley Expanded
+10514,Tempered Galaxy Sword,filler,"WEAPON,DEPRECATED",Stardew Valley Expanded
+10515,Tempered Galaxy Dagger,filler,"WEAPON,DEPRECATED",Stardew Valley Expanded
+10516,Tempered Galaxy Hammer,filler,"WEAPON,DEPRECATED",Stardew Valley Expanded
+10517,Grandpa's Shed,progression,,Stardew Valley Expanded
+10518,Aurora Vineyard Tablet,progression,,Stardew Valley Expanded
+10519,Scarlett's Job Offer,progression,,Stardew Valley Expanded
+10520,Morgan's Schooling,progression,,Stardew Valley Expanded
+10601,Magic Elixir Recipe,progression,"CHEFSANITY,CHEFSANITY_PURCHASE",Magic
+10602,Travel Core Recipe,progression,CRAFTSANITY,Magic
+10603,Haste Elixir Recipe,progression,CRAFTSANITY,Stardew Valley Expanded
+10604,Hero Elixir Recipe,progression,CRAFTSANITY,Stardew Valley Expanded
+10605,Armor Elixir Recipe,progression,CRAFTSANITY,Stardew Valley Expanded
+10606,Neanderthal Skeleton Recipe,progression,CRAFTSANITY,Boarding House and Bus Stop Extension
+10607,Pterodactyl Skeleton L Recipe,progression,CRAFTSANITY,Boarding House and Bus Stop Extension
+10608,Pterodactyl Skeleton M Recipe,progression,CRAFTSANITY,Boarding House and Bus Stop Extension
+10609,Pterodactyl Skeleton R Recipe,progression,CRAFTSANITY,Boarding House and Bus Stop Extension
+10610,T-Rex Skeleton L Recipe,progression,CRAFTSANITY,Boarding House and Bus Stop Extension
+10611,T-Rex Skeleton M Recipe,progression,CRAFTSANITY,Boarding House and Bus Stop Extension
+10612,T-Rex Skeleton R Recipe,progression,CRAFTSANITY,Boarding House and Bus Stop Extension
+10701,Resource Pack: 3 Magic Elixir,filler,RESOURCE_PACK,Magic
+10702,Resource Pack: 3 Travel Core,filler,RESOURCE_PACK,Magic
+10703,Preservation Chamber,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",Archaeology
+10704,Hardwood Preservation Chamber,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",Archaeology
+10705,Resource Pack: 3 Water Shifter,filler,RESOURCE_PACK,Archaeology
+10706,Resource Pack: 5 Hardwood Display,filler,RESOURCE_PACK,Archaeology
+10707,Resource Pack: 5 Wooden Display,filler,RESOURCE_PACK,Archaeology
+10708,Grinder,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",Archaeology
+10709,Ancient Battery Production Station,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",Archaeology
+10710,Hero Elixir,filler,RESOURCE_PACK,Starde Valley Expanded
+10711,Aegis Elixir,filler,RESOURCE_PACK,Stardew Valley Expanded
+10712,Haste Elixir,filler,RESOURCE_PACK,Stardew Valley Expanded
+10713,Lightning Elixir,filler,RESOURCE_PACK,Stardew Valley Expanded
+10714,Armor Elixir,filler,RESOURCE_PACK,Stardew Valley Expanded
+10715,Gravity Elixir,filler,RESOURCE_PACK,Stardew Valley Expanded
+10716,Barbarian Elixir,filler,RESOURCE_PACK,Stardew Valley Expanded
diff --git a/worlds/stardew_valley/data/locations.csv b/worlds/stardew_valley/data/locations.csv
index ef56bf5a12ba..68667ac5c4bf 100644
--- a/worlds/stardew_valley/data/locations.csv
+++ b/worlds/stardew_valley/data/locations.csv
@@ -1,1291 +1,2939 @@
-id,region,name,tags,mod_name
-1,Crafts Room,Spring Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE,MANDATORY",
-2,Crafts Room,Summer Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE,MANDATORY",
-3,Crafts Room,Fall Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE,MANDATORY",
-4,Crafts Room,Winter Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE,MANDATORY",
-5,Crafts Room,Construction Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE,MANDATORY",
-6,Crafts Room,Exotic Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE,MANDATORY",
-7,Pantry,Spring Crops Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY,PANTRY_BUNDLE",
-8,Pantry,Summer Crops Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY,PANTRY_BUNDLE",
-9,Pantry,Fall Crops Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY,PANTRY_BUNDLE",
-10,Pantry,Quality Crops Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY,PANTRY_BUNDLE",
-11,Pantry,Animal Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY,PANTRY_BUNDLE",
-12,Pantry,Artisan Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY,PANTRY_BUNDLE",
-13,Fish Tank,River Fish Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE,MANDATORY",
-14,Fish Tank,Lake Fish Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE,MANDATORY",
-15,Fish Tank,Ocean Fish Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE,MANDATORY",
-16,Fish Tank,Night Fishing Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE,MANDATORY",
-17,Fish Tank,Crab Pot Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE,MANDATORY",
-18,Fish Tank,Specialty Fish Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE,MANDATORY",
-19,Boiler Room,Blacksmith's Bundle,"BOILER_ROOM_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY",
-20,Boiler Room,Geologist's Bundle,"BOILER_ROOM_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY",
-21,Boiler Room,Adventurer's Bundle,"BOILER_ROOM_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY",
-22,Bulletin Board,Chef's Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY",
-23,Bulletin Board,Dye Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY",
-24,Bulletin Board,Field Research Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY",
-25,Bulletin Board,Fodder Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY",
-26,Bulletin Board,Enchanter's Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY",
-27,Vault,"2,500g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY,VAULT_BUNDLE",
-28,Vault,"5,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY,VAULT_BUNDLE",
-29,Vault,"10,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY,VAULT_BUNDLE",
-30,Vault,"25,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,MANDATORY,VAULT_BUNDLE",
-31,Abandoned JojaMart,The Missing Bundle,BUNDLE,
-32,Crafts Room,Complete Crafts Room,"COMMUNITY_CENTER_ROOM,MANDATORY",
-33,Pantry,Complete Pantry,"COMMUNITY_CENTER_ROOM,MANDATORY",
-34,Fish Tank,Complete Fish Tank,"COMMUNITY_CENTER_ROOM,MANDATORY",
-35,Boiler Room,Complete Boiler Room,"COMMUNITY_CENTER_ROOM,MANDATORY",
-36,Bulletin Board,Complete Bulletin Board,"COMMUNITY_CENTER_ROOM,MANDATORY",
-37,Vault,Complete Vault,"COMMUNITY_CENTER_ROOM,MANDATORY",
-101,Pierre's General Store,Large Pack,BACKPACK,
-102,Pierre's General Store,Deluxe Pack,BACKPACK,
-103,Clint's Blacksmith,Copper Hoe Upgrade,"HOE_UPGRADE,TOOL_UPGRADE",
-104,Clint's Blacksmith,Iron Hoe Upgrade,"HOE_UPGRADE,TOOL_UPGRADE",
-105,Clint's Blacksmith,Gold Hoe Upgrade,"HOE_UPGRADE,TOOL_UPGRADE",
-106,Clint's Blacksmith,Iridium Hoe Upgrade,"HOE_UPGRADE,TOOL_UPGRADE",
-107,Clint's Blacksmith,Copper Pickaxe Upgrade,"PICKAXE_UPGRADE,TOOL_UPGRADE",
-108,Clint's Blacksmith,Iron Pickaxe Upgrade,"PICKAXE_UPGRADE,TOOL_UPGRADE",
-109,Clint's Blacksmith,Gold Pickaxe Upgrade,"PICKAXE_UPGRADE,TOOL_UPGRADE",
-110,Clint's Blacksmith,Iridium Pickaxe Upgrade,"PICKAXE_UPGRADE,TOOL_UPGRADE",
-111,Clint's Blacksmith,Copper Axe Upgrade,"AXE_UPGRADE,TOOL_UPGRADE",
-112,Clint's Blacksmith,Iron Axe Upgrade,"AXE_UPGRADE,TOOL_UPGRADE",
-113,Clint's Blacksmith,Gold Axe Upgrade,"AXE_UPGRADE,TOOL_UPGRADE",
-114,Clint's Blacksmith,Iridium Axe Upgrade,"AXE_UPGRADE,TOOL_UPGRADE",
-115,Clint's Blacksmith,Copper Watering Can Upgrade,"TOOL_UPGRADE,WATERING_CAN_UPGRADE",
-116,Clint's Blacksmith,Iron Watering Can Upgrade,"TOOL_UPGRADE,WATERING_CAN_UPGRADE",
-117,Clint's Blacksmith,Gold Watering Can Upgrade,"TOOL_UPGRADE,WATERING_CAN_UPGRADE",
-118,Clint's Blacksmith,Iridium Watering Can Upgrade,"TOOL_UPGRADE,WATERING_CAN_UPGRADE",
-119,Clint's Blacksmith,Copper Trash Can Upgrade,"TOOL_UPGRADE,TRASH_CAN_UPGRADE",
-120,Clint's Blacksmith,Iron Trash Can Upgrade,"TOOL_UPGRADE,TRASH_CAN_UPGRADE",
-121,Clint's Blacksmith,Gold Trash Can Upgrade,"TOOL_UPGRADE,TRASH_CAN_UPGRADE",
-122,Clint's Blacksmith,Iridium Trash Can Upgrade,"TOOL_UPGRADE,TRASH_CAN_UPGRADE",
-123,Willy's Fish Shop,Purchase Training Rod,"FISHING_ROD_UPGRADE,TOOL_UPGRADE",
-124,Beach,Bamboo Pole Cutscene,"FISHING_ROD_UPGRADE,TOOL_UPGRADE",
-125,Willy's Fish Shop,Purchase Fiberglass Rod,"FISHING_ROD_UPGRADE,TOOL_UPGRADE",
-126,Willy's Fish Shop,Purchase Iridium Rod,"FISHING_ROD_UPGRADE,TOOL_UPGRADE",
-201,The Mines - Floor 10,The Mines Floor 10 Treasure,"MANDATORY,THE_MINES_TREASURE",
-202,The Mines - Floor 20,The Mines Floor 20 Treasure,"MANDATORY,THE_MINES_TREASURE",
-203,The Mines - Floor 40,The Mines Floor 40 Treasure,"MANDATORY,THE_MINES_TREASURE",
-204,The Mines - Floor 50,The Mines Floor 50 Treasure,"MANDATORY,THE_MINES_TREASURE",
-205,The Mines - Floor 60,The Mines Floor 60 Treasure,"MANDATORY,THE_MINES_TREASURE",
-206,The Mines - Floor 70,The Mines Floor 70 Treasure,"MANDATORY,THE_MINES_TREASURE",
-207,The Mines - Floor 80,The Mines Floor 80 Treasure,"MANDATORY,THE_MINES_TREASURE",
-208,The Mines - Floor 90,The Mines Floor 90 Treasure,"MANDATORY,THE_MINES_TREASURE",
-209,The Mines - Floor 100,The Mines Floor 100 Treasure,"MANDATORY,THE_MINES_TREASURE",
-210,The Mines - Floor 110,The Mines Floor 110 Treasure,"MANDATORY,THE_MINES_TREASURE",
-211,The Mines - Floor 120,The Mines Floor 120 Treasure,"MANDATORY,THE_MINES_TREASURE",
-212,Quarry Mine,Grim Reaper statue,MANDATORY,
-213,The Mines,The Mines Entrance Cutscene,MANDATORY,
-214,The Mines - Floor 5,Floor 5 Elevator,ELEVATOR,
-215,The Mines - Floor 10,Floor 10 Elevator,ELEVATOR,
-216,The Mines - Floor 15,Floor 15 Elevator,ELEVATOR,
-217,The Mines - Floor 20,Floor 20 Elevator,ELEVATOR,
-218,The Mines - Floor 25,Floor 25 Elevator,ELEVATOR,
-219,The Mines - Floor 30,Floor 30 Elevator,ELEVATOR,
-220,The Mines - Floor 35,Floor 35 Elevator,ELEVATOR,
-221,The Mines - Floor 40,Floor 40 Elevator,ELEVATOR,
-222,The Mines - Floor 45,Floor 45 Elevator,ELEVATOR,
-223,The Mines - Floor 50,Floor 50 Elevator,ELEVATOR,
-224,The Mines - Floor 55,Floor 55 Elevator,ELEVATOR,
-225,The Mines - Floor 60,Floor 60 Elevator,ELEVATOR,
-226,The Mines - Floor 65,Floor 65 Elevator,ELEVATOR,
-227,The Mines - Floor 70,Floor 70 Elevator,ELEVATOR,
-228,The Mines - Floor 75,Floor 75 Elevator,ELEVATOR,
-229,The Mines - Floor 80,Floor 80 Elevator,ELEVATOR,
-230,The Mines - Floor 85,Floor 85 Elevator,ELEVATOR,
-231,The Mines - Floor 90,Floor 90 Elevator,ELEVATOR,
-232,The Mines - Floor 95,Floor 95 Elevator,ELEVATOR,
-233,The Mines - Floor 100,Floor 100 Elevator,ELEVATOR,
-234,The Mines - Floor 105,Floor 105 Elevator,ELEVATOR,
-235,The Mines - Floor 110,Floor 110 Elevator,ELEVATOR,
-236,The Mines - Floor 115,Floor 115 Elevator,ELEVATOR,
-237,The Mines - Floor 120,Floor 120 Elevator,ELEVATOR,
-251,Volcano - Floor 10,Volcano Caldera Treasure,"MANDATORY,GINGER_ISLAND",
-301,Stardew Valley,Level 1 Farming,"FARMING_LEVEL,SKILL_LEVEL",
-302,Stardew Valley,Level 2 Farming,"FARMING_LEVEL,SKILL_LEVEL",
-303,Stardew Valley,Level 3 Farming,"FARMING_LEVEL,SKILL_LEVEL",
-304,Stardew Valley,Level 4 Farming,"FARMING_LEVEL,SKILL_LEVEL",
-305,Stardew Valley,Level 5 Farming,"FARMING_LEVEL,SKILL_LEVEL",
-306,Stardew Valley,Level 6 Farming,"FARMING_LEVEL,SKILL_LEVEL",
-307,Stardew Valley,Level 7 Farming,"FARMING_LEVEL,SKILL_LEVEL",
-308,Stardew Valley,Level 8 Farming,"FARMING_LEVEL,SKILL_LEVEL",
-309,Stardew Valley,Level 9 Farming,"FARMING_LEVEL,SKILL_LEVEL",
-310,Stardew Valley,Level 10 Farming,"FARMING_LEVEL,SKILL_LEVEL",
-311,Stardew Valley,Level 1 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
-312,Stardew Valley,Level 2 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
-313,Stardew Valley,Level 3 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
-314,Stardew Valley,Level 4 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
-315,Stardew Valley,Level 5 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
-316,Stardew Valley,Level 6 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
-317,Stardew Valley,Level 7 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
-318,Stardew Valley,Level 8 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
-319,Stardew Valley,Level 9 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
-320,Stardew Valley,Level 10 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
-321,Stardew Valley,Level 1 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
-322,Stardew Valley,Level 2 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
-323,Stardew Valley,Level 3 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
-324,Stardew Valley,Level 4 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
-325,Stardew Valley,Level 5 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
-326,Stardew Valley,Level 6 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
-327,Stardew Valley,Level 7 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
-328,Stardew Valley,Level 8 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
-329,Stardew Valley,Level 9 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
-330,Stardew Valley,Level 10 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
-331,Stardew Valley,Level 1 Mining,"MINING_LEVEL,SKILL_LEVEL",
-332,Stardew Valley,Level 2 Mining,"MINING_LEVEL,SKILL_LEVEL",
-333,Stardew Valley,Level 3 Mining,"MINING_LEVEL,SKILL_LEVEL",
-334,Stardew Valley,Level 4 Mining,"MINING_LEVEL,SKILL_LEVEL",
-335,Stardew Valley,Level 5 Mining,"MINING_LEVEL,SKILL_LEVEL",
-336,Stardew Valley,Level 6 Mining,"MINING_LEVEL,SKILL_LEVEL",
-337,Stardew Valley,Level 7 Mining,"MINING_LEVEL,SKILL_LEVEL",
-338,Stardew Valley,Level 8 Mining,"MINING_LEVEL,SKILL_LEVEL",
-339,Stardew Valley,Level 9 Mining,"MINING_LEVEL,SKILL_LEVEL",
-340,Stardew Valley,Level 10 Mining,"MINING_LEVEL,SKILL_LEVEL",
-341,Stardew Valley,Level 1 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
-342,Stardew Valley,Level 2 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
-343,Stardew Valley,Level 3 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
-344,Stardew Valley,Level 4 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
-345,Stardew Valley,Level 5 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
-346,Stardew Valley,Level 6 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
-347,Stardew Valley,Level 7 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
-348,Stardew Valley,Level 8 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
-349,Stardew Valley,Level 9 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
-350,Stardew Valley,Level 10 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
-401,Carpenter Shop,Coop Blueprint,BUILDING_BLUEPRINT,
-402,Carpenter Shop,Big Coop Blueprint,BUILDING_BLUEPRINT,
-403,Carpenter Shop,Deluxe Coop Blueprint,BUILDING_BLUEPRINT,
-404,Carpenter Shop,Barn Blueprint,BUILDING_BLUEPRINT,
-405,Carpenter Shop,Big Barn Blueprint,BUILDING_BLUEPRINT,
-406,Carpenter Shop,Deluxe Barn Blueprint,BUILDING_BLUEPRINT,
-407,Carpenter Shop,Well Blueprint,BUILDING_BLUEPRINT,
-408,Carpenter Shop,Silo Blueprint,BUILDING_BLUEPRINT,
-409,Carpenter Shop,Mill Blueprint,BUILDING_BLUEPRINT,
-410,Carpenter Shop,Shed Blueprint,BUILDING_BLUEPRINT,
-411,Carpenter Shop,Big Shed Blueprint,BUILDING_BLUEPRINT,
-412,Carpenter Shop,Fish Pond Blueprint,BUILDING_BLUEPRINT,
-413,Carpenter Shop,Stable Blueprint,BUILDING_BLUEPRINT,
-414,Carpenter Shop,Slime Hutch Blueprint,BUILDING_BLUEPRINT,
-415,Carpenter Shop,Shipping Bin Blueprint,BUILDING_BLUEPRINT,
-416,Carpenter Shop,Kitchen Blueprint,BUILDING_BLUEPRINT,
-417,Carpenter Shop,Kids Room Blueprint,BUILDING_BLUEPRINT,
-418,Carpenter Shop,Cellar Blueprint,BUILDING_BLUEPRINT,
-501,Town,Introductions,"MANDATORY,QUEST",
-502,Town,How To Win Friends,"MANDATORY,QUEST",
-503,Farm,Getting Started,"MANDATORY,QUEST",
-504,Farm,Raising Animals,"MANDATORY,QUEST",
-505,Farm,Advancement,"MANDATORY,QUEST",
-506,Museum,Archaeology,"MANDATORY,QUEST",
-507,Wizard Tower,Meet The Wizard,"MANDATORY,QUEST",
-508,Farm,Forging Ahead,"MANDATORY,QUEST",
-509,Farm,Smelting,"MANDATORY,QUEST",
-510,The Mines - Floor 5,Initiation,"MANDATORY,QUEST",
-511,Forest,Robin's Lost Axe,"MANDATORY,QUEST",
-512,Sam's House,Jodi's Request,"MANDATORY,QUEST",
-513,Marnie's Ranch,"Mayor's ""Shorts""","MANDATORY,QUEST",
-514,Tunnel Entrance,Blackberry Basket,"MANDATORY,QUEST",
-515,Marnie's Ranch,Marnie's Request,"MANDATORY,QUEST",
-516,Town,Pam Is Thirsty,"MANDATORY,QUEST",
-517,Wizard Tower,A Dark Reagent,"MANDATORY,QUEST",
-518,Marnie's Ranch,Cow's Delight,"MANDATORY,QUEST",
-519,Skull Cavern Entrance,The Skull Key,"MANDATORY,QUEST",
-520,Town,Crop Research,"MANDATORY,QUEST",
-521,Town,Knee Therapy,"MANDATORY,QUEST",
-522,Town,Robin's Request,"MANDATORY,QUEST",
-523,Skull Cavern,Qi's Challenge,"MANDATORY,QUEST",
-524,Desert,The Mysterious Qi,"MANDATORY,QUEST",
-525,Town,Carving Pumpkins,"MANDATORY,QUEST",
-526,Town,A Winter Mystery,"MANDATORY,QUEST",
-527,Secret Woods,Strange Note,"MANDATORY,QUEST",
-528,Skull Cavern,Cryptic Note,"MANDATORY,QUEST",
-529,Town,Fresh Fruit,"MANDATORY,QUEST",
-530,Town,Aquatic Research,"MANDATORY,QUEST",
-531,Town,A Soldier's Star,"MANDATORY,QUEST",
-532,Town,Mayor's Need,"MANDATORY,QUEST",
-533,Saloon,Wanted: Lobster,"MANDATORY,QUEST",
-534,Town,Pam Needs Juice,"MANDATORY,QUEST",
-535,Sam's House,Fish Casserole,"MANDATORY,QUEST",
-536,Beach,Catch A Squid,"MANDATORY,QUEST",
-537,Saloon,Fish Stew,"MANDATORY,QUEST",
-538,Town,Pierre's Notice,"MANDATORY,QUEST",
-539,Town,Clint's Attempt,"MANDATORY,QUEST",
-540,Town,A Favor For Clint,"MANDATORY,QUEST",
-541,Wizard Tower,Staff Of Power,"MANDATORY,QUEST",
-542,Town,Granny's Gift,"MANDATORY,QUEST",
-543,Saloon,Exotic Spirits,"MANDATORY,QUEST",
-544,Town,Catch a Lingcod,"MANDATORY,QUEST",
-545,Island West,The Pirate's Wife,"GINGER_ISLAND,MANDATORY,QUEST",
-546,Railroad,Dark Talisman,"MANDATORY,QUEST",
-547,Witch's Swamp,Goblin Problem,"MANDATORY,QUEST",
-548,Witch's Hut,Magic Ink,"MANDATORY,QUEST",
-601,JotPK World 1,JotPK: Boots 1,"ARCADE_MACHINE,JOTPK",
-602,JotPK World 1,JotPK: Boots 2,"ARCADE_MACHINE,JOTPK",
-603,JotPK World 1,JotPK: Gun 1,"ARCADE_MACHINE,JOTPK",
-604,JotPK World 2,JotPK: Gun 2,"ARCADE_MACHINE,JOTPK",
-605,JotPK World 2,JotPK: Gun 3,"ARCADE_MACHINE,JOTPK",
-606,JotPK World 3,JotPK: Super Gun,"ARCADE_MACHINE,JOTPK",
-607,JotPK World 1,JotPK: Ammo 1,"ARCADE_MACHINE,JOTPK",
-608,JotPK World 2,JotPK: Ammo 2,"ARCADE_MACHINE,JOTPK",
-609,JotPK World 3,JotPK: Ammo 3,"ARCADE_MACHINE,JOTPK",
-610,JotPK World 1,JotPK: Cowboy 1,"ARCADE_MACHINE,JOTPK",
-611,JotPK World 2,JotPK: Cowboy 2,"ARCADE_MACHINE,JOTPK",
-612,Junimo Kart 1,Junimo Kart: Crumble Cavern,"ARCADE_MACHINE,JUNIMO_KART",
-613,Junimo Kart 1,Junimo Kart: Slippery Slopes,"ARCADE_MACHINE,JUNIMO_KART",
-614,Junimo Kart 2,Junimo Kart: Secret Level,"ARCADE_MACHINE,JUNIMO_KART",
-615,Junimo Kart 2,Junimo Kart: The Gem Sea Giant,"ARCADE_MACHINE,JUNIMO_KART",
-616,Junimo Kart 2,Junimo Kart: Slomp's Stomp,"ARCADE_MACHINE,JUNIMO_KART",
-617,Junimo Kart 2,Junimo Kart: Ghastly Galleon,"ARCADE_MACHINE,JUNIMO_KART",
-618,Junimo Kart 3,Junimo Kart: Glowshroom Grotto,"ARCADE_MACHINE,JUNIMO_KART",
-619,Junimo Kart 3,Junimo Kart: Red Hot Rollercoaster,"ARCADE_MACHINE,JUNIMO_KART",
-620,JotPK World 3,Journey of the Prairie King Victory,"ARCADE_MACHINE_VICTORY,JOTPK",
-621,Junimo Kart 3,Junimo Kart: Sunset Speedway (Victory),"ARCADE_MACHINE_VICTORY,JUNIMO_KART",
-701,Secret Woods,Old Master Cannoli,MANDATORY,
-702,Beach,Beach Bridge Repair,MANDATORY,
-703,Desert,Galaxy Sword Shrine,MANDATORY,
-704,Farmhouse,Have a Baby,MANDATORY,
-705,Farmhouse,Have Another Baby,MANDATORY,
-801,Town,Help Wanted: Gathering 1,HELP_WANTED,
-802,Town,Help Wanted: Gathering 2,HELP_WANTED,
-803,Town,Help Wanted: Gathering 3,HELP_WANTED,
-804,Town,Help Wanted: Gathering 4,HELP_WANTED,
-805,Town,Help Wanted: Gathering 5,HELP_WANTED,
-806,Town,Help Wanted: Gathering 6,HELP_WANTED,
-807,Town,Help Wanted: Gathering 7,HELP_WANTED,
-808,Town,Help Wanted: Gathering 8,HELP_WANTED,
-811,Town,Help Wanted: Slay Monsters 1,HELP_WANTED,
-812,Town,Help Wanted: Slay Monsters 2,HELP_WANTED,
-813,Town,Help Wanted: Slay Monsters 3,HELP_WANTED,
-814,Town,Help Wanted: Slay Monsters 4,HELP_WANTED,
-815,Town,Help Wanted: Slay Monsters 5,HELP_WANTED,
-816,Town,Help Wanted: Slay Monsters 6,HELP_WANTED,
-817,Town,Help Wanted: Slay Monsters 7,HELP_WANTED,
-818,Town,Help Wanted: Slay Monsters 8,HELP_WANTED,
-821,Town,Help Wanted: Fishing 1,HELP_WANTED,
-822,Town,Help Wanted: Fishing 2,HELP_WANTED,
-823,Town,Help Wanted: Fishing 3,HELP_WANTED,
-824,Town,Help Wanted: Fishing 4,HELP_WANTED,
-825,Town,Help Wanted: Fishing 5,HELP_WANTED,
-826,Town,Help Wanted: Fishing 6,HELP_WANTED,
-827,Town,Help Wanted: Fishing 7,HELP_WANTED,
-828,Town,Help Wanted: Fishing 8,HELP_WANTED,
-841,Town,Help Wanted: Item Delivery 1,HELP_WANTED,
-842,Town,Help Wanted: Item Delivery 2,HELP_WANTED,
-843,Town,Help Wanted: Item Delivery 3,HELP_WANTED,
-844,Town,Help Wanted: Item Delivery 4,HELP_WANTED,
-845,Town,Help Wanted: Item Delivery 5,HELP_WANTED,
-846,Town,Help Wanted: Item Delivery 6,HELP_WANTED,
-847,Town,Help Wanted: Item Delivery 7,HELP_WANTED,
-848,Town,Help Wanted: Item Delivery 8,HELP_WANTED,
-849,Town,Help Wanted: Item Delivery 9,HELP_WANTED,
-850,Town,Help Wanted: Item Delivery 10,HELP_WANTED,
-851,Town,Help Wanted: Item Delivery 11,HELP_WANTED,
-852,Town,Help Wanted: Item Delivery 12,HELP_WANTED,
-853,Town,Help Wanted: Item Delivery 13,HELP_WANTED,
-854,Town,Help Wanted: Item Delivery 14,HELP_WANTED,
-855,Town,Help Wanted: Item Delivery 15,HELP_WANTED,
-856,Town,Help Wanted: Item Delivery 16,HELP_WANTED,
-857,Town,Help Wanted: Item Delivery 17,HELP_WANTED,
-858,Town,Help Wanted: Item Delivery 18,HELP_WANTED,
-859,Town,Help Wanted: Item Delivery 19,HELP_WANTED,
-860,Town,Help Wanted: Item Delivery 20,HELP_WANTED,
-861,Town,Help Wanted: Item Delivery 21,HELP_WANTED,
-862,Town,Help Wanted: Item Delivery 22,HELP_WANTED,
-863,Town,Help Wanted: Item Delivery 23,HELP_WANTED,
-864,Town,Help Wanted: Item Delivery 24,HELP_WANTED,
-865,Town,Help Wanted: Item Delivery 25,HELP_WANTED,
-866,Town,Help Wanted: Item Delivery 26,HELP_WANTED,
-867,Town,Help Wanted: Item Delivery 27,HELP_WANTED,
-868,Town,Help Wanted: Item Delivery 28,HELP_WANTED,
-869,Town,Help Wanted: Item Delivery 29,HELP_WANTED,
-870,Town,Help Wanted: Item Delivery 30,HELP_WANTED,
-871,Town,Help Wanted: Item Delivery 31,HELP_WANTED,
-872,Town,Help Wanted: Item Delivery 32,HELP_WANTED,
-901,Forest,Traveling Merchant Sunday Item 1,"MANDATORY,TRAVELING_MERCHANT",
-902,Forest,Traveling Merchant Sunday Item 2,"MANDATORY,TRAVELING_MERCHANT",
-903,Forest,Traveling Merchant Sunday Item 3,"MANDATORY,TRAVELING_MERCHANT",
-911,Forest,Traveling Merchant Monday Item 1,"MANDATORY,TRAVELING_MERCHANT",
-912,Forest,Traveling Merchant Monday Item 2,"MANDATORY,TRAVELING_MERCHANT",
-913,Forest,Traveling Merchant Monday Item 3,"MANDATORY,TRAVELING_MERCHANT",
-921,Forest,Traveling Merchant Tuesday Item 1,"MANDATORY,TRAVELING_MERCHANT",
-922,Forest,Traveling Merchant Tuesday Item 2,"MANDATORY,TRAVELING_MERCHANT",
-923,Forest,Traveling Merchant Tuesday Item 3,"MANDATORY,TRAVELING_MERCHANT",
-931,Forest,Traveling Merchant Wednesday Item 1,"MANDATORY,TRAVELING_MERCHANT",
-932,Forest,Traveling Merchant Wednesday Item 2,"MANDATORY,TRAVELING_MERCHANT",
-933,Forest,Traveling Merchant Wednesday Item 3,"MANDATORY,TRAVELING_MERCHANT",
-941,Forest,Traveling Merchant Thursday Item 1,"MANDATORY,TRAVELING_MERCHANT",
-942,Forest,Traveling Merchant Thursday Item 2,"MANDATORY,TRAVELING_MERCHANT",
-943,Forest,Traveling Merchant Thursday Item 3,"MANDATORY,TRAVELING_MERCHANT",
-951,Forest,Traveling Merchant Friday Item 1,"MANDATORY,TRAVELING_MERCHANT",
-952,Forest,Traveling Merchant Friday Item 2,"MANDATORY,TRAVELING_MERCHANT",
-953,Forest,Traveling Merchant Friday Item 3,"MANDATORY,TRAVELING_MERCHANT",
-961,Forest,Traveling Merchant Saturday Item 1,"MANDATORY,TRAVELING_MERCHANT",
-962,Forest,Traveling Merchant Saturday Item 2,"MANDATORY,TRAVELING_MERCHANT",
-963,Forest,Traveling Merchant Saturday Item 3,"MANDATORY,TRAVELING_MERCHANT",
-1001,Mountain,Fishsanity: Carp,FISHSANITY,
-1002,Beach,Fishsanity: Herring,FISHSANITY,
-1003,Forest,Fishsanity: Smallmouth Bass,FISHSANITY,
-1004,Beach,Fishsanity: Anchovy,FISHSANITY,
-1005,Beach,Fishsanity: Sardine,FISHSANITY,
-1006,Forest,Fishsanity: Sunfish,FISHSANITY,
-1007,Forest,Fishsanity: Perch,FISHSANITY,
-1008,Forest,Fishsanity: Chub,FISHSANITY,
-1009,Forest,Fishsanity: Bream,FISHSANITY,
-1010,Beach,Fishsanity: Red Snapper,FISHSANITY,
-1011,Beach,Fishsanity: Sea Cucumber,FISHSANITY,
-1012,Forest,Fishsanity: Rainbow Trout,FISHSANITY,
-1013,Forest,Fishsanity: Walleye,FISHSANITY,
-1014,Forest,Fishsanity: Shad,FISHSANITY,
-1015,Mountain,Fishsanity: Bullhead,FISHSANITY,
-1016,Mountain,Fishsanity: Largemouth Bass,FISHSANITY,
-1017,Forest,Fishsanity: Salmon,FISHSANITY,
-1018,The Mines - Floor 20,Fishsanity: Ghostfish,FISHSANITY,
-1019,Beach,Fishsanity: Tilapia,FISHSANITY,
-1020,Secret Woods,Fishsanity: Woodskip,FISHSANITY,
-1021,Beach,Fishsanity: Flounder,FISHSANITY,
-1022,Beach,Fishsanity: Halibut,FISHSANITY,
-1023,Island West,Fishsanity: Lionfish,"FISHSANITY,GINGER_ISLAND",
-1024,Mutant Bug Lair,Fishsanity: Slimejack,FISHSANITY,
-1025,Forest,Fishsanity: Midnight Carp,FISHSANITY,
-1026,Beach,Fishsanity: Red Mullet,FISHSANITY,
-1027,Forest,Fishsanity: Pike,FISHSANITY,
-1028,Forest,Fishsanity: Tiger Trout,FISHSANITY,
-1029,Island West,Fishsanity: Blue Discus,"FISHSANITY,GINGER_ISLAND",
-1030,Beach,Fishsanity: Albacore,FISHSANITY,
-1031,Desert,Fishsanity: Sandfish,FISHSANITY,
-1032,The Mines - Floor 20,Fishsanity: Stonefish,FISHSANITY,
-1033,Beach,Fishsanity: Tuna,FISHSANITY,
-1034,Beach,Fishsanity: Eel,FISHSANITY,
-1035,Forest,Fishsanity: Catfish,FISHSANITY,
-1036,Beach,Fishsanity: Squid,FISHSANITY,
-1037,Mountain,Fishsanity: Sturgeon,FISHSANITY,
-1038,Forest,Fishsanity: Dorado,FISHSANITY,
-1039,Beach,Fishsanity: Pufferfish,FISHSANITY,
-1040,Witch's Swamp,Fishsanity: Void Salmon,FISHSANITY,
-1041,Beach,Fishsanity: Super Cucumber,FISHSANITY,
-1042,Pirate Cove,Fishsanity: Stingray,"FISHSANITY,GINGER_ISLAND",
-1043,The Mines - Floor 60,Fishsanity: Ice Pip,FISHSANITY,
-1044,Forest,Fishsanity: Lingcod,FISHSANITY,
-1045,Desert,Fishsanity: Scorpion Carp,FISHSANITY,
-1046,The Mines - Floor 100,Fishsanity: Lava Eel,FISHSANITY,
-1047,Beach,Fishsanity: Octopus,FISHSANITY,
-1048,Beach,Fishsanity: Midnight Squid,FISHSANITY,
-1049,Beach,Fishsanity: Spook Fish,FISHSANITY,
-1050,Beach,Fishsanity: Blobfish,FISHSANITY,
-1051,Beach,Fishsanity: Crimsonfish,FISHSANITY,
-1052,Town,Fishsanity: Angler,FISHSANITY,
-1053,Mountain,Fishsanity: Legend,FISHSANITY,
-1054,Forest,Fishsanity: Glacierfish,FISHSANITY,
-1055,Sewer,Fishsanity: Mutant Carp,FISHSANITY,
-1056,Town,Fishsanity: Crayfish,FISHSANITY,
-1057,Town,Fishsanity: Snail,FISHSANITY,
-1058,Town,Fishsanity: Periwinkle,FISHSANITY,
-1059,Beach,Fishsanity: Lobster,FISHSANITY,
-1060,Beach,Fishsanity: Clam,FISHSANITY,
-1061,Beach,Fishsanity: Crab,FISHSANITY,
-1062,Beach,Fishsanity: Cockle,FISHSANITY,
-1063,Beach,Fishsanity: Mussel,FISHSANITY,
-1064,Beach,Fishsanity: Shrimp,FISHSANITY,
-1065,Beach,Fishsanity: Oyster,FISHSANITY,
-1100,Stardew Valley,Museumsanity: 5 Donations,MUSEUM_MILESTONES,
-1101,Stardew Valley,Museumsanity: 10 Donations,MUSEUM_MILESTONES,
-1102,Stardew Valley,Museumsanity: 15 Donations,MUSEUM_MILESTONES,
-1103,Stardew Valley,Museumsanity: 20 Donations,MUSEUM_MILESTONES,
-1104,Stardew Valley,Museumsanity: 25 Donations,MUSEUM_MILESTONES,
-1105,Stardew Valley,Museumsanity: 30 Donations,MUSEUM_MILESTONES,
-1106,Stardew Valley,Museumsanity: 35 Donations,MUSEUM_MILESTONES,
-1107,Stardew Valley,Museumsanity: 40 Donations,MUSEUM_MILESTONES,
-1108,Stardew Valley,Museumsanity: 50 Donations,MUSEUM_MILESTONES,
-1109,Stardew Valley,Museumsanity: 60 Donations,MUSEUM_MILESTONES,
-1110,Stardew Valley,Museumsanity: 70 Donations,MUSEUM_MILESTONES,
-1111,Stardew Valley,Museumsanity: 80 Donations,MUSEUM_MILESTONES,
-1112,Stardew Valley,Museumsanity: 90 Donations,MUSEUM_MILESTONES,
-1113,Stardew Valley,Museumsanity: 95 Donations,MUSEUM_MILESTONES,
-1114,Stardew Valley,Museumsanity: 11 Minerals,MUSEUM_MILESTONES,
-1115,Stardew Valley,Museumsanity: 21 Minerals,MUSEUM_MILESTONES,
-1116,Stardew Valley,Museumsanity: 31 Minerals,MUSEUM_MILESTONES,
-1117,Stardew Valley,Museumsanity: 41 Minerals,MUSEUM_MILESTONES,
-1118,Stardew Valley,Museumsanity: 50 Minerals,MUSEUM_MILESTONES,
-1119,Stardew Valley,Museumsanity: 3 Artifacts,MUSEUM_MILESTONES,
-1120,Stardew Valley,Museumsanity: 6 Artifacts,MUSEUM_MILESTONES,
-1121,Stardew Valley,Museumsanity: 9 Artifacts,MUSEUM_MILESTONES,
-1122,Stardew Valley,Museumsanity: 11 Artifacts,MUSEUM_MILESTONES,
-1123,Stardew Valley,Museumsanity: 15 Artifacts,MUSEUM_MILESTONES,
-1124,Stardew Valley,Museumsanity: 20 Artifacts,MUSEUM_MILESTONES,
-1125,Stardew Valley,Museumsanity: Dwarf Scrolls,MUSEUM_MILESTONES,
-1126,Stardew Valley,Museumsanity: Skeleton Front,MUSEUM_MILESTONES,
-1127,Stardew Valley,Museumsanity: Skeleton Middle,MUSEUM_MILESTONES,
-1128,Stardew Valley,Museumsanity: Skeleton Back,MUSEUM_MILESTONES,
-1201,The Mines - Floor 20,Museumsanity: Dwarf Scroll I,MUSEUM_DONATIONS,
-1202,The Mines - Floor 20,Museumsanity: Dwarf Scroll II,MUSEUM_DONATIONS,
-1203,The Mines - Floor 60,Museumsanity: Dwarf Scroll III,MUSEUM_DONATIONS,
-1204,The Mines - Floor 100,Museumsanity: Dwarf Scroll IV,MUSEUM_DONATIONS,
-1205,Town,Museumsanity: Chipped Amphora,MUSEUM_DONATIONS,
-1206,Forest,Museumsanity: Arrowhead,MUSEUM_DONATIONS,
-1207,Forest,Museumsanity: Ancient Doll,MUSEUM_DONATIONS,
-1208,Forest,Museumsanity: Elvish Jewelry,MUSEUM_DONATIONS,
-1209,Forest,Museumsanity: Chewing Stick,MUSEUM_DONATIONS,
-1210,Forest,Museumsanity: Ornamental Fan,MUSEUM_DONATIONS,
-1211,Mountain,Museumsanity: Dinosaur Egg,MUSEUM_DONATIONS,
-1212,Stardew Valley,Museumsanity: Rare Disc,MUSEUM_DONATIONS,
-1213,Forest,Museumsanity: Ancient Sword,MUSEUM_DONATIONS,
-1214,Town,Museumsanity: Rusty Spoon,MUSEUM_DONATIONS,
-1215,Farm,Museumsanity: Rusty Spur,MUSEUM_DONATIONS,
-1216,Mountain,Museumsanity: Rusty Cog,MUSEUM_DONATIONS,
-1217,Farm,Museumsanity: Chicken Statue,MUSEUM_DONATIONS,
-1218,Forest,Museumsanity: Ancient Seed,"MUSEUM_DONATIONS,MUSEUM_MILESTONES",
-1219,Forest,Museumsanity: Prehistoric Tool,MUSEUM_DONATIONS,
-1220,Beach,Museumsanity: Dried Starfish,MUSEUM_DONATIONS,
-1221,Beach,Museumsanity: Anchor,MUSEUM_DONATIONS,
-1222,Beach,Museumsanity: Glass Shards,MUSEUM_DONATIONS,
-1223,Forest,Museumsanity: Bone Flute,MUSEUM_DONATIONS,
-1224,Forest,Museumsanity: Prehistoric Handaxe,MUSEUM_DONATIONS,
-1225,The Mines - Floor 20,Museumsanity: Dwarvish Helm,MUSEUM_DONATIONS,
-1226,The Mines - Floor 60,Museumsanity: Dwarf Gadget,MUSEUM_DONATIONS,
-1227,Forest,Museumsanity: Ancient Drum,MUSEUM_DONATIONS,
-1228,Desert,Museumsanity: Golden Mask,MUSEUM_DONATIONS,
-1229,Desert,Museumsanity: Golden Relic,MUSEUM_DONATIONS,
-1230,Town,Museumsanity: Strange Doll (Green),MUSEUM_DONATIONS,
-1231,Desert,Museumsanity: Strange Doll,MUSEUM_DONATIONS,
-1232,Forest,Museumsanity: Prehistoric Scapula,MUSEUM_DONATIONS,
-1233,Forest,Museumsanity: Prehistoric Tibia,MUSEUM_DONATIONS,
-1234,Dig Site,Museumsanity: Prehistoric Skull,MUSEUM_DONATIONS,
-1235,Dig Site,Museumsanity: Skeletal Hand,MUSEUM_DONATIONS,
-1236,Dig Site,Museumsanity: Prehistoric Rib,MUSEUM_DONATIONS,
-1237,Dig Site,Museumsanity: Prehistoric Vertebra,MUSEUM_DONATIONS,
-1238,Dig Site,Museumsanity: Skeletal Tail,MUSEUM_DONATIONS,
-1239,Dig Site,Museumsanity: Nautilus Fossil,MUSEUM_DONATIONS,
-1240,Forest,Museumsanity: Amphibian Fossil,MUSEUM_DONATIONS,
-1241,Forest,Museumsanity: Palm Fossil,MUSEUM_DONATIONS,
-1242,Forest,Museumsanity: Trilobite,MUSEUM_DONATIONS,
-1243,The Mines - Floor 20,Museumsanity: Quartz,MUSEUM_DONATIONS,
-1244,The Mines - Floor 100,Museumsanity: Fire Quartz,MUSEUM_DONATIONS,
-1245,The Mines - Floor 60,Museumsanity: Frozen Tear,MUSEUM_DONATIONS,
-1246,The Mines - Floor 20,Museumsanity: Earth Crystal,MUSEUM_DONATIONS,
-1247,The Mines - Floor 100,Museumsanity: Emerald,MUSEUM_DONATIONS,
-1248,The Mines - Floor 60,Museumsanity: Aquamarine,MUSEUM_DONATIONS,
-1249,The Mines - Floor 100,Museumsanity: Ruby,MUSEUM_DONATIONS,
-1250,The Mines - Floor 20,Museumsanity: Amethyst,MUSEUM_DONATIONS,
-1251,The Mines - Floor 20,Museumsanity: Topaz,MUSEUM_DONATIONS,
-1252,The Mines - Floor 60,Museumsanity: Jade,MUSEUM_DONATIONS,
-1253,The Mines - Floor 60,Museumsanity: Diamond,MUSEUM_DONATIONS,
-1254,Skull Cavern Floor 100,Museumsanity: Prismatic Shard,MUSEUM_DONATIONS,
-1255,Town,Museumsanity: Alamite,MUSEUM_DONATIONS,
-1256,Town,Museumsanity: Bixite,MUSEUM_DONATIONS,
-1257,Town,Museumsanity: Baryte,MUSEUM_DONATIONS,
-1258,Town,Museumsanity: Aerinite,MUSEUM_DONATIONS,
-1259,Town,Museumsanity: Calcite,MUSEUM_DONATIONS,
-1260,Town,Museumsanity: Dolomite,MUSEUM_DONATIONS,
-1261,Town,Museumsanity: Esperite,MUSEUM_DONATIONS,
-1262,Town,Museumsanity: Fluorapatite,MUSEUM_DONATIONS,
-1263,Town,Museumsanity: Geminite,MUSEUM_DONATIONS,
-1264,Town,Museumsanity: Helvite,MUSEUM_DONATIONS,
-1265,Town,Museumsanity: Jamborite,MUSEUM_DONATIONS,
-1266,Town,Museumsanity: Jagoite,MUSEUM_DONATIONS,
-1267,Town,Museumsanity: Kyanite,MUSEUM_DONATIONS,
-1268,Town,Museumsanity: Lunarite,MUSEUM_DONATIONS,
-1269,Town,Museumsanity: Malachite,MUSEUM_DONATIONS,
-1270,Town,Museumsanity: Neptunite,MUSEUM_DONATIONS,
-1271,Town,Museumsanity: Lemon Stone,MUSEUM_DONATIONS,
-1272,Town,Museumsanity: Nekoite,MUSEUM_DONATIONS,
-1273,Town,Museumsanity: Orpiment,MUSEUM_DONATIONS,
-1274,Town,Museumsanity: Petrified Slime,MUSEUM_DONATIONS,
-1275,Town,Museumsanity: Thunder Egg,MUSEUM_DONATIONS,
-1276,Town,Museumsanity: Pyrite,MUSEUM_DONATIONS,
-1277,Town,Museumsanity: Ocean Stone,MUSEUM_DONATIONS,
-1278,Town,Museumsanity: Ghost Crystal,MUSEUM_DONATIONS,
-1279,Town,Museumsanity: Tigerseye,MUSEUM_DONATIONS,
-1280,Town,Museumsanity: Jasper,MUSEUM_DONATIONS,
-1281,Town,Museumsanity: Opal,MUSEUM_DONATIONS,
-1282,Town,Museumsanity: Fire Opal,MUSEUM_DONATIONS,
-1283,Town,Museumsanity: Celestine,MUSEUM_DONATIONS,
-1284,Town,Museumsanity: Marble,MUSEUM_DONATIONS,
-1285,Town,Museumsanity: Sandstone,MUSEUM_DONATIONS,
-1286,Town,Museumsanity: Granite,MUSEUM_DONATIONS,
-1287,Town,Museumsanity: Basalt,MUSEUM_DONATIONS,
-1288,Town,Museumsanity: Limestone,MUSEUM_DONATIONS,
-1289,Town,Museumsanity: Soapstone,MUSEUM_DONATIONS,
-1290,Town,Museumsanity: Hematite,MUSEUM_DONATIONS,
-1291,Town,Museumsanity: Mudstone,MUSEUM_DONATIONS,
-1292,Town,Museumsanity: Obsidian,MUSEUM_DONATIONS,
-1293,Town,Museumsanity: Slate,MUSEUM_DONATIONS,
-1294,Town,Museumsanity: Fairy Stone,MUSEUM_DONATIONS,
-1295,Town,Museumsanity: Star Shards,MUSEUM_DONATIONS,
-1301,Town,Friendsanity: Alex 1 <3,FRIENDSANITY,
-1302,Town,Friendsanity: Alex 2 <3,FRIENDSANITY,
-1303,Town,Friendsanity: Alex 3 <3,FRIENDSANITY,
-1304,Town,Friendsanity: Alex 4 <3,FRIENDSANITY,
-1305,Town,Friendsanity: Alex 5 <3,FRIENDSANITY,
-1306,Town,Friendsanity: Alex 6 <3,FRIENDSANITY,
-1307,Town,Friendsanity: Alex 7 <3,FRIENDSANITY,
-1308,Town,Friendsanity: Alex 8 <3,FRIENDSANITY,
-1309,Town,Friendsanity: Alex 9 <3,FRIENDSANITY,
-1310,Town,Friendsanity: Alex 10 <3,FRIENDSANITY,
-1311,Town,Friendsanity: Alex 11 <3,FRIENDSANITY,
-1312,Town,Friendsanity: Alex 12 <3,FRIENDSANITY,
-1313,Town,Friendsanity: Alex 13 <3,FRIENDSANITY,
-1314,Town,Friendsanity: Alex 14 <3,FRIENDSANITY,
-1315,Beach,Friendsanity: Elliott 1 <3,FRIENDSANITY,
-1316,Beach,Friendsanity: Elliott 2 <3,FRIENDSANITY,
-1317,Beach,Friendsanity: Elliott 3 <3,FRIENDSANITY,
-1318,Beach,Friendsanity: Elliott 4 <3,FRIENDSANITY,
-1319,Beach,Friendsanity: Elliott 5 <3,FRIENDSANITY,
-1320,Beach,Friendsanity: Elliott 6 <3,FRIENDSANITY,
-1321,Beach,Friendsanity: Elliott 7 <3,FRIENDSANITY,
-1322,Beach,Friendsanity: Elliott 8 <3,FRIENDSANITY,
-1323,Beach,Friendsanity: Elliott 9 <3,FRIENDSANITY,
-1324,Beach,Friendsanity: Elliott 10 <3,FRIENDSANITY,
-1325,Beach,Friendsanity: Elliott 11 <3,FRIENDSANITY,
-1326,Beach,Friendsanity: Elliott 12 <3,FRIENDSANITY,
-1327,Beach,Friendsanity: Elliott 13 <3,FRIENDSANITY,
-1328,Beach,Friendsanity: Elliott 14 <3,FRIENDSANITY,
-1329,Town,Friendsanity: Harvey 1 <3,FRIENDSANITY,
-1330,Town,Friendsanity: Harvey 2 <3,FRIENDSANITY,
-1331,Town,Friendsanity: Harvey 3 <3,FRIENDSANITY,
-1332,Town,Friendsanity: Harvey 4 <3,FRIENDSANITY,
-1333,Town,Friendsanity: Harvey 5 <3,FRIENDSANITY,
-1334,Town,Friendsanity: Harvey 6 <3,FRIENDSANITY,
-1335,Town,Friendsanity: Harvey 7 <3,FRIENDSANITY,
-1336,Town,Friendsanity: Harvey 8 <3,FRIENDSANITY,
-1337,Town,Friendsanity: Harvey 9 <3,FRIENDSANITY,
-1338,Town,Friendsanity: Harvey 10 <3,FRIENDSANITY,
-1339,Town,Friendsanity: Harvey 11 <3,FRIENDSANITY,
-1340,Town,Friendsanity: Harvey 12 <3,FRIENDSANITY,
-1341,Town,Friendsanity: Harvey 13 <3,FRIENDSANITY,
-1342,Town,Friendsanity: Harvey 14 <3,FRIENDSANITY,
-1343,Town,Friendsanity: Sam 1 <3,FRIENDSANITY,
-1344,Town,Friendsanity: Sam 2 <3,FRIENDSANITY,
-1345,Town,Friendsanity: Sam 3 <3,FRIENDSANITY,
-1346,Town,Friendsanity: Sam 4 <3,FRIENDSANITY,
-1347,Town,Friendsanity: Sam 5 <3,FRIENDSANITY,
-1348,Town,Friendsanity: Sam 6 <3,FRIENDSANITY,
-1349,Town,Friendsanity: Sam 7 <3,FRIENDSANITY,
-1350,Town,Friendsanity: Sam 8 <3,FRIENDSANITY,
-1351,Town,Friendsanity: Sam 9 <3,FRIENDSANITY,
-1352,Town,Friendsanity: Sam 10 <3,FRIENDSANITY,
-1353,Town,Friendsanity: Sam 11 <3,FRIENDSANITY,
-1354,Town,Friendsanity: Sam 12 <3,FRIENDSANITY,
-1355,Town,Friendsanity: Sam 13 <3,FRIENDSANITY,
-1356,Town,Friendsanity: Sam 14 <3,FRIENDSANITY,
-1357,Carpenter Shop,Friendsanity: Sebastian 1 <3,FRIENDSANITY,
-1358,Carpenter Shop,Friendsanity: Sebastian 2 <3,FRIENDSANITY,
-1359,Carpenter Shop,Friendsanity: Sebastian 3 <3,FRIENDSANITY,
-1360,Carpenter Shop,Friendsanity: Sebastian 4 <3,FRIENDSANITY,
-1361,Carpenter Shop,Friendsanity: Sebastian 5 <3,FRIENDSANITY,
-1362,Carpenter Shop,Friendsanity: Sebastian 6 <3,FRIENDSANITY,
-1363,Carpenter Shop,Friendsanity: Sebastian 7 <3,FRIENDSANITY,
-1364,Carpenter Shop,Friendsanity: Sebastian 8 <3,FRIENDSANITY,
-1365,Carpenter Shop,Friendsanity: Sebastian 9 <3,FRIENDSANITY,
-1366,Carpenter Shop,Friendsanity: Sebastian 10 <3,FRIENDSANITY,
-1367,Carpenter Shop,Friendsanity: Sebastian 11 <3,FRIENDSANITY,
-1368,Carpenter Shop,Friendsanity: Sebastian 12 <3,FRIENDSANITY,
-1369,Carpenter Shop,Friendsanity: Sebastian 13 <3,FRIENDSANITY,
-1370,Carpenter Shop,Friendsanity: Sebastian 14 <3,FRIENDSANITY,
-1371,Marnie's Ranch,Friendsanity: Shane 1 <3,FRIENDSANITY,
-1372,Marnie's Ranch,Friendsanity: Shane 2 <3,FRIENDSANITY,
-1373,Marnie's Ranch,Friendsanity: Shane 3 <3,FRIENDSANITY,
-1374,Marnie's Ranch,Friendsanity: Shane 4 <3,FRIENDSANITY,
-1375,Marnie's Ranch,Friendsanity: Shane 5 <3,FRIENDSANITY,
-1376,Marnie's Ranch,Friendsanity: Shane 6 <3,FRIENDSANITY,
-1377,Marnie's Ranch,Friendsanity: Shane 7 <3,FRIENDSANITY,
-1378,Marnie's Ranch,Friendsanity: Shane 8 <3,FRIENDSANITY,
-1379,Marnie's Ranch,Friendsanity: Shane 9 <3,FRIENDSANITY,
-1380,Marnie's Ranch,Friendsanity: Shane 10 <3,FRIENDSANITY,
-1381,Marnie's Ranch,Friendsanity: Shane 11 <3,FRIENDSANITY,
-1382,Marnie's Ranch,Friendsanity: Shane 12 <3,FRIENDSANITY,
-1383,Marnie's Ranch,Friendsanity: Shane 13 <3,FRIENDSANITY,
-1384,Marnie's Ranch,Friendsanity: Shane 14 <3,FRIENDSANITY,
-1385,Town,Friendsanity: Abigail 1 <3,FRIENDSANITY,
-1386,Town,Friendsanity: Abigail 2 <3,FRIENDSANITY,
-1387,Town,Friendsanity: Abigail 3 <3,FRIENDSANITY,
-1388,Town,Friendsanity: Abigail 4 <3,FRIENDSANITY,
-1389,Town,Friendsanity: Abigail 5 <3,FRIENDSANITY,
-1390,Town,Friendsanity: Abigail 6 <3,FRIENDSANITY,
-1391,Town,Friendsanity: Abigail 7 <3,FRIENDSANITY,
-1392,Town,Friendsanity: Abigail 8 <3,FRIENDSANITY,
-1393,Town,Friendsanity: Abigail 9 <3,FRIENDSANITY,
-1394,Town,Friendsanity: Abigail 10 <3,FRIENDSANITY,
-1395,Town,Friendsanity: Abigail 11 <3,FRIENDSANITY,
-1396,Town,Friendsanity: Abigail 12 <3,FRIENDSANITY,
-1397,Town,Friendsanity: Abigail 13 <3,FRIENDSANITY,
-1398,Town,Friendsanity: Abigail 14 <3,FRIENDSANITY,
-1399,Town,Friendsanity: Emily 1 <3,FRIENDSANITY,
-1400,Town,Friendsanity: Emily 2 <3,FRIENDSANITY,
-1401,Town,Friendsanity: Emily 3 <3,FRIENDSANITY,
-1402,Town,Friendsanity: Emily 4 <3,FRIENDSANITY,
-1403,Town,Friendsanity: Emily 5 <3,FRIENDSANITY,
-1404,Town,Friendsanity: Emily 6 <3,FRIENDSANITY,
-1405,Town,Friendsanity: Emily 7 <3,FRIENDSANITY,
-1406,Town,Friendsanity: Emily 8 <3,FRIENDSANITY,
-1407,Town,Friendsanity: Emily 9 <3,FRIENDSANITY,
-1408,Town,Friendsanity: Emily 10 <3,FRIENDSANITY,
-1409,Town,Friendsanity: Emily 11 <3,FRIENDSANITY,
-1410,Town,Friendsanity: Emily 12 <3,FRIENDSANITY,
-1411,Town,Friendsanity: Emily 13 <3,FRIENDSANITY,
-1412,Town,Friendsanity: Emily 14 <3,FRIENDSANITY,
-1413,Town,Friendsanity: Haley 1 <3,FRIENDSANITY,
-1414,Town,Friendsanity: Haley 2 <3,FRIENDSANITY,
-1415,Town,Friendsanity: Haley 3 <3,FRIENDSANITY,
-1416,Town,Friendsanity: Haley 4 <3,FRIENDSANITY,
-1417,Town,Friendsanity: Haley 5 <3,FRIENDSANITY,
-1418,Town,Friendsanity: Haley 6 <3,FRIENDSANITY,
-1419,Town,Friendsanity: Haley 7 <3,FRIENDSANITY,
-1420,Town,Friendsanity: Haley 8 <3,FRIENDSANITY,
-1421,Town,Friendsanity: Haley 9 <3,FRIENDSANITY,
-1422,Town,Friendsanity: Haley 10 <3,FRIENDSANITY,
-1423,Town,Friendsanity: Haley 11 <3,FRIENDSANITY,
-1424,Town,Friendsanity: Haley 12 <3,FRIENDSANITY,
-1425,Town,Friendsanity: Haley 13 <3,FRIENDSANITY,
-1426,Town,Friendsanity: Haley 14 <3,FRIENDSANITY,
-1427,Forest,Friendsanity: Leah 1 <3,FRIENDSANITY,
-1428,Forest,Friendsanity: Leah 2 <3,FRIENDSANITY,
-1429,Forest,Friendsanity: Leah 3 <3,FRIENDSANITY,
-1430,Forest,Friendsanity: Leah 4 <3,FRIENDSANITY,
-1431,Forest,Friendsanity: Leah 5 <3,FRIENDSANITY,
-1432,Forest,Friendsanity: Leah 6 <3,FRIENDSANITY,
-1433,Forest,Friendsanity: Leah 7 <3,FRIENDSANITY,
-1434,Forest,Friendsanity: Leah 8 <3,FRIENDSANITY,
-1435,Forest,Friendsanity: Leah 9 <3,FRIENDSANITY,
-1436,Forest,Friendsanity: Leah 10 <3,FRIENDSANITY,
-1437,Forest,Friendsanity: Leah 11 <3,FRIENDSANITY,
-1438,Forest,Friendsanity: Leah 12 <3,FRIENDSANITY,
-1439,Forest,Friendsanity: Leah 13 <3,FRIENDSANITY,
-1440,Forest,Friendsanity: Leah 14 <3,FRIENDSANITY,
-1441,Carpenter Shop,Friendsanity: Maru 1 <3,FRIENDSANITY,
-1442,Carpenter Shop,Friendsanity: Maru 2 <3,FRIENDSANITY,
-1443,Carpenter Shop,Friendsanity: Maru 3 <3,FRIENDSANITY,
-1444,Carpenter Shop,Friendsanity: Maru 4 <3,FRIENDSANITY,
-1445,Carpenter Shop,Friendsanity: Maru 5 <3,FRIENDSANITY,
-1446,Carpenter Shop,Friendsanity: Maru 6 <3,FRIENDSANITY,
-1447,Carpenter Shop,Friendsanity: Maru 7 <3,FRIENDSANITY,
-1448,Carpenter Shop,Friendsanity: Maru 8 <3,FRIENDSANITY,
-1449,Carpenter Shop,Friendsanity: Maru 9 <3,FRIENDSANITY,
-1450,Carpenter Shop,Friendsanity: Maru 10 <3,FRIENDSANITY,
-1451,Carpenter Shop,Friendsanity: Maru 11 <3,FRIENDSANITY,
-1452,Carpenter Shop,Friendsanity: Maru 12 <3,FRIENDSANITY,
-1453,Carpenter Shop,Friendsanity: Maru 13 <3,FRIENDSANITY,
-1454,Carpenter Shop,Friendsanity: Maru 14 <3,FRIENDSANITY,
-1455,Town,Friendsanity: Penny 1 <3,FRIENDSANITY,
-1456,Town,Friendsanity: Penny 2 <3,FRIENDSANITY,
-1457,Town,Friendsanity: Penny 3 <3,FRIENDSANITY,
-1458,Town,Friendsanity: Penny 4 <3,FRIENDSANITY,
-1459,Town,Friendsanity: Penny 5 <3,FRIENDSANITY,
-1460,Town,Friendsanity: Penny 6 <3,FRIENDSANITY,
-1461,Town,Friendsanity: Penny 7 <3,FRIENDSANITY,
-1462,Town,Friendsanity: Penny 8 <3,FRIENDSANITY,
-1463,Town,Friendsanity: Penny 9 <3,FRIENDSANITY,
-1464,Town,Friendsanity: Penny 10 <3,FRIENDSANITY,
-1465,Town,Friendsanity: Penny 11 <3,FRIENDSANITY,
-1466,Town,Friendsanity: Penny 12 <3,FRIENDSANITY,
-1467,Town,Friendsanity: Penny 13 <3,FRIENDSANITY,
-1468,Town,Friendsanity: Penny 14 <3,FRIENDSANITY,
-1469,Town,Friendsanity: Caroline 1 <3,FRIENDSANITY,
-1470,Town,Friendsanity: Caroline 2 <3,FRIENDSANITY,
-1471,Town,Friendsanity: Caroline 3 <3,FRIENDSANITY,
-1472,Town,Friendsanity: Caroline 4 <3,FRIENDSANITY,
-1473,Town,Friendsanity: Caroline 5 <3,FRIENDSANITY,
-1474,Town,Friendsanity: Caroline 6 <3,FRIENDSANITY,
-1475,Town,Friendsanity: Caroline 7 <3,FRIENDSANITY,
-1476,Town,Friendsanity: Caroline 8 <3,FRIENDSANITY,
-1477,Town,Friendsanity: Caroline 9 <3,FRIENDSANITY,
-1478,Town,Friendsanity: Caroline 10 <3,FRIENDSANITY,
-1480,Town,Friendsanity: Clint 1 <3,FRIENDSANITY,
-1481,Town,Friendsanity: Clint 2 <3,FRIENDSANITY,
-1482,Town,Friendsanity: Clint 3 <3,FRIENDSANITY,
-1483,Town,Friendsanity: Clint 4 <3,FRIENDSANITY,
-1484,Town,Friendsanity: Clint 5 <3,FRIENDSANITY,
-1485,Town,Friendsanity: Clint 6 <3,FRIENDSANITY,
-1486,Town,Friendsanity: Clint 7 <3,FRIENDSANITY,
-1487,Town,Friendsanity: Clint 8 <3,FRIENDSANITY,
-1488,Town,Friendsanity: Clint 9 <3,FRIENDSANITY,
-1489,Town,Friendsanity: Clint 10 <3,FRIENDSANITY,
-1491,Carpenter Shop,Friendsanity: Demetrius 1 <3,FRIENDSANITY,
-1492,Carpenter Shop,Friendsanity: Demetrius 2 <3,FRIENDSANITY,
-1493,Carpenter Shop,Friendsanity: Demetrius 3 <3,FRIENDSANITY,
-1494,Carpenter Shop,Friendsanity: Demetrius 4 <3,FRIENDSANITY,
-1495,Carpenter Shop,Friendsanity: Demetrius 5 <3,FRIENDSANITY,
-1496,Carpenter Shop,Friendsanity: Demetrius 6 <3,FRIENDSANITY,
-1497,Carpenter Shop,Friendsanity: Demetrius 7 <3,FRIENDSANITY,
-1498,Carpenter Shop,Friendsanity: Demetrius 8 <3,FRIENDSANITY,
-1499,Carpenter Shop,Friendsanity: Demetrius 9 <3,FRIENDSANITY,
-1500,Carpenter Shop,Friendsanity: Demetrius 10 <3,FRIENDSANITY,
-1502,The Mines,Friendsanity: Dwarf 1 <3,FRIENDSANITY,
-1503,The Mines,Friendsanity: Dwarf 2 <3,FRIENDSANITY,
-1504,The Mines,Friendsanity: Dwarf 3 <3,FRIENDSANITY,
-1505,The Mines,Friendsanity: Dwarf 4 <3,FRIENDSANITY,
-1506,The Mines,Friendsanity: Dwarf 5 <3,FRIENDSANITY,
-1507,The Mines,Friendsanity: Dwarf 6 <3,FRIENDSANITY,
-1508,The Mines,Friendsanity: Dwarf 7 <3,FRIENDSANITY,
-1509,The Mines,Friendsanity: Dwarf 8 <3,FRIENDSANITY,
-1510,The Mines,Friendsanity: Dwarf 9 <3,FRIENDSANITY,
-1511,The Mines,Friendsanity: Dwarf 10 <3,FRIENDSANITY,
-1513,Town,Friendsanity: Evelyn 1 <3,FRIENDSANITY,
-1514,Town,Friendsanity: Evelyn 2 <3,FRIENDSANITY,
-1515,Town,Friendsanity: Evelyn 3 <3,FRIENDSANITY,
-1516,Town,Friendsanity: Evelyn 4 <3,FRIENDSANITY,
-1517,Town,Friendsanity: Evelyn 5 <3,FRIENDSANITY,
-1518,Town,Friendsanity: Evelyn 6 <3,FRIENDSANITY,
-1519,Town,Friendsanity: Evelyn 7 <3,FRIENDSANITY,
-1520,Town,Friendsanity: Evelyn 8 <3,FRIENDSANITY,
-1521,Town,Friendsanity: Evelyn 9 <3,FRIENDSANITY,
-1522,Town,Friendsanity: Evelyn 10 <3,FRIENDSANITY,
-1524,Town,Friendsanity: George 1 <3,FRIENDSANITY,
-1525,Town,Friendsanity: George 2 <3,FRIENDSANITY,
-1526,Town,Friendsanity: George 3 <3,FRIENDSANITY,
-1527,Town,Friendsanity: George 4 <3,FRIENDSANITY,
-1528,Town,Friendsanity: George 5 <3,FRIENDSANITY,
-1529,Town,Friendsanity: George 6 <3,FRIENDSANITY,
-1530,Town,Friendsanity: George 7 <3,FRIENDSANITY,
-1531,Town,Friendsanity: George 8 <3,FRIENDSANITY,
-1532,Town,Friendsanity: George 9 <3,FRIENDSANITY,
-1533,Town,Friendsanity: George 10 <3,FRIENDSANITY,
-1535,Town,Friendsanity: Gus 1 <3,FRIENDSANITY,
-1536,Town,Friendsanity: Gus 2 <3,FRIENDSANITY,
-1537,Town,Friendsanity: Gus 3 <3,FRIENDSANITY,
-1538,Town,Friendsanity: Gus 4 <3,FRIENDSANITY,
-1539,Town,Friendsanity: Gus 5 <3,FRIENDSANITY,
-1540,Town,Friendsanity: Gus 6 <3,FRIENDSANITY,
-1541,Town,Friendsanity: Gus 7 <3,FRIENDSANITY,
-1542,Town,Friendsanity: Gus 8 <3,FRIENDSANITY,
-1543,Town,Friendsanity: Gus 9 <3,FRIENDSANITY,
-1544,Town,Friendsanity: Gus 10 <3,FRIENDSANITY,
-1546,Marnie's Ranch,Friendsanity: Jas 1 <3,FRIENDSANITY,
-1547,Marnie's Ranch,Friendsanity: Jas 2 <3,FRIENDSANITY,
-1548,Marnie's Ranch,Friendsanity: Jas 3 <3,FRIENDSANITY,
-1549,Marnie's Ranch,Friendsanity: Jas 4 <3,FRIENDSANITY,
-1550,Marnie's Ranch,Friendsanity: Jas 5 <3,FRIENDSANITY,
-1551,Marnie's Ranch,Friendsanity: Jas 6 <3,FRIENDSANITY,
-1552,Marnie's Ranch,Friendsanity: Jas 7 <3,FRIENDSANITY,
-1553,Marnie's Ranch,Friendsanity: Jas 8 <3,FRIENDSANITY,
-1554,Marnie's Ranch,Friendsanity: Jas 9 <3,FRIENDSANITY,
-1555,Marnie's Ranch,Friendsanity: Jas 10 <3,FRIENDSANITY,
-1557,Town,Friendsanity: Jodi 1 <3,FRIENDSANITY,
-1558,Town,Friendsanity: Jodi 2 <3,FRIENDSANITY,
-1559,Town,Friendsanity: Jodi 3 <3,FRIENDSANITY,
-1560,Town,Friendsanity: Jodi 4 <3,FRIENDSANITY,
-1561,Town,Friendsanity: Jodi 5 <3,FRIENDSANITY,
-1562,Town,Friendsanity: Jodi 6 <3,FRIENDSANITY,
-1563,Town,Friendsanity: Jodi 7 <3,FRIENDSANITY,
-1564,Town,Friendsanity: Jodi 8 <3,FRIENDSANITY,
-1565,Town,Friendsanity: Jodi 9 <3,FRIENDSANITY,
-1566,Town,Friendsanity: Jodi 10 <3,FRIENDSANITY,
-1568,Town,Friendsanity: Kent 1 <3,FRIENDSANITY,
-1569,Town,Friendsanity: Kent 2 <3,FRIENDSANITY,
-1570,Town,Friendsanity: Kent 3 <3,FRIENDSANITY,
-1571,Town,Friendsanity: Kent 4 <3,FRIENDSANITY,
-1572,Town,Friendsanity: Kent 5 <3,FRIENDSANITY,
-1573,Town,Friendsanity: Kent 6 <3,FRIENDSANITY,
-1574,Town,Friendsanity: Kent 7 <3,FRIENDSANITY,
-1575,Town,Friendsanity: Kent 8 <3,FRIENDSANITY,
-1576,Town,Friendsanity: Kent 9 <3,FRIENDSANITY,
-1577,Town,Friendsanity: Kent 10 <3,FRIENDSANITY,
-1579,Sewer,Friendsanity: Krobus 1 <3,FRIENDSANITY,
-1580,Sewer,Friendsanity: Krobus 2 <3,FRIENDSANITY,
-1581,Sewer,Friendsanity: Krobus 3 <3,FRIENDSANITY,
-1582,Sewer,Friendsanity: Krobus 4 <3,FRIENDSANITY,
-1583,Sewer,Friendsanity: Krobus 5 <3,FRIENDSANITY,
-1584,Sewer,Friendsanity: Krobus 6 <3,FRIENDSANITY,
-1585,Sewer,Friendsanity: Krobus 7 <3,FRIENDSANITY,
-1586,Sewer,Friendsanity: Krobus 8 <3,FRIENDSANITY,
-1587,Sewer,Friendsanity: Krobus 9 <3,FRIENDSANITY,
-1588,Sewer,Friendsanity: Krobus 10 <3,FRIENDSANITY,
-1590,Leo's Hut,Friendsanity: Leo 1 <3,"FRIENDSANITY,GINGER_ISLAND",
-1591,Leo's Hut,Friendsanity: Leo 2 <3,"FRIENDSANITY,GINGER_ISLAND",
-1592,Leo's Hut,Friendsanity: Leo 3 <3,"FRIENDSANITY,GINGER_ISLAND",
-1593,Leo's Hut,Friendsanity: Leo 4 <3,"FRIENDSANITY,GINGER_ISLAND",
-1594,Leo's Hut,Friendsanity: Leo 5 <3,"FRIENDSANITY,GINGER_ISLAND",
-1595,Leo's Hut,Friendsanity: Leo 6 <3,"FRIENDSANITY,GINGER_ISLAND",
-1596,Leo's Hut,Friendsanity: Leo 7 <3,"FRIENDSANITY,GINGER_ISLAND",
-1597,Leo's Hut,Friendsanity: Leo 8 <3,"FRIENDSANITY,GINGER_ISLAND",
-1598,Leo's Hut,Friendsanity: Leo 9 <3,"FRIENDSANITY,GINGER_ISLAND",
-1599,Leo's Hut,Friendsanity: Leo 10 <3,"FRIENDSANITY,GINGER_ISLAND",
-1601,Town,Friendsanity: Lewis 1 <3,FRIENDSANITY,
-1602,Town,Friendsanity: Lewis 2 <3,FRIENDSANITY,
-1603,Town,Friendsanity: Lewis 3 <3,FRIENDSANITY,
-1604,Town,Friendsanity: Lewis 4 <3,FRIENDSANITY,
-1605,Town,Friendsanity: Lewis 5 <3,FRIENDSANITY,
-1606,Town,Friendsanity: Lewis 6 <3,FRIENDSANITY,
-1607,Town,Friendsanity: Lewis 7 <3,FRIENDSANITY,
-1608,Town,Friendsanity: Lewis 8 <3,FRIENDSANITY,
-1609,Town,Friendsanity: Lewis 9 <3,FRIENDSANITY,
-1610,Town,Friendsanity: Lewis 10 <3,FRIENDSANITY,
-1612,Mountain,Friendsanity: Linus 1 <3,FRIENDSANITY,
-1613,Mountain,Friendsanity: Linus 2 <3,FRIENDSANITY,
-1614,Mountain,Friendsanity: Linus 3 <3,FRIENDSANITY,
-1615,Mountain,Friendsanity: Linus 4 <3,FRIENDSANITY,
-1616,Mountain,Friendsanity: Linus 5 <3,FRIENDSANITY,
-1617,Mountain,Friendsanity: Linus 6 <3,FRIENDSANITY,
-1618,Mountain,Friendsanity: Linus 7 <3,FRIENDSANITY,
-1619,Mountain,Friendsanity: Linus 8 <3,FRIENDSANITY,
-1620,Mountain,Friendsanity: Linus 9 <3,FRIENDSANITY,
-1621,Mountain,Friendsanity: Linus 10 <3,FRIENDSANITY,
-1623,Marnie's Ranch,Friendsanity: Marnie 1 <3,FRIENDSANITY,
-1624,Marnie's Ranch,Friendsanity: Marnie 2 <3,FRIENDSANITY,
-1625,Marnie's Ranch,Friendsanity: Marnie 3 <3,FRIENDSANITY,
-1626,Marnie's Ranch,Friendsanity: Marnie 4 <3,FRIENDSANITY,
-1627,Marnie's Ranch,Friendsanity: Marnie 5 <3,FRIENDSANITY,
-1628,Marnie's Ranch,Friendsanity: Marnie 6 <3,FRIENDSANITY,
-1629,Marnie's Ranch,Friendsanity: Marnie 7 <3,FRIENDSANITY,
-1630,Marnie's Ranch,Friendsanity: Marnie 8 <3,FRIENDSANITY,
-1631,Marnie's Ranch,Friendsanity: Marnie 9 <3,FRIENDSANITY,
-1632,Marnie's Ranch,Friendsanity: Marnie 10 <3,FRIENDSANITY,
-1634,Town,Friendsanity: Pam 1 <3,FRIENDSANITY,
-1635,Town,Friendsanity: Pam 2 <3,FRIENDSANITY,
-1636,Town,Friendsanity: Pam 3 <3,FRIENDSANITY,
-1637,Town,Friendsanity: Pam 4 <3,FRIENDSANITY,
-1638,Town,Friendsanity: Pam 5 <3,FRIENDSANITY,
-1639,Town,Friendsanity: Pam 6 <3,FRIENDSANITY,
-1640,Town,Friendsanity: Pam 7 <3,FRIENDSANITY,
-1641,Town,Friendsanity: Pam 8 <3,FRIENDSANITY,
-1642,Town,Friendsanity: Pam 9 <3,FRIENDSANITY,
-1643,Town,Friendsanity: Pam 10 <3,FRIENDSANITY,
-1645,Town,Friendsanity: Pierre 1 <3,FRIENDSANITY,
-1646,Town,Friendsanity: Pierre 2 <3,FRIENDSANITY,
-1647,Town,Friendsanity: Pierre 3 <3,FRIENDSANITY,
-1648,Town,Friendsanity: Pierre 4 <3,FRIENDSANITY,
-1649,Town,Friendsanity: Pierre 5 <3,FRIENDSANITY,
-1650,Town,Friendsanity: Pierre 6 <3,FRIENDSANITY,
-1651,Town,Friendsanity: Pierre 7 <3,FRIENDSANITY,
-1652,Town,Friendsanity: Pierre 8 <3,FRIENDSANITY,
-1653,Town,Friendsanity: Pierre 9 <3,FRIENDSANITY,
-1654,Town,Friendsanity: Pierre 10 <3,FRIENDSANITY,
-1656,Carpenter Shop,Friendsanity: Robin 1 <3,FRIENDSANITY,
-1657,Carpenter Shop,Friendsanity: Robin 2 <3,FRIENDSANITY,
-1658,Carpenter Shop,Friendsanity: Robin 3 <3,FRIENDSANITY,
-1659,Carpenter Shop,Friendsanity: Robin 4 <3,FRIENDSANITY,
-1660,Carpenter Shop,Friendsanity: Robin 5 <3,FRIENDSANITY,
-1661,Carpenter Shop,Friendsanity: Robin 6 <3,FRIENDSANITY,
-1662,Carpenter Shop,Friendsanity: Robin 7 <3,FRIENDSANITY,
-1663,Carpenter Shop,Friendsanity: Robin 8 <3,FRIENDSANITY,
-1664,Carpenter Shop,Friendsanity: Robin 9 <3,FRIENDSANITY,
-1665,Carpenter Shop,Friendsanity: Robin 10 <3,FRIENDSANITY,
-1667,Oasis,Friendsanity: Sandy 1 <3,FRIENDSANITY,
-1668,Oasis,Friendsanity: Sandy 2 <3,FRIENDSANITY,
-1669,Oasis,Friendsanity: Sandy 3 <3,FRIENDSANITY,
-1670,Oasis,Friendsanity: Sandy 4 <3,FRIENDSANITY,
-1671,Oasis,Friendsanity: Sandy 5 <3,FRIENDSANITY,
-1672,Oasis,Friendsanity: Sandy 6 <3,FRIENDSANITY,
-1673,Oasis,Friendsanity: Sandy 7 <3,FRIENDSANITY,
-1674,Oasis,Friendsanity: Sandy 8 <3,FRIENDSANITY,
-1675,Oasis,Friendsanity: Sandy 9 <3,FRIENDSANITY,
-1676,Oasis,Friendsanity: Sandy 10 <3,FRIENDSANITY,
-1678,Town,Friendsanity: Vincent 1 <3,FRIENDSANITY,
-1679,Town,Friendsanity: Vincent 2 <3,FRIENDSANITY,
-1680,Town,Friendsanity: Vincent 3 <3,FRIENDSANITY,
-1681,Town,Friendsanity: Vincent 4 <3,FRIENDSANITY,
-1682,Town,Friendsanity: Vincent 5 <3,FRIENDSANITY,
-1683,Town,Friendsanity: Vincent 6 <3,FRIENDSANITY,
-1684,Town,Friendsanity: Vincent 7 <3,FRIENDSANITY,
-1685,Town,Friendsanity: Vincent 8 <3,FRIENDSANITY,
-1686,Town,Friendsanity: Vincent 9 <3,FRIENDSANITY,
-1687,Town,Friendsanity: Vincent 10 <3,FRIENDSANITY,
-1689,Beach,Friendsanity: Willy 1 <3,FRIENDSANITY,
-1690,Beach,Friendsanity: Willy 2 <3,FRIENDSANITY,
-1691,Beach,Friendsanity: Willy 3 <3,FRIENDSANITY,
-1692,Beach,Friendsanity: Willy 4 <3,FRIENDSANITY,
-1693,Beach,Friendsanity: Willy 5 <3,FRIENDSANITY,
-1694,Beach,Friendsanity: Willy 6 <3,FRIENDSANITY,
-1695,Beach,Friendsanity: Willy 7 <3,FRIENDSANITY,
-1696,Beach,Friendsanity: Willy 8 <3,FRIENDSANITY,
-1697,Beach,Friendsanity: Willy 9 <3,FRIENDSANITY,
-1698,Beach,Friendsanity: Willy 10 <3,FRIENDSANITY,
-1700,Forest,Friendsanity: Wizard 1 <3,FRIENDSANITY,
-1701,Forest,Friendsanity: Wizard 2 <3,FRIENDSANITY,
-1702,Forest,Friendsanity: Wizard 3 <3,FRIENDSANITY,
-1703,Forest,Friendsanity: Wizard 4 <3,FRIENDSANITY,
-1704,Forest,Friendsanity: Wizard 5 <3,FRIENDSANITY,
-1705,Forest,Friendsanity: Wizard 6 <3,FRIENDSANITY,
-1706,Forest,Friendsanity: Wizard 7 <3,FRIENDSANITY,
-1707,Forest,Friendsanity: Wizard 8 <3,FRIENDSANITY,
-1708,Forest,Friendsanity: Wizard 9 <3,FRIENDSANITY,
-1709,Forest,Friendsanity: Wizard 10 <3,FRIENDSANITY,
-1710,Farm,Friendsanity: Pet 1 <3,FRIENDSANITY,
-1711,Farm,Friendsanity: Pet 2 <3,FRIENDSANITY,
-1712,Farm,Friendsanity: Pet 3 <3,FRIENDSANITY,
-1713,Farm,Friendsanity: Pet 4 <3,FRIENDSANITY,
-1714,Farm,Friendsanity: Pet 5 <3,FRIENDSANITY,
-1715,Farm,Friendsanity: Friend 1 <3,FRIENDSANITY,
-1716,Farm,Friendsanity: Friend 2 <3,FRIENDSANITY,
-1717,Farm,Friendsanity: Friend 3 <3,FRIENDSANITY,
-1718,Farm,Friendsanity: Friend 4 <3,FRIENDSANITY,
-1719,Farm,Friendsanity: Friend 5 <3,FRIENDSANITY,
-1720,Farm,Friendsanity: Friend 6 <3,FRIENDSANITY,
-1721,Farm,Friendsanity: Friend 7 <3,FRIENDSANITY,
-1722,Farm,Friendsanity: Friend 8 <3,FRIENDSANITY,
-1723,Farm,Friendsanity: Suitor 9 <3,FRIENDSANITY,
-1724,Farm,Friendsanity: Suitor 10 <3,FRIENDSANITY,
-1725,Farm,Friendsanity: Spouse 11 <3,FRIENDSANITY,
-1726,Farm,Friendsanity: Spouse 12 <3,FRIENDSANITY,
-1727,Farm,Friendsanity: Spouse 13 <3,FRIENDSANITY,
-1728,Farm,Friendsanity: Spouse 14 <3,FRIENDSANITY,
-2001,Town,Egg Hunt Victory,FESTIVAL,
-2002,Town,Egg Festival: Strawberry Seeds,FESTIVAL,
-2003,Forest,Dance with someone,FESTIVAL,
-2004,Forest,Rarecrow #5 (Woman),FESTIVAL,
-2005,Beach,Luau Soup,FESTIVAL,
-2006,Beach,Dance of the Moonlight Jellies,FESTIVAL,
-2007,Town,Smashing Stone,FESTIVAL,
-2008,Town,Grange Display,FESTIVAL,
-2009,Town,Rarecrow #1 (Turnip Head),FESTIVAL,
-2010,Town,Fair Stardrop,FESTIVAL,
-2011,Town,Spirit's Eve Maze,FESTIVAL,
-2012,Town,Rarecrow #2 (Witch),FESTIVAL,
-2013,Forest,Win Fishing Competition,FESTIVAL,
-2014,Forest,Rarecrow #4 (Snowman),FESTIVAL,
-2015,Beach,Mermaid Pearl,FESTIVAL,
-2016,Beach,Cone Hat,FESTIVAL_HARD,
-2017,Beach,Iridium Fireplace,FESTIVAL_HARD,
-2018,Beach,Rarecrow #7 (Tanuki),FESTIVAL,
-2019,Beach,Rarecrow #8 (Tribal Mask),FESTIVAL,
-2020,Beach,Lupini: Red Eagle,FESTIVAL,
-2021,Beach,Lupini: Portrait Of A Mermaid,FESTIVAL,
-2022,Beach,Lupini: Solar Kingdom,FESTIVAL,
-2023,Beach,Lupini: Clouds,FESTIVAL_HARD,
-2024,Beach,Lupini: 1000 Years From Now,FESTIVAL_HARD,
-2025,Beach,Lupini: Three Trees,FESTIVAL_HARD,
-2026,Beach,Lupini: The Serpent,FESTIVAL_HARD,
-2027,Beach,Lupini: 'Tropical Fish #173',FESTIVAL_HARD,
-2028,Beach,Lupini: Land Of Clay,FESTIVAL_HARD,
-2029,Town,Secret Santa,FESTIVAL,
-2030,Town,The Legend of the Winter Star,FESTIVAL,
-2031,Farm,Collect All Rarecrows,FESTIVAL,
-2101,Town,Island Ingredients,"GINGER_ISLAND,SPECIAL_ORDER_BOARD",
-2102,Town,Cave Patrol,SPECIAL_ORDER_BOARD,
-2103,Town,Aquatic Overpopulation,SPECIAL_ORDER_BOARD,
-2104,Town,Biome Balance,SPECIAL_ORDER_BOARD,
-2105,Town,Rock Rejuvenation,SPECIAL_ORDER_BOARD,
-2106,Town,Gifts for George,SPECIAL_ORDER_BOARD,
-2107,Town,Fragments of the past,"GINGER_ISLAND,SPECIAL_ORDER_BOARD",
-2108,Town,Gus' Famous Omelet,SPECIAL_ORDER_BOARD,
-2109,Town,Crop Order,SPECIAL_ORDER_BOARD,
-2110,Town,Community Cleanup,SPECIAL_ORDER_BOARD,
-2111,Town,The Strong Stuff,SPECIAL_ORDER_BOARD,
-2112,Town,Pierre's Prime Produce,SPECIAL_ORDER_BOARD,
-2113,Town,Robin's Project,SPECIAL_ORDER_BOARD,
-2114,Town,Robin's Resource Rush,SPECIAL_ORDER_BOARD,
-2115,Town,Juicy Bugs Wanted!,SPECIAL_ORDER_BOARD,
-2116,Town,Tropical Fish,"GINGER_ISLAND,SPECIAL_ORDER_BOARD",
-2117,Town,A Curious Substance,SPECIAL_ORDER_BOARD,
-2118,Town,Prismatic Jelly,SPECIAL_ORDER_BOARD,
-2151,Qi's Walnut Room,Qi's Crop,"GINGER_ISLAND,SPECIAL_ORDER_QI",
-2152,Qi's Walnut Room,Let's Play A Game,"GINGER_ISLAND,SPECIAL_ORDER_QI,JUNIMO_KART",
-2153,Qi's Walnut Room,Four Precious Stones,"GINGER_ISLAND,SPECIAL_ORDER_QI",
-2154,Qi's Walnut Room,Qi's Hungry Challenge,"GINGER_ISLAND,SPECIAL_ORDER_QI",
-2155,Qi's Walnut Room,Qi's Cuisine,"GINGER_ISLAND,SPECIAL_ORDER_QI",
-2156,Qi's Walnut Room,Qi's Kindness,"GINGER_ISLAND,SPECIAL_ORDER_QI",
-2157,Qi's Walnut Room,Extended Family,"GINGER_ISLAND,SPECIAL_ORDER_QI",
-2158,Qi's Walnut Room,Danger In The Deep,"GINGER_ISLAND,SPECIAL_ORDER_QI",
-2159,Qi's Walnut Room,Skull Cavern Invasion,"GINGER_ISLAND,SPECIAL_ORDER_QI",
-2160,Qi's Walnut Room,Qi's Prismatic Grange,"GINGER_ISLAND,SPECIAL_ORDER_QI",
-2201,Boat Tunnel,Repair Ticket Machine,GINGER_ISLAND,
-2202,Boat Tunnel,Repair Boat Hull,GINGER_ISLAND,
-2203,Boat Tunnel,Repair Boat Anchor,GINGER_ISLAND,
-2204,Leo's Hut,Leo's Parrot,"GINGER_ISLAND,WALNUT_PURCHASE",
-2205,Island South,Island West Turtle,"GINGER_ISLAND,WALNUT_PURCHASE",
-2206,Island West,Island Farmhouse,"GINGER_ISLAND,WALNUT_PURCHASE",
-2207,Island Farmhouse,Island Mailbox,"GINGER_ISLAND,WALNUT_PURCHASE",
-2208,Island Farmhouse,Farm Obelisk,"GINGER_ISLAND,WALNUT_PURCHASE",
-2209,Island North,Dig Site Bridge,"GINGER_ISLAND,WALNUT_PURCHASE",
-2210,Island North,Island Trader,"GINGER_ISLAND,WALNUT_PURCHASE",
-2211,Volcano Entrance,Volcano Bridge,"GINGER_ISLAND,WALNUT_PURCHASE",
-2212,Volcano - Floor 5,Volcano Exit Shortcut,"GINGER_ISLAND,WALNUT_PURCHASE",
-2213,Island South,Island Resort,"GINGER_ISLAND,WALNUT_PURCHASE",
-2214,Island West,Parrot Express,"GINGER_ISLAND,WALNUT_PURCHASE",
-2215,Dig Site,Open Professor Snail Cave,"GINGER_ISLAND",
-2216,Field Office,Complete Island Field Office,"GINGER_ISLAND",
-2301,Farm,Harvest Amaranth,"CROPSANITY",
-2302,Farm,Harvest Artichoke,"CROPSANITY",
-2303,Farm,Harvest Beet,"CROPSANITY",
-2304,Farm,Harvest Blue Jazz,"CROPSANITY",
-2305,Farm,Harvest Blueberry,"CROPSANITY",
-2306,Farm,Harvest Bok Choy,"CROPSANITY",
-2307,Farm,Harvest Cauliflower,"CROPSANITY",
-2308,Farm,Harvest Corn,"CROPSANITY",
-2309,Farm,Harvest Cranberries,"CROPSANITY",
-2310,Farm,Harvest Eggplant,"CROPSANITY",
-2311,Farm,Harvest Fairy Rose,"CROPSANITY",
-2312,Farm,Harvest Garlic,"CROPSANITY",
-2313,Farm,Harvest Grape,"CROPSANITY",
-2314,Farm,Harvest Green Bean,"CROPSANITY",
-2315,Farm,Harvest Hops,"CROPSANITY",
-2316,Farm,Harvest Hot Pepper,"CROPSANITY",
-2317,Farm,Harvest Kale,"CROPSANITY",
-2318,Farm,Harvest Melon,"CROPSANITY",
-2319,Farm,Harvest Parsnip,"CROPSANITY",
-2320,Farm,Harvest Poppy,"CROPSANITY",
-2321,Farm,Harvest Potato,"CROPSANITY",
-2322,Farm,Harvest Pumpkin,"CROPSANITY",
-2323,Farm,Harvest Radish,"CROPSANITY",
-2324,Farm,Harvest Red Cabbage,"CROPSANITY",
-2325,Farm,Harvest Rhubarb,"CROPSANITY",
-2326,Farm,Harvest Starfruit,"CROPSANITY",
-2327,Farm,Harvest Strawberry,"CROPSANITY",
-2328,Farm,Harvest Summer Spangle,"CROPSANITY",
-2329,Farm,Harvest Sunflower,"CROPSANITY",
-2330,Farm,Harvest Tomato,"CROPSANITY",
-2331,Farm,Harvest Tulip,"CROPSANITY",
-2332,Farm,Harvest Unmilled Rice,"CROPSANITY",
-2333,Farm,Harvest Wheat,"CROPSANITY",
-2334,Farm,Harvest Yam,"CROPSANITY",
-2335,Farm,Harvest Cactus Fruit,"CROPSANITY",
-2336,Farm,Harvest Pineapple,"CROPSANITY,GINGER_ISLAND",
-2337,Farm,Harvest Taro Root,"CROPSANITY,GINGER_ISLAND",
-2338,Farm,Harvest Sweet Gem Berry,"CROPSANITY",
-2339,Farm,Harvest Apple,"CROPSANITY",
-2340,Farm,Harvest Apricot,"CROPSANITY",
-2341,Farm,Harvest Cherry,"CROPSANITY",
-2342,Farm,Harvest Orange,"CROPSANITY",
-2343,Farm,Harvest Pomegranate,"CROPSANITY",
-2344,Farm,Harvest Peach,"CROPSANITY",
-2345,Farm,Harvest Banana,"CROPSANITY,GINGER_ISLAND",
-2346,Farm,Harvest Mango,"CROPSANITY,GINGER_ISLAND",
-2347,Farm,Harvest Coffee Bean,"CROPSANITY",
-5001,Stardew Valley,Level 1 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
-5002,Stardew Valley,Level 2 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
-5003,Stardew Valley,Level 3 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
-5004,Stardew Valley,Level 4 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
-5005,Stardew Valley,Level 5 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
-5006,Stardew Valley,Level 6 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
-5007,Stardew Valley,Level 7 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
-5008,Stardew Valley,Level 8 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
-5009,Stardew Valley,Level 9 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
-5010,Stardew Valley,Level 10 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
-5011,Stardew Valley,Level 1 Socializing,"SOCIALIZING_LEVEL,SKILL_LEVEL",Socializing Skill
-5012,Stardew Valley,Level 2 Socializing,"SOCIALIZING_LEVEL,SKILL_LEVEL",Socializing Skill
-5013,Stardew Valley,Level 3 Socializing,"SOCIALIZING_LEVEL,SKILL_LEVEL",Socializing Skill
-5014,Stardew Valley,Level 4 Socializing,"SOCIALIZING_LEVEL,SKILL_LEVEL",Socializing Skill
-5015,Stardew Valley,Level 5 Socializing,"SOCIALIZING_LEVEL,SKILL_LEVEL",Socializing Skill
-5016,Stardew Valley,Level 6 Socializing,"SOCIALIZING_LEVEL,SKILL_LEVEL",Socializing Skill
-5017,Stardew Valley,Level 7 Socializing,"SOCIALIZING_LEVEL,SKILL_LEVEL",Socializing Skill
-5018,Stardew Valley,Level 8 Socializing,"SOCIALIZING_LEVEL,SKILL_LEVEL",Socializing Skill
-5019,Stardew Valley,Level 9 Socializing,"SOCIALIZING_LEVEL,SKILL_LEVEL",Socializing Skill
-5020,Stardew Valley,Level 10 Socializing,"SOCIALIZING_LEVEL,SKILL_LEVEL",Socializing Skill
-5021,Stardew Valley,Level 1 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
-5022,Stardew Valley,Level 2 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
-5023,Stardew Valley,Level 3 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
-5024,Stardew Valley,Level 4 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
-5025,Stardew Valley,Level 5 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
-5026,Stardew Valley,Level 6 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
-5027,Stardew Valley,Level 7 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
-5028,Stardew Valley,Level 8 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
-5029,Stardew Valley,Level 9 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
-5030,Stardew Valley,Level 10 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
-5031,Stardew Valley,Level 1 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
-5032,Stardew Valley,Level 2 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
-5033,Stardew Valley,Level 3 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
-5034,Stardew Valley,Level 4 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
-5035,Stardew Valley,Level 5 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
-5036,Stardew Valley,Level 6 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
-5037,Stardew Valley,Level 7 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
-5038,Stardew Valley,Level 8 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
-5039,Stardew Valley,Level 9 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
-5040,Stardew Valley,Level 10 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
-5041,Stardew Valley,Level 1 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
-5042,Stardew Valley,Level 2 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
-5043,Stardew Valley,Level 3 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
-5044,Stardew Valley,Level 4 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
-5045,Stardew Valley,Level 5 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
-5046,Stardew Valley,Level 6 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
-5047,Stardew Valley,Level 7 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
-5048,Stardew Valley,Level 8 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
-5049,Stardew Valley,Level 9 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
-5050,Stardew Valley,Level 10 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
-5051,Stardew Valley,Level 1 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
-5052,Stardew Valley,Level 2 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
-5053,Stardew Valley,Level 3 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
-5054,Stardew Valley,Level 4 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
-5055,Stardew Valley,Level 5 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
-5056,Stardew Valley,Level 6 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
-5057,Stardew Valley,Level 7 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
-5058,Stardew Valley,Level 8 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
-5059,Stardew Valley,Level 9 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
-5060,Stardew Valley,Level 10 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
-5501,Stardew Valley,Analyze: Clear Debris,MANDATORY,Magic
-5502,Stardew Valley,Analyze: Till,MANDATORY,Magic
-5503,Stardew Valley,Analyze: Water,MANDATORY,Magic
-5504,Stardew Valley,Analyze All Toil School Locations,MANDATORY,Magic
-5505,Stardew Valley,Analyze: Evac,MANDATORY,Magic
-5506,Stardew Valley,Analyze: Haste,MANDATORY,Magic
-5507,Stardew Valley,Analyze: Heal,MANDATORY,Magic
-5508,Stardew Valley,Analyze All Life School Locations,MANDATORY,Magic
-5509,Stardew Valley,Analyze: Descend,MANDATORY,Magic
-5510,Stardew Valley,Analyze: Fireball,MANDATORY,Magic
-5511,Stardew Valley,Analyze: Frostbite,MANDATORY,Magic
-5512,Stardew Valley,Analyze All Elemental School Locations,MANDATORY,Magic
-5513,Stardew Valley,Analyze: Lantern,MANDATORY,Magic
-5514,Stardew Valley,Analyze: Tendrils,MANDATORY,Magic
-5515,Stardew Valley,Analyze: Shockwave,MANDATORY,Magic
-5516,Stardew Valley,Analyze All Nature School Locations,MANDATORY,Magic
-5517,Stardew Valley,Analyze: Meteor,MANDATORY,Magic
-5518,Stardew Valley,Analyze: Lucksteal,MANDATORY,Magic
-5519,Stardew Valley,Analyze: Bloodmana,MANDATORY,Magic
-5520,Stardew Valley,Analyze All Eldritch School Locations,MANDATORY,Magic
-5521,Stardew Valley,Analyze Every Magic School Location,MANDATORY,Magic
-6001,Town,Friendsanity: Jasper 1 <3,FRIENDSANITY,Professor Jasper Thomas
-6002,Town,Friendsanity: Jasper 2 <3,FRIENDSANITY,Professor Jasper Thomas
-6003,Town,Friendsanity: Jasper 3 <3,FRIENDSANITY,Professor Jasper Thomas
-6004,Town,Friendsanity: Jasper 4 <3,FRIENDSANITY,Professor Jasper Thomas
-6005,Town,Friendsanity: Jasper 5 <3,FRIENDSANITY,Professor Jasper Thomas
-6006,Town,Friendsanity: Jasper 6 <3,FRIENDSANITY,Professor Jasper Thomas
-6007,Town,Friendsanity: Jasper 7 <3,FRIENDSANITY,Professor Jasper Thomas
-6008,Town,Friendsanity: Jasper 8 <3,FRIENDSANITY,Professor Jasper Thomas
-6009,Town,Friendsanity: Jasper 9 <3,FRIENDSANITY,Professor Jasper Thomas
-6010,Town,Friendsanity: Jasper 10 <3,FRIENDSANITY,Professor Jasper Thomas
-6011,Town,Friendsanity: Jasper 11 <3,FRIENDSANITY,Professor Jasper Thomas
-6012,Town,Friendsanity: Jasper 12 <3,FRIENDSANITY,Professor Jasper Thomas
-6013,Town,Friendsanity: Jasper 13 <3,FRIENDSANITY,Professor Jasper Thomas
-6014,Town,Friendsanity: Jasper 14 <3,FRIENDSANITY,Professor Jasper Thomas
-6015,Secret Woods,Friendsanity: Yoba 1 <3,FRIENDSANITY,Custom NPC - Yoba
-6016,Secret Woods,Friendsanity: Yoba 2 <3,FRIENDSANITY,Custom NPC - Yoba
-6017,Secret Woods,Friendsanity: Yoba 3 <3,FRIENDSANITY,Custom NPC - Yoba
-6018,Secret Woods,Friendsanity: Yoba 4 <3,FRIENDSANITY,Custom NPC - Yoba
-6019,Secret Woods,Friendsanity: Yoba 5 <3,FRIENDSANITY,Custom NPC - Yoba
-6020,Secret Woods,Friendsanity: Yoba 6 <3,FRIENDSANITY,Custom NPC - Yoba
-6021,Secret Woods,Friendsanity: Yoba 7 <3,FRIENDSANITY,Custom NPC - Yoba
-6022,Secret Woods,Friendsanity: Yoba 8 <3,FRIENDSANITY,Custom NPC - Yoba
-6023,Secret Woods,Friendsanity: Yoba 9 <3,FRIENDSANITY,Custom NPC - Yoba
-6024,Secret Woods,Friendsanity: Yoba 10 <3,FRIENDSANITY,Custom NPC - Yoba
-6025,Forest,Friendsanity: Mr. Ginger 1 <3,FRIENDSANITY,Mister Ginger (cat npc)
-6026,Forest,Friendsanity: Mr. Ginger 2 <3,FRIENDSANITY,Mister Ginger (cat npc)
-6027,Forest,Friendsanity: Mr. Ginger 3 <3,FRIENDSANITY,Mister Ginger (cat npc)
-6028,Forest,Friendsanity: Mr. Ginger 4 <3,FRIENDSANITY,Mister Ginger (cat npc)
-6029,Forest,Friendsanity: Mr. Ginger 5 <3,FRIENDSANITY,Mister Ginger (cat npc)
-6030,Forest,Friendsanity: Mr. Ginger 6 <3,FRIENDSANITY,Mister Ginger (cat npc)
-6031,Forest,Friendsanity: Mr. Ginger 7 <3,FRIENDSANITY,Mister Ginger (cat npc)
-6032,Forest,Friendsanity: Mr. Ginger 8 <3,FRIENDSANITY,Mister Ginger (cat npc)
-6033,Forest,Friendsanity: Mr. Ginger 9 <3,FRIENDSANITY,Mister Ginger (cat npc)
-6034,Forest,Friendsanity: Mr. Ginger 10 <3,FRIENDSANITY,Mister Ginger (cat npc)
-6035,Town,Friendsanity: Ayeisha 1 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
-6036,Town,Friendsanity: Ayeisha 2 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
-6037,Town,Friendsanity: Ayeisha 3 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
-6038,Town,Friendsanity: Ayeisha 4 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
-6039,Town,Friendsanity: Ayeisha 5 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
-6040,Town,Friendsanity: Ayeisha 6 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
-6041,Town,Friendsanity: Ayeisha 7 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
-6042,Town,Friendsanity: Ayeisha 8 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
-6043,Town,Friendsanity: Ayeisha 9 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
-6044,Town,Friendsanity: Ayeisha 10 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
-6045,Town,Friendsanity: Shiko 1 <3,FRIENDSANITY,Shiko - New Custom NPC
-6046,Town,Friendsanity: Shiko 2 <3,FRIENDSANITY,Shiko - New Custom NPC
-6047,Town,Friendsanity: Shiko 3 <3,FRIENDSANITY,Shiko - New Custom NPC
-6048,Town,Friendsanity: Shiko 4 <3,FRIENDSANITY,Shiko - New Custom NPC
-6049,Town,Friendsanity: Shiko 5 <3,FRIENDSANITY,Shiko - New Custom NPC
-6050,Town,Friendsanity: Shiko 6 <3,FRIENDSANITY,Shiko - New Custom NPC
-6051,Town,Friendsanity: Shiko 7 <3,FRIENDSANITY,Shiko - New Custom NPC
-6052,Town,Friendsanity: Shiko 8 <3,FRIENDSANITY,Shiko - New Custom NPC
-6053,Town,Friendsanity: Shiko 9 <3,FRIENDSANITY,Shiko - New Custom NPC
-6054,Town,Friendsanity: Shiko 10 <3,FRIENDSANITY,Shiko - New Custom NPC
-6055,Town,Friendsanity: Shiko 11 <3,FRIENDSANITY,Shiko - New Custom NPC
-6056,Town,Friendsanity: Shiko 12 <3,FRIENDSANITY,Shiko - New Custom NPC
-6057,Town,Friendsanity: Shiko 13 <3,FRIENDSANITY,Shiko - New Custom NPC
-6058,Town,Friendsanity: Shiko 14 <3,FRIENDSANITY,Shiko - New Custom NPC
-6059,Forest,Friendsanity: Wellwick 1 <3,FRIENDSANITY,'Prophet' Wellwick
-6060,Forest,Friendsanity: Wellwick 2 <3,FRIENDSANITY,'Prophet' Wellwick
-6061,Forest,Friendsanity: Wellwick 3 <3,FRIENDSANITY,'Prophet' Wellwick
-6062,Forest,Friendsanity: Wellwick 4 <3,FRIENDSANITY,'Prophet' Wellwick
-6063,Forest,Friendsanity: Wellwick 5 <3,FRIENDSANITY,'Prophet' Wellwick
-6064,Forest,Friendsanity: Wellwick 6 <3,FRIENDSANITY,'Prophet' Wellwick
-6065,Forest,Friendsanity: Wellwick 7 <3,FRIENDSANITY,'Prophet' Wellwick
-6066,Forest,Friendsanity: Wellwick 8 <3,FRIENDSANITY,'Prophet' Wellwick
-6067,Forest,Friendsanity: Wellwick 9 <3,FRIENDSANITY,'Prophet' Wellwick
-6068,Forest,Friendsanity: Wellwick 10 <3,FRIENDSANITY,'Prophet' Wellwick
-6069,Forest,Friendsanity: Wellwick 11 <3,FRIENDSANITY,'Prophet' Wellwick
-6070,Forest,Friendsanity: Wellwick 12 <3,FRIENDSANITY,'Prophet' Wellwick
-6071,Forest,Friendsanity: Wellwick 13 <3,FRIENDSANITY,'Prophet' Wellwick
-6072,Forest,Friendsanity: Wellwick 14 <3,FRIENDSANITY,'Prophet' Wellwick
-6073,Forest,Friendsanity: Delores 1 <3,FRIENDSANITY,Delores - Custom NPC
-6074,Forest,Friendsanity: Delores 2 <3,FRIENDSANITY,Delores - Custom NPC
-6075,Forest,Friendsanity: Delores 3 <3,FRIENDSANITY,Delores - Custom NPC
-6076,Forest,Friendsanity: Delores 4 <3,FRIENDSANITY,Delores - Custom NPC
-6077,Forest,Friendsanity: Delores 5 <3,FRIENDSANITY,Delores - Custom NPC
-6078,Forest,Friendsanity: Delores 6 <3,FRIENDSANITY,Delores - Custom NPC
-6079,Forest,Friendsanity: Delores 7 <3,FRIENDSANITY,Delores - Custom NPC
-6080,Forest,Friendsanity: Delores 8 <3,FRIENDSANITY,Delores - Custom NPC
-6081,Forest,Friendsanity: Delores 9 <3,FRIENDSANITY,Delores - Custom NPC
-6082,Forest,Friendsanity: Delores 10 <3,FRIENDSANITY,Delores - Custom NPC
-6083,Forest,Friendsanity: Delores 11 <3,FRIENDSANITY,Delores - Custom NPC
-6084,Forest,Friendsanity: Delores 12 <3,FRIENDSANITY,Delores - Custom NPC
-6085,Forest,Friendsanity: Delores 13 <3,FRIENDSANITY,Delores - Custom NPC
-6086,Forest,Friendsanity: Delores 14 <3,FRIENDSANITY,Delores - Custom NPC
-6087,Forest,Friendsanity: Alec 1 <3,FRIENDSANITY,Alec Revisited
-6088,Forest,Friendsanity: Alec 2 <3,FRIENDSANITY,Alec Revisited
-6089,Forest,Friendsanity: Alec 3 <3,FRIENDSANITY,Alec Revisited
-6090,Forest,Friendsanity: Alec 4 <3,FRIENDSANITY,Alec Revisited
-6091,Forest,Friendsanity: Alec 5 <3,FRIENDSANITY,Alec Revisited
-6092,Forest,Friendsanity: Alec 6 <3,FRIENDSANITY,Alec Revisited
-6093,Forest,Friendsanity: Alec 7 <3,FRIENDSANITY,Alec Revisited
-6094,Forest,Friendsanity: Alec 8 <3,FRIENDSANITY,Alec Revisited
-6095,Forest,Friendsanity: Alec 9 <3,FRIENDSANITY,Alec Revisited
-6096,Forest,Friendsanity: Alec 10 <3,FRIENDSANITY,Alec Revisited
-6097,Forest,Friendsanity: Alec 11 <3,FRIENDSANITY,Alec Revisited
-6098,Forest,Friendsanity: Alec 12 <3,FRIENDSANITY,Alec Revisited
-6099,Forest,Friendsanity: Alec 13 <3,FRIENDSANITY,Alec Revisited
-6100,Forest,Friendsanity: Alec 14 <3,FRIENDSANITY,Alec Revisited
-6101,Forest,Friendsanity: Eugene 1 <3,FRIENDSANITY,Custom NPC Eugene
-6102,Forest,Friendsanity: Eugene 2 <3,FRIENDSANITY,Custom NPC Eugene
-6103,Forest,Friendsanity: Eugene 3 <3,FRIENDSANITY,Custom NPC Eugene
-6104,Forest,Friendsanity: Eugene 4 <3,FRIENDSANITY,Custom NPC Eugene
-6105,Forest,Friendsanity: Eugene 5 <3,FRIENDSANITY,Custom NPC Eugene
-6106,Forest,Friendsanity: Eugene 6 <3,FRIENDSANITY,Custom NPC Eugene
-6107,Forest,Friendsanity: Eugene 7 <3,FRIENDSANITY,Custom NPC Eugene
-6108,Forest,Friendsanity: Eugene 8 <3,FRIENDSANITY,Custom NPC Eugene
-6109,Forest,Friendsanity: Eugene 9 <3,FRIENDSANITY,Custom NPC Eugene
-6110,Forest,Friendsanity: Eugene 10 <3,FRIENDSANITY,Custom NPC Eugene
-6111,Forest,Friendsanity: Eugene 11 <3,FRIENDSANITY,Custom NPC Eugene
-6112,Forest,Friendsanity: Eugene 12 <3,FRIENDSANITY,Custom NPC Eugene
-6113,Forest,Friendsanity: Eugene 13 <3,FRIENDSANITY,Custom NPC Eugene
-6114,Forest,Friendsanity: Eugene 14 <3,FRIENDSANITY,Custom NPC Eugene
-6115,Forest,Friendsanity: Juna 1 <3,FRIENDSANITY,Juna - Roommate NPC
-6116,Forest,Friendsanity: Juna 2 <3,FRIENDSANITY,Juna - Roommate NPC
-6117,Forest,Friendsanity: Juna 3 <3,FRIENDSANITY,Juna - Roommate NPC
-6118,Forest,Friendsanity: Juna 4 <3,FRIENDSANITY,Juna - Roommate NPC
-6119,Forest,Friendsanity: Juna 5 <3,FRIENDSANITY,Juna - Roommate NPC
-6120,Forest,Friendsanity: Juna 6 <3,FRIENDSANITY,Juna - Roommate NPC
-6121,Forest,Friendsanity: Juna 7 <3,FRIENDSANITY,Juna - Roommate NPC
-6122,Forest,Friendsanity: Juna 8 <3,FRIENDSANITY,Juna - Roommate NPC
-6123,Forest,Friendsanity: Juna 9 <3,FRIENDSANITY,Juna - Roommate NPC
-6124,Forest,Friendsanity: Juna 10 <3,FRIENDSANITY,Juna - Roommate NPC
-6125,Town,Friendsanity: Riley 1 <3,FRIENDSANITY,Custom NPC - Riley
-6126,Town,Friendsanity: Riley 2 <3,FRIENDSANITY,Custom NPC - Riley
-6127,Town,Friendsanity: Riley 3 <3,FRIENDSANITY,Custom NPC - Riley
-6128,Town,Friendsanity: Riley 4 <3,FRIENDSANITY,Custom NPC - Riley
-6129,Town,Friendsanity: Riley 5 <3,FRIENDSANITY,Custom NPC - Riley
-6130,Town,Friendsanity: Riley 6 <3,FRIENDSANITY,Custom NPC - Riley
-6131,Town,Friendsanity: Riley 7 <3,FRIENDSANITY,Custom NPC - Riley
-6132,Town,Friendsanity: Riley 8 <3,FRIENDSANITY,Custom NPC - Riley
-6133,Town,Friendsanity: Riley 9 <3,FRIENDSANITY,Custom NPC - Riley
-6134,Town,Friendsanity: Riley 10 <3,FRIENDSANITY,Custom NPC - Riley
-6135,Town,Friendsanity: Riley 11 <3,FRIENDSANITY,Custom NPC - Riley
-6136,Town,Friendsanity: Riley 12 <3,FRIENDSANITY,Custom NPC - Riley
-6137,Town,Friendsanity: Riley 13 <3,FRIENDSANITY,Custom NPC - Riley
-6138,Town,Friendsanity: Riley 14 <3,FRIENDSANITY,Custom NPC - Riley
-7001,Pierre's General Store,Premium Pack,BACKPACK,Bigger Backpack
-7002,Carpenter Shop,Tractor Garage Blueprint,BUILDING_BLUEPRINT,Tractor Mod
-7003,The Deep Woods Depth 100,Pet the Deep Woods Unicorn,MANDATORY,DeepWoods
-7004,The Deep Woods Depth 50,Breaking Up Deep Woods Gingerbread House,MANDATORY,DeepWoods
-7005,The Deep Woods Depth 50,Drinking From Deep Woods Fountain,MANDATORY,DeepWoods
-7006,The Deep Woods Depth 100,Deep Woods Treasure Chest,MANDATORY,DeepWoods
-7007,The Deep Woods Depth 100,Deep Woods Trash Bin,MANDATORY,DeepWoods
-7008,The Deep Woods Depth 50,Chop Down a Deep Woods Iridium Tree,MANDATORY,DeepWoods
-7009,The Deep Woods Depth 10,The Deep Woods: Depth 10,ELEVATOR,DeepWoods
-7010,The Deep Woods Depth 20,The Deep Woods: Depth 20,ELEVATOR,DeepWoods
-7011,The Deep Woods Depth 30,The Deep Woods: Depth 30,ELEVATOR,DeepWoods
-7012,The Deep Woods Depth 40,The Deep Woods: Depth 40,ELEVATOR,DeepWoods
-7013,The Deep Woods Depth 50,The Deep Woods: Depth 50,ELEVATOR,DeepWoods
-7014,The Deep Woods Depth 60,The Deep Woods: Depth 60,ELEVATOR,DeepWoods
-7015,The Deep Woods Depth 70,The Deep Woods: Depth 70,ELEVATOR,DeepWoods
-7016,The Deep Woods Depth 80,The Deep Woods: Depth 80,ELEVATOR,DeepWoods
-7017,The Deep Woods Depth 90,The Deep Woods: Depth 90,ELEVATOR,DeepWoods
-7018,The Deep Woods Depth 100,The Deep Woods: Depth 100,ELEVATOR,DeepWoods
-7019,The Deep Woods Depth 50,Purify an Infested Lichtung,MANDATORY,DeepWoods
-7020,Skull Cavern Floor 25,Skull Cavern: Floor 25,ELEVATOR,Skull Cavern Elevator
-7021,Skull Cavern Floor 50,Skull Cavern: Floor 50,ELEVATOR,Skull Cavern Elevator
-7022,Skull Cavern Floor 75,Skull Cavern: Floor 75,ELEVATOR,Skull Cavern Elevator
-7023,Skull Cavern Floor 100,Skull Cavern: Floor 100,ELEVATOR,Skull Cavern Elevator
-7024,Skull Cavern Floor 125,Skull Cavern: Floor 125,ELEVATOR,Skull Cavern Elevator
-7025,Skull Cavern Floor 150,Skull Cavern: Floor 150,ELEVATOR,Skull Cavern Elevator
-7026,Skull Cavern Floor 175,Skull Cavern: Floor 175,ELEVATOR,Skull Cavern Elevator
-7027,Skull Cavern Floor 200,Skull Cavern: Floor 200,ELEVATOR,Skull Cavern Elevator
-7501,Town,Missing Envelope,"MANDATORY,QUEST",Ayeisha - The Postal Worker (Custom NPC)
-7502,Town,Lost Emerald Ring,"MANDATORY,QUEST",Ayeisha - The Postal Worker (Custom NPC)
-7503,Forest,Mr.Ginger's request,"MANDATORY,QUEST",Mister Ginger (cat npc)
-7504,Forest,Juna's Drink Request,"MANDATORY,QUEST",Juna - Roommate NPC
-7505,Forest,Juna's BFF Request,"MANDATORY,QUEST",Juna - Roommate NPC
-7506,Forest,Juna's Monster Mash,SPECIAL_ORDER_BOARD,Juna - Roommate NPC
+id,region,name,tags,mod_name
+1,Crafts Room,Spring Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
+2,Crafts Room,Summer Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
+3,Crafts Room,Fall Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
+4,Crafts Room,Winter Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
+5,Crafts Room,Construction Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
+6,Crafts Room,Exotic Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
+7,Pantry,Spring Crops Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,PANTRY_BUNDLE",
+8,Pantry,Summer Crops Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,PANTRY_BUNDLE",
+9,Pantry,Fall Crops Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,PANTRY_BUNDLE",
+10,Pantry,Quality Crops Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,PANTRY_BUNDLE",
+11,Pantry,Animal Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,PANTRY_BUNDLE",
+12,Pantry,Artisan Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,PANTRY_BUNDLE",
+13,Fish Tank,River Fish Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+14,Fish Tank,Lake Fish Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+15,Fish Tank,Ocean Fish Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+16,Fish Tank,Night Fishing Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+17,Fish Tank,Crab Pot Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+18,Fish Tank,Specialty Fish Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+19,Boiler Room,Blacksmith's Bundle,"BOILER_ROOM_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
+20,Boiler Room,Geologist's Bundle,"BOILER_ROOM_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
+21,Boiler Room,Adventurer's Bundle,"BOILER_ROOM_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
+22,Bulletin Board,Chef's Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
+23,Bulletin Board,Dye Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
+24,Bulletin Board,Field Research Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
+25,Bulletin Board,Fodder Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
+26,Bulletin Board,Enchanter's Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
+27,Vault,"2,500g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+28,Vault,"5,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+29,Vault,"10,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+30,Vault,"25,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+31,Abandoned JojaMart,The Missing Bundle,BUNDLE,
+32,Crafts Room,Complete Crafts Room,COMMUNITY_CENTER_ROOM,
+33,Pantry,Complete Pantry,COMMUNITY_CENTER_ROOM,
+34,Fish Tank,Complete Fish Tank,COMMUNITY_CENTER_ROOM,
+35,Boiler Room,Complete Boiler Room,COMMUNITY_CENTER_ROOM,
+36,Bulletin Board,Complete Bulletin Board,COMMUNITY_CENTER_ROOM,
+37,Vault,Complete Vault,COMMUNITY_CENTER_ROOM,
+39,Fish Tank,Deep Fishing Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+40,Crafts Room,Beach Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
+41,Crafts Room,Mines Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
+42,Crafts Room,Desert Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
+43,Crafts Room,Island Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
+44,Crafts Room,Sticky Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
+45,Crafts Room,Wild Medicine Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
+46,Crafts Room,Quality Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
+47,Boiler Room,Paleontologist's Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,BOILER_ROOM_BUNDLE",
+48,Boiler Room,Archaeologist's Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,BOILER_ROOM_BUNDLE",
+49,Pantry,Slime Farmer Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,PANTRY_BUNDLE",
+50,Pantry,Rare Crops Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,PANTRY_BUNDLE",
+51,Pantry,Fish Farmer's Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,PANTRY_BUNDLE",
+52,Pantry,Garden Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,PANTRY_BUNDLE",
+53,Pantry,Brewer's Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,PANTRY_BUNDLE",
+54,Pantry,Orchard Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,PANTRY_BUNDLE",
+55,Pantry,Island Crops Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,PANTRY_BUNDLE",
+56,Pantry,Agronomist's Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+57,Fish Tank,Tackle Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+58,Fish Tank,Trash Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+59,Fish Tank,Spring Fishing Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+60,Fish Tank,Summer Fishing Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+61,Fish Tank,Fall Fishing Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+62,Fish Tank,Winter Fishing Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+63,Fish Tank,Rain Fishing Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+64,Fish Tank,Quality Fish Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+65,Fish Tank,Master Fisher's Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+66,Fish Tank,Legendary Fish Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+67,Fish Tank,Island Fish Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+68,Fish Tank,Master Baiter Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
+69,Boiler Room,Recycling Bundle,"BOILER_ROOM_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
+70,Boiler Room,Treasure Hunter's Bundle,"BOILER_ROOM_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
+71,Boiler Room,Engineer's Bundle,"BOILER_ROOM_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
+72,Boiler Room,Demolition Bundle,"BOILER_ROOM_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
+73,Bulletin Board,Children's Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
+74,Bulletin Board,Forager's Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
+75,Bulletin Board,Home Cook's Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
+76,Bulletin Board,Bartender's Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
+77,Vault,250g Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+78,Vault,500g Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+79,Vault,"1,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+80,Vault,"2,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+81,Vault,"5,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+82,Vault,"1,500g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+83,Vault,"3,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+84,Vault,"3,500g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+85,Vault,"4,500g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+86,Vault,"6,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+87,Vault,"7,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+88,Vault,"9,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+89,Vault,"14,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+90,Vault,"15,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+91,Vault,"18,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+92,Vault,"20,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+93,Vault,"35,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+94,Vault,"40,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+95,Vault,"45,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+96,Vault,"100,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+97,Vault,Gambler's Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+98,Vault,Carnival Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+99,Vault,Walnut Hunter Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+100,Vault,Qi's Helper Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
+101,Pierre's General Store,Large Pack,BACKPACK,
+102,Pierre's General Store,Deluxe Pack,BACKPACK,
+103,Blacksmith Copper Upgrades,Copper Hoe Upgrade,"HOE_UPGRADE,TOOL_UPGRADE",
+104,Blacksmith Iron Upgrades,Iron Hoe Upgrade,"HOE_UPGRADE,TOOL_UPGRADE",
+105,Blacksmith Gold Upgrades,Gold Hoe Upgrade,"HOE_UPGRADE,TOOL_UPGRADE",
+106,Blacksmith Iridium Upgrades,Iridium Hoe Upgrade,"HOE_UPGRADE,TOOL_UPGRADE",
+107,Blacksmith Copper Upgrades,Copper Pickaxe Upgrade,"PICKAXE_UPGRADE,TOOL_UPGRADE",
+108,Blacksmith Iron Upgrades,Iron Pickaxe Upgrade,"PICKAXE_UPGRADE,TOOL_UPGRADE",
+109,Blacksmith Gold Upgrades,Gold Pickaxe Upgrade,"PICKAXE_UPGRADE,TOOL_UPGRADE",
+110,Blacksmith Iridium Upgrades,Iridium Pickaxe Upgrade,"PICKAXE_UPGRADE,TOOL_UPGRADE",
+111,Blacksmith Copper Upgrades,Copper Axe Upgrade,"AXE_UPGRADE,TOOL_UPGRADE",
+112,Blacksmith Iron Upgrades,Iron Axe Upgrade,"AXE_UPGRADE,TOOL_UPGRADE",
+113,Blacksmith Gold Upgrades,Gold Axe Upgrade,"AXE_UPGRADE,TOOL_UPGRADE",
+114,Blacksmith Iridium Upgrades,Iridium Axe Upgrade,"AXE_UPGRADE,TOOL_UPGRADE",
+115,Blacksmith Copper Upgrades,Copper Watering Can Upgrade,"TOOL_UPGRADE,WATERING_CAN_UPGRADE",
+116,Blacksmith Iron Upgrades,Iron Watering Can Upgrade,"TOOL_UPGRADE,WATERING_CAN_UPGRADE",
+117,Blacksmith Gold Upgrades,Gold Watering Can Upgrade,"TOOL_UPGRADE,WATERING_CAN_UPGRADE",
+118,Blacksmith Iridium Upgrades,Iridium Watering Can Upgrade,"TOOL_UPGRADE,WATERING_CAN_UPGRADE",
+119,Blacksmith Copper Upgrades,Copper Trash Can Upgrade,"TOOL_UPGRADE,TRASH_CAN_UPGRADE",
+120,Blacksmith Iron Upgrades,Iron Trash Can Upgrade,"TOOL_UPGRADE,TRASH_CAN_UPGRADE",
+121,Blacksmith Gold Upgrades,Gold Trash Can Upgrade,"TOOL_UPGRADE,TRASH_CAN_UPGRADE",
+122,Blacksmith Iridium Upgrades,Iridium Trash Can Upgrade,"TOOL_UPGRADE,TRASH_CAN_UPGRADE",
+123,Willy's Fish Shop,Purchase Training Rod,"FISHING_ROD_UPGRADE,TOOL_UPGRADE",
+124,Beach,Bamboo Pole Cutscene,"FISHING_ROD_UPGRADE,TOOL_UPGRADE",
+125,Willy's Fish Shop,Purchase Fiberglass Rod,"FISHING_ROD_UPGRADE,TOOL_UPGRADE",
+126,Willy's Fish Shop,Purchase Iridium Rod,"FISHING_ROD_UPGRADE,TOOL_UPGRADE",
+201,The Mines - Floor 10,The Mines Floor 10 Treasure,"MANDATORY,THE_MINES_TREASURE",
+202,The Mines - Floor 20,The Mines Floor 20 Treasure,"MANDATORY,THE_MINES_TREASURE",
+203,The Mines - Floor 40,The Mines Floor 40 Treasure,"MANDATORY,THE_MINES_TREASURE",
+204,The Mines - Floor 50,The Mines Floor 50 Treasure,"MANDATORY,THE_MINES_TREASURE",
+205,The Mines - Floor 60,The Mines Floor 60 Treasure,"MANDATORY,THE_MINES_TREASURE",
+206,The Mines - Floor 70,The Mines Floor 70 Treasure,"MANDATORY,THE_MINES_TREASURE",
+207,The Mines - Floor 80,The Mines Floor 80 Treasure,"MANDATORY,THE_MINES_TREASURE",
+208,The Mines - Floor 90,The Mines Floor 90 Treasure,"MANDATORY,THE_MINES_TREASURE",
+209,The Mines - Floor 100,The Mines Floor 100 Treasure,"MANDATORY,THE_MINES_TREASURE",
+210,The Mines - Floor 110,The Mines Floor 110 Treasure,"MANDATORY,THE_MINES_TREASURE",
+211,The Mines - Floor 120,The Mines Floor 120 Treasure,"MANDATORY,THE_MINES_TREASURE",
+212,Quarry Mine,Grim Reaper statue,MANDATORY,
+213,The Mines,The Mines Entrance Cutscene,MANDATORY,
+214,The Mines - Floor 5,Floor 5 Elevator,ELEVATOR,
+215,The Mines - Floor 10,Floor 10 Elevator,ELEVATOR,
+216,The Mines - Floor 15,Floor 15 Elevator,ELEVATOR,
+217,The Mines - Floor 20,Floor 20 Elevator,ELEVATOR,
+218,The Mines - Floor 25,Floor 25 Elevator,ELEVATOR,
+219,The Mines - Floor 30,Floor 30 Elevator,ELEVATOR,
+220,The Mines - Floor 35,Floor 35 Elevator,ELEVATOR,
+221,The Mines - Floor 40,Floor 40 Elevator,ELEVATOR,
+222,The Mines - Floor 45,Floor 45 Elevator,ELEVATOR,
+223,The Mines - Floor 50,Floor 50 Elevator,ELEVATOR,
+224,The Mines - Floor 55,Floor 55 Elevator,ELEVATOR,
+225,The Mines - Floor 60,Floor 60 Elevator,ELEVATOR,
+226,The Mines - Floor 65,Floor 65 Elevator,ELEVATOR,
+227,The Mines - Floor 70,Floor 70 Elevator,ELEVATOR,
+228,The Mines - Floor 75,Floor 75 Elevator,ELEVATOR,
+229,The Mines - Floor 80,Floor 80 Elevator,ELEVATOR,
+230,The Mines - Floor 85,Floor 85 Elevator,ELEVATOR,
+231,The Mines - Floor 90,Floor 90 Elevator,ELEVATOR,
+232,The Mines - Floor 95,Floor 95 Elevator,ELEVATOR,
+233,The Mines - Floor 100,Floor 100 Elevator,ELEVATOR,
+234,The Mines - Floor 105,Floor 105 Elevator,ELEVATOR,
+235,The Mines - Floor 110,Floor 110 Elevator,ELEVATOR,
+236,The Mines - Floor 115,Floor 115 Elevator,ELEVATOR,
+237,The Mines - Floor 120,Floor 120 Elevator,ELEVATOR,
+250,Shipping,Demetrius's Breakthrough,MANDATORY
+251,Volcano - Floor 10,Volcano Caldera Treasure,"GINGER_ISLAND,MANDATORY",
+301,Farming,Level 1 Farming,"FARMING_LEVEL,SKILL_LEVEL",
+302,Farming,Level 2 Farming,"FARMING_LEVEL,SKILL_LEVEL",
+303,Farming,Level 3 Farming,"FARMING_LEVEL,SKILL_LEVEL",
+304,Farming,Level 4 Farming,"FARMING_LEVEL,SKILL_LEVEL",
+305,Farming,Level 5 Farming,"FARMING_LEVEL,SKILL_LEVEL",
+306,Farming,Level 6 Farming,"FARMING_LEVEL,SKILL_LEVEL",
+307,Farming,Level 7 Farming,"FARMING_LEVEL,SKILL_LEVEL",
+308,Farming,Level 8 Farming,"FARMING_LEVEL,SKILL_LEVEL",
+309,Farming,Level 9 Farming,"FARMING_LEVEL,SKILL_LEVEL",
+310,Farming,Level 10 Farming,"FARMING_LEVEL,SKILL_LEVEL",
+311,Fishing,Level 1 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
+312,Fishing,Level 2 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
+313,Fishing,Level 3 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
+314,Fishing,Level 4 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
+315,Fishing,Level 5 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
+316,Fishing,Level 6 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
+317,Fishing,Level 7 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
+318,Fishing,Level 8 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
+319,Fishing,Level 9 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
+320,Fishing,Level 10 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
+321,Forest,Level 1 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
+322,Forest,Level 2 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
+323,Forest,Level 3 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
+324,Forest,Level 4 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
+325,Forest,Level 5 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
+326,Secret Woods,Level 6 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
+327,Secret Woods,Level 7 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
+328,Secret Woods,Level 8 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
+329,Secret Woods,Level 9 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
+330,Secret Woods,Level 10 Foraging,"FORAGING_LEVEL,SKILL_LEVEL",
+331,The Mines - Floor 20,Level 1 Mining,"MINING_LEVEL,SKILL_LEVEL",
+332,The Mines - Floor 30,Level 2 Mining,"MINING_LEVEL,SKILL_LEVEL",
+333,The Mines - Floor 40,Level 3 Mining,"MINING_LEVEL,SKILL_LEVEL",
+334,The Mines - Floor 50,Level 4 Mining,"MINING_LEVEL,SKILL_LEVEL",
+335,The Mines - Floor 60,Level 5 Mining,"MINING_LEVEL,SKILL_LEVEL",
+336,The Mines - Floor 70,Level 6 Mining,"MINING_LEVEL,SKILL_LEVEL",
+337,The Mines - Floor 80,Level 7 Mining,"MINING_LEVEL,SKILL_LEVEL",
+338,The Mines - Floor 90,Level 8 Mining,"MINING_LEVEL,SKILL_LEVEL",
+339,The Mines - Floor 100,Level 9 Mining,"MINING_LEVEL,SKILL_LEVEL",
+340,The Mines - Floor 110,Level 10 Mining,"MINING_LEVEL,SKILL_LEVEL",
+341,The Mines - Floor 20,Level 1 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
+342,The Mines - Floor 30,Level 2 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
+343,The Mines - Floor 40,Level 3 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
+344,The Mines - Floor 50,Level 4 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
+345,The Mines - Floor 60,Level 5 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
+346,The Mines - Floor 70,Level 6 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
+347,The Mines - Floor 80,Level 7 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
+348,The Mines - Floor 90,Level 8 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
+349,The Mines - Floor 100,Level 9 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
+350,The Mines - Floor 110,Level 10 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
+401,Carpenter Shop,Coop Blueprint,BUILDING_BLUEPRINT,
+402,Carpenter Shop,Big Coop Blueprint,BUILDING_BLUEPRINT,
+403,Carpenter Shop,Deluxe Coop Blueprint,BUILDING_BLUEPRINT,
+404,Carpenter Shop,Barn Blueprint,BUILDING_BLUEPRINT,
+405,Carpenter Shop,Big Barn Blueprint,BUILDING_BLUEPRINT,
+406,Carpenter Shop,Deluxe Barn Blueprint,BUILDING_BLUEPRINT,
+407,Carpenter Shop,Well Blueprint,BUILDING_BLUEPRINT,
+408,Carpenter Shop,Silo Blueprint,BUILDING_BLUEPRINT,
+409,Carpenter Shop,Mill Blueprint,BUILDING_BLUEPRINT,
+410,Carpenter Shop,Shed Blueprint,BUILDING_BLUEPRINT,
+411,Carpenter Shop,Big Shed Blueprint,BUILDING_BLUEPRINT,
+412,Carpenter Shop,Fish Pond Blueprint,BUILDING_BLUEPRINT,
+413,Carpenter Shop,Stable Blueprint,BUILDING_BLUEPRINT,
+414,Carpenter Shop,Slime Hutch Blueprint,BUILDING_BLUEPRINT,
+415,Carpenter Shop,Shipping Bin Blueprint,BUILDING_BLUEPRINT,
+416,Carpenter Shop,Kitchen Blueprint,BUILDING_BLUEPRINT,
+417,Carpenter Shop,Kids Room Blueprint,BUILDING_BLUEPRINT,
+418,Carpenter Shop,Cellar Blueprint,BUILDING_BLUEPRINT,
+501,Town,Introductions,"STORY_QUEST",
+502,Town,How To Win Friends,"STORY_QUEST",
+503,Farm,Getting Started,"STORY_QUEST",
+504,Farm,Raising Animals,"STORY_QUEST",
+505,Farm,Advancement,"STORY_QUEST",
+506,Museum,Archaeology,"STORY_QUEST",
+507,Wizard Tower,Meet The Wizard,"STORY_QUEST",
+508,The Mines - Floor 5,Forging Ahead,"STORY_QUEST",
+509,The Mines - Floor 10,Smelting,"STORY_QUEST",
+510,The Mines - Floor 15,Initiation,"STORY_QUEST",
+511,Mountain,Robin's Lost Axe,"STORY_QUEST",
+512,Town,Jodi's Request,"STORY_QUEST",
+513,Town,"Mayor's ""Shorts""","STORY_QUEST",
+514,Mountain,Blackberry Basket,"STORY_QUEST",
+515,Marnie's Ranch,Marnie's Request,"STORY_QUEST",
+516,Town,Pam Is Thirsty,"STORY_QUEST",
+517,Wizard Tower,A Dark Reagent,"STORY_QUEST",
+518,Forest,Cow's Delight,"STORY_QUEST",
+519,Skull Cavern Entrance,The Skull Key,"STORY_QUEST",
+520,Mountain,Crop Research,"STORY_QUEST",
+521,Alex's House,Knee Therapy,"STORY_QUEST",
+522,Mountain,Robin's Request,"STORY_QUEST",
+523,Skull Cavern Floor 25,Qi's Challenge,"STORY_QUEST",
+524,Desert,The Mysterious Qi,"STORY_QUEST",
+525,Town,Carving Pumpkins,"STORY_QUEST",
+526,Town,A Winter Mystery,"STORY_QUEST",
+527,Secret Woods,Strange Note,"STORY_QUEST",
+528,Skull Cavern Floor 100,Cryptic Note,"STORY_QUEST",
+529,Town,Fresh Fruit,"STORY_QUEST",
+530,Mountain,Aquatic Research,"STORY_QUEST",
+531,Town,A Soldier's Star,"STORY_QUEST",
+532,Town,Mayor's Need,"STORY_QUEST",
+533,Saloon,Wanted: Lobster,"STORY_QUEST",
+534,Town,Pam Needs Juice,"STORY_QUEST",
+535,Sam's House,Fish Casserole,"STORY_QUEST",
+536,Beach,Catch A Squid,"STORY_QUEST",
+537,Saloon,Fish Stew,"STORY_QUEST",
+538,Pierre's General Store,Pierre's Notice,"STORY_QUEST",
+539,Clint's Blacksmith,Clint's Attempt,"STORY_QUEST",
+540,Town,A Favor For Clint,"STORY_QUEST",
+541,Wizard Tower,Staff Of Power,"STORY_QUEST",
+542,Town,Granny's Gift,"STORY_QUEST",
+543,Desert,Exotic Spirits,"STORY_QUEST",
+544,Fishing,Catch a Lingcod,"STORY_QUEST",
+545,Island West,The Pirate's Wife,"GINGER_ISLAND,STORY_QUEST",
+546,Mutant Bug Lair,Dark Talisman,"STORY_QUEST",
+547,Witch's Swamp,Goblin Problem,"STORY_QUEST",
+548,Witch's Hut,Magic Ink,"STORY_QUEST",
+601,JotPK World 1,JotPK: Boots 1,"ARCADE_MACHINE,JOTPK",
+602,JotPK World 1,JotPK: Boots 2,"ARCADE_MACHINE,JOTPK",
+603,JotPK World 1,JotPK: Gun 1,"ARCADE_MACHINE,JOTPK",
+604,JotPK World 2,JotPK: Gun 2,"ARCADE_MACHINE,JOTPK",
+605,JotPK World 2,JotPK: Gun 3,"ARCADE_MACHINE,JOTPK",
+606,JotPK World 3,JotPK: Super Gun,"ARCADE_MACHINE,JOTPK",
+607,JotPK World 1,JotPK: Ammo 1,"ARCADE_MACHINE,JOTPK",
+608,JotPK World 2,JotPK: Ammo 2,"ARCADE_MACHINE,JOTPK",
+609,JotPK World 3,JotPK: Ammo 3,"ARCADE_MACHINE,JOTPK",
+610,JotPK World 1,JotPK: Cowboy 1,"ARCADE_MACHINE,JOTPK",
+611,JotPK World 2,JotPK: Cowboy 2,"ARCADE_MACHINE,JOTPK",
+612,Junimo Kart 1,Junimo Kart: Crumble Cavern,"ARCADE_MACHINE,JUNIMO_KART",
+613,Junimo Kart 1,Junimo Kart: Slippery Slopes,"ARCADE_MACHINE,JUNIMO_KART",
+614,Junimo Kart 2,Junimo Kart: Secret Level,"ARCADE_MACHINE,JUNIMO_KART",
+615,Junimo Kart 2,Junimo Kart: The Gem Sea Giant,"ARCADE_MACHINE,JUNIMO_KART",
+616,Junimo Kart 2,Junimo Kart: Slomp's Stomp,"ARCADE_MACHINE,JUNIMO_KART",
+617,Junimo Kart 2,Junimo Kart: Ghastly Galleon,"ARCADE_MACHINE,JUNIMO_KART",
+618,Junimo Kart 3,Junimo Kart: Glowshroom Grotto,"ARCADE_MACHINE,JUNIMO_KART",
+619,Junimo Kart 3,Junimo Kart: Red Hot Rollercoaster,"ARCADE_MACHINE,JUNIMO_KART",
+620,JotPK World 3,Journey of the Prairie King Victory,"ARCADE_MACHINE_VICTORY,JOTPK",
+621,Junimo Kart 3,Junimo Kart: Sunset Speedway (Victory),"ARCADE_MACHINE_VICTORY,JUNIMO_KART",
+701,Secret Woods,Old Master Cannoli,MANDATORY,
+702,Beach,Beach Bridge Repair,MANDATORY,
+703,Desert,Galaxy Sword Shrine,MANDATORY,
+704,Farmhouse,Have a Baby,BABY,
+705,Farmhouse,Have Another Baby,BABY,
+706,Farmhouse,Spouse Stardrop,,
+707,Sewer,Krobus Stardrop,MANDATORY,
+801,Forest,Help Wanted: Gathering 1,HELP_WANTED,
+802,Forest,Help Wanted: Gathering 2,HELP_WANTED,
+803,Forest,Help Wanted: Gathering 3,HELP_WANTED,
+804,Forest,Help Wanted: Gathering 4,HELP_WANTED,
+805,Forest,Help Wanted: Gathering 5,HELP_WANTED,
+806,Forest,Help Wanted: Gathering 6,HELP_WANTED,
+807,Forest,Help Wanted: Gathering 7,HELP_WANTED,
+808,Forest,Help Wanted: Gathering 8,HELP_WANTED,
+811,The Mines - Floor 5,Help Wanted: Slay Monsters 1,HELP_WANTED,
+812,The Mines - Floor 15,Help Wanted: Slay Monsters 2,HELP_WANTED,
+813,The Mines - Floor 25,Help Wanted: Slay Monsters 3,HELP_WANTED,
+814,The Mines - Floor 35,Help Wanted: Slay Monsters 4,HELP_WANTED,
+815,The Mines - Floor 45,Help Wanted: Slay Monsters 5,HELP_WANTED,
+816,The Mines - Floor 55,Help Wanted: Slay Monsters 6,HELP_WANTED,
+817,The Mines - Floor 65,Help Wanted: Slay Monsters 7,HELP_WANTED,
+818,The Mines - Floor 75,Help Wanted: Slay Monsters 8,HELP_WANTED,
+821,Fishing,Help Wanted: Fishing 1,HELP_WANTED,
+822,Fishing,Help Wanted: Fishing 2,HELP_WANTED,
+823,Fishing,Help Wanted: Fishing 3,HELP_WANTED,
+824,Fishing,Help Wanted: Fishing 4,HELP_WANTED,
+825,Fishing,Help Wanted: Fishing 5,HELP_WANTED,
+826,Fishing,Help Wanted: Fishing 6,HELP_WANTED,
+827,Fishing,Help Wanted: Fishing 7,HELP_WANTED,
+828,Fishing,Help Wanted: Fishing 8,HELP_WANTED,
+841,Town,Help Wanted: Item Delivery 1,HELP_WANTED,
+842,Town,Help Wanted: Item Delivery 2,HELP_WANTED,
+843,Town,Help Wanted: Item Delivery 3,HELP_WANTED,
+844,Town,Help Wanted: Item Delivery 4,HELP_WANTED,
+845,Town,Help Wanted: Item Delivery 5,HELP_WANTED,
+846,Town,Help Wanted: Item Delivery 6,HELP_WANTED,
+847,Town,Help Wanted: Item Delivery 7,HELP_WANTED,
+848,Town,Help Wanted: Item Delivery 8,HELP_WANTED,
+849,Town,Help Wanted: Item Delivery 9,HELP_WANTED,
+850,Town,Help Wanted: Item Delivery 10,HELP_WANTED,
+851,Town,Help Wanted: Item Delivery 11,HELP_WANTED,
+852,Town,Help Wanted: Item Delivery 12,HELP_WANTED,
+853,Town,Help Wanted: Item Delivery 13,HELP_WANTED,
+854,Town,Help Wanted: Item Delivery 14,HELP_WANTED,
+855,Town,Help Wanted: Item Delivery 15,HELP_WANTED,
+856,Town,Help Wanted: Item Delivery 16,HELP_WANTED,
+857,Town,Help Wanted: Item Delivery 17,HELP_WANTED,
+858,Town,Help Wanted: Item Delivery 18,HELP_WANTED,
+859,Town,Help Wanted: Item Delivery 19,HELP_WANTED,
+860,Town,Help Wanted: Item Delivery 20,HELP_WANTED,
+861,Town,Help Wanted: Item Delivery 21,HELP_WANTED,
+862,Town,Help Wanted: Item Delivery 22,HELP_WANTED,
+863,Town,Help Wanted: Item Delivery 23,HELP_WANTED,
+864,Town,Help Wanted: Item Delivery 24,HELP_WANTED,
+865,Town,Help Wanted: Item Delivery 25,HELP_WANTED,
+866,Town,Help Wanted: Item Delivery 26,HELP_WANTED,
+867,Town,Help Wanted: Item Delivery 27,HELP_WANTED,
+868,Town,Help Wanted: Item Delivery 28,HELP_WANTED,
+869,Town,Help Wanted: Item Delivery 29,HELP_WANTED,
+870,Town,Help Wanted: Item Delivery 30,HELP_WANTED,
+871,Town,Help Wanted: Item Delivery 31,HELP_WANTED,
+872,Town,Help Wanted: Item Delivery 32,HELP_WANTED,
+901,Traveling Cart Sunday,Traveling Merchant Sunday Item 1,"MANDATORY,TRAVELING_MERCHANT",
+902,Traveling Cart Sunday,Traveling Merchant Sunday Item 2,"MANDATORY,TRAVELING_MERCHANT",
+903,Traveling Cart Sunday,Traveling Merchant Sunday Item 3,"MANDATORY,TRAVELING_MERCHANT",
+911,Traveling Cart Monday,Traveling Merchant Monday Item 1,"MANDATORY,TRAVELING_MERCHANT",
+912,Traveling Cart Monday,Traveling Merchant Monday Item 2,"MANDATORY,TRAVELING_MERCHANT",
+913,Traveling Cart Monday,Traveling Merchant Monday Item 3,"MANDATORY,TRAVELING_MERCHANT",
+921,Traveling Cart Tuesday,Traveling Merchant Tuesday Item 1,"MANDATORY,TRAVELING_MERCHANT",
+922,Traveling Cart Tuesday,Traveling Merchant Tuesday Item 2,"MANDATORY,TRAVELING_MERCHANT",
+923,Traveling Cart Tuesday,Traveling Merchant Tuesday Item 3,"MANDATORY,TRAVELING_MERCHANT",
+931,Traveling Cart Wednesday,Traveling Merchant Wednesday Item 1,"MANDATORY,TRAVELING_MERCHANT",
+932,Traveling Cart Wednesday,Traveling Merchant Wednesday Item 2,"MANDATORY,TRAVELING_MERCHANT",
+933,Traveling Cart Wednesday,Traveling Merchant Wednesday Item 3,"MANDATORY,TRAVELING_MERCHANT",
+941,Traveling Cart Thursday,Traveling Merchant Thursday Item 1,"MANDATORY,TRAVELING_MERCHANT",
+942,Traveling Cart Thursday,Traveling Merchant Thursday Item 2,"MANDATORY,TRAVELING_MERCHANT",
+943,Traveling Cart Thursday,Traveling Merchant Thursday Item 3,"MANDATORY,TRAVELING_MERCHANT",
+951,Traveling Cart Friday,Traveling Merchant Friday Item 1,"MANDATORY,TRAVELING_MERCHANT",
+952,Traveling Cart Friday,Traveling Merchant Friday Item 2,"MANDATORY,TRAVELING_MERCHANT",
+953,Traveling Cart Friday,Traveling Merchant Friday Item 3,"MANDATORY,TRAVELING_MERCHANT",
+961,Traveling Cart Saturday,Traveling Merchant Saturday Item 1,"MANDATORY,TRAVELING_MERCHANT",
+962,Traveling Cart Saturday,Traveling Merchant Saturday Item 2,"MANDATORY,TRAVELING_MERCHANT",
+963,Traveling Cart Saturday,Traveling Merchant Saturday Item 3,"MANDATORY,TRAVELING_MERCHANT",
+1001,Fishing,Fishsanity: Carp,FISHSANITY,
+1002,Fishing,Fishsanity: Herring,FISHSANITY,
+1003,Fishing,Fishsanity: Smallmouth Bass,FISHSANITY,
+1004,Fishing,Fishsanity: Anchovy,FISHSANITY,
+1005,Fishing,Fishsanity: Sardine,FISHSANITY,
+1006,Fishing,Fishsanity: Sunfish,FISHSANITY,
+1007,Fishing,Fishsanity: Perch,FISHSANITY,
+1008,Fishing,Fishsanity: Chub,FISHSANITY,
+1009,Fishing,Fishsanity: Bream,FISHSANITY,
+1010,Fishing,Fishsanity: Red Snapper,FISHSANITY,
+1011,Fishing,Fishsanity: Sea Cucumber,FISHSANITY,
+1012,Fishing,Fishsanity: Rainbow Trout,FISHSANITY,
+1013,Fishing,Fishsanity: Walleye,FISHSANITY,
+1014,Fishing,Fishsanity: Shad,FISHSANITY,
+1015,Fishing,Fishsanity: Bullhead,FISHSANITY,
+1016,Fishing,Fishsanity: Largemouth Bass,FISHSANITY,
+1017,Fishing,Fishsanity: Salmon,FISHSANITY,
+1018,Fishing,Fishsanity: Ghostfish,FISHSANITY,
+1019,Fishing,Fishsanity: Tilapia,FISHSANITY,
+1020,Fishing,Fishsanity: Woodskip,FISHSANITY,
+1021,Fishing,Fishsanity: Flounder,FISHSANITY,
+1022,Fishing,Fishsanity: Halibut,FISHSANITY,
+1023,Fishing,Fishsanity: Lionfish,"FISHSANITY,GINGER_ISLAND",
+1024,Fishing,Fishsanity: Slimejack,FISHSANITY,
+1025,Fishing,Fishsanity: Midnight Carp,FISHSANITY,
+1026,Fishing,Fishsanity: Red Mullet,FISHSANITY,
+1027,Fishing,Fishsanity: Pike,FISHSANITY,
+1028,Fishing,Fishsanity: Tiger Trout,FISHSANITY,
+1029,Fishing,Fishsanity: Blue Discus,"FISHSANITY,GINGER_ISLAND",
+1030,Fishing,Fishsanity: Albacore,FISHSANITY,
+1031,Fishing,Fishsanity: Sandfish,FISHSANITY,
+1032,Fishing,Fishsanity: Stonefish,FISHSANITY,
+1033,Fishing,Fishsanity: Tuna,FISHSANITY,
+1034,Fishing,Fishsanity: Eel,FISHSANITY,
+1035,Fishing,Fishsanity: Catfish,FISHSANITY,
+1036,Fishing,Fishsanity: Squid,FISHSANITY,
+1037,Fishing,Fishsanity: Sturgeon,FISHSANITY,
+1038,Fishing,Fishsanity: Dorado,FISHSANITY,
+1039,Fishing,Fishsanity: Pufferfish,FISHSANITY,
+1040,Fishing,Fishsanity: Void Salmon,FISHSANITY,
+1041,Fishing,Fishsanity: Super Cucumber,FISHSANITY,
+1042,Fishing,Fishsanity: Stingray,"FISHSANITY,GINGER_ISLAND",
+1043,Fishing,Fishsanity: Ice Pip,FISHSANITY,
+1044,Fishing,Fishsanity: Lingcod,FISHSANITY,
+1045,Desert,Fishsanity: Scorpion Carp,FISHSANITY,
+1046,Fishing,Fishsanity: Lava Eel,FISHSANITY,
+1047,Fishing,Fishsanity: Octopus,FISHSANITY,
+1048,Fishing,Fishsanity: Midnight Squid,FISHSANITY,
+1049,Fishing,Fishsanity: Spook Fish,FISHSANITY,
+1050,Fishing,Fishsanity: Blobfish,FISHSANITY,
+1051,Fishing,Fishsanity: Crimsonfish,FISHSANITY,
+1052,Fishing,Fishsanity: Angler,FISHSANITY,
+1053,Fishing,Fishsanity: Legend,FISHSANITY,
+1054,Fishing,Fishsanity: Glacierfish,FISHSANITY,
+1055,Fishing,Fishsanity: Mutant Carp,FISHSANITY,
+1056,Town,Fishsanity: Crayfish,FISHSANITY,
+1057,Town,Fishsanity: Snail,FISHSANITY,
+1058,Town,Fishsanity: Periwinkle,FISHSANITY,
+1059,Beach,Fishsanity: Lobster,FISHSANITY,
+1060,Beach,Fishsanity: Clam,FISHSANITY,
+1061,Beach,Fishsanity: Crab,FISHSANITY,
+1062,Beach,Fishsanity: Cockle,FISHSANITY,
+1063,Beach,Fishsanity: Mussel,FISHSANITY,
+1064,Beach,Fishsanity: Shrimp,FISHSANITY,
+1065,Beach,Fishsanity: Oyster,FISHSANITY,
+1066,Beach,Fishsanity: Son of Crimsonfish,"FISHSANITY,GINGER_ISLAND,REQUIRES_QI_ORDERS",
+1067,Beach,Fishsanity: Glacierfish Jr.,"FISHSANITY,GINGER_ISLAND,REQUIRES_QI_ORDERS",
+1068,Beach,Fishsanity: Legend II,"FISHSANITY,GINGER_ISLAND,REQUIRES_QI_ORDERS",
+1069,Beach,Fishsanity: Ms. Angler,"FISHSANITY,GINGER_ISLAND,REQUIRES_QI_ORDERS",
+1070,Beach,Fishsanity: Radioactive Carp,"FISHSANITY,GINGER_ISLAND,REQUIRES_QI_ORDERS",
+1100,Museum,Museumsanity: 5 Donations,MUSEUM_MILESTONES,
+1101,Museum,Museumsanity: 10 Donations,MUSEUM_MILESTONES,
+1102,Museum,Museumsanity: 15 Donations,MUSEUM_MILESTONES,
+1103,Museum,Museumsanity: 20 Donations,MUSEUM_MILESTONES,
+1104,Museum,Museumsanity: 25 Donations,MUSEUM_MILESTONES,
+1105,Museum,Museumsanity: 30 Donations,MUSEUM_MILESTONES,
+1106,Museum,Museumsanity: 35 Donations,MUSEUM_MILESTONES,
+1107,Museum,Museumsanity: 40 Donations,MUSEUM_MILESTONES,
+1108,Museum,Museumsanity: 50 Donations,MUSEUM_MILESTONES,
+1109,Museum,Museumsanity: 60 Donations,MUSEUM_MILESTONES,
+1110,Museum,Museumsanity: 70 Donations,MUSEUM_MILESTONES,
+1111,Museum,Museumsanity: 80 Donations,MUSEUM_MILESTONES,
+1112,Museum,Museumsanity: 90 Donations,MUSEUM_MILESTONES,
+1113,Museum,Museumsanity: 95 Donations,MUSEUM_MILESTONES,
+1114,Museum,Museumsanity: 11 Minerals,MUSEUM_MILESTONES,
+1115,Museum,Museumsanity: 21 Minerals,MUSEUM_MILESTONES,
+1116,Museum,Museumsanity: 31 Minerals,MUSEUM_MILESTONES,
+1117,Museum,Museumsanity: 41 Minerals,MUSEUM_MILESTONES,
+1118,Museum,Museumsanity: 50 Minerals,MUSEUM_MILESTONES,
+1119,Museum,Museumsanity: 3 Artifacts,MUSEUM_MILESTONES,
+1120,Museum,Museumsanity: 6 Artifacts,MUSEUM_MILESTONES,
+1121,Museum,Museumsanity: 9 Artifacts,MUSEUM_MILESTONES,
+1122,Museum,Museumsanity: 11 Artifacts,MUSEUM_MILESTONES,
+1123,Museum,Museumsanity: 15 Artifacts,MUSEUM_MILESTONES,
+1124,Museum,Museumsanity: 20 Artifacts,MUSEUM_MILESTONES,
+1125,Museum,Museumsanity: Dwarf Scrolls,MUSEUM_MILESTONES,
+1126,Museum,Museumsanity: Skeleton Front,MUSEUM_MILESTONES,
+1127,Museum,Museumsanity: Skeleton Middle,MUSEUM_MILESTONES,
+1128,Museum,Museumsanity: Skeleton Back,MUSEUM_MILESTONES,
+1201,Museum,Museumsanity: Dwarf Scroll I,MUSEUM_DONATIONS,
+1202,Museum,Museumsanity: Dwarf Scroll II,MUSEUM_DONATIONS,
+1203,Museum,Museumsanity: Dwarf Scroll III,MUSEUM_DONATIONS,
+1204,Museum,Museumsanity: Dwarf Scroll IV,MUSEUM_DONATIONS,
+1205,Museum,Museumsanity: Chipped Amphora,MUSEUM_DONATIONS,
+1206,Museum,Museumsanity: Arrowhead,MUSEUM_DONATIONS,
+1207,Museum,Museumsanity: Ancient Doll,MUSEUM_DONATIONS,
+1208,Museum,Museumsanity: Elvish Jewelry,MUSEUM_DONATIONS,
+1209,Museum,Museumsanity: Chewing Stick,MUSEUM_DONATIONS,
+1210,Museum,Museumsanity: Ornamental Fan,MUSEUM_DONATIONS,
+1211,Museum,Museumsanity: Dinosaur Egg,MUSEUM_DONATIONS,
+1212,Museum,Museumsanity: Rare Disc,MUSEUM_DONATIONS,
+1213,Museum,Museumsanity: Ancient Sword,MUSEUM_DONATIONS,
+1214,Museum,Museumsanity: Rusty Spoon,MUSEUM_DONATIONS,
+1215,Museum,Museumsanity: Rusty Spur,MUSEUM_DONATIONS,
+1216,Museum,Museumsanity: Rusty Cog,MUSEUM_DONATIONS,
+1217,Museum,Museumsanity: Chicken Statue,MUSEUM_DONATIONS,
+1218,Museum,Museumsanity: Ancient Seed,"MUSEUM_DONATIONS,MUSEUM_MILESTONES",
+1219,Museum,Museumsanity: Prehistoric Tool,MUSEUM_DONATIONS,
+1220,Museum,Museumsanity: Dried Starfish,MUSEUM_DONATIONS,
+1221,Museum,Museumsanity: Anchor,MUSEUM_DONATIONS,
+1222,Museum,Museumsanity: Glass Shards,MUSEUM_DONATIONS,
+1223,Museum,Museumsanity: Bone Flute,MUSEUM_DONATIONS,
+1224,Museum,Museumsanity: Prehistoric Handaxe,MUSEUM_DONATIONS,
+1225,Museum,Museumsanity: Dwarvish Helm,MUSEUM_DONATIONS,
+1226,Museum,Museumsanity: Dwarf Gadget,MUSEUM_DONATIONS,
+1227,Museum,Museumsanity: Ancient Drum,MUSEUM_DONATIONS,
+1228,Museum,Museumsanity: Golden Mask,MUSEUM_DONATIONS,
+1229,Museum,Museumsanity: Golden Relic,MUSEUM_DONATIONS,
+1230,Museum,Museumsanity: Strange Doll (Green),MUSEUM_DONATIONS,
+1231,Museum,Museumsanity: Strange Doll,MUSEUM_DONATIONS,
+1232,Museum,Museumsanity: Prehistoric Scapula,MUSEUM_DONATIONS,
+1233,Museum,Museumsanity: Prehistoric Tibia,MUSEUM_DONATIONS,
+1234,Museum,Museumsanity: Prehistoric Skull,MUSEUM_DONATIONS,
+1235,Museum,Museumsanity: Skeletal Hand,MUSEUM_DONATIONS,
+1236,Museum,Museumsanity: Prehistoric Rib,MUSEUM_DONATIONS,
+1237,Museum,Museumsanity: Prehistoric Vertebra,MUSEUM_DONATIONS,
+1238,Museum,Museumsanity: Skeletal Tail,MUSEUM_DONATIONS,
+1239,Museum,Museumsanity: Nautilus Fossil,MUSEUM_DONATIONS,
+1240,Museum,Museumsanity: Amphibian Fossil,MUSEUM_DONATIONS,
+1241,Museum,Museumsanity: Palm Fossil,MUSEUM_DONATIONS,
+1242,Museum,Museumsanity: Trilobite,MUSEUM_DONATIONS,
+1243,Museum,Museumsanity: Quartz,MUSEUM_DONATIONS,
+1244,Museum,Museumsanity: Fire Quartz,MUSEUM_DONATIONS,
+1245,Museum,Museumsanity: Frozen Tear,MUSEUM_DONATIONS,
+1246,Museum,Museumsanity: Earth Crystal,MUSEUM_DONATIONS,
+1247,Museum,Museumsanity: Emerald,MUSEUM_DONATIONS,
+1248,Museum,Museumsanity: Aquamarine,MUSEUM_DONATIONS,
+1249,Museum,Museumsanity: Ruby,MUSEUM_DONATIONS,
+1250,Museum,Museumsanity: Amethyst,MUSEUM_DONATIONS,
+1251,Museum,Museumsanity: Topaz,MUSEUM_DONATIONS,
+1252,Museum,Museumsanity: Jade,MUSEUM_DONATIONS,
+1253,Museum,Museumsanity: Diamond,MUSEUM_DONATIONS,
+1254,Museum,Museumsanity: Prismatic Shard,MUSEUM_DONATIONS,
+1255,Museum,Museumsanity: Alamite,MUSEUM_DONATIONS,
+1256,Museum,Museumsanity: Bixite,MUSEUM_DONATIONS,
+1257,Museum,Museumsanity: Baryte,MUSEUM_DONATIONS,
+1258,Museum,Museumsanity: Aerinite,MUSEUM_DONATIONS,
+1259,Museum,Museumsanity: Calcite,MUSEUM_DONATIONS,
+1260,Museum,Museumsanity: Dolomite,MUSEUM_DONATIONS,
+1261,Museum,Museumsanity: Esperite,MUSEUM_DONATIONS,
+1262,Museum,Museumsanity: Fluorapatite,MUSEUM_DONATIONS,
+1263,Museum,Museumsanity: Geminite,MUSEUM_DONATIONS,
+1264,Museum,Museumsanity: Helvite,MUSEUM_DONATIONS,
+1265,Museum,Museumsanity: Jamborite,MUSEUM_DONATIONS,
+1266,Museum,Museumsanity: Jagoite,MUSEUM_DONATIONS,
+1267,Museum,Museumsanity: Kyanite,MUSEUM_DONATIONS,
+1268,Museum,Museumsanity: Lunarite,MUSEUM_DONATIONS,
+1269,Museum,Museumsanity: Malachite,MUSEUM_DONATIONS,
+1270,Museum,Museumsanity: Neptunite,MUSEUM_DONATIONS,
+1271,Museum,Museumsanity: Lemon Stone,MUSEUM_DONATIONS,
+1272,Museum,Museumsanity: Nekoite,MUSEUM_DONATIONS,
+1273,Museum,Museumsanity: Orpiment,MUSEUM_DONATIONS,
+1274,Museum,Museumsanity: Petrified Slime,MUSEUM_DONATIONS,
+1275,Museum,Museumsanity: Thunder Egg,MUSEUM_DONATIONS,
+1276,Museum,Museumsanity: Pyrite,MUSEUM_DONATIONS,
+1277,Museum,Museumsanity: Ocean Stone,MUSEUM_DONATIONS,
+1278,Museum,Museumsanity: Ghost Crystal,MUSEUM_DONATIONS,
+1279,Museum,Museumsanity: Tigerseye,MUSEUM_DONATIONS,
+1280,Museum,Museumsanity: Jasper,MUSEUM_DONATIONS,
+1281,Museum,Museumsanity: Opal,MUSEUM_DONATIONS,
+1282,Museum,Museumsanity: Fire Opal,MUSEUM_DONATIONS,
+1283,Museum,Museumsanity: Celestine,MUSEUM_DONATIONS,
+1284,Museum,Museumsanity: Marble,MUSEUM_DONATIONS,
+1285,Museum,Museumsanity: Sandstone,MUSEUM_DONATIONS,
+1286,Museum,Museumsanity: Granite,MUSEUM_DONATIONS,
+1287,Museum,Museumsanity: Basalt,MUSEUM_DONATIONS,
+1288,Museum,Museumsanity: Limestone,MUSEUM_DONATIONS,
+1289,Museum,Museumsanity: Soapstone,MUSEUM_DONATIONS,
+1290,Museum,Museumsanity: Hematite,MUSEUM_DONATIONS,
+1291,Museum,Museumsanity: Mudstone,MUSEUM_DONATIONS,
+1292,Museum,Museumsanity: Obsidian,MUSEUM_DONATIONS,
+1293,Museum,Museumsanity: Slate,MUSEUM_DONATIONS,
+1294,Museum,Museumsanity: Fairy Stone,MUSEUM_DONATIONS,
+1295,Museum,Museumsanity: Star Shards,MUSEUM_DONATIONS,
+1301,Alex's House,Friendsanity: Alex 1 <3,FRIENDSANITY,
+1302,Alex's House,Friendsanity: Alex 2 <3,FRIENDSANITY,
+1303,Alex's House,Friendsanity: Alex 3 <3,FRIENDSANITY,
+1304,Alex's House,Friendsanity: Alex 4 <3,FRIENDSANITY,
+1305,Alex's House,Friendsanity: Alex 5 <3,FRIENDSANITY,
+1306,Alex's House,Friendsanity: Alex 6 <3,FRIENDSANITY,
+1307,Alex's House,Friendsanity: Alex 7 <3,FRIENDSANITY,
+1308,Alex's House,Friendsanity: Alex 8 <3,FRIENDSANITY,
+1309,Alex's House,Friendsanity: Alex 9 <3,FRIENDSANITY,
+1310,Alex's House,Friendsanity: Alex 10 <3,FRIENDSANITY,
+1311,Alex's House,Friendsanity: Alex 11 <3,FRIENDSANITY,
+1312,Alex's House,Friendsanity: Alex 12 <3,FRIENDSANITY,
+1313,Alex's House,Friendsanity: Alex 13 <3,FRIENDSANITY,
+1314,Alex's House,Friendsanity: Alex 14 <3,FRIENDSANITY,
+1315,Elliott's House,Friendsanity: Elliott 1 <3,FRIENDSANITY,
+1316,Elliott's House,Friendsanity: Elliott 2 <3,FRIENDSANITY,
+1317,Elliott's House,Friendsanity: Elliott 3 <3,FRIENDSANITY,
+1318,Elliott's House,Friendsanity: Elliott 4 <3,FRIENDSANITY,
+1319,Elliott's House,Friendsanity: Elliott 5 <3,FRIENDSANITY,
+1320,Elliott's House,Friendsanity: Elliott 6 <3,FRIENDSANITY,
+1321,Elliott's House,Friendsanity: Elliott 7 <3,FRIENDSANITY,
+1322,Elliott's House,Friendsanity: Elliott 8 <3,FRIENDSANITY,
+1323,Elliott's House,Friendsanity: Elliott 9 <3,FRIENDSANITY,
+1324,Elliott's House,Friendsanity: Elliott 10 <3,FRIENDSANITY,
+1325,Elliott's House,Friendsanity: Elliott 11 <3,FRIENDSANITY,
+1326,Elliott's House,Friendsanity: Elliott 12 <3,FRIENDSANITY,
+1327,Elliott's House,Friendsanity: Elliott 13 <3,FRIENDSANITY,
+1328,Elliott's House,Friendsanity: Elliott 14 <3,FRIENDSANITY,
+1329,Hospital,Friendsanity: Harvey 1 <3,FRIENDSANITY,
+1330,Hospital,Friendsanity: Harvey 2 <3,FRIENDSANITY,
+1331,Hospital,Friendsanity: Harvey 3 <3,FRIENDSANITY,
+1332,Hospital,Friendsanity: Harvey 4 <3,FRIENDSANITY,
+1333,Hospital,Friendsanity: Harvey 5 <3,FRIENDSANITY,
+1334,Hospital,Friendsanity: Harvey 6 <3,FRIENDSANITY,
+1335,Hospital,Friendsanity: Harvey 7 <3,FRIENDSANITY,
+1336,Hospital,Friendsanity: Harvey 8 <3,FRIENDSANITY,
+1337,Hospital,Friendsanity: Harvey 9 <3,FRIENDSANITY,
+1338,Hospital,Friendsanity: Harvey 10 <3,FRIENDSANITY,
+1339,Hospital,Friendsanity: Harvey 11 <3,FRIENDSANITY,
+1340,Hospital,Friendsanity: Harvey 12 <3,FRIENDSANITY,
+1341,Hospital,Friendsanity: Harvey 13 <3,FRIENDSANITY,
+1342,Hospital,Friendsanity: Harvey 14 <3,FRIENDSANITY,
+1343,Sam's House,Friendsanity: Sam 1 <3,FRIENDSANITY,
+1344,Sam's House,Friendsanity: Sam 2 <3,FRIENDSANITY,
+1345,Sam's House,Friendsanity: Sam 3 <3,FRIENDSANITY,
+1346,Sam's House,Friendsanity: Sam 4 <3,FRIENDSANITY,
+1347,Sam's House,Friendsanity: Sam 5 <3,FRIENDSANITY,
+1348,Sam's House,Friendsanity: Sam 6 <3,FRIENDSANITY,
+1349,Sam's House,Friendsanity: Sam 7 <3,FRIENDSANITY,
+1350,Sam's House,Friendsanity: Sam 8 <3,FRIENDSANITY,
+1351,Sam's House,Friendsanity: Sam 9 <3,FRIENDSANITY,
+1352,Sam's House,Friendsanity: Sam 10 <3,FRIENDSANITY,
+1353,Sam's House,Friendsanity: Sam 11 <3,FRIENDSANITY,
+1354,Sam's House,Friendsanity: Sam 12 <3,FRIENDSANITY,
+1355,Sam's House,Friendsanity: Sam 13 <3,FRIENDSANITY,
+1356,Sam's House,Friendsanity: Sam 14 <3,FRIENDSANITY,
+1357,Carpenter Shop,Friendsanity: Sebastian 1 <3,FRIENDSANITY,
+1358,Carpenter Shop,Friendsanity: Sebastian 2 <3,FRIENDSANITY,
+1359,Carpenter Shop,Friendsanity: Sebastian 3 <3,FRIENDSANITY,
+1360,Carpenter Shop,Friendsanity: Sebastian 4 <3,FRIENDSANITY,
+1361,Carpenter Shop,Friendsanity: Sebastian 5 <3,FRIENDSANITY,
+1362,Carpenter Shop,Friendsanity: Sebastian 6 <3,FRIENDSANITY,
+1363,Carpenter Shop,Friendsanity: Sebastian 7 <3,FRIENDSANITY,
+1364,Carpenter Shop,Friendsanity: Sebastian 8 <3,FRIENDSANITY,
+1365,Carpenter Shop,Friendsanity: Sebastian 9 <3,FRIENDSANITY,
+1366,Carpenter Shop,Friendsanity: Sebastian 10 <3,FRIENDSANITY,
+1367,Carpenter Shop,Friendsanity: Sebastian 11 <3,FRIENDSANITY,
+1368,Carpenter Shop,Friendsanity: Sebastian 12 <3,FRIENDSANITY,
+1369,Carpenter Shop,Friendsanity: Sebastian 13 <3,FRIENDSANITY,
+1370,Carpenter Shop,Friendsanity: Sebastian 14 <3,FRIENDSANITY,
+1371,Marnie's Ranch,Friendsanity: Shane 1 <3,FRIENDSANITY,
+1372,Marnie's Ranch,Friendsanity: Shane 2 <3,FRIENDSANITY,
+1373,Marnie's Ranch,Friendsanity: Shane 3 <3,FRIENDSANITY,
+1374,Marnie's Ranch,Friendsanity: Shane 4 <3,FRIENDSANITY,
+1375,Marnie's Ranch,Friendsanity: Shane 5 <3,FRIENDSANITY,
+1376,Marnie's Ranch,Friendsanity: Shane 6 <3,FRIENDSANITY,
+1377,Marnie's Ranch,Friendsanity: Shane 7 <3,FRIENDSANITY,
+1378,Marnie's Ranch,Friendsanity: Shane 8 <3,FRIENDSANITY,
+1379,Marnie's Ranch,Friendsanity: Shane 9 <3,FRIENDSANITY,
+1380,Marnie's Ranch,Friendsanity: Shane 10 <3,FRIENDSANITY,
+1381,Marnie's Ranch,Friendsanity: Shane 11 <3,FRIENDSANITY,
+1382,Marnie's Ranch,Friendsanity: Shane 12 <3,FRIENDSANITY,
+1383,Marnie's Ranch,Friendsanity: Shane 13 <3,FRIENDSANITY,
+1384,Marnie's Ranch,Friendsanity: Shane 14 <3,FRIENDSANITY,
+1385,Pierre's General Store,Friendsanity: Abigail 1 <3,FRIENDSANITY,
+1386,Pierre's General Store,Friendsanity: Abigail 2 <3,FRIENDSANITY,
+1387,Pierre's General Store,Friendsanity: Abigail 3 <3,FRIENDSANITY,
+1388,Pierre's General Store,Friendsanity: Abigail 4 <3,FRIENDSANITY,
+1389,Pierre's General Store,Friendsanity: Abigail 5 <3,FRIENDSANITY,
+1390,Pierre's General Store,Friendsanity: Abigail 6 <3,FRIENDSANITY,
+1391,Pierre's General Store,Friendsanity: Abigail 7 <3,FRIENDSANITY,
+1392,Pierre's General Store,Friendsanity: Abigail 8 <3,FRIENDSANITY,
+1393,Pierre's General Store,Friendsanity: Abigail 9 <3,FRIENDSANITY,
+1394,Pierre's General Store,Friendsanity: Abigail 10 <3,FRIENDSANITY,
+1395,Pierre's General Store,Friendsanity: Abigail 11 <3,FRIENDSANITY,
+1396,Pierre's General Store,Friendsanity: Abigail 12 <3,FRIENDSANITY,
+1397,Pierre's General Store,Friendsanity: Abigail 13 <3,FRIENDSANITY,
+1398,Pierre's General Store,Friendsanity: Abigail 14 <3,FRIENDSANITY,
+1399,Haley's House,Friendsanity: Emily 1 <3,FRIENDSANITY,
+1400,Haley's House,Friendsanity: Emily 2 <3,FRIENDSANITY,
+1401,Haley's House,Friendsanity: Emily 3 <3,FRIENDSANITY,
+1402,Haley's House,Friendsanity: Emily 4 <3,FRIENDSANITY,
+1403,Haley's House,Friendsanity: Emily 5 <3,FRIENDSANITY,
+1404,Haley's House,Friendsanity: Emily 6 <3,FRIENDSANITY,
+1405,Haley's House,Friendsanity: Emily 7 <3,FRIENDSANITY,
+1406,Haley's House,Friendsanity: Emily 8 <3,FRIENDSANITY,
+1407,Haley's House,Friendsanity: Emily 9 <3,FRIENDSANITY,
+1408,Haley's House,Friendsanity: Emily 10 <3,FRIENDSANITY,
+1409,Haley's House,Friendsanity: Emily 11 <3,FRIENDSANITY,
+1410,Haley's House,Friendsanity: Emily 12 <3,FRIENDSANITY,
+1411,Haley's House,Friendsanity: Emily 13 <3,FRIENDSANITY,
+1412,Haley's House,Friendsanity: Emily 14 <3,FRIENDSANITY,
+1413,Haley's House,Friendsanity: Haley 1 <3,FRIENDSANITY,
+1414,Haley's House,Friendsanity: Haley 2 <3,FRIENDSANITY,
+1415,Haley's House,Friendsanity: Haley 3 <3,FRIENDSANITY,
+1416,Haley's House,Friendsanity: Haley 4 <3,FRIENDSANITY,
+1417,Haley's House,Friendsanity: Haley 5 <3,FRIENDSANITY,
+1418,Haley's House,Friendsanity: Haley 6 <3,FRIENDSANITY,
+1419,Haley's House,Friendsanity: Haley 7 <3,FRIENDSANITY,
+1420,Haley's House,Friendsanity: Haley 8 <3,FRIENDSANITY,
+1421,Haley's House,Friendsanity: Haley 9 <3,FRIENDSANITY,
+1422,Haley's House,Friendsanity: Haley 10 <3,FRIENDSANITY,
+1423,Haley's House,Friendsanity: Haley 11 <3,FRIENDSANITY,
+1424,Haley's House,Friendsanity: Haley 12 <3,FRIENDSANITY,
+1425,Haley's House,Friendsanity: Haley 13 <3,FRIENDSANITY,
+1426,Haley's House,Friendsanity: Haley 14 <3,FRIENDSANITY,
+1427,Leah's Cottage,Friendsanity: Leah 1 <3,FRIENDSANITY,
+1428,Leah's Cottage,Friendsanity: Leah 2 <3,FRIENDSANITY,
+1429,Leah's Cottage,Friendsanity: Leah 3 <3,FRIENDSANITY,
+1430,Leah's Cottage,Friendsanity: Leah 4 <3,FRIENDSANITY,
+1431,Leah's Cottage,Friendsanity: Leah 5 <3,FRIENDSANITY,
+1432,Leah's Cottage,Friendsanity: Leah 6 <3,FRIENDSANITY,
+1433,Leah's Cottage,Friendsanity: Leah 7 <3,FRIENDSANITY,
+1434,Leah's Cottage,Friendsanity: Leah 8 <3,FRIENDSANITY,
+1435,Leah's Cottage,Friendsanity: Leah 9 <3,FRIENDSANITY,
+1436,Leah's Cottage,Friendsanity: Leah 10 <3,FRIENDSANITY,
+1437,Leah's Cottage,Friendsanity: Leah 11 <3,FRIENDSANITY,
+1438,Leah's Cottage,Friendsanity: Leah 12 <3,FRIENDSANITY,
+1439,Leah's Cottage,Friendsanity: Leah 13 <3,FRIENDSANITY,
+1440,Leah's Cottage,Friendsanity: Leah 14 <3,FRIENDSANITY,
+1441,Carpenter Shop,Friendsanity: Maru 1 <3,FRIENDSANITY,
+1442,Carpenter Shop,Friendsanity: Maru 2 <3,FRIENDSANITY,
+1443,Carpenter Shop,Friendsanity: Maru 3 <3,FRIENDSANITY,
+1444,Carpenter Shop,Friendsanity: Maru 4 <3,FRIENDSANITY,
+1445,Carpenter Shop,Friendsanity: Maru 5 <3,FRIENDSANITY,
+1446,Carpenter Shop,Friendsanity: Maru 6 <3,FRIENDSANITY,
+1447,Carpenter Shop,Friendsanity: Maru 7 <3,FRIENDSANITY,
+1448,Carpenter Shop,Friendsanity: Maru 8 <3,FRIENDSANITY,
+1449,Carpenter Shop,Friendsanity: Maru 9 <3,FRIENDSANITY,
+1450,Carpenter Shop,Friendsanity: Maru 10 <3,FRIENDSANITY,
+1451,Carpenter Shop,Friendsanity: Maru 11 <3,FRIENDSANITY,
+1452,Carpenter Shop,Friendsanity: Maru 12 <3,FRIENDSANITY,
+1453,Carpenter Shop,Friendsanity: Maru 13 <3,FRIENDSANITY,
+1454,Carpenter Shop,Friendsanity: Maru 14 <3,FRIENDSANITY,
+1455,Trailer,Friendsanity: Penny 1 <3,FRIENDSANITY,
+1456,Trailer,Friendsanity: Penny 2 <3,FRIENDSANITY,
+1457,Trailer,Friendsanity: Penny 3 <3,FRIENDSANITY,
+1458,Trailer,Friendsanity: Penny 4 <3,FRIENDSANITY,
+1459,Trailer,Friendsanity: Penny 5 <3,FRIENDSANITY,
+1460,Trailer,Friendsanity: Penny 6 <3,FRIENDSANITY,
+1461,Trailer,Friendsanity: Penny 7 <3,FRIENDSANITY,
+1462,Trailer,Friendsanity: Penny 8 <3,FRIENDSANITY,
+1463,Trailer,Friendsanity: Penny 9 <3,FRIENDSANITY,
+1464,Trailer,Friendsanity: Penny 10 <3,FRIENDSANITY,
+1465,Trailer,Friendsanity: Penny 11 <3,FRIENDSANITY,
+1466,Trailer,Friendsanity: Penny 12 <3,FRIENDSANITY,
+1467,Trailer,Friendsanity: Penny 13 <3,FRIENDSANITY,
+1468,Trailer,Friendsanity: Penny 14 <3,FRIENDSANITY,
+1469,Pierre's General Store,Friendsanity: Caroline 1 <3,FRIENDSANITY,
+1470,Pierre's General Store,Friendsanity: Caroline 2 <3,FRIENDSANITY,
+1471,Pierre's General Store,Friendsanity: Caroline 3 <3,FRIENDSANITY,
+1472,Pierre's General Store,Friendsanity: Caroline 4 <3,FRIENDSANITY,
+1473,Pierre's General Store,Friendsanity: Caroline 5 <3,FRIENDSANITY,
+1474,Pierre's General Store,Friendsanity: Caroline 6 <3,FRIENDSANITY,
+1475,Pierre's General Store,Friendsanity: Caroline 7 <3,FRIENDSANITY,
+1476,Pierre's General Store,Friendsanity: Caroline 8 <3,FRIENDSANITY,
+1477,Pierre's General Store,Friendsanity: Caroline 9 <3,FRIENDSANITY,
+1478,Pierre's General Store,Friendsanity: Caroline 10 <3,FRIENDSANITY,
+1480,Clint's Blacksmith,Friendsanity: Clint 1 <3,FRIENDSANITY,
+1481,Clint's Blacksmith,Friendsanity: Clint 2 <3,FRIENDSANITY,
+1482,Clint's Blacksmith,Friendsanity: Clint 3 <3,FRIENDSANITY,
+1483,Clint's Blacksmith,Friendsanity: Clint 4 <3,FRIENDSANITY,
+1484,Clint's Blacksmith,Friendsanity: Clint 5 <3,FRIENDSANITY,
+1485,Clint's Blacksmith,Friendsanity: Clint 6 <3,FRIENDSANITY,
+1486,Clint's Blacksmith,Friendsanity: Clint 7 <3,FRIENDSANITY,
+1487,Clint's Blacksmith,Friendsanity: Clint 8 <3,FRIENDSANITY,
+1488,Clint's Blacksmith,Friendsanity: Clint 9 <3,FRIENDSANITY,
+1489,Clint's Blacksmith,Friendsanity: Clint 10 <3,FRIENDSANITY,
+1491,Carpenter Shop,Friendsanity: Demetrius 1 <3,FRIENDSANITY,
+1492,Carpenter Shop,Friendsanity: Demetrius 2 <3,FRIENDSANITY,
+1493,Carpenter Shop,Friendsanity: Demetrius 3 <3,FRIENDSANITY,
+1494,Carpenter Shop,Friendsanity: Demetrius 4 <3,FRIENDSANITY,
+1495,Carpenter Shop,Friendsanity: Demetrius 5 <3,FRIENDSANITY,
+1496,Carpenter Shop,Friendsanity: Demetrius 6 <3,FRIENDSANITY,
+1497,Carpenter Shop,Friendsanity: Demetrius 7 <3,FRIENDSANITY,
+1498,Carpenter Shop,Friendsanity: Demetrius 8 <3,FRIENDSANITY,
+1499,Carpenter Shop,Friendsanity: Demetrius 9 <3,FRIENDSANITY,
+1500,Carpenter Shop,Friendsanity: Demetrius 10 <3,FRIENDSANITY,
+1502,Mines Dwarf Shop,Friendsanity: Dwarf 1 <3,FRIENDSANITY,
+1503,Mines Dwarf Shop,Friendsanity: Dwarf 2 <3,FRIENDSANITY,
+1504,Mines Dwarf Shop,Friendsanity: Dwarf 3 <3,FRIENDSANITY,
+1505,Mines Dwarf Shop,Friendsanity: Dwarf 4 <3,FRIENDSANITY,
+1506,Mines Dwarf Shop,Friendsanity: Dwarf 5 <3,FRIENDSANITY,
+1507,Mines Dwarf Shop,Friendsanity: Dwarf 6 <3,FRIENDSANITY,
+1508,Mines Dwarf Shop,Friendsanity: Dwarf 7 <3,FRIENDSANITY,
+1509,Mines Dwarf Shop,Friendsanity: Dwarf 8 <3,FRIENDSANITY,
+1510,Mines Dwarf Shop,Friendsanity: Dwarf 9 <3,FRIENDSANITY,
+1511,Mines Dwarf Shop,Friendsanity: Dwarf 10 <3,FRIENDSANITY,
+1513,Alex's House,Friendsanity: Evelyn 1 <3,FRIENDSANITY,
+1514,Alex's House,Friendsanity: Evelyn 2 <3,FRIENDSANITY,
+1515,Alex's House,Friendsanity: Evelyn 3 <3,FRIENDSANITY,
+1516,Alex's House,Friendsanity: Evelyn 4 <3,FRIENDSANITY,
+1517,Alex's House,Friendsanity: Evelyn 5 <3,FRIENDSANITY,
+1518,Alex's House,Friendsanity: Evelyn 6 <3,FRIENDSANITY,
+1519,Alex's House,Friendsanity: Evelyn 7 <3,FRIENDSANITY,
+1520,Alex's House,Friendsanity: Evelyn 8 <3,FRIENDSANITY,
+1521,Alex's House,Friendsanity: Evelyn 9 <3,FRIENDSANITY,
+1522,Alex's House,Friendsanity: Evelyn 10 <3,FRIENDSANITY,
+1524,Alex's House,Friendsanity: George 1 <3,FRIENDSANITY,
+1525,Alex's House,Friendsanity: George 2 <3,FRIENDSANITY,
+1526,Alex's House,Friendsanity: George 3 <3,FRIENDSANITY,
+1527,Alex's House,Friendsanity: George 4 <3,FRIENDSANITY,
+1528,Alex's House,Friendsanity: George 5 <3,FRIENDSANITY,
+1529,Alex's House,Friendsanity: George 6 <3,FRIENDSANITY,
+1530,Alex's House,Friendsanity: George 7 <3,FRIENDSANITY,
+1531,Alex's House,Friendsanity: George 8 <3,FRIENDSANITY,
+1532,Alex's House,Friendsanity: George 9 <3,FRIENDSANITY,
+1533,Alex's House,Friendsanity: George 10 <3,FRIENDSANITY,
+1535,Saloon,Friendsanity: Gus 1 <3,FRIENDSANITY,
+1536,Saloon,Friendsanity: Gus 2 <3,FRIENDSANITY,
+1537,Saloon,Friendsanity: Gus 3 <3,FRIENDSANITY,
+1538,Saloon,Friendsanity: Gus 4 <3,FRIENDSANITY,
+1539,Saloon,Friendsanity: Gus 5 <3,FRIENDSANITY,
+1540,Saloon,Friendsanity: Gus 6 <3,FRIENDSANITY,
+1541,Saloon,Friendsanity: Gus 7 <3,FRIENDSANITY,
+1542,Saloon,Friendsanity: Gus 8 <3,FRIENDSANITY,
+1543,Saloon,Friendsanity: Gus 9 <3,FRIENDSANITY,
+1544,Saloon,Friendsanity: Gus 10 <3,FRIENDSANITY,
+1546,Marnie's Ranch,Friendsanity: Jas 1 <3,FRIENDSANITY,
+1547,Marnie's Ranch,Friendsanity: Jas 2 <3,FRIENDSANITY,
+1548,Marnie's Ranch,Friendsanity: Jas 3 <3,FRIENDSANITY,
+1549,Marnie's Ranch,Friendsanity: Jas 4 <3,FRIENDSANITY,
+1550,Marnie's Ranch,Friendsanity: Jas 5 <3,FRIENDSANITY,
+1551,Marnie's Ranch,Friendsanity: Jas 6 <3,FRIENDSANITY,
+1552,Marnie's Ranch,Friendsanity: Jas 7 <3,FRIENDSANITY,
+1553,Marnie's Ranch,Friendsanity: Jas 8 <3,FRIENDSANITY,
+1554,Marnie's Ranch,Friendsanity: Jas 9 <3,FRIENDSANITY,
+1555,Marnie's Ranch,Friendsanity: Jas 10 <3,FRIENDSANITY,
+1557,Sam's House,Friendsanity: Jodi 1 <3,FRIENDSANITY,
+1558,Sam's House,Friendsanity: Jodi 2 <3,FRIENDSANITY,
+1559,Sam's House,Friendsanity: Jodi 3 <3,FRIENDSANITY,
+1560,Sam's House,Friendsanity: Jodi 4 <3,FRIENDSANITY,
+1561,Sam's House,Friendsanity: Jodi 5 <3,FRIENDSANITY,
+1562,Sam's House,Friendsanity: Jodi 6 <3,FRIENDSANITY,
+1563,Sam's House,Friendsanity: Jodi 7 <3,FRIENDSANITY,
+1564,Sam's House,Friendsanity: Jodi 8 <3,FRIENDSANITY,
+1565,Sam's House,Friendsanity: Jodi 9 <3,FRIENDSANITY,
+1566,Sam's House,Friendsanity: Jodi 10 <3,FRIENDSANITY,
+1568,Sam's House,Friendsanity: Kent 1 <3,FRIENDSANITY,
+1569,Sam's House,Friendsanity: Kent 2 <3,FRIENDSANITY,
+1570,Sam's House,Friendsanity: Kent 3 <3,FRIENDSANITY,
+1571,Sam's House,Friendsanity: Kent 4 <3,FRIENDSANITY,
+1572,Sam's House,Friendsanity: Kent 5 <3,FRIENDSANITY,
+1573,Sam's House,Friendsanity: Kent 6 <3,FRIENDSANITY,
+1574,Sam's House,Friendsanity: Kent 7 <3,FRIENDSANITY,
+1575,Sam's House,Friendsanity: Kent 8 <3,FRIENDSANITY,
+1576,Sam's House,Friendsanity: Kent 9 <3,FRIENDSANITY,
+1577,Sam's House,Friendsanity: Kent 10 <3,FRIENDSANITY,
+1579,Sewer,Friendsanity: Krobus 1 <3,FRIENDSANITY,
+1580,Sewer,Friendsanity: Krobus 2 <3,FRIENDSANITY,
+1581,Sewer,Friendsanity: Krobus 3 <3,FRIENDSANITY,
+1582,Sewer,Friendsanity: Krobus 4 <3,FRIENDSANITY,
+1583,Sewer,Friendsanity: Krobus 5 <3,FRIENDSANITY,
+1584,Sewer,Friendsanity: Krobus 6 <3,FRIENDSANITY,
+1585,Sewer,Friendsanity: Krobus 7 <3,FRIENDSANITY,
+1586,Sewer,Friendsanity: Krobus 8 <3,FRIENDSANITY,
+1587,Sewer,Friendsanity: Krobus 9 <3,FRIENDSANITY,
+1588,Sewer,Friendsanity: Krobus 10 <3,FRIENDSANITY,
+1590,Leo's Hut,Friendsanity: Leo 1 <3,"FRIENDSANITY,GINGER_ISLAND",
+1591,Leo's Hut,Friendsanity: Leo 2 <3,"FRIENDSANITY,GINGER_ISLAND",
+1592,Leo's Hut,Friendsanity: Leo 3 <3,"FRIENDSANITY,GINGER_ISLAND",
+1593,Leo's Hut,Friendsanity: Leo 4 <3,"FRIENDSANITY,GINGER_ISLAND",
+1594,Leo's Hut,Friendsanity: Leo 5 <3,"FRIENDSANITY,GINGER_ISLAND",
+1595,Leo's Hut,Friendsanity: Leo 6 <3,"FRIENDSANITY,GINGER_ISLAND",
+1596,Leo's Hut,Friendsanity: Leo 7 <3,"FRIENDSANITY,GINGER_ISLAND",
+1597,Leo's Hut,Friendsanity: Leo 8 <3,"FRIENDSANITY,GINGER_ISLAND",
+1598,Leo's Hut,Friendsanity: Leo 9 <3,"FRIENDSANITY,GINGER_ISLAND",
+1599,Leo's Hut,Friendsanity: Leo 10 <3,"FRIENDSANITY,GINGER_ISLAND",
+1601,Mayor's Manor,Friendsanity: Lewis 1 <3,FRIENDSANITY,
+1602,Mayor's Manor,Friendsanity: Lewis 2 <3,FRIENDSANITY,
+1603,Mayor's Manor,Friendsanity: Lewis 3 <3,FRIENDSANITY,
+1604,Mayor's Manor,Friendsanity: Lewis 4 <3,FRIENDSANITY,
+1605,Mayor's Manor,Friendsanity: Lewis 5 <3,FRIENDSANITY,
+1606,Mayor's Manor,Friendsanity: Lewis 6 <3,FRIENDSANITY,
+1607,Mayor's Manor,Friendsanity: Lewis 7 <3,FRIENDSANITY,
+1608,Mayor's Manor,Friendsanity: Lewis 8 <3,FRIENDSANITY,
+1609,Mayor's Manor,Friendsanity: Lewis 9 <3,FRIENDSANITY,
+1610,Mayor's Manor,Friendsanity: Lewis 10 <3,FRIENDSANITY,
+1612,Tent,Friendsanity: Linus 1 <3,FRIENDSANITY,
+1613,Tent,Friendsanity: Linus 2 <3,FRIENDSANITY,
+1614,Tent,Friendsanity: Linus 3 <3,FRIENDSANITY,
+1615,Tent,Friendsanity: Linus 4 <3,FRIENDSANITY,
+1616,Tent,Friendsanity: Linus 5 <3,FRIENDSANITY,
+1617,Tent,Friendsanity: Linus 6 <3,FRIENDSANITY,
+1618,Tent,Friendsanity: Linus 7 <3,FRIENDSANITY,
+1619,Tent,Friendsanity: Linus 8 <3,FRIENDSANITY,
+1620,Tent,Friendsanity: Linus 9 <3,FRIENDSANITY,
+1621,Tent,Friendsanity: Linus 10 <3,FRIENDSANITY,
+1623,Marnie's Ranch,Friendsanity: Marnie 1 <3,FRIENDSANITY,
+1624,Marnie's Ranch,Friendsanity: Marnie 2 <3,FRIENDSANITY,
+1625,Marnie's Ranch,Friendsanity: Marnie 3 <3,FRIENDSANITY,
+1626,Marnie's Ranch,Friendsanity: Marnie 4 <3,FRIENDSANITY,
+1627,Marnie's Ranch,Friendsanity: Marnie 5 <3,FRIENDSANITY,
+1628,Marnie's Ranch,Friendsanity: Marnie 6 <3,FRIENDSANITY,
+1629,Marnie's Ranch,Friendsanity: Marnie 7 <3,FRIENDSANITY,
+1630,Marnie's Ranch,Friendsanity: Marnie 8 <3,FRIENDSANITY,
+1631,Marnie's Ranch,Friendsanity: Marnie 9 <3,FRIENDSANITY,
+1632,Marnie's Ranch,Friendsanity: Marnie 10 <3,FRIENDSANITY,
+1634,Trailer,Friendsanity: Pam 1 <3,FRIENDSANITY,
+1635,Trailer,Friendsanity: Pam 2 <3,FRIENDSANITY,
+1636,Trailer,Friendsanity: Pam 3 <3,FRIENDSANITY,
+1637,Trailer,Friendsanity: Pam 4 <3,FRIENDSANITY,
+1638,Trailer,Friendsanity: Pam 5 <3,FRIENDSANITY,
+1639,Trailer,Friendsanity: Pam 6 <3,FRIENDSANITY,
+1640,Trailer,Friendsanity: Pam 7 <3,FRIENDSANITY,
+1641,Trailer,Friendsanity: Pam 8 <3,FRIENDSANITY,
+1642,Trailer,Friendsanity: Pam 9 <3,FRIENDSANITY,
+1643,Trailer,Friendsanity: Pam 10 <3,FRIENDSANITY,
+1645,Pierre's General Store,Friendsanity: Pierre 1 <3,FRIENDSANITY,
+1646,Pierre's General Store,Friendsanity: Pierre 2 <3,FRIENDSANITY,
+1647,Pierre's General Store,Friendsanity: Pierre 3 <3,FRIENDSANITY,
+1648,Pierre's General Store,Friendsanity: Pierre 4 <3,FRIENDSANITY,
+1649,Pierre's General Store,Friendsanity: Pierre 5 <3,FRIENDSANITY,
+1650,Pierre's General Store,Friendsanity: Pierre 6 <3,FRIENDSANITY,
+1651,Pierre's General Store,Friendsanity: Pierre 7 <3,FRIENDSANITY,
+1652,Pierre's General Store,Friendsanity: Pierre 8 <3,FRIENDSANITY,
+1653,Pierre's General Store,Friendsanity: Pierre 9 <3,FRIENDSANITY,
+1654,Pierre's General Store,Friendsanity: Pierre 10 <3,FRIENDSANITY,
+1656,Carpenter Shop,Friendsanity: Robin 1 <3,FRIENDSANITY,
+1657,Carpenter Shop,Friendsanity: Robin 2 <3,FRIENDSANITY,
+1658,Carpenter Shop,Friendsanity: Robin 3 <3,FRIENDSANITY,
+1659,Carpenter Shop,Friendsanity: Robin 4 <3,FRIENDSANITY,
+1660,Carpenter Shop,Friendsanity: Robin 5 <3,FRIENDSANITY,
+1661,Carpenter Shop,Friendsanity: Robin 6 <3,FRIENDSANITY,
+1662,Carpenter Shop,Friendsanity: Robin 7 <3,FRIENDSANITY,
+1663,Carpenter Shop,Friendsanity: Robin 8 <3,FRIENDSANITY,
+1664,Carpenter Shop,Friendsanity: Robin 9 <3,FRIENDSANITY,
+1665,Carpenter Shop,Friendsanity: Robin 10 <3,FRIENDSANITY,
+1667,Oasis,Friendsanity: Sandy 1 <3,FRIENDSANITY,
+1668,Oasis,Friendsanity: Sandy 2 <3,FRIENDSANITY,
+1669,Oasis,Friendsanity: Sandy 3 <3,FRIENDSANITY,
+1670,Oasis,Friendsanity: Sandy 4 <3,FRIENDSANITY,
+1671,Oasis,Friendsanity: Sandy 5 <3,FRIENDSANITY,
+1672,Oasis,Friendsanity: Sandy 6 <3,FRIENDSANITY,
+1673,Oasis,Friendsanity: Sandy 7 <3,FRIENDSANITY,
+1674,Oasis,Friendsanity: Sandy 8 <3,FRIENDSANITY,
+1675,Oasis,Friendsanity: Sandy 9 <3,FRIENDSANITY,
+1676,Oasis,Friendsanity: Sandy 10 <3,FRIENDSANITY,
+1678,Sam's House,Friendsanity: Vincent 1 <3,FRIENDSANITY,
+1679,Sam's House,Friendsanity: Vincent 2 <3,FRIENDSANITY,
+1680,Sam's House,Friendsanity: Vincent 3 <3,FRIENDSANITY,
+1681,Sam's House,Friendsanity: Vincent 4 <3,FRIENDSANITY,
+1682,Sam's House,Friendsanity: Vincent 5 <3,FRIENDSANITY,
+1683,Sam's House,Friendsanity: Vincent 6 <3,FRIENDSANITY,
+1684,Sam's House,Friendsanity: Vincent 7 <3,FRIENDSANITY,
+1685,Sam's House,Friendsanity: Vincent 8 <3,FRIENDSANITY,
+1686,Sam's House,Friendsanity: Vincent 9 <3,FRIENDSANITY,
+1687,Sam's House,Friendsanity: Vincent 10 <3,FRIENDSANITY,
+1689,Willy's Fish Shop,Friendsanity: Willy 1 <3,FRIENDSANITY,
+1690,Willy's Fish Shop,Friendsanity: Willy 2 <3,FRIENDSANITY,
+1691,Willy's Fish Shop,Friendsanity: Willy 3 <3,FRIENDSANITY,
+1692,Willy's Fish Shop,Friendsanity: Willy 4 <3,FRIENDSANITY,
+1693,Willy's Fish Shop,Friendsanity: Willy 5 <3,FRIENDSANITY,
+1694,Willy's Fish Shop,Friendsanity: Willy 6 <3,FRIENDSANITY,
+1695,Willy's Fish Shop,Friendsanity: Willy 7 <3,FRIENDSANITY,
+1696,Willy's Fish Shop,Friendsanity: Willy 8 <3,FRIENDSANITY,
+1697,Willy's Fish Shop,Friendsanity: Willy 9 <3,FRIENDSANITY,
+1698,Willy's Fish Shop,Friendsanity: Willy 10 <3,FRIENDSANITY,
+1700,Wizard Tower,Friendsanity: Wizard 1 <3,FRIENDSANITY,
+1701,Wizard Tower,Friendsanity: Wizard 2 <3,FRIENDSANITY,
+1702,Wizard Tower,Friendsanity: Wizard 3 <3,FRIENDSANITY,
+1703,Wizard Tower,Friendsanity: Wizard 4 <3,FRIENDSANITY,
+1704,Wizard Tower,Friendsanity: Wizard 5 <3,FRIENDSANITY,
+1705,Wizard Tower,Friendsanity: Wizard 6 <3,FRIENDSANITY,
+1706,Wizard Tower,Friendsanity: Wizard 7 <3,FRIENDSANITY,
+1707,Wizard Tower,Friendsanity: Wizard 8 <3,FRIENDSANITY,
+1708,Wizard Tower,Friendsanity: Wizard 9 <3,FRIENDSANITY,
+1709,Wizard Tower,Friendsanity: Wizard 10 <3,FRIENDSANITY,
+1710,Farm,Friendsanity: Pet 1 <3,FRIENDSANITY,
+1711,Farm,Friendsanity: Pet 2 <3,FRIENDSANITY,
+1712,Farm,Friendsanity: Pet 3 <3,FRIENDSANITY,
+1713,Farm,Friendsanity: Pet 4 <3,FRIENDSANITY,
+1714,Farm,Friendsanity: Pet 5 <3,FRIENDSANITY,
+1715,Town,Friendsanity: Friend 1 <3,FRIENDSANITY,
+1716,Town,Friendsanity: Friend 2 <3,FRIENDSANITY,
+1717,Town,Friendsanity: Friend 3 <3,FRIENDSANITY,
+1718,Town,Friendsanity: Friend 4 <3,FRIENDSANITY,
+1719,Town,Friendsanity: Friend 5 <3,FRIENDSANITY,
+1720,Town,Friendsanity: Friend 6 <3,FRIENDSANITY,
+1721,Town,Friendsanity: Friend 7 <3,FRIENDSANITY,
+1722,Town,Friendsanity: Friend 8 <3,FRIENDSANITY,
+1723,Town,Friendsanity: Suitor 9 <3,FRIENDSANITY,
+1724,Town,Friendsanity: Suitor 10 <3,FRIENDSANITY,
+1725,Town,Friendsanity: Spouse 11 <3,FRIENDSANITY,
+1726,Town,Friendsanity: Spouse 12 <3,FRIENDSANITY,
+1727,Town,Friendsanity: Spouse 13 <3,FRIENDSANITY,
+1728,Town,Friendsanity: Spouse 14 <3,FRIENDSANITY,
+2001,Egg Festival,Egg Hunt Victory,FESTIVAL,
+2002,Egg Festival,Egg Festival: Strawberry Seeds,FESTIVAL,
+2003,Flower Dance,Dance with someone,FESTIVAL,
+2004,Flower Dance,Rarecrow #5 (Woman),FESTIVAL,
+2005,Luau,Luau Soup,FESTIVAL,
+2006,Dance of the Moonlight Jellies,Dance of the Moonlight Jellies,FESTIVAL,
+2007,Stardew Valley Fair,Smashing Stone,FESTIVAL,
+2008,Stardew Valley Fair,Grange Display,FESTIVAL,
+2009,Stardew Valley Fair,Rarecrow #1 (Turnip Head),FESTIVAL,
+2010,Stardew Valley Fair,Fair Stardrop,FESTIVAL,
+2011,Spirit's Eve,Spirit's Eve Maze,FESTIVAL,
+2012,Spirit's Eve,Rarecrow #2 (Witch),FESTIVAL,
+2013,Festival of Ice,Win Fishing Competition,FESTIVAL,
+2014,Festival of Ice,Rarecrow #4 (Snowman),FESTIVAL,
+2015,Night Market,Mermaid Pearl,FESTIVAL,
+2016,Night Market,Cone Hat,FESTIVAL_HARD,
+2017,Night Market,Iridium Fireplace,FESTIVAL_HARD,
+2018,Night Market,Rarecrow #7 (Tanuki),FESTIVAL,
+2019,Night Market,Rarecrow #8 (Tribal Mask),FESTIVAL,
+2020,Night Market,Lupini: Red Eagle,FESTIVAL,
+2021,Night Market,Lupini: Portrait Of A Mermaid,FESTIVAL,
+2022,Night Market,Lupini: Solar Kingdom,FESTIVAL,
+2023,Night Market,Lupini: Clouds,FESTIVAL_HARD,
+2024,Night Market,Lupini: 1000 Years From Now,FESTIVAL_HARD,
+2025,Night Market,Lupini: Three Trees,FESTIVAL_HARD,
+2026,Night Market,Lupini: The Serpent,FESTIVAL_HARD,
+2027,Night Market,Lupini: 'Tropical Fish #173',FESTIVAL_HARD,
+2028,Night Market,Lupini: Land Of Clay,FESTIVAL_HARD,
+2029,Feast of the Winter Star,Secret Santa,FESTIVAL,
+2030,Feast of the Winter Star,The Legend of the Winter Star,FESTIVAL,
+2031,Farm,Collect All Rarecrows,FESTIVAL,
+2032,Flower Dance,Tub o' Flowers Recipe,FESTIVAL,
+2033,Spirit's Eve,Jack-O-Lantern Recipe,FESTIVAL,
+2034,Dance of the Moonlight Jellies,Moonlight Jellies Banner,FESTIVAL,
+2035,Dance of the Moonlight Jellies,Starport Decal,FESTIVAL,
+2036,Casino,Rarecrow #3 (Alien),FESTIVAL,
+2101,Town,Island Ingredients,"GINGER_ISLAND,SPECIAL_ORDER_BOARD",
+2102,The Mines - Floor 75,Cave Patrol,SPECIAL_ORDER_BOARD,
+2103,Fishing,Aquatic Overpopulation,SPECIAL_ORDER_BOARD,
+2104,Fishing,Biome Balance,SPECIAL_ORDER_BOARD,
+2105,Haley's House,Rock Rejuvenation,SPECIAL_ORDER_BOARD,
+2106,Alex's House,Gifts for George,SPECIAL_ORDER_BOARD,
+2107,Museum,Fragments of the past,"GINGER_ISLAND,SPECIAL_ORDER_BOARD",
+2108,Saloon,Gus' Famous Omelet,SPECIAL_ORDER_BOARD,
+2109,Farm,Crop Order,SPECIAL_ORDER_BOARD,
+2110,Railroad,Community Cleanup,SPECIAL_ORDER_BOARD,
+2111,Trailer,The Strong Stuff,SPECIAL_ORDER_BOARD,
+2112,Pierre's General Store,Pierre's Prime Produce,SPECIAL_ORDER_BOARD,
+2113,Carpenter Shop,Robin's Project,SPECIAL_ORDER_BOARD,
+2114,Carpenter Shop,Robin's Resource Rush,SPECIAL_ORDER_BOARD,
+2115,Beach,Juicy Bugs Wanted!,SPECIAL_ORDER_BOARD,
+2116,Town,Tropical Fish,"GINGER_ISLAND,SPECIAL_ORDER_BOARD",
+2117,The Mines - Floor 75,A Curious Substance,SPECIAL_ORDER_BOARD,
+2118,The Mines - Floor 35,Prismatic Jelly,SPECIAL_ORDER_BOARD,
+2151,Qi's Walnut Room,Qi's Crop,"GINGER_ISLAND,SPECIAL_ORDER_QI",
+2152,Qi's Walnut Room,Let's Play A Game,"GINGER_ISLAND,JUNIMO_KART,SPECIAL_ORDER_QI",
+2153,Qi's Walnut Room,Four Precious Stones,"GINGER_ISLAND,SPECIAL_ORDER_QI",
+2154,Qi's Walnut Room,Qi's Hungry Challenge,"GINGER_ISLAND,SPECIAL_ORDER_QI",
+2155,Qi's Walnut Room,Qi's Cuisine,"GINGER_ISLAND,SPECIAL_ORDER_QI",
+2156,Qi's Walnut Room,Qi's Kindness,"GINGER_ISLAND,SPECIAL_ORDER_QI",
+2157,Qi's Walnut Room,Extended Family,"GINGER_ISLAND,SPECIAL_ORDER_QI",
+2158,Qi's Walnut Room,Danger In The Deep,"GINGER_ISLAND,SPECIAL_ORDER_QI",
+2159,Qi's Walnut Room,Skull Cavern Invasion,"GINGER_ISLAND,SPECIAL_ORDER_QI",
+2160,Qi's Walnut Room,Qi's Prismatic Grange,"GINGER_ISLAND,SPECIAL_ORDER_QI",
+2201,Boat Tunnel,Repair Ticket Machine,GINGER_ISLAND,
+2202,Boat Tunnel,Repair Boat Hull,GINGER_ISLAND,
+2203,Boat Tunnel,Repair Boat Anchor,GINGER_ISLAND,
+2204,Leo's Hut,Leo's Parrot,"GINGER_ISLAND,WALNUT_PURCHASE",
+2205,Island South,Island West Turtle,"GINGER_ISLAND,WALNUT_PURCHASE",
+2206,Island West,Island Farmhouse,"GINGER_ISLAND,WALNUT_PURCHASE",
+2207,Island Farmhouse,Island Mailbox,"GINGER_ISLAND,WALNUT_PURCHASE",
+2208,Island Farmhouse,Farm Obelisk,"GINGER_ISLAND,WALNUT_PURCHASE",
+2209,Island North,Dig Site Bridge,"GINGER_ISLAND,WALNUT_PURCHASE",
+2210,Island North,Island Trader,"GINGER_ISLAND,WALNUT_PURCHASE",
+2211,Volcano Entrance,Volcano Bridge,"GINGER_ISLAND,WALNUT_PURCHASE",
+2212,Volcano - Floor 5,Volcano Exit Shortcut,"GINGER_ISLAND,WALNUT_PURCHASE",
+2213,Island South,Island Resort,"GINGER_ISLAND,WALNUT_PURCHASE",
+2214,Island West,Parrot Express,"GINGER_ISLAND,WALNUT_PURCHASE",
+2215,Dig Site,Open Professor Snail Cave,GINGER_ISLAND,
+2216,Field Office,Complete Island Field Office,GINGER_ISLAND,
+2301,Farming,Harvest Amaranth,CROPSANITY,
+2302,Farming,Harvest Artichoke,CROPSANITY,
+2303,Farming,Harvest Beet,CROPSANITY,
+2304,Farming,Harvest Blue Jazz,CROPSANITY,
+2305,Farming,Harvest Blueberry,CROPSANITY,
+2306,Farming,Harvest Bok Choy,CROPSANITY,
+2307,Farming,Harvest Cauliflower,CROPSANITY,
+2308,Farming,Harvest Corn,CROPSANITY,
+2309,Farming,Harvest Cranberries,CROPSANITY,
+2310,Farming,Harvest Eggplant,CROPSANITY,
+2311,Farming,Harvest Fairy Rose,CROPSANITY,
+2312,Farming,Harvest Garlic,CROPSANITY,
+2313,Farming,Harvest Grape,CROPSANITY,
+2314,Farming,Harvest Green Bean,CROPSANITY,
+2315,Farming,Harvest Hops,CROPSANITY,
+2316,Farming,Harvest Hot Pepper,CROPSANITY,
+2317,Farming,Harvest Kale,CROPSANITY,
+2318,Farming,Harvest Melon,CROPSANITY,
+2319,Farming,Harvest Parsnip,CROPSANITY,
+2320,Farming,Harvest Poppy,CROPSANITY,
+2321,Farming,Harvest Potato,CROPSANITY,
+2322,Farming,Harvest Pumpkin,CROPSANITY,
+2323,Farming,Harvest Radish,CROPSANITY,
+2324,Farming,Harvest Red Cabbage,CROPSANITY,
+2325,Farming,Harvest Rhubarb,CROPSANITY,
+2326,Farming,Harvest Starfruit,CROPSANITY,
+2327,Farming,Harvest Strawberry,CROPSANITY,
+2328,Farming,Harvest Summer Spangle,CROPSANITY,
+2329,Farming,Harvest Sunflower,CROPSANITY,
+2330,Farming,Harvest Tomato,CROPSANITY,
+2331,Farming,Harvest Tulip,CROPSANITY,
+2332,Farming,Harvest Unmilled Rice,CROPSANITY,
+2333,Farming,Harvest Wheat,CROPSANITY,
+2334,Farming,Harvest Yam,CROPSANITY,
+2335,Farming,Harvest Cactus Fruit,CROPSANITY,
+2336,Farming,Harvest Pineapple,"CROPSANITY,GINGER_ISLAND",
+2337,Farming,Harvest Taro Root,"CROPSANITY,GINGER_ISLAND",
+2338,Farming,Harvest Sweet Gem Berry,CROPSANITY,
+2339,Farming,Harvest Apple,CROPSANITY,
+2340,Farming,Harvest Apricot,CROPSANITY,
+2341,Farming,Harvest Cherry,CROPSANITY,
+2342,Farming,Harvest Orange,CROPSANITY,
+2343,Farming,Harvest Pomegranate,CROPSANITY,
+2344,Farming,Harvest Peach,CROPSANITY,
+2345,Farming,Harvest Banana,"CROPSANITY,GINGER_ISLAND",
+2346,Farming,Harvest Mango,"CROPSANITY,GINGER_ISLAND",
+2347,Farming,Harvest Coffee Bean,CROPSANITY,
+2401,Shipping,Shipsanity: Duck Egg,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2402,Shipping,Shipsanity: Duck Feather,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2403,Shipping,Shipsanity: Egg,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2404,Shipping,Shipsanity: Egg (Brown),"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2405,Shipping,Shipsanity: Goat Milk,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2406,Shipping,Shipsanity: Large Goat Milk,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2407,Shipping,Shipsanity: Large Egg,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2408,Shipping,Shipsanity: Large Egg (Brown),"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2409,Shipping,Shipsanity: Large Milk,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2410,Shipping,Shipsanity: Milk,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2411,Shipping,Shipsanity: Rabbit's Foot,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2412,Shipping,Shipsanity: Roe,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2413,Shipping,Shipsanity: Truffle,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2414,Shipping,Shipsanity: Void Egg,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2415,Shipping,Shipsanity: Wool,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2416,Shipping,Shipsanity: Anchor,SHIPSANITY,
+2417,Shipping,Shipsanity: Ancient Doll,SHIPSANITY,
+2418,Shipping,Shipsanity: Ancient Drum,SHIPSANITY,
+2419,Shipping,Shipsanity: Ancient Seed,SHIPSANITY,
+2420,Shipping,Shipsanity: Ancient Sword,SHIPSANITY,
+2421,Shipping,Shipsanity: Arrowhead,SHIPSANITY,
+2422,Shipping,Shipsanity: Artifact Trove,SHIPSANITY,
+2423,Shipping,Shipsanity: Bone Flute,SHIPSANITY,
+2424,Shipping,Shipsanity: Chewing Stick,SHIPSANITY,
+2425,Shipping,Shipsanity: Chicken Statue,SHIPSANITY,
+2426,Shipping,Shipsanity: Chipped Amphora,SHIPSANITY,
+2427,Shipping,Shipsanity: Dinosaur Egg,SHIPSANITY,
+2428,Shipping,Shipsanity: Dried Starfish,SHIPSANITY,
+2429,Shipping,Shipsanity: Dwarf Gadget,SHIPSANITY,
+2430,Shipping,Shipsanity: Dwarf Scroll I,SHIPSANITY,
+2431,Shipping,Shipsanity: Dwarf Scroll II,SHIPSANITY,
+2432,Shipping,Shipsanity: Dwarf Scroll III,SHIPSANITY,
+2433,Shipping,Shipsanity: Dwarf Scroll IV,SHIPSANITY,
+2434,Shipping,Shipsanity: Dwarvish Helm,SHIPSANITY,
+2435,Shipping,Shipsanity: Elvish Jewelry,SHIPSANITY,
+2436,Shipping,Shipsanity: Glass Shards,SHIPSANITY,
+2437,Shipping,Shipsanity: Golden Mask,SHIPSANITY,
+2438,Shipping,Shipsanity: Golden Relic,SHIPSANITY,
+2439,Shipping,Shipsanity: Ornamental Fan,SHIPSANITY,
+2440,Shipping,Shipsanity: Prehistoric Handaxe,SHIPSANITY,
+2441,Shipping,Shipsanity: Prehistoric Tool,SHIPSANITY,
+2442,Shipping,Shipsanity: Rare Disc,SHIPSANITY,
+2443,Shipping,Shipsanity: Rusty Cog,SHIPSANITY,
+2444,Shipping,Shipsanity: Rusty Spoon,SHIPSANITY,
+2445,Shipping,Shipsanity: Rusty Spur,SHIPSANITY,
+2446,Shipping,Shipsanity: Strange Doll,SHIPSANITY,
+2447,Shipping,Shipsanity: Strange Doll (Green),SHIPSANITY,
+2448,Shipping,Shipsanity: Treasure Chest,SHIPSANITY,
+2449,Shipping,Shipsanity: Aged Roe,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2450,Shipping,Shipsanity: Beer,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2451,Shipping,Shipsanity: Caviar,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2452,Shipping,Shipsanity: Cheese,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2453,Shipping,Shipsanity: Cloth,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2454,Shipping,Shipsanity: Coffee,SHIPSANITY,
+2455,Shipping,Shipsanity: Dinosaur Mayonnaise,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2456,Shipping,Shipsanity: Duck Mayonnaise,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2457,Shipping,Shipsanity: Goat Cheese,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2458,Shipping,Shipsanity: Green Tea,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2459,Shipping,Shipsanity: Honey,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2460,Shipping,Shipsanity: Jelly,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2461,Shipping,Shipsanity: Juice,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2462,Shipping,Shipsanity: Maple Syrup,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2463,Shipping,Shipsanity: Mayonnaise,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2464,Shipping,Shipsanity: Mead,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2465,Shipping,Shipsanity: Oak Resin,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2466,Shipping,Shipsanity: Pale Ale,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2467,Shipping,Shipsanity: Pickles,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2468,Shipping,Shipsanity: Pine Tar,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2469,Shipping,Shipsanity: Truffle Oil,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2470,Shipping,Shipsanity: Void Mayonnaise,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2471,Shipping,Shipsanity: Wine,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2472,Shipping,Shipsanity: Algae Soup,SHIPSANITY,
+2473,Shipping,Shipsanity: Artichoke Dip,SHIPSANITY,
+2474,Shipping,Shipsanity: Autumn's Bounty,SHIPSANITY,
+2475,Shipping,Shipsanity: Baked Fish,SHIPSANITY,
+2476,Shipping,Shipsanity: Bean Hotpot,SHIPSANITY,
+2477,Shipping,Shipsanity: Blackberry Cobbler,SHIPSANITY,
+2478,Shipping,Shipsanity: Blueberry Tart,SHIPSANITY,
+2479,Shipping,Shipsanity: Bread,SHIPSANITY,
+2480,Shipping,Shipsanity: Bruschetta,SHIPSANITY,
+2481,Shipping,Shipsanity: Carp Surprise,SHIPSANITY,
+2482,Shipping,Shipsanity: Cheese Cauliflower,SHIPSANITY,
+2483,Shipping,Shipsanity: Chocolate Cake,SHIPSANITY,
+2484,Shipping,Shipsanity: Chowder,SHIPSANITY,
+2485,Shipping,Shipsanity: Coleslaw,SHIPSANITY,
+2486,Shipping,Shipsanity: Complete Breakfast,SHIPSANITY,
+2487,Shipping,Shipsanity: Cookies,SHIPSANITY,
+2488,Shipping,Shipsanity: Crab Cakes,SHIPSANITY,
+2489,Shipping,Shipsanity: Cranberry Candy,SHIPSANITY,
+2490,Shipping,Shipsanity: Cranberry Sauce,SHIPSANITY,
+2491,Shipping,Shipsanity: Crispy Bass,SHIPSANITY,
+2492,Shipping,Shipsanity: Dish O' The Sea,SHIPSANITY,
+2493,Shipping,Shipsanity: Eggplant Parmesan,SHIPSANITY,
+2494,Shipping,Shipsanity: Escargot,SHIPSANITY,
+2495,Shipping,Shipsanity: Farmer's Lunch,SHIPSANITY,
+2496,Shipping,Shipsanity: Fiddlehead Risotto,SHIPSANITY,
+2497,Shipping,Shipsanity: Fish Stew,SHIPSANITY,
+2498,Shipping,Shipsanity: Fish Taco,SHIPSANITY,
+2499,Shipping,Shipsanity: Fried Calamari,SHIPSANITY,
+2500,Shipping,Shipsanity: Fried Eel,SHIPSANITY,
+2501,Shipping,Shipsanity: Fried Egg,SHIPSANITY,
+2502,Shipping,Shipsanity: Fried Mushroom,SHIPSANITY,
+2503,Shipping,Shipsanity: Fruit Salad,SHIPSANITY,
+2504,Shipping,Shipsanity: Glazed Yams,SHIPSANITY,
+2505,Shipping,Shipsanity: Hashbrowns,SHIPSANITY,
+2506,Shipping,Shipsanity: Ice Cream,SHIPSANITY,
+2507,Shipping,Shipsanity: Lobster Bisque,SHIPSANITY,
+2508,Shipping,Shipsanity: Lucky Lunch,SHIPSANITY,
+2509,Shipping,Shipsanity: Maki Roll,SHIPSANITY,
+2510,Shipping,Shipsanity: Maple Bar,SHIPSANITY,
+2511,Shipping,Shipsanity: Miner's Treat,SHIPSANITY,
+2512,Shipping,Shipsanity: Omelet,SHIPSANITY,
+2513,Shipping,Shipsanity: Pale Broth,SHIPSANITY,
+2514,Shipping,Shipsanity: Pancakes,SHIPSANITY,
+2515,Shipping,Shipsanity: Parsnip Soup,SHIPSANITY,
+2516,Shipping,Shipsanity: Pepper Poppers,SHIPSANITY,
+2517,Shipping,Shipsanity: Pink Cake,SHIPSANITY,
+2518,Shipping,Shipsanity: Pizza,SHIPSANITY,
+2519,Shipping,Shipsanity: Plum Pudding,SHIPSANITY,
+2520,Shipping,Shipsanity: Poppyseed Muffin,SHIPSANITY,
+2521,Shipping,Shipsanity: Pumpkin Pie,SHIPSANITY,
+2522,Shipping,Shipsanity: Pumpkin Soup,SHIPSANITY,
+2523,Shipping,Shipsanity: Radish Salad,SHIPSANITY,
+2524,Shipping,Shipsanity: Red Plate,SHIPSANITY,
+2525,Shipping,Shipsanity: Rhubarb Pie,SHIPSANITY,
+2526,Shipping,Shipsanity: Rice Pudding,SHIPSANITY,
+2527,Shipping,Shipsanity: Roasted Hazelnuts,SHIPSANITY,
+2528,Shipping,Shipsanity: Roots Platter,SHIPSANITY,
+2529,Shipping,Shipsanity: Salad,SHIPSANITY,
+2530,Shipping,Shipsanity: Salmon Dinner,SHIPSANITY,
+2531,Shipping,Shipsanity: Sashimi,SHIPSANITY,
+2532,Shipping,Shipsanity: Seafoam Pudding,SHIPSANITY,
+2533,Shipping,Shipsanity: Shrimp Cocktail,SHIPSANITY,
+2534,Shipping,Shipsanity: Spaghetti,SHIPSANITY,
+2535,Shipping,Shipsanity: Spicy Eel,SHIPSANITY,
+2536,Shipping,Shipsanity: Squid Ink Ravioli,SHIPSANITY,
+2537,Shipping,Shipsanity: Stir Fry,SHIPSANITY,
+2538,Shipping,Shipsanity: Strange Bun,SHIPSANITY,
+2539,Shipping,Shipsanity: Stuffing,SHIPSANITY,
+2540,Shipping,Shipsanity: Super Meal,SHIPSANITY,
+2541,Shipping,Shipsanity: Survival Burger,SHIPSANITY,
+2542,Shipping,Shipsanity: Tom Kha Soup,SHIPSANITY,
+2543,Shipping,Shipsanity: Tortilla,SHIPSANITY,
+2544,Shipping,Shipsanity: Triple Shot Espresso,SHIPSANITY,
+2545,Shipping,Shipsanity: Trout Soup,SHIPSANITY,
+2546,Shipping,Shipsanity: Vegetable Medley,SHIPSANITY,
+2547,Shipping,Shipsanity: Bait,SHIPSANITY,
+2548,Shipping,Shipsanity: Barbed Hook,SHIPSANITY,
+2549,Shipping,Shipsanity: Basic Fertilizer,SHIPSANITY,
+2550,Shipping,Shipsanity: Basic Retaining Soil,SHIPSANITY,
+2551,Shipping,Shipsanity: Blue Slime Egg,SHIPSANITY,
+2552,Shipping,Shipsanity: Bomb,SHIPSANITY,
+2553,Shipping,Shipsanity: Brick Floor,SHIPSANITY,
+2554,Shipping,Shipsanity: Bug Steak,SHIPSANITY,
+2555,Shipping,Shipsanity: Cherry Bomb,SHIPSANITY,
+2556,Shipping,Shipsanity: Cobblestone Path,SHIPSANITY,
+2557,Shipping,Shipsanity: Cookout Kit,SHIPSANITY,
+2558,Shipping,Shipsanity: Cork Bobber,SHIPSANITY,
+2559,Shipping,Shipsanity: Crab Pot,SHIPSANITY,
+2560,Shipping,Shipsanity: Crystal Floor,SHIPSANITY,
+2561,Shipping,Shipsanity: Crystal Path,SHIPSANITY,
+2562,Shipping,Shipsanity: Deluxe Speed-Gro,SHIPSANITY,
+2563,Shipping,Shipsanity: Dressed Spinner,SHIPSANITY,
+2564,Shipping,Shipsanity: Drum Block,SHIPSANITY,
+2565,Shipping,Shipsanity: Explosive Ammo,SHIPSANITY,
+2566,Shipping,Shipsanity: Fiber Seeds,SHIPSANITY,
+2567,Shipping,Shipsanity: Field Snack,SHIPSANITY,
+2568,Shipping,Shipsanity: Flute Block,SHIPSANITY,
+2569,Shipping,Shipsanity: Gate,SHIPSANITY,
+2570,Shipping,Shipsanity: Gravel Path,SHIPSANITY,
+2571,Shipping,Shipsanity: Green Slime Egg,SHIPSANITY,
+2572,Shipping,Shipsanity: Hardwood Fence,SHIPSANITY,
+2573,Shipping,Shipsanity: Iridium Sprinkler,SHIPSANITY,
+2574,Shipping,Shipsanity: Iron Fence,SHIPSANITY,
+2575,Shipping,Shipsanity: Jack-O-Lantern,SHIPSANITY,
+2576,Shipping,Shipsanity: Lead Bobber,SHIPSANITY,
+2577,Shipping,Shipsanity: Life Elixir,SHIPSANITY,
+2578,Shipping,Shipsanity: Magnet,SHIPSANITY,
+2579,Shipping,Shipsanity: Mega Bomb,SHIPSANITY,
+2580,Shipping,Shipsanity: Monster Musk,SHIPSANITY,
+2581,Shipping,Shipsanity: Oil of Garlic,SHIPSANITY,
+2582,Shipping,Shipsanity: Purple Slime Egg,SHIPSANITY,
+2583,Shipping,Shipsanity: Quality Bobber,SHIPSANITY,
+2584,Shipping,Shipsanity: Quality Fertilizer,SHIPSANITY,
+2585,Shipping,Shipsanity: Quality Retaining Soil,SHIPSANITY,
+2586,Shipping,Shipsanity: Quality Sprinkler,SHIPSANITY,
+2587,Shipping,Shipsanity: Rain Totem,SHIPSANITY,
+2588,Shipping,Shipsanity: Red Slime Egg,SHIPSANITY,
+2589,Shipping,Shipsanity: Rustic Plank Floor,SHIPSANITY,
+2590,Shipping,Shipsanity: Speed-Gro,SHIPSANITY,
+2591,Shipping,Shipsanity: Spinner,SHIPSANITY,
+2592,Shipping,Shipsanity: Sprinkler,SHIPSANITY,
+2593,Shipping,Shipsanity: Stepping Stone Path,SHIPSANITY,
+2594,Shipping,Shipsanity: Stone Fence,SHIPSANITY,
+2595,Shipping,Shipsanity: Stone Floor,SHIPSANITY,
+2596,Shipping,Shipsanity: Stone Walkway Floor,SHIPSANITY,
+2597,Shipping,Shipsanity: Straw Floor,SHIPSANITY,
+2598,Shipping,Shipsanity: Torch,SHIPSANITY,
+2599,Shipping,Shipsanity: Trap Bobber,SHIPSANITY,
+2600,Shipping,Shipsanity: Treasure Hunter,SHIPSANITY,
+2601,Shipping,Shipsanity: Tree Fertilizer,SHIPSANITY,
+2602,Shipping,Shipsanity: Warp Totem: Beach,SHIPSANITY,
+2603,Shipping,Shipsanity: Warp Totem: Desert,SHIPSANITY,
+2604,Shipping,Shipsanity: Warp Totem: Farm,SHIPSANITY,
+2605,Shipping,Shipsanity: Warp Totem: Island,"SHIPSANITY,GINGER_ISLAND",
+2606,Shipping,Shipsanity: Warp Totem: Mountains,SHIPSANITY,
+2607,Shipping,Shipsanity: Weathered Floor,SHIPSANITY,
+2608,Shipping,Shipsanity: Wild Bait,SHIPSANITY,
+2609,Shipping,Shipsanity: Wood Fence,SHIPSANITY,
+2610,Shipping,Shipsanity: Wood Floor,SHIPSANITY,
+2611,Shipping,Shipsanity: Wood Path,SHIPSANITY,
+2612,Shipping,Shipsanity: Amaranth,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2613,Shipping,Shipsanity: Ancient Fruit,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2614,Shipping,Shipsanity: Apple,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2615,Shipping,Shipsanity: Apricot,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2616,Shipping,Shipsanity: Artichoke,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2617,Shipping,Shipsanity: Beet,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2618,Shipping,Shipsanity: Blue Jazz,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2619,Shipping,Shipsanity: Blueberry,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2620,Shipping,Shipsanity: Bok Choy,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2621,Shipping,Shipsanity: Cauliflower,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2622,Shipping,Shipsanity: Cherry,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2623,Shipping,Shipsanity: Corn,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2624,Shipping,Shipsanity: Cranberries,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2625,Shipping,Shipsanity: Eggplant,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2626,Shipping,Shipsanity: Fairy Rose,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2627,Shipping,Shipsanity: Garlic,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2628,Shipping,Shipsanity: Grape,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2629,Shipping,Shipsanity: Green Bean,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2630,Shipping,Shipsanity: Hops,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2631,Shipping,Shipsanity: Hot Pepper,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2632,Shipping,Shipsanity: Kale,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2633,Shipping,Shipsanity: Melon,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2634,Shipping,Shipsanity: Orange,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2635,Shipping,Shipsanity: Parsnip,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2636,Shipping,Shipsanity: Peach,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2637,Shipping,Shipsanity: Pomegranate,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2638,Shipping,Shipsanity: Poppy,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2639,Shipping,Shipsanity: Potato,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2640,Shipping,Shipsanity: Pumpkin,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2641,Shipping,Shipsanity: Radish,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2642,Shipping,Shipsanity: Red Cabbage,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2643,Shipping,Shipsanity: Rhubarb,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2644,Shipping,Shipsanity: Starfruit,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2645,Shipping,Shipsanity: Strawberry,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2646,Shipping,Shipsanity: Summer Spangle,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2647,Shipping,Shipsanity: Sunflower,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2648,Shipping,Shipsanity: Sweet Gem Berry,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2649,Shipping,Shipsanity: Tea Leaves,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2650,Shipping,Shipsanity: Tomato,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2651,Shipping,Shipsanity: Tulip,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2652,Shipping,Shipsanity: Unmilled Rice,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2653,Shipping,Shipsanity: Wheat,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2654,Shipping,Shipsanity: Yam,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2655,Shipping,Shipsanity: Albacore,"SHIPSANITY,SHIPSANITY_FISH",
+2656,Shipping,Shipsanity: Anchovy,"SHIPSANITY,SHIPSANITY_FISH",
+2657,Shipping,Shipsanity: Angler,"SHIPSANITY,SHIPSANITY_FISH",
+2658,Shipping,Shipsanity: Blobfish,"SHIPSANITY,SHIPSANITY_FISH",
+2659,Shipping,Shipsanity: Bream,"SHIPSANITY,SHIPSANITY_FISH",
+2660,Shipping,Shipsanity: Bullhead,"SHIPSANITY,SHIPSANITY_FISH",
+2661,Shipping,Shipsanity: Carp,"SHIPSANITY,SHIPSANITY_FISH",
+2662,Shipping,Shipsanity: Catfish,"SHIPSANITY,SHIPSANITY_FISH",
+2663,Shipping,Shipsanity: Chub,"SHIPSANITY,SHIPSANITY_FISH",
+2664,Shipping,Shipsanity: Cockle,"SHIPSANITY,SHIPSANITY_FISH",
+2665,Shipping,Shipsanity: Crab,"SHIPSANITY,SHIPSANITY_FISH",
+2666,Shipping,Shipsanity: Crayfish,"SHIPSANITY,SHIPSANITY_FISH",
+2667,Shipping,Shipsanity: Crimsonfish,"SHIPSANITY,SHIPSANITY_FISH",
+2668,Shipping,Shipsanity: Dorado,"SHIPSANITY,SHIPSANITY_FISH",
+2669,Shipping,Shipsanity: Eel,"SHIPSANITY,SHIPSANITY_FISH",
+2670,Shipping,Shipsanity: Flounder,"SHIPSANITY,SHIPSANITY_FISH",
+2671,Shipping,Shipsanity: Ghostfish,"SHIPSANITY,SHIPSANITY_FISH",
+2672,Shipping,Shipsanity: Glacierfish,"SHIPSANITY,SHIPSANITY_FISH",
+2673,Shipping,Shipsanity: Halibut,"SHIPSANITY,SHIPSANITY_FISH",
+2674,Shipping,Shipsanity: Herring,"SHIPSANITY,SHIPSANITY_FISH",
+2675,Shipping,Shipsanity: Ice Pip,"SHIPSANITY,SHIPSANITY_FISH",
+2676,Shipping,Shipsanity: Largemouth Bass,"SHIPSANITY,SHIPSANITY_FISH",
+2677,Shipping,Shipsanity: Lava Eel,"SHIPSANITY,SHIPSANITY_FISH",
+2678,Shipping,Shipsanity: Legend,"SHIPSANITY,SHIPSANITY_FISH",
+2679,Shipping,Shipsanity: Lingcod,"SHIPSANITY,SHIPSANITY_FISH",
+2680,Shipping,Shipsanity: Lobster,"SHIPSANITY,SHIPSANITY_FISH",
+2681,Shipping,Shipsanity: Midnight Carp,"SHIPSANITY,SHIPSANITY_FISH",
+2682,Shipping,Shipsanity: Midnight Squid,"SHIPSANITY,SHIPSANITY_FISH",
+2683,Shipping,Shipsanity: Mussel,"SHIPSANITY,SHIPSANITY_FISH",
+2684,Shipping,Shipsanity: Mutant Carp,"SHIPSANITY,SHIPSANITY_FISH",
+2685,Shipping,Shipsanity: Octopus,"SHIPSANITY,SHIPSANITY_FISH",
+2686,Shipping,Shipsanity: Oyster,"SHIPSANITY,SHIPSANITY_FISH",
+2687,Shipping,Shipsanity: Perch,"SHIPSANITY,SHIPSANITY_FISH",
+2688,Shipping,Shipsanity: Periwinkle,"SHIPSANITY,SHIPSANITY_FISH",
+2689,Shipping,Shipsanity: Pike,"SHIPSANITY,SHIPSANITY_FISH",
+2690,Shipping,Shipsanity: Pufferfish,"SHIPSANITY,SHIPSANITY_FISH",
+2691,Shipping,Shipsanity: Rainbow Trout,"SHIPSANITY,SHIPSANITY_FISH",
+2692,Shipping,Shipsanity: Red Mullet,"SHIPSANITY,SHIPSANITY_FISH",
+2693,Shipping,Shipsanity: Red Snapper,"SHIPSANITY,SHIPSANITY_FISH",
+2694,Shipping,Shipsanity: Salmon,"SHIPSANITY,SHIPSANITY_FISH",
+2695,Shipping,Shipsanity: Sandfish,"SHIPSANITY,SHIPSANITY_FISH",
+2696,Shipping,Shipsanity: Sardine,"SHIPSANITY,SHIPSANITY_FISH",
+2697,Shipping,Shipsanity: Scorpion Carp,"SHIPSANITY,SHIPSANITY_FISH",
+2698,Shipping,Shipsanity: Sea Cucumber,"SHIPSANITY,SHIPSANITY_FISH",
+2699,Shipping,Shipsanity: Shad,"SHIPSANITY,SHIPSANITY_FISH",
+2700,Shipping,Shipsanity: Shrimp,"SHIPSANITY,SHIPSANITY_FISH",
+2701,Shipping,Shipsanity: Slimejack,"SHIPSANITY,SHIPSANITY_FISH",
+2702,Shipping,Shipsanity: Smallmouth Bass,"SHIPSANITY,SHIPSANITY_FISH",
+2703,Shipping,Shipsanity: Snail,"SHIPSANITY,SHIPSANITY_FISH",
+2704,Shipping,Shipsanity: Spook Fish,"SHIPSANITY,SHIPSANITY_FISH",
+2705,Shipping,Shipsanity: Squid,"SHIPSANITY,SHIPSANITY_FISH",
+2706,Shipping,Shipsanity: Stonefish,"SHIPSANITY,SHIPSANITY_FISH",
+2707,Shipping,Shipsanity: Sturgeon,"SHIPSANITY,SHIPSANITY_FISH",
+2708,Shipping,Shipsanity: Sunfish,"SHIPSANITY,SHIPSANITY_FISH",
+2709,Shipping,Shipsanity: Super Cucumber,"SHIPSANITY,SHIPSANITY_FISH",
+2710,Shipping,Shipsanity: Tiger Trout,"SHIPSANITY,SHIPSANITY_FISH",
+2711,Shipping,Shipsanity: Tilapia,"SHIPSANITY,SHIPSANITY_FISH",
+2712,Shipping,Shipsanity: Tuna,"SHIPSANITY,SHIPSANITY_FISH",
+2713,Shipping,Shipsanity: Void Salmon,"SHIPSANITY,SHIPSANITY_FISH",
+2714,Shipping,Shipsanity: Walleye,"SHIPSANITY,SHIPSANITY_FISH",
+2715,Shipping,Shipsanity: Woodskip,"SHIPSANITY,SHIPSANITY_FISH",
+2716,Shipping,Shipsanity: Blackberry,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2717,Shipping,Shipsanity: Cactus Fruit,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2718,Shipping,Shipsanity: Cave Carrot,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2719,Shipping,Shipsanity: Chanterelle,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2720,Shipping,Shipsanity: Clam,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2721,Shipping,Shipsanity: Coconut,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2722,Shipping,Shipsanity: Common Mushroom,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2723,Shipping,Shipsanity: Coral,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2724,Shipping,Shipsanity: Crocus,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2725,Shipping,Shipsanity: Crystal Fruit,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2726,Shipping,Shipsanity: Daffodil,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2727,Shipping,Shipsanity: Dandelion,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2728,Shipping,Shipsanity: Fiddlehead Fern,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2729,Shipping,Shipsanity: Hazelnut,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2730,Shipping,Shipsanity: Holly,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2731,Shipping,Shipsanity: Leek,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2732,Shipping,Shipsanity: Morel,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2733,Shipping,Shipsanity: Nautilus Shell,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2734,Shipping,Shipsanity: Purple Mushroom,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2735,Shipping,Shipsanity: Rainbow Shell,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2736,Shipping,Shipsanity: Red Mushroom,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2737,Shipping,Shipsanity: Salmonberry,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2738,Shipping,Shipsanity: Sea Urchin,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2739,Shipping,Shipsanity: Snow Yam,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2740,Shipping,Shipsanity: Spice Berry,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2741,Shipping,Shipsanity: Spring Onion,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2742,Shipping,Shipsanity: Sweet Pea,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2743,Shipping,Shipsanity: Wild Horseradish,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2744,Shipping,Shipsanity: Wild Plum,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2745,Shipping,Shipsanity: Winter Root,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2746,Shipping,Shipsanity: Tea Set,SHIPSANITY,
+2747,Shipping,Shipsanity: Battery Pack,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2748,Shipping,Shipsanity: Clay,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2749,Shipping,Shipsanity: Copper Bar,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2750,Shipping,Shipsanity: Fiber,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2751,Shipping,Shipsanity: Gold Bar,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2752,Shipping,Shipsanity: Hardwood,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2753,Shipping,Shipsanity: Iridium Bar,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2754,Shipping,Shipsanity: Iron Bar,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2755,Shipping,Shipsanity: Oil,SHIPSANITY,
+2756,Shipping,Shipsanity: Refined Quartz,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2757,Shipping,Shipsanity: Rice,SHIPSANITY,
+2758,Shipping,Shipsanity: Sap,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2759,Shipping,Shipsanity: Stone,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2760,Shipping,Shipsanity: Sugar,SHIPSANITY,
+2761,Shipping,Shipsanity: Vinegar,SHIPSANITY,
+2762,Shipping,Shipsanity: Wheat Flour,SHIPSANITY,
+2763,Shipping,Shipsanity: Wood,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2764,Shipping,Shipsanity: Aerinite,SHIPSANITY,
+2765,Shipping,Shipsanity: Alamite,SHIPSANITY,
+2766,Shipping,Shipsanity: Amethyst,SHIPSANITY,
+2767,Shipping,Shipsanity: Amphibian Fossil,SHIPSANITY,
+2768,Shipping,Shipsanity: Aquamarine,SHIPSANITY,
+2769,Shipping,Shipsanity: Baryte,SHIPSANITY,
+2770,Shipping,Shipsanity: Basalt,SHIPSANITY,
+2771,Shipping,Shipsanity: Bixite,SHIPSANITY,
+2772,Shipping,Shipsanity: Calcite,SHIPSANITY,
+2773,Shipping,Shipsanity: Celestine,SHIPSANITY,
+2774,Shipping,Shipsanity: Coal,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2775,Shipping,Shipsanity: Copper Ore,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2776,Shipping,Shipsanity: Diamond,SHIPSANITY,
+2777,Shipping,Shipsanity: Dolomite,SHIPSANITY,
+2778,Shipping,Shipsanity: Earth Crystal,SHIPSANITY,
+2779,Shipping,Shipsanity: Emerald,SHIPSANITY,
+2780,Shipping,Shipsanity: Esperite,SHIPSANITY,
+2781,Shipping,Shipsanity: Fairy Stone,SHIPSANITY,
+2782,Shipping,Shipsanity: Fire Opal,SHIPSANITY,
+2783,Shipping,Shipsanity: Fire Quartz,SHIPSANITY,
+2784,Shipping,Shipsanity: Fluorapatite,SHIPSANITY,
+2785,Shipping,Shipsanity: Frozen Geode,SHIPSANITY,
+2786,Shipping,Shipsanity: Frozen Tear,SHIPSANITY,
+2787,Shipping,Shipsanity: Geminite,SHIPSANITY,
+2788,Shipping,Shipsanity: Geode,SHIPSANITY,
+2789,Shipping,Shipsanity: Ghost Crystal,SHIPSANITY,
+2790,Shipping,Shipsanity: Gold Ore,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2791,Shipping,Shipsanity: Granite,SHIPSANITY,
+2792,Shipping,Shipsanity: Helvite,SHIPSANITY,
+2793,Shipping,Shipsanity: Hematite,SHIPSANITY,
+2794,Shipping,Shipsanity: Iridium Ore,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2795,Shipping,Shipsanity: Iron Ore,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2796,Shipping,Shipsanity: Jade,SHIPSANITY,
+2797,Shipping,Shipsanity: Jagoite,SHIPSANITY,
+2798,Shipping,Shipsanity: Jamborite,SHIPSANITY,
+2799,Shipping,Shipsanity: Jasper,SHIPSANITY,
+2800,Shipping,Shipsanity: Kyanite,SHIPSANITY,
+2801,Shipping,Shipsanity: Lemon Stone,SHIPSANITY,
+2802,Shipping,Shipsanity: Limestone,SHIPSANITY,
+2803,Shipping,Shipsanity: Lunarite,SHIPSANITY,
+2804,Shipping,Shipsanity: Magma Geode,SHIPSANITY,
+2805,Shipping,Shipsanity: Malachite,SHIPSANITY,
+2806,Shipping,Shipsanity: Marble,SHIPSANITY,
+2807,Shipping,Shipsanity: Mudstone,SHIPSANITY,
+2808,Shipping,Shipsanity: Nautilus Fossil,SHIPSANITY,
+2809,Shipping,Shipsanity: Nekoite,SHIPSANITY,
+2810,Shipping,Shipsanity: Neptunite,SHIPSANITY,
+2811,Shipping,Shipsanity: Obsidian,SHIPSANITY,
+2812,Shipping,Shipsanity: Ocean Stone,SHIPSANITY,
+2813,Shipping,Shipsanity: Omni Geode,SHIPSANITY,
+2814,Shipping,Shipsanity: Opal,SHIPSANITY,
+2815,Shipping,Shipsanity: Orpiment,SHIPSANITY,
+2816,Shipping,Shipsanity: Palm Fossil,SHIPSANITY,
+2817,Shipping,Shipsanity: Petrified Slime,SHIPSANITY,
+2818,Shipping,Shipsanity: Prehistoric Rib,SHIPSANITY,
+2819,Shipping,Shipsanity: Prehistoric Scapula,SHIPSANITY,
+2820,Shipping,Shipsanity: Prehistoric Skull,SHIPSANITY,
+2821,Shipping,Shipsanity: Prehistoric Tibia,SHIPSANITY,
+2822,Shipping,Shipsanity: Prehistoric Vertebra,SHIPSANITY,
+2823,Shipping,Shipsanity: Prismatic Shard,SHIPSANITY,
+2824,Shipping,Shipsanity: Pyrite,SHIPSANITY,
+2825,Shipping,Shipsanity: Quartz,SHIPSANITY,
+2826,Shipping,Shipsanity: Ruby,SHIPSANITY,
+2827,Shipping,Shipsanity: Sandstone,SHIPSANITY,
+2828,Shipping,Shipsanity: Skeletal Hand,SHIPSANITY,
+2829,Shipping,Shipsanity: Skeletal Tail,SHIPSANITY,
+2830,Shipping,Shipsanity: Slate,SHIPSANITY,
+2831,Shipping,Shipsanity: Soapstone,SHIPSANITY,
+2832,Shipping,Shipsanity: Star Shards,SHIPSANITY,
+2833,Shipping,Shipsanity: Thunder Egg,SHIPSANITY,
+2834,Shipping,Shipsanity: Tigerseye,SHIPSANITY,
+2835,Shipping,Shipsanity: Topaz,SHIPSANITY,
+2836,Shipping,Shipsanity: Trilobite,SHIPSANITY,
+2837,Shipping,Shipsanity: Bat Wing,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2838,Shipping,Shipsanity: Bone Fragment,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2839,Shipping,Shipsanity: Curiosity Lure,SHIPSANITY,
+2840,Shipping,Shipsanity: Slime,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2841,Shipping,Shipsanity: Solar Essence,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2842,Shipping,Shipsanity: Squid Ink,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2843,Shipping,Shipsanity: Void Essence,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2844,Shipping,Shipsanity: Bouquet,SHIPSANITY,
+2845,Shipping,Shipsanity: Energy Tonic,SHIPSANITY,
+2846,Shipping,Shipsanity: Golden Pumpkin,SHIPSANITY,
+2847,Shipping,Shipsanity: Green Algae,SHIPSANITY,
+2848,Shipping,Shipsanity: Hay,SHIPSANITY,
+2849,Shipping,Shipsanity: Magic Rock Candy,SHIPSANITY,
+2850,Shipping,Shipsanity: Muscle Remedy,SHIPSANITY,
+2851,Shipping,Shipsanity: Pearl,SHIPSANITY,
+2852,Shipping,Shipsanity: Rotten Plant,SHIPSANITY,
+2853,Shipping,Shipsanity: Seaweed,SHIPSANITY,
+2854,Shipping,Shipsanity: Void Ghost Pendant,SHIPSANITY,
+2855,Shipping,Shipsanity: White Algae,SHIPSANITY,
+2856,Shipping,Shipsanity: Wilted Bouquet,SHIPSANITY,
+2857,Shipping,Shipsanity: Secret Note,SHIPSANITY,
+2858,Shipping,Shipsanity: Acorn,SHIPSANITY,
+2859,Shipping,Shipsanity: Amaranth Seeds,SHIPSANITY,
+2860,Shipping,Shipsanity: Ancient Seeds,SHIPSANITY,
+2861,Shipping,Shipsanity: Apple Sapling,SHIPSANITY,
+2862,Shipping,Shipsanity: Apricot Sapling,SHIPSANITY,
+2863,Shipping,Shipsanity: Artichoke Seeds,SHIPSANITY,
+2864,Shipping,Shipsanity: Bean Starter,SHIPSANITY,
+2865,Shipping,Shipsanity: Beet Seeds,SHIPSANITY,
+2866,Shipping,Shipsanity: Blueberry Seeds,SHIPSANITY,
+2867,Shipping,Shipsanity: Bok Choy Seeds,SHIPSANITY,
+2868,Shipping,Shipsanity: Cactus Seeds,SHIPSANITY,
+2869,Shipping,Shipsanity: Cauliflower Seeds,SHIPSANITY,
+2870,Shipping,Shipsanity: Cherry Sapling,SHIPSANITY,
+2871,Shipping,Shipsanity: Coffee Bean,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2872,Shipping,Shipsanity: Corn Seeds,SHIPSANITY,
+2873,Shipping,Shipsanity: Cranberry Seeds,SHIPSANITY,
+2874,Shipping,Shipsanity: Eggplant Seeds,SHIPSANITY,
+2875,Shipping,Shipsanity: Fairy Seeds,SHIPSANITY,
+2876,Shipping,Shipsanity: Fall Seeds,SHIPSANITY,
+2877,Shipping,Shipsanity: Garlic Seeds,SHIPSANITY,
+2878,Shipping,Shipsanity: Grape Starter,SHIPSANITY,
+2879,Shipping,Shipsanity: Grass Starter,SHIPSANITY,
+2880,Shipping,Shipsanity: Hops Starter,SHIPSANITY,
+2881,Shipping,Shipsanity: Jazz Seeds,SHIPSANITY,
+2882,Shipping,Shipsanity: Kale Seeds,SHIPSANITY,
+2883,Shipping,Shipsanity: Mahogany Seed,SHIPSANITY,
+2884,Shipping,Shipsanity: Maple Seed,SHIPSANITY,
+2885,Shipping,Shipsanity: Melon Seeds,SHIPSANITY,
+2886,Shipping,Shipsanity: Mixed Seeds,SHIPSANITY,
+2887,Shipping,Shipsanity: Orange Sapling,SHIPSANITY,
+2888,Shipping,Shipsanity: Parsnip Seeds,SHIPSANITY,
+2889,Shipping,Shipsanity: Peach Sapling,SHIPSANITY,
+2890,Shipping,Shipsanity: Pepper Seeds,SHIPSANITY,
+2891,Shipping,Shipsanity: Pine Cone,SHIPSANITY,
+2892,Shipping,Shipsanity: Pomegranate Sapling,SHIPSANITY,
+2893,Shipping,Shipsanity: Poppy Seeds,SHIPSANITY,
+2894,Shipping,Shipsanity: Potato Seeds,SHIPSANITY,
+2895,Shipping,Shipsanity: Pumpkin Seeds,SHIPSANITY,
+2896,Shipping,Shipsanity: Radish Seeds,SHIPSANITY,
+2897,Shipping,Shipsanity: Rare Seed,SHIPSANITY,
+2898,Shipping,Shipsanity: Red Cabbage Seeds,SHIPSANITY,
+2899,Shipping,Shipsanity: Rhubarb Seeds,SHIPSANITY,
+2900,Shipping,Shipsanity: Rice Shoot,SHIPSANITY,
+2901,Shipping,Shipsanity: Spangle Seeds,SHIPSANITY,
+2902,Shipping,Shipsanity: Spring Seeds,SHIPSANITY,
+2903,Shipping,Shipsanity: Starfruit Seeds,SHIPSANITY,
+2904,Shipping,Shipsanity: Strawberry Seeds,SHIPSANITY,
+2905,Shipping,Shipsanity: Summer Seeds,SHIPSANITY,
+2906,Shipping,Shipsanity: Sunflower Seeds,SHIPSANITY,
+2907,Shipping,Shipsanity: Tea Sapling,SHIPSANITY,
+2908,Shipping,Shipsanity: Tomato Seeds,SHIPSANITY,
+2909,Shipping,Shipsanity: Tulip Bulb,SHIPSANITY,
+2910,Shipping,Shipsanity: Wheat Seeds,SHIPSANITY,
+2911,Shipping,Shipsanity: Winter Seeds,SHIPSANITY,
+2912,Shipping,Shipsanity: Yam Seeds,SHIPSANITY,
+2913,Shipping,Shipsanity: Broken CD,SHIPSANITY,
+2914,Shipping,Shipsanity: Broken Glasses,SHIPSANITY,
+2915,Shipping,Shipsanity: Driftwood,SHIPSANITY,
+2916,Shipping,Shipsanity: Joja Cola,SHIPSANITY,
+2917,Shipping,Shipsanity: Soggy Newspaper,SHIPSANITY,
+2918,Shipping,Shipsanity: Trash,SHIPSANITY,
+2919,Shipping,Shipsanity: Bug Meat,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2920,Shipping,Shipsanity: Golden Egg,SHIPSANITY,
+2921,Shipping,Shipsanity: Ostrich Egg,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2922,Shipping,Shipsanity: Fossilized Leg,"GINGER_ISLAND,SHIPSANITY",
+2923,Shipping,Shipsanity: Fossilized Ribs,"GINGER_ISLAND,SHIPSANITY",
+2924,Shipping,Shipsanity: Fossilized Skull,"GINGER_ISLAND,SHIPSANITY",
+2925,Shipping,Shipsanity: Fossilized Spine,"GINGER_ISLAND,SHIPSANITY",
+2926,Shipping,Shipsanity: Fossilized Tail,"GINGER_ISLAND,SHIPSANITY",
+2927,Shipping,Shipsanity: Mummified Bat,"GINGER_ISLAND,SHIPSANITY",
+2928,Shipping,Shipsanity: Mummified Frog,"GINGER_ISLAND,SHIPSANITY",
+2929,Shipping,Shipsanity: Snake Skull,"GINGER_ISLAND,SHIPSANITY",
+2930,Shipping,Shipsanity: Snake Vertebrae,"GINGER_ISLAND,SHIPSANITY",
+2931,Shipping,Shipsanity: Banana Pudding,"GINGER_ISLAND,SHIPSANITY",
+2932,Shipping,Shipsanity: Ginger Ale,"GINGER_ISLAND,SHIPSANITY",
+2933,Shipping,Shipsanity: Mango Sticky Rice,"GINGER_ISLAND,SHIPSANITY",
+2934,Shipping,Shipsanity: Pina Colada,"GINGER_ISLAND,SHIPSANITY",
+2935,Shipping,Shipsanity: Poi,"GINGER_ISLAND,SHIPSANITY",
+2936,Shipping,Shipsanity: Tropical Curry,"GINGER_ISLAND,SHIPSANITY",
+2937,Shipping,Shipsanity: Deluxe Fertilizer,"GINGER_ISLAND,SHIPSANITY",
+2938,Shipping,Shipsanity: Deluxe Retaining Soil,"GINGER_ISLAND,SHIPSANITY",
+2939,Shipping,Shipsanity: Fairy Dust,"GINGER_ISLAND,SHIPSANITY",
+2940,Shipping,Shipsanity: Hyper Speed-Gro,"GINGER_ISLAND,SHIPSANITY",
+2941,Shipping,Shipsanity: Magic Bait,"GINGER_ISLAND,SHIPSANITY",
+2942,Shipping,Shipsanity: Banana,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2943,Shipping,Shipsanity: Mango,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2944,Shipping,Shipsanity: Pineapple,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2945,Shipping,Shipsanity: Qi Fruit,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_CROP,REQUIRES_QI_ORDERS",
+2946,Shipping,Shipsanity: Taro Root,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2947,Shipping,Shipsanity: Blue Discus,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_FISH",
+2948,Shipping,Shipsanity: Glacierfish Jr.,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_FISH,REQUIRES_QI_ORDERS",
+2949,Shipping,Shipsanity: Legend II,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_FISH,REQUIRES_QI_ORDERS",
+2950,Shipping,Shipsanity: Lionfish,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_FISH",
+2951,Shipping,Shipsanity: Ms. Angler,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_FISH,REQUIRES_QI_ORDERS",
+2952,Shipping,Shipsanity: Radioactive Carp,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_FISH,REQUIRES_QI_ORDERS",
+2953,Shipping,Shipsanity: Son of Crimsonfish,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_FISH,REQUIRES_QI_ORDERS",
+2954,Shipping,Shipsanity: Stingray,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_FISH",
+2955,Shipping,Shipsanity: Ginger,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2956,Shipping,Shipsanity: Magma Cap,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
+2957,Shipping,Shipsanity: Cinder Shard,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
+2958,Shipping,Shipsanity: Dragon Tooth,"GINGER_ISLAND,SHIPSANITY",
+2959,Shipping,Shipsanity: Qi Seasoning,"GINGER_ISLAND,SHIPSANITY,REQUIRES_QI_ORDERS",
+2960,Shipping,Shipsanity: Radioactive Bar,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_FULL_SHIPMENT,REQUIRES_QI_ORDERS",
+2961,Shipping,Shipsanity: Radioactive Ore,"GINGER_ISLAND,SHIPSANITY,SHIPSANITY_FULL_SHIPMENT,REQUIRES_QI_ORDERS",
+2962,Shipping,Shipsanity: Enricher,"GINGER_ISLAND,SHIPSANITY,REQUIRES_QI_ORDERS",
+2963,Shipping,Shipsanity: Pressure Nozzle,"GINGER_ISLAND,SHIPSANITY,REQUIRES_QI_ORDERS",
+2964,Shipping,Shipsanity: Galaxy Soul,"GINGER_ISLAND,SHIPSANITY,REQUIRES_QI_ORDERS",
+2965,Shipping,Shipsanity: Tiger Slime Egg,"GINGER_ISLAND,SHIPSANITY",
+2966,Shipping,Shipsanity: Movie Ticket,"SHIPSANITY",
+2967,Shipping,Shipsanity: Journal Scrap,"GINGER_ISLAND,SHIPSANITY",
+2968,Shipping,Shipsanity: Banana Sapling,"GINGER_ISLAND,SHIPSANITY",
+2969,Shipping,Shipsanity: Mango Sapling,"GINGER_ISLAND,SHIPSANITY",
+2970,Shipping,Shipsanity: Mushroom Tree Seed,"GINGER_ISLAND,SHIPSANITY,REQUIRES_QI_ORDERS",
+2971,Shipping,Shipsanity: Pineapple Seeds,"GINGER_ISLAND,SHIPSANITY",
+2972,Shipping,Shipsanity: Qi Bean,"GINGER_ISLAND,SHIPSANITY",
+2973,Shipping,Shipsanity: Taro Tuber,"GINGER_ISLAND,SHIPSANITY",
+3001,Adventurer's Guild,Monster Eradication: Slimes,"MONSTERSANITY,MONSTERSANITY_GOALS",
+3002,Adventurer's Guild,Monster Eradication: Void Spirits,"MONSTERSANITY,MONSTERSANITY_GOALS",
+3003,Adventurer's Guild,Monster Eradication: Bats,"MONSTERSANITY,MONSTERSANITY_GOALS",
+3004,Adventurer's Guild,Monster Eradication: Skeletons,"MONSTERSANITY,MONSTERSANITY_GOALS",
+3005,Adventurer's Guild,Monster Eradication: Cave Insects,"MONSTERSANITY,MONSTERSANITY_GOALS",
+3006,Adventurer's Guild,Monster Eradication: Duggies,"MONSTERSANITY,MONSTERSANITY_GOALS",
+3007,Adventurer's Guild,Monster Eradication: Dust Sprites,"MONSTERSANITY,MONSTERSANITY_GOALS",
+3008,Adventurer's Guild,Monster Eradication: Rock Crabs,"MONSTERSANITY,MONSTERSANITY_GOALS",
+3009,Adventurer's Guild,Monster Eradication: Mummies,"MONSTERSANITY,MONSTERSANITY_GOALS",
+3010,Adventurer's Guild,Monster Eradication: Pepper Rex,"MONSTERSANITY,MONSTERSANITY_GOALS,MONSTERSANITY_MONSTER",
+3011,Adventurer's Guild,Monster Eradication: Serpents,"MONSTERSANITY,MONSTERSANITY_GOALS",
+3012,Adventurer's Guild,Monster Eradication: Magma Sprites,"GINGER_ISLAND,MONSTERSANITY,MONSTERSANITY_GOALS",
+3020,Adventurer's Guild,Monster Eradication: 200 Slimes,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3021,Adventurer's Guild,Monster Eradication: 400 Slimes,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3022,Adventurer's Guild,Monster Eradication: 600 Slimes,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3023,Adventurer's Guild,Monster Eradication: 800 Slimes,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3024,Adventurer's Guild,Monster Eradication: 30 Void Spirits,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3025,Adventurer's Guild,Monster Eradication: 60 Void Spirits,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3026,Adventurer's Guild,Monster Eradication: 90 Void Spirits,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3027,Adventurer's Guild,Monster Eradication: 120 Void Spirits,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3028,Adventurer's Guild,Monster Eradication: 40 Bats,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3029,Adventurer's Guild,Monster Eradication: 80 Bats,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3030,Adventurer's Guild,Monster Eradication: 120 Bats,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3031,Adventurer's Guild,Monster Eradication: 160 Bats,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3032,Adventurer's Guild,Monster Eradication: 10 Skeletons,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3033,Adventurer's Guild,Monster Eradication: 20 Skeletons,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3034,Adventurer's Guild,Monster Eradication: 30 Skeletons,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3035,Adventurer's Guild,Monster Eradication: 40 Skeletons,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3036,Adventurer's Guild,Monster Eradication: 25 Cave Insects,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3037,Adventurer's Guild,Monster Eradication: 50 Cave Insects,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3038,Adventurer's Guild,Monster Eradication: 75 Cave Insects,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3039,Adventurer's Guild,Monster Eradication: 100 Cave Insects,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3040,Adventurer's Guild,Monster Eradication: 6 Duggies,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3041,Adventurer's Guild,Monster Eradication: 12 Duggies,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3042,Adventurer's Guild,Monster Eradication: 18 Duggies,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3043,Adventurer's Guild,Monster Eradication: 24 Duggies,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3044,Adventurer's Guild,Monster Eradication: 100 Dust Sprites,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3045,Adventurer's Guild,Monster Eradication: 200 Dust Sprites,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3046,Adventurer's Guild,Monster Eradication: 300 Dust Sprites,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3047,Adventurer's Guild,Monster Eradication: 400 Dust Sprites,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3048,Adventurer's Guild,Monster Eradication: 12 Rock Crabs,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3049,Adventurer's Guild,Monster Eradication: 24 Rock Crabs,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3050,Adventurer's Guild,Monster Eradication: 36 Rock Crabs,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3051,Adventurer's Guild,Monster Eradication: 48 Rock Crabs,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3052,Adventurer's Guild,Monster Eradication: 20 Mummies,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3053,Adventurer's Guild,Monster Eradication: 40 Mummies,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3054,Adventurer's Guild,Monster Eradication: 60 Mummies,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3055,Adventurer's Guild,Monster Eradication: 80 Mummies,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3056,Adventurer's Guild,Monster Eradication: 10 Pepper Rex,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3057,Adventurer's Guild,Monster Eradication: 20 Pepper Rex,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3058,Adventurer's Guild,Monster Eradication: 30 Pepper Rex,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3059,Adventurer's Guild,Monster Eradication: 40 Pepper Rex,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3060,Adventurer's Guild,Monster Eradication: 50 Serpents,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3061,Adventurer's Guild,Monster Eradication: 100 Serpents,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3062,Adventurer's Guild,Monster Eradication: 150 Serpents,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3063,Adventurer's Guild,Monster Eradication: 200 Serpents,"MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3064,Adventurer's Guild,Monster Eradication: 30 Magma Sprites,"GINGER_ISLAND,MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3065,Adventurer's Guild,Monster Eradication: 60 Magma Sprites,"GINGER_ISLAND,MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3066,Adventurer's Guild,Monster Eradication: 90 Magma Sprites,"GINGER_ISLAND,MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3067,Adventurer's Guild,Monster Eradication: 120 Magma Sprites,"GINGER_ISLAND,MONSTERSANITY,MONSTERSANITY_PROGRESSIVE_GOALS",
+3101,Adventurer's Guild,Monster Eradication: Green Slime,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3102,Adventurer's Guild,Monster Eradication: Frost Jelly,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3103,Adventurer's Guild,Monster Eradication: Sludge,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3104,Adventurer's Guild,Monster Eradication: Tiger Slime,"GINGER_ISLAND,MONSTERSANITY,MONSTERSANITY_MONSTER",
+3105,Adventurer's Guild,Monster Eradication: Shadow Shaman,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3106,Adventurer's Guild,Monster Eradication: Shadow Brute,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3107,Adventurer's Guild,Monster Eradication: Shadow Sniper,"GINGER_ISLAND,REQUIRES_QI_ORDERS,MONSTERSANITY,MONSTERSANITY_MONSTER",
+3108,Adventurer's Guild,Monster Eradication: Bat,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3109,Adventurer's Guild,Monster Eradication: Frost Bat,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3110,Adventurer's Guild,Monster Eradication: Lava Bat,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3111,Adventurer's Guild,Monster Eradication: Iridium Bat,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3112,Adventurer's Guild,Monster Eradication: Skeleton,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3113,Adventurer's Guild,Monster Eradication: Skeleton Mage,"GINGER_ISLAND,REQUIRES_QI_ORDERS,MONSTERSANITY,MONSTERSANITY_MONSTER",
+3114,Adventurer's Guild,Monster Eradication: Bug,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3115,Adventurer's Guild,Monster Eradication: Fly,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3116,Adventurer's Guild,Monster Eradication: Grub,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3117,Adventurer's Guild,Monster Eradication: Duggy,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3118,Adventurer's Guild,Monster Eradication: Magma Duggy,"GINGER_ISLAND,REQUIRES_QI_ORDERS,MONSTERSANITY,MONSTERSANITY_MONSTER",
+3119,Adventurer's Guild,Monster Eradication: Dust Sprite,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3120,Adventurer's Guild,Monster Eradication: Rock Crab,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3121,Adventurer's Guild,Monster Eradication: Lava Crab,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3122,Adventurer's Guild,Monster Eradication: Iridium Crab,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3123,Adventurer's Guild,Monster Eradication: Mummy,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3124,Adventurer's Guild,Monster Eradication: Serpent,"MONSTERSANITY,MONSTERSANITY_MONSTER",
+3125,Adventurer's Guild,Monster Eradication: Royal Serpent,"GINGER_ISLAND,REQUIRES_QI_ORDERS,MONSTERSANITY,MONSTERSANITY_MONSTER",
+3126,Adventurer's Guild,Monster Eradication: Magma Sprite,"GINGER_ISLAND,MONSTERSANITY,MONSTERSANITY_MONSTER",
+3127,Adventurer's Guild,Monster Eradication: Magma Sparker,"GINGER_ISLAND,MONSTERSANITY,MONSTERSANITY_MONSTER",
+3201,Kitchen,Cook Algae Soup,COOKSANITY,
+3202,Kitchen,Cook Artichoke Dip,"COOKSANITY,COOKSANITY_QOS",
+3203,Kitchen,Cook Autumn's Bounty,COOKSANITY,
+3204,Kitchen,Cook Baked Fish,"COOKSANITY,COOKSANITY_QOS",
+3205,Kitchen,Cook Banana Pudding,"COOKSANITY,GINGER_ISLAND",
+3206,Kitchen,Cook Bean Hotpot,COOKSANITY,
+3207,Kitchen,Cook Blackberry Cobbler,"COOKSANITY,COOKSANITY_QOS",
+3208,Kitchen,Cook Blueberry Tart,COOKSANITY,
+3209,Kitchen,Cook Bread,"COOKSANITY,COOKSANITY_QOS",
+3210,Kitchen,Cook Bruschetta,"COOKSANITY,COOKSANITY_QOS",
+3211,Kitchen,Cook Carp Surprise,"COOKSANITY,COOKSANITY_QOS",
+3212,Kitchen,Cook Cheese Cauliflower,COOKSANITY,
+3213,Kitchen,Cook Chocolate Cake,"COOKSANITY,COOKSANITY_QOS",
+3214,Kitchen,Cook Chowder,COOKSANITY,
+3215,Kitchen,Cook Coleslaw,"COOKSANITY,COOKSANITY_QOS",
+3216,Kitchen,Cook Complete Breakfast,"COOKSANITY,COOKSANITY_QOS",
+3217,Kitchen,Cook Cookies,COOKSANITY,
+3218,Kitchen,Cook Crab Cakes,"COOKSANITY,COOKSANITY_QOS",
+3219,Kitchen,Cook Cranberry Candy,"COOKSANITY,COOKSANITY_QOS",
+3220,Kitchen,Cook Cranberry Sauce,COOKSANITY,
+3221,Kitchen,Cook Crispy Bass,COOKSANITY,
+3222,Kitchen,Cook Dish O' The Sea,COOKSANITY,
+3223,Kitchen,Cook Eggplant Parmesan,COOKSANITY,
+3224,Kitchen,Cook Escargot,COOKSANITY,
+3225,Kitchen,Cook Farmer's Lunch,COOKSANITY,
+3226,Kitchen,Cook Fiddlehead Risotto,"COOKSANITY,COOKSANITY_QOS",
+3227,Kitchen,Cook Fish Stew,COOKSANITY,
+3228,Kitchen,Cook Fish Taco,COOKSANITY,
+3229,Kitchen,Cook Fried Calamari,COOKSANITY,
+3230,Kitchen,Cook Fried Eel,COOKSANITY,
+3231,Kitchen,Cook Fried Egg,COOKSANITY,
+3232,Kitchen,Cook Fried Mushroom,COOKSANITY,
+3233,Kitchen,Cook Fruit Salad,"COOKSANITY,COOKSANITY_QOS",
+3234,Kitchen,Cook Ginger Ale,"COOKSANITY,GINGER_ISLAND",
+3235,Kitchen,Cook Glazed Yams,"COOKSANITY,COOKSANITY_QOS",
+3236,Kitchen,Cook Hashbrowns,"COOKSANITY,COOKSANITY_QOS",
+3237,Kitchen,Cook Ice Cream,COOKSANITY,
+3238,Kitchen,Cook Lobster Bisque,"COOKSANITY,COOKSANITY_QOS",
+3239,Kitchen,Cook Lucky Lunch,"COOKSANITY,COOKSANITY_QOS",
+3240,Kitchen,Cook Maki Roll,"COOKSANITY,COOKSANITY_QOS",
+3241,Kitchen,Cook Mango Sticky Rice,"COOKSANITY,GINGER_ISLAND",
+3242,Kitchen,Cook Maple Bar,"COOKSANITY,COOKSANITY_QOS",
+3243,Kitchen,Cook Miner's Treat,COOKSANITY,
+3244,Kitchen,Cook Omelet,"COOKSANITY,COOKSANITY_QOS",
+3245,Kitchen,Cook Pale Broth,COOKSANITY,
+3246,Kitchen,Cook Pancakes,"COOKSANITY,COOKSANITY_QOS",
+3247,Kitchen,Cook Parsnip Soup,COOKSANITY,
+3248,Kitchen,Cook Pepper Poppers,COOKSANITY,
+3249,Kitchen,Cook Pink Cake,"COOKSANITY,COOKSANITY_QOS",
+3250,Kitchen,Cook Pizza,"COOKSANITY,COOKSANITY_QOS",
+3251,Kitchen,Cook Plum Pudding,"COOKSANITY,COOKSANITY_QOS",
+3252,Kitchen,Cook Poi,"COOKSANITY,GINGER_ISLAND",
+3253,Kitchen,Cook Poppyseed Muffin,"COOKSANITY,COOKSANITY_QOS",
+3254,Kitchen,Cook Pumpkin Pie,"COOKSANITY,COOKSANITY_QOS",
+3255,Kitchen,Cook Pumpkin Soup,COOKSANITY,
+3256,Kitchen,Cook Radish Salad,"COOKSANITY,COOKSANITY_QOS",
+3257,Kitchen,Cook Red Plate,COOKSANITY,
+3258,Kitchen,Cook Rhubarb Pie,COOKSANITY,
+3259,Kitchen,Cook Rice Pudding,COOKSANITY,
+3260,Kitchen,Cook Roasted Hazelnuts,"COOKSANITY,COOKSANITY_QOS",
+3261,Kitchen,Cook Roots Platter,COOKSANITY,
+3262,Kitchen,Cook Salad,COOKSANITY,
+3263,Kitchen,Cook Salmon Dinner,COOKSANITY,
+3264,Kitchen,Cook Sashimi,COOKSANITY,
+3265,Kitchen,Cook Seafoam Pudding,COOKSANITY,
+3266,Kitchen,Cook Shrimp Cocktail,"COOKSANITY,COOKSANITY_QOS",
+3267,Kitchen,Cook Spaghetti,COOKSANITY,
+3268,Kitchen,Cook Spicy Eel,COOKSANITY,
+3269,Kitchen,Cook Squid Ink Ravioli,COOKSANITY,
+3270,Kitchen,Cook Stir Fry,"COOKSANITY,COOKSANITY_QOS",
+3271,Kitchen,Cook Strange Bun,COOKSANITY,
+3272,Kitchen,Cook Stuffing,COOKSANITY,
+3273,Kitchen,Cook Super Meal,COOKSANITY,
+3274,Kitchen,Cook Survival Burger,COOKSANITY,
+3275,Kitchen,Cook Tom Kha Soup,COOKSANITY,
+3276,Kitchen,Cook Tortilla,"COOKSANITY,COOKSANITY_QOS",
+3277,Kitchen,Cook Triple Shot Espresso,COOKSANITY,
+3278,Kitchen,Cook Tropical Curry,"COOKSANITY,GINGER_ISLAND",
+3279,Kitchen,Cook Trout Soup,"COOKSANITY,COOKSANITY_QOS",
+3280,Kitchen,Cook Vegetable Medley,COOKSANITY,
+3301,Farm,Algae Soup Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3302,The Queen of Sauce,Artichoke Dip Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3303,Farm,Autumn's Bounty Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3304,The Queen of Sauce,Baked Fish Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3305,Farm,Banana Pudding Recipe,"CHEFSANITY,GINGER_ISLAND,CHEFSANITY_PURCHASE",
+3306,Farm,Bean Hotpot Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3307,The Queen of Sauce,Blackberry Cobbler Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3308,Farm,Blueberry Tart Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3309,The Queen of Sauce,Bread Recipe,"CHEFSANITY,CHEFSANITY_QOS,CHEFSANITY_FRIENDSHIP,CHEFSANITY_PURCHASE",
+3310,The Queen of Sauce,Bruschetta Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3311,The Queen of Sauce,Carp Surprise Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3312,Farm,Cheese Cauliflower Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3313,The Queen of Sauce,Chocolate Cake Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3314,Farm,Chowder Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3315,The Queen of Sauce,Coleslaw Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3316,The Queen of Sauce,Complete Breakfast Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3317,Farm,Cookies Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3318,The Queen of Sauce,Crab Cakes Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3319,The Queen of Sauce,Cranberry Candy Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3320,Farm,Cranberry Sauce Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3321,Farm,Crispy Bass Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3322,Farm,Dish O' The Sea Recipe,"CHEFSANITY,CHEFSANITY_SKILL",
+3323,Farm,Eggplant Parmesan Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3324,Farm,Escargot Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3325,Farm,Farmer's Lunch Recipe,"CHEFSANITY,CHEFSANITY_SKILL",
+3326,The Queen of Sauce,Fiddlehead Risotto Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3327,Farm,Fish Stew Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3328,Farm,Fish Taco Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3329,Farm,Fried Calamari Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3330,Farm,Fried Eel Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3331,Farm,Fried Egg Recipe,CHEFSANITY_STARTER,
+3332,Farm,Fried Mushroom Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3333,The Queen of Sauce,Fruit Salad Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3334,Farm,Ginger Ale Recipe,"CHEFSANITY,GINGER_ISLAND,CHEFSANITY_PURCHASE",
+3335,The Queen of Sauce,Glazed Yams Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3336,The Queen of Sauce,Hashbrowns Recipe,"CHEFSANITY,CHEFSANITY_QOS,CHEFSANITY_PURCHASE",
+3337,Farm,Ice Cream Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3338,The Queen of Sauce,Lobster Bisque Recipe,"CHEFSANITY,CHEFSANITY_QOS,CHEFSANITY_FRIENDSHIP",
+3339,The Queen of Sauce,Lucky Lunch Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3340,The Queen of Sauce,Maki Roll Recipe,"CHEFSANITY,CHEFSANITY_QOS,CHEFSANITY_PURCHASE",
+3341,Farm,Mango Sticky Rice Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP,GINGER_ISLAND",
+3342,The Queen of Sauce,Maple Bar Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3343,Farm,Miner's Treat Recipe,"CHEFSANITY,CHEFSANITY_SKILL",
+3344,The Queen of Sauce,Omelet Recipe,"CHEFSANITY,CHEFSANITY_QOS,CHEFSANITY_PURCHASE",
+3345,Farm,Pale Broth Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3346,The Queen of Sauce,Pancakes Recipe,"CHEFSANITY,CHEFSANITY_QOS,CHEFSANITY_PURCHASE",
+3347,Farm,Parsnip Soup Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3348,Farm,Pepper Poppers Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3349,The Queen of Sauce,Pink Cake Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3350,The Queen of Sauce,Pizza Recipe,"CHEFSANITY,CHEFSANITY_QOS,CHEFSANITY_PURCHASE",
+3351,The Queen of Sauce,Plum Pudding Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3352,Farm,Poi Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP,GINGER_ISLAND",
+3353,The Queen of Sauce,Poppyseed Muffin Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3354,The Queen of Sauce,Pumpkin Pie Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3355,Farm,Pumpkin Soup Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3356,The Queen of Sauce,Radish Salad Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3357,Farm,Red Plate Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3358,Farm,Rhubarb Pie Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3359,Farm,Rice Pudding Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3360,The Queen of Sauce,Roasted Hazelnuts Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3361,Farm,Roots Platter Recipe,"CHEFSANITY,CHEFSANITY_SKILL",
+3362,Farm,Salad Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3363,Farm,Salmon Dinner Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3364,Farm,Sashimi Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3365,Farm,Seafoam Pudding Recipe,"CHEFSANITY,CHEFSANITY_SKILL",
+3366,The Queen of Sauce,Shrimp Cocktail Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3367,Farm,Spaghetti Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3368,Farm,Spicy Eel Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3369,Farm,Squid Ink Ravioli Recipe,"CHEFSANITY,CHEFSANITY_SKILL",
+3370,The Queen of Sauce,Stir Fry Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3371,Farm,Strange Bun Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3372,Farm,Stuffing Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3373,Farm,Super Meal Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3374,Farm,Survival Burger Recipe,"CHEFSANITY,CHEFSANITY_SKILL",
+3375,Farm,Tom Kha Soup Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3376,The Queen of Sauce,Tortilla Recipe,"CHEFSANITY,CHEFSANITY_QOS,CHEFSANITY_PURCHASE",
+3377,Saloon,Triple Shot Espresso Recipe,"CHEFSANITY,CHEFSANITY_PURCHASE",
+3378,Island Resort,Tropical Curry Recipe,"CHEFSANITY,GINGER_ISLAND,CHEFSANITY_PURCHASE",
+3379,The Queen of Sauce,Trout Soup Recipe,"CHEFSANITY,CHEFSANITY_QOS",
+3380,Farm,Vegetable Medley Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
+3401,Farm,Craft Cherry Bomb,CRAFTSANITY,
+3402,Farm,Craft Bomb,CRAFTSANITY,
+3403,Farm,Craft Mega Bomb,CRAFTSANITY,
+3404,Farm,Craft Gate,CRAFTSANITY,
+3405,Farm,Craft Wood Fence,CRAFTSANITY,
+3406,Farm,Craft Stone Fence,CRAFTSANITY,
+3407,Farm,Craft Iron Fence,CRAFTSANITY,
+3408,Farm,Craft Hardwood Fence,CRAFTSANITY,
+3409,Farm,Craft Sprinkler,CRAFTSANITY,
+3410,Farm,Craft Quality Sprinkler,CRAFTSANITY,
+3411,Farm,Craft Iridium Sprinkler,CRAFTSANITY,
+3412,Farm,Craft Bee House,CRAFTSANITY,
+3413,Farm,Craft Cask,CRAFTSANITY,
+3414,Farm,Craft Cheese Press,CRAFTSANITY,
+3415,Farm,Craft Keg,CRAFTSANITY,
+3416,Farm,Craft Loom,CRAFTSANITY,
+3417,Farm,Craft Mayonnaise Machine,CRAFTSANITY,
+3418,Farm,Craft Oil Maker,CRAFTSANITY,
+3419,Farm,Craft Preserves Jar,CRAFTSANITY,
+3420,Farm,Craft Basic Fertilizer,CRAFTSANITY,
+3421,Farm,Craft Quality Fertilizer,CRAFTSANITY,
+3422,Farm,Craft Deluxe Fertilizer,CRAFTSANITY,
+3423,Farm,Craft Speed-Gro,CRAFTSANITY,
+3424,Farm,Craft Deluxe Speed-Gro,CRAFTSANITY,
+3425,Farm,Craft Hyper Speed-Gro,"CRAFTSANITY,GINGER_ISLAND",
+3426,Farm,Craft Basic Retaining Soil,CRAFTSANITY,
+3427,Farm,Craft Quality Retaining Soil,CRAFTSANITY,
+3428,Farm,Craft Deluxe Retaining Soil,"CRAFTSANITY,GINGER_ISLAND",
+3429,Farm,Craft Tree Fertilizer,CRAFTSANITY,
+3430,Farm,Craft Spring Seeds,CRAFTSANITY,
+3431,Farm,Craft Summer Seeds,CRAFTSANITY,
+3432,Farm,Craft Fall Seeds,CRAFTSANITY,
+3433,Farm,Craft Winter Seeds,CRAFTSANITY,
+3434,Farm,Craft Ancient Seeds,CRAFTSANITY,
+3435,Farm,Craft Grass Starter,CRAFTSANITY,
+3436,Farm,Craft Tea Sapling,CRAFTSANITY,
+3437,Farm,Craft Fiber Seeds,CRAFTSANITY,
+3438,Farm,Craft Wood Floor,CRAFTSANITY,
+3439,Farm,Craft Rustic Plank Floor,CRAFTSANITY,
+3440,Farm,Craft Straw Floor,CRAFTSANITY,
+3441,Farm,Craft Weathered Floor,CRAFTSANITY,
+3442,Farm,Craft Crystal Floor,CRAFTSANITY,
+3443,Farm,Craft Stone Floor,CRAFTSANITY,
+3444,Farm,Craft Stone Walkway Floor,CRAFTSANITY,
+3445,Farm,Craft Brick Floor,CRAFTSANITY,
+3446,Farm,Craft Wood Path,CRAFTSANITY,
+3447,Farm,Craft Gravel Path,CRAFTSANITY,
+3448,Farm,Craft Cobblestone Path,CRAFTSANITY,
+3449,Farm,Craft Stepping Stone Path,CRAFTSANITY,
+3450,Farm,Craft Crystal Path,CRAFTSANITY,
+3451,Farm,Craft Spinner,CRAFTSANITY,
+3452,Farm,Craft Trap Bobber,CRAFTSANITY,
+3453,Farm,Craft Cork Bobber,CRAFTSANITY,
+3454,Farm,Craft Quality Bobber,CRAFTSANITY,
+3455,Farm,Craft Treasure Hunter,CRAFTSANITY,
+3456,Farm,Craft Dressed Spinner,CRAFTSANITY,
+3457,Farm,Craft Barbed Hook,CRAFTSANITY,
+3458,Farm,Craft Magnet,CRAFTSANITY,
+3459,Farm,Craft Bait,CRAFTSANITY,
+3460,Farm,Craft Wild Bait,CRAFTSANITY,
+3461,Farm,Craft Magic Bait,"CRAFTSANITY,GINGER_ISLAND",
+3462,Farm,Craft Crab Pot,CRAFTSANITY,
+3463,Farm,Craft Sturdy Ring,CRAFTSANITY,
+3464,Farm,Craft Warrior Ring,CRAFTSANITY,
+3465,Farm,Craft Ring of Yoba,CRAFTSANITY,
+3466,Farm,Craft Thorns Ring,"CRAFTSANITY,GINGER_ISLAND",
+3467,Farm,Craft Glowstone Ring,CRAFTSANITY,
+3468,Farm,Craft Iridium Band,CRAFTSANITY,
+3469,Farm,Craft Wedding Ring,CRAFTSANITY,
+3470,Farm,Craft Field Snack,CRAFTSANITY,
+3471,Farm,Craft Bug Steak,CRAFTSANITY,
+3472,Farm,Craft Life Elixir,CRAFTSANITY,
+3473,Farm,Craft Oil of Garlic,CRAFTSANITY,
+3474,Farm,Craft Monster Musk,CRAFTSANITY,
+3475,Farm,Craft Fairy Dust,CRAFTSANITY,
+3476,Farm,Craft Warp Totem: Beach,CRAFTSANITY,
+3477,Farm,Craft Warp Totem: Mountains,CRAFTSANITY,
+3478,Farm,Craft Warp Totem: Farm,CRAFTSANITY,
+3479,Farm,Craft Warp Totem: Desert,CRAFTSANITY,
+3480,Farm,Craft Warp Totem: Island,"CRAFTSANITY,GINGER_ISLAND",
+3481,Farm,Craft Rain Totem,CRAFTSANITY,
+3482,Farm,Craft Torch,CRAFTSANITY,
+3483,Farm,Craft Campfire,CRAFTSANITY,
+3484,Farm,Craft Wooden Brazier,CRAFTSANITY,
+3485,Farm,Craft Stone Brazier,CRAFTSANITY,
+3486,Farm,Craft Gold Brazier,CRAFTSANITY,
+3487,Farm,Craft Carved Brazier,CRAFTSANITY,
+3488,Farm,Craft Stump Brazier,CRAFTSANITY,
+3489,Farm,Craft Barrel Brazier,CRAFTSANITY,
+3490,Farm,Craft Skull Brazier,CRAFTSANITY,
+3491,Farm,Craft Marble Brazier,CRAFTSANITY,
+3492,Farm,Craft Wood Lamp-post,CRAFTSANITY,
+3493,Farm,Craft Iron Lamp-post,CRAFTSANITY,
+3494,Farm,Craft Jack-O-Lantern,CRAFTSANITY,
+3495,Farm,Craft Bone Mill,CRAFTSANITY,
+3496,Farm,Craft Charcoal Kiln,CRAFTSANITY,
+3497,Farm,Craft Crystalarium,CRAFTSANITY,
+3498,Farm,Craft Furnace,CRAFTSANITY,
+3499,Farm,Craft Geode Crusher,CRAFTSANITY,
+3500,Farm,Craft Heavy Tapper,"CRAFTSANITY,GINGER_ISLAND",
+3501,Farm,Craft Lightning Rod,CRAFTSANITY,
+3502,Farm,Craft Ostrich Incubator,"CRAFTSANITY,GINGER_ISLAND",
+3503,Farm,Craft Recycling Machine,CRAFTSANITY,
+3504,Farm,Craft Seed Maker,CRAFTSANITY,
+3505,Farm,Craft Slime Egg-Press,CRAFTSANITY,
+3506,Farm,Craft Slime Incubator,CRAFTSANITY,
+3507,Farm,Craft Solar Panel,"CRAFTSANITY,GINGER_ISLAND",
+3508,Farm,Craft Tapper,CRAFTSANITY,
+3509,Farm,Craft Worm Bin,CRAFTSANITY,
+3510,Farm,Craft Tub o' Flowers,CRAFTSANITY,
+3511,Farm,Craft Wicked Statue,CRAFTSANITY,
+3512,Farm,Craft Flute Block,CRAFTSANITY,
+3513,Farm,Craft Drum Block,CRAFTSANITY,
+3514,Farm,Craft Chest,CRAFTSANITY,
+3515,Farm,Craft Stone Chest,CRAFTSANITY,
+3516,Farm,Craft Wood Sign,CRAFTSANITY,
+3517,Farm,Craft Stone Sign,CRAFTSANITY,
+3518,Farm,Craft Dark Sign,CRAFTSANITY,
+3519,Farm,Craft Garden Pot,CRAFTSANITY,
+3520,Farm,Craft Scarecrow,CRAFTSANITY,
+3521,Farm,Craft Deluxe Scarecrow,CRAFTSANITY,
+3522,Farm,Craft Staircase,CRAFTSANITY,
+3523,Farm,Craft Explosive Ammo,CRAFTSANITY,
+3524,Farm,Craft Transmute (Fe),CRAFTSANITY,
+3525,Farm,Craft Transmute (Au),CRAFTSANITY,
+3526,Farm,Craft Mini-Jukebox,CRAFTSANITY,
+3527,Farm,Craft Mini-Obelisk,CRAFTSANITY,
+3528,Farm,Craft Farm Computer,CRAFTSANITY,
+3529,Farm,Craft Hopper,"CRAFTSANITY,GINGER_ISLAND",
+3530,Farm,Craft Cookout Kit,CRAFTSANITY,
+3551,Pierre's General Store,Grass Starter Recipe,CRAFTSANITY,
+3552,Carpenter Shop,Wood Floor Recipe,CRAFTSANITY,
+3553,Carpenter Shop,Rustic Plank Floor Recipe,CRAFTSANITY,
+3554,Carpenter Shop,Straw Floor Recipe,CRAFTSANITY,
+3555,Mines Dwarf Shop,Weathered Floor Recipe,CRAFTSANITY,
+3556,Sewer,Crystal Floor Recipe,CRAFTSANITY,
+3557,Carpenter Shop,Stone Floor Recipe,CRAFTSANITY,
+3558,Carpenter Shop,Stone Walkway Floor Recipe,CRAFTSANITY,
+3559,Carpenter Shop,Brick Floor Recipe,CRAFTSANITY,
+3560,Carpenter Shop,Stepping Stone Path Recipe,CRAFTSANITY,
+3561,Carpenter Shop,Crystal Path Recipe,CRAFTSANITY,
+3562,Traveling Cart,Wedding Ring Recipe,CRAFTSANITY,
+3563,Volcano Dwarf Shop,Warp Totem: Island Recipe,"CRAFTSANITY,GINGER_ISLAND",
+3564,Carpenter Shop,Wooden Brazier Recipe,CRAFTSANITY,
+3565,Carpenter Shop,Stone Brazier Recipe,CRAFTSANITY,
+3566,Carpenter Shop,Gold Brazier Recipe,CRAFTSANITY,
+3567,Carpenter Shop,Carved Brazier Recipe,CRAFTSANITY,
+3568,Carpenter Shop,Stump Brazier Recipe,CRAFTSANITY,
+3569,Carpenter Shop,Barrel Brazier Recipe,CRAFTSANITY,
+3570,Carpenter Shop,Skull Brazier Recipe,CRAFTSANITY,
+3571,Carpenter Shop,Marble Brazier Recipe,CRAFTSANITY,
+3572,Carpenter Shop,Wood Lamp-post Recipe,CRAFTSANITY,
+3573,Carpenter Shop,Iron Lamp-post Recipe,CRAFTSANITY,
+3574,Sewer,Wicked Statue Recipe,CRAFTSANITY,
+3575,Desert,Warp Totem: Desert Recipe,"CRAFTSANITY",
+3576,Island Trader,Deluxe Retaining Soil Recipe,"CRAFTSANITY,GINGER_ISLAND",
+5001,Stardew Valley,Level 1 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
+5002,Stardew Valley,Level 2 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
+5003,Stardew Valley,Level 3 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
+5004,Stardew Valley,Level 4 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
+5005,Stardew Valley,Level 5 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
+5006,Stardew Valley,Level 6 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
+5007,Stardew Valley,Level 7 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
+5008,Stardew Valley,Level 8 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
+5009,Stardew Valley,Level 9 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
+5010,Stardew Valley,Level 10 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
+5011,Stardew Valley,Level 1 Socializing,"SKILL_LEVEL,SOCIALIZING_LEVEL",Socializing Skill
+5012,Stardew Valley,Level 2 Socializing,"SKILL_LEVEL,SOCIALIZING_LEVEL",Socializing Skill
+5013,Stardew Valley,Level 3 Socializing,"SKILL_LEVEL,SOCIALIZING_LEVEL",Socializing Skill
+5014,Stardew Valley,Level 4 Socializing,"SKILL_LEVEL,SOCIALIZING_LEVEL",Socializing Skill
+5015,Stardew Valley,Level 5 Socializing,"SKILL_LEVEL,SOCIALIZING_LEVEL",Socializing Skill
+5016,Stardew Valley,Level 6 Socializing,"SKILL_LEVEL,SOCIALIZING_LEVEL",Socializing Skill
+5017,Stardew Valley,Level 7 Socializing,"SKILL_LEVEL,SOCIALIZING_LEVEL",Socializing Skill
+5018,Stardew Valley,Level 8 Socializing,"SKILL_LEVEL,SOCIALIZING_LEVEL",Socializing Skill
+5019,Stardew Valley,Level 9 Socializing,"SKILL_LEVEL,SOCIALIZING_LEVEL",Socializing Skill
+5020,Stardew Valley,Level 10 Socializing,"SKILL_LEVEL,SOCIALIZING_LEVEL",Socializing Skill
+5021,Magic Altar,Level 1 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
+5022,Magic Altar,Level 2 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
+5023,Magic Altar,Level 3 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
+5024,Magic Altar,Level 4 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
+5025,Magic Altar,Level 5 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
+5026,Magic Altar,Level 6 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
+5027,Magic Altar,Level 7 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
+5028,Magic Altar,Level 8 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
+5029,Magic Altar,Level 9 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
+5030,Magic Altar,Level 10 Magic,"MAGIC_LEVEL,SKILL_LEVEL",Magic
+5031,Town,Level 1 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
+5032,Town,Level 2 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
+5033,Town,Level 3 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
+5034,Town,Level 4 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
+5035,Town,Level 5 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
+5036,Town,Level 6 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
+5037,Town,Level 7 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
+5038,Town,Level 8 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
+5039,Town,Level 9 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
+5040,Town,Level 10 Binning,"BINNING_LEVEL,SKILL_LEVEL",Binning Skill
+5041,Stardew Valley,Level 1 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
+5042,Stardew Valley,Level 2 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
+5043,Stardew Valley,Level 3 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
+5044,Stardew Valley,Level 4 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
+5045,Stardew Valley,Level 5 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
+5046,Stardew Valley,Level 6 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
+5047,Stardew Valley,Level 7 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
+5048,Stardew Valley,Level 8 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
+5049,Stardew Valley,Level 9 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
+5050,Stardew Valley,Level 10 Archaeology,"ARCHAEOLOGY_LEVEL,SKILL_LEVEL",Archaeology
+5051,Stardew Valley,Level 1 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
+5052,Stardew Valley,Level 2 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
+5053,Stardew Valley,Level 3 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
+5054,Stardew Valley,Level 4 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
+5055,Stardew Valley,Level 5 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
+5056,Stardew Valley,Level 6 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
+5057,Stardew Valley,Level 7 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
+5058,Stardew Valley,Level 8 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
+5059,Stardew Valley,Level 9 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
+5060,Stardew Valley,Level 10 Cooking,"COOKING_LEVEL,SKILL_LEVEL",Cooking Skill
+5501,Magic Altar,Analyze: Clear Debris,MANDATORY,Magic
+5502,Magic Altar,Analyze: Till,MANDATORY,Magic
+5503,Magic Altar,Analyze: Water,MANDATORY,Magic
+5504,Magic Altar,Analyze All Toil School Locations,MANDATORY,Magic
+5505,Magic Altar,Analyze: Evac,MANDATORY,Magic
+5506,Magic Altar,Analyze: Haste,MANDATORY,Magic
+5507,Magic Altar,Analyze: Heal,MANDATORY,Magic
+5508,Magic Altar,Analyze All Life School Locations,MANDATORY,Magic
+5509,Magic Altar,Analyze: Descend,MANDATORY,Magic
+5510,Magic Altar,Analyze: Fireball,MANDATORY,Magic
+5511,Magic Altar,Analyze: Frostbolt,MANDATORY,Magic
+5512,Magic Altar,Analyze All Elemental School Locations,MANDATORY,Magic
+5513,Magic Altar,Analyze: Lantern,MANDATORY,Magic
+5514,Magic Altar,Analyze: Tendrils,MANDATORY,Magic
+5515,Magic Altar,Analyze: Shockwave,MANDATORY,Magic
+5516,Magic Altar,Analyze All Nature School Locations,MANDATORY,Magic
+5517,Magic Altar,Analyze: Meteor,MANDATORY,Magic
+5518,Magic Altar,Analyze: Lucksteal,MANDATORY,Magic
+5519,Magic Altar,Analyze: Bloodmana,MANDATORY,Magic
+5520,Magic Altar,Analyze All Eldritch School Locations,MANDATORY,Magic
+5521,Magic Altar,Analyze Every Magic School Location,MANDATORY,Magic
+6001,Museum,Friendsanity: Jasper 1 <3,FRIENDSANITY,Professor Jasper Thomas
+6002,Museum,Friendsanity: Jasper 2 <3,FRIENDSANITY,Professor Jasper Thomas
+6003,Museum,Friendsanity: Jasper 3 <3,FRIENDSANITY,Professor Jasper Thomas
+6004,Museum,Friendsanity: Jasper 4 <3,FRIENDSANITY,Professor Jasper Thomas
+6005,Museum,Friendsanity: Jasper 5 <3,FRIENDSANITY,Professor Jasper Thomas
+6006,Museum,Friendsanity: Jasper 6 <3,FRIENDSANITY,Professor Jasper Thomas
+6007,Museum,Friendsanity: Jasper 7 <3,FRIENDSANITY,Professor Jasper Thomas
+6008,Museum,Friendsanity: Jasper 8 <3,FRIENDSANITY,Professor Jasper Thomas
+6009,Museum,Friendsanity: Jasper 9 <3,FRIENDSANITY,Professor Jasper Thomas
+6010,Museum,Friendsanity: Jasper 10 <3,FRIENDSANITY,Professor Jasper Thomas
+6011,Museum,Friendsanity: Jasper 11 <3,FRIENDSANITY,Professor Jasper Thomas
+6012,Museum,Friendsanity: Jasper 12 <3,FRIENDSANITY,Professor Jasper Thomas
+6013,Museum,Friendsanity: Jasper 13 <3,FRIENDSANITY,Professor Jasper Thomas
+6014,Museum,Friendsanity: Jasper 14 <3,FRIENDSANITY,Professor Jasper Thomas
+6015,Yoba's Clearing,Friendsanity: Yoba 1 <3,FRIENDSANITY,Custom NPC - Yoba
+6016,Yoba's Clearing,Friendsanity: Yoba 2 <3,FRIENDSANITY,Custom NPC - Yoba
+6017,Yoba's Clearing,Friendsanity: Yoba 3 <3,FRIENDSANITY,Custom NPC - Yoba
+6018,Yoba's Clearing,Friendsanity: Yoba 4 <3,FRIENDSANITY,Custom NPC - Yoba
+6019,Yoba's Clearing,Friendsanity: Yoba 5 <3,FRIENDSANITY,Custom NPC - Yoba
+6020,Yoba's Clearing,Friendsanity: Yoba 6 <3,FRIENDSANITY,Custom NPC - Yoba
+6021,Yoba's Clearing,Friendsanity: Yoba 7 <3,FRIENDSANITY,Custom NPC - Yoba
+6022,Yoba's Clearing,Friendsanity: Yoba 8 <3,FRIENDSANITY,Custom NPC - Yoba
+6023,Yoba's Clearing,Friendsanity: Yoba 9 <3,FRIENDSANITY,Custom NPC - Yoba
+6024,Yoba's Clearing,Friendsanity: Yoba 10 <3,FRIENDSANITY,Custom NPC - Yoba
+6025,Marnie's Ranch,Friendsanity: Mr. Ginger 1 <3,FRIENDSANITY,Mister Ginger (cat npc)
+6026,Marnie's Ranch,Friendsanity: Mr. Ginger 2 <3,FRIENDSANITY,Mister Ginger (cat npc)
+6027,Marnie's Ranch,Friendsanity: Mr. Ginger 3 <3,FRIENDSANITY,Mister Ginger (cat npc)
+6028,Marnie's Ranch,Friendsanity: Mr. Ginger 4 <3,FRIENDSANITY,Mister Ginger (cat npc)
+6029,Marnie's Ranch,Friendsanity: Mr. Ginger 5 <3,FRIENDSANITY,Mister Ginger (cat npc)
+6030,Marnie's Ranch,Friendsanity: Mr. Ginger 6 <3,FRIENDSANITY,Mister Ginger (cat npc)
+6031,Marnie's Ranch,Friendsanity: Mr. Ginger 7 <3,FRIENDSANITY,Mister Ginger (cat npc)
+6032,Marnie's Ranch,Friendsanity: Mr. Ginger 8 <3,FRIENDSANITY,Mister Ginger (cat npc)
+6033,Marnie's Ranch,Friendsanity: Mr. Ginger 9 <3,FRIENDSANITY,Mister Ginger (cat npc)
+6034,Marnie's Ranch,Friendsanity: Mr. Ginger 10 <3,FRIENDSANITY,Mister Ginger (cat npc)
+6035,Town,Friendsanity: Ayeisha 1 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
+6036,Town,Friendsanity: Ayeisha 2 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
+6037,Town,Friendsanity: Ayeisha 3 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
+6038,Town,Friendsanity: Ayeisha 4 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
+6039,Town,Friendsanity: Ayeisha 5 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
+6040,Town,Friendsanity: Ayeisha 6 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
+6041,Town,Friendsanity: Ayeisha 7 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
+6042,Town,Friendsanity: Ayeisha 8 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
+6043,Town,Friendsanity: Ayeisha 9 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
+6044,Town,Friendsanity: Ayeisha 10 <3,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
+6045,Saloon,Friendsanity: Shiko 1 <3,FRIENDSANITY,Shiko - New Custom NPC
+6046,Saloon,Friendsanity: Shiko 2 <3,FRIENDSANITY,Shiko - New Custom NPC
+6047,Saloon,Friendsanity: Shiko 3 <3,FRIENDSANITY,Shiko - New Custom NPC
+6048,Saloon,Friendsanity: Shiko 4 <3,FRIENDSANITY,Shiko - New Custom NPC
+6049,Saloon,Friendsanity: Shiko 5 <3,FRIENDSANITY,Shiko - New Custom NPC
+6050,Saloon,Friendsanity: Shiko 6 <3,FRIENDSANITY,Shiko - New Custom NPC
+6051,Saloon,Friendsanity: Shiko 7 <3,FRIENDSANITY,Shiko - New Custom NPC
+6052,Saloon,Friendsanity: Shiko 8 <3,FRIENDSANITY,Shiko - New Custom NPC
+6053,Saloon,Friendsanity: Shiko 9 <3,FRIENDSANITY,Shiko - New Custom NPC
+6054,Saloon,Friendsanity: Shiko 10 <3,FRIENDSANITY,Shiko - New Custom NPC
+6055,Saloon,Friendsanity: Shiko 11 <3,FRIENDSANITY,Shiko - New Custom NPC
+6056,Saloon,Friendsanity: Shiko 12 <3,FRIENDSANITY,Shiko - New Custom NPC
+6057,Saloon,Friendsanity: Shiko 13 <3,FRIENDSANITY,Shiko - New Custom NPC
+6058,Saloon,Friendsanity: Shiko 14 <3,FRIENDSANITY,Shiko - New Custom NPC
+6059,Wizard Tower,Friendsanity: Wellwick 1 <3,FRIENDSANITY,'Prophet' Wellwick
+6060,Wizard Tower,Friendsanity: Wellwick 2 <3,FRIENDSANITY,'Prophet' Wellwick
+6061,Wizard Tower,Friendsanity: Wellwick 3 <3,FRIENDSANITY,'Prophet' Wellwick
+6062,Wizard Tower,Friendsanity: Wellwick 4 <3,FRIENDSANITY,'Prophet' Wellwick
+6063,Wizard Tower,Friendsanity: Wellwick 5 <3,FRIENDSANITY,'Prophet' Wellwick
+6064,Wizard Tower,Friendsanity: Wellwick 6 <3,FRIENDSANITY,'Prophet' Wellwick
+6065,Wizard Tower,Friendsanity: Wellwick 7 <3,FRIENDSANITY,'Prophet' Wellwick
+6066,Wizard Tower,Friendsanity: Wellwick 8 <3,FRIENDSANITY,'Prophet' Wellwick
+6067,Wizard Tower,Friendsanity: Wellwick 9 <3,FRIENDSANITY,'Prophet' Wellwick
+6068,Wizard Tower,Friendsanity: Wellwick 10 <3,FRIENDSANITY,'Prophet' Wellwick
+6069,Wizard Tower,Friendsanity: Wellwick 11 <3,FRIENDSANITY,'Prophet' Wellwick
+6070,Wizard Tower,Friendsanity: Wellwick 12 <3,FRIENDSANITY,'Prophet' Wellwick
+6071,Wizard Tower,Friendsanity: Wellwick 13 <3,FRIENDSANITY,'Prophet' Wellwick
+6072,Wizard Tower,Friendsanity: Wellwick 14 <3,FRIENDSANITY,'Prophet' Wellwick
+6073,Forest,Friendsanity: Delores 1 <3,FRIENDSANITY,Delores - Custom NPC
+6074,Forest,Friendsanity: Delores 2 <3,FRIENDSANITY,Delores - Custom NPC
+6075,Forest,Friendsanity: Delores 3 <3,FRIENDSANITY,Delores - Custom NPC
+6076,Forest,Friendsanity: Delores 4 <3,FRIENDSANITY,Delores - Custom NPC
+6077,Forest,Friendsanity: Delores 5 <3,FRIENDSANITY,Delores - Custom NPC
+6078,Forest,Friendsanity: Delores 6 <3,FRIENDSANITY,Delores - Custom NPC
+6079,Forest,Friendsanity: Delores 7 <3,FRIENDSANITY,Delores - Custom NPC
+6080,Forest,Friendsanity: Delores 8 <3,FRIENDSANITY,Delores - Custom NPC
+6081,Forest,Friendsanity: Delores 9 <3,FRIENDSANITY,Delores - Custom NPC
+6082,Forest,Friendsanity: Delores 10 <3,FRIENDSANITY,Delores - Custom NPC
+6083,Forest,Friendsanity: Delores 11 <3,FRIENDSANITY,Delores - Custom NPC
+6084,Forest,Friendsanity: Delores 12 <3,FRIENDSANITY,Delores - Custom NPC
+6085,Forest,Friendsanity: Delores 13 <3,FRIENDSANITY,Delores - Custom NPC
+6086,Forest,Friendsanity: Delores 14 <3,FRIENDSANITY,Delores - Custom NPC
+6087,Alec's Pet Shop,Friendsanity: Alec 1 <3,FRIENDSANITY,Alec Revisited
+6088,Alec's Pet Shop,Friendsanity: Alec 2 <3,FRIENDSANITY,Alec Revisited
+6089,Alec's Pet Shop,Friendsanity: Alec 3 <3,FRIENDSANITY,Alec Revisited
+6090,Alec's Pet Shop,Friendsanity: Alec 4 <3,FRIENDSANITY,Alec Revisited
+6091,Alec's Pet Shop,Friendsanity: Alec 5 <3,FRIENDSANITY,Alec Revisited
+6092,Alec's Pet Shop,Friendsanity: Alec 6 <3,FRIENDSANITY,Alec Revisited
+6093,Alec's Pet Shop,Friendsanity: Alec 7 <3,FRIENDSANITY,Alec Revisited
+6094,Alec's Pet Shop,Friendsanity: Alec 8 <3,FRIENDSANITY,Alec Revisited
+6095,Alec's Pet Shop,Friendsanity: Alec 9 <3,FRIENDSANITY,Alec Revisited
+6096,Alec's Pet Shop,Friendsanity: Alec 10 <3,FRIENDSANITY,Alec Revisited
+6097,Alec's Pet Shop,Friendsanity: Alec 11 <3,FRIENDSANITY,Alec Revisited
+6098,Alec's Pet Shop,Friendsanity: Alec 12 <3,FRIENDSANITY,Alec Revisited
+6099,Alec's Pet Shop,Friendsanity: Alec 13 <3,FRIENDSANITY,Alec Revisited
+6100,Alec's Pet Shop,Friendsanity: Alec 14 <3,FRIENDSANITY,Alec Revisited
+6101,Eugene's Garden,Friendsanity: Eugene 1 <3,FRIENDSANITY,Custom NPC Eugene
+6102,Eugene's Garden,Friendsanity: Eugene 2 <3,FRIENDSANITY,Custom NPC Eugene
+6103,Eugene's Garden,Friendsanity: Eugene 3 <3,FRIENDSANITY,Custom NPC Eugene
+6104,Eugene's Garden,Friendsanity: Eugene 4 <3,FRIENDSANITY,Custom NPC Eugene
+6105,Eugene's Garden,Friendsanity: Eugene 5 <3,FRIENDSANITY,Custom NPC Eugene
+6106,Eugene's Garden,Friendsanity: Eugene 6 <3,FRIENDSANITY,Custom NPC Eugene
+6107,Eugene's Garden,Friendsanity: Eugene 7 <3,FRIENDSANITY,Custom NPC Eugene
+6108,Eugene's Garden,Friendsanity: Eugene 8 <3,FRIENDSANITY,Custom NPC Eugene
+6109,Eugene's Garden,Friendsanity: Eugene 9 <3,FRIENDSANITY,Custom NPC Eugene
+6110,Eugene's Garden,Friendsanity: Eugene 10 <3,FRIENDSANITY,Custom NPC Eugene
+6111,Eugene's Garden,Friendsanity: Eugene 11 <3,FRIENDSANITY,Custom NPC Eugene
+6112,Eugene's Garden,Friendsanity: Eugene 12 <3,FRIENDSANITY,Custom NPC Eugene
+6113,Eugene's Garden,Friendsanity: Eugene 13 <3,FRIENDSANITY,Custom NPC Eugene
+6114,Eugene's Garden,Friendsanity: Eugene 14 <3,FRIENDSANITY,Custom NPC Eugene
+6115,Forest,Friendsanity: Juna 1 <3,FRIENDSANITY,Juna - Roommate NPC
+6116,Forest,Friendsanity: Juna 2 <3,FRIENDSANITY,Juna - Roommate NPC
+6117,Forest,Friendsanity: Juna 3 <3,FRIENDSANITY,Juna - Roommate NPC
+6118,Forest,Friendsanity: Juna 4 <3,FRIENDSANITY,Juna - Roommate NPC
+6119,Forest,Friendsanity: Juna 5 <3,FRIENDSANITY,Juna - Roommate NPC
+6120,Forest,Friendsanity: Juna 6 <3,FRIENDSANITY,Juna - Roommate NPC
+6121,Forest,Friendsanity: Juna 7 <3,FRIENDSANITY,Juna - Roommate NPC
+6122,Forest,Friendsanity: Juna 8 <3,FRIENDSANITY,Juna - Roommate NPC
+6123,Forest,Friendsanity: Juna 9 <3,FRIENDSANITY,Juna - Roommate NPC
+6124,Forest,Friendsanity: Juna 10 <3,FRIENDSANITY,Juna - Roommate NPC
+6125,Riley's House,Friendsanity: Riley 1 <3,FRIENDSANITY,Custom NPC - Riley
+6126,Riley's House,Friendsanity: Riley 2 <3,FRIENDSANITY,Custom NPC - Riley
+6127,Riley's House,Friendsanity: Riley 3 <3,FRIENDSANITY,Custom NPC - Riley
+6128,Riley's House,Friendsanity: Riley 4 <3,FRIENDSANITY,Custom NPC - Riley
+6129,Riley's House,Friendsanity: Riley 5 <3,FRIENDSANITY,Custom NPC - Riley
+6130,Riley's House,Friendsanity: Riley 6 <3,FRIENDSANITY,Custom NPC - Riley
+6131,Riley's House,Friendsanity: Riley 7 <3,FRIENDSANITY,Custom NPC - Riley
+6132,Riley's House,Friendsanity: Riley 8 <3,FRIENDSANITY,Custom NPC - Riley
+6133,Riley's House,Friendsanity: Riley 9 <3,FRIENDSANITY,Custom NPC - Riley
+6134,Riley's House,Friendsanity: Riley 10 <3,FRIENDSANITY,Custom NPC - Riley
+6135,Riley's House,Friendsanity: Riley 11 <3,FRIENDSANITY,Custom NPC - Riley
+6136,Riley's House,Friendsanity: Riley 12 <3,FRIENDSANITY,Custom NPC - Riley
+6137,Riley's House,Friendsanity: Riley 13 <3,FRIENDSANITY,Custom NPC - Riley
+6138,Riley's House,Friendsanity: Riley 14 <3,FRIENDSANITY,Custom NPC - Riley
+6139,JojaMart,Friendsanity: Claire 1 <3,FRIENDSANITY,Stardew Valley Expanded
+6140,JojaMart,Friendsanity: Claire 2 <3,FRIENDSANITY,Stardew Valley Expanded
+6141,JojaMart,Friendsanity: Claire 3 <3,FRIENDSANITY,Stardew Valley Expanded
+6142,JojaMart,Friendsanity: Claire 4 <3,FRIENDSANITY,Stardew Valley Expanded
+6143,JojaMart,Friendsanity: Claire 5 <3,FRIENDSANITY,Stardew Valley Expanded
+6144,JojaMart,Friendsanity: Claire 6 <3,FRIENDSANITY,Stardew Valley Expanded
+6145,JojaMart,Friendsanity: Claire 7 <3,FRIENDSANITY,Stardew Valley Expanded
+6146,JojaMart,Friendsanity: Claire 8 <3,FRIENDSANITY,Stardew Valley Expanded
+6147,JojaMart,Friendsanity: Claire 9 <3,FRIENDSANITY,Stardew Valley Expanded
+6148,JojaMart,Friendsanity: Claire 10 <3,FRIENDSANITY,Stardew Valley Expanded
+6149,JojaMart,Friendsanity: Claire 11 <3,FRIENDSANITY,Stardew Valley Expanded
+6150,JojaMart,Friendsanity: Claire 12 <3,FRIENDSANITY,Stardew Valley Expanded
+6151,JojaMart,Friendsanity: Claire 13 <3,FRIENDSANITY,Stardew Valley Expanded
+6152,JojaMart,Friendsanity: Claire 14 <3,FRIENDSANITY,Stardew Valley Expanded
+6153,Galmoran Outpost,Friendsanity: Lance 1 <3,"FRIENDSANITY,GINGER_ISLAND",Stardew Valley Expanded
+6154,Galmoran Outpost,Friendsanity: Lance 2 <3,"FRIENDSANITY,GINGER_ISLAND",Stardew Valley Expanded
+6155,Galmoran Outpost,Friendsanity: Lance 3 <3,"FRIENDSANITY,GINGER_ISLAND",Stardew Valley Expanded
+6156,Galmoran Outpost,Friendsanity: Lance 4 <3,"FRIENDSANITY,GINGER_ISLAND",Stardew Valley Expanded
+6157,Galmoran Outpost,Friendsanity: Lance 5 <3,"FRIENDSANITY,GINGER_ISLAND",Stardew Valley Expanded
+6158,Galmoran Outpost,Friendsanity: Lance 6 <3,"FRIENDSANITY,GINGER_ISLAND",Stardew Valley Expanded
+6159,Galmoran Outpost,Friendsanity: Lance 7 <3,"FRIENDSANITY,GINGER_ISLAND",Stardew Valley Expanded
+6160,Galmoran Outpost,Friendsanity: Lance 8 <3,"FRIENDSANITY,GINGER_ISLAND",Stardew Valley Expanded
+6161,Galmoran Outpost,Friendsanity: Lance 9 <3,"FRIENDSANITY,GINGER_ISLAND",Stardew Valley Expanded
+6162,Galmoran Outpost,Friendsanity: Lance 10 <3,"FRIENDSANITY,GINGER_ISLAND",Stardew Valley Expanded
+6163,Galmoran Outpost,Friendsanity: Lance 11 <3,"FRIENDSANITY,GINGER_ISLAND",Stardew Valley Expanded
+6164,Galmoran Outpost,Friendsanity: Lance 12 <3,"FRIENDSANITY,GINGER_ISLAND",Stardew Valley Expanded
+6165,Galmoran Outpost,Friendsanity: Lance 13 <3,"FRIENDSANITY,GINGER_ISLAND",Stardew Valley Expanded
+6166,Galmoran Outpost,Friendsanity: Lance 14 <3,"FRIENDSANITY,GINGER_ISLAND",Stardew Valley Expanded
+6167,Jenkins' Residence,Friendsanity: Olivia 1 <3,FRIENDSANITY,Stardew Valley Expanded
+6168,Jenkins' Residence,Friendsanity: Olivia 2 <3,FRIENDSANITY,Stardew Valley Expanded
+6169,Jenkins' Residence,Friendsanity: Olivia 3 <3,FRIENDSANITY,Stardew Valley Expanded
+6170,Jenkins' Residence,Friendsanity: Olivia 4 <3,FRIENDSANITY,Stardew Valley Expanded
+6171,Jenkins' Residence,Friendsanity: Olivia 5 <3,FRIENDSANITY,Stardew Valley Expanded
+6172,Jenkins' Residence,Friendsanity: Olivia 6 <3,FRIENDSANITY,Stardew Valley Expanded
+6173,Jenkins' Residence,Friendsanity: Olivia 7 <3,FRIENDSANITY,Stardew Valley Expanded
+6174,Jenkins' Residence,Friendsanity: Olivia 8 <3,FRIENDSANITY,Stardew Valley Expanded
+6175,Jenkins' Residence,Friendsanity: Olivia 9 <3,FRIENDSANITY,Stardew Valley Expanded
+6176,Jenkins' Residence,Friendsanity: Olivia 10 <3,FRIENDSANITY,Stardew Valley Expanded
+6177,Jenkins' Residence,Friendsanity: Olivia 11 <3,FRIENDSANITY,Stardew Valley Expanded
+6178,Jenkins' Residence,Friendsanity: Olivia 12 <3,FRIENDSANITY,Stardew Valley Expanded
+6179,Jenkins' Residence,Friendsanity: Olivia 13 <3,FRIENDSANITY,Stardew Valley Expanded
+6180,Jenkins' Residence,Friendsanity: Olivia 14 <3,FRIENDSANITY,Stardew Valley Expanded
+6181,Wizard Tower,Friendsanity: Wizard 11 <3,FRIENDSANITY,Stardew Valley Expanded
+6182,Wizard Tower,Friendsanity: Wizard 12 <3,FRIENDSANITY,Stardew Valley Expanded
+6183,Wizard Tower,Friendsanity: Wizard 13 <3,FRIENDSANITY,Stardew Valley Expanded
+6184,Wizard Tower,Friendsanity: Wizard 14 <3,FRIENDSANITY,Stardew Valley Expanded
+6185,Blue Moon Vineyard,Friendsanity: Sophia 1 <3,FRIENDSANITY,Stardew Valley Expanded
+6186,Blue Moon Vineyard,Friendsanity: Sophia 2 <3,FRIENDSANITY,Stardew Valley Expanded
+6187,Blue Moon Vineyard,Friendsanity: Sophia 3 <3,FRIENDSANITY,Stardew Valley Expanded
+6188,Blue Moon Vineyard,Friendsanity: Sophia 4 <3,FRIENDSANITY,Stardew Valley Expanded
+6189,Blue Moon Vineyard,Friendsanity: Sophia 5 <3,FRIENDSANITY,Stardew Valley Expanded
+6190,Blue Moon Vineyard,Friendsanity: Sophia 6 <3,FRIENDSANITY,Stardew Valley Expanded
+6191,Blue Moon Vineyard,Friendsanity: Sophia 7 <3,FRIENDSANITY,Stardew Valley Expanded
+6192,Blue Moon Vineyard,Friendsanity: Sophia 8 <3,FRIENDSANITY,Stardew Valley Expanded
+6193,Blue Moon Vineyard,Friendsanity: Sophia 9 <3,FRIENDSANITY,Stardew Valley Expanded
+6194,Blue Moon Vineyard,Friendsanity: Sophia 10 <3,FRIENDSANITY,Stardew Valley Expanded
+6195,Blue Moon Vineyard,Friendsanity: Sophia 11 <3,FRIENDSANITY,Stardew Valley Expanded
+6196,Blue Moon Vineyard,Friendsanity: Sophia 12 <3,FRIENDSANITY,Stardew Valley Expanded
+6197,Blue Moon Vineyard,Friendsanity: Sophia 13 <3,FRIENDSANITY,Stardew Valley Expanded
+6198,Blue Moon Vineyard,Friendsanity: Sophia 14 <3,FRIENDSANITY,Stardew Valley Expanded
+6199,Jenkins' Residence,Friendsanity: Victor 1 <3,FRIENDSANITY,Stardew Valley Expanded
+6200,Jenkins' Residence,Friendsanity: Victor 2 <3,FRIENDSANITY,Stardew Valley Expanded
+6201,Jenkins' Residence,Friendsanity: Victor 3 <3,FRIENDSANITY,Stardew Valley Expanded
+6202,Jenkins' Residence,Friendsanity: Victor 4 <3,FRIENDSANITY,Stardew Valley Expanded
+6203,Jenkins' Residence,Friendsanity: Victor 5 <3,FRIENDSANITY,Stardew Valley Expanded
+6204,Jenkins' Residence,Friendsanity: Victor 6 <3,FRIENDSANITY,Stardew Valley Expanded
+6205,Jenkins' Residence,Friendsanity: Victor 7 <3,FRIENDSANITY,Stardew Valley Expanded
+6206,Jenkins' Residence,Friendsanity: Victor 8 <3,FRIENDSANITY,Stardew Valley Expanded
+6207,Jenkins' Residence,Friendsanity: Victor 9 <3,FRIENDSANITY,Stardew Valley Expanded
+6208,Jenkins' Residence,Friendsanity: Victor 10 <3,FRIENDSANITY,Stardew Valley Expanded
+6209,Jenkins' Residence,Friendsanity: Victor 11 <3,FRIENDSANITY,Stardew Valley Expanded
+6210,Jenkins' Residence,Friendsanity: Victor 12 <3,FRIENDSANITY,Stardew Valley Expanded
+6211,Jenkins' Residence,Friendsanity: Victor 13 <3,FRIENDSANITY,Stardew Valley Expanded
+6212,Jenkins' Residence,Friendsanity: Victor 14 <3,FRIENDSANITY,Stardew Valley Expanded
+6213,Fairhaven Farm,Friendsanity: Andy 1 <3,FRIENDSANITY,Stardew Valley Expanded
+6214,Fairhaven Farm,Friendsanity: Andy 2 <3,FRIENDSANITY,Stardew Valley Expanded
+6215,Fairhaven Farm,Friendsanity: Andy 3 <3,FRIENDSANITY,Stardew Valley Expanded
+6216,Fairhaven Farm,Friendsanity: Andy 4 <3,FRIENDSANITY,Stardew Valley Expanded
+6217,Fairhaven Farm,Friendsanity: Andy 5 <3,FRIENDSANITY,Stardew Valley Expanded
+6218,Fairhaven Farm,Friendsanity: Andy 6 <3,FRIENDSANITY,Stardew Valley Expanded
+6219,Fairhaven Farm,Friendsanity: Andy 7 <3,FRIENDSANITY,Stardew Valley Expanded
+6220,Fairhaven Farm,Friendsanity: Andy 8 <3,FRIENDSANITY,Stardew Valley Expanded
+6221,Fairhaven Farm,Friendsanity: Andy 9 <3,FRIENDSANITY,Stardew Valley Expanded
+6222,Fairhaven Farm,Friendsanity: Andy 10 <3,FRIENDSANITY,Stardew Valley Expanded
+6223,Aurora Vineyard,Friendsanity: Apples 1 <3,FRIENDSANITY,Stardew Valley Expanded
+6224,Aurora Vineyard,Friendsanity: Apples 2 <3,FRIENDSANITY,Stardew Valley Expanded
+6225,Aurora Vineyard,Friendsanity: Apples 3 <3,FRIENDSANITY,Stardew Valley Expanded
+6226,Aurora Vineyard,Friendsanity: Apples 4 <3,FRIENDSANITY,Stardew Valley Expanded
+6227,Aurora Vineyard,Friendsanity: Apples 5 <3,FRIENDSANITY,Stardew Valley Expanded
+6228,Aurora Vineyard,Friendsanity: Apples 6 <3,FRIENDSANITY,Stardew Valley Expanded
+6229,Aurora Vineyard,Friendsanity: Apples 7 <3,FRIENDSANITY,Stardew Valley Expanded
+6230,Aurora Vineyard,Friendsanity: Apples 8 <3,FRIENDSANITY,Stardew Valley Expanded
+6231,Aurora Vineyard,Friendsanity: Apples 9 <3,FRIENDSANITY,Stardew Valley Expanded
+6232,Aurora Vineyard,Friendsanity: Apples 10 <3,FRIENDSANITY,Stardew Valley Expanded
+6233,Museum,Friendsanity: Gunther 1 <3,FRIENDSANITY,Stardew Valley Expanded
+6234,Museum,Friendsanity: Gunther 2 <3,FRIENDSANITY,Stardew Valley Expanded
+6235,Museum,Friendsanity: Gunther 3 <3,FRIENDSANITY,Stardew Valley Expanded
+6236,Museum,Friendsanity: Gunther 4 <3,FRIENDSANITY,Stardew Valley Expanded
+6237,Museum,Friendsanity: Gunther 5 <3,FRIENDSANITY,Stardew Valley Expanded
+6238,Museum,Friendsanity: Gunther 6 <3,FRIENDSANITY,Stardew Valley Expanded
+6239,Museum,Friendsanity: Gunther 7 <3,FRIENDSANITY,Stardew Valley Expanded
+6240,Museum,Friendsanity: Gunther 8 <3,FRIENDSANITY,Stardew Valley Expanded
+6241,Museum,Friendsanity: Gunther 9 <3,FRIENDSANITY,Stardew Valley Expanded
+6242,Museum,Friendsanity: Gunther 10 <3,FRIENDSANITY,Stardew Valley Expanded
+6243,JojaMart,Friendsanity: Martin 1 <3,FRIENDSANITY,Stardew Valley Expanded
+6244,JojaMart,Friendsanity: Martin 2 <3,FRIENDSANITY,Stardew Valley Expanded
+6245,JojaMart,Friendsanity: Martin 3 <3,FRIENDSANITY,Stardew Valley Expanded
+6246,JojaMart,Friendsanity: Martin 4 <3,FRIENDSANITY,Stardew Valley Expanded
+6247,JojaMart,Friendsanity: Martin 5 <3,FRIENDSANITY,Stardew Valley Expanded
+6248,JojaMart,Friendsanity: Martin 6 <3,FRIENDSANITY,Stardew Valley Expanded
+6249,JojaMart,Friendsanity: Martin 7 <3,FRIENDSANITY,Stardew Valley Expanded
+6250,JojaMart,Friendsanity: Martin 8 <3,FRIENDSANITY,Stardew Valley Expanded
+6251,JojaMart,Friendsanity: Martin 9 <3,FRIENDSANITY,Stardew Valley Expanded
+6252,JojaMart,Friendsanity: Martin 10 <3,FRIENDSANITY,Stardew Valley Expanded
+6253,Adventurer's Guild,Friendsanity: Marlon 1 <3,FRIENDSANITY,Stardew Valley Expanded
+6254,Adventurer's Guild,Friendsanity: Marlon 2 <3,FRIENDSANITY,Stardew Valley Expanded
+6255,Adventurer's Guild,Friendsanity: Marlon 3 <3,FRIENDSANITY,Stardew Valley Expanded
+6256,Adventurer's Guild,Friendsanity: Marlon 4 <3,FRIENDSANITY,Stardew Valley Expanded
+6257,Adventurer's Guild,Friendsanity: Marlon 5 <3,FRIENDSANITY,Stardew Valley Expanded
+6258,Adventurer's Guild,Friendsanity: Marlon 6 <3,FRIENDSANITY,Stardew Valley Expanded
+6259,Adventurer's Guild,Friendsanity: Marlon 7 <3,FRIENDSANITY,Stardew Valley Expanded
+6260,Adventurer's Guild,Friendsanity: Marlon 8 <3,FRIENDSANITY,Stardew Valley Expanded
+6261,Adventurer's Guild,Friendsanity: Marlon 9 <3,FRIENDSANITY,Stardew Valley Expanded
+6262,Adventurer's Guild,Friendsanity: Marlon 10 <3,FRIENDSANITY,Stardew Valley Expanded
+6263,Wizard Tower,Friendsanity: Morgan 1 <3,FRIENDSANITY,Stardew Valley Expanded
+6264,Wizard Tower,Friendsanity: Morgan 2 <3,FRIENDSANITY,Stardew Valley Expanded
+6265,Wizard Tower,Friendsanity: Morgan 3 <3,FRIENDSANITY,Stardew Valley Expanded
+6266,Wizard Tower,Friendsanity: Morgan 4 <3,FRIENDSANITY,Stardew Valley Expanded
+6267,Wizard Tower,Friendsanity: Morgan 5 <3,FRIENDSANITY,Stardew Valley Expanded
+6268,Wizard Tower,Friendsanity: Morgan 6 <3,FRIENDSANITY,Stardew Valley Expanded
+6269,Wizard Tower,Friendsanity: Morgan 7 <3,FRIENDSANITY,Stardew Valley Expanded
+6270,Wizard Tower,Friendsanity: Morgan 8 <3,FRIENDSANITY,Stardew Valley Expanded
+6271,Wizard Tower,Friendsanity: Morgan 9 <3,FRIENDSANITY,Stardew Valley Expanded
+6272,Wizard Tower,Friendsanity: Morgan 10 <3,FRIENDSANITY,Stardew Valley Expanded
+6273,Scarlett's House,Friendsanity: Scarlett 1 <3,FRIENDSANITY,Stardew Valley Expanded
+6274,Scarlett's House,Friendsanity: Scarlett 2 <3,FRIENDSANITY,Stardew Valley Expanded
+6275,Scarlett's House,Friendsanity: Scarlett 3 <3,FRIENDSANITY,Stardew Valley Expanded
+6276,Scarlett's House,Friendsanity: Scarlett 4 <3,FRIENDSANITY,Stardew Valley Expanded
+6277,Scarlett's House,Friendsanity: Scarlett 5 <3,FRIENDSANITY,Stardew Valley Expanded
+6278,Scarlett's House,Friendsanity: Scarlett 6 <3,FRIENDSANITY,Stardew Valley Expanded
+6279,Scarlett's House,Friendsanity: Scarlett 7 <3,FRIENDSANITY,Stardew Valley Expanded
+6280,Scarlett's House,Friendsanity: Scarlett 8 <3,FRIENDSANITY,Stardew Valley Expanded
+6281,Scarlett's House,Friendsanity: Scarlett 9 <3,FRIENDSANITY,Stardew Valley Expanded
+6282,Scarlett's House,Friendsanity: Scarlett 10 <3,FRIENDSANITY,Stardew Valley Expanded
+6283,Susan's House,Friendsanity: Susan 1 <3,FRIENDSANITY,Stardew Valley Expanded
+6284,Susan's House,Friendsanity: Susan 2 <3,FRIENDSANITY,Stardew Valley Expanded
+6285,Susan's House,Friendsanity: Susan 3 <3,FRIENDSANITY,Stardew Valley Expanded
+6286,Susan's House,Friendsanity: Susan 4 <3,FRIENDSANITY,Stardew Valley Expanded
+6287,Susan's House,Friendsanity: Susan 5 <3,FRIENDSANITY,Stardew Valley Expanded
+6288,Susan's House,Friendsanity: Susan 6 <3,FRIENDSANITY,Stardew Valley Expanded
+6289,Susan's House,Friendsanity: Susan 7 <3,FRIENDSANITY,Stardew Valley Expanded
+6290,Susan's House,Friendsanity: Susan 8 <3,FRIENDSANITY,Stardew Valley Expanded
+6291,Susan's House,Friendsanity: Susan 9 <3,FRIENDSANITY,Stardew Valley Expanded
+6292,Susan's House,Friendsanity: Susan 10 <3,FRIENDSANITY,Stardew Valley Expanded
+6293,JojaMart,Friendsanity: Morris 1 <3,FRIENDSANITY,Stardew Valley Expanded
+6294,JojaMart,Friendsanity: Morris 2 <3,FRIENDSANITY,Stardew Valley Expanded
+6295,JojaMart,Friendsanity: Morris 3 <3,FRIENDSANITY,Stardew Valley Expanded
+6296,JojaMart,Friendsanity: Morris 4 <3,FRIENDSANITY,Stardew Valley Expanded
+6297,JojaMart,Friendsanity: Morris 5 <3,FRIENDSANITY,Stardew Valley Expanded
+6298,JojaMart,Friendsanity: Morris 6 <3,FRIENDSANITY,Stardew Valley Expanded
+6299,JojaMart,Friendsanity: Morris 7 <3,FRIENDSANITY,Stardew Valley Expanded
+6300,JojaMart,Friendsanity: Morris 8 <3,FRIENDSANITY,Stardew Valley Expanded
+6301,JojaMart,Friendsanity: Morris 9 <3,FRIENDSANITY,Stardew Valley Expanded
+6302,JojaMart,Friendsanity: Morris 10 <3,FRIENDSANITY,Stardew Valley Expanded
+6303,Witch's Swamp,Friendsanity: Zic 1 <3,FRIENDSANITY,Distant Lands - Witch Swamp Overhaul
+6304,Witch's Swamp,Friendsanity: Zic 2 <3,FRIENDSANITY,Distant Lands - Witch Swamp Overhaul
+6305,Witch's Swamp,Friendsanity: Zic 3 <3,FRIENDSANITY,Distant Lands - Witch Swamp Overhaul
+6306,Witch's Swamp,Friendsanity: Zic 4 <3,FRIENDSANITY,Distant Lands - Witch Swamp Overhaul
+6307,Witch's Swamp,Friendsanity: Zic 5 <3,FRIENDSANITY,Distant Lands - Witch Swamp Overhaul
+6308,Witch's Swamp,Friendsanity: Zic 6 <3,FRIENDSANITY,Distant Lands - Witch Swamp Overhaul
+6309,Witch's Swamp,Friendsanity: Zic 7 <3,FRIENDSANITY,Distant Lands - Witch Swamp Overhaul
+6310,Witch's Swamp,Friendsanity: Zic 8 <3,FRIENDSANITY,Distant Lands - Witch Swamp Overhaul
+6311,Witch's Swamp,Friendsanity: Zic 9 <3,FRIENDSANITY,Distant Lands - Witch Swamp Overhaul
+6312,Witch's Swamp,Friendsanity: Zic 10 <3,FRIENDSANITY,Distant Lands - Witch Swamp Overhaul
+6313,Witch's Attic,Friendsanity: Alecto 1 <3,FRIENDSANITY,Alecto the Witch
+6314,Witch's Attic,Friendsanity: Alecto 2 <3,FRIENDSANITY,Alecto the Witch
+6315,Witch's Attic,Friendsanity: Alecto 3 <3,FRIENDSANITY,Alecto the Witch
+6316,Witch's Attic,Friendsanity: Alecto 4 <3,FRIENDSANITY,Alecto the Witch
+6317,Witch's Attic,Friendsanity: Alecto 5 <3,FRIENDSANITY,Alecto the Witch
+6318,Witch's Attic,Friendsanity: Alecto 6 <3,FRIENDSANITY,Alecto the Witch
+6319,Witch's Attic,Friendsanity: Alecto 7 <3,FRIENDSANITY,Alecto the Witch
+6320,Witch's Attic,Friendsanity: Alecto 8 <3,FRIENDSANITY,Alecto the Witch
+6321,Witch's Attic,Friendsanity: Alecto 9 <3,FRIENDSANITY,Alecto the Witch
+6322,Witch's Attic,Friendsanity: Alecto 10 <3,FRIENDSANITY,Alecto the Witch
+6323,Mouse House,Friendsanity: Lacey 1 <3,FRIENDSANITY,Hat Mouse Lacey
+6324,Mouse House,Friendsanity: Lacey 2 <3,FRIENDSANITY,Hat Mouse Lacey
+6325,Mouse House,Friendsanity: Lacey 3 <3,FRIENDSANITY,Hat Mouse Lacey
+6326,Mouse House,Friendsanity: Lacey 4 <3,FRIENDSANITY,Hat Mouse Lacey
+6327,Mouse House,Friendsanity: Lacey 5 <3,FRIENDSANITY,Hat Mouse Lacey
+6328,Mouse House,Friendsanity: Lacey 6 <3,FRIENDSANITY,Hat Mouse Lacey
+6329,Mouse House,Friendsanity: Lacey 7 <3,FRIENDSANITY,Hat Mouse Lacey
+6330,Mouse House,Friendsanity: Lacey 8 <3,FRIENDSANITY,Hat Mouse Lacey
+6331,Mouse House,Friendsanity: Lacey 9 <3,FRIENDSANITY,Hat Mouse Lacey
+6332,Mouse House,Friendsanity: Lacey 10 <3,FRIENDSANITY,Hat Mouse Lacey
+6333,Mouse House,Friendsanity: Lacey 11 <3,FRIENDSANITY,Hat Mouse Lacey
+6334,Mouse House,Friendsanity: Lacey 12 <3,FRIENDSANITY,Hat Mouse Lacey
+6335,Mouse House,Friendsanity: Lacey 13 <3,FRIENDSANITY,Hat Mouse Lacey
+6336,Mouse House,Friendsanity: Lacey 14 <3,FRIENDSANITY,Hat Mouse Lacey
+6337,Boarding House - First Floor,Friendsanity: Joel 1 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6338,Boarding House - First Floor,Friendsanity: Joel 2 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6339,Boarding House - First Floor,Friendsanity: Joel 3 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6340,Boarding House - First Floor,Friendsanity: Joel 4 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6341,Boarding House - First Floor,Friendsanity: Joel 5 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6342,Boarding House - First Floor,Friendsanity: Joel 6 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6343,Boarding House - First Floor,Friendsanity: Joel 7 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6344,Boarding House - First Floor,Friendsanity: Joel 8 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6345,Boarding House - First Floor,Friendsanity: Joel 9 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6346,Boarding House - First Floor,Friendsanity: Joel 10 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6347,Boarding House - First Floor,Friendsanity: Sheila 1 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6348,Boarding House - First Floor,Friendsanity: Sheila 2 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6349,Boarding House - First Floor,Friendsanity: Sheila 3 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6350,Boarding House - First Floor,Friendsanity: Sheila 4 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6351,Boarding House - First Floor,Friendsanity: Sheila 5 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6352,Boarding House - First Floor,Friendsanity: Sheila 6 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6353,Boarding House - First Floor,Friendsanity: Sheila 7 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6354,Boarding House - First Floor,Friendsanity: Sheila 8 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6355,Boarding House - First Floor,Friendsanity: Sheila 9 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6356,Boarding House - First Floor,Friendsanity: Sheila 10 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6357,Boarding House - First Floor,Friendsanity: Sheila 11 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6358,Boarding House - First Floor,Friendsanity: Sheila 12 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6359,Boarding House - First Floor,Friendsanity: Sheila 13 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6360,Boarding House - First Floor,Friendsanity: Sheila 14 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6361,The Lost Valley,Friendsanity: Gregory 1 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6362,The Lost Valley,Friendsanity: Gregory 2 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6363,The Lost Valley,Friendsanity: Gregory 3 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6364,The Lost Valley,Friendsanity: Gregory 4 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6365,The Lost Valley,Friendsanity: Gregory 5 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6366,The Lost Valley,Friendsanity: Gregory 6 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6367,The Lost Valley,Friendsanity: Gregory 7 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6368,The Lost Valley,Friendsanity: Gregory 8 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6369,The Lost Valley,Friendsanity: Gregory 9 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6370,The Lost Valley,Friendsanity: Gregory 10 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6371,The Lost Valley,Friendsanity: Gregory 11 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6372,The Lost Valley,Friendsanity: Gregory 12 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6373,The Lost Valley,Friendsanity: Gregory 13 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+6374,The Lost Valley,Friendsanity: Gregory 14 <3,FRIENDSANITY,Boarding House and Bus Stop Extension
+7001,Pierre's General Store,Premium Pack,BACKPACK,Bigger Backpack
+7002,Carpenter Shop,Tractor Garage Blueprint,BUILDING_BLUEPRINT,Tractor Mod
+7003,The Deep Woods Depth 100,Pet the Deep Woods Unicorn,MANDATORY,DeepWoods
+7004,The Deep Woods Depth 50,Breaking Up Deep Woods Gingerbread House,MANDATORY,DeepWoods
+7005,The Deep Woods Depth 50,Drinking From Deep Woods Fountain,MANDATORY,DeepWoods
+7006,The Deep Woods Depth 100,Deep Woods Treasure Chest,MANDATORY,DeepWoods
+7007,The Deep Woods Depth 100,Deep Woods Trash Bin,MANDATORY,DeepWoods
+7008,The Deep Woods Depth 50,Chop Down a Deep Woods Iridium Tree,MANDATORY,DeepWoods
+7009,The Deep Woods Depth 10,The Deep Woods: Depth 10,ELEVATOR,DeepWoods
+7010,The Deep Woods Depth 20,The Deep Woods: Depth 20,ELEVATOR,DeepWoods
+7011,The Deep Woods Depth 30,The Deep Woods: Depth 30,ELEVATOR,DeepWoods
+7012,The Deep Woods Depth 40,The Deep Woods: Depth 40,ELEVATOR,DeepWoods
+7013,The Deep Woods Depth 50,The Deep Woods: Depth 50,ELEVATOR,DeepWoods
+7014,The Deep Woods Depth 60,The Deep Woods: Depth 60,ELEVATOR,DeepWoods
+7015,The Deep Woods Depth 70,The Deep Woods: Depth 70,ELEVATOR,DeepWoods
+7016,The Deep Woods Depth 80,The Deep Woods: Depth 80,ELEVATOR,DeepWoods
+7017,The Deep Woods Depth 90,The Deep Woods: Depth 90,ELEVATOR,DeepWoods
+7018,The Deep Woods Depth 100,The Deep Woods: Depth 100,ELEVATOR,DeepWoods
+7019,The Deep Woods Depth 50,Purify an Infested Lichtung,MANDATORY,DeepWoods
+7020,Skull Cavern Floor 25,Skull Cavern: Floor 25,ELEVATOR,Skull Cavern Elevator
+7021,Skull Cavern Floor 50,Skull Cavern: Floor 50,ELEVATOR,Skull Cavern Elevator
+7022,Skull Cavern Floor 75,Skull Cavern: Floor 75,ELEVATOR,Skull Cavern Elevator
+7023,Skull Cavern Floor 100,Skull Cavern: Floor 100,ELEVATOR,Skull Cavern Elevator
+7024,Skull Cavern Floor 125,Skull Cavern: Floor 125,ELEVATOR,Skull Cavern Elevator
+7025,Skull Cavern Floor 150,Skull Cavern: Floor 150,ELEVATOR,Skull Cavern Elevator
+7026,Skull Cavern Floor 175,Skull Cavern: Floor 175,ELEVATOR,Skull Cavern Elevator
+7027,Skull Cavern Floor 200,Skull Cavern: Floor 200,ELEVATOR,Skull Cavern Elevator
+7028,The Deep Woods Depth 100,The Sword in the Stone,MANDATORY,DeepWoods
+7051,Abandoned Mines - 1A,Abandoned Treasure - Floor 1A,MANDATORY,Boarding House and Bus Stop Extension
+7052,Abandoned Mines - 1B,Abandoned Treasure - Floor 1B,MANDATORY,Boarding House and Bus Stop Extension
+7053,Abandoned Mines - 2A,Abandoned Treasure - Floor 2A,MANDATORY,Boarding House and Bus Stop Extension
+7054,Abandoned Mines - 2B,Abandoned Treasure - Floor 2B,MANDATORY,Boarding House and Bus Stop Extension
+7055,Abandoned Mines - 3,Abandoned Treasure - Floor 3,MANDATORY,Boarding House and Bus Stop Extension
+7056,Abandoned Mines - 4,Abandoned Treasure - Floor 4,MANDATORY,Boarding House and Bus Stop Extension
+7057,Abandoned Mines - 5,Abandoned Treasure - Floor 5,MANDATORY,Boarding House and Bus Stop Extension
+7401,Farm,Cook Magic Elixir,COOKSANITY,Magic
+7402,Farm,Craft Travel Core,CRAFTSANITY,Magic
+7403,Farm,Craft Haste Elixir,CRAFTSANITY,Stardew Valley Expanded
+7404,Farm,Craft Hero Elixir,CRAFTSANITY,Stardew Valley Expanded
+7405,Farm,Craft Armor Elixir,CRAFTSANITY,Stardew Valley Expanded
+7406,Witch's Swamp,Craft Ginger Tincture,"CRAFTSANITY,GINGER_ISLAND",Distant Lands - Witch Swamp Overhaul
+7407,Farm,Craft Glass Path,CRAFTSANITY,Archaeology
+7408,Farm,Craft Glass Bazier,CRAFTSANITY,Archaeology
+7409,Farm,Craft Glass Fence,CRAFTSANITY,Archaeology
+7410,Farm,Craft Bone Path,CRAFTSANITY,Archaeology
+7411,Farm,Craft Water Shifter,CRAFTSANITY,Archaeology
+7412,Farm,Craft Wooden Display,CRAFTSANITY,Archaeology
+7413,Farm,Craft Hardwood Display,CRAFTSANITY,Archaeology
+7414,Farm,Craft Dwarf Gadget: Infinite Volcano Simulation,"CRAFTSANITY,GINGER_ISLAND",Archaeology
+7415,Farm,Craft Grinder,CRAFTSANITY,Archaeology
+7416,Farm,Craft Preservation Chamber,CRAFTSANITY,Archaeology
+7417,Farm,Craft Hardwood Preservation Chamber,CRAFTSANITY,Archaeology
+7418,Farm,Craft Ancient Battery Production Station,CRAFTSANITY,Archaeology
+7419,Farm,Craft Neanderthal Skeleton,CRAFTSANITY,Boarding House and Bus Stop Extension
+7420,Farm,Craft Pterodactyl Skeleton L,CRAFTSANITY,Boarding House and Bus Stop Extension
+7421,Farm,Craft Pterodactyl Skeleton M,CRAFTSANITY,Boarding House and Bus Stop Extension
+7422,Farm,Craft Pterodactyl Skeleton R,CRAFTSANITY,Boarding House and Bus Stop Extension
+7423,Farm,Craft T-Rex Skeleton L,CRAFTSANITY,Boarding House and Bus Stop Extension
+7424,Farm,Craft T-Rex Skeleton M,CRAFTSANITY,Boarding House and Bus Stop Extension
+7425,Farm,Craft T-Rex Skeleton R,CRAFTSANITY,Boarding House and Bus Stop Extension
+7451,Adventurer's Guild,Magic Elixir Recipe,"CHEFSANITY,CHEFSANITY_PURCHASE",Magic
+7452,Adventurer's Guild,Travel Core Recipe,CRAFTSANITY,Magic
+7453,Alesia Shop,Haste Elixir Recipe,CRAFTSANITY,Stardew Valley Expanded
+7454,Isaac Shop,Hero Elixir Recipe,CRAFTSANITY,Stardew Valley Expanded
+7455,Alesia Shop,Armor Elixir Recipe,CRAFTSANITY,Stardew Valley Expanded
+7501,Mountain,Missing Envelope,"STORY_QUEST",Ayeisha - The Postal Worker (Custom NPC)
+7502,Forest,Lost Emerald Ring,"STORY_QUEST",Ayeisha - The Postal Worker (Custom NPC)
+7503,Forest,Mr.Ginger's request,"STORY_QUEST",Mister Ginger (cat npc)
+7504,Forest,Juna's Drink Request,"STORY_QUEST",Juna - Roommate NPC
+7505,Forest,Juna's BFF Request,"STORY_QUEST",Juna - Roommate NPC
+7506,Forest,Juna's Monster Mash,SPECIAL_ORDER_BOARD,Juna - Roommate NPC
+7507,Adventurer's Guild,Marlon's Boat,"STORY_QUEST,GINGER_ISLAND",Stardew Valley Expanded
+7508,Railroad,The Railroad Boulder,"STORY_QUEST",Stardew Valley Expanded
+7509,Grandpa's Shed Interior,Grandpa's Shed,"STORY_QUEST",Stardew Valley Expanded
+7510,Aurora Vineyard,Aurora Vineyard,"STORY_QUEST",Stardew Valley Expanded
+7511,Lance's House Main,Monster Crops,"STORY_QUEST,GINGER_ISLAND",Stardew Valley Expanded
+7512,Sewer,Void Soul Retrieval,"STORY_QUEST,GINGER_ISLAND",Stardew Valley Expanded
+7513,Fairhaven Farm,Andy's Cellar,SPECIAL_ORDER_BOARD,Stardew Valley Expanded
+7514,Adventurer's Guild,A Mysterious Venture,SPECIAL_ORDER_BOARD,Stardew Valley Expanded
+7515,Jenkins' Residence,An Elegant Reception,SPECIAL_ORDER_BOARD,Stardew Valley Expanded
+7516,Sophia's House,Fairy Garden,"SPECIAL_ORDER_BOARD,GINGER_ISLAND",Stardew Valley Expanded
+7517,Susan's House,Homemade Fertilizer,SPECIAL_ORDER_BOARD,Stardew Valley Expanded
+7519,Witch's Swamp,Corrupted Crops Task,GINGER_ISLAND,Distant Lands - Witch Swamp Overhaul
+7520,Witch's Swamp,A New Pot,STORY_QUEST,Distant Lands - Witch Swamp Overhaul
+7521,Witch's Swamp,Fancy Blanket Task,STORY_QUEST,Distant Lands - Witch Swamp Overhaul
+7522,Witch's Swamp,Witch's order,GINGER_ISLAND,Distant Lands - Witch Swamp Overhaul
+7523,Boarding House - First Floor,Pumpkin Soup,STORY_QUEST,Boarding House and Bus Stop Extension
+7524,Museum,Geode Order,SPECIAL_ORDER_BOARD,Professor Jasper Thomas
+7525,Museum,Dwarven Scrolls,SPECIAL_ORDER_BOARD,Professor Jasper Thomas
+7526,Mouse House,Hats for the Hat Mouse,STORY_QUEST,Hat Mouse Lacey
+7551,Kitchen,Cook Baked Berry Oatmeal,COOKSANITY,Stardew Valley Expanded
+7552,Kitchen,Cook Flower Cookie,COOKSANITY,Stardew Valley Expanded
+7553,Kitchen,Cook Big Bark Burger,COOKSANITY,Stardew Valley Expanded
+7554,Kitchen,Cook Frog Legs,COOKSANITY,Stardew Valley Expanded
+7555,Kitchen,Cook Glazed Butterfish,COOKSANITY,Stardew Valley Expanded
+7556,Kitchen,Cook Mixed Berry Pie,COOKSANITY,Stardew Valley Expanded
+7557,Kitchen,Cook Mushroom Berry Rice,COOKSANITY,Stardew Valley Expanded
+7558,Kitchen,Cook Seaweed Salad,COOKSANITY,Stardew Valley Expanded
+7559,Kitchen,Cook Void Delight,COOKSANITY,Stardew Valley Expanded
+7560,Kitchen,Cook Void Salmon Sushi,COOKSANITY,Stardew Valley Expanded
+7561,Kitchen,Cook Mushroom Kebab,COOKSANITY,Distant Lands - Witch Swamp Overhaul
+7562,Kitchen,Cook Crayfish Soup,COOKSANITY,Distant Lands - Witch Swamp Overhaul
+7563,Kitchen,Cook Pemmican,COOKSANITY,Distant Lands - Witch Swamp Overhaul
+7564,Kitchen,Cook Void Mint Tea,COOKSANITY,Distant Lands - Witch Swamp Overhaul
+7565,Kitchen,Cook Special Pumpkin Soup,COOKSANITY,Boarding House and Bus Stop Extension
+7601,Bear Shop,Baked Berry Oatmeal Recipe,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
+7602,Bear Shop,Flower Cookie Recipe,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
+7603,Saloon,Big Bark Burger Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",Stardew Valley Expanded
+7604,Adventurer's Guild,Frog Legs Recipe,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
+7605,Saloon,Glazed Butterfish Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",Stardew Valley Expanded
+7606,Saloon,Mixed Berry Pie Recipe,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
+7607,Adventurer's Guild,Mushroom Berry Rice Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",Stardew Valley Expanded
+7608,Adventurer's Guild,Seaweed Salad Recipe,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
+7609,Sewer,Void Delight Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",Stardew Valley Expanded
+7610,Sewer,Void Salmon Sushi Recipe,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
+7611,Witch's Swamp,Mushroom Kebab Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",Distant Lands - Witch Swamp Overhaul
+7613,Witch's Swamp,Pemmican Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",Distant Lands - Witch Swamp Overhaul
+7614,Witch's Swamp,Void Mint Tea Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",Distant Lands - Witch Swamp Overhaul
+7616,Mines Dwarf Shop,Neanderthal Skeleton Recipe,CRAFTSANITY,Boarding House and Bus Stop Extension
+7617,Mines Dwarf Shop,Pterodactyl Skeleton L Recipe,CRAFTSANITY,Boarding House and Bus Stop Extension
+7618,Mines Dwarf Shop,Pterodactyl Skeleton M Recipe,CRAFTSANITY,Boarding House and Bus Stop Extension
+7619,Mines Dwarf Shop,Pterodactyl Skeleton R Recipe,CRAFTSANITY,Boarding House and Bus Stop Extension
+7620,Mines Dwarf Shop,T-Rex Skeleton L Recipe,CRAFTSANITY,Boarding House and Bus Stop Extension
+7621,Mines Dwarf Shop,T-Rex Skeleton M Recipe,CRAFTSANITY,Boarding House and Bus Stop Extension
+7622,Mines Dwarf Shop,T-Rex Skeleton R Recipe,CRAFTSANITY,Boarding House and Bus Stop Extension
+7651,Alesia Shop,Tempered Galaxy Dagger,MANDATORY,Stardew Valley Expanded
+7652,Isaac Shop,Tempered Galaxy Sword,MANDATORY,Stardew Valley Expanded
+7653,Isaac Shop,Tempered Galaxy Hammer,MANDATORY,Stardew Valley Expanded
+7701,Island South,Fishsanity: Baby Lunaloo,"FISHSANITY,GINGER_ISLAND",Stardew Valley Expanded
+7702,Crimson Badlands,Fishsanity: Bonefish,FISHSANITY,Stardew Valley Expanded
+7703,Forest,Fishsanity: Bull Trout,FISHSANITY,Stardew Valley Expanded
+7704,Forest West,Fishsanity: Butterfish,FISHSANITY,Stardew Valley Expanded
+7705,Island South,Fishsanity: Clownfish,"FISHSANITY,GINGER_ISLAND",Stardew Valley Expanded
+7706,Highlands Outside,Fishsanity: Daggerfish,"FISHSANITY,GINGER_ISLAND",Stardew Valley Expanded
+7707,Mountain,Fishsanity: Frog,FISHSANITY,Stardew Valley Expanded
+7708,Highlands Outside,Fishsanity: Gemfish,"FISHSANITY,GINGER_ISLAND",Stardew Valley Expanded
+7709,Sprite Spring,Fishsanity: Goldenfish,FISHSANITY,Stardew Valley Expanded
+7710,Secret Woods,Fishsanity: Grass Carp,FISHSANITY,Stardew Valley Expanded
+7711,Forest West,Fishsanity: King Salmon,FISHSANITY,Stardew Valley Expanded
+7712,Island West,Fishsanity: Lunaloo,"FISHSANITY,GINGER_ISLAND",Stardew Valley Expanded
+7713,Sprite Spring,Fishsanity: Meteor Carp,FISHSANITY,Stardew Valley Expanded
+7714,Town,Fishsanity: Minnow,FISHSANITY,Stardew Valley Expanded
+7715,Forest West,Fishsanity: Puppyfish,FISHSANITY,Stardew Valley Expanded
+7716,Sewer,Fishsanity: Radioactive Bass,FISHSANITY,Stardew Valley Expanded
+7717,Island West,Fishsanity: Seahorse,"FISHSANITY,GINGER_ISLAND",Stardew Valley Expanded
+7718,Island West,Fishsanity: Sea Sponge,"FISHSANITY,GINGER_ISLAND",Stardew Valley Expanded
+7719,Island South,Fishsanity: Shiny Lunaloo,"FISHSANITY,GINGER_ISLAND",Stardew Valley Expanded
+7720,Mutant Bug Lair,Fishsanity: Snatcher Worm,FISHSANITY,Stardew Valley Expanded
+7721,Beach,Fishsanity: Starfish,"FISHSANITY,GINGER_ISLAND",Stardew Valley Expanded
+7722,Fable Reef,Fishsanity: Torpedo Trout,"FISHSANITY,GINGER_ISLAND",Stardew Valley Expanded
+7723,Witch's Swamp,Fishsanity: Void Eel,FISHSANITY,Stardew Valley Expanded
+7724,Mutant Bug Lair,Fishsanity: Water Grub,FISHSANITY,Stardew Valley Expanded
+7725,Crimson Badlands,Fishsanity: Undeadfish,FISHSANITY,Stardew Valley Expanded
+7726,Shearwater Bridge,Fishsanity: Kittyfish,FISHSANITY,Stardew Valley Expanded
+7727,Blue Moon Vineyard,Fishsanity: Dulse Seaweed,FISHSANITY,Stardew Valley Expanded
+7728,Witch's Swamp,Fishsanity: Void Minnow,FISHSANITY,Distant Lands - Witch Swamp Overhaul
+7729,Witch's Swamp,Fishsanity: Swamp Leech,FISHSANITY,Distant Lands - Witch Swamp Overhaul
+7730,Witch's Swamp,Fishsanity: Giant Horsehoe Crab,FISHSANITY,Distant Lands - Witch Swamp Overhaul
+7731,Witch's Swamp,Fishsanity: Purple Algae,FISHSANITY,Distant Lands - Witch Swamp Overhaul
+7901,Farm,Harvest Monster Fruit,"CROPSANITY,GINGER_ISLAND",Stardew Valley Expanded
+7902,Farm,Harvest Salal Berry,CROPSANITY,Stardew Valley Expanded
+7903,Farm,Harvest Slime Berry,"CROPSANITY,GINGER_ISLAND",Stardew Valley Expanded
+7904,Farm,Harvest Ancient Fiber,CROPSANITY,Stardew Valley Expanded
+7905,Farm,Harvest Monster Mushroom,"CROPSANITY,GINGER_ISLAND",Stardew Valley Expanded
+7906,Farm,Harvest Void Root,"CROPSANITY,GINGER_ISLAND",Stardew Valley Expanded
+7907,Farm,Harvest Void Mint Leaves,CROPSANITY,Distant Lands - Witch Swamp Overhaul
+7908,Farm,Harvest Vile Ancient Fruit,CROPSANITY,Distant Lands - Witch Swamp Overhaul
+8001,Shipping,Shipsanity: Magic Elixir,SHIPSANITY,Magic
+8002,Shipping,Shipsanity: Travel Core,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Magic
+8003,Shipping,Shipsanity: Aegis Elixir,SHIPSANITY,Stardew Valley Expanded
+8004,Shipping,Shipsanity: Aged Blue Moon Wine,SHIPSANITY,Stardew Valley Expanded
+8005,Shipping,Shipsanity: Ancient Ferns Seed,SHIPSANITY,Stardew Valley Expanded
+8006,Shipping,Shipsanity: Ancient Fiber,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
+8007,Shipping,Shipsanity: Armor Elixir,SHIPSANITY,Stardew Valley Expanded
+8008,Shipping,Shipsanity: Baby Lunaloo,"SHIPSANITY,SHIPSANITY_FISH,GINGER_ISLAND",Stardew Valley Expanded
+8009,Shipping,Shipsanity: Baked Berry Oatmeal,SHIPSANITY,Stardew Valley Expanded
+8010,Shipping,Shipsanity: Barbarian Elixir,SHIPSANITY,Stardew Valley Expanded
+8011,Shipping,Shipsanity: Bearberrys,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
+8012,Shipping,Shipsanity: Big Bark Burger,SHIPSANITY,Stardew Valley Expanded
+8013,Shipping,Shipsanity: Big Conch,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
+8014,Shipping,Shipsanity: Blue Moon Wine,SHIPSANITY,Stardew Valley Expanded
+8015,Shipping,Shipsanity: Bonefish,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
+8016,Shipping,Shipsanity: Bull Trout,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
+8017,Shipping,Shipsanity: Butterfish,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
+8018,Shipping,Shipsanity: Clownfish,"SHIPSANITY,SHIPSANITY_FISH,GINGER_ISLAND",Stardew Valley Expanded
+8019,Shipping,Shipsanity: Daggerfish,"SHIPSANITY,SHIPSANITY_FISH,GINGER_ISLAND",Stardew Valley Expanded
+8020,Shipping,Shipsanity: Dewdrop Berry,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
+8021,Shipping,Shipsanity: Dried Sand Dollar,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
+8022,Shipping,Shipsanity: Dulse Seaweed,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
+8023,Shipping,Shipsanity: Ferngill Primrose,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
+8024,Shipping,Shipsanity: Flower Cookie,SHIPSANITY,Stardew Valley Expanded
+8025,Shipping,Shipsanity: Frog,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
+8026,Shipping,Shipsanity: Frog Legs,SHIPSANITY,Stardew Valley Expanded
+8027,Shipping,Shipsanity: Fungus Seed,"SHIPSANITY,GINGER_ISLAND",Stardew Valley Expanded
+8029,Shipping,Shipsanity: Gemfish,"SHIPSANITY,SHIPSANITY_FISH,GINGER_ISLAND",Stardew Valley Expanded
+8030,Shipping,Shipsanity: Glazed Butterfish,"SHIPSANITY",Stardew Valley Expanded
+8031,Shipping,Shipsanity: Golden Ocean Flower,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT,GINGER_ISLAND",Stardew Valley Expanded
+8032,Shipping,Shipsanity: Goldenfish,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
+8033,Shipping,Shipsanity: Goldenrod,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
+8034,Shipping,Shipsanity: Grampleton Orange Chicken,SHIPSANITY,Stardew Valley Expanded
+8035,Shipping,Shipsanity: Grass Carp,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
+8036,Shipping,Shipsanity: Gravity Elixir,SHIPSANITY,Stardew Valley Expanded
+8037,Shipping,Shipsanity: Green Mushroom,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT,GINGER_ISLAND",Stardew Valley Expanded
+8038,Shipping,Shipsanity: Haste Elixir,SHIPSANITY,Stardew Valley Expanded
+8039,Shipping,Shipsanity: Hero Elixir,SHIPSANITY,Stardew Valley Expanded
+8040,Shipping,Shipsanity: King Salmon,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
+8050,Shipping,Shipsanity: Kittyfish,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
+8051,Shipping,Shipsanity: Lightning Elixir,SHIPSANITY,Stardew Valley Expanded
+8052,Shipping,Shipsanity: Lucky Four Leaf Clover,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
+8053,Shipping,Shipsanity: Lunaloo,"SHIPSANITY,SHIPSANITY_FISH,GINGER_ISLAND",Stardew Valley Expanded
+8054,Shipping,Shipsanity: Meteor Carp,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
+8055,Shipping,Shipsanity: Minnow,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
+8056,Shipping,Shipsanity: Mixed Berry Pie,SHIPSANITY,Stardew Valley Expanded
+8057,Shipping,Shipsanity: Monster Fruit,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT,GINGER_ISLAND",Stardew Valley Expanded
+8058,Shipping,Shipsanity: Monster Mushroom,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT,GINGER_ISLAND",Stardew Valley Expanded
+8059,Shipping,Shipsanity: Mushroom Berry Rice,SHIPSANITY,Stardew Valley Expanded
+8060,Shipping,Shipsanity: Mushroom Colony,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
+8061,Shipping,Shipsanity: Ornate Treasure Chest,"SHIPSANITY,GINGER_ISLAND",Stardew Valley Expanded
+8062,Shipping,Shipsanity: Poison Mushroom,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
+8063,Shipping,Shipsanity: Puppyfish,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
+8064,Shipping,Shipsanity: Radioactive Bass,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
+8065,Shipping,Shipsanity: Red Baneberry,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
+8066,Shipping,Shipsanity: Rusty Blade,SHIPSANITY,Stardew Valley Expanded
+8067,Shipping,Shipsanity: Salal Berry,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT,GINGER_ISLAND",Stardew Valley Expanded
+8068,Shipping,Shipsanity: Sea Sponge,"SHIPSANITY,SHIPSANITY_FISH,GINGER_ISLAND",Stardew Valley Expanded
+8069,Shipping,Shipsanity: Seahorse,"SHIPSANITY,SHIPSANITY_FISH,GINGER_ISLAND",Stardew Valley Expanded
+8070,Shipping,Shipsanity: Seaweed Salad,SHIPSANITY,Stardew Valley Expanded
+8071,Shipping,Shipsanity: Shiny Lunaloo,"SHIPSANITY,SHIPSANITY_FISH,GINGER_ISLAND",Stardew Valley Expanded
+8072,Shipping,Shipsanity: Shrub Seed,"SHIPSANITY,GINGER_ISLAND",Stardew Valley Expanded
+8073,Shipping,Shipsanity: Slime Berry,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT,GINGER_ISLAND",Stardew Valley Expanded
+8074,Shipping,Shipsanity: Slime Seed,"SHIPSANITY,GINGER_ISLAND",Stardew Valley Expanded
+8075,Shipping,Shipsanity: Smelly Rafflesia,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
+8076,Shipping,Shipsanity: Sports Drink,SHIPSANITY,Stardew Valley Expanded
+8077,Shipping,Shipsanity: Stalk Seed,"SHIPSANITY,GINGER_ISLAND",Stardew Valley Expanded
+8078,Shipping,Shipsanity: Stamina Capsule,SHIPSANITY,Stardew Valley Expanded
+8079,Shipping,Shipsanity: Starfish,"SHIPSANITY,SHIPSANITY_FISH,GINGER_ISLAND",Stardew Valley Expanded
+8080,Shipping,Shipsanity: Swirl Stone,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
+8081,Shipping,Shipsanity: Thistle,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
+8082,Shipping,Shipsanity: Torpedo Trout,"SHIPSANITY,SHIPSANITY_FISH,GINGER_ISLAND",Stardew Valley Expanded
+8083,Shipping,Shipsanity: Undeadfish,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
+8084,Shipping,Shipsanity: Void Delight,SHIPSANITY,Stardew Valley Expanded
+8085,Shipping,Shipsanity: Void Eel,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
+8086,Shipping,Shipsanity: Void Pebble,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
+8087,Shipping,Shipsanity: Void Root,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT,GINGER_ISLAND",Stardew Valley Expanded
+8088,Shipping,Shipsanity: Void Salmon Sushi,SHIPSANITY,Stardew Valley Expanded
+8089,Shipping,Shipsanity: Void Seed,"SHIPSANITY,GINGER_ISLAND",Stardew Valley Expanded
+8090,Shipping,Shipsanity: Void Shard,SHIPSANITY,Stardew Valley Expanded
+8091,Shipping,Shipsanity: Void Soul,SHIPSANITY,Stardew Valley Expanded
+8092,Shipping,Shipsanity: Water Grub,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
+8093,Shipping,Shipsanity: Winter Star Rose,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
+8094,Shipping,Shipsanity: Wooden Display: Amphibian Fossil,SHIPSANITY,Archaeology
+8095,Shipping,Shipsanity: Hardwood Display: Amphibian Fossil,SHIPSANITY,Archaeology
+8096,Shipping,Shipsanity: Wooden Display: Anchor,SHIPSANITY,Archaeology
+8097,Shipping,Shipsanity: Hardwood Display: Anchor,SHIPSANITY,Archaeology
+8098,Shipping,Shipsanity: Wooden Display: Ancient Doll,SHIPSANITY,Archaeology
+8099,Shipping,Shipsanity: Hardwood Display: Ancient Doll,SHIPSANITY,Archaeology
+8100,Shipping,Shipsanity: Wooden Display: Ancient Drum,SHIPSANITY,Archaeology
+8101,Shipping,Shipsanity: Hardwood Display: Ancient Drum,SHIPSANITY,Archaeology
+8102,Shipping,Shipsanity: Wooden Display: Ancient Seed,SHIPSANITY,Archaeology
+8103,Shipping,Shipsanity: Hardwood Display: Ancient Seed,SHIPSANITY,Archaeology
+8104,Shipping,Shipsanity: Wooden Display: Ancient Sword,SHIPSANITY,Archaeology
+8105,Shipping,Shipsanity: Hardwood Display: Ancient Sword,SHIPSANITY,Archaeology
+8106,Shipping,Shipsanity: Wooden Display: Arrowhead,SHIPSANITY,Archaeology
+8107,Shipping,Shipsanity: Hardwood Display: Arrowhead,SHIPSANITY,Archaeology
+8108,Shipping,Shipsanity: Wooden Display: Bone Flute,SHIPSANITY,Archaeology
+8109,Shipping,Shipsanity: Hardwood Display: Bone Flute,SHIPSANITY,Archaeology
+8110,Shipping,Shipsanity: Wooden Display: Chewing Stick,SHIPSANITY,Archaeology
+8111,Shipping,Shipsanity: Hardwood Display: Chewing Stick,SHIPSANITY,Archaeology
+8112,Shipping,Shipsanity: Wooden Display: Chicken Statue,SHIPSANITY,Archaeology
+8113,Shipping,Shipsanity: Hardwood Display: Chicken Statue,SHIPSANITY,Archaeology
+8114,Shipping,Shipsanity: Wooden Display: Chipped Amphora,SHIPSANITY,Archaeology
+8115,Shipping,Shipsanity: Hardwood Display: Chipped Amphora,SHIPSANITY,Archaeology
+8116,Shipping,Shipsanity: Wooden Display: Dinosaur Egg,SHIPSANITY,Archaeology
+8117,Shipping,Shipsanity: Hardwood Display: Dinosaur Egg,SHIPSANITY,Archaeology
+8118,Shipping,Shipsanity: Wooden Display: Dried Starfish,SHIPSANITY,Archaeology
+8119,Shipping,Shipsanity: Hardwood Display: Dried Starfish,SHIPSANITY,Archaeology
+8120,Shipping,Shipsanity: Wooden Display: Dwarf Gadget,SHIPSANITY,Archaeology
+8121,Shipping,Shipsanity: Hardwood Display: Dwarf Gadget,SHIPSANITY,Archaeology
+8122,Shipping,Shipsanity: Wooden Display: Dwarf Scroll I,SHIPSANITY,Archaeology
+8123,Shipping,Shipsanity: Hardwood Display: Dwarf Scroll I,SHIPSANITY,Archaeology
+8124,Shipping,Shipsanity: Wooden Display: Dwarf Scroll II,SHIPSANITY,Archaeology
+8125,Shipping,Shipsanity: Hardwood Display: Dwarf Scroll II,SHIPSANITY,Archaeology
+8126,Shipping,Shipsanity: Wooden Display: Dwarf Scroll III,SHIPSANITY,Archaeology
+8127,Shipping,Shipsanity: Hardwood Display: Dwarf Scroll III,SHIPSANITY,Archaeology
+8128,Shipping,Shipsanity: Wooden Display: Dwarf Scroll IV,SHIPSANITY,Archaeology
+8129,Shipping,Shipsanity: Hardwood Display: Dwarf Scroll IV,SHIPSANITY,Archaeology
+8130,Shipping,Shipsanity: Wooden Display: Dwarvish Helm,SHIPSANITY,Archaeology
+8131,Shipping,Shipsanity: Hardwood Display: Dwarvish Helm,SHIPSANITY,Archaeology
+8132,Shipping,Shipsanity: Wooden Display: Elvish Jewelry,SHIPSANITY,Archaeology
+8133,Shipping,Shipsanity: Hardwood Display: Elvish Jewelry,SHIPSANITY,Archaeology
+8134,Shipping,Shipsanity: Wooden Display: Fossilized Leg,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8135,Shipping,Shipsanity: Hardwood Display: Fossilized Leg,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8136,Shipping,Shipsanity: Wooden Display: Fossilized Ribs,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8137,Shipping,Shipsanity: Hardwood Display: Fossilized Ribs,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8138,Shipping,Shipsanity: Wooden Display: Fossilized Skull,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8139,Shipping,Shipsanity: Hardwood Display: Fossilized Skull,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8140,Shipping,Shipsanity: Wooden Display: Fossilized Spine,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8141,Shipping,Shipsanity: Hardwood Display: Fossilized Spine,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8142,Shipping,Shipsanity: Wooden Display: Fossilized Tail,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8143,Shipping,Shipsanity: Hardwood Display: Fossilized Tail,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8144,Shipping,Shipsanity: Wooden Display: Glass Shards,SHIPSANITY,Archaeology
+8145,Shipping,Shipsanity: Hardwood Display: Glass Shards,SHIPSANITY,Archaeology
+8146,Shipping,Shipsanity: Wooden Display: Golden Mask,SHIPSANITY,Archaeology
+8147,Shipping,Shipsanity: Hardwood Display: Golden Mask,SHIPSANITY,Archaeology
+8148,Shipping,Shipsanity: Wooden Display: Golden Relic,SHIPSANITY,Archaeology
+8149,Shipping,Shipsanity: Hardwood Display: Golden Relic,SHIPSANITY,Archaeology
+8150,Shipping,Shipsanity: Wooden Display: Mummified Bat,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8151,Shipping,Shipsanity: Hardwood Display: Mummified Bat,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8152,Shipping,Shipsanity: Wooden Display: Mummified Frog,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8153,Shipping,Shipsanity: Hardwood Display: Mummified Frog,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8154,Shipping,Shipsanity: Wooden Display: Nautilus Fossil,SHIPSANITY,Archaeology
+8155,Shipping,Shipsanity: Hardwood Display: Nautilus Fossil,SHIPSANITY,Archaeology
+8156,Shipping,Shipsanity: Wooden Display: Ornamental Fan,SHIPSANITY,Archaeology
+8157,Shipping,Shipsanity: Hardwood Display: Ornamental Fan,SHIPSANITY,Archaeology
+8158,Shipping,Shipsanity: Wooden Display: Palm Fossil,SHIPSANITY,Archaeology
+8159,Shipping,Shipsanity: Hardwood Display: Palm Fossil,SHIPSANITY,Archaeology
+8160,Shipping,Shipsanity: Wooden Display: Prehistoric Handaxe,SHIPSANITY,Archaeology
+8161,Shipping,Shipsanity: Hardwood Display: Prehistoric Handaxe,SHIPSANITY,Archaeology
+8162,Shipping,Shipsanity: Wooden Display: Prehistoric Rib,SHIPSANITY,Archaeology
+8163,Shipping,Shipsanity: Hardwood Display: Prehistoric Rib,SHIPSANITY,Archaeology
+8164,Shipping,Shipsanity: Wooden Display: Prehistoric Scapula,SHIPSANITY,Archaeology
+8165,Shipping,Shipsanity: Hardwood Display: Prehistoric Scapula,SHIPSANITY,Archaeology
+8166,Shipping,Shipsanity: Wooden Display: Prehistoric Skull,SHIPSANITY,Archaeology
+8167,Shipping,Shipsanity: Hardwood Display: Prehistoric Skull,SHIPSANITY,Archaeology
+8168,Shipping,Shipsanity: Wooden Display: Prehistoric Tibia,SHIPSANITY,Archaeology
+8169,Shipping,Shipsanity: Hardwood Display: Prehistoric Tibia,SHIPSANITY,Archaeology
+8170,Shipping,Shipsanity: Wooden Display: Prehistoric Tool,SHIPSANITY,Archaeology
+8171,Shipping,Shipsanity: Hardwood Display: Prehistoric Tool,SHIPSANITY,Archaeology
+8172,Shipping,Shipsanity: Wooden Display: Prehistoric Vertebra,SHIPSANITY,Archaeology
+8173,Shipping,Shipsanity: Hardwood Display: Prehistoric Vertebra,SHIPSANITY,Archaeology
+8174,Shipping,Shipsanity: Wooden Display: Rare Disc,SHIPSANITY,Archaeology
+8175,Shipping,Shipsanity: Hardwood Display: Rare Disc,SHIPSANITY,Archaeology
+8176,Shipping,Shipsanity: Wooden Display: Rusty Cog,SHIPSANITY,Archaeology
+8177,Shipping,Shipsanity: Hardwood Display: Rusty Cog,SHIPSANITY,Archaeology
+8178,Shipping,Shipsanity: Wooden Display: Rusty Spoon,SHIPSANITY,Archaeology
+8179,Shipping,Shipsanity: Hardwood Display: Rusty Spoon,SHIPSANITY,Archaeology
+8180,Shipping,Shipsanity: Wooden Display: Rusty Spur,SHIPSANITY,Archaeology
+8181,Shipping,Shipsanity: Hardwood Display: Rusty Spur,SHIPSANITY,Archaeology
+8182,Shipping,Shipsanity: Wooden Display: Skeletal Hand,SHIPSANITY,Archaeology
+8183,Shipping,Shipsanity: Hardwood Display: Skeletal Hand,SHIPSANITY,Archaeology
+8184,Shipping,Shipsanity: Wooden Display: Skeletal Tail,SHIPSANITY,Archaeology
+8185,Shipping,Shipsanity: Hardwood Display: Skeletal Tail,SHIPSANITY,Archaeology
+8186,Shipping,Shipsanity: Wooden Display: Snake Skull,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8187,Shipping,Shipsanity: Hardwood Display: Snake Skull,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8188,Shipping,Shipsanity: Wooden Display: Snake Vertebrae,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8189,Shipping,Shipsanity: Hardwood Display: Snake Vertebrae,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8190,Shipping,Shipsanity: Wooden Display: Strange Doll (Green),SHIPSANITY,Archaeology
+8191,Shipping,Shipsanity: Hardwood Display: Strange Doll (Green),SHIPSANITY,Archaeology
+8192,Shipping,Shipsanity: Wooden Display: Strange Doll,SHIPSANITY,Archaeology
+8193,Shipping,Shipsanity: Hardwood Display: Strange Doll,SHIPSANITY,Archaeology
+8194,Shipping,Shipsanity: Wooden Display: Trilobite Fossil,SHIPSANITY,Archaeology
+8195,Shipping,Shipsanity: Hardwood Display: Trilobite Fossil,SHIPSANITY,Archaeology
+8196,Shipping,Shipsanity: Bone Path,SHIPSANITY,Archaeology
+8197,Shipping,Shipsanity: Glass Fence,SHIPSANITY,Archaeology
+8198,Shipping,Shipsanity: Glass Path,SHIPSANITY,Archaeology
+8199,Shipping,Shipsanity: Hardwood Display,SHIPSANITY,Archaeology
+8200,Shipping,Shipsanity: Wooden Display,SHIPSANITY,Archaeology
+8201,Shipping,Shipsanity: Dwarf Gadget: Infinite Volcano Simulation,"SHIPSANITY,GINGER_ISLAND",Archaeology
+8202,Shipping,Shipsanity: Water Shifter,SHIPSANITY,Archaeology
+8203,Shipping,Shipsanity: Brown Amanita,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Distant Lands - Witch Swamp Overhaul
+8204,Shipping,Shipsanity: Swamp Herb,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Distant Lands - Witch Swamp Overhaul
+8205,Shipping,Shipsanity: Void Mint Seeds,SHIPSANITY,Distant Lands - Witch Swamp Overhaul
+8206,Shipping,Shipsanity: Vile Ancient Fruit Seeds,SHIPSANITY,Distant Lands - Witch Swamp Overhaul
+8207,Shipping,Shipsanity: Void Mint Leaves,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",Distant Lands - Witch Swamp Overhaul
+8208,Shipping,Shipsanity: Vile Ancient Fruit,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",Distant Lands - Witch Swamp Overhaul
+8209,Shipping,Shipsanity: Void Minnow,"SHIPSANITY,SHIPSANITY_FISH",Distant Lands - Witch Swamp Overhaul
+8210,Shipping,Shipsanity: Swamp Leech,"SHIPSANITY,SHIPSANITY_FISH",Distant Lands - Witch Swamp Overhaul
+8211,Shipping,Shipsanity: Purple Algae,SHIPSANITY,Distant Lands - Witch Swamp Overhaul
+8212,Shipping,Shipsanity: Giant Horsehoe Crab,"SHIPSANITY,SHIPSANITY_FISH",Distant Lands - Witch Swamp Overhaul
+8213,Shipping,Shipsanity: Mushroom Kebab,SHIPSANITY,Distant Lands - Witch Swamp Overhaul
+8214,Shipping,Shipsanity: Crayfish Soup,SHIPSANITY,Distant Lands - Witch Swamp Overhaul
+8215,Shipping,Shipsanity: Pemmican,SHIPSANITY,Distant Lands - Witch Swamp Overhaul
+8216,Shipping,Shipsanity: Void Mint Tea,SHIPSANITY,Distant Lands - Witch Swamp Overhaul
+8217,Shipping,Shipsanity: Ginger Tincture,"SHIPSANITY,GINGER_ISLAND",Distant Lands - Witch Swamp Overhaul
+8218,Shipping,Shipsanity: Neanderthal Limb Bones,SHIPSANITY,Boarding House and Bus Stop Extension
+8219,Shipping,Shipsanity: Dinosaur Claw,SHIPSANITY,Boarding House and Bus Stop Extension
+8220,Shipping,Shipsanity: Special Pumpkin Soup,SHIPSANITY,Boarding House and Bus Stop Extension
+8221,Shipping,Shipsanity: Pterodactyl L Wing Bone,SHIPSANITY,Boarding House and Bus Stop Extension
+8222,Shipping,Shipsanity: Dinosaur Skull,SHIPSANITY,Boarding House and Bus Stop Extension
+8223,Shipping,Shipsanity: Dinosaur Tooth,SHIPSANITY,Boarding House and Bus Stop Extension
+8224,Shipping,Shipsanity: Pterodactyl Egg,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Boarding House and Bus Stop Extension
+8225,Shipping,Shipsanity: Pterodactyl Ribs,SHIPSANITY,Boarding House and Bus Stop Extension
+8226,Shipping,Shipsanity: Dinosaur Vertebra,SHIPSANITY,Boarding House and Bus Stop Extension
+8227,Shipping,Shipsanity: Neanderthal Ribs,SHIPSANITY,Boarding House and Bus Stop Extension
+8228,Shipping,Shipsanity: Dinosaur Pelvis,SHIPSANITY,Boarding House and Bus Stop Extension
+8229,Shipping,Shipsanity: Dinosaur Ribs,SHIPSANITY,Boarding House and Bus Stop Extension
+8230,Shipping,Shipsanity: Pterodactyl Phalange,SHIPSANITY,Boarding House and Bus Stop Extension
+8231,Shipping,Shipsanity: Pterodactyl Vertebra,SHIPSANITY,Boarding House and Bus Stop Extension
+8232,Shipping,Shipsanity: Neanderthal Pelvis,SHIPSANITY,Boarding House and Bus Stop Extension
+8233,Shipping,Shipsanity: Pterodactyl Skull,SHIPSANITY,Boarding House and Bus Stop Extension
+8234,Shipping,Shipsanity: Dinosaur Femur,SHIPSANITY,Boarding House and Bus Stop Extension
+8235,Shipping,Shipsanity: Pterodactyl Claw,SHIPSANITY,Boarding House and Bus Stop Extension
+8236,Shipping,Shipsanity: Neanderthal Skull,SHIPSANITY,Boarding House and Bus Stop Extension
+8237,Shipping,Shipsanity: Pterodactyl R Wing Bone,SHIPSANITY,Boarding House and Bus Stop Extension
diff --git a/worlds/stardew_valley/data/monster_data.py b/worlds/stardew_valley/data/monster_data.py
index 6030571f89fe..b423fce4a3db 100644
--- a/worlds/stardew_valley/data/monster_data.py
+++ b/worlds/stardew_valley/data/monster_data.py
@@ -1,8 +1,173 @@
-class Monster:
- duggy = "Duggy"
- blue_slime = "Blue Slime"
- pepper_rex = "Pepper Rex"
- stone_golem = "Stone Golem"
+from dataclasses import dataclass
+from typing import List, Tuple, Dict, Set, Callable
+from ..mods.mod_data import ModNames
+from ..mods.mod_monster_locations import modded_monsters_locations
+from ..strings.monster_names import Monster, MonsterCategory
+from ..strings.performance_names import Performance
+from ..strings.region_names import Region
-frozen_monsters = (Monster.blue_slime,)
+
+@dataclass(frozen=True)
+class StardewMonster:
+ name: str
+ category: str
+ locations: Tuple[str]
+ difficulty: str
+
+ def __repr__(self):
+ return f"{self.name} [{self.category}] (Locations: {self.locations} |" \
+ f" Difficulty: {self.difficulty}) |"
+
+
+slime_hutch = (Region.slime_hutch,)
+mines_floor_20 = (Region.mines_floor_20,)
+mines_floor_60 = (Region.mines_floor_60,)
+mines_floor_100 = (Region.mines_floor_100,)
+dangerous_mines_20 = (Region.dangerous_mines_20,)
+dangerous_mines_60 = (Region.dangerous_mines_60,)
+dangerous_mines_100 = (Region.dangerous_mines_100,)
+quarry_mine = (Region.quarry_mine,)
+mutant_bug_lair = (Region.mutant_bug_lair,)
+skull_cavern = (Region.skull_cavern_25,)
+skull_cavern_high = (Region.skull_cavern_75,)
+skull_cavern_dangerous = (Region.dangerous_skull_cavern,)
+tiger_slime_grove = (Region.island_west,)
+volcano = (Region.volcano_floor_5,)
+volcano_high = (Region.volcano_floor_10,)
+
+all_monsters: List[StardewMonster] = []
+monster_modifications_by_mod: Dict[str, Dict[str, Callable[[str, StardewMonster], StardewMonster]]] = {}
+
+
+def create_monster(name: str, category: str, locations: Tuple[str, ...], difficulty: str) -> StardewMonster:
+ monster = StardewMonster(name, category, locations, difficulty)
+ all_monsters.append(monster)
+ return monster
+
+
+def update_monster_locations(mod_name: str, monster: StardewMonster):
+ new_locations = modded_monsters_locations[mod_name][monster.name]
+ total_locations = tuple(sorted(set(monster.locations + new_locations)))
+ return StardewMonster(monster.name, monster.category, total_locations, monster.difficulty)
+
+
+def register_monster_modification(mod_name: str, monster: StardewMonster, modification_function):
+ if mod_name not in monster_modifications_by_mod:
+ monster_modifications_by_mod[mod_name] = {}
+ monster_modifications_by_mod[mod_name][monster.name] = modification_function
+
+
+green_slime = create_monster(Monster.green_slime, MonsterCategory.slime, mines_floor_20, Performance.basic)
+blue_slime = create_monster(Monster.blue_slime, MonsterCategory.slime, mines_floor_60, Performance.decent)
+red_slime = create_monster(Monster.red_slime, MonsterCategory.slime, mines_floor_100, Performance.good)
+purple_slime = create_monster(Monster.purple_slime, MonsterCategory.slime, skull_cavern, Performance.great)
+yellow_slime = create_monster(Monster.yellow_slime, MonsterCategory.slime, skull_cavern_high, Performance.galaxy)
+black_slime = create_monster(Monster.black_slime, MonsterCategory.slime, slime_hutch, Performance.decent)
+copper_slime = create_monster(Monster.copper_slime, MonsterCategory.slime, quarry_mine, Performance.decent)
+iron_slime = create_monster(Monster.iron_slime, MonsterCategory.slime, quarry_mine, Performance.good)
+tiger_slime = create_monster(Monster.tiger_slime, MonsterCategory.slime, tiger_slime_grove, Performance.galaxy)
+
+shadow_shaman = create_monster(Monster.shadow_shaman, MonsterCategory.void_spirits, mines_floor_100, Performance.good)
+shadow_shaman_dangerous = create_monster(Monster.shadow_shaman_dangerous, MonsterCategory.void_spirits, dangerous_mines_100, Performance.galaxy)
+shadow_brute = create_monster(Monster.shadow_brute, MonsterCategory.void_spirits, mines_floor_100, Performance.good)
+shadow_brute_dangerous = create_monster(Monster.shadow_brute_dangerous, MonsterCategory.void_spirits, dangerous_mines_100, Performance.galaxy)
+shadow_sniper = create_monster(Monster.shadow_sniper, MonsterCategory.void_spirits, dangerous_mines_100, Performance.galaxy)
+
+bat = create_monster(Monster.bat, MonsterCategory.bats, mines_floor_20, Performance.basic)
+bat_dangerous = create_monster(Monster.bat_dangerous, MonsterCategory.bats, dangerous_mines_20, Performance.galaxy)
+frost_bat = create_monster(Monster.frost_bat, MonsterCategory.bats, mines_floor_60, Performance.decent)
+frost_bat_dangerous = create_monster(Monster.frost_bat_dangerous, MonsterCategory.bats, dangerous_mines_60, Performance.galaxy)
+lava_bat = create_monster(Monster.lava_bat, MonsterCategory.bats, mines_floor_100, Performance.good)
+iridium_bat = create_monster(Monster.iridium_bat, MonsterCategory.bats, skull_cavern_high, Performance.great)
+
+skeleton = create_monster(Monster.skeleton, MonsterCategory.skeletons, mines_floor_100, Performance.good)
+skeleton_dangerous = create_monster(Monster.skeleton_dangerous, MonsterCategory.skeletons, dangerous_mines_100, Performance.galaxy)
+skeleton_mage = create_monster(Monster.skeleton_mage, MonsterCategory.skeletons, dangerous_mines_100, Performance.galaxy)
+
+bug = create_monster(Monster.bug, MonsterCategory.cave_insects, mines_floor_20, Performance.basic)
+bug_dangerous = create_monster(Monster.bug_dangerous, MonsterCategory.cave_insects, dangerous_mines_20, Performance.galaxy)
+cave_fly = create_monster(Monster.cave_fly, MonsterCategory.cave_insects, mines_floor_20, Performance.basic)
+cave_fly_dangerous = create_monster(Monster.cave_fly_dangerous, MonsterCategory.cave_insects, dangerous_mines_60, Performance.galaxy)
+grub = create_monster(Monster.grub, MonsterCategory.cave_insects, mines_floor_20, Performance.basic)
+grub_dangerous = create_monster(Monster.grub_dangerous, MonsterCategory.cave_insects, dangerous_mines_60, Performance.galaxy)
+mutant_fly = create_monster(Monster.mutant_fly, MonsterCategory.cave_insects, mutant_bug_lair, Performance.good)
+mutant_grub = create_monster(Monster.mutant_grub, MonsterCategory.cave_insects, mutant_bug_lair, Performance.good)
+armored_bug = create_monster(Monster.armored_bug, MonsterCategory.cave_insects, skull_cavern, Performance.basic) # Requires 'Bug Killer' enchantment
+armored_bug_dangerous = create_monster(Monster.armored_bug_dangerous, MonsterCategory.cave_insects, skull_cavern,
+ Performance.good) # Requires 'Bug Killer' enchantment
+
+duggy = create_monster(Monster.duggy, MonsterCategory.duggies, mines_floor_20, Performance.basic)
+duggy_dangerous = create_monster(Monster.duggy_dangerous, MonsterCategory.duggies, dangerous_mines_20, Performance.great)
+magma_duggy = create_monster(Monster.magma_duggy, MonsterCategory.duggies, volcano, Performance.galaxy)
+
+dust_sprite = create_monster(Monster.dust_sprite, MonsterCategory.dust_sprites, mines_floor_60, Performance.basic)
+dust_sprite_dangerous = create_monster(Monster.dust_sprite_dangerous, MonsterCategory.dust_sprites, dangerous_mines_60, Performance.great)
+
+rock_crab = create_monster(Monster.rock_crab, MonsterCategory.rock_crabs, mines_floor_20, Performance.basic)
+rock_crab_dangerous = create_monster(Monster.rock_crab_dangerous, MonsterCategory.rock_crabs, dangerous_mines_20, Performance.great)
+lava_crab = create_monster(Monster.lava_crab, MonsterCategory.rock_crabs, mines_floor_100, Performance.good)
+lava_crab_dangerous = create_monster(Monster.lava_crab_dangerous, MonsterCategory.rock_crabs, dangerous_mines_100, Performance.galaxy)
+iridium_crab = create_monster(Monster.iridium_crab, MonsterCategory.rock_crabs, skull_cavern, Performance.great)
+
+mummy = create_monster(Monster.mummy, MonsterCategory.mummies, skull_cavern, Performance.great) # Requires bombs or "Crusader" enchantment
+mummy_dangerous = create_monster(Monster.mummy_dangerous, MonsterCategory.mummies, skull_cavern_dangerous,
+ Performance.maximum) # Requires bombs or "Crusader" enchantment
+
+pepper_rex = create_monster(Monster.pepper_rex, MonsterCategory.pepper_rex, skull_cavern, Performance.great)
+
+serpent = create_monster(Monster.serpent, MonsterCategory.serpents, skull_cavern, Performance.galaxy)
+royal_serpent = create_monster(Monster.royal_serpent, MonsterCategory.serpents, skull_cavern_dangerous, Performance.maximum)
+
+magma_sprite = create_monster(Monster.magma_sprite, MonsterCategory.magma_sprites, volcano, Performance.galaxy)
+magma_sparker = create_monster(Monster.magma_sparker, MonsterCategory.magma_sprites, volcano_high, Performance.galaxy)
+
+register_monster_modification(ModNames.sve, shadow_brute_dangerous, update_monster_locations)
+register_monster_modification(ModNames.sve, shadow_sniper, update_monster_locations)
+register_monster_modification(ModNames.sve, shadow_shaman_dangerous, update_monster_locations)
+register_monster_modification(ModNames.sve, mummy_dangerous, update_monster_locations)
+register_monster_modification(ModNames.sve, royal_serpent, update_monster_locations)
+register_monster_modification(ModNames.sve, skeleton_dangerous, update_monster_locations)
+register_monster_modification(ModNames.sve, skeleton_mage, update_monster_locations)
+register_monster_modification(ModNames.sve, dust_sprite_dangerous, update_monster_locations)
+
+register_monster_modification(ModNames.deepwoods, shadow_brute, update_monster_locations)
+register_monster_modification(ModNames.deepwoods, cave_fly, update_monster_locations)
+register_monster_modification(ModNames.deepwoods, green_slime, update_monster_locations)
+
+register_monster_modification(ModNames.boarding_house, pepper_rex, update_monster_locations)
+register_monster_modification(ModNames.boarding_house, shadow_brute, update_monster_locations)
+register_monster_modification(ModNames.boarding_house, iridium_bat, update_monster_locations)
+register_monster_modification(ModNames.boarding_house, frost_bat, update_monster_locations)
+register_monster_modification(ModNames.boarding_house, cave_fly, update_monster_locations)
+register_monster_modification(ModNames.boarding_house, bat, update_monster_locations)
+register_monster_modification(ModNames.boarding_house, grub, update_monster_locations)
+register_monster_modification(ModNames.boarding_house, bug, update_monster_locations)
+
+
+def all_monsters_by_name_given_mods(mods: Set[str]) -> Dict[str, StardewMonster]:
+ monsters_by_name = {}
+ for monster in all_monsters:
+ current_monster = monster
+ for mod in monster_modifications_by_mod:
+ if mod not in mods or monster.name not in monster_modifications_by_mod[mod]:
+ continue
+ modification_function = monster_modifications_by_mod[mod][monster.name]
+ current_monster = modification_function(mod, current_monster)
+ monsters_by_name[monster.name] = current_monster
+ return monsters_by_name
+
+
+def all_monsters_by_category_given_mods(mods: Set[str]) -> Dict[str, Tuple[StardewMonster, ...]]:
+ monsters_by_category = {}
+ for monster in all_monsters:
+ current_monster = monster
+ for mod in monster_modifications_by_mod:
+ if mod not in mods or monster.name not in monster_modifications_by_mod[mod]:
+ continue
+ modification_function = monster_modifications_by_mod[mod][monster.name]
+ current_monster = modification_function(mod, current_monster)
+ if current_monster.category not in monsters_by_category:
+ monsters_by_category[monster.category] = ()
+ monsters_by_category[current_monster.category] = monsters_by_category[current_monster.category] + (current_monster,)
+ return monsters_by_category
diff --git a/worlds/stardew_valley/data/museum_data.py b/worlds/stardew_valley/data/museum_data.py
index b786f5b2d00c..544bb92e6e55 100644
--- a/worlds/stardew_valley/data/museum_data.py
+++ b/worlds/stardew_valley/data/museum_data.py
@@ -3,23 +3,24 @@
from dataclasses import dataclass
from typing import List, Tuple, Union, Optional
-from . import common_data as common
-from .game_item import GameItem
-from .monster_data import Monster
+from ..strings.monster_names import Monster
+from ..strings.fish_names import WaterChest
+from ..strings.forageable_names import Forageable
+from ..strings.metal_names import Mineral, Artifact, Fossil
from ..strings.region_names import Region
from ..strings.geode_names import Geode
@dataclass(frozen=True)
-class MuseumItem(GameItem):
+class MuseumItem:
+ item_name: str
locations: Tuple[str, ...]
geodes: Tuple[str, ...]
monsters: Tuple[str, ...]
difficulty: float
@staticmethod
- def of(name: str,
- item_id: int,
+ def of(item_name: str,
difficulty: float,
locations: Union[str, Tuple[str, ...]],
geodes: Union[str, Tuple[str, ...]],
@@ -33,10 +34,10 @@ def of(name: str,
if isinstance(monsters, str):
monsters = (monsters,)
- return MuseumItem(name, item_id, locations, geodes, monsters, difficulty)
+ return MuseumItem(item_name, locations, geodes, monsters, difficulty)
def __repr__(self):
- return f"{self.name} [{self.item_id}] (Locations: {self.locations} |" \
+ return f"{self.item_name} (Locations: {self.locations} |" \
f" Geodes: {self.geodes} |" \
f" Monsters: {self.monsters}) "
@@ -50,20 +51,18 @@ def __repr__(self):
def create_artifact(name: str,
- item_id: int,
difficulty: float,
locations: Union[str, Tuple[str, ...]] = (),
geodes: Union[str, Tuple[str, ...]] = (),
monsters: Union[str, Tuple[str, ...]] = ()) -> MuseumItem:
- artifact_item = MuseumItem.of(name, item_id, difficulty, locations, geodes, monsters)
+ artifact_item = MuseumItem.of(name, difficulty, locations, geodes, monsters)
all_museum_artifacts.append(artifact_item)
all_museum_items.append(artifact_item)
return artifact_item
def create_mineral(name: str,
- item_id: int,
- locations: Union[str, Tuple[str, ...]],
+ locations: Union[str, Tuple[str, ...]] = (),
geodes: Union[str, Tuple[str, ...]] = (),
monsters: Union[str, Tuple[str, ...]] = (),
difficulty: Optional[float] = None) -> MuseumItem:
@@ -78,212 +77,207 @@ def create_mineral(name: str,
if "Omni Geode" in geodes:
difficulty += 31.0 / 2750.0 * 100
- mineral_item = MuseumItem.of(name, item_id, difficulty, locations, geodes, monsters)
+ mineral_item = MuseumItem.of(name, difficulty, locations, geodes, monsters)
all_museum_minerals.append(mineral_item)
all_museum_items.append(mineral_item)
return mineral_item
class Artifact:
- dwarf_scroll_i = create_artifact("Dwarf Scroll I", 96, 5.6, Region.mines_floor_20,
+ dwarf_scroll_i = create_artifact("Dwarf Scroll I", 5.6, Region.mines_floor_20,
monsters=unlikely)
- dwarf_scroll_ii = create_artifact("Dwarf Scroll II", 97, 3, Region.mines_floor_20,
+ dwarf_scroll_ii = create_artifact("Dwarf Scroll II", 3, Region.mines_floor_20,
monsters=unlikely)
- dwarf_scroll_iii = create_artifact("Dwarf Scroll III", 98, 7.5, Region.mines_floor_60,
+ dwarf_scroll_iii = create_artifact("Dwarf Scroll III", 7.5, Region.mines_floor_60,
monsters=Monster.blue_slime)
- dwarf_scroll_iv = create_artifact("Dwarf Scroll IV", 99, 4, Region.mines_floor_100)
- chipped_amphora = create_artifact("Chipped Amphora", 100, 6.7, Region.town,
+ dwarf_scroll_iv = create_artifact("Dwarf Scroll IV", 4, Region.mines_floor_100)
+ chipped_amphora = create_artifact("Chipped Amphora", 6.7, Region.town,
geodes=Geode.artifact_trove)
- arrowhead = create_artifact("Arrowhead", 101, 8.5, (Region.mountain, Region.forest, Region.bus_stop),
+ arrowhead = create_artifact("Arrowhead", 8.5, (Region.mountain, Region.forest, Region.bus_stop),
geodes=Geode.artifact_trove)
- ancient_doll = create_artifact("Ancient Doll", 103, 13.1, (Region.mountain, Region.forest, Region.bus_stop),
- geodes=(Geode.artifact_trove, common.fishing_chest))
- elvish_jewelry = create_artifact("Elvish Jewelry", 104, 5.3, Region.forest,
- geodes=(Geode.artifact_trove, common.fishing_chest))
- chewing_stick = create_artifact("Chewing Stick", 105, 10.3, (Region.mountain, Region.forest, Region.town),
- geodes=(Geode.artifact_trove, common.fishing_chest))
- ornamental_fan = create_artifact("Ornamental Fan", 106, 7.4, (Region.beach, Region.forest, Region.town),
- geodes=(Geode.artifact_trove, common.fishing_chest))
- dinosaur_egg = create_artifact("Dinosaur Egg", 107, 11.4, (Region.mountain, Region.skull_cavern),
- geodes=common.fishing_chest,
+ ancient_doll = create_artifact("Ancient Doll", 13.1, (Region.mountain, Region.forest, Region.bus_stop),
+ geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
+ elvish_jewelry = create_artifact("Elvish Jewelry", 5.3, Region.forest,
+ geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
+ chewing_stick = create_artifact("Chewing Stick", 10.3, (Region.mountain, Region.forest, Region.town),
+ geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
+ ornamental_fan = create_artifact("Ornamental Fan", 7.4, (Region.beach, Region.forest, Region.town),
+ geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
+ dinosaur_egg = create_artifact("Dinosaur Egg", 11.4, (Region.mountain, Region.skull_cavern),
+ geodes=WaterChest.fishing_chest,
monsters=Monster.pepper_rex)
- rare_disc = create_artifact("Rare Disc", 108, 5.6, Region.stardew_valley,
- geodes=(Geode.artifact_trove, common.fishing_chest),
+ rare_disc = create_artifact("Rare Disc", 5.6, Region.stardew_valley,
+ geodes=(Geode.artifact_trove, WaterChest.fishing_chest),
monsters=unlikely)
- ancient_sword = create_artifact("Ancient Sword", 109, 5.8, (Region.forest, Region.mountain),
- geodes=(Geode.artifact_trove, common.fishing_chest))
- rusty_spoon = create_artifact("Rusty Spoon", 110, 9.6, Region.town,
- geodes=(Geode.artifact_trove, common.fishing_chest))
- rusty_spur = create_artifact("Rusty Spur", 111, 15.6, Region.farm,
- geodes=(Geode.artifact_trove, common.fishing_chest))
- rusty_cog = create_artifact("Rusty Cog", 112, 9.6, Region.mountain,
- geodes=(Geode.artifact_trove, common.fishing_chest))
- chicken_statue = create_artifact("Chicken Statue", 113, 13.5, Region.farm,
- geodes=(Geode.artifact_trove, common.fishing_chest))
- ancient_seed = create_artifact("Ancient Seed", 114, 8.4, (Region.forest, Region.mountain),
- geodes=(Geode.artifact_trove, common.fishing_chest),
+ ancient_sword = create_artifact("Ancient Sword", 5.8, (Region.forest, Region.mountain),
+ geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
+ rusty_spoon = create_artifact("Rusty Spoon", 9.6, Region.town,
+ geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
+ rusty_spur = create_artifact("Rusty Spur", 15.6, Region.farm,
+ geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
+ rusty_cog = create_artifact("Rusty Cog", 9.6, Region.mountain,
+ geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
+ chicken_statue = create_artifact("Chicken Statue", 13.5, Region.farm,
+ geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
+ ancient_seed = create_artifact("Ancient Seed", 8.4, (Region.forest, Region.mountain),
+ geodes=(Geode.artifact_trove, WaterChest.fishing_chest),
monsters=unlikely)
- prehistoric_tool = create_artifact("Prehistoric Tool", 115, 11.1, (Region.mountain, Region.forest, Region.bus_stop),
- geodes=(Geode.artifact_trove, common.fishing_chest))
- dried_starfish = create_artifact("Dried Starfish", 116, 12.5, Region.beach,
- geodes=(Geode.artifact_trove, common.fishing_chest))
- anchor = create_artifact("Anchor", 117, 8.5, Region.beach, geodes=(Geode.artifact_trove, common.fishing_chest))
- glass_shards = create_artifact("Glass Shards", 118, 11.5, Region.beach,
- geodes=(Geode.artifact_trove, common.fishing_chest))
- bone_flute = create_artifact("Bone Flute", 119, 6.3, (Region.mountain, Region.forest, Region.town),
- geodes=(Geode.artifact_trove, common.fishing_chest))
- prehistoric_handaxe = create_artifact("Prehistoric Handaxe", 120, 13.7,
+ prehistoric_tool = create_artifact("Prehistoric Tool", 11.1, (Region.mountain, Region.forest, Region.bus_stop),
+ geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
+ dried_starfish = create_artifact("Dried Starfish", 12.5, Region.beach,
+ geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
+ anchor = create_artifact("Anchor", 8.5, Region.beach, geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
+ glass_shards = create_artifact("Glass Shards", 11.5, Region.beach,
+ geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
+ bone_flute = create_artifact("Bone Flute", 6.3, (Region.mountain, Region.forest, Region.town),
+ geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
+ prehistoric_handaxe = create_artifact(Artifact.prehistoric_handaxe, 13.7,
(Region.mountain, Region.forest, Region.bus_stop),
geodes=Geode.artifact_trove)
- dwarvish_helm = create_artifact("Dwarvish Helm", 121, 8.7, Region.mines_floor_20,
+ dwarvish_helm = create_artifact("Dwarvish Helm", 8.7, Region.mines_floor_20,
geodes=(Geode.geode, Geode.omni, Geode.artifact_trove))
- dwarf_gadget = create_artifact("Dwarf Gadget", 122, 9.7, Region.mines_floor_60,
+ dwarf_gadget = create_artifact("Dwarf Gadget", 9.7, Region.mines_floor_60,
geodes=(Geode.magma, Geode.omni, Geode.artifact_trove))
- ancient_drum = create_artifact("Ancient Drum", 123, 9.5, (Region.bus_stop, Region.forest, Region.town),
+ ancient_drum = create_artifact("Ancient Drum", 9.5, (Region.bus_stop, Region.forest, Region.town),
geodes=(Geode.frozen, Geode.omni, Geode.artifact_trove))
- golden_mask = create_artifact("Golden Mask", 124, 6.7, Region.desert,
+ golden_mask = create_artifact("Golden Mask", 6.7, Region.desert,
geodes=Geode.artifact_trove)
- golden_relic = create_artifact("Golden Relic", 125, 9.7, Region.desert,
+ golden_relic = create_artifact("Golden Relic", 9.7, Region.desert,
geodes=Geode.artifact_trove)
- strange_doll_green = create_artifact("Strange Doll (Green)", 126, 10, Region.town,
- geodes=common.secret_note)
- strange_doll = create_artifact("Strange Doll", 127, 10, Region.desert,
- geodes=common.secret_note)
- prehistoric_scapula = create_artifact("Prehistoric Scapula", 579, 6.2,
+ strange_doll_green = create_artifact("Strange Doll (Green)", 10, Region.town,
+ geodes=Forageable.secret_note)
+ strange_doll = create_artifact("Strange Doll", 10, Region.desert,
+ geodes=Forageable.secret_note)
+ prehistoric_scapula = create_artifact("Prehistoric Scapula", 6.2,
(Region.dig_site, Region.forest, Region.town))
- prehistoric_tibia = create_artifact("Prehistoric Tibia", 580, 16.6,
+ prehistoric_tibia = create_artifact("Prehistoric Tibia", 16.6,
(Region.dig_site, Region.forest, Region.railroad))
- prehistoric_skull = create_artifact("Prehistoric Skull", 581, 3.9, (Region.dig_site, Region.mountain))
- skeletal_hand = create_artifact("Skeletal Hand", 582, 7.9, (Region.dig_site, Region.backwoods, Region.beach))
- prehistoric_rib = create_artifact("Prehistoric Rib", 583, 15, (Region.dig_site, Region.farm, Region.town),
+ prehistoric_skull = create_artifact("Prehistoric Skull", 3.9, (Region.dig_site, Region.mountain))
+ skeletal_hand = create_artifact(Fossil.skeletal_hand, 7.9, (Region.dig_site, Region.backwoods, Region.beach))
+ prehistoric_rib = create_artifact("Prehistoric Rib", 15, (Region.dig_site, Region.farm, Region.town),
monsters=Monster.pepper_rex)
- prehistoric_vertebra = create_artifact("Prehistoric Vertebra", 584, 12.7, (Region.dig_site, Region.bus_stop),
+ prehistoric_vertebra = create_artifact("Prehistoric Vertebra", 12.7, (Region.dig_site, Region.bus_stop),
monsters=Monster.pepper_rex)
- skeletal_tail = create_artifact("Skeletal Tail", 585, 5.1, (Region.dig_site, Region.mines_floor_20),
- geodes=common.fishing_chest)
- nautilus_fossil = create_artifact("Nautilus Fossil", 586, 6.9, (Region.dig_site, Region.beach),
- geodes=common.fishing_chest)
- amphibian_fossil = create_artifact("Amphibian Fossil", 587, 6.3, (Region.dig_site, Region.forest, Region.mountain),
- geodes=common.fishing_chest)
- palm_fossil = create_artifact("Palm Fossil", 588, 10.2,
+ skeletal_tail = create_artifact("Skeletal Tail", 5.1, (Region.dig_site, Region.mines_floor_20),
+ geodes=WaterChest.fishing_chest)
+ nautilus_fossil = create_artifact("Nautilus Fossil", 6.9, (Region.dig_site, Region.beach),
+ geodes=WaterChest.fishing_chest)
+ amphibian_fossil = create_artifact("Amphibian Fossil", 6.3, (Region.dig_site, Region.forest, Region.mountain),
+ geodes=WaterChest.fishing_chest)
+ palm_fossil = create_artifact("Palm Fossil", 10.2,
(Region.dig_site, Region.desert, Region.forest, Region.beach))
- trilobite = create_artifact("Trilobite", 589, 7.4, (Region.dig_site, Region.desert, Region.forest, Region.beach))
+ trilobite = create_artifact("Trilobite", 7.4, (Region.dig_site, Region.desert, Region.forest, Region.beach))
class Mineral:
- quartz = create_mineral("Quartz", 80, Region.mines_floor_20,
- monsters=Monster.stone_golem)
- fire_quartz = create_mineral("Fire Quartz", 82, Region.mines_floor_100,
- geodes=(Geode.magma, Geode.omni, common.fishing_chest),
+ quartz = create_mineral(Mineral.quartz, Region.mines_floor_20)
+ fire_quartz = create_mineral("Fire Quartz", Region.mines_floor_100,
+ geodes=(Geode.magma, Geode.omni, WaterChest.fishing_chest),
difficulty=1.0 / 12.0)
- frozen_tear = create_mineral("Frozen Tear", 84, Region.mines_floor_60,
- geodes=(Geode.frozen, Geode.omni, common.fishing_chest),
+ frozen_tear = create_mineral("Frozen Tear", Region.mines_floor_60,
+ geodes=(Geode.frozen, Geode.omni, WaterChest.fishing_chest),
monsters=unlikely,
difficulty=1.0 / 12.0)
- earth_crystal = create_mineral("Earth Crystal", 86, Region.mines_floor_20,
- geodes=(Geode.geode, Geode.omni, common.fishing_chest),
+ earth_crystal = create_mineral("Earth Crystal", Region.mines_floor_20,
+ geodes=(Geode.geode, Geode.omni, WaterChest.fishing_chest),
monsters=Monster.duggy,
difficulty=1.0 / 12.0)
- emerald = create_mineral("Emerald", 60, Region.mines_floor_100,
- geodes=common.fishing_chest)
- aquamarine = create_mineral("Aquamarine", 62, Region.mines_floor_60,
- geodes=common.fishing_chest)
- ruby = create_mineral("Ruby", 64, Region.mines_floor_100,
- geodes=common.fishing_chest)
- amethyst = create_mineral("Amethyst", 66, Region.mines_floor_20,
- geodes=common.fishing_chest)
- topaz = create_mineral("Topaz", 68, Region.mines_floor_20,
- geodes=common.fishing_chest)
- jade = create_mineral("Jade", 70, Region.mines_floor_60,
- geodes=common.fishing_chest)
- diamond = create_mineral("Diamond", 72, Region.mines_floor_60,
- geodes=common.fishing_chest)
- prismatic_shard = create_mineral("Prismatic Shard", 74, Region.skull_cavern_100,
+ emerald = create_mineral("Emerald", Region.mines_floor_100,
+ geodes=WaterChest.fishing_chest)
+ aquamarine = create_mineral("Aquamarine", Region.mines_floor_60,
+ geodes=WaterChest.fishing_chest)
+ ruby = create_mineral("Ruby", Region.mines_floor_100,
+ geodes=WaterChest.fishing_chest)
+ amethyst = create_mineral("Amethyst", Region.mines_floor_20,
+ geodes=WaterChest.fishing_chest)
+ topaz = create_mineral("Topaz", Region.mines_floor_20,
+ geodes=WaterChest.fishing_chest)
+ jade = create_mineral("Jade", Region.mines_floor_60,
+ geodes=WaterChest.fishing_chest)
+ diamond = create_mineral("Diamond", Region.mines_floor_60,
+ geodes=WaterChest.fishing_chest)
+ prismatic_shard = create_mineral("Prismatic Shard", Region.skull_cavern_100,
geodes=unlikely,
monsters=unlikely)
- alamite = create_mineral("Alamite", 538, Region.town,
+ alamite = create_mineral("Alamite",
geodes=(Geode.geode, Geode.omni))
- bixite = create_mineral("Bixite", 539, Region.town,
+ bixite = create_mineral("Bixite",
geodes=(Geode.magma, Geode.omni),
monsters=unlikely)
- baryte = create_mineral("Baryte", 540, Region.town,
+ baryte = create_mineral("Baryte",
geodes=(Geode.magma, Geode.omni))
- aerinite = create_mineral("Aerinite", 541, Region.town,
+ aerinite = create_mineral("Aerinite",
geodes=(Geode.frozen, Geode.omni))
- calcite = create_mineral("Calcite", 542, Region.town,
+ calcite = create_mineral("Calcite",
geodes=(Geode.geode, Geode.omni))
- dolomite = create_mineral("Dolomite", 543, Region.town,
+ dolomite = create_mineral("Dolomite",
geodes=(Geode.magma, Geode.omni))
- esperite = create_mineral("Esperite", 544, Region.town,
+ esperite = create_mineral("Esperite",
geodes=(Geode.frozen, Geode.omni))
- fluorapatite = create_mineral("Fluorapatite", 545, Region.town,
+ fluorapatite = create_mineral("Fluorapatite",
geodes=(Geode.frozen, Geode.omni))
- geminite = create_mineral("Geminite", 546, Region.town,
+ geminite = create_mineral("Geminite",
geodes=(Geode.frozen, Geode.omni))
- helvite = create_mineral("Helvite", 547, Region.town,
+ helvite = create_mineral("Helvite",
geodes=(Geode.magma, Geode.omni))
- jamborite = create_mineral("Jamborite", 548, Region.town,
+ jamborite = create_mineral("Jamborite",
geodes=(Geode.geode, Geode.omni))
- jagoite = create_mineral("Jagoite", 549, Region.town,
+ jagoite = create_mineral("Jagoite",
geodes=(Geode.geode, Geode.omni))
- kyanite = create_mineral("Kyanite", 550, Region.town,
+ kyanite = create_mineral("Kyanite",
geodes=(Geode.frozen, Geode.omni))
- lunarite = create_mineral("Lunarite", 551, Region.town,
+ lunarite = create_mineral("Lunarite",
geodes=(Geode.frozen, Geode.omni))
- malachite = create_mineral("Malachite", 552, Region.town,
+ malachite = create_mineral("Malachite",
geodes=(Geode.geode, Geode.omni))
- neptunite = create_mineral("Neptunite", 553, Region.town,
+ neptunite = create_mineral("Neptunite",
geodes=(Geode.magma, Geode.omni))
- lemon_stone = create_mineral("Lemon Stone", 554, Region.town,
+ lemon_stone = create_mineral("Lemon Stone",
geodes=(Geode.magma, Geode.omni))
- nekoite = create_mineral("Nekoite", 555, Region.town,
+ nekoite = create_mineral("Nekoite",
geodes=(Geode.geode, Geode.omni))
- orpiment = create_mineral("Orpiment", 556, Region.town,
+ orpiment = create_mineral("Orpiment",
geodes=(Geode.geode, Geode.omni))
- petrified_slime = create_mineral("Petrified Slime", 557, Region.town,
- geodes=(Geode.geode, Geode.omni))
- thunder_egg = create_mineral("Thunder Egg", 558, Region.town,
+ petrified_slime = create_mineral(Mineral.petrified_slime, Region.slime_hutch)
+ thunder_egg = create_mineral("Thunder Egg",
geodes=(Geode.geode, Geode.omni))
- pyrite = create_mineral("Pyrite", 559, Region.town,
+ pyrite = create_mineral("Pyrite",
geodes=(Geode.frozen, Geode.omni))
- ocean_stone = create_mineral("Ocean Stone", 560, Region.town,
+ ocean_stone = create_mineral("Ocean Stone",
geodes=(Geode.frozen, Geode.omni))
- ghost_crystal = create_mineral("Ghost Crystal", 561, Region.town,
+ ghost_crystal = create_mineral("Ghost Crystal",
geodes=(Geode.frozen, Geode.omni))
- tigerseye = create_mineral("Tigerseye", 562, Region.town,
+ tigerseye = create_mineral("Tigerseye",
geodes=(Geode.magma, Geode.omni))
- jasper = create_mineral("Jasper", 563, Region.town,
+ jasper = create_mineral("Jasper",
geodes=(Geode.magma, Geode.omni))
- opal = create_mineral("Opal", 564, Region.town,
+ opal = create_mineral("Opal",
geodes=(Geode.frozen, Geode.omni))
- fire_opal = create_mineral("Fire Opal", 565, Region.town,
+ fire_opal = create_mineral("Fire Opal",
geodes=(Geode.magma, Geode.omni))
- celestine = create_mineral("Celestine", 566, Region.town,
+ celestine = create_mineral("Celestine",
geodes=(Geode.geode, Geode.omni))
- marble = create_mineral("Marble", 567, Region.town,
+ marble = create_mineral("Marble",
geodes=(Geode.frozen, Geode.omni))
- sandstone = create_mineral("Sandstone", 568, Region.town,
+ sandstone = create_mineral("Sandstone",
geodes=(Geode.geode, Geode.omni))
- granite = create_mineral("Granite", 569, Region.town,
+ granite = create_mineral("Granite",
geodes=(Geode.geode, Geode.omni))
- basalt = create_mineral("Basalt", 570, Region.town,
+ basalt = create_mineral("Basalt",
geodes=(Geode.magma, Geode.omni))
- limestone = create_mineral("Limestone", 571, Region.town,
+ limestone = create_mineral("Limestone",
geodes=(Geode.geode, Geode.omni))
- soapstone = create_mineral("Soapstone", 572, Region.town,
+ soapstone = create_mineral("Soapstone",
geodes=(Geode.frozen, Geode.omni))
- hematite = create_mineral("Hematite", 573, Region.town,
+ hematite = create_mineral("Hematite",
geodes=(Geode.frozen, Geode.omni))
- mudstone = create_mineral("Mudstone", 574, Region.town,
+ mudstone = create_mineral("Mudstone",
geodes=(Geode.geode, Geode.omni))
- obsidian = create_mineral("Obsidian", 575, Region.town,
+ obsidian = create_mineral("Obsidian",
geodes=(Geode.magma, Geode.omni))
- slate = create_mineral("Slate", 576, Region.town,
- geodes=(Geode.geode, Geode.omni))
- fairy_stone = create_mineral("Fairy Stone", 577, Region.town,
- geodes=(Geode.frozen, Geode.omni))
- star_shards = create_mineral("Star Shards", 578, Region.town,
- geodes=(Geode.magma, Geode.omni))
+ slate = create_mineral("Slate", geodes=(Geode.geode, Geode.omni))
+ fairy_stone = create_mineral("Fairy Stone", geodes=(Geode.frozen, Geode.omni))
+ star_shards = create_mineral("Star Shards", geodes=(Geode.magma, Geode.omni))
dwarf_scrolls = (Artifact.dwarf_scroll_i, Artifact.dwarf_scroll_ii, Artifact.dwarf_scroll_iii, Artifact.dwarf_scroll_iv)
@@ -291,4 +285,4 @@ class Mineral:
skeleton_middle = (Artifact.prehistoric_rib, Artifact.prehistoric_vertebra)
skeleton_back = (Artifact.prehistoric_tibia, Artifact.skeletal_tail)
-all_museum_items_by_name = {item.name: item for item in all_museum_items}
+all_museum_items_by_name = {item.item_name: item for item in all_museum_items}
diff --git a/worlds/stardew_valley/data/recipe_data.py b/worlds/stardew_valley/data/recipe_data.py
index dc1b490bf93c..62dcd8709c64 100644
--- a/worlds/stardew_valley/data/recipe_data.py
+++ b/worlds/stardew_valley/data/recipe_data.py
@@ -1,78 +1,35 @@
-from typing import Dict, List
-
+from typing import Dict, List, Optional
+from ..mods.mod_data import ModNames
+from .recipe_source import RecipeSource, FriendshipSource, SkillSource, QueenOfSauceSource, ShopSource, StarterSource, ShopTradeSource, ShopFriendshipSource
from ..strings.animal_product_names import AnimalProduct
from ..strings.artisan_good_names import ArtisanGood
-from ..strings.crop_names import Fruit, Vegetable
-from ..strings.fish_names import Fish, WaterItem
+from ..strings.craftable_names import ModEdible, Edible
+from ..strings.crop_names import Fruit, Vegetable, SVEFruit, DistantLandsCrop
+from ..strings.fish_names import Fish, SVEFish, WaterItem, DistantLandsFish
from ..strings.flower_names import Flower
-from ..strings.forageable_names import Forageable
+from ..strings.forageable_names import Forageable, SVEForage, DistantLandsForageable
from ..strings.ingredient_names import Ingredient
-from ..strings.food_names import Meal, Beverage
-from ..strings.region_names import Region
+from ..strings.food_names import Meal, SVEMeal, Beverage, DistantLandsMeal, BoardingHouseMeal
+from ..strings.material_names import Material
+from ..strings.metal_names import Fossil
+from ..strings.monster_drop_names import Loot
+from ..strings.region_names import Region, SVERegion
from ..strings.season_names import Season
from ..strings.skill_names import Skill
-from ..strings.villager_names import NPC
-
-
-class RecipeSource:
- pass
-
-
-class StarterSource(RecipeSource):
- pass
-
-
-class QueenOfSauceSource(RecipeSource):
- year: int
- season: str
- day: int
-
- def __init__(self, year: int, season: str, day: int):
- self.year = year
- self.season = season
- self.day = day
-
-
-class FriendshipSource(RecipeSource):
- friend: str
- hearts: int
-
- def __init__(self, friend: str, hearts: int):
- self.friend = friend
- self.hearts = hearts
-
-
-class SkillSource(RecipeSource):
- skill: str
- level: int
-
- def __init__(self, skill: str, level: int):
- self.skill = skill
- self.level = level
-
-
-class ShopSource(RecipeSource):
- region: str
- price: int
-
- def __init__(self, region: str, price: int):
- self.region = region
- self.price = price
-
-
-class ShopTradeSource(ShopSource):
- currency: str
+from ..strings.villager_names import NPC, ModNPC
class CookingRecipe:
meal: str
ingredients: Dict[str, int]
source: RecipeSource
+ mod_name: Optional[str] = None
- def __init__(self, meal: str, ingredients: Dict[str, int], source: RecipeSource):
+ def __init__(self, meal: str, ingredients: Dict[str, int], source: RecipeSource, mod_name: Optional[str] = None):
self.meal = meal
self.ingredients = ingredients
self.source = source
+ self.mod_name = mod_name
def __repr__(self):
return f"{self.meal} (Source: {self.source} |" \
@@ -82,9 +39,14 @@ def __repr__(self):
all_cooking_recipes: List[CookingRecipe] = []
-def friendship_recipe(name: str, friend: str, hearts: int, ingredients: Dict[str, int]) -> CookingRecipe:
+def friendship_recipe(name: str, friend: str, hearts: int, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CookingRecipe:
source = FriendshipSource(friend, hearts)
- return create_recipe(name, ingredients, source)
+ return create_recipe(name, ingredients, source, mod_name)
+
+
+def friendship_and_shop_recipe(name: str, friend: str, hearts: int, region: str, price: int, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CookingRecipe:
+ source = ShopFriendshipSource(friend, hearts, region, price)
+ return create_recipe(name, ingredients, source, mod_name)
def skill_recipe(name: str, skill: str, level: int, ingredients: Dict[str, int]) -> CookingRecipe:
@@ -92,8 +54,13 @@ def skill_recipe(name: str, skill: str, level: int, ingredients: Dict[str, int])
return create_recipe(name, ingredients, source)
-def shop_recipe(name: str, region: str, price: int, ingredients: Dict[str, int]) -> CookingRecipe:
+def shop_recipe(name: str, region: str, price: int, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CookingRecipe:
source = ShopSource(region, price)
+ return create_recipe(name, ingredients, source, mod_name)
+
+
+def shop_trade_recipe(name: str, region: str, currency: str, price: int, ingredients: Dict[str, int]) -> CookingRecipe:
+ source = ShopTradeSource(region, currency, price)
return create_recipe(name, ingredients, source)
@@ -107,34 +74,44 @@ def starter_recipe(name: str, ingredients: Dict[str, int]) -> CookingRecipe:
return create_recipe(name, ingredients, source)
-def create_recipe(name: str, ingredients: Dict[str, int], source: RecipeSource) -> CookingRecipe:
- recipe = CookingRecipe(name, ingredients, source)
+def create_recipe(name: str, ingredients: Dict[str, int], source: RecipeSource, mod_name: Optional[str] = None) -> CookingRecipe:
+ recipe = CookingRecipe(name, ingredients, source, mod_name)
all_cooking_recipes.append(recipe)
return recipe
algae_soup = friendship_recipe(Meal.algae_soup, NPC.clint, 3, {WaterItem.green_algae: 4})
artichoke_dip = queen_of_sauce_recipe(Meal.artichoke_dip, 1, Season.fall, 28, {Vegetable.artichoke: 1, AnimalProduct.cow_milk: 1})
+autumn_bounty = friendship_recipe(Meal.autumn_bounty, NPC.demetrius, 7, {Vegetable.yam: 1, Vegetable.pumpkin: 1})
baked_fish = queen_of_sauce_recipe(Meal.baked_fish, 1, Season.summer, 7, {Fish.sunfish: 1, Fish.bream: 1, Ingredient.wheat_flour: 1})
+banana_pudding = shop_trade_recipe(Meal.banana_pudding, Region.island_trader, Fossil.bone_fragment, 30, {Fruit.banana: 1, AnimalProduct.cow_milk: 1, Ingredient.sugar: 1})
bean_hotpot = friendship_recipe(Meal.bean_hotpot, NPC.clint, 7, {Vegetable.green_bean: 2})
blackberry_cobbler_ingredients = {Forageable.blackberry: 2, Ingredient.sugar: 1, Ingredient.wheat_flour: 1}
blackberry_cobbler_qos = queen_of_sauce_recipe(Meal.blackberry_cobbler, 2, Season.fall, 14, blackberry_cobbler_ingredients)
-blueberry_tart = friendship_recipe(Meal.blueberry_tart, NPC.pierre, 3, {Fruit.blueberry: 1, Ingredient.wheat_flour: 1, Ingredient.sugar: 1, AnimalProduct.any_egg: 1})
+blueberry_tart_ingredients = {Fruit.blueberry: 1, Ingredient.wheat_flour: 1, Ingredient.sugar: 1, AnimalProduct.any_egg: 1}
+blueberry_tart = friendship_recipe(Meal.blueberry_tart, NPC.pierre, 3, blueberry_tart_ingredients)
bread = queen_of_sauce_recipe(Meal.bread, 1, Season.summer, 28, {Ingredient.wheat_flour: 1})
+bruschetta = queen_of_sauce_recipe(Meal.bruschetta, 2, Season.winter, 21, {Meal.bread: 1, Ingredient.oil: 1, Vegetable.tomato: 1})
+carp_surprise = queen_of_sauce_recipe(Meal.carp_surprise, 2, Season.summer, 7, {Fish.carp: 4})
cheese_cauliflower = friendship_recipe(Meal.cheese_cauliflower, NPC.pam, 3, {Vegetable.cauliflower: 1, ArtisanGood.cheese: 1})
chocolate_cake_ingredients = {Ingredient.wheat_flour: 1, Ingredient.sugar: 1, AnimalProduct.chicken_egg: 1}
chocolate_cake_qos = queen_of_sauce_recipe(Meal.chocolate_cake, 1, Season.winter, 14, chocolate_cake_ingredients)
-chowder = friendship_recipe(Meal.chowder, NPC.willy, 3, {WaterItem.clam: 1, AnimalProduct.cow_milk: 1})
-complete_breakfast = queen_of_sauce_recipe(Meal.complete_breakfast, 2, Season.spring, 21, {Meal.fried_egg: 1, AnimalProduct.milk: 1, Meal.hashbrowns: 1, Meal.pancakes: 1})
+chowder = friendship_recipe(Meal.chowder, NPC.willy, 3, {Fish.clam: 1, AnimalProduct.cow_milk: 1})
+coleslaw = queen_of_sauce_recipe(Meal.coleslaw, 14, Season.spring, 14, {Vegetable.red_cabbage: 1, Ingredient.vinegar: 1, ArtisanGood.mayonnaise: 1})
+complete_breakfast_ingredients = {Meal.fried_egg: 1, AnimalProduct.milk: 1, Meal.hashbrowns: 1, Meal.pancakes: 1}
+complete_breakfast = queen_of_sauce_recipe(Meal.complete_breakfast, 2, Season.spring, 21, complete_breakfast_ingredients)
+cookie = friendship_recipe(Meal.cookie, NPC.evelyn, 4, {Ingredient.wheat_flour: 1, Ingredient.sugar: 1, AnimalProduct.chicken_egg: 1})
crab_cakes_ingredients = {Fish.crab: 1, Ingredient.wheat_flour: 1, AnimalProduct.chicken_egg: 1, Ingredient.oil: 1}
crab_cakes_qos = queen_of_sauce_recipe(Meal.crab_cakes, 2, Season.fall, 21, crab_cakes_ingredients)
cranberry_candy = queen_of_sauce_recipe(Meal.cranberry_candy, 1, Season.winter, 28, {Fruit.cranberries: 1, Fruit.apple: 1, Ingredient.sugar: 1})
+cranberry_sauce = friendship_recipe(Meal.cranberry_sauce, NPC.gus, 7, {Fruit.cranberries: 1, Ingredient.sugar: 1})
crispy_bass = friendship_recipe(Meal.crispy_bass, NPC.kent, 3, {Fish.largemouth_bass: 1, Ingredient.wheat_flour: 1, Ingredient.oil: 1})
dish_o_the_sea = skill_recipe(Meal.dish_o_the_sea, Skill.fishing, 3, {Fish.sardine: 2, Meal.hashbrowns: 1})
eggplant_parmesan = friendship_recipe(Meal.eggplant_parmesan, NPC.lewis, 7, {Vegetable.eggplant: 1, Vegetable.tomato: 1})
escargot = friendship_recipe(Meal.escargot, NPC.willy, 5, {Fish.snail: 1, Vegetable.garlic: 1})
farmer_lunch = skill_recipe(Meal.farmer_lunch, Skill.farming, 3, {Meal.omelet: 2, Vegetable.parsnip: 1})
fiddlehead_risotto = queen_of_sauce_recipe(Meal.fiddlehead_risotto, 2, Season.fall, 28, {Ingredient.oil: 1, Forageable.fiddlehead_fern: 1, Vegetable.garlic: 1})
+fish_stew = friendship_recipe(Meal.fish_stew, NPC.willy, 7, {Fish.crayfish: 1, Fish.mussel: 1, Fish.periwinkle: 1, Vegetable.tomato: 1})
fish_taco = friendship_recipe(Meal.fish_taco, NPC.linus, 7, {Fish.tuna: 1, Meal.tortilla: 1, Vegetable.red_cabbage: 1, ArtisanGood.mayonnaise: 1})
fried_calamari = friendship_recipe(Meal.fried_calamari, NPC.jodi, 3, {Fish.squid: 1, Ingredient.wheat_flour: 1, Ingredient.oil: 1})
fried_eel = friendship_recipe(Meal.fried_eel, NPC.george, 3, {Fish.eel: 1, Ingredient.oil: 1})
@@ -145,7 +122,12 @@ def create_recipe(name: str, ingredients: Dict[str, int], source: RecipeSource)
glazed_yams = queen_of_sauce_recipe(Meal.glazed_yams, 1, Season.fall, 21, {Vegetable.yam: 1, Ingredient.sugar: 1})
hashbrowns = queen_of_sauce_recipe(Meal.hashbrowns, 2, Season.spring, 14, {Vegetable.potato: 1, Ingredient.oil: 1})
ice_cream = friendship_recipe(Meal.ice_cream, NPC.jodi, 7, {AnimalProduct.cow_milk: 1, Ingredient.sugar: 1})
+lobster_bisque_ingredients = {Fish.lobster: 1, AnimalProduct.cow_milk: 1}
+lobster_bisque_friend = friendship_recipe(Meal.lobster_bisque, NPC.willy, 9, lobster_bisque_ingredients)
+lobster_bisque_qos = queen_of_sauce_recipe(Meal.lobster_bisque, 2, Season.winter, 14, lobster_bisque_ingredients)
+lucky_lunch = queen_of_sauce_recipe(Meal.lucky_lunch, 2, Season.spring, 28, {Fish.sea_cucumber: 1, Meal.tortilla: 1, Flower.blue_jazz: 1})
maki_roll = queen_of_sauce_recipe(Meal.maki_roll, 1, Season.summer, 21, {Fish.any: 1, WaterItem.seaweed: 1, Ingredient.rice: 1})
+mango_sticky_rice = friendship_recipe(Meal.mango_sticky_rice, NPC.leo, 7, {Fruit.mango: 1, Forageable.coconut: 1, Ingredient.rice: 1})
maple_bar = queen_of_sauce_recipe(Meal.maple_bar, 2, Season.summer, 14, {ArtisanGood.maple_syrup: 1, Ingredient.sugar: 1, Ingredient.wheat_flour: 1})
miners_treat = skill_recipe(Meal.miners_treat, Skill.mining, 3, {Forageable.cave_carrot: 2, Ingredient.sugar: 1, AnimalProduct.cow_milk: 1})
omelet = queen_of_sauce_recipe(Meal.omelet, 1, Season.spring, 28, {AnimalProduct.chicken_egg: 1, AnimalProduct.cow_milk: 1})
@@ -159,9 +141,12 @@ def create_recipe(name: str, ingredients: Dict[str, int], source: RecipeSource)
pizza_qos = queen_of_sauce_recipe(Meal.pizza, 2, Season.spring, 7, pizza_ingredients)
pizza_saloon = shop_recipe(Meal.pizza, Region.saloon, 150, pizza_ingredients)
plum_pudding = queen_of_sauce_recipe(Meal.plum_pudding, 1, Season.winter, 7, {Forageable.wild_plum: 2, Ingredient.wheat_flour: 1, Ingredient.sugar: 1})
+poi = friendship_recipe(Meal.poi, NPC.leo, 3, {Vegetable.taro_root: 4})
poppyseed_muffin = queen_of_sauce_recipe(Meal.poppyseed_muffin, 2, Season.winter, 7, {Flower.poppy: 1, Ingredient.wheat_flour: 1, Ingredient.sugar: 1})
pumpkin_pie_ingredients = {Vegetable.pumpkin: 1, Ingredient.wheat_flour: 1, Ingredient.sugar: 1, AnimalProduct.cow_milk: 1}
pumpkin_pie_qos = queen_of_sauce_recipe(Meal.pumpkin_pie, 1, Season.winter, 21, pumpkin_pie_ingredients)
+pumpkin_soup = friendship_recipe(Meal.pumpkin_soup, NPC.robin, 7, {Vegetable.pumpkin: 1, AnimalProduct.cow_milk: 1})
+radish_salad = queen_of_sauce_recipe(Meal.radish_salad, 1, Season.spring, 21, {Ingredient.oil: 1, Ingredient.vinegar: 1, Vegetable.radish: 1})
red_plate = friendship_recipe(Meal.red_plate, NPC.emily, 7, {Vegetable.red_cabbage: 1, Vegetable.radish: 1})
rhubarb_pie = friendship_recipe(Meal.rhubarb_pie, NPC.marnie, 7, {Fruit.rhubarb: 1, Ingredient.wheat_flour: 1, Ingredient.sugar: 1})
rice_pudding = friendship_recipe(Meal.rice_pudding, NPC.evelyn, 7, {AnimalProduct.milk: 1, Ingredient.sugar: 1, Ingredient.rice: 1})
@@ -170,21 +155,59 @@ def create_recipe(name: str, ingredients: Dict[str, int], source: RecipeSource)
salad = friendship_recipe(Meal.salad, NPC.emily, 3, {Forageable.leek: 1, Forageable.dandelion: 1, Ingredient.vinegar: 1})
salmon_dinner = friendship_recipe(Meal.salmon_dinner, NPC.gus, 3, {Fish.salmon: 1, Vegetable.amaranth: 1, Vegetable.kale: 1})
sashimi = friendship_recipe(Meal.sashimi, NPC.linus, 3, {Fish.any: 1})
+seafoam_pudding = skill_recipe(Meal.seafoam_pudding, Skill.fishing, 9, {Fish.flounder: 1, Fish.midnight_carp: 1, AnimalProduct.squid_ink: 1})
+shrimp_cocktail = queen_of_sauce_recipe(Meal.shrimp_cocktail, 2, Season.winter, 28, {Vegetable.tomato: 1, Fish.shrimp: 1, Forageable.wild_horseradish: 1})
spaghetti = friendship_recipe(Meal.spaghetti, NPC.lewis, 3, {Vegetable.tomato: 1, Ingredient.wheat_flour: 1})
+spicy_eel = friendship_recipe(Meal.spicy_eel, NPC.george, 7, {Fish.eel: 1, Fruit.hot_pepper: 1})
+squid_ink_ravioli = skill_recipe(Meal.squid_ink_ravioli, Skill.combat, 9, {AnimalProduct.squid_ink: 1, Ingredient.wheat_flour: 1, Vegetable.tomato: 1})
stir_fry_ingredients = {Forageable.cave_carrot: 1, Forageable.common_mushroom: 1, Vegetable.kale: 1, Ingredient.sugar: 1}
stir_fry_qos = queen_of_sauce_recipe(Meal.stir_fry, 1, Season.spring, 7, stir_fry_ingredients)
+strange_bun = friendship_recipe(Meal.strange_bun, NPC.shane, 7, {Ingredient.wheat_flour: 1, Fish.periwinkle: 1, ArtisanGood.void_mayonnaise: 1})
stuffing = friendship_recipe(Meal.stuffing, NPC.pam, 7, {Meal.bread: 1, Fruit.cranberries: 1, Forageable.hazelnut: 1})
+super_meal = friendship_recipe(Meal.super_meal, NPC.kent, 7, {Vegetable.bok_choy: 1, Fruit.cranberries: 1, Vegetable.artichoke: 1})
survival_burger = skill_recipe(Meal.survival_burger, Skill.foraging, 2, {Meal.bread: 1, Forageable.cave_carrot: 1, Vegetable.eggplant: 1})
+tom_kha_soup = friendship_recipe(Meal.tom_kha_soup, NPC.sandy, 7, {Forageable.coconut: 1, Fish.shrimp: 1, Forageable.common_mushroom: 1})
tortilla_ingredients = {Vegetable.corn: 1}
tortilla_qos = queen_of_sauce_recipe(Meal.tortilla, 1, Season.fall, 7, tortilla_ingredients)
tortilla_saloon = shop_recipe(Meal.tortilla, Region.saloon, 100, tortilla_ingredients)
triple_shot_espresso = shop_recipe(Beverage.triple_shot_espresso, Region.saloon, 5000, {Beverage.coffee: 3})
tropical_curry = shop_recipe(Meal.tropical_curry, Region.island_resort, 2000, {Forageable.coconut: 1, Fruit.pineapple: 1, Fruit.hot_pepper: 1})
+trout_soup = queen_of_sauce_recipe(Meal.trout_soup, 1, Season.fall, 14, {Fish.rainbow_trout: 1, WaterItem.green_algae: 1})
vegetable_medley = friendship_recipe(Meal.vegetable_medley, NPC.caroline, 7, {Vegetable.tomato: 1, Vegetable.beet: 1})
-
-
-
-
-
-
+magic_elixir = shop_recipe(ModEdible.magic_elixir, Region.adventurer_guild, 3000, {Edible.life_elixir: 1, Forageable.purple_mushroom: 1}, ModNames.magic)
+
+baked_berry_oatmeal = shop_recipe(SVEMeal.baked_berry_oatmeal, SVERegion.bear_shop, 0, {Forageable.salmonberry: 15, Forageable.blackberry: 15,
+ Ingredient.sugar: 1, Ingredient.wheat_flour: 2}, ModNames.sve)
+big_bark_burger = friendship_and_shop_recipe(SVEMeal.big_bark_burger, NPC.gus, 5, Region.saloon, 5500,
+ {SVEFish.puppyfish: 1, Meal.bread: 1, Ingredient.oil: 1}, ModNames.sve)
+flower_cookie = shop_recipe(SVEMeal.flower_cookie, SVERegion.bear_shop, 0, {SVEForage.ferngill_primrose: 1, SVEForage.goldenrod: 1,
+ SVEForage.winter_star_rose: 1, Ingredient.wheat_flour: 1, Ingredient.sugar: 1,
+ AnimalProduct.large_egg: 1}, ModNames.sve)
+frog_legs = shop_recipe(SVEMeal.frog_legs, Region.adventurer_guild, 2000, {SVEFish.frog: 1, Ingredient.oil: 1, Ingredient.wheat_flour: 1}, ModNames.sve)
+glazed_butterfish = friendship_and_shop_recipe(SVEMeal.glazed_butterfish, NPC.gus, 10, Region.saloon, 4000,
+ {SVEFish.butterfish: 1, Ingredient.wheat_flour: 1, Ingredient.oil: 1}, ModNames.sve)
+mixed_berry_pie = shop_recipe(SVEMeal.mixed_berry_pie, Region.saloon, 3500, {Fruit.strawberry: 6, SVEFruit.salal_berry: 6, Forageable.blackberry: 6,
+ SVEForage.bearberrys: 6, Ingredient.sugar: 1, Ingredient.wheat_flour: 1},
+ ModNames.sve)
+mushroom_berry_rice = friendship_and_shop_recipe(SVEMeal.mushroom_berry_rice, ModNPC.marlon, 6, Region.adventurer_guild, 1500, {SVEForage.poison_mushroom: 3, SVEForage.red_baneberry: 10,
+ Ingredient.rice: 1, Ingredient.sugar: 2}, ModNames.sve)
+seaweed_salad = shop_recipe(SVEMeal.seaweed_salad, Region.fish_shop, 1250, {SVEFish.dulse_seaweed: 2, WaterItem.seaweed: 2, Ingredient.oil: 1}, ModNames.sve)
+void_delight = friendship_and_shop_recipe(SVEMeal.void_delight, NPC.krobus, 10, Region.sewer, 5000,
+ {SVEFish.void_eel: 1, Loot.void_essence: 50, Loot.solar_essence: 20}, ModNames.sve)
+void_salmon_sushi = friendship_and_shop_recipe(SVEMeal.void_salmon_sushi, NPC.krobus, 10, Region.sewer, 5000,
+ {Fish.void_salmon: 1, ArtisanGood.void_mayonnaise: 1, WaterItem.seaweed: 3}, ModNames.sve)
+
+mushroom_kebab = friendship_recipe(DistantLandsMeal.mushroom_kebab, ModNPC.goblin, 2, {Forageable.chanterelle: 1, Forageable.common_mushroom: 1,
+ Forageable.red_mushroom: 1, Material.wood: 1}, ModNames.distant_lands)
+void_mint_tea = friendship_recipe(DistantLandsMeal.void_mint_tea, ModNPC.goblin, 4, {DistantLandsCrop.void_mint: 1}, ModNames.distant_lands)
+crayfish_soup = friendship_recipe(DistantLandsMeal.crayfish_soup, ModNPC.goblin, 6, {Forageable.cave_carrot: 1, Fish.crayfish: 1,
+ DistantLandsFish.purple_algae: 1, WaterItem.white_algae: 1}, ModNames.distant_lands)
+pemmican = friendship_recipe(DistantLandsMeal.pemmican, ModNPC.goblin, 8, {Loot.bug_meat: 1, Fish.any: 1, Forageable.salmonberry: 3,
+ Material.stone: 2}, ModNames.distant_lands)
+
+special_pumpkin_soup = friendship_recipe(BoardingHouseMeal.special_pumpkin_soup, ModNPC.joel, 6, {Vegetable.pumpkin: 2, AnimalProduct.large_goat_milk: 1,
+ Vegetable.garlic: 1}, ModNames.boarding_house)
+
+
+all_cooking_recipes_by_name = {recipe.meal: recipe for recipe in all_cooking_recipes}
\ No newline at end of file
diff --git a/worlds/stardew_valley/data/recipe_source.py b/worlds/stardew_valley/data/recipe_source.py
new file mode 100644
index 000000000000..8dd622e926e7
--- /dev/null
+++ b/worlds/stardew_valley/data/recipe_source.py
@@ -0,0 +1,149 @@
+from typing import Union, List, Tuple
+
+
+class RecipeSource:
+
+ def __repr__(self):
+ return f"RecipeSource"
+
+
+class StarterSource(RecipeSource):
+
+ def __repr__(self):
+ return f"StarterSource"
+
+
+class ArchipelagoSource(RecipeSource):
+ ap_item: Tuple[str]
+
+ def __init__(self, ap_item: Union[str, List[str]]):
+ if isinstance(ap_item, str):
+ ap_item = [ap_item]
+ self.ap_item = tuple(ap_item)
+
+ def __repr__(self):
+ return f"ArchipelagoSource {self.ap_item}"
+
+
+class LogicSource(RecipeSource):
+ logic_rule: str
+
+ def __init__(self, logic_rule: str):
+ self.logic_rule = logic_rule
+
+ def __repr__(self):
+ return f"LogicSource {self.logic_rule}"
+
+
+class QueenOfSauceSource(RecipeSource):
+ year: int
+ season: str
+ day: int
+
+ def __init__(self, year: int, season: str, day: int):
+ self.year = year
+ self.season = season
+ self.day = day
+
+ def __repr__(self):
+ return f"QueenOfSauceSource at year {self.year} {self.season} {self.day}"
+
+
+class QuestSource(RecipeSource):
+ quest: str
+
+ def __init__(self, quest: str):
+ self.quest = quest
+
+ def __repr__(self):
+ return f"QuestSource at quest {self.quest}"
+
+
+class FriendshipSource(RecipeSource):
+ friend: str
+ hearts: int
+
+ def __init__(self, friend: str, hearts: int):
+ self.friend = friend
+ self.hearts = hearts
+
+ def __repr__(self):
+ return f"FriendshipSource at {self.friend} {self.hearts} <3"
+
+
+class CutsceneSource(FriendshipSource):
+ region: str
+
+ def __init__(self, region: str, friend: str, hearts: int):
+ super().__init__(friend, hearts)
+ self.region = region
+
+ def __repr__(self):
+ return f"CutsceneSource at {self.region}"
+
+
+class SkillSource(RecipeSource):
+ skill: str
+ level: int
+
+ def __init__(self, skill: str, level: int):
+ self.skill = skill
+ self.level = level
+
+ def __repr__(self):
+ return f"SkillSource at level {self.level} {self.skill}"
+
+
+class ShopSource(RecipeSource):
+ region: str
+ price: int
+
+ def __init__(self, region: str, price: int):
+ self.region = region
+ self.price = price
+
+ def __repr__(self):
+ return f"ShopSource at {self.region} costing {self.price}g"
+
+
+class ShopFriendshipSource(RecipeSource):
+ friend: str
+ hearts: int
+ region: str
+ price: int
+
+ def __init__(self, friend: str, hearts: int, region: str, price: int):
+ self.friend = friend
+ self.hearts = hearts
+ self.region = region
+ self.price = price
+
+ def __repr__(self):
+ return f"ShopFriendshipSource at {self.region} costing {self.price}g when {self.friend} has {self.hearts} hearts"
+
+
+class FestivalShopSource(ShopSource):
+
+ def __init__(self, region: str, price: int):
+ super().__init__(region, price)
+
+
+class ShopTradeSource(ShopSource):
+ currency: str
+
+ def __init__(self, region: str, currency: str, price: int):
+ super().__init__(region, price)
+ self.currency = currency
+
+ def __repr__(self):
+ return f"ShopTradeSource at {self.region} costing {self.price} {self.currency}"
+
+
+class SpecialOrderSource(RecipeSource):
+ special_order: str
+
+ def __init__(self, special_order: str):
+ self.special_order = special_order
+
+ def __repr__(self):
+ return f"SpecialOrderSource from {self.special_order}"
diff --git a/worlds/stardew_valley/data/shipsanity_unimplemented_items.csv b/worlds/stardew_valley/data/shipsanity_unimplemented_items.csv
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/worlds/stardew_valley/data/villagers_data.py b/worlds/stardew_valley/data/villagers_data.py
index e858d46f34a3..718bce743b1c 100644
--- a/worlds/stardew_valley/data/villagers_data.py
+++ b/worlds/stardew_valley/data/villagers_data.py
@@ -1,7 +1,10 @@
from dataclasses import dataclass
-from typing import List, Tuple, Optional, Dict
-from ..strings.region_names import Region
+from typing import List, Tuple, Optional, Dict, Callable, Set
+
from ..mods.mod_data import ModNames
+from ..strings.food_names import Beverage
+from ..strings.generic_names import Generic
+from ..strings.region_names import Region, SVERegion, AlectoRegion, BoardingHouseRegion, LaceyRegion
from ..strings.season_names import Season
from ..strings.villager_names import NPC, ModNPC
@@ -10,11 +13,11 @@
class Villager:
name: str
bachelor: bool
- locations: Tuple[str]
+ locations: Tuple[str, ...]
birthday: str
- gifts: Tuple[str]
+ gifts: Tuple[str, ...]
available: bool
- mod_name: Optional[str]
+ mod_name: str
def __repr__(self):
return f"{self.name} [Bachelor: {self.bachelor}] [Available from start: {self.available}]" \
@@ -41,6 +44,23 @@ def __repr__(self):
secret_woods = (Region.secret_woods,)
wizard_tower = (Region.wizard_tower,)
+# Stardew Valley Expanded Locations
+adventurer = (Region.adventurer_guild,)
+highlands = (SVERegion.highlands_outside,)
+bluemoon = (SVERegion.blue_moon_vineyard,)
+aurora = (SVERegion.aurora_vineyard,)
+museum = (Region.museum,)
+jojamart = (Region.jojamart,)
+railroad = (Region.railroad,)
+junimo = (SVERegion.junimo_woods,)
+
+# Stray Locations
+witch_swamp = (Region.witch_swamp,)
+witch_attic = (AlectoRegion.witch_attic,)
+hat_house = (LaceyRegion.hat_house,)
+the_lost_valley = (BoardingHouseRegion.the_lost_valley,)
+boarding_house = (BoardingHouseRegion.boarding_house_first,)
+
golden_pumpkin = ("Golden Pumpkin",)
# magic_rock_candy = ("Magic Rock Candy",)
pearl = ("Pearl",)
@@ -183,7 +203,7 @@ def __repr__(self):
pale_ale = ("Pale Ale",)
parsnip = ("Parsnip",)
# parsnip_soup = ("Parsnip Soup",)
-pina_colada = ("Piña Colada",)
+pina_colada = (Beverage.pina_colada,)
pam_loves = beer + cactus_fruit + glazed_yams + mead + pale_ale + parsnip + pina_colada # | parsnip_soup
# fried_calamari = ("Fried Calamari",)
pierre_loves = () # fried_calamari
@@ -209,7 +229,7 @@ def __repr__(self):
void_essence = ("Void Essence",)
wizard_loves = purple_mushroom + solar_essence + super_cucumber + void_essence
-#Custom NPC Items and Loves
+# Custom NPC Items and Loves
blueberry = ("Blueberry",)
chanterelle = ("Chanterelle",)
@@ -271,8 +291,72 @@ def __repr__(self):
juna_loves = ancient_doll + elvish_jewelry + dinosaur_egg + strange_doll + joja_cola + hashbrowns + pancakes + \
pink_cake + jelly + ghost_crystal + prehistoric_scapula + cherry
+glazed_butterfish = ("Glazed Butterfish",)
+aged_blue_moon_wine = ("Aged Blue Moon Wine",)
+blue_moon_wine = ("Blue Moon Wine",)
+daggerfish = ("Daggerfish",)
+gemfish = ("Gemfish",)
+green_mushroom = ("Green Mushroom",)
+monster_mushroom = ("Monster Mushroom",)
+swirl_stone = ("Swirl Stone",)
+torpedo_trout = ("Torpedo Trout",)
+void_shard = ("Void Shard",)
+ornate_treasure_chest = ("Ornate Treasure Chest",)
+frog_legs = ("Frog Legs",)
+void_delight = ("Void Delight",)
+void_pebble = ("Void Pebble",)
+void_salmon_sushi = ("Void Salmon Sushi",)
+puppyfish = ("Puppyfish",)
+butterfish = ("Butterfish",)
+king_salmon = ("King Salmon",)
+frog = ("Frog",)
+kittyfish = ("Kittyfish",)
+big_bark_burger = ("Big Bark Burger",)
+starfruit = ("Starfruit",)
+bruschetta = ("Brushetta",)
+apricot = ("Apricot",)
+ocean_stone = ("Ocean Stone",)
+fairy_stone = ("Fairy Stone",)
+lunarite = ("Lunarite",)
+bean_hotpot = ("Bean Hotpot",)
+petrified_slime = ("Petrified Slime",)
+ornamental_fan = ("Ornamental Fan",)
+ancient_sword = ("Ancient Sword",)
+star_shards = ("Star Shards",)
+life_elixir = ("Life Elixir",)
+juice = ("Juice",)
+lobster_bisque = ("Lobster Bisque",)
+chowder = ("Chowder",)
+goat_milk = ("Goat Milk",)
+maple_syrup = ("Maple Syrup",)
+cookie = ("Cookie",)
+blueberry_tart = ("Blueberry Tart",)
+
+claire_loves = green_tea + sunflower + energy_tonic + bruschetta + apricot + ocean_stone + glazed_butterfish
+lance_loves = aged_blue_moon_wine + daggerfish + gemfish + golden_pumpkin + \
+ green_mushroom + monster_mushroom + swirl_stone + torpedo_trout + tropical_curry + void_shard + \
+ ornate_treasure_chest
+olivia_loves = wine + chocolate_cake + pink_cake + golden_mask + golden_relic + \
+ blue_moon_wine + aged_blue_moon_wine
+sophia_loves = fairy_rose + fairy_stone + puppyfish
+victor_loves = spaghetti + battery_pack + duck_feather + lunarite + \
+ aged_blue_moon_wine + blue_moon_wine + butterfish
+andy_loves = pearl + beer + mead + pale_ale + farmers_lunch + glazed_butterfish + butterfish + \
+ king_salmon + blackberry_cobbler
+gunther_loves = bean_hotpot + petrified_slime + salmon_dinner + elvish_jewelry + ornamental_fan + \
+ dinosaur_egg + rare_disc + ancient_sword + dwarvish_helm + dwarf_gadget + golden_mask + golden_relic + \
+ star_shards
+marlon_loves = roots_platter + life_elixir + aged_blue_moon_wine + void_delight
+martin_loves = juice + ice_cream + big_bark_burger
+morgan_loves = iridium_bar + void_egg + void_mayonnaise + frog + kittyfish
+morris_loves = lobster_bisque + chowder + truffle_oil + star_shards + aged_blue_moon_wine
+scarlett_loves = goat_cheese + duck_feather + goat_milk + cherry + maple_syrup + honey + \
+ chocolate_cake + pink_cake + jade + glazed_yams # actually large milk but meh
+susan_loves = pancakes + chocolate_cake + pink_cake + ice_cream + cookie + pumpkin_pie + rhubarb_pie + \
+ blueberry_tart + blackberry_cobbler + cranberry_candy + red_plate
all_villagers: List[Villager] = []
+villager_modifications_by_mod: Dict[str, Dict[str, Callable[[str, Villager], Villager]]] = {}
def villager(name: str, bachelor: bool, locations: Tuple[str, ...], birthday: str, gifts: Tuple[str, ...],
@@ -282,6 +366,19 @@ def villager(name: str, bachelor: bool, locations: Tuple[str, ...], birthday: st
return npc
+def adapt_wizard_to_sve(mod_name: str, npc: Villager):
+ if npc.mod_name:
+ mod_name = npc.mod_name
+ # The wizard leaves his tower on sunday, for like 1 hour... Good enough to meet him!
+ return Villager(npc.name, True, npc.locations + forest, npc.birthday, npc.gifts, npc.available, mod_name)
+
+
+def register_villager_modification(mod_name: str, npc: Villager, modification_function):
+ if mod_name not in villager_modifications_by_mod:
+ villager_modifications_by_mod[mod_name] = {}
+ villager_modifications_by_mod[mod_name][npc.name] = modification_function
+
+
josh = villager(NPC.alex, True, town + alex_house, Season.summer, universal_loves + complete_breakfast + salmon_dinner, True)
elliott = villager(NPC.elliott, True, town + beach + elliott_house, Season.fall, universal_loves + elliott_loves, True)
harvey = villager(NPC.harvey, True, town + hospital, Season.winter, universal_loves + harvey_loves, True)
@@ -326,13 +423,42 @@ def villager(name: str, bachelor: bool, locations: Tuple[str, ...], birthday: st
juna = villager(ModNPC.juna, False, forest, Season.summer, universal_loves + juna_loves, True, ModNames.juna)
kitty = villager(ModNPC.mr_ginger, False, forest, Season.summer, universal_loves + mister_ginger_loves, True, ModNames.ginger)
shiko = villager(ModNPC.shiko, True, town, Season.winter, universal_loves + shiko_loves, True, ModNames.shiko)
-wellwick = villager(ModNPC.wellwick, True, forest, Season.winter, universal_loves + wellwick_loves, True, ModNames.shiko)
+wellwick = villager(ModNPC.wellwick, True, forest, Season.winter, universal_loves + wellwick_loves, True, ModNames.wellwick)
yoba = villager(ModNPC.yoba, False, secret_woods, Season.spring, universal_loves + yoba_loves, False, ModNames.yoba)
riley = villager(ModNPC.riley, True, town, Season.spring, universal_loves, True, ModNames.riley)
+zic = villager(ModNPC.goblin, False, witch_swamp, Season.fall, void_mayonnaise, False, ModNames.distant_lands)
+alecto = villager(ModNPC.alecto, False, witch_attic, Generic.any, universal_loves, False, ModNames.alecto)
+lacey = villager(ModNPC.lacey, True, forest, Season.spring, universal_loves, True, ModNames.lacey)
+
+# Boarding House Villagers
+gregory = villager(ModNPC.gregory, True, the_lost_valley, Season.fall, universal_loves, False, ModNames.boarding_house)
+sheila = villager(ModNPC.sheila, True, boarding_house, Season.spring, universal_loves, True, ModNames.boarding_house)
+joel = villager(ModNPC.joel, False, boarding_house, Season.winter, universal_loves, True, ModNames.boarding_house)
+
+# SVE Villagers
+claire = villager(ModNPC.claire, True, town + jojamart, Season.fall, universal_loves + claire_loves, True, ModNames.sve)
+lance = villager(ModNPC.lance, True, adventurer + highlands + island, Season.spring, lance_loves, False, ModNames.sve)
+mommy = villager(ModNPC.olivia, True, town, Season.spring, universal_loves_no_rabbit_foot + olivia_loves, True, ModNames.sve)
+sophia = villager(ModNPC.sophia, True, bluemoon, Season.winter, universal_loves_no_rabbit_foot + sophia_loves, True, ModNames.sve)
+victor = villager(ModNPC.victor, True, town, Season.summer, universal_loves + victor_loves, True, ModNames.sve)
+andy = villager(ModNPC.andy, False, forest, Season.spring, universal_loves + andy_loves, True, ModNames.sve)
+apples = villager(ModNPC.apples, False, aurora + junimo, Generic.any, starfruit, False, ModNames.sve)
+gunther = villager(ModNPC.gunther, False, museum, Season.winter, universal_loves + gunther_loves, True, ModNames.jasper_sve)
+martin = villager(ModNPC.martin, False, town + jojamart, Season.summer, universal_loves + martin_loves, True, ModNames.sve)
+marlon = villager(ModNPC.marlon, False, adventurer, Season.winter, universal_loves + marlon_loves, False, ModNames.jasper_sve)
+morgan = villager(ModNPC.morgan, False, forest, Season.fall, universal_loves_no_rabbit_foot + morgan_loves, False, ModNames.sve)
+scarlett = villager(ModNPC.scarlett, False, bluemoon, Season.summer, universal_loves + scarlett_loves, False, ModNames.sve)
+susan = villager(ModNPC.susan, False, railroad, Season.fall, universal_loves + susan_loves, False, ModNames.sve)
+morris = villager(ModNPC.morris, False, jojamart, Season.spring, universal_loves + morris_loves, True, ModNames.sve)
+
+# Modified villagers; not included in all villagers
+
+register_villager_modification(ModNames.sve, wizard, adapt_wizard_to_sve)
all_villagers_by_name: Dict[str, Villager] = {villager.name: villager for villager in all_villagers}
all_villagers_by_mod: Dict[str, List[Villager]] = {}
all_villagers_by_mod_by_name: Dict[str, Dict[str, Villager]] = {}
+
for npc in all_villagers:
mod = npc.mod_name
name = npc.name
@@ -344,3 +470,27 @@ def villager(name: str, bachelor: bool, locations: Tuple[str, ...], birthday: st
all_villagers_by_mod_by_name[mod] = {}
all_villagers_by_mod_by_name[mod][name] = npc
+
+def villager_included_for_any_mod(npc: Villager, mods: Set[str]):
+ if not npc.mod_name:
+ return True
+ for mod in npc.mod_name.split(","):
+ if mod in mods:
+ return True
+ return False
+
+
+def get_villagers_for_mods(mods: Set[str]) -> List[Villager]:
+ villagers_for_current_mods = []
+ for npc in all_villagers:
+ if not villager_included_for_any_mod(npc, mods):
+ continue
+ modified_npc = npc
+ for active_mod in mods:
+ if (active_mod not in villager_modifications_by_mod or
+ npc.name not in villager_modifications_by_mod[active_mod]):
+ continue
+ modification = villager_modifications_by_mod[active_mod][npc.name]
+ modified_npc = modification(active_mod, modified_npc)
+ villagers_for_current_mods.append(modified_npc)
+ return villagers_for_current_mods
diff --git a/worlds/stardew_valley/docs/en_Stardew Valley.md b/worlds/stardew_valley/docs/en_Stardew Valley.md
index a880a40b971a..c29ae859e095 100644
--- a/worlds/stardew_valley/docs/en_Stardew Valley.md
+++ b/worlds/stardew_valley/docs/en_Stardew Valley.md
@@ -1,74 +1,102 @@
# Stardew Valley
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
-A vast number of objectives in Stardew Valley can be shuffled around the multiworld. Most of these are optional, and the player can customize their experience in their YAML file.
+A vast number of objectives in Stardew Valley can be shuffled around the multiworld. Most of these are optional, and the
+player can customize their experience in their YAML file.
-For these objectives, if they have a vanilla reward, this reward will instead be an item in the multiworld. For the remaining number of such objectives, there are a number of "Resource Pack" items, which are simply an item or a stack of items that may be useful to the player.
+For these objectives, if they have a vanilla reward, this reward will instead be an item in the multiworld. For the remaining
+number of such objectives, there are a number of "Resource Pack" items, which are simply an item or a stack of items that
+may be useful to the player.
## What is the goal of Stardew Valley?
-The player can choose from a number of goals, using their YAML settings.
+The player can choose from a number of goals, using their YAML options.
- Complete the [Community Center](https://stardewvalleywiki.com/Bundles)
- Succeed [Grandpa's Evaluation](https://stardewvalleywiki.com/Grandpa) with 4 lit candles
- Reach the bottom of the [Pelican Town Mineshaft](https://stardewvalleywiki.com/The_Mines)
-- Complete the [Cryptic Note](https://stardewvalleywiki.com/Secret_Notes#Secret_Note_.2310) quest, by meeting Mr Qi on floor 100 of the Skull Cavern
-- Get the achievement [Master Angler](https://stardewvalleywiki.com/Fish), which requires catching every fish in the game
-- Get the achievement [A Complete Collection](https://stardewvalleywiki.com/Museum), which requires donating all the artifacts and minerals to the museum
-- Get the achievement [Full House](https://stardewvalleywiki.com/Children), which requires getting married and having two kids.
-- Get recognized as the [Greatest Walnut Hunter](https://stardewvalleywiki.com/Golden_Walnut) by Mr Qi, which requires finding all 130 golden walnuts on ginger island
+- Complete the [Cryptic Note](https://stardewvalleywiki.com/Secret_Notes#Secret_Note_.2310) quest, by meeting Mr Qi on
+floor 100 of the Skull Cavern
+- Become a [Master Angler](https://stardewvalleywiki.com/Fish), which requires catching every fish in your slot
+- Restore [A Complete Collection](https://stardewvalleywiki.com/Museum), which requires donating all the artifacts and
+minerals to the museum
+- Get the achievement [Full House](https://stardewvalleywiki.com/Children), which requires getting married and having two kids
+- Get recognized as the [Greatest Walnut Hunter](https://stardewvalleywiki.com/Golden_Walnut) by Mr Qi, which requires
+finding all 130 golden walnuts on ginger island
+- Become the [Protector of the Valley](https://stardewvalleywiki.com/Adventurer%27s_Guild#Monster_Eradication_Goals) by
+completing all the monster slayer goals at the Adventure Guild
+- Complete a [Full Shipment](https://stardewvalleywiki.com/Shipping#Collection) by shipping every item in your slot
+- Become a [Gourmet Chef](https://stardewvalleywiki.com/Cooking) by cooking every recipe in your slot
+- Become a [Craft Master](https://stardewvalleywiki.com/Crafting) by crafting every item
+- Earn the title of [Legend](https://stardewvalleywiki.com/Gold) by earning 10 000 000g
+- Solve the [Mystery of the Stardrops](https://stardewvalleywiki.com/Stardrop) by finding every stardrop
+- Finish 100% of your randomizer slot with Allsanity: Complete every check in your slot
- Achieve [Perfection](https://stardewvalleywiki.com/Perfection) in your save file
+The following goals [Community Center, Master Angler, Protector of the Valley, Full Shipment and Gourmet Chef] will adapt
+to other options in your slots, and are therefore customizable in duration and difficulty. For example, if you set "Fishsanity"
+to "Exclude Legendaries", and pick the Master Angler goal, you will not need to catch the legendaries to complete the goal.
+
## What are location checks in Stardew Valley?
Location checks in Stardew Valley always include:
- [Community Center Bundles](https://stardewvalleywiki.com/Bundles)
- [Mineshaft Chest Rewards](https://stardewvalleywiki.com/The_Mines#Remixed_Rewards)
-- [Story Quests](https://stardewvalleywiki.com/Quests#List_of_Story_Quests)
- [Traveling Merchant Items](https://stardewvalleywiki.com/Traveling_Cart)
-- Isolated objectives such as the [beach bridge](https://stardewvalleywiki.com/The_Beach#Tide_Pools), [Old Master Cannoli](https://stardewvalleywiki.com/Secret_Woods#Old_Master_Cannoli), [Grim Reaper Statue](https://stardewvalleywiki.com/Golden_Scythe), etc
+- Isolated objectives such as the [beach bridge](https://stardewvalleywiki.com/The_Beach#Tide_Pools),
+[Old Master Cannoli](https://stardewvalleywiki.com/Secret_Woods#Old_Master_Cannoli),
+[Grim Reaper Statue](https://stardewvalleywiki.com/Golden_Scythe), etc
There also are a number of location checks that are optional, and individual players choose to include them or not in their shuffling:
-- Tools and Fishing Rod Upgrades
-- Carpenter Buildings
-- Backpack Upgrades
-- Mine Elevator Levels
-- Skill Levels
+- [Tools and Fishing Rod Upgrades](https://stardewvalleywiki.com/Tools)
+- [Carpenter Buildings](https://stardewvalleywiki.com/Carpenter%27s_Shop#Farm_Buildings)
+- [Backpack Upgrades](https://stardewvalleywiki.com/Tools#Other_Tools)
+- [Mine Elevator Levels](https://stardewvalleywiki.com/The_Mines#Staircases)
+- [Skill Levels](https://stardewvalleywiki.com/Skills)
- Arcade Machines
-- Help Wanted Quests
-- Participating in Festivals
-- Special Orders from the town board, or from Mr Qi
-- Cropsanity: Growing and Harvesting individual crop types
-- Fishsanity: Catching individual fish
-- Museumsanity: Donating individual items, or reaching milestones for museum donations
-- Friendsanity: Reaching specific friendship levels with NPCs
+- [Story Quests](https://stardewvalleywiki.com/Quests#List_of_Story_Quests)
+- [Help Wanted Quests](https://stardewvalleywiki.com/Quests#Help_Wanted_Quests)
+- Participating in [Festivals](https://stardewvalleywiki.com/Festivals)
+- [Special Orders](https://stardewvalleywiki.com/Quests#List_of_Special_Orders) from the town board, or from
+[Mr Qi](https://stardewvalleywiki.com/Quests#List_of_Mr._Qi.27s_Special_Orders)
+- [Cropsanity](https://stardewvalleywiki.com/Crops): Growing and Harvesting individual crop types
+- [Fishsanity](https://stardewvalleywiki.com/Fish): Catching individual fish
+- [Museumsanity](https://stardewvalleywiki.com/Museum): Donating individual items, or reaching milestones for museum donations
+- [Friendsanity](https://stardewvalleywiki.com/Friendship): Reaching specific friendship levels with NPCs
+- [Monstersanity](https://stardewvalleywiki.com/Adventurer%27s_Guild#Monster_Eradication_Goals): Completing monster slayer goals
+- [Cooksanity](https://stardewvalleywiki.com/Cooking): Cooking individual recipes
+- [Chefsanity](https://stardewvalleywiki.com/Cooking#Recipes): Learning cooking recipes
+- [Craftsanity](https://stardewvalleywiki.com/Crafting): Crafting individual items
+- [Shipsanity](https://stardewvalleywiki.com/Shipping): Shipping individual items
## Which items can be in another player's world?
Every normal reward from the above locations can be in another player's world.
For the locations which do not include a normal reward, Resource Packs and traps are instead added to the pool. Traps are optional.
-A player can enable some settings that will add some items to the pool that are relevant to progression
+A player can enable some options that will add some items to the pool that are relevant to progression
- Seasons Randomizer:
- - All 4 seasons will be items, and one of them will be selected randomly and be added to the player's start inventory
- - At the end of each month, the player can choose the next season, instead of following the vanilla season order. On Seasons Randomizer, they can only choose from the seasons they have received.
+ - All 4 seasons will be items, and one of them will be selected randomly and be added to the player's start inventory.
+ - At the end of each month, the player can choose the next season, instead of following the vanilla season order. On Seasons Randomizer, they can only choose from the seasons they have received.
- Cropsanity:
- - Every single seed in the game starts off locked and cannot be purchased from any merchant. Their unlocks are received as multiworld items. Growing each seed and harvesting the resulting crop sends a location check
- - The way merchants sell seeds is considerably changed. Pierre sells fewer seeds at a high price, while Joja sells unlimited seeds but in huge discount packs, not individually.
+ - Every single seed in the game starts off locked and cannot be purchased from any merchant. Their unlocks are received as multiworld items. Growing each seed and harvesting the resulting crop sends a location check
+ - The way merchants sell seeds is considerably changed. Pierre sells fewer seeds at a high price, while Joja sells unlimited seeds but in huge discount packs, not individually.
- Museumsanity:
- - The items that are normally obtained from museum donation milestones are added to the item pool. Some items, like the magic rock candy, are duplicated for convenience.
- - The Traveling Merchant now sells artifacts and minerals, with a bias towards undonated ones, to mitigate randomness. She will sell these items as the player receives "Traveling Merchant Metal Detector" items.
+ - The items that are normally obtained from museum donation milestones are added to the item pool. Some items, like the magic rock candy, are duplicated for convenience.
+ - The Traveling Merchant now sells artifacts and minerals, with a bias towards undonated ones, to mitigate randomness. She will sell these items as the player receives "Traveling Merchant Metal Detector" items.
- TV Channels
- Babies
+ - Only if Friendsanity is enabled
There are a few extra vanilla items, which are added to the pool for convenience, but do not have a matching location. These include
- [Wizard Buildings](https://stardewvalleywiki.com/Wizard%27s_Tower#Buildings)
- [Return Scepter](https://stardewvalleywiki.com/Return_Scepter)
+- [Qi Walnut Room QoL items](https://stardewvalleywiki.com/Qi%27s_Walnut_Room#Stock)
And lastly, some Archipelago-exclusive items exist in the pool, which are designed around game balance and QoL. These include:
- Arcade Machine buffs (Only if the arcade machines are randomized)
@@ -80,50 +108,61 @@ And lastly, some Archipelago-exclusive items exist in the pool, which are design
## When the player receives an item, what happens?
-Since Pelican Town is a remote area, it takes one business day for every item to reach the player. If an item is received while online, it will appear in the player's mailbox the next morning, with a message from the sender telling them where it was found.
-If an item is received while offline, it will be in the mailbox as soon as the player logs in.
+Since Pelican Town is a remote area, it takes one business day for every item to reach the player. If an item is received
+while online, it will appear in the player's mailbox the next morning, with a message from the sender telling them where
+it was found. If an item is received while offline, it will be in the mailbox as soon as the player logs in.
-Some items will be directly attached to the letter, while some others will instead be a world-wide unlock, and the letter only serves to tell the player about it.
+Some items will be directly attached to the letter, while some others will instead be a world-wide unlock, and the letter
+only serves to tell the player about it.
-In some cases, like receiving Carpenter and Wizard buildings, the player will still need to go ask Robin to construct the building that they have received, so they can choose its position. This construction will be completely free.
+In some cases, like receiving Carpenter and Wizard buildings, the player will still need to go ask Robin to construct the
+building that they have received, so they can choose its position. This construction will be completely free.
## Mods
-Starting in version 4.x.x, some Stardew Valley mods unrelated to Archipelago are officially "supported".
-This means that, for these specific mods, if you decide to include them in your yaml settings, the multiworld will be generated with the assumption that you will install and play with these mods.
-The multiworld will contain related items and locations for these mods, the specifics will vary from mod to mod
+Some Stardew Valley mods unrelated to Archipelago are officially "supported".
+This means that, for these specific mods, if you decide to include them in your yaml options, the multiworld will be generated
+with the assumption that you will install and play with these mods. The multiworld will contain related items and locations
+for these mods, the specifics will vary from mod to mod
-[Supported Mods Documentation](https://github.com/agilbert1412/StardewArchipelago/blob/4.x.x/Documentation/Supported%20Mods.md)
+[Supported Mods Documentation](https://github.com/agilbert1412/StardewArchipelago/blob/5.x.x/Documentation/Supported%20Mods.md)
List of supported mods:
- General
- - [DeepWoods](https://www.nexusmods.com/stardewvalley/mods/2571)
- - [Tractor Mod](https://www.nexusmods.com/stardewvalley/mods/1401)
- - [Bigger Backpack](https://www.nexusmods.com/stardewvalley/mods/1845)
- - [Skull Cavern Elevator](https://www.nexusmods.com/stardewvalley/mods/963)
+ - [Stardew Valley Expanded](https://www.nexusmods.com/stardewvalley/mods/3753)
+ - [DeepWoods](https://www.nexusmods.com/stardewvalley/mods/2571)
+ - [Skull Cavern Elevator](https://www.nexusmods.com/stardewvalley/mods/963)
+ - [Bigger Backpack](https://www.nexusmods.com/stardewvalley/mods/1845)
+ - [Tractor Mod](https://www.nexusmods.com/stardewvalley/mods/1401)
+ - [Distant Lands - Witch Swamp Overhaul](https://www.nexusmods.com/stardewvalley/mods/18109)
- Skills
- - [Luck Skill](https://www.nexusmods.com/stardewvalley/mods/521)
- - [Magic](https://www.nexusmods.com/stardewvalley/mods/2007)
- - [Socializing Skill](https://www.nexusmods.com/stardewvalley/mods/14142)
- - [Archaeology](https://www.nexusmods.com/stardewvalley/mods/15793)
- - [Cooking Skill](https://www.nexusmods.com/stardewvalley/mods/522)
- - [Binning Skill](https://www.nexusmods.com/stardewvalley/mods/14073)
+ - [Magic](https://www.nexusmods.com/stardewvalley/mods/2007)
+ - [Luck Skill](https://www.nexusmods.com/stardewvalley/mods/521)
+ - [Socializing Skill](https://www.nexusmods.com/stardewvalley/mods/14142)
+ - [Archaeology](https://www.nexusmods.com/stardewvalley/mods/15793)
+ - [Cooking Skill](https://www.nexusmods.com/stardewvalley/mods/522)
+ - [Binning Skill](https://www.nexusmods.com/stardewvalley/mods/14073)
- NPCs
- - [Ayeisha - The Postal Worker (Custom NPC)](https://www.nexusmods.com/stardewvalley/mods/6427)
- - [Mister Ginger (cat npc)](https://www.nexusmods.com/stardewvalley/mods/5295)
- - [Juna - Roommate NPC](https://www.nexusmods.com/stardewvalley/mods/8606)
- - [Professor Jasper Thomas](https://www.nexusmods.com/stardewvalley/mods/5599)
- - [Alec Revisited](https://www.nexusmods.com/stardewvalley/mods/10697)
- - [Custom NPC - Yoba](https://www.nexusmods.com/stardewvalley/mods/14871)
- - [Custom NPC Eugene](https://www.nexusmods.com/stardewvalley/mods/9222)
- - ['Prophet' Wellwick](https://www.nexusmods.com/stardewvalley/mods/6462)
- - [Shiko - New Custom NPC](https://www.nexusmods.com/stardewvalley/mods/3732)
- - [Delores - Custom NPC](https://www.nexusmods.com/stardewvalley/mods/5510)
- - [Custom NPC - Riley](https://www.nexusmods.com/stardewvalley/mods/5811)
+ - [Ayeisha - The Postal Worker (Custom NPC)](https://www.nexusmods.com/stardewvalley/mods/6427)
+ - [Mister Ginger (cat npc)](https://www.nexusmods.com/stardewvalley/mods/5295)
+ - [Juna - Roommate NPC](https://www.nexusmods.com/stardewvalley/mods/8606)
+ - [Professor Jasper Thomas](https://www.nexusmods.com/stardewvalley/mods/5599)
+ - [Alec Revisited](https://www.nexusmods.com/stardewvalley/mods/10697)
+ - [Custom NPC - Yoba](https://www.nexusmods.com/stardewvalley/mods/14871)
+ - [Custom NPC Eugene](https://www.nexusmods.com/stardewvalley/mods/9222)
+ - ['Prophet' Wellwick](https://www.nexusmods.com/stardewvalley/mods/6462)
+ - [Shiko - New Custom NPC](https://www.nexusmods.com/stardewvalley/mods/3732)
+ - [Delores - Custom NPC](https://www.nexusmods.com/stardewvalley/mods/5510)
+ - [Custom NPC - Riley](https://www.nexusmods.com/stardewvalley/mods/5811)
+ - [Alecto the Witch](https://www.nexusmods.com/stardewvalley/mods/10671)
+
+Some of these mods might need a patch mod to tie the randomizer with the mod. These can be found
+[here](https://github.com/Witchybun/SDV-Randomizer-Content-Patcher/releases)
## Multiplayer
-You cannot play an Archipelago Slot in multiplayer at the moment. There is no short-terms plans to support that feature.
+You cannot play an Archipelago Slot in multiplayer at the moment. There are no short-term plans to support that feature.
-You can, however, send Stardew Valley objects as gifts from one Stardew Player to another Stardew player, using in-game Joja Prime delivery, for a fee. This exclusive feature can be turned off if you don't want to send and receive gifts.
+You can, however, send Stardew Valley objects as gifts from one Stardew Player to another Stardew player, using in-game
+Joja Prime delivery, for a fee. This exclusive feature can be turned off if you don't want to send and receive gifts.
diff --git a/worlds/stardew_valley/docs/setup_en.md b/worlds/stardew_valley/docs/setup_en.md
index 68c7fb9af6a0..74caf9b7daba 100644
--- a/worlds/stardew_valley/docs/setup_en.md
+++ b/worlds/stardew_valley/docs/setup_en.md
@@ -3,18 +3,25 @@
## Required Software
- Stardew Valley on PC (Recommended: [Steam version](https://store.steampowered.com/app/413150/Stardew_Valley/))
-- SMAPI ([Mod loader for Stardew Valley](https://smapi.io/))
-- [StardewArchipelago Mod Release 4.x.x](https://github.com/agilbert1412/StardewArchipelago/releases)
- - It is important to use a mod release of version 4.x.x to play seeds that have been generated here. Later releases can only be used with later releases of the world generator, that are not hosted on archipelago.gg yet.
+ - You need version 1.5.6. It is available in a public beta branch on Steam ![image](https://i.imgur.com/uKAUmF0.png).
+ - If your Stardew is not on Steam, you are responsible for finding a way to downgrade it.
+ - This measure is temporary. We are working hard to bring the mod to Stardew 1.6 as soon as possible.
+- SMAPI 3.x.x ([Mod loader for Stardew Valley](https://www.nexusmods.com/stardewvalley/mods/2400?tab=files))
+ - Same as Stardew Valley itself, SMAPI needs a slightly older version to be compatible with Stardew Valley 1.5.6 ![image](https://i.imgur.com/kzgObHy.png)
+- [StardewArchipelago Mod Release 5.x.x](https://github.com/agilbert1412/StardewArchipelago/releases)
+ - It is important to use a mod release of version 5.x.x to play seeds that have been generated here. Later releases
+ can only be used with later releases of the world generator, that are not hosted on archipelago.gg yet.
## Optional Software
- Archipelago from the [Archipelago Releases Page](https://github.com/ArchipelagoMW/Archipelago/releases)
- - (Only for the TextClient)
+ * (Only for the TextClient)
- Other Stardew Valley Mods [Nexus Mods](https://www.nexusmods.com/stardewvalley)
- - There are [supported mods](https://github.com/agilbert1412/StardewArchipelago/blob/4.x.x/Documentation/Supported%20Mods.md) that you can add to your yaml to include them with the Archipelago randomization
+ * There are [supported mods](https://github.com/agilbert1412/StardewArchipelago/blob/5.x.x/Documentation/Supported%20Mods.md)
+ that you can add to your yaml to include them with the Archipelago randomization
- - It is **not** recommended to further mod Stardew Valley with unsupported mods, although it is possible to do so. Mod interactions can be unpredictable, and no support will be offered for related bugs.
- - The more unsupported mods you have, and the bigger they are, the more likely things are to break.
+ * It is **not** recommended to further mod Stardew Valley with unsupported mods, although it is possible to do so.
+ Mod interactions can be unpredictable, and no support will be offered for related bugs.
+ * The more unsupported mods you have, and the bigger they are, the more likely things are to break.
## Configuring your YAML file
@@ -25,16 +32,16 @@ guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
### Where do I get a YAML file?
-You can customize your settings by visiting the [Stardew Valley Player Settings Page](/games/Stardew%20Valley/player-settings)
+You can customize your options by visiting the [Stardew Valley Player Options Page](/games/Stardew%20Valley/player-options)
## Joining a MultiWorld Game
### Installing the mod
-- Install [SMAPI](https://smapi.io/) by following the instructions on their website
-- Download and extract the [StardewArchipelago](https://github.com/agilbert1412/StardewArchipelago/releases) mod into your Stardew Valley "Mods" folder
-- *OPTIONAL*: If you want to launch your game through Steam, add the following to your Stardew Valley launch options:
- - "[PATH TO STARDEW VALLEY]\Stardew Valley\StardewModdingAPI.exe" %command%
+- Install [SMAPI version 3.x.x](https://www.nexusmods.com/stardewvalley/mods/2400?tab=files) by following the instructions on the mod page
+- Download and extract the [StardewArchipelago](https://github.com/agilbert1412/StardewArchipelago/releases) mod into
+your Stardew Valley "Mods" folder
+- *OPTIONAL*: If you want to launch your game through Steam, add the following to your Stardew Valley launch options: `"[PATH TO STARDEW VALLEY]\Stardew Valley\StardewModdingAPI.exe" %command%`
- Otherwise just launch "StardewModdingAPI.exe" in your installation folder directly
- Stardew Valley should launch itself alongside a console which allows you to read mod information and interact with some of them.
@@ -69,19 +76,25 @@ If the room's ip or port **does** change, you can follow these instructions to m
### Interacting with the MultiWorld from in-game
-When you connect, you should see a message in the chat informing you of the `!!help` command. This command will list other Stardew-exclusive chat commands you can use.
+When you connect, you should see a message in the chat informing you of the `!!help` command. This command will list other
+Stardew-exclusive chat commands you can use.
-Furthermore, you can use the in-game chat box to talk to other players in the multiworld, assuming they are using a game that supports chatting.
+Furthermore, you can use the in-game chat box to talk to other players in the multiworld, assuming they are using a game
+that supports chatting.
-Lastly, you can also run Archipelago commands `!help` from the in game chat box, allowing you to request hints on certain items, or check missing locations.
+Lastly, you can also run Archipelago commands `!help` from the in game chat box, allowing you to request hints on certain
+items, or check missing locations.
-It is important to note that the Stardew Valley chat is fairly limited in its capabilities. For example, it doesn't allow scrolling up to see history that has been pushed off screen. The SMAPI console running alonside your game will have the full history as well and may be better suited to read older messages.
-For a better chat experience, you can also use the official Archipelago Text Client, altough it will not allow you to run Stardew-exclusive commands.
+It is important to note that the Stardew Valley chat is fairly limited in its capabilities. For example, it doesn't allow
+scrolling up to see history that has been pushed off screen. The SMAPI console running alonside your game will have the
+full history as well and may be better suited to read older messages.
+For a better chat experience, you can also use the official Archipelago Text Client, altough it will not allow you to run
+Stardew-exclusive commands.
### Playing with supported mods
-See the [Supported mods documentation](https://github.com/agilbert1412/StardewArchipelago/blob/4.x.x/Documentation/Supported%20Mods.md)
+See the [Supported mods documentation](https://github.com/agilbert1412/StardewArchipelago/blob/5.x.x/Documentation/Supported%20Mods.md)
### Multiplayer
-You cannot play an Archipelago Slot in multiplayer at the moment. There are no short-terms plans to support that feature.
\ No newline at end of file
+You cannot play an Archipelago Slot in multiplayer at the moment. There are no short-term plans to support that feature.
\ No newline at end of file
diff --git a/worlds/stardew_valley/early_items.py b/worlds/stardew_valley/early_items.py
new file mode 100644
index 000000000000..78170f29fee7
--- /dev/null
+++ b/worlds/stardew_valley/early_items.py
@@ -0,0 +1,65 @@
+from random import Random
+
+from .options import BuildingProgression, StardewValleyOptions, BackpackProgression, ExcludeGingerIsland, SeasonRandomization, SpecialOrderLocations, \
+ Monstersanity, ToolProgression, SkillProgression, Cooksanity, Chefsanity
+
+early_candidate_rate = 4
+always_early_candidates = ["Greenhouse", "Desert Obelisk", "Rusty Key"]
+seasons = ["Spring", "Summer", "Fall", "Winter"]
+
+
+def setup_early_items(multiworld, options: StardewValleyOptions, player: int, random: Random):
+ early_forced = []
+ early_candidates = []
+ early_candidates.extend(always_early_candidates)
+
+ add_seasonal_candidates(early_candidates, options)
+
+ if options.building_progression & BuildingProgression.option_progressive:
+ early_forced.append("Shipping Bin")
+ early_candidates.append("Progressive Coop")
+ early_candidates.append("Progressive Barn")
+
+ if options.backpack_progression == BackpackProgression.option_early_progressive:
+ early_forced.append("Progressive Backpack")
+
+ if options.tool_progression & ToolProgression.option_progressive:
+ early_forced.append("Progressive Fishing Rod")
+ early_forced.append("Progressive Pickaxe")
+
+ if options.skill_progression == SkillProgression.option_progressive:
+ early_forced.append("Fishing Level")
+
+ if options.quest_locations >= 0:
+ early_candidates.append("Magnifying Glass")
+
+ if options.special_order_locations != SpecialOrderLocations.option_disabled:
+ early_candidates.append("Special Order Board")
+
+ if options.cooksanity != Cooksanity.option_none | options.chefsanity & Chefsanity.option_queen_of_sauce:
+ early_candidates.append("The Queen of Sauce")
+
+ if options.monstersanity == Monstersanity.option_none:
+ early_candidates.append("Progressive Weapon")
+ else:
+ early_candidates.append("Progressive Sword")
+
+ if options.exclude_ginger_island == ExcludeGingerIsland.option_false:
+ early_candidates.append("Island Obelisk")
+
+ early_forced.extend(random.sample(early_candidates, len(early_candidates) // early_candidate_rate))
+
+ for item_name in early_forced:
+ if item_name in multiworld.early_items[player]:
+ continue
+ multiworld.early_items[player][item_name] = 1
+
+
+def add_seasonal_candidates(early_candidates, options):
+ if options.season_randomization == SeasonRandomization.option_progressive:
+ early_candidates.extend(["Progressive Season"] * 3)
+ return
+ if options.season_randomization == SeasonRandomization.option_disabled:
+ return
+
+ early_candidates.extend(seasons)
diff --git a/worlds/stardew_valley/items.py b/worlds/stardew_valley/items.py
index 1f0735f4aebc..d0cb09bd9953 100644
--- a/worlds/stardew_valley/items.py
+++ b/worlds/stardew_valley/items.py
@@ -8,11 +8,19 @@
from BaseClasses import Item, ItemClassification
from . import data
-from .data.villagers_data import all_villagers
+from .data.villagers_data import get_villagers_for_mods
from .mods.mod_data import ModNames
-from .options import StardewValleyOptions, TrapItems, FestivalLocations, ExcludeGingerIsland, SpecialOrderLocations, SeasonRandomization, Cropsanity, Friendsanity, Museumsanity, \
- Fishsanity, BuildingProgression, SkillProgression, ToolProgression, ElevatorProgression, BackpackProgression, ArcadeMachineLocations
+from .options import StardewValleyOptions, TrapItems, FestivalLocations, ExcludeGingerIsland, SpecialOrderLocations, SeasonRandomization, Cropsanity, \
+ Friendsanity, Museumsanity, \
+ Fishsanity, BuildingProgression, SkillProgression, ToolProgression, ElevatorProgression, BackpackProgression, ArcadeMachineLocations, Monstersanity, Goal, \
+ Chefsanity, Craftsanity, BundleRandomization, EntranceRandomization, Shipsanity
+from .strings.ap_names.ap_weapon_names import APWeapon
from .strings.ap_names.buff_names import Buff
+from .strings.ap_names.community_upgrade_names import CommunityUpgrade
+from .strings.ap_names.event_names import Event
+from .strings.ap_names.mods.mod_items import SVEQuestItem
+from .strings.villager_names import NPC, ModNPC
+from .strings.wallet_item_names import Wallet
ITEM_CODE_OFFSET = 717000
@@ -25,21 +33,20 @@ class Group(enum.Enum):
FRIENDSHIP_PACK = enum.auto()
COMMUNITY_REWARD = enum.auto()
TRASH = enum.auto()
- MINES_FLOOR_10 = enum.auto()
- MINES_FLOOR_20 = enum.auto()
- MINES_FLOOR_50 = enum.auto()
- MINES_FLOOR_60 = enum.auto()
- MINES_FLOOR_80 = enum.auto()
- MINES_FLOOR_90 = enum.auto()
- MINES_FLOOR_110 = enum.auto()
FOOTWEAR = enum.auto()
HATS = enum.auto()
RING = enum.auto()
WEAPON = enum.auto()
+ WEAPON_GENERIC = enum.auto()
+ WEAPON_SWORD = enum.auto()
+ WEAPON_CLUB = enum.auto()
+ WEAPON_DAGGER = enum.auto()
+ WEAPON_SLINGSHOT = enum.auto()
PROGRESSIVE_TOOLS = enum.auto()
SKILL_LEVEL_UP = enum.auto()
+ BUILDING = enum.auto()
+ WIZARD_BUILDING = enum.auto()
ARCADE_MACHINE_BUFFS = enum.auto()
- GALAXY_WEAPONS = enum.auto()
BASE_RESOURCE = enum.auto()
WARP_TOTEM = enum.auto()
GEODE = enum.auto()
@@ -65,7 +72,17 @@ class Group(enum.Enum):
GINGER_ISLAND = enum.auto()
WALNUT_PURCHASE = enum.auto()
TV_CHANNEL = enum.auto()
+ QI_CRAFTING_RECIPE = enum.auto()
+ CHEFSANITY = enum.auto()
+ CHEFSANITY_STARTER = enum.auto()
+ CHEFSANITY_QOS = enum.auto()
+ CHEFSANITY_PURCHASE = enum.auto()
+ CHEFSANITY_FRIENDSHIP = enum.auto()
+ CHEFSANITY_SKILL = enum.auto()
+ CRAFTSANITY = enum.auto()
+ # Mods
MAGIC_SPELL = enum.auto()
+ MOD_WARP = enum.auto()
@dataclass(frozen=True)
@@ -90,7 +107,12 @@ def has_any_group(self, *group: Group) -> bool:
class StardewItemFactory(Protocol):
- def __call__(self, name: Union[str, ItemData]) -> Item:
+ def __call__(self, name: Union[str, ItemData], override_classification: ItemClassification = None) -> Item:
+ raise NotImplementedError
+
+
+class StardewItemDeleter(Protocol):
+ def __call__(self, item: Item):
raise NotImplementedError
@@ -113,8 +135,11 @@ def load_item_csv():
events = [
- ItemData(None, "Victory", ItemClassification.progression),
- ItemData(None, "Month End", ItemClassification.progression),
+ ItemData(None, Event.victory, ItemClassification.progression),
+ ItemData(None, Event.can_construct_buildings, ItemClassification.progression),
+ ItemData(None, Event.start_dark_talisman_quest, ItemClassification.progression),
+ ItemData(None, Event.can_ship_items, ItemClassification.progression),
+ ItemData(None, Event.can_shop_at_pierre, ItemClassification.progression),
]
all_items: List[ItemData] = load_item_csv() + events
@@ -138,16 +163,19 @@ def initialize_item_table():
initialize_groups()
-def create_items(item_factory: StardewItemFactory, locations_count: int, items_to_exclude: List[Item],
+def get_too_many_items_error_message(locations_count: int, items_count: int) -> str:
+ return f"There should be at least as many locations [{locations_count}] as there are mandatory items [{items_count}]"
+
+
+def create_items(item_factory: StardewItemFactory, item_deleter: StardewItemDeleter, locations_count: int, items_to_exclude: List[Item],
options: StardewValleyOptions, random: Random) -> List[Item]:
items = []
unique_items = create_unique_items(item_factory, options, random)
- for item in items_to_exclude:
- if item in unique_items:
- unique_items.remove(item)
+ remove_items(item_deleter, items_to_exclude, unique_items)
+
+ remove_items_if_no_room_for_them(item_deleter, unique_items, locations_count, random)
- assert len(unique_items) <= locations_count, f"There should be at least as many locations [{locations_count}] as there are mandatory items [{len(unique_items)}]"
items += unique_items
logger.debug(f"Created {len(unique_items)} unique items")
@@ -162,38 +190,71 @@ def create_items(item_factory: StardewItemFactory, locations_count: int, items_t
return items
+def remove_items(item_deleter: StardewItemDeleter, items_to_remove, items):
+ for item in items_to_remove:
+ if item in items:
+ items.remove(item)
+ item_deleter(item)
+
+
+def remove_items_if_no_room_for_them(item_deleter: StardewItemDeleter, unique_items: List[Item], locations_count: int, random: Random):
+ if len(unique_items) <= locations_count:
+ return
+
+ number_of_items_to_remove = len(unique_items) - locations_count
+ removable_items = [item for item in unique_items if item.classification == ItemClassification.filler or item.classification == ItemClassification.trap]
+ if len(removable_items) < number_of_items_to_remove:
+ logger.debug(f"Player has more items than locations, trying to remove {number_of_items_to_remove} random non-progression items")
+ removable_items = [item for item in unique_items if not item.classification & ItemClassification.progression]
+ else:
+ logger.debug(f"Player has more items than locations, trying to remove {number_of_items_to_remove} random filler items")
+ assert len(removable_items) >= number_of_items_to_remove, get_too_many_items_error_message(locations_count, len(unique_items))
+ items_to_remove = random.sample(removable_items, number_of_items_to_remove)
+ remove_items(item_deleter, items_to_remove, unique_items)
+
+
def create_unique_items(item_factory: StardewItemFactory, options: StardewValleyOptions, random: Random) -> List[Item]:
items = []
items.extend(item_factory(item) for item in items_by_group[Group.COMMUNITY_REWARD])
+ items.append(item_factory(CommunityUpgrade.movie_theater)) # It is a community reward, but we need two of them
+ items.append(item_factory(Wallet.metal_detector)) # Always offer at least one metal detector
create_backpack_items(item_factory, options, items)
- create_mine_rewards(item_factory, items, random)
+ create_weapons(item_factory, options, items)
+ items.append(item_factory("Skull Key"))
create_elevators(item_factory, options, items)
create_tools(item_factory, options, items)
create_skills(item_factory, options, items)
create_wizard_buildings(item_factory, options, items)
create_carpenter_buildings(item_factory, options, items)
+ items.append(item_factory("Railroad Boulder Removed"))
+ items.append(item_factory(CommunityUpgrade.fruit_bats))
+ items.append(item_factory(CommunityUpgrade.mushroom_boxes))
items.append(item_factory("Beach Bridge"))
- items.append(item_factory("Dark Talisman"))
- create_tv_channels(item_factory, items)
- create_special_quest_rewards(item_factory, items)
+ create_tv_channels(item_factory, options, items)
+ create_special_quest_rewards(item_factory, options, items)
create_stardrops(item_factory, options, items)
create_museum_items(item_factory, options, items)
create_arcade_machine_items(item_factory, options, items)
- items.append(item_factory(random.choice(items_by_group[Group.GALAXY_WEAPONS])))
create_player_buffs(item_factory, options, items)
create_traveling_merchant_items(item_factory, items)
items.append(item_factory("Return Scepter"))
create_seasons(item_factory, options, items)
create_seeds(item_factory, options, items)
- create_friendsanity_items(item_factory, options, items)
+ create_friendsanity_items(item_factory, options, items, random)
create_festival_rewards(item_factory, options, items)
- create_babies(item_factory, items, random)
create_special_order_board_rewards(item_factory, options, items)
create_special_order_qi_rewards(item_factory, options, items)
create_walnut_purchase_rewards(item_factory, options, items)
+ create_crafting_recipes(item_factory, options, items)
+ create_cooking_recipes(item_factory, options, items)
+ create_shipsanity_items(item_factory, options, items)
+ create_goal_items(item_factory, options, items)
+ items.append(item_factory("Golden Egg"))
create_magic_mod_spells(item_factory, options, items)
+ create_deepwoods_pendants(item_factory, options, items)
+ create_archaeology_items(item_factory, options, items)
return items
@@ -206,18 +267,27 @@ def create_backpack_items(item_factory: StardewItemFactory, options: StardewVall
items.append(item_factory("Progressive Backpack"))
-def create_mine_rewards(item_factory: StardewItemFactory, items: List[Item], random: Random):
- items.append(item_factory("Rusty Sword"))
- items.append(item_factory(random.choice(items_by_group[Group.MINES_FLOOR_10])))
- items.append(item_factory(random.choice(items_by_group[Group.MINES_FLOOR_20])))
- items.append(item_factory("Slingshot"))
- items.append(item_factory(random.choice(items_by_group[Group.MINES_FLOOR_50])))
- items.append(item_factory(random.choice(items_by_group[Group.MINES_FLOOR_60])))
- items.append(item_factory("Master Slingshot"))
- items.append(item_factory(random.choice(items_by_group[Group.MINES_FLOOR_80])))
- items.append(item_factory(random.choice(items_by_group[Group.MINES_FLOOR_90])))
- items.append(item_factory(random.choice(items_by_group[Group.MINES_FLOOR_110])))
- items.append(item_factory("Skull Key"))
+def create_weapons(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
+ weapons = weapons_count(options)
+ items.extend(item_factory(item) for item in [APWeapon.slingshot] * 2)
+ monstersanity = options.monstersanity
+ if monstersanity == Monstersanity.option_none: # Without monstersanity, might not be enough checks to split the weapons
+ items.extend(item_factory(item) for item in [APWeapon.weapon] * weapons)
+ items.extend(item_factory(item) for item in [APWeapon.footwear] * 3) # 1-2 | 3-4 | 6-7-8
+ return
+
+ items.extend(item_factory(item) for item in [APWeapon.sword] * weapons)
+ items.extend(item_factory(item) for item in [APWeapon.club] * weapons)
+ items.extend(item_factory(item) for item in [APWeapon.dagger] * weapons)
+ items.extend(item_factory(item) for item in [APWeapon.footwear] * 4) # 1-2 | 3-4 | 6-7-8 | 11-13
+ if monstersanity == Monstersanity.option_goals or monstersanity == Monstersanity.option_one_per_category or \
+ monstersanity == Monstersanity.option_short_goals or monstersanity == Monstersanity.option_very_short_goals:
+ return
+ if options.exclude_ginger_island == ExcludeGingerIsland.option_true:
+ rings_items = [item for item in items_by_group[Group.RING] if item.classification is not ItemClassification.filler]
+ else:
+ rings_items = [item for item in items_by_group[Group.RING]]
+ items.extend(item_factory(item) for item in rings_items)
def create_elevators(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
@@ -232,8 +302,14 @@ def create_elevators(item_factory: StardewItemFactory, options: StardewValleyOpt
def create_tools(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
- if options.tool_progression == ToolProgression.option_progressive:
- items.extend(item_factory(item) for item in items_by_group[Group.PROGRESSIVE_TOOLS] * 4)
+ if options.tool_progression & ToolProgression.option_progressive:
+ for item_data in items_by_group[Group.PROGRESSIVE_TOOLS]:
+ name = item_data.name
+ if "Trash Can" in name:
+ items.extend([item_factory(item) for item in [item_data] * 3])
+ items.append(item_factory(item_data, ItemClassification.useful))
+ else:
+ items.extend([item_factory(item) for item in [item_data] * 4])
items.append(item_factory("Golden Scythe"))
@@ -246,11 +322,12 @@ def create_skills(item_factory: StardewItemFactory, options: StardewValleyOption
def create_wizard_buildings(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
- items.append(item_factory("Earth Obelisk"))
- items.append(item_factory("Water Obelisk"))
+ useless_buildings_classification = ItemClassification.progression_skip_balancing if world_is_perfection(options) else ItemClassification.useful
+ items.append(item_factory("Earth Obelisk", useless_buildings_classification))
+ items.append(item_factory("Water Obelisk", useless_buildings_classification))
items.append(item_factory("Desert Obelisk"))
items.append(item_factory("Junimo Hut"))
- items.append(item_factory("Gold Clock"))
+ items.append(item_factory("Gold Clock", useless_buildings_classification))
if options.exclude_ginger_island == ExcludeGingerIsland.option_false:
items.append(item_factory("Island Obelisk"))
if ModNames.deepwoods in options.mods:
@@ -258,89 +335,107 @@ def create_wizard_buildings(item_factory: StardewItemFactory, options: StardewVa
def create_carpenter_buildings(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
- if options.building_progression in {BuildingProgression.option_progressive,
- BuildingProgression.option_progressive_early_shipping_bin}:
- items.append(item_factory("Progressive Coop"))
- items.append(item_factory("Progressive Coop"))
- items.append(item_factory("Progressive Coop"))
- items.append(item_factory("Progressive Barn"))
- items.append(item_factory("Progressive Barn"))
- items.append(item_factory("Progressive Barn"))
- items.append(item_factory("Well"))
- items.append(item_factory("Silo"))
- items.append(item_factory("Mill"))
- items.append(item_factory("Progressive Shed"))
- items.append(item_factory("Progressive Shed"))
- items.append(item_factory("Fish Pond"))
- items.append(item_factory("Stable"))
- items.append(item_factory("Slime Hutch"))
- items.append(item_factory("Shipping Bin"))
- items.append(item_factory("Progressive House"))
- items.append(item_factory("Progressive House"))
- items.append(item_factory("Progressive House"))
- if ModNames.tractor in options.mods:
- items.append(item_factory("Tractor Garage"))
-
-
-def create_special_quest_rewards(item_factory: StardewItemFactory, items: List[Item]):
- items.append(item_factory("Adventurer's Guild"))
- items.append(item_factory("Club Card"))
- items.append(item_factory("Magnifying Glass"))
- items.append(item_factory("Bear's Knowledge"))
- items.append(item_factory("Iridium Snake Milk"))
+ building_option = options.building_progression
+ if not building_option & BuildingProgression.option_progressive:
+ return
+ items.append(item_factory("Progressive Coop"))
+ items.append(item_factory("Progressive Coop"))
+ items.append(item_factory("Progressive Coop"))
+ items.append(item_factory("Progressive Barn"))
+ items.append(item_factory("Progressive Barn"))
+ items.append(item_factory("Progressive Barn"))
+ items.append(item_factory("Well"))
+ items.append(item_factory("Silo"))
+ items.append(item_factory("Mill"))
+ items.append(item_factory("Progressive Shed"))
+ items.append(item_factory("Progressive Shed", ItemClassification.useful))
+ items.append(item_factory("Fish Pond"))
+ items.append(item_factory("Stable"))
+ items.append(item_factory("Slime Hutch"))
+ items.append(item_factory("Shipping Bin"))
+ items.append(item_factory("Progressive House"))
+ items.append(item_factory("Progressive House"))
+ items.append(item_factory("Progressive House"))
+ if ModNames.tractor in options.mods:
+ items.append(item_factory("Tractor Garage"))
+
+
+def create_special_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
+ if options.quest_locations < 0:
+ return
+ # items.append(item_factory("Adventurer's Guild")) # Now unlocked always!
+ items.append(item_factory(Wallet.club_card))
+ items.append(item_factory(Wallet.magnifying_glass))
+ if ModNames.sve in options.mods:
+ items.append(item_factory(Wallet.bears_knowledge))
+ else:
+ items.append(item_factory(Wallet.bears_knowledge, ItemClassification.useful)) # Not necessary outside of SVE
+ items.append(item_factory(Wallet.iridium_snake_milk))
+ items.append(item_factory("Fairy Dust Recipe"))
+ items.append(item_factory("Dark Talisman"))
+ create_special_quest_rewards_sve(item_factory, options, items)
+ create_distant_lands_quest_rewards(item_factory, options, items)
+ create_boarding_house_quest_rewards(item_factory, options, items)
def create_stardrops(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
- items.append(item_factory("Stardrop")) # The Mines level 100
- items.append(item_factory("Stardrop")) # Old Master Cannoli
+ stardrops_classification = get_stardrop_classification(options)
+ items.append(item_factory("Stardrop", stardrops_classification)) # The Mines level 100
+ items.append(item_factory("Stardrop", stardrops_classification)) # Old Master Cannoli
+ items.append(item_factory("Stardrop", stardrops_classification)) # Krobus Stardrop
if options.fishsanity != Fishsanity.option_none:
- items.append(item_factory("Stardrop")) #Master Angler Stardrop
+ items.append(item_factory("Stardrop", stardrops_classification)) # Master Angler Stardrop
if ModNames.deepwoods in options.mods:
- items.append(item_factory("Stardrop")) # Petting the Unicorn
+ items.append(item_factory("Stardrop", stardrops_classification)) # Petting the Unicorn
+ if options.friendsanity != Friendsanity.option_none:
+ items.append(item_factory("Stardrop", stardrops_classification)) # Spouse Stardrop
def create_museum_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
- items.append(item_factory("Rusty Key"))
- items.append(item_factory("Dwarvish Translation Guide"))
+ items.append(item_factory(Wallet.rusty_key))
+ items.append(item_factory(Wallet.dwarvish_translation_guide))
items.append(item_factory("Ancient Seeds Recipe"))
+ items.append(item_factory("Stardrop", get_stardrop_classification(options)))
if options.museumsanity == Museumsanity.option_none:
return
items.extend(item_factory(item) for item in ["Magic Rock Candy"] * 10)
items.extend(item_factory(item) for item in ["Ancient Seeds"] * 5)
- items.extend(item_factory(item) for item in ["Traveling Merchant Metal Detector"] * 4)
- items.append(item_factory("Stardrop"))
+ items.append(item_factory(Wallet.metal_detector))
-def create_friendsanity_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
+def create_friendsanity_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item], random: Random):
+ island_villagers = [NPC.leo, ModNPC.lance]
if options.friendsanity == Friendsanity.option_none:
return
+ create_babies(item_factory, items, random)
exclude_non_bachelors = options.friendsanity == Friendsanity.option_bachelors
exclude_locked_villagers = options.friendsanity == Friendsanity.option_starting_npcs or \
options.friendsanity == Friendsanity.option_bachelors
include_post_marriage_hearts = options.friendsanity == Friendsanity.option_all_with_marriage
exclude_ginger_island = options.exclude_ginger_island == ExcludeGingerIsland.option_true
+ mods = options.mods
heart_size = options.friendsanity_heart_size
- for villager in all_villagers:
- if villager.mod_name not in options.mods and villager.mod_name is not None:
- continue
+ for villager in get_villagers_for_mods(mods.value):
if not villager.available and exclude_locked_villagers:
continue
if not villager.bachelor and exclude_non_bachelors:
continue
- if villager.name == "Leo" and exclude_ginger_island:
+ if villager.name in island_villagers and exclude_ginger_island:
continue
heart_cap = 8 if villager.bachelor else 10
if include_post_marriage_hearts and villager.bachelor:
heart_cap = 14
+ classification = ItemClassification.progression
for heart in range(1, 15):
if heart > heart_cap:
break
if heart % heart_size == 0 or heart == heart_cap:
- items.append(item_factory(f"{villager.name} <3"))
+ items.append(item_factory(f"{villager.name} <3", classification))
if not exclude_non_bachelors:
+ need_pet = options.goal == Goal.option_grandpa_evaluation
for heart in range(1, 6):
if heart % heart_size == 0 or heart == 5:
- items.append(item_factory(f"Pet <3"))
+ items.append(item_factory(f"Pet <3", ItemClassification.progression_skip_balancing if need_pet else ItemClassification.useful))
def create_babies(item_factory: StardewItemFactory, items: List[Item], random: Random):
@@ -368,8 +463,19 @@ def create_arcade_machine_items(item_factory: StardewItemFactory, options: Stard
def create_player_buffs(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
- items.extend(item_factory(item) for item in [Buff.movement] * options.movement_buff_number.value)
- items.extend(item_factory(item) for item in [Buff.luck] * options.luck_buff_number.value)
+ movement_buffs: int = options.movement_buff_number.value
+ luck_buffs: int = options.luck_buff_number.value
+ need_all_buffs = options.special_order_locations == SpecialOrderLocations.option_board_qi
+ need_half_buffs = options.festival_locations == FestivalLocations.option_easy
+ create_player_buff(item_factory, Buff.movement, movement_buffs, need_all_buffs, need_half_buffs, items)
+ create_player_buff(item_factory, Buff.luck, luck_buffs, True, need_half_buffs, items)
+
+
+def create_player_buff(item_factory, buff: str, amount: int, need_all_buffs: bool, need_half_buffs: bool, items: List[Item]):
+ progression_buffs = amount if need_all_buffs else (amount // 2 if need_half_buffs else 0)
+ useful_buffs = amount - progression_buffs
+ items.extend(item_factory(item) for item in [buff] * progression_buffs)
+ items.extend(item_factory(item, ItemClassification.useful) for item in [buff] * useful_buffs)
def create_traveling_merchant_items(item_factory: StardewItemFactory, items: List[Item]):
@@ -393,17 +499,19 @@ def create_seeds(item_factory: StardewItemFactory, options: StardewValleyOptions
if options.cropsanity == Cropsanity.option_disabled:
return
- include_ginger_island = options.exclude_ginger_island != ExcludeGingerIsland.option_true
- seed_items = [item_factory(item) for item in items_by_group[Group.CROPSANITY] if include_ginger_island or Group.GINGER_ISLAND not in item.groups]
+ base_seed_items = [item for item in items_by_group[Group.CROPSANITY]]
+ filtered_seed_items = remove_excluded_items(base_seed_items, options)
+ seed_items = [item_factory(item) for item in filtered_seed_items]
items.extend(seed_items)
def create_festival_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
+ items.append(item_factory("Deluxe Scarecrow Recipe"))
if options.festival_locations == FestivalLocations.option_disabled:
return
- items.extend([*[item_factory(item) for item in items_by_group[Group.FESTIVAL] if item.classification != ItemClassification.filler],
- item_factory("Stardrop")])
+ festival_rewards = [item_factory(item) for item in items_by_group[Group.FESTIVAL] if item.classification != ItemClassification.filler]
+ items.extend([*festival_rewards, item_factory("Stardrop", get_stardrop_classification(options))])
def create_walnut_purchase_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
@@ -417,26 +525,102 @@ def create_walnut_purchase_rewards(item_factory: StardewItemFactory, options: St
*[item_factory(item) for item in items_by_group[Group.WALNUT_PURCHASE]]])
-
def create_special_order_board_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
if options.special_order_locations == SpecialOrderLocations.option_disabled:
return
- items.extend([item_factory(item) for item in items_by_group[Group.SPECIAL_ORDER_BOARD]])
+ special_order_board_items = [item for item in items_by_group[Group.SPECIAL_ORDER_BOARD]]
+
+ items.extend([item_factory(item) for item in special_order_board_items])
+
+
+def special_order_board_item_classification(item: ItemData, need_all_recipes: bool) -> ItemClassification:
+ if item.classification is ItemClassification.useful:
+ return ItemClassification.useful
+ if item.name == "Special Order Board":
+ return ItemClassification.progression
+ if need_all_recipes and "Recipe" in item.name:
+ return ItemClassification.progression_skip_balancing
+ if item.name == "Monster Musk Recipe":
+ return ItemClassification.progression_skip_balancing
+ return ItemClassification.useful
def create_special_order_qi_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
- if (options.special_order_locations != SpecialOrderLocations.option_board_qi or
- options.exclude_ginger_island == ExcludeGingerIsland.option_true):
+ if options.exclude_ginger_island == ExcludeGingerIsland.option_true:
return
- qi_gem_rewards = ["100 Qi Gems", "10 Qi Gems", "40 Qi Gems", "25 Qi Gems", "25 Qi Gems",
- "40 Qi Gems", "20 Qi Gems", "50 Qi Gems", "40 Qi Gems", "35 Qi Gems"]
+ qi_gem_rewards = []
+ if options.bundle_randomization >= BundleRandomization.option_remixed:
+ qi_gem_rewards.append("15 Qi Gems")
+ qi_gem_rewards.append("15 Qi Gems")
+
+ if options.special_order_locations == SpecialOrderLocations.option_board_qi:
+ qi_gem_rewards.extend(["100 Qi Gems", "10 Qi Gems", "40 Qi Gems", "25 Qi Gems", "25 Qi Gems",
+ "40 Qi Gems", "20 Qi Gems", "50 Qi Gems", "40 Qi Gems", "35 Qi Gems"])
+
qi_gem_items = [item_factory(reward) for reward in qi_gem_rewards]
items.extend(qi_gem_items)
-def create_tv_channels(item_factory: StardewItemFactory, items: List[Item]):
- items.extend([item_factory(item) for item in items_by_group[Group.TV_CHANNEL]])
+def create_tv_channels(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
+ channels = [channel for channel in items_by_group[Group.TV_CHANNEL]]
+ if options.entrance_randomization == EntranceRandomization.option_disabled:
+ channels = [channel for channel in channels if channel.name != "The Gateway Gazette"]
+ items.extend([item_factory(item) for item in channels])
+
+
+def create_crafting_recipes(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
+ has_craftsanity = options.craftsanity == Craftsanity.option_all
+ crafting_recipes = []
+ crafting_recipes.extend([recipe for recipe in items_by_group[Group.QI_CRAFTING_RECIPE]])
+ if has_craftsanity:
+ crafting_recipes.extend([recipe for recipe in items_by_group[Group.CRAFTSANITY]])
+ crafting_recipes = remove_excluded_items(crafting_recipes, options)
+ items.extend([item_factory(item) for item in crafting_recipes])
+
+
+def create_cooking_recipes(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
+ chefsanity = options.chefsanity
+ if chefsanity == Chefsanity.option_none:
+ return
+
+ chefsanity_recipes_by_name = {recipe.name: recipe for recipe in items_by_group[Group.CHEFSANITY_STARTER]} # Dictionary to not make duplicates
+
+ if chefsanity & Chefsanity.option_queen_of_sauce:
+ chefsanity_recipes_by_name.update({recipe.name: recipe for recipe in items_by_group[Group.CHEFSANITY_QOS]})
+ if chefsanity & Chefsanity.option_purchases:
+ chefsanity_recipes_by_name.update({recipe.name: recipe for recipe in items_by_group[Group.CHEFSANITY_PURCHASE]})
+ if chefsanity & Chefsanity.option_friendship:
+ chefsanity_recipes_by_name.update({recipe.name: recipe for recipe in items_by_group[Group.CHEFSANITY_FRIENDSHIP]})
+ if chefsanity & Chefsanity.option_skills:
+ chefsanity_recipes_by_name.update({recipe.name: recipe for recipe in items_by_group[Group.CHEFSANITY_SKILL]})
+
+ filtered_chefsanity_recipes = remove_excluded_items(list(chefsanity_recipes_by_name.values()), options)
+ items.extend([item_factory(item) for item in filtered_chefsanity_recipes])
+
+
+def create_shipsanity_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
+ shipsanity = options.shipsanity
+ if shipsanity != Shipsanity.option_everything:
+ return
+
+ items.append(item_factory(Wallet.metal_detector))
+
+
+def create_goal_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
+ goal = options.goal
+ if goal != Goal.option_perfection and goal != Goal.option_complete_collection:
+ return
+
+ items.append(item_factory(Wallet.metal_detector))
+
+
+def create_archaeology_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
+ mods = options.mods
+ if ModNames.archaeology not in mods:
+ return
+
+ items.append(item_factory(Wallet.metal_detector))
def create_filler_festival_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions) -> List[Item]:
@@ -449,10 +633,47 @@ def create_filler_festival_rewards(item_factory: StardewItemFactory, options: St
def create_magic_mod_spells(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
if ModNames.magic not in options.mods:
- return []
+ return
items.extend([item_factory(item) for item in items_by_group[Group.MAGIC_SPELL]])
+def create_deepwoods_pendants(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
+ if ModNames.deepwoods not in options.mods:
+ return
+ items.extend([item_factory(item) for item in ["Pendant of Elders", "Pendant of Community", "Pendant of Depths"]])
+
+
+def create_special_quest_rewards_sve(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
+ if ModNames.sve not in options.mods:
+ return
+
+ items.extend([item_factory(item) for item in items_by_group[Group.MOD_WARP] if item.mod_name == ModNames.sve])
+
+ if options.quest_locations < 0:
+ return
+
+ exclude_ginger_island = options.exclude_ginger_island == ExcludeGingerIsland.option_true
+ items.extend([item_factory(item) for item in SVEQuestItem.sve_quest_items])
+ if exclude_ginger_island:
+ return
+ items.extend([item_factory(item) for item in SVEQuestItem.sve_quest_items_ginger_island])
+
+
+def create_distant_lands_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
+ if options.quest_locations < 0 or ModNames.distant_lands not in options.mods:
+ return
+ items.append(item_factory("Crayfish Soup Recipe"))
+ if options.exclude_ginger_island == ExcludeGingerIsland.option_true:
+ return
+ items.append(item_factory("Ginger Tincture Recipe"))
+
+
+def create_boarding_house_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
+ if options.quest_locations < 0 or ModNames.boarding_house not in options.mods:
+ return
+ items.append(item_factory("Special Pumpkin Soup Recipe"))
+
+
def create_unique_filler_items(item_factory: StardewItemFactory, options: StardewValleyOptions, random: Random,
available_item_slots: int) -> List[Item]:
items = []
@@ -464,6 +685,13 @@ def create_unique_filler_items(item_factory: StardewItemFactory, options: Starde
return items
+def weapons_count(options: StardewValleyOptions):
+ weapon_count = 5
+ if ModNames.sve in options.mods:
+ weapon_count += 1
+ return weapon_count
+
+
def fill_with_resource_packs_and_traps(item_factory: StardewItemFactory, options: StardewValleyOptions, random: Random,
items_already_added: List[Item],
number_locations: int) -> List[Item]:
@@ -477,27 +705,31 @@ def fill_with_resource_packs_and_traps(item_factory: StardewItemFactory, options
priority_filler_items = []
priority_filler_items.extend(useful_resource_packs)
+
if include_traps:
priority_filler_items.extend(trap_items)
exclude_ginger_island = options.exclude_ginger_island == ExcludeGingerIsland.option_true
- all_filler_packs = get_all_filler_items(include_traps, exclude_ginger_island)
- priority_filler_items = remove_excluded_packs(priority_filler_items, exclude_ginger_island)
+ all_filler_packs = remove_excluded_items(get_all_filler_items(include_traps, exclude_ginger_island), options)
+ priority_filler_items = remove_excluded_items(priority_filler_items, options)
number_priority_items = len(priority_filler_items)
required_resource_pack = number_locations - len(items_already_added)
if required_resource_pack < number_priority_items:
chosen_priority_items = [item_factory(resource_pack) for resource_pack in
- random.sample(priority_filler_items, required_resource_pack)]
+ random.sample(priority_filler_items, required_resource_pack)]
return chosen_priority_items
items = []
- chosen_priority_items = [item_factory(resource_pack) for resource_pack in priority_filler_items]
+ chosen_priority_items = [item_factory(resource_pack,
+ ItemClassification.trap if resource_pack.classification == ItemClassification.trap else ItemClassification.useful)
+ for resource_pack in priority_filler_items]
items.extend(chosen_priority_items)
required_resource_pack -= number_priority_items
all_filler_packs = [filler_pack for filler_pack in all_filler_packs
if Group.MAXIMUM_ONE not in filler_pack.groups or
- filler_pack.name not in [priority_item.name for priority_item in priority_filler_items]]
+ (filler_pack.name not in [priority_item.name for priority_item in
+ priority_filler_items] and filler_pack.name not in items_already_added_names)]
while required_resource_pack > 0:
resource_pack = random.choice(all_filler_packs)
@@ -505,10 +737,11 @@ def fill_with_resource_packs_and_traps(item_factory: StardewItemFactory, options
while exactly_2 and required_resource_pack == 1:
resource_pack = random.choice(all_filler_packs)
exactly_2 = Group.EXACTLY_TWO in resource_pack.groups
- items.append(item_factory(resource_pack))
+ classification = ItemClassification.useful if resource_pack.classification == ItemClassification.progression else resource_pack.classification
+ items.append(item_factory(resource_pack, classification))
required_resource_pack -= 1
if exactly_2:
- items.append(item_factory(resource_pack))
+ items.append(item_factory(resource_pack, classification))
required_resource_pack -= 1
if exactly_2 or Group.MAXIMUM_ONE in resource_pack.groups:
all_filler_packs.remove(resource_pack)
@@ -516,11 +749,27 @@ def fill_with_resource_packs_and_traps(item_factory: StardewItemFactory, options
return items
-def remove_excluded_packs(packs, exclude_ginger_island: bool):
- included_packs = [pack for pack in packs if Group.DEPRECATED not in pack.groups]
- if exclude_ginger_island:
- included_packs = [pack for pack in included_packs if Group.GINGER_ISLAND not in pack.groups]
- return included_packs
+def filter_deprecated_items(items: List[ItemData]) -> List[ItemData]:
+ return [item for item in items if Group.DEPRECATED not in item.groups]
+
+
+def filter_ginger_island_items(exclude_island: bool, items: List[ItemData]) -> List[ItemData]:
+ return [item for item in items if not exclude_island or Group.GINGER_ISLAND not in item.groups]
+
+
+def filter_mod_items(mods: Set[str], items: List[ItemData]) -> List[ItemData]:
+ return [item for item in items if item.mod_name is None or item.mod_name in mods]
+
+
+def remove_excluded_items(items, options: StardewValleyOptions):
+ return remove_excluded_items_island_mods(items, options.exclude_ginger_island == ExcludeGingerIsland.option_true, options.mods.value)
+
+
+def remove_excluded_items_island_mods(items, exclude_ginger_island: bool, mods: Set[str]):
+ deprecated_filter = filter_deprecated_items(items)
+ ginger_island_filter = filter_ginger_island_items(exclude_ginger_island, deprecated_filter)
+ mod_filter = filter_mod_items(mods, ginger_island_filter)
+ return mod_filter
def remove_limited_amount_packs(packs):
@@ -528,9 +777,21 @@ def remove_limited_amount_packs(packs):
def get_all_filler_items(include_traps: bool, exclude_ginger_island: bool):
- all_filler_packs = [pack for pack in items_by_group[Group.RESOURCE_PACK]]
- all_filler_packs.extend(items_by_group[Group.TRASH])
+ all_filler_items = [pack for pack in items_by_group[Group.RESOURCE_PACK]]
+ all_filler_items.extend(items_by_group[Group.TRASH])
if include_traps:
- all_filler_packs.extend(items_by_group[Group.TRAP])
- all_filler_packs = remove_excluded_packs(all_filler_packs, exclude_ginger_island)
- return all_filler_packs
+ all_filler_items.extend(items_by_group[Group.TRAP])
+ all_filler_items = remove_excluded_items_island_mods(all_filler_items, exclude_ginger_island, set())
+ return all_filler_items
+
+
+def get_stardrop_classification(options) -> ItemClassification:
+ return ItemClassification.progression_skip_balancing if world_is_perfection(options) or world_is_stardrops(options) else ItemClassification.useful
+
+
+def world_is_perfection(options) -> bool:
+ return options.goal == Goal.option_perfection
+
+
+def world_is_stardrops(options) -> bool:
+ return options.goal == Goal.option_mystery_of_the_stardrops
diff --git a/worlds/stardew_valley/locations.py b/worlds/stardew_valley/locations.py
index 345796b0311e..103b3bd96081 100644
--- a/worlds/stardew_valley/locations.py
+++ b/worlds/stardew_valley/locations.py
@@ -2,16 +2,21 @@
import enum
from dataclasses import dataclass
from random import Random
-from typing import Optional, Dict, Protocol, List, FrozenSet
+from typing import Optional, Dict, Protocol, List, FrozenSet, Iterable
from . import data
-from .options import StardewValleyOptions
-from .data.fish_data import legendary_fish, special_fish, all_fish
+from .bundles.bundle_room import BundleRoom
+from .data.fish_data import special_fish, get_fish_for_mods
from .data.museum_data import all_museum_items
-from .data.villagers_data import all_villagers
-from .options import ExcludeGingerIsland, Friendsanity, ArcadeMachineLocations, SpecialOrderLocations, Cropsanity, Fishsanity, Museumsanity, FestivalLocations, SkillProgression, BuildingProgression, ToolProgression, ElevatorProgression, BackpackProgression
+from .data.villagers_data import get_villagers_for_mods
+from .mods.mod_data import ModNames
+from .options import ExcludeGingerIsland, Friendsanity, ArcadeMachineLocations, SpecialOrderLocations, Cropsanity, Fishsanity, Museumsanity, FestivalLocations, \
+ SkillProgression, BuildingProgression, ToolProgression, ElevatorProgression, BackpackProgression
+from .options import StardewValleyOptions, Craftsanity, Chefsanity, Cooksanity, Shipsanity, Monstersanity
from .strings.goal_names import Goal
+from .strings.quest_names import ModQuest
from .strings.region_names import Region
+from .strings.villager_names import NPC, ModNPC
LOCATION_CODE_OFFSET = 717000
@@ -45,7 +50,7 @@ class LocationTags(enum.Enum):
COMBAT_LEVEL = enum.auto()
MINING_LEVEL = enum.auto()
BUILDING_BLUEPRINT = enum.auto()
- QUEST = enum.auto()
+ STORY_QUEST = enum.auto()
ARCADE_MACHINE = enum.auto()
ARCADE_MACHINE_VICTORY = enum.auto()
JOTPK = enum.auto()
@@ -60,8 +65,29 @@ class LocationTags(enum.Enum):
FESTIVAL_HARD = enum.auto()
SPECIAL_ORDER_BOARD = enum.auto()
SPECIAL_ORDER_QI = enum.auto()
+ REQUIRES_QI_ORDERS = enum.auto()
GINGER_ISLAND = enum.auto()
WALNUT_PURCHASE = enum.auto()
+
+ BABY = enum.auto()
+ MONSTERSANITY = enum.auto()
+ MONSTERSANITY_GOALS = enum.auto()
+ MONSTERSANITY_PROGRESSIVE_GOALS = enum.auto()
+ MONSTERSANITY_MONSTER = enum.auto()
+ SHIPSANITY = enum.auto()
+ SHIPSANITY_CROP = enum.auto()
+ SHIPSANITY_FISH = enum.auto()
+ SHIPSANITY_FULL_SHIPMENT = enum.auto()
+ COOKSANITY = enum.auto()
+ COOKSANITY_QOS = enum.auto()
+ CHEFSANITY = enum.auto()
+ CHEFSANITY_QOS = enum.auto()
+ CHEFSANITY_PURCHASE = enum.auto()
+ CHEFSANITY_FRIENDSHIP = enum.auto()
+ CHEFSANITY_SKILL = enum.auto()
+ CHEFSANITY_STARTER = enum.auto()
+ CRAFTSANITY = enum.auto()
+ # Mods
# Skill Mods
LUCK_LEVEL = enum.auto()
BINNING_LEVEL = enum.auto()
@@ -112,10 +138,17 @@ def load_location_csv() -> List[LocationData]:
LocationData(None, Region.community_center, Goal.community_center),
LocationData(None, Region.mines_floor_120, Goal.bottom_of_the_mines),
LocationData(None, Region.skull_cavern_100, Goal.cryptic_note),
- LocationData(None, Region.farm, Goal.master_angler),
+ LocationData(None, Region.beach, Goal.master_angler),
LocationData(None, Region.museum, Goal.complete_museum),
LocationData(None, Region.farm_house, Goal.full_house),
LocationData(None, Region.island_west, Goal.greatest_walnut_hunter),
+ LocationData(None, Region.adventurer_guild, Goal.protector_of_the_valley),
+ LocationData(None, Region.shipping, Goal.full_shipment),
+ LocationData(None, Region.kitchen, Goal.gourmet_chef),
+ LocationData(None, Region.farm, Goal.craft_master),
+ LocationData(None, Region.shipping, Goal.legend),
+ LocationData(None, Region.farm, Goal.mystery_of_the_stardrops),
+ LocationData(None, Region.farm, Goal.allsanity),
LocationData(None, Region.qi_walnut_room, Goal.perfection),
]
@@ -139,13 +172,20 @@ def extend_cropsanity_locations(randomized_locations: List[LocationData], option
if options.cropsanity == Cropsanity.option_disabled:
return
- cropsanity_locations = locations_by_tag[LocationTags.CROPSANITY]
+ cropsanity_locations = [item for item in locations_by_tag[LocationTags.CROPSANITY] if not item.mod_name or item.mod_name in options.mods]
cropsanity_locations = filter_ginger_island(options, cropsanity_locations)
randomized_locations.extend(cropsanity_locations)
-def extend_help_wanted_quests(randomized_locations: List[LocationData], desired_number_of_quests: int):
- for i in range(0, desired_number_of_quests):
+def extend_quests_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
+ if options.quest_locations < 0:
+ return
+
+ story_quest_locations = locations_by_tag[LocationTags.STORY_QUEST]
+ story_quest_locations = filter_disabled_locations(options, story_quest_locations)
+ randomized_locations.extend(story_quest_locations)
+
+ for i in range(0, options.quest_locations.value):
batch = i // 7
index_this_batch = i % 7
if index_this_batch < 4:
@@ -161,27 +201,30 @@ def extend_help_wanted_quests(randomized_locations: List[LocationData], desired_
def extend_fishsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, random: Random):
prefix = "Fishsanity: "
- if options.fishsanity == Fishsanity.option_none:
+ fishsanity = options.fishsanity
+ active_fish = get_fish_for_mods(options.mods.value)
+ if fishsanity == Fishsanity.option_none:
return
- elif options.fishsanity == Fishsanity.option_legendaries:
- randomized_locations.extend(location_table[f"{prefix}{legendary.name}"] for legendary in legendary_fish)
- elif options.fishsanity == Fishsanity.option_special:
+ elif fishsanity == Fishsanity.option_legendaries:
+ fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in active_fish if fish.legendary]
+ randomized_locations.extend(filter_disabled_locations(options, fish_locations))
+ elif fishsanity == Fishsanity.option_special:
randomized_locations.extend(location_table[f"{prefix}{special.name}"] for special in special_fish)
- elif options.fishsanity == Fishsanity.option_randomized:
- fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish if random.random() < 0.4]
- randomized_locations.extend(filter_ginger_island(options, fish_locations))
- elif options.fishsanity == Fishsanity.option_all:
- fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish]
- randomized_locations.extend(filter_ginger_island(options, fish_locations))
- elif options.fishsanity == Fishsanity.option_exclude_legendaries:
- fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish if fish not in legendary_fish]
- randomized_locations.extend(filter_ginger_island(options, fish_locations))
- elif options.fishsanity == Fishsanity.option_exclude_hard_fish:
- fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish if fish.difficulty < 80]
- randomized_locations.extend(filter_ginger_island(options, fish_locations))
+ elif fishsanity == Fishsanity.option_randomized:
+ fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in active_fish if random.random() < 0.4]
+ randomized_locations.extend(filter_disabled_locations(options, fish_locations))
+ elif fishsanity == Fishsanity.option_all:
+ fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in active_fish]
+ randomized_locations.extend(filter_disabled_locations(options, fish_locations))
+ elif fishsanity == Fishsanity.option_exclude_legendaries:
+ fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in active_fish if not fish.legendary]
+ randomized_locations.extend(filter_disabled_locations(options, fish_locations))
+ elif fishsanity == Fishsanity.option_exclude_hard_fish:
+ fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in active_fish if fish.difficulty < 80]
+ randomized_locations.extend(filter_disabled_locations(options, fish_locations))
elif options.fishsanity == Fishsanity.option_only_easy_fish:
- fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish if fish.difficulty < 50]
- randomized_locations.extend(filter_ginger_island(options, fish_locations))
+ fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in active_fish if fish.difficulty < 50]
+ randomized_locations.extend(filter_disabled_locations(options, fish_locations))
def extend_museumsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, random: Random):
@@ -191,30 +234,31 @@ def extend_museumsanity_locations(randomized_locations: List[LocationData], opti
elif options.museumsanity == Museumsanity.option_milestones:
randomized_locations.extend(locations_by_tag[LocationTags.MUSEUM_MILESTONES])
elif options.museumsanity == Museumsanity.option_randomized:
- randomized_locations.extend(location_table[f"{prefix}{museum_item.name}"]
+ randomized_locations.extend(location_table[f"{prefix}{museum_item.item_name}"]
for museum_item in all_museum_items if random.random() < 0.4)
elif options.museumsanity == Museumsanity.option_all:
- randomized_locations.extend(location_table[f"{prefix}{museum_item.name}"] for museum_item in all_museum_items)
+ randomized_locations.extend(location_table[f"{prefix}{museum_item.item_name}"] for museum_item in all_museum_items)
def extend_friendsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
+ island_villagers = [NPC.leo, ModNPC.lance]
if options.friendsanity == Friendsanity.option_none:
return
- exclude_leo = options.exclude_ginger_island == ExcludeGingerIsland.option_true
+ randomized_locations.append(location_table[f"Spouse Stardrop"])
+ extend_baby_locations(randomized_locations)
+ exclude_ginger_island = options.exclude_ginger_island == ExcludeGingerIsland.option_true
exclude_non_bachelors = options.friendsanity == Friendsanity.option_bachelors
exclude_locked_villagers = options.friendsanity == Friendsanity.option_starting_npcs or \
options.friendsanity == Friendsanity.option_bachelors
include_post_marriage_hearts = options.friendsanity == Friendsanity.option_all_with_marriage
heart_size = options.friendsanity_heart_size
- for villager in all_villagers:
- if villager.mod_name not in options.mods and villager.mod_name is not None:
- continue
+ for villager in get_villagers_for_mods(options.mods.value):
if not villager.available and exclude_locked_villagers:
continue
if not villager.bachelor and exclude_non_bachelors:
continue
- if villager.name == "Leo" and exclude_leo:
+ if villager.name in island_villagers and exclude_ginger_island:
continue
heart_cap = 8 if villager.bachelor else 10
if include_post_marriage_hearts and villager.bachelor:
@@ -230,6 +274,11 @@ def extend_friendsanity_locations(randomized_locations: List[LocationData], opti
randomized_locations.append(location_table[f"Friendsanity: Pet {heart} <3"])
+def extend_baby_locations(randomized_locations: List[LocationData]):
+ baby_locations = [location for location in locations_by_tag[LocationTags.BABY]]
+ randomized_locations.extend(baby_locations)
+
+
def extend_festival_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
if options.festival_locations == FestivalLocations.option_disabled:
return
@@ -256,7 +305,8 @@ def extend_special_order_locations(randomized_locations: List[LocationData], opt
randomized_locations.extend(board_locations)
if options.special_order_locations == SpecialOrderLocations.option_board_qi and include_island:
include_arcade = options.arcade_machine_locations != ArcadeMachineLocations.option_disabled
- qi_orders = [location for location in locations_by_tag[LocationTags.SPECIAL_ORDER_QI] if include_arcade or LocationTags.JUNIMO_KART not in location.tags]
+ qi_orders = [location for location in locations_by_tag[LocationTags.SPECIAL_ORDER_QI] if
+ include_arcade or LocationTags.JUNIMO_KART not in location.tags]
randomized_locations.extend(qi_orders)
@@ -271,12 +321,31 @@ def extend_walnut_purchase_locations(randomized_locations: List[LocationData], o
randomized_locations.extend(locations_by_tag[LocationTags.WALNUT_PURCHASE])
-def extend_mandatory_locations(randomized_locations: List[LocationData], options):
+def extend_mandatory_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
mandatory_locations = [location for location in locations_by_tag[LocationTags.MANDATORY]]
filtered_mandatory_locations = filter_disabled_locations(options, mandatory_locations)
randomized_locations.extend(filtered_mandatory_locations)
+def extend_situational_quest_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
+ if options.quest_locations < 0:
+ return
+ if ModNames.distant_lands in options.mods:
+ if ModNames.alecto in options.mods:
+ randomized_locations.append(location_table[ModQuest.WitchOrder])
+ else:
+ randomized_locations.append(location_table[ModQuest.CorruptedCropsTask])
+
+
+def extend_bundle_locations(randomized_locations: List[LocationData], bundle_rooms: List[BundleRoom]):
+ for room in bundle_rooms:
+ room_location = f"Complete {room.name}"
+ if room_location in location_table:
+ randomized_locations.append(location_table[room_location])
+ for bundle in room.bundles:
+ randomized_locations.append(location_table[bundle.name])
+
+
def extend_backpack_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
if options.backpack_progression == BackpackProgression.option_vanilla:
return
@@ -293,15 +362,99 @@ def extend_elevator_locations(randomized_locations: List[LocationData], options:
randomized_locations.extend(filtered_elevator_locations)
+def extend_monstersanity_locations(randomized_locations: List[LocationData], options):
+ monstersanity = options.monstersanity
+ if monstersanity == Monstersanity.option_none:
+ return
+ if monstersanity == Monstersanity.option_one_per_monster or monstersanity == Monstersanity.option_split_goals:
+ monster_locations = [location for location in locations_by_tag[LocationTags.MONSTERSANITY_MONSTER]]
+ filtered_monster_locations = filter_disabled_locations(options, monster_locations)
+ randomized_locations.extend(filtered_monster_locations)
+ return
+ goal_locations = [location for location in locations_by_tag[LocationTags.MONSTERSANITY_GOALS]]
+ filtered_goal_locations = filter_disabled_locations(options, goal_locations)
+ randomized_locations.extend(filtered_goal_locations)
+ if monstersanity != Monstersanity.option_progressive_goals:
+ return
+ progressive_goal_locations = [location for location in locations_by_tag[LocationTags.MONSTERSANITY_PROGRESSIVE_GOALS]]
+ filtered_progressive_goal_locations = filter_disabled_locations(options, progressive_goal_locations)
+ randomized_locations.extend(filtered_progressive_goal_locations)
+
+
+def extend_shipsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
+ shipsanity = options.shipsanity
+ if shipsanity == Shipsanity.option_none:
+ return
+ if shipsanity == Shipsanity.option_everything:
+ ship_locations = [location for location in locations_by_tag[LocationTags.SHIPSANITY]]
+ filtered_ship_locations = filter_disabled_locations(options, ship_locations)
+ randomized_locations.extend(filtered_ship_locations)
+ return
+ shipsanity_locations = set()
+ if shipsanity == Shipsanity.option_fish or shipsanity == Shipsanity.option_full_shipment_with_fish:
+ shipsanity_locations = shipsanity_locations.union({location for location in locations_by_tag[LocationTags.SHIPSANITY_FISH]})
+ if shipsanity == Shipsanity.option_crops:
+ shipsanity_locations = shipsanity_locations.union({location for location in locations_by_tag[LocationTags.SHIPSANITY_CROP]})
+ if shipsanity == Shipsanity.option_full_shipment or shipsanity == Shipsanity.option_full_shipment_with_fish:
+ shipsanity_locations = shipsanity_locations.union({location for location in locations_by_tag[LocationTags.SHIPSANITY_FULL_SHIPMENT]})
+
+ filtered_shipsanity_locations = filter_disabled_locations(options, list(shipsanity_locations))
+ randomized_locations.extend(filtered_shipsanity_locations)
+
+
+def extend_cooksanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
+ cooksanity = options.cooksanity
+ if cooksanity == Cooksanity.option_none:
+ return
+ if cooksanity == Cooksanity.option_queen_of_sauce:
+ cooksanity_locations = (location for location in locations_by_tag[LocationTags.COOKSANITY_QOS])
+ else:
+ cooksanity_locations = (location for location in locations_by_tag[LocationTags.COOKSANITY])
+
+ filtered_cooksanity_locations = filter_disabled_locations(options, cooksanity_locations)
+ randomized_locations.extend(filtered_cooksanity_locations)
+
+
+def extend_chefsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
+ chefsanity = options.chefsanity
+ if chefsanity == Chefsanity.option_none:
+ return
+
+ chefsanity_locations_by_name = {} # Dictionary to not make duplicates
+
+ if chefsanity & Chefsanity.option_queen_of_sauce:
+ chefsanity_locations_by_name.update({location.name: location for location in locations_by_tag[LocationTags.CHEFSANITY_QOS]})
+ if chefsanity & Chefsanity.option_purchases:
+ chefsanity_locations_by_name.update({location.name: location for location in locations_by_tag[LocationTags.CHEFSANITY_PURCHASE]})
+ if chefsanity & Chefsanity.option_friendship:
+ chefsanity_locations_by_name.update({location.name: location for location in locations_by_tag[LocationTags.CHEFSANITY_FRIENDSHIP]})
+ if chefsanity & Chefsanity.option_skills:
+ chefsanity_locations_by_name.update({location.name: location for location in locations_by_tag[LocationTags.CHEFSANITY_SKILL]})
+
+ filtered_chefsanity_locations = filter_disabled_locations(options, list(chefsanity_locations_by_name.values()))
+ randomized_locations.extend(filtered_chefsanity_locations)
+
+
+def extend_craftsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
+ if options.craftsanity == Craftsanity.option_none:
+ return
+
+ craftsanity_locations = [craft for craft in locations_by_tag[LocationTags.CRAFTSANITY]]
+ filtered_chefsanity_locations = filter_disabled_locations(options, craftsanity_locations)
+ randomized_locations.extend(filtered_chefsanity_locations)
+
+
def create_locations(location_collector: StardewLocationCollector,
+ bundle_rooms: List[BundleRoom],
options: StardewValleyOptions,
random: Random):
randomized_locations = []
extend_mandatory_locations(randomized_locations, options)
+ extend_bundle_locations(randomized_locations, bundle_rooms)
extend_backpack_locations(randomized_locations, options)
- if not options.tool_progression == ToolProgression.option_vanilla:
+ if options.tool_progression & ToolProgression.option_progressive:
randomized_locations.extend(locations_by_tag[LocationTags.TOOL_UPGRADE])
extend_elevator_locations(randomized_locations, options)
@@ -311,7 +464,7 @@ def create_locations(location_collector: StardewLocationCollector,
if location.mod_name is None or location.mod_name in options.mods:
randomized_locations.append(location_table[location.name])
- if not options.building_progression == BuildingProgression.option_vanilla:
+ if options.building_progression & BuildingProgression.option_progressive:
for location in locations_by_tag[LocationTags.BUILDING_BLUEPRINT]:
if location.mod_name is None or location.mod_name in options.mods:
randomized_locations.append(location_table[location.name])
@@ -323,7 +476,6 @@ def create_locations(location_collector: StardewLocationCollector,
randomized_locations.extend(locations_by_tag[LocationTags.ARCADE_MACHINE])
extend_cropsanity_locations(randomized_locations, options)
- extend_help_wanted_quests(randomized_locations, options.help_wanted_locations.value)
extend_fishsanity_locations(randomized_locations, options, random)
extend_museumsanity_locations(randomized_locations, options, random)
extend_friendsanity_locations(randomized_locations, options)
@@ -332,21 +484,34 @@ def create_locations(location_collector: StardewLocationCollector,
extend_special_order_locations(randomized_locations, options)
extend_walnut_purchase_locations(randomized_locations, options)
+ extend_monstersanity_locations(randomized_locations, options)
+ extend_shipsanity_locations(randomized_locations, options)
+ extend_cooksanity_locations(randomized_locations, options)
+ extend_chefsanity_locations(randomized_locations, options)
+ extend_craftsanity_locations(randomized_locations, options)
+ extend_quests_locations(randomized_locations, options)
+ extend_situational_quest_locations(randomized_locations, options)
+
for location_data in randomized_locations:
location_collector(location_data.name, location_data.code, location_data.region)
-def filter_ginger_island(options: StardewValleyOptions, locations: List[LocationData]) -> List[LocationData]:
+def filter_ginger_island(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
include_island = options.exclude_ginger_island == ExcludeGingerIsland.option_false
- return [location for location in locations if include_island or LocationTags.GINGER_ISLAND not in location.tags]
+ return (location for location in locations if include_island or LocationTags.GINGER_ISLAND not in location.tags)
+
+
+def filter_qi_order_locations(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
+ include_qi_orders = options.special_order_locations == SpecialOrderLocations.option_board_qi
+ return (location for location in locations if include_qi_orders or LocationTags.REQUIRES_QI_ORDERS not in location.tags)
-def filter_modded_locations(options: StardewValleyOptions, locations: List[LocationData]) -> List[LocationData]:
- current_mod_names = options.mods
- return [location for location in locations if location.mod_name is None or location.mod_name in current_mod_names]
+def filter_modded_locations(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
+ return (location for location in locations if location.mod_name is None or location.mod_name in options.mods)
-def filter_disabled_locations(options: StardewValleyOptions, locations: List[LocationData]) -> List[LocationData]:
- locations_first_pass = filter_ginger_island(options, locations)
- locations_second_pass = filter_modded_locations(options, locations_first_pass)
- return locations_second_pass
+def filter_disabled_locations(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
+ locations_island_filter = filter_ginger_island(options, locations)
+ locations_qi_filter = filter_qi_order_locations(options, locations_island_filter)
+ locations_mod_filter = filter_modded_locations(options, locations_qi_filter)
+ return locations_mod_filter
diff --git a/worlds/stardew_valley/logic.py b/worlds/stardew_valley/logic.py
deleted file mode 100644
index d4476a3f313a..000000000000
--- a/worlds/stardew_valley/logic.py
+++ /dev/null
@@ -1,1626 +0,0 @@
-from __future__ import annotations
-
-import math
-from dataclasses import dataclass, field
-from typing import Dict, Union, Optional, Iterable, Sized, List, Set
-
-from .data import all_fish, FishItem, all_purchasable_seeds, SeedItem, all_crops, CropItem
-from .data.bundle_data import BundleItem
-from .data.crops_data import crops_by_name
-from .data.fish_data import island_fish
-from .data.museum_data import all_museum_items, MuseumItem, all_museum_artifacts, all_museum_minerals
-from .data.recipe_data import all_cooking_recipes, CookingRecipe, RecipeSource, FriendshipSource, QueenOfSauceSource, \
- StarterSource, ShopSource, SkillSource
-from .data.villagers_data import all_villagers_by_name, Villager
-from .items import all_items, Group
-from .mods.logic.buildings import get_modded_building_rules
-from .mods.logic.quests import get_modded_quest_rules
-from .mods.logic.special_orders import get_modded_special_orders_rules
-from .mods.logic.skullcavernelevator import has_skull_cavern_elevator_to_floor
-from .mods.mod_data import ModNames
-from .mods.logic import magic, skills
-from .options import Museumsanity, SeasonRandomization, StardewValleyOptions, BuildingProgression, SkillProgression, ToolProgression, Friendsanity, Cropsanity, \
- ExcludeGingerIsland, ElevatorProgression, ArcadeMachineLocations, FestivalLocations, SpecialOrderLocations
-from .regions import vanilla_regions
-from .stardew_rule import False_, Reach, Or, True_, Received, Count, And, Has, TotalReceived, StardewRule
-from .strings.animal_names import Animal, coop_animals, barn_animals
-from .strings.animal_product_names import AnimalProduct
-from .strings.ap_names.buff_names import Buff
-from .strings.ap_names.transport_names import Transportation
-from .strings.artisan_good_names import ArtisanGood
-from .strings.building_names import Building
-from .strings.calendar_names import Weekday
-from .strings.craftable_names import Craftable
-from .strings.crop_names import Fruit, Vegetable, all_fruits, all_vegetables
-from .strings.fertilizer_names import Fertilizer
-from .strings.festival_check_names import FestivalCheck
-from .strings.fish_names import Fish, Trash, WaterItem
-from .strings.flower_names import Flower
-from .strings.forageable_names import Forageable
-from .strings.fruit_tree_names import Sapling
-from .strings.generic_names import Generic
-from .strings.geode_names import Geode
-from .strings.gift_names import Gift
-from .strings.ingredient_names import Ingredient
-from .strings.material_names import Material
-from .strings.machine_names import Machine
-from .strings.food_names import Meal, Beverage
-from .strings.metal_names import Ore, MetalBar, Mineral, Fossil
-from .strings.monster_drop_names import Loot
-from .strings.performance_names import Performance
-from .strings.quest_names import Quest
-from .strings.region_names import Region
-from .strings.season_names import Season
-from .strings.seed_names import Seed
-from .strings.skill_names import Skill, ModSkill
-from .strings.special_order_names import SpecialOrder
-from .strings.spells import MagicSpell
-from .strings.tool_names import Tool, ToolMaterial, APTool
-from .strings.tv_channel_names import Channel
-from .strings.villager_names import NPC
-from .strings.wallet_item_names import Wallet
-from .strings.weapon_names import Weapon
-
-MAX_MONTHS = 12
-MONEY_PER_MONTH = 15000
-MISSING_ITEM = "THIS ITEM IS MISSING"
-
-tool_materials = {
- ToolMaterial.copper: 1,
- ToolMaterial.iron: 2,
- ToolMaterial.gold: 3,
- ToolMaterial.iridium: 4
-}
-
-tool_upgrade_prices = {
- ToolMaterial.copper: 2000,
- ToolMaterial.iron: 5000,
- ToolMaterial.gold: 10000,
- ToolMaterial.iridium: 25000
-}
-
-fishing_regions = [Region.beach, Region.town, Region.forest, Region.mountain, Region.island_south, Region.island_west]
-
-
-@dataclass(frozen=True, repr=False)
-class StardewLogic:
- player: int
- options: StardewValleyOptions
-
- item_rules: Dict[str, StardewRule] = field(default_factory=dict)
- sapling_rules: Dict[str, StardewRule] = field(default_factory=dict)
- tree_fruit_rules: Dict[str, StardewRule] = field(default_factory=dict)
- seed_rules: Dict[str, StardewRule] = field(default_factory=dict)
- cooking_rules: Dict[str, StardewRule] = field(default_factory=dict)
- crop_rules: Dict[str, StardewRule] = field(default_factory=dict)
- fish_rules: Dict[str, StardewRule] = field(default_factory=dict)
- museum_rules: Dict[str, StardewRule] = field(default_factory=dict)
- building_rules: Dict[str, StardewRule] = field(default_factory=dict)
- quest_rules: Dict[str, StardewRule] = field(default_factory=dict)
- festival_rules: Dict[str, StardewRule] = field(default_factory=dict)
- special_order_rules: Dict[str, StardewRule] = field(default_factory=dict)
-
- def __post_init__(self):
- self.fish_rules.update({fish.name: self.can_catch_fish(fish) for fish in all_fish})
- self.museum_rules.update({donation.name: self.can_find_museum_item(donation) for donation in all_museum_items})
-
- for recipe in all_cooking_recipes:
- can_cook_rule = self.can_cook(recipe)
- if recipe.meal in self.cooking_rules:
- can_cook_rule = can_cook_rule | self.cooking_rules[recipe.meal]
- self.cooking_rules[recipe.meal] = can_cook_rule
-
- self.sapling_rules.update({
- Sapling.apple: self.can_buy_sapling(Fruit.apple),
- Sapling.apricot: self.can_buy_sapling(Fruit.apricot),
- Sapling.cherry: self.can_buy_sapling(Fruit.cherry),
- Sapling.orange: self.can_buy_sapling(Fruit.orange),
- Sapling.peach: self.can_buy_sapling(Fruit.peach),
- Sapling.pomegranate: self.can_buy_sapling(Fruit.pomegranate),
- Sapling.banana: self.can_buy_sapling(Fruit.banana),
- Sapling.mango: self.can_buy_sapling(Fruit.mango),
- })
-
- self.tree_fruit_rules.update({
- Fruit.apple: self.can_plant_and_grow_item(Season.fall),
- Fruit.apricot: self.can_plant_and_grow_item(Season.spring),
- Fruit.cherry: self.can_plant_and_grow_item(Season.spring),
- Fruit.orange: self.can_plant_and_grow_item(Season.summer),
- Fruit.peach: self.can_plant_and_grow_item(Season.summer),
- Fruit.pomegranate: self.can_plant_and_grow_item(Season.fall),
- Fruit.banana: self.can_plant_and_grow_item(Season.summer),
- Fruit.mango: self.can_plant_and_grow_item(Season.summer),
- })
-
- for tree_fruit in self.tree_fruit_rules:
- existing_rules = self.tree_fruit_rules[tree_fruit]
- sapling = f"{tree_fruit} Sapling"
- self.tree_fruit_rules[tree_fruit] = existing_rules & self.has(sapling) & self.has_lived_months(1)
-
- self.seed_rules.update({seed.name: self.can_buy_seed(seed) for seed in all_purchasable_seeds})
- self.crop_rules.update({crop.name: self.can_grow_crop(crop) for crop in all_crops})
- self.crop_rules.update({
- Seed.coffee: (self.has_season(Season.spring) | self.has_season(
- Season.summer)) & self.can_buy_seed(crops_by_name[Seed.coffee].seed),
- Fruit.ancient_fruit: (self.received("Ancient Seeds") | self.received("Ancient Seeds Recipe")) &
- self.can_reach_region(Region.greenhouse) & self.has(Machine.seed_maker),
- })
-
- self.item_rules.update({
- ArtisanGood.aged_roe: self.can_preserves_jar(AnimalProduct.roe),
- AnimalProduct.any_egg: self.has(AnimalProduct.chicken_egg) | self.has(AnimalProduct.duck_egg),
- Fish.any: Or([self.can_catch_fish(fish) for fish in all_fish]),
- Geode.artifact_trove: self.has(Geode.omni) & self.can_reach_region(Region.desert),
- Craftable.bait: (self.has_skill_level(Skill.fishing, 2) & self.has(Loot.bug_meat)) | self.has(Machine.worm_bin),
- Fertilizer.basic: (self.has(Material.sap) & self.has_farming_level(1)) | (self.has_lived_months(1) & self.can_spend_money_at(Region.pierre_store, 100)),
- Fertilizer.quality: (self.has_farming_level(9) & self.has(Material.sap) & self.has(Fish.any)) | (self.has_year_two() & self.can_spend_money_at(Region.pierre_store, 150)),
- Fertilizer.deluxe: False_(),
- # self.received("Deluxe Fertilizer Recipe") & self.has(MetalBar.iridium) & self.has(SVItem.sap),
- Fertilizer.tree: self.has_skill_level(Skill.foraging, 7) & self.has(Material.fiber) & self.has(Material.stone),
- Loot.bat_wing: self.can_mine_in_the_mines_floor_41_80() | self.can_mine_in_the_skull_cavern(),
- ArtisanGood.battery_pack: (self.has(Machine.lightning_rod) & self.has_any_season_not_winter()) | self.has(Machine.solar_panel),
- Machine.bee_house: self.has_farming_level(3) & self.has(MetalBar.iron) & self.has(ArtisanGood.maple_syrup) & self.has(Material.coal) & self.has(Material.wood),
- Beverage.beer: self.can_keg(Vegetable.wheat) | self.can_spend_money_at(Region.saloon, 400),
- Forageable.blackberry: self.can_forage(Season.fall),
- Craftable.bomb: self.has_skill_level(Skill.mining, 6) & self.has(Material.coal) & self.has(Ore.iron),
- Fossil.bone_fragment: self.can_reach_region(Region.dig_site),
- Gift.bouquet: self.has_relationship(Generic.bachelor, 8) & self.can_spend_money_at(Region.pierre_store, 100),
- Meal.bread: self.can_spend_money_at(Region.saloon, 120),
- Trash.broken_cd: self.can_crab_pot(),
- Trash.broken_glasses: self.can_crab_pot(),
- Loot.bug_meat: self.can_mine_in_the_mines_floor_1_40(),
- Forageable.cactus_fruit: self.can_forage(Generic.any, Region.desert),
- Machine.cask: self.has_house(3) & self.can_reach_region(Region.cellar) & self.has(Material.wood) & self.has(Material.hardwood),
- Forageable.cave_carrot: self.can_forage(Generic.any, Region.mines_floor_10, True),
- ArtisanGood.caviar: self.can_preserves_jar(AnimalProduct.sturgeon_roe),
- Forageable.chanterelle: self.can_forage(Season.fall, Region.secret_woods),
- Machine.cheese_press: self.has_farming_level(6) & self.has(Material.wood) & self.has(Material.stone) & self.has(Material.hardwood) & self.has(MetalBar.copper),
- ArtisanGood.cheese: (self.has(AnimalProduct.cow_milk) & self.has(Machine.cheese_press)) | (self.can_reach_region(Region.desert) & self.has(Mineral.emerald)),
- Craftable.cherry_bomb: self.has_skill_level(Skill.mining, 1) & self.has(Material.coal) & self.has(Ore.copper),
- Animal.chicken: self.can_buy_animal(Animal.chicken),
- AnimalProduct.chicken_egg: self.has([AnimalProduct.egg, AnimalProduct.brown_egg, AnimalProduct.large_egg, AnimalProduct.large_brown_egg], 1),
- Material.cinder_shard: self.can_reach_region(Region.volcano_floor_5),
- WaterItem.clam: self.can_forage(Generic.any, Region.beach),
- Material.clay: self.can_reach_any_region([Region.farm, Region.beach, Region.quarry]) & self.has_tool(Tool.hoe),
- ArtisanGood.cloth: (self.has(AnimalProduct.wool) & self.has(Machine.loom)) | (self.can_reach_region(Region.desert) & self.has(Mineral.aquamarine)),
- Material.coal: self.can_mine_in_the_mines_floor_41_80() | self.can_do_panning(),
- WaterItem.cockle: self.can_forage(Generic.any, Region.beach),
- Forageable.coconut: self.can_forage(Generic.any, Region.desert),
- Beverage.coffee: self.can_keg(Seed.coffee) | self.has(Machine.coffee_maker) | (self.can_spend_money_at(Region.saloon, 300)) | self.has("Hot Java Ring"),
- Machine.coffee_maker: self.received(Machine.coffee_maker),
- Forageable.common_mushroom: self.can_forage(Season.fall) | (self.can_forage(Season.spring, Region.secret_woods)),
- MetalBar.copper: self.can_smelt(Ore.copper),
- Ore.copper: self.can_mine_in_the_mines_floor_1_40() | self.can_mine_in_the_skull_cavern() | self.can_do_panning(),
- WaterItem.coral: self.can_forage(Generic.any, Region.tide_pools) | self.can_forage(Season.summer, Region.beach),
- Animal.cow: self.can_buy_animal(Animal.cow),
- AnimalProduct.cow_milk: self.has(AnimalProduct.milk) | self.has(AnimalProduct.large_milk),
- Fish.crab: self.can_crab_pot(Region.beach),
- Machine.crab_pot: self.has_skill_level(Skill.fishing, 3) & (self.can_spend_money_at(Region.fish_shop, 1500) | (self.has(MetalBar.iron) & self.has(Material.wood))),
- Fish.crayfish: self.can_crab_pot(Region.town),
- Forageable.crocus: self.can_forage(Season.winter),
- Forageable.crystal_fruit: self.can_forage(Season.winter),
- Forageable.daffodil: self.can_forage(Season.spring),
- Forageable.dandelion: self.can_forage(Season.spring),
- Animal.dinosaur: self.has_building(Building.big_coop) & self.has(AnimalProduct.dinosaur_egg),
- Forageable.dragon_tooth: self.can_forage(Generic.any, Region.volcano_floor_10),
- "Dried Starfish": self.can_fish() & self.can_reach_region(Region.beach),
- Trash.driftwood: self.can_crab_pot(),
- AnimalProduct.duck_egg: self.has_animal(Animal.duck),
- AnimalProduct.duck_feather: self.has_happy_animal(Animal.duck),
- Animal.duck: self.can_buy_animal(Animal.duck),
- AnimalProduct.egg: self.has_animal(Animal.chicken),
- AnimalProduct.brown_egg: self.has_animal(Animal.chicken),
- "Energy Tonic": self.can_reach_region(Region.hospital) & self.can_spend_money(1000),
- Material.fiber: True_(),
- Forageable.fiddlehead_fern: self.can_forage(Season.summer, Region.secret_woods),
- "Magic Rock Candy": self.can_reach_region(Region.desert) & self.has("Prismatic Shard"),
- "Fishing Chest": self.can_fish_chests(),
- Craftable.flute_block: self.has_relationship(NPC.robin, 6) & self.can_reach_region(Region.carpenter) & self.has(Material.wood) & self.has(Ore.copper) & self.has(Material.fiber),
- Geode.frozen: self.can_mine_in_the_mines_floor_41_80(),
- Machine.furnace: self.has(Material.stone) & self.has(Ore.copper),
- Geode.geode: self.can_mine_in_the_mines_floor_1_40(),
- Forageable.ginger: self.can_forage(Generic.any, Region.island_west, True),
- ArtisanGood.goat_cheese: self.has(AnimalProduct.goat_milk) & self.has(Machine.cheese_press),
- AnimalProduct.goat_milk: self.has(Animal.goat),
- Animal.goat: self.can_buy_animal(Animal.goat),
- MetalBar.gold: self.can_smelt(Ore.gold),
- Ore.gold: self.can_mine_in_the_mines_floor_81_120() | self.can_mine_in_the_skull_cavern() | self.can_do_panning(),
- Geode.golden_coconut: self.can_reach_region(Region.island_north),
- Gift.golden_pumpkin: self.has_season(Season.fall) | self.can_open_geode(Geode.artifact_trove),
- WaterItem.green_algae: self.can_fish_in_freshwater(),
- ArtisanGood.green_tea: self.can_keg(Vegetable.tea_leaves),
- Material.hardwood: self.has_tool(Tool.axe, ToolMaterial.copper) & (self.can_reach_region(Region.secret_woods) | self.can_reach_region(Region.island_south)),
- Forageable.hay: self.has_building(Building.silo) & self.has_tool(Tool.scythe),
- Forageable.hazelnut: self.can_forage(Season.fall),
- Forageable.holly: self.can_forage(Season.winter),
- ArtisanGood.honey: self.can_spend_money_at(Region.oasis, 200) | (self.has(Machine.bee_house) & self.has_any_season_not_winter()),
- "Hot Java Ring": self.can_reach_region(Region.volcano_floor_10),
- Meal.ice_cream: (self.has_season(Season.summer) & self.can_spend_money_at(Region.town, 250)) | self.can_spend_money_at(Region.oasis, 240),
- # | (self.can_cook() & self.has_relationship(NPC.jodi, 7) & self.has(AnimalProduct.cow_milk) & self.has(Ingredient.sugar)),
- MetalBar.iridium: self.can_smelt(Ore.iridium),
- Ore.iridium: self.can_mine_in_the_skull_cavern(),
- MetalBar.iron: self.can_smelt(Ore.iron),
- Ore.iron: self.can_mine_in_the_mines_floor_41_80() | self.can_mine_in_the_skull_cavern() | self.can_do_panning(),
- ArtisanGood.jelly: self.has_jelly(),
- Trash.joja_cola: self.can_spend_money_at(Region.saloon, 75),
- "JotPK Small Buff": self.has_jotpk_power_level(2),
- "JotPK Medium Buff": self.has_jotpk_power_level(4),
- "JotPK Big Buff": self.has_jotpk_power_level(7),
- "JotPK Max Buff": self.has_jotpk_power_level(9),
- ArtisanGood.juice: self.has_juice(),
- "Junimo Kart Small Buff": self.has_junimo_kart_power_level(2),
- "Junimo Kart Medium Buff": self.has_junimo_kart_power_level(4),
- "Junimo Kart Big Buff": self.has_junimo_kart_power_level(6),
- "Junimo Kart Max Buff": self.has_junimo_kart_power_level(8),
- Machine.keg: self.has_farming_level(8) & self.has(Material.wood) & self.has(MetalBar.iron) & self.has(MetalBar.copper) & self.has(ArtisanGood.oak_resin),
- AnimalProduct.large_egg: self.has_happy_animal(Animal.chicken),
- AnimalProduct.large_brown_egg: self.has_happy_animal(Animal.chicken),
- AnimalProduct.large_goat_milk: self.has_happy_animal(Animal.goat),
- AnimalProduct.large_milk: self.has_happy_animal(Animal.cow),
- Forageable.leek: self.can_forage(Season.spring),
- Craftable.life_elixir: self.has_skill_level(Skill.combat, 2) & self.has(Forageable.red_mushroom) & self.has(Forageable.purple_mushroom) & self.has(Forageable.morel) & self.has(Forageable.chanterelle),
- Machine.lightning_rod: self.has_skill_level(Skill.foraging, 6) & self.has(MetalBar.iron) & self.has(MetalBar.quartz) & self.has(Loot.bat_wing),
- Fish.lobster: self.can_crab_pot(Region.beach),
- Machine.loom: self.has_farming_level(7) & self.has(Material.wood) & self.has(Material.fiber) & self.has(ArtisanGood.pine_tar),
- Forageable.magma_cap: self.can_forage(Generic.any, Region.volcano_floor_5),
- Geode.magma: self.can_mine_in_the_mines_floor_81_120() | (self.has(Fish.lava_eel) & self.has_building(Building.fish_pond)),
- ArtisanGood.maple_syrup: self.has(Machine.tapper),
- ArtisanGood.mayonnaise: self.has(Machine.mayonnaise_machine) & self.has(AnimalProduct.chicken_egg),
- Machine.mayonnaise_machine: self.has_farming_level(2) & self.has(Material.wood) & self.has(Material.stone) & self.has("Earth Crystal") & self.has(MetalBar.copper),
- ArtisanGood.mead: self.can_keg(ArtisanGood.honey),
- Craftable.mega_bomb: self.has_skill_level(Skill.mining, 8) & self.has(Ore.gold) & self.has(Loot.solar_essence) & self.has(Loot.void_essence),
- Gift.mermaid_pendant: self.can_reach_region(Region.tide_pools) & self.has_relationship(Generic.bachelor, 10) & self.has_house(1) & self.has(Craftable.rain_totem),
- AnimalProduct.milk: self.has_animal(Animal.cow),
- Craftable.monster_musk: self.has_prismatic_jelly_reward_access() & self.has(Loot.slime) & self.has(Loot.bat_wing),
- Forageable.morel: self.can_forage(Season.spring, Region.secret_woods),
- "Muscle Remedy": self.can_reach_region(Region.hospital) & self.can_spend_money(1000),
- Fish.mussel: self.can_forage(Generic.any, Region.beach) or self.has(Fish.mussel_node),
- Fish.mussel_node: self.can_reach_region(Region.island_west),
- WaterItem.nautilus_shell: self.can_forage(Season.winter, Region.beach),
- ArtisanGood.oak_resin: self.has(Machine.tapper),
- Ingredient.oil: self.can_spend_money_at(Region.pierre_store, 200) | (self.has(Machine.oil_maker) & (self.has(Vegetable.corn) | self.has(Flower.sunflower) | self.has(Seed.sunflower))),
- Machine.oil_maker: self.has_farming_level(8) & self.has(Loot.slime) & self.has(Material.hardwood) & self.has(MetalBar.gold),
- Craftable.oil_of_garlic: (self.has_skill_level(Skill.combat, 6) & self.has(Vegetable.garlic) & self.has(Ingredient.oil)) | (self.can_spend_money_at(Region.mines_dwarf_shop, 3000)),
- Geode.omni: self.can_mine_in_the_mines_floor_41_80() | self.can_reach_region(Region.desert) | self.can_do_panning() | self.received(Wallet.rusty_key) | (self.has(Fish.octopus) & self.has_building(Building.fish_pond)) | self.can_reach_region(Region.volcano_floor_10),
- Animal.ostrich: self.has_building(Building.barn) & self.has(AnimalProduct.ostrich_egg) & self.has(Machine.ostrich_incubator),
- AnimalProduct.ostrich_egg: self.can_forage(Generic.any, Region.island_north, True),
- Machine.ostrich_incubator: self.received("Ostrich Incubator Recipe") & self.has(Fossil.bone_fragment) & self.has(Material.hardwood) & self.has(Material.cinder_shard),
- Fish.oyster: self.can_forage(Generic.any, Region.beach),
- ArtisanGood.pale_ale: self.can_keg(Vegetable.hops),
- Gift.pearl: (self.has(Fish.blobfish) & self.has_building(Building.fish_pond)) | self.can_open_geode(Geode.artifact_trove),
- Fish.periwinkle: self.can_crab_pot(Region.town),
- ArtisanGood.pickles: self.has_pickle(),
- Animal.pig: self.can_buy_animal(Animal.pig),
- Beverage.pina_colada: self.can_spend_money_at(Region.island_resort, 600),
- ArtisanGood.pine_tar: self.has(Machine.tapper),
- Meal.pizza: self.can_spend_money_at(Region.saloon, 600),
- Machine.preserves_jar: self.has_farming_level(4) & self.has(Material.wood) & self.has(Material.stone) & self.has(Material.coal),
- Forageable.purple_mushroom: self.can_forage(Generic.any, Region.mines_floor_95) | self.can_forage(Generic.any, Region.skull_cavern_25),
- Animal.rabbit: self.can_buy_animal(Animal.rabbit),
- AnimalProduct.rabbit_foot: self.has_happy_animal(Animal.rabbit),
- MetalBar.radioactive: self.can_smelt(Ore.radioactive),
- Ore.radioactive: self.can_mine_perfectly() & self.can_reach_region(Region.qi_walnut_room),
- Forageable.rainbow_shell: self.can_forage(Season.summer, Region.beach),
- Craftable.rain_totem: self.has_skill_level(Skill.foraging, 9) & self.has(Material.hardwood) & self.has(ArtisanGood.truffle_oil) & self.has(ArtisanGood.pine_tar),
- Machine.recycling_machine: self.has_skill_level(Skill.fishing, 4) & self.has(Material.wood) & self.has(Material.stone) & self.has(MetalBar.iron),
- Forageable.red_mushroom: self.can_forage(Season.summer, Region.secret_woods) | self.can_forage(Season.fall, Region.secret_woods),
- MetalBar.quartz: self.can_smelt("Quartz") | self.can_smelt("Fire Quartz") |
- (self.has(Machine.recycling_machine) & (self.has(Trash.broken_cd) | self.has(Trash.broken_glasses))),
- Ingredient.rice: self.can_spend_money_at(Region.pierre_store, 200) | (
- self.has_building(Building.mill) & self.has(Vegetable.unmilled_rice)),
- AnimalProduct.roe: self.can_fish() & self.has_building(Building.fish_pond),
- Meal.salad: self.can_spend_money_at(Region.saloon, 220),
- # | (self.can_cook() & self.has_relationship(NPC.emily, 3) & self.has(Forageable.leek) & self.has(Forageable.dandelion) &
- # self.has(Ingredient.vinegar)),
- Forageable.salmonberry: self.can_forage(Season.spring),
- Material.sap: self.can_chop_trees(),
- Craftable.scarecrow: self.has_farming_level(1) & self.has(Material.wood) & self.has(Material.coal) & self.has(Material.fiber),
- WaterItem.sea_urchin: self.can_forage(Generic.any, Region.tide_pools),
- WaterItem.seaweed: (self.can_fish() & self.can_reach_region(Region.beach)) | self.can_reach_region(
- Region.tide_pools),
- Forageable.secret_note: self.received(Wallet.magnifying_glass) & (self.can_chop_trees() | self.can_mine_in_the_mines_floor_1_40()),
- Machine.seed_maker: self.has_farming_level(9) & self.has(Material.wood) & self.has(MetalBar.gold) & self.has(
- Material.coal),
- Animal.sheep: self.can_buy_animal(Animal.sheep),
- Fish.shrimp: self.can_crab_pot(Region.beach),
- Loot.slime: self.can_mine_in_the_mines_floor_1_40(),
- Weapon.any_slingshot: self.received(Weapon.slingshot) | self.received(Weapon.master_slingshot),
- Fish.snail: self.can_crab_pot(Region.town),
- Forageable.snow_yam: self.can_forage(Season.winter, Region.beach, True),
- Trash.soggy_newspaper: self.can_crab_pot(),
- Loot.solar_essence: self.can_mine_in_the_mines_floor_41_80() | self.can_mine_in_the_skull_cavern(),
- Machine.solar_panel: self.received("Solar Panel Recipe") & self.has(MetalBar.quartz) & self.has(
- MetalBar.iron) & self.has(MetalBar.gold),
- Meal.spaghetti: self.can_spend_money_at(Region.saloon, 240),
- Forageable.spice_berry: self.can_forage(Season.summer),
- Forageable.spring_onion: self.can_forage(Season.spring),
- AnimalProduct.squid_ink: self.can_mine_in_the_mines_floor_81_120() | (self.has_building(Building.fish_pond) & self.has(Fish.squid)),
- Craftable.staircase: self.has_skill_level(Skill.mining, 2) & self.has(Material.stone),
- Material.stone: self.has_tool(Tool.pickaxe),
- Meal.strange_bun: self.has_relationship(NPC.shane, 7) & self.has(Ingredient.wheat_flour) & self.has(Fish.periwinkle) & self.has(ArtisanGood.void_mayonnaise),
- AnimalProduct.sturgeon_roe: self.has(Fish.sturgeon) & self.has_building(Building.fish_pond),
- Ingredient.sugar: self.can_spend_money_at(Region.pierre_store, 100) | (
- self.has_building(Building.mill) & self.has(Vegetable.beet)),
- Forageable.sweet_pea: self.can_forage(Season.summer),
- Machine.tapper: self.has_skill_level(Skill.foraging, 3) & self.has(Material.wood) & self.has(MetalBar.copper),
- Vegetable.tea_leaves: self.has(Sapling.tea) & self.has_lived_months(2) & self.has_any_season_not_winter(),
- Sapling.tea: self.has_relationship(NPC.caroline, 2) & self.has(Material.fiber) & self.has(Material.wood),
- Trash.trash: self.can_crab_pot(),
- Beverage.triple_shot_espresso: self.has("Hot Java Ring"),
- ArtisanGood.truffle_oil: self.has(AnimalProduct.truffle) & self.has(Machine.oil_maker),
- AnimalProduct.truffle: self.has_animal(Animal.pig) & self.has_any_season_not_winter(),
- Ingredient.vinegar: self.can_spend_money_at(Region.pierre_store, 200),
- AnimalProduct.void_egg: self.can_spend_money_at(Region.sewer, 5000) | (self.has_building(Building.fish_pond) & self.has(Fish.void_salmon)),
- Loot.void_essence: self.can_mine_in_the_mines_floor_81_120() | self.can_mine_in_the_skull_cavern(),
- ArtisanGood.void_mayonnaise: (self.can_reach_region(Region.witch_swamp) & self.can_fish()) | (self.has(Machine.mayonnaise_machine) & self.has(AnimalProduct.void_egg)),
- Ingredient.wheat_flour: self.can_spend_money_at(Region.pierre_store, 100) |
- (self.has_building(Building.mill) & self.has(Vegetable.wheat)),
- WaterItem.white_algae: self.can_fish() & self.can_reach_region(Region.mines_floor_20),
- Forageable.wild_horseradish: self.can_forage(Season.spring),
- Forageable.wild_plum: self.can_forage(Season.fall),
- Gift.wilted_bouquet: self.has(Machine.furnace) & self.has(Gift.bouquet) & self.has(Material.coal),
- ArtisanGood.wine: self.has_wine(),
- Forageable.winter_root: self.can_forage(Season.winter, Region.forest, True),
- Material.wood: self.has_tool(Tool.axe),
- AnimalProduct.wool: self.has_animal(Animal.rabbit) | self.has_animal(Animal.sheep),
- Machine.worm_bin: self.has_skill_level(Skill.fishing, 8) & self.has(Material.hardwood) & self.has(MetalBar.gold) & self.has(MetalBar.iron) & self.has(Material.fiber),
- })
- self.item_rules.update(self.fish_rules)
- self.item_rules.update(self.museum_rules)
- self.item_rules.update(self.sapling_rules)
- self.item_rules.update(self.tree_fruit_rules)
- self.item_rules.update(self.seed_rules)
- self.item_rules.update(self.crop_rules)
-
- # For some recipes, the cooked item can be obtained directly, so we either cook it or get it
- for recipe in self.cooking_rules:
- cooking_rule = self.cooking_rules[recipe]
- obtention_rule = self.item_rules[recipe] if recipe in self.item_rules else False_()
- self.item_rules[recipe] = obtention_rule | cooking_rule
-
- self.building_rules.update({
- Building.barn: self.can_spend_money_at(Region.carpenter, 6000) & self.has([Material.wood, Material.stone]),
- Building.big_barn: self.can_spend_money_at(Region.carpenter, 12000) & self.has([Material.wood, Material.stone]) & self.has_building(Building.barn),
- Building.deluxe_barn: self.can_spend_money_at(Region.carpenter, 25000) & self.has([Material.wood, Material.stone]) & self.has_building(Building.big_barn),
- Building.coop: self.can_spend_money_at(Region.carpenter, 4000) & self.has([Material.wood, Material.stone]),
- Building.big_coop: self.can_spend_money_at(Region.carpenter, 10000) & self.has([Material.wood, Material.stone]) & self.has_building(Building.coop),
- Building.deluxe_coop: self.can_spend_money_at(Region.carpenter, 20000) & self.has([Material.wood, Material.stone]) & self.has_building(Building.big_coop),
- Building.fish_pond: self.can_spend_money_at(Region.carpenter, 5000) & self.has([Material.stone, WaterItem.seaweed, WaterItem.green_algae]),
- Building.mill: self.can_spend_money_at(Region.carpenter, 2500) & self.has([Material.stone, Material.wood, ArtisanGood.cloth]),
- Building.shed: self.can_spend_money_at(Region.carpenter, 15000) & self.has(Material.wood),
- Building.big_shed: self.can_spend_money_at(Region.carpenter, 20000) & self.has([Material.wood, Material.stone]) & self.has_building(Building.shed),
- Building.silo: self.can_spend_money_at(Region.carpenter, 100) & self.has([Material.stone, Material.clay, MetalBar.copper]),
- Building.slime_hutch: self.can_spend_money_at(Region.carpenter, 10000) & self.has([Material.stone, MetalBar.quartz, MetalBar.iridium]),
- Building.stable: self.can_spend_money_at(Region.carpenter, 10000) & self.has([Material.hardwood, MetalBar.iron]),
- Building.well: self.can_spend_money_at(Region.carpenter, 1000) & self.has(Material.stone),
- Building.shipping_bin: self.can_spend_money_at(Region.carpenter, 250) & self.has(Material.wood),
- Building.kitchen: self.can_spend_money_at(Region.carpenter, 10000) & self.has(Material.wood) & self.has_house(0),
- Building.kids_room: self.can_spend_money_at(Region.carpenter, 50000) & self.has(Material.hardwood) & self.has_house(1),
- Building.cellar: self.can_spend_money_at(Region.carpenter, 100000) & self.has_house(2),
- })
-
- self.building_rules.update(get_modded_building_rules(self, self.options.mods))
-
- self.quest_rules.update({
- Quest.introductions: self.can_reach_region(Region.town),
- Quest.how_to_win_friends: self.can_complete_quest(Quest.introductions),
- Quest.getting_started: self.has(Vegetable.parsnip) & self.has_tool(Tool.hoe) & self.can_water(0),
- Quest.to_the_beach: self.can_reach_region(Region.beach),
- Quest.raising_animals: self.can_complete_quest(Quest.getting_started) & self.has_building(Building.coop),
- Quest.advancement: self.can_complete_quest(Quest.getting_started) & self.has(Craftable.scarecrow),
- Quest.archaeology: (self.has_tool(Tool.hoe) | self.can_mine_in_the_mines_floor_1_40() | self.can_fish()) & self.can_reach_region(Region.museum),
- Quest.meet_the_wizard: self.can_reach_region(Region.town) & self.can_reach_region(Region.community_center) & self.can_reach_region(Region.wizard_tower),
- Quest.forging_ahead: self.has(Ore.copper) & self.has(Machine.furnace),
- Quest.smelting: self.has(MetalBar.copper),
- Quest.initiation: self.can_mine_in_the_mines_floor_1_40(),
- Quest.robins_lost_axe: self.has_season(Season.spring) & self.can_reach_region(Region.forest) & self.can_meet(NPC.robin),
- Quest.jodis_request: self.has_season(Season.spring) & self.has(Vegetable.cauliflower) & self.can_meet(NPC.jodi),
- Quest.mayors_shorts: self.has_season(Season.summer) & self.can_reach_region(Region.ranch) &
- (self.has_relationship(NPC.marnie, 2) | (magic.can_blink(self))) & self.can_meet(NPC.lewis),
- Quest.blackberry_basket: self.has_season(Season.fall) & self.can_meet(NPC.linus),
- Quest.marnies_request: self.has_relationship(NPC.marnie, 3) & self.has(Forageable.cave_carrot) & self.can_reach_region(Region.ranch),
- Quest.pam_is_thirsty: self.has_season(Season.summer) & self.has(ArtisanGood.pale_ale) & self.can_meet(NPC.pam),
- Quest.a_dark_reagent: self.has_season(Season.winter) & self.has(Loot.void_essence) & self.can_meet(NPC.wizard),
- Quest.cows_delight: self.has_season(Season.fall) & self.has(Vegetable.amaranth) & self.can_meet(NPC.marnie),
- Quest.the_skull_key: self.received(Wallet.skull_key) & self.can_reach_region(Region.skull_cavern_entrance),
- Quest.crop_research: self.has_season(Season.summer) & self.has(Fruit.melon) & self.can_meet(NPC.demetrius),
- Quest.knee_therapy: self.has_season(Season.summer) & self.has(Fruit.hot_pepper) & self.can_meet(NPC.george),
- Quest.robins_request: self.has_season(Season.winter) & self.has(Material.hardwood) & self.can_meet(NPC.robin),
- Quest.qis_challenge: self.can_mine_in_the_skull_cavern(),
- Quest.the_mysterious_qi: self.can_reach_region(Region.bus_tunnel) & self.has(ArtisanGood.battery_pack) & self.can_reach_region(Region.desert) & self.has(Forageable.rainbow_shell) & self.has(Vegetable.beet) & self.has(Loot.solar_essence),
- Quest.carving_pumpkins: self.has_season(Season.fall) & self.has(Vegetable.pumpkin) & self.can_meet(NPC.caroline),
- Quest.a_winter_mystery: self.has_season(Season.winter) & self.can_reach_region(Region.town),
- Quest.strange_note: self.has(Forageable.secret_note) & self.can_reach_region(Region.secret_woods) & self.has(ArtisanGood.maple_syrup),
- Quest.cryptic_note: self.has(Forageable.secret_note) & self.can_reach_region(Region.skull_cavern_100),
- Quest.fresh_fruit: self.has_season(Season.spring) & self.has(Fruit.apricot) & self.can_meet(NPC.emily),
- Quest.aquatic_research: self.has_season(Season.summer) & self.has(Fish.pufferfish) & self.can_meet(NPC.demetrius),
- Quest.a_soldiers_star: self.has_season(Season.summer) & self.has_year_two() & self.has(Fruit.starfruit) & self.can_meet(NPC.kent),
- Quest.mayors_need: self.has_season(Season.summer) & self.has(ArtisanGood.truffle_oil) & self.can_meet(NPC.lewis),
- Quest.wanted_lobster: self.has_season(Season.fall) & self.has_season(Season.fall) & self.has(Fish.lobster) & self.can_meet(NPC.gus),
- Quest.pam_needs_juice: self.has_season(Season.fall) & self.has(ArtisanGood.battery_pack) & self.can_meet(NPC.pam),
- Quest.fish_casserole: self.has_relationship(NPC.jodi, 4) & self.has(Fish.largemouth_bass) & self.can_reach_region(Region.sam_house),
- Quest.catch_a_squid: self.has_season(Season.winter) & self.has(Fish.squid) & self.can_meet(NPC.willy),
- Quest.fish_stew: self.has_season(Season.winter) & self.has(Fish.albacore) & self.can_meet(NPC.gus),
- Quest.pierres_notice: self.has_season(Season.spring) & self.has("Sashimi") & self.can_meet(NPC.pierre),
- Quest.clints_attempt: self.has_season(Season.winter) & self.has(Mineral.amethyst) & self.can_meet(NPC.emily),
- Quest.a_favor_for_clint: self.has_season(Season.winter) & self.has(MetalBar.iron) & self.can_meet(NPC.clint),
- Quest.staff_of_power: self.has_season(Season.winter) & self.has(MetalBar.iridium) & self.can_meet(NPC.wizard),
- Quest.grannys_gift: self.has_season(Season.spring) & self.has(Forageable.leek) & self.can_meet(NPC.evelyn),
- Quest.exotic_spirits: self.has_season(Season.winter) & self.has(Forageable.coconut) & self.can_meet(NPC.gus),
- Quest.catch_a_lingcod: self.has_season(Season.winter) & self.has("Lingcod") & self.can_meet(NPC.willy),
- Quest.dark_talisman: self.has_rusty_key() & self.can_reach_region(Region.railroad) & self.can_meet(NPC.krobus) & self.can_reach_region(Region.mutant_bug_lair),
- Quest.goblin_problem: self.can_reach_region(Region.witch_swamp) & self.has(ArtisanGood.void_mayonnaise),
- Quest.magic_ink: self.can_reach_region(Region.witch_hut) & self.can_meet(NPC.wizard),
- Quest.the_pirates_wife: self.can_reach_region(Region.island_west) & self.can_meet(NPC.kent) &
- self.can_meet(NPC.gus) & self.can_meet(NPC.sandy) & self.can_meet(NPC.george) &
- self.can_meet(NPC.wizard) & self.can_meet(NPC.willy),
- })
-
- self.quest_rules.update(get_modded_quest_rules(self, self.options.mods))
-
- self.festival_rules.update({
- FestivalCheck.egg_hunt: self.has_season(Season.spring) & self.can_reach_region(Region.town) & self.can_win_egg_hunt(),
- FestivalCheck.strawberry_seeds: self.has_season(Season.spring) & self.can_reach_region(Region.town) & self.can_spend_money(1000),
- FestivalCheck.dance: self.has_season(Season.spring) & self.can_reach_region(Region.forest) & self.has_relationship(Generic.bachelor, 4),
- FestivalCheck.rarecrow_5: self.has_season(Season.spring) & self.can_reach_region(Region.forest) & self.can_spend_money(2500),
- FestivalCheck.luau_soup: self.has_season(Season.summer) & self.can_reach_region(Region.beach) & self.can_succeed_luau_soup(),
- FestivalCheck.moonlight_jellies: self.has_season(Season.summer) & self.can_reach_region(Region.beach),
- FestivalCheck.smashing_stone: self.has_season(Season.fall) & self.can_reach_region(Region.town),
- FestivalCheck.grange_display: self.has_season(Season.fall) & self.can_reach_region(Region.town) & self.can_succeed_grange_display(),
- FestivalCheck.rarecrow_1: self.has_season(Season.fall) & self.can_reach_region(Region.town), # only cost star tokens
- FestivalCheck.fair_stardrop: self.has_season(Season.fall) & self.can_reach_region(Region.town), # only cost star tokens
- FestivalCheck.spirit_eve_maze: self.has_season(Season.fall) & self.can_reach_region(Region.town),
- FestivalCheck.rarecrow_2: self.has_season(Season.fall) & self.can_reach_region(Region.town) & self.can_spend_money(5000),
- FestivalCheck.fishing_competition: self.has_season(Season.winter) & self.can_reach_region(Region.forest) & self.can_win_fishing_competition(),
- FestivalCheck.rarecrow_4: self.has_season(Season.winter) & self.can_reach_region(Region.forest) & self.can_spend_money(5000),
- FestivalCheck.mermaid_pearl: self.has_season(Season.winter) & self.can_reach_region(Region.beach),
- FestivalCheck.cone_hat: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.can_spend_money(2500),
- FestivalCheck.iridium_fireplace: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.can_spend_money(15000),
- FestivalCheck.rarecrow_7: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.can_spend_money(5000) & self.can_donate_museum_artifacts(20),
- FestivalCheck.rarecrow_8: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.can_spend_money(5000) & self.can_donate_museum_items(40),
- FestivalCheck.lupini_red_eagle: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.can_spend_money(1200),
- FestivalCheck.lupini_portrait_mermaid: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.can_spend_money(1200),
- FestivalCheck.lupini_solar_kingdom: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.can_spend_money(1200),
- FestivalCheck.lupini_clouds: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.has_year_two() & self.can_spend_money(1200),
- FestivalCheck.lupini_1000_years: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.has_year_two() & self.can_spend_money(1200),
- FestivalCheck.lupini_three_trees: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.has_year_two() & self.can_spend_money(1200),
- FestivalCheck.lupini_the_serpent: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.has_year_three() & self.can_spend_money(1200),
- FestivalCheck.lupini_tropical_fish: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.has_year_three() & self.can_spend_money(1200),
- FestivalCheck.lupini_land_of_clay: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.has_year_three() & self.can_spend_money(1200),
- FestivalCheck.secret_santa: self.has_season(Season.winter) & self.can_reach_region(Region.town) & self.has_any_universal_love(),
- FestivalCheck.legend_of_the_winter_star: self.has_season(Season.winter) & self.can_reach_region(Region.town),
- FestivalCheck.all_rarecrows: self.can_reach_region(Region.farm) & self.has_all_rarecrows(),
- })
-
- self.special_order_rules.update({
- SpecialOrder.island_ingredients: self.can_meet(NPC.caroline) & self.has_island_transport() & self.can_farm_perfectly() &
- self.can_ship(Vegetable.taro_root) & self.can_ship(Fruit.pineapple) & self.can_ship(Forageable.ginger),
- SpecialOrder.cave_patrol: self.can_meet(NPC.clint) & self.can_mine_perfectly() & self.can_mine_to_floor(120),
- SpecialOrder.aquatic_overpopulation: self.can_meet(NPC.demetrius) & self.can_fish_perfectly(),
- SpecialOrder.biome_balance: self.can_meet(NPC.demetrius) & self.can_fish_perfectly(),
- SpecialOrder.rock_rejuivenation: self.has_relationship(NPC.emily, 4) & self.has(Mineral.ruby) & self.has(Mineral.topaz) &
- self.has(Mineral.emerald) & self.has(Mineral.jade) & self.has(Mineral.amethyst) &
- self.has(ArtisanGood.cloth) & self.can_reach_region(Region.haley_house),
- SpecialOrder.gifts_for_george: self.can_reach_region(Region.alex_house) & self.has_season(Season.spring) & self.has(Forageable.leek),
- SpecialOrder.fragments_of_the_past: self.can_reach_region(Region.museum) & self.can_reach_region(Region.dig_site) & self.has_tool(Tool.pickaxe),
- SpecialOrder.gus_famous_omelet: self.can_reach_region(Region.saloon) & self.has(AnimalProduct.any_egg),
- SpecialOrder.crop_order: self.can_farm_perfectly() & self.can_ship(),
- SpecialOrder.community_cleanup: self.can_reach_region(Region.railroad) & self.can_crab_pot(),
- SpecialOrder.the_strong_stuff: self.can_reach_region(Region.trailer) & self.can_keg(Vegetable.potato),
- SpecialOrder.pierres_prime_produce: self.can_reach_region(Region.pierre_store) & self.can_farm_perfectly(),
- SpecialOrder.robins_project: self.can_meet(NPC.robin) & self.can_reach_region(Region.carpenter) & self.can_chop_perfectly() &
- self.has(Material.hardwood),
- SpecialOrder.robins_resource_rush: self.can_meet(NPC.robin) & self.can_reach_region(Region.carpenter) & self.can_chop_perfectly() &
- self.has(Fertilizer.tree) & self.can_mine_perfectly(),
- SpecialOrder.juicy_bugs_wanted_yum: self.can_reach_region(Region.beach) & self.has(Loot.bug_meat),
- SpecialOrder.tropical_fish: self.can_meet(NPC.willy) & self.received("Island Resort") & self.has_island_transport() &
- self.has(Fish.stingray) & self.has(Fish.blue_discus) & self.has(Fish.lionfish),
- SpecialOrder.a_curious_substance: self.can_reach_region(Region.wizard_tower) & self.can_mine_perfectly() & self.can_mine_to_floor(80),
- SpecialOrder.prismatic_jelly: self.can_reach_region(Region.wizard_tower) & self.can_mine_perfectly() & self.can_mine_to_floor(40),
- SpecialOrder.qis_crop: self.can_farm_perfectly() & self.can_reach_region(Region.greenhouse) &
- self.can_reach_region(Region.island_west) & self.has_total_skill_level(50) &
- self.has(Machine.seed_maker) & self.has_building(Building.shipping_bin),
- SpecialOrder.lets_play_a_game: self.has_junimo_kart_max_level(),
- SpecialOrder.four_precious_stones: self.has_lived_months(MAX_MONTHS) & self.has("Prismatic Shard") &
- self.can_mine_perfectly_in_the_skull_cavern(),
- SpecialOrder.qis_hungry_challenge: self.can_mine_perfectly_in_the_skull_cavern() & self.has_max_buffs(),
- SpecialOrder.qis_cuisine: self.can_cook() & (self.can_spend_money_at(Region.saloon, 205000) | self.can_spend_money_at(Region.pierre_store, 170000)) &
- self.can_ship(),
- SpecialOrder.qis_kindness: self.can_give_loved_gifts_to_everyone(),
- SpecialOrder.extended_family: self.can_fish_perfectly() & self.has(Fish.angler) & self.has(Fish.glacierfish) &
- self.has(Fish.crimsonfish) & self.has(Fish.mutant_carp) & self.has(Fish.legend),
- SpecialOrder.danger_in_the_deep: self.can_mine_perfectly() & self.has_mine_elevator_to_floor(120),
- SpecialOrder.skull_cavern_invasion: self.can_mine_perfectly_in_the_skull_cavern() & self.has_max_buffs(),
- SpecialOrder.qis_prismatic_grange: self.has(Loot.bug_meat) & # 100 Bug Meat
- self.can_spend_money_at(Region.saloon, 24000) & # 100 Spaghetti
- self.can_spend_money_at(Region.blacksmith, 15000) & # 100 Copper Ore
- self.can_spend_money_at(Region.ranch, 5000) & # 100 Hay
- self.can_spend_money_at(Region.saloon, 22000) & # 100 Salads
- self.can_spend_money_at(Region.saloon, 7500) & # 100 Joja Cola
- self.can_spend_money(80000), # I need this extra rule because money rules aren't additive...
- })
-
- self.special_order_rules.update(get_modded_special_orders_rules(self, self.options.mods))
-
- def has(self, items: Union[str, (Iterable[str], Sized)], count: Optional[int] = None) -> StardewRule:
- if isinstance(items, str):
- return Has(items, self.item_rules)
-
- if len(items) == 0:
- return True_()
-
- if count is None or count == len(items):
- return And(self.has(item) for item in items)
-
- if count == 1:
- return Or(self.has(item) for item in items)
-
- return Count(count, (self.has(item) for item in items))
-
- def received(self, items: Union[str, Iterable[str]], count: Optional[int] = 1) -> StardewRule:
- if count <= 0 or not items:
- return True_()
-
- if isinstance(items, str):
- return Received(items, self.player, count)
-
- if count is None:
- return And(self.received(item) for item in items)
-
- if count == 1:
- return Or(self.received(item) for item in items)
-
- return TotalReceived(count, items, self.player)
-
- def can_reach_region(self, spot: str) -> StardewRule:
- return Reach(spot, "Region", self.player)
-
- def can_reach_any_region(self, spots: Iterable[str]) -> StardewRule:
- return Or(self.can_reach_region(spot) for spot in spots)
-
- def can_reach_all_regions(self, spots: Iterable[str]) -> StardewRule:
- return And(self.can_reach_region(spot) for spot in spots)
-
- def can_reach_all_regions_except_one(self, spots: Iterable[str]) -> StardewRule:
- num_required = len(list(spots)) - 1
- if num_required <= 0:
- num_required = len(list(spots))
- return Count(num_required, [self.can_reach_region(spot) for spot in spots])
-
- def can_reach_location(self, spot: str) -> StardewRule:
- return Reach(spot, "Location", self.player)
-
- def can_reach_entrance(self, spot: str) -> StardewRule:
- return Reach(spot, "Entrance", self.player)
-
- def can_have_earned_total_money(self, amount: int) -> StardewRule:
- return self.has_lived_months(min(8, amount // MONEY_PER_MONTH))
-
- def can_spend_money(self, amount: int) -> StardewRule:
- if self.options.starting_money == -1:
- return True_()
- return self.has_lived_months(min(8, amount // (MONEY_PER_MONTH // 5)))
-
- def can_spend_money_at(self, region: str, amount: int) -> StardewRule:
- return self.can_reach_region(region) & self.can_spend_money(amount)
-
- def has_tool(self, tool: str, material: str = ToolMaterial.basic) -> StardewRule:
- if material == ToolMaterial.basic or tool == Tool.scythe:
- return True_()
-
- if self.options.tool_progression == ToolProgression.option_progressive:
- return self.received(f"Progressive {tool}", count=tool_materials[material])
-
- return self.has(f"{material} Bar") & self.can_spend_money(tool_upgrade_prices[material])
-
- def can_earn_skill_level(self, skill: str, level: int) -> StardewRule:
- if level <= 0:
- return True_()
-
- tool_level = (level - 1) // 2
- tool_material = ToolMaterial.tiers[tool_level]
- months = max(1, level - 1)
- months_rule = self.has_lived_months(months)
- previous_level_rule = self.has_skill_level(skill, level - 1)
-
- if skill == Skill.fishing:
- xp_rule = self.can_get_fishing_xp() & self.has_tool(Tool.fishing_rod, ToolMaterial.tiers[max(tool_level, 3)])
- elif skill == Skill.farming:
- xp_rule = self.can_get_farming_xp() & self.has_tool(Tool.hoe, tool_material) & self.can_water(tool_level)
- elif skill == Skill.foraging:
- xp_rule = self.can_get_foraging_xp() & \
- (self.has_tool(Tool.axe, tool_material) | magic.can_use_clear_debris_instead_of_tool_level(self, tool_level))
- elif skill == Skill.mining:
- xp_rule = self.can_get_mining_xp() & \
- (self.has_tool(Tool.pickaxe, tool_material) | magic.can_use_clear_debris_instead_of_tool_level(self, tool_level))
- elif skill == Skill.combat:
- combat_tier = Performance.tiers[tool_level]
- xp_rule = self.can_get_combat_xp() & self.can_do_combat_at_level(combat_tier)
- else:
- xp_rule = skills.can_earn_mod_skill_level(self, skill, level)
-
- return previous_level_rule & months_rule & xp_rule
-
- def has_skill_level(self, skill: str, level: int) -> StardewRule:
- if level <= 0:
- return True_()
-
- if self.options.skill_progression == SkillProgression.option_progressive:
- return self.received(f"{skill} Level", count=level)
-
- return self.can_earn_skill_level(skill, level)
-
- def has_farming_level(self, level: int) -> StardewRule:
- return self.has_skill_level(Skill.farming, level)
-
- def has_total_skill_level(self, level: int, allow_modded_skills: bool = False) -> StardewRule:
- if level <= 0:
- return True_()
-
- if self.options.skill_progression == SkillProgression.option_progressive:
- skills_items = ["Farming Level", "Mining Level", "Foraging Level",
- "Fishing Level", "Combat Level"]
- if allow_modded_skills:
- skills.append_mod_skill_level(skills_items, self.options)
- return self.received(skills_items, count=level)
-
- months_with_4_skills = max(1, (level // 4) - 1)
- months_with_5_skills = max(1, (level // 5) - 1)
- rule_with_fishing = self.has_lived_months(months_with_5_skills) & self.can_get_fishing_xp()
- if level > 40:
- return rule_with_fishing
- return self.has_lived_months(months_with_4_skills) | rule_with_fishing
-
- def has_building(self, building: str) -> StardewRule:
- carpenter_rule = self.can_reach_region(Region.carpenter)
- if not self.options.building_progression == BuildingProgression.option_vanilla:
- count = 1
- if building in [Building.coop, Building.barn, Building.shed]:
- building = f"Progressive {building}"
- elif building.startswith("Big"):
- count = 2
- building = " ".join(["Progressive", *building.split(" ")[1:]])
- elif building.startswith("Deluxe"):
- count = 3
- building = " ".join(["Progressive", *building.split(" ")[1:]])
- return self.received(f"{building}", count) & carpenter_rule
-
- return Has(building, self.building_rules) & carpenter_rule
-
- def has_house(self, upgrade_level: int) -> StardewRule:
- if upgrade_level < 1:
- return True_()
-
- if upgrade_level > 3:
- return False_()
-
- if not self.options.building_progression == BuildingProgression.option_vanilla:
- return self.received(f"Progressive House", upgrade_level) & self.can_reach_region(Region.carpenter)
-
- if upgrade_level == 1:
- return Has(Building.kitchen, self.building_rules)
-
- if upgrade_level == 2:
- return Has(Building.kids_room, self.building_rules)
-
- # if upgrade_level == 3:
- return Has(Building.cellar, self.building_rules)
-
- def can_complete_quest(self, quest: str) -> StardewRule:
- return Has(quest, self.quest_rules)
-
- def can_complete_special_order(self, specialorder: str) -> StardewRule:
- return Has(specialorder, self.special_order_rules)
-
- def can_get_farming_xp(self) -> StardewRule:
- crop_rules = []
- for crop in all_crops:
- crop_rules.append(self.can_grow_crop(crop))
- return Or(crop_rules)
-
- def can_get_foraging_xp(self) -> StardewRule:
- tool_rule = self.has_tool(Tool.axe)
- tree_rule = self.can_reach_region(Region.forest) & self.has_any_season_not_winter()
- stump_rule = self.can_reach_region(Region.secret_woods) & self.has_tool(Tool.axe, ToolMaterial.copper)
- return tool_rule & (tree_rule | stump_rule)
-
- def can_get_mining_xp(self) -> StardewRule:
- tool_rule = self.has_tool(Tool.pickaxe)
- stone_rule = self.can_reach_any_region([Region.mines_floor_5, Region.quarry, Region.skull_cavern_25, Region.volcano_floor_5])
- return tool_rule & stone_rule
-
- def can_get_combat_xp(self) -> StardewRule:
- tool_rule = self.has_any_weapon()
- enemy_rule = self.can_reach_any_region([Region.mines_floor_5, Region.skull_cavern_25, Region.volcano_floor_5])
- return tool_rule & enemy_rule
-
- def can_get_fishing_xp(self) -> StardewRule:
- if self.options.skill_progression == SkillProgression.option_progressive:
- return self.can_fish() | self.can_crab_pot()
-
- return self.can_fish()
-
- def can_fish(self, difficulty: int = 0) -> StardewRule:
- skill_required = max(0, int((difficulty / 10) - 1))
- if difficulty <= 40:
- skill_required = 0
- skill_rule = self.has_skill_level(Skill.fishing, skill_required)
- region_rule = self.can_reach_any_region(fishing_regions)
- number_fishing_rod_required = 1 if difficulty < 50 else 2
- if self.options.tool_progression == ToolProgression.option_progressive:
- return self.received("Progressive Fishing Rod", number_fishing_rod_required) & skill_rule & region_rule
-
- return skill_rule & region_rule
-
- def can_fish_in_freshwater(self) -> StardewRule:
- return self.can_fish() & self.can_reach_any_region([Region.forest, Region.town, Region.mountain])
-
- def has_max_fishing(self) -> StardewRule:
- skill_rule = self.has_skill_level(Skill.fishing, 10)
- return self.has_max_fishing_rod() & skill_rule
-
- def can_fish_chests(self) -> StardewRule:
- skill_rule = self.has_skill_level(Skill.fishing, 4)
- return self.has_max_fishing_rod() & skill_rule
-
- def can_buy_seed(self, seed: SeedItem) -> StardewRule:
- if self.options.cropsanity == Cropsanity.option_disabled:
- item_rule = True_()
- else:
- item_rule = self.received(seed.name)
- season_rule = self.has_any_season(seed.seasons)
- region_rule = self.can_reach_all_regions(seed.regions)
- currency_rule = self.can_spend_money(1000)
- if seed.name == Seed.pineapple:
- currency_rule = self.has(Forageable.magma_cap)
- if seed.name == Seed.taro:
- currency_rule = self.has(Fossil.bone_fragment)
- return season_rule & region_rule & item_rule & currency_rule
-
- def can_buy_sapling(self, fruit: str) -> StardewRule:
- sapling_prices = {Fruit.apple: 4000, Fruit.apricot: 2000, Fruit.cherry: 3400, Fruit.orange: 4000,
- Fruit.peach: 6000,
- Fruit.pomegranate: 6000, Fruit.banana: 0, Fruit.mango: 0}
- received_sapling = self.received(f"{fruit} Sapling")
- if self.options.cropsanity == Cropsanity.option_disabled:
- allowed_buy_sapling = True_()
- else:
- allowed_buy_sapling = received_sapling
- can_buy_sapling = self.can_spend_money_at(Region.pierre_store, sapling_prices[fruit])
- if fruit == Fruit.banana:
- can_buy_sapling = self.has_island_trader() & self.has(Forageable.dragon_tooth)
- elif fruit == Fruit.mango:
- can_buy_sapling = self.has_island_trader() & self.has(Fish.mussel_node)
-
- return allowed_buy_sapling & can_buy_sapling
-
- def can_grow_crop(self, crop: CropItem) -> StardewRule:
- season_rule = self.has_any_season(crop.farm_growth_seasons)
- seed_rule = self.has(crop.seed.name)
- farm_rule = self.can_reach_region(Region.farm) & season_rule
- tool_rule = self.has_tool(Tool.hoe) & self.has_tool(Tool.watering_can)
- region_rule = farm_rule | self.can_reach_region(Region.greenhouse) | self.can_reach_region(Region.island_west)
- return seed_rule & region_rule & tool_rule
-
- def can_plant_and_grow_item(self, seasons: Union[str, Iterable[str]]) -> StardewRule:
- if isinstance(seasons, str):
- seasons = [seasons]
- season_rule = self.has_any_season(seasons) | self.can_reach_region(Region.greenhouse) | self.has_island_farm()
- farm_rule = self.can_reach_region(Region.farm) | self.can_reach_region(
- Region.greenhouse) | self.has_island_farm()
- return season_rule & farm_rule
-
- def has_island_farm(self) -> StardewRule:
- return self.can_reach_region(Region.island_south)
-
- def can_catch_fish(self, fish: FishItem) -> StardewRule:
- region_rule = self.can_reach_any_region(fish.locations)
- season_rule = self.has_any_season(fish.seasons)
- if fish.difficulty == -1:
- difficulty_rule = self.can_crab_pot()
- else:
- difficulty_rule = self.can_fish(fish.difficulty)
- return region_rule & season_rule & difficulty_rule
-
- def can_catch_every_fish(self) -> StardewRule:
- rules = [self.has_skill_level(Skill.fishing, 10), self.has_max_fishing_rod()]
- for fish in all_fish:
- if self.options.exclude_ginger_island == ExcludeGingerIsland.option_true and \
- fish in island_fish:
- continue
- rules.append(self.can_catch_fish(fish))
- return And(rules)
-
- def has_max_fishing_rod(self) -> StardewRule:
- if self.options.tool_progression == ToolProgression.option_progressive:
- return self.received(APTool.fishing_rod, 4)
- return self.can_get_fishing_xp()
-
- def can_cook(self, recipe: CookingRecipe = None) -> StardewRule:
- cook_rule = self.has_house(1) | self.has_skill_level(Skill.foraging, 9)
- if recipe is None:
- return cook_rule
-
- learn_rule = self.can_learn_recipe(recipe.source)
- ingredients_rule = And([self.has(ingredient) for ingredient in recipe.ingredients])
- number_ingredients = sum(recipe.ingredients[ingredient] for ingredient in recipe.ingredients)
- time_rule = self.has_lived_months(number_ingredients)
- return cook_rule & learn_rule & ingredients_rule & time_rule
-
- def can_learn_recipe(self, source: RecipeSource) -> StardewRule:
- if isinstance(source, StarterSource):
- return True_()
- if isinstance(source, ShopSource):
- return self.can_spend_money_at(source.region, source.price)
- if isinstance(source, SkillSource):
- return self.has_skill_level(source.skill, source.level)
- if isinstance(source, FriendshipSource):
- return self.has_relationship(source.friend, source.hearts)
- if isinstance(source, QueenOfSauceSource):
- year_rule = self.has_year_two() if source.year == 2 else self.has_year_three()
- return self.can_watch(Channel.queen_of_sauce) & self.has_season(source.season) & year_rule
-
- return False_()
-
- def can_watch(self, channel: str = None):
- tv_rule = True_()
- if channel is None:
- return tv_rule
- return self.received(channel) & tv_rule
-
- def can_smelt(self, item: str) -> StardewRule:
- return self.has(Machine.furnace) & self.has(item)
-
- def can_do_panning(self, item: str = Generic.any) -> StardewRule:
- return self.received("Glittering Boulder Removed")
-
- def can_crab_pot(self, region: str = Generic.any) -> StardewRule:
- crab_pot_rule = self.has(Craftable.bait)
- if self.options.skill_progression == SkillProgression.option_progressive:
- crab_pot_rule = crab_pot_rule & self.has(Machine.crab_pot)
- else:
- crab_pot_rule = crab_pot_rule & self.can_get_fishing_xp()
-
- if region != Generic.any:
- return crab_pot_rule & self.can_reach_region(region)
-
- water_region_rules = self.can_reach_any_region(fishing_regions)
- return crab_pot_rule & water_region_rules
-
- # Regions
- def can_mine_in_the_mines_floor_1_40(self) -> StardewRule:
- return self.can_reach_region(Region.mines_floor_5)
-
- def can_mine_in_the_mines_floor_41_80(self) -> StardewRule:
- return self.can_reach_region(Region.mines_floor_45)
-
- def can_mine_in_the_mines_floor_81_120(self) -> StardewRule:
- return self.can_reach_region(Region.mines_floor_85)
-
- def can_mine_in_the_skull_cavern(self) -> StardewRule:
- return (self.can_progress_in_the_mines_from_floor(120) &
- self.can_reach_region(Region.skull_cavern))
-
- def can_mine_perfectly(self) -> StardewRule:
- return self.can_progress_in_the_mines_from_floor(160)
-
- def can_mine_perfectly_in_the_skull_cavern(self) -> StardewRule:
- return (self.can_mine_perfectly() &
- self.can_reach_region(Region.skull_cavern))
-
- def can_farm_perfectly(self) -> StardewRule:
- tool_rule = self.has_tool(Tool.hoe, ToolMaterial.iridium) & self.can_water(4)
- return tool_rule & self.has_farming_level(10)
-
- def can_fish_perfectly(self) -> StardewRule:
- skill_rule = self.has_skill_level(Skill.fishing, 10)
- return skill_rule & self.has_max_fishing_rod()
-
- def can_chop_trees(self) -> StardewRule:
- return self.has_tool(Tool.axe) & self.can_reach_region(Region.forest)
-
- def can_chop_perfectly(self) -> StardewRule:
- magic_rule = (magic.can_use_clear_debris_instead_of_tool_level(self, 3)) & self.has_skill_level(ModSkill.magic, 10)
- tool_rule = self.has_tool(Tool.axe, ToolMaterial.iridium)
- foraging_rule = self.has_skill_level(Skill.foraging, 10)
- region_rule = self.can_reach_region(Region.forest)
- return region_rule & ((tool_rule & foraging_rule) | magic_rule)
-
- def has_max_buffs(self) -> StardewRule:
- return self.received(Buff.movement, self.options.movement_buff_number.value) & self.received(Buff.luck, self.options.luck_buff_number.value)
-
- def get_weapon_rule_for_floor_tier(self, tier: int):
- if tier >= 4:
- return self.can_do_combat_at_level(Performance.galaxy)
- if tier >= 3:
- return self.can_do_combat_at_level(Performance.great)
- if tier >= 2:
- return self.can_do_combat_at_level(Performance.good)
- if tier >= 1:
- return self.can_do_combat_at_level(Performance.decent)
- return self.can_do_combat_at_level(Performance.basic)
-
- def can_progress_in_the_mines_from_floor(self, floor: int) -> StardewRule:
- tier = int(floor / 40)
- rules = []
- weapon_rule = self.get_weapon_rule_for_floor_tier(tier)
- rules.append(weapon_rule)
- if self.options.tool_progression == ToolProgression.option_progressive:
- rules.append(self.has_tool(Tool.pickaxe, ToolMaterial.tiers[tier]))
- if self.options.skill_progression == SkillProgression.option_progressive:
- combat_tier = min(10, max(0, tier * 2))
- rules.append(self.has_skill_level(Skill.combat, combat_tier))
- return And(rules)
-
- def can_progress_easily_in_the_mines_from_floor(self, floor: int) -> StardewRule:
- tier = int(floor / 40) + 1
- rules = []
- weapon_rule = self.get_weapon_rule_for_floor_tier(tier)
- rules.append(weapon_rule)
- if self.options.tool_progression == ToolProgression.option_progressive:
- rules.append(self.has_tool(Tool.pickaxe, ToolMaterial.tiers[tier]))
- if self.options.skill_progression == SkillProgression.option_progressive:
- combat_tier = min(10, max(0, tier * 2))
- rules.append(self.has_skill_level(Skill.combat, combat_tier))
- return And(rules)
-
- def has_mine_elevator_to_floor(self, floor: int) -> StardewRule:
- if self.options.elevator_progression != ElevatorProgression.option_vanilla:
- return self.received("Progressive Mine Elevator", count=int(floor / 5))
- return True_()
-
- def can_mine_to_floor(self, floor: int) -> StardewRule:
- previous_elevator = max(floor - 5, 0)
- previous_previous_elevator = max(floor - 10, 0)
- return ((self.has_mine_elevator_to_floor(previous_elevator) &
- self.can_progress_in_the_mines_from_floor(previous_elevator)) |
- (self.has_mine_elevator_to_floor(previous_previous_elevator) &
- self.can_progress_easily_in_the_mines_from_floor(previous_previous_elevator)))
-
- def can_progress_in_the_skull_cavern_from_floor(self, floor: int) -> StardewRule:
- tier = floor // 50
- rules = []
- weapon_rule = self.has_great_weapon()
- rules.append(weapon_rule)
- rules.append(self.can_cook())
- if self.options.tool_progression == ToolProgression.option_progressive:
- rules.append(self.received("Progressive Pickaxe", min(4, max(0, tier + 2))))
- if self.options.skill_progression == SkillProgression.option_progressive:
- skill_tier = min(10, max(0, tier * 2 + 6))
- rules.extend({self.has_skill_level(Skill.combat, skill_tier),
- self.has_skill_level(Skill.mining, skill_tier)})
- return And(rules)
-
- def can_progress_easily_in_the_skull_cavern_from_floor(self, floor: int) -> StardewRule:
- return self.can_progress_in_the_skull_cavern_from_floor(floor + 50)
-
- def can_mine_to_skull_cavern_floor(self, floor: int) -> StardewRule:
- previous_elevator = max(floor - 25, 0)
- previous_previous_elevator = max(floor - 50, 0)
- has_mine_elevator = self.has_mine_elevator_to_floor(5) # Skull Cavern Elevator menu needs a normal elevator...
- return ((has_skull_cavern_elevator_to_floor(self, previous_elevator) &
- self.can_progress_in_the_skull_cavern_from_floor(previous_elevator)) |
- (has_skull_cavern_elevator_to_floor(self, previous_previous_elevator) &
- self.can_progress_easily_in_the_skull_cavern_from_floor(previous_previous_elevator))) & has_mine_elevator
-
- def has_jotpk_power_level(self, power_level: int) -> StardewRule:
- if self.options.arcade_machine_locations != ArcadeMachineLocations.option_full_shuffling:
- return True_()
- jotpk_buffs = ["JotPK: Progressive Boots", "JotPK: Progressive Gun",
- "JotPK: Progressive Ammo", "JotPK: Extra Life", "JotPK: Increased Drop Rate"]
- return self.received(jotpk_buffs, power_level)
-
- def has_junimo_kart_power_level(self, power_level: int) -> StardewRule:
- if self.options.arcade_machine_locations != ArcadeMachineLocations.option_full_shuffling:
- return True_()
- return self.received("Junimo Kart: Extra Life", power_level)
-
- def has_junimo_kart_max_level(self) -> StardewRule:
- play_rule = self.can_reach_region(Region.junimo_kart_3)
- if self.options.arcade_machine_locations != ArcadeMachineLocations.option_full_shuffling:
- return play_rule
- return self.has_junimo_kart_power_level(8)
-
- def has_traveling_merchant(self, tier: int = 1):
- traveling_merchant_days = [f"Traveling Merchant: {day}" for day in Weekday.all_days]
- return self.received(traveling_merchant_days, tier)
-
- def can_get_married(self) -> StardewRule:
- return self.has_relationship(Generic.bachelor, 10) & self.has(Gift.mermaid_pendant)
-
- def has_children(self, number_children: int) -> StardewRule:
- if number_children <= 0:
- return True_()
- possible_kids = ["Cute Baby", "Ugly Baby"]
- return self.received(possible_kids, number_children) & self.has_house(2)
-
- def can_reproduce(self, number_children: int = 1) -> StardewRule:
- if number_children <= 0:
- return True_()
- return self.can_get_married() & self.has_house(2) & self.has_relationship(Generic.bachelor, 12) & self.has_children(number_children - 1)
-
- def has_relationship(self, npc: str, hearts: int = 1) -> StardewRule:
- if hearts <= 0:
- return True_()
- friendsanity = self.options.friendsanity
- if friendsanity == Friendsanity.option_none:
- return self.can_earn_relationship(npc, hearts)
- if npc not in all_villagers_by_name:
- if npc == NPC.pet:
- if friendsanity == Friendsanity.option_bachelors:
- return self.can_befriend_pet(hearts)
- return self.received_hearts(NPC.pet, hearts)
- if npc == Generic.any or npc == Generic.bachelor:
- possible_friends = []
- for name in all_villagers_by_name:
- if not self.npc_is_in_current_slot(name):
- continue
- if npc == Generic.any or all_villagers_by_name[name].bachelor:
- possible_friends.append(self.has_relationship(name, hearts))
- return Or(possible_friends)
- if npc == Generic.all:
- mandatory_friends = []
- for name in all_villagers_by_name:
- if not self.npc_is_in_current_slot(name):
- continue
- mandatory_friends.append(self.has_relationship(name, hearts))
- return And(mandatory_friends)
- if npc.isnumeric():
- possible_friends = []
- for name in all_villagers_by_name:
- if not self.npc_is_in_current_slot(name):
- continue
- possible_friends.append(self.has_relationship(name, hearts))
- return Count(int(npc), possible_friends)
- return self.can_earn_relationship(npc, hearts)
-
- if not self.npc_is_in_current_slot(npc):
- return True_()
- villager = all_villagers_by_name[npc]
- if friendsanity == Friendsanity.option_bachelors and not villager.bachelor:
- return self.can_earn_relationship(npc, hearts)
- if friendsanity == Friendsanity.option_starting_npcs and not villager.available:
- return self.can_earn_relationship(npc, hearts)
- is_capped_at_8 = villager.bachelor and friendsanity != Friendsanity.option_all_with_marriage
- if is_capped_at_8 and hearts > 8:
- return self.received_hearts(villager, 8) & self.can_earn_relationship(npc, hearts)
- return self.received_hearts(villager, hearts)
-
- def received_hearts(self, npc: Union[str, Villager], hearts: int) -> StardewRule:
- if isinstance(npc, Villager):
- return self.received_hearts(npc.name, hearts)
- heart_size = self.options.friendsanity_heart_size.value
- return self.received(self.heart(npc), math.ceil(hearts / heart_size))
-
- def can_meet(self, npc: str) -> StardewRule:
- if npc not in all_villagers_by_name or not self.npc_is_in_current_slot(npc):
- return True_()
- villager = all_villagers_by_name[npc]
- rules = [self.can_reach_any_region(villager.locations)]
- if npc == NPC.kent:
- rules.append(self.has_year_two())
- elif npc == NPC.leo:
- rules.append(self.received("Island West Turtle"))
-
- return And(rules)
-
- def can_give_loved_gifts_to_everyone(self) -> StardewRule:
- rules = []
- for npc in all_villagers_by_name:
- if not self.npc_is_in_current_slot(npc):
- continue
- villager = all_villagers_by_name[npc]
- gift_rule = self.has_any_universal_love()
- meet_rule = self.can_meet(npc)
- rules.append(meet_rule & gift_rule)
- loved_gifts_rules = And(rules)
- simplified_rules = loved_gifts_rules.simplify()
- return simplified_rules
-
- def can_earn_relationship(self, npc: str, hearts: int = 0) -> StardewRule:
- if hearts <= 0:
- return True_()
-
- heart_size = self.options.friendsanity_heart_size.value
- previous_heart = hearts - heart_size
- previous_heart_rule = self.has_relationship(npc, previous_heart)
-
- if npc == NPC.pet:
- earn_rule = self.can_befriend_pet(hearts)
- elif npc == NPC.wizard and ModNames.magic in self.options.mods:
- earn_rule = self.can_meet(npc) & self.has_lived_months(hearts)
- elif npc in all_villagers_by_name:
- if not self.npc_is_in_current_slot(npc):
- return previous_heart_rule
- villager = all_villagers_by_name[npc]
- rule_if_birthday = self.has_season(villager.birthday) & self.has_any_universal_love() & self.has_lived_months(hearts // 2)
- rule_if_not_birthday = self.has_lived_months(hearts)
- earn_rule = self.can_meet(npc) & (rule_if_birthday | rule_if_not_birthday)
- if villager.bachelor:
- if hearts > 8:
- earn_rule = earn_rule & self.can_date(npc)
- if hearts > 10:
- earn_rule = earn_rule & self.can_marry(npc)
- else:
- earn_rule = self.has_lived_months(min(hearts // 2, 8))
-
- return previous_heart_rule & earn_rule
-
- def can_date(self, npc: str) -> StardewRule:
- return self.has_relationship(npc, 8) & self.has(Gift.bouquet)
-
- def can_marry(self, npc: str) -> StardewRule:
- return self.has_relationship(npc, 10) & self.has(Gift.mermaid_pendant)
-
- def can_befriend_pet(self, hearts: int):
- if hearts <= 0:
- return True_()
- points = hearts * 200
- points_per_month = 12 * 14
- points_per_water_month = 18 * 14
- return self.can_reach_region(Region.farm) & \
- ((self.can_water(0) & self.has_lived_months(points // points_per_water_month)) |
- self.has_lived_months(points // points_per_month))
-
- def can_complete_bundle(self, bundle_requirements: List[BundleItem], number_required: int) -> StardewRule:
- item_rules = []
- highest_quality_yet = 0
- can_speak_junimo = self.can_reach_region(Region.wizard_tower)
- for bundle_item in bundle_requirements:
- if bundle_item.item.item_id == -1:
- return can_speak_junimo & self.can_spend_money(bundle_item.amount)
- else:
- item_rules.append(bundle_item.item.name)
- if bundle_item.quality > highest_quality_yet:
- highest_quality_yet = bundle_item.quality
- return can_speak_junimo & self.has(item_rules, number_required) & self.can_grow_gold_quality(highest_quality_yet)
-
- def can_grow_gold_quality(self, quality: int) -> StardewRule:
- if quality <= 0:
- return True_()
- if quality == 1:
- return self.has_farming_level(5) | (self.has_fertilizer(1) & self.has_farming_level(2)) | (
- self.has_fertilizer(2) & self.has_farming_level(1)) | self.has_fertilizer(3)
- if quality == 2:
- return self.has_farming_level(10) | (self.has_fertilizer(1) & self.has_farming_level(5)) | (
- self.has_fertilizer(2) & self.has_farming_level(3)) | (
- self.has_fertilizer(3) & self.has_farming_level(2))
- if quality >= 3:
- return self.has_fertilizer(3) & self.has_farming_level(4)
-
- def has_fertilizer(self, tier: int) -> StardewRule:
- if tier <= 0:
- return True_()
- if tier == 1:
- return self.has(Fertilizer.basic)
- if tier == 2:
- return self.has(Fertilizer.quality)
- if tier >= 3:
- return self.has(Fertilizer.deluxe)
-
- def can_complete_field_office(self) -> StardewRule:
- field_office = self.can_reach_region(Region.field_office)
- professor_snail = self.received("Open Professor Snail Cave")
- dig_site = self.can_reach_region(Region.dig_site)
- tools = self.has_tool(Tool.pickaxe) & self.has_tool(Tool.hoe) & self.has_tool(Tool.scythe)
- leg_and_snake_skull = dig_site
- ribs_and_spine = self.can_reach_region(Region.island_south)
- skull = self.can_open_geode(Geode.golden_coconut)
- tail = self.can_do_panning() & dig_site
- frog = self.can_reach_region(Region.island_east)
- bat = self.can_reach_region(Region.volcano_floor_5)
- snake_vertebrae = self.can_reach_region(Region.island_west)
- return field_office & professor_snail & tools & leg_and_snake_skull & ribs_and_spine & skull & tail & frog & bat & snake_vertebrae
-
- def can_complete_community_center(self) -> StardewRule:
- return (self.can_reach_location("Complete Crafts Room") &
- self.can_reach_location("Complete Pantry") &
- self.can_reach_location("Complete Fish Tank") &
- self.can_reach_location("Complete Bulletin Board") &
- self.can_reach_location("Complete Vault") &
- self.can_reach_location("Complete Boiler Room"))
-
- def can_finish_grandpa_evaluation(self) -> StardewRule:
- # https://stardewvalleywiki.com/Grandpa
- rules_worth_a_point = [self.can_have_earned_total_money(50000), # 50 000g
- self.can_have_earned_total_money(100000), # 100 000g
- self.can_have_earned_total_money(200000), # 200 000g
- self.can_have_earned_total_money(300000), # 300 000g
- self.can_have_earned_total_money(500000), # 500 000g
- self.can_have_earned_total_money(1000000), # 1 000 000g first point
- self.can_have_earned_total_money(1000000), # 1 000 000g second point
- self.has_total_skill_level(30), # Total Skills: 30
- self.has_total_skill_level(50), # Total Skills: 50
- self.can_complete_museum(), # Completing the museum for a point
- # Catching every fish not expected
- # Shipping every item not expected
- self.can_get_married() & self.has_house(2),
- self.has_relationship("5", 8), # 5 Friends
- self.has_relationship("10", 8), # 10 friends
- self.has_relationship(NPC.pet, 5), # Max Pet
- self.can_complete_community_center(), # Community Center Completion
- self.can_complete_community_center(), # CC Ceremony first point
- self.can_complete_community_center(), # CC Ceremony second point
- self.received(Wallet.skull_key), # Skull Key obtained
- self.has_rusty_key(), # Rusty key obtained
- ]
- return Count(12, rules_worth_a_point)
-
- def has_island_transport(self) -> StardewRule:
- return self.received(Transportation.island_obelisk) | self.received(Transportation.boat_repair)
-
- def has_any_weapon(self) -> StardewRule:
- return self.has_decent_weapon() | self.received(item.name for item in all_items if Group.WEAPON in item.groups)
-
- def has_decent_weapon(self) -> StardewRule:
- return (self.has_good_weapon() |
- self.received(item.name for item in all_items
- if Group.WEAPON in item.groups and
- (Group.MINES_FLOOR_50 in item.groups or Group.MINES_FLOOR_60 in item.groups)))
-
- def has_good_weapon(self) -> StardewRule:
- return ((self.has_great_weapon() |
- self.received(item.name for item in all_items
- if Group.WEAPON in item.groups and
- (Group.MINES_FLOOR_80 in item.groups or Group.MINES_FLOOR_90 in item.groups))) &
- self.received("Adventurer's Guild"))
-
- def has_great_weapon(self) -> StardewRule:
- return ((self.has_galaxy_weapon() |
- self.received(item.name for item in all_items
- if Group.WEAPON in item.groups and Group.MINES_FLOOR_110 in item.groups)) &
- self.received("Adventurer's Guild"))
-
- def has_galaxy_weapon(self) -> StardewRule:
- return (self.received(item.name for item in all_items
- if Group.WEAPON in item.groups and Group.GALAXY_WEAPONS in item.groups) &
- self.received("Adventurer's Guild"))
-
- def has_year_two(self) -> StardewRule:
- return self.has_lived_months(4)
-
- def has_year_three(self) -> StardewRule:
- return self.has_lived_months(8)
-
- def can_speak_dwarf(self) -> StardewRule:
- return self.received("Dwarvish Translation Guide")
-
- def can_donate_museum_item(self, item: MuseumItem) -> StardewRule:
- return self.can_reach_region(Region.museum) & self.can_find_museum_item(item)
-
- def can_donate_museum_items(self, number: int) -> StardewRule:
- return self.can_reach_region(Region.museum) & self.can_find_museum_items(number)
-
- def can_donate_museum_artifacts(self, number: int) -> StardewRule:
- return self.can_reach_region(Region.museum) & self.can_find_museum_artifacts(number)
-
- def can_donate_museum_minerals(self, number: int) -> StardewRule:
- return self.can_reach_region(Region.museum) & self.can_find_museum_minerals(number)
-
- def can_find_museum_item(self, item: MuseumItem) -> StardewRule:
- region_rule = self.can_reach_all_regions_except_one(item.locations)
- geodes_rule = And([self.can_open_geode(geode) for geode in item.geodes])
- # monster_rule = self.can_farm_monster(item.monsters)
- # extra_rule = True_()
- pan_rule = False_()
- if item.name == "Earth Crystal" or item.name == "Fire Quartz" or item.name == "Frozen Tear":
- pan_rule = self.can_do_panning()
- return pan_rule | (region_rule & geodes_rule) # & monster_rule & extra_rule
-
- def can_find_museum_artifacts(self, number: int) -> StardewRule:
- rules = []
- for artifact in all_museum_artifacts:
- rules.append(self.can_find_museum_item(artifact))
-
- return Count(number, rules)
-
- def can_find_museum_minerals(self, number: int) -> StardewRule:
- rules = []
- for mineral in all_museum_minerals:
- rules.append(self.can_find_museum_item(mineral))
-
- return Count(number, rules)
-
- def can_find_museum_items(self, number: int) -> StardewRule:
- rules = []
- for donation in all_museum_items:
- rules.append(self.can_find_museum_item(donation))
-
- return Count(number, rules)
-
- def can_complete_museum(self) -> StardewRule:
- rules = [self.can_reach_region(Region.museum), self.can_mine_perfectly()]
-
- if self.options.museumsanity != Museumsanity.option_none:
- rules.append(self.received("Traveling Merchant Metal Detector", 4))
-
- for donation in all_museum_items:
- rules.append(self.can_find_museum_item(donation))
- return And(rules)
-
- def has_season(self, season: str) -> StardewRule:
- if season == Generic.any:
- return True_()
- seasons_order = [Season.spring, Season.summer, Season.fall, Season.winter]
- if self.options.season_randomization == SeasonRandomization.option_progressive:
- return self.received(Season.progressive, seasons_order.index(season))
- if self.options.season_randomization == SeasonRandomization.option_disabled:
- if season == Season.spring:
- return True_()
- return self.has_lived_months(1)
- return self.received(season)
-
- def has_any_season(self, seasons: Iterable[str]):
- if not seasons:
- return True_()
- return Or([self.has_season(season) for season in seasons])
-
- def has_any_season_not_winter(self):
- return self.has_any_season([Season.spring, Season.summer, Season.fall])
-
- def has_all_seasons(self, seasons: Iterable[str]):
- if not seasons:
- return True_()
- return And([self.has_season(season) for season in seasons])
-
- def has_lived_months(self, number: int) -> StardewRule:
- number = max(0, min(number, MAX_MONTHS))
- return self.received("Month End", number)
-
- def has_rusty_key(self) -> StardewRule:
- return self.received(Wallet.rusty_key)
-
- def can_win_egg_hunt(self) -> StardewRule:
- number_of_movement_buffs = self.options.movement_buff_number.value
- if self.options.festival_locations == FestivalLocations.option_hard or number_of_movement_buffs < 2:
- return True_()
- return self.received(Buff.movement, number_of_movement_buffs // 2)
-
- def can_succeed_luau_soup(self) -> StardewRule:
- if self.options.festival_locations != FestivalLocations.option_hard:
- return True_()
- eligible_fish = [Fish.blobfish, Fish.crimsonfish, "Ice Pip", Fish.lava_eel, Fish.legend, Fish.angler, Fish.catfish, Fish.glacierfish,
- Fish.mutant_carp, Fish.spookfish, Fish.stingray, Fish.sturgeon, "Super Cucumber"]
- fish_rule = [self.has(fish) for fish in eligible_fish]
- eligible_kegables = [Fruit.ancient_fruit, Fruit.apple, Fruit.banana, Forageable.coconut, Forageable.crystal_fruit, Fruit.mango,
- Fruit.melon, Fruit.orange, Fruit.peach, Fruit.pineapple, Fruit.pomegranate, Fruit.rhubarb,
- Fruit.starfruit, Fruit.strawberry, Forageable.cactus_fruit,
- Fruit.cherry, Fruit.cranberries, Fruit.grape, Forageable.spice_berry, Forageable.wild_plum, Vegetable.hops, Vegetable.wheat]
- keg_rules = [self.can_keg(kegable) for kegable in eligible_kegables]
- aged_rule = [self.can_age(rule, "Iridium") for rule in keg_rules]
- # There are a few other valid items but I don't feel like coding them all
- return Or(fish_rule) | Or(aged_rule)
-
- def can_succeed_grange_display(self) -> StardewRule:
- if self.options.festival_locations != FestivalLocations.option_hard:
- return True_()
- animal_rule = self.has_animal(Generic.any)
- artisan_rule = self.can_keg(Generic.any) | self.can_preserves_jar(Generic.any)
- cooking_rule = True_() # Salads at the bar are good enough
- fish_rule = self.can_fish(50)
- forage_rule = True_() # Hazelnut always available since the grange display is in fall
- mineral_rule = self.can_open_geode(Generic.any) # More than half the minerals are good enough
- good_fruits = [Fruit.apple, Fruit.banana, Forageable.coconut, Forageable.crystal_fruit, Fruit.mango, Fruit.orange, Fruit.peach,
- Fruit.pomegranate,
- Fruit.strawberry, Fruit.melon, Fruit.rhubarb, Fruit.pineapple, Fruit.ancient_fruit, Fruit.starfruit, ]
- fruit_rule = Or([self.has(fruit) for fruit in good_fruits])
- good_vegetables = [Vegetable.amaranth, Vegetable.artichoke, Vegetable.beet, Vegetable.cauliflower, Forageable.fiddlehead_fern, Vegetable.kale,
- Vegetable.radish, Vegetable.taro_root, Vegetable.yam, Vegetable.red_cabbage, Vegetable.pumpkin]
- vegetable_rule = Or([self.has(vegetable) for vegetable in good_vegetables])
-
- return animal_rule & artisan_rule & cooking_rule & fish_rule & \
- forage_rule & fruit_rule & mineral_rule & vegetable_rule
-
- def can_win_fishing_competition(self) -> StardewRule:
- return self.can_fish(60)
-
- def has_any_universal_love(self) -> StardewRule:
- return self.has(Gift.golden_pumpkin) | self.has("Magic Rock Candy") | self.has(Gift.pearl) | self.has(
- "Prismatic Shard") | self.has(AnimalProduct.rabbit_foot)
-
- def has_jelly(self) -> StardewRule:
- return self.can_preserves_jar(Fruit.any)
-
- def has_pickle(self) -> StardewRule:
- return self.can_preserves_jar(Vegetable.any)
-
- def can_preserves_jar(self, item: str) -> StardewRule:
- machine_rule = self.has(Machine.preserves_jar)
- if item == Generic.any:
- return machine_rule
- if item == Fruit.any:
- return machine_rule & self.has(all_fruits, 1)
- if item == Vegetable.any:
- return machine_rule & self.has(all_vegetables, 1)
- return machine_rule & self.has(item)
-
- def has_wine(self) -> StardewRule:
- return self.can_keg(Fruit.any)
-
- def has_juice(self) -> StardewRule:
- return self.can_keg(Vegetable.any)
-
- def can_keg(self, item: str) -> StardewRule:
- machine_rule = self.has(Machine.keg)
- if item == Generic.any:
- return machine_rule
- if item == Fruit.any:
- return machine_rule & self.has(all_fruits, 1)
- if item == Vegetable.any:
- return machine_rule & self.has(all_vegetables, 1)
- return machine_rule & self.has(item)
-
- def can_age(self, item: Union[str, StardewRule], quality: str) -> StardewRule:
- months = 1
- if quality == "Gold":
- months = 2
- elif quality == "Iridium":
- months = 3
- if isinstance(item, str):
- rule = self.has(item)
- else:
- rule: StardewRule = item
- return self.has(Machine.cask) & self.has_lived_months(months) & rule
-
- def can_buy_animal(self, animal: str) -> StardewRule:
- price = 0
- building = ""
- if animal == Animal.chicken:
- price = 800
- building = Building.coop
- elif animal == Animal.cow:
- price = 1500
- building = Building.barn
- elif animal == Animal.goat:
- price = 4000
- building = Building.big_barn
- elif animal == Animal.duck:
- price = 1200
- building = Building.big_coop
- elif animal == Animal.sheep:
- price = 8000
- building = Building.deluxe_barn
- elif animal == Animal.rabbit:
- price = 8000
- building = Building.deluxe_coop
- elif animal == Animal.pig:
- price = 16000
- building = Building.deluxe_barn
- else:
- return True_()
- return self.can_spend_money_at(Region.ranch, price) & self.has_building(building)
-
- def has_animal(self, animal: str) -> StardewRule:
- if animal == Generic.any:
- return self.has_any_animal()
- elif animal == Building.coop:
- return self.has_any_coop_animal()
- elif animal == Building.barn:
- return self.has_any_barn_animal()
- return self.has(animal)
-
- def has_happy_animal(self, animal: str) -> StardewRule:
- return self.has_animal(animal) & self.has(Forageable.hay)
-
- def has_any_animal(self) -> StardewRule:
- return self.has_any_coop_animal() | self.has_any_barn_animal()
-
- def has_any_coop_animal(self) -> StardewRule:
- coop_rule = Or([self.has_animal(coop_animal) for coop_animal in coop_animals])
- return coop_rule
-
- def has_any_barn_animal(self) -> StardewRule:
- barn_rule = Or([self.has_animal(barn_animal) for barn_animal in barn_animals])
- return barn_rule
-
- def can_open_geode(self, geode: str) -> StardewRule:
- blacksmith_access = self.can_reach_region("Clint's Blacksmith")
- geodes = [Geode.geode, Geode.frozen, Geode.magma, Geode.omni]
- if geode == Generic.any:
- return blacksmith_access & Or([self.has(geode_type) for geode_type in geodes])
- return blacksmith_access & self.has(geode)
-
- def has_island_trader(self) -> StardewRule:
- if self.options.exclude_ginger_island == ExcludeGingerIsland.option_true:
- return False_()
- return self.can_reach_region(Region.island_trader)
-
- def has_walnut(self, number: int) -> StardewRule:
- if self.options.exclude_ginger_island == ExcludeGingerIsland.option_true:
- return False_()
- if number <= 0:
- return True_()
- # https://stardewcommunitywiki.com/Golden_Walnut#Walnut_Locations
- reach_south = self.can_reach_region(Region.island_south)
- reach_north = self.can_reach_region(Region.island_north)
- reach_west = self.can_reach_region(Region.island_west)
- reach_hut = self.can_reach_region(Region.leo_hut)
- reach_southeast = self.can_reach_region(Region.island_south_east)
- reach_field_office = self.can_reach_region(Region.field_office)
- reach_pirate_cove = self.can_reach_region(Region.pirate_cove)
- reach_outside_areas = And(reach_south, reach_north, reach_west, reach_hut)
- reach_volcano_regions = [self.can_reach_region(Region.volcano),
- self.can_reach_region(Region.volcano_secret_beach),
- self.can_reach_region(Region.volcano_floor_5),
- self.can_reach_region(Region.volcano_floor_10)]
- reach_volcano = Or(reach_volcano_regions)
- reach_all_volcano = And(reach_volcano_regions)
- reach_walnut_regions = [reach_south, reach_north, reach_west, reach_volcano, reach_field_office]
- reach_caves = And(self.can_reach_region(Region.qi_walnut_room), self.can_reach_region(Region.dig_site),
- self.can_reach_region(Region.gourmand_frog_cave),
- self.can_reach_region(Region.colored_crystals_cave),
- self.can_reach_region(Region.shipwreck), self.has(Weapon.any_slingshot))
- reach_entire_island = And(reach_outside_areas, reach_field_office, reach_all_volcano,
- reach_caves, reach_southeast, reach_pirate_cove)
- if number <= 5:
- return Or(reach_south, reach_north, reach_west, reach_volcano)
- if number <= 10:
- return Count(2, reach_walnut_regions)
- if number <= 15:
- return Count(3, reach_walnut_regions)
- if number <= 20:
- return And(reach_walnut_regions)
- if number <= 50:
- return reach_entire_island
- gems = [Mineral.amethyst, Mineral.aquamarine, Mineral.emerald, Mineral.ruby, Mineral.topaz]
- return reach_entire_island & self.has(Fruit.banana) & self.has(gems) & self.can_mine_perfectly() & \
- self.can_fish_perfectly() & self.has(Craftable.flute_block) & self.has(Seed.melon) & self.has(Seed.wheat) & self.has(Seed.garlic) & \
- self.can_complete_field_office()
-
- def has_everything(self, all_progression_items: Set[str]) -> StardewRule:
- all_regions = [region.name for region in vanilla_regions]
- rules = self.received(all_progression_items, len(all_progression_items)) & \
- self.can_reach_all_regions(all_regions)
- return rules
-
- def heart(self, npc: Union[str, Villager]) -> str:
- if isinstance(npc, str):
- return f"{npc} <3"
- return self.heart(npc.name)
-
- def can_forage(self, season: str, region: str = Region.forest, need_hoe: bool = False) -> StardewRule:
- season_rule = self.has_season(season)
- region_rule = self.can_reach_region(region)
- if need_hoe:
- return season_rule & region_rule & self.has_tool(Tool.hoe)
- return season_rule & region_rule
-
- def npc_is_in_current_slot(self, name: str) -> bool:
- npc = all_villagers_by_name[name]
- mod = npc.mod_name
- return mod is None or mod in self.options.mods
-
- def can_do_combat_at_level(self, level: str) -> StardewRule:
- if level == Performance.basic:
- return self.has_any_weapon() | magic.has_any_spell(self)
- if level == Performance.decent:
- return self.has_decent_weapon() | magic.has_decent_spells(self)
- if level == Performance.good:
- return self.has_good_weapon() | magic.has_good_spells(self)
- if level == Performance.great:
- return self.has_great_weapon() | magic.has_great_spells(self)
- if level == Performance.galaxy:
- return self.has_galaxy_weapon() | magic.has_amazing_spells(self)
-
- def can_water(self, level: int) -> StardewRule:
- tool_rule = self.has_tool(Tool.watering_can, ToolMaterial.tiers[level])
- spell_rule = (self.received(MagicSpell.water) & magic.can_use_altar(self) & self.has_skill_level(ModSkill.magic, level))
- return tool_rule | spell_rule
-
- def has_prismatic_jelly_reward_access(self) -> StardewRule:
- if self.options.special_order_locations == SpecialOrderLocations.option_disabled:
- return self.can_complete_special_order("Prismatic Jelly")
- return self.received("Monster Musk Recipe")
-
- def has_all_rarecrows(self) -> StardewRule:
- rules = []
- for rarecrow_number in range(1, 9):
- rules.append(self.received(f"Rarecrow #{rarecrow_number}"))
- return And(rules)
-
- def can_ship(self, item: str = "") -> StardewRule:
- shipping_bin_rule = self.has_building(Building.shipping_bin)
- if item == "":
- return shipping_bin_rule
- return shipping_bin_rule & self.has(item)
-
diff --git a/worlds/stardew_valley/logic/__init__.py b/worlds/stardew_valley/logic/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/worlds/stardew_valley/logic/ability_logic.py b/worlds/stardew_valley/logic/ability_logic.py
new file mode 100644
index 000000000000..ae12ffee4742
--- /dev/null
+++ b/worlds/stardew_valley/logic/ability_logic.py
@@ -0,0 +1,46 @@
+from typing import Union
+
+from .base_logic import BaseLogicMixin, BaseLogic
+from .mine_logic import MineLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from .skill_logic import SkillLogicMixin
+from .tool_logic import ToolLogicMixin
+from ..mods.logic.magic_logic import MagicLogicMixin
+from ..stardew_rule import StardewRule
+from ..strings.region_names import Region
+from ..strings.skill_names import Skill, ModSkill
+from ..strings.tool_names import ToolMaterial, Tool
+
+
+class AbilityLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.ability = AbilityLogic(*args, **kwargs)
+
+
+class AbilityLogic(BaseLogic[Union[AbilityLogicMixin, RegionLogicMixin, ReceivedLogicMixin, ToolLogicMixin, SkillLogicMixin, MineLogicMixin, MagicLogicMixin]]):
+ def can_mine_perfectly(self) -> StardewRule:
+ return self.logic.mine.can_progress_in_the_mines_from_floor(160)
+
+ def can_mine_perfectly_in_the_skull_cavern(self) -> StardewRule:
+ return (self.logic.ability.can_mine_perfectly() &
+ self.logic.region.can_reach(Region.skull_cavern))
+
+ def can_farm_perfectly(self) -> StardewRule:
+ tool_rule = self.logic.tool.has_tool(Tool.hoe, ToolMaterial.iridium) & self.logic.tool.can_water(4)
+ return tool_rule & self.logic.skill.has_farming_level(10)
+
+ def can_fish_perfectly(self) -> StardewRule:
+ skill_rule = self.logic.skill.has_level(Skill.fishing, 10)
+ return skill_rule & self.logic.tool.has_fishing_rod(4)
+
+ def can_chop_trees(self) -> StardewRule:
+ return self.logic.tool.has_tool(Tool.axe) & self.logic.region.can_reach(Region.forest)
+
+ def can_chop_perfectly(self) -> StardewRule:
+ magic_rule = (self.logic.magic.can_use_clear_debris_instead_of_tool_level(3)) & self.logic.mod.skill.has_mod_level(ModSkill.magic, 10)
+ tool_rule = self.logic.tool.has_tool(Tool.axe, ToolMaterial.iridium)
+ foraging_rule = self.logic.skill.has_level(Skill.foraging, 10)
+ region_rule = self.logic.region.can_reach(Region.forest)
+ return region_rule & ((tool_rule & foraging_rule) | magic_rule)
diff --git a/worlds/stardew_valley/logic/action_logic.py b/worlds/stardew_valley/logic/action_logic.py
new file mode 100644
index 000000000000..820ae4ead429
--- /dev/null
+++ b/worlds/stardew_valley/logic/action_logic.py
@@ -0,0 +1,40 @@
+from typing import Union
+
+from Utils import cache_self1
+from .base_logic import BaseLogic, BaseLogicMixin
+from .has_logic import HasLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from ..stardew_rule import StardewRule, True_, Or
+from ..strings.generic_names import Generic
+from ..strings.geode_names import Geode
+from ..strings.region_names import Region
+
+
+class ActionLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.action = ActionLogic(*args, **kwargs)
+
+
+class ActionLogic(BaseLogic[Union[ActionLogicMixin, RegionLogicMixin, ReceivedLogicMixin, HasLogicMixin]]):
+
+ def can_watch(self, channel: str = None):
+ tv_rule = True_()
+ if channel is None:
+ return tv_rule
+ return self.logic.received(channel) & tv_rule
+
+ def can_pan(self) -> StardewRule:
+ return self.logic.received("Glittering Boulder Removed") & self.logic.region.can_reach(Region.mountain)
+
+ def can_pan_at(self, region: str) -> StardewRule:
+ return self.logic.region.can_reach(region) & self.logic.action.can_pan()
+
+ @cache_self1
+ def can_open_geode(self, geode: str) -> StardewRule:
+ blacksmith_access = self.logic.region.can_reach(Region.blacksmith)
+ geodes = [Geode.geode, Geode.frozen, Geode.magma, Geode.omni]
+ if geode == Generic.any:
+ return blacksmith_access & Or(*(self.logic.has(geode_type) for geode_type in geodes))
+ return blacksmith_access & self.logic.has(geode)
diff --git a/worlds/stardew_valley/logic/animal_logic.py b/worlds/stardew_valley/logic/animal_logic.py
new file mode 100644
index 000000000000..eb1ebeeec54b
--- /dev/null
+++ b/worlds/stardew_valley/logic/animal_logic.py
@@ -0,0 +1,59 @@
+from typing import Union
+
+from .base_logic import BaseLogicMixin, BaseLogic
+from .building_logic import BuildingLogicMixin
+from .has_logic import HasLogicMixin
+from .money_logic import MoneyLogicMixin
+from ..stardew_rule import StardewRule, true_
+from ..strings.animal_names import Animal, coop_animals, barn_animals
+from ..strings.building_names import Building
+from ..strings.forageable_names import Forageable
+from ..strings.generic_names import Generic
+from ..strings.region_names import Region
+
+cost_and_building_by_animal = {
+ Animal.chicken: (800, Building.coop),
+ Animal.cow: (1500, Building.barn),
+ Animal.goat: (4000, Building.big_barn),
+ Animal.duck: (1200, Building.big_coop),
+ Animal.sheep: (8000, Building.deluxe_barn),
+ Animal.rabbit: (8000, Building.deluxe_coop),
+ Animal.pig: (16000, Building.deluxe_barn)
+}
+
+
+class AnimalLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.animal = AnimalLogic(*args, **kwargs)
+
+
+class AnimalLogic(BaseLogic[Union[HasLogicMixin, MoneyLogicMixin, BuildingLogicMixin]]):
+
+ def can_buy_animal(self, animal: str) -> StardewRule:
+ try:
+ price, building = cost_and_building_by_animal[animal]
+ except KeyError:
+ return true_
+ return self.logic.money.can_spend_at(Region.ranch, price) & self.logic.building.has_building(building)
+
+ def has_animal(self, animal: str) -> StardewRule:
+ if animal == Generic.any:
+ return self.has_any_animal()
+ elif animal == Building.coop:
+ return self.has_any_coop_animal()
+ elif animal == Building.barn:
+ return self.has_any_barn_animal()
+ return self.logic.has(animal)
+
+ def has_happy_animal(self, animal: str) -> StardewRule:
+ return self.has_animal(animal) & self.logic.has(Forageable.hay)
+
+ def has_any_animal(self) -> StardewRule:
+ return self.has_any_coop_animal() | self.has_any_barn_animal()
+
+ def has_any_coop_animal(self) -> StardewRule:
+ return self.logic.has_any(*coop_animals)
+
+ def has_any_barn_animal(self) -> StardewRule:
+ return self.logic.has_any(*barn_animals)
diff --git a/worlds/stardew_valley/logic/arcade_logic.py b/worlds/stardew_valley/logic/arcade_logic.py
new file mode 100644
index 000000000000..5e6a02a18435
--- /dev/null
+++ b/worlds/stardew_valley/logic/arcade_logic.py
@@ -0,0 +1,34 @@
+from typing import Union
+
+from .base_logic import BaseLogic, BaseLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from .. import options
+from ..stardew_rule import StardewRule, True_
+from ..strings.region_names import Region
+
+
+class ArcadeLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.arcade = ArcadeLogic(*args, **kwargs)
+
+
+class ArcadeLogic(BaseLogic[Union[ArcadeLogicMixin, RegionLogicMixin, ReceivedLogicMixin]]):
+
+ def has_jotpk_power_level(self, power_level: int) -> StardewRule:
+ if self.options.arcade_machine_locations != options.ArcadeMachineLocations.option_full_shuffling:
+ return True_()
+ jotpk_buffs = ("JotPK: Progressive Boots", "JotPK: Progressive Gun", "JotPK: Progressive Ammo", "JotPK: Extra Life", "JotPK: Increased Drop Rate")
+ return self.logic.received_n(*jotpk_buffs, count=power_level)
+
+ def has_junimo_kart_power_level(self, power_level: int) -> StardewRule:
+ if self.options.arcade_machine_locations != options.ArcadeMachineLocations.option_full_shuffling:
+ return True_()
+ return self.logic.received("Junimo Kart: Extra Life", power_level)
+
+ def has_junimo_kart_max_level(self) -> StardewRule:
+ play_rule = self.logic.region.can_reach(Region.junimo_kart_3)
+ if self.options.arcade_machine_locations != options.ArcadeMachineLocations.option_full_shuffling:
+ return play_rule
+ return self.logic.arcade.has_junimo_kart_power_level(8)
diff --git a/worlds/stardew_valley/logic/artisan_logic.py b/worlds/stardew_valley/logic/artisan_logic.py
new file mode 100644
index 000000000000..cdc2186d807a
--- /dev/null
+++ b/worlds/stardew_valley/logic/artisan_logic.py
@@ -0,0 +1,53 @@
+from typing import Union
+
+from .base_logic import BaseLogic, BaseLogicMixin
+from .has_logic import HasLogicMixin
+from .time_logic import TimeLogicMixin
+from ..stardew_rule import StardewRule
+from ..strings.crop_names import all_vegetables, all_fruits, Vegetable, Fruit
+from ..strings.generic_names import Generic
+from ..strings.machine_names import Machine
+
+
+class ArtisanLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.artisan = ArtisanLogic(*args, **kwargs)
+
+
+class ArtisanLogic(BaseLogic[Union[ArtisanLogicMixin, TimeLogicMixin, HasLogicMixin]]):
+
+ def has_jelly(self) -> StardewRule:
+ return self.logic.artisan.can_preserves_jar(Fruit.any)
+
+ def has_pickle(self) -> StardewRule:
+ return self.logic.artisan.can_preserves_jar(Vegetable.any)
+
+ def can_preserves_jar(self, item: str) -> StardewRule:
+ machine_rule = self.logic.has(Machine.preserves_jar)
+ if item == Generic.any:
+ return machine_rule
+ if item == Fruit.any:
+ return machine_rule & self.logic.has_any(*all_fruits)
+ if item == Vegetable.any:
+ return machine_rule & self.logic.has_any(*all_vegetables)
+ return machine_rule & self.logic.has(item)
+
+ def has_wine(self) -> StardewRule:
+ return self.logic.artisan.can_keg(Fruit.any)
+
+ def has_juice(self) -> StardewRule:
+ return self.logic.artisan.can_keg(Vegetable.any)
+
+ def can_keg(self, item: str) -> StardewRule:
+ machine_rule = self.logic.has(Machine.keg)
+ if item == Generic.any:
+ return machine_rule
+ if item == Fruit.any:
+ return machine_rule & self.logic.has_any(*all_fruits)
+ if item == Vegetable.any:
+ return machine_rule & self.logic.has_any(*all_vegetables)
+ return machine_rule & self.logic.has(item)
+
+ def can_mayonnaise(self, item: str) -> StardewRule:
+ return self.logic.has(Machine.mayonnaise_machine) & self.logic.has(item)
diff --git a/worlds/stardew_valley/logic/base_logic.py b/worlds/stardew_valley/logic/base_logic.py
new file mode 100644
index 000000000000..9cfd089ea4f6
--- /dev/null
+++ b/worlds/stardew_valley/logic/base_logic.py
@@ -0,0 +1,50 @@
+from __future__ import annotations
+
+from typing import TypeVar, Generic, Dict, Collection
+
+from ..options import StardewValleyOptions
+from ..stardew_rule import StardewRule
+
+
+class LogicRegistry:
+
+ def __init__(self):
+ self.item_rules: Dict[str, StardewRule] = {}
+ self.sapling_rules: Dict[str, StardewRule] = {}
+ self.tree_fruit_rules: Dict[str, StardewRule] = {}
+ self.seed_rules: Dict[str, StardewRule] = {}
+ self.cooking_rules: Dict[str, StardewRule] = {}
+ self.crafting_rules: Dict[str, StardewRule] = {}
+ self.crop_rules: Dict[str, StardewRule] = {}
+ self.fish_rules: Dict[str, StardewRule] = {}
+ self.museum_rules: Dict[str, StardewRule] = {}
+ self.festival_rules: Dict[str, StardewRule] = {}
+ self.quest_rules: Dict[str, StardewRule] = {}
+ self.building_rules: Dict[str, StardewRule] = {}
+ self.special_order_rules: Dict[str, StardewRule] = {}
+
+ self.sve_location_rules: Dict[str, StardewRule] = {}
+
+
+class BaseLogicMixin:
+ def __init__(self, *args, **kwargs):
+ pass
+
+
+T = TypeVar("T", bound=BaseLogicMixin)
+
+
+class BaseLogic(BaseLogicMixin, Generic[T]):
+ player: int
+ registry: LogicRegistry
+ options: StardewValleyOptions
+ regions: Collection[str]
+ logic: T
+
+ def __init__(self, player: int, registry: LogicRegistry, options: StardewValleyOptions, regions: Collection[str], logic: T):
+ super().__init__(player, registry, options, regions, logic)
+ self.player = player
+ self.registry = registry
+ self.options = options
+ self.regions = regions
+ self.logic = logic
diff --git a/worlds/stardew_valley/logic/buff_logic.py b/worlds/stardew_valley/logic/buff_logic.py
new file mode 100644
index 000000000000..fee9c9fc4d25
--- /dev/null
+++ b/worlds/stardew_valley/logic/buff_logic.py
@@ -0,0 +1,23 @@
+from typing import Union
+
+from .base_logic import BaseLogicMixin, BaseLogic
+from .received_logic import ReceivedLogicMixin
+from ..stardew_rule import StardewRule
+from ..strings.ap_names.buff_names import Buff
+
+
+class BuffLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.buff = BuffLogic(*args, **kwargs)
+
+
+class BuffLogic(BaseLogic[Union[ReceivedLogicMixin]]):
+ def has_max_buffs(self) -> StardewRule:
+ return self.has_max_speed() & self.has_max_luck()
+
+ def has_max_speed(self) -> StardewRule:
+ return self.logic.received(Buff.movement, self.options.movement_buff_number.value)
+
+ def has_max_luck(self) -> StardewRule:
+ return self.logic.received(Buff.luck, self.options.luck_buff_number.value)
diff --git a/worlds/stardew_valley/logic/building_logic.py b/worlds/stardew_valley/logic/building_logic.py
new file mode 100644
index 000000000000..7be3d19ec33b
--- /dev/null
+++ b/worlds/stardew_valley/logic/building_logic.py
@@ -0,0 +1,95 @@
+from typing import Dict, Union
+
+from Utils import cache_self1
+from .base_logic import BaseLogic, BaseLogicMixin
+from .has_logic import HasLogicMixin
+from .money_logic import MoneyLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from ..options import BuildingProgression
+from ..stardew_rule import StardewRule, True_, False_, Has
+from ..strings.ap_names.event_names import Event
+from ..strings.artisan_good_names import ArtisanGood
+from ..strings.building_names import Building
+from ..strings.fish_names import WaterItem
+from ..strings.material_names import Material
+from ..strings.metal_names import MetalBar
+
+
+class BuildingLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.building = BuildingLogic(*args, **kwargs)
+
+
+class BuildingLogic(BaseLogic[Union[BuildingLogicMixin, MoneyLogicMixin, RegionLogicMixin, ReceivedLogicMixin, HasLogicMixin]]):
+ def initialize_rules(self):
+ self.registry.building_rules.update({
+ # @formatter:off
+ Building.barn: self.logic.money.can_spend(6000) & self.logic.has_all(Material.wood, Material.stone),
+ Building.big_barn: self.logic.money.can_spend(12000) & self.logic.has_all(Material.wood, Material.stone) & self.logic.building.has_building(Building.barn),
+ Building.deluxe_barn: self.logic.money.can_spend(25000) & self.logic.has_all(Material.wood, Material.stone) & self.logic.building.has_building(Building.big_barn),
+ Building.coop: self.logic.money.can_spend(4000) & self.logic.has_all(Material.wood, Material.stone),
+ Building.big_coop: self.logic.money.can_spend(10000) & self.logic.has_all(Material.wood, Material.stone) & self.logic.building.has_building(Building.coop),
+ Building.deluxe_coop: self.logic.money.can_spend(20000) & self.logic.has_all(Material.wood, Material.stone) & self.logic.building.has_building(Building.big_coop),
+ Building.fish_pond: self.logic.money.can_spend(5000) & self.logic.has_all(Material.stone, WaterItem.seaweed, WaterItem.green_algae),
+ Building.mill: self.logic.money.can_spend(2500) & self.logic.has_all(Material.stone, Material.wood, ArtisanGood.cloth),
+ Building.shed: self.logic.money.can_spend(15000) & self.logic.has(Material.wood),
+ Building.big_shed: self.logic.money.can_spend(20000) & self.logic.has_all(Material.wood, Material.stone) & self.logic.building.has_building(Building.shed),
+ Building.silo: self.logic.money.can_spend(100) & self.logic.has_all(Material.stone, Material.clay, MetalBar.copper),
+ Building.slime_hutch: self.logic.money.can_spend(10000) & self.logic.has_all(Material.stone, MetalBar.quartz, MetalBar.iridium),
+ Building.stable: self.logic.money.can_spend(10000) & self.logic.has_all(Material.hardwood, MetalBar.iron),
+ Building.well: self.logic.money.can_spend(1000) & self.logic.has(Material.stone),
+ Building.shipping_bin: self.logic.money.can_spend(250) & self.logic.has(Material.wood),
+ Building.kitchen: self.logic.money.can_spend(10000) & self.logic.has(Material.wood) & self.logic.building.has_house(0),
+ Building.kids_room: self.logic.money.can_spend(50000) & self.logic.has(Material.hardwood) & self.logic.building.has_house(1),
+ Building.cellar: self.logic.money.can_spend(100000) & self.logic.building.has_house(2),
+ # @formatter:on
+ })
+
+ def update_rules(self, new_rules: Dict[str, StardewRule]):
+ self.registry.building_rules.update(new_rules)
+
+ @cache_self1
+ def has_building(self, building: str) -> StardewRule:
+ # Shipping bin is special. The mod auto-builds it when received, no need to go to Robin.
+ if building is Building.shipping_bin:
+ if not self.options.building_progression & BuildingProgression.option_progressive:
+ return True_()
+ return self.logic.received(building)
+
+ carpenter_rule = self.logic.received(Event.can_construct_buildings)
+ if not self.options.building_progression & BuildingProgression.option_progressive:
+ return Has(building, self.registry.building_rules) & carpenter_rule
+
+ count = 1
+ if building in [Building.coop, Building.barn, Building.shed]:
+ building = f"Progressive {building}"
+ elif building.startswith("Big"):
+ count = 2
+ building = " ".join(["Progressive", *building.split(" ")[1:]])
+ elif building.startswith("Deluxe"):
+ count = 3
+ building = " ".join(["Progressive", *building.split(" ")[1:]])
+ return self.logic.received(building, count) & carpenter_rule
+
+ @cache_self1
+ def has_house(self, upgrade_level: int) -> StardewRule:
+ if upgrade_level < 1:
+ return True_()
+
+ if upgrade_level > 3:
+ return False_()
+
+ carpenter_rule = self.logic.received(Event.can_construct_buildings)
+ if self.options.building_progression & BuildingProgression.option_progressive:
+ return carpenter_rule & self.logic.received(f"Progressive House", upgrade_level)
+
+ if upgrade_level == 1:
+ return carpenter_rule & Has(Building.kitchen, self.registry.building_rules)
+
+ if upgrade_level == 2:
+ return carpenter_rule & Has(Building.kids_room, self.registry.building_rules)
+
+ # if upgrade_level == 3:
+ return carpenter_rule & Has(Building.cellar, self.registry.building_rules)
diff --git a/worlds/stardew_valley/logic/bundle_logic.py b/worlds/stardew_valley/logic/bundle_logic.py
new file mode 100644
index 000000000000..1ae07cf2ed82
--- /dev/null
+++ b/worlds/stardew_valley/logic/bundle_logic.py
@@ -0,0 +1,66 @@
+from functools import cached_property
+from typing import Union, List
+
+from .base_logic import BaseLogicMixin, BaseLogic
+from .farming_logic import FarmingLogicMixin
+from .fishing_logic import FishingLogicMixin
+from .has_logic import HasLogicMixin
+from .money_logic import MoneyLogicMixin
+from .region_logic import RegionLogicMixin
+from .skill_logic import SkillLogicMixin
+from ..bundles.bundle import Bundle
+from ..stardew_rule import StardewRule, And, True_
+from ..strings.currency_names import Currency
+from ..strings.machine_names import Machine
+from ..strings.quality_names import CropQuality, ForageQuality, FishQuality, ArtisanQuality
+from ..strings.region_names import Region
+
+
+class BundleLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.bundle = BundleLogic(*args, **kwargs)
+
+
+class BundleLogic(BaseLogic[Union[HasLogicMixin, RegionLogicMixin, MoneyLogicMixin, FarmingLogicMixin, FishingLogicMixin, SkillLogicMixin]]):
+ # Should be cached
+ def can_complete_bundle(self, bundle: Bundle) -> StardewRule:
+ item_rules = []
+ qualities = []
+ can_speak_junimo = self.logic.region.can_reach(Region.wizard_tower)
+ for bundle_item in bundle.items:
+ if Currency.is_currency(bundle_item.item_name):
+ return can_speak_junimo & self.logic.money.can_trade(bundle_item.item_name, bundle_item.amount)
+
+ item_rules.append(bundle_item.item_name)
+ qualities.append(bundle_item.quality)
+ quality_rules = self.get_quality_rules(qualities)
+ item_rules = self.logic.has_n(*item_rules, count=bundle.number_required)
+ return can_speak_junimo & item_rules & quality_rules
+
+ def get_quality_rules(self, qualities: List[str]) -> StardewRule:
+ crop_quality = CropQuality.get_highest(qualities)
+ fish_quality = FishQuality.get_highest(qualities)
+ forage_quality = ForageQuality.get_highest(qualities)
+ artisan_quality = ArtisanQuality.get_highest(qualities)
+ quality_rules = []
+ if crop_quality != CropQuality.basic:
+ quality_rules.append(self.logic.farming.can_grow_crop_quality(crop_quality))
+ if fish_quality != FishQuality.basic:
+ quality_rules.append(self.logic.fishing.can_catch_quality_fish(fish_quality))
+ if forage_quality != ForageQuality.basic:
+ quality_rules.append(self.logic.skill.can_forage_quality(forage_quality))
+ if artisan_quality != ArtisanQuality.basic:
+ quality_rules.append(self.logic.has(Machine.cask))
+ if not quality_rules:
+ return True_()
+ return And(*quality_rules)
+
+ @cached_property
+ def can_complete_community_center(self) -> StardewRule:
+ return (self.logic.region.can_reach_location("Complete Crafts Room") &
+ self.logic.region.can_reach_location("Complete Pantry") &
+ self.logic.region.can_reach_location("Complete Fish Tank") &
+ self.logic.region.can_reach_location("Complete Bulletin Board") &
+ self.logic.region.can_reach_location("Complete Vault") &
+ self.logic.region.can_reach_location("Complete Boiler Room"))
diff --git a/worlds/stardew_valley/logic/combat_logic.py b/worlds/stardew_valley/logic/combat_logic.py
new file mode 100644
index 000000000000..ba825192a99e
--- /dev/null
+++ b/worlds/stardew_valley/logic/combat_logic.py
@@ -0,0 +1,57 @@
+from functools import cached_property
+from typing import Union
+
+from Utils import cache_self1
+from .base_logic import BaseLogicMixin, BaseLogic
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from ..mods.logic.magic_logic import MagicLogicMixin
+from ..stardew_rule import StardewRule, Or, False_
+from ..strings.ap_names.ap_weapon_names import APWeapon
+from ..strings.performance_names import Performance
+
+valid_weapons = (APWeapon.weapon, APWeapon.sword, APWeapon.club, APWeapon.dagger)
+
+
+class CombatLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.combat = CombatLogic(*args, **kwargs)
+
+
+class CombatLogic(BaseLogic[Union[CombatLogicMixin, RegionLogicMixin, ReceivedLogicMixin, MagicLogicMixin]]):
+ @cache_self1
+ def can_fight_at_level(self, level: str) -> StardewRule:
+ if level == Performance.basic:
+ return self.logic.combat.has_any_weapon | self.logic.magic.has_any_spell()
+ if level == Performance.decent:
+ return self.logic.combat.has_decent_weapon | self.logic.magic.has_decent_spells()
+ if level == Performance.good:
+ return self.logic.combat.has_good_weapon | self.logic.magic.has_good_spells()
+ if level == Performance.great:
+ return self.logic.combat.has_great_weapon | self.logic.magic.has_great_spells()
+ if level == Performance.galaxy:
+ return self.logic.combat.has_galaxy_weapon | self.logic.magic.has_amazing_spells()
+ if level == Performance.maximum:
+ return self.logic.combat.has_galaxy_weapon | self.logic.magic.has_amazing_spells() # Someday we will have the ascended weapons in AP
+ return False_()
+
+ @cached_property
+ def has_any_weapon(self) -> StardewRule:
+ return self.logic.received_any(*valid_weapons)
+
+ @cached_property
+ def has_decent_weapon(self) -> StardewRule:
+ return Or(*(self.logic.received(weapon, 2) for weapon in valid_weapons))
+
+ @cached_property
+ def has_good_weapon(self) -> StardewRule:
+ return Or(*(self.logic.received(weapon, 3) for weapon in valid_weapons))
+
+ @cached_property
+ def has_great_weapon(self) -> StardewRule:
+ return Or(*(self.logic.received(weapon, 4) for weapon in valid_weapons))
+
+ @cached_property
+ def has_galaxy_weapon(self) -> StardewRule:
+ return Or(*(self.logic.received(weapon, 5) for weapon in valid_weapons))
diff --git a/worlds/stardew_valley/logic/cooking_logic.py b/worlds/stardew_valley/logic/cooking_logic.py
new file mode 100644
index 000000000000..51cc74d0517a
--- /dev/null
+++ b/worlds/stardew_valley/logic/cooking_logic.py
@@ -0,0 +1,108 @@
+from functools import cached_property
+from typing import Union
+
+from Utils import cache_self1
+from .action_logic import ActionLogicMixin
+from .base_logic import BaseLogicMixin, BaseLogic
+from .building_logic import BuildingLogicMixin
+from .has_logic import HasLogicMixin
+from .money_logic import MoneyLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from .relationship_logic import RelationshipLogicMixin
+from .season_logic import SeasonLogicMixin
+from .skill_logic import SkillLogicMixin
+from ..data.recipe_data import RecipeSource, StarterSource, ShopSource, SkillSource, FriendshipSource, \
+ QueenOfSauceSource, CookingRecipe, ShopFriendshipSource, \
+ all_cooking_recipes_by_name
+from ..data.recipe_source import CutsceneSource, ShopTradeSource
+from ..locations import locations_by_tag, LocationTags
+from ..options import Chefsanity
+from ..options import ExcludeGingerIsland
+from ..stardew_rule import StardewRule, True_, False_, And
+from ..strings.region_names import Region
+from ..strings.skill_names import Skill
+from ..strings.tv_channel_names import Channel
+
+
+class CookingLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.cooking = CookingLogic(*args, **kwargs)
+
+
+class CookingLogic(BaseLogic[Union[HasLogicMixin, ReceivedLogicMixin, RegionLogicMixin, SeasonLogicMixin, MoneyLogicMixin, ActionLogicMixin,
+BuildingLogicMixin, RelationshipLogicMixin, SkillLogicMixin, CookingLogicMixin]]):
+ @cached_property
+ def can_cook_in_kitchen(self) -> StardewRule:
+ return self.logic.building.has_house(1) | self.logic.skill.has_level(Skill.foraging, 9)
+
+ # Should be cached
+ def can_cook(self, recipe: CookingRecipe = None) -> StardewRule:
+ cook_rule = self.logic.region.can_reach(Region.kitchen)
+ if recipe is None:
+ return cook_rule
+
+ recipe_rule = self.logic.cooking.knows_recipe(recipe.source, recipe.meal)
+ ingredients_rule = self.logic.has_all(*sorted(recipe.ingredients))
+ return cook_rule & recipe_rule & ingredients_rule
+
+ # Should be cached
+ def knows_recipe(self, source: RecipeSource, meal_name: str) -> StardewRule:
+ if self.options.chefsanity == Chefsanity.option_none:
+ return self.logic.cooking.can_learn_recipe(source)
+ if isinstance(source, StarterSource):
+ return self.logic.cooking.received_recipe(meal_name)
+ if isinstance(source, ShopTradeSource) and self.options.chefsanity & Chefsanity.option_purchases:
+ return self.logic.cooking.received_recipe(meal_name)
+ if isinstance(source, ShopSource) and self.options.chefsanity & Chefsanity.option_purchases:
+ return self.logic.cooking.received_recipe(meal_name)
+ if isinstance(source, SkillSource) and self.options.chefsanity & Chefsanity.option_skills:
+ return self.logic.cooking.received_recipe(meal_name)
+ if isinstance(source, CutsceneSource) and self.options.chefsanity & Chefsanity.option_friendship:
+ return self.logic.cooking.received_recipe(meal_name)
+ if isinstance(source, FriendshipSource) and self.options.chefsanity & Chefsanity.option_friendship:
+ return self.logic.cooking.received_recipe(meal_name)
+ if isinstance(source, QueenOfSauceSource) and self.options.chefsanity & Chefsanity.option_queen_of_sauce:
+ return self.logic.cooking.received_recipe(meal_name)
+ if isinstance(source, ShopFriendshipSource) and self.options.chefsanity & Chefsanity.option_friendship:
+ return self.logic.cooking.received_recipe(meal_name)
+ return self.logic.cooking.can_learn_recipe(source)
+
+ @cache_self1
+ def can_learn_recipe(self, source: RecipeSource) -> StardewRule:
+ if isinstance(source, StarterSource):
+ return True_()
+ if isinstance(source, ShopTradeSource):
+ return self.logic.money.can_trade_at(source.region, source.currency, source.price)
+ if isinstance(source, ShopSource):
+ return self.logic.money.can_spend_at(source.region, source.price)
+ if isinstance(source, SkillSource):
+ return self.logic.skill.has_level(source.skill, source.level)
+ if isinstance(source, CutsceneSource):
+ return self.logic.region.can_reach(source.region) & self.logic.relationship.has_hearts(source.friend, source.hearts)
+ if isinstance(source, FriendshipSource):
+ return self.logic.relationship.has_hearts(source.friend, source.hearts)
+ if isinstance(source, QueenOfSauceSource):
+ return self.logic.action.can_watch(Channel.queen_of_sauce) & self.logic.season.has(source.season)
+ if isinstance(source, ShopFriendshipSource):
+ return self.logic.money.can_spend_at(source.region, source.price) & self.logic.relationship.has_hearts(source.friend, source.hearts)
+ return False_()
+
+ @cache_self1
+ def received_recipe(self, meal_name: str):
+ return self.logic.received(f"{meal_name} Recipe")
+
+ @cached_property
+ def can_cook_everything(self) -> StardewRule:
+ cooksanity_prefix = "Cook "
+ all_recipes_names = []
+ exclude_island = self.options.exclude_ginger_island == ExcludeGingerIsland.option_true
+ for location in locations_by_tag[LocationTags.COOKSANITY]:
+ if exclude_island and LocationTags.GINGER_ISLAND in location.tags:
+ continue
+ if location.mod_name and location.mod_name not in self.options.mods:
+ continue
+ all_recipes_names.append(location.name[len(cooksanity_prefix):])
+ all_recipes = [all_cooking_recipes_by_name[recipe_name] for recipe_name in all_recipes_names]
+ return And(*(self.logic.cooking.can_cook(recipe) for recipe in all_recipes))
diff --git a/worlds/stardew_valley/logic/crafting_logic.py b/worlds/stardew_valley/logic/crafting_logic.py
new file mode 100644
index 000000000000..8c267b7d1090
--- /dev/null
+++ b/worlds/stardew_valley/logic/crafting_logic.py
@@ -0,0 +1,111 @@
+from functools import cached_property
+from typing import Union
+
+from Utils import cache_self1
+from .base_logic import BaseLogicMixin, BaseLogic
+from .has_logic import HasLogicMixin
+from .money_logic import MoneyLogicMixin
+from .quest_logic import QuestLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from .relationship_logic import RelationshipLogicMixin
+from .skill_logic import SkillLogicMixin
+from .special_order_logic import SpecialOrderLogicMixin
+from .. import options
+from ..data.craftable_data import CraftingRecipe, all_crafting_recipes_by_name
+from ..data.recipe_data import StarterSource, ShopSource, SkillSource, FriendshipSource
+from ..data.recipe_source import CutsceneSource, ShopTradeSource, ArchipelagoSource, LogicSource, SpecialOrderSource, \
+ FestivalShopSource, QuestSource
+from ..locations import locations_by_tag, LocationTags
+from ..options import Craftsanity, SpecialOrderLocations, ExcludeGingerIsland
+from ..stardew_rule import StardewRule, True_, False_, And
+from ..strings.region_names import Region
+
+
+class CraftingLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.crafting = CraftingLogic(*args, **kwargs)
+
+
+class CraftingLogic(BaseLogic[Union[ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, MoneyLogicMixin, RelationshipLogicMixin,
+SkillLogicMixin, SpecialOrderLogicMixin, CraftingLogicMixin, QuestLogicMixin]]):
+ @cache_self1
+ def can_craft(self, recipe: CraftingRecipe = None) -> StardewRule:
+ if recipe is None:
+ return True_()
+
+ learn_rule = self.logic.crafting.knows_recipe(recipe)
+ ingredients_rule = self.logic.has_all(*recipe.ingredients)
+ return learn_rule & ingredients_rule
+
+ @cache_self1
+ def knows_recipe(self, recipe: CraftingRecipe) -> StardewRule:
+ if isinstance(recipe.source, ArchipelagoSource):
+ return self.logic.received_all(*recipe.source.ap_item)
+ if isinstance(recipe.source, FestivalShopSource):
+ if self.options.festival_locations == options.FestivalLocations.option_disabled:
+ return self.logic.crafting.can_learn_recipe(recipe)
+ else:
+ return self.logic.crafting.received_recipe(recipe.item)
+ if isinstance(recipe.source, QuestSource):
+ if self.options.quest_locations < 0:
+ return self.logic.crafting.can_learn_recipe(recipe)
+ else:
+ return self.logic.crafting.received_recipe(recipe.item)
+ if self.options.craftsanity == Craftsanity.option_none:
+ return self.logic.crafting.can_learn_recipe(recipe)
+ if isinstance(recipe.source, StarterSource) or isinstance(recipe.source, ShopTradeSource) or isinstance(
+ recipe.source, ShopSource):
+ return self.logic.crafting.received_recipe(recipe.item)
+ if isinstance(recipe.source, SpecialOrderSource) and self.options.special_order_locations != SpecialOrderLocations.option_disabled:
+ return self.logic.crafting.received_recipe(recipe.item)
+ return self.logic.crafting.can_learn_recipe(recipe)
+
+ @cache_self1
+ def can_learn_recipe(self, recipe: CraftingRecipe) -> StardewRule:
+ if isinstance(recipe.source, StarterSource):
+ return True_()
+ if isinstance(recipe.source, ArchipelagoSource):
+ return self.logic.received_all(*recipe.source.ap_item)
+ if isinstance(recipe.source, ShopTradeSource):
+ return self.logic.money.can_trade_at(recipe.source.region, recipe.source.currency, recipe.source.price)
+ if isinstance(recipe.source, ShopSource):
+ return self.logic.money.can_spend_at(recipe.source.region, recipe.source.price)
+ if isinstance(recipe.source, SkillSource):
+ return self.logic.skill.has_level(recipe.source.skill, recipe.source.level)
+ if isinstance(recipe.source, CutsceneSource):
+ return self.logic.region.can_reach(recipe.source.region) & self.logic.relationship.has_hearts(recipe.source.friend, recipe.source.hearts)
+ if isinstance(recipe.source, FriendshipSource):
+ return self.logic.relationship.has_hearts(recipe.source.friend, recipe.source.hearts)
+ if isinstance(recipe.source, QuestSource):
+ return self.logic.quest.can_complete_quest(recipe.source.quest)
+ if isinstance(recipe.source, SpecialOrderSource):
+ if self.options.special_order_locations == SpecialOrderLocations.option_disabled:
+ return self.logic.special_order.can_complete_special_order(recipe.source.special_order)
+ return self.logic.crafting.received_recipe(recipe.item)
+ if isinstance(recipe.source, LogicSource):
+ if recipe.source.logic_rule == "Cellar":
+ return self.logic.region.can_reach(Region.cellar)
+
+ return False_()
+
+ @cache_self1
+ def received_recipe(self, item_name: str):
+ return self.logic.received(f"{item_name} Recipe")
+
+ @cached_property
+ def can_craft_everything(self) -> StardewRule:
+ craftsanity_prefix = "Craft "
+ all_recipes_names = []
+ exclude_island = self.options.exclude_ginger_island == ExcludeGingerIsland.option_true
+ for location in locations_by_tag[LocationTags.CRAFTSANITY]:
+ if not location.name.startswith(craftsanity_prefix):
+ continue
+ if exclude_island and LocationTags.GINGER_ISLAND in location.tags:
+ continue
+ if location.mod_name and location.mod_name not in self.options.mods:
+ continue
+ all_recipes_names.append(location.name[len(craftsanity_prefix):])
+ all_recipes = [all_crafting_recipes_by_name[recipe_name] for recipe_name in all_recipes_names]
+ return And(*(self.logic.crafting.can_craft(recipe) for recipe in all_recipes))
diff --git a/worlds/stardew_valley/logic/crop_logic.py b/worlds/stardew_valley/logic/crop_logic.py
new file mode 100644
index 000000000000..8c107ba6a5df
--- /dev/null
+++ b/worlds/stardew_valley/logic/crop_logic.py
@@ -0,0 +1,72 @@
+from typing import Union, Iterable
+
+from Utils import cache_self1
+from .base_logic import BaseLogicMixin, BaseLogic
+from .has_logic import HasLogicMixin
+from .money_logic import MoneyLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from .season_logic import SeasonLogicMixin
+from .tool_logic import ToolLogicMixin
+from .traveling_merchant_logic import TravelingMerchantLogicMixin
+from ..data import CropItem, SeedItem
+from ..options import Cropsanity, ExcludeGingerIsland
+from ..stardew_rule import StardewRule, True_, False_
+from ..strings.craftable_names import Craftable
+from ..strings.forageable_names import Forageable
+from ..strings.machine_names import Machine
+from ..strings.metal_names import Fossil
+from ..strings.region_names import Region
+from ..strings.seed_names import Seed
+from ..strings.tool_names import Tool
+
+
+class CropLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.crop = CropLogic(*args, **kwargs)
+
+
+class CropLogic(BaseLogic[Union[HasLogicMixin, ReceivedLogicMixin, RegionLogicMixin, TravelingMerchantLogicMixin, SeasonLogicMixin, MoneyLogicMixin,
+ ToolLogicMixin, CropLogicMixin]]):
+ @cache_self1
+ def can_grow(self, crop: CropItem) -> StardewRule:
+ season_rule = self.logic.season.has_any(crop.farm_growth_seasons)
+ seed_rule = self.logic.has(crop.seed.name)
+ farm_rule = self.logic.region.can_reach(Region.farm) & season_rule
+ tool_rule = self.logic.tool.has_tool(Tool.hoe) & self.logic.tool.has_tool(Tool.watering_can)
+ region_rule = farm_rule | self.logic.region.can_reach(Region.greenhouse) | self.logic.crop.has_island_farm()
+ if crop.name == Forageable.cactus_fruit:
+ region_rule = self.logic.region.can_reach(Region.greenhouse) | self.logic.has(Craftable.garden_pot)
+ return seed_rule & region_rule & tool_rule
+
+ def can_plant_and_grow_item(self, seasons: Union[str, Iterable[str]]) -> StardewRule:
+ if isinstance(seasons, str):
+ seasons = [seasons]
+ season_rule = self.logic.season.has_any(seasons) | self.logic.region.can_reach(Region.greenhouse) | self.logic.crop.has_island_farm()
+ farm_rule = self.logic.region.can_reach(Region.farm) | self.logic.region.can_reach(Region.greenhouse) | self.logic.crop.has_island_farm()
+ return season_rule & farm_rule
+
+ def has_island_farm(self) -> StardewRule:
+ if self.options.exclude_ginger_island == ExcludeGingerIsland.option_false:
+ return self.logic.region.can_reach(Region.island_west)
+ return False_()
+
+ @cache_self1
+ def can_buy_seed(self, seed: SeedItem) -> StardewRule:
+ if seed.requires_island and self.options.exclude_ginger_island == ExcludeGingerIsland.option_true:
+ return False_()
+ if self.options.cropsanity == Cropsanity.option_disabled or seed.name == Seed.qi_bean:
+ item_rule = True_()
+ else:
+ item_rule = self.logic.received(seed.name)
+ if seed.name == Seed.coffee:
+ item_rule = item_rule & self.logic.traveling_merchant.has_days(3)
+ season_rule = self.logic.season.has_any(seed.seasons)
+ region_rule = self.logic.region.can_reach_all(seed.regions)
+ currency_rule = self.logic.money.can_spend(1000)
+ if seed.name == Seed.pineapple:
+ currency_rule = self.logic.has(Forageable.magma_cap)
+ if seed.name == Seed.taro:
+ currency_rule = self.logic.has(Fossil.bone_fragment)
+ return season_rule & region_rule & item_rule & currency_rule
diff --git a/worlds/stardew_valley/logic/farming_logic.py b/worlds/stardew_valley/logic/farming_logic.py
new file mode 100644
index 000000000000..b255aa27f785
--- /dev/null
+++ b/worlds/stardew_valley/logic/farming_logic.py
@@ -0,0 +1,41 @@
+from typing import Union
+
+from .base_logic import BaseLogicMixin, BaseLogic
+from .has_logic import HasLogicMixin
+from .skill_logic import SkillLogicMixin
+from ..stardew_rule import StardewRule, True_, False_
+from ..strings.fertilizer_names import Fertilizer
+from ..strings.quality_names import CropQuality
+
+
+class FarmingLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.farming = FarmingLogic(*args, **kwargs)
+
+
+class FarmingLogic(BaseLogic[Union[HasLogicMixin, SkillLogicMixin, FarmingLogicMixin]]):
+ def has_fertilizer(self, tier: int) -> StardewRule:
+ if tier <= 0:
+ return True_()
+ if tier == 1:
+ return self.logic.has(Fertilizer.basic)
+ if tier == 2:
+ return self.logic.has(Fertilizer.quality)
+ if tier >= 3:
+ return self.logic.has(Fertilizer.deluxe)
+
+ def can_grow_crop_quality(self, quality: str) -> StardewRule:
+ if quality == CropQuality.basic:
+ return True_()
+ if quality == CropQuality.silver:
+ return self.logic.skill.has_farming_level(5) | (self.logic.farming.has_fertilizer(1) & self.logic.skill.has_farming_level(2)) | (
+ self.logic.farming.has_fertilizer(2) & self.logic.skill.has_farming_level(1)) | self.logic.farming.has_fertilizer(3)
+ if quality == CropQuality.gold:
+ return self.logic.skill.has_farming_level(10) | (
+ self.logic.farming.has_fertilizer(1) & self.logic.skill.has_farming_level(5)) | (
+ self.logic.farming.has_fertilizer(2) & self.logic.skill.has_farming_level(3)) | (
+ self.logic.farming.has_fertilizer(3) & self.logic.skill.has_farming_level(2))
+ if quality == CropQuality.iridium:
+ return self.logic.farming.has_fertilizer(3) & self.logic.skill.has_farming_level(4)
+ return False_()
diff --git a/worlds/stardew_valley/logic/fishing_logic.py b/worlds/stardew_valley/logic/fishing_logic.py
new file mode 100644
index 000000000000..a7399a65d99c
--- /dev/null
+++ b/worlds/stardew_valley/logic/fishing_logic.py
@@ -0,0 +1,101 @@
+from typing import Union, List
+
+from Utils import cache_self1
+from .base_logic import BaseLogicMixin, BaseLogic
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from .season_logic import SeasonLogicMixin
+from .skill_logic import SkillLogicMixin
+from .tool_logic import ToolLogicMixin
+from ..data import FishItem, fish_data
+from ..locations import LocationTags, locations_by_tag
+from ..options import ExcludeGingerIsland, Fishsanity
+from ..options import SpecialOrderLocations
+from ..stardew_rule import StardewRule, True_, False_, And
+from ..strings.fish_names import SVEFish
+from ..strings.quality_names import FishQuality
+from ..strings.region_names import Region
+from ..strings.skill_names import Skill
+
+
+class FishingLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.fishing = FishingLogic(*args, **kwargs)
+
+
+class FishingLogic(BaseLogic[Union[FishingLogicMixin, ReceivedLogicMixin, RegionLogicMixin, SeasonLogicMixin, ToolLogicMixin, SkillLogicMixin]]):
+ def can_fish_in_freshwater(self) -> StardewRule:
+ return self.logic.skill.can_fish() & self.logic.region.can_reach_any((Region.forest, Region.town, Region.mountain))
+
+ def has_max_fishing(self) -> StardewRule:
+ skill_rule = self.logic.skill.has_level(Skill.fishing, 10)
+ return self.logic.tool.has_fishing_rod(4) & skill_rule
+
+ def can_fish_chests(self) -> StardewRule:
+ skill_rule = self.logic.skill.has_level(Skill.fishing, 6)
+ return self.logic.tool.has_fishing_rod(4) & skill_rule
+
+ def can_fish_at(self, region: str) -> StardewRule:
+ return self.logic.skill.can_fish() & self.logic.region.can_reach(region)
+
+ @cache_self1
+ def can_catch_fish(self, fish: FishItem) -> StardewRule:
+ quest_rule = True_()
+ if fish.extended_family:
+ quest_rule = self.logic.fishing.can_start_extended_family_quest()
+ region_rule = self.logic.region.can_reach_any(fish.locations)
+ season_rule = self.logic.season.has_any(fish.seasons)
+ if fish.difficulty == -1:
+ difficulty_rule = self.logic.skill.can_crab_pot
+ else:
+ difficulty_rule = self.logic.skill.can_fish(difficulty=(120 if fish.legendary else fish.difficulty))
+ if fish.name == SVEFish.kittyfish:
+ item_rule = self.logic.received("Kittyfish Spell")
+ else:
+ item_rule = True_()
+ return quest_rule & region_rule & season_rule & difficulty_rule & item_rule
+
+ def can_start_extended_family_quest(self) -> StardewRule:
+ if self.options.exclude_ginger_island == ExcludeGingerIsland.option_true:
+ return False_()
+ if self.options.special_order_locations != SpecialOrderLocations.option_board_qi:
+ return False_()
+ return self.logic.region.can_reach(Region.qi_walnut_room) & And(*(self.logic.fishing.can_catch_fish(fish) for fish in fish_data.legendary_fish))
+
+ def can_catch_quality_fish(self, fish_quality: str) -> StardewRule:
+ if fish_quality == FishQuality.basic:
+ return True_()
+ rod_rule = self.logic.tool.has_fishing_rod(2)
+ if fish_quality == FishQuality.silver:
+ return rod_rule
+ if fish_quality == FishQuality.gold:
+ return rod_rule & self.logic.skill.has_level(Skill.fishing, 4)
+ if fish_quality == FishQuality.iridium:
+ return rod_rule & self.logic.skill.has_level(Skill.fishing, 10)
+
+ raise ValueError(f"Quality {fish_quality} is unknown.")
+
+ def can_catch_every_fish(self) -> StardewRule:
+ rules = [self.has_max_fishing()]
+ exclude_island = self.options.exclude_ginger_island == ExcludeGingerIsland.option_true
+ exclude_extended_family = self.options.special_order_locations != SpecialOrderLocations.option_board_qi
+ for fish in fish_data.get_fish_for_mods(self.options.mods.value):
+ if exclude_island and fish in fish_data.island_fish:
+ continue
+ if exclude_extended_family and fish in fish_data.extended_family:
+ continue
+ rules.append(self.logic.fishing.can_catch_fish(fish))
+ return And(*rules)
+
+ def can_catch_every_fish_in_slot(self, all_location_names_in_slot: List[str]) -> StardewRule:
+ if self.options.fishsanity == Fishsanity.option_none:
+ return self.can_catch_every_fish()
+
+ rules = [self.has_max_fishing()]
+
+ for fishsanity_location in locations_by_tag[LocationTags.FISHSANITY]:
+ if fishsanity_location.name not in all_location_names_in_slot:
+ continue
+ rules.append(self.logic.region.can_reach_location(fishsanity_location.name))
+ return And(*rules)
diff --git a/worlds/stardew_valley/logic/gift_logic.py b/worlds/stardew_valley/logic/gift_logic.py
new file mode 100644
index 000000000000..527da6876411
--- /dev/null
+++ b/worlds/stardew_valley/logic/gift_logic.py
@@ -0,0 +1,20 @@
+from functools import cached_property
+
+from .base_logic import BaseLogic, BaseLogicMixin
+from .has_logic import HasLogicMixin
+from ..stardew_rule import StardewRule
+from ..strings.animal_product_names import AnimalProduct
+from ..strings.gift_names import Gift
+
+
+class GiftLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.gifts = GiftLogic(*args, **kwargs)
+
+
+class GiftLogic(BaseLogic[HasLogicMixin]):
+
+ @cached_property
+ def has_any_universal_love(self) -> StardewRule:
+ return self.logic.has_any(Gift.golden_pumpkin, Gift.pearl, "Prismatic Shard", AnimalProduct.rabbit_foot)
diff --git a/worlds/stardew_valley/logic/has_logic.py b/worlds/stardew_valley/logic/has_logic.py
new file mode 100644
index 000000000000..d92d4224d7d2
--- /dev/null
+++ b/worlds/stardew_valley/logic/has_logic.py
@@ -0,0 +1,34 @@
+from .base_logic import BaseLogic
+from ..stardew_rule import StardewRule, And, Or, Has, Count
+
+
+class HasLogicMixin(BaseLogic[None]):
+ # Should be cached
+ def has(self, item: str) -> StardewRule:
+ return Has(item, self.registry.item_rules)
+
+ def has_all(self, *items: str):
+ assert items, "Can't have all of no items."
+
+ return And(*(self.has(item) for item in items))
+
+ def has_any(self, *items: str):
+ assert items, "Can't have any of no items."
+
+ return Or(*(self.has(item) for item in items))
+
+ def has_n(self, *items: str, count: int):
+ return self.count(count, *(self.has(item) for item in items))
+
+ @staticmethod
+ def count(count: int, *rules: StardewRule) -> StardewRule:
+ assert rules, "Can't create a Count conditions without rules"
+ assert len(rules) >= count, "Count need at least as many rules as the count"
+
+ if count == 1:
+ return Or(*rules)
+
+ if count == len(rules):
+ return And(*rules)
+
+ return Count(list(rules), count)
diff --git a/worlds/stardew_valley/logic/logic.py b/worlds/stardew_valley/logic/logic.py
new file mode 100644
index 000000000000..a7fcec922838
--- /dev/null
+++ b/worlds/stardew_valley/logic/logic.py
@@ -0,0 +1,674 @@
+from __future__ import annotations
+
+from dataclasses import dataclass
+from typing import Collection
+
+from .ability_logic import AbilityLogicMixin
+from .action_logic import ActionLogicMixin
+from .animal_logic import AnimalLogicMixin
+from .arcade_logic import ArcadeLogicMixin
+from .artisan_logic import ArtisanLogicMixin
+from .base_logic import LogicRegistry
+from .buff_logic import BuffLogicMixin
+from .building_logic import BuildingLogicMixin
+from .bundle_logic import BundleLogicMixin
+from .combat_logic import CombatLogicMixin
+from .cooking_logic import CookingLogicMixin
+from .crafting_logic import CraftingLogicMixin
+from .crop_logic import CropLogicMixin
+from .farming_logic import FarmingLogicMixin
+from .fishing_logic import FishingLogicMixin
+from .gift_logic import GiftLogicMixin
+from .has_logic import HasLogicMixin
+from .mine_logic import MineLogicMixin
+from .money_logic import MoneyLogicMixin
+from .monster_logic import MonsterLogicMixin
+from .museum_logic import MuseumLogicMixin
+from .pet_logic import PetLogicMixin
+from .quest_logic import QuestLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from .relationship_logic import RelationshipLogicMixin
+from .season_logic import SeasonLogicMixin
+from .shipping_logic import ShippingLogicMixin
+from .skill_logic import SkillLogicMixin
+from .special_order_logic import SpecialOrderLogicMixin
+from .time_logic import TimeLogicMixin
+from .tool_logic import ToolLogicMixin
+from .traveling_merchant_logic import TravelingMerchantLogicMixin
+from .wallet_logic import WalletLogicMixin
+from ..data import all_purchasable_seeds, all_crops
+from ..data.craftable_data import all_crafting_recipes
+from ..data.crops_data import crops_by_name
+from ..data.fish_data import get_fish_for_mods
+from ..data.museum_data import all_museum_items
+from ..data.recipe_data import all_cooking_recipes
+from ..mods.logic.magic_logic import MagicLogicMixin
+from ..mods.logic.mod_logic import ModLogicMixin
+from ..mods.mod_data import ModNames
+from ..options import Cropsanity, SpecialOrderLocations, ExcludeGingerIsland, FestivalLocations, Fishsanity, Friendsanity, StardewValleyOptions
+from ..stardew_rule import False_, Or, True_, And, StardewRule
+from ..strings.animal_names import Animal
+from ..strings.animal_product_names import AnimalProduct
+from ..strings.ap_names.ap_weapon_names import APWeapon
+from ..strings.ap_names.buff_names import Buff
+from ..strings.ap_names.community_upgrade_names import CommunityUpgrade
+from ..strings.artisan_good_names import ArtisanGood
+from ..strings.building_names import Building
+from ..strings.craftable_names import Consumable, Furniture, Ring, Fishing, Lighting, WildSeeds
+from ..strings.crop_names import Fruit, Vegetable
+from ..strings.currency_names import Currency
+from ..strings.decoration_names import Decoration
+from ..strings.fertilizer_names import Fertilizer, SpeedGro, RetainingSoil
+from ..strings.festival_check_names import FestivalCheck
+from ..strings.fish_names import Fish, Trash, WaterItem, WaterChest
+from ..strings.flower_names import Flower
+from ..strings.food_names import Meal, Beverage
+from ..strings.forageable_names import Forageable
+from ..strings.fruit_tree_names import Sapling
+from ..strings.generic_names import Generic
+from ..strings.geode_names import Geode
+from ..strings.gift_names import Gift
+from ..strings.ingredient_names import Ingredient
+from ..strings.machine_names import Machine
+from ..strings.material_names import Material
+from ..strings.metal_names import Ore, MetalBar, Mineral, Fossil
+from ..strings.monster_drop_names import Loot
+from ..strings.monster_names import Monster
+from ..strings.region_names import Region
+from ..strings.season_names import Season
+from ..strings.seed_names import Seed, TreeSeed
+from ..strings.skill_names import Skill
+from ..strings.tool_names import Tool, ToolMaterial
+from ..strings.villager_names import NPC
+from ..strings.wallet_item_names import Wallet
+
+
+@dataclass(frozen=False, repr=False)
+class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogicMixin, TravelingMerchantLogicMixin, TimeLogicMixin,
+ SeasonLogicMixin, MoneyLogicMixin, ActionLogicMixin, ArcadeLogicMixin, ArtisanLogicMixin, GiftLogicMixin,
+ BuildingLogicMixin, ShippingLogicMixin, RelationshipLogicMixin, MuseumLogicMixin, WalletLogicMixin, AnimalLogicMixin,
+ CombatLogicMixin, MagicLogicMixin, MonsterLogicMixin, ToolLogicMixin, PetLogicMixin, CropLogicMixin,
+ SkillLogicMixin, FarmingLogicMixin, BundleLogicMixin, FishingLogicMixin, MineLogicMixin, CookingLogicMixin, AbilityLogicMixin,
+ SpecialOrderLogicMixin, QuestLogicMixin, CraftingLogicMixin, ModLogicMixin):
+ player: int
+ options: StardewValleyOptions
+ regions: Collection[str]
+
+ def __init__(self, player: int, options: StardewValleyOptions, regions: Collection[str]):
+ self.registry = LogicRegistry()
+ super().__init__(player, self.registry, options, regions, self)
+
+ self.registry.fish_rules.update({fish.name: self.fishing.can_catch_fish(fish) for fish in get_fish_for_mods(self.options.mods.value)})
+ self.registry.museum_rules.update({donation.item_name: self.museum.can_find_museum_item(donation) for donation in all_museum_items})
+
+ for recipe in all_cooking_recipes:
+ if recipe.mod_name and recipe.mod_name not in self.options.mods:
+ continue
+ can_cook_rule = self.cooking.can_cook(recipe)
+ if recipe.meal in self.registry.cooking_rules:
+ can_cook_rule = can_cook_rule | self.registry.cooking_rules[recipe.meal]
+ self.registry.cooking_rules[recipe.meal] = can_cook_rule
+
+ for recipe in all_crafting_recipes:
+ if recipe.mod_name and recipe.mod_name not in self.options.mods:
+ continue
+ can_craft_rule = self.crafting.can_craft(recipe)
+ if recipe.item in self.registry.crafting_rules:
+ can_craft_rule = can_craft_rule | self.registry.crafting_rules[recipe.item]
+ self.registry.crafting_rules[recipe.item] = can_craft_rule
+
+ self.registry.sapling_rules.update({
+ Sapling.apple: self.can_buy_sapling(Fruit.apple),
+ Sapling.apricot: self.can_buy_sapling(Fruit.apricot),
+ Sapling.cherry: self.can_buy_sapling(Fruit.cherry),
+ Sapling.orange: self.can_buy_sapling(Fruit.orange),
+ Sapling.peach: self.can_buy_sapling(Fruit.peach),
+ Sapling.pomegranate: self.can_buy_sapling(Fruit.pomegranate),
+ Sapling.banana: self.can_buy_sapling(Fruit.banana),
+ Sapling.mango: self.can_buy_sapling(Fruit.mango),
+ })
+
+ self.registry.tree_fruit_rules.update({
+ Fruit.apple: self.crop.can_plant_and_grow_item(Season.fall),
+ Fruit.apricot: self.crop.can_plant_and_grow_item(Season.spring),
+ Fruit.cherry: self.crop.can_plant_and_grow_item(Season.spring),
+ Fruit.orange: self.crop.can_plant_and_grow_item(Season.summer),
+ Fruit.peach: self.crop.can_plant_and_grow_item(Season.summer),
+ Fruit.pomegranate: self.crop.can_plant_and_grow_item(Season.fall),
+ Fruit.banana: self.crop.can_plant_and_grow_item(Season.summer),
+ Fruit.mango: self.crop.can_plant_and_grow_item(Season.summer),
+ })
+
+ for tree_fruit in self.registry.tree_fruit_rules:
+ existing_rules = self.registry.tree_fruit_rules[tree_fruit]
+ sapling = f"{tree_fruit} Sapling"
+ self.registry.tree_fruit_rules[tree_fruit] = existing_rules & self.has(sapling) & self.time.has_lived_months(1)
+
+ self.registry.seed_rules.update({seed.name: self.crop.can_buy_seed(seed) for seed in all_purchasable_seeds})
+ self.registry.crop_rules.update({crop.name: self.crop.can_grow(crop) for crop in all_crops})
+ self.registry.crop_rules.update({
+ Seed.coffee: (self.season.has(Season.spring) | self.season.has(Season.summer)) & self.crop.can_buy_seed(crops_by_name[Seed.coffee].seed),
+ Fruit.ancient_fruit: (self.received("Ancient Seeds") | self.received("Ancient Seeds Recipe")) &
+ self.region.can_reach(Region.greenhouse) & self.has(Machine.seed_maker),
+ })
+
+ # @formatter:off
+ self.registry.item_rules.update({
+ "Energy Tonic": self.money.can_spend_at(Region.hospital, 1000),
+ WaterChest.fishing_chest: self.fishing.can_fish_chests(),
+ WaterChest.treasure: self.fishing.can_fish_chests(),
+ Ring.hot_java_ring: self.region.can_reach(Region.volcano_floor_10),
+ "Galaxy Soul": self.money.can_trade_at(Region.qi_walnut_room, Currency.qi_gem, 40),
+ "JotPK Big Buff": self.arcade.has_jotpk_power_level(7),
+ "JotPK Max Buff": self.arcade.has_jotpk_power_level(9),
+ "JotPK Medium Buff": self.arcade.has_jotpk_power_level(4),
+ "JotPK Small Buff": self.arcade.has_jotpk_power_level(2),
+ "Junimo Kart Big Buff": self.arcade.has_junimo_kart_power_level(6),
+ "Junimo Kart Max Buff": self.arcade.has_junimo_kart_power_level(8),
+ "Junimo Kart Medium Buff": self.arcade.has_junimo_kart_power_level(4),
+ "Junimo Kart Small Buff": self.arcade.has_junimo_kart_power_level(2),
+ "Magic Rock Candy": self.region.can_reach(Region.desert) & self.has("Prismatic Shard"),
+ "Muscle Remedy": self.money.can_spend_at(Region.hospital, 1000),
+ # self.has(Ingredient.vinegar)),
+ # self.received("Deluxe Fertilizer Recipe") & self.has(MetalBar.iridium) & self.has(SVItem.sap),
+ # | (self.ability.can_cook() & self.relationship.has_hearts(NPC.emily, 3) & self.has(Forageable.leek) & self.has(Forageable.dandelion) &
+ # | (self.ability.can_cook() & self.relationship.has_hearts(NPC.jodi, 7) & self.has(AnimalProduct.cow_milk) & self.has(Ingredient.sugar)),
+ Animal.chicken: self.animal.can_buy_animal(Animal.chicken),
+ Animal.cow: self.animal.can_buy_animal(Animal.cow),
+ Animal.dinosaur: self.building.has_building(Building.big_coop) & self.has(AnimalProduct.dinosaur_egg),
+ Animal.duck: self.animal.can_buy_animal(Animal.duck),
+ Animal.goat: self.animal.can_buy_animal(Animal.goat),
+ Animal.ostrich: self.building.has_building(Building.barn) & self.has(AnimalProduct.ostrich_egg) & self.has(Machine.ostrich_incubator),
+ Animal.pig: self.animal.can_buy_animal(Animal.pig),
+ Animal.rabbit: self.animal.can_buy_animal(Animal.rabbit),
+ Animal.sheep: self.animal.can_buy_animal(Animal.sheep),
+ AnimalProduct.any_egg: self.has_any(AnimalProduct.chicken_egg, AnimalProduct.duck_egg),
+ AnimalProduct.brown_egg: self.animal.has_animal(Animal.chicken),
+ AnimalProduct.chicken_egg: self.has_any(AnimalProduct.egg, AnimalProduct.brown_egg, AnimalProduct.large_egg, AnimalProduct.large_brown_egg),
+ AnimalProduct.cow_milk: self.has_any(AnimalProduct.milk, AnimalProduct.large_milk),
+ AnimalProduct.duck_egg: self.animal.has_animal(Animal.duck),
+ AnimalProduct.duck_feather: self.animal.has_happy_animal(Animal.duck),
+ AnimalProduct.egg: self.animal.has_animal(Animal.chicken),
+ AnimalProduct.goat_milk: self.has(Animal.goat),
+ AnimalProduct.golden_egg: self.received(AnimalProduct.golden_egg) & (self.money.can_spend_at(Region.ranch, 100000) | self.money.can_trade_at(Region.qi_walnut_room, Currency.qi_gem, 100)),
+ AnimalProduct.large_brown_egg: self.animal.has_happy_animal(Animal.chicken),
+ AnimalProduct.large_egg: self.animal.has_happy_animal(Animal.chicken),
+ AnimalProduct.large_goat_milk: self.animal.has_happy_animal(Animal.goat),
+ AnimalProduct.large_milk: self.animal.has_happy_animal(Animal.cow),
+ AnimalProduct.milk: self.animal.has_animal(Animal.cow),
+ AnimalProduct.ostrich_egg: self.tool.can_forage(Generic.any, Region.island_north, True),
+ AnimalProduct.rabbit_foot: self.animal.has_happy_animal(Animal.rabbit),
+ AnimalProduct.roe: self.skill.can_fish() & self.building.has_building(Building.fish_pond),
+ AnimalProduct.squid_ink: self.mine.can_mine_in_the_mines_floor_81_120() | (self.building.has_building(Building.fish_pond) & self.has(Fish.squid)),
+ AnimalProduct.sturgeon_roe: self.has(Fish.sturgeon) & self.building.has_building(Building.fish_pond),
+ AnimalProduct.truffle: self.animal.has_animal(Animal.pig) & self.season.has_any_not_winter(),
+ AnimalProduct.void_egg: self.money.can_spend_at(Region.sewer, 5000) | (self.building.has_building(Building.fish_pond) & self.has(Fish.void_salmon)),
+ AnimalProduct.wool: self.animal.has_animal(Animal.rabbit) | self.animal.has_animal(Animal.sheep),
+ AnimalProduct.slime_egg_green: self.has(Machine.slime_egg_press) & self.has(Loot.slime),
+ AnimalProduct.slime_egg_blue: self.has(Machine.slime_egg_press) & self.has(Loot.slime) & self.time.has_lived_months(3),
+ AnimalProduct.slime_egg_red: self.has(Machine.slime_egg_press) & self.has(Loot.slime) & self.time.has_lived_months(6),
+ AnimalProduct.slime_egg_purple: self.has(Machine.slime_egg_press) & self.has(Loot.slime) & self.time.has_lived_months(9),
+ AnimalProduct.slime_egg_tiger: self.has(Fish.lionfish) & self.building.has_building(Building.fish_pond),
+ ArtisanGood.aged_roe: self.artisan.can_preserves_jar(AnimalProduct.roe),
+ ArtisanGood.battery_pack: (self.has(Machine.lightning_rod) & self.season.has_any_not_winter()) | self.has(Machine.solar_panel),
+ ArtisanGood.caviar: self.artisan.can_preserves_jar(AnimalProduct.sturgeon_roe),
+ ArtisanGood.cheese: (self.has(AnimalProduct.cow_milk) & self.has(Machine.cheese_press)) | (self.region.can_reach(Region.desert) & self.has(Mineral.emerald)),
+ ArtisanGood.cloth: (self.has(AnimalProduct.wool) & self.has(Machine.loom)) | (self.region.can_reach(Region.desert) & self.has(Mineral.aquamarine)),
+ ArtisanGood.dinosaur_mayonnaise: self.artisan.can_mayonnaise(AnimalProduct.dinosaur_egg),
+ ArtisanGood.duck_mayonnaise: self.artisan.can_mayonnaise(AnimalProduct.duck_egg),
+ ArtisanGood.goat_cheese: self.has(AnimalProduct.goat_milk) & self.has(Machine.cheese_press),
+ ArtisanGood.green_tea: self.artisan.can_keg(Vegetable.tea_leaves),
+ ArtisanGood.honey: self.money.can_spend_at(Region.oasis, 200) | (self.has(Machine.bee_house) & self.season.has_any_not_winter()),
+ ArtisanGood.jelly: self.artisan.has_jelly(),
+ ArtisanGood.juice: self.artisan.has_juice(),
+ ArtisanGood.maple_syrup: self.has(Machine.tapper),
+ ArtisanGood.mayonnaise: self.artisan.can_mayonnaise(AnimalProduct.chicken_egg),
+ ArtisanGood.mead: self.artisan.can_keg(ArtisanGood.honey),
+ ArtisanGood.oak_resin: self.has(Machine.tapper),
+ ArtisanGood.pale_ale: self.artisan.can_keg(Vegetable.hops),
+ ArtisanGood.pickles: self.artisan.has_pickle(),
+ ArtisanGood.pine_tar: self.has(Machine.tapper),
+ ArtisanGood.truffle_oil: self.has(AnimalProduct.truffle) & self.has(Machine.oil_maker),
+ ArtisanGood.void_mayonnaise: (self.skill.can_fish(Region.witch_swamp)) | (self.artisan.can_mayonnaise(AnimalProduct.void_egg)),
+ ArtisanGood.wine: self.artisan.has_wine(),
+ Beverage.beer: self.artisan.can_keg(Vegetable.wheat) | self.money.can_spend_at(Region.saloon, 400),
+ Beverage.coffee: self.artisan.can_keg(Seed.coffee) | self.has(Machine.coffee_maker) | (self.money.can_spend_at(Region.saloon, 300)) | self.has("Hot Java Ring"),
+ Beverage.pina_colada: self.money.can_spend_at(Region.island_resort, 600),
+ Beverage.triple_shot_espresso: self.has("Hot Java Ring"),
+ Decoration.rotten_plant: self.has(Lighting.jack_o_lantern) & self.season.has(Season.winter),
+ Fertilizer.basic: self.money.can_spend_at(Region.pierre_store, 100),
+ Fertilizer.quality: self.time.has_year_two & self.money.can_spend_at(Region.pierre_store, 150),
+ Fertilizer.tree: self.skill.has_level(Skill.foraging, 7) & self.has(Material.fiber) & self.has(Material.stone),
+ Fish.any: Or(*(self.fishing.can_catch_fish(fish) for fish in get_fish_for_mods(self.options.mods.value))),
+ Fish.crab: self.skill.can_crab_pot_at(Region.beach),
+ Fish.crayfish: self.skill.can_crab_pot_at(Region.town),
+ Fish.lobster: self.skill.can_crab_pot_at(Region.beach),
+ Fish.mussel: self.tool.can_forage(Generic.any, Region.beach) or self.has(Fish.mussel_node),
+ Fish.mussel_node: self.region.can_reach(Region.island_west),
+ Fish.oyster: self.tool.can_forage(Generic.any, Region.beach),
+ Fish.periwinkle: self.skill.can_crab_pot_at(Region.town),
+ Fish.shrimp: self.skill.can_crab_pot_at(Region.beach),
+ Fish.snail: self.skill.can_crab_pot_at(Region.town),
+ Fishing.curiosity_lure: self.monster.can_kill(self.monster.all_monsters_by_name[Monster.mummy]),
+ Fishing.lead_bobber: self.skill.has_level(Skill.fishing, 6) & self.money.can_spend_at(Region.fish_shop, 200),
+ Forageable.blackberry: self.tool.can_forage(Season.fall) | self.has_fruit_bats(),
+ Forageable.cactus_fruit: self.tool.can_forage(Generic.any, Region.desert),
+ Forageable.cave_carrot: self.tool.can_forage(Generic.any, Region.mines_floor_10, True),
+ Forageable.chanterelle: self.tool.can_forage(Season.fall, Region.secret_woods) | self.has_mushroom_cave(),
+ Forageable.coconut: self.tool.can_forage(Generic.any, Region.desert),
+ Forageable.common_mushroom: self.tool.can_forage(Season.fall) | (self.tool.can_forage(Season.spring, Region.secret_woods)) | self.has_mushroom_cave(),
+ Forageable.crocus: self.tool.can_forage(Season.winter),
+ Forageable.crystal_fruit: self.tool.can_forage(Season.winter),
+ Forageable.daffodil: self.tool.can_forage(Season.spring),
+ Forageable.dandelion: self.tool.can_forage(Season.spring),
+ Forageable.dragon_tooth: self.tool.can_forage(Generic.any, Region.volcano_floor_10),
+ Forageable.fiddlehead_fern: self.tool.can_forage(Season.summer, Region.secret_woods),
+ Forageable.ginger: self.tool.can_forage(Generic.any, Region.island_west, True),
+ Forageable.hay: self.building.has_building(Building.silo) & self.tool.has_tool(Tool.scythe),
+ Forageable.hazelnut: self.tool.can_forage(Season.fall),
+ Forageable.holly: self.tool.can_forage(Season.winter),
+ Forageable.journal_scrap: self.region.can_reach_all((Region.island_west, Region.island_north, Region.island_south, Region.volcano_floor_10)) & self.quest.has_magnifying_glass() & (self.ability.can_chop_trees() | self.mine.can_mine_in_the_mines_floor_1_40()),
+ Forageable.leek: self.tool.can_forage(Season.spring),
+ Forageable.magma_cap: self.tool.can_forage(Generic.any, Region.volcano_floor_5),
+ Forageable.morel: self.tool.can_forage(Season.spring, Region.secret_woods) | self.has_mushroom_cave(),
+ Forageable.purple_mushroom: self.tool.can_forage(Generic.any, Region.mines_floor_95) | self.tool.can_forage(Generic.any, Region.skull_cavern_25) | self.has_mushroom_cave(),
+ Forageable.rainbow_shell: self.tool.can_forage(Season.summer, Region.beach),
+ Forageable.red_mushroom: self.tool.can_forage(Season.summer, Region.secret_woods) | self.tool.can_forage(Season.fall, Region.secret_woods) | self.has_mushroom_cave(),
+ Forageable.salmonberry: self.tool.can_forage(Season.spring) | self.has_fruit_bats(),
+ Forageable.secret_note: self.quest.has_magnifying_glass() & (self.ability.can_chop_trees() | self.mine.can_mine_in_the_mines_floor_1_40()),
+ Forageable.snow_yam: self.tool.can_forage(Season.winter, Region.beach, True),
+ Forageable.spice_berry: self.tool.can_forage(Season.summer) | self.has_fruit_bats(),
+ Forageable.spring_onion: self.tool.can_forage(Season.spring),
+ Forageable.sweet_pea: self.tool.can_forage(Season.summer),
+ Forageable.wild_horseradish: self.tool.can_forage(Season.spring),
+ Forageable.wild_plum: self.tool.can_forage(Season.fall) | self.has_fruit_bats(),
+ Forageable.winter_root: self.tool.can_forage(Season.winter, Region.forest, True),
+ Fossil.bone_fragment: (self.region.can_reach(Region.dig_site) & self.tool.has_tool(Tool.pickaxe)) | self.monster.can_kill(Monster.skeleton),
+ Fossil.fossilized_leg: self.region.can_reach(Region.dig_site) & self.tool.has_tool(Tool.pickaxe),
+ Fossil.fossilized_ribs: self.region.can_reach(Region.island_south) & self.tool.has_tool(Tool.hoe),
+ Fossil.fossilized_skull: self.action.can_open_geode(Geode.golden_coconut),
+ Fossil.fossilized_spine: self.skill.can_fish(Region.dig_site),
+ Fossil.fossilized_tail: self.action.can_pan_at(Region.dig_site),
+ Fossil.mummified_bat: self.region.can_reach(Region.volcano_floor_10),
+ Fossil.mummified_frog: self.region.can_reach(Region.island_east) & self.tool.has_tool(Tool.scythe),
+ Fossil.snake_skull: self.region.can_reach(Region.dig_site) & self.tool.has_tool(Tool.hoe),
+ Fossil.snake_vertebrae: self.region.can_reach(Region.island_west) & self.tool.has_tool(Tool.hoe),
+ Geode.artifact_trove: self.has(Geode.omni) & self.region.can_reach(Region.desert),
+ Geode.frozen: self.mine.can_mine_in_the_mines_floor_41_80(),
+ Geode.geode: self.mine.can_mine_in_the_mines_floor_1_40(),
+ Geode.golden_coconut: self.region.can_reach(Region.island_north),
+ Geode.magma: self.mine.can_mine_in_the_mines_floor_81_120() | (self.has(Fish.lava_eel) & self.building.has_building(Building.fish_pond)),
+ Geode.omni: self.mine.can_mine_in_the_mines_floor_41_80() | self.region.can_reach(Region.desert) | self.action.can_pan() | self.received(Wallet.rusty_key) | (self.has(Fish.octopus) & self.building.has_building(Building.fish_pond)) | self.region.can_reach(Region.volcano_floor_10),
+ Gift.bouquet: self.relationship.has_hearts(Generic.bachelor, 8) & self.money.can_spend_at(Region.pierre_store, 100),
+ Gift.golden_pumpkin: self.season.has(Season.fall) | self.action.can_open_geode(Geode.artifact_trove),
+ Gift.mermaid_pendant: self.region.can_reach(Region.tide_pools) & self.relationship.has_hearts(Generic.bachelor, 10) & self.building.has_house(1) & self.has(Consumable.rain_totem),
+ Gift.movie_ticket: self.money.can_spend_at(Region.movie_ticket_stand, 1000),
+ Gift.pearl: (self.has(Fish.blobfish) & self.building.has_building(Building.fish_pond)) | self.action.can_open_geode(Geode.artifact_trove),
+ Gift.tea_set: self.season.has(Season.winter) & self.time.has_lived_max_months,
+ Gift.void_ghost_pendant: self.money.can_trade_at(Region.desert, Loot.void_essence, 200) & self.relationship.has_hearts(NPC.krobus, 10),
+ Gift.wilted_bouquet: self.has(Machine.furnace) & self.has(Gift.bouquet) & self.has(Material.coal),
+ Ingredient.oil: self.money.can_spend_at(Region.pierre_store, 200) | (self.has(Machine.oil_maker) & (self.has(Vegetable.corn) | self.has(Flower.sunflower) | self.has(Seed.sunflower))),
+ Ingredient.qi_seasoning: self.money.can_trade_at(Region.qi_walnut_room, Currency.qi_gem, 10),
+ Ingredient.rice: self.money.can_spend_at(Region.pierre_store, 200) | (self.building.has_building(Building.mill) & self.has(Vegetable.unmilled_rice)),
+ Ingredient.sugar: self.money.can_spend_at(Region.pierre_store, 100) | (self.building.has_building(Building.mill) & self.has(Vegetable.beet)),
+ Ingredient.vinegar: self.money.can_spend_at(Region.pierre_store, 200),
+ Ingredient.wheat_flour: self.money.can_spend_at(Region.pierre_store, 100) | (self.building.has_building(Building.mill) & self.has(Vegetable.wheat)),
+ Loot.bat_wing: self.mine.can_mine_in_the_mines_floor_41_80() | self.mine.can_mine_in_the_skull_cavern(),
+ Loot.bug_meat: self.mine.can_mine_in_the_mines_floor_1_40(),
+ Loot.slime: self.mine.can_mine_in_the_mines_floor_1_40(),
+ Loot.solar_essence: self.mine.can_mine_in_the_mines_floor_41_80() | self.mine.can_mine_in_the_skull_cavern(),
+ Loot.void_essence: self.mine.can_mine_in_the_mines_floor_81_120() | self.mine.can_mine_in_the_skull_cavern(),
+ Machine.bee_house: self.skill.has_farming_level(3) & self.has(MetalBar.iron) & self.has(ArtisanGood.maple_syrup) & self.has(Material.coal) & self.has(Material.wood),
+ Machine.cask: self.building.has_house(3) & self.region.can_reach(Region.cellar) & self.has(Material.wood) & self.has(Material.hardwood),
+ Machine.cheese_press: self.skill.has_farming_level(6) & self.has(Material.wood) & self.has(Material.stone) & self.has(Material.hardwood) & self.has(MetalBar.copper),
+ Machine.coffee_maker: self.received(Machine.coffee_maker),
+ Machine.crab_pot: self.skill.has_level(Skill.fishing, 3) & (self.money.can_spend_at(Region.fish_shop, 1500) | (self.has(MetalBar.iron) & self.has(Material.wood))),
+ Machine.furnace: self.has(Material.stone) & self.has(Ore.copper),
+ Machine.keg: self.skill.has_farming_level(8) & self.has(Material.wood) & self.has(MetalBar.iron) & self.has(MetalBar.copper) & self.has(ArtisanGood.oak_resin),
+ Machine.lightning_rod: self.skill.has_level(Skill.foraging, 6) & self.has(MetalBar.iron) & self.has(MetalBar.quartz) & self.has(Loot.bat_wing),
+ Machine.loom: self.skill.has_farming_level(7) & self.has(Material.wood) & self.has(Material.fiber) & self.has(ArtisanGood.pine_tar),
+ Machine.mayonnaise_machine: self.skill.has_farming_level(2) & self.has(Material.wood) & self.has(Material.stone) & self.has("Earth Crystal") & self.has(MetalBar.copper),
+ Machine.ostrich_incubator: self.received("Ostrich Incubator Recipe") & self.has(Fossil.bone_fragment) & self.has(Material.hardwood) & self.has(Material.cinder_shard),
+ Machine.preserves_jar: self.skill.has_farming_level(4) & self.has(Material.wood) & self.has(Material.stone) & self.has(Material.coal),
+ Machine.recycling_machine: self.skill.has_level(Skill.fishing, 4) & self.has(Material.wood) & self.has(Material.stone) & self.has(MetalBar.iron),
+ Machine.seed_maker: self.skill.has_farming_level(9) & self.has(Material.wood) & self.has(MetalBar.gold) & self.has(Material.coal),
+ Machine.solar_panel: self.received("Solar Panel Recipe") & self.has(MetalBar.quartz) & self.has(MetalBar.iron) & self.has(MetalBar.gold),
+ Machine.tapper: self.skill.has_level(Skill.foraging, 3) & self.has(Material.wood) & self.has(MetalBar.copper),
+ Machine.worm_bin: self.skill.has_level(Skill.fishing, 8) & self.has(Material.hardwood) & self.has(MetalBar.gold) & self.has(MetalBar.iron) & self.has(Material.fiber),
+ Machine.enricher: self.money.can_trade_at(Region.qi_walnut_room, Currency.qi_gem, 20),
+ Machine.pressure_nozzle: self.money.can_trade_at(Region.qi_walnut_room, Currency.qi_gem, 20),
+ Material.cinder_shard: self.region.can_reach(Region.volcano_floor_5),
+ Material.clay: self.region.can_reach_any((Region.farm, Region.beach, Region.quarry)) & self.tool.has_tool(Tool.hoe),
+ Material.coal: self.mine.can_mine_in_the_mines_floor_41_80() | self.action.can_pan(),
+ Material.fiber: True_(),
+ Material.hardwood: self.tool.has_tool(Tool.axe, ToolMaterial.copper) & (self.region.can_reach(Region.secret_woods) | self.region.can_reach(Region.island_west)),
+ Material.sap: self.ability.can_chop_trees(),
+ Material.stone: self.tool.has_tool(Tool.pickaxe),
+ Material.wood: self.tool.has_tool(Tool.axe),
+ Meal.bread: self.money.can_spend_at(Region.saloon, 120),
+ Meal.ice_cream: (self.season.has(Season.summer) & self.money.can_spend_at(Region.town, 250)) | self.money.can_spend_at(Region.oasis, 240),
+ Meal.pizza: self.money.can_spend_at(Region.saloon, 600),
+ Meal.salad: self.money.can_spend_at(Region.saloon, 220),
+ Meal.spaghetti: self.money.can_spend_at(Region.saloon, 240),
+ Meal.strange_bun: self.relationship.has_hearts(NPC.shane, 7) & self.has(Ingredient.wheat_flour) & self.has(Fish.periwinkle) & self.has(ArtisanGood.void_mayonnaise),
+ MetalBar.copper: self.can_smelt(Ore.copper),
+ MetalBar.gold: self.can_smelt(Ore.gold),
+ MetalBar.iridium: self.can_smelt(Ore.iridium),
+ MetalBar.iron: self.can_smelt(Ore.iron),
+ MetalBar.quartz: self.can_smelt(Mineral.quartz) | self.can_smelt("Fire Quartz") | (self.has(Machine.recycling_machine) & (self.has(Trash.broken_cd) | self.has(Trash.broken_glasses))),
+ MetalBar.radioactive: self.can_smelt(Ore.radioactive),
+ Ore.copper: self.mine.can_mine_in_the_mines_floor_1_40() | self.mine.can_mine_in_the_skull_cavern() | self.action.can_pan(),
+ Ore.gold: self.mine.can_mine_in_the_mines_floor_81_120() | self.mine.can_mine_in_the_skull_cavern() | self.action.can_pan(),
+ Ore.iridium: self.mine.can_mine_in_the_skull_cavern() | self.can_fish_pond(Fish.super_cucumber),
+ Ore.iron: self.mine.can_mine_in_the_mines_floor_41_80() | self.mine.can_mine_in_the_skull_cavern() | self.action.can_pan(),
+ Ore.radioactive: self.ability.can_mine_perfectly() & self.region.can_reach(Region.qi_walnut_room),
+ RetainingSoil.basic: self.money.can_spend_at(Region.pierre_store, 100),
+ RetainingSoil.quality: self.time.has_year_two & self.money.can_spend_at(Region.pierre_store, 150),
+ Sapling.tea: self.relationship.has_hearts(NPC.caroline, 2) & self.has(Material.fiber) & self.has(Material.wood),
+ Seed.mixed: self.tool.has_tool(Tool.scythe) & self.region.can_reach_all((Region.farm, Region.forest, Region.town)),
+ SpeedGro.basic: self.money.can_spend_at(Region.pierre_store, 100),
+ SpeedGro.deluxe: self.time.has_year_two & self.money.can_spend_at(Region.pierre_store, 150),
+ Trash.broken_cd: self.skill.can_crab_pot,
+ Trash.broken_glasses: self.skill.can_crab_pot,
+ Trash.driftwood: self.skill.can_crab_pot,
+ Trash.joja_cola: self.money.can_spend_at(Region.saloon, 75),
+ Trash.soggy_newspaper: self.skill.can_crab_pot,
+ Trash.trash: self.skill.can_crab_pot,
+ TreeSeed.acorn: self.skill.has_level(Skill.foraging, 1) & self.ability.can_chop_trees(),
+ TreeSeed.mahogany: self.region.can_reach(Region.secret_woods) & self.tool.has_tool(Tool.axe, ToolMaterial.iron) & self.skill.has_level(Skill.foraging, 1),
+ TreeSeed.maple: self.skill.has_level(Skill.foraging, 1) & self.ability.can_chop_trees(),
+ TreeSeed.mushroom: self.money.can_trade_at(Region.qi_walnut_room, Currency.qi_gem, 5),
+ TreeSeed.pine: self.skill.has_level(Skill.foraging, 1) & self.ability.can_chop_trees(),
+ Vegetable.tea_leaves: self.has(Sapling.tea) & self.time.has_lived_months(2) & self.season.has_any_not_winter(),
+ Fish.clam: self.tool.can_forage(Generic.any, Region.beach),
+ Fish.cockle: self.tool.can_forage(Generic.any, Region.beach),
+ WaterItem.coral: self.tool.can_forage(Generic.any, Region.tide_pools) | self.tool.can_forage(Season.summer, Region.beach),
+ WaterItem.green_algae: self.fishing.can_fish_in_freshwater(),
+ WaterItem.nautilus_shell: self.tool.can_forage(Season.winter, Region.beach),
+ WaterItem.sea_urchin: self.tool.can_forage(Generic.any, Region.tide_pools),
+ WaterItem.seaweed: self.skill.can_fish(Region.tide_pools),
+ WaterItem.white_algae: self.skill.can_fish(Region.mines_floor_20),
+ WildSeeds.grass_starter: self.money.can_spend_at(Region.pierre_store, 100),
+ })
+ # @formatter:on
+ self.registry.item_rules.update(self.registry.fish_rules)
+ self.registry.item_rules.update(self.registry.museum_rules)
+ self.registry.item_rules.update(self.registry.sapling_rules)
+ self.registry.item_rules.update(self.registry.tree_fruit_rules)
+ self.registry.item_rules.update(self.registry.seed_rules)
+ self.registry.item_rules.update(self.registry.crop_rules)
+
+ self.registry.item_rules.update(self.mod.item.get_modded_item_rules())
+ self.mod.item.modify_vanilla_item_rules_with_mod_additions(self.registry.item_rules) # New regions and content means new ways to obtain old items
+
+ # For some recipes, the cooked item can be obtained directly, so we either cook it or get it
+ for recipe in self.registry.cooking_rules:
+ cooking_rule = self.registry.cooking_rules[recipe]
+ obtention_rule = self.registry.item_rules[recipe] if recipe in self.registry.item_rules else False_()
+ self.registry.item_rules[recipe] = obtention_rule | cooking_rule
+
+ # For some recipes, the crafted item can be obtained directly, so we either craft it or get it
+ for recipe in self.registry.crafting_rules:
+ crafting_rule = self.registry.crafting_rules[recipe]
+ obtention_rule = self.registry.item_rules[recipe] if recipe in self.registry.item_rules else False_()
+ self.registry.item_rules[recipe] = obtention_rule | crafting_rule
+
+ self.building.initialize_rules()
+ self.building.update_rules(self.mod.building.get_modded_building_rules())
+
+ self.quest.initialize_rules()
+ self.quest.update_rules(self.mod.quest.get_modded_quest_rules())
+
+ self.registry.festival_rules.update({
+ FestivalCheck.egg_hunt: self.can_win_egg_hunt(),
+ FestivalCheck.strawberry_seeds: self.money.can_spend(1000),
+ FestivalCheck.dance: self.relationship.has_hearts(Generic.bachelor, 4),
+ FestivalCheck.tub_o_flowers: self.money.can_spend(2000),
+ FestivalCheck.rarecrow_5: self.money.can_spend(2500),
+ FestivalCheck.luau_soup: self.can_succeed_luau_soup(),
+ FestivalCheck.moonlight_jellies: True_(),
+ FestivalCheck.moonlight_jellies_banner: self.money.can_spend(800),
+ FestivalCheck.starport_decal: self.money.can_spend(1000),
+ FestivalCheck.smashing_stone: True_(),
+ FestivalCheck.grange_display: self.can_succeed_grange_display(),
+ FestivalCheck.rarecrow_1: True_(), # only cost star tokens
+ FestivalCheck.fair_stardrop: True_(), # only cost star tokens
+ FestivalCheck.spirit_eve_maze: True_(),
+ FestivalCheck.jack_o_lantern: self.money.can_spend(2000),
+ FestivalCheck.rarecrow_2: self.money.can_spend(5000),
+ FestivalCheck.fishing_competition: self.can_win_fishing_competition(),
+ FestivalCheck.rarecrow_4: self.money.can_spend(5000),
+ FestivalCheck.mermaid_pearl: self.has(Forageable.secret_note),
+ FestivalCheck.cone_hat: self.money.can_spend(2500),
+ FestivalCheck.iridium_fireplace: self.money.can_spend(15000),
+ FestivalCheck.rarecrow_7: self.money.can_spend(5000) & self.museum.can_donate_museum_artifacts(20),
+ FestivalCheck.rarecrow_8: self.money.can_spend(5000) & self.museum.can_donate_museum_items(40),
+ FestivalCheck.lupini_red_eagle: self.money.can_spend(1200),
+ FestivalCheck.lupini_portrait_mermaid: self.money.can_spend(1200),
+ FestivalCheck.lupini_solar_kingdom: self.money.can_spend(1200),
+ FestivalCheck.lupini_clouds: self.time.has_year_two & self.money.can_spend(1200),
+ FestivalCheck.lupini_1000_years: self.time.has_year_two & self.money.can_spend(1200),
+ FestivalCheck.lupini_three_trees: self.time.has_year_two & self.money.can_spend(1200),
+ FestivalCheck.lupini_the_serpent: self.time.has_year_three & self.money.can_spend(1200),
+ FestivalCheck.lupini_tropical_fish: self.time.has_year_three & self.money.can_spend(1200),
+ FestivalCheck.lupini_land_of_clay: self.time.has_year_three & self.money.can_spend(1200),
+ FestivalCheck.secret_santa: self.gifts.has_any_universal_love,
+ FestivalCheck.legend_of_the_winter_star: True_(),
+ FestivalCheck.rarecrow_3: True_(),
+ FestivalCheck.all_rarecrows: self.region.can_reach(Region.farm) & self.has_all_rarecrows(),
+ })
+
+ self.special_order.initialize_rules()
+ self.special_order.update_rules(self.mod.special_order.get_modded_special_orders_rules())
+
+ def can_buy_sapling(self, fruit: str) -> StardewRule:
+ sapling_prices = {Fruit.apple: 4000, Fruit.apricot: 2000, Fruit.cherry: 3400, Fruit.orange: 4000,
+ Fruit.peach: 6000,
+ Fruit.pomegranate: 6000, Fruit.banana: 0, Fruit.mango: 0}
+ received_sapling = self.received(f"{fruit} Sapling")
+ if self.options.cropsanity == Cropsanity.option_disabled:
+ allowed_buy_sapling = True_()
+ else:
+ allowed_buy_sapling = received_sapling
+ can_buy_sapling = self.money.can_spend_at(Region.pierre_store, sapling_prices[fruit])
+ if fruit == Fruit.banana:
+ can_buy_sapling = self.has_island_trader() & self.has(Forageable.dragon_tooth)
+ elif fruit == Fruit.mango:
+ can_buy_sapling = self.has_island_trader() & self.has(Fish.mussel_node)
+
+ return allowed_buy_sapling & can_buy_sapling
+
+ def can_smelt(self, item: str) -> StardewRule:
+ return self.has(Machine.furnace) & self.has(item)
+
+ def can_complete_field_office(self) -> StardewRule:
+ field_office = self.region.can_reach(Region.field_office)
+ professor_snail = self.received("Open Professor Snail Cave")
+ tools = self.tool.has_tool(Tool.pickaxe) & self.tool.has_tool(Tool.hoe) & self.tool.has_tool(Tool.scythe)
+ leg_and_snake_skull = self.has_all(Fossil.fossilized_leg, Fossil.snake_skull)
+ ribs_and_spine = self.has_all(Fossil.fossilized_ribs, Fossil.fossilized_spine)
+ skull = self.has(Fossil.fossilized_skull)
+ tail = self.has(Fossil.fossilized_tail)
+ frog = self.has(Fossil.mummified_frog)
+ bat = self.has(Fossil.mummified_bat)
+ snake_vertebrae = self.has(Fossil.snake_vertebrae)
+ return field_office & professor_snail & tools & leg_and_snake_skull & ribs_and_spine & skull & tail & frog & bat & snake_vertebrae
+
+ def can_finish_grandpa_evaluation(self) -> StardewRule:
+ # https://stardewvalleywiki.com/Grandpa
+ rules_worth_a_point = [
+ self.money.can_have_earned_total(50000), # 50 000g
+ self.money.can_have_earned_total(100000), # 100 000g
+ self.money.can_have_earned_total(200000), # 200 000g
+ self.money.can_have_earned_total(300000), # 300 000g
+ self.money.can_have_earned_total(500000), # 500 000g
+ self.money.can_have_earned_total(1000000), # 1 000 000g first point
+ self.money.can_have_earned_total(1000000), # 1 000 000g second point
+ self.skill.has_total_level(30), # Total Skills: 30
+ self.skill.has_total_level(50), # Total Skills: 50
+ self.museum.can_complete_museum(), # Completing the museum for a point
+ # Catching every fish not expected
+ # Shipping every item not expected
+ self.relationship.can_get_married() & self.building.has_house(2),
+ self.relationship.has_hearts("5", 8), # 5 Friends
+ self.relationship.has_hearts("10", 8), # 10 friends
+ self.pet.has_hearts(5), # Max Pet
+ self.bundle.can_complete_community_center, # Community Center Completion
+ self.bundle.can_complete_community_center, # CC Ceremony first point
+ self.bundle.can_complete_community_center, # CC Ceremony second point
+ self.received(Wallet.skull_key), # Skull Key obtained
+ self.wallet.has_rusty_key(), # Rusty key obtained
+ ]
+ return self.count(12, *rules_worth_a_point)
+
+ def can_win_egg_hunt(self) -> StardewRule:
+ number_of_movement_buffs = self.options.movement_buff_number
+ if self.options.festival_locations == FestivalLocations.option_hard or number_of_movement_buffs < 2:
+ return True_()
+ return self.received(Buff.movement, number_of_movement_buffs // 2)
+
+ def can_succeed_luau_soup(self) -> StardewRule:
+ if self.options.festival_locations != FestivalLocations.option_hard:
+ return True_()
+ eligible_fish = [Fish.blobfish, Fish.crimsonfish, "Ice Pip", Fish.lava_eel, Fish.legend, Fish.angler, Fish.catfish, Fish.glacierfish, Fish.mutant_carp,
+ Fish.spookfish, Fish.stingray, Fish.sturgeon, "Super Cucumber"]
+ fish_rule = self.has_any(*eligible_fish)
+ eligible_kegables = [Fruit.ancient_fruit, Fruit.apple, Fruit.banana, Forageable.coconut, Forageable.crystal_fruit, Fruit.mango, Fruit.melon,
+ Fruit.orange, Fruit.peach, Fruit.pineapple, Fruit.pomegranate, Fruit.rhubarb, Fruit.starfruit, Fruit.strawberry,
+ Forageable.cactus_fruit, Fruit.cherry, Fruit.cranberries, Fruit.grape, Forageable.spice_berry, Forageable.wild_plum,
+ Vegetable.hops, Vegetable.wheat]
+ keg_rules = [self.artisan.can_keg(kegable) for kegable in eligible_kegables]
+ aged_rule = self.has(Machine.cask) & Or(*keg_rules)
+ # There are a few other valid items, but I don't feel like coding them all
+ return fish_rule | aged_rule
+
+ def can_succeed_grange_display(self) -> StardewRule:
+ if self.options.festival_locations != FestivalLocations.option_hard:
+ return True_()
+
+ animal_rule = self.animal.has_animal(Generic.any)
+ artisan_rule = self.artisan.can_keg(Generic.any) | self.artisan.can_preserves_jar(Generic.any)
+ cooking_rule = self.money.can_spend_at(Region.saloon, 220) # Salads at the bar are good enough
+ fish_rule = self.skill.can_fish(difficulty=50)
+ forage_rule = self.region.can_reach_any((Region.forest, Region.backwoods)) # Hazelnut always available since the grange display is in fall
+ mineral_rule = self.action.can_open_geode(Generic.any) # More than half the minerals are good enough
+ good_fruits = [Fruit.apple, Fruit.banana, Forageable.coconut, Forageable.crystal_fruit, Fruit.mango, Fruit.orange, Fruit.peach, Fruit.pomegranate,
+ Fruit.strawberry, Fruit.melon, Fruit.rhubarb, Fruit.pineapple, Fruit.ancient_fruit, Fruit.starfruit, ]
+ fruit_rule = self.has_any(*good_fruits)
+ good_vegetables = [Vegetable.amaranth, Vegetable.artichoke, Vegetable.beet, Vegetable.cauliflower, Forageable.fiddlehead_fern, Vegetable.kale,
+ Vegetable.radish, Vegetable.taro_root, Vegetable.yam, Vegetable.red_cabbage, Vegetable.pumpkin]
+ vegetable_rule = self.has_any(*good_vegetables)
+
+ return animal_rule & artisan_rule & cooking_rule & fish_rule & \
+ forage_rule & fruit_rule & mineral_rule & vegetable_rule
+
+ def can_win_fishing_competition(self) -> StardewRule:
+ return self.skill.can_fish(difficulty=60)
+
+ def has_island_trader(self) -> StardewRule:
+ if self.options.exclude_ginger_island == ExcludeGingerIsland.option_true:
+ return False_()
+ return self.region.can_reach(Region.island_trader)
+
+ def has_walnut(self, number: int) -> StardewRule:
+ if self.options.exclude_ginger_island == ExcludeGingerIsland.option_true:
+ return False_()
+ if number <= 0:
+ return True_()
+ # https://stardewcommunitywiki.com/Golden_Walnut#Walnut_Locations
+ reach_south = self.region.can_reach(Region.island_south)
+ reach_north = self.region.can_reach(Region.island_north)
+ reach_west = self.region.can_reach(Region.island_west)
+ reach_hut = self.region.can_reach(Region.leo_hut)
+ reach_southeast = self.region.can_reach(Region.island_south_east)
+ reach_field_office = self.region.can_reach(Region.field_office)
+ reach_pirate_cove = self.region.can_reach(Region.pirate_cove)
+ reach_outside_areas = And(reach_south, reach_north, reach_west, reach_hut)
+ reach_volcano_regions = [self.region.can_reach(Region.volcano),
+ self.region.can_reach(Region.volcano_secret_beach),
+ self.region.can_reach(Region.volcano_floor_5),
+ self.region.can_reach(Region.volcano_floor_10)]
+ reach_volcano = Or(*reach_volcano_regions)
+ reach_all_volcano = And(*reach_volcano_regions)
+ reach_walnut_regions = [reach_south, reach_north, reach_west, reach_volcano, reach_field_office]
+ reach_caves = And(self.region.can_reach(Region.qi_walnut_room), self.region.can_reach(Region.dig_site),
+ self.region.can_reach(Region.gourmand_frog_cave),
+ self.region.can_reach(Region.colored_crystals_cave),
+ self.region.can_reach(Region.shipwreck), self.received(APWeapon.slingshot))
+ reach_entire_island = And(reach_outside_areas, reach_all_volcano,
+ reach_caves, reach_southeast, reach_field_office, reach_pirate_cove)
+ if number <= 5:
+ return Or(reach_south, reach_north, reach_west, reach_volcano)
+ if number <= 10:
+ return self.count(2, *reach_walnut_regions)
+ if number <= 15:
+ return self.count(3, *reach_walnut_regions)
+ if number <= 20:
+ return And(*reach_walnut_regions)
+ if number <= 50:
+ return reach_entire_island
+ gems = (Mineral.amethyst, Mineral.aquamarine, Mineral.emerald, Mineral.ruby, Mineral.topaz)
+ return reach_entire_island & self.has(Fruit.banana) & self.has_all(*gems) & self.ability.can_mine_perfectly() & \
+ self.ability.can_fish_perfectly() & self.has(Furniture.flute_block) & self.has(Seed.melon) & self.has(Seed.wheat) & self.has(Seed.garlic) & \
+ self.can_complete_field_office()
+
+ def has_all_stardrops(self) -> StardewRule:
+ other_rules = []
+ number_of_stardrops_to_receive = 0
+ number_of_stardrops_to_receive += 1 # The Mines level 100
+ number_of_stardrops_to_receive += 1 # Old Master Cannoli
+ number_of_stardrops_to_receive += 1 # Museum Stardrop
+ number_of_stardrops_to_receive += 1 # Krobus Stardrop
+
+ if self.options.fishsanity == Fishsanity.option_none: # Master Angler Stardrop
+ other_rules.append(self.fishing.can_catch_every_fish())
+ else:
+ number_of_stardrops_to_receive += 1
+
+ if self.options.festival_locations == FestivalLocations.option_disabled: # Fair Stardrop
+ other_rules.append(self.season.has(Season.fall))
+ else:
+ number_of_stardrops_to_receive += 1
+
+ if self.options.friendsanity == Friendsanity.option_none: # Spouse Stardrop
+ other_rules.append(self.relationship.has_hearts(Generic.bachelor, 13))
+ else:
+ number_of_stardrops_to_receive += 1
+
+ if ModNames.deepwoods in self.options.mods: # Petting the Unicorn
+ number_of_stardrops_to_receive += 1
+
+ if not other_rules:
+ return self.received("Stardrop", number_of_stardrops_to_receive)
+
+ return self.received("Stardrop", number_of_stardrops_to_receive) & And(*other_rules)
+
+ def has_prismatic_jelly_reward_access(self) -> StardewRule:
+ if self.options.special_order_locations == SpecialOrderLocations.option_disabled:
+ return self.special_order.can_complete_special_order("Prismatic Jelly")
+ return self.received("Monster Musk Recipe")
+
+ def has_all_rarecrows(self) -> StardewRule:
+ rules = []
+ for rarecrow_number in range(1, 9):
+ rules.append(self.received(f"Rarecrow #{rarecrow_number}"))
+ return And(*rules)
+
+ def has_abandoned_jojamart(self) -> StardewRule:
+ return self.received(CommunityUpgrade.movie_theater, 1)
+
+ def has_movie_theater(self) -> StardewRule:
+ return self.received(CommunityUpgrade.movie_theater, 2)
+
+ def can_use_obelisk(self, obelisk: str) -> StardewRule:
+ return self.region.can_reach(Region.farm) & self.received(obelisk)
+
+ def has_fruit_bats(self) -> StardewRule:
+ return self.region.can_reach(Region.farm_cave) & self.received(CommunityUpgrade.fruit_bats)
+
+ def has_mushroom_cave(self) -> StardewRule:
+ return self.region.can_reach(Region.farm_cave) & self.received(CommunityUpgrade.mushroom_boxes)
+
+ def can_fish_pond(self, fish: str) -> StardewRule:
+ return self.building.has_building(Building.fish_pond) & self.has(fish)
diff --git a/worlds/stardew_valley/logic/logic_and_mods_design.md b/worlds/stardew_valley/logic/logic_and_mods_design.md
new file mode 100644
index 000000000000..14716e1af0e1
--- /dev/null
+++ b/worlds/stardew_valley/logic/logic_and_mods_design.md
@@ -0,0 +1,58 @@
+# Logic mixin
+
+Mixins are used to split the logic building methods in multiple classes, so it's more scoped and easier to extend specific methods.
+
+One single instance of Logic is necessary so mods can change the logics. This means that, when calling itself, a `Logic` class has to call its instance in
+the `logic`, because it might have been overriden.
+
+```python
+class TimeLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.time = TimeLogic(*args, **kwargs)
+
+
+class TimeLogic(BaseLogic[Union[TimeLogicMixin, ReceivedLogicMixin]]):
+
+ def has_lived_months(self, number: int) -> StardewRule:
+ return self.logic.received(Event.month_end, number)
+
+ def has_year_two(self) -> StardewRule:
+ return self.logic.time.has_lived_months(4)
+
+ def has_year_three(self) -> StardewRule:
+ return self.logic.time.has_lived_months(8)
+```
+
+Creating the rules for actual items has to be outside the `logic` instance. Once the vanilla logic builder is created, mods will be able to replace the logic
+implementations by their own modified version. For instance, the `combat` logic can be replaced by the magic mod to extends its methods to add spells in the
+combat logic.
+
+## Logic class created on the fly (idea)
+
+The logic class could be created dynamically, based on the `LogicMixin` provided by the content packs. This would allow replacing completely mixins, instead of
+overriding their logic afterward. Might be too complicated for no real gain tho...
+
+# Content pack (idea)
+
+Instead of using modules to hold the data, and have each mod adding their data to existing content, each mod data should be in a `ContentPack`. Vanilla, Ginger
+Island, or anything that could be disabled would be in a content pack as well.
+
+Eventually, Vanilla content could even be disabled (a split would be required for items that are necessary to all content packs) to have a Ginger Island only
+play through created without the heavy vanilla logic computation.
+
+## Unpacking
+
+Steps to unpack content follows the same steps has the world initialisation. Content pack however need to be unpacked in a specific order, based on their
+dependencies. Vanilla would always be first, then anything that depends only on Vanilla, etc.
+
+1. In `generate_early`, content packs are selected. The logic builders are created and content packs are unpacked so all their content is in the proper
+ item/npc/weapon lists.
+ - `ContentPack` instances are shared across players. However, some mods need to modify content of other packs. In that case, an instance of the content is
+ created specifically for that player (For instance, SVE changes the Wizard). This probably does not happen enough to require sharing those instances. If
+ necessary, a FlyWeight design pattern could be used.
+2. In `create_regions`, AP regions and entrances are unpacked, and randomized if needed.
+3. In `create_items`, AP items are unpacked, and randomized.
+4. In `set_rules`, the rules are applied to the AP entrances and locations. Each content pack have to apply the proper rules for their entrances and locations.
+ - (idea) To begin this step, sphere 0 could be simplified instantly as sphere 0 regions and items are already known.
+5. Nothing to do in `generate_basic`.
diff --git a/worlds/stardew_valley/logic/mine_logic.py b/worlds/stardew_valley/logic/mine_logic.py
new file mode 100644
index 000000000000..2c2eaabfd8ee
--- /dev/null
+++ b/worlds/stardew_valley/logic/mine_logic.py
@@ -0,0 +1,86 @@
+from typing import Union
+
+from Utils import cache_self1
+from .base_logic import BaseLogicMixin, BaseLogic
+from .combat_logic import CombatLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from .skill_logic import SkillLogicMixin
+from .tool_logic import ToolLogicMixin
+from .. import options
+from ..options import ToolProgression
+from ..stardew_rule import StardewRule, And, True_
+from ..strings.performance_names import Performance
+from ..strings.region_names import Region
+from ..strings.skill_names import Skill
+from ..strings.tool_names import Tool, ToolMaterial
+
+
+class MineLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.mine = MineLogic(*args, **kwargs)
+
+
+class MineLogic(BaseLogic[Union[MineLogicMixin, RegionLogicMixin, ReceivedLogicMixin, CombatLogicMixin, ToolLogicMixin, SkillLogicMixin]]):
+ # Regions
+ def can_mine_in_the_mines_floor_1_40(self) -> StardewRule:
+ return self.logic.region.can_reach(Region.mines_floor_5)
+
+ def can_mine_in_the_mines_floor_41_80(self) -> StardewRule:
+ return self.logic.region.can_reach(Region.mines_floor_45)
+
+ def can_mine_in_the_mines_floor_81_120(self) -> StardewRule:
+ return self.logic.region.can_reach(Region.mines_floor_85)
+
+ def can_mine_in_the_skull_cavern(self) -> StardewRule:
+ return (self.logic.mine.can_progress_in_the_mines_from_floor(120) &
+ self.logic.region.can_reach(Region.skull_cavern))
+
+ @cache_self1
+ def get_weapon_rule_for_floor_tier(self, tier: int):
+ if tier >= 4:
+ return self.logic.combat.can_fight_at_level(Performance.galaxy)
+ if tier >= 3:
+ return self.logic.combat.can_fight_at_level(Performance.great)
+ if tier >= 2:
+ return self.logic.combat.can_fight_at_level(Performance.good)
+ if tier >= 1:
+ return self.logic.combat.can_fight_at_level(Performance.decent)
+ return self.logic.combat.can_fight_at_level(Performance.basic)
+
+ @cache_self1
+ def can_progress_in_the_mines_from_floor(self, floor: int) -> StardewRule:
+ tier = floor // 40
+ rules = []
+ weapon_rule = self.logic.mine.get_weapon_rule_for_floor_tier(tier)
+ rules.append(weapon_rule)
+ if self.options.tool_progression & ToolProgression.option_progressive:
+ rules.append(self.logic.tool.has_tool(Tool.pickaxe, ToolMaterial.tiers[tier]))
+ if self.options.skill_progression == options.SkillProgression.option_progressive:
+ skill_tier = min(10, max(0, tier * 2))
+ rules.append(self.logic.skill.has_level(Skill.combat, skill_tier))
+ rules.append(self.logic.skill.has_level(Skill.mining, skill_tier))
+ return And(*rules)
+
+ @cache_self1
+ def has_mine_elevator_to_floor(self, floor: int) -> StardewRule:
+ if floor < 0:
+ floor = 0
+ if self.options.elevator_progression != options.ElevatorProgression.option_vanilla:
+ return self.logic.received("Progressive Mine Elevator", floor // 5)
+ return True_()
+
+ @cache_self1
+ def can_progress_in_the_skull_cavern_from_floor(self, floor: int) -> StardewRule:
+ tier = floor // 50
+ rules = []
+ weapon_rule = self.logic.combat.has_great_weapon
+ rules.append(weapon_rule)
+ if self.options.tool_progression & ToolProgression.option_progressive:
+ rules.append(self.logic.received("Progressive Pickaxe", min(4, max(0, tier + 2))))
+ if self.options.skill_progression == options.SkillProgression.option_progressive:
+ skill_tier = min(10, max(0, tier * 2 + 6))
+ rules.extend({self.logic.skill.has_level(Skill.combat, skill_tier),
+ self.logic.skill.has_level(Skill.mining, skill_tier)})
+ return And(*rules)
diff --git a/worlds/stardew_valley/logic/money_logic.py b/worlds/stardew_valley/logic/money_logic.py
new file mode 100644
index 000000000000..92945a3636a8
--- /dev/null
+++ b/worlds/stardew_valley/logic/money_logic.py
@@ -0,0 +1,99 @@
+from typing import Union
+
+from Utils import cache_self1
+from .base_logic import BaseLogicMixin, BaseLogic
+from .buff_logic import BuffLogicMixin
+from .has_logic import HasLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from .time_logic import TimeLogicMixin
+from ..options import SpecialOrderLocations
+from ..stardew_rule import StardewRule, True_, HasProgressionPercent, False_
+from ..strings.ap_names.event_names import Event
+from ..strings.currency_names import Currency
+from ..strings.region_names import Region
+
+qi_gem_rewards = ("100 Qi Gems", "50 Qi Gems", "40 Qi Gems", "35 Qi Gems", "25 Qi Gems",
+ "20 Qi Gems", "15 Qi Gems", "10 Qi Gems")
+
+
+class MoneyLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.money = MoneyLogic(*args, **kwargs)
+
+
+class MoneyLogic(BaseLogic[Union[RegionLogicMixin, MoneyLogicMixin, TimeLogicMixin, RegionLogicMixin, ReceivedLogicMixin, HasLogicMixin, BuffLogicMixin]]):
+
+ @cache_self1
+ def can_have_earned_total(self, amount: int) -> StardewRule:
+ if amount < 1000:
+ return True_()
+
+ pierre_rule = self.logic.region.can_reach_all((Region.pierre_store, Region.forest))
+ willy_rule = self.logic.region.can_reach_all((Region.fish_shop, Region.fishing))
+ clint_rule = self.logic.region.can_reach_all((Region.blacksmith, Region.mines_floor_5))
+ robin_rule = self.logic.region.can_reach_all((Region.carpenter, Region.secret_woods))
+ shipping_rule = self.logic.received(Event.can_ship_items)
+
+ if amount < 2000:
+ selling_any_rule = pierre_rule | willy_rule | clint_rule | robin_rule | shipping_rule
+ return selling_any_rule
+
+ if amount < 5000:
+ selling_all_rule = (pierre_rule & willy_rule & clint_rule & robin_rule) | shipping_rule
+ return selling_all_rule
+
+ if amount < 10000:
+ return shipping_rule
+
+ seed_rules = self.logic.received(Event.can_shop_at_pierre)
+ if amount < 40000:
+ return shipping_rule & seed_rules
+
+ percent_progression_items_needed = min(90, amount // 20000)
+ return shipping_rule & seed_rules & HasProgressionPercent(self.player, percent_progression_items_needed)
+
+ @cache_self1
+ def can_spend(self, amount: int) -> StardewRule:
+ if self.options.starting_money == -1:
+ return True_()
+ return self.logic.money.can_have_earned_total(amount * 5)
+
+ # Should be cached
+ def can_spend_at(self, region: str, amount: int) -> StardewRule:
+ return self.logic.region.can_reach(region) & self.logic.money.can_spend(amount)
+
+ # Should be cached
+ def can_trade(self, currency: str, amount: int) -> StardewRule:
+ if amount == 0:
+ return True_()
+ if currency == Currency.money:
+ return self.can_spend(amount)
+ if currency == Currency.star_token:
+ return self.logic.region.can_reach(Region.fair)
+ if currency == Currency.qi_coin:
+ return self.logic.region.can_reach(Region.casino) & self.logic.buff.has_max_luck()
+ if currency == Currency.qi_gem:
+ if self.options.special_order_locations == SpecialOrderLocations.option_board_qi:
+ number_rewards = min(len(qi_gem_rewards), max(1, (amount // 10)))
+ return self.logic.received_n(*qi_gem_rewards, count=number_rewards)
+ number_rewards = 2
+ return self.logic.received_n(*qi_gem_rewards, count=number_rewards) & self.logic.region.can_reach(Region.qi_walnut_room) & \
+ self.logic.region.can_reach(Region.saloon) & self.can_have_earned_total(5000)
+ if currency == Currency.golden_walnut:
+ return self.can_spend_walnut(amount)
+
+ return self.logic.has(currency) & self.logic.time.has_lived_months(amount)
+
+ # Should be cached
+ def can_trade_at(self, region: str, currency: str, amount: int) -> StardewRule:
+ if amount == 0:
+ return True_()
+ if currency == Currency.money:
+ return self.logic.money.can_spend_at(region, amount)
+
+ return self.logic.region.can_reach(region) & self.can_trade(currency, amount)
+
+ def can_spend_walnut(self, amount: int) -> StardewRule:
+ return False_()
diff --git a/worlds/stardew_valley/logic/monster_logic.py b/worlds/stardew_valley/logic/monster_logic.py
new file mode 100644
index 000000000000..790f492347e6
--- /dev/null
+++ b/worlds/stardew_valley/logic/monster_logic.py
@@ -0,0 +1,69 @@
+from functools import cached_property
+from typing import Iterable, Union, Hashable
+
+from Utils import cache_self1
+from .base_logic import BaseLogicMixin, BaseLogic
+from .combat_logic import CombatLogicMixin
+from .region_logic import RegionLogicMixin
+from .time_logic import TimeLogicMixin, MAX_MONTHS
+from .. import options
+from ..data import monster_data
+from ..stardew_rule import StardewRule, Or, And
+from ..strings.region_names import Region
+
+
+class MonsterLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.monster = MonsterLogic(*args, **kwargs)
+
+
+class MonsterLogic(BaseLogic[Union[MonsterLogicMixin, RegionLogicMixin, CombatLogicMixin, TimeLogicMixin]]):
+
+ @cached_property
+ def all_monsters_by_name(self):
+ return monster_data.all_monsters_by_name_given_mods(self.options.mods.value)
+
+ @cached_property
+ def all_monsters_by_category(self):
+ return monster_data.all_monsters_by_category_given_mods(self.options.mods.value)
+
+ def can_kill(self, monster: Union[str, monster_data.StardewMonster], amount_tier: int = 0) -> StardewRule:
+ if isinstance(monster, str):
+ monster = self.all_monsters_by_name[monster]
+ region_rule = self.logic.region.can_reach_any(monster.locations)
+ combat_rule = self.logic.combat.can_fight_at_level(monster.difficulty)
+ if amount_tier <= 0:
+ amount_tier = 0
+ time_rule = self.logic.time.has_lived_months(amount_tier)
+ return region_rule & combat_rule & time_rule
+
+ @cache_self1
+ def can_kill_many(self, monster: monster_data.StardewMonster) -> StardewRule:
+ return self.logic.monster.can_kill(monster, MAX_MONTHS / 3)
+
+ @cache_self1
+ def can_kill_max(self, monster: monster_data.StardewMonster) -> StardewRule:
+ return self.logic.monster.can_kill(monster, MAX_MONTHS)
+
+ # Should be cached
+ def can_kill_any(self, monsters: (Iterable[monster_data.StardewMonster], Hashable), amount_tier: int = 0) -> StardewRule:
+ rules = [self.logic.monster.can_kill(monster, amount_tier) for monster in monsters]
+ return Or(*rules)
+
+ # Should be cached
+ def can_kill_all(self, monsters: (Iterable[monster_data.StardewMonster], Hashable), amount_tier: int = 0) -> StardewRule:
+ rules = [self.logic.monster.can_kill(monster, amount_tier) for monster in monsters]
+ return And(*rules)
+
+ def can_complete_all_monster_slaying_goals(self) -> StardewRule:
+ rules = [self.logic.time.has_lived_max_months]
+ exclude_island = self.options.exclude_ginger_island == options.ExcludeGingerIsland.option_true
+ island_regions = [Region.volcano_floor_5, Region.volcano_floor_10, Region.island_west, Region.dangerous_skull_cavern]
+ for category in self.all_monsters_by_category:
+ if exclude_island and all(all(location in island_regions for location in monster.locations)
+ for monster in self.all_monsters_by_category[category]):
+ continue
+ rules.append(self.logic.monster.can_kill_any(self.all_monsters_by_category[category]))
+
+ return And(*rules)
diff --git a/worlds/stardew_valley/logic/museum_logic.py b/worlds/stardew_valley/logic/museum_logic.py
new file mode 100644
index 000000000000..59ef0f6499c1
--- /dev/null
+++ b/worlds/stardew_valley/logic/museum_logic.py
@@ -0,0 +1,80 @@
+from typing import Union
+
+from Utils import cache_self1
+from .action_logic import ActionLogicMixin
+from .base_logic import BaseLogic, BaseLogicMixin
+from .has_logic import HasLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from .. import options
+from ..data.museum_data import MuseumItem, all_museum_items, all_museum_artifacts, all_museum_minerals
+from ..stardew_rule import StardewRule, And, False_
+from ..strings.region_names import Region
+
+
+class MuseumLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.museum = MuseumLogic(*args, **kwargs)
+
+
+class MuseumLogic(BaseLogic[Union[ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, ActionLogicMixin, MuseumLogicMixin]]):
+
+ def can_donate_museum_items(self, number: int) -> StardewRule:
+ return self.logic.region.can_reach(Region.museum) & self.logic.museum.can_find_museum_items(number)
+
+ def can_donate_museum_artifacts(self, number: int) -> StardewRule:
+ return self.logic.region.can_reach(Region.museum) & self.logic.museum.can_find_museum_artifacts(number)
+
+ @cache_self1
+ def can_find_museum_item(self, item: MuseumItem) -> StardewRule:
+ if item.locations:
+ region_rule = self.logic.region.can_reach_all_except_one(item.locations)
+ else:
+ region_rule = False_()
+ if item.geodes:
+ geodes_rule = And(*(self.logic.action.can_open_geode(geode) for geode in item.geodes))
+ else:
+ geodes_rule = False_()
+ # monster_rule = self.can_farm_monster(item.monsters)
+ # extra_rule = True_()
+ pan_rule = False_()
+ if item.item_name == "Earth Crystal" or item.item_name == "Fire Quartz" or item.item_name == "Frozen Tear":
+ pan_rule = self.logic.action.can_pan()
+ return pan_rule | region_rule | geodes_rule # & monster_rule & extra_rule
+
+ def can_find_museum_artifacts(self, number: int) -> StardewRule:
+ rules = []
+ for artifact in all_museum_artifacts:
+ rules.append(self.logic.museum.can_find_museum_item(artifact))
+
+ return self.logic.count(number, *rules)
+
+ def can_find_museum_minerals(self, number: int) -> StardewRule:
+ rules = []
+ for mineral in all_museum_minerals:
+ rules.append(self.logic.museum.can_find_museum_item(mineral))
+
+ return self.logic.count(number, *rules)
+
+ def can_find_museum_items(self, number: int) -> StardewRule:
+ rules = []
+ for donation in all_museum_items:
+ rules.append(self.logic.museum.can_find_museum_item(donation))
+
+ return self.logic.count(number, *rules)
+
+ def can_complete_museum(self) -> StardewRule:
+ rules = [self.logic.region.can_reach(Region.museum)]
+
+ if self.options.museumsanity == options.Museumsanity.option_none:
+ rules.append(self.logic.received("Traveling Merchant Metal Detector", 2))
+ else:
+ rules.append(self.logic.received("Traveling Merchant Metal Detector", 3))
+
+ for donation in all_museum_items:
+ rules.append(self.logic.museum.can_find_museum_item(donation))
+ return And(*rules) & self.logic.region.can_reach(Region.museum)
+
+ def can_donate(self, item: str) -> StardewRule:
+ return self.logic.has(item) & self.logic.region.can_reach(Region.museum)
diff --git a/worlds/stardew_valley/logic/pet_logic.py b/worlds/stardew_valley/logic/pet_logic.py
new file mode 100644
index 000000000000..5d7d79a358ca
--- /dev/null
+++ b/worlds/stardew_valley/logic/pet_logic.py
@@ -0,0 +1,50 @@
+import math
+from typing import Union
+
+from .base_logic import BaseLogicMixin, BaseLogic
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from .time_logic import TimeLogicMixin
+from .tool_logic import ToolLogicMixin
+from ..data.villagers_data import Villager
+from ..options import Friendsanity
+from ..stardew_rule import StardewRule, True_
+from ..strings.region_names import Region
+from ..strings.villager_names import NPC
+
+
+class PetLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.pet = PetLogic(*args, **kwargs)
+
+
+class PetLogic(BaseLogic[Union[RegionLogicMixin, ReceivedLogicMixin, TimeLogicMixin, ToolLogicMixin]]):
+ def has_hearts(self, hearts: int = 1) -> StardewRule:
+ if hearts <= 0:
+ return True_()
+ if self.options.friendsanity == Friendsanity.option_none or self.options.friendsanity == Friendsanity.option_bachelors:
+ return self.can_befriend_pet(hearts)
+ return self.received_hearts(NPC.pet, hearts)
+
+ def received_hearts(self, npc: Union[str, Villager], hearts: int) -> StardewRule:
+ if isinstance(npc, Villager):
+ return self.received_hearts(npc.name, hearts)
+ return self.logic.received(self.heart(npc), math.ceil(hearts / self.options.friendsanity_heart_size))
+
+ def can_befriend_pet(self, hearts: int) -> StardewRule:
+ if hearts <= 0:
+ return True_()
+ points = hearts * 200
+ points_per_month = 12 * 14
+ points_per_water_month = 18 * 14
+ farm_rule = self.logic.region.can_reach(Region.farm)
+ time_with_water_rule = self.logic.tool.can_water(0) & self.logic.time.has_lived_months(points // points_per_water_month)
+ time_without_water_rule = self.logic.time.has_lived_months(points // points_per_month)
+ time_rule = time_with_water_rule | time_without_water_rule
+ return farm_rule & time_rule
+
+ def heart(self, npc: Union[str, Villager]) -> str:
+ if isinstance(npc, str):
+ return f"{npc} <3"
+ return self.heart(npc.name)
diff --git a/worlds/stardew_valley/logic/quest_logic.py b/worlds/stardew_valley/logic/quest_logic.py
new file mode 100644
index 000000000000..bc1f731429c6
--- /dev/null
+++ b/worlds/stardew_valley/logic/quest_logic.py
@@ -0,0 +1,128 @@
+from typing import Dict, Union
+
+from .base_logic import BaseLogicMixin, BaseLogic
+from .building_logic import BuildingLogicMixin
+from .combat_logic import CombatLogicMixin
+from .cooking_logic import CookingLogicMixin
+from .fishing_logic import FishingLogicMixin
+from .has_logic import HasLogicMixin
+from .mine_logic import MineLogicMixin
+from .money_logic import MoneyLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from .relationship_logic import RelationshipLogicMixin
+from .season_logic import SeasonLogicMixin
+from .skill_logic import SkillLogicMixin
+from .time_logic import TimeLogicMixin
+from .tool_logic import ToolLogicMixin
+from .wallet_logic import WalletLogicMixin
+from ..stardew_rule import StardewRule, Has, True_
+from ..strings.artisan_good_names import ArtisanGood
+from ..strings.building_names import Building
+from ..strings.craftable_names import Craftable
+from ..strings.crop_names import Fruit, Vegetable
+from ..strings.fish_names import Fish
+from ..strings.food_names import Meal
+from ..strings.forageable_names import Forageable
+from ..strings.machine_names import Machine
+from ..strings.material_names import Material
+from ..strings.metal_names import MetalBar, Ore, Mineral
+from ..strings.monster_drop_names import Loot
+from ..strings.quest_names import Quest
+from ..strings.region_names import Region
+from ..strings.season_names import Season
+from ..strings.tool_names import Tool
+from ..strings.villager_names import NPC
+from ..strings.wallet_item_names import Wallet
+
+
+class QuestLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.quest = QuestLogic(*args, **kwargs)
+
+
+class QuestLogic(BaseLogic[Union[HasLogicMixin, ReceivedLogicMixin, MoneyLogicMixin, MineLogicMixin, RegionLogicMixin, RelationshipLogicMixin, ToolLogicMixin,
+FishingLogicMixin, CookingLogicMixin, CombatLogicMixin, SeasonLogicMixin, SkillLogicMixin, WalletLogicMixin, QuestLogicMixin, BuildingLogicMixin, TimeLogicMixin]]):
+
+ def initialize_rules(self):
+ self.update_rules({
+ Quest.introductions: True_(),
+ Quest.how_to_win_friends: self.logic.quest.can_complete_quest(Quest.introductions),
+ Quest.getting_started: self.logic.has(Vegetable.parsnip),
+ Quest.to_the_beach: self.logic.region.can_reach(Region.beach),
+ Quest.raising_animals: self.logic.quest.can_complete_quest(Quest.getting_started) & self.logic.building.has_building(Building.coop),
+ Quest.advancement: self.logic.quest.can_complete_quest(Quest.getting_started) & self.logic.has(Craftable.scarecrow),
+ Quest.archaeology: self.logic.tool.has_tool(Tool.hoe) | self.logic.mine.can_mine_in_the_mines_floor_1_40() | self.logic.skill.can_fish(),
+ Quest.rat_problem: self.logic.region.can_reach_all((Region.town, Region.community_center)),
+ Quest.meet_the_wizard: self.logic.quest.can_complete_quest(Quest.rat_problem),
+ Quest.forging_ahead: self.logic.has(Ore.copper) & self.logic.has(Machine.furnace),
+ Quest.smelting: self.logic.has(MetalBar.copper),
+ Quest.initiation: self.logic.mine.can_mine_in_the_mines_floor_1_40(),
+ Quest.robins_lost_axe: self.logic.season.has(Season.spring) & self.logic.relationship.can_meet(NPC.robin),
+ Quest.jodis_request: self.logic.season.has(Season.spring) & self.logic.has(Vegetable.cauliflower) & self.logic.relationship.can_meet(NPC.jodi),
+ Quest.mayors_shorts: self.logic.season.has(Season.summer) & self.logic.relationship.has_hearts(NPC.marnie, 2) &
+ self.logic.relationship.can_meet(NPC.lewis),
+ Quest.blackberry_basket: self.logic.season.has(Season.fall) & self.logic.relationship.can_meet(NPC.linus),
+ Quest.marnies_request: self.logic.relationship.has_hearts(NPC.marnie, 3) & self.logic.has(Forageable.cave_carrot),
+ Quest.pam_is_thirsty: self.logic.season.has(Season.summer) & self.logic.has(ArtisanGood.pale_ale) & self.logic.relationship.can_meet(NPC.pam),
+ Quest.a_dark_reagent: self.logic.season.has(Season.winter) & self.logic.has(Loot.void_essence) & self.logic.relationship.can_meet(NPC.wizard),
+ Quest.cows_delight: self.logic.season.has(Season.fall) & self.logic.has(Vegetable.amaranth) & self.logic.relationship.can_meet(NPC.marnie),
+ Quest.the_skull_key: self.logic.received(Wallet.skull_key),
+ Quest.crop_research: self.logic.season.has(Season.summer) & self.logic.has(Fruit.melon) & self.logic.relationship.can_meet(NPC.demetrius),
+ Quest.knee_therapy: self.logic.season.has(Season.summer) & self.logic.has(Fruit.hot_pepper) & self.logic.relationship.can_meet(NPC.george),
+ Quest.robins_request: self.logic.season.has(Season.winter) & self.logic.has(Material.hardwood) & self.logic.relationship.can_meet(NPC.robin),
+ Quest.qis_challenge: True_(), # The skull cavern floor 25 already has rules
+ Quest.the_mysterious_qi: (self.logic.region.can_reach_all((Region.bus_tunnel, Region.railroad, Region.mayor_house)) &
+ self.logic.has_all(ArtisanGood.battery_pack, Forageable.rainbow_shell, Vegetable.beet, Loot.solar_essence)),
+ Quest.carving_pumpkins: self.logic.season.has(Season.fall) & self.logic.has(Vegetable.pumpkin) & self.logic.relationship.can_meet(NPC.caroline),
+ Quest.a_winter_mystery: self.logic.season.has(Season.winter),
+ Quest.strange_note: self.logic.has(Forageable.secret_note) & self.logic.has(ArtisanGood.maple_syrup),
+ Quest.cryptic_note: self.logic.has(Forageable.secret_note),
+ Quest.fresh_fruit: self.logic.season.has(Season.spring) & self.logic.has(Fruit.apricot) & self.logic.relationship.can_meet(NPC.emily),
+ Quest.aquatic_research: self.logic.season.has(Season.summer) & self.logic.has(Fish.pufferfish) & self.logic.relationship.can_meet(NPC.demetrius),
+ Quest.a_soldiers_star: (self.logic.season.has(Season.summer) & self.logic.time.has_year_two & self.logic.has(Fruit.starfruit) &
+ self.logic.relationship.can_meet(NPC.kent)),
+ Quest.mayors_need: self.logic.season.has(Season.summer) & self.logic.has(ArtisanGood.truffle_oil) & self.logic.relationship.can_meet(NPC.lewis),
+ Quest.wanted_lobster: (self.logic.season.has(Season.fall) & self.logic.season.has(Season.fall) & self.logic.has(Fish.lobster) &
+ self.logic.relationship.can_meet(NPC.gus)),
+ Quest.pam_needs_juice: self.logic.season.has(Season.fall) & self.logic.has(ArtisanGood.battery_pack) & self.logic.relationship.can_meet(NPC.pam),
+ Quest.fish_casserole: self.logic.relationship.has_hearts(NPC.jodi, 4) & self.logic.has(Fish.largemouth_bass),
+ Quest.catch_a_squid: self.logic.season.has(Season.winter) & self.logic.has(Fish.squid) & self.logic.relationship.can_meet(NPC.willy),
+ Quest.fish_stew: self.logic.season.has(Season.winter) & self.logic.has(Fish.albacore) & self.logic.relationship.can_meet(NPC.gus),
+ Quest.pierres_notice: self.logic.season.has(Season.spring) & self.logic.has(Meal.sashimi) & self.logic.relationship.can_meet(NPC.pierre),
+ Quest.clints_attempt: self.logic.season.has(Season.winter) & self.logic.has(Mineral.amethyst) & self.logic.relationship.can_meet(NPC.emily),
+ Quest.a_favor_for_clint: self.logic.season.has(Season.winter) & self.logic.has(MetalBar.iron) & self.logic.relationship.can_meet(NPC.clint),
+ Quest.staff_of_power: self.logic.season.has(Season.winter) & self.logic.has(MetalBar.iridium) & self.logic.relationship.can_meet(NPC.wizard),
+ Quest.grannys_gift: self.logic.season.has(Season.spring) & self.logic.has(Forageable.leek) & self.logic.relationship.can_meet(NPC.evelyn),
+ Quest.exotic_spirits: self.logic.season.has(Season.winter) & self.logic.has(Forageable.coconut) & self.logic.relationship.can_meet(NPC.gus),
+ Quest.catch_a_lingcod: self.logic.season.has(Season.winter) & self.logic.has(Fish.lingcod) & self.logic.relationship.can_meet(NPC.willy),
+ Quest.dark_talisman: self.logic.region.can_reach(Region.railroad) & self.logic.wallet.has_rusty_key() & self.logic.relationship.can_meet(
+ NPC.krobus),
+ Quest.goblin_problem: self.logic.region.can_reach(Region.witch_swamp),
+ Quest.magic_ink: self.logic.relationship.can_meet(NPC.wizard),
+ Quest.the_pirates_wife: self.logic.relationship.can_meet(NPC.kent) & self.logic.relationship.can_meet(NPC.gus) &
+ self.logic.relationship.can_meet(NPC.sandy) & self.logic.relationship.can_meet(NPC.george) &
+ self.logic.relationship.can_meet(NPC.wizard) & self.logic.relationship.can_meet(NPC.willy),
+ })
+
+ def update_rules(self, new_rules: Dict[str, StardewRule]):
+ self.registry.quest_rules.update(new_rules)
+
+ def can_complete_quest(self, quest: str) -> StardewRule:
+ return Has(quest, self.registry.quest_rules)
+
+ def has_club_card(self) -> StardewRule:
+ if self.options.quest_locations < 0:
+ return self.logic.quest.can_complete_quest(Quest.the_mysterious_qi)
+ return self.logic.received(Wallet.club_card)
+
+ def has_magnifying_glass(self) -> StardewRule:
+ if self.options.quest_locations < 0:
+ return self.logic.quest.can_complete_quest(Quest.a_winter_mystery)
+ return self.logic.received(Wallet.magnifying_glass)
+
+ def has_dark_talisman(self) -> StardewRule:
+ if self.options.quest_locations < 0:
+ return self.logic.quest.can_complete_quest(Quest.dark_talisman)
+ return self.logic.received(Wallet.dark_talisman)
diff --git a/worlds/stardew_valley/logic/received_logic.py b/worlds/stardew_valley/logic/received_logic.py
new file mode 100644
index 000000000000..66dc078ad46f
--- /dev/null
+++ b/worlds/stardew_valley/logic/received_logic.py
@@ -0,0 +1,35 @@
+from typing import Optional
+
+from .base_logic import BaseLogic, BaseLogicMixin
+from .has_logic import HasLogicMixin
+from ..stardew_rule import StardewRule, Received, And, Or, TotalReceived
+
+
+class ReceivedLogicMixin(BaseLogic[HasLogicMixin], BaseLogicMixin):
+ # Should be cached
+ def received(self, item: str, count: Optional[int] = 1) -> StardewRule:
+ assert count >= 0, "Can't receive a negative amount of item."
+
+ return Received(item, self.player, count)
+
+ def received_all(self, *items: str):
+ assert items, "Can't receive all of no items."
+
+ return And(*(self.received(item) for item in items))
+
+ def received_any(self, *items: str):
+ assert items, "Can't receive any of no items."
+
+ return Or(*(self.received(item) for item in items))
+
+ def received_once(self, *items: str, count: int):
+ assert items, "Can't receive once of no items."
+ assert count >= 0, "Can't receive a negative amount of item."
+
+ return self.logic.count(count, *(self.received(item) for item in items))
+
+ def received_n(self, *items: str, count: int):
+ assert items, "Can't receive n of no items."
+ assert count >= 0, "Can't receive a negative amount of item."
+
+ return TotalReceived(count, items, self.player)
diff --git a/worlds/stardew_valley/logic/region_logic.py b/worlds/stardew_valley/logic/region_logic.py
new file mode 100644
index 000000000000..81dabf45aac5
--- /dev/null
+++ b/worlds/stardew_valley/logic/region_logic.py
@@ -0,0 +1,65 @@
+from typing import Tuple, Union
+
+from Utils import cache_self1
+from .base_logic import BaseLogic, BaseLogicMixin
+from .has_logic import HasLogicMixin
+from ..options import EntranceRandomization
+from ..stardew_rule import StardewRule, And, Or, Reach, false_, true_
+from ..strings.region_names import Region
+
+main_outside_area = {Region.menu, Region.stardew_valley, Region.farm_house, Region.farm, Region.town, Region.beach, Region.mountain, Region.forest,
+ Region.bus_stop, Region.backwoods, Region.bus_tunnel, Region.tunnel_entrance}
+always_accessible_regions_without_er = {*main_outside_area, Region.community_center, Region.pantry, Region.crafts_room, Region.fish_tank, Region.boiler_room,
+ Region.vault, Region.bulletin_board, Region.mines, Region.hospital, Region.carpenter, Region.alex_house,
+ Region.elliott_house, Region.ranch, Region.farm_cave, Region.wizard_tower, Region.tent, Region.pierre_store,
+ Region.saloon, Region.blacksmith, Region.trailer, Region.museum, Region.mayor_house, Region.haley_house,
+ Region.sam_house, Region.jojamart, Region.fish_shop}
+
+always_regions_by_setting = {EntranceRandomization.option_disabled: always_accessible_regions_without_er,
+ EntranceRandomization.option_pelican_town: always_accessible_regions_without_er,
+ EntranceRandomization.option_non_progression: always_accessible_regions_without_er,
+ EntranceRandomization.option_buildings: main_outside_area,
+ EntranceRandomization.option_chaos: always_accessible_regions_without_er}
+
+
+class RegionLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.region = RegionLogic(*args, **kwargs)
+
+
+class RegionLogic(BaseLogic[Union[RegionLogicMixin, HasLogicMixin]]):
+
+ @cache_self1
+ def can_reach(self, region_name: str) -> StardewRule:
+ if region_name in always_regions_by_setting[self.options.entrance_randomization]:
+ return true_
+
+ if region_name not in self.regions:
+ return false_
+
+ return Reach(region_name, "Region", self.player)
+
+ @cache_self1
+ def can_reach_any(self, region_names: Tuple[str, ...]) -> StardewRule:
+ return Or(*(self.logic.region.can_reach(spot) for spot in region_names))
+
+ @cache_self1
+ def can_reach_all(self, region_names: Tuple[str, ...]) -> StardewRule:
+ return And(*(self.logic.region.can_reach(spot) for spot in region_names))
+
+ @cache_self1
+ def can_reach_all_except_one(self, region_names: Tuple[str, ...]) -> StardewRule:
+ region_names = list(region_names)
+ num_required = len(region_names) - 1
+ if num_required <= 0:
+ num_required = len(region_names)
+ return self.logic.count(num_required, *(self.logic.region.can_reach(spot) for spot in region_names))
+
+ @cache_self1
+ def can_reach_location(self, location_name: str) -> StardewRule:
+ return Reach(location_name, "Location", self.player)
+
+ # @cache_self1
+ # def can_reach_entrance(self, entrance_name: str) -> StardewRule:
+ # return Reach(entrance_name, "Entrance", self.player)
diff --git a/worlds/stardew_valley/logic/relationship_logic.py b/worlds/stardew_valley/logic/relationship_logic.py
new file mode 100644
index 000000000000..fb0267bddb1a
--- /dev/null
+++ b/worlds/stardew_valley/logic/relationship_logic.py
@@ -0,0 +1,185 @@
+import math
+from functools import cached_property
+from typing import Union, List
+
+from Utils import cache_self1
+from .base_logic import BaseLogic, BaseLogicMixin
+from .building_logic import BuildingLogicMixin
+from .gift_logic import GiftLogicMixin
+from .has_logic import HasLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from .season_logic import SeasonLogicMixin
+from .time_logic import TimeLogicMixin
+from ..data.villagers_data import all_villagers_by_name, Villager, get_villagers_for_mods
+from ..options import Friendsanity
+from ..stardew_rule import StardewRule, True_, And, Or
+from ..strings.ap_names.mods.mod_items import SVEQuestItem
+from ..strings.crop_names import Fruit
+from ..strings.generic_names import Generic
+from ..strings.gift_names import Gift
+from ..strings.region_names import Region
+from ..strings.season_names import Season
+from ..strings.villager_names import NPC, ModNPC
+
+possible_kids = ("Cute Baby", "Ugly Baby")
+
+
+def heart_item_name(npc: Union[str, Villager]) -> str:
+ if isinstance(npc, Villager):
+ npc = npc.name
+
+ return f"{npc} <3"
+
+
+class RelationshipLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.relationship = RelationshipLogic(*args, **kwargs)
+
+
+class RelationshipLogic(BaseLogic[Union[
+ RelationshipLogicMixin, BuildingLogicMixin, SeasonLogicMixin, TimeLogicMixin, GiftLogicMixin, RegionLogicMixin, ReceivedLogicMixin, HasLogicMixin]]):
+
+ @cached_property
+ def all_villagers_given_mods(self) -> List[Villager]:
+ return get_villagers_for_mods(self.options.mods.value)
+
+ def can_date(self, npc: str) -> StardewRule:
+ return self.logic.relationship.has_hearts(npc, 8) & self.logic.has(Gift.bouquet)
+
+ def can_marry(self, npc: str) -> StardewRule:
+ return self.logic.relationship.has_hearts(npc, 10) & self.logic.has(Gift.mermaid_pendant)
+
+ def can_get_married(self) -> StardewRule:
+ return self.logic.relationship.has_hearts(Generic.bachelor, 10) & self.logic.has(Gift.mermaid_pendant)
+
+ def has_children(self, number_children: int) -> StardewRule:
+ if number_children <= 0:
+ return True_()
+ if self.options.friendsanity == Friendsanity.option_none:
+ return self.logic.relationship.can_reproduce(number_children)
+ return self.logic.received_n(*possible_kids, count=number_children) & self.logic.building.has_house(2)
+
+ def can_reproduce(self, number_children: int = 1) -> StardewRule:
+ if number_children <= 0:
+ return True_()
+ baby_rules = [self.logic.relationship.can_get_married(), self.logic.building.has_house(2), self.logic.relationship.has_hearts(Generic.bachelor, 12),
+ self.logic.relationship.has_children(number_children - 1)]
+ return And(*baby_rules)
+
+ # Should be cached
+ def has_hearts(self, npc: str, hearts: int = 1) -> StardewRule:
+ if hearts <= 0:
+ return True_()
+ if self.options.friendsanity == Friendsanity.option_none:
+ return self.logic.relationship.can_earn_relationship(npc, hearts)
+ if npc not in all_villagers_by_name:
+ if npc == Generic.any or npc == Generic.bachelor:
+ possible_friends = []
+ for name in all_villagers_by_name:
+ if not self.npc_is_in_current_slot(name):
+ continue
+ if npc == Generic.any or all_villagers_by_name[name].bachelor:
+ possible_friends.append(self.logic.relationship.has_hearts(name, hearts))
+ return Or(*possible_friends)
+ if npc == Generic.all:
+ mandatory_friends = []
+ for name in all_villagers_by_name:
+ if not self.npc_is_in_current_slot(name):
+ continue
+ mandatory_friends.append(self.logic.relationship.has_hearts(name, hearts))
+ return And(*mandatory_friends)
+ if npc.isnumeric():
+ possible_friends = []
+ for name in all_villagers_by_name:
+ if not self.npc_is_in_current_slot(name):
+ continue
+ possible_friends.append(self.logic.relationship.has_hearts(name, hearts))
+ return self.logic.count(int(npc), *possible_friends)
+ return self.can_earn_relationship(npc, hearts)
+
+ if not self.npc_is_in_current_slot(npc):
+ return True_()
+ villager = all_villagers_by_name[npc]
+ if self.options.friendsanity == Friendsanity.option_bachelors and not villager.bachelor:
+ return self.logic.relationship.can_earn_relationship(npc, hearts)
+ if self.options.friendsanity == Friendsanity.option_starting_npcs and not villager.available:
+ return self.logic.relationship.can_earn_relationship(npc, hearts)
+ is_capped_at_8 = villager.bachelor and self.options.friendsanity != Friendsanity.option_all_with_marriage
+ if is_capped_at_8 and hearts > 8:
+ return self.logic.relationship.received_hearts(villager.name, 8) & self.logic.relationship.can_earn_relationship(npc, hearts)
+ return self.logic.relationship.received_hearts(villager.name, hearts)
+
+ # Should be cached
+ def received_hearts(self, npc: str, hearts: int) -> StardewRule:
+ heart_item = heart_item_name(npc)
+ number_required = math.ceil(hearts / self.options.friendsanity_heart_size)
+ return self.logic.received(heart_item, number_required)
+
+ @cache_self1
+ def can_meet(self, npc: str) -> StardewRule:
+ if npc not in all_villagers_by_name or not self.npc_is_in_current_slot(npc):
+ return True_()
+ villager = all_villagers_by_name[npc]
+ rules = [self.logic.region.can_reach_any(villager.locations)]
+ if npc == NPC.kent:
+ rules.append(self.logic.time.has_year_two)
+ elif npc == NPC.leo:
+ rules.append(self.logic.received("Island West Turtle"))
+ elif npc == ModNPC.lance:
+ rules.append(self.logic.region.can_reach(Region.volcano_floor_10))
+ elif npc == ModNPC.apples:
+ rules.append(self.logic.has(Fruit.starfruit))
+ elif npc == ModNPC.scarlett:
+ scarlett_job = self.logic.received(SVEQuestItem.scarlett_job_offer)
+ scarlett_spring = self.logic.season.has(Season.spring) & self.can_meet(ModNPC.andy)
+ scarlett_summer = self.logic.season.has(Season.summer) & self.can_meet(ModNPC.susan)
+ scarlett_fall = self.logic.season.has(Season.fall) & self.can_meet(ModNPC.sophia)
+ rules.append(scarlett_job & (scarlett_spring | scarlett_summer | scarlett_fall))
+ elif npc == ModNPC.morgan:
+ rules.append(self.logic.received(SVEQuestItem.morgan_schooling))
+ elif npc == ModNPC.goblin:
+ rules.append(self.logic.region.can_reach_all((Region.witch_hut, Region.wizard_tower)))
+
+ return And(*rules)
+
+ def can_give_loved_gifts_to_everyone(self) -> StardewRule:
+ rules = []
+ for npc in all_villagers_by_name:
+ if not self.npc_is_in_current_slot(npc):
+ continue
+ meet_rule = self.logic.relationship.can_meet(npc)
+ rules.append(meet_rule)
+ rules.append(self.logic.gifts.has_any_universal_love)
+ return And(*rules)
+
+ # Should be cached
+ def can_earn_relationship(self, npc: str, hearts: int = 0) -> StardewRule:
+ if hearts <= 0:
+ return True_()
+
+ previous_heart = hearts - self.options.friendsanity_heart_size
+ previous_heart_rule = self.logic.relationship.has_hearts(npc, previous_heart)
+
+ if npc not in all_villagers_by_name or not self.npc_is_in_current_slot(npc):
+ return previous_heart_rule
+
+ rules = [previous_heart_rule, self.logic.relationship.can_meet(npc)]
+ villager = all_villagers_by_name[npc]
+ if hearts > 2 or hearts > self.options.friendsanity_heart_size:
+ rules.append(self.logic.season.has(villager.birthday))
+ if villager.birthday == Generic.any:
+ rules.append(self.logic.season.has_all() | self.logic.time.has_year_three) # push logic back for any birthday-less villager
+ if villager.bachelor:
+ if hearts > 8:
+ rules.append(self.logic.relationship.can_date(npc))
+ if hearts > 10:
+ rules.append(self.logic.relationship.can_marry(npc))
+
+ return And(*rules)
+
+ @cache_self1
+ def npc_is_in_current_slot(self, name: str) -> bool:
+ npc = all_villagers_by_name[name]
+ return npc in self.all_villagers_given_mods
diff --git a/worlds/stardew_valley/logic/season_logic.py b/worlds/stardew_valley/logic/season_logic.py
new file mode 100644
index 000000000000..1953502099b4
--- /dev/null
+++ b/worlds/stardew_valley/logic/season_logic.py
@@ -0,0 +1,44 @@
+from typing import Iterable, Union
+
+from Utils import cache_self1
+from .base_logic import BaseLogic, BaseLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .time_logic import TimeLogicMixin
+from ..options import SeasonRandomization
+from ..stardew_rule import StardewRule, True_, Or, And
+from ..strings.generic_names import Generic
+from ..strings.season_names import Season
+
+
+class SeasonLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.season = SeasonLogic(*args, **kwargs)
+
+
+class SeasonLogic(BaseLogic[Union[SeasonLogicMixin, TimeLogicMixin, ReceivedLogicMixin]]):
+
+ @cache_self1
+ def has(self, season: str) -> StardewRule:
+ if season == Generic.any:
+ return True_()
+ seasons_order = [Season.spring, Season.summer, Season.fall, Season.winter]
+ if self.options.season_randomization == SeasonRandomization.option_progressive:
+ return self.logic.received(Season.progressive, seasons_order.index(season))
+ if self.options.season_randomization == SeasonRandomization.option_disabled:
+ if season == Season.spring:
+ return True_()
+ return self.logic.time.has_lived_months(1)
+ return self.logic.received(season)
+
+ def has_any(self, seasons: Iterable[str]):
+ if not seasons:
+ return True_()
+ return Or(*(self.logic.season.has(season) for season in seasons))
+
+ def has_any_not_winter(self):
+ return self.logic.season.has_any([Season.spring, Season.summer, Season.fall])
+
+ def has_all(self):
+ seasons = [Season.spring, Season.summer, Season.fall, Season.winter]
+ return And(*(self.logic.season.has(season) for season in seasons))
diff --git a/worlds/stardew_valley/logic/shipping_logic.py b/worlds/stardew_valley/logic/shipping_logic.py
new file mode 100644
index 000000000000..52c97561b326
--- /dev/null
+++ b/worlds/stardew_valley/logic/shipping_logic.py
@@ -0,0 +1,60 @@
+from functools import cached_property
+from typing import Union, List
+
+from Utils import cache_self1
+from .base_logic import BaseLogic, BaseLogicMixin
+from .building_logic import BuildingLogicMixin
+from .has_logic import HasLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from ..locations import LocationTags, locations_by_tag
+from ..options import ExcludeGingerIsland, Shipsanity
+from ..options import SpecialOrderLocations
+from ..stardew_rule import StardewRule, And
+from ..strings.ap_names.event_names import Event
+from ..strings.building_names import Building
+
+
+class ShippingLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.shipping = ShippingLogic(*args, **kwargs)
+
+
+class ShippingLogic(BaseLogic[Union[ReceivedLogicMixin, ShippingLogicMixin, BuildingLogicMixin, RegionLogicMixin, HasLogicMixin]]):
+
+ @cached_property
+ def can_use_shipping_bin(self) -> StardewRule:
+ return self.logic.building.has_building(Building.shipping_bin)
+
+ @cache_self1
+ def can_ship(self, item: str) -> StardewRule:
+ return self.logic.received(Event.can_ship_items) & self.logic.has(item)
+
+ def can_ship_everything(self) -> StardewRule:
+ shipsanity_prefix = "Shipsanity: "
+ all_items_to_ship = []
+ exclude_island = self.options.exclude_ginger_island == ExcludeGingerIsland.option_true
+ exclude_qi = self.options.special_order_locations != SpecialOrderLocations.option_board_qi
+ mod_list = self.options.mods.value
+ for location in locations_by_tag[LocationTags.SHIPSANITY_FULL_SHIPMENT]:
+ if exclude_island and LocationTags.GINGER_ISLAND in location.tags:
+ continue
+ if exclude_qi and LocationTags.REQUIRES_QI_ORDERS in location.tags:
+ continue
+ if location.mod_name and location.mod_name not in mod_list:
+ continue
+ all_items_to_ship.append(location.name[len(shipsanity_prefix):])
+ return self.logic.building.has_building(Building.shipping_bin) & self.logic.has_all(*all_items_to_ship)
+
+ def can_ship_everything_in_slot(self, all_location_names_in_slot: List[str]) -> StardewRule:
+ if self.options.shipsanity == Shipsanity.option_none:
+ return self.can_ship_everything()
+
+ rules = [self.logic.building.has_building(Building.shipping_bin)]
+
+ for shipsanity_location in locations_by_tag[LocationTags.SHIPSANITY]:
+ if shipsanity_location.name not in all_location_names_in_slot:
+ continue
+ rules.append(self.logic.region.can_reach_location(shipsanity_location.name))
+ return And(*rules)
diff --git a/worlds/stardew_valley/logic/skill_logic.py b/worlds/stardew_valley/logic/skill_logic.py
new file mode 100644
index 000000000000..35946a0a4d36
--- /dev/null
+++ b/worlds/stardew_valley/logic/skill_logic.py
@@ -0,0 +1,180 @@
+from functools import cached_property
+from typing import Union, Tuple
+
+from Utils import cache_self1
+from .base_logic import BaseLogicMixin, BaseLogic
+from .combat_logic import CombatLogicMixin
+from .crop_logic import CropLogicMixin
+from .has_logic import HasLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from .season_logic import SeasonLogicMixin
+from .time_logic import TimeLogicMixin
+from .tool_logic import ToolLogicMixin
+from .. import options
+from ..data import all_crops
+from ..mods.logic.magic_logic import MagicLogicMixin
+from ..mods.logic.mod_skills_levels import get_mod_skill_levels
+from ..stardew_rule import StardewRule, True_, Or, False_
+from ..strings.craftable_names import Fishing
+from ..strings.machine_names import Machine
+from ..strings.performance_names import Performance
+from ..strings.quality_names import ForageQuality
+from ..strings.region_names import Region
+from ..strings.skill_names import Skill, all_mod_skills
+from ..strings.tool_names import ToolMaterial, Tool
+
+fishing_regions = (Region.beach, Region.town, Region.forest, Region.mountain, Region.island_south, Region.island_west)
+
+
+class SkillLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.skill = SkillLogic(*args, **kwargs)
+
+
+class SkillLogic(BaseLogic[Union[HasLogicMixin, ReceivedLogicMixin, RegionLogicMixin, SeasonLogicMixin, TimeLogicMixin, ToolLogicMixin, SkillLogicMixin,
+CombatLogicMixin, CropLogicMixin, MagicLogicMixin]]):
+ # Should be cached
+ def can_earn_level(self, skill: str, level: int) -> StardewRule:
+ if level <= 0:
+ return True_()
+
+ tool_level = (level - 1) // 2
+ tool_material = ToolMaterial.tiers[tool_level]
+ months = max(1, level - 1)
+ months_rule = self.logic.time.has_lived_months(months)
+
+ if self.options.skill_progression != options.SkillProgression.option_vanilla:
+ previous_level_rule = self.logic.skill.has_level(skill, level - 1)
+ else:
+ previous_level_rule = True_()
+
+ if skill == Skill.fishing:
+ xp_rule = self.logic.tool.has_fishing_rod(max(tool_level, 1))
+ elif skill == Skill.farming:
+ xp_rule = self.logic.tool.has_tool(Tool.hoe, tool_material) & self.logic.tool.can_water(tool_level)
+ elif skill == Skill.foraging:
+ xp_rule = self.logic.tool.has_tool(Tool.axe, tool_material) | self.logic.magic.can_use_clear_debris_instead_of_tool_level(tool_level)
+ elif skill == Skill.mining:
+ xp_rule = self.logic.tool.has_tool(Tool.pickaxe, tool_material) | \
+ self.logic.magic.can_use_clear_debris_instead_of_tool_level(tool_level)
+ xp_rule = xp_rule & self.logic.region.can_reach(Region.mines_floor_5)
+ elif skill == Skill.combat:
+ combat_tier = Performance.tiers[tool_level]
+ xp_rule = self.logic.combat.can_fight_at_level(combat_tier)
+ xp_rule = xp_rule & self.logic.region.can_reach(Region.mines_floor_5)
+ elif skill in all_mod_skills:
+ # Ideal solution would be to add a logic registry, but I'm too lazy.
+ return self.logic.mod.skill.can_earn_mod_skill_level(skill, level)
+ else:
+ raise Exception(f"Unknown skill: {skill}")
+
+ return previous_level_rule & months_rule & xp_rule
+
+ # Should be cached
+ def has_level(self, skill: str, level: int) -> StardewRule:
+ if level <= 0:
+ return True_()
+
+ if self.options.skill_progression == options.SkillProgression.option_progressive:
+ return self.logic.received(f"{skill} Level", level)
+
+ return self.logic.skill.can_earn_level(skill, level)
+
+ @cache_self1
+ def has_farming_level(self, level: int) -> StardewRule:
+ return self.logic.skill.has_level(Skill.farming, level)
+
+ # Should be cached
+ def has_total_level(self, level: int, allow_modded_skills: bool = False) -> StardewRule:
+ if level <= 0:
+ return True_()
+
+ if self.options.skill_progression == options.SkillProgression.option_progressive:
+ skills_items = ("Farming Level", "Mining Level", "Foraging Level", "Fishing Level", "Combat Level")
+ if allow_modded_skills:
+ skills_items += get_mod_skill_levels(self.options.mods)
+ return self.logic.received_n(*skills_items, count=level)
+
+ months_with_4_skills = max(1, (level // 4) - 1)
+ months_with_5_skills = max(1, (level // 5) - 1)
+ rule_with_fishing = self.logic.time.has_lived_months(months_with_5_skills) & self.logic.skill.can_get_fishing_xp
+ if level > 40:
+ return rule_with_fishing
+ return self.logic.time.has_lived_months(months_with_4_skills) | rule_with_fishing
+
+ @cached_property
+ def can_get_farming_xp(self) -> StardewRule:
+ crop_rules = []
+ for crop in all_crops:
+ crop_rules.append(self.logic.crop.can_grow(crop))
+ return Or(*crop_rules)
+
+ @cached_property
+ def can_get_foraging_xp(self) -> StardewRule:
+ tool_rule = self.logic.tool.has_tool(Tool.axe)
+ tree_rule = self.logic.region.can_reach(Region.forest) & self.logic.season.has_any_not_winter()
+ stump_rule = self.logic.region.can_reach(Region.secret_woods) & self.logic.tool.has_tool(Tool.axe, ToolMaterial.copper)
+ return tool_rule & (tree_rule | stump_rule)
+
+ @cached_property
+ def can_get_mining_xp(self) -> StardewRule:
+ tool_rule = self.logic.tool.has_tool(Tool.pickaxe)
+ stone_rule = self.logic.region.can_reach_any((Region.mines_floor_5, Region.quarry, Region.skull_cavern_25, Region.volcano_floor_5))
+ return tool_rule & stone_rule
+
+ @cached_property
+ def can_get_combat_xp(self) -> StardewRule:
+ tool_rule = self.logic.combat.has_any_weapon()
+ enemy_rule = self.logic.region.can_reach_any((Region.mines_floor_5, Region.skull_cavern_25, Region.volcano_floor_5))
+ return tool_rule & enemy_rule
+
+ @cached_property
+ def can_get_fishing_xp(self) -> StardewRule:
+ if self.options.skill_progression == options.SkillProgression.option_progressive:
+ return self.logic.skill.can_fish() | self.logic.skill.can_crab_pot
+
+ return self.logic.skill.can_fish()
+
+ # Should be cached
+ def can_fish(self, regions: Union[str, Tuple[str, ...]] = None, difficulty: int = 0) -> StardewRule:
+ if isinstance(regions, str):
+ regions = regions,
+
+ if regions is None or len(regions) == 0:
+ regions = fishing_regions
+
+ skill_required = min(10, max(0, int((difficulty / 10) - 1)))
+ if difficulty <= 40:
+ skill_required = 0
+
+ skill_rule = self.logic.skill.has_level(Skill.fishing, skill_required)
+ region_rule = self.logic.region.can_reach_any(regions)
+ # Training rod only works with fish < 50. Fiberglass does not help you to catch higher difficulty fish, so it's skipped in logic.
+ number_fishing_rod_required = 1 if difficulty < 50 else (2 if difficulty < 80 else 4)
+ return self.logic.tool.has_fishing_rod(number_fishing_rod_required) & skill_rule & region_rule
+
+ @cache_self1
+ def can_crab_pot_at(self, region: str) -> StardewRule:
+ return self.logic.skill.can_crab_pot & self.logic.region.can_reach(region)
+
+ @cached_property
+ def can_crab_pot(self) -> StardewRule:
+ crab_pot_rule = self.logic.has(Fishing.bait)
+ if self.options.skill_progression == options.SkillProgression.option_progressive:
+ crab_pot_rule = crab_pot_rule & self.logic.has(Machine.crab_pot)
+ else:
+ crab_pot_rule = crab_pot_rule & self.logic.skill.can_get_fishing_xp
+
+ water_region_rules = self.logic.region.can_reach_any(fishing_regions)
+ return crab_pot_rule & water_region_rules
+
+ def can_forage_quality(self, quality: str) -> StardewRule:
+ if quality == ForageQuality.basic:
+ return True_()
+ if quality == ForageQuality.silver:
+ return self.has_level(Skill.foraging, 5)
+ if quality == ForageQuality.gold:
+ return self.has_level(Skill.foraging, 9)
+ return False_()
diff --git a/worlds/stardew_valley/logic/special_order_logic.py b/worlds/stardew_valley/logic/special_order_logic.py
new file mode 100644
index 000000000000..e0b1a7e2fb27
--- /dev/null
+++ b/worlds/stardew_valley/logic/special_order_logic.py
@@ -0,0 +1,111 @@
+from typing import Dict, Union
+
+from .ability_logic import AbilityLogicMixin
+from .arcade_logic import ArcadeLogicMixin
+from .artisan_logic import ArtisanLogicMixin
+from .base_logic import BaseLogicMixin, BaseLogic
+from .buff_logic import BuffLogicMixin
+from .cooking_logic import CookingLogicMixin
+from .has_logic import HasLogicMixin
+from .mine_logic import MineLogicMixin
+from .money_logic import MoneyLogicMixin
+from .monster_logic import MonsterLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from .relationship_logic import RelationshipLogicMixin
+from .season_logic import SeasonLogicMixin
+from .shipping_logic import ShippingLogicMixin
+from .skill_logic import SkillLogicMixin
+from .time_logic import TimeLogicMixin
+from .tool_logic import ToolLogicMixin
+from ..stardew_rule import StardewRule, Has
+from ..strings.animal_product_names import AnimalProduct
+from ..strings.ap_names.event_names import Event
+from ..strings.ap_names.transport_names import Transportation
+from ..strings.artisan_good_names import ArtisanGood
+from ..strings.crop_names import Vegetable, Fruit
+from ..strings.fertilizer_names import Fertilizer
+from ..strings.fish_names import Fish
+from ..strings.forageable_names import Forageable
+from ..strings.machine_names import Machine
+from ..strings.material_names import Material
+from ..strings.metal_names import Mineral
+from ..strings.monster_drop_names import Loot
+from ..strings.monster_names import Monster
+from ..strings.region_names import Region
+from ..strings.season_names import Season
+from ..strings.special_order_names import SpecialOrder
+from ..strings.tool_names import Tool
+from ..strings.villager_names import NPC
+
+
+class SpecialOrderLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.special_order = SpecialOrderLogic(*args, **kwargs)
+
+
+class SpecialOrderLogic(BaseLogic[Union[HasLogicMixin, ReceivedLogicMixin, RegionLogicMixin, SeasonLogicMixin, TimeLogicMixin, MoneyLogicMixin,
+ShippingLogicMixin, ArcadeLogicMixin, ArtisanLogicMixin, RelationshipLogicMixin, ToolLogicMixin, SkillLogicMixin,
+MineLogicMixin, CookingLogicMixin, BuffLogicMixin,
+AbilityLogicMixin, SpecialOrderLogicMixin, MonsterLogicMixin]]):
+
+ def initialize_rules(self):
+ self.update_rules({
+ SpecialOrder.island_ingredients: self.logic.relationship.can_meet(NPC.caroline) & self.logic.special_order.has_island_transport() &
+ self.logic.ability.can_farm_perfectly() & self.logic.shipping.can_ship(Vegetable.taro_root) &
+ self.logic.shipping.can_ship(Fruit.pineapple) & self.logic.shipping.can_ship(Forageable.ginger),
+ SpecialOrder.cave_patrol: self.logic.relationship.can_meet(NPC.clint),
+ SpecialOrder.aquatic_overpopulation: self.logic.relationship.can_meet(NPC.demetrius) & self.logic.ability.can_fish_perfectly(),
+ SpecialOrder.biome_balance: self.logic.relationship.can_meet(NPC.demetrius) & self.logic.ability.can_fish_perfectly(),
+ SpecialOrder.rock_rejuivenation: (self.logic.relationship.has_hearts(NPC.emily, 4) &
+ self.logic.has_all(Mineral.ruby, Mineral.topaz, Mineral.emerald, Mineral.jade, Mineral.amethyst,
+ ArtisanGood.cloth)),
+ SpecialOrder.gifts_for_george: self.logic.season.has(Season.spring) & self.logic.has(Forageable.leek),
+ SpecialOrder.fragments_of_the_past: self.logic.monster.can_kill(Monster.skeleton),
+ SpecialOrder.gus_famous_omelet: self.logic.has(AnimalProduct.any_egg),
+ SpecialOrder.crop_order: self.logic.ability.can_farm_perfectly() & self.logic.received(Event.can_ship_items),
+ SpecialOrder.community_cleanup: self.logic.skill.can_crab_pot,
+ SpecialOrder.the_strong_stuff: self.logic.artisan.can_keg(Vegetable.potato),
+ SpecialOrder.pierres_prime_produce: self.logic.ability.can_farm_perfectly(),
+ SpecialOrder.robins_project: self.logic.relationship.can_meet(NPC.robin) & self.logic.ability.can_chop_perfectly() &
+ self.logic.has(Material.hardwood),
+ SpecialOrder.robins_resource_rush: self.logic.relationship.can_meet(NPC.robin) & self.logic.ability.can_chop_perfectly() &
+ self.logic.has(Fertilizer.tree) & self.logic.ability.can_mine_perfectly(),
+ SpecialOrder.juicy_bugs_wanted: self.logic.has(Loot.bug_meat),
+ SpecialOrder.tropical_fish: self.logic.relationship.can_meet(NPC.willy) & self.logic.received("Island Resort") &
+ self.logic.special_order.has_island_transport() &
+ self.logic.has(Fish.stingray) & self.logic.has(Fish.blue_discus) & self.logic.has(Fish.lionfish),
+ SpecialOrder.a_curious_substance: self.logic.region.can_reach(Region.wizard_tower),
+ SpecialOrder.prismatic_jelly: self.logic.region.can_reach(Region.wizard_tower),
+ SpecialOrder.qis_crop: self.logic.ability.can_farm_perfectly() & self.logic.region.can_reach(Region.greenhouse) &
+ self.logic.region.can_reach(Region.island_west) & self.logic.skill.has_total_level(50) &
+ self.logic.has(Machine.seed_maker) & self.logic.received(Event.can_ship_items),
+ SpecialOrder.lets_play_a_game: self.logic.arcade.has_junimo_kart_max_level(),
+ SpecialOrder.four_precious_stones: self.logic.time.has_lived_max_months & self.logic.has("Prismatic Shard") &
+ self.logic.ability.can_mine_perfectly_in_the_skull_cavern(),
+ SpecialOrder.qis_hungry_challenge: self.logic.ability.can_mine_perfectly_in_the_skull_cavern() & self.logic.buff.has_max_buffs(),
+ SpecialOrder.qis_cuisine: self.logic.cooking.can_cook() & self.logic.received(Event.can_ship_items) &
+ (self.logic.money.can_spend_at(Region.saloon, 205000) | self.logic.money.can_spend_at(Region.pierre_store, 170000)),
+ SpecialOrder.qis_kindness: self.logic.relationship.can_give_loved_gifts_to_everyone(),
+ SpecialOrder.extended_family: self.logic.ability.can_fish_perfectly() & self.logic.has(Fish.angler) & self.logic.has(Fish.glacierfish) &
+ self.logic.has(Fish.crimsonfish) & self.logic.has(Fish.mutant_carp) & self.logic.has(Fish.legend),
+ SpecialOrder.danger_in_the_deep: self.logic.ability.can_mine_perfectly() & self.logic.mine.has_mine_elevator_to_floor(120),
+ SpecialOrder.skull_cavern_invasion: self.logic.ability.can_mine_perfectly_in_the_skull_cavern() & self.logic.buff.has_max_buffs(),
+ SpecialOrder.qis_prismatic_grange: self.logic.has(Loot.bug_meat) & # 100 Bug Meat
+ self.logic.money.can_spend_at(Region.saloon, 24000) & # 100 Spaghetti
+ self.logic.money.can_spend_at(Region.blacksmith, 15000) & # 100 Copper Ore
+ self.logic.money.can_spend_at(Region.ranch, 5000) & # 100 Hay
+ self.logic.money.can_spend_at(Region.saloon, 22000) & # 100 Salads
+ self.logic.money.can_spend_at(Region.saloon, 7500) & # 100 Joja Cola
+ self.logic.money.can_spend(80000), # I need this extra rule because money rules aren't additive...
+ })
+
+ def update_rules(self, new_rules: Dict[str, StardewRule]):
+ self.registry.special_order_rules.update(new_rules)
+
+ def can_complete_special_order(self, special_order: str) -> StardewRule:
+ return Has(special_order, self.registry.special_order_rules)
+
+ def has_island_transport(self) -> StardewRule:
+ return self.logic.received(Transportation.island_obelisk) | self.logic.received(Transportation.boat_repair)
diff --git a/worlds/stardew_valley/logic/time_logic.py b/worlds/stardew_valley/logic/time_logic.py
new file mode 100644
index 000000000000..9dcebfe82a4f
--- /dev/null
+++ b/worlds/stardew_valley/logic/time_logic.py
@@ -0,0 +1,38 @@
+from functools import cached_property
+from typing import Union
+
+from Utils import cache_self1
+from .base_logic import BaseLogic, BaseLogicMixin
+from .received_logic import ReceivedLogicMixin
+from ..stardew_rule import StardewRule, HasProgressionPercent, True_
+
+MAX_MONTHS = 12
+MONTH_COEFFICIENT = 24 // MAX_MONTHS
+
+
+class TimeLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.time = TimeLogic(*args, **kwargs)
+
+
+class TimeLogic(BaseLogic[Union[TimeLogicMixin, ReceivedLogicMixin]]):
+
+ @cache_self1
+ def has_lived_months(self, number: int) -> StardewRule:
+ if number <= 0:
+ return True_()
+ number = min(number, MAX_MONTHS)
+ return HasProgressionPercent(self.player, number * MONTH_COEFFICIENT)
+
+ @cached_property
+ def has_lived_max_months(self) -> StardewRule:
+ return self.logic.time.has_lived_months(MAX_MONTHS)
+
+ @cached_property
+ def has_year_two(self) -> StardewRule:
+ return self.logic.time.has_lived_months(4)
+
+ @cached_property
+ def has_year_three(self) -> StardewRule:
+ return self.logic.time.has_lived_months(8)
diff --git a/worlds/stardew_valley/logic/tool_logic.py b/worlds/stardew_valley/logic/tool_logic.py
new file mode 100644
index 000000000000..1b1dc2a52120
--- /dev/null
+++ b/worlds/stardew_valley/logic/tool_logic.py
@@ -0,0 +1,89 @@
+from typing import Union, Iterable
+
+from Utils import cache_self1
+from .base_logic import BaseLogicMixin, BaseLogic
+from .has_logic import HasLogicMixin
+from .money_logic import MoneyLogicMixin
+from .received_logic import ReceivedLogicMixin
+from .region_logic import RegionLogicMixin
+from .season_logic import SeasonLogicMixin
+from ..mods.logic.magic_logic import MagicLogicMixin
+from ..options import ToolProgression
+from ..stardew_rule import StardewRule, True_, False_
+from ..strings.ap_names.skill_level_names import ModSkillLevel
+from ..strings.region_names import Region
+from ..strings.spells import MagicSpell
+from ..strings.tool_names import ToolMaterial, Tool
+
+fishing_rod_prices = {
+ 3: 1800,
+ 4: 7500,
+}
+
+tool_materials = {
+ ToolMaterial.copper: 1,
+ ToolMaterial.iron: 2,
+ ToolMaterial.gold: 3,
+ ToolMaterial.iridium: 4
+}
+
+tool_upgrade_prices = {
+ ToolMaterial.copper: 2000,
+ ToolMaterial.iron: 5000,
+ ToolMaterial.gold: 10000,
+ ToolMaterial.iridium: 25000
+}
+
+
+class ToolLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.tool = ToolLogic(*args, **kwargs)
+
+
+class ToolLogic(BaseLogic[Union[ToolLogicMixin, HasLogicMixin, ReceivedLogicMixin, RegionLogicMixin, SeasonLogicMixin, MoneyLogicMixin, MagicLogicMixin]]):
+ # Should be cached
+ def has_tool(self, tool: str, material: str = ToolMaterial.basic) -> StardewRule:
+ assert tool != Tool.fishing_rod, "Use `has_fishing_rod` instead of `has_tool`."
+
+ if material == ToolMaterial.basic or tool == Tool.scythe:
+ return True_()
+
+ if self.options.tool_progression & ToolProgression.option_progressive:
+ return self.logic.received(f"Progressive {tool}", tool_materials[material])
+
+ return self.logic.has(f"{material} Bar") & self.logic.money.can_spend_at(Region.blacksmith, tool_upgrade_prices[material])
+
+ def can_use_tool_at(self, tool: str, material: str, region: str) -> StardewRule:
+ return self.has_tool(tool, material) & self.logic.region.can_reach(region)
+
+ @cache_self1
+ def has_fishing_rod(self, level: int) -> StardewRule:
+ assert 1 <= level <= 4, "Fishing rod 0 isn't real, it can't hurt you. Training is 1, Bamboo is 2, Fiberglass is 3 and Iridium is 4."
+
+ if self.options.tool_progression & ToolProgression.option_progressive:
+ return self.logic.received(f"Progressive {Tool.fishing_rod}", level)
+
+ if level <= 2:
+ # We assume you always have access to the Bamboo pole, because mod side there is a builtin way to get it back.
+ return self.logic.region.can_reach(Region.beach)
+
+ return self.logic.money.can_spend_at(Region.fish_shop, fishing_rod_prices[level])
+
+ # Should be cached
+ def can_forage(self, season: Union[str, Iterable[str]], region: str = Region.forest, need_hoe: bool = False) -> StardewRule:
+ season_rule = False_()
+ if isinstance(season, str):
+ season_rule = self.logic.season.has(season)
+ elif isinstance(season, Iterable):
+ season_rule = self.logic.season.has_any(season)
+ region_rule = self.logic.region.can_reach(region)
+ if need_hoe:
+ return season_rule & region_rule & self.logic.tool.has_tool(Tool.hoe)
+ return season_rule & region_rule
+
+ @cache_self1
+ def can_water(self, level: int) -> StardewRule:
+ tool_rule = self.logic.tool.has_tool(Tool.watering_can, ToolMaterial.tiers[level])
+ spell_rule = self.logic.received(MagicSpell.water) & self.logic.magic.can_use_altar() & self.logic.received(ModSkillLevel.magic_level, level)
+ return tool_rule | spell_rule
diff --git a/worlds/stardew_valley/logic/traveling_merchant_logic.py b/worlds/stardew_valley/logic/traveling_merchant_logic.py
new file mode 100644
index 000000000000..4123ded5bf24
--- /dev/null
+++ b/worlds/stardew_valley/logic/traveling_merchant_logic.py
@@ -0,0 +1,26 @@
+from typing import Union
+
+from .base_logic import BaseLogic, BaseLogicMixin
+from .received_logic import ReceivedLogicMixin
+from ..stardew_rule import True_
+from ..strings.calendar_names import Weekday
+
+
+class TravelingMerchantLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.traveling_merchant = TravelingMerchantLogic(*args, **kwargs)
+
+
+class TravelingMerchantLogic(BaseLogic[Union[TravelingMerchantLogicMixin, ReceivedLogicMixin]]):
+
+ def has_days(self, number_days: int = 1):
+ if number_days <= 0:
+ return True_()
+
+ traveling_merchant_days = tuple(f"Traveling Merchant: {day}" for day in Weekday.all_days)
+ if number_days == 1:
+ return self.logic.received_any(*traveling_merchant_days)
+
+ tier = min(7, max(1, number_days))
+ return self.logic.received_n(*traveling_merchant_days, count=tier)
diff --git a/worlds/stardew_valley/logic/wallet_logic.py b/worlds/stardew_valley/logic/wallet_logic.py
new file mode 100644
index 000000000000..3a6d12640028
--- /dev/null
+++ b/worlds/stardew_valley/logic/wallet_logic.py
@@ -0,0 +1,19 @@
+from .base_logic import BaseLogic, BaseLogicMixin
+from .received_logic import ReceivedLogicMixin
+from ..stardew_rule import StardewRule
+from ..strings.wallet_item_names import Wallet
+
+
+class WalletLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.wallet = WalletLogic(*args, **kwargs)
+
+
+class WalletLogic(BaseLogic[ReceivedLogicMixin]):
+
+ def can_speak_dwarf(self) -> StardewRule:
+ return self.logic.received(Wallet.dwarvish_translation_guide)
+
+ def has_rusty_key(self) -> StardewRule:
+ return self.logic.received(Wallet.rusty_key)
diff --git a/worlds/stardew_valley/mods/logic/buildings.py b/worlds/stardew_valley/mods/logic/buildings.py
deleted file mode 100644
index 5ca4bf32d785..000000000000
--- a/worlds/stardew_valley/mods/logic/buildings.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from typing import Union
-
-from ...strings.artisan_good_names import ArtisanGood
-from ...strings.building_names import ModBuilding
-from ..mod_data import ModNames
-from ...strings.metal_names import MetalBar
-from ...strings.region_names import Region
-
-
-def get_modded_building_rules(vanilla_logic, active_mods):
- buildings = {}
- if ModNames.tractor in active_mods:
- buildings.update({
- ModBuilding.tractor_garage: vanilla_logic.can_spend_money_at(Region.carpenter, 150000) & vanilla_logic.has(MetalBar.iron) &
- vanilla_logic.has(MetalBar.iridium) & vanilla_logic.has(ArtisanGood.battery_pack)})
- return buildings
diff --git a/worlds/stardew_valley/mods/logic/buildings_logic.py b/worlds/stardew_valley/mods/logic/buildings_logic.py
new file mode 100644
index 000000000000..388204a47614
--- /dev/null
+++ b/worlds/stardew_valley/mods/logic/buildings_logic.py
@@ -0,0 +1,28 @@
+from typing import Dict, Union
+
+from ..mod_data import ModNames
+from ...logic.base_logic import BaseLogicMixin, BaseLogic
+from ...logic.has_logic import HasLogicMixin
+from ...logic.money_logic import MoneyLogicMixin
+from ...stardew_rule import StardewRule
+from ...strings.artisan_good_names import ArtisanGood
+from ...strings.building_names import ModBuilding
+from ...strings.metal_names import MetalBar
+from ...strings.region_names import Region
+
+
+class ModBuildingLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.building = ModBuildingLogic(*args, **kwargs)
+
+
+class ModBuildingLogic(BaseLogic[Union[MoneyLogicMixin, HasLogicMixin]]):
+
+ def get_modded_building_rules(self) -> Dict[str, StardewRule]:
+ buildings = dict()
+ if ModNames.tractor in self.options.mods:
+ tractor_rule = (self.logic.money.can_spend_at(Region.carpenter, 150000) &
+ self.logic.has_all(MetalBar.iron, MetalBar.iridium, ArtisanGood.battery_pack))
+ buildings.update({ModBuilding.tractor_garage: tractor_rule})
+ return buildings
diff --git a/worlds/stardew_valley/mods/logic/deepwoods.py b/worlds/stardew_valley/mods/logic/deepwoods.py
deleted file mode 100644
index 2aa90e5b76b6..000000000000
--- a/worlds/stardew_valley/mods/logic/deepwoods.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from ...strings.craftable_names import Craftable
-from ...strings.performance_names import Performance
-from ...strings.skill_names import Skill
-from ...strings.tool_names import Tool, ToolMaterial
-from ...strings.ap_names.transport_names import ModTransportation
-from ...stardew_rule import StardewRule, True_, And
-from ... import options
-
-
-def can_reach_woods_depth(vanilla_logic, depth: int) -> StardewRule:
- tier = int(depth / 25) + 1
- rules = []
- if depth > 10:
- rules.append(vanilla_logic.has(Craftable.bomb) | vanilla_logic.has_tool(Tool.axe, ToolMaterial.iridium))
- if depth > 30:
- rules.append(vanilla_logic.received(ModTransportation.woods_obelisk))
- if depth > 50:
- rules.append(vanilla_logic.can_do_combat_at_level(Performance.great) & vanilla_logic.can_cook() &
- vanilla_logic.received(ModTransportation.woods_obelisk))
- if vanilla_logic.options.skill_progression == options.SkillProgression.option_progressive:
- combat_tier = min(10, max(0, tier + 5))
- rules.append(vanilla_logic.has_skill_level(Skill.combat, combat_tier))
- return And(rules)
-
-
-def has_woods_rune_to_depth(vanilla_logic, floor: int) -> StardewRule:
- if vanilla_logic.options.elevator_progression == options.ElevatorProgression.option_vanilla:
- return True_()
- return vanilla_logic.received("Progressive Woods Obelisk Sigils", count=int(floor / 10))
-
-
-def can_chop_to_depth(vanilla_logic, floor: int) -> StardewRule:
- previous_elevator = max(floor - 10, 0)
- return (has_woods_rune_to_depth(vanilla_logic, previous_elevator) &
- can_reach_woods_depth(vanilla_logic, previous_elevator))
diff --git a/worlds/stardew_valley/mods/logic/deepwoods_logic.py b/worlds/stardew_valley/mods/logic/deepwoods_logic.py
new file mode 100644
index 000000000000..7699521542a7
--- /dev/null
+++ b/worlds/stardew_valley/mods/logic/deepwoods_logic.py
@@ -0,0 +1,73 @@
+from typing import Union
+
+from ... import options
+from ...logic.base_logic import BaseLogicMixin, BaseLogic
+from ...logic.combat_logic import CombatLogicMixin
+from ...logic.cooking_logic import CookingLogicMixin
+from ...logic.has_logic import HasLogicMixin
+from ...logic.received_logic import ReceivedLogicMixin
+from ...logic.skill_logic import SkillLogicMixin
+from ...logic.tool_logic import ToolLogicMixin
+from ...mods.mod_data import ModNames
+from ...options import ElevatorProgression
+from ...stardew_rule import StardewRule, True_, And, true_
+from ...strings.ap_names.mods.mod_items import DeepWoodsItem, SkillLevel
+from ...strings.ap_names.transport_names import ModTransportation
+from ...strings.craftable_names import Bomb
+from ...strings.food_names import Meal
+from ...strings.performance_names import Performance
+from ...strings.skill_names import Skill
+from ...strings.tool_names import Tool, ToolMaterial
+
+
+class DeepWoodsLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.deepwoods = DeepWoodsLogic(*args, **kwargs)
+
+
+class DeepWoodsLogic(BaseLogic[Union[SkillLogicMixin, ReceivedLogicMixin, HasLogicMixin, CombatLogicMixin, ToolLogicMixin, SkillLogicMixin,
+CookingLogicMixin]]):
+
+ def can_reach_woods_depth(self, depth: int) -> StardewRule:
+ # Assuming you can always do the 10 first floor
+ if depth <= 10:
+ return true_
+
+ rules = []
+
+ if depth > 10:
+ rules.append(self.logic.has(Bomb.bomb) | self.logic.tool.has_tool(Tool.axe, ToolMaterial.iridium))
+ if depth > 30:
+ rules.append(self.logic.received(ModTransportation.woods_obelisk))
+ if depth > 50:
+ rules.append(self.logic.combat.can_fight_at_level(Performance.great) & self.logic.cooking.can_cook() &
+ self.logic.received(ModTransportation.woods_obelisk))
+
+ tier = int(depth / 25) + 1
+ if self.options.skill_progression == options.SkillProgression.option_progressive:
+ combat_tier = min(10, max(0, tier + 5))
+ rules.append(self.logic.skill.has_level(Skill.combat, combat_tier))
+
+ return And(*rules)
+
+ def has_woods_rune_to_depth(self, floor: int) -> StardewRule:
+ if self.options.elevator_progression == ElevatorProgression.option_vanilla:
+ return True_()
+ return self.logic.received(DeepWoodsItem.obelisk_sigil, int(floor / 10))
+
+ def can_chop_to_depth(self, floor: int) -> StardewRule:
+ previous_elevator = max(floor - 10, 0)
+ return (self.has_woods_rune_to_depth(previous_elevator) &
+ self.can_reach_woods_depth(previous_elevator))
+
+ def can_pull_sword(self) -> StardewRule:
+ rules = [self.logic.received(DeepWoodsItem.pendant_depths) & self.logic.received(DeepWoodsItem.pendant_community) &
+ self.logic.received(DeepWoodsItem.pendant_elder),
+ self.logic.skill.has_total_level(40)]
+ if ModNames.luck_skill in self.options.mods:
+ rules.append(self.logic.received(SkillLevel.luck, 7))
+ else:
+ rules.append(
+ self.logic.has(Meal.magic_rock_candy)) # You need more luck than this, but it'll push the logic down a ways; you can get the rest there.
+ return And(*rules)
diff --git a/worlds/stardew_valley/mods/logic/elevator_logic.py b/worlds/stardew_valley/mods/logic/elevator_logic.py
new file mode 100644
index 000000000000..f1d12bcb1c37
--- /dev/null
+++ b/worlds/stardew_valley/mods/logic/elevator_logic.py
@@ -0,0 +1,18 @@
+from ...logic.base_logic import BaseLogicMixin, BaseLogic
+from ...logic.received_logic import ReceivedLogicMixin
+from ...mods.mod_data import ModNames
+from ...options import ElevatorProgression
+from ...stardew_rule import StardewRule, True_
+
+
+class ModElevatorLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.elevator = ModElevatorLogic(*args, **kwargs)
+
+
+class ModElevatorLogic(BaseLogic[ReceivedLogicMixin]):
+ def has_skull_cavern_elevator_to_floor(self, floor: int) -> StardewRule:
+ if self.options.elevator_progression != ElevatorProgression.option_vanilla and ModNames.skull_cavern_elevator in self.options.mods:
+ return self.logic.received("Progressive Skull Cavern Elevator", floor // 25)
+ return True_()
diff --git a/worlds/stardew_valley/mods/logic/item_logic.py b/worlds/stardew_valley/mods/logic/item_logic.py
new file mode 100644
index 000000000000..8f5e676d8c2d
--- /dev/null
+++ b/worlds/stardew_valley/mods/logic/item_logic.py
@@ -0,0 +1,289 @@
+from typing import Dict, Union
+
+from ..mod_data import ModNames
+from ... import options
+from ...data.craftable_data import all_crafting_recipes_by_name
+from ...logic.base_logic import BaseLogicMixin, BaseLogic
+from ...logic.combat_logic import CombatLogicMixin
+from ...logic.cooking_logic import CookingLogicMixin
+from ...logic.crafting_logic import CraftingLogicMixin
+from ...logic.crop_logic import CropLogicMixin
+from ...logic.fishing_logic import FishingLogicMixin
+from ...logic.has_logic import HasLogicMixin
+from ...logic.money_logic import MoneyLogicMixin
+from ...logic.museum_logic import MuseumLogicMixin
+from ...logic.quest_logic import QuestLogicMixin
+from ...logic.received_logic import ReceivedLogicMixin
+from ...logic.region_logic import RegionLogicMixin
+from ...logic.relationship_logic import RelationshipLogicMixin
+from ...logic.season_logic import SeasonLogicMixin
+from ...logic.skill_logic import SkillLogicMixin
+from ...logic.time_logic import TimeLogicMixin
+from ...logic.tool_logic import ToolLogicMixin
+from ...options import Cropsanity
+from ...stardew_rule import StardewRule, True_
+from ...strings.artisan_good_names import ModArtisanGood
+from ...strings.craftable_names import ModCraftable, ModEdible, ModMachine
+from ...strings.crop_names import SVEVegetable, SVEFruit, DistantLandsCrop, Fruit
+from ...strings.fish_names import WaterItem
+from ...strings.flower_names import Flower
+from ...strings.food_names import SVEMeal, SVEBeverage
+from ...strings.forageable_names import SVEForage, DistantLandsForageable, Forageable
+from ...strings.gift_names import SVEGift
+from ...strings.ingredient_names import Ingredient
+from ...strings.material_names import Material
+from ...strings.metal_names import all_fossils, all_artifacts, Ore, ModFossil
+from ...strings.monster_drop_names import ModLoot, Loot
+from ...strings.performance_names import Performance
+from ...strings.quest_names import ModQuest
+from ...strings.region_names import Region, SVERegion, DeepWoodsRegion, BoardingHouseRegion
+from ...strings.season_names import Season
+from ...strings.seed_names import SVESeed, DistantLandsSeed
+from ...strings.skill_names import Skill
+from ...strings.tool_names import Tool, ToolMaterial
+from ...strings.villager_names import ModNPC
+
+display_types = [ModCraftable.wooden_display, ModCraftable.hardwood_display]
+display_items = all_artifacts + all_fossils
+
+
+class ModItemLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.item = ModItemLogic(*args, **kwargs)
+
+
+class ModItemLogic(BaseLogic[Union[CombatLogicMixin, ReceivedLogicMixin, CropLogicMixin, CookingLogicMixin, FishingLogicMixin, HasLogicMixin, MoneyLogicMixin,
+RegionLogicMixin, SeasonLogicMixin, RelationshipLogicMixin, MuseumLogicMixin, ToolLogicMixin, CraftingLogicMixin, SkillLogicMixin, TimeLogicMixin, QuestLogicMixin]]):
+
+ def get_modded_item_rules(self) -> Dict[str, StardewRule]:
+ items = dict()
+ if ModNames.sve in self.options.mods:
+ items.update(self.get_sve_item_rules())
+ if ModNames.archaeology in self.options.mods:
+ items.update(self.get_archaeology_item_rules())
+ if ModNames.distant_lands in self.options.mods:
+ items.update(self.get_distant_lands_item_rules())
+ if ModNames.boarding_house in self.options.mods:
+ items.update(self.get_boarding_house_item_rules())
+ return items
+
+ def modify_vanilla_item_rules_with_mod_additions(self, item_rule: Dict[str, StardewRule]):
+ if ModNames.sve in self.options.mods:
+ item_rule.update(self.get_modified_item_rules_for_sve(item_rule))
+ if ModNames.deepwoods in self.options.mods:
+ item_rule.update(self.get_modified_item_rules_for_deep_woods(item_rule))
+ return item_rule
+
+ def get_sve_item_rules(self):
+ return {SVEGift.aged_blue_moon_wine: self.logic.money.can_spend_at(SVERegion.sophias_house, 28000),
+ SVEGift.blue_moon_wine: self.logic.money.can_spend_at(SVERegion.sophias_house, 3000),
+ SVESeed.fungus_seed: self.logic.region.can_reach(SVERegion.highlands_cavern) & self.logic.combat.has_good_weapon,
+ ModLoot.green_mushroom: self.logic.region.can_reach(SVERegion.highlands_outside) &
+ self.logic.tool.has_tool(Tool.axe, ToolMaterial.iron) & self.logic.season.has_any_not_winter(),
+ SVEFruit.monster_fruit: self.logic.season.has(Season.summer) & self.logic.has(SVESeed.stalk_seed),
+ SVEVegetable.monster_mushroom: self.logic.season.has(Season.fall) & self.logic.has(SVESeed.fungus_seed),
+ SVEForage.ornate_treasure_chest: self.logic.region.can_reach(SVERegion.highlands_outside) & self.logic.combat.has_galaxy_weapon &
+ self.logic.tool.has_tool(Tool.axe, ToolMaterial.iron),
+ SVEFruit.slime_berry: self.logic.season.has(Season.spring) & self.logic.has(SVESeed.slime_seed),
+ SVESeed.slime_seed: self.logic.region.can_reach(SVERegion.highlands_outside) & self.logic.combat.has_good_weapon,
+ SVESeed.stalk_seed: self.logic.region.can_reach(SVERegion.highlands_outside) & self.logic.combat.has_good_weapon,
+ SVEForage.swirl_stone: self.logic.region.can_reach(SVERegion.crimson_badlands) & self.logic.combat.has_great_weapon,
+ SVEVegetable.void_root: self.logic.season.has(Season.winter) & self.logic.has(SVESeed.void_seed),
+ SVESeed.void_seed: self.logic.region.can_reach(SVERegion.highlands_cavern) & self.logic.combat.has_good_weapon,
+ SVEForage.void_soul: self.logic.region.can_reach(
+ SVERegion.crimson_badlands) & self.logic.combat.has_good_weapon & self.logic.cooking.can_cook(),
+ SVEForage.winter_star_rose: self.logic.region.can_reach(SVERegion.summit) & self.logic.season.has(Season.winter),
+ SVEForage.bearberrys: self.logic.region.can_reach(Region.secret_woods) & self.logic.season.has(Season.winter),
+ SVEForage.poison_mushroom: self.logic.region.can_reach(Region.secret_woods) & self.logic.season.has_any([Season.summer, Season.fall]),
+ SVEForage.red_baneberry: self.logic.region.can_reach(Region.secret_woods) & self.logic.season.has(Season.summer),
+ SVEForage.ferngill_primrose: self.logic.region.can_reach(SVERegion.summit) & self.logic.season.has(Season.spring),
+ SVEForage.goldenrod: self.logic.region.can_reach(SVERegion.summit) & (
+ self.logic.season.has(Season.summer) | self.logic.season.has(Season.fall)),
+ SVESeed.shrub_seed: self.logic.region.can_reach(Region.secret_woods) & self.logic.tool.has_tool(Tool.hoe, ToolMaterial.basic),
+ SVEFruit.salal_berry: self.logic.crop.can_plant_and_grow_item([Season.spring, Season.summer]) & self.logic.has(SVESeed.shrub_seed),
+ ModEdible.aegis_elixir: self.logic.money.can_spend_at(SVERegion.galmoran_outpost, 28000),
+ ModEdible.lightning_elixir: self.logic.money.can_spend_at(SVERegion.galmoran_outpost, 12000),
+ ModEdible.barbarian_elixir: self.logic.money.can_spend_at(SVERegion.galmoran_outpost, 22000),
+ ModEdible.gravity_elixir: self.logic.money.can_spend_at(SVERegion.galmoran_outpost, 4000),
+ SVESeed.ancient_ferns_seed: self.logic.region.can_reach(Region.secret_woods) & self.logic.tool.has_tool(Tool.hoe, ToolMaterial.basic),
+ SVEVegetable.ancient_fiber: self.logic.crop.can_plant_and_grow_item(Season.summer) & self.logic.has(SVESeed.ancient_ferns_seed),
+ SVEForage.big_conch: self.logic.region.can_reach_any((Region.beach, SVERegion.fable_reef)),
+ SVEForage.dewdrop_berry: self.logic.region.can_reach(SVERegion.enchanted_grove),
+ SVEForage.dried_sand_dollar: self.logic.region.can_reach(SVERegion.fable_reef) | (self.logic.region.can_reach(Region.beach) &
+ self.logic.season.has_any([Season.summer, Season.fall])),
+ SVEForage.golden_ocean_flower: self.logic.region.can_reach(SVERegion.fable_reef),
+ SVEMeal.grampleton_orange_chicken: self.logic.money.can_spend_at(Region.saloon, 650) & self.logic.relationship.has_hearts(ModNPC.sophia, 6),
+ ModEdible.hero_elixir: self.logic.money.can_spend_at(SVERegion.isaac_shop, 8000),
+ SVEForage.lucky_four_leaf_clover: self.logic.region.can_reach_any((Region.secret_woods, SVERegion.forest_west)) &
+ self.logic.season.has_any([Season.spring, Season.summer]),
+ SVEForage.mushroom_colony: self.logic.region.can_reach_any((Region.secret_woods, SVERegion.junimo_woods, SVERegion.forest_west)) &
+ self.logic.season.has(Season.fall),
+ SVEForage.rusty_blade: self.logic.region.can_reach(SVERegion.crimson_badlands) & self.logic.combat.has_great_weapon,
+ SVEForage.smelly_rafflesia: self.logic.region.can_reach(Region.secret_woods),
+ SVEBeverage.sports_drink: self.logic.money.can_spend_at(Region.hospital, 750),
+ "Stamina Capsule": self.logic.money.can_spend_at(Region.hospital, 4000),
+ SVEForage.thistle: self.logic.region.can_reach(SVERegion.summit),
+ SVEForage.void_pebble: self.logic.region.can_reach(SVERegion.crimson_badlands) & self.logic.combat.has_great_weapon,
+ ModLoot.void_shard: self.logic.region.can_reach(SVERegion.crimson_badlands) & self.logic.combat.has_galaxy_weapon &
+ self.logic.skill.has_level(Skill.combat, 10) & self.logic.region.can_reach(Region.saloon) & self.logic.time.has_year_three
+ }
+ # @formatter:on
+
+ def get_modified_item_rules_for_sve(self, items: Dict[str, StardewRule]):
+ return {
+ Loot.void_essence: items[Loot.void_essence] | self.logic.region.can_reach(SVERegion.highlands_cavern) | self.logic.region.can_reach(
+ SVERegion.crimson_badlands),
+ Loot.solar_essence: items[Loot.solar_essence] | self.logic.region.can_reach(SVERegion.crimson_badlands),
+ Flower.tulip: items[Flower.tulip] | self.logic.tool.can_forage(Season.spring, SVERegion.sprite_spring),
+ Flower.blue_jazz: items[Flower.blue_jazz] | self.logic.tool.can_forage(Season.spring, SVERegion.sprite_spring),
+ Flower.summer_spangle: items[Flower.summer_spangle] | self.logic.tool.can_forage(Season.summer, SVERegion.sprite_spring),
+ Flower.sunflower: items[Flower.sunflower] | self.logic.tool.can_forage((Season.summer, Season.fall), SVERegion.sprite_spring),
+ Flower.fairy_rose: items[Flower.fairy_rose] | self.logic.tool.can_forage(Season.fall, SVERegion.sprite_spring),
+ Fruit.ancient_fruit: items[Fruit.ancient_fruit] | (
+ self.logic.tool.can_forage((Season.spring, Season.summer, Season.fall), SVERegion.sprite_spring) &
+ self.logic.time.has_year_three) | self.logic.region.can_reach(SVERegion.sprite_spring_cave),
+ Fruit.sweet_gem_berry: items[Fruit.sweet_gem_berry] | (
+ self.logic.tool.can_forage((Season.spring, Season.summer, Season.fall), SVERegion.sprite_spring) &
+ self.logic.time.has_year_three),
+ WaterItem.coral: items[WaterItem.coral] | self.logic.region.can_reach(SVERegion.fable_reef),
+ Forageable.rainbow_shell: items[Forageable.rainbow_shell] | self.logic.region.can_reach(SVERegion.fable_reef),
+ WaterItem.sea_urchin: items[WaterItem.sea_urchin] | self.logic.region.can_reach(SVERegion.fable_reef),
+ Forageable.red_mushroom: items[Forageable.red_mushroom] | self.logic.tool.can_forage((Season.summer, Season.fall), SVERegion.forest_west) |
+ self.logic.region.can_reach(SVERegion.sprite_spring_cave),
+ Forageable.purple_mushroom: items[Forageable.purple_mushroom] | self.logic.tool.can_forage(Season.fall, SVERegion.forest_west) |
+ self.logic.region.can_reach(SVERegion.sprite_spring_cave),
+ Forageable.morel: items[Forageable.morel] | self.logic.tool.can_forage(Season.fall, SVERegion.forest_west),
+ Forageable.chanterelle: items[Forageable.chanterelle] | self.logic.tool.can_forage(Season.fall, SVERegion.forest_west) |
+ self.logic.region.can_reach(SVERegion.sprite_spring_cave),
+ Ore.copper: items[Ore.copper] | (self.logic.tool.can_use_tool_at(Tool.pickaxe, ToolMaterial.basic, SVERegion.highlands_cavern) &
+ self.logic.combat.can_fight_at_level(Performance.great)),
+ Ore.iron: items[Ore.iron] | (self.logic.tool.can_use_tool_at(Tool.pickaxe, ToolMaterial.basic, SVERegion.highlands_cavern) &
+ self.logic.combat.can_fight_at_level(Performance.great)),
+ Ore.iridium: items[Ore.iridium] | (self.logic.tool.can_use_tool_at(Tool.pickaxe, ToolMaterial.basic, SVERegion.crimson_badlands) &
+ self.logic.combat.can_fight_at_level(Performance.maximum)),
+ }
+
+ def get_modified_item_rules_for_deep_woods(self, items: Dict[str, StardewRule]):
+ options_to_update = {
+ Fruit.apple: items[Fruit.apple] | self.logic.region.can_reach(DeepWoodsRegion.floor_10), # Deep enough to have seen such a tree at least once
+ Fruit.apricot: items[Fruit.apricot] | self.logic.region.can_reach(DeepWoodsRegion.floor_10),
+ Fruit.cherry: items[Fruit.cherry] | self.logic.region.can_reach(DeepWoodsRegion.floor_10),
+ Fruit.orange: items[Fruit.orange] | self.logic.region.can_reach(DeepWoodsRegion.floor_10),
+ Fruit.peach: items[Fruit.peach] | self.logic.region.can_reach(DeepWoodsRegion.floor_10),
+ Fruit.pomegranate: items[Fruit.pomegranate] | self.logic.region.can_reach(DeepWoodsRegion.floor_10),
+ Fruit.mango: items[Fruit.mango] | self.logic.region.can_reach(DeepWoodsRegion.floor_10),
+ Flower.tulip: items[Flower.tulip] | self.logic.tool.can_forage(Season.not_winter, DeepWoodsRegion.floor_10),
+ Flower.blue_jazz: items[Flower.blue_jazz] | self.logic.region.can_reach(DeepWoodsRegion.floor_10),
+ Flower.summer_spangle: items[Flower.summer_spangle] | self.logic.tool.can_forage(Season.not_winter, DeepWoodsRegion.floor_10),
+ Flower.poppy: items[Flower.poppy] | self.logic.tool.can_forage(Season.not_winter, DeepWoodsRegion.floor_10),
+ Flower.fairy_rose: items[Flower.fairy_rose] | self.logic.region.can_reach(DeepWoodsRegion.floor_10),
+ Material.hardwood: items[Material.hardwood] | self.logic.tool.can_use_tool_at(Tool.axe, ToolMaterial.iron, DeepWoodsRegion.floor_10),
+ Ingredient.sugar: items[Ingredient.sugar] | self.logic.tool.can_use_tool_at(Tool.axe, ToolMaterial.gold, DeepWoodsRegion.floor_50),
+ # Gingerbread House
+ Ingredient.wheat_flour: items[Ingredient.wheat_flour] | self.logic.tool.can_use_tool_at(Tool.axe, ToolMaterial.gold, DeepWoodsRegion.floor_50),
+ # Gingerbread House
+ }
+
+ if self.options.tool_progression & options.ToolProgression.option_progressive:
+ options_to_update.update({
+ Ore.iridium: items[Ore.iridium] | self.logic.tool.can_use_tool_at(Tool.axe, ToolMaterial.iridium, DeepWoodsRegion.floor_50), # Iridium Tree
+ })
+
+ return options_to_update
+
+ def get_archaeology_item_rules(self):
+ archaeology_item_rules = {}
+ preservation_chamber_rule = self.logic.has(ModMachine.preservation_chamber)
+ hardwood_preservation_chamber_rule = self.logic.has(ModMachine.hardwood_preservation_chamber)
+ for item in display_items:
+ for display_type in display_types:
+ if item == "Trilobite":
+ location_name = f"{display_type}: Trilobite Fossil"
+ else:
+ location_name = f"{display_type}: {item}"
+ display_item_rule = self.logic.crafting.can_craft(all_crafting_recipes_by_name[display_type]) & self.logic.has(item)
+ if "Wooden" in display_type:
+ archaeology_item_rules[location_name] = display_item_rule & preservation_chamber_rule
+ else:
+ archaeology_item_rules[location_name] = display_item_rule & hardwood_preservation_chamber_rule
+ return archaeology_item_rules
+
+ def get_distant_lands_item_rules(self):
+ return {
+ DistantLandsForageable.swamp_herb: self.logic.region.can_reach(Region.witch_swamp),
+ DistantLandsForageable.brown_amanita: self.logic.region.can_reach(Region.witch_swamp),
+ DistantLandsSeed.vile_ancient_fruit: self.logic.quest.can_complete_quest(ModQuest.WitchOrder) | self.logic.quest.can_complete_quest(
+ ModQuest.CorruptedCropsTask),
+ DistantLandsSeed.void_mint: self.logic.quest.can_complete_quest(ModQuest.WitchOrder) | self.logic.quest.can_complete_quest(
+ ModQuest.CorruptedCropsTask),
+ DistantLandsCrop.void_mint: self.logic.season.has_any_not_winter() & self.logic.has(DistantLandsSeed.void_mint),
+ DistantLandsCrop.vile_ancient_fruit: self.logic.season.has_any_not_winter() & self.logic.has(DistantLandsSeed.vile_ancient_fruit),
+ }
+
+ def get_boarding_house_item_rules(self):
+ return {
+ # Mob Drops from lost valley enemies
+ ModArtisanGood.pterodactyl_egg: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.good),
+ ModFossil.pterodactyl_claw: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.good),
+ ModFossil.pterodactyl_ribs: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.good),
+ ModFossil.pterodactyl_vertebra: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.good),
+ ModFossil.pterodactyl_skull: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.good),
+ ModFossil.pterodactyl_phalange: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.good),
+ ModFossil.pterodactyl_l_wing_bone: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.good),
+ ModFossil.pterodactyl_r_wing_bone: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.good),
+ ModFossil.dinosaur_skull: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.good),
+ ModFossil.dinosaur_tooth: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.good),
+ ModFossil.dinosaur_femur: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.good),
+ ModFossil.dinosaur_pelvis: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.good),
+ ModFossil.dinosaur_ribs: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.good),
+ ModFossil.dinosaur_vertebra: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.good),
+ ModFossil.dinosaur_claw: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.good),
+ ModFossil.neanderthal_skull: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.great),
+ ModFossil.neanderthal_ribs: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.great),
+ ModFossil.neanderthal_pelvis: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.great),
+ ModFossil.neanderthal_limb_bones: self.logic.region.can_reach_any((BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1,
+ BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level(
+ Performance.great),
+ }
+
+ def has_seed_unlocked(self, seed_name: str):
+ if self.options.cropsanity == Cropsanity.option_disabled:
+ return True_()
+ return self.logic.received(seed_name)
diff --git a/worlds/stardew_valley/mods/logic/magic.py b/worlds/stardew_valley/mods/logic/magic.py
deleted file mode 100644
index 709376399c87..000000000000
--- a/worlds/stardew_valley/mods/logic/magic.py
+++ /dev/null
@@ -1,80 +0,0 @@
-from ...strings.region_names import MagicRegion
-from ...mods.mod_data import ModNames
-from ...strings.spells import MagicSpell
-from ...strings.ap_names.skill_level_names import ModSkillLevel
-from ...stardew_rule import Count, StardewRule, False_
-from ... import options
-
-
-def can_use_clear_debris_instead_of_tool_level(vanilla_logic, level: int) -> StardewRule:
- if ModNames.magic not in vanilla_logic.options.mods:
- return False_()
- return vanilla_logic.received(MagicSpell.clear_debris) & can_use_altar(vanilla_logic) & vanilla_logic.received(ModSkillLevel.magic_level, level)
-
-
-def can_use_altar(vanilla_logic) -> StardewRule:
- if ModNames.magic not in vanilla_logic.options.mods:
- return False_()
- return vanilla_logic.can_reach_region(MagicRegion.altar)
-
-
-def has_any_spell(vanilla_logic) -> StardewRule:
- if ModNames.magic not in vanilla_logic.options.mods:
- return False_()
- return can_use_altar(vanilla_logic)
-
-
-def has_attack_spell_count(vanilla_logic, count: int) -> StardewRule:
- attack_spell_rule = [vanilla_logic.received(MagicSpell.fireball), vanilla_logic.received(
- MagicSpell.frostbite), vanilla_logic.received(MagicSpell.shockwave), vanilla_logic.received(MagicSpell.spirit),
- vanilla_logic.received(MagicSpell.meteor)
- ]
- return Count(count, attack_spell_rule)
-
-
-def has_support_spell_count(vanilla_logic, count: int) -> StardewRule:
- support_spell_rule = [can_use_altar(vanilla_logic), vanilla_logic.received(ModSkillLevel.magic_level, 2),
- vanilla_logic.received(MagicSpell.descend), vanilla_logic.received(MagicSpell.heal),
- vanilla_logic.received(MagicSpell.tendrils)]
- return Count(count, support_spell_rule)
-
-
-def has_decent_spells(vanilla_logic) -> StardewRule:
- if ModNames.magic not in vanilla_logic.options.mods:
- return False_()
- magic_resource_rule = can_use_altar(vanilla_logic) & vanilla_logic.received(ModSkillLevel.magic_level, 2)
- magic_attack_options_rule = has_attack_spell_count(vanilla_logic, 1)
- return magic_resource_rule & magic_attack_options_rule
-
-
-def has_good_spells(vanilla_logic) -> StardewRule:
- if ModNames.magic not in vanilla_logic.options.mods:
- return False_()
- magic_resource_rule = can_use_altar(vanilla_logic) & vanilla_logic.received(ModSkillLevel.magic_level, 4)
- magic_attack_options_rule = has_attack_spell_count(vanilla_logic, 2)
- magic_support_options_rule = has_support_spell_count(vanilla_logic, 1)
- return magic_resource_rule & magic_attack_options_rule & magic_support_options_rule
-
-
-def has_great_spells(vanilla_logic) -> StardewRule:
- if ModNames.magic not in vanilla_logic.options.mods:
- return False_()
- magic_resource_rule = can_use_altar(vanilla_logic) & vanilla_logic.received(ModSkillLevel.magic_level, 6)
- magic_attack_options_rule = has_attack_spell_count(vanilla_logic, 3)
- magic_support_options_rule = has_support_spell_count(vanilla_logic, 1)
- return magic_resource_rule & magic_attack_options_rule & magic_support_options_rule
-
-
-def has_amazing_spells(vanilla_logic) -> StardewRule:
- if ModNames.magic not in vanilla_logic.options.mods:
- return False_()
- magic_resource_rule = can_use_altar(vanilla_logic) & vanilla_logic.received(ModSkillLevel.magic_level, 8)
- magic_attack_options_rule = has_attack_spell_count(vanilla_logic, 4)
- magic_support_options_rule = has_support_spell_count(vanilla_logic, 2)
- return magic_resource_rule & magic_attack_options_rule & magic_support_options_rule
-
-
-def can_blink(vanilla_logic) -> StardewRule:
- if ModNames.magic not in vanilla_logic.options.mods:
- return False_()
- return vanilla_logic.received(MagicSpell.blink) & can_use_altar(vanilla_logic)
diff --git a/worlds/stardew_valley/mods/logic/magic_logic.py b/worlds/stardew_valley/mods/logic/magic_logic.py
new file mode 100644
index 000000000000..99482b063056
--- /dev/null
+++ b/worlds/stardew_valley/mods/logic/magic_logic.py
@@ -0,0 +1,82 @@
+from typing import Union
+
+from ...logic.base_logic import BaseLogicMixin, BaseLogic
+from ...logic.has_logic import HasLogicMixin
+from ...logic.received_logic import ReceivedLogicMixin
+from ...logic.region_logic import RegionLogicMixin
+from ...mods.mod_data import ModNames
+from ...stardew_rule import StardewRule, False_
+from ...strings.ap_names.skill_level_names import ModSkillLevel
+from ...strings.region_names import MagicRegion
+from ...strings.spells import MagicSpell
+
+
+class MagicLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.magic = MagicLogic(*args, **kwargs)
+
+
+# TODO add logic.mods.magic for altar
+class MagicLogic(BaseLogic[Union[RegionLogicMixin, ReceivedLogicMixin, HasLogicMixin]]):
+ def can_use_clear_debris_instead_of_tool_level(self, level: int) -> StardewRule:
+ if ModNames.magic not in self.options.mods:
+ return False_()
+ return self.logic.received(MagicSpell.clear_debris) & self.can_use_altar() & self.logic.received(ModSkillLevel.magic_level, level)
+
+ def can_use_altar(self) -> StardewRule:
+ if ModNames.magic not in self.options.mods:
+ return False_()
+ return self.logic.region.can_reach(MagicRegion.altar)
+
+ def has_any_spell(self) -> StardewRule:
+ if ModNames.magic not in self.options.mods:
+ return False_()
+ return self.can_use_altar()
+
+ def has_attack_spell_count(self, count: int) -> StardewRule:
+ attack_spell_rule = [self.logic.received(MagicSpell.fireball), self.logic.received(MagicSpell.frostbite), self.logic.received(MagicSpell.shockwave),
+ self.logic.received(MagicSpell.spirit), self.logic.received(MagicSpell.meteor)]
+ return self.logic.count(count, *attack_spell_rule)
+
+ def has_support_spell_count(self, count: int) -> StardewRule:
+ support_spell_rule = [self.can_use_altar(), self.logic.received(ModSkillLevel.magic_level, 2),
+ self.logic.received(MagicSpell.descend), self.logic.received(MagicSpell.heal),
+ self.logic.received(MagicSpell.tendrils)]
+ return self.logic.count(count, *support_spell_rule)
+
+ def has_decent_spells(self) -> StardewRule:
+ if ModNames.magic not in self.options.mods:
+ return False_()
+ magic_resource_rule = self.can_use_altar() & self.logic.received(ModSkillLevel.magic_level, 2)
+ magic_attack_options_rule = self.has_attack_spell_count(1)
+ return magic_resource_rule & magic_attack_options_rule
+
+ def has_good_spells(self) -> StardewRule:
+ if ModNames.magic not in self.options.mods:
+ return False_()
+ magic_resource_rule = self.can_use_altar() & self.logic.received(ModSkillLevel.magic_level, 4)
+ magic_attack_options_rule = self.has_attack_spell_count(2)
+ magic_support_options_rule = self.has_support_spell_count(1)
+ return magic_resource_rule & magic_attack_options_rule & magic_support_options_rule
+
+ def has_great_spells(self) -> StardewRule:
+ if ModNames.magic not in self.options.mods:
+ return False_()
+ magic_resource_rule = self.can_use_altar() & self.logic.received(ModSkillLevel.magic_level, 6)
+ magic_attack_options_rule = self.has_attack_spell_count(3)
+ magic_support_options_rule = self.has_support_spell_count(1)
+ return magic_resource_rule & magic_attack_options_rule & magic_support_options_rule
+
+ def has_amazing_spells(self) -> StardewRule:
+ if ModNames.magic not in self.options.mods:
+ return False_()
+ magic_resource_rule = self.can_use_altar() & self.logic.received(ModSkillLevel.magic_level, 8)
+ magic_attack_options_rule = self.has_attack_spell_count(4)
+ magic_support_options_rule = self.has_support_spell_count(2)
+ return magic_resource_rule & magic_attack_options_rule & magic_support_options_rule
+
+ def can_blink(self) -> StardewRule:
+ if ModNames.magic not in self.options.mods:
+ return False_()
+ return self.logic.received(MagicSpell.blink) & self.can_use_altar()
diff --git a/worlds/stardew_valley/mods/logic/mod_logic.py b/worlds/stardew_valley/mods/logic/mod_logic.py
new file mode 100644
index 000000000000..37c17183dbb0
--- /dev/null
+++ b/worlds/stardew_valley/mods/logic/mod_logic.py
@@ -0,0 +1,21 @@
+from .buildings_logic import ModBuildingLogicMixin
+from .deepwoods_logic import DeepWoodsLogicMixin
+from .elevator_logic import ModElevatorLogicMixin
+from .item_logic import ModItemLogicMixin
+from .magic_logic import MagicLogicMixin
+from .quests_logic import ModQuestLogicMixin
+from .skills_logic import ModSkillLogicMixin
+from .special_orders_logic import ModSpecialOrderLogicMixin
+from .sve_logic import SVELogicMixin
+from ...logic.base_logic import BaseLogicMixin
+
+
+class ModLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.mod = ModLogic(*args, **kwargs)
+
+
+class ModLogic(ModElevatorLogicMixin, MagicLogicMixin, ModSkillLogicMixin, ModItemLogicMixin, ModQuestLogicMixin, ModBuildingLogicMixin,
+ ModSpecialOrderLogicMixin, DeepWoodsLogicMixin, SVELogicMixin):
+ pass
diff --git a/worlds/stardew_valley/mods/logic/mod_skills_levels.py b/worlds/stardew_valley/mods/logic/mod_skills_levels.py
new file mode 100644
index 000000000000..18402283857b
--- /dev/null
+++ b/worlds/stardew_valley/mods/logic/mod_skills_levels.py
@@ -0,0 +1,21 @@
+from typing import Tuple
+
+from ...mods.mod_data import ModNames
+from ...options import Mods
+
+
+def get_mod_skill_levels(mods: Mods) -> Tuple[str]:
+ skills_items = []
+ if ModNames.luck_skill in mods:
+ skills_items.append("Luck Level")
+ if ModNames.socializing_skill in mods:
+ skills_items.append("Socializing Level")
+ if ModNames.magic in mods:
+ skills_items.append("Magic Level")
+ if ModNames.archaeology in mods:
+ skills_items.append("Archaeology Level")
+ if ModNames.binning_skill in mods:
+ skills_items.append("Binning Level")
+ if ModNames.cooking_skill in mods:
+ skills_items.append("Cooking Level")
+ return tuple(skills_items)
diff --git a/worlds/stardew_valley/mods/logic/quests.py b/worlds/stardew_valley/mods/logic/quests.py
deleted file mode 100644
index bf185754b2be..000000000000
--- a/worlds/stardew_valley/mods/logic/quests.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from typing import Union
-from ...strings.quest_names import ModQuest
-from ..mod_data import ModNames
-from ...strings.food_names import Meal, Beverage
-from ...strings.monster_drop_names import Loot
-from ...strings.villager_names import ModNPC
-from ...strings.season_names import Season
-from ...strings.region_names import Region
-
-
-def get_modded_quest_rules(vanilla_logic, active_mods):
- quests = {}
- if ModNames.juna in active_mods:
- quests.update({
- ModQuest.JunaCola: vanilla_logic.has_relationship(ModNPC.juna, 3) & vanilla_logic.has(Beverage.joja_cola),
- ModQuest.JunaSpaghetti: vanilla_logic.has_relationship(ModNPC.juna, 6) & vanilla_logic.has(Meal.spaghetti)
- })
-
- if ModNames.ginger in active_mods:
- quests.update({
- ModQuest.MrGinger: vanilla_logic.has_relationship(ModNPC.mr_ginger, 6) & vanilla_logic.has(Loot.void_essence)
- })
-
- if ModNames.ayeisha in active_mods:
- quests.update({
- ModQuest.AyeishaEnvelope: (vanilla_logic.has_season(Season.spring) | vanilla_logic.has_season(Season.fall)) &
- vanilla_logic.can_reach_region(Region.mountain),
- ModQuest.AyeishaRing: vanilla_logic.has_season(Season.winter) & vanilla_logic.can_reach_region(Region.forest)
- })
-
- return quests
diff --git a/worlds/stardew_valley/mods/logic/quests_logic.py b/worlds/stardew_valley/mods/logic/quests_logic.py
new file mode 100644
index 000000000000..40b5545ee39f
--- /dev/null
+++ b/worlds/stardew_valley/mods/logic/quests_logic.py
@@ -0,0 +1,128 @@
+from typing import Dict, Union
+
+from ..mod_data import ModNames
+from ...logic.base_logic import BaseLogic, BaseLogicMixin
+from ...logic.has_logic import HasLogicMixin
+from ...logic.quest_logic import QuestLogicMixin
+from ...logic.monster_logic import MonsterLogicMixin
+from ...logic.received_logic import ReceivedLogicMixin
+from ...logic.region_logic import RegionLogicMixin
+from ...logic.relationship_logic import RelationshipLogicMixin
+from ...logic.season_logic import SeasonLogicMixin
+from ...logic.time_logic import TimeLogicMixin
+from ...stardew_rule import StardewRule
+from ...strings.animal_product_names import AnimalProduct
+from ...strings.artisan_good_names import ArtisanGood
+from ...strings.crop_names import Fruit, SVEFruit, SVEVegetable, Vegetable
+from ...strings.fertilizer_names import Fertilizer
+from ...strings.food_names import Meal, Beverage
+from ...strings.forageable_names import SVEForage
+from ...strings.material_names import Material
+from ...strings.metal_names import Ore, MetalBar
+from ...strings.monster_drop_names import Loot
+from ...strings.monster_names import Monster
+from ...strings.quest_names import Quest, ModQuest
+from ...strings.region_names import Region, SVERegion, BoardingHouseRegion
+from ...strings.season_names import Season
+from ...strings.villager_names import ModNPC, NPC
+from ...strings.wallet_item_names import Wallet
+
+
+class ModQuestLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.quest = ModQuestLogic(*args, **kwargs)
+
+
+class ModQuestLogic(BaseLogic[Union[HasLogicMixin, QuestLogicMixin, ReceivedLogicMixin, RegionLogicMixin,
+ TimeLogicMixin, SeasonLogicMixin, RelationshipLogicMixin, MonsterLogicMixin]]):
+ def get_modded_quest_rules(self) -> Dict[str, StardewRule]:
+ quests = dict()
+ quests.update(self._get_juna_quest_rules())
+ quests.update(self._get_mr_ginger_quest_rules())
+ quests.update(self._get_ayeisha_quest_rules())
+ quests.update(self._get_sve_quest_rules())
+ quests.update(self._get_distant_lands_quest_rules())
+ quests.update(self._get_boarding_house_quest_rules())
+ quests.update((self._get_hat_mouse_quest_rules()))
+ return quests
+
+ def _get_juna_quest_rules(self):
+ if ModNames.juna not in self.options.mods:
+ return {}
+
+ return {
+ ModQuest.JunaCola: self.logic.relationship.has_hearts(ModNPC.juna, 3) & self.logic.has(Beverage.joja_cola),
+ ModQuest.JunaSpaghetti: self.logic.relationship.has_hearts(ModNPC.juna, 6) & self.logic.has(Meal.spaghetti)
+ }
+
+ def _get_mr_ginger_quest_rules(self):
+ if ModNames.ginger not in self.options.mods:
+ return {}
+
+ return {
+ ModQuest.MrGinger: self.logic.relationship.has_hearts(ModNPC.mr_ginger, 6) & self.logic.has(Loot.void_essence)
+ }
+
+ def _get_ayeisha_quest_rules(self):
+ if ModNames.ayeisha not in self.options.mods:
+ return {}
+
+ return {
+ ModQuest.AyeishaEnvelope: (self.logic.season.has(Season.spring) | self.logic.season.has(Season.fall)),
+ ModQuest.AyeishaRing: self.logic.season.has(Season.winter)
+ }
+
+ def _get_sve_quest_rules(self):
+ if ModNames.sve not in self.options.mods:
+ return {}
+
+ return {
+ ModQuest.RailroadBoulder: self.logic.received(Wallet.skull_key) & self.logic.has_all(*(Ore.iridium, Material.coal)) &
+ self.logic.region.can_reach(Region.blacksmith) & self.logic.region.can_reach(Region.railroad),
+ ModQuest.GrandpasShed: self.logic.has_all(*(Material.hardwood, MetalBar.iron, ArtisanGood.battery_pack, Material.stone)) &
+ self.logic.region.can_reach(SVERegion.grandpas_shed),
+ ModQuest.MarlonsBoat: self.logic.has_all(*(Loot.void_essence, Loot.solar_essence, Loot.slime, Loot.bat_wing, Loot.bug_meat)) &
+ self.logic.relationship.can_meet(ModNPC.lance) & self.logic.region.can_reach(SVERegion.guild_summit),
+ ModQuest.AuroraVineyard: self.logic.has(Fruit.starfruit) & self.logic.region.can_reach(SVERegion.aurora_vineyard),
+ ModQuest.MonsterCrops: self.logic.has_all(*(SVEVegetable.monster_mushroom, SVEFruit.slime_berry, SVEFruit.monster_fruit, SVEVegetable.void_root)),
+ ModQuest.VoidSoul: self.logic.has(SVEForage.void_soul) & self.logic.region.can_reach(Region.farm) &
+ self.logic.season.has_any_not_winter() & self.logic.region.can_reach(SVERegion.badlands_entrance) &
+ self.logic.relationship.has_hearts(NPC.krobus, 10) & self.logic.quest.can_complete_quest(ModQuest.MonsterCrops) &
+ self.logic.monster.can_kill_any((Monster.shadow_brute, Monster.shadow_shaman, Monster.shadow_sniper)),
+ }
+
+ def _get_distant_lands_quest_rules(self):
+ if ModNames.distant_lands not in self.options.mods:
+ return {}
+
+ return {
+ ModQuest.CorruptedCropsTask: self.logic.region.can_reach(Region.wizard_tower) & self.logic.has(Fertilizer.deluxe) &
+ self.logic.quest.can_complete_quest(Quest.magic_ink),
+ ModQuest.WitchOrder: self.logic.region.can_reach(Region.witch_swamp) & self.logic.has(Fertilizer.deluxe) &
+ self.logic.quest.can_complete_quest(Quest.magic_ink),
+ ModQuest.ANewPot: self.logic.region.can_reach(Region.saloon) &
+ self.logic.region.can_reach(Region.sam_house) & self.logic.region.can_reach(Region.pierre_store) &
+ self.logic.region.can_reach(Region.blacksmith) & self.logic.has(MetalBar.iron) & self.logic.relationship.has_hearts(ModNPC.goblin,
+ 6),
+ ModQuest.FancyBlanketTask: self.logic.region.can_reach(Region.haley_house) & self.logic.has(AnimalProduct.wool) &
+ self.logic.has(ArtisanGood.cloth) & self.logic.relationship.has_hearts(ModNPC.goblin, 10) &
+ self.logic.relationship.has_hearts(NPC.emily, 8) & self.logic.season.has(Season.winter)
+
+ }
+
+ def _get_boarding_house_quest_rules(self):
+ if ModNames.boarding_house not in self.options.mods:
+ return {}
+
+ return {
+ ModQuest.PumpkinSoup: self.logic.region.can_reach(BoardingHouseRegion.boarding_house_first) & self.logic.has(Vegetable.pumpkin)
+ }
+
+ def _get_hat_mouse_quest_rules(self):
+ if ModNames.lacey not in self.options.mods:
+ return {}
+
+ return {
+ ModQuest.HatMouseHat: self.logic.relationship.has_hearts(ModNPC.lacey, 2) & self.logic.time.has_lived_months(4)
+ }
diff --git a/worlds/stardew_valley/mods/logic/skills.py b/worlds/stardew_valley/mods/logic/skills.py
deleted file mode 100644
index 24402a088b16..000000000000
--- a/worlds/stardew_valley/mods/logic/skills.py
+++ /dev/null
@@ -1,94 +0,0 @@
-from typing import List, Union
-from . import magic
-from ...strings.building_names import Building
-from ...strings.geode_names import Geode
-from ...strings.region_names import Region
-from ...strings.skill_names import ModSkill
-from ...strings.spells import MagicSpell
-from ...strings.machine_names import Machine
-from ...strings.tool_names import Tool, ToolMaterial
-from ...mods.mod_data import ModNames
-from ...data.villagers_data import all_villagers
-from ...stardew_rule import Count, StardewRule, False_
-from ... import options
-
-
-def append_mod_skill_level(skills_items: List[str], active_mods):
- if ModNames.luck_skill in active_mods:
- skills_items.append("Luck Level")
- if ModNames.socializing_skill in active_mods:
- skills_items.append("Socializing Level")
- if ModNames.magic in active_mods:
- skills_items.append("Magic Level")
- if ModNames.archaeology in active_mods:
- skills_items.append("Archaeology Level")
- if ModNames.binning_skill in active_mods:
- skills_items.append("Binning Level")
- if ModNames.cooking_skill in active_mods:
- skills_items.append("Cooking Level")
-
-
-def can_earn_mod_skill_level(logic, skill: str, level: int) -> StardewRule:
- if ModNames.luck_skill in logic.options.mods and skill == ModSkill.luck:
- return can_earn_luck_skill_level(logic, level)
- if ModNames.magic in logic.options.mods and skill == ModSkill.magic:
- return can_earn_magic_skill_level(logic, level)
- if ModNames.socializing_skill in logic.options.mods and skill == ModSkill.socializing:
- return can_earn_socializing_skill_level(logic, level)
- if ModNames.archaeology in logic.options.mods and skill == ModSkill.archaeology:
- return can_earn_archaeology_skill_level(logic, level)
- if ModNames.cooking_skill in logic.options.mods and skill == ModSkill.cooking:
- return can_earn_cooking_skill_level(logic, level)
- if ModNames.binning_skill in logic.options.mods and skill == ModSkill.binning:
- return can_earn_binning_skill_level(logic, level)
- return False_()
-
-
-def can_earn_luck_skill_level(vanilla_logic, level: int) -> StardewRule:
- if level >= 6:
- return vanilla_logic.can_fish_chests() | vanilla_logic.can_open_geode(Geode.magma)
- else:
- return vanilla_logic.can_fish_chests() | vanilla_logic.can_open_geode(Geode.geode)
-
-
-def can_earn_magic_skill_level(vanilla_logic, level: int) -> StardewRule:
- spell_count = [vanilla_logic.received(MagicSpell.clear_debris), vanilla_logic.received(MagicSpell.water),
- vanilla_logic.received(MagicSpell.blink), vanilla_logic.received(MagicSpell.fireball),
- vanilla_logic.received(MagicSpell.frostbite),
- vanilla_logic.received(MagicSpell.descend), vanilla_logic.received(MagicSpell.tendrils),
- vanilla_logic.received(MagicSpell.shockwave),
- vanilla_logic.received(MagicSpell.meteor),
- vanilla_logic.received(MagicSpell.spirit)]
- return magic.can_use_altar(vanilla_logic) & Count(level, spell_count)
-
-
-def can_earn_socializing_skill_level(vanilla_logic, level: int) -> StardewRule:
- villager_count = []
- for villager in all_villagers:
- if villager.mod_name in vanilla_logic.options.mods or villager.mod_name is None:
- villager_count.append(vanilla_logic.can_earn_relationship(villager.name, level))
- return Count(level * 2, villager_count)
-
-
-def can_earn_archaeology_skill_level(vanilla_logic, level: int) -> StardewRule:
- if level >= 6:
- return vanilla_logic.can_do_panning() | vanilla_logic.has_tool(Tool.hoe, ToolMaterial.gold)
- else:
- return vanilla_logic.can_do_panning() | vanilla_logic.has_tool(Tool.hoe, ToolMaterial.basic)
-
-
-def can_earn_cooking_skill_level(vanilla_logic, level: int) -> StardewRule:
- if level >= 6:
- return vanilla_logic.can_cook() & vanilla_logic.can_fish() & vanilla_logic.can_reach_region(Region.saloon) & \
- vanilla_logic.has_building(Building.coop) & vanilla_logic.has_building(Building.barn)
- else:
- return vanilla_logic.can_cook()
-
-
-def can_earn_binning_skill_level(vanilla_logic, level: int) -> StardewRule:
- if level >= 6:
- return vanilla_logic.can_reach_region(Region.town) & vanilla_logic.has(Machine.recycling_machine) & \
- (vanilla_logic.can_fish() | vanilla_logic.can_crab_pot())
- else:
- return vanilla_logic.can_reach_region(Region.town) | (vanilla_logic.has(Machine.recycling_machine) &
- (vanilla_logic.can_fish() | vanilla_logic.can_crab_pot()))
diff --git a/worlds/stardew_valley/mods/logic/skills_logic.py b/worlds/stardew_valley/mods/logic/skills_logic.py
new file mode 100644
index 000000000000..ce8bebbffef5
--- /dev/null
+++ b/worlds/stardew_valley/mods/logic/skills_logic.py
@@ -0,0 +1,110 @@
+from typing import Union
+
+from .magic_logic import MagicLogicMixin
+from ...data.villagers_data import all_villagers
+from ...logic.action_logic import ActionLogicMixin
+from ...logic.base_logic import BaseLogicMixin, BaseLogic
+from ...logic.building_logic import BuildingLogicMixin
+from ...logic.cooking_logic import CookingLogicMixin
+from ...logic.fishing_logic import FishingLogicMixin
+from ...logic.has_logic import HasLogicMixin
+from ...logic.received_logic import ReceivedLogicMixin
+from ...logic.region_logic import RegionLogicMixin
+from ...logic.relationship_logic import RelationshipLogicMixin
+from ...logic.tool_logic import ToolLogicMixin
+from ...mods.mod_data import ModNames
+from ...options import SkillProgression
+from ...stardew_rule import StardewRule, False_, True_
+from ...strings.ap_names.mods.mod_items import SkillLevel
+from ...strings.craftable_names import ModCraftable, ModMachine
+from ...strings.building_names import Building
+from ...strings.geode_names import Geode
+from ...strings.machine_names import Machine
+from ...strings.region_names import Region
+from ...strings.skill_names import ModSkill
+from ...strings.spells import MagicSpell
+from ...strings.tool_names import Tool, ToolMaterial
+
+
+class ModSkillLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.skill = ModSkillLogic(*args, **kwargs)
+
+
+class ModSkillLogic(BaseLogic[Union[HasLogicMixin, ReceivedLogicMixin, RegionLogicMixin, ActionLogicMixin, RelationshipLogicMixin, BuildingLogicMixin,
+ToolLogicMixin, FishingLogicMixin, CookingLogicMixin, MagicLogicMixin]]):
+ def has_mod_level(self, skill: str, level: int) -> StardewRule:
+ if level <= 0:
+ return True_()
+
+ if self.options.skill_progression == SkillProgression.option_progressive:
+ return self.logic.received(f"{skill} Level", level)
+
+ return self.can_earn_mod_skill_level(skill, level)
+
+ def can_earn_mod_skill_level(self, skill: str, level: int) -> StardewRule:
+ if ModNames.luck_skill in self.options.mods and skill == ModSkill.luck:
+ return self.can_earn_luck_skill_level(level)
+ if ModNames.magic in self.options.mods and skill == ModSkill.magic:
+ return self.can_earn_magic_skill_level(level)
+ if ModNames.socializing_skill in self.options.mods and skill == ModSkill.socializing:
+ return self.can_earn_socializing_skill_level(level)
+ if ModNames.archaeology in self.options.mods and skill == ModSkill.archaeology:
+ return self.can_earn_archaeology_skill_level(level)
+ if ModNames.cooking_skill in self.options.mods and skill == ModSkill.cooking:
+ return self.can_earn_cooking_skill_level(level)
+ if ModNames.binning_skill in self.options.mods and skill == ModSkill.binning:
+ return self.can_earn_binning_skill_level(level)
+ return False_()
+
+ def can_earn_luck_skill_level(self, level: int) -> StardewRule:
+ if level >= 6:
+ return self.logic.fishing.can_fish_chests() | self.logic.action.can_open_geode(Geode.magma)
+ if level >= 3:
+ return self.logic.fishing.can_fish_chests() | self.logic.action.can_open_geode(Geode.geode)
+ return True_() # You can literally wake up and or get them by opening starting chests.
+
+ def can_earn_magic_skill_level(self, level: int) -> StardewRule:
+ spell_count = [self.logic.received(MagicSpell.clear_debris), self.logic.received(MagicSpell.water),
+ self.logic.received(MagicSpell.blink), self.logic.received(MagicSpell.fireball),
+ self.logic.received(MagicSpell.frostbite),
+ self.logic.received(MagicSpell.descend), self.logic.received(MagicSpell.tendrils),
+ self.logic.received(MagicSpell.shockwave),
+ self.logic.received(MagicSpell.meteor),
+ self.logic.received(MagicSpell.spirit)]
+ return self.logic.count(level, *spell_count)
+
+ def can_earn_socializing_skill_level(self, level: int) -> StardewRule:
+ villager_count = []
+ for villager in all_villagers:
+ if villager.mod_name in self.options.mods or villager.mod_name is None:
+ villager_count.append(self.logic.relationship.can_earn_relationship(villager.name, level))
+ return self.logic.count(level * 2, *villager_count)
+
+ def can_earn_archaeology_skill_level(self, level: int) -> StardewRule:
+ shifter_rule = True_()
+ preservation_rule = True_()
+ if self.options.skill_progression == self.options.skill_progression.option_progressive:
+ shifter_rule = self.logic.has(ModCraftable.water_shifter)
+ preservation_rule = self.logic.has(ModMachine.hardwood_preservation_chamber)
+ if level >= 8:
+ return (self.logic.action.can_pan() & self.logic.tool.has_tool(Tool.hoe, ToolMaterial.gold)) & shifter_rule & preservation_rule
+ if level >= 5:
+ return (self.logic.action.can_pan() & self.logic.tool.has_tool(Tool.hoe, ToolMaterial.iron)) & shifter_rule
+ if level >= 3:
+ return self.logic.action.can_pan() | self.logic.tool.has_tool(Tool.hoe, ToolMaterial.copper)
+ return self.logic.action.can_pan() | self.logic.tool.has_tool(Tool.hoe, ToolMaterial.basic)
+
+ def can_earn_cooking_skill_level(self, level: int) -> StardewRule:
+ if level >= 6:
+ return self.logic.cooking.can_cook() & self.logic.region.can_reach(Region.saloon) & \
+ self.logic.building.has_building(Building.coop) & self.logic.building.has_building(Building.barn)
+ else:
+ return self.logic.cooking.can_cook()
+
+ def can_earn_binning_skill_level(self, level: int) -> StardewRule:
+ if level >= 6:
+ return self.logic.has(Machine.recycling_machine)
+ else:
+ return True_() # You can always earn levels 1-5 with trash cans
diff --git a/worlds/stardew_valley/mods/logic/skullcavernelevator.py b/worlds/stardew_valley/mods/logic/skullcavernelevator.py
deleted file mode 100644
index 9a5140ae39c9..000000000000
--- a/worlds/stardew_valley/mods/logic/skullcavernelevator.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from ...stardew_rule import Count, StardewRule, True_
-from ...mods.mod_data import ModNames
-from ... import options
-
-
-def has_skull_cavern_elevator_to_floor(self, floor: int) -> StardewRule:
- if self.options.elevator_progression != options.ElevatorProgression.option_vanilla and \
- ModNames.skull_cavern_elevator in self.options.mods:
- return self.received("Progressive Skull Cavern Elevator", floor // 25)
- return True_()
diff --git a/worlds/stardew_valley/mods/logic/special_orders.py b/worlds/stardew_valley/mods/logic/special_orders.py
deleted file mode 100644
index 45d5d572dc05..000000000000
--- a/worlds/stardew_valley/mods/logic/special_orders.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from typing import Union
-from ...strings.craftable_names import Craftable
-from ...strings.food_names import Meal
-from ...strings.material_names import Material
-from ...strings.monster_drop_names import Loot
-from ...strings.region_names import Region
-from ...strings.special_order_names import SpecialOrder, ModSpecialOrder
-from ...strings.villager_names import ModNPC
-from ..mod_data import ModNames
-
-
-def get_modded_special_orders_rules(vanilla_logic, active_mods):
- special_orders = {}
- if ModNames.juna in active_mods:
- special_orders.update({
- ModSpecialOrder.junas_monster_mash: vanilla_logic.has_relationship(ModNPC.juna, 4) &
- vanilla_logic.can_complete_special_order(SpecialOrder.a_curious_substance) &
- vanilla_logic.has_rusty_key() &
- vanilla_logic.can_reach_region(Region.forest) & vanilla_logic.has(Craftable.monster_musk) &
- vanilla_logic.has("Energy Tonic") & vanilla_logic.has(Material.sap) & vanilla_logic.has(Loot.bug_meat) &
- vanilla_logic.has(Craftable.oil_of_garlic) & vanilla_logic.has(Meal.strange_bun)
- })
-
- return special_orders
diff --git a/worlds/stardew_valley/mods/logic/special_orders_logic.py b/worlds/stardew_valley/mods/logic/special_orders_logic.py
new file mode 100644
index 000000000000..e51a23d50254
--- /dev/null
+++ b/worlds/stardew_valley/mods/logic/special_orders_logic.py
@@ -0,0 +1,76 @@
+from typing import Union
+
+from ...data.craftable_data import all_crafting_recipes_by_name
+from ..mod_data import ModNames
+from ...logic.action_logic import ActionLogicMixin
+from ...logic.artisan_logic import ArtisanLogicMixin
+from ...logic.base_logic import BaseLogicMixin, BaseLogic
+from ...logic.crafting_logic import CraftingLogicMixin
+from ...logic.crop_logic import CropLogicMixin
+from ...logic.has_logic import HasLogicMixin
+from ...logic.received_logic import ReceivedLogicMixin
+from ...logic.region_logic import RegionLogicMixin
+from ...logic.relationship_logic import RelationshipLogicMixin
+from ...logic.season_logic import SeasonLogicMixin
+from ...logic.wallet_logic import WalletLogicMixin
+from ...strings.ap_names.community_upgrade_names import CommunityUpgrade
+from ...strings.artisan_good_names import ArtisanGood
+from ...strings.craftable_names import Consumable, Edible, Bomb
+from ...strings.crop_names import Fruit
+from ...strings.fertilizer_names import Fertilizer
+from ...strings.food_names import Meal
+from ...strings.geode_names import Geode
+from ...strings.material_names import Material
+from ...strings.metal_names import MetalBar, Artifact
+from ...strings.monster_drop_names import Loot
+from ...strings.region_names import Region, SVERegion
+from ...strings.special_order_names import SpecialOrder, ModSpecialOrder
+from ...strings.villager_names import ModNPC
+
+
+class ModSpecialOrderLogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.special_order = ModSpecialOrderLogic(*args, **kwargs)
+
+
+class ModSpecialOrderLogic(BaseLogic[Union[ActionLogicMixin, ArtisanLogicMixin, CraftingLogicMixin, CropLogicMixin, HasLogicMixin, RegionLogicMixin,
+ReceivedLogicMixin, RelationshipLogicMixin, SeasonLogicMixin, WalletLogicMixin]]):
+ def get_modded_special_orders_rules(self):
+ special_orders = {}
+ if ModNames.juna in self.options.mods:
+ special_orders.update({
+ ModSpecialOrder.junas_monster_mash: self.logic.relationship.has_hearts(ModNPC.juna, 4) &
+ self.registry.special_order_rules[SpecialOrder.a_curious_substance] &
+ self.logic.wallet.has_rusty_key() &
+ self.logic.region.can_reach(Region.forest) & self.logic.has(Consumable.monster_musk) &
+ self.logic.has("Energy Tonic") & self.logic.has(Material.sap) & self.logic.has(Loot.bug_meat) &
+ self.logic.has(Edible.oil_of_garlic) & self.logic.has(Meal.strange_bun)
+ })
+ if ModNames.sve in self.options.mods:
+ special_orders.update({
+ ModSpecialOrder.andys_cellar: self.logic.has(Material.stone) & self.logic.has(Material.wood) & self.logic.has(Material.hardwood) &
+ self.logic.has(MetalBar.iron) & self.logic.received(CommunityUpgrade.movie_theater, 1) &
+ self.logic.region.can_reach(SVERegion.fairhaven_farm),
+ ModSpecialOrder.a_mysterious_venture: self.logic.has(Bomb.cherry_bomb) & self.logic.has(Bomb.bomb) & self.logic.has(Bomb.mega_bomb) &
+ self.logic.region.can_reach(Region.adventurer_guild),
+ ModSpecialOrder.an_elegant_reception: self.logic.artisan.can_keg(Fruit.starfruit) & self.logic.has(ArtisanGood.cheese) &
+ self.logic.has(ArtisanGood.goat_cheese) & self.logic.season.has_any_not_winter() &
+ self.logic.region.can_reach(SVERegion.jenkins_cellar),
+ ModSpecialOrder.fairy_garden: self.logic.has(Consumable.fairy_dust) &
+ self.logic.region.can_reach(Region.island_south) & (
+ self.logic.action.can_open_geode(Geode.frozen) | self.logic.action.can_open_geode(Geode.omni)) &
+ self.logic.region.can_reach(SVERegion.blue_moon_vineyard),
+ ModSpecialOrder.homemade_fertilizer: self.logic.crafting.can_craft(all_crafting_recipes_by_name[Fertilizer.quality]) &
+ self.logic.region.can_reach(SVERegion.susans_house) # quest requires you make the fertilizer
+ })
+
+ if ModNames.jasper in self.options.mods:
+ special_orders.update({
+ ModSpecialOrder.dwarf_scroll: self.logic.has_all(*(Artifact.dwarf_scroll_i, Artifact.dwarf_scroll_ii, Artifact.dwarf_scroll_iii,
+ Artifact.dwarf_scroll_iv,)),
+ ModSpecialOrder.geode_order: self.logic.has_all(*(Geode.geode, Geode.frozen, Geode.magma, Geode.omni,)) &
+ self.logic.relationship.has_hearts(ModNPC.jasper, 8)
+ })
+
+ return special_orders
diff --git a/worlds/stardew_valley/mods/logic/sve_logic.py b/worlds/stardew_valley/mods/logic/sve_logic.py
new file mode 100644
index 000000000000..1254338fe2fc
--- /dev/null
+++ b/worlds/stardew_valley/mods/logic/sve_logic.py
@@ -0,0 +1,55 @@
+from typing import Union
+
+from ..mod_regions import SVERegion
+from ...logic.base_logic import BaseLogicMixin, BaseLogic
+from ...logic.combat_logic import CombatLogicMixin
+from ...logic.cooking_logic import CookingLogicMixin
+from ...logic.has_logic import HasLogicMixin
+from ...logic.money_logic import MoneyLogicMixin
+from ...logic.quest_logic import QuestLogicMixin
+from ...logic.received_logic import ReceivedLogicMixin
+from ...logic.region_logic import RegionLogicMixin
+from ...logic.relationship_logic import RelationshipLogicMixin
+from ...logic.season_logic import SeasonLogicMixin
+from ...logic.time_logic import TimeLogicMixin
+from ...logic.tool_logic import ToolLogicMixin
+from ...strings.ap_names.mods.mod_items import SVELocation, SVERunes, SVEQuestItem
+from ...strings.quest_names import Quest
+from ...strings.region_names import Region
+from ...strings.tool_names import Tool, ToolMaterial
+from ...strings.wallet_item_names import Wallet
+from ...stardew_rule import Or
+from ...strings.quest_names import ModQuest
+
+
+class SVELogicMixin(BaseLogicMixin):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.sve = SVELogic(*args, **kwargs)
+
+
+class SVELogic(BaseLogic[Union[HasLogicMixin, ReceivedLogicMixin, QuestLogicMixin, RegionLogicMixin, RelationshipLogicMixin, TimeLogicMixin, ToolLogicMixin,
+ CookingLogicMixin, MoneyLogicMixin, CombatLogicMixin, SeasonLogicMixin, QuestLogicMixin]]):
+ def initialize_rules(self):
+ self.registry.sve_location_rules.update({
+ SVELocation.tempered_galaxy_sword: self.logic.money.can_spend_at(SVERegion.alesia_shop, 350000),
+ SVELocation.tempered_galaxy_dagger: self.logic.money.can_spend_at(SVERegion.isaac_shop, 600000),
+ SVELocation.tempered_galaxy_hammer: self.logic.money.can_spend_at(SVERegion.isaac_shop, 400000),
+ })
+
+ def has_any_rune(self):
+ rune_list = SVERunes.nexus_items
+ return Or(*(self.logic.received(rune) for rune in rune_list))
+
+ def has_iridium_bomb(self):
+ if self.options.quest_locations < 0:
+ return self.logic.quest.can_complete_quest(ModQuest.RailroadBoulder)
+ return self.logic.received(SVEQuestItem.iridium_bomb)
+
+ def can_buy_bear_recipe(self):
+ access_rule = (self.logic.quest.can_complete_quest(Quest.strange_note) & self.logic.tool.has_tool(Tool.axe, ToolMaterial.basic) &
+ self.logic.tool.has_tool(Tool.pickaxe, ToolMaterial.basic))
+ forage_rule = self.logic.region.can_reach_any((Region.forest, Region.backwoods, Region.mountain))
+ knowledge_rule = self.logic.received(Wallet.bears_knowledge)
+ return access_rule & forage_rule & knowledge_rule
+
diff --git a/worlds/stardew_valley/mods/mod_data.py b/worlds/stardew_valley/mods/mod_data.py
index 30fe96c9d906..a4d3b9828aa6 100644
--- a/worlds/stardew_valley/mods/mod_data.py
+++ b/worlds/stardew_valley/mods/mod_data.py
@@ -21,6 +21,13 @@ class ModNames:
ayeisha = "Ayeisha - The Postal Worker (Custom NPC)"
riley = "Custom NPC - Riley"
skull_cavern_elevator = "Skull Cavern Elevator"
+ sve = "Stardew Valley Expanded"
+ alecto = "Alecto the Witch"
+ distant_lands = "Distant Lands - Witch Swamp Overhaul"
+ lacey = "Hat Mouse Lacey"
+ boarding_house = "Boarding House and Bus Stop Extension"
+
+ jasper_sve = jasper + "," + sve
all_mods = frozenset({ModNames.deepwoods, ModNames.tractor, ModNames.big_backpack,
@@ -28,4 +35,5 @@ class ModNames:
ModNames.cooking_skill, ModNames.binning_skill, ModNames.juna,
ModNames.jasper, ModNames.alec, ModNames.yoba, ModNames.eugene,
ModNames.wellwick, ModNames.ginger, ModNames.shiko, ModNames.delores,
- ModNames.ayeisha, ModNames.riley, ModNames.skull_cavern_elevator})
+ ModNames.ayeisha, ModNames.riley, ModNames.skull_cavern_elevator, ModNames.sve, ModNames.alecto,
+ ModNames.distant_lands, ModNames.lacey, ModNames.boarding_house})
diff --git a/worlds/stardew_valley/mods/mod_monster_locations.py b/worlds/stardew_valley/mods/mod_monster_locations.py
new file mode 100644
index 000000000000..96ded1b2fc39
--- /dev/null
+++ b/worlds/stardew_valley/mods/mod_monster_locations.py
@@ -0,0 +1,40 @@
+from typing import Dict, Tuple
+
+from .mod_data import ModNames
+from ..strings.monster_names import Monster
+from ..strings.region_names import SVERegion, DeepWoodsRegion, BoardingHouseRegion
+
+sve_monsters_locations: Dict[str, Tuple[str, ...]] = {
+ Monster.shadow_brute_dangerous: (SVERegion.highlands_cavern,),
+ Monster.shadow_sniper: (SVERegion.highlands_cavern,),
+ Monster.shadow_shaman_dangerous: (SVERegion.highlands_cavern,),
+ Monster.mummy_dangerous: (SVERegion.crimson_badlands,),
+ Monster.royal_serpent: (SVERegion.crimson_badlands,),
+ Monster.skeleton_dangerous: (SVERegion.crimson_badlands,),
+ Monster.skeleton_mage: (SVERegion.crimson_badlands,),
+ Monster.dust_sprite_dangerous: (SVERegion.highlands_outside,),
+}
+
+deepwoods_monsters_locations: Dict[str, Tuple[str, ...]] = {
+ Monster.shadow_brute: (DeepWoodsRegion.floor_10,),
+ Monster.cave_fly: (DeepWoodsRegion.floor_10,),
+ Monster.green_slime: (DeepWoodsRegion.floor_10,),
+}
+
+boardinghouse_monsters_locations: Dict[str, Tuple[str, ...]] = {
+ Monster.shadow_brute: (BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1, BoardingHouseRegion.lost_valley_house_2,),
+ Monster.pepper_rex: (BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_1, BoardingHouseRegion.lost_valley_house_2,),
+ Monster.iridium_bat: (BoardingHouseRegion.lost_valley_ruins, BoardingHouseRegion.lost_valley_house_2,),
+ Monster.grub: (BoardingHouseRegion.abandoned_mines_1a, BoardingHouseRegion.abandoned_mines_1b, BoardingHouseRegion.abandoned_mines_2a,
+ BoardingHouseRegion.abandoned_mines_2b,),
+ Monster.bug: (BoardingHouseRegion.abandoned_mines_1a, BoardingHouseRegion.abandoned_mines_1b,),
+ Monster.bat: (BoardingHouseRegion.abandoned_mines_2a, BoardingHouseRegion.abandoned_mines_2b,),
+ Monster.cave_fly: (BoardingHouseRegion.abandoned_mines_3, BoardingHouseRegion.abandoned_mines_4, BoardingHouseRegion.abandoned_mines_5,),
+ Monster.frost_bat: (BoardingHouseRegion.abandoned_mines_3, BoardingHouseRegion.abandoned_mines_4, BoardingHouseRegion.abandoned_mines_5,),
+}
+
+modded_monsters_locations: Dict[str, Dict[str, Tuple[str, ...]]] = {
+ ModNames.sve: sve_monsters_locations,
+ ModNames.deepwoods: deepwoods_monsters_locations,
+ ModNames.boarding_house: boardinghouse_monsters_locations
+}
diff --git a/worlds/stardew_valley/mods/mod_regions.py b/worlds/stardew_valley/mods/mod_regions.py
index b05bc9538dba..df0a12f6ef18 100644
--- a/worlds/stardew_valley/mods/mod_regions.py
+++ b/worlds/stardew_valley/mods/mod_regions.py
@@ -1,8 +1,10 @@
-from ..strings.entrance_names import DeepWoodsEntrance, EugeneEntrance, \
- JasperEntrance, AlecEntrance, YobaEntrance, JunaEntrance, MagicEntrance, AyeishaEntrance, RileyEntrance
-from ..strings.region_names import Region, DeepWoodsRegion, EugeneRegion, JasperRegion, \
- AlecRegion, YobaRegion, JunaRegion, MagicRegion, AyeishaRegion, RileyRegion
-from ..region_classes import RegionData, ConnectionData, RandomizationFlag, ModRegionData
+from typing import Dict, List
+
+from ..strings.entrance_names import Entrance, DeepWoodsEntrance, EugeneEntrance, LaceyEntrance, BoardingHouseEntrance, \
+ JasperEntrance, AlecEntrance, YobaEntrance, JunaEntrance, MagicEntrance, AyeishaEntrance, RileyEntrance, SVEEntrance, AlectoEntrance
+from ..strings.region_names import Region, DeepWoodsRegion, EugeneRegion, JasperRegion, BoardingHouseRegion, \
+ AlecRegion, YobaRegion, JunaRegion, MagicRegion, AyeishaRegion, RileyRegion, SVERegion, AlectoRegion, LaceyRegion
+from ..region_classes import RegionData, ConnectionData, ModificationFlag, RandomizationFlag, ModRegionData
from .mod_data import ModNames
deep_woods_regions = [
@@ -131,6 +133,232 @@
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA)
]
+stardew_valley_expanded_regions = [
+ RegionData(Region.backwoods, [SVEEntrance.backwoods_to_grove]),
+ RegionData(SVERegion.enchanted_grove, [SVEEntrance.grove_to_outpost_warp, SVEEntrance.grove_to_wizard_warp,
+ SVEEntrance.grove_to_farm_warp, SVEEntrance.grove_to_guild_warp, SVEEntrance.grove_to_junimo_warp,
+ SVEEntrance.grove_to_spring_warp, SVEEntrance.grove_to_aurora_warp]),
+ RegionData(SVERegion.grove_farm_warp, [SVEEntrance.farm_warp_to_farm]),
+ RegionData(SVERegion.grove_aurora_warp, [SVEEntrance.aurora_warp_to_aurora]),
+ RegionData(SVERegion.grove_guild_warp, [SVEEntrance.guild_warp_to_guild]),
+ RegionData(SVERegion.grove_junimo_warp, [SVEEntrance.junimo_warp_to_junimo]),
+ RegionData(SVERegion.grove_spring_warp, [SVEEntrance.spring_warp_to_spring]),
+ RegionData(SVERegion.grove_outpost_warp, [SVEEntrance.outpost_warp_to_outpost]),
+ RegionData(SVERegion.grove_wizard_warp, [SVEEntrance.wizard_warp_to_wizard]),
+ RegionData(SVERegion.galmoran_outpost, [SVEEntrance.outpost_to_badlands_entrance, SVEEntrance.use_alesia_shop,
+ SVEEntrance.use_isaac_shop]),
+ RegionData(SVERegion.badlands_entrance, [SVEEntrance.badlands_entrance_to_badlands]),
+ RegionData(SVERegion.crimson_badlands, [SVEEntrance.badlands_to_cave]),
+ RegionData(SVERegion.badlands_cave),
+ RegionData(Region.bus_stop, [SVEEntrance.bus_stop_to_shed]),
+ RegionData(SVERegion.grandpas_shed, [SVEEntrance.grandpa_shed_to_interior, SVEEntrance.grandpa_shed_to_town]),
+ RegionData(SVERegion.grandpas_shed_interior, [SVEEntrance.grandpa_interior_to_upstairs]),
+ RegionData(SVERegion.grandpas_shed_upstairs),
+ RegionData(Region.forest,
+ [SVEEntrance.forest_to_fairhaven, SVEEntrance.forest_to_west, SVEEntrance.forest_to_lost_woods,
+ SVEEntrance.forest_to_bmv, SVEEntrance.forest_to_marnie_shed]),
+ RegionData(SVERegion.marnies_shed),
+ RegionData(SVERegion.fairhaven_farm),
+ RegionData(Region.town, [SVEEntrance.town_to_bmv, SVEEntrance.town_to_jenkins,
+ SVEEntrance.town_to_bridge, SVEEntrance.town_to_plot]),
+ RegionData(SVERegion.blue_moon_vineyard, [SVEEntrance.bmv_to_sophia, SVEEntrance.bmv_to_beach]),
+ RegionData(SVERegion.sophias_house),
+ RegionData(SVERegion.jenkins_residence, [SVEEntrance.jenkins_to_cellar]),
+ RegionData(SVERegion.jenkins_cellar),
+ RegionData(SVERegion.unclaimed_plot, [SVEEntrance.plot_to_bridge]),
+ RegionData(SVERegion.shearwater),
+ RegionData(Region.museum, [SVEEntrance.museum_to_gunther_bedroom]),
+ RegionData(SVERegion.gunther_bedroom),
+ RegionData(Region.fish_shop, [SVEEntrance.fish_shop_to_willy_bedroom]),
+ RegionData(SVERegion.willy_bedroom),
+ RegionData(Region.mountain, [SVEEntrance.mountain_to_guild_summit]),
+ RegionData(SVERegion.guild_summit, [SVEEntrance.guild_to_interior, SVEEntrance.guild_to_mines,
+ SVEEntrance.summit_to_highlands]),
+ RegionData(Region.railroad, [SVEEntrance.to_susan_house, SVEEntrance.enter_summit, SVEEntrance.railroad_to_grampleton_station]),
+ RegionData(SVERegion.grampleton_station, [SVEEntrance.grampleton_station_to_grampleton_suburbs]),
+ RegionData(SVERegion.grampleton_suburbs, [SVEEntrance.grampleton_suburbs_to_scarlett_house]),
+ RegionData(SVERegion.scarlett_house),
+ RegionData(Region.wizard_basement, [SVEEntrance.wizard_to_fable_reef]),
+ RegionData(SVERegion.fable_reef, [SVEEntrance.fable_reef_to_guild]),
+ RegionData(SVERegion.first_slash_guild, [SVEEntrance.first_slash_guild_to_hallway]),
+ RegionData(SVERegion.first_slash_hallway, [SVEEntrance.first_slash_hallway_to_room]),
+ RegionData(SVERegion.first_slash_spare_room),
+ RegionData(SVERegion.highlands_outside, [SVEEntrance.highlands_to_lance, SVEEntrance.highlands_to_cave]),
+ RegionData(SVERegion.highlands_cavern, [SVEEntrance.to_dwarf_prison]),
+ RegionData(SVERegion.dwarf_prison),
+ RegionData(SVERegion.lances_house, [SVEEntrance.lance_to_ladder]),
+ RegionData(SVERegion.lances_ladder, [SVEEntrance.lance_ladder_to_highlands]),
+ RegionData(SVERegion.forest_west, [SVEEntrance.forest_west_to_spring, SVEEntrance.west_to_aurora,
+ SVEEntrance.use_bear_shop]),
+ RegionData(SVERegion.aurora_vineyard, [SVEEntrance.to_aurora_basement]),
+ RegionData(SVERegion.aurora_vineyard_basement),
+ RegionData(Region.secret_woods, [SVEEntrance.secret_woods_to_west]),
+ RegionData(SVERegion.bear_shop),
+ RegionData(SVERegion.sprite_spring, [SVEEntrance.sprite_spring_to_cave]),
+ RegionData(SVERegion.sprite_spring_cave),
+ RegionData(SVERegion.lost_woods, [SVEEntrance.lost_woods_to_junimo_woods]),
+ RegionData(SVERegion.junimo_woods, [SVEEntrance.use_purple_junimo]),
+ RegionData(SVERegion.purple_junimo_shop),
+ RegionData(SVERegion.alesia_shop),
+ RegionData(SVERegion.isaac_shop),
+ RegionData(SVERegion.summit),
+ RegionData(SVERegion.susans_house),
+ RegionData(Region.mountain, [Entrance.mountain_to_adventurer_guild, Entrance.mountain_to_the_mines], ModificationFlag.MODIFIED)
+
+]
+
+mandatory_sve_connections = [
+ ConnectionData(SVEEntrance.town_to_jenkins, SVERegion.jenkins_residence, flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
+ ConnectionData(SVEEntrance.jenkins_to_cellar, SVERegion.jenkins_cellar, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.forest_to_bmv, SVERegion.blue_moon_vineyard),
+ ConnectionData(SVEEntrance.bmv_to_beach, Region.beach),
+ ConnectionData(SVEEntrance.town_to_plot, SVERegion.unclaimed_plot),
+ ConnectionData(SVEEntrance.town_to_bmv, SVERegion.blue_moon_vineyard),
+ ConnectionData(SVEEntrance.town_to_bridge, SVERegion.shearwater),
+ ConnectionData(SVEEntrance.plot_to_bridge, SVERegion.shearwater),
+ ConnectionData(SVEEntrance.bus_stop_to_shed, SVERegion.grandpas_shed),
+ ConnectionData(SVEEntrance.grandpa_shed_to_interior, SVERegion.grandpas_shed_interior, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
+ ConnectionData(SVEEntrance.grandpa_interior_to_upstairs, SVERegion.grandpas_shed_upstairs, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.grandpa_shed_to_town, Region.town),
+ ConnectionData(SVEEntrance.bmv_to_sophia, SVERegion.sophias_house, flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
+ ConnectionData(SVEEntrance.summit_to_highlands, SVERegion.highlands_outside, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(SVEEntrance.guild_to_interior, Region.adventurer_guild, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.backwoods_to_grove, SVERegion.enchanted_grove, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
+ ConnectionData(SVEEntrance.grove_to_outpost_warp, SVERegion.grove_outpost_warp),
+ ConnectionData(SVEEntrance.outpost_warp_to_outpost, SVERegion.galmoran_outpost, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.grove_to_wizard_warp, SVERegion.grove_wizard_warp),
+ ConnectionData(SVEEntrance.wizard_warp_to_wizard, Region.wizard_basement, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.grove_to_aurora_warp, SVERegion.grove_aurora_warp),
+ ConnectionData(SVEEntrance.aurora_warp_to_aurora, SVERegion.aurora_vineyard_basement, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.grove_to_farm_warp, SVERegion.grove_farm_warp),
+ ConnectionData(SVEEntrance.to_aurora_basement, SVERegion.aurora_vineyard_basement, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.farm_warp_to_farm, Region.farm),
+ ConnectionData(SVEEntrance.grove_to_guild_warp, SVERegion.grove_guild_warp),
+ ConnectionData(SVEEntrance.guild_warp_to_guild, Region.adventurer_guild, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.grove_to_junimo_warp, SVERegion.grove_junimo_warp),
+ ConnectionData(SVEEntrance.junimo_warp_to_junimo, SVERegion.junimo_woods, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.use_purple_junimo, SVERegion.purple_junimo_shop),
+ ConnectionData(SVEEntrance.grove_to_spring_warp, SVERegion.grove_spring_warp),
+ ConnectionData(SVEEntrance.spring_warp_to_spring, SVERegion.sprite_spring, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.wizard_to_fable_reef, SVERegion.fable_reef, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(SVEEntrance.fable_reef_to_guild, SVERegion.first_slash_guild, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(SVEEntrance.outpost_to_badlands_entrance, SVERegion.badlands_entrance, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.badlands_entrance_to_badlands, SVERegion.crimson_badlands, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.badlands_to_cave, SVERegion.badlands_cave, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.guild_to_mines, Region.mines, flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
+ ConnectionData(SVEEntrance.mountain_to_guild_summit, SVERegion.guild_summit),
+ ConnectionData(SVEEntrance.forest_to_west, SVERegion.forest_west),
+ ConnectionData(SVEEntrance.secret_woods_to_west, SVERegion.forest_west),
+ ConnectionData(SVEEntrance.west_to_aurora, SVERegion.aurora_vineyard, flag=RandomizationFlag.NON_PROGRESSION),
+ ConnectionData(SVEEntrance.forest_to_lost_woods, SVERegion.lost_woods),
+ ConnectionData(SVEEntrance.lost_woods_to_junimo_woods, SVERegion.junimo_woods),
+ ConnectionData(SVEEntrance.forest_to_marnie_shed, SVERegion.marnies_shed, flag=RandomizationFlag.NON_PROGRESSION),
+ ConnectionData(SVEEntrance.forest_west_to_spring, SVERegion.sprite_spring, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.to_susan_house, SVERegion.susans_house, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.enter_summit, SVERegion.summit, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.forest_to_fairhaven, SVERegion.fairhaven_farm, flag=RandomizationFlag.NON_PROGRESSION),
+ ConnectionData(SVEEntrance.highlands_to_lance, SVERegion.lances_house, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(SVEEntrance.lance_to_ladder, SVERegion.lances_ladder, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(SVEEntrance.lance_ladder_to_highlands, SVERegion.highlands_outside, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(SVEEntrance.highlands_to_cave, SVERegion.highlands_cavern, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(SVEEntrance.use_bear_shop, SVERegion.bear_shop),
+ ConnectionData(SVEEntrance.use_purple_junimo, SVERegion.purple_junimo_shop),
+ ConnectionData(SVEEntrance.use_alesia_shop, SVERegion.alesia_shop),
+ ConnectionData(SVEEntrance.use_isaac_shop, SVERegion.isaac_shop),
+ ConnectionData(SVEEntrance.to_dwarf_prison, SVERegion.dwarf_prison, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(SVEEntrance.railroad_to_grampleton_station, SVERegion.grampleton_station),
+ ConnectionData(SVEEntrance.grampleton_station_to_grampleton_suburbs, SVERegion.grampleton_suburbs),
+ ConnectionData(SVEEntrance.grampleton_suburbs_to_scarlett_house, SVERegion.scarlett_house, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.first_slash_guild_to_hallway, SVERegion.first_slash_hallway, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(SVEEntrance.first_slash_hallway_to_room, SVERegion.first_slash_spare_room, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(SVEEntrance.sprite_spring_to_cave, SVERegion.sprite_spring_cave, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.fish_shop_to_willy_bedroom, SVERegion.willy_bedroom, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(SVEEntrance.museum_to_gunther_bedroom, SVERegion.gunther_bedroom, flag=RandomizationFlag.BUILDINGS),
+]
+
+alecto_regions = [
+ RegionData(Region.witch_hut, [AlectoEntrance.witch_hut_to_witch_attic]),
+ RegionData(AlectoRegion.witch_attic)
+]
+
+alecto_entrances = [
+ ConnectionData(AlectoEntrance.witch_hut_to_witch_attic, AlectoRegion.witch_attic, flag=RandomizationFlag.BUILDINGS)
+]
+
+lacey_regions = [
+ RegionData(Region.forest, [LaceyEntrance.forest_to_hat_house]),
+ RegionData(LaceyRegion.hat_house)
+]
+
+lacey_entrances = [
+ ConnectionData(LaceyEntrance.forest_to_hat_house, LaceyRegion.hat_house, flag=RandomizationFlag.BUILDINGS)
+]
+
+boarding_house_regions = [
+ RegionData(Region.bus_stop, [BoardingHouseEntrance.bus_stop_to_boarding_house_plateau]),
+ RegionData(BoardingHouseRegion.boarding_house_plateau, [BoardingHouseEntrance.boarding_house_plateau_to_boarding_house_first,
+ BoardingHouseEntrance.boarding_house_plateau_to_buffalo_ranch,
+ BoardingHouseEntrance.boarding_house_plateau_to_abandoned_mines_entrance]),
+ RegionData(BoardingHouseRegion.boarding_house_first, [BoardingHouseEntrance.boarding_house_first_to_boarding_house_second]),
+ RegionData(BoardingHouseRegion.boarding_house_second),
+ RegionData(BoardingHouseRegion.buffalo_ranch),
+ RegionData(BoardingHouseRegion.abandoned_mines_entrance, [BoardingHouseEntrance.abandoned_mines_entrance_to_abandoned_mines_1a,
+ BoardingHouseEntrance.abandoned_mines_entrance_to_the_lost_valley]),
+ RegionData(BoardingHouseRegion.abandoned_mines_1a, [BoardingHouseEntrance.abandoned_mines_1a_to_abandoned_mines_1b]),
+ RegionData(BoardingHouseRegion.abandoned_mines_1b, [BoardingHouseEntrance.abandoned_mines_1b_to_abandoned_mines_2a]),
+ RegionData(BoardingHouseRegion.abandoned_mines_2a, [BoardingHouseEntrance.abandoned_mines_2a_to_abandoned_mines_2b]),
+ RegionData(BoardingHouseRegion.abandoned_mines_2b, [BoardingHouseEntrance.abandoned_mines_2b_to_abandoned_mines_3]),
+ RegionData(BoardingHouseRegion.abandoned_mines_3, [BoardingHouseEntrance.abandoned_mines_3_to_abandoned_mines_4]),
+ RegionData(BoardingHouseRegion.abandoned_mines_4, [BoardingHouseEntrance.abandoned_mines_4_to_abandoned_mines_5]),
+ RegionData(BoardingHouseRegion.abandoned_mines_5, [BoardingHouseEntrance.abandoned_mines_5_to_the_lost_valley]),
+ RegionData(BoardingHouseRegion.the_lost_valley, [BoardingHouseEntrance.the_lost_valley_to_gregory_tent,
+ BoardingHouseEntrance.lost_valley_to_lost_valley_minecart,
+ BoardingHouseEntrance.the_lost_valley_to_lost_valley_ruins]),
+ RegionData(BoardingHouseRegion.gregory_tent),
+ RegionData(BoardingHouseRegion.lost_valley_ruins, [BoardingHouseEntrance.lost_valley_ruins_to_lost_valley_house_1,
+ BoardingHouseEntrance.lost_valley_ruins_to_lost_valley_house_2]),
+ RegionData(BoardingHouseRegion.lost_valley_minecart),
+ RegionData(BoardingHouseRegion.lost_valley_house_1),
+ RegionData(BoardingHouseRegion.lost_valley_house_2)
+]
+
+boarding_house_entrances = [
+ ConnectionData(BoardingHouseEntrance.bus_stop_to_boarding_house_plateau, BoardingHouseRegion.boarding_house_plateau),
+ ConnectionData(BoardingHouseEntrance.boarding_house_plateau_to_boarding_house_first, BoardingHouseRegion.boarding_house_first,
+ flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
+ ConnectionData(BoardingHouseEntrance.boarding_house_first_to_boarding_house_second, BoardingHouseRegion.boarding_house_second,
+ flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(BoardingHouseEntrance.boarding_house_plateau_to_buffalo_ranch, BoardingHouseRegion.buffalo_ranch,
+ flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
+ ConnectionData(BoardingHouseEntrance.boarding_house_plateau_to_abandoned_mines_entrance, BoardingHouseRegion.abandoned_mines_entrance,
+ flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
+ ConnectionData(BoardingHouseEntrance.abandoned_mines_entrance_to_the_lost_valley, BoardingHouseRegion.lost_valley_minecart, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(BoardingHouseEntrance.abandoned_mines_entrance_to_abandoned_mines_1a, BoardingHouseRegion.abandoned_mines_1a,
+ flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(BoardingHouseEntrance.abandoned_mines_1a_to_abandoned_mines_1b, BoardingHouseRegion.abandoned_mines_1b, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(BoardingHouseEntrance.abandoned_mines_1b_to_abandoned_mines_2a, BoardingHouseRegion.abandoned_mines_2a, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(BoardingHouseEntrance.abandoned_mines_2a_to_abandoned_mines_2b, BoardingHouseRegion.abandoned_mines_2b, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(BoardingHouseEntrance.abandoned_mines_2b_to_abandoned_mines_3, BoardingHouseRegion.abandoned_mines_3, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(BoardingHouseEntrance.abandoned_mines_3_to_abandoned_mines_4, BoardingHouseRegion.abandoned_mines_4, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(BoardingHouseEntrance.abandoned_mines_4_to_abandoned_mines_5, BoardingHouseRegion.abandoned_mines_5, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(BoardingHouseEntrance.abandoned_mines_5_to_the_lost_valley, BoardingHouseRegion.the_lost_valley, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(BoardingHouseEntrance.the_lost_valley_to_gregory_tent, BoardingHouseRegion.gregory_tent, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(BoardingHouseEntrance.lost_valley_to_lost_valley_minecart, BoardingHouseRegion.lost_valley_minecart),
+ ConnectionData(BoardingHouseEntrance.the_lost_valley_to_lost_valley_ruins, BoardingHouseRegion.lost_valley_ruins, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(BoardingHouseEntrance.lost_valley_ruins_to_lost_valley_house_1, BoardingHouseRegion.lost_valley_house_1, flag=RandomizationFlag.BUILDINGS),
+ ConnectionData(BoardingHouseEntrance.lost_valley_ruins_to_lost_valley_house_2, BoardingHouseRegion.lost_valley_house_2, flag=RandomizationFlag.BUILDINGS)
+
+
+]
+
+vanilla_connections_to_remove_by_mod: Dict[str, List[ConnectionData]] = {
+ ModNames.sve: [ConnectionData(Entrance.mountain_to_the_mines, Region.mines,
+ flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
+ ConnectionData(Entrance.mountain_to_adventurer_guild, Region.adventurer_guild,
+ flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
+ ]
+}
+
ModDataList = {
ModNames.deepwoods: ModRegionData(ModNames.deepwoods, deep_woods_regions, deep_woods_entrances),
ModNames.eugene: ModRegionData(ModNames.eugene, eugene_regions, eugene_entrances),
@@ -141,4 +369,8 @@
ModNames.magic: ModRegionData(ModNames.magic, magic_regions, magic_entrances),
ModNames.ayeisha: ModRegionData(ModNames.ayeisha, ayeisha_regions, ayeisha_entrances),
ModNames.riley: ModRegionData(ModNames.riley, riley_regions, riley_entrances),
+ ModNames.sve: ModRegionData(ModNames.sve, stardew_valley_expanded_regions, mandatory_sve_connections),
+ ModNames.alecto: ModRegionData(ModNames.alecto, alecto_regions, alecto_entrances),
+ ModNames.lacey: ModRegionData(ModNames.lacey, lacey_regions, lacey_entrances),
+ ModNames.boarding_house: ModRegionData(ModNames.boarding_house, boarding_house_regions, boarding_house_entrances),
}
diff --git a/worlds/stardew_valley/options.py b/worlds/stardew_valley/options.py
index 267ebd7a63de..055407d97d4a 100644
--- a/worlds/stardew_valley/options.py
+++ b/worlds/stardew_valley/options.py
@@ -1,21 +1,32 @@
from dataclasses import dataclass
-from typing import Dict
+from typing import Protocol, ClassVar
-from Options import Range, NamedRange, Toggle, Choice, OptionSet, PerGameCommonOptions, DeathLink, Option
+from Options import Range, NamedRange, Toggle, Choice, OptionSet, PerGameCommonOptions, DeathLink
from .mods.mod_data import ModNames
+class StardewValleyOption(Protocol):
+ internal_name: ClassVar[str]
+
+
class Goal(Choice):
"""What's your goal with this play-through?
- Community Center: Complete the Community Center.
- Grandpa's Evaluation: Succeed grandpa's evaluation with 4 lit candles.
- Bottom of the Mines: Reach level 120 in the mineshaft.
- Cryptic Note: Complete the quest "Cryptic Note" where Mr Qi asks you to reach floor 100 in the Skull Cavern.
- Master Angler: Catch every fish in the game. Pairs well with Fishsanity.
- Complete Collection: Complete the museum by donating every possible item. Pairs well with Museumsanity.
- Full House: Get married and have two children. Pairs well with Friendsanity.
+ Community Center: Complete the Community Center
+ Grandpa's Evaluation: Succeed Grandpa's evaluation with 4 lit candles
+ Bottom of the Mines: Reach level 120 in the mineshaft
+ Cryptic Note: Complete the quest "Cryptic Note" where Mr Qi asks you to reach floor 100 in the Skull Cavern
+ Master Angler: Catch every fish. Adapts to chosen Fishsanity option
+ Complete Collection: Complete the museum by donating every possible item. Pairs well with Museumsanity
+ Full House: Get married and have two children. Pairs well with Friendsanity
Greatest Walnut Hunter: Find all 130 Golden Walnuts
- Perfection: Attain Perfection, based on the vanilla definition.
+ Protector of the Valley: Complete all the monster slayer goals. Adapts to Monstersanity
+ Full Shipment: Ship every item in the collection tab. Adapts to Shipsanity
+ Gourmet Chef: Cook every recipe. Adapts to Cooksanity
+ Craft Master: Craft every item.
+ Legend: Earn 10 000 000g
+ Mystery of the Stardrops: Find every stardrop
+ Allsanity: Complete every check in your slot
+ Perfection: Attain Perfection, based on the vanilla definition
"""
internal_name = "goal"
display_name = "Goal"
@@ -28,16 +39,18 @@ class Goal(Choice):
option_complete_collection = 5
option_full_house = 6
option_greatest_walnut_hunter = 7
+ option_protector_of_the_valley = 8
+ option_full_shipment = 9
+ option_gourmet_chef = 10
+ option_craft_master = 11
+ option_legend = 12
+ option_mystery_of_the_stardrops = 13
# option_junimo_kart =
# option_prairie_king =
# option_fector_challenge =
- # option_craft_master =
- # option_mystery_of_the_stardrops =
- # option_protector_of_the_valley =
- # option_full_shipment =
- # option_legend =
# option_beloved_farmer =
# option_master_of_the_five_ways =
+ option_allsanity = 24
option_perfection = 25
@classmethod
@@ -48,6 +61,20 @@ def get_option_name(cls, value) -> str:
return super().get_option_name(value)
+class FarmType(Choice):
+ """What farm to play on?"""
+ internal_name = "farm_type"
+ display_name = "Farm Type"
+ default = "random"
+ option_standard = 0
+ option_riverland = 1
+ option_forest = 2
+ option_hill_top = 3
+ option_wilderness = 4
+ option_four_corners = 5
+ option_beach = 6
+
+
class StartingMoney(NamedRange):
"""Amount of gold when arriving at the farm.
Set to -1 or unlimited for infinite money"""
@@ -90,28 +117,36 @@ class BundleRandomization(Choice):
"""What items are needed for the community center bundles?
Vanilla: Standard bundles from the vanilla game
Thematic: Every bundle will require random items compatible with their original theme
+ Remixed: Picks bundles at random from thematic, vanilla remixed and new custom ones
Shuffled: Every bundle will require random items and follow no particular structure"""
internal_name = "bundle_randomization"
display_name = "Bundle Randomization"
- default = 1
+ default = 2
option_vanilla = 0
option_thematic = 1
- option_shuffled = 2
+ option_remixed = 2
+ option_shuffled = 3
class BundlePrice(Choice):
"""How many items are needed for the community center bundles?
+ Minimum: Every bundle will require only one item
Very Cheap: Every bundle will require 2 items fewer than usual
Cheap: Every bundle will require 1 item fewer than usual
Normal: Every bundle will require the vanilla number of items
- Expensive: Every bundle will require 1 extra item when applicable"""
+ Expensive: Every bundle will require 1 extra item
+ Very Expensive: Every bundle will require 2 extra items
+ Maximum: Every bundle will require many extra items"""
internal_name = "bundle_price"
display_name = "Bundle Price"
- default = 2
- option_very_cheap = 0
- option_cheap = 1
- option_normal = 2
- option_expensive = 3
+ default = 0
+ option_minimum = -8
+ option_very_cheap = -2
+ option_cheap = -1
+ option_normal = 0
+ option_expensive = 1
+ option_very_expensive = 2
+ option_maximum = 8
class EntranceRandomization(Choice):
@@ -162,7 +197,7 @@ class Cropsanity(Choice):
"""Formerly named "Seed Shuffle"
Pierre now sells a random amount of seasonal seeds and Joja sells them without season requirements, but only in huge packs.
Disabled: All the seeds are unlocked from the start, there are no location checks for growing and harvesting crops
- Shuffled: Seeds are unlocked as archipelago items, for each seed there is a location check for growing and harvesting that crop
+ Enabled: Seeds are unlocked as archipelago items, for each seed there is a location check for growing and harvesting that crop
"""
internal_name = "cropsanity"
display_name = "Cropsanity"
@@ -189,12 +224,18 @@ class BackpackProgression(Choice):
class ToolProgression(Choice):
"""Shuffle the tool upgrades?
Vanilla: Clint will upgrade your tools with metal bars.
- Progressive: You will randomly find Progressive Tool upgrades."""
+ Progressive: You will randomly find Progressive Tool upgrades.
+ Cheap: Tool Upgrades will cost 2/5th as much
+ Very Cheap: Tool Upgrades will cost 1/5th as much"""
internal_name = "tool_progression"
display_name = "Tool Progression"
default = 1
- option_vanilla = 0
- option_progressive = 1
+ option_vanilla = 0b000 # 0
+ option_progressive = 0b001 # 1
+ option_vanilla_cheap = 0b010 # 2
+ option_vanilla_very_cheap = 0b100 # 4
+ option_progressive_cheap = 0b011 # 3
+ option_progressive_very_cheap = 0b101 # 5
class ElevatorProgression(Choice):
@@ -228,13 +269,18 @@ class BuildingProgression(Choice):
Progressive: You will receive the buildings and will be able to build the first one of each type for free,
once it is received. If you want more of the same building, it will cost the vanilla price.
Progressive early shipping bin: Same as Progressive, but the shipping bin will be placed early in the multiworld.
+ Cheap: Buildings will cost half as much
+ Very Cheap: Buildings will cost 1/5th as much
"""
internal_name = "building_progression"
display_name = "Building Progression"
- default = 2
- option_vanilla = 0
- option_progressive = 1
- option_progressive_early_shipping_bin = 2
+ default = 3
+ option_vanilla = 0b000 # 0
+ option_vanilla_cheap = 0b010 # 2
+ option_vanilla_very_cheap = 0b100 # 4
+ option_progressive = 0b001 # 1
+ option_progressive_cheap = 0b011 # 3
+ option_progressive_very_cheap = 0b101 # 5
class FestivalLocations(Choice):
@@ -283,19 +329,23 @@ class SpecialOrderLocations(Choice):
option_board_qi = 2
-class HelpWantedLocations(NamedRange):
- """Include location checks for Help Wanted quests
- Out of every 7 quests, 4 will be item deliveries, and then 1 of each for: Fishing, Gathering and Slaying Monsters.
- Choosing a multiple of 7 is recommended."""
- internal_name = "help_wanted_locations"
+class QuestLocations(NamedRange):
+ """Include location checks for quests
+ None: No quests are checks
+ Story: Only story quests are checks
+ Number: Story quests and help wanted quests are checks up to the specified amount. Multiple of 7 recommended
+ Out of every 7 help wanted quests, 4 will be item deliveries, and then 1 of each for: Fishing, Gathering and Slaying Monsters.
+ Extra Help wanted quests might be added if current settings don't have enough locations"""
+ internal_name = "quest_locations"
default = 7
range_start = 0
range_end = 56
# step = 7
- display_name = "Number of Help Wanted locations"
+ display_name = "Quest Locations"
special_range_names = {
- "none": 0,
+ "none": -1,
+ "story": 0,
"minimum": 7,
"normal": 14,
"lots": 28,
@@ -306,7 +356,7 @@ class HelpWantedLocations(NamedRange):
class Fishsanity(Choice):
"""Locations for catching a fish the first time?
None: There are no locations for catching fish
- Legendaries: Each of the 5 legendary fish are checks
+ Legendaries: Each of the 5 legendary fish are checks, plus the extended family if qi board is turned on
Special: A curated selection of strong fish are checks
Randomized: A random selection of fish are checks
All: Every single fish in the game is a location that contains an item. Pairs well with the Master Angler Goal
@@ -344,6 +394,115 @@ class Museumsanity(Choice):
option_all = 3
+class Monstersanity(Choice):
+ """Locations for slaying monsters?
+ None: There are no checks for slaying monsters
+ One per category: Every category visible at the adventure guild gives one check
+ One per Monster: Every unique monster gives one check
+ Monster Eradication Goals: The Monster Eradication Goals each contain one check
+ Short Monster Eradication Goals: The Monster Eradication Goals each contain one check, but are reduced by 60%
+ Very Short Monster Eradication Goals: The Monster Eradication Goals each contain one check, but are reduced by 90%
+ Progressive Eradication Goals: The Monster Eradication Goals each contain 5 checks, each 20% of the way
+ Split Eradication Goals: The Monster Eradication Goals are split by monsters, each monster has one check
+ """
+ internal_name = "monstersanity"
+ display_name = "Monstersanity"
+ default = 1
+ option_none = 0
+ option_one_per_category = 1
+ option_one_per_monster = 2
+ option_goals = 3
+ option_short_goals = 4
+ option_very_short_goals = 5
+ option_progressive_goals = 6
+ option_split_goals = 7
+
+
+class Shipsanity(Choice):
+ """Locations for shipping items?
+ None: There are no checks for shipping items
+ Crops: Every crop and forageable being shipped is a check
+ Fish: Every fish being shipped is a check except legendaries
+ Full Shipment: Every item in the Collections page is a check
+ Full Shipment With Fish: Every item in the Collections page and every fish is a check
+ Everything: Every item in the game that can be shipped is a check
+ """
+ internal_name = "shipsanity"
+ display_name = "Shipsanity"
+ default = 0
+ option_none = 0
+ option_crops = 1
+ # option_quality_crops = 2
+ option_fish = 3
+ # option_quality_fish = 4
+ option_full_shipment = 5
+ # option_quality_full_shipment = 6
+ option_full_shipment_with_fish = 7
+ # option_quality_full_shipment_with_fish = 8
+ option_everything = 9
+ # option_quality_everything = 10
+
+
+class Cooksanity(Choice):
+ """Locations for cooking food?
+ None: There are no checks for cooking
+ Queen of Sauce: Every Queen of Sauce Recipe can be cooked for a check
+ All: Every cooking recipe can be cooked for a check
+ """
+ internal_name = "cooksanity"
+ display_name = "Cooksanity"
+ default = 0
+ option_none = 0
+ option_queen_of_sauce = 1
+ option_all = 2
+
+
+class Chefsanity(NamedRange):
+ """Locations for leaning cooking recipes?
+ Vanilla: All cooking recipes are learned normally
+ Queen of Sauce: Every Queen of sauce episode is a check, all queen of sauce recipes are items
+ Purchases: Every purchasable recipe is a check
+ Friendship: Recipes obtained from friendship are checks
+ Skills: Recipes obtained from skills are checks
+ All: Learning every cooking recipe is a check
+ """
+ internal_name = "chefsanity"
+ display_name = "Chefsanity"
+ default = 0
+ range_start = 0
+ range_end = 15
+
+ option_none = 0b0000 # 0
+ option_queen_of_sauce = 0b0001 # 1
+ option_purchases = 0b0010 # 2
+ option_qos_and_purchases = 0b0011 # 3
+ option_skills = 0b0100 # 4
+ option_friendship = 0b1000 # 8
+ option_all = 0b1111 # 15
+
+ special_range_names = {
+ "none": 0b0000, # 0
+ "queen_of_sauce": 0b0001, # 1
+ "purchases": 0b0010, # 2
+ "qos_and_purchases": 0b0011, # 3
+ "skills": 0b0100, # 4
+ "friendship": 0b1000, # 8
+ "all": 0b1111, # 15
+ }
+
+
+class Craftsanity(Choice):
+ """Checks for crafting items?
+ If enabled, all recipes purchased in shops will be checks as well.
+ Recipes obtained from other sources will depend on related archipelago settings
+ """
+ internal_name = "craftsanity"
+ display_name = "Craftsanity"
+ default = 0
+ option_none = 0
+ option_all = 1
+
+
class Friendsanity(Choice):
"""Shuffle Friendships?
None: Friendship hearts are earned normally
@@ -530,13 +689,15 @@ class Mods(OptionSet):
ModNames.cooking_skill, ModNames.binning_skill, ModNames.juna,
ModNames.jasper, ModNames.alec, ModNames.yoba, ModNames.eugene,
ModNames.wellwick, ModNames.ginger, ModNames.shiko, ModNames.delores,
- ModNames.ayeisha, ModNames.riley, ModNames.skull_cavern_elevator
+ ModNames.ayeisha, ModNames.riley, ModNames.skull_cavern_elevator, ModNames.sve, ModNames.distant_lands,
+ ModNames.alecto, ModNames.lacey, ModNames.boarding_house
}
@dataclass
class StardewValleyOptions(PerGameCommonOptions):
goal: Goal
+ farm_type: FarmType
starting_money: StartingMoney
profit_margin: ProfitMargin
bundle_randomization: BundleRandomization
@@ -552,9 +713,14 @@ class StardewValleyOptions(PerGameCommonOptions):
elevator_progression: ElevatorProgression
arcade_machine_locations: ArcadeMachineLocations
special_order_locations: SpecialOrderLocations
- help_wanted_locations: HelpWantedLocations
+ quest_locations: QuestLocations
fishsanity: Fishsanity
museumsanity: Museumsanity
+ monstersanity: Monstersanity
+ shipsanity: Shipsanity
+ cooksanity: Cooksanity
+ chefsanity: Chefsanity
+ craftsanity: Craftsanity
friendsanity: Friendsanity
friendsanity_heart_size: FriendsanityHeartSize
movement_buff_number: NumberOfMovementBuffs
diff --git a/worlds/stardew_valley/presets.py b/worlds/stardew_valley/presets.py
index 8823c52e5b20..e75eb5c5fcde 100644
--- a/worlds/stardew_valley/presets.py
+++ b/worlds/stardew_valley/presets.py
@@ -3,14 +3,15 @@
from Options import Accessibility, ProgressionBalancing, DeathLink
from .options import Goal, StartingMoney, ProfitMargin, BundleRandomization, BundlePrice, EntranceRandomization, SeasonRandomization, Cropsanity, \
BackpackProgression, ToolProgression, ElevatorProgression, SkillProgression, BuildingProgression, FestivalLocations, ArcadeMachineLocations, \
- SpecialOrderLocations, HelpWantedLocations, Fishsanity, Museumsanity, Friendsanity, FriendsanityHeartSize, NumberOfMovementBuffs, NumberOfLuckBuffs, \
+ SpecialOrderLocations, QuestLocations, Fishsanity, Museumsanity, Friendsanity, FriendsanityHeartSize, NumberOfMovementBuffs, NumberOfLuckBuffs, \
ExcludeGingerIsland, TrapItems, MultipleDaySleepEnabled, MultipleDaySleepCost, ExperienceMultiplier, FriendshipMultiplier, DebrisMultiplier, QuickStart, \
- Gifting
+ Gifting, FarmType, Monstersanity, Shipsanity, Cooksanity, Chefsanity, Craftsanity
all_random_settings = {
"progression_balancing": "random",
"accessibility": "random",
Goal.internal_name: "random",
+ FarmType.internal_name: "random",
StartingMoney.internal_name: "random",
ProfitMargin.internal_name: "random",
BundleRandomization.internal_name: "random",
@@ -26,9 +27,14 @@
FestivalLocations.internal_name: "random",
ArcadeMachineLocations.internal_name: "random",
SpecialOrderLocations.internal_name: "random",
- HelpWantedLocations.internal_name: "random",
+ QuestLocations.internal_name: "random",
Fishsanity.internal_name: "random",
Museumsanity.internal_name: "random",
+ Monstersanity.internal_name: "random",
+ Shipsanity.internal_name: "random",
+ Cooksanity.internal_name: "random",
+ Chefsanity.internal_name: "random",
+ Craftsanity.internal_name: "random",
Friendsanity.internal_name: "random",
FriendsanityHeartSize.internal_name: "random",
NumberOfMovementBuffs.internal_name: "random",
@@ -49,6 +55,7 @@
"progression_balancing": ProgressionBalancing.default,
"accessibility": Accessibility.option_items,
Goal.internal_name: Goal.option_community_center,
+ FarmType.internal_name: "random",
StartingMoney.internal_name: "very rich",
ProfitMargin.internal_name: "double",
BundleRandomization.internal_name: BundleRandomization.option_thematic,
@@ -57,16 +64,21 @@
SeasonRandomization.internal_name: SeasonRandomization.option_randomized_not_winter,
Cropsanity.internal_name: Cropsanity.option_enabled,
BackpackProgression.internal_name: BackpackProgression.option_early_progressive,
- ToolProgression.internal_name: ToolProgression.option_progressive,
+ ToolProgression.internal_name: ToolProgression.option_progressive_very_cheap,
ElevatorProgression.internal_name: ElevatorProgression.option_progressive,
SkillProgression.internal_name: SkillProgression.option_progressive,
- BuildingProgression.internal_name: BuildingProgression.option_progressive_early_shipping_bin,
+ BuildingProgression.internal_name: BuildingProgression.option_progressive_very_cheap,
FestivalLocations.internal_name: FestivalLocations.option_easy,
ArcadeMachineLocations.internal_name: ArcadeMachineLocations.option_disabled,
SpecialOrderLocations.internal_name: SpecialOrderLocations.option_disabled,
- HelpWantedLocations.internal_name: "minimum",
+ QuestLocations.internal_name: "minimum",
Fishsanity.internal_name: Fishsanity.option_only_easy_fish,
Museumsanity.internal_name: Museumsanity.option_milestones,
+ Monstersanity.internal_name: Monstersanity.option_one_per_category,
+ Shipsanity.internal_name: Shipsanity.option_none,
+ Cooksanity.internal_name: Cooksanity.option_none,
+ Chefsanity.internal_name: Chefsanity.option_none,
+ Craftsanity.internal_name: Craftsanity.option_none,
Friendsanity.internal_name: Friendsanity.option_none,
FriendsanityHeartSize.internal_name: 4,
NumberOfMovementBuffs.internal_name: 8,
@@ -87,24 +99,30 @@
"progression_balancing": 25,
"accessibility": Accessibility.option_locations,
Goal.internal_name: Goal.option_community_center,
+ FarmType.internal_name: "random",
StartingMoney.internal_name: "rich",
ProfitMargin.internal_name: 150,
- BundleRandomization.internal_name: BundleRandomization.option_thematic,
+ BundleRandomization.internal_name: BundleRandomization.option_remixed,
BundlePrice.internal_name: BundlePrice.option_normal,
EntranceRandomization.internal_name: EntranceRandomization.option_non_progression,
SeasonRandomization.internal_name: SeasonRandomization.option_randomized,
Cropsanity.internal_name: Cropsanity.option_enabled,
BackpackProgression.internal_name: BackpackProgression.option_early_progressive,
- ToolProgression.internal_name: ToolProgression.option_progressive,
+ ToolProgression.internal_name: ToolProgression.option_progressive_cheap,
ElevatorProgression.internal_name: ElevatorProgression.option_progressive_from_previous_floor,
SkillProgression.internal_name: SkillProgression.option_progressive,
- BuildingProgression.internal_name: BuildingProgression.option_progressive_early_shipping_bin,
+ BuildingProgression.internal_name: BuildingProgression.option_progressive_cheap,
FestivalLocations.internal_name: FestivalLocations.option_hard,
ArcadeMachineLocations.internal_name: ArcadeMachineLocations.option_victories_easy,
SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_only,
- HelpWantedLocations.internal_name: "normal",
+ QuestLocations.internal_name: "normal",
Fishsanity.internal_name: Fishsanity.option_exclude_legendaries,
Museumsanity.internal_name: Museumsanity.option_milestones,
+ Monstersanity.internal_name: Monstersanity.option_one_per_monster,
+ Shipsanity.internal_name: Shipsanity.option_none,
+ Cooksanity.internal_name: Cooksanity.option_none,
+ Chefsanity.internal_name: Chefsanity.option_queen_of_sauce,
+ Craftsanity.internal_name: Craftsanity.option_none,
Friendsanity.internal_name: Friendsanity.option_starting_npcs,
FriendsanityHeartSize.internal_name: 4,
NumberOfMovementBuffs.internal_name: 6,
@@ -125,9 +143,10 @@
"progression_balancing": 0,
"accessibility": Accessibility.option_locations,
Goal.internal_name: Goal.option_grandpa_evaluation,
+ FarmType.internal_name: "random",
StartingMoney.internal_name: "extra",
ProfitMargin.internal_name: "normal",
- BundleRandomization.internal_name: BundleRandomization.option_thematic,
+ BundleRandomization.internal_name: BundleRandomization.option_remixed,
BundlePrice.internal_name: BundlePrice.option_expensive,
EntranceRandomization.internal_name: EntranceRandomization.option_buildings,
SeasonRandomization.internal_name: SeasonRandomization.option_randomized,
@@ -140,9 +159,14 @@
FestivalLocations.internal_name: FestivalLocations.option_hard,
ArcadeMachineLocations.internal_name: ArcadeMachineLocations.option_full_shuffling,
SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi,
- HelpWantedLocations.internal_name: "lots",
+ QuestLocations.internal_name: "lots",
Fishsanity.internal_name: Fishsanity.option_all,
Museumsanity.internal_name: Museumsanity.option_all,
+ Monstersanity.internal_name: Monstersanity.option_progressive_goals,
+ Shipsanity.internal_name: Shipsanity.option_crops,
+ Cooksanity.internal_name: Cooksanity.option_queen_of_sauce,
+ Chefsanity.internal_name: Chefsanity.option_qos_and_purchases,
+ Craftsanity.internal_name: Craftsanity.option_none,
Friendsanity.internal_name: Friendsanity.option_all,
FriendsanityHeartSize.internal_name: 4,
NumberOfMovementBuffs.internal_name: 4,
@@ -163,10 +187,11 @@
"progression_balancing": 0,
"accessibility": Accessibility.option_locations,
Goal.internal_name: Goal.option_community_center,
+ FarmType.internal_name: "random",
StartingMoney.internal_name: "vanilla",
ProfitMargin.internal_name: "half",
BundleRandomization.internal_name: BundleRandomization.option_shuffled,
- BundlePrice.internal_name: BundlePrice.option_expensive,
+ BundlePrice.internal_name: BundlePrice.option_very_expensive,
EntranceRandomization.internal_name: EntranceRandomization.option_buildings,
SeasonRandomization.internal_name: SeasonRandomization.option_randomized,
Cropsanity.internal_name: Cropsanity.option_enabled,
@@ -178,9 +203,14 @@
FestivalLocations.internal_name: FestivalLocations.option_hard,
ArcadeMachineLocations.internal_name: ArcadeMachineLocations.option_full_shuffling,
SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi,
- HelpWantedLocations.internal_name: "maximum",
+ QuestLocations.internal_name: "maximum",
Fishsanity.internal_name: Fishsanity.option_special,
Museumsanity.internal_name: Museumsanity.option_all,
+ Monstersanity.internal_name: Monstersanity.option_split_goals,
+ Shipsanity.internal_name: Shipsanity.option_full_shipment_with_fish,
+ Cooksanity.internal_name: Cooksanity.option_queen_of_sauce,
+ Chefsanity.internal_name: Chefsanity.option_qos_and_purchases,
+ Craftsanity.internal_name: Craftsanity.option_none,
Friendsanity.internal_name: Friendsanity.option_all_with_marriage,
FriendsanityHeartSize.internal_name: 4,
NumberOfMovementBuffs.internal_name: 2,
@@ -201,24 +231,30 @@
"progression_balancing": ProgressionBalancing.default,
"accessibility": Accessibility.option_items,
Goal.internal_name: Goal.option_bottom_of_the_mines,
+ FarmType.internal_name: "random",
StartingMoney.internal_name: "filthy rich",
ProfitMargin.internal_name: "quadruple",
- BundleRandomization.internal_name: BundleRandomization.option_thematic,
- BundlePrice.internal_name: BundlePrice.option_very_cheap,
+ BundleRandomization.internal_name: BundleRandomization.option_remixed,
+ BundlePrice.internal_name: BundlePrice.option_minimum,
EntranceRandomization.internal_name: EntranceRandomization.option_disabled,
SeasonRandomization.internal_name: SeasonRandomization.option_randomized_not_winter,
Cropsanity.internal_name: Cropsanity.option_disabled,
BackpackProgression.internal_name: BackpackProgression.option_early_progressive,
- ToolProgression.internal_name: ToolProgression.option_progressive,
+ ToolProgression.internal_name: ToolProgression.option_progressive_very_cheap,
ElevatorProgression.internal_name: ElevatorProgression.option_progressive_from_previous_floor,
SkillProgression.internal_name: SkillProgression.option_progressive,
- BuildingProgression.internal_name: BuildingProgression.option_progressive_early_shipping_bin,
+ BuildingProgression.internal_name: BuildingProgression.option_progressive_very_cheap,
FestivalLocations.internal_name: FestivalLocations.option_disabled,
ArcadeMachineLocations.internal_name: ArcadeMachineLocations.option_disabled,
SpecialOrderLocations.internal_name: SpecialOrderLocations.option_disabled,
- HelpWantedLocations.internal_name: "none",
+ QuestLocations.internal_name: "none",
Fishsanity.internal_name: Fishsanity.option_none,
Museumsanity.internal_name: Museumsanity.option_none,
+ Monstersanity.internal_name: Monstersanity.option_none,
+ Shipsanity.internal_name: Shipsanity.option_none,
+ Cooksanity.internal_name: Cooksanity.option_none,
+ Chefsanity.internal_name: Chefsanity.option_none,
+ Craftsanity.internal_name: Craftsanity.option_none,
Friendsanity.internal_name: Friendsanity.option_none,
FriendsanityHeartSize.internal_name: 4,
NumberOfMovementBuffs.internal_name: 10,
@@ -235,10 +271,11 @@
"death_link": "false",
}
-lowsanity_settings = {
+minsanity_settings = {
"progression_balancing": ProgressionBalancing.default,
"accessibility": Accessibility.option_minimal,
Goal.internal_name: Goal.default,
+ FarmType.internal_name: "random",
StartingMoney.internal_name: StartingMoney.default,
ProfitMargin.internal_name: ProfitMargin.default,
BundleRandomization.internal_name: BundleRandomization.default,
@@ -254,9 +291,14 @@
FestivalLocations.internal_name: FestivalLocations.option_disabled,
ArcadeMachineLocations.internal_name: ArcadeMachineLocations.option_disabled,
SpecialOrderLocations.internal_name: SpecialOrderLocations.option_disabled,
- HelpWantedLocations.internal_name: "none",
+ QuestLocations.internal_name: "none",
Fishsanity.internal_name: Fishsanity.option_none,
Museumsanity.internal_name: Museumsanity.option_none,
+ Monstersanity.internal_name: Monstersanity.option_none,
+ Shipsanity.internal_name: Shipsanity.option_none,
+ Cooksanity.internal_name: Cooksanity.option_none,
+ Chefsanity.internal_name: Chefsanity.option_none,
+ Craftsanity.internal_name: Craftsanity.option_none,
Friendsanity.internal_name: Friendsanity.option_none,
FriendsanityHeartSize.internal_name: FriendsanityHeartSize.default,
NumberOfMovementBuffs.internal_name: NumberOfMovementBuffs.default,
@@ -277,6 +319,7 @@
"progression_balancing": ProgressionBalancing.default,
"accessibility": Accessibility.option_locations,
Goal.internal_name: Goal.default,
+ FarmType.internal_name: "random",
StartingMoney.internal_name: StartingMoney.default,
ProfitMargin.internal_name: ProfitMargin.default,
BundleRandomization.internal_name: BundleRandomization.default,
@@ -288,13 +331,18 @@
ToolProgression.internal_name: ToolProgression.option_progressive,
ElevatorProgression.internal_name: ElevatorProgression.option_progressive,
SkillProgression.internal_name: SkillProgression.option_progressive,
- BuildingProgression.internal_name: BuildingProgression.option_progressive_early_shipping_bin,
+ BuildingProgression.internal_name: BuildingProgression.option_progressive,
FestivalLocations.internal_name: FestivalLocations.option_hard,
ArcadeMachineLocations.internal_name: ArcadeMachineLocations.option_full_shuffling,
SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi,
- HelpWantedLocations.internal_name: "maximum",
+ QuestLocations.internal_name: "maximum",
Fishsanity.internal_name: Fishsanity.option_all,
Museumsanity.internal_name: Museumsanity.option_all,
+ Monstersanity.internal_name: Monstersanity.option_progressive_goals,
+ Shipsanity.internal_name: Shipsanity.option_everything,
+ Cooksanity.internal_name: Cooksanity.option_all,
+ Chefsanity.internal_name: Chefsanity.option_all,
+ Craftsanity.internal_name: Craftsanity.option_all,
Friendsanity.internal_name: Friendsanity.option_all,
FriendsanityHeartSize.internal_name: 1,
NumberOfMovementBuffs.internal_name: 12,
@@ -318,6 +366,6 @@
"Hard": hard_settings,
"Nightmare": nightmare_settings,
"Short": short_settings,
- "Lowsanity": lowsanity_settings,
+ "Minsanity": minsanity_settings,
"Allsanity": allsanity_settings,
}
diff --git a/worlds/stardew_valley/region_classes.py b/worlds/stardew_valley/region_classes.py
index 9db322416a4d..eaabcfa5fd36 100644
--- a/worlds/stardew_valley/region_classes.py
+++ b/worlds/stardew_valley/region_classes.py
@@ -5,6 +5,10 @@
connector_keyword = " to "
+class ModificationFlag(IntFlag):
+ NOT_MODIFIED = 0
+ MODIFIED = 1
+
class RandomizationFlag(IntFlag):
NOT_RANDOMIZED = 0b0
PELICAN_TOWN = 0b11111
@@ -20,6 +24,7 @@ class RandomizationFlag(IntFlag):
class RegionData:
name: str
exits: List[str] = field(default_factory=list)
+ flag: ModificationFlag = ModificationFlag.NOT_MODIFIED
def get_merged_with(self, exits: List[str]):
merged_exits = []
@@ -29,6 +34,10 @@ def get_merged_with(self, exits: List[str]):
merged_exits = list(set(merged_exits))
return RegionData(self.name, merged_exits)
+ def get_without_exit(self, exit_to_remove: str):
+ exits = [exit for exit in self.exits if exit != exit_to_remove]
+ return RegionData(self.name, exits)
+
def get_clone(self):
return self.get_merged_with(None)
diff --git a/worlds/stardew_valley/regions.py b/worlds/stardew_valley/regions.py
index d8e224841143..4284b438f806 100644
--- a/worlds/stardew_valley/regions.py
+++ b/worlds/stardew_valley/regions.py
@@ -2,11 +2,11 @@
from typing import Iterable, Dict, Protocol, List, Tuple, Set
from BaseClasses import Region, Entrance
-from .options import EntranceRandomization, ExcludeGingerIsland, Museumsanity
+from .options import EntranceRandomization, ExcludeGingerIsland, Museumsanity, StardewValleyOptions
from .strings.entrance_names import Entrance
from .strings.region_names import Region
-from .region_classes import RegionData, ConnectionData, RandomizationFlag
-from .mods.mod_regions import ModDataList
+from .region_classes import RegionData, ConnectionData, RandomizationFlag, ModificationFlag
+from .mods.mod_regions import ModDataList, vanilla_connections_to_remove_by_mod
class RegionFactory(Protocol):
@@ -17,12 +17,18 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region:
vanilla_regions = [
RegionData(Region.menu, [Entrance.to_stardew_valley]),
RegionData(Region.stardew_valley, [Entrance.to_farmhouse]),
- RegionData(Region.farm_house, [Entrance.farmhouse_to_farm, Entrance.downstairs_to_cellar]),
+ RegionData(Region.farm_house, [Entrance.farmhouse_to_farm, Entrance.downstairs_to_cellar, Entrance.farmhouse_cooking, Entrance.watch_queen_of_sauce]),
RegionData(Region.cellar),
+ RegionData(Region.kitchen),
+ RegionData(Region.queen_of_sauce),
RegionData(Region.farm,
[Entrance.farm_to_backwoods, Entrance.farm_to_bus_stop, Entrance.farm_to_forest,
Entrance.farm_to_farmcave, Entrance.enter_greenhouse,
- Entrance.use_desert_obelisk, Entrance.use_island_obelisk]),
+ Entrance.enter_coop, Entrance.enter_barn,
+ Entrance.enter_shed, Entrance.enter_slime_hutch,
+ Entrance.farming, Entrance.shipping]),
+ RegionData(Region.farming),
+ RegionData(Region.shipping),
RegionData(Region.backwoods, [Entrance.backwoods_to_mountain]),
RegionData(Region.bus_stop,
[Entrance.bus_stop_to_town, Entrance.take_bus_to_desert, Entrance.bus_stop_to_tunnel_entrance]),
@@ -30,8 +36,22 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region:
[Entrance.forest_to_town, Entrance.enter_secret_woods, Entrance.forest_to_wizard_tower,
Entrance.forest_to_marnie_ranch,
Entrance.forest_to_leah_cottage, Entrance.forest_to_sewer,
- Entrance.buy_from_traveling_merchant]),
- RegionData(Region.traveling_cart),
+ Entrance.buy_from_traveling_merchant,
+ Entrance.attend_flower_dance, Entrance.attend_festival_of_ice]),
+ RegionData(Region.traveling_cart, [Entrance.buy_from_traveling_merchant_sunday,
+ Entrance.buy_from_traveling_merchant_monday,
+ Entrance.buy_from_traveling_merchant_tuesday,
+ Entrance.buy_from_traveling_merchant_wednesday,
+ Entrance.buy_from_traveling_merchant_thursday,
+ Entrance.buy_from_traveling_merchant_friday,
+ Entrance.buy_from_traveling_merchant_saturday]),
+ RegionData(Region.traveling_cart_sunday),
+ RegionData(Region.traveling_cart_monday),
+ RegionData(Region.traveling_cart_tuesday),
+ RegionData(Region.traveling_cart_wednesday),
+ RegionData(Region.traveling_cart_thursday),
+ RegionData(Region.traveling_cart_friday),
+ RegionData(Region.traveling_cart_saturday),
RegionData(Region.farm_cave),
RegionData(Region.greenhouse),
RegionData(Region.mountain,
@@ -51,15 +71,19 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region:
Entrance.town_to_sam_house, Entrance.town_to_haley_house, Entrance.town_to_sewer,
Entrance.town_to_clint_blacksmith,
Entrance.town_to_museum,
- Entrance.town_to_jojamart]),
- RegionData(Region.beach,
- [Entrance.beach_to_willy_fish_shop, Entrance.enter_elliott_house, Entrance.enter_tide_pools]),
+ Entrance.town_to_jojamart, Entrance.purchase_movie_ticket,
+ Entrance.attend_egg_festival, Entrance.attend_fair, Entrance.attend_spirit_eve, Entrance.attend_winter_star]),
+ RegionData(Region.beach, [Entrance.beach_to_willy_fish_shop, Entrance.enter_elliott_house, Entrance.enter_tide_pools,
+ Entrance.fishing,
+ Entrance.attend_luau, Entrance.attend_moonlight_jellies, Entrance.attend_night_market]),
+ RegionData(Region.fishing),
RegionData(Region.railroad, [Entrance.enter_bathhouse_entrance, Entrance.enter_witch_warp_cave]),
RegionData(Region.ranch),
RegionData(Region.leah_house),
RegionData(Region.sewer, [Entrance.enter_mutant_bug_lair]),
RegionData(Region.mutant_bug_lair),
- RegionData(Region.wizard_tower, [Entrance.enter_wizard_basement]),
+ RegionData(Region.wizard_tower, [Entrance.enter_wizard_basement,
+ Entrance.use_desert_obelisk, Entrance.use_island_obelisk]),
RegionData(Region.wizard_basement),
RegionData(Region.tent),
RegionData(Region.carpenter, [Entrance.enter_sebastian_room]),
@@ -79,14 +103,27 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region:
RegionData(Region.pierre_store, [Entrance.enter_sunroom]),
RegionData(Region.sunroom),
RegionData(Region.saloon, [Entrance.play_journey_of_the_prairie_king, Entrance.play_junimo_kart]),
+ RegionData(Region.jotpk_world_1, [Entrance.reach_jotpk_world_2]),
+ RegionData(Region.jotpk_world_2, [Entrance.reach_jotpk_world_3]),
+ RegionData(Region.jotpk_world_3),
+ RegionData(Region.junimo_kart_1, [Entrance.reach_junimo_kart_2]),
+ RegionData(Region.junimo_kart_2, [Entrance.reach_junimo_kart_3]),
+ RegionData(Region.junimo_kart_3),
RegionData(Region.alex_house),
RegionData(Region.trailer),
RegionData(Region.mayor_house),
RegionData(Region.sam_house),
RegionData(Region.haley_house),
- RegionData(Region.blacksmith),
+ RegionData(Region.blacksmith, [Entrance.blacksmith_copper]),
+ RegionData(Region.blacksmith_copper, [Entrance.blacksmith_iron]),
+ RegionData(Region.blacksmith_iron, [Entrance.blacksmith_gold]),
+ RegionData(Region.blacksmith_gold, [Entrance.blacksmith_iridium]),
+ RegionData(Region.blacksmith_iridium),
RegionData(Region.museum),
- RegionData(Region.jojamart),
+ RegionData(Region.jojamart, [Entrance.enter_abandoned_jojamart]),
+ RegionData(Region.abandoned_jojamart, [Entrance.enter_movie_theater]),
+ RegionData(Region.movie_ticket_stand),
+ RegionData(Region.movie_theater),
RegionData(Region.fish_shop, [Entrance.fish_shop_to_boat_tunnel]),
RegionData(Region.boat_tunnel, [Entrance.boat_to_ginger_island]),
RegionData(Region.elliott_house),
@@ -105,18 +142,16 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region:
RegionData(Region.oasis, [Entrance.enter_casino]),
RegionData(Region.casino),
RegionData(Region.skull_cavern_entrance, [Entrance.enter_skull_cavern]),
- RegionData(Region.skull_cavern, [Entrance.mine_to_skull_cavern_floor_25, Entrance.mine_to_skull_cavern_floor_50,
- Entrance.mine_to_skull_cavern_floor_75, Entrance.mine_to_skull_cavern_floor_100,
- Entrance.mine_to_skull_cavern_floor_125, Entrance.mine_to_skull_cavern_floor_150,
- Entrance.mine_to_skull_cavern_floor_175, Entrance.mine_to_skull_cavern_floor_200]),
- RegionData(Region.skull_cavern_25),
- RegionData(Region.skull_cavern_50),
- RegionData(Region.skull_cavern_75),
- RegionData(Region.skull_cavern_100),
- RegionData(Region.skull_cavern_125),
- RegionData(Region.skull_cavern_150),
- RegionData(Region.skull_cavern_175),
- RegionData(Region.skull_cavern_200),
+ RegionData(Region.skull_cavern, [Entrance.mine_to_skull_cavern_floor_25]),
+ RegionData(Region.skull_cavern_25, [Entrance.mine_to_skull_cavern_floor_50]),
+ RegionData(Region.skull_cavern_50, [Entrance.mine_to_skull_cavern_floor_75]),
+ RegionData(Region.skull_cavern_75, [Entrance.mine_to_skull_cavern_floor_100]),
+ RegionData(Region.skull_cavern_100, [Entrance.mine_to_skull_cavern_floor_125]),
+ RegionData(Region.skull_cavern_125, [Entrance.mine_to_skull_cavern_floor_150]),
+ RegionData(Region.skull_cavern_150, [Entrance.mine_to_skull_cavern_floor_175]),
+ RegionData(Region.skull_cavern_175, [Entrance.mine_to_skull_cavern_floor_200]),
+ RegionData(Region.skull_cavern_200, [Entrance.enter_dangerous_skull_cavern]),
+ RegionData(Region.dangerous_skull_cavern),
RegionData(Region.island_south, [Entrance.island_south_to_west, Entrance.island_south_to_north,
Entrance.island_south_to_east, Entrance.island_south_to_southeast,
Entrance.use_island_resort,
@@ -144,7 +179,7 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region:
RegionData(Region.volcano_dwarf_shop),
RegionData(Region.volcano_floor_10),
RegionData(Region.island_trader),
- RegionData(Region.island_farmhouse),
+ RegionData(Region.island_farmhouse, [Entrance.island_cooking]),
RegionData(Region.gourmand_frog_cave),
RegionData(Region.colored_crystals_cave),
RegionData(Region.shipwreck),
@@ -156,50 +191,49 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region:
[Entrance.dig_site_to_professor_snail_cave, Entrance.parrot_express_dig_site_to_volcano,
Entrance.parrot_express_dig_site_to_docks, Entrance.parrot_express_dig_site_to_jungle]),
RegionData(Region.professor_snail_cave),
- RegionData(Region.jotpk_world_1, [Entrance.reach_jotpk_world_2]),
- RegionData(Region.jotpk_world_2, [Entrance.reach_jotpk_world_3]),
- RegionData(Region.jotpk_world_3),
- RegionData(Region.junimo_kart_1, [Entrance.reach_junimo_kart_2]),
- RegionData(Region.junimo_kart_2, [Entrance.reach_junimo_kart_3]),
- RegionData(Region.junimo_kart_3),
RegionData(Region.mines, [Entrance.talk_to_mines_dwarf,
- Entrance.dig_to_mines_floor_5, Entrance.dig_to_mines_floor_10,
- Entrance.dig_to_mines_floor_15, Entrance.dig_to_mines_floor_20,
- Entrance.dig_to_mines_floor_25, Entrance.dig_to_mines_floor_30,
- Entrance.dig_to_mines_floor_35, Entrance.dig_to_mines_floor_40,
- Entrance.dig_to_mines_floor_45, Entrance.dig_to_mines_floor_50,
- Entrance.dig_to_mines_floor_55, Entrance.dig_to_mines_floor_60,
- Entrance.dig_to_mines_floor_65, Entrance.dig_to_mines_floor_70,
- Entrance.dig_to_mines_floor_75, Entrance.dig_to_mines_floor_80,
- Entrance.dig_to_mines_floor_85, Entrance.dig_to_mines_floor_90,
- Entrance.dig_to_mines_floor_95, Entrance.dig_to_mines_floor_100,
- Entrance.dig_to_mines_floor_105, Entrance.dig_to_mines_floor_110,
- Entrance.dig_to_mines_floor_115, Entrance.dig_to_mines_floor_120]),
+ Entrance.dig_to_mines_floor_5]),
RegionData(Region.mines_dwarf_shop),
- RegionData(Region.mines_floor_5),
- RegionData(Region.mines_floor_10),
- RegionData(Region.mines_floor_15),
- RegionData(Region.mines_floor_20),
- RegionData(Region.mines_floor_25),
- RegionData(Region.mines_floor_30),
- RegionData(Region.mines_floor_35),
- RegionData(Region.mines_floor_40),
- RegionData(Region.mines_floor_45),
- RegionData(Region.mines_floor_50),
- RegionData(Region.mines_floor_55),
- RegionData(Region.mines_floor_60),
- RegionData(Region.mines_floor_65),
- RegionData(Region.mines_floor_70),
- RegionData(Region.mines_floor_75),
- RegionData(Region.mines_floor_80),
- RegionData(Region.mines_floor_85),
- RegionData(Region.mines_floor_90),
- RegionData(Region.mines_floor_95),
- RegionData(Region.mines_floor_100),
- RegionData(Region.mines_floor_105),
- RegionData(Region.mines_floor_110),
- RegionData(Region.mines_floor_115),
- RegionData(Region.mines_floor_120),
+ RegionData(Region.mines_floor_5, [Entrance.dig_to_mines_floor_10]),
+ RegionData(Region.mines_floor_10, [Entrance.dig_to_mines_floor_15]),
+ RegionData(Region.mines_floor_15, [Entrance.dig_to_mines_floor_20]),
+ RegionData(Region.mines_floor_20, [Entrance.dig_to_mines_floor_25]),
+ RegionData(Region.mines_floor_25, [Entrance.dig_to_mines_floor_30]),
+ RegionData(Region.mines_floor_30, [Entrance.dig_to_mines_floor_35]),
+ RegionData(Region.mines_floor_35, [Entrance.dig_to_mines_floor_40]),
+ RegionData(Region.mines_floor_40, [Entrance.dig_to_mines_floor_45]),
+ RegionData(Region.mines_floor_45, [Entrance.dig_to_mines_floor_50]),
+ RegionData(Region.mines_floor_50, [Entrance.dig_to_mines_floor_55]),
+ RegionData(Region.mines_floor_55, [Entrance.dig_to_mines_floor_60]),
+ RegionData(Region.mines_floor_60, [Entrance.dig_to_mines_floor_65]),
+ RegionData(Region.mines_floor_65, [Entrance.dig_to_mines_floor_70]),
+ RegionData(Region.mines_floor_70, [Entrance.dig_to_mines_floor_75]),
+ RegionData(Region.mines_floor_75, [Entrance.dig_to_mines_floor_80]),
+ RegionData(Region.mines_floor_80, [Entrance.dig_to_mines_floor_85]),
+ RegionData(Region.mines_floor_85, [Entrance.dig_to_mines_floor_90]),
+ RegionData(Region.mines_floor_90, [Entrance.dig_to_mines_floor_95]),
+ RegionData(Region.mines_floor_95, [Entrance.dig_to_mines_floor_100]),
+ RegionData(Region.mines_floor_100, [Entrance.dig_to_mines_floor_105]),
+ RegionData(Region.mines_floor_105, [Entrance.dig_to_mines_floor_110]),
+ RegionData(Region.mines_floor_110, [Entrance.dig_to_mines_floor_115]),
+ RegionData(Region.mines_floor_115, [Entrance.dig_to_mines_floor_120]),
+ RegionData(Region.mines_floor_120, [Entrance.dig_to_dangerous_mines_20, Entrance.dig_to_dangerous_mines_60, Entrance.dig_to_dangerous_mines_100]),
+ RegionData(Region.dangerous_mines_20),
+ RegionData(Region.dangerous_mines_60),
+ RegionData(Region.dangerous_mines_100),
+ RegionData(Region.coop),
+ RegionData(Region.barn),
+ RegionData(Region.shed),
+ RegionData(Region.slime_hutch),
+ RegionData(Region.egg_festival),
+ RegionData(Region.flower_dance),
+ RegionData(Region.luau),
+ RegionData(Region.moonlight_jellies),
+ RegionData(Region.fair),
+ RegionData(Region.spirit_eve),
+ RegionData(Region.festival_of_ice),
+ RegionData(Region.night_market),
+ RegionData(Region.winter_star),
]
# Exists and where they lead
@@ -208,13 +242,21 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region:
ConnectionData(Entrance.to_farmhouse, Region.farm_house),
ConnectionData(Entrance.farmhouse_to_farm, Region.farm),
ConnectionData(Entrance.downstairs_to_cellar, Region.cellar),
+ ConnectionData(Entrance.farmhouse_cooking, Region.kitchen),
+ ConnectionData(Entrance.watch_queen_of_sauce, Region.queen_of_sauce),
ConnectionData(Entrance.farm_to_backwoods, Region.backwoods),
ConnectionData(Entrance.farm_to_bus_stop, Region.bus_stop),
ConnectionData(Entrance.farm_to_forest, Region.forest),
ConnectionData(Entrance.farm_to_farmcave, Region.farm_cave, flag=RandomizationFlag.NON_PROGRESSION),
+ ConnectionData(Entrance.farming, Region.farming),
ConnectionData(Entrance.enter_greenhouse, Region.greenhouse),
+ ConnectionData(Entrance.enter_coop, Region.coop),
+ ConnectionData(Entrance.enter_barn, Region.barn),
+ ConnectionData(Entrance.enter_shed, Region.shed),
+ ConnectionData(Entrance.enter_slime_hutch, Region.slime_hutch),
+ ConnectionData(Entrance.shipping, Region.shipping),
ConnectionData(Entrance.use_desert_obelisk, Region.desert),
- ConnectionData(Entrance.use_island_obelisk, Region.island_south),
+ ConnectionData(Entrance.use_island_obelisk, Region.island_south, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.use_farm_obelisk, Region.farm),
ConnectionData(Entrance.backwoods_to_mountain, Region.mountain),
ConnectionData(Entrance.bus_stop_to_town, Region.town),
@@ -232,6 +274,13 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region:
ConnectionData(Entrance.enter_secret_woods, Region.secret_woods),
ConnectionData(Entrance.forest_to_sewer, Region.sewer, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.buy_from_traveling_merchant, Region.traveling_cart),
+ ConnectionData(Entrance.buy_from_traveling_merchant_sunday, Region.traveling_cart_sunday),
+ ConnectionData(Entrance.buy_from_traveling_merchant_monday, Region.traveling_cart_monday),
+ ConnectionData(Entrance.buy_from_traveling_merchant_tuesday, Region.traveling_cart_tuesday),
+ ConnectionData(Entrance.buy_from_traveling_merchant_wednesday, Region.traveling_cart_wednesday),
+ ConnectionData(Entrance.buy_from_traveling_merchant_thursday, Region.traveling_cart_thursday),
+ ConnectionData(Entrance.buy_from_traveling_merchant_friday, Region.traveling_cart_friday),
+ ConnectionData(Entrance.buy_from_traveling_merchant_saturday, Region.traveling_cart_saturday),
ConnectionData(Entrance.town_to_sewer, Region.sewer, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_mutant_bug_lair, Region.mutant_bug_lair, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.mountain_to_railroad, Region.railroad),
@@ -267,6 +316,10 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region:
ConnectionData(Entrance.enter_sunroom, Region.sunroom, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.town_to_clint_blacksmith, Region.blacksmith,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
+ ConnectionData(Entrance.blacksmith_copper, Region.blacksmith_copper),
+ ConnectionData(Entrance.blacksmith_iron, Region.blacksmith_iron),
+ ConnectionData(Entrance.blacksmith_gold, Region.blacksmith_gold),
+ ConnectionData(Entrance.blacksmith_iridium, Region.blacksmith_iridium),
ConnectionData(Entrance.town_to_saloon, Region.saloon,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.play_journey_of_the_prairie_king, Region.jotpk_world_1),
@@ -289,6 +342,9 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region:
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_jojamart, Region.jojamart,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
+ ConnectionData(Entrance.purchase_movie_ticket, Region.movie_ticket_stand),
+ ConnectionData(Entrance.enter_abandoned_jojamart, Region.abandoned_jojamart),
+ ConnectionData(Entrance.enter_movie_theater, Region.movie_theater),
ConnectionData(Entrance.town_to_beach, Region.beach),
ConnectionData(Entrance.enter_elliott_house, Region.elliott_house,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
@@ -296,8 +352,9 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region:
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.fish_shop_to_boat_tunnel, Region.boat_tunnel,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
- ConnectionData(Entrance.boat_to_ginger_island, Region.island_south),
+ ConnectionData(Entrance.boat_to_ginger_island, Region.island_south, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.enter_tide_pools, Region.tide_pools),
+ ConnectionData(Entrance.fishing, Region.fishing),
ConnectionData(Entrance.mountain_to_the_mines, Region.mines,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.talk_to_mines_dwarf, Region.mines_dwarf_shop),
@@ -325,6 +382,9 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region:
ConnectionData(Entrance.dig_to_mines_floor_110, Region.mines_floor_110),
ConnectionData(Entrance.dig_to_mines_floor_115, Region.mines_floor_115),
ConnectionData(Entrance.dig_to_mines_floor_120, Region.mines_floor_120),
+ ConnectionData(Entrance.dig_to_dangerous_mines_20, Region.dangerous_mines_20, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.dig_to_dangerous_mines_60, Region.dangerous_mines_60, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.dig_to_dangerous_mines_100, Region.dangerous_mines_100, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.enter_skull_cavern_entrance, Region.skull_cavern_entrance,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_oasis, Region.oasis,
@@ -339,6 +399,7 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region:
ConnectionData(Entrance.mine_to_skull_cavern_floor_150, Region.skull_cavern_150),
ConnectionData(Entrance.mine_to_skull_cavern_floor_175, Region.skull_cavern_175),
ConnectionData(Entrance.mine_to_skull_cavern_floor_200, Region.skull_cavern_200),
+ ConnectionData(Entrance.enter_dangerous_skull_cavern, Region.dangerous_skull_cavern, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.enter_witch_warp_cave, Region.witch_warp_cave, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_witch_swamp, Region.witch_swamp, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_witch_hut, Region.witch_hut, flag=RandomizationFlag.BUILDINGS),
@@ -352,17 +413,17 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region:
ConnectionData(Entrance.island_south_to_east, Region.island_east, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_south_to_southeast, Region.island_south_east,
flag=RandomizationFlag.GINGER_ISLAND),
- ConnectionData(Entrance.use_island_resort, Region.island_resort),
+ ConnectionData(Entrance.use_island_resort, Region.island_resort, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_west_to_islandfarmhouse, Region.island_farmhouse,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.island_cooking, Region.kitchen),
ConnectionData(Entrance.island_west_to_gourmand_cave, Region.gourmand_frog_cave,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_west_to_crystals_cave, Region.colored_crystals_cave,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_west_to_shipwreck, Region.shipwreck,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
- ConnectionData(Entrance.island_west_to_qi_walnut_room, Region.qi_walnut_room,
- flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.island_west_to_qi_walnut_room, Region.qi_walnut_room, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_east_to_leo_hut, Region.leo_hut,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_east_to_island_shrine, Region.island_shrine,
@@ -378,21 +439,30 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region:
ConnectionData(Entrance.volcano_to_secret_beach, Region.volcano_secret_beach,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.talk_to_island_trader, Region.island_trader, flag=RandomizationFlag.GINGER_ISLAND),
- ConnectionData(Entrance.climb_to_volcano_5, Region.volcano_floor_5),
- ConnectionData(Entrance.talk_to_volcano_dwarf, Region.volcano_dwarf_shop),
- ConnectionData(Entrance.climb_to_volcano_10, Region.volcano_floor_10),
- ConnectionData(Entrance.parrot_express_jungle_to_docks, Region.island_south),
- ConnectionData(Entrance.parrot_express_dig_site_to_docks, Region.island_south),
- ConnectionData(Entrance.parrot_express_volcano_to_docks, Region.island_south),
- ConnectionData(Entrance.parrot_express_volcano_to_jungle, Region.island_west),
- ConnectionData(Entrance.parrot_express_docks_to_jungle, Region.island_west),
- ConnectionData(Entrance.parrot_express_dig_site_to_jungle, Region.island_west),
- ConnectionData(Entrance.parrot_express_docks_to_dig_site, Region.dig_site),
- ConnectionData(Entrance.parrot_express_volcano_to_dig_site, Region.dig_site),
- ConnectionData(Entrance.parrot_express_jungle_to_dig_site, Region.dig_site),
- ConnectionData(Entrance.parrot_express_dig_site_to_volcano, Region.island_north),
- ConnectionData(Entrance.parrot_express_docks_to_volcano, Region.island_north),
- ConnectionData(Entrance.parrot_express_jungle_to_volcano, Region.island_north),
+ ConnectionData(Entrance.climb_to_volcano_5, Region.volcano_floor_5, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.talk_to_volcano_dwarf, Region.volcano_dwarf_shop, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.climb_to_volcano_10, Region.volcano_floor_10, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.parrot_express_jungle_to_docks, Region.island_south, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.parrot_express_dig_site_to_docks, Region.island_south, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.parrot_express_volcano_to_docks, Region.island_south, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.parrot_express_volcano_to_jungle, Region.island_west, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.parrot_express_docks_to_jungle, Region.island_west, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.parrot_express_dig_site_to_jungle, Region.island_west, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.parrot_express_docks_to_dig_site, Region.dig_site, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.parrot_express_volcano_to_dig_site, Region.dig_site, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.parrot_express_jungle_to_dig_site, Region.dig_site, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.parrot_express_dig_site_to_volcano, Region.island_north, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.parrot_express_docks_to_volcano, Region.island_north, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.parrot_express_jungle_to_volcano, Region.island_north, flag=RandomizationFlag.GINGER_ISLAND),
+ ConnectionData(Entrance.attend_egg_festival, Region.egg_festival),
+ ConnectionData(Entrance.attend_flower_dance, Region.flower_dance),
+ ConnectionData(Entrance.attend_luau, Region.luau),
+ ConnectionData(Entrance.attend_moonlight_jellies, Region.moonlight_jellies),
+ ConnectionData(Entrance.attend_fair, Region.fair),
+ ConnectionData(Entrance.attend_spirit_eve, Region.spirit_eve),
+ ConnectionData(Entrance.attend_festival_of_ice, Region.festival_of_ice),
+ ConnectionData(Entrance.attend_night_market, Region.night_market),
+ ConnectionData(Entrance.attend_winter_star, Region.winter_star),
]
@@ -409,78 +479,105 @@ def create_final_regions(world_options) -> List[RegionData]:
(region for region in final_regions if region.name == mod_region.name), None)
if existing_region:
final_regions.remove(existing_region)
+ if ModificationFlag.MODIFIED in mod_region.flag:
+ mod_region = modify_vanilla_regions(existing_region, mod_region)
final_regions.append(existing_region.get_merged_with(mod_region.exits))
continue
-
final_regions.append(mod_region.get_clone())
+
return final_regions
-def create_final_connections(world_options) -> List[ConnectionData]:
- final_connections = []
- final_connections.extend(vanilla_connections)
- if world_options.mods is None:
- return final_connections
- for mod in world_options.mods.value:
+def create_final_connections_and_regions(world_options) -> Tuple[Dict[str, ConnectionData], Dict[str, RegionData]]:
+ regions_data: Dict[str, RegionData] = {region.name: region for region in create_final_regions(world_options)}
+ connections = {connection.name: connection for connection in vanilla_connections}
+ connections = modify_connections_for_mods(connections, world_options.mods)
+ include_island = world_options.exclude_ginger_island == ExcludeGingerIsland.option_false
+ return remove_ginger_island_regions_and_connections(regions_data, connections, include_island)
+
+
+def remove_ginger_island_regions_and_connections(regions_by_name: Dict[str, RegionData], connections: Dict[str, ConnectionData], include_island: bool):
+ if include_island:
+ return connections, regions_by_name
+ for connection_name in list(connections):
+ connection = connections[connection_name]
+ if connection.flag & RandomizationFlag.GINGER_ISLAND:
+ regions_by_name.pop(connection.destination, None)
+ connections.pop(connection_name)
+ regions_by_name = {name: regions_by_name[name].get_without_exit(connection_name) for name in regions_by_name}
+ return connections, regions_by_name
+
+
+def modify_connections_for_mods(connections: Dict[str, ConnectionData], mods) -> Dict[str, ConnectionData]:
+ if mods is None:
+ return connections
+ for mod in mods.value:
if mod not in ModDataList:
continue
- final_connections.extend(ModDataList[mod].connections)
- return final_connections
+ if mod in vanilla_connections_to_remove_by_mod:
+ for connection_data in vanilla_connections_to_remove_by_mod[mod]:
+ connections.pop(connection_data.name)
+ connections.update({connection.name: connection for connection in ModDataList[mod].connections})
+ return connections
-def create_regions(region_factory: RegionFactory, random: Random, world_options) -> Tuple[
- Dict[str, Region], Dict[str, str]]:
- final_regions = create_final_regions(world_options)
- regions: Dict[str: Region] = {region.name: region_factory(region.name, region.exits) for region in
- final_regions}
- entrances: Dict[str: Entrance] = {entrance.name: entrance
- for region in regions.values()
- for entrance in region.exits}
+def modify_vanilla_regions(existing_region: RegionData, modified_region: RegionData) -> RegionData:
- regions_by_name: Dict[str, RegionData] = {region.name: region for region in final_regions}
- connections, randomized_data = randomize_connections(random, world_options, regions_by_name)
+ updated_region = existing_region
+ region_exits = updated_region.exits
+ modified_exits = modified_region.exits
+ for exits in modified_exits:
+ region_exits.remove(exits)
- for connection in connections:
- if connection.name in entrances:
- entrances[connection.name].connect(regions[connection.destination])
+ return updated_region
+
+
+def create_regions(region_factory: RegionFactory, random: Random, world_options: StardewValleyOptions) -> Tuple[
+ Dict[str, Region], Dict[str, Entrance], Dict[str, str]]:
+ entrances_data, regions_data = create_final_connections_and_regions(world_options)
+ regions_by_name: Dict[str: Region] = {region_name: region_factory(region_name, regions_data[region_name].exits) for region_name in regions_data}
+ entrances_by_name: Dict[str: Entrance] = {entrance.name: entrance for region in regions_by_name.values() for entrance in region.exits
+ if entrance.name in entrances_data}
- return regions, randomized_data
+ connections, randomized_data = randomize_connections(random, world_options, regions_data, entrances_data)
+
+ for connection in connections:
+ if connection.name in entrances_by_name:
+ entrances_by_name[connection.name].connect(regions_by_name[connection.destination])
+ return regions_by_name, entrances_by_name, randomized_data
-def randomize_connections(random: Random, world_options, regions_by_name) -> Tuple[
- List[ConnectionData], Dict[str, str]]:
- connections_to_randomize = []
- final_connections = create_final_connections(world_options)
- connections_by_name: Dict[str, ConnectionData] = {connection.name: connection for connection in final_connections}
+def randomize_connections(random: Random, world_options: StardewValleyOptions, regions_by_name: Dict[str, RegionData],
+ connections_by_name: Dict[str, ConnectionData]) -> Tuple[List[ConnectionData], Dict[str, str]]:
+ connections_to_randomize: List[ConnectionData] = []
if world_options.entrance_randomization == EntranceRandomization.option_pelican_town:
- connections_to_randomize = [connection for connection in final_connections if
- RandomizationFlag.PELICAN_TOWN in connection.flag]
+ connections_to_randomize = [connections_by_name[connection] for connection in connections_by_name if
+ RandomizationFlag.PELICAN_TOWN in connections_by_name[connection].flag]
elif world_options.entrance_randomization == EntranceRandomization.option_non_progression:
- connections_to_randomize = [connection for connection in final_connections if
- RandomizationFlag.NON_PROGRESSION in connection.flag]
+ connections_to_randomize = [connections_by_name[connection] for connection in connections_by_name if
+ RandomizationFlag.NON_PROGRESSION in connections_by_name[connection].flag]
elif world_options.entrance_randomization == EntranceRandomization.option_buildings:
- connections_to_randomize = [connection for connection in final_connections if
- RandomizationFlag.BUILDINGS in connection.flag]
+ connections_to_randomize = [connections_by_name[connection] for connection in connections_by_name if
+ RandomizationFlag.BUILDINGS in connections_by_name[connection].flag]
elif world_options.entrance_randomization == EntranceRandomization.option_chaos:
- connections_to_randomize = [connection for connection in final_connections if
- RandomizationFlag.BUILDINGS in connection.flag]
- connections_to_randomize = exclude_island_if_necessary(connections_to_randomize, world_options)
+ connections_to_randomize = [connections_by_name[connection] for connection in connections_by_name if
+ RandomizationFlag.BUILDINGS in connections_by_name[connection].flag]
+ connections_to_randomize = remove_excluded_entrances(connections_to_randomize, world_options)
# On Chaos, we just add the connections to randomize, unshuffled, and the client does it every day
randomized_data_for_mod = {}
for connection in connections_to_randomize:
randomized_data_for_mod[connection.name] = connection.name
randomized_data_for_mod[connection.reverse] = connection.reverse
- return final_connections, randomized_data_for_mod
+ return list(connections_by_name.values()), randomized_data_for_mod
connections_to_randomize = remove_excluded_entrances(connections_to_randomize, world_options)
-
random.shuffle(connections_to_randomize)
destination_pool = list(connections_to_randomize)
random.shuffle(destination_pool)
randomized_connections = randomize_chosen_connections(connections_to_randomize, destination_pool)
- add_non_randomized_connections(final_connections, connections_to_randomize, randomized_connections)
+ add_non_randomized_connections(list(connections_by_name.values()), connections_to_randomize, randomized_connections)
swap_connections_until_valid(regions_by_name, connections_by_name, randomized_connections, connections_to_randomize, random)
randomized_connections_for_generation = create_connections_for_generation(randomized_connections)
@@ -489,25 +586,14 @@ def randomize_connections(random: Random, world_options, regions_by_name) -> Tup
return randomized_connections_for_generation, randomized_data_for_mod
-def remove_excluded_entrances(connections_to_randomize, world_options):
+def remove_excluded_entrances(connections_to_randomize: List[ConnectionData], world_options: StardewValleyOptions) -> List[ConnectionData]:
exclude_island = world_options.exclude_ginger_island == ExcludeGingerIsland.option_true
- exclude_sewers = world_options.museumsanity == Museumsanity.option_none
if exclude_island:
connections_to_randomize = [connection for connection in connections_to_randomize if RandomizationFlag.GINGER_ISLAND not in connection.flag]
- if exclude_sewers:
- connections_to_randomize = [connection for connection in connections_to_randomize if Region.sewer not in connection.name or Region.sewer not in connection.reverse]
return connections_to_randomize
-def exclude_island_if_necessary(connections_to_randomize: List[ConnectionData], world_options) -> List[ConnectionData]:
- exclude_island = world_options.exclude_ginger_island == ExcludeGingerIsland.option_true
- if exclude_island:
- connections_to_randomize = [connection for connection in connections_to_randomize if
- RandomizationFlag.GINGER_ISLAND not in connection.flag]
- return connections_to_randomize
-
-
def randomize_chosen_connections(connections_to_randomize: List[ConnectionData],
destination_pool: List[ConnectionData]) -> Dict[ConnectionData, ConnectionData]:
randomized_connections = {}
@@ -517,8 +603,7 @@ def randomize_chosen_connections(connections_to_randomize: List[ConnectionData],
return randomized_connections
-def create_connections_for_generation(randomized_connections: Dict[ConnectionData, ConnectionData]) -> List[
- ConnectionData]:
+def create_connections_for_generation(randomized_connections: Dict[ConnectionData, ConnectionData]) -> List[ConnectionData]:
connections = []
for connection in randomized_connections:
destination = randomized_connections[connection]
@@ -536,37 +621,50 @@ def create_data_for_mod(randomized_connections: Dict[ConnectionData, ConnectionD
add_to_mod_data(connection, destination, randomized_data_for_mod)
return randomized_data_for_mod
+
def add_to_mod_data(connection: ConnectionData, destination: ConnectionData, randomized_data_for_mod: Dict[str, str]):
randomized_data_for_mod[connection.name] = destination.name
randomized_data_for_mod[destination.reverse] = connection.reverse
-def add_non_randomized_connections(connections, connections_to_randomize: List[ConnectionData],
+def add_non_randomized_connections(all_connections: List[ConnectionData], connections_to_randomize: List[ConnectionData],
randomized_connections: Dict[ConnectionData, ConnectionData]):
- for connection in connections:
+ for connection in all_connections:
if connection in connections_to_randomize:
continue
randomized_connections[connection] = connection
-def swap_connections_until_valid(regions_by_name, connections_by_name, randomized_connections: Dict[ConnectionData, ConnectionData],
+def swap_connections_until_valid(regions_by_name, connections_by_name: Dict[str, ConnectionData], randomized_connections: Dict[ConnectionData, ConnectionData],
connections_to_randomize: List[ConnectionData], random: Random):
while True:
reachable_regions, unreachable_regions = find_reachable_regions(regions_by_name, connections_by_name, randomized_connections)
if not unreachable_regions:
return randomized_connections
- swap_one_connection(regions_by_name, connections_by_name, randomized_connections, reachable_regions,
- unreachable_regions, connections_to_randomize, random)
+ swap_one_random_connection(regions_by_name, connections_by_name, randomized_connections, reachable_regions,
+ unreachable_regions, connections_to_randomize, random)
+
+
+def region_should_be_reachable(region_name: str, connections_in_slot: Iterable[ConnectionData]) -> bool:
+ if region_name == Region.menu:
+ return True
+ for connection in connections_in_slot:
+ if region_name == connection.destination:
+ return True
+ return False
def find_reachable_regions(regions_by_name, connections_by_name,
randomized_connections: Dict[ConnectionData, ConnectionData]):
reachable_regions = {Region.menu}
unreachable_regions = {region for region in regions_by_name.keys()}
+ # unreachable_regions = {region for region in regions_by_name.keys() if region_should_be_reachable(region, connections_by_name.values())}
unreachable_regions.remove(Region.menu)
exits_to_explore = list(regions_by_name[Region.menu].exits)
while exits_to_explore:
exit_name = exits_to_explore.pop()
+ # if exit_name not in connections_by_name:
+ # continue
exit_connection = connections_by_name[exit_name]
replaced_connection = randomized_connections[exit_connection]
target_region_name = replaced_connection.destination
@@ -580,9 +678,9 @@ def find_reachable_regions(regions_by_name, connections_by_name,
return reachable_regions, unreachable_regions
-def swap_one_connection(regions_by_name, connections_by_name,randomized_connections: Dict[ConnectionData, ConnectionData],
- reachable_regions: Set[str], unreachable_regions: Set[str],
- connections_to_randomize: List[ConnectionData], random: Random):
+def swap_one_random_connection(regions_by_name, connections_by_name, randomized_connections: Dict[ConnectionData, ConnectionData],
+ reachable_regions: Set[str], unreachable_regions: Set[str],
+ connections_to_randomize: List[ConnectionData], random: Random):
randomized_connections_already_shuffled = {connection: randomized_connections[connection]
for connection in randomized_connections
if connection != randomized_connections[connection]}
@@ -604,7 +702,11 @@ def swap_one_connection(regions_by_name, connections_by_name,randomized_connecti
chosen_reachable_entrance_name = random.choice(chosen_reachable_region.exits)
chosen_reachable_entrance = connections_by_name[chosen_reachable_entrance_name]
- reachable_destination = randomized_connections[chosen_reachable_entrance]
- unreachable_destination = randomized_connections[chosen_unreachable_entrance]
- randomized_connections[chosen_reachable_entrance] = unreachable_destination
- randomized_connections[chosen_unreachable_entrance] = reachable_destination
+ swap_two_connections(chosen_reachable_entrance, chosen_unreachable_entrance, randomized_connections)
+
+
+def swap_two_connections(entrance_1, entrance_2, randomized_connections):
+ reachable_destination = randomized_connections[entrance_1]
+ unreachable_destination = randomized_connections[entrance_2]
+ randomized_connections[entrance_1] = unreachable_destination
+ randomized_connections[entrance_2] = reachable_destination
diff --git a/worlds/stardew_valley/requirements.txt b/worlds/stardew_valley/requirements.txt
index a7141f6aa805..b0922176e43b 100644
--- a/worlds/stardew_valley/requirements.txt
+++ b/worlds/stardew_valley/requirements.txt
@@ -1 +1 @@
-importlib_resources; python_version <= '3.8'
\ No newline at end of file
+importlib_resources; python_version <= '3.8'
diff --git a/worlds/stardew_valley/rules.py b/worlds/stardew_valley/rules.py
index f56dec39a1f0..8002031ac792 100644
--- a/worlds/stardew_valley/rules.py
+++ b/worlds/stardew_valley/rules.py
@@ -1,28 +1,45 @@
import itertools
-from typing import List
+from typing import List, Dict, Set
from BaseClasses import MultiWorld
from worlds.generic import Rules as MultiWorldRules
-from .options import StardewValleyOptions, ToolProgression, BuildingProgression, SkillProgression, ExcludeGingerIsland, Cropsanity, SpecialOrderLocations, Museumsanity, \
- BackpackProgression, ArcadeMachineLocations
-from .strings.entrance_names import dig_to_mines_floor, dig_to_skull_floor, Entrance, move_to_woods_depth, \
- DeepWoodsEntrance, AlecEntrance, MagicEntrance
-from .data.museum_data import all_museum_items, all_museum_minerals, all_museum_artifacts, \
- dwarf_scrolls, skeleton_front, \
- skeleton_middle, skeleton_back, all_museum_items_by_name, Artifact
-from .strings.region_names import Region
+from . import locations
+from .bundles.bundle_room import BundleRoom
+from .data.craftable_data import all_crafting_recipes_by_name
+from .data.museum_data import all_museum_items, dwarf_scrolls, skeleton_front, skeleton_middle, skeleton_back, all_museum_items_by_name, all_museum_minerals, \
+ all_museum_artifacts, Artifact
+from .data.recipe_data import all_cooking_recipes_by_name
+from .locations import LocationTags
+from .logic.logic import StardewLogic
+from .logic.time_logic import MAX_MONTHS
+from .logic.tool_logic import tool_upgrade_prices
from .mods.mod_data import ModNames
-from .mods.logic import magic, deepwoods
-from .locations import LocationTags, locations_by_tag
-from .logic import StardewLogic, And, tool_upgrade_prices
+from .options import StardewValleyOptions, Friendsanity
+from .options import ToolProgression, BuildingProgression, ExcludeGingerIsland, SpecialOrderLocations, Museumsanity, BackpackProgression, Shipsanity, \
+ Monstersanity, Chefsanity, Craftsanity, ArcadeMachineLocations, Cooksanity, Cropsanity, SkillProgression
+from .stardew_rule import And, StardewRule
+from .stardew_rule.indirect_connection import look_for_indirect_connection
+from .strings.ap_names.event_names import Event
+from .strings.ap_names.mods.mod_items import SVEQuestItem, SVERunes
from .strings.ap_names.transport_names import Transportation
from .strings.artisan_good_names import ArtisanGood
+from .strings.building_names import Building
+from .strings.bundle_names import CCRoom
from .strings.calendar_names import Weekday
-from .strings.craftable_names import Craftable
+from .strings.craftable_names import Bomb
+from .strings.crop_names import Fruit
+from .strings.entrance_names import dig_to_mines_floor, dig_to_skull_floor, Entrance, move_to_woods_depth, DeepWoodsEntrance, AlecEntrance, \
+ SVEEntrance, LaceyEntrance, BoardingHouseEntrance
+from .strings.generic_names import Generic
from .strings.material_names import Material
from .strings.metal_names import MetalBar
+from .strings.performance_names import Performance
+from .strings.quest_names import Quest
+from .strings.region_names import Region
+from .strings.season_names import Season
from .strings.skill_names import ModSkill, Skill
from .strings.tool_names import Tool, ToolMaterial
+from .strings.tv_channel_names import Channel
from .strings.villager_names import NPC, ModNPC
from .strings.wallet_item_names import Wallet
@@ -32,267 +49,335 @@ def set_rules(world):
world_options = world.options
player = world.player
logic = world.logic
- current_bundles = world.modified_bundles
-
- all_location_names = list(location.name for location in multiworld.get_locations(player))
+ bundle_rooms: List[BundleRoom] = world.modified_bundles
- set_entrance_rules(logic, multiworld, player, world_options)
+ all_location_names = set(location.name for location in multiworld.get_locations(player))
+ set_entrance_rules(logic, multiworld, player, world_options)
set_ginger_island_rules(logic, multiworld, player, world_options)
- # Those checks do not exist if ToolProgression is vanilla
- if world_options.tool_progression != ToolProgression.option_vanilla:
- MultiWorldRules.add_rule(multiworld.get_location("Purchase Fiberglass Rod", player),
- (logic.has_skill_level(Skill.fishing, 2) & logic.can_spend_money(1800)).simplify())
- MultiWorldRules.add_rule(multiworld.get_location("Purchase Iridium Rod", player),
- (logic.has_skill_level(Skill.fishing, 6) & logic.can_spend_money(7500)).simplify())
-
- materials = [None, "Copper", "Iron", "Gold", "Iridium"]
- tool = [Tool.hoe, Tool.pickaxe, Tool.axe, Tool.watering_can, Tool.watering_can, Tool.trash_can]
- for (previous, material), tool in itertools.product(zip(materials[:4], materials[1:]), tool):
- if previous is None:
- MultiWorldRules.add_rule(multiworld.get_location(f"{material} {tool} Upgrade", player),
- (logic.has(f"{material} Ore") &
- logic.can_spend_money(tool_upgrade_prices[material])).simplify())
- else:
- MultiWorldRules.add_rule(multiworld.get_location(f"{material} {tool} Upgrade", player),
- (logic.has(f"{material} Ore") & logic.has_tool(tool, previous) &
- logic.can_spend_money(tool_upgrade_prices[material])).simplify())
-
+ set_tool_rules(logic, multiworld, player, world_options)
set_skills_rules(logic, multiworld, player, world_options)
-
- # Bundles
- for bundle in current_bundles.values():
- location = multiworld.get_location(bundle.get_name_with_bundle(), player)
- rules = logic.can_complete_bundle(bundle.requirements, bundle.number_required)
- simplified_rules = rules.simplify()
- MultiWorldRules.set_rule(location, simplified_rules)
- MultiWorldRules.add_rule(multiworld.get_location("Complete Crafts Room", player),
- And(logic.can_reach_location(bundle.name)
- for bundle in locations_by_tag[LocationTags.CRAFTS_ROOM_BUNDLE]).simplify())
- MultiWorldRules.add_rule(multiworld.get_location("Complete Pantry", player),
- And(logic.can_reach_location(bundle.name)
- for bundle in locations_by_tag[LocationTags.PANTRY_BUNDLE]).simplify())
- MultiWorldRules.add_rule(multiworld.get_location("Complete Fish Tank", player),
- And(logic.can_reach_location(bundle.name)
- for bundle in locations_by_tag[LocationTags.FISH_TANK_BUNDLE]).simplify())
- MultiWorldRules.add_rule(multiworld.get_location("Complete Boiler Room", player),
- And(logic.can_reach_location(bundle.name)
- for bundle in locations_by_tag[LocationTags.BOILER_ROOM_BUNDLE]).simplify())
- MultiWorldRules.add_rule(multiworld.get_location("Complete Bulletin Board", player),
- And(logic.can_reach_location(bundle.name)
- for bundle
- in locations_by_tag[LocationTags.BULLETIN_BOARD_BUNDLE]).simplify())
- MultiWorldRules.add_rule(multiworld.get_location("Complete Vault", player),
- And(logic.can_reach_location(bundle.name)
- for bundle in locations_by_tag[LocationTags.VAULT_BUNDLE]).simplify())
-
- # Buildings
- if world_options.building_progression != BuildingProgression.option_vanilla:
- for building in locations_by_tag[LocationTags.BUILDING_BLUEPRINT]:
- if building.mod_name is not None and building.mod_name not in world_options.mods:
- continue
- MultiWorldRules.set_rule(multiworld.get_location(building.name, player),
- logic.building_rules[building.name.replace(" Blueprint", "")].simplify())
-
+ set_bundle_rules(bundle_rooms, logic, multiworld, player)
+ set_building_rules(logic, multiworld, player, world_options)
set_cropsanity_rules(all_location_names, logic, multiworld, player, world_options)
set_story_quests_rules(all_location_names, logic, multiworld, player, world_options)
set_special_order_rules(all_location_names, logic, multiworld, player, world_options)
set_help_wanted_quests_rules(logic, multiworld, player, world_options)
set_fishsanity_rules(all_location_names, logic, multiworld, player)
set_museumsanity_rules(all_location_names, logic, multiworld, player, world_options)
- set_friendsanity_rules(all_location_names, logic, multiworld, player)
+
+ set_friendsanity_rules(all_location_names, logic, multiworld, player, world_options)
set_backpack_rules(logic, multiworld, player, world_options)
set_festival_rules(all_location_names, logic, multiworld, player)
+ set_monstersanity_rules(all_location_names, logic, multiworld, player, world_options)
+ set_shipsanity_rules(all_location_names, logic, multiworld, player, world_options)
+ set_cooksanity_rules(all_location_names, logic, multiworld, player, world_options)
+ set_chefsanity_rules(all_location_names, logic, multiworld, player, world_options)
+ set_craftsanity_rules(all_location_names, logic, multiworld, player, world_options)
+ set_isolated_locations_rules(logic, multiworld, player)
+ set_traveling_merchant_day_rules(logic, multiworld, player)
+ set_arcade_machine_rules(logic, multiworld, player, world_options)
+
+ set_deepwoods_rules(logic, multiworld, player, world_options)
+ set_magic_spell_rules(logic, multiworld, player, world_options)
+ set_sve_rules(logic, multiworld, player, world_options)
+
+def set_isolated_locations_rules(logic: StardewLogic, multiworld, player):
MultiWorldRules.add_rule(multiworld.get_location("Old Master Cannoli", player),
- logic.has("Sweet Gem Berry").simplify())
+ logic.has(Fruit.sweet_gem_berry))
MultiWorldRules.add_rule(multiworld.get_location("Galaxy Sword Shrine", player),
- logic.has("Prismatic Shard").simplify())
- MultiWorldRules.add_rule(multiworld.get_location("Have a Baby", player),
- logic.can_reproduce(1).simplify())
- MultiWorldRules.add_rule(multiworld.get_location("Have Another Baby", player),
- logic.can_reproduce(2).simplify())
+ logic.has("Prismatic Shard"))
+ MultiWorldRules.add_rule(multiworld.get_location("Krobus Stardrop", player),
+ logic.money.can_spend(20000))
+ MultiWorldRules.add_rule(multiworld.get_location("Demetrius's Breakthrough", player),
+ logic.money.can_have_earned_total(25000))
- set_traveling_merchant_rules(logic, multiworld, player)
- set_arcade_machine_rules(logic, multiworld, player, world_options)
- set_deepwoods_rules(logic, multiworld, player, world_options)
- set_magic_spell_rules(logic, multiworld, player, world_options)
+def set_tool_rules(logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
+ if not world_options.tool_progression & ToolProgression.option_progressive:
+ return
+
+ MultiWorldRules.add_rule(multiworld.get_location("Purchase Fiberglass Rod", player),
+ (logic.skill.has_level(Skill.fishing, 2) & logic.money.can_spend(1800)))
+ MultiWorldRules.add_rule(multiworld.get_location("Purchase Iridium Rod", player),
+ (logic.skill.has_level(Skill.fishing, 6) & logic.money.can_spend(7500)))
-def set_skills_rules(logic, multiworld, player, world_options):
- # Skills
- if world_options.skill_progression != SkillProgression.option_vanilla:
- for i in range(1, 11):
- set_skill_rule(logic, multiworld, player, Skill.farming, i)
- set_skill_rule(logic, multiworld, player, Skill.fishing, i)
- set_skill_rule(logic, multiworld, player, Skill.foraging, i)
- set_skill_rule(logic, multiworld, player, Skill.mining, i)
- set_skill_rule(logic, multiworld, player, Skill.combat, i)
-
- # Modded Skills
- if ModNames.luck_skill in world_options.mods:
- set_skill_rule(logic, multiworld, player, ModSkill.luck, i)
- if ModNames.magic in world_options.mods:
- set_skill_rule(logic, multiworld, player, ModSkill.magic, i)
- if ModNames.binning_skill in world_options.mods:
- set_skill_rule(logic, multiworld, player, ModSkill.binning, i)
- if ModNames.cooking_skill in world_options.mods:
- set_skill_rule(logic, multiworld, player, ModSkill.cooking, i)
- if ModNames.socializing_skill in world_options.mods:
- set_skill_rule(logic, multiworld, player, ModSkill.socializing, i)
- if ModNames.archaeology in world_options.mods:
- set_skill_rule(logic, multiworld, player, ModSkill.archaeology, i)
-
-
-def set_skill_rule(logic, multiworld, player, skill: str, level: int):
+ materials = [None, "Copper", "Iron", "Gold", "Iridium"]
+ tool = [Tool.hoe, Tool.pickaxe, Tool.axe, Tool.watering_can, Tool.watering_can, Tool.trash_can]
+ for (previous, material), tool in itertools.product(zip(materials[:4], materials[1:]), tool):
+ if previous is None:
+ continue
+ tool_upgrade_location = multiworld.get_location(f"{material} {tool} Upgrade", player)
+ MultiWorldRules.set_rule(tool_upgrade_location, logic.tool.has_tool(tool, previous))
+
+
+def set_building_rules(logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
+ if not world_options.building_progression & BuildingProgression.option_progressive:
+ return
+
+ for building in locations.locations_by_tag[LocationTags.BUILDING_BLUEPRINT]:
+ if building.mod_name is not None and building.mod_name not in world_options.mods:
+ continue
+ MultiWorldRules.set_rule(multiworld.get_location(building.name, player),
+ logic.registry.building_rules[building.name.replace(" Blueprint", "")])
+
+
+def set_bundle_rules(bundle_rooms: List[BundleRoom], logic: StardewLogic, multiworld, player):
+ for bundle_room in bundle_rooms:
+ room_rules = []
+ for bundle in bundle_room.bundles:
+ location = multiworld.get_location(bundle.name, player)
+ bundle_rules = logic.bundle.can_complete_bundle(bundle)
+ room_rules.append(bundle_rules)
+ MultiWorldRules.set_rule(location, bundle_rules)
+ if bundle_room.name == CCRoom.abandoned_joja_mart:
+ continue
+ room_location = f"Complete {bundle_room.name}"
+ MultiWorldRules.add_rule(multiworld.get_location(room_location, player), And(*room_rules))
+
+
+def set_skills_rules(logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
+ mods = world_options.mods
+ if world_options.skill_progression == SkillProgression.option_vanilla:
+ return
+ for i in range(1, 11):
+ set_vanilla_skill_rule_for_level(logic, multiworld, player, i)
+ set_modded_skill_rule_for_level(logic, multiworld, player, mods, i)
+
+
+def set_vanilla_skill_rule_for_level(logic: StardewLogic, multiworld, player, level: int):
+ set_vanilla_skill_rule(logic, multiworld, player, Skill.farming, level)
+ set_vanilla_skill_rule(logic, multiworld, player, Skill.fishing, level)
+ set_vanilla_skill_rule(logic, multiworld, player, Skill.foraging, level)
+ set_vanilla_skill_rule(logic, multiworld, player, Skill.mining, level)
+ set_vanilla_skill_rule(logic, multiworld, player, Skill.combat, level)
+
+
+def set_modded_skill_rule_for_level(logic: StardewLogic, multiworld, player, mods, level: int):
+ if ModNames.luck_skill in mods:
+ set_modded_skill_rule(logic, multiworld, player, ModSkill.luck, level)
+ if ModNames.magic in mods:
+ set_modded_skill_rule(logic, multiworld, player, ModSkill.magic, level)
+ if ModNames.binning_skill in mods:
+ set_modded_skill_rule(logic, multiworld, player, ModSkill.binning, level)
+ if ModNames.cooking_skill in mods:
+ set_modded_skill_rule(logic, multiworld, player, ModSkill.cooking, level)
+ if ModNames.socializing_skill in mods:
+ set_modded_skill_rule(logic, multiworld, player, ModSkill.socializing, level)
+ if ModNames.archaeology in mods:
+ set_modded_skill_rule(logic, multiworld, player, ModSkill.archaeology, level)
+
+
+def get_skill_level_location(multiworld, player, skill: str, level: int):
location_name = f"Level {level} {skill}"
- location = multiworld.get_location(location_name, player)
- rule = logic.can_earn_skill_level(skill, level).simplify()
- MultiWorldRules.set_rule(location, rule)
+ return multiworld.get_location(location_name, player)
+
+
+def set_vanilla_skill_rule(logic: StardewLogic, multiworld, player, skill: str, level: int):
+ rule = logic.skill.can_earn_level(skill, level)
+ MultiWorldRules.set_rule(get_skill_level_location(multiworld, player, skill, level), rule)
+
+
+def set_modded_skill_rule(logic: StardewLogic, multiworld, player, skill: str, level: int):
+ rule = logic.mod.skill.can_earn_mod_skill_level(skill, level)
+ MultiWorldRules.set_rule(get_skill_level_location(multiworld, player, skill, level), rule)
+
+
+def set_entrance_rules(logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
+ set_mines_floor_entrance_rules(logic, multiworld, player)
+ set_skull_cavern_floor_entrance_rules(logic, multiworld, player)
+ set_blacksmith_entrance_rules(logic, multiworld, player)
+ set_skill_entrance_rules(logic, multiworld, player)
+ set_traveling_merchant_day_rules(logic, multiworld, player)
+ set_dangerous_mine_rules(logic, multiworld, player, world_options)
+
+ set_entrance_rule(multiworld, player, Entrance.enter_tide_pools, logic.received("Beach Bridge") | (logic.mod.magic.can_blink()))
+ set_entrance_rule(multiworld, player, Entrance.enter_quarry, logic.received("Bridge Repair") | (logic.mod.magic.can_blink()))
+ set_entrance_rule(multiworld, player, Entrance.enter_secret_woods, logic.tool.has_tool(Tool.axe, "Iron") | (logic.mod.magic.can_blink()))
+ set_entrance_rule(multiworld, player, Entrance.forest_to_sewer, logic.wallet.has_rusty_key())
+ set_entrance_rule(multiworld, player, Entrance.town_to_sewer, logic.wallet.has_rusty_key())
+ set_entrance_rule(multiworld, player, Entrance.enter_abandoned_jojamart, logic.has_abandoned_jojamart())
+ movie_theater_rule = logic.has_movie_theater()
+ set_entrance_rule(multiworld, player, Entrance.enter_movie_theater, movie_theater_rule)
+ set_entrance_rule(multiworld, player, Entrance.purchase_movie_ticket, movie_theater_rule)
+ set_entrance_rule(multiworld, player, Entrance.take_bus_to_desert, logic.received("Bus Repair"))
+ set_entrance_rule(multiworld, player, Entrance.enter_skull_cavern, logic.received(Wallet.skull_key))
+ set_entrance_rule(multiworld, player, Entrance.talk_to_mines_dwarf, logic.wallet.can_speak_dwarf() & logic.tool.has_tool(Tool.pickaxe, ToolMaterial.iron))
+ set_entrance_rule(multiworld, player, Entrance.buy_from_traveling_merchant, logic.traveling_merchant.has_days())
+
+ set_farm_buildings_entrance_rules(logic, multiworld, player)
+
+ set_entrance_rule(multiworld, player, Entrance.mountain_to_railroad, logic.received("Railroad Boulder Removed"))
+ set_entrance_rule(multiworld, player, Entrance.enter_witch_warp_cave, logic.quest.has_dark_talisman() | (logic.mod.magic.can_blink()))
+ set_entrance_rule(multiworld, player, Entrance.enter_witch_hut, (logic.has(ArtisanGood.void_mayonnaise) | logic.mod.magic.can_blink()))
+ set_entrance_rule(multiworld, player, Entrance.enter_mutant_bug_lair,
+ (logic.received(Event.start_dark_talisman_quest) & logic.relationship.can_meet(NPC.krobus)) | logic.mod.magic.can_blink())
+ set_entrance_rule(multiworld, player, Entrance.enter_casino, logic.quest.has_club_card())
+
+ set_bedroom_entrance_rules(logic, multiworld, player, world_options)
+ set_festival_entrance_rules(logic, multiworld, player)
+ set_island_entrance_rule(multiworld, player, Entrance.island_cooking, logic.cooking.can_cook_in_kitchen, world_options)
+ set_entrance_rule(multiworld, player, Entrance.farmhouse_cooking, logic.cooking.can_cook_in_kitchen)
+ set_entrance_rule(multiworld, player, Entrance.shipping, logic.shipping.can_use_shipping_bin)
+ set_entrance_rule(multiworld, player, Entrance.watch_queen_of_sauce, logic.action.can_watch(Channel.queen_of_sauce))
+
+
+def set_dangerous_mine_rules(logic, multiworld, player, world_options: StardewValleyOptions):
+ if world_options.exclude_ginger_island == ExcludeGingerIsland.option_true:
+ return
+ dangerous_mine_rule = logic.mine.has_mine_elevator_to_floor(120) & logic.region.can_reach(Region.qi_walnut_room)
+ set_entrance_rule(multiworld, player, Entrance.dig_to_dangerous_mines_20, dangerous_mine_rule)
+ set_entrance_rule(multiworld, player, Entrance.dig_to_dangerous_mines_60, dangerous_mine_rule)
+ set_entrance_rule(multiworld, player, Entrance.dig_to_dangerous_mines_100, dangerous_mine_rule)
+ set_entrance_rule(multiworld, player, Entrance.enter_dangerous_skull_cavern,
+ (logic.received(Wallet.skull_key) & logic.region.can_reach(Region.qi_walnut_room)))
+
+
+def set_farm_buildings_entrance_rules(logic, multiworld, player):
+ set_entrance_rule(multiworld, player, Entrance.use_desert_obelisk, logic.can_use_obelisk(Transportation.desert_obelisk))
+ set_entrance_rule(multiworld, player, Entrance.enter_greenhouse, logic.received("Greenhouse"))
+ set_entrance_rule(multiworld, player, Entrance.enter_coop, logic.building.has_building(Building.coop))
+ set_entrance_rule(multiworld, player, Entrance.enter_barn, logic.building.has_building(Building.barn))
+ set_entrance_rule(multiworld, player, Entrance.enter_shed, logic.building.has_building(Building.shed))
+ set_entrance_rule(multiworld, player, Entrance.enter_slime_hutch, logic.building.has_building(Building.slime_hutch))
+
+
+def set_bedroom_entrance_rules(logic, multiworld, player, world_options: StardewValleyOptions):
+ set_entrance_rule(multiworld, player, Entrance.enter_harvey_room, logic.relationship.has_hearts(NPC.harvey, 2))
+ set_entrance_rule(multiworld, player, Entrance.mountain_to_maru_room, logic.relationship.has_hearts(NPC.maru, 2))
+ set_entrance_rule(multiworld, player, Entrance.enter_sebastian_room, (logic.relationship.has_hearts(NPC.sebastian, 2) | logic.mod.magic.can_blink()))
+ set_entrance_rule(multiworld, player, Entrance.forest_to_leah_cottage, logic.relationship.has_hearts(NPC.leah, 2))
+ set_entrance_rule(multiworld, player, Entrance.enter_elliott_house, logic.relationship.has_hearts(NPC.elliott, 2))
+ set_entrance_rule(multiworld, player, Entrance.enter_sunroom, logic.relationship.has_hearts(NPC.caroline, 2))
+ set_entrance_rule(multiworld, player, Entrance.enter_wizard_basement, logic.relationship.has_hearts(NPC.wizard, 4))
+ if ModNames.alec in world_options.mods:
+ set_entrance_rule(multiworld, player, AlecEntrance.petshop_to_bedroom, (logic.relationship.has_hearts(ModNPC.alec, 2) | logic.mod.magic.can_blink()))
+ if ModNames.lacey in world_options.mods:
+ set_entrance_rule(multiworld, player, LaceyEntrance.forest_to_hat_house, logic.relationship.has_hearts(ModNPC.lacey, 2))
-def set_entrance_rules(logic, multiworld, player, world_options: StardewValleyOptions):
+def set_mines_floor_entrance_rules(logic, multiworld, player):
for floor in range(5, 120 + 5, 5):
- MultiWorldRules.set_rule(multiworld.get_entrance(dig_to_mines_floor(floor), player),
- logic.can_mine_to_floor(floor).simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_tide_pools, player),
- logic.received("Beach Bridge") | (magic.can_blink(logic)).simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_quarry, player),
- logic.received("Bridge Repair") | (magic.can_blink(logic)).simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_secret_woods, player),
- logic.has_tool(Tool.axe, "Iron") | (magic.can_blink(logic)).simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.forest_to_sewer, player),
- logic.has_rusty_key().simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.town_to_sewer, player),
- logic.has_rusty_key().simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.take_bus_to_desert, player),
- logic.received("Bus Repair").simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_skull_cavern, player),
- logic.received(Wallet.skull_key).simplify())
+ rule = logic.mine.has_mine_elevator_to_floor(floor - 10)
+ if floor == 5 or floor == 45 or floor == 85:
+ rule = rule & logic.mine.can_progress_in_the_mines_from_floor(floor)
+ entrance = multiworld.get_entrance(dig_to_mines_floor(floor), player)
+ MultiWorldRules.set_rule(entrance, rule)
+
+
+def set_skull_cavern_floor_entrance_rules(logic, multiworld, player):
for floor in range(25, 200 + 25, 25):
- MultiWorldRules.set_rule(multiworld.get_entrance(dig_to_skull_floor(floor), player),
- logic.can_mine_to_skull_cavern_floor(floor).simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.talk_to_mines_dwarf, player),
- logic.can_speak_dwarf() & logic.has_tool(Tool.pickaxe, ToolMaterial.iron))
-
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.use_desert_obelisk, player),
- logic.received(Transportation.desert_obelisk).simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.use_island_obelisk, player),
- logic.received(Transportation.island_obelisk).simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.use_farm_obelisk, player),
- logic.received(Transportation.farm_obelisk).simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.buy_from_traveling_merchant, player),
- logic.has_traveling_merchant())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_greenhouse, player),
- logic.received("Greenhouse"))
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.mountain_to_adventurer_guild, player),
- logic.received("Adventurer's Guild"))
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.mountain_to_railroad, player),
- logic.has_lived_months(2))
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_witch_warp_cave, player),
- logic.received(Wallet.dark_talisman) | (magic.can_blink(logic)).simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_witch_hut, player),
- (logic.has(ArtisanGood.void_mayonnaise) | magic.can_blink(logic)).simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_mutant_bug_lair, player),
- ((logic.has_rusty_key() & logic.can_reach_region(Region.railroad) &
- logic.can_meet(NPC.krobus) | magic.can_blink(logic)).simplify()))
-
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_harvey_room, player),
- logic.has_relationship(NPC.harvey, 2))
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.mountain_to_maru_room, player),
- logic.has_relationship(NPC.maru, 2))
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_sebastian_room, player),
- (logic.has_relationship(NPC.sebastian, 2) | magic.can_blink(logic)).simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.forest_to_leah_cottage, player),
- logic.has_relationship(NPC.leah, 2))
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_elliott_house, player),
- logic.has_relationship(NPC.elliott, 2))
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_sunroom, player),
- logic.has_relationship(NPC.caroline, 2))
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_wizard_basement, player),
- logic.has_relationship(NPC.wizard, 4))
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.mountain_to_leo_treehouse, player),
- logic.received("Treehouse"))
- if ModNames.alec in world_options.mods:
- MultiWorldRules.set_rule(multiworld.get_entrance(AlecEntrance.petshop_to_bedroom, player),
- (logic.has_relationship(ModNPC.alec, 2) | magic.can_blink(logic)).simplify())
+ rule = logic.mod.elevator.has_skull_cavern_elevator_to_floor(floor - 25)
+ if floor == 25 or floor == 75 or floor == 125:
+ rule = rule & logic.mine.can_progress_in_the_skull_cavern_from_floor(floor)
+ entrance = multiworld.get_entrance(dig_to_skull_floor(floor), player)
+ MultiWorldRules.set_rule(entrance, rule)
+
+
+def set_blacksmith_entrance_rules(logic, multiworld, player):
+ set_blacksmith_upgrade_rule(logic, multiworld, player, Entrance.blacksmith_copper, MetalBar.copper, ToolMaterial.copper)
+ set_blacksmith_upgrade_rule(logic, multiworld, player, Entrance.blacksmith_iron, MetalBar.iron, ToolMaterial.iron)
+ set_blacksmith_upgrade_rule(logic, multiworld, player, Entrance.blacksmith_gold, MetalBar.gold, ToolMaterial.gold)
+ set_blacksmith_upgrade_rule(logic, multiworld, player, Entrance.blacksmith_iridium, MetalBar.iridium, ToolMaterial.iridium)
+
+
+def set_skill_entrance_rules(logic, multiworld, player):
+ set_entrance_rule(multiworld, player, Entrance.farming, logic.skill.can_get_farming_xp)
+ set_entrance_rule(multiworld, player, Entrance.fishing, logic.skill.can_get_fishing_xp)
+
+
+def set_blacksmith_upgrade_rule(logic, multiworld, player, entrance_name: str, item_name: str, tool_material: str):
+ material_entrance = multiworld.get_entrance(entrance_name, player)
+ upgrade_rule = logic.has(item_name) & logic.money.can_spend(tool_upgrade_prices[tool_material])
+ MultiWorldRules.set_rule(material_entrance, upgrade_rule)
+
+
+def set_festival_entrance_rules(logic, multiworld, player):
+ set_entrance_rule(multiworld, player, Entrance.attend_egg_festival, logic.season.has(Season.spring))
+ set_entrance_rule(multiworld, player, Entrance.attend_flower_dance, logic.season.has(Season.spring))
+
+ set_entrance_rule(multiworld, player, Entrance.attend_luau, logic.season.has(Season.summer))
+ set_entrance_rule(multiworld, player, Entrance.attend_moonlight_jellies, logic.season.has(Season.summer))
+
+ set_entrance_rule(multiworld, player, Entrance.attend_fair, logic.season.has(Season.fall))
+ set_entrance_rule(multiworld, player, Entrance.attend_spirit_eve, logic.season.has(Season.fall))
+
+ set_entrance_rule(multiworld, player, Entrance.attend_festival_of_ice, logic.season.has(Season.winter))
+ set_entrance_rule(multiworld, player, Entrance.attend_night_market, logic.season.has(Season.winter))
+ set_entrance_rule(multiworld, player, Entrance.attend_winter_star, logic.season.has(Season.winter))
def set_ginger_island_rules(logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
- set_island_entrances_rules(logic, multiworld, player)
+ set_island_entrances_rules(logic, multiworld, player, world_options)
if world_options.exclude_ginger_island == ExcludeGingerIsland.option_true:
return
set_boat_repair_rules(logic, multiworld, player)
set_island_parrot_rules(logic, multiworld, player)
MultiWorldRules.add_rule(multiworld.get_location("Open Professor Snail Cave", player),
- logic.has(Craftable.cherry_bomb).simplify())
+ logic.has(Bomb.cherry_bomb))
MultiWorldRules.add_rule(multiworld.get_location("Complete Island Field Office", player),
- logic.can_complete_field_office().simplify())
+ logic.can_complete_field_office())
def set_boat_repair_rules(logic: StardewLogic, multiworld, player):
MultiWorldRules.add_rule(multiworld.get_location("Repair Boat Hull", player),
- logic.has(Material.hardwood).simplify())
+ logic.has(Material.hardwood))
MultiWorldRules.add_rule(multiworld.get_location("Repair Boat Anchor", player),
- logic.has(MetalBar.iridium).simplify())
+ logic.has(MetalBar.iridium))
MultiWorldRules.add_rule(multiworld.get_location("Repair Ticket Machine", player),
- logic.has(ArtisanGood.battery_pack).simplify())
-
-
-def set_island_entrances_rules(logic: StardewLogic, multiworld, player):
- boat_repaired = logic.received(Transportation.boat_repair).simplify()
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.fish_shop_to_boat_tunnel, player),
- boat_repaired)
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.boat_to_ginger_island, player),
- boat_repaired)
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.island_south_to_west, player),
- logic.received("Island West Turtle").simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.island_south_to_north, player),
- logic.received("Island North Turtle").simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.island_west_to_islandfarmhouse, player),
- logic.received("Island Farmhouse").simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.island_west_to_gourmand_cave, player),
- logic.received("Island Farmhouse").simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.island_north_to_dig_site, player),
- logic.received("Dig Site Bridge").simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.dig_site_to_professor_snail_cave, player),
- logic.received("Open Professor Snail Cave").simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.talk_to_island_trader, player),
- logic.received("Island Trader").simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.island_south_to_southeast, player),
- logic.received("Island Resort").simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.use_island_resort, player),
- logic.received("Island Resort").simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.island_west_to_qi_walnut_room, player),
- logic.received("Qi Walnut Room").simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.island_north_to_volcano, player),
- (logic.can_water(0) | logic.received("Volcano Bridge") |
- magic.can_blink(logic)).simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.volcano_to_secret_beach, player),
- logic.can_water(2).simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.climb_to_volcano_5, player),
- (logic.can_mine_perfectly() & logic.can_water(1)).simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.talk_to_volcano_dwarf, player),
- logic.can_speak_dwarf())
- MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.climb_to_volcano_10, player),
- (logic.can_mine_perfectly() & logic.can_water(1) & logic.received("Volcano Exit Shortcut")).simplify())
+ logic.has(ArtisanGood.battery_pack))
+
+
+def set_island_entrances_rules(logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
+ boat_repaired = logic.received(Transportation.boat_repair)
+ dig_site_rule = logic.received("Dig Site Bridge")
+ entrance_rules = {
+ Entrance.use_island_obelisk: logic.can_use_obelisk(Transportation.island_obelisk),
+ Entrance.use_farm_obelisk: logic.can_use_obelisk(Transportation.farm_obelisk),
+ Entrance.fish_shop_to_boat_tunnel: boat_repaired,
+ Entrance.boat_to_ginger_island: boat_repaired,
+ Entrance.island_south_to_west: logic.received("Island West Turtle"),
+ Entrance.island_south_to_north: logic.received("Island North Turtle"),
+ Entrance.island_west_to_islandfarmhouse: logic.received("Island Farmhouse"),
+ Entrance.island_west_to_gourmand_cave: logic.received("Island Farmhouse"),
+ Entrance.island_north_to_dig_site: dig_site_rule,
+ Entrance.dig_site_to_professor_snail_cave: logic.received("Open Professor Snail Cave"),
+ Entrance.talk_to_island_trader: logic.received("Island Trader"),
+ Entrance.island_south_to_southeast: logic.received("Island Resort"),
+ Entrance.use_island_resort: logic.received("Island Resort"),
+ Entrance.island_west_to_qi_walnut_room: logic.received("Qi Walnut Room"),
+ Entrance.island_north_to_volcano: logic.tool.can_water(0) | logic.received("Volcano Bridge") | logic.mod.magic.can_blink(),
+ Entrance.volcano_to_secret_beach: logic.tool.can_water(2),
+ Entrance.climb_to_volcano_5: logic.ability.can_mine_perfectly() & logic.tool.can_water(1),
+ Entrance.talk_to_volcano_dwarf: logic.wallet.can_speak_dwarf(),
+ Entrance.climb_to_volcano_10: logic.ability.can_mine_perfectly() & logic.tool.can_water(1),
+ Entrance.mountain_to_leo_treehouse: logic.received("Treehouse"),
+ }
parrots = [Entrance.parrot_express_docks_to_volcano, Entrance.parrot_express_jungle_to_volcano,
Entrance.parrot_express_dig_site_to_volcano, Entrance.parrot_express_docks_to_dig_site,
Entrance.parrot_express_jungle_to_dig_site, Entrance.parrot_express_volcano_to_dig_site,
Entrance.parrot_express_docks_to_jungle, Entrance.parrot_express_dig_site_to_jungle,
Entrance.parrot_express_volcano_to_jungle, Entrance.parrot_express_jungle_to_docks,
Entrance.parrot_express_dig_site_to_docks, Entrance.parrot_express_volcano_to_docks]
+ parrot_express_rule = logic.received(Transportation.parrot_express)
+ parrot_express_to_dig_site_rule = dig_site_rule & parrot_express_rule
for parrot in parrots:
- MultiWorldRules.set_rule(multiworld.get_entrance(parrot, player), logic.received(Transportation.parrot_express).simplify())
+ if "Dig Site" in parrot:
+ entrance_rules[parrot] = parrot_express_to_dig_site_rule
+ else:
+ entrance_rules[parrot] = parrot_express_rule
+
+ set_many_island_entrances_rules(multiworld, player, entrance_rules, world_options)
def set_island_parrot_rules(logic: StardewLogic, multiworld, player):
- has_walnut = logic.has_walnut(1).simplify()
- has_5_walnut = logic.has_walnut(5).simplify()
- has_10_walnut = logic.has_walnut(10).simplify()
- has_20_walnut = logic.has_walnut(20).simplify()
+ has_walnut = logic.has_walnut(1)
+ has_5_walnut = logic.has_walnut(5)
+ has_10_walnut = logic.has_walnut(10)
+ has_20_walnut = logic.has_walnut(20)
MultiWorldRules.add_rule(multiworld.get_location("Leo's Parrot", player),
has_walnut)
MultiWorldRules.add_rule(multiworld.get_location("Island West Turtle", player),
@@ -309,7 +394,7 @@ def set_island_parrot_rules(logic: StardewLogic, multiworld, player):
has_10_walnut & logic.received("Island Farmhouse"))
MultiWorldRules.add_rule(multiworld.get_location("Volcano Bridge", player),
has_5_walnut & logic.received("Island West Turtle") &
- logic.can_reach_region(Region.volcano_floor_10))
+ logic.region.can_reach(Region.volcano_floor_10))
MultiWorldRules.add_rule(multiworld.get_location("Volcano Exit Shortcut", player),
has_5_walnut & logic.received("Island West Turtle"))
MultiWorldRules.add_rule(multiworld.get_location("Island Resort", player),
@@ -318,45 +403,47 @@ def set_island_parrot_rules(logic: StardewLogic, multiworld, player):
has_10_walnut)
-def set_cropsanity_rules(all_location_names: List[str], logic, multiworld, player, world_options: StardewValleyOptions):
+def set_cropsanity_rules(all_location_names: Set[str], logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
if world_options.cropsanity == Cropsanity.option_disabled:
return
harvest_prefix = "Harvest "
harvest_prefix_length = len(harvest_prefix)
- for harvest_location in locations_by_tag[LocationTags.CROPSANITY]:
+ for harvest_location in locations.locations_by_tag[LocationTags.CROPSANITY]:
if harvest_location.name in all_location_names and (harvest_location.mod_name is None or harvest_location.mod_name in world_options.mods):
crop_name = harvest_location.name[harvest_prefix_length:]
MultiWorldRules.set_rule(multiworld.get_location(harvest_location.name, player),
- logic.has(crop_name).simplify())
+ logic.has(crop_name))
-def set_story_quests_rules(all_location_names: List[str], logic, multiworld, player, world_options: StardewValleyOptions):
- for quest in locations_by_tag[LocationTags.QUEST]:
+def set_story_quests_rules(all_location_names: Set[str], logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
+ if world_options.quest_locations < 0:
+ return
+ for quest in locations.locations_by_tag[LocationTags.STORY_QUEST]:
if quest.name in all_location_names and (quest.mod_name is None or quest.mod_name in world_options.mods):
MultiWorldRules.set_rule(multiworld.get_location(quest.name, player),
- logic.quest_rules[quest.name].simplify())
+ logic.registry.quest_rules[quest.name])
-def set_special_order_rules(all_location_names: List[str], logic: StardewLogic, multiworld, player,
+def set_special_order_rules(all_location_names: Set[str], logic: StardewLogic, multiworld, player,
world_options: StardewValleyOptions):
if world_options.special_order_locations == SpecialOrderLocations.option_disabled:
return
- board_rule = logic.received("Special Order Board") & logic.has_lived_months(4)
- for board_order in locations_by_tag[LocationTags.SPECIAL_ORDER_BOARD]:
+ board_rule = logic.received("Special Order Board") & logic.time.has_lived_months(4)
+ for board_order in locations.locations_by_tag[LocationTags.SPECIAL_ORDER_BOARD]:
if board_order.name in all_location_names:
- order_rule = board_rule & logic.special_order_rules[board_order.name]
- MultiWorldRules.set_rule(multiworld.get_location(board_order.name, player), order_rule.simplify())
+ order_rule = board_rule & logic.registry.special_order_rules[board_order.name]
+ MultiWorldRules.set_rule(multiworld.get_location(board_order.name, player), order_rule)
if world_options.exclude_ginger_island == ExcludeGingerIsland.option_true:
return
if world_options.special_order_locations == SpecialOrderLocations.option_board_only:
return
- qi_rule = logic.can_reach_region(Region.qi_walnut_room) & logic.has_lived_months(8)
- for qi_order in locations_by_tag[LocationTags.SPECIAL_ORDER_QI]:
+ qi_rule = logic.region.can_reach(Region.qi_walnut_room) & logic.time.has_lived_months(8)
+ for qi_order in locations.locations_by_tag[LocationTags.SPECIAL_ORDER_QI]:
if qi_order.name in all_location_names:
- order_rule = qi_rule & logic.special_order_rules[qi_order.name]
- MultiWorldRules.set_rule(multiworld.get_location(qi_order.name, player), order_rule.simplify())
+ order_rule = qi_rule & logic.registry.special_order_rules[qi_order.name]
+ MultiWorldRules.set_rule(multiworld.get_location(qi_order.name, player), order_rule)
help_wanted_prefix = "Help Wanted:"
@@ -367,19 +454,21 @@ def set_special_order_rules(all_location_names: List[str], logic: StardewLogic,
def set_help_wanted_quests_rules(logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
- help_wanted_number = world_options.help_wanted_locations
+ help_wanted_number = world_options.quest_locations.value
+ if help_wanted_number < 0:
+ return
for i in range(0, help_wanted_number):
set_number = i // 7
- month_rule = logic.has_lived_months(set_number).simplify()
+ month_rule = logic.time.has_lived_months(set_number)
quest_number = set_number + 1
quest_number_in_set = i % 7
if quest_number_in_set < 4:
quest_number = set_number * 4 + quest_number_in_set + 1
set_help_wanted_delivery_rule(multiworld, player, month_rule, quest_number)
elif quest_number_in_set == 4:
- set_help_wanted_fishing_rule(logic, multiworld, player, month_rule, quest_number)
+ set_help_wanted_fishing_rule(multiworld, player, month_rule, quest_number)
elif quest_number_in_set == 5:
- set_help_wanted_slay_monsters_rule(logic, multiworld, player, month_rule, quest_number)
+ set_help_wanted_slay_monsters_rule(multiworld, player, month_rule, quest_number)
elif quest_number_in_set == 6:
set_help_wanted_gathering_rule(multiworld, player, month_rule, quest_number)
@@ -394,50 +483,47 @@ def set_help_wanted_gathering_rule(multiworld, player, month_rule, quest_number)
MultiWorldRules.set_rule(multiworld.get_location(location_name, player), month_rule)
-def set_help_wanted_fishing_rule(logic: StardewLogic, multiworld, player, month_rule, quest_number):
+def set_help_wanted_fishing_rule(multiworld, player, month_rule, quest_number):
location_name = f"{help_wanted_prefix} {fishing} {quest_number}"
- fishing_rule = month_rule & logic.can_fish()
- MultiWorldRules.set_rule(multiworld.get_location(location_name, player), fishing_rule.simplify())
+ MultiWorldRules.set_rule(multiworld.get_location(location_name, player), month_rule)
-def set_help_wanted_slay_monsters_rule(logic: StardewLogic, multiworld, player, month_rule, quest_number):
+def set_help_wanted_slay_monsters_rule(multiworld, player, month_rule, quest_number):
location_name = f"{help_wanted_prefix} {slay_monsters} {quest_number}"
- slay_rule = month_rule & logic.can_do_combat_at_level("Basic")
- MultiWorldRules.set_rule(multiworld.get_location(location_name, player), slay_rule.simplify())
+ MultiWorldRules.set_rule(multiworld.get_location(location_name, player), month_rule)
-def set_fishsanity_rules(all_location_names: List[str], logic: StardewLogic, multiworld: MultiWorld, player: int):
+def set_fishsanity_rules(all_location_names: Set[str], logic: StardewLogic, multiworld: MultiWorld, player: int):
fish_prefix = "Fishsanity: "
- for fish_location in locations_by_tag[LocationTags.FISHSANITY]:
+ for fish_location in locations.locations_by_tag[LocationTags.FISHSANITY]:
if fish_location.name in all_location_names:
fish_name = fish_location.name[len(fish_prefix):]
MultiWorldRules.set_rule(multiworld.get_location(fish_location.name, player),
- logic.has(fish_name).simplify())
+ logic.has(fish_name))
-def set_museumsanity_rules(all_location_names: List[str], logic: StardewLogic, multiworld: MultiWorld, player: int,
+def set_museumsanity_rules(all_location_names: Set[str], logic: StardewLogic, multiworld: MultiWorld, player: int,
world_options: StardewValleyOptions):
museum_prefix = "Museumsanity: "
if world_options.museumsanity == Museumsanity.option_milestones:
- for museum_milestone in locations_by_tag[LocationTags.MUSEUM_MILESTONES]:
+ for museum_milestone in locations.locations_by_tag[LocationTags.MUSEUM_MILESTONES]:
set_museum_milestone_rule(logic, multiworld, museum_milestone, museum_prefix, player)
elif world_options.museumsanity != Museumsanity.option_none:
set_museum_individual_donations_rules(all_location_names, logic, multiworld, museum_prefix, player)
def set_museum_individual_donations_rules(all_location_names, logic: StardewLogic, multiworld, museum_prefix, player):
- all_donations = sorted(locations_by_tag[LocationTags.MUSEUM_DONATIONS],
+ all_donations = sorted(locations.locations_by_tag[LocationTags.MUSEUM_DONATIONS],
key=lambda x: all_museum_items_by_name[x.name[len(museum_prefix):]].difficulty, reverse=True)
counter = 0
number_donations = len(all_donations)
for museum_location in all_donations:
if museum_location.name in all_location_names:
donation_name = museum_location.name[len(museum_prefix):]
- required_detectors = counter * 5 // number_donations
- rule = logic.can_donate_museum_item(all_museum_items_by_name[donation_name]) & logic.received("Traveling Merchant Metal Detector",
- required_detectors)
+ required_detectors = counter * 3 // number_donations
+ rule = logic.museum.can_find_museum_item(all_museum_items_by_name[donation_name]) & logic.received(Wallet.metal_detector, required_detectors)
MultiWorldRules.set_rule(multiworld.get_location(museum_location.name, player),
- rule.simplify())
+ rule)
counter += 1
@@ -447,33 +533,33 @@ def set_museum_milestone_rule(logic: StardewLogic, multiworld: MultiWorld, museu
donations_suffix = " Donations"
minerals_suffix = " Minerals"
artifacts_suffix = " Artifacts"
- metal_detector = "Traveling Merchant Metal Detector"
+ metal_detector = Wallet.metal_detector
rule = None
if milestone_name.endswith(donations_suffix):
- rule = get_museum_item_count_rule(logic, donations_suffix, milestone_name, all_museum_items, logic.can_donate_museum_items)
+ rule = get_museum_item_count_rule(logic, donations_suffix, milestone_name, all_museum_items, logic.museum.can_find_museum_items)
elif milestone_name.endswith(minerals_suffix):
- rule = get_museum_item_count_rule(logic, minerals_suffix, milestone_name, all_museum_minerals, logic.can_donate_museum_minerals)
+ rule = get_museum_item_count_rule(logic, minerals_suffix, milestone_name, all_museum_minerals, logic.museum.can_find_museum_minerals)
elif milestone_name.endswith(artifacts_suffix):
- rule = get_museum_item_count_rule(logic, artifacts_suffix, milestone_name, all_museum_artifacts, logic.can_donate_museum_artifacts)
+ rule = get_museum_item_count_rule(logic, artifacts_suffix, milestone_name, all_museum_artifacts, logic.museum.can_find_museum_artifacts)
elif milestone_name == "Dwarf Scrolls":
- rule = And([logic.can_donate_museum_item(item) for item in dwarf_scrolls]) & logic.received(metal_detector, 4)
+ rule = And(*(logic.museum.can_find_museum_item(item) for item in dwarf_scrolls)) & logic.received(metal_detector, 2)
elif milestone_name == "Skeleton Front":
- rule = And([logic.can_donate_museum_item(item) for item in skeleton_front]) & logic.received(metal_detector, 4)
+ rule = And(*(logic.museum.can_find_museum_item(item) for item in skeleton_front)) & logic.received(metal_detector, 2)
elif milestone_name == "Skeleton Middle":
- rule = And([logic.can_donate_museum_item(item) for item in skeleton_middle]) & logic.received(metal_detector, 4)
+ rule = And(*(logic.museum.can_find_museum_item(item) for item in skeleton_middle)) & logic.received(metal_detector, 2)
elif milestone_name == "Skeleton Back":
- rule = And([logic.can_donate_museum_item(item) for item in skeleton_back]) & logic.received(metal_detector, 4)
+ rule = And(*(logic.museum.can_find_museum_item(item) for item in skeleton_back)) & logic.received(metal_detector, 2)
elif milestone_name == "Ancient Seed":
- rule = logic.can_donate_museum_item(Artifact.ancient_seed) & logic.received(metal_detector, 4)
+ rule = logic.museum.can_find_museum_item(Artifact.ancient_seed) & logic.received(metal_detector, 2)
if rule is None:
return
- MultiWorldRules.set_rule(multiworld.get_location(museum_milestone.name, player), rule.simplify())
+ MultiWorldRules.set_rule(multiworld.get_location(museum_milestone.name, player), rule)
def get_museum_item_count_rule(logic: StardewLogic, suffix, milestone_name, accepted_items, donation_func):
- metal_detector = "Traveling Merchant Metal Detector"
+ metal_detector = Wallet.metal_detector
num = int(milestone_name[:milestone_name.index(suffix)])
- required_detectors = (num - 1) * 5 // len(accepted_items)
+ required_detectors = (num - 1) * 3 // len(accepted_items)
rule = donation_func(num) & logic.received(metal_detector, required_detectors)
return rule
@@ -481,63 +567,214 @@ def get_museum_item_count_rule(logic: StardewLogic, suffix, milestone_name, acce
def set_backpack_rules(logic: StardewLogic, multiworld: MultiWorld, player: int, world_options: StardewValleyOptions):
if world_options.backpack_progression != BackpackProgression.option_vanilla:
MultiWorldRules.set_rule(multiworld.get_location("Large Pack", player),
- logic.can_spend_money(2000).simplify())
+ logic.money.can_spend(2000))
MultiWorldRules.set_rule(multiworld.get_location("Deluxe Pack", player),
- (logic.can_spend_money(10000) & logic.received("Progressive Backpack")).simplify())
+ (logic.money.can_spend(10000) & logic.received("Progressive Backpack")))
if ModNames.big_backpack in world_options.mods:
MultiWorldRules.set_rule(multiworld.get_location("Premium Pack", player),
- (logic.can_spend_money(150000) &
- logic.received("Progressive Backpack", 2)).simplify())
+ (logic.money.can_spend(150000) &
+ logic.received("Progressive Backpack", 2)))
-def set_festival_rules(all_location_names: List[str], logic: StardewLogic, multiworld, player):
+def set_festival_rules(all_location_names: Set[str], logic: StardewLogic, multiworld, player):
festival_locations = []
- festival_locations.extend(locations_by_tag[LocationTags.FESTIVAL])
- festival_locations.extend(locations_by_tag[LocationTags.FESTIVAL_HARD])
+ festival_locations.extend(locations.locations_by_tag[LocationTags.FESTIVAL])
+ festival_locations.extend(locations.locations_by_tag[LocationTags.FESTIVAL_HARD])
for festival in festival_locations:
if festival.name in all_location_names:
MultiWorldRules.set_rule(multiworld.get_location(festival.name, player),
- logic.festival_rules[festival.name].simplify())
+ logic.registry.festival_rules[festival.name])
+
+monster_eradication_prefix = "Monster Eradication: "
+
+
+def set_monstersanity_rules(all_location_names: Set[str], logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
+ monstersanity_option = world_options.monstersanity
+ if monstersanity_option == Monstersanity.option_none:
+ return
+
+ if monstersanity_option == Monstersanity.option_one_per_monster or monstersanity_option == Monstersanity.option_split_goals:
+ set_monstersanity_monster_rules(all_location_names, logic, multiworld, player, monstersanity_option)
+ return
+
+ if monstersanity_option == Monstersanity.option_progressive_goals:
+ set_monstersanity_progressive_category_rules(all_location_names, logic, multiworld, player)
+ return
-def set_traveling_merchant_rules(logic: StardewLogic, multiworld: MultiWorld, player: int):
+ set_monstersanity_category_rules(all_location_names, logic, multiworld, player, monstersanity_option)
+
+
+def set_monstersanity_monster_rules(all_location_names: Set[str], logic: StardewLogic, multiworld, player, monstersanity_option):
+ for monster_name in logic.monster.all_monsters_by_name:
+ location_name = f"{monster_eradication_prefix}{monster_name}"
+ if location_name not in all_location_names:
+ continue
+ location = multiworld.get_location(location_name, player)
+ if monstersanity_option == Monstersanity.option_split_goals:
+ rule = logic.monster.can_kill_many(logic.monster.all_monsters_by_name[monster_name])
+ else:
+ rule = logic.monster.can_kill(logic.monster.all_monsters_by_name[monster_name])
+ MultiWorldRules.set_rule(location, rule)
+
+
+def set_monstersanity_progressive_category_rules(all_location_names: Set[str], logic: StardewLogic, multiworld, player):
+ for monster_category in logic.monster.all_monsters_by_category:
+ set_monstersanity_progressive_single_category_rules(all_location_names, logic, multiworld, player, monster_category)
+
+
+def set_monstersanity_progressive_single_category_rules(all_location_names: Set[str], logic: StardewLogic, multiworld, player, monster_category: str):
+ location_names = [name for name in all_location_names if name.startswith(monster_eradication_prefix) and name.endswith(monster_category)]
+ if not location_names:
+ return
+ location_names = sorted(location_names, key=lambda name: get_monster_eradication_number(name, monster_category))
+ for i in range(5):
+ location_name = location_names[i]
+ set_monstersanity_progressive_category_rule(all_location_names, logic, multiworld, player, monster_category, location_name, i)
+
+
+def set_monstersanity_progressive_category_rule(all_location_names: Set[str], logic: StardewLogic, multiworld, player,
+ monster_category: str, location_name: str, goal_index):
+ if location_name not in all_location_names:
+ return
+ location = multiworld.get_location(location_name, player)
+ if goal_index < 3:
+ rule = logic.monster.can_kill_any(logic.monster.all_monsters_by_category[monster_category], goal_index + 1)
+ else:
+ rule = logic.monster.can_kill_any(logic.monster.all_monsters_by_category[monster_category], goal_index * 2)
+ MultiWorldRules.set_rule(location, rule)
+
+
+def get_monster_eradication_number(location_name, monster_category) -> int:
+ number = location_name[len(monster_eradication_prefix):-len(monster_category)]
+ number = number.strip()
+ if number.isdigit():
+ return int(number)
+ return 1000
+
+
+def set_monstersanity_category_rules(all_location_names: Set[str], logic: StardewLogic, multiworld, player, monstersanity_option):
+ for monster_category in logic.monster.all_monsters_by_category:
+ location_name = f"{monster_eradication_prefix}{monster_category}"
+ if location_name not in all_location_names:
+ continue
+ location = multiworld.get_location(location_name, player)
+ if monstersanity_option == Monstersanity.option_one_per_category:
+ rule = logic.monster.can_kill_any(logic.monster.all_monsters_by_category[monster_category])
+ else:
+ rule = logic.monster.can_kill_any(logic.monster.all_monsters_by_category[monster_category], MAX_MONTHS)
+ MultiWorldRules.set_rule(location, rule)
+
+
+def set_shipsanity_rules(all_location_names: Set[str], logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
+ shipsanity_option = world_options.shipsanity
+ if shipsanity_option == Shipsanity.option_none:
+ return
+
+ shipsanity_prefix = "Shipsanity: "
+ for location in locations.locations_by_tag[LocationTags.SHIPSANITY]:
+ if location.name not in all_location_names:
+ continue
+ item_to_ship = location.name[len(shipsanity_prefix):]
+ MultiWorldRules.set_rule(multiworld.get_location(location.name, player), logic.shipping.can_ship(item_to_ship))
+
+
+def set_cooksanity_rules(all_location_names: Set[str], logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
+ cooksanity_option = world_options.cooksanity
+ if cooksanity_option == Cooksanity.option_none:
+ return
+
+ cooksanity_prefix = "Cook "
+ for location in locations.locations_by_tag[LocationTags.COOKSANITY]:
+ if location.name not in all_location_names:
+ continue
+ recipe_name = location.name[len(cooksanity_prefix):]
+ recipe = all_cooking_recipes_by_name[recipe_name]
+ cook_rule = logic.cooking.can_cook(recipe)
+ MultiWorldRules.set_rule(multiworld.get_location(location.name, player), cook_rule)
+
+
+def set_chefsanity_rules(all_location_names: Set[str], logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
+ chefsanity_option = world_options.chefsanity
+ if chefsanity_option == Chefsanity.option_none:
+ return
+
+ chefsanity_suffix = " Recipe"
+ for location in locations.locations_by_tag[LocationTags.CHEFSANITY]:
+ if location.name not in all_location_names:
+ continue
+ recipe_name = location.name[:-len(chefsanity_suffix)]
+ recipe = all_cooking_recipes_by_name[recipe_name]
+ learn_rule = logic.cooking.can_learn_recipe(recipe.source)
+ MultiWorldRules.set_rule(multiworld.get_location(location.name, player), learn_rule)
+
+
+def set_craftsanity_rules(all_location_names: Set[str], logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
+ craftsanity_option = world_options.craftsanity
+ if craftsanity_option == Craftsanity.option_none:
+ return
+
+ craft_prefix = "Craft "
+ craft_suffix = " Recipe"
+ for location in locations.locations_by_tag[LocationTags.CRAFTSANITY]:
+ if location.name not in all_location_names:
+ continue
+ if location.name.endswith(craft_suffix):
+ recipe_name = location.name[:-len(craft_suffix)]
+ recipe = all_crafting_recipes_by_name[recipe_name]
+ craft_rule = logic.crafting.can_learn_recipe(recipe)
+ else:
+ recipe_name = location.name[len(craft_prefix):]
+ recipe = all_crafting_recipes_by_name[recipe_name]
+ craft_rule = logic.crafting.can_craft(recipe)
+ MultiWorldRules.set_rule(multiworld.get_location(location.name, player), craft_rule)
+
+
+def set_traveling_merchant_day_rules(logic: StardewLogic, multiworld: MultiWorld, player: int):
for day in Weekday.all_days:
item_for_day = f"Traveling Merchant: {day}"
- for i in range(1, 4):
- location_name = f"Traveling Merchant {day} Item {i}"
- MultiWorldRules.set_rule(multiworld.get_location(location_name, player),
- logic.received(item_for_day))
+ entrance_name = f"Buy from Traveling Merchant {day}"
+ set_entrance_rule(multiworld, player, entrance_name, logic.received(item_for_day))
def set_arcade_machine_rules(logic: StardewLogic, multiworld: MultiWorld, player: int, world_options: StardewValleyOptions):
MultiWorldRules.add_rule(multiworld.get_entrance(Entrance.play_junimo_kart, player),
- logic.received(Wallet.skull_key).simplify())
+ logic.received(Wallet.skull_key))
if world_options.arcade_machine_locations != ArcadeMachineLocations.option_full_shuffling:
return
MultiWorldRules.add_rule(multiworld.get_entrance(Entrance.play_junimo_kart, player),
- logic.has("Junimo Kart Small Buff").simplify())
+ logic.has("Junimo Kart Small Buff"))
MultiWorldRules.add_rule(multiworld.get_entrance(Entrance.reach_junimo_kart_2, player),
- logic.has("Junimo Kart Medium Buff").simplify())
+ logic.has("Junimo Kart Medium Buff"))
MultiWorldRules.add_rule(multiworld.get_entrance(Entrance.reach_junimo_kart_3, player),
- logic.has("Junimo Kart Big Buff").simplify())
+ logic.has("Junimo Kart Big Buff"))
MultiWorldRules.add_rule(multiworld.get_location("Junimo Kart: Sunset Speedway (Victory)", player),
- logic.has("Junimo Kart Max Buff").simplify())
+ logic.has("Junimo Kart Max Buff"))
MultiWorldRules.add_rule(multiworld.get_entrance(Entrance.play_journey_of_the_prairie_king, player),
- logic.has("JotPK Small Buff").simplify())
+ logic.has("JotPK Small Buff"))
MultiWorldRules.add_rule(multiworld.get_entrance(Entrance.reach_jotpk_world_2, player),
- logic.has("JotPK Medium Buff").simplify())
+ logic.has("JotPK Medium Buff"))
MultiWorldRules.add_rule(multiworld.get_entrance(Entrance.reach_jotpk_world_3, player),
- logic.has("JotPK Big Buff").simplify())
+ logic.has("JotPK Big Buff"))
MultiWorldRules.add_rule(multiworld.get_location("Journey of the Prairie King Victory", player),
- logic.has("JotPK Max Buff").simplify())
+ logic.has("JotPK Max Buff"))
-def set_friendsanity_rules(all_location_names: List[str], logic: StardewLogic, multiworld: MultiWorld, player: int):
+def set_friendsanity_rules(all_location_names: Set[str], logic: StardewLogic, multiworld: MultiWorld, player: int, world_options: StardewValleyOptions):
+ if world_options.friendsanity == Friendsanity.option_none:
+ return
+ MultiWorldRules.add_rule(multiworld.get_location("Spouse Stardrop", player),
+ logic.relationship.has_hearts(Generic.bachelor, 13))
+ MultiWorldRules.add_rule(multiworld.get_location("Have a Baby", player),
+ logic.relationship.can_reproduce(1))
+ MultiWorldRules.add_rule(multiworld.get_location("Have Another Baby", player),
+ logic.relationship.can_reproduce(2))
+
friend_prefix = "Friendsanity: "
friend_suffix = " <3"
- for friend_location in locations_by_tag[LocationTags.FRIENDSANITY]:
- if friend_location.name not in all_location_names:
+ for friend_location in locations.locations_by_tag[LocationTags.FRIENDSANITY]:
+ if not friend_location.name in all_location_names:
continue
friend_location_without_prefix = friend_location.name[len(friend_prefix):]
friend_location_trimmed = friend_location_without_prefix[:friend_location_without_prefix.index(friend_suffix)]
@@ -545,89 +782,144 @@ def set_friendsanity_rules(all_location_names: List[str], logic: StardewLogic, m
friend_name = friend_location_trimmed[:split_index]
num_hearts = int(friend_location_trimmed[split_index + 1:])
MultiWorldRules.set_rule(multiworld.get_location(friend_location.name, player),
- logic.can_earn_relationship(friend_name, num_hearts).simplify())
+ logic.relationship.can_earn_relationship(friend_name, num_hearts))
def set_deepwoods_rules(logic: StardewLogic, multiworld: MultiWorld, player: int, world_options: StardewValleyOptions):
if ModNames.deepwoods in world_options.mods:
MultiWorldRules.add_rule(multiworld.get_location("Breaking Up Deep Woods Gingerbread House", player),
- logic.has_tool(Tool.axe, "Gold") & deepwoods.can_reach_woods_depth(logic, 50).simplify())
+ logic.tool.has_tool(Tool.axe, "Gold"))
MultiWorldRules.add_rule(multiworld.get_location("Chop Down a Deep Woods Iridium Tree", player),
- logic.has_tool(Tool.axe, "Iridium").simplify())
- MultiWorldRules.set_rule(multiworld.get_entrance(DeepWoodsEntrance.use_woods_obelisk, player),
- logic.received("Woods Obelisk").simplify())
+ logic.tool.has_tool(Tool.axe, "Iridium"))
+ set_entrance_rule(multiworld, player, DeepWoodsEntrance.use_woods_obelisk, logic.received("Woods Obelisk"))
for depth in range(10, 100 + 10, 10):
- MultiWorldRules.set_rule(multiworld.get_entrance(move_to_woods_depth(depth), player),
- deepwoods.can_chop_to_depth(logic, depth).simplify())
+ set_entrance_rule(multiworld, player, move_to_woods_depth(depth), logic.mod.deepwoods.can_chop_to_depth(depth))
+ MultiWorldRules.add_rule(multiworld.get_location("The Sword in the Stone", player),
+ logic.mod.deepwoods.can_pull_sword() & logic.mod.deepwoods.can_chop_to_depth(100))
def set_magic_spell_rules(logic: StardewLogic, multiworld: MultiWorld, player: int, world_options: StardewValleyOptions):
if ModNames.magic not in world_options.mods:
return
- MultiWorldRules.set_rule(multiworld.get_entrance(MagicEntrance.store_to_altar, player),
- (logic.has_relationship(NPC.wizard, 3) &
- logic.can_reach_region(Region.wizard_tower)).simplify())
MultiWorldRules.add_rule(multiworld.get_location("Analyze: Clear Debris", player),
- ((logic.has_tool("Axe", "Basic") | logic.has_tool("Pickaxe", "Basic"))
- & magic.can_use_altar(logic)).simplify())
+ (logic.tool.has_tool("Axe", "Basic") | logic.tool.has_tool("Pickaxe", "Basic")))
MultiWorldRules.add_rule(multiworld.get_location("Analyze: Till", player),
- (logic.has_tool("Hoe", "Basic") & magic.can_use_altar(logic)).simplify())
+ logic.tool.has_tool("Hoe", "Basic"))
MultiWorldRules.add_rule(multiworld.get_location("Analyze: Water", player),
- (logic.has_tool("Watering Can", "Basic") & magic.can_use_altar(logic)).simplify())
+ logic.tool.has_tool("Watering Can", "Basic"))
MultiWorldRules.add_rule(multiworld.get_location("Analyze All Toil School Locations", player),
- (logic.has_tool("Watering Can", "Basic") & logic.has_tool("Hoe", "Basic")
- & (logic.has_tool("Axe", "Basic") | logic.has_tool("Pickaxe", "Basic"))
- & magic.can_use_altar(logic)).simplify())
+ (logic.tool.has_tool("Watering Can", "Basic") & logic.tool.has_tool("Hoe", "Basic")
+ & (logic.tool.has_tool("Axe", "Basic") | logic.tool.has_tool("Pickaxe", "Basic"))))
# Do I *want* to add boots into logic when you get them even in vanilla without effort? idk
MultiWorldRules.add_rule(multiworld.get_location("Analyze: Evac", player),
- (logic.can_mine_perfectly() & magic.can_use_altar(logic)).simplify())
+ logic.ability.can_mine_perfectly())
MultiWorldRules.add_rule(multiworld.get_location("Analyze: Haste", player),
- (logic.has("Coffee") & magic.can_use_altar(logic)).simplify())
+ logic.has("Coffee"))
MultiWorldRules.add_rule(multiworld.get_location("Analyze: Heal", player),
- (logic.has("Life Elixir") & magic.can_use_altar(logic)).simplify())
+ logic.has("Life Elixir"))
MultiWorldRules.add_rule(multiworld.get_location("Analyze All Life School Locations", player),
(logic.has("Coffee") & logic.has("Life Elixir")
- & logic.can_mine_perfectly() & magic.can_use_altar(logic)).simplify())
+ & logic.ability.can_mine_perfectly()))
MultiWorldRules.add_rule(multiworld.get_location("Analyze: Descend", player),
- (logic.can_reach_region(Region.mines) & magic.can_use_altar(logic)).simplify())
+ logic.region.can_reach(Region.mines))
MultiWorldRules.add_rule(multiworld.get_location("Analyze: Fireball", player),
- (logic.has("Fire Quartz") & magic.can_use_altar(logic)).simplify())
- MultiWorldRules.add_rule(multiworld.get_location("Analyze: Frostbite", player),
- (logic.can_mine_to_floor(70) & logic.can_fish(85) & magic.can_use_altar(logic)).simplify())
+ logic.has("Fire Quartz"))
+ MultiWorldRules.add_rule(multiworld.get_location("Analyze: Frostbolt", player),
+ logic.region.can_reach(Region.mines_floor_60) & logic.skill.can_fish(difficulty=85))
MultiWorldRules.add_rule(multiworld.get_location("Analyze All Elemental School Locations", player),
- (logic.can_reach_region(Region.mines) & logic.has("Fire Quartz")
- & logic.can_reach_region(Region.mines_floor_70) & logic.can_fish(85) &
- magic.can_use_altar(logic)).simplify())
- MultiWorldRules.add_rule(multiworld.get_location("Analyze: Lantern", player),
- magic.can_use_altar(logic).simplify())
+ logic.has("Fire Quartz") & logic.region.can_reach(Region.mines_floor_60) & logic.skill.can_fish(difficulty=85))
+ # MultiWorldRules.add_rule(multiworld.get_location("Analyze: Lantern", player),)
MultiWorldRules.add_rule(multiworld.get_location("Analyze: Tendrils", player),
- (logic.can_reach_region(Region.farm) & magic.can_use_altar(logic)).simplify())
+ logic.region.can_reach(Region.farm))
MultiWorldRules.add_rule(multiworld.get_location("Analyze: Shockwave", player),
- (logic.has("Earth Crystal") & magic.can_use_altar(logic)).simplify())
+ logic.has("Earth Crystal"))
MultiWorldRules.add_rule(multiworld.get_location("Analyze All Nature School Locations", player),
- (logic.has("Earth Crystal") & logic.can_reach_region("Farm") &
- magic.can_use_altar(logic)).simplify()),
+ (logic.has("Earth Crystal") & logic.region.can_reach("Farm"))),
MultiWorldRules.add_rule(multiworld.get_location("Analyze: Meteor", player),
- (logic.can_reach_region(Region.farm) & logic.has_lived_months(12)
- & magic.can_use_altar(logic)).simplify()),
+ (logic.region.can_reach(Region.farm) & logic.time.has_lived_months(12))),
MultiWorldRules.add_rule(multiworld.get_location("Analyze: Lucksteal", player),
- (logic.can_reach_region(Region.witch_hut) & magic.can_use_altar(logic)).simplify())
+ logic.region.can_reach(Region.witch_hut))
MultiWorldRules.add_rule(multiworld.get_location("Analyze: Bloodmana", player),
- (logic.can_reach_region(Region.mines_floor_100) & magic.can_use_altar(logic)).simplify())
+ logic.region.can_reach(Region.mines_floor_100))
MultiWorldRules.add_rule(multiworld.get_location("Analyze All Eldritch School Locations", player),
- (logic.can_reach_region(Region.witch_hut) &
- logic.can_reach_region(Region.mines_floor_100) &
- logic.can_reach_region(Region.farm) & logic.has_lived_months(12) &
- magic.can_use_altar(logic)).simplify())
+ (logic.region.can_reach(Region.witch_hut) &
+ logic.region.can_reach(Region.mines_floor_100) &
+ logic.region.can_reach(Region.farm) & logic.time.has_lived_months(12)))
MultiWorldRules.add_rule(multiworld.get_location("Analyze Every Magic School Location", player),
- (logic.has_tool("Watering Can", "Basic") & logic.has_tool("Hoe", "Basic")
- & (logic.has_tool("Axe", "Basic") | logic.has_tool("Pickaxe", "Basic")) &
+ (logic.tool.has_tool("Watering Can", "Basic") & logic.tool.has_tool("Hoe", "Basic")
+ & (logic.tool.has_tool("Axe", "Basic") | logic.tool.has_tool("Pickaxe", "Basic")) &
logic.has("Coffee") & logic.has("Life Elixir")
- & logic.can_mine_perfectly() & logic.has("Earth Crystal") &
- logic.can_reach_region(Region.mines) &
- logic.has("Fire Quartz") & logic.can_fish(85) &
- logic.can_reach_region(Region.witch_hut) &
- logic.can_reach_region(Region.mines_floor_100) &
- logic.can_reach_region(Region.farm) & logic.has_lived_months(12) &
- magic.can_use_altar(logic)).simplify())
+ & logic.ability.can_mine_perfectly() & logic.has("Earth Crystal") &
+ logic.has("Fire Quartz") & logic.skill.can_fish(difficulty=85) &
+ logic.region.can_reach(Region.witch_hut) &
+ logic.region.can_reach(Region.mines_floor_100) &
+ logic.region.can_reach(Region.farm) & logic.time.has_lived_months(12)))
+
+
+def set_sve_rules(logic: StardewLogic, multiworld: MultiWorld, player: int, world_options: StardewValleyOptions):
+ if ModNames.sve not in world_options.mods:
+ return
+ set_entrance_rule(multiworld, player, SVEEntrance.forest_to_lost_woods, logic.bundle.can_complete_community_center)
+ set_entrance_rule(multiworld, player, SVEEntrance.enter_summit, logic.mod.sve.has_iridium_bomb())
+ set_entrance_rule(multiworld, player, SVEEntrance.backwoods_to_grove, logic.mod.sve.has_any_rune())
+ set_entrance_rule(multiworld, player, SVEEntrance.badlands_to_cave, logic.has("Aegis Elixir") | logic.combat.can_fight_at_level(Performance.maximum))
+ set_entrance_rule(multiworld, player, SVEEntrance.forest_west_to_spring, logic.quest.can_complete_quest(Quest.magic_ink))
+ set_entrance_rule(multiworld, player, SVEEntrance.railroad_to_grampleton_station, logic.received(SVEQuestItem.scarlett_job_offer))
+ set_entrance_rule(multiworld, player, SVEEntrance.secret_woods_to_west, logic.tool.has_tool(Tool.axe, ToolMaterial.iron))
+ set_entrance_rule(multiworld, player, SVEEntrance.grandpa_shed_to_interior, logic.tool.has_tool(Tool.axe, ToolMaterial.iron))
+ set_entrance_rule(multiworld, player, SVEEntrance.aurora_warp_to_aurora, logic.received(SVERunes.nexus_aurora))
+ set_entrance_rule(multiworld, player, SVEEntrance.farm_warp_to_farm, logic.received(SVERunes.nexus_farm))
+ set_entrance_rule(multiworld, player, SVEEntrance.guild_warp_to_guild, logic.received(SVERunes.nexus_guild))
+ set_entrance_rule(multiworld, player, SVEEntrance.junimo_warp_to_junimo, logic.received(SVERunes.nexus_junimo))
+ set_entrance_rule(multiworld, player, SVEEntrance.spring_warp_to_spring, logic.received(SVERunes.nexus_spring))
+ set_entrance_rule(multiworld, player, SVEEntrance.outpost_warp_to_outpost, logic.received(SVERunes.nexus_outpost))
+ set_entrance_rule(multiworld, player, SVEEntrance.wizard_warp_to_wizard, logic.received(SVERunes.nexus_wizard))
+ set_entrance_rule(multiworld, player, SVEEntrance.use_purple_junimo, logic.relationship.has_hearts(ModNPC.apples, 10))
+ set_entrance_rule(multiworld, player, SVEEntrance.grandpa_interior_to_upstairs, logic.received(SVEQuestItem.grandpa_shed))
+ set_entrance_rule(multiworld, player, SVEEntrance.use_bear_shop, (logic.mod.sve.can_buy_bear_recipe()))
+ set_entrance_rule(multiworld, player, SVEEntrance.railroad_to_grampleton_station, logic.received(SVEQuestItem.scarlett_job_offer))
+ set_entrance_rule(multiworld, player, SVEEntrance.museum_to_gunther_bedroom, logic.relationship.has_hearts(ModNPC.gunther, 2))
+ logic.mod.sve.initialize_rules()
+ for location in logic.registry.sve_location_rules:
+ MultiWorldRules.set_rule(multiworld.get_location(location, player),
+ logic.registry.sve_location_rules[location])
+ set_sve_ginger_island_rules(logic, multiworld, player, world_options)
+ set_boarding_house_rules(logic, multiworld, player, world_options)
+
+
+def set_sve_ginger_island_rules(logic: StardewLogic, multiworld: MultiWorld, player: int, world_options: StardewValleyOptions):
+ if world_options.exclude_ginger_island == ExcludeGingerIsland.option_true:
+ return
+ set_entrance_rule(multiworld, player, SVEEntrance.summit_to_highlands, logic.received(SVEQuestItem.marlon_boat_paddle))
+ set_entrance_rule(multiworld, player, SVEEntrance.wizard_to_fable_reef, logic.received(SVEQuestItem.fable_reef_portal))
+ set_entrance_rule(multiworld, player, SVEEntrance.highlands_to_cave,
+ logic.tool.has_tool(Tool.pickaxe, ToolMaterial.iron) & logic.tool.has_tool(Tool.axe, ToolMaterial.iron))
+
+
+def set_boarding_house_rules(logic: StardewLogic, multiworld: MultiWorld, player: int, world_options: StardewValleyOptions):
+ if ModNames.boarding_house not in world_options.mods:
+ return
+ set_entrance_rule(multiworld, player, BoardingHouseEntrance.the_lost_valley_to_lost_valley_ruins, logic.tool.has_tool(Tool.axe, ToolMaterial.iron))
+
+
+def set_entrance_rule(multiworld, player, entrance: str, rule: StardewRule):
+ potentially_required_regions = look_for_indirect_connection(rule)
+ if potentially_required_regions:
+ for region in potentially_required_regions:
+ multiworld.register_indirect_condition(multiworld.get_region(region, player), multiworld.get_entrance(entrance, player))
+
+ MultiWorldRules.set_rule(multiworld.get_entrance(entrance, player), rule)
+
+
+def set_island_entrance_rule(multiworld, player, entrance: str, rule: StardewRule, world_options: StardewValleyOptions):
+ if world_options.exclude_ginger_island == ExcludeGingerIsland.option_true:
+ return
+ set_entrance_rule(multiworld, player, entrance, rule)
+
+
+def set_many_island_entrances_rules(multiworld, player, entrance_rules: Dict[str, StardewRule], world_options: StardewValleyOptions):
+ if world_options.exclude_ginger_island == ExcludeGingerIsland.option_true:
+ return
+ for entrance, rule in entrance_rules.items():
+ set_entrance_rule(multiworld, player, entrance, rule)
diff --git a/worlds/stardew_valley/scripts/update_data.py b/worlds/stardew_valley/scripts/update_data.py
index 7b31a3705c5c..ae8f7f8d5503 100644
--- a/worlds/stardew_valley/scripts/update_data.py
+++ b/worlds/stardew_valley/scripts/update_data.py
@@ -12,7 +12,7 @@
from worlds.stardew_valley import LocationData
from worlds.stardew_valley.items import load_item_csv, Group, ItemData
-from worlds.stardew_valley.locations import load_location_csv
+from worlds.stardew_valley.locations import load_location_csv, LocationTags
RESOURCE_PACK_CODE_OFFSET = 5000
script_folder = Path(__file__)
@@ -34,14 +34,15 @@ def write_item_csv(items: List[ItemData]):
def write_location_csv(locations: List[LocationData]):
with open((script_folder.parent.parent / "data/locations.csv").resolve(), "w", newline="") as file:
- write = csv.DictWriter(file, ["id", "region", "name", "tags"])
+ write = csv.DictWriter(file, ["id", "region", "name", "tags", "mod_name"])
write.writeheader()
for location in locations:
location_dict = {
"id": location.code_without_offset,
"name": location.name,
"region": location.region,
- "tags": ",".join(sorted(group.name for group in location.tags))
+ "tags": ",".join(sorted(group.name for group in location.tags)),
+ "mod_name": location.mod_name
}
write.writerow(location_dict)
@@ -76,12 +77,11 @@ def write_location_csv(locations: List[LocationData]):
location_counter = itertools.count(max(location.code_without_offset
for location in loaded_locations
if location.code_without_offset is not None) + 1)
-
locations_to_write = []
for location in loaded_locations:
if location.code_without_offset is None:
locations_to_write.append(
- LocationData(next(location_counter), location.region, location.name, location.tags))
+ LocationData(next(location_counter), location.region, location.name, location.mod_name, location.tags))
continue
locations_to_write.append(location)
diff --git a/worlds/stardew_valley/stardew_rule.py b/worlds/stardew_valley/stardew_rule.py
deleted file mode 100644
index 9c96de00d333..000000000000
--- a/worlds/stardew_valley/stardew_rule.py
+++ /dev/null
@@ -1,384 +0,0 @@
-from __future__ import annotations
-
-from dataclasses import dataclass
-from typing import Iterable, Dict, List, Union, FrozenSet, Set
-
-from BaseClasses import CollectionState, ItemClassification
-from .items import item_table
-
-MISSING_ITEM = "THIS ITEM IS MISSING"
-
-
-class StardewRule:
- def __call__(self, state: CollectionState) -> bool:
- raise NotImplementedError
-
- def __or__(self, other) -> StardewRule:
- if type(other) is Or:
- return Or(self, *other.rules)
-
- return Or(self, other)
-
- def __and__(self, other) -> StardewRule:
- if type(other) is And:
- return And(other.rules.union({self}))
-
- return And(self, other)
-
- def get_difficulty(self):
- raise NotImplementedError
-
- def simplify(self) -> StardewRule:
- return self
-
-
-class True_(StardewRule): # noqa
-
- def __new__(cls, _cache=[]): # noqa
- # Only one single instance will be ever created.
- if not _cache:
- _cache.append(super(True_, cls).__new__(cls))
- return _cache[0]
-
- def __call__(self, state: CollectionState) -> bool:
- return True
-
- def __or__(self, other) -> StardewRule:
- return self
-
- def __and__(self, other) -> StardewRule:
- return other
-
- def __repr__(self):
- return "True"
-
- def get_difficulty(self):
- return 0
-
-
-class False_(StardewRule): # noqa
-
- def __new__(cls, _cache=[]): # noqa
- # Only one single instance will be ever created.
- if not _cache:
- _cache.append(super(False_, cls).__new__(cls))
- return _cache[0]
-
- def __call__(self, state: CollectionState) -> bool:
- return False
-
- def __or__(self, other) -> StardewRule:
- return other
-
- def __and__(self, other) -> StardewRule:
- return self
-
- def __repr__(self):
- return "False"
-
- def get_difficulty(self):
- return 999999999
-
-
-false_ = False_()
-true_ = True_()
-assert false_ is False_()
-assert true_ is True_()
-
-
-class Or(StardewRule):
- rules: FrozenSet[StardewRule]
- _simplified: bool
-
- def __init__(self, rule: Union[StardewRule, Iterable[StardewRule]], *rules: StardewRule):
- rules_list: Set[StardewRule]
-
- if isinstance(rule, Iterable):
- rules_list = {*rule}
- else:
- rules_list = {rule}
-
- if rules is not None:
- rules_list.update(rules)
-
- assert rules_list, "Can't create a Or conditions without rules"
-
- if any(type(rule) is Or for rule in rules_list):
- new_rules: Set[StardewRule] = set()
- for rule in rules_list:
- if type(rule) is Or:
- new_rules.update(rule.rules)
- else:
- new_rules.add(rule)
- rules_list = new_rules
-
- self.rules = frozenset(rules_list)
- self._simplified = False
-
- def __call__(self, state: CollectionState) -> bool:
- return any(rule(state) for rule in self.rules)
-
- def __repr__(self):
- return f"({' | '.join(repr(rule) for rule in self.rules)})"
-
- def __or__(self, other):
- if other is true_:
- return other
- if other is false_:
- return self
- if type(other) is Or:
- return Or(self.rules.union(other.rules))
-
- return Or(self.rules.union({other}))
-
- def __eq__(self, other):
- return isinstance(other, self.__class__) and other.rules == self.rules
-
- def __hash__(self):
- return hash(self.rules)
-
- def get_difficulty(self):
- return min(rule.get_difficulty() for rule in self.rules)
-
- def simplify(self) -> StardewRule:
- if self._simplified:
- return self
- if true_ in self.rules:
- return true_
-
- simplified_rules = [simplified for simplified in {rule.simplify() for rule in self.rules}
- if simplified is not false_]
-
- if not simplified_rules:
- return false_
-
- if len(simplified_rules) == 1:
- return simplified_rules[0]
-
- self.rules = frozenset(simplified_rules)
- self._simplified = True
- return self
-
-
-class And(StardewRule):
- rules: FrozenSet[StardewRule]
- _simplified: bool
-
- def __init__(self, rule: Union[StardewRule, Iterable[StardewRule]], *rules: StardewRule):
- rules_list: Set[StardewRule]
-
- if isinstance(rule, Iterable):
- rules_list = {*rule}
- else:
- rules_list = {rule}
-
- if rules is not None:
- rules_list.update(rules)
-
- if not rules_list:
- rules_list.add(true_)
- elif any(type(rule) is And for rule in rules_list):
- new_rules: Set[StardewRule] = set()
- for rule in rules_list:
- if type(rule) is And:
- new_rules.update(rule.rules)
- else:
- new_rules.add(rule)
- rules_list = new_rules
-
- self.rules = frozenset(rules_list)
- self._simplified = False
-
- def __call__(self, state: CollectionState) -> bool:
- return all(rule(state) for rule in self.rules)
-
- def __repr__(self):
- return f"({' & '.join(repr(rule) for rule in self.rules)})"
-
- def __and__(self, other):
- if other is true_:
- return self
- if other is false_:
- return other
- if type(other) is And:
- return And(self.rules.union(other.rules))
-
- return And(self.rules.union({other}))
-
- def __eq__(self, other):
- return isinstance(other, self.__class__) and other.rules == self.rules
-
- def __hash__(self):
- return hash(self.rules)
-
- def get_difficulty(self):
- return max(rule.get_difficulty() for rule in self.rules)
-
- def simplify(self) -> StardewRule:
- if self._simplified:
- return self
- if false_ in self.rules:
- return false_
-
- simplified_rules = [simplified for simplified in {rule.simplify() for rule in self.rules}
- if simplified is not true_]
-
- if not simplified_rules:
- return true_
-
- if len(simplified_rules) == 1:
- return simplified_rules[0]
-
- self.rules = frozenset(simplified_rules)
- self._simplified = True
- return self
-
-
-class Count(StardewRule):
- count: int
- rules: List[StardewRule]
-
- def __init__(self, count: int, rule: Union[StardewRule, Iterable[StardewRule]], *rules: StardewRule):
- rules_list: List[StardewRule]
-
- if isinstance(rule, Iterable):
- rules_list = [*rule]
- else:
- rules_list = [rule]
-
- if rules is not None:
- rules_list.extend(rules)
-
- assert rules_list, "Can't create a Count conditions without rules"
- assert len(rules_list) >= count, "Count need at least as many rules at the count"
-
- self.rules = rules_list
- self.count = count
-
- def __call__(self, state: CollectionState) -> bool:
- c = 0
- for r in self.rules:
- if r(state):
- c += 1
- if c >= self.count:
- return True
- return False
-
- def __repr__(self):
- return f"Received {self.count} {repr(self.rules)}"
-
- def get_difficulty(self):
- rules_sorted_by_difficulty = sorted(self.rules, key=lambda x: x.get_difficulty())
- easiest_n_rules = rules_sorted_by_difficulty[0:self.count]
- return max(rule.get_difficulty() for rule in easiest_n_rules)
-
- def simplify(self):
- return Count(self.count, [rule.simplify() for rule in self.rules])
-
-
-class TotalReceived(StardewRule):
- count: int
- items: Iterable[str]
- player: int
-
- def __init__(self, count: int, items: Union[str, Iterable[str]], player: int):
- items_list: List[str]
-
- if isinstance(items, Iterable):
- items_list = [*items]
- else:
- items_list = [items]
-
- assert items_list, "Can't create a Total Received conditions without items"
- for item in items_list:
- assert item_table[item].classification & ItemClassification.progression, \
- "Item has to be progression to be used in logic"
-
- self.player = player
- self.items = items_list
- self.count = count
-
- def __call__(self, state: CollectionState) -> bool:
- c = 0
- for item in self.items:
- c += state.count(item, self.player)
- if c >= self.count:
- return True
- return False
-
- def __repr__(self):
- return f"Received {self.count} {self.items}"
-
- def get_difficulty(self):
- return self.count
-
-
-@dataclass(frozen=True)
-class Received(StardewRule):
- item: str
- player: int
- count: int
-
- def __post_init__(self):
- assert item_table[self.item].classification & ItemClassification.progression, \
- f"Item [{item_table[self.item].name}] has to be progression to be used in logic"
-
- def __call__(self, state: CollectionState) -> bool:
- return state.has(self.item, self.player, self.count)
-
- def __repr__(self):
- if self.count == 1:
- return f"Received {self.item}"
- return f"Received {self.count} {self.item}"
-
- def get_difficulty(self):
- if self.item == "Spring":
- return 0
- if self.item == "Summer":
- return 1
- if self.item == "Fall":
- return 2
- if self.item == "Winter":
- return 3
- return self.count
-
-
-@dataclass(frozen=True)
-class Reach(StardewRule):
- spot: str
- resolution_hint: str
- player: int
-
- def __call__(self, state: CollectionState) -> bool:
- return state.can_reach(self.spot, self.resolution_hint, self.player)
-
- def __repr__(self):
- return f"Reach {self.resolution_hint} {self.spot}"
-
- def get_difficulty(self):
- return 1
-
-
-@dataclass(frozen=True)
-class Has(StardewRule):
- item: str
- # For sure there is a better way than just passing all the rules everytime
- other_rules: Dict[str, StardewRule]
-
- def __call__(self, state: CollectionState) -> bool:
- if isinstance(self.item, str):
- return self.other_rules[self.item](state)
-
- def __repr__(self):
- if not self.item in self.other_rules:
- return f"Has {self.item} -> {MISSING_ITEM}"
- return f"Has {self.item} -> {repr(self.other_rules[self.item])}"
-
- def get_difficulty(self):
- return self.other_rules[self.item].get_difficulty() + 1
-
- def __hash__(self):
- return hash(self.item)
-
- def simplify(self) -> StardewRule:
- return self.other_rules[self.item].simplify()
diff --git a/worlds/stardew_valley/stardew_rule/__init__.py b/worlds/stardew_valley/stardew_rule/__init__.py
new file mode 100644
index 000000000000..73b2d1b66747
--- /dev/null
+++ b/worlds/stardew_valley/stardew_rule/__init__.py
@@ -0,0 +1,4 @@
+from .base import *
+from .literal import *
+from .protocol import *
+from .state import *
diff --git a/worlds/stardew_valley/stardew_rule/base.py b/worlds/stardew_valley/stardew_rule/base.py
new file mode 100644
index 000000000000..007d2b64dc41
--- /dev/null
+++ b/worlds/stardew_valley/stardew_rule/base.py
@@ -0,0 +1,448 @@
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from collections import deque
+from functools import cached_property
+from itertools import chain
+from threading import Lock
+from typing import Iterable, Dict, List, Union, Sized, Hashable, Callable, Tuple, Set, Optional
+
+from BaseClasses import CollectionState
+from .literal import true_, false_, LiteralStardewRule
+from .protocol import StardewRule
+
+MISSING_ITEM = "THIS ITEM IS MISSING"
+
+
+class BaseStardewRule(StardewRule, ABC):
+
+ def __or__(self, other) -> StardewRule:
+ if other is true_ or other is false_ or type(other) is Or:
+ return other | self
+
+ return Or(self, other)
+
+ def __and__(self, other) -> StardewRule:
+ if other is true_ or other is false_ or type(other) is And:
+ return other & self
+
+ return And(self, other)
+
+
+class CombinableStardewRule(BaseStardewRule, ABC):
+
+ @property
+ @abstractmethod
+ def combination_key(self) -> Hashable:
+ raise NotImplementedError
+
+ @property
+ @abstractmethod
+ def value(self):
+ raise NotImplementedError
+
+ def is_same_rule(self, other: CombinableStardewRule):
+ return self.combination_key == other.combination_key
+
+ def add_into(self, rules: Dict[Hashable, CombinableStardewRule], reducer: Callable[[CombinableStardewRule, CombinableStardewRule], CombinableStardewRule]) \
+ -> Dict[Hashable, CombinableStardewRule]:
+ rules = dict(rules)
+
+ if self.combination_key in rules:
+ rules[self.combination_key] = reducer(self, rules[self.combination_key])
+ else:
+ rules[self.combination_key] = self
+
+ return rules
+
+ def __and__(self, other):
+ if isinstance(other, CombinableStardewRule) and self.is_same_rule(other):
+ return And.combine(self, other)
+ return super().__and__(other)
+
+ def __or__(self, other):
+ if isinstance(other, CombinableStardewRule) and self.is_same_rule(other):
+ return Or.combine(self, other)
+ return super().__or__(other)
+
+
+class _SimplificationState:
+ original_simplifiable_rules: Tuple[StardewRule, ...]
+
+ rules_to_simplify: deque[StardewRule]
+ simplified_rules: Set[StardewRule]
+ lock: Lock
+
+ def __init__(self, simplifiable_rules: Tuple[StardewRule, ...], rules_to_simplify: Optional[deque[StardewRule]] = None,
+ simplified_rules: Optional[Set[StardewRule]] = None):
+ if simplified_rules is None:
+ simplified_rules = set()
+
+ self.original_simplifiable_rules = simplifiable_rules
+ self.rules_to_simplify = rules_to_simplify
+ self.simplified_rules = simplified_rules
+ self.locked = False
+
+ @property
+ def is_simplified(self):
+ return self.rules_to_simplify is not None and not self.rules_to_simplify
+
+ def short_circuit(self, complement: LiteralStardewRule):
+ self.rules_to_simplify = deque()
+ self.simplified_rules = {complement}
+
+ def try_popleft(self):
+ try:
+ self.rules_to_simplify.popleft()
+ except IndexError:
+ pass
+
+ def acquire_copy(self):
+ state = _SimplificationState(self.original_simplifiable_rules, self.rules_to_simplify.copy(), self.simplified_rules.copy())
+ state.acquire()
+ return state
+
+ def merge(self, other: _SimplificationState):
+ return _SimplificationState(self.original_simplifiable_rules + other.original_simplifiable_rules)
+
+ def add(self, rule: StardewRule):
+ return _SimplificationState(self.original_simplifiable_rules + (rule,))
+
+ def acquire(self):
+ """
+ This just set a boolean to True and is absolutely not thread safe. It just works because AP is single-threaded.
+ """
+ if self.locked is True:
+ return False
+
+ self.locked = True
+ return True
+
+ def release(self):
+ assert self.locked
+ self.locked = False
+
+
+class AggregatingStardewRule(BaseStardewRule, ABC):
+ """
+ Logic for both "And" and "Or" rules.
+ """
+ identity: LiteralStardewRule
+ complement: LiteralStardewRule
+ symbol: str
+
+ combinable_rules: Dict[Hashable, CombinableStardewRule]
+ simplification_state: _SimplificationState
+ _last_short_circuiting_rule: Optional[StardewRule] = None
+
+ def __init__(self, *rules: StardewRule, _combinable_rules=None, _simplification_state=None):
+ if _combinable_rules is None:
+ assert rules, f"Can't create an aggregating condition without rules"
+ rules, _combinable_rules = self.split_rules(rules)
+ _simplification_state = _SimplificationState(rules)
+
+ self.combinable_rules = _combinable_rules
+ self.simplification_state = _simplification_state
+
+ @property
+ def original_rules(self):
+ return RepeatableChain(self.combinable_rules.values(), self.simplification_state.original_simplifiable_rules)
+
+ @property
+ def current_rules(self):
+ if self.simplification_state.rules_to_simplify is None:
+ return self.original_rules
+
+ return RepeatableChain(self.combinable_rules.values(), self.simplification_state.simplified_rules, self.simplification_state.rules_to_simplify)
+
+ @classmethod
+ def split_rules(cls, rules: Union[Iterable[StardewRule]]) -> Tuple[Tuple[StardewRule, ...], Dict[Hashable, CombinableStardewRule]]:
+ other_rules = []
+ reduced_rules = {}
+ for rule in rules:
+ if isinstance(rule, CombinableStardewRule):
+ key = rule.combination_key
+ if key not in reduced_rules:
+ reduced_rules[key] = rule
+ continue
+
+ reduced_rules[key] = cls.combine(reduced_rules[key], rule)
+ continue
+
+ if type(rule) is cls:
+ other_rules.extend(rule.simplification_state.original_simplifiable_rules) # noqa
+ reduced_rules = cls.merge(reduced_rules, rule.combinable_rules) # noqa
+ continue
+
+ other_rules.append(rule)
+
+ return tuple(other_rules), reduced_rules
+
+ @classmethod
+ def merge(cls, left: Dict[Hashable, CombinableStardewRule], right: Dict[Hashable, CombinableStardewRule]) -> Dict[Hashable, CombinableStardewRule]:
+ reduced_rules = dict(left)
+ for key, rule in right.items():
+ if key not in reduced_rules:
+ reduced_rules[key] = rule
+ continue
+
+ reduced_rules[key] = cls.combine(reduced_rules[key], rule)
+
+ return reduced_rules
+
+ @staticmethod
+ @abstractmethod
+ def combine(left: CombinableStardewRule, right: CombinableStardewRule) -> CombinableStardewRule:
+ raise NotImplementedError
+
+ def short_circuit_simplification(self):
+ self.simplification_state.short_circuit(self.complement)
+ self.combinable_rules = {}
+ return self.complement, self.complement.value
+
+ def short_circuit_evaluation(self, rule):
+ self._last_short_circuiting_rule = rule
+ return self, self.complement.value
+
+ def evaluate_while_simplifying(self, state: CollectionState) -> Tuple[StardewRule, bool]:
+ """
+ The global idea here is the same as short-circuiting operators, applied to evaluation and rule simplification.
+ """
+
+ # Directly checking last rule that short-circuited, in case state has not changed.
+ if self._last_short_circuiting_rule:
+ if self._last_short_circuiting_rule(state) is self.complement.value:
+ return self.short_circuit_evaluation(self._last_short_circuiting_rule)
+ self._last_short_circuiting_rule = None
+
+ # Combinable rules are considered already simplified, so we evaluate them right away to go faster.
+ for rule in self.combinable_rules.values():
+ if rule(state) is self.complement.value:
+ return self.short_circuit_evaluation(rule)
+
+ if self.simplification_state.is_simplified:
+ # The rule is fully simplified, so now we can only evaluate.
+ for rule in self.simplification_state.simplified_rules:
+ if rule(state) is self.complement.value:
+ return self.short_circuit_evaluation(rule)
+ return self, self.identity.value
+
+ return self.evaluate_while_simplifying_stateful(state)
+
+ def evaluate_while_simplifying_stateful(self, state):
+ local_state = self.simplification_state
+ try:
+ # Creating a new copy, so we don't modify the rules while we're already evaluating it. This can happen if a rule is used for an entrance and a
+ # location. When evaluating a given rule what requires access to a region, the region cache can get an update. If it does, we could enter this rule
+ # again. Since the simplification is stateful, the set of simplified rules can be modified while it's being iterated on, and cause a crash.
+ #
+ # After investigation, for millions of call to this method, copy were acquired 425 times.
+ # Merging simplification state in parent call was deemed useless.
+ if not local_state.acquire():
+ local_state = local_state.acquire_copy()
+ self.simplification_state = local_state
+
+ # Evaluating what has already been simplified. First it will be faster than simplifying "new" rules, but we also assume that if we reach this point
+ # and there are already are simplified rule, one of these rules has short-circuited, and might again, so we can leave early.
+ for rule in local_state.simplified_rules:
+ if rule(state) is self.complement.value:
+ return self.short_circuit_evaluation(rule)
+
+ # If the queue is None, it means we have not start simplifying. Otherwise, we will continue simplification where we left.
+ if local_state.rules_to_simplify is None:
+ rules_to_simplify = frozenset(local_state.original_simplifiable_rules)
+ if self.complement in rules_to_simplify:
+ return self.short_circuit_simplification()
+ local_state.rules_to_simplify = deque(rules_to_simplify)
+
+ # Start simplification where we left.
+ while local_state.rules_to_simplify:
+ result = self.evaluate_rule_while_simplifying_stateful(local_state, state)
+ local_state.try_popleft()
+ if result is not None:
+ return result
+
+ # The whole rule has been simplified and evaluated without short-circuit.
+ return self, self.identity.value
+ finally:
+ local_state.release()
+
+ def evaluate_rule_while_simplifying_stateful(self, local_state, state):
+ simplified, value = local_state.rules_to_simplify[0].evaluate_while_simplifying(state)
+
+ # Identity is removed from the resulting simplification since it does not affect the result.
+ if simplified is self.identity:
+ return
+
+ # If we find a complement here, we know the rule will always short-circuit, what ever the state.
+ if simplified is self.complement:
+ return self.short_circuit_simplification()
+ # Keep the simplified rule to be reevaluated later.
+ local_state.simplified_rules.add(simplified)
+
+ # Now we use the value to short-circuit if it is the complement.
+ if value is self.complement.value:
+ return self.short_circuit_evaluation(simplified)
+
+ def __str__(self):
+ return f"({self.symbol.join(str(rule) for rule in self.original_rules)})"
+
+ def __repr__(self):
+ return f"({self.symbol.join(repr(rule) for rule in self.original_rules)})"
+
+ def __eq__(self, other):
+ return (isinstance(other, type(self)) and self.combinable_rules == other.combinable_rules and
+ self.simplification_state.original_simplifiable_rules == self.simplification_state.original_simplifiable_rules)
+
+ def __hash__(self):
+ return hash((id(self.combinable_rules), self.simplification_state.original_simplifiable_rules))
+
+
+class Or(AggregatingStardewRule):
+ identity = false_
+ complement = true_
+ symbol = " | "
+
+ def __call__(self, state: CollectionState) -> bool:
+ return self.evaluate_while_simplifying(state)[1]
+
+ def __or__(self, other):
+ if other is true_ or other is false_:
+ return other | self
+
+ if isinstance(other, CombinableStardewRule):
+ return Or(_combinable_rules=other.add_into(self.combinable_rules, self.combine), _simplification_state=self.simplification_state)
+
+ if type(other) is Or:
+ return Or(_combinable_rules=self.merge(self.combinable_rules, other.combinable_rules),
+ _simplification_state=self.simplification_state.merge(other.simplification_state))
+
+ return Or(_combinable_rules=self.combinable_rules, _simplification_state=self.simplification_state.add(other))
+
+ @staticmethod
+ def combine(left: CombinableStardewRule, right: CombinableStardewRule) -> CombinableStardewRule:
+ return min(left, right, key=lambda x: x.value)
+
+ def get_difficulty(self):
+ return min(rule.get_difficulty() for rule in self.original_rules)
+
+
+class And(AggregatingStardewRule):
+ identity = true_
+ complement = false_
+ symbol = " & "
+
+ def __call__(self, state: CollectionState) -> bool:
+ return self.evaluate_while_simplifying(state)[1]
+
+ def __and__(self, other):
+ if other is true_ or other is false_:
+ return other & self
+
+ if isinstance(other, CombinableStardewRule):
+ return And(_combinable_rules=other.add_into(self.combinable_rules, self.combine), _simplification_state=self.simplification_state)
+
+ if type(other) is And:
+ return And(_combinable_rules=self.merge(self.combinable_rules, other.combinable_rules),
+ _simplification_state=self.simplification_state.merge(other.simplification_state))
+
+ return And(_combinable_rules=self.combinable_rules, _simplification_state=self.simplification_state.add(other))
+
+ @staticmethod
+ def combine(left: CombinableStardewRule, right: CombinableStardewRule) -> CombinableStardewRule:
+ return max(left, right, key=lambda x: x.value)
+
+ def get_difficulty(self):
+ return max(rule.get_difficulty() for rule in self.original_rules)
+
+
+class Count(BaseStardewRule):
+ count: int
+ rules: List[StardewRule]
+
+ def __init__(self, rules: List[StardewRule], count: int):
+ self.rules = rules
+ self.count = count
+
+ def evaluate_while_simplifying(self, state: CollectionState) -> Tuple[StardewRule, bool]:
+ c = 0
+ for i in range(self.rules_count):
+ self.rules[i], value = self.rules[i].evaluate_while_simplifying(state)
+ if value:
+ c += 1
+
+ if c >= self.count:
+ return self, True
+ if c + self.rules_count - i < self.count:
+ break
+
+ return self, False
+
+ def __call__(self, state: CollectionState) -> bool:
+ return self.evaluate_while_simplifying(state)[1]
+
+ @cached_property
+ def rules_count(self):
+ return len(self.rules)
+
+ def get_difficulty(self):
+ self.rules = sorted(self.rules, key=lambda x: x.get_difficulty())
+ # In an optimal situation, all the simplest rules will be true. Since the rules are sorted, we know that the most difficult rule we might have to do
+ # is the one at the "self.count".
+ return self.rules[self.count - 1].get_difficulty()
+
+ def __repr__(self):
+ return f"Received {self.count} {repr(self.rules)}"
+
+
+class Has(BaseStardewRule):
+ item: str
+ # For sure there is a better way than just passing all the rules everytime
+ other_rules: Dict[str, StardewRule]
+
+ def __init__(self, item: str, other_rules: Dict[str, StardewRule]):
+ self.item = item
+ self.other_rules = other_rules
+
+ def __call__(self, state: CollectionState) -> bool:
+ return self.evaluate_while_simplifying(state)[1]
+
+ def evaluate_while_simplifying(self, state: CollectionState) -> Tuple[StardewRule, bool]:
+ return self.other_rules[self.item].evaluate_while_simplifying(state)
+
+ def get_difficulty(self):
+ return self.other_rules[self.item].get_difficulty() + 1
+
+ def __str__(self):
+ if self.item not in self.other_rules:
+ return f"Has {self.item} -> {MISSING_ITEM}"
+ return f"Has {self.item}"
+
+ def __repr__(self):
+ if self.item not in self.other_rules:
+ return f"Has {self.item} -> {MISSING_ITEM}"
+ return f"Has {self.item} -> {repr(self.other_rules[self.item])}"
+
+ def __hash__(self):
+ return hash(self.item)
+
+
+class RepeatableChain(Iterable, Sized):
+ """
+ Essentially a copy of what's in the core, with proper type hinting
+ """
+
+ def __init__(self, *iterable: Union[Iterable, Sized]):
+ self.iterables = iterable
+
+ def __iter__(self):
+ return chain.from_iterable(self.iterables)
+
+ def __bool__(self):
+ return any(sub_iterable for sub_iterable in self.iterables)
+
+ def __len__(self):
+ return sum(len(iterable) for iterable in self.iterables)
+
+ def __contains__(self, item):
+ return any(item in it for it in self.iterables)
diff --git a/worlds/stardew_valley/stardew_rule/indirect_connection.py b/worlds/stardew_valley/stardew_rule/indirect_connection.py
new file mode 100644
index 000000000000..2bbddb16818f
--- /dev/null
+++ b/worlds/stardew_valley/stardew_rule/indirect_connection.py
@@ -0,0 +1,39 @@
+from functools import singledispatch
+from typing import Set
+
+from . import StardewRule, Reach, Count, AggregatingStardewRule, Has
+
+
+def look_for_indirect_connection(rule: StardewRule) -> Set[str]:
+ required_regions = set()
+ _find(rule, required_regions)
+ return required_regions
+
+
+@singledispatch
+def _find(rule: StardewRule, regions: Set[str]):
+ ...
+
+
+@_find.register
+def _(rule: AggregatingStardewRule, regions: Set[str]):
+ for r in rule.original_rules:
+ _find(r, regions)
+
+
+@_find.register
+def _(rule: Count, regions: Set[str]):
+ for r in rule.rules:
+ _find(r, regions)
+
+
+@_find.register
+def _(rule: Has, regions: Set[str]):
+ r = rule.other_rules[rule.item]
+ _find(r, regions)
+
+
+@_find.register
+def _(rule: Reach, regions: Set[str]):
+ if rule.resolution_hint == "Region":
+ regions.add(rule.spot)
diff --git a/worlds/stardew_valley/stardew_rule/literal.py b/worlds/stardew_valley/stardew_rule/literal.py
new file mode 100644
index 000000000000..58f7bae047fa
--- /dev/null
+++ b/worlds/stardew_valley/stardew_rule/literal.py
@@ -0,0 +1,62 @@
+from abc import ABC
+from typing import Tuple
+
+from BaseClasses import CollectionState
+from .protocol import StardewRule
+
+
+class LiteralStardewRule(StardewRule, ABC):
+ value: bool
+
+ def evaluate_while_simplifying(self, state: CollectionState) -> Tuple[StardewRule, bool]:
+ return self, self.value
+
+ def __call__(self, state: CollectionState) -> bool:
+ return self.value
+
+ def __repr__(self):
+ return str(self.value)
+
+
+class True_(LiteralStardewRule): # noqa
+ value = True
+
+ def __new__(cls, _cache=[]): # noqa
+ # Only one single instance will be ever created.
+ if not _cache:
+ _cache.append(super(True_, cls).__new__(cls))
+ return _cache[0]
+
+ def __or__(self, other) -> StardewRule:
+ return self
+
+ def __and__(self, other) -> StardewRule:
+ return other
+
+ def get_difficulty(self):
+ return 0
+
+
+class False_(LiteralStardewRule): # noqa
+ value = False
+
+ def __new__(cls, _cache=[]): # noqa
+ # Only one single instance will be ever created.
+ if not _cache:
+ _cache.append(super(False_, cls).__new__(cls))
+ return _cache[0]
+
+ def __or__(self, other) -> StardewRule:
+ return other
+
+ def __and__(self, other) -> StardewRule:
+ return self
+
+ def get_difficulty(self):
+ return 999999999
+
+
+false_ = False_()
+true_ = True_()
+assert false_
+assert true_
diff --git a/worlds/stardew_valley/stardew_rule/protocol.py b/worlds/stardew_valley/stardew_rule/protocol.py
new file mode 100644
index 000000000000..c20394d5b826
--- /dev/null
+++ b/worlds/stardew_valley/stardew_rule/protocol.py
@@ -0,0 +1,30 @@
+from __future__ import annotations
+
+from abc import abstractmethod
+from typing import Protocol, Tuple, runtime_checkable
+
+from BaseClasses import CollectionState
+
+
+@runtime_checkable
+class StardewRule(Protocol):
+
+ @abstractmethod
+ def __call__(self, state: CollectionState) -> bool:
+ ...
+
+ @abstractmethod
+ def __and__(self, other: StardewRule):
+ ...
+
+ @abstractmethod
+ def __or__(self, other: StardewRule):
+ ...
+
+ @abstractmethod
+ def evaluate_while_simplifying(self, state: CollectionState) -> Tuple[StardewRule, bool]:
+ ...
+
+ @abstractmethod
+ def get_difficulty(self):
+ ...
diff --git a/worlds/stardew_valley/stardew_rule/state.py b/worlds/stardew_valley/stardew_rule/state.py
new file mode 100644
index 000000000000..a0fce7c7c19e
--- /dev/null
+++ b/worlds/stardew_valley/stardew_rule/state.py
@@ -0,0 +1,140 @@
+from dataclasses import dataclass
+from typing import Iterable, Union, List, Tuple, Hashable
+
+from BaseClasses import ItemClassification, CollectionState
+from .base import BaseStardewRule, CombinableStardewRule
+from .protocol import StardewRule
+from ..items import item_table
+
+
+class TotalReceived(BaseStardewRule):
+ count: int
+ items: Iterable[str]
+ player: int
+
+ def __init__(self, count: int, items: Union[str, Iterable[str]], player: int):
+ items_list: List[str]
+
+ if isinstance(items, Iterable):
+ items_list = [*items]
+ else:
+ items_list = [items]
+
+ assert items_list, "Can't create a Total Received conditions without items"
+ for item in items_list:
+ assert item_table[item].classification & ItemClassification.progression, \
+ f"Item [{item_table[item].name}] has to be progression to be used in logic"
+
+ self.player = player
+ self.items = items_list
+ self.count = count
+
+ def __call__(self, state: CollectionState) -> bool:
+ c = 0
+ for item in self.items:
+ c += state.count(item, self.player)
+ if c >= self.count:
+ return True
+ return False
+
+ def evaluate_while_simplifying(self, state: CollectionState) -> Tuple[StardewRule, bool]:
+ return self, self(state)
+
+ def get_difficulty(self):
+ return self.count
+
+ def __repr__(self):
+ return f"Received {self.count} {self.items}"
+
+
+@dataclass(frozen=True)
+class Received(CombinableStardewRule):
+ item: str
+ player: int
+ count: int
+
+ def __post_init__(self):
+ assert item_table[self.item].classification & ItemClassification.progression, \
+ f"Item [{item_table[self.item].name}] has to be progression to be used in logic"
+
+ @property
+ def combination_key(self) -> Hashable:
+ return self.item
+
+ @property
+ def value(self):
+ return self.count
+
+ def __call__(self, state: CollectionState) -> bool:
+ return state.has(self.item, self.player, self.count)
+
+ def evaluate_while_simplifying(self, state: CollectionState) -> Tuple[StardewRule, bool]:
+ return self, self(state)
+
+ def __repr__(self):
+ if self.count == 1:
+ return f"Received {self.item}"
+ return f"Received {self.count} {self.item}"
+
+ def get_difficulty(self):
+ return self.count
+
+
+@dataclass(frozen=True)
+class Reach(BaseStardewRule):
+ spot: str
+ resolution_hint: str
+ player: int
+
+ def __call__(self, state: CollectionState) -> bool:
+ if self.resolution_hint == 'Region' and self.spot not in state.multiworld.regions.region_cache[self.player]:
+ return False
+ return state.can_reach(self.spot, self.resolution_hint, self.player)
+
+ def evaluate_while_simplifying(self, state: CollectionState) -> Tuple[StardewRule, bool]:
+ return self, self(state)
+
+ def __repr__(self):
+ return f"Reach {self.resolution_hint} {self.spot}"
+
+ def get_difficulty(self):
+ return 1
+
+
+@dataclass(frozen=True)
+class HasProgressionPercent(CombinableStardewRule):
+ player: int
+ percent: int
+
+ def __post_init__(self):
+ assert self.percent > 0, "HasProgressionPercent rule must be above 0%"
+ assert self.percent <= 100, "HasProgressionPercent rule can't require more than 100% of items"
+
+ @property
+ def combination_key(self) -> Hashable:
+ return HasProgressionPercent.__name__
+
+ @property
+ def value(self):
+ return self.percent
+
+ def __call__(self, state: CollectionState) -> bool:
+ stardew_world = state.multiworld.worlds[self.player]
+ total_count = stardew_world.total_progression_items
+ needed_count = (total_count * self.percent) // 100
+ total_count = 0
+ for item in state.prog_items[self.player]:
+ item_count = state.prog_items[self.player][item]
+ total_count += item_count
+ if total_count >= needed_count:
+ return True
+ return False
+
+ def evaluate_while_simplifying(self, state: CollectionState) -> Tuple[StardewRule, bool]:
+ return self, self(state)
+
+ def __repr__(self):
+ return f"HasProgressionPercent {self.percent}"
+
+ def get_difficulty(self):
+ return self.percent
diff --git a/worlds/stardew_valley/strings/animal_product_names.py b/worlds/stardew_valley/strings/animal_product_names.py
index 6656e70e580d..f89b610ae89d 100644
--- a/worlds/stardew_valley/strings/animal_product_names.py
+++ b/worlds/stardew_valley/strings/animal_product_names.py
@@ -1,24 +1,30 @@
class AnimalProduct:
any_egg = "Any Egg"
- chicken_egg = "Chicken Egg"
- egg = "Egg"
brown_egg = "Egg (Brown)"
- large_egg = "Large Egg"
- large_brown_egg = "Large Egg (Brown)"
- milk = "Milk"
- large_milk = "Large Milk"
+ chicken_egg = "Chicken Egg"
cow_milk = "Cow Milk"
- wool = "Wool"
- goat_milk = "Goat Milk"
- large_goat_milk = "Large Goat Milk"
+ dinosaur_egg = "Dinosaur Egg"
duck_egg = "Duck Egg"
duck_feather = "Duck Feather"
- void_egg = "Void Egg"
- truffle = "Truffle"
+ egg = "Egg"
+ goat_milk = "Goat Milk"
+ golden_egg = "Golden Egg"
+ large_brown_egg = "Large Egg (Brown)"
+ large_egg = "Large Egg"
+ large_goat_milk = "Large Goat Milk"
+ large_milk = "Large Milk"
+ milk = "Milk"
+ ostrich_egg = "Ostrich Egg"
rabbit_foot = "Rabbit's Foot"
roe = "Roe"
- sturgeon_roe = "Sturgeon Roe"
- ostrich_egg = "Ostrich Egg"
- dinosaur_egg = "Dinosaur Egg"
+ slime_egg_blue = "Blue Slime Egg"
+ slime_egg_green = "Green Slime Egg"
+ slime_egg_purple = "Purple Slime Egg"
+ slime_egg_red = "Red Slime Egg"
+ slime_egg_tiger = "Tiger Slime Egg"
squid_ink = "Squid Ink"
+ sturgeon_roe = "Sturgeon Roe"
+ truffle = "Truffle"
+ void_egg = "Void Egg"
+ wool = "Wool"
diff --git a/worlds/stardew_valley/strings/ap_names/ap_weapon_names.py b/worlds/stardew_valley/strings/ap_names/ap_weapon_names.py
new file mode 100644
index 000000000000..7fcd6873761c
--- /dev/null
+++ b/worlds/stardew_valley/strings/ap_names/ap_weapon_names.py
@@ -0,0 +1,7 @@
+class APWeapon:
+ weapon = "Progressive Weapon"
+ sword = "Progressive Sword"
+ club = "Progressive Club"
+ dagger = "Progressive Dagger"
+ slingshot = "Progressive Slingshot"
+ footwear = "Progressive Footwear"
diff --git a/worlds/stardew_valley/strings/ap_names/community_upgrade_names.py b/worlds/stardew_valley/strings/ap_names/community_upgrade_names.py
new file mode 100644
index 000000000000..68dad8e75287
--- /dev/null
+++ b/worlds/stardew_valley/strings/ap_names/community_upgrade_names.py
@@ -0,0 +1,4 @@
+class CommunityUpgrade:
+ fruit_bats = "Fruit Bats"
+ mushroom_boxes = "Mushroom Boxes"
+ movie_theater = "Progressive Movie Theater"
diff --git a/worlds/stardew_valley/strings/ap_names/event_names.py b/worlds/stardew_valley/strings/ap_names/event_names.py
new file mode 100644
index 000000000000..08b9d8f8131c
--- /dev/null
+++ b/worlds/stardew_valley/strings/ap_names/event_names.py
@@ -0,0 +1,6 @@
+class Event:
+ victory = "Victory"
+ can_construct_buildings = "Can Construct Buildings"
+ start_dark_talisman_quest = "Start Dark Talisman Quest"
+ can_ship_items = "Can Ship Items"
+ can_shop_at_pierre = "Can Shop At Pierre's"
diff --git a/worlds/stardew_valley/strings/ap_names/mods/mod_items.py b/worlds/stardew_valley/strings/ap_names/mods/mod_items.py
new file mode 100644
index 000000000000..ccc2765544a6
--- /dev/null
+++ b/worlds/stardew_valley/strings/ap_names/mods/mod_items.py
@@ -0,0 +1,50 @@
+from typing import List
+
+
+class DeepWoodsItem:
+ pendant_community = "Pendant of Community"
+ pendant_elder = "Pendant of Elders"
+ pendant_depths = "Pendant of Depths"
+ obelisk_sigil = "Progressive Woods Obelisk Sigils"
+
+
+class SkillLevel:
+ luck = "Luck Level"
+ archaeology = "Archaeology Level"
+
+
+class SVEQuestItem:
+ aurora_vineyard_tablet = "Aurora Vineyard Tablet"
+ iridium_bomb = "Iridium Bomb"
+ void_soul = "Void Spirit Peace Agreement"
+ kittyfish_spell = "Kittyfish Spell"
+ scarlett_job_offer = "Scarlett's Job Offer"
+ morgan_schooling = "Morgan's Schooling"
+ diamond_wand = "Diamond Wand"
+ marlon_boat_paddle = "Marlon's Boat Paddle"
+ fable_reef_portal = "Fable Reef Portal"
+ grandpa_shed = "Grandpa's Shed"
+
+ sve_quest_items: List[str] = [aurora_vineyard_tablet, iridium_bomb, void_soul, kittyfish_spell, scarlett_job_offer, morgan_schooling, grandpa_shed]
+ sve_quest_items_ginger_island: List[str] = [marlon_boat_paddle, fable_reef_portal]
+
+
+class SVELocation:
+ tempered_galaxy_sword = "Tempered Galaxy Sword"
+ tempered_galaxy_hammer = "Tempered Galaxy Hammer"
+ tempered_galaxy_dagger = "Tempered Galaxy Dagger"
+ diamond_wand = "Lance's Diamond Wand"
+ monster_crops = "Monster Crops"
+
+
+class SVERunes:
+ nexus_guild = "Nexus: Adventurer's Guild Runes"
+ nexus_junimo = "Nexus: Junimo Woods Runes"
+ nexus_outpost = "Nexus: Outpost Runes"
+ nexus_aurora = "Nexus: Aurora Vineyard Runes"
+ nexus_spring = "Nexus: Sprite Spring Runes"
+ nexus_farm = "Nexus: Farm Runes"
+ nexus_wizard = "Nexus: Wizard Runes"
+
+ nexus_items: List[str] = [nexus_farm, nexus_wizard, nexus_spring, nexus_aurora, nexus_guild, nexus_junimo, nexus_outpost]
+
diff --git a/worlds/stardew_valley/strings/artisan_good_names.py b/worlds/stardew_valley/strings/artisan_good_names.py
index 469644d95fd3..a017cff1f9dd 100644
--- a/worlds/stardew_valley/strings/artisan_good_names.py
+++ b/worlds/stardew_valley/strings/artisan_good_names.py
@@ -21,3 +21,7 @@ class ArtisanGood:
caviar = "Caviar"
green_tea = "Green Tea"
mead = "Mead"
+
+
+class ModArtisanGood:
+ pterodactyl_egg = "Pterodactyl Egg"
diff --git a/worlds/stardew_valley/strings/bundle_names.py b/worlds/stardew_valley/strings/bundle_names.py
new file mode 100644
index 000000000000..de8d8af3877f
--- /dev/null
+++ b/worlds/stardew_valley/strings/bundle_names.py
@@ -0,0 +1,80 @@
+class CCRoom:
+ pantry = "Pantry"
+ crafts_room = "Crafts Room"
+ fish_tank = "Fish Tank"
+ bulletin_board = "Bulletin Board"
+ vault = "Vault"
+ boiler_room = "Boiler Room"
+ abandoned_joja_mart = "Abandoned Joja Mart"
+
+
+class BundleName:
+ spring_foraging = "Spring Foraging Bundle"
+ summer_foraging = "Summer Foraging Bundle"
+ fall_foraging = "Fall Foraging Bundle"
+ winter_foraging = "Winter Foraging Bundle"
+ construction = "Construction Bundle"
+ exotic_foraging = "Exotic Foraging Bundle"
+ beach_foraging = "Beach Foraging Bundle"
+ mines_foraging = "Mines Foraging Bundle"
+ desert_foraging = "Desert Foraging Bundle"
+ island_foraging = "Island Foraging Bundle"
+ sticky = "Sticky Bundle"
+ wild_medicine = "Wild Medicine Bundle"
+ quality_foraging = "Quality Foraging Bundle"
+ spring_crops = "Spring Crops Bundle"
+ summer_crops = "Summer Crops Bundle"
+ fall_crops = "Fall Crops Bundle"
+ quality_crops = "Quality Crops Bundle"
+ animal = "Animal Bundle"
+ artisan = "Artisan Bundle"
+ rare_crops = "Rare Crops Bundle"
+ fish_farmer = "Fish Farmer's Bundle"
+ garden = "Garden Bundle"
+ brewer = "Brewer's Bundle"
+ orchard = "Orchard Bundle"
+ island_crops = "Island Crops Bundle"
+ agronomist = "Agronomist's Bundle"
+ slime_farmer = "Slime Farmer Bundle"
+ river_fish = "River Fish Bundle"
+ lake_fish = "Lake Fish Bundle"
+ ocean_fish = "Ocean Fish Bundle"
+ night_fish = "Night Fishing Bundle"
+ crab_pot = "Crab Pot Bundle"
+ trash = "Trash Bundle"
+ recycling = "Recycling Bundle"
+ specialty_fish = "Specialty Fish Bundle"
+ spring_fish = "Spring Fishing Bundle"
+ summer_fish = "Summer Fishing Bundle"
+ fall_fish = "Fall Fishing Bundle"
+ winter_fish = "Winter Fishing Bundle"
+ rain_fish = "Rain Fishing Bundle"
+ quality_fish = "Quality Fish Bundle"
+ master_fisher = "Master Fisher's Bundle"
+ legendary_fish = "Legendary Fish Bundle"
+ island_fish = "Island Fish Bundle"
+ deep_fishing = "Deep Fishing Bundle"
+ tackle = "Tackle Bundle"
+ bait = "Master Baiter Bundle"
+ blacksmith = "Blacksmith's Bundle"
+ geologist = "Geologist's Bundle"
+ adventurer = "Adventurer's Bundle"
+ treasure_hunter = "Treasure Hunter's Bundle"
+ engineer = "Engineer's Bundle"
+ demolition = "Demolition Bundle"
+ paleontologist = "Paleontologist's Bundle"
+ archaeologist = "Archaeologist's Bundle"
+ chef = "Chef's Bundle"
+ dye = "Dye Bundle"
+ field_research = "Field Research Bundle"
+ fodder = "Fodder Bundle"
+ enchanter = "Enchanter's Bundle"
+ children = "Children's Bundle"
+ forager = "Forager's Bundle"
+ home_cook = "Home Cook's Bundle"
+ bartender = "Bartender's Bundle"
+ gambler = "Gambler's Bundle"
+ carnival = "Carnival Bundle"
+ walnut_hunter = "Walnut Hunter Bundle"
+ qi_helper = "Qi's Helper Bundle"
+ missing_bundle = "The Missing Bundle"
diff --git a/worlds/stardew_valley/strings/craftable_names.py b/worlds/stardew_valley/strings/craftable_names.py
index a1ee15b12fde..74a77a8e9467 100644
--- a/worlds/stardew_valley/strings/craftable_names.py
+++ b/worlds/stardew_valley/strings/craftable_names.py
@@ -1,16 +1,185 @@
-class Craftable:
- bait = "Bait"
+class Bomb:
cherry_bomb = "Cherry Bomb"
bomb = "Bomb"
mega_bomb = "Mega Bomb"
- staircase = "Staircase"
- scarecrow = "Scarecrow"
- rain_totem = "Rain Totem"
- flute_block = "Flute Block"
+
+
+class Fence:
+ gate = "Gate"
+ wood = "Wood Fence"
+ stone = "Stone Fence"
+ iron = "Iron Fence"
+ hardwood = "Hardwood Fence"
+
+
+class Sprinkler:
+ basic = "Sprinkler"
+ quality = "Quality Sprinkler"
+ iridium = "Iridium Sprinkler"
+
+
+class WildSeeds:
+ spring = "Spring Seeds"
+ summer = "Summer Seeds"
+ fall = "Fall Seeds"
+ winter = "Winter Seeds"
+ ancient = "Ancient Seeds"
+ grass_starter = "Grass Starter"
+ tea_sapling = "Tea Sapling"
+ fiber = "Fiber Seeds"
+
+
+class Floor:
+ wood = "Wood Floor"
+ rustic = "Rustic Plank Floor"
+ straw = "Straw Floor"
+ weathered = "Weathered Floor"
+ crystal = "Crystal Floor"
+ stone = "Stone Floor"
+ stone_walkway = "Stone Walkway Floor"
+ brick = "Brick Floor"
+ wood_path = "Wood Path"
+ gravel_path = "Gravel Path"
+ cobblestone_path = "Cobblestone Path"
+ stepping_stone_path = "Stepping Stone Path"
+ crystal_path = "Crystal Path"
+
+
+class Fishing:
+ spinner = "Spinner"
+ trap_bobber = "Trap Bobber"
+ cork_bobber = "Cork Bobber"
+ quality_bobber = "Quality Bobber"
+ treasure_hunter = "Treasure Hunter"
+ dressed_spinner = "Dressed Spinner"
+ barbed_hook = "Barbed Hook"
+ magnet = "Magnet"
+ bait = "Bait"
+ wild_bait = "Wild Bait"
+ magic_bait = "Magic Bait"
+ lead_bobber = "Lead Bobber"
+ curiosity_lure = "Curiosity Lure"
+
+
+class Ring:
+ hot_java_ring = "Hot Java Ring"
+ sturdy_ring = "Sturdy Ring"
+ warrior_ring = "Warrior Ring"
+ ring_of_yoba = "Ring of Yoba"
+ thorns_ring = "Thorns Ring"
+ glowstone_ring = "Glowstone Ring"
+ iridium_band = "Iridium Band"
+ wedding_ring = "Wedding Ring"
+
+
+class Edible:
+ field_snack = "Field Snack"
+ bug_steak = "Bug Steak"
life_elixir = "Life Elixir"
- monster_musk = "Monster Musk"
oil_of_garlic = "Oil of Garlic"
+class Consumable:
+ monster_musk = "Monster Musk"
+ fairy_dust = "Fairy Dust"
+ warp_totem_beach = "Warp Totem: Beach"
+ warp_totem_mountains = "Warp Totem: Mountains"
+ warp_totem_farm = "Warp Totem: Farm"
+ warp_totem_desert = "Warp Totem: Desert"
+ warp_totem_island = "Warp Totem: Island"
+ rain_totem = "Rain Totem"
+
+
+class Lighting:
+ torch = "Torch"
+ campfire = "Campfire"
+ wooden_brazier = "Wooden Brazier"
+ stone_brazier = "Stone Brazier"
+ gold_brazier = "Gold Brazier"
+ carved_brazier = "Carved Brazier"
+ stump_brazier = "Stump Brazier"
+ barrel_brazier = "Barrel Brazier"
+ skull_brazier = "Skull Brazier"
+ marble_brazier = "Marble Brazier"
+ wood_lamp_post = "Wood Lamp-post"
+ iron_lamp_post = "Iron Lamp-post"
+ jack_o_lantern = "Jack-O-Lantern"
+
+
+class Furniture:
+ tub_o_flowers = "Tub o' Flowers"
+ wicked_statue = "Wicked Statue"
+ flute_block = "Flute Block"
+ drum_block = "Drum Block"
+
+
+class Storage:
+ chest = "Chest"
+ stone_chest = "Stone Chest"
+
+
+class Sign:
+ wood = "Wood Sign"
+ stone = "Stone Sign"
+ dark = "Dark Sign"
+
+
+class Craftable:
+ garden_pot = "Garden Pot"
+ scarecrow = "Scarecrow"
+ deluxe_scarecrow = "Deluxe Scarecrow"
+ staircase = "Staircase"
+ explosive_ammo = "Explosive Ammo"
+ transmute_fe = "Transmute (Fe)"
+ transmute_au = "Transmute (Au)"
+ mini_jukebox = "Mini-Jukebox"
+ mini_obelisk = "Mini-Obelisk"
+ farm_computer = "Farm Computer"
+ hopper = "Hopper"
+ cookout_kit = "Cookout Kit"
+
+
+class ModEdible:
+ magic_elixir = "Magic Elixir"
+ aegis_elixir = "Aegis Elixir"
+ armor_elixir = "Armor Elixir"
+ barbarian_elixir = "Barbarian Elixir"
+ lightning_elixir = "Lightning Elixir"
+ gravity_elixir = "Gravity Elixir"
+ hero_elixir = "Hero Elixir"
+ haste_elixir = "Haste Elixir"
+
+
+class ModCraftable:
+ travel_core = "Travel Core"
+ glass_bazier = "Glass Bazier"
+ water_shifter = "Water Shifter"
+ glass_fence = "Glass Fence"
+ wooden_display = "Wooden Display"
+ hardwood_display = "Hardwood Display"
+ neanderthal_skeleton = "Neanderthal Skeleton"
+ pterodactyl_skeleton_l = "Pterodactyl Skeleton L"
+ pterodactyl_skeleton_m = "Pterodactyl Skeleton M"
+ pterodactyl_skeleton_r = "Pterodactyl Skeleton R"
+ trex_skeleton_l = "T-Rex Skeleton L"
+ trex_skeleton_m = "T-Rex Skeleton M"
+ trex_skeleton_r = "T-Rex Skeleton R"
+
+
+class ModMachine:
+ preservation_chamber = "Preservation Chamber"
+ hardwood_preservation_chamber = "Hardwood Preservation Chamber"
+ grinder = "Grinder"
+ ancient_battery = "Ancient Battery Production Station"
+
+
+class ModFloor:
+ glass_path = "Glass Path"
+ bone_path = "Bone Path"
+
+
+class ModConsumable:
+ volcano_totem = "Dwarf Gadget: Infinite Volcano Simulation"
+ ginger_tincture = "Ginger Tincture"
diff --git a/worlds/stardew_valley/strings/crop_names.py b/worlds/stardew_valley/strings/crop_names.py
index 2b5ea4d32768..295e40005f75 100644
--- a/worlds/stardew_valley/strings/crop_names.py
+++ b/worlds/stardew_valley/strings/crop_names.py
@@ -13,6 +13,7 @@ def fruity(name: str) -> str:
class Fruit:
+ sweet_gem_berry = fruity("Sweet Gem Berry")
any = "Any Fruit"
blueberry = fruity("Blueberry")
melon = fruity("Melon")
@@ -38,6 +39,7 @@ class Vegetable:
any = "Any Vegetable"
parsnip = veggie("Parsnip")
garlic = veggie("Garlic")
+ bok_choy = "Bok Choy"
wheat = "Wheat"
potato = veggie("Potato")
corn = veggie("Corn")
@@ -57,3 +59,24 @@ class Vegetable:
yam = veggie("Yam")
radish = veggie("Radish")
taro_root = veggie("Taro Root")
+
+
+class SVEFruit:
+ slime_berry = "Slime Berry"
+ monster_fruit = "Monster Fruit"
+ salal_berry = "Salal Berry"
+
+
+class SVEVegetable:
+ monster_mushroom = "Monster Mushroom"
+ void_root = "Void Root"
+ ancient_fiber = "Ancient Fiber"
+
+
+class DistantLandsCrop:
+ void_mint = "Void Mint Leaves"
+ vile_ancient_fruit = "Vile Ancient Fruit"
+
+
+all_vegetables = tuple(all_vegetables)
+all_fruits = tuple(all_fruits)
diff --git a/worlds/stardew_valley/strings/currency_names.py b/worlds/stardew_valley/strings/currency_names.py
new file mode 100644
index 000000000000..5192466c9ca7
--- /dev/null
+++ b/worlds/stardew_valley/strings/currency_names.py
@@ -0,0 +1,13 @@
+class Currency:
+ qi_coin = "Qi Coin"
+ golden_walnut = "Golden Walnut"
+ qi_gem = "Qi Gem"
+ star_token = "Star Token"
+ money = "Money"
+ cinder_shard = "Cinder Shard"
+
+ @staticmethod
+ def is_currency(item: str) -> bool:
+ return item in [Currency.qi_coin, Currency.golden_walnut, Currency.qi_gem, Currency.star_token, Currency.money]
+
+
diff --git a/worlds/stardew_valley/strings/decoration_names.py b/worlds/stardew_valley/strings/decoration_names.py
new file mode 100644
index 000000000000..150a106c6a20
--- /dev/null
+++ b/worlds/stardew_valley/strings/decoration_names.py
@@ -0,0 +1,2 @@
+class Decoration:
+ rotten_plant = "Rotten Plant"
diff --git a/worlds/stardew_valley/strings/entrance_names.py b/worlds/stardew_valley/strings/entrance_names.py
index e744400cfbd5..00823d62ea07 100644
--- a/worlds/stardew_valley/strings/entrance_names.py
+++ b/worlds/stardew_valley/strings/entrance_names.py
@@ -2,6 +2,10 @@ def dig_to_mines_floor(floor: int) -> str:
return f"Dig to The Mines - Floor {floor}"
+def dig_to_dangerous_mines_floor(floor: int) -> str:
+ return f"Dig to the Dangerous Mines - Floor {floor}"
+
+
def dig_to_skull_floor(floor: int) -> str:
return f"Mine to Skull Cavern Floor {floor}"
@@ -22,6 +26,10 @@ class Entrance:
farm_to_forest = "Farm to Forest"
farm_to_farmcave = "Farm to Farmcave"
enter_greenhouse = "Farm to Greenhouse"
+ enter_coop = "Farm to Coop"
+ enter_barn = "Farm to Barn"
+ enter_shed = "Farm to Shed"
+ enter_slime_hutch = "Farm to Slime Hutch"
use_desert_obelisk = "Use Desert Obelisk"
use_island_obelisk = "Use Island Obelisk"
use_farm_obelisk = "Use Farm Obelisk"
@@ -35,6 +43,13 @@ class Entrance:
forest_to_leah_cottage = "Forest to Leah's Cottage"
forest_to_sewer = "Forest to Sewer"
buy_from_traveling_merchant = "Buy from Traveling Merchant"
+ buy_from_traveling_merchant_sunday = "Buy from Traveling Merchant Sunday"
+ buy_from_traveling_merchant_monday = "Buy from Traveling Merchant Monday"
+ buy_from_traveling_merchant_tuesday = "Buy from Traveling Merchant Tuesday"
+ buy_from_traveling_merchant_wednesday = "Buy from Traveling Merchant Wednesday"
+ buy_from_traveling_merchant_thursday = "Buy from Traveling Merchant Thursday"
+ buy_from_traveling_merchant_friday = "Buy from Traveling Merchant Friday"
+ buy_from_traveling_merchant_saturday = "Buy from Traveling Merchant Saturday"
mountain_to_railroad = "Mountain to Railroad"
mountain_to_tent = "Mountain to Tent"
mountain_to_carpenter_shop = "Mountain to Carpenter Shop"
@@ -63,6 +78,9 @@ class Entrance:
town_to_clint_blacksmith = "Town to Clint's Blacksmith"
town_to_museum = "Town to Museum"
town_to_jojamart = "Town to JojaMart"
+ purchase_movie_ticket = "Purchase Movie Ticket"
+ enter_abandoned_jojamart = "Enter Abandoned Joja Mart"
+ enter_movie_theater = "Enter Movie Theater"
beach_to_willy_fish_shop = "Beach to Willy's Fish Shop"
fish_shop_to_boat_tunnel = "Fish Shop to Boat Tunnel"
boat_to_ginger_island = "Take the Boat to Ginger Island"
@@ -101,6 +119,7 @@ class Entrance:
mine_to_skull_cavern_floor_150 = dig_to_skull_floor(150)
mine_to_skull_cavern_floor_175 = dig_to_skull_floor(175)
mine_to_skull_cavern_floor_200 = dig_to_skull_floor(200)
+ enter_dangerous_skull_cavern = "Enter the Dangerous Skull Cavern"
talk_to_mines_dwarf = "Talk to Mines Dwarf"
dig_to_mines_floor_5 = dig_to_mines_floor(5)
dig_to_mines_floor_10 = dig_to_mines_floor(10)
@@ -126,6 +145,9 @@ class Entrance:
dig_to_mines_floor_110 = dig_to_mines_floor(110)
dig_to_mines_floor_115 = dig_to_mines_floor(115)
dig_to_mines_floor_120 = dig_to_mines_floor(120)
+ dig_to_dangerous_mines_20 = dig_to_dangerous_mines_floor(20)
+ dig_to_dangerous_mines_60 = dig_to_dangerous_mines_floor(60)
+ dig_to_dangerous_mines_100 = dig_to_dangerous_mines_floor(100)
island_south_to_west = "Island South to West"
island_south_to_north = "Island South to North"
island_south_to_east = "Island South to East"
@@ -161,6 +183,26 @@ class Entrance:
parrot_express_jungle_to_docks = "Parrot Express Jungle to Docks"
parrot_express_dig_site_to_docks = "Parrot Express Dig Site to Docks"
parrot_express_volcano_to_docks = "Parrot Express Volcano to Docks"
+ farmhouse_cooking = "Farmhouse Cooking"
+ island_cooking = "Island Cooking"
+ shipping = "Use Shipping Bin"
+ watch_queen_of_sauce = "Watch Queen of Sauce"
+ blacksmith_copper = "Upgrade Copper Tools"
+ blacksmith_iron = "Upgrade Iron Tools"
+ blacksmith_gold = "Upgrade Gold Tools"
+ blacksmith_iridium = "Upgrade Iridium Tools"
+ farming = "Start Farming"
+ fishing = "Start Fishing"
+ attend_egg_festival = "Attend Egg Festival"
+ attend_flower_dance = "Attend Flower Dance"
+ attend_luau = "Attend Luau"
+ attend_moonlight_jellies = "Attend Dance of the Moonlight Jellies"
+ attend_fair = "Attend Stardew Valley Fair"
+ attend_spirit_eve = "Attend Spirit's Eve"
+ attend_festival_of_ice = "Attend Festival of Ice"
+ attend_night_market = "Attend Night Market"
+ attend_winter_star = "Attend Feast of the Winter Star"
+
# Skull Cavern Elevator
@@ -215,3 +257,103 @@ class AyeishaEntrance:
class RileyEntrance:
town_to_riley = "Town to Riley's House"
+
+class SVEEntrance:
+ backwoods_to_grove = "Backwoods to Enchanted Grove"
+ grove_to_outpost_warp = "Enchanted Grove to Grove Outpost Warp"
+ outpost_warp_to_outpost = "Grove Outpost Warp to Galmoran Outpost"
+ grove_to_wizard_warp = "Enchanted Grove to Grove Wizard Warp"
+ wizard_warp_to_wizard = "Grove Wizard Warp to Wizard Basement"
+ grove_to_aurora_warp = "Enchanted Grove to Grove Aurora Vineyard Warp"
+ aurora_warp_to_aurora = "Grove Aurora Vineyard Warp to Aurora Vineyard Basement"
+ grove_to_farm_warp = "Enchanted Grove to Grove Farm Warp"
+ farm_warp_to_farm = "Grove Farm Warp to Farm"
+ grove_to_guild_warp = "Enchanted Grove to Grove Guild Warp"
+ guild_warp_to_guild = "Grove Guild Warp to Guild Summit"
+ grove_to_junimo_warp = "Enchanted Grove to Grove Junimo Woods Warp"
+ junimo_warp_to_junimo = "Grove Junimo Woods Warp to Junimo Woods"
+ grove_to_spring_warp = "Enchanted Grove to Grove Sprite Spring Warp"
+ spring_warp_to_spring = "Grove Sprite Spring Warp to Sprite Spring"
+ wizard_to_fable_reef = "Wizard Basement to Fable Reef"
+ bus_stop_to_shed = "Bus Stop to Grandpa's Shed"
+ grandpa_shed_to_interior = "Grandpa's Shed to Grandpa's Shed Interior"
+ grandpa_shed_to_town = "Grandpa's Shed to Town"
+ grandpa_interior_to_upstairs = "Grandpa's Shed Interior to Grandpa's Shed Upstairs"
+ forest_to_fairhaven = "Forest to Fairhaven Farm"
+ forest_to_west = "Forest to Forest West"
+ forest_to_lost_woods = "Forest to Lost Woods"
+ lost_woods_to_junimo_woods = "Lost Woods to Junimo Woods"
+ use_purple_junimo = "Talk to Purple Junimo"
+ forest_to_bmv = "Forest to Blue Moon Vineyard"
+ forest_to_marnie_shed = "Forest to Marnie's Shed"
+ town_to_bmv = "Town to Blue Moon Vineyard"
+ town_to_jenkins = "Town to Jenkins' Residence"
+ town_to_bridge = "Town to Shearwater Bridge"
+ town_to_plot = "Town to Unclaimed Plot"
+ bmv_to_sophia = "Blue Moon Vineyard to Sophia's House"
+ bmv_to_beach = "Blue Moon Vineyard to Beach"
+ jenkins_to_cellar = "Jenkins' Residence to Jenkins' Cellar"
+ plot_to_bridge = "Unclaimed Plot to Shearwater Bridge"
+ mountain_to_guild_summit = "Mountain to Guild Summit"
+ guild_to_interior = "Guild Summit to Adventurer's Guild"
+ guild_to_mines = "Guild Summit to The Mines"
+ summit_to_boat = "Guild Summit to Marlon's Boat"
+ summit_to_highlands = "Guild Summit to Highlands Outside"
+ to_aurora_basement = "Aurora Vineyard to Aurora Vineyard Basement"
+ outpost_to_badlands_entrance = "Galmoran Outpost to Badlands Entrance"
+ use_alesia_shop = "Talk to Alesia"
+ use_isaac_shop = "Talk to Isaac"
+ badlands_entrance_to_badlands = "Badlands Entrance to Crimson Badlands"
+ badlands_to_cave = "Crimson Badlands to Badlands Cave"
+ to_susan_house = "Railroad to Susan's House"
+ enter_summit = "Railroad to Summit"
+ fable_reef_to_guild = "Fable Reef to First Slash Guild"
+ highlands_to_lance = "Highlands Outside to Lance's House Main"
+ lance_to_ladder = "Lance's House Main to Lance's House Ladder"
+ highlands_to_cave = "Highlands Outside to Highlands Cavern"
+ to_dwarf_prison = "Highlands Cavern to Highlands Cavern Prison"
+ lance_ladder_to_highlands = "Lance's House Ladder to Highlands Outside"
+ forest_west_to_spring = "Forest West to Sprite Spring"
+ west_to_aurora = "Forest West to Aurora Vineyard"
+ use_bear_shop = "Talk to Bear Shop"
+ secret_woods_to_west = "Secret Woods to Forest West"
+ to_outpost_roof = "Galmoran Outpost to Galmoran Outpost Roof"
+ railroad_to_grampleton_station = "Railroad to Grampleton Station"
+ grampleton_station_to_grampleton_suburbs = "Grampleton Station to Grampleton Suburbs"
+ grampleton_suburbs_to_scarlett_house = "Grampleton Suburbs to Scarlett's House"
+ first_slash_guild_to_hallway = "First Slash Guild to First Slash Hallway"
+ first_slash_hallway_to_room = "First Slash Hallway to First Slash Spare Room"
+ sprite_spring_to_cave = "Sprite Spring to Sprite Spring Cave"
+ fish_shop_to_willy_bedroom = "Willy's Fish Shop to Willy's Bedroom"
+ museum_to_gunther_bedroom = "Museum to Gunther's Bedroom"
+
+
+class AlectoEntrance:
+ witch_hut_to_witch_attic = "Witch's Hut to Witch's Attic"
+
+
+class LaceyEntrance:
+ forest_to_hat_house = "Forest to Mouse House"
+
+
+class BoardingHouseEntrance:
+ bus_stop_to_boarding_house_plateau = "Bus Stop to Boarding House Outside"
+ boarding_house_plateau_to_boarding_house_first = "Boarding House Outside to Boarding House - First Floor"
+ boarding_house_first_to_boarding_house_second = "Boarding House - First Floor to Boarding House - Second Floor"
+ boarding_house_plateau_to_abandoned_mines_entrance = "Boarding House Outside to Abandoned Mines Entrance"
+ abandoned_mines_entrance_to_abandoned_mines_1a = "Abandoned Mines Entrance to Abandoned Mines - 1A"
+ abandoned_mines_1a_to_abandoned_mines_1b = "Abandoned Mines - 1A to Abandoned Mines - 1B"
+ abandoned_mines_1b_to_abandoned_mines_2a = "Abandoned Mines - 1B to Abandoned Mines - 2A"
+ abandoned_mines_2a_to_abandoned_mines_2b = "Abandoned Mines - 2A to Abandoned Mines - 2B"
+ abandoned_mines_2b_to_abandoned_mines_3 = "Abandoned Mines - 2B to Abandoned Mines - 3"
+ abandoned_mines_3_to_abandoned_mines_4 = "Abandoned Mines - 3 to Abandoned Mines - 4"
+ abandoned_mines_4_to_abandoned_mines_5 = "Abandoned Mines - 4 to Abandoned Mines - 5"
+ abandoned_mines_5_to_the_lost_valley = "Abandoned Mines - 5 to The Lost Valley"
+ lost_valley_to_lost_valley_minecart = "The Lost Valley to Lost Valley Minecart"
+ abandoned_mines_entrance_to_the_lost_valley = "Abandoned Mines Entrance to The Lost Valley"
+ the_lost_valley_to_gregory_tent = "The Lost Valley to Gregory's Tent"
+ the_lost_valley_to_lost_valley_ruins = "The Lost Valley to Lost Valley Ruins"
+ lost_valley_ruins_to_lost_valley_house_1 = "Lost Valley Ruins to Lost Valley Ruins - First House"
+ lost_valley_ruins_to_lost_valley_house_2 = "Lost Valley Ruins to Lost Valley Ruins - Second House"
+ boarding_house_plateau_to_buffalo_ranch = "Boarding House Outside to Buffalo's Ranch"
+
diff --git a/worlds/stardew_valley/strings/festival_check_names.py b/worlds/stardew_valley/strings/festival_check_names.py
index 404878999fc7..73a9d3978eab 100644
--- a/worlds/stardew_valley/strings/festival_check_names.py
+++ b/worlds/stardew_valley/strings/festival_check_names.py
@@ -20,6 +20,7 @@ class FestivalCheck:
moonlight_jellies = "Dance of the Moonlight Jellies"
rarecrow_1 = "Rarecrow #1 (Turnip Head)"
rarecrow_2 = "Rarecrow #2 (Witch)"
+ rarecrow_3 = "Rarecrow #3 (Alien)"
rarecrow_4 = "Rarecrow #4 (Snowman)"
rarecrow_5 = "Rarecrow #5 (Woman)"
rarecrow_7 = "Rarecrow #7 (Tanuki)"
@@ -30,3 +31,7 @@ class FestivalCheck:
spirit_eve_maze = "Spirit's Eve Maze"
strawberry_seeds = "Egg Festival: Strawberry Seeds"
all_rarecrows = "Collect All Rarecrows"
+ tub_o_flowers = "Tub o' Flowers Recipe"
+ jack_o_lantern = "Jack-O-Lantern Recipe"
+ moonlight_jellies_banner = "Moonlight Jellies Banner"
+ starport_decal = "Starport Decal"
diff --git a/worlds/stardew_valley/strings/fish_names.py b/worlds/stardew_valley/strings/fish_names.py
index 8ee778103752..cd59d749ee01 100644
--- a/worlds/stardew_valley/strings/fish_names.py
+++ b/worlds/stardew_valley/strings/fish_names.py
@@ -1,49 +1,84 @@
class Fish:
+ albacore = "Albacore"
+ anchovy = "Anchovy"
angler = "Angler"
any = "Any Fish"
+ blob_fish = "Blobfish"
blobfish = "Blobfish"
blue_discus = "Blue Discus"
bream = "Bream"
+ bullhead = "Bullhead"
+ carp = "Carp"
catfish = "Catfish"
+ chub = "Chub"
+ clam = "Clam"
+ cockle = "Cockle"
crab = "Crab"
crayfish = "Crayfish"
crimsonfish = "Crimsonfish"
dorado = "Dorado"
+ eel = "Eel"
+ flounder = "Flounder"
+ ghostfish = "Ghostfish"
glacierfish = "Glacierfish"
+ glacierfish_jr = "Glacierfish Jr."
+ halibut = "Halibut"
+ herring = "Herring"
+ ice_pip = "Ice Pip"
+ largemouth_bass = "Largemouth Bass"
lava_eel = "Lava Eel"
legend = "Legend"
+ legend_ii = "Legend II"
+ lingcod = "Lingcod"
lionfish = "Lionfish"
lobster = "Lobster"
+ midnight_carp = "Midnight Carp"
+ midnight_squid = "Midnight Squid"
+ ms_angler = "Ms. Angler"
mussel = "Mussel"
mussel_node = "Mussel Node"
mutant_carp = "Mutant Carp"
octopus = "Octopus"
oyster = "Oyster"
+ perch = "Perch"
+ periwinkle = "Periwinkle"
+ pike = "Pike"
pufferfish = "Pufferfish"
+ radioactive_carp = "Radioactive Carp"
+ rainbow_trout = "Rainbow Trout"
+ red_mullet = "Red Mullet"
+ red_snapper = "Red Snapper"
+ salmon = "Salmon"
+ sandfish = "Sandfish"
+ sardine = "Sardine"
+ scorpion_carp = "Scorpion Carp"
+ sea_cucumber = "Sea Cucumber"
+ shad = "Shad"
+ shrimp = "Shrimp"
+ slimejack = "Slimejack"
+ smallmouth_bass = "Smallmouth Bass"
+ snail = "Snail"
+ son_of_crimsonfish = "Son of Crimsonfish"
+ spook_fish = "Spook Fish"
spookfish = "Spook Fish"
squid = "Squid"
stingray = "Stingray"
+ stonefish = "Stonefish"
sturgeon = "Sturgeon"
sunfish = "Sunfish"
- void_salmon = "Void Salmon"
- albacore = "Albacore"
- largemouth_bass = "Largemouth Bass"
- smallmouth_bass = "Smallmouth Bass"
- sardine = "Sardine"
- periwinkle = "Periwinkle"
- shrimp = "Shrimp"
- snail = "Snail"
+ super_cucumber = "Super Cucumber"
+ tiger_trout = "Tiger Trout"
+ tilapia = "Tilapia"
tuna = "Tuna"
- eel = "Eel"
- salmon = "Salmon"
+ void_salmon = "Void Salmon"
+ walleye = "Walleye"
+ woodskip = "Woodskip"
class WaterItem:
seaweed = "Seaweed"
green_algae = "Green Algae"
white_algae = "White Algae"
- clam = "Clam"
- cockle = "Cockle"
coral = "Coral"
nautilus_shell = "Nautilus Shell"
sea_urchin = "Sea Urchin"
@@ -58,5 +93,44 @@ class Trash:
soggy_newspaper = "Soggy Newspaper"
+class WaterChest:
+ fishing_chest = "Fishing Chest"
+ treasure = "Treasure Chest"
+
+
+class SVEFish:
+ baby_lunaloo = "Baby Lunaloo"
+ bonefish = "Bonefish"
+ bull_trout = "Bull Trout"
+ butterfish = "Butterfish"
+ clownfish = "Clownfish"
+ daggerfish = "Daggerfish"
+ frog = "Frog"
+ gemfish = "Gemfish"
+ goldenfish = "Goldenfish"
+ grass_carp = "Grass Carp"
+ king_salmon = "King Salmon"
+ kittyfish = "Kittyfish"
+ lunaloo = "Lunaloo"
+ meteor_carp = "Meteor Carp"
+ minnow = "Minnow"
+ puppyfish = "Puppyfish"
+ radioactive_bass = "Radioactive Bass"
+ seahorse = "Seahorse"
+ shiny_lunaloo = "Shiny Lunaloo"
+ snatcher_worm = "Snatcher Worm"
+ starfish = "Starfish"
+ torpedo_trout = "Torpedo Trout"
+ undeadfish = "Undeadfish"
+ void_eel = "Void Eel"
+ water_grub = "Water Grub"
+ sea_sponge = "Sea Sponge"
+ dulse_seaweed = "Dulse Seaweed"
+
+class DistantLandsFish:
+ void_minnow = "Void Minnow"
+ swamp_leech = "Swamp Leech"
+ purple_algae = "Purple Algae"
+ giant_horsehoe_crab = "Giant Horsehoe Crab"
diff --git a/worlds/stardew_valley/strings/flower_names.py b/worlds/stardew_valley/strings/flower_names.py
index a804682f1b55..7e708fc3c074 100644
--- a/worlds/stardew_valley/strings/flower_names.py
+++ b/worlds/stardew_valley/strings/flower_names.py
@@ -1,3 +1,7 @@
class Flower:
- sunflower = "Sunflower"
+ blue_jazz = "Blue Jazz"
+ fairy_rose = "Fairy Rose"
poppy = "Poppy"
+ summer_spangle = "Summer Spangle"
+ sunflower = "Sunflower"
+ tulip = "Tulip"
diff --git a/worlds/stardew_valley/strings/food_names.py b/worlds/stardew_valley/strings/food_names.py
index 55e3ef0a7bd3..6e2f98fd581b 100644
--- a/worlds/stardew_valley/strings/food_names.py
+++ b/worlds/stardew_valley/strings/food_names.py
@@ -1,67 +1,118 @@
class Meal:
- blueberry_tart = "Blueberry Tart"
- bread = "Bread"
- fiddlehead_risotto = "Fiddlehead Risotto"
- complete_breakfast = "Complete Breakfast"
- fried_egg = "Fried Egg"
- hashbrowns = "Hashbrowns"
- pancakes = "Pancakes"
- ice_cream = "Ice Cream"
- maki_roll = "Maki Roll"
- miners_treat = "Miner's Treat"
- omelet = "Omelet"
- parsnip_soup = "Parsnip Soup"
- pink_cake = "Pink Cake"
- pizza = "Pizza"
- pumpkin_pie = "Pumpkin Pie"
- roasted_hazelnuts = "Roasted Hazelnuts"
- salad = "Salad"
- spaghetti = "Spaghetti"
- tortilla = "Tortilla"
+ banana_pudding = "Banana Pudding"
+ poi = "Poi"
+ mango_sticky_rice = "Mango Sticky Rice"
algae_soup = "Algae Soup"
artichoke_dip = "Artichoke Dip"
+ autumn_bounty = "Autumn's Bounty"
baked_fish = "Baked Fish"
bean_hotpot = "Bean Hotpot"
blackberry_cobbler = "Blackberry Cobbler"
+ blueberry_tart = "Blueberry Tart"
+ bread = "Bread"
+ bruschetta = "Bruschetta"
+ carp_surprise = "Carp Surprise"
cheese_cauliflower = "Cheese Cauliflower"
chocolate_cake = "Chocolate Cake"
chowder = "Chowder"
+ coleslaw = "Coleslaw"
+ complete_breakfast = "Complete Breakfast"
+ cookie = "Cookies"
crab_cakes = "Crab Cakes"
cranberry_candy = "Cranberry Candy"
+ cranberry_sauce = "Cranberry Sauce"
crispy_bass = "Crispy Bass"
dish_o_the_sea = "Dish O' The Sea"
eggplant_parmesan = "Eggplant Parmesan"
escargot = "Escargot"
farmer_lunch = "Farmer's Lunch"
+ fiddlehead_risotto = "Fiddlehead Risotto"
+ fish_stew = "Fish Stew"
fish_taco = "Fish Taco"
fried_calamari = "Fried Calamari"
fried_eel = "Fried Eel"
+ fried_egg = "Fried Egg"
fried_mushroom = "Fried Mushroom"
fruit_salad = "Fruit Salad"
glazed_yams = "Glazed Yams"
+ hashbrowns = "Hashbrowns"
+ ice_cream = "Ice Cream"
+ lobster_bisque = "Lobster Bisque"
+ lucky_lunch = "Lucky Lunch"
+ maki_roll = "Maki Roll"
maple_bar = "Maple Bar"
+ miners_treat = "Miner's Treat"
+ omelet = "Omelet"
pale_broth = "Pale Broth"
+ pancakes = "Pancakes"
+ parsnip_soup = "Parsnip Soup"
pepper_poppers = "Pepper Poppers"
+ pink_cake = "Pink Cake"
+ pizza = "Pizza"
plum_pudding = "Plum Pudding"
poppyseed_muffin = "Poppyseed Muffin"
+ pumpkin_pie = "Pumpkin Pie"
+ pumpkin_soup = "Pumpkin Soup"
+ radish_salad = "Radish Salad"
red_plate = "Red Plate"
rhubarb_pie = "Rhubarb Pie"
rice_pudding = "Rice Pudding"
+ roasted_hazelnuts = "Roasted Hazelnuts"
roots_platter = "Roots Platter"
+ salad = "Salad"
salmon_dinner = "Salmon Dinner"
sashimi = "Sashimi"
+ seafoam_pudding = "Seafoam Pudding"
+ shrimp_cocktail = "Shrimp Cocktail"
+ spaghetti = "Spaghetti"
+ spicy_eel = "Spicy Eel"
+ squid_ink_ravioli = "Squid Ink Ravioli"
stir_fry = "Stir Fry"
strange_bun = "Strange Bun"
stuffing = "Stuffing"
+ super_meal = "Super Meal"
survival_burger = "Survival Burger"
+ tom_kha_soup = "Tom Kha Soup"
+ tortilla = "Tortilla"
tropical_curry = "Tropical Curry"
+ trout_soup = "Trout Soup"
vegetable_medley = "Vegetable Medley"
+ magic_rock_candy = "Magic Rock Candy"
class Beverage:
- pina_colada = "Piña Colada"
+ pina_colada = "Pina Colada"
ginger_ale = "Ginger Ale"
coffee = "Coffee"
triple_shot_espresso = "Triple Shot Espresso"
beer = "Beer"
joja_cola = "Joja Cola"
+
+
+class SVEMeal:
+ baked_berry_oatmeal = "Baked Berry Oatmeal"
+ big_bark_burger = "Big Bark Burger"
+ flower_cookie = "Flower Cookie"
+ frog_legs = "Frog Legs"
+ glazed_butterfish = "Glazed Butterfish"
+ mixed_berry_pie = "Mixed Berry Pie"
+ mushroom_berry_rice = "Mushroom Berry Rice"
+ seaweed_salad = "Seaweed Salad"
+ void_delight = "Void Delight"
+ void_salmon_sushi = "Void Salmon Sushi"
+ grampleton_orange_chicken = "Grampleton Orange Chicken"
+
+
+class SVEBeverage:
+ sports_drink = "Sports Drink"
+
+
+class DistantLandsMeal:
+ mushroom_kebab = "Mushroom Kebab"
+ crayfish_soup = "Crayfish Soup"
+ pemmican = "Pemmican"
+ void_mint_tea = "Void Mint Tea"
+
+
+class BoardingHouseMeal:
+ special_pumpkin_soup = "Special Pumpkin Soup"
diff --git a/worlds/stardew_valley/strings/forageable_names.py b/worlds/stardew_valley/strings/forageable_names.py
index b29ff317cf77..24127beb9838 100644
--- a/worlds/stardew_valley/strings/forageable_names.py
+++ b/worlds/stardew_valley/strings/forageable_names.py
@@ -14,6 +14,7 @@ class Forageable:
hay = "Hay"
hazelnut = "Hazelnut"
holly = "Holly"
+ journal_scrap = "Journal Scrap"
leek = "Leek"
magma_cap = "Magma Cap"
morel = "Morel"
@@ -32,4 +33,29 @@ class Forageable:
spring_onion = "Spring Onion"
+class SVEForage:
+ ornate_treasure_chest = "Ornate Treasure Chest"
+ swirl_stone = "Swirl Stone"
+ void_pebble = "Void Pebble"
+ void_soul = "Void Soul"
+ ferngill_primrose = "Ferngill Primrose"
+ goldenrod = "Goldenrod"
+ winter_star_rose = "Winter Star Rose"
+ bearberrys = "Bearberrys"
+ poison_mushroom = "Poison Mushroom"
+ red_baneberry = "Red Baneberry"
+ big_conch = "Big Conch"
+ dewdrop_berry = "Dewdrop Berry"
+ dried_sand_dollar = "Dried Sand Dollar"
+ golden_ocean_flower = "Golden Ocean Flower"
+ lucky_four_leaf_clover = "Lucky Four Leaf Clover"
+ mushroom_colony = "Mushroom Colony"
+ poison_mushroom = "Poison Mushroom"
+ rusty_blade = "Rusty Blade"
+ smelly_rafflesia = "Smelly Rafflesia"
+ thistle = "Thistle"
+
+class DistantLandsForageable:
+ brown_amanita = "Brown Amanita"
+ swamp_herb = "Swamp Herb"
diff --git a/worlds/stardew_valley/strings/gift_names.py b/worlds/stardew_valley/strings/gift_names.py
index 0baf31d5dbfd..9362f453cfea 100644
--- a/worlds/stardew_valley/strings/gift_names.py
+++ b/worlds/stardew_valley/strings/gift_names.py
@@ -1,6 +1,14 @@
class Gift:
bouquet = "Bouquet"
- wilted_bouquet = "Wilted Bouquet"
- pearl = "Pearl"
golden_pumpkin = "Golden Pumpkin"
mermaid_pendant = "Mermaid's Pendant"
+ movie_ticket = "Movie Ticket"
+ pearl = "Pearl"
+ tea_set = "Tea Set"
+ void_ghost_pendant = "Void Ghost Pendant"
+ wilted_bouquet = "Wilted Bouquet"
+
+
+class SVEGift:
+ blue_moon_wine = "Blue Moon Wine"
+ aged_blue_moon_wine = "Aged Blue Moon Wine"
diff --git a/worlds/stardew_valley/strings/goal_names.py b/worlds/stardew_valley/strings/goal_names.py
index da8b7d847006..601b00510428 100644
--- a/worlds/stardew_valley/strings/goal_names.py
+++ b/worlds/stardew_valley/strings/goal_names.py
@@ -7,4 +7,11 @@ class Goal:
complete_museum = "Complete the Museum Collection"
full_house = "Full House"
greatest_walnut_hunter = "Greatest Walnut Hunter"
+ protector_of_the_valley = "Protector of the Valley"
+ full_shipment = "Full Shipment"
+ gourmet_chef = "Gourmet Chef"
+ craft_master = "Craft Master"
+ legend = "Legend"
+ mystery_of_the_stardrops = "Mystery of the Stardrops"
+ allsanity = "Allsanity"
perfection = "Perfection"
diff --git a/worlds/stardew_valley/strings/ingredient_names.py b/worlds/stardew_valley/strings/ingredient_names.py
index 22271d661587..5537c7353c42 100644
--- a/worlds/stardew_valley/strings/ingredient_names.py
+++ b/worlds/stardew_valley/strings/ingredient_names.py
@@ -4,3 +4,4 @@ class Ingredient:
oil = "Oil"
rice = "Rice"
vinegar = "Vinegar"
+ qi_seasoning = "Qi Seasoning"
diff --git a/worlds/stardew_valley/strings/machine_names.py b/worlds/stardew_valley/strings/machine_names.py
index 55d6cef79401..f9be78c41a03 100644
--- a/worlds/stardew_valley/strings/machine_names.py
+++ b/worlds/stardew_valley/strings/machine_names.py
@@ -1,22 +1,29 @@
class Machine:
bee_house = "Bee House"
+ bone_mill = "Bone Mill"
cask = "Cask"
charcoal_kiln = "Charcoal Kiln"
cheese_press = "Cheese Press"
+ coffee_maker = "Coffee Maker"
+ crab_pot = "Crab Pot"
+ crystalarium = "Crystalarium"
+ enricher = "Enricher"
furnace = "Furnace"
geode_crusher = "Geode Crusher"
+ heavy_tapper = "Heavy Tapper"
keg = "Keg"
lightning_rod = "Lightning Rod"
loom = "Loom"
mayonnaise_machine = "Mayonnaise Machine"
oil_maker = "Oil Maker"
+ ostrich_incubator = "Ostrich Incubator"
preserves_jar = "Preserves Jar"
+ pressure_nozzle = "Pressure Nozzle"
recycling_machine = "Recycling Machine"
seed_maker = "Seed Maker"
+ slime_egg_press = "Slime Egg-Press"
+ slime_incubator = "Slime Incubator"
solar_panel = "Solar Panel"
tapper = "Tapper"
worm_bin = "Worm Bin"
- coffee_maker = "Coffee Maker"
- crab_pot = "Crab Pot"
- ostrich_incubator = "Ostrich Incubator"
diff --git a/worlds/stardew_valley/strings/metal_names.py b/worlds/stardew_valley/strings/metal_names.py
index 67aefc692a56..bf15b9d01c8e 100644
--- a/worlds/stardew_valley/strings/metal_names.py
+++ b/worlds/stardew_valley/strings/metal_names.py
@@ -1,3 +1,17 @@
+all_fossils = []
+all_artifacts = []
+
+
+def fossil(name: str):
+ all_fossils.append(name)
+ return name
+
+
+def artifact(name: str):
+ all_artifacts.append(name)
+ return name
+
+
class Ore:
copper = "Copper Ore"
iron = "Iron Ore"
@@ -16,6 +30,14 @@ class MetalBar:
class Mineral:
+ petrified_slime = "Petrified Slime"
+ quartz = "Quartz"
+ earth_crystal = "Earth Crystal"
+ fire_quartz = "Fire Quartz"
+ marble = "Marble"
+ prismatic_shard = "Prismatic Shard"
+ diamond = "Diamond"
+ frozen_tear = "Frozen Tear"
aquamarine = "Aquamarine"
topaz = "Topaz"
jade = "Jade"
@@ -25,11 +47,99 @@ class Mineral:
class Artifact:
- pass # Eventually this will be the artifact names
+ prehistoric_handaxe = "Prehistoric Handaxe"
+ dwarf_gadget = artifact("Dwarf Gadget")
+ ancient_seed = artifact("Ancient Seed")
+ glass_shards = artifact("Glass Shards")
+ rusty_cog = artifact("Rusty Cog")
+ rare_disc = artifact("Rare Disc")
+ ancient_doll = artifact("Ancient Doll")
+ ancient_drum = artifact("Ancient Drum")
+ ancient_sword = artifact("Ancient Sword")
+ arrowhead = artifact("Arrowhead")
+ bone_flute = artifact("Bone Flute")
+ chewing_stick = artifact("Chewing Stick")
+ chicken_statue = artifact("Chicken Statue")
+ anchor = artifact("Anchor")
+ chipped_amphora = artifact("Chipped Amphora")
+ dwarf_scroll_i = artifact("Dwarf Scroll I")
+ dwarf_scroll_ii = artifact("Dwarf Scroll II")
+ dwarf_scroll_iii = artifact("Dwarf Scroll III")
+ dwarf_scroll_iv = artifact("Dwarf Scroll IV")
+ dwarvish_helm = artifact("Dwarvish Helm")
+ elvish_jewelry = artifact("Elvish Jewelry")
+ golden_mask = artifact("Golden Mask")
+ golden_relic = artifact("Golden Relic")
+ ornamental_fan = artifact("Ornamental Fan")
+ prehistoric_hammer = artifact("Prehistoric Handaxe")
+ prehistoric_tool = artifact("Prehistoric Tool")
+ rusty_spoon = artifact("Rusty Spoon")
+ rusty_spur = artifact("Rusty Spur")
+ strange_doll_green = artifact("Strange Doll (Green)")
+ strange_doll = artifact("Strange Doll")
class Fossil:
+ amphibian_fossil = fossil("Amphibian Fossil")
bone_fragment = "Bone Fragment"
+ dinosaur_egg = fossil("Dinosaur Egg")
+ dried_starfish = fossil("Dried Starfish")
+ fossilized_leg = fossil("Fossilized Leg")
+ fossilized_ribs = fossil("Fossilized Ribs")
+ fossilized_skull = fossil("Fossilized Skull")
+ fossilized_spine = fossil("Fossilized Spine")
+ fossilized_tail = fossil("Fossilized Tail")
+ mummified_bat = fossil("Mummified Bat")
+ mummified_frog = fossil("Mummified Frog")
+ nautilus_fossil = fossil("Nautilus Fossil")
+ palm_fossil = fossil("Palm Fossil")
+ prehistoric_hand = fossil("Skeletal Hand")
+ prehistoric_rib = fossil("Prehistoric Rib")
+ prehistoric_scapula = fossil("Prehistoric Scapula")
+ prehistoric_skull = fossil("Prehistoric Skull")
+ prehistoric_tibia = fossil("Prehistoric Tibia")
+ prehistoric_vertebra = fossil("Prehistoric Vertebra")
+ skeletal_hand = "Skeletal Hand"
+ skeletal_tail = fossil("Skeletal Tail")
+ snake_skull = fossil("Snake Skull")
+ snake_vertebrae = fossil("Snake Vertebrae")
+ trilobite = fossil("Trilobite")
+
+
+class ModArtifact:
+ ancient_hilt = "Ancient Hilt"
+ ancient_blade = "Ancient Blade"
+ mask_piece_1 = "Mask Piece 1"
+ mask_piece_2 = "Mask Piece 2"
+ mask_piece_3 = "Mask Piece 3"
+ ancient_doll_body = "Ancient Doll Body"
+ ancient_doll_legs = "Ancient Doll Legs"
+ prismatic_shard_piece_1 = "Prismatic Shard Piece 1"
+ prismatic_shard_piece_2 = "Prismatic Shard Piece 2"
+ prismatic_shard_piece_3 = "Prismatic Shard Piece 3"
+ prismatic_shard_piece_4 = "Prismatic Shard Piece 4"
+ chipped_amphora_piece_1 = "Chipped Amphora Piece 1"
+ chipped_amphora_piece_2 = "Chipped Amphora Piece 2"
+
+class ModFossil:
+ neanderthal_limb_bones = "Neanderthal Limb Bones"
+ neanderthal_ribs = "Neanderthal Ribs"
+ neanderthal_skull = "Neanderthal Skull"
+ neanderthal_pelvis = "Neanderthal Pelvis"
+ dinosaur_tooth = "Dinosaur Tooth"
+ dinosaur_skull = "Dinosaur Skull"
+ dinosaur_claw = "Dinosaur Claw"
+ dinosaur_femur = "Dinosaur Femur"
+ dinosaur_ribs = "Dinosaur Ribs"
+ dinosaur_pelvis = "Dinosaur Pelvis"
+ dinosaur_vertebra = "Dinosaur Vertebra"
+ pterodactyl_ribs = "Pterodactyl Ribs"
+ pterodactyl_skull = "Pterodactyl Skull"
+ pterodactyl_r_wing_bone = "Pterodactyl R Wing Bone"
+ pterodactyl_l_wing_bone = "Pterodactyl L Wing Bone"
+ pterodactyl_phalange = "Pterodactyl Phalange"
+ pterodactyl_vertebra = "Pterodactyl Vertebra"
+ pterodactyl_claw = "Pterodactyl Claw"
diff --git a/worlds/stardew_valley/strings/monster_drop_names.py b/worlds/stardew_valley/strings/monster_drop_names.py
index 1b9f42429d07..c42e7ad5ede0 100644
--- a/worlds/stardew_valley/strings/monster_drop_names.py
+++ b/worlds/stardew_valley/strings/monster_drop_names.py
@@ -1,6 +1,17 @@
class Loot:
+ blue_slime_egg = "Blue Slime Egg"
+ red_slime_egg = "Red Slime Egg"
+ purple_slime_egg = "Purple Slime Egg"
+ green_slime_egg = "Green Slime Egg"
+ tiger_slime_egg = "Tiger Slime Egg"
slime = "Slime"
bug_meat = "Bug Meat"
bat_wing = "Bat Wing"
solar_essence = "Solar Essence"
void_essence = "Void Essence"
+
+
+class ModLoot:
+ void_shard = "Void Shard"
+ green_mushroom = "Green Mushroom"
+
diff --git a/worlds/stardew_valley/strings/monster_names.py b/worlds/stardew_valley/strings/monster_names.py
new file mode 100644
index 000000000000..e995d563f059
--- /dev/null
+++ b/worlds/stardew_valley/strings/monster_names.py
@@ -0,0 +1,67 @@
+class Monster:
+ green_slime = "Green Slime"
+ blue_slime = "Frost Jelly"
+ red_slime = "Sludge" # Yeah I know this is weird that these two are the same name
+ purple_slime = "Sludge" # Blame CA
+ yellow_slime = "Yellow Slime"
+ black_slime = "Black Slime"
+ copper_slime = "Copper Slime"
+ iron_slime = "Iron Slime"
+ tiger_slime = "Tiger Slime"
+ shadow_shaman = "Shadow Shaman"
+ shadow_shaman_dangerous = "Dangerous Shadow Shaman"
+ shadow_brute = "Shadow Brute"
+ shadow_brute_dangerous = "Dangerous Shadow Brute"
+ shadow_sniper = "Shadow Sniper"
+ bat = "Bat"
+ bat_dangerous = "Dangerous Bat"
+ frost_bat = "Frost Bat"
+ frost_bat_dangerous = "Dangerous Frost Bat"
+ lava_bat = "Lava Bat"
+ iridium_bat = "Iridium Bat"
+ skeleton = "Skeleton"
+ skeleton_dangerous = "Dangerous Skeleton"
+ skeleton_mage = "Skeleton Mage"
+ bug = "Bug"
+ bug_dangerous = "Dangerous Bug"
+ cave_fly = "Fly"
+ cave_fly_dangerous = "Dangerous Cave Fly"
+ grub = "Grub"
+ grub_dangerous = "Dangerous Grub"
+ mutant_fly = "Mutant Fly"
+ mutant_grub = "Mutant Grub"
+ armored_bug = "Armored Bug"
+ armored_bug_dangerous = "Armored Bug (dangerous)"
+ duggy = "Duggy"
+ duggy_dangerous = "Dangerous Duggy"
+ magma_duggy = "Magma Duggy"
+ dust_sprite = "Dust Sprite"
+ dust_sprite_dangerous = "Dangerous Dust Sprite"
+ rock_crab = "Rock Crab"
+ rock_crab_dangerous = "Dangerous Rock Crab"
+ lava_crab = "Lava Crab"
+ lava_crab_dangerous = "Dangerous Lava Crab"
+ iridium_crab = "Iridium Crab"
+ mummy = "Mummy"
+ mummy_dangerous = "Dangerous Mummy"
+ pepper_rex = "Pepper Rex"
+ serpent = "Serpent"
+ royal_serpent = "Royal Serpent"
+ magma_sprite = "Magma Sprite"
+ magma_sparker = "Magma Sparker"
+
+
+class MonsterCategory:
+ slime = "Slimes"
+ void_spirits = "Void Spirits"
+ bats = "Bats"
+ skeletons = "Skeletons"
+ cave_insects = "Cave Insects"
+ duggies = "Duggies"
+ dust_sprites = "Dust Sprites"
+ rock_crabs = "Rock Crabs"
+ mummies = "Mummies"
+ pepper_rex = "Pepper Rex"
+ serpents = "Serpents"
+ magma_sprites = "Magma Sprites"
+
diff --git a/worlds/stardew_valley/strings/quality_names.py b/worlds/stardew_valley/strings/quality_names.py
new file mode 100644
index 000000000000..740bb5a3efc2
--- /dev/null
+++ b/worlds/stardew_valley/strings/quality_names.py
@@ -0,0 +1,63 @@
+from typing import List
+
+
+class CropQuality:
+ basic = "Basic Crop"
+ silver = "Silver Crop"
+ gold = "Gold Crop"
+ iridium = "Iridium Crop"
+
+ @staticmethod
+ def get_highest(qualities: List[str]) -> str:
+ for quality in crop_qualities_in_desc_order:
+ if quality in qualities:
+ return quality
+ return CropQuality.basic
+
+
+class FishQuality:
+ basic = "Basic Fish"
+ silver = "Silver Fish"
+ gold = "Gold Fish"
+ iridium = "Iridium Fish"
+
+ @staticmethod
+ def get_highest(qualities: List[str]) -> str:
+ for quality in fish_qualities_in_desc_order:
+ if quality in qualities:
+ return quality
+ return FishQuality.basic
+
+
+class ForageQuality:
+ basic = "Basic Forage"
+ silver = "Silver Forage"
+ gold = "Gold Forage"
+ iridium = "Iridium Forage"
+
+ @staticmethod
+ def get_highest(qualities: List[str]) -> str:
+ for quality in forage_qualities_in_desc_order:
+ if quality in qualities:
+ return quality
+ return ForageQuality.basic
+
+
+class ArtisanQuality:
+ basic = "Basic Artisan"
+ silver = "Silver Artisan"
+ gold = "Gold Artisan"
+ iridium = "Iridium Artisan"
+
+ @staticmethod
+ def get_highest(qualities: List[str]) -> str:
+ for quality in artisan_qualities_in_desc_order:
+ if quality in qualities:
+ return quality
+ return ArtisanQuality.basic
+
+
+crop_qualities_in_desc_order = [CropQuality.iridium, CropQuality.gold, CropQuality.silver, CropQuality.basic]
+fish_qualities_in_desc_order = [FishQuality.iridium, FishQuality.gold, FishQuality.silver, FishQuality.basic]
+forage_qualities_in_desc_order = [ForageQuality.iridium, ForageQuality.gold, ForageQuality.silver, ForageQuality.basic]
+artisan_qualities_in_desc_order = [ArtisanQuality.iridium, ArtisanQuality.gold, ArtisanQuality.silver, ArtisanQuality.basic]
diff --git a/worlds/stardew_valley/strings/quest_names.py b/worlds/stardew_valley/strings/quest_names.py
index 112e40a5d84d..2c02381609ec 100644
--- a/worlds/stardew_valley/strings/quest_names.py
+++ b/worlds/stardew_valley/strings/quest_names.py
@@ -6,6 +6,7 @@ class Quest:
raising_animals = "Raising Animals"
advancement = "Advancement"
archaeology = "Archaeology"
+ rat_problem = "Rat Problem"
meet_the_wizard = "Meet The Wizard"
forging_ahead = "Forging Ahead"
smelting = "Smelting"
@@ -49,9 +50,23 @@ class Quest:
goblin_problem = "Goblin Problem"
magic_ink = "Magic Ink"
+
class ModQuest:
MrGinger = "Mr.Ginger's request"
AyeishaEnvelope = "Missing Envelope"
AyeishaRing = "Lost Emerald Ring"
JunaCola = "Juna's Drink Request"
- JunaSpaghetti = "Juna's BFF Request"
\ No newline at end of file
+ JunaSpaghetti = "Juna's BFF Request"
+ RailroadBoulder = "The Railroad Boulder"
+ GrandpasShed = "Grandpa's Shed"
+ MarlonsBoat = "Marlon's Boat"
+ AuroraVineyard = "Aurora Vineyard"
+ MonsterCrops = "Monster Crops"
+ VoidSoul = "Void Soul Retrieval"
+ WizardInvite = "Wizard's Invite"
+ CorruptedCropsTask = "Corrupted Crops Task"
+ ANewPot = "A New Pot"
+ FancyBlanketTask = "Fancy Blanket Task"
+ WitchOrder = "Witch's order"
+ PumpkinSoup = "Pumpkin Soup"
+ HatMouseHat = "Hats for the Hat Mouse"
diff --git a/worlds/stardew_valley/strings/region_names.py b/worlds/stardew_valley/strings/region_names.py
index 9fa257114eb3..0fdab64fef68 100644
--- a/worlds/stardew_valley/strings/region_names.py
+++ b/worlds/stardew_valley/strings/region_names.py
@@ -4,6 +4,10 @@ class Region:
farm_house = "Farmhouse"
cellar = "Cellar"
farm = "Farm"
+ coop = "Coop"
+ barn = "Barn"
+ shed = "Shed"
+ slime_hutch = "Slime Hutch"
town = "Town"
beach = "Beach"
mountain = "Mountain"
@@ -63,12 +67,20 @@ class Region:
skull_cavern_150 = "Skull Cavern Floor 150"
skull_cavern_175 = "Skull Cavern Floor 175"
skull_cavern_200 = "Skull Cavern Floor 200"
+ dangerous_skull_cavern = "Dangerous Skull Cavern"
hospital = "Hospital"
carpenter = "Carpenter Shop"
alex_house = "Alex's House"
elliott_house = "Elliott's House"
ranch = "Marnie's Ranch"
traveling_cart = "Traveling Cart"
+ traveling_cart_sunday = "Traveling Cart Sunday"
+ traveling_cart_monday = "Traveling Cart Monday"
+ traveling_cart_tuesday = "Traveling Cart Tuesday"
+ traveling_cart_wednesday = "Traveling Cart Wednesday"
+ traveling_cart_thursday = "Traveling Cart Thursday"
+ traveling_cart_friday = "Traveling Cart Friday"
+ traveling_cart_saturday = "Traveling Cart Saturday"
farm_cave = "Farmcave"
greenhouse = "Greenhouse"
tunnel_entrance = "Tunnel Entrance"
@@ -94,6 +106,9 @@ class Region:
haley_house = "Haley's House"
sam_house = "Sam's House"
jojamart = "JojaMart"
+ abandoned_jojamart = "Abandoned JojaMart"
+ movie_theater = "Movie Theater"
+ movie_ticket_stand = "Ticket Stand"
fish_shop = "Willy's Fish Shop"
boat_tunnel = "Boat Tunnel"
tide_pools = "Tide Pools"
@@ -130,6 +145,27 @@ class Region:
mines_floor_110 = "The Mines - Floor 110"
mines_floor_115 = "The Mines - Floor 115"
mines_floor_120 = "The Mines - Floor 120"
+ dangerous_mines_20 = "Dangerous Mines - Floor 20"
+ dangerous_mines_60 = "Dangerous Mines - Floor 60"
+ dangerous_mines_100 = "Dangerous Mines - Floor 100"
+ kitchen = "Kitchen"
+ shipping = "Shipping"
+ queen_of_sauce = "The Queen of Sauce"
+ blacksmith_copper = "Blacksmith Copper Upgrades"
+ blacksmith_iron = "Blacksmith Iron Upgrades"
+ blacksmith_gold = "Blacksmith Gold Upgrades"
+ blacksmith_iridium = "Blacksmith Iridium Upgrades"
+ farming = "Farming"
+ fishing = "Fishing"
+ egg_festival = "Egg Festival"
+ flower_dance = "Flower Dance"
+ luau = "Luau"
+ moonlight_jellies = "Dance of the Moonlight Jellies"
+ fair = "Stardew Valley Fair"
+ spirit_eve = "Spirit's Eve"
+ festival_of_ice = "Festival of Ice"
+ night_market = "Night Market"
+ winter_star = "Feast of the Winter Star"
class DeepWoodsRegion:
@@ -180,3 +216,91 @@ class AyeishaRegion:
class RileyRegion:
riley_house = "Riley's House"
+
+
+class SVERegion:
+ grandpas_shed = "Grandpa's Shed"
+ grandpas_shed_front_door = "Grandpa's Shed Front Door"
+ grandpas_shed_interior = "Grandpa's Shed Interior"
+ grandpas_shed_upstairs = "Grandpa's Shed Upstairs"
+ grove_outpost_warp = "Grove Outpost Warp"
+ grove_wizard_warp = "Grove Wizard Warp"
+ grove_farm_warp = "Grove Farm Warp"
+ grove_aurora_warp = "Grove Aurora Vineyard Warp"
+ grove_guild_warp = "Grove Guild Warp"
+ grove_junimo_warp = "Grove Junimo Woods Warp"
+ grove_spring_warp = "Grove Sprite Spring Warp"
+ marnies_shed = "Marnie's Shed"
+ fairhaven_farm = "Fairhaven Farm"
+ blue_moon_vineyard = "Blue Moon Vineyard"
+ sophias_house = "Sophia's House"
+ jenkins_residence = "Jenkins' Residence"
+ jenkins_cellar = "Jenkins' Cellar"
+ unclaimed_plot = "Unclaimed Plot"
+ shearwater = "Shearwater Bridge"
+ guild_summit = "Guild Summit"
+ fable_reef = "Fable Reef"
+ first_slash_guild = "First Slash Guild"
+ highlands_outside = "Highlands Outside"
+ highlands_cavern = "Highlands Cavern"
+ dwarf_prison = "Highlands Cavern Prison"
+ lances_house = "Lance's House Main"
+ lances_ladder = "Lance's House Ladder"
+ forest_west = "Forest West"
+ aurora_vineyard = "Aurora Vineyard"
+ aurora_vineyard_basement = "Aurora Vineyard Basement"
+ bear_shop = "Bear Shop"
+ sprite_spring = "Sprite Spring"
+ lost_woods = "Lost Woods"
+ junimo_woods = "Junimo Woods"
+ purple_junimo_shop = "Purple Junimo Shop"
+ enchanted_grove = "Enchanted Grove"
+ galmoran_outpost = "Galmoran Outpost"
+ badlands_entrance = "Badlands Entrance"
+ crimson_badlands = "Crimson Badlands"
+ alesia_shop = "Alesia Shop"
+ isaac_shop = "Isaac Shop"
+ summit = "Summit"
+ susans_house = "Susan's House"
+ marlon_boat = "Marlon's Boat"
+ badlands_cave = "Badlands Cave"
+ outpost_roof = "Galmoran Outpost Roof"
+ grampleton_station = "Grampleton Station"
+ grampleton_suburbs = "Grampleton Suburbs"
+ scarlett_house = "Scarlett's House"
+ first_slash_hallway = "First Slash Hallway"
+ first_slash_spare_room = "First Slash Spare Room"
+ sprite_spring_cave = "Sprite Spring Cave"
+ willy_bedroom = "Willy's Bedroom"
+ gunther_bedroom = "Gunther's Bedroom"
+
+
+class AlectoRegion:
+ witch_attic = "Witch's Attic"
+
+
+class LaceyRegion:
+ hat_house = "Mouse House"
+
+
+class BoardingHouseRegion:
+ boarding_house_plateau = "Boarding House Outside"
+ boarding_house_first = "Boarding House - First Floor"
+ boarding_house_second = "Boarding House - Second Floor"
+ abandoned_mines_entrance = "Abandoned Mines Entrance"
+ abandoned_mines_1a = "Abandoned Mines - 1A"
+ abandoned_mines_1b = "Abandoned Mines - 1B"
+ abandoned_mines_2a = "Abandoned Mines - 2A"
+ abandoned_mines_2b = "Abandoned Mines - 2B"
+ abandoned_mines_3 = "Abandoned Mines - 3"
+ abandoned_mines_4 = "Abandoned Mines - 4"
+ abandoned_mines_5 = "Abandoned Mines - 5"
+ the_lost_valley = "The Lost Valley"
+ gregory_tent = "Gregory's Tent"
+ lost_valley_ruins = "Lost Valley Ruins"
+ lost_valley_minecart = "Lost Valley Minecart"
+ lost_valley_house_1 = "Lost Valley Ruins - First House"
+ lost_valley_house_2 = "Lost Valley Ruins - Second House"
+ buffalo_ranch = "Buffalo's Ranch"
+
+
diff --git a/worlds/stardew_valley/strings/season_names.py b/worlds/stardew_valley/strings/season_names.py
index 93c58fceb26c..f3659bc87fe0 100644
--- a/worlds/stardew_valley/strings/season_names.py
+++ b/worlds/stardew_valley/strings/season_names.py
@@ -3,4 +3,6 @@ class Season:
summer = "Summer"
fall = "Fall"
winter = "Winter"
- progressive = "Progressive Season"
\ No newline at end of file
+ progressive = "Progressive Season"
+
+ not_winter = (spring, summer, fall,)
diff --git a/worlds/stardew_valley/strings/seed_names.py b/worlds/stardew_valley/strings/seed_names.py
index 080bdf854006..398b370f2745 100644
--- a/worlds/stardew_valley/strings/seed_names.py
+++ b/worlds/stardew_valley/strings/seed_names.py
@@ -1,9 +1,37 @@
class Seed:
- sunflower = "Sunflower Seeds"
- tomato = "Tomato Seeds"
- melon = "Melon Seeds"
- wheat = "Wheat Seeds"
+ coffee = "Coffee Bean"
garlic = "Garlic Seeds"
+ jazz = "Jazz Seeds"
+ melon = "Melon Seeds"
+ mixed = "Mixed Seeds"
pineapple = "Pineapple Seeds"
+ poppy = "Poppy Seeds"
+ qi_bean = "Qi Bean"
+ spangle = "Spangle Seeds"
+ sunflower = "Sunflower Seeds"
taro = "Taro Tuber"
- coffee = "Coffee Bean"
+ tomato = "Tomato Seeds"
+ tulip = "Tulip Bulb"
+ wheat = "Wheat Seeds"
+
+
+class TreeSeed:
+ acorn = "Acorn"
+ maple = "Maple Seed"
+ pine = "Pine Cone"
+ mahogany = "Mahogany Seed"
+ mushroom = "Mushroom Tree Seed"
+
+
+class SVESeed:
+ stalk_seed = "Stalk Seed"
+ fungus_seed = "Fungus Seed"
+ slime_seed = "Slime Seed"
+ void_seed = "Void Seed"
+ shrub_seed = "Shrub Seed"
+ ancient_ferns_seed = "Ancient Ferns Seed"
+
+
+class DistantLandsSeed:
+ void_mint = "Void Mint Seeds"
+ vile_ancient_fruit = "Vile Ancient Fruit Seeds"
diff --git a/worlds/stardew_valley/strings/skill_names.py b/worlds/stardew_valley/strings/skill_names.py
index 7e7fdb798122..bae4c26fd716 100644
--- a/worlds/stardew_valley/strings/skill_names.py
+++ b/worlds/stardew_valley/strings/skill_names.py
@@ -13,3 +13,6 @@ class ModSkill:
cooking = "Cooking"
magic = "Magic"
socializing = "Socializing"
+
+
+all_mod_skills = {ModSkill.luck, ModSkill.binning, ModSkill.archaeology, ModSkill.cooking, ModSkill.magic, ModSkill.socializing}
diff --git a/worlds/stardew_valley/strings/special_order_names.py b/worlds/stardew_valley/strings/special_order_names.py
index 04eec828c0b0..9802c01532c1 100644
--- a/worlds/stardew_valley/strings/special_order_names.py
+++ b/worlds/stardew_valley/strings/special_order_names.py
@@ -13,7 +13,7 @@ class SpecialOrder:
pierres_prime_produce = "Pierre's Prime Produce"
robins_project = "Robin's Project"
robins_resource_rush = "Robin's Resource Rush"
- juicy_bugs_wanted_yum = "Juicy Bugs Wanted!"
+ juicy_bugs_wanted = "Juicy Bugs Wanted!"
tropical_fish = "Tropical Fish"
a_curious_substance = "A Curious Substance"
prismatic_jelly = "Prismatic Jelly"
@@ -31,3 +31,10 @@ class SpecialOrder:
class ModSpecialOrder:
junas_monster_mash = "Juna's Monster Mash"
+ andys_cellar = "Andy's Cellar"
+ a_mysterious_venture = "A Mysterious Venture"
+ an_elegant_reception = "An Elegant Reception"
+ fairy_garden = "Fairy Garden"
+ homemade_fertilizer = "Homemade Fertilizer"
+ geode_order = "Geode Order"
+ dwarf_scroll = "Dwarven Scrolls"
diff --git a/worlds/stardew_valley/strings/spells.py b/worlds/stardew_valley/strings/spells.py
index fd2a515db9ff..ef5545c56902 100644
--- a/worlds/stardew_valley/strings/spells.py
+++ b/worlds/stardew_valley/strings/spells.py
@@ -9,7 +9,7 @@ class MagicSpell:
buff = "Spell: Buff"
shockwave = "Spell: Shockwave"
fireball = "Spell: Fireball"
- frostbite = "Spell: Frostbite"
+ frostbite = "Spell: Frostbolt"
teleport = "Spell: Teleport"
lantern = "Spell: Lantern"
tendrils = "Spell: Tendrils"
diff --git a/worlds/stardew_valley/strings/villager_names.py b/worlds/stardew_valley/strings/villager_names.py
index 5bf13ea8dd7e..7e87be64f1ea 100644
--- a/worlds/stardew_valley/strings/villager_names.py
+++ b/worlds/stardew_valley/strings/villager_names.py
@@ -46,4 +46,24 @@ class ModNPC:
riley = "Riley"
shiko = "Shiko"
wellwick = "Wellwick"
- yoba = "Yoba"
\ No newline at end of file
+ yoba = "Yoba"
+ lance = "Lance"
+ apples = "Apples"
+ claire = "Claire"
+ olivia = "Olivia"
+ sophia = "Sophia"
+ victor = "Victor"
+ andy = "Andy"
+ gunther = "Gunther"
+ martin = "Martin"
+ marlon = "Marlon"
+ morgan = "Morgan"
+ morris = "Morris"
+ scarlett = "Scarlett"
+ susan = "Susan"
+ alecto = "Alecto"
+ goblin = "Zic"
+ lacey = "Lacey"
+ gregory = "Gregory"
+ sheila = "Sheila"
+ joel = "Joel"
diff --git a/worlds/stardew_valley/strings/wallet_item_names.py b/worlds/stardew_valley/strings/wallet_item_names.py
index 31026ebbaeae..28f09b0558fc 100644
--- a/worlds/stardew_valley/strings/wallet_item_names.py
+++ b/worlds/stardew_valley/strings/wallet_item_names.py
@@ -1,5 +1,10 @@
class Wallet:
+ metal_detector = "Traveling Merchant Metal Detector"
+ iridium_snake_milk = "Iridium Snake Milk"
+ bears_knowledge = "Bear's Knowledge"
+ dwarvish_translation_guide = "Dwarvish Translation Guide"
magnifying_glass = "Magnifying Glass"
rusty_key = "Rusty Key"
skull_key = "Skull Key"
dark_talisman = "Dark Talisman"
+ club_card = "Club Card"
diff --git a/worlds/stardew_valley/strings/weapon_names.py b/worlds/stardew_valley/strings/weapon_names.py
index 009cd6df0d6f..1c3e508cfa99 100644
--- a/worlds/stardew_valley/strings/weapon_names.py
+++ b/worlds/stardew_valley/strings/weapon_names.py
@@ -1,4 +1,3 @@
class Weapon:
slingshot = "Slingshot"
master_slingshot = "Master Slingshot"
- any_slingshot = "Any Slingshot"
diff --git a/worlds/stardew_valley/test/TestBundles.py b/worlds/stardew_valley/test/TestBundles.py
index a13829eb67ea..cd6828cd79e5 100644
--- a/worlds/stardew_valley/test/TestBundles.py
+++ b/worlds/stardew_valley/test/TestBundles.py
@@ -1,30 +1,29 @@
import unittest
-from ..data.bundle_data import all_bundle_items, quality_crops_items
+from ..data.bundle_data import all_bundle_items_except_money, quality_crops_items_thematic
+from ..strings.crop_names import Fruit
+from ..strings.quality_names import CropQuality
class TestBundles(unittest.TestCase):
def test_all_bundle_items_have_3_parts(self):
- for bundle_item in all_bundle_items:
- with self.subTest(bundle_item.item.name):
- self.assertGreater(len(bundle_item.item.name), 0)
- id = bundle_item.item.item_id
- self.assertGreaterEqual(id, -1)
- self.assertNotEqual(id, 0)
+ for bundle_item in all_bundle_items_except_money:
+ with self.subTest(bundle_item.item_name):
+ self.assertGreater(len(bundle_item.item_name), 0)
self.assertGreater(bundle_item.amount, 0)
- self.assertGreaterEqual(bundle_item.quality, 0)
+ self.assertTrue(bundle_item.quality)
def test_quality_crops_have_correct_amounts(self):
- for bundle_item in quality_crops_items:
- with self.subTest(bundle_item.item.name):
- name = bundle_item.item.name
- if name == "Sweet Gem Berry" or name == "Ancient Fruit":
+ for bundle_item in quality_crops_items_thematic:
+ with self.subTest(bundle_item.item_name):
+ name = bundle_item.item_name
+ if name == Fruit.sweet_gem_berry or name == Fruit.ancient_fruit:
self.assertEqual(bundle_item.amount, 1)
else:
self.assertEqual(bundle_item.amount, 5)
def test_quality_crops_have_correct_quality(self):
- for bundle_item in quality_crops_items:
- with self.subTest(bundle_item.item.name):
- self.assertEqual(bundle_item.quality, 2)
+ for bundle_item in quality_crops_items_thematic:
+ with self.subTest(bundle_item.item_name):
+ self.assertEqual(bundle_item.quality, CropQuality.gold)
diff --git a/worlds/stardew_valley/test/TestCrops.py b/worlds/stardew_valley/test/TestCrops.py
new file mode 100644
index 000000000000..38b736367b80
--- /dev/null
+++ b/worlds/stardew_valley/test/TestCrops.py
@@ -0,0 +1,20 @@
+from . import SVTestBase
+from .. import options
+
+
+class TestCropsanityRules(SVTestBase):
+ options = {
+ options.Cropsanity.internal_name: options.Cropsanity.option_enabled
+ }
+
+ def test_need_greenhouse_for_cactus(self):
+ harvest_cactus = self.world.logic.region.can_reach_location("Harvest Cactus Fruit")
+ self.assert_rule_false(harvest_cactus, self.multiworld.state)
+
+ self.multiworld.state.collect(self.world.create_item("Cactus Seeds"), event=False)
+ self.multiworld.state.collect(self.world.create_item("Shipping Bin"), event=False)
+ self.multiworld.state.collect(self.world.create_item("Desert Obelisk"), event=False)
+ self.assert_rule_false(harvest_cactus, self.multiworld.state)
+
+ self.multiworld.state.collect(self.world.create_item("Greenhouse"), event=False)
+ self.assert_rule_true(harvest_cactus, self.multiworld.state)
diff --git a/worlds/stardew_valley/test/TestDynamicGoals.py b/worlds/stardew_valley/test/TestDynamicGoals.py
new file mode 100644
index 000000000000..fe1bfb5f3044
--- /dev/null
+++ b/worlds/stardew_valley/test/TestDynamicGoals.py
@@ -0,0 +1,108 @@
+from typing import List, Tuple
+
+from . import SVTestBase
+from .assertion import WorldAssertMixin
+from .. import options, StardewItem
+from ..strings.ap_names.ap_weapon_names import APWeapon
+from ..strings.ap_names.transport_names import Transportation
+from ..strings.fish_names import Fish
+from ..strings.tool_names import APTool
+from ..strings.wallet_item_names import Wallet
+
+
+def collect_fishing_abilities(tester: SVTestBase):
+ for i in range(4):
+ tester.multiworld.state.collect(tester.world.create_item(APTool.fishing_rod), event=False)
+ tester.multiworld.state.collect(tester.world.create_item(APTool.pickaxe), event=False)
+ tester.multiworld.state.collect(tester.world.create_item(APTool.axe), event=False)
+ tester.multiworld.state.collect(tester.world.create_item(APWeapon.weapon), event=False)
+ for i in range(10):
+ tester.multiworld.state.collect(tester.world.create_item("Fishing Level"), event=False)
+ tester.multiworld.state.collect(tester.world.create_item("Combat Level"), event=False)
+ tester.multiworld.state.collect(tester.world.create_item("Mining Level"), event=False)
+ for i in range(17):
+ tester.multiworld.state.collect(tester.world.create_item("Progressive Mine Elevator"), event=False)
+ tester.multiworld.state.collect(tester.world.create_item("Spring"), event=False)
+ tester.multiworld.state.collect(tester.world.create_item("Summer"), event=False)
+ tester.multiworld.state.collect(tester.world.create_item("Fall"), event=False)
+ tester.multiworld.state.collect(tester.world.create_item("Winter"), event=False)
+ tester.multiworld.state.collect(tester.world.create_item(Transportation.desert_obelisk), event=False)
+ tester.multiworld.state.collect(tester.world.create_item("Railroad Boulder Removed"), event=False)
+ tester.multiworld.state.collect(tester.world.create_item("Island North Turtle"), event=False)
+ tester.multiworld.state.collect(tester.world.create_item("Island West Turtle"), event=False)
+
+
+def create_and_collect(tester: SVTestBase, item_name: str) -> StardewItem:
+ item = tester.world.create_item(item_name)
+ tester.multiworld.state.collect(item, event=False)
+ return item
+
+
+def create_and_collect_fishing_access_items(tester: SVTestBase) -> List[Tuple[StardewItem, str]]:
+ items = [(create_and_collect(tester, Wallet.dark_talisman), Fish.void_salmon),
+ (create_and_collect(tester, Wallet.rusty_key), Fish.slimejack),
+ (create_and_collect(tester, "Progressive Mine Elevator"), Fish.lava_eel),
+ (create_and_collect(tester, Transportation.island_obelisk), Fish.lionfish),
+ (create_and_collect(tester, "Island Resort"), Fish.stingray)]
+ return items
+
+
+class TestMasterAnglerNoFishsanity(WorldAssertMixin, SVTestBase):
+ options = {
+ options.Goal.internal_name: options.Goal.option_master_angler,
+ options.Fishsanity.internal_name: options.Fishsanity.option_none,
+ options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false
+ }
+
+ def test_need_all_fish_to_win(self):
+ collect_fishing_abilities(self)
+ self.assert_cannot_reach_victory(self.multiworld)
+ critical_items = create_and_collect_fishing_access_items(self)
+ self.assert_can_reach_victory(self.multiworld)
+ for item, fish in critical_items:
+ with self.subTest(f"Needed: {fish}"):
+ self.assert_item_was_necessary_for_victory(item, self.multiworld)
+
+
+class TestMasterAnglerNoFishsanityNoGingerIsland(WorldAssertMixin, SVTestBase):
+ options = {
+ options.Goal.internal_name: options.Goal.option_master_angler,
+ options.Fishsanity.internal_name: options.Fishsanity.option_none,
+ options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true
+ }
+
+ def test_need_fish_to_win(self):
+ collect_fishing_abilities(self)
+ self.assert_cannot_reach_victory(self.multiworld)
+ items = create_and_collect_fishing_access_items(self)
+ self.assert_can_reach_victory(self.multiworld)
+ unecessary_items = [(item, fish) for (item, fish) in items if fish in [Fish.lionfish, Fish.stingray]]
+ necessary_items = [(item, fish) for (item, fish) in items if (item, fish) not in unecessary_items]
+ for item, fish in necessary_items:
+ with self.subTest(f"Needed: {fish}"):
+ self.assert_item_was_necessary_for_victory(item, self.multiworld)
+ for item, fish in unecessary_items:
+ with self.subTest(f"Not Needed: {fish}"):
+ self.assert_item_was_not_necessary_for_victory(item, self.multiworld)
+
+
+class TestMasterAnglerFishsanityNoHardFish(WorldAssertMixin, SVTestBase):
+ options = {
+ options.Goal.internal_name: options.Goal.option_master_angler,
+ options.Fishsanity.internal_name: options.Fishsanity.option_exclude_hard_fish,
+ options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false
+ }
+
+ def test_need_fish_to_win(self):
+ collect_fishing_abilities(self)
+ self.assert_cannot_reach_victory(self.multiworld)
+ items = create_and_collect_fishing_access_items(self)
+ self.assert_can_reach_victory(self.multiworld)
+ unecessary_items = [(item, fish) for (item, fish) in items if fish in [Fish.void_salmon, Fish.stingray, Fish.lava_eel]]
+ necessary_items = [(item, fish) for (item, fish) in items if (item, fish) not in unecessary_items]
+ for item, fish in necessary_items:
+ with self.subTest(f"Needed: {fish}"):
+ self.assert_item_was_necessary_for_victory(item, self.multiworld)
+ for item, fish in unecessary_items:
+ with self.subTest(f"Not Needed: {fish}"):
+ self.assert_item_was_not_necessary_for_victory(item, self.multiworld)
diff --git a/worlds/stardew_valley/test/TestGeneration.py b/worlds/stardew_valley/test/TestGeneration.py
index 46c6685ad536..55ad4f07544b 100644
--- a/worlds/stardew_valley/test/TestGeneration.py
+++ b/worlds/stardew_valley/test/TestGeneration.py
@@ -1,28 +1,26 @@
-import typing
+from typing import List
-from BaseClasses import ItemClassification, MultiWorld
-from . import setup_solo_multiworld, SVTestBase, SVTestCase, allsanity_options_with_mods, \
- allsanity_options_without_mods, minimal_locations_maximal_items
-from .. import locations, items, location_table, options
+from BaseClasses import ItemClassification, Item
+from . import SVTestBase, allsanity_options_without_mods, \
+ allsanity_options_with_mods, minimal_locations_maximal_items, minimal_locations_maximal_items_with_island, get_minsanity_options, default_options
+from .. import items, location_table, options
from ..data.villagers_data import all_villagers_by_name, all_villagers_by_mod_by_name
-from ..items import items_by_group, Group
+from ..items import Group, item_table
from ..locations import LocationTags
from ..mods.mod_data import ModNames
-
-
-def get_real_locations(tester: typing.Union[SVTestBase, SVTestCase], multiworld: MultiWorld):
- return [location for location in multiworld.get_locations(tester.player) if not location.event]
-
-
-def get_real_location_names(tester: typing.Union[SVTestBase, SVTestCase], multiworld: MultiWorld):
- return [location.name for location in multiworld.get_locations(tester.player) if not location.event]
+from ..options import Friendsanity, SpecialOrderLocations, Shipsanity, Chefsanity, SeasonRandomization, Craftsanity, ExcludeGingerIsland, ToolProgression, \
+ FriendsanityHeartSize
+from ..strings.region_names import Region
class TestBaseItemGeneration(SVTestBase):
options = {
- options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
- options.SeasonRandomization.internal_name: options.SeasonRandomization.option_progressive,
- options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi,
+ Friendsanity.internal_name: Friendsanity.option_all_with_marriage,
+ SeasonRandomization.internal_name: SeasonRandomization.option_progressive,
+ SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi,
+ Shipsanity.internal_name: Shipsanity.option_everything,
+ Chefsanity.internal_name: Chefsanity.option_all,
+ Craftsanity.internal_name: Craftsanity.option_all,
}
def test_all_progression_items_are_added_to_the_pool(self):
@@ -30,21 +28,19 @@ def test_all_progression_items_are_added_to_the_pool(self):
# Ignore all the stuff that the algorithm chooses one of, instead of all, to fulfill logical progression
items_to_ignore = [event.name for event in items.events]
items_to_ignore.extend(item.name for item in items.all_items if item.mod_name is not None)
+ items_to_ignore.extend(deprecated.name for deprecated in items.items_by_group[Group.DEPRECATED])
items_to_ignore.extend(season.name for season in items.items_by_group[Group.SEASON])
items_to_ignore.extend(weapon.name for weapon in items.items_by_group[Group.WEAPON])
- items_to_ignore.extend(footwear.name for footwear in items.items_by_group[Group.FOOTWEAR])
items_to_ignore.extend(baby.name for baby in items.items_by_group[Group.BABY])
items_to_ignore.extend(resource_pack.name for resource_pack in items.items_by_group[Group.RESOURCE_PACK])
- progression_items = [item for item in items.all_items if item.classification is ItemClassification.progression
- and item.name not in items_to_ignore]
+ items_to_ignore.append("The Gateway Gazette")
+ progression_items = [item for item in items.all_items if item.classification is ItemClassification.progression and item.name not in items_to_ignore]
for progression_item in progression_items:
with self.subTest(f"{progression_item.name}"):
self.assertIn(progression_item.name, all_created_items)
def test_creates_as_many_item_as_non_event_locations(self):
- non_event_locations = [location for location in get_real_locations(self, self.multiworld) if
- not location.event]
-
+ non_event_locations = self.get_real_locations()
self.assertEqual(len(non_event_locations), len(self.multiworld.itempool))
def test_does_not_create_deprecated_items(self):
@@ -69,9 +65,12 @@ def test_does_not_create_exactly_two_items(self):
class TestNoGingerIslandItemGeneration(SVTestBase):
options = {
- options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
- options.SeasonRandomization.internal_name: options.SeasonRandomization.option_progressive,
- options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true
+ Friendsanity.internal_name: Friendsanity.option_all_with_marriage,
+ SeasonRandomization.internal_name: SeasonRandomization.option_progressive,
+ ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true,
+ Shipsanity.internal_name: Shipsanity.option_everything,
+ Chefsanity.internal_name: Chefsanity.option_all,
+ Craftsanity.internal_name: Craftsanity.option_all,
}
def test_all_progression_items_except_island_are_added_to_the_pool(self):
@@ -79,12 +78,12 @@ def test_all_progression_items_except_island_are_added_to_the_pool(self):
# Ignore all the stuff that the algorithm chooses one of, instead of all, to fulfill logical progression
items_to_ignore = [event.name for event in items.events]
items_to_ignore.extend(item.name for item in items.all_items if item.mod_name is not None)
+ items_to_ignore.extend(deprecated.name for deprecated in items.items_by_group[Group.DEPRECATED])
items_to_ignore.extend(season.name for season in items.items_by_group[Group.SEASON])
items_to_ignore.extend(season.name for season in items.items_by_group[Group.WEAPON])
- items_to_ignore.extend(season.name for season in items.items_by_group[Group.FOOTWEAR])
items_to_ignore.extend(baby.name for baby in items.items_by_group[Group.BABY])
- progression_items = [item for item in items.all_items if item.classification is ItemClassification.progression
- and item.name not in items_to_ignore]
+ items_to_ignore.append("The Gateway Gazette")
+ progression_items = [item for item in items.all_items if item.classification is ItemClassification.progression and item.name not in items_to_ignore]
for progression_item in progression_items:
with self.subTest(f"{progression_item.name}"):
if Group.GINGER_ISLAND in progression_item.groups:
@@ -93,8 +92,7 @@ def test_all_progression_items_except_island_are_added_to_the_pool(self):
self.assertIn(progression_item.name, all_created_items)
def test_creates_as_many_item_as_non_event_locations(self):
- non_event_locations = [location for location in get_real_locations(self, self.multiworld) if
- not location.event]
+ non_event_locations = self.get_real_locations()
self.assertEqual(len(non_event_locations), len(self.multiworld.itempool))
@@ -118,34 +116,154 @@ def test_does_not_create_exactly_two_items(self):
self.assertTrue(count == 0 or count == 2)
-class TestRemixedMineRewards(SVTestBase):
- def test_when_generate_world_then_one_reward_is_added_per_chest(self):
- # assert self.world.create_item("Rusty Sword") in self.multiworld.itempool
- self.assertTrue(any(self.world.create_item(item) in self.multiworld.itempool
- for item in items_by_group[Group.MINES_FLOOR_10]))
- self.assertTrue(any(self.world.create_item(item) in self.multiworld.itempool
- for item in items_by_group[Group.MINES_FLOOR_20]))
- self.assertIn(self.world.create_item("Slingshot"), self.multiworld.itempool)
- self.assertTrue(any(self.world.create_item(item) in self.multiworld.itempool
- for item in items_by_group[Group.MINES_FLOOR_50]))
- self.assertTrue(any(self.world.create_item(item) in self.multiworld.itempool
- for item in items_by_group[Group.MINES_FLOOR_60]))
- self.assertIn(self.world.create_item("Master Slingshot"), self.multiworld.itempool)
- self.assertTrue(any(self.world.create_item(item) in self.multiworld.itempool
- for item in items_by_group[Group.MINES_FLOOR_80]))
- self.assertTrue(any(self.world.create_item(item) in self.multiworld.itempool
- for item in items_by_group[Group.MINES_FLOOR_90]))
- self.assertIn(self.world.create_item("Stardrop"), self.multiworld.itempool)
- self.assertTrue(any(self.world.create_item(item) in self.multiworld.itempool
- for item in items_by_group[Group.MINES_FLOOR_110]))
- self.assertIn(self.world.create_item("Skull Key"), self.multiworld.itempool)
-
- # This test has a 1/90,000 chance to fail... Sorry in advance
- def test_when_generate_world_then_rewards_are_not_all_vanilla(self):
- self.assertFalse(all(self.world.create_item(item) in self.multiworld.itempool
- for item in
- ["Leather Boots", "Steel Smallsword", "Tundra Boots", "Crystal Dagger", "Firewalker Boots",
- "Obsidian Edge", "Space Boots"]))
+class TestMonstersanityNone(SVTestBase):
+ options = {options.Monstersanity.internal_name: options.Monstersanity.option_none}
+
+ def test_when_generate_world_then_5_generic_weapons_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Weapon"), 5)
+
+ def test_when_generate_world_then_zero_specific_weapons_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Sword"), 0)
+ self.assertEqual(item_pool.count("Progressive Club"), 0)
+ self.assertEqual(item_pool.count("Progressive Dagger"), 0)
+
+ def test_when_generate_world_then_2_slingshots_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Slingshot"), 2)
+
+ def test_when_generate_world_then_3_shoes_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Footwear"), 3)
+
+
+class TestMonstersanityGoals(SVTestBase):
+ options = {options.Monstersanity.internal_name: options.Monstersanity.option_goals}
+
+ def test_when_generate_world_then_no_generic_weapons_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Weapon"), 0)
+
+ def test_when_generate_world_then_5_specific_weapons_of_each_type_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Sword"), 5)
+ self.assertEqual(item_pool.count("Progressive Club"), 5)
+ self.assertEqual(item_pool.count("Progressive Dagger"), 5)
+
+ def test_when_generate_world_then_2_slingshots_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Slingshot"), 2)
+
+ def test_when_generate_world_then_4_shoes_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Footwear"), 4)
+
+ def test_when_generate_world_then_all_monster_checks_are_inaccessible(self):
+ for location in self.get_real_locations():
+ if LocationTags.MONSTERSANITY not in location_table[location.name].tags:
+ continue
+ with self.subTest(location.name):
+ self.assertFalse(location.can_reach(self.multiworld.state))
+
+
+class TestMonstersanityOnePerCategory(SVTestBase):
+ options = {options.Monstersanity.internal_name: options.Monstersanity.option_one_per_category}
+
+ def test_when_generate_world_then_no_generic_weapons_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Weapon"), 0)
+
+ def test_when_generate_world_then_5_specific_weapons_of_each_type_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Sword"), 5)
+ self.assertEqual(item_pool.count("Progressive Club"), 5)
+ self.assertEqual(item_pool.count("Progressive Dagger"), 5)
+
+ def test_when_generate_world_then_2_slingshots_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Slingshot"), 2)
+
+ def test_when_generate_world_then_4_shoes_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Footwear"), 4)
+
+ def test_when_generate_world_then_all_monster_checks_are_inaccessible(self):
+ for location in self.get_real_locations():
+ if LocationTags.MONSTERSANITY not in location_table[location.name].tags:
+ continue
+ with self.subTest(location.name):
+ self.assertFalse(location.can_reach(self.multiworld.state))
+
+
+class TestMonstersanityProgressive(SVTestBase):
+ options = {options.Monstersanity.internal_name: options.Monstersanity.option_progressive_goals}
+
+ def test_when_generate_world_then_no_generic_weapons_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Weapon"), 0)
+
+ def test_when_generate_world_then_5_specific_weapons_of_each_type_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Sword"), 5)
+ self.assertEqual(item_pool.count("Progressive Club"), 5)
+ self.assertEqual(item_pool.count("Progressive Dagger"), 5)
+
+ def test_when_generate_world_then_2_slingshots_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Slingshot"), 2)
+
+ def test_when_generate_world_then_4_shoes_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Footwear"), 4)
+
+ def test_when_generate_world_then_many_rings_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertIn("Hot Java Ring", item_pool)
+ self.assertIn("Wedding Ring", item_pool)
+ self.assertIn("Slime Charmer Ring", item_pool)
+
+ def test_when_generate_world_then_all_monster_checks_are_inaccessible(self):
+ for location in self.get_real_locations():
+ if LocationTags.MONSTERSANITY not in location_table[location.name].tags:
+ continue
+ with self.subTest(location.name):
+ self.assertFalse(location.can_reach(self.multiworld.state))
+
+
+class TestMonstersanitySplit(SVTestBase):
+ options = {options.Monstersanity.internal_name: options.Monstersanity.option_split_goals}
+
+ def test_when_generate_world_then_no_generic_weapons_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Weapon"), 0)
+
+ def test_when_generate_world_then_5_specific_weapons_of_each_type_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Sword"), 5)
+ self.assertEqual(item_pool.count("Progressive Club"), 5)
+ self.assertEqual(item_pool.count("Progressive Dagger"), 5)
+
+ def test_when_generate_world_then_2_slingshots_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Slingshot"), 2)
+
+ def test_when_generate_world_then_4_shoes_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(item_pool.count("Progressive Footwear"), 4)
+
+ def test_when_generate_world_then_many_rings_in_the_pool(self):
+ item_pool = [item.name for item in self.multiworld.itempool]
+ self.assertIn("Hot Java Ring", item_pool)
+ self.assertIn("Wedding Ring", item_pool)
+ self.assertIn("Slime Charmer Ring", item_pool)
+
+ def test_when_generate_world_then_all_monster_checks_are_inaccessible(self):
+ for location in self.get_real_locations():
+ if LocationTags.MONSTERSANITY not in location_table[location.name].tags:
+ continue
+ with self.subTest(location.name):
+ self.assertFalse(location.can_reach(self.multiworld.state))
class TestProgressiveElevator(SVTestBase):
@@ -155,57 +273,173 @@ class TestProgressiveElevator(SVTestBase):
options.SkillProgression.internal_name: options.SkillProgression.option_progressive,
}
- def test_given_access_to_floor_115_when_find_another_elevator_then_has_access_to_floor_120(self):
- self.collect([self.get_item_by_name("Progressive Pickaxe")] * 2)
- self.collect([self.get_item_by_name("Progressive Mine Elevator")] * 22)
- self.collect(self.multiworld.create_item("Bone Sword", self.player))
- self.collect([self.get_item_by_name("Combat Level")] * 4)
- self.collect(self.get_item_by_name("Adventurer's Guild"))
+ def test_given_elevator_to_floor_105_when_find_another_elevator_then_has_access_to_floor_120(self):
+ items_for_115 = self.generate_items_for_mine_115()
+ last_elevator = self.get_item_by_name("Progressive Mine Elevator")
+ self.collect(items_for_115)
+ floor_115 = self.multiworld.get_region("The Mines - Floor 115", self.player)
+ floor_120 = self.multiworld.get_region("The Mines - Floor 120", self.player)
+
+ self.assertTrue(floor_115.can_reach(self.multiworld.state))
+ self.assertFalse(floor_120.can_reach(self.multiworld.state))
- self.assertFalse(self.multiworld.get_region("The Mines - Floor 120", self.player).can_reach(self.multiworld.state))
+ self.collect(last_elevator)
- self.collect(self.get_item_by_name("Progressive Mine Elevator"))
+ self.assertTrue(floor_120.can_reach(self.multiworld.state))
- self.assertTrue(self.multiworld.get_region("The Mines - Floor 120", self.player).can_reach(self.multiworld.state))
+ def generate_items_for_mine_115(self) -> List[Item]:
+ pickaxes = [self.get_item_by_name("Progressive Pickaxe")] * 2
+ elevators = [self.get_item_by_name("Progressive Mine Elevator")] * 21
+ swords = [self.get_item_by_name("Progressive Sword")] * 3
+ combat_levels = [self.get_item_by_name("Combat Level")] * 4
+ mining_levels = [self.get_item_by_name("Mining Level")] * 4
+ return [*combat_levels, *mining_levels, *elevators, *pickaxes, *swords]
- def test_given_access_to_floor_115_when_find_another_pickaxe_and_sword_then_has_access_to_floor_120(self):
- self.collect([self.get_item_by_name("Progressive Pickaxe")] * 2)
- self.collect([self.get_item_by_name("Progressive Mine Elevator")] * 22)
- self.collect(self.multiworld.create_item("Bone Sword", self.player))
- self.collect([self.get_item_by_name("Combat Level")] * 4)
- self.collect(self.get_item_by_name("Adventurer's Guild"))
+ def generate_items_for_extra_mine_levels(self, weapon_name: str) -> List[Item]:
+ last_pickaxe = self.get_item_by_name("Progressive Pickaxe")
+ last_weapon = self.multiworld.create_item(weapon_name, self.player)
+ second_last_combat_level = self.get_item_by_name("Combat Level")
+ last_combat_level = self.get_item_by_name("Combat Level")
+ second_last_mining_level = self.get_item_by_name("Mining Level")
+ last_mining_level = self.get_item_by_name("Mining Level")
+ return [last_pickaxe, last_weapon, second_last_combat_level, last_combat_level, second_last_mining_level, last_mining_level]
- self.assertFalse(self.multiworld.get_region("The Mines - Floor 120", self.player).can_reach(self.multiworld.state))
- self.collect(self.get_item_by_name("Progressive Pickaxe"))
- self.collect(self.multiworld.create_item("Steel Falchion", self.player))
- self.collect(self.get_item_by_name("Combat Level"))
- self.collect(self.get_item_by_name("Combat Level"))
+class TestSkullCavernLogic(SVTestBase):
+ options = {
+ options.ElevatorProgression.internal_name: options.ElevatorProgression.option_vanilla,
+ ToolProgression.internal_name: ToolProgression.option_progressive,
+ options.SkillProgression.internal_name: options.SkillProgression.option_progressive,
+ }
- self.assertTrue(self.multiworld.get_region("The Mines - Floor 120", self.player).can_reach(self.multiworld.state))
+ def test_given_access_to_floor_115_when_find_more_tools_then_has_access_to_skull_cavern_25(self):
+ items_for_115 = self.generate_items_for_mine_115()
+ items_for_skull_50 = self.generate_items_for_skull_50()
+ items_for_skull_100 = self.generate_items_for_skull_100()
+ self.collect(items_for_115)
+ floor_115 = self.multiworld.get_region(Region.mines_floor_115, self.player)
+ skull_25 = self.multiworld.get_region(Region.skull_cavern_25, self.player)
+ skull_75 = self.multiworld.get_region(Region.skull_cavern_75, self.player)
+
+ self.assertTrue(floor_115.can_reach(self.multiworld.state))
+ self.assertFalse(skull_25.can_reach(self.multiworld.state))
+ self.assertFalse(skull_75.can_reach(self.multiworld.state))
+
+ self.remove(items_for_115)
+ self.collect(items_for_skull_50)
+
+ self.assertTrue(floor_115.can_reach(self.multiworld.state))
+ self.assertTrue(skull_25.can_reach(self.multiworld.state))
+ self.assertFalse(skull_75.can_reach(self.multiworld.state))
+
+ self.remove(items_for_skull_50)
+ self.collect(items_for_skull_100)
+
+ self.assertTrue(floor_115.can_reach(self.multiworld.state))
+ self.assertTrue(skull_25.can_reach(self.multiworld.state))
+ self.assertTrue(skull_75.can_reach(self.multiworld.state))
+
+ def generate_items_for_mine_115(self) -> List[Item]:
+ pickaxes = [self.get_item_by_name("Progressive Pickaxe")] * 2
+ swords = [self.get_item_by_name("Progressive Sword")] * 3
+ combat_levels = [self.get_item_by_name("Combat Level")] * 4
+ mining_levels = [self.get_item_by_name("Mining Level")] * 4
+ bus = self.get_item_by_name("Bus Repair")
+ skull_key = self.get_item_by_name("Skull Key")
+ return [*combat_levels, *mining_levels, *pickaxes, *swords, bus, skull_key]
+
+ def generate_items_for_skull_50(self) -> List[Item]:
+ pickaxes = [self.get_item_by_name("Progressive Pickaxe")] * 3
+ swords = [self.get_item_by_name("Progressive Sword")] * 4
+ combat_levels = [self.get_item_by_name("Combat Level")] * 6
+ mining_levels = [self.get_item_by_name("Mining Level")] * 6
+ bus = self.get_item_by_name("Bus Repair")
+ skull_key = self.get_item_by_name("Skull Key")
+ return [*combat_levels, *mining_levels, *pickaxes, *swords, bus, skull_key]
+
+ def generate_items_for_skull_100(self) -> List[Item]:
+ pickaxes = [self.get_item_by_name("Progressive Pickaxe")] * 4
+ swords = [self.get_item_by_name("Progressive Sword")] * 5
+ combat_levels = [self.get_item_by_name("Combat Level")] * 8
+ mining_levels = [self.get_item_by_name("Mining Level")] * 8
+ bus = self.get_item_by_name("Bus Repair")
+ skull_key = self.get_item_by_name("Skull Key")
+ return [*combat_levels, *mining_levels, *pickaxes, *swords, bus, skull_key]
class TestLocationGeneration(SVTestBase):
def test_all_location_created_are_in_location_table(self):
- for location in get_real_locations(self, self.multiworld):
+ for location in self.get_real_locations():
if not location.event:
self.assertIn(location.name, location_table)
-class TestLocationAndItemCount(SVTestCase):
+class TestMinLocationAndMaxItem(SVTestBase):
+ options = minimal_locations_maximal_items()
+
+ # They do not pass and I don't know why.
+ skip_base_tests = True
def test_minimal_location_maximal_items_still_valid(self):
- min_max_options = minimal_locations_maximal_items()
- multiworld = setup_solo_multiworld(min_max_options)
- valid_locations = get_real_locations(self, multiworld)
- self.assertGreaterEqual(len(valid_locations), len(multiworld.itempool))
+ valid_locations = self.get_real_locations()
+ number_locations = len(valid_locations)
+ number_items = len([item for item in self.multiworld.itempool
+ if Group.RESOURCE_PACK not in item_table[item.name].groups and Group.TRAP not in item_table[item.name].groups])
+ self.assertGreaterEqual(number_locations, number_items)
+ print(f"Stardew Valley - Minimum Locations: {number_locations}, Maximum Items: {number_items} [ISLAND EXCLUDED]")
+
+
+class TestMinLocationAndMaxItemWithIsland(SVTestBase):
+ options = minimal_locations_maximal_items_with_island()
+
+ def test_minimal_location_maximal_items_with_island_still_valid(self):
+ valid_locations = self.get_real_locations()
+ number_locations = len(valid_locations)
+ number_items = len([item for item in self.multiworld.itempool
+ if Group.RESOURCE_PACK not in item_table[item.name].groups and Group.TRAP not in item_table[item.name].groups])
+ self.assertGreaterEqual(number_locations, number_items)
+ print(f"Stardew Valley - Minimum Locations: {number_locations}, Maximum Items: {number_items} [ISLAND INCLUDED]")
+
+
+class TestMinSanityHasAllExpectedLocations(SVTestBase):
+ options = get_minsanity_options()
+
+ def test_minsanity_has_fewer_than_locations(self):
+ expected_locations = 76
+ real_locations = self.get_real_locations()
+ number_locations = len(real_locations)
+ self.assertLessEqual(number_locations, expected_locations)
+ print(f"Stardew Valley - Minsanity Locations: {number_locations}")
+ if number_locations != expected_locations:
+ print(f"\tDisappeared Locations Detected!"
+ f"\n\tPlease update test_minsanity_has_fewer_than_locations"
+ f"\n\t\tExpected: {expected_locations}"
+ f"\n\t\tActual: {number_locations}")
+
+
+class TestDefaultSettingsHasAllExpectedLocations(SVTestBase):
+ options = default_options()
+
+ def test_default_settings_has_exactly_locations(self):
+ expected_locations = 422
+ real_locations = self.get_real_locations()
+ number_locations = len(real_locations)
+ print(f"Stardew Valley - Default options locations: {number_locations}")
+ if number_locations != expected_locations:
+ print(f"\tNew locations detected!"
+ f"\n\tPlease update test_default_settings_has_exactly_locations"
+ f"\n\t\tExpected: {expected_locations}"
+ f"\n\t\tActual: {number_locations}")
+
+
+class TestAllSanitySettingsHasAllExpectedLocations(SVTestBase):
+ options = allsanity_options_without_mods()
def test_allsanity_without_mods_has_at_least_locations(self):
- expected_locations = 994
- allsanity_options = allsanity_options_without_mods()
- multiworld = setup_solo_multiworld(allsanity_options)
- number_locations = len(get_real_locations(self, multiworld))
+ expected_locations = 1956
+ real_locations = self.get_real_locations()
+ number_locations = len(real_locations)
self.assertGreaterEqual(number_locations, expected_locations)
print(f"Stardew Valley - Allsanity Locations without mods: {number_locations}")
if number_locations != expected_locations:
@@ -214,11 +448,14 @@ def test_allsanity_without_mods_has_at_least_locations(self):
f"\n\t\tExpected: {expected_locations}"
f"\n\t\tActual: {number_locations}")
+
+class TestAllSanityWithModsSettingsHasAllExpectedLocations(SVTestBase):
+ options = allsanity_options_with_mods()
+
def test_allsanity_with_mods_has_at_least_locations(self):
- expected_locations = 1246
- allsanity_options = allsanity_options_with_mods()
- multiworld = setup_solo_multiworld(allsanity_options)
- number_locations = len(get_real_locations(self, multiworld))
+ expected_locations = 2804
+ real_locations = self.get_real_locations()
+ number_locations = len(real_locations)
self.assertGreaterEqual(number_locations, expected_locations)
print(f"\nStardew Valley - Allsanity Locations with all mods: {number_locations}")
if number_locations != expected_locations:
@@ -230,7 +467,7 @@ def test_allsanity_with_mods_has_at_least_locations(self):
class TestFriendsanityNone(SVTestBase):
options = {
- options.Friendsanity.internal_name: options.Friendsanity.option_none,
+ Friendsanity.internal_name: Friendsanity.option_none,
}
@property
@@ -238,34 +475,46 @@ def run_default_tests(self) -> bool:
# None is default
return False
- def test_no_friendsanity_items(self):
+ def test_friendsanity_none(self):
+ with self.subTest("No Items"):
+ self.check_no_friendsanity_items()
+ with self.subTest("No Locations"):
+ self.check_no_friendsanity_locations()
+
+ def check_no_friendsanity_items(self):
for item in self.multiworld.itempool:
self.assertFalse(item.name.endswith(" <3"))
- def test_no_friendsanity_locations(self):
- for location_name in get_real_location_names(self, self.multiworld):
+ def check_no_friendsanity_locations(self):
+ for location_name in self.get_real_location_names():
self.assertFalse(location_name.startswith("Friendsanity"))
class TestFriendsanityBachelors(SVTestBase):
options = {
- options.Friendsanity.internal_name: options.Friendsanity.option_bachelors,
- options.FriendsanityHeartSize.internal_name: 1,
+ Friendsanity.internal_name: Friendsanity.option_bachelors,
+ FriendsanityHeartSize.internal_name: 1,
}
bachelors = {"Harvey", "Elliott", "Sam", "Alex", "Shane", "Sebastian", "Emily", "Haley", "Leah", "Abigail", "Penny",
"Maru"}
- def test_friendsanity_only_bachelor_items(self):
+ def test_friendsanity_only_bachelors(self):
+ with self.subTest("Items are valid"):
+ self.check_only_bachelors_items()
+ with self.subTest("Locations are valid"):
+ self.check_only_bachelors_locations()
+
+ def check_only_bachelors_items(self):
suffix = " <3"
for item in self.multiworld.itempool:
if item.name.endswith(suffix):
villager_name = item.name[:item.name.index(suffix)]
self.assertIn(villager_name, self.bachelors)
- def test_friendsanity_only_bachelor_locations(self):
+ def check_only_bachelors_locations(self):
prefix = "Friendsanity: "
suffix = " <3"
- for location_name in get_real_location_names(self, self.multiworld):
+ for location_name in self.get_real_location_names():
if location_name.startswith(prefix):
name_no_prefix = location_name[len(prefix):]
name_trimmed = name_no_prefix[:name_no_prefix.index(suffix)]
@@ -278,22 +527,28 @@ def test_friendsanity_only_bachelor_locations(self):
class TestFriendsanityStartingNpcs(SVTestBase):
options = {
- options.Friendsanity.internal_name: options.Friendsanity.option_starting_npcs,
- options.FriendsanityHeartSize.internal_name: 1,
+ Friendsanity.internal_name: Friendsanity.option_starting_npcs,
+ FriendsanityHeartSize.internal_name: 1,
}
excluded_npcs = {"Leo", "Krobus", "Dwarf", "Sandy", "Kent"}
- def test_friendsanity_only_starting_npcs_items(self):
+ def test_friendsanity_only_starting_npcs(self):
+ with self.subTest("Items are valid"):
+ self.check_only_starting_npcs_items()
+ with self.subTest("Locations are valid"):
+ self.check_only_starting_npcs_locations()
+
+ def check_only_starting_npcs_items(self):
suffix = " <3"
for item in self.multiworld.itempool:
if item.name.endswith(suffix):
villager_name = item.name[:item.name.index(suffix)]
self.assertNotIn(villager_name, self.excluded_npcs)
- def test_friendsanity_only_starting_npcs_locations(self):
+ def check_only_starting_npcs_locations(self):
prefix = "Friendsanity: "
suffix = " <3"
- for location_name in get_real_location_names(self, self.multiworld):
+ for location_name in self.get_real_location_names():
if location_name.startswith(prefix):
name_no_prefix = location_name[len(prefix):]
name_trimmed = name_no_prefix[:name_no_prefix.index(suffix)]
@@ -312,44 +567,71 @@ def test_friendsanity_only_starting_npcs_locations(self):
class TestFriendsanityAllNpcs(SVTestBase):
options = {
- options.Friendsanity.internal_name: options.Friendsanity.option_all,
- options.FriendsanityHeartSize.internal_name: 1,
+ Friendsanity.internal_name: Friendsanity.option_all,
+ FriendsanityHeartSize.internal_name: 4,
}
- def test_friendsanity_all_items(self):
+ def test_friendsanity_all_npcs(self):
+ with self.subTest("Items are valid"):
+ self.check_items_are_valid()
+ with self.subTest("Correct number of items"):
+ self.check_correct_number_of_items()
+ with self.subTest("Locations are valid"):
+ self.check_locations_are_valid()
+
+ def check_items_are_valid(self):
suffix = " <3"
for item in self.multiworld.itempool:
if item.name.endswith(suffix):
villager_name = item.name[:item.name.index(suffix)]
self.assertTrue(villager_name in all_villagers_by_mod_by_name[ModNames.vanilla] or villager_name == "Pet")
- def test_friendsanity_all_locations(self):
+ def check_correct_number_of_items(self):
+ suffix = " <3"
+ item_names = [item.name for item in self.multiworld.itempool]
+ for villager_name in all_villagers_by_mod_by_name[ModNames.vanilla]:
+ heart_item_name = f"{villager_name}{suffix}"
+ number_heart_items = item_names.count(heart_item_name)
+ if all_villagers_by_name[villager_name].bachelor:
+ self.assertEqual(number_heart_items, 2)
+ else:
+ self.assertEqual(number_heart_items, 3)
+ self.assertEqual(item_names.count("Pet <3"), 2)
+
+ def check_locations_are_valid(self):
prefix = "Friendsanity: "
suffix = " <3"
- for location_name in get_real_location_names(self, self.multiworld):
- if location_name.startswith(prefix):
- name_no_prefix = location_name[len(prefix):]
- name_trimmed = name_no_prefix[:name_no_prefix.index(suffix)]
- parts = name_trimmed.split(" ")
- name = parts[0]
- hearts = parts[1]
- self.assertTrue(name in all_villagers_by_mod_by_name[ModNames.vanilla] or name == "Pet")
- if name == "Pet":
- self.assertLessEqual(int(hearts), 5)
- elif all_villagers_by_name[name].bachelor:
- self.assertLessEqual(int(hearts), 8)
- else:
- self.assertLessEqual(int(hearts), 10)
+ for location_name in self.get_real_location_names():
+ if not location_name.startswith(prefix):
+ continue
+ name_no_prefix = location_name[len(prefix):]
+ name_trimmed = name_no_prefix[:name_no_prefix.index(suffix)]
+ parts = name_trimmed.split(" ")
+ name = parts[0]
+ hearts = int(parts[1])
+ self.assertTrue(name in all_villagers_by_mod_by_name[ModNames.vanilla] or name == "Pet")
+ if name == "Pet":
+ self.assertTrue(hearts == 4 or hearts == 5)
+ elif all_villagers_by_name[name].bachelor:
+ self.assertTrue(hearts == 4 or hearts == 8 or hearts == 12 or hearts == 14)
+ else:
+ self.assertTrue(hearts == 4 or hearts == 8 or hearts == 10)
class TestFriendsanityAllNpcsExcludingGingerIsland(SVTestBase):
options = {
- options.Friendsanity.internal_name: options.Friendsanity.option_all,
- options.FriendsanityHeartSize.internal_name: 1,
- options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true
+ Friendsanity.internal_name: Friendsanity.option_all,
+ FriendsanityHeartSize.internal_name: 4,
+ ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true
}
- def test_friendsanity_all_items(self):
+ def test_friendsanity_all_npcs_exclude_island(self):
+ with self.subTest("Items"):
+ self.check_items()
+ with self.subTest("Locations"):
+ self.check_locations()
+
+ def check_items(self):
suffix = " <3"
for item in self.multiworld.itempool:
if item.name.endswith(suffix):
@@ -357,10 +639,10 @@ def test_friendsanity_all_items(self):
self.assertNotEqual(villager_name, "Leo")
self.assertTrue(villager_name in all_villagers_by_mod_by_name[ModNames.vanilla] or villager_name == "Pet")
- def test_friendsanity_all_locations(self):
+ def check_locations(self):
prefix = "Friendsanity: "
suffix = " <3"
- for location_name in get_real_location_names(self, self.multiworld):
+ for location_name in self.get_real_location_names():
if location_name.startswith(prefix):
name_no_prefix = location_name[len(prefix):]
name_trimmed = name_no_prefix[:name_no_prefix.index(suffix)]
@@ -377,84 +659,28 @@ def test_friendsanity_all_locations(self):
self.assertLessEqual(int(hearts), 10)
-class TestFriendsanityAllNpcsWithMarriage(SVTestBase):
+class TestFriendsanityHeartSize3(SVTestBase):
options = {
- options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
- options.FriendsanityHeartSize.internal_name: 1,
+ Friendsanity.internal_name: Friendsanity.option_all_with_marriage,
+ FriendsanityHeartSize.internal_name: 3,
}
- def test_friendsanity_all_with_marriage_items(self):
+ def test_friendsanity_all_npcs_with_marriage(self):
+ with self.subTest("Items are valid"):
+ self.check_items_are_valid()
+ with self.subTest("Correct number of items"):
+ self.check_correct_number_of_items()
+ with self.subTest("Locations are valid"):
+ self.check_locations_are_valid()
+
+ def check_items_are_valid(self):
suffix = " <3"
for item in self.multiworld.itempool:
if item.name.endswith(suffix):
villager_name = item.name[:item.name.index(suffix)]
self.assertTrue(villager_name in all_villagers_by_mod_by_name[ModNames.vanilla] or villager_name == "Pet")
- def test_friendsanity_all_with_marriage_locations(self):
- prefix = "Friendsanity: "
- suffix = " <3"
- for location_name in get_real_location_names(self, self.multiworld):
- if location_name.startswith(prefix):
- name_no_prefix = location_name[len(prefix):]
- name_trimmed = name_no_prefix[:name_no_prefix.index(suffix)]
- parts = name_trimmed.split(" ")
- name = parts[0]
- hearts = parts[1]
- self.assertTrue(name in all_villagers_by_mod_by_name[ModNames.vanilla] or name == "Pet")
- if name == "Pet":
- self.assertLessEqual(int(hearts), 5)
- elif all_villagers_by_name[name].bachelor:
- self.assertLessEqual(int(hearts), 14)
- else:
- self.assertLessEqual(int(hearts), 10)
-
-
-""" # Assuming math is correct if we check 2 points
-class TestFriendsanityAllNpcsWithMarriageHeartSize2(SVTestBase):
- options = {
- options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
- options.FriendsanityHeartSize.internal_name: 2,
- }
-
- def test_friendsanity_all_with_marriage_items(self):
- suffix = " <3"
- item_names = [item.name for item in self.multiworld.itempool]
- for villager_name in all_villagers_by_mod_by_name[ModNames.vanilla]:
- heart_item_name = f"{villager_name}{suffix}"
- number_heart_items = item_names.count(heart_item_name)
- if all_villagers_by_name[villager_name].bachelor:
- self.assertEqual(number_heart_items, 7)
- else:
- self.assertEqual(number_heart_items, 5)
- self.assertEqual(item_names.count("Pet <3"), 3)
-
- def test_friendsanity_all_with_marriage_locations(self):
- prefix = "Friendsanity: "
- suffix = " <3"
- for location_name in get_real_location_names(self, self.multiworld):
- if not location_name.startswith(prefix):
- continue
- name_no_prefix = location_name[len(prefix):]
- name_trimmed = name_no_prefix[:name_no_prefix.index(suffix)]
- parts = name_trimmed.split(" ")
- name = parts[0]
- hearts = int(parts[1])
- self.assertTrue(name in all_villagers_by_mod_by_name[ModNames.vanilla] or name == "Pet")
- if name == "Pet":
- self.assertTrue(hearts == 2 or hearts == 4 or hearts == 5)
- elif all_villagers_by_name[name].bachelor:
- self.assertTrue(hearts == 2 or hearts == 4 or hearts == 6 or hearts == 8 or hearts == 10 or hearts == 12 or hearts == 14)
- else:
- self.assertTrue(hearts == 2 or hearts == 4 or hearts == 6 or hearts == 8 or hearts == 10)
-
-
-class TestFriendsanityAllNpcsWithMarriageHeartSize3(SVTestBase):
- options = {
- options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
- options.FriendsanityHeartSize.internal_name: 3,
- }
-
- def test_friendsanity_all_with_marriage_items(self):
+ def check_correct_number_of_items(self):
suffix = " <3"
item_names = [item.name for item in self.multiworld.itempool]
for villager_name in all_villagers_by_mod_by_name[ModNames.vanilla]:
@@ -466,10 +692,10 @@ def test_friendsanity_all_with_marriage_items(self):
self.assertEqual(number_heart_items, 4)
self.assertEqual(item_names.count("Pet <3"), 2)
- def test_friendsanity_all_with_marriage_locations(self):
+ def check_locations_are_valid(self):
prefix = "Friendsanity: "
suffix = " <3"
- for location_name in get_real_location_names(self, self.multiworld):
+ for location_name in self.get_real_location_names():
if not location_name.startswith(prefix):
continue
name_no_prefix = location_name[len(prefix):]
@@ -486,52 +712,28 @@ def test_friendsanity_all_with_marriage_locations(self):
self.assertTrue(hearts == 3 or hearts == 6 or hearts == 9 or hearts == 10)
-class TestFriendsanityAllNpcsWithMarriageHeartSize4(SVTestBase):
+class TestFriendsanityHeartSize5(SVTestBase):
options = {
- options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
- options.FriendsanityHeartSize.internal_name: 4,
+ Friendsanity.internal_name: Friendsanity.option_all_with_marriage,
+ FriendsanityHeartSize.internal_name: 5,
}
- def test_friendsanity_all_with_marriage_items(self):
- suffix = " <3"
- item_names = [item.name for item in self.multiworld.itempool]
- for villager_name in all_villagers_by_mod_by_name[ModNames.vanilla]:
- heart_item_name = f"{villager_name}{suffix}"
- number_heart_items = item_names.count(heart_item_name)
- if all_villagers_by_name[villager_name].bachelor:
- self.assertEqual(number_heart_items, 4)
- else:
- self.assertEqual(number_heart_items, 3)
- self.assertEqual(item_names.count("Pet <3"), 2)
+ def test_friendsanity_all_npcs_with_marriage(self):
+ with self.subTest("Items are valid"):
+ self.check_items_are_valid()
+ with self.subTest("Correct number of items"):
+ self.check_correct_number_of_items()
+ with self.subTest("Locations are valid"):
+ self.check_locations_are_valid()
- def test_friendsanity_all_with_marriage_locations(self):
- prefix = "Friendsanity: "
+ def check_items_are_valid(self):
suffix = " <3"
- for location_name in get_real_location_names(self, self.multiworld):
- if not location_name.startswith(prefix):
- continue
- name_no_prefix = location_name[len(prefix):]
- name_trimmed = name_no_prefix[:name_no_prefix.index(suffix)]
- parts = name_trimmed.split(" ")
- name = parts[0]
- hearts = int(parts[1])
- self.assertTrue(name in all_villagers_by_mod_by_name[ModNames.vanilla] or name == "Pet")
- if name == "Pet":
- self.assertTrue(hearts == 4 or hearts == 5)
- elif all_villagers_by_name[name].bachelor:
- self.assertTrue(hearts == 4 or hearts == 8 or hearts == 12 or hearts == 14)
- else:
- self.assertTrue(hearts == 4 or hearts == 8 or hearts == 10)
-"""
-
-
-class TestFriendsanityAllNpcsWithMarriageHeartSize5(SVTestBase):
- options = {
- options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
- options.FriendsanityHeartSize.internal_name: 5,
- }
+ for item in self.multiworld.itempool:
+ if item.name.endswith(suffix):
+ villager_name = item.name[:item.name.index(suffix)]
+ self.assertTrue(villager_name in all_villagers_by_mod_by_name[ModNames.vanilla] or villager_name == "Pet")
- def test_friendsanity_all_with_marriage_items(self):
+ def check_correct_number_of_items(self):
suffix = " <3"
item_names = [item.name for item in self.multiworld.itempool]
for villager_name in all_villagers_by_mod_by_name[ModNames.vanilla]:
@@ -543,10 +745,10 @@ def test_friendsanity_all_with_marriage_items(self):
self.assertEqual(number_heart_items, 2)
self.assertEqual(item_names.count("Pet <3"), 1)
- def test_friendsanity_all_with_marriage_locations(self):
+ def check_locations_are_valid(self):
prefix = "Friendsanity: "
suffix = " <3"
- for location_name in get_real_location_names(self, self.multiworld):
+ for location_name in self.get_real_location_names():
if not location_name.startswith(prefix):
continue
name_no_prefix = location_name[len(prefix):]
@@ -561,3 +763,341 @@ def test_friendsanity_all_with_marriage_locations(self):
self.assertTrue(hearts == 5 or hearts == 10 or hearts == 14)
else:
self.assertTrue(hearts == 5 or hearts == 10)
+
+
+class TestShipsanityNone(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_none
+ }
+
+ def test_no_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event:
+ with self.subTest(location.name):
+ self.assertFalse("Shipsanity" in location.name)
+ self.assertNotIn(LocationTags.SHIPSANITY, location_table[location.name].tags)
+
+
+class TestShipsanityCrops(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_crops,
+ SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi
+ }
+
+ def test_only_crop_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags:
+ with self.subTest(location.name):
+ self.assertIn(LocationTags.SHIPSANITY_CROP, location_table[location.name].tags)
+
+ def test_include_island_crop_shipsanity_locations(self):
+ location_names = [location.name for location in self.multiworld.get_locations(self.player)]
+ self.assertIn("Shipsanity: Banana", location_names)
+ self.assertIn("Shipsanity: Mango", location_names)
+ self.assertIn("Shipsanity: Pineapple", location_names)
+ self.assertIn("Shipsanity: Taro Root", location_names)
+ self.assertIn("Shipsanity: Ginger", location_names)
+ self.assertIn("Shipsanity: Magma Cap", location_names)
+ self.assertIn("Shipsanity: Qi Fruit", location_names)
+
+
+class TestShipsanityCropsExcludeIsland(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_crops,
+ ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true
+ }
+
+ def test_only_crop_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags:
+ with self.subTest(location.name):
+ self.assertIn(LocationTags.SHIPSANITY_CROP, location_table[location.name].tags)
+
+ def test_only_mainland_crop_shipsanity_locations(self):
+ location_names = [location.name for location in self.multiworld.get_locations(self.player)]
+ self.assertNotIn("Shipsanity: Banana", location_names)
+ self.assertNotIn("Shipsanity: Mango", location_names)
+ self.assertNotIn("Shipsanity: Pineapple", location_names)
+ self.assertNotIn("Shipsanity: Taro Root", location_names)
+ self.assertNotIn("Shipsanity: Ginger", location_names)
+ self.assertNotIn("Shipsanity: Magma Cap", location_names)
+ self.assertNotIn("Shipsanity: Qi Fruit", location_names)
+
+
+class TestShipsanityCropsNoQiCropWithoutSpecialOrders(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_crops,
+ SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_only
+ }
+
+ def test_only_crop_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags:
+ with self.subTest(location.name):
+ self.assertIn(LocationTags.SHIPSANITY_CROP, location_table[location.name].tags)
+
+ def test_island_crops_without_qi_fruit_shipsanity_locations(self):
+ location_names = [location.name for location in self.multiworld.get_locations(self.player)]
+ self.assertIn("Shipsanity: Banana", location_names)
+ self.assertIn("Shipsanity: Mango", location_names)
+ self.assertIn("Shipsanity: Pineapple", location_names)
+ self.assertIn("Shipsanity: Taro Root", location_names)
+ self.assertIn("Shipsanity: Ginger", location_names)
+ self.assertIn("Shipsanity: Magma Cap", location_names)
+ self.assertNotIn("Shipsanity: Qi Fruit", location_names)
+
+
+class TestShipsanityFish(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_fish,
+ SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi
+ }
+
+ def test_only_fish_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags:
+ with self.subTest(location.name):
+ self.assertIn(LocationTags.SHIPSANITY_FISH, location_table[location.name].tags)
+
+ def test_include_island_fish_shipsanity_locations(self):
+ location_names = [location.name for location in self.multiworld.get_locations(self.player)]
+ self.assertIn("Shipsanity: Blue Discus", location_names)
+ self.assertIn("Shipsanity: Lionfish", location_names)
+ self.assertIn("Shipsanity: Stingray", location_names)
+ self.assertIn("Shipsanity: Glacierfish Jr.", location_names)
+ self.assertIn("Shipsanity: Legend II", location_names)
+ self.assertIn("Shipsanity: Ms. Angler", location_names)
+ self.assertIn("Shipsanity: Radioactive Carp", location_names)
+ self.assertIn("Shipsanity: Son of Crimsonfish", location_names)
+
+
+class TestShipsanityFishExcludeIsland(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_fish,
+ ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true
+ }
+
+ def test_only_fish_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags:
+ with self.subTest(location.name):
+ self.assertIn(LocationTags.SHIPSANITY_FISH, location_table[location.name].tags)
+
+ def test_exclude_island_fish_shipsanity_locations(self):
+ location_names = [location.name for location in self.multiworld.get_locations(self.player)]
+ self.assertNotIn("Shipsanity: Blue Discus", location_names)
+ self.assertNotIn("Shipsanity: Lionfish", location_names)
+ self.assertNotIn("Shipsanity: Stingray", location_names)
+ self.assertNotIn("Shipsanity: Glacierfish Jr.", location_names)
+ self.assertNotIn("Shipsanity: Legend II", location_names)
+ self.assertNotIn("Shipsanity: Ms. Angler", location_names)
+ self.assertNotIn("Shipsanity: Radioactive Carp", location_names)
+ self.assertNotIn("Shipsanity: Son of Crimsonfish", location_names)
+
+
+class TestShipsanityFishExcludeQiOrders(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_fish,
+ SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_only
+ }
+
+ def test_only_fish_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags:
+ with self.subTest(location.name):
+ self.assertIn(LocationTags.SHIPSANITY_FISH, location_table[location.name].tags)
+
+ def test_include_island_fish_no_extended_family_shipsanity_locations(self):
+ location_names = [location.name for location in self.multiworld.get_locations(self.player)]
+ self.assertIn("Shipsanity: Blue Discus", location_names)
+ self.assertIn("Shipsanity: Lionfish", location_names)
+ self.assertIn("Shipsanity: Stingray", location_names)
+ self.assertNotIn("Shipsanity: Glacierfish Jr.", location_names)
+ self.assertNotIn("Shipsanity: Legend II", location_names)
+ self.assertNotIn("Shipsanity: Ms. Angler", location_names)
+ self.assertNotIn("Shipsanity: Radioactive Carp", location_names)
+ self.assertNotIn("Shipsanity: Son of Crimsonfish", location_names)
+
+
+class TestShipsanityFullShipment(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_full_shipment,
+ SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi
+ }
+
+ def test_only_full_shipment_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags:
+ with self.subTest(location.name):
+ self.assertIn(LocationTags.SHIPSANITY_FULL_SHIPMENT, location_table[location.name].tags)
+ self.assertNotIn(LocationTags.SHIPSANITY_FISH, location_table[location.name].tags)
+
+ def test_include_island_items_shipsanity_locations(self):
+ location_names = [location.name for location in self.multiworld.get_locations(self.player)]
+ self.assertIn("Shipsanity: Cinder Shard", location_names)
+ self.assertIn("Shipsanity: Bone Fragment", location_names)
+ self.assertIn("Shipsanity: Radioactive Ore", location_names)
+ self.assertIn("Shipsanity: Radioactive Bar", location_names)
+ self.assertIn("Shipsanity: Banana", location_names)
+ self.assertIn("Shipsanity: Mango", location_names)
+ self.assertIn("Shipsanity: Pineapple", location_names)
+ self.assertIn("Shipsanity: Taro Root", location_names)
+ self.assertIn("Shipsanity: Ginger", location_names)
+ self.assertIn("Shipsanity: Magma Cap", location_names)
+
+
+class TestShipsanityFullShipmentExcludeIsland(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_full_shipment,
+ ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true
+ }
+
+ def test_only_full_shipment_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags:
+ with self.subTest(location.name):
+ self.assertIn(LocationTags.SHIPSANITY_FULL_SHIPMENT, location_table[location.name].tags)
+ self.assertNotIn(LocationTags.SHIPSANITY_FISH, location_table[location.name].tags)
+
+ def test_exclude_island_items_shipsanity_locations(self):
+ location_names = [location.name for location in self.multiworld.get_locations(self.player)]
+ self.assertNotIn("Shipsanity: Cinder Shard", location_names)
+ self.assertNotIn("Shipsanity: Radioactive Ore", location_names)
+ self.assertNotIn("Shipsanity: Radioactive Bar", location_names)
+ self.assertNotIn("Shipsanity: Banana", location_names)
+ self.assertNotIn("Shipsanity: Mango", location_names)
+ self.assertNotIn("Shipsanity: Pineapple", location_names)
+ self.assertNotIn("Shipsanity: Taro Root", location_names)
+ self.assertNotIn("Shipsanity: Ginger", location_names)
+ self.assertNotIn("Shipsanity: Magma Cap", location_names)
+
+
+class TestShipsanityFullShipmentExcludeQiBoard(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_full_shipment,
+ SpecialOrderLocations.internal_name: SpecialOrderLocations.option_disabled
+ }
+
+ def test_only_full_shipment_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags:
+ with self.subTest(location.name):
+ self.assertIn(LocationTags.SHIPSANITY_FULL_SHIPMENT, location_table[location.name].tags)
+ self.assertNotIn(LocationTags.SHIPSANITY_FISH, location_table[location.name].tags)
+
+ def test_exclude_qi_board_items_shipsanity_locations(self):
+ location_names = [location.name for location in self.multiworld.get_locations(self.player)]
+ self.assertIn("Shipsanity: Cinder Shard", location_names)
+ self.assertIn("Shipsanity: Bone Fragment", location_names)
+ self.assertNotIn("Shipsanity: Radioactive Ore", location_names)
+ self.assertNotIn("Shipsanity: Radioactive Bar", location_names)
+ self.assertIn("Shipsanity: Banana", location_names)
+ self.assertIn("Shipsanity: Mango", location_names)
+ self.assertIn("Shipsanity: Pineapple", location_names)
+ self.assertIn("Shipsanity: Taro Root", location_names)
+ self.assertIn("Shipsanity: Ginger", location_names)
+ self.assertIn("Shipsanity: Magma Cap", location_names)
+
+
+class TestShipsanityFullShipmentWithFish(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_full_shipment_with_fish,
+ SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi
+ }
+
+ def test_only_full_shipment_and_fish_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags:
+ with self.subTest(location.name):
+ self.assertTrue(LocationTags.SHIPSANITY_FULL_SHIPMENT in location_table[location.name].tags or
+ LocationTags.SHIPSANITY_FISH in location_table[location.name].tags)
+
+ def test_include_island_items_shipsanity_locations(self):
+ location_names = [location.name for location in self.multiworld.get_locations(self.player)]
+ self.assertIn("Shipsanity: Cinder Shard", location_names)
+ self.assertIn("Shipsanity: Bone Fragment", location_names)
+ self.assertIn("Shipsanity: Radioactive Ore", location_names)
+ self.assertIn("Shipsanity: Radioactive Bar", location_names)
+ self.assertIn("Shipsanity: Banana", location_names)
+ self.assertIn("Shipsanity: Mango", location_names)
+ self.assertIn("Shipsanity: Pineapple", location_names)
+ self.assertIn("Shipsanity: Taro Root", location_names)
+ self.assertIn("Shipsanity: Ginger", location_names)
+ self.assertIn("Shipsanity: Magma Cap", location_names)
+ self.assertIn("Shipsanity: Blue Discus", location_names)
+ self.assertIn("Shipsanity: Lionfish", location_names)
+ self.assertIn("Shipsanity: Stingray", location_names)
+ self.assertIn("Shipsanity: Glacierfish Jr.", location_names)
+ self.assertIn("Shipsanity: Legend II", location_names)
+ self.assertIn("Shipsanity: Ms. Angler", location_names)
+ self.assertIn("Shipsanity: Radioactive Carp", location_names)
+ self.assertIn("Shipsanity: Son of Crimsonfish", location_names)
+
+
+class TestShipsanityFullShipmentWithFishExcludeIsland(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_full_shipment_with_fish,
+ ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true
+ }
+
+ def test_only_full_shipment_and_fish_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags:
+ with self.subTest(location.name):
+ self.assertTrue(LocationTags.SHIPSANITY_FULL_SHIPMENT in location_table[location.name].tags or
+ LocationTags.SHIPSANITY_FISH in location_table[location.name].tags)
+
+ def test_exclude_island_items_shipsanity_locations(self):
+ location_names = [location.name for location in self.multiworld.get_locations(self.player)]
+ self.assertNotIn("Shipsanity: Cinder Shard", location_names)
+ self.assertNotIn("Shipsanity: Radioactive Ore", location_names)
+ self.assertNotIn("Shipsanity: Radioactive Bar", location_names)
+ self.assertNotIn("Shipsanity: Banana", location_names)
+ self.assertNotIn("Shipsanity: Mango", location_names)
+ self.assertNotIn("Shipsanity: Pineapple", location_names)
+ self.assertNotIn("Shipsanity: Taro Root", location_names)
+ self.assertNotIn("Shipsanity: Ginger", location_names)
+ self.assertNotIn("Shipsanity: Magma Cap", location_names)
+ self.assertNotIn("Shipsanity: Blue Discus", location_names)
+ self.assertNotIn("Shipsanity: Lionfish", location_names)
+ self.assertNotIn("Shipsanity: Stingray", location_names)
+ self.assertNotIn("Shipsanity: Glacierfish Jr.", location_names)
+ self.assertNotIn("Shipsanity: Legend II", location_names)
+ self.assertNotIn("Shipsanity: Ms. Angler", location_names)
+ self.assertNotIn("Shipsanity: Radioactive Carp", location_names)
+ self.assertNotIn("Shipsanity: Son of Crimsonfish", location_names)
+
+
+class TestShipsanityFullShipmentWithFishExcludeQiBoard(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_full_shipment_with_fish,
+ SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_only
+ }
+
+ def test_only_full_shipment_and_fish_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags:
+ with self.subTest(location.name):
+ self.assertTrue(LocationTags.SHIPSANITY_FULL_SHIPMENT in location_table[location.name].tags or
+ LocationTags.SHIPSANITY_FISH in location_table[location.name].tags)
+
+ def test_exclude_qi_board_items_shipsanity_locations(self):
+ location_names = [location.name for location in self.multiworld.get_locations(self.player)]
+ self.assertIn("Shipsanity: Cinder Shard", location_names)
+ self.assertIn("Shipsanity: Bone Fragment", location_names)
+ self.assertNotIn("Shipsanity: Radioactive Ore", location_names)
+ self.assertNotIn("Shipsanity: Radioactive Bar", location_names)
+ self.assertIn("Shipsanity: Banana", location_names)
+ self.assertIn("Shipsanity: Mango", location_names)
+ self.assertIn("Shipsanity: Pineapple", location_names)
+ self.assertIn("Shipsanity: Taro Root", location_names)
+ self.assertIn("Shipsanity: Ginger", location_names)
+ self.assertIn("Shipsanity: Magma Cap", location_names)
+ self.assertIn("Shipsanity: Blue Discus", location_names)
+ self.assertIn("Shipsanity: Lionfish", location_names)
+ self.assertIn("Shipsanity: Stingray", location_names)
+ self.assertNotIn("Shipsanity: Glacierfish Jr.", location_names)
+ self.assertNotIn("Shipsanity: Legend II", location_names)
+ self.assertNotIn("Shipsanity: Ms. Angler", location_names)
+ self.assertNotIn("Shipsanity: Radioactive Carp", location_names)
+ self.assertNotIn("Shipsanity: Son of Crimsonfish", location_names)
diff --git a/worlds/stardew_valley/test/TestItemLink.py b/worlds/stardew_valley/test/TestItemLink.py
index f55ab8ca347d..39bf553cab2d 100644
--- a/worlds/stardew_valley/test/TestItemLink.py
+++ b/worlds/stardew_valley/test/TestItemLink.py
@@ -9,7 +9,7 @@ class TestItemLinksEverythingIncluded(SVTestBase):
options.TrapItems.internal_name: options.TrapItems.option_medium}
def test_filler_of_all_types_generated(self):
- max_number_filler = 115
+ max_number_filler = 114
filler_generated = []
at_least_one_trap = False
at_least_one_island = False
@@ -60,7 +60,7 @@ class TestItemLinksNoTraps(SVTestBase):
options.TrapItems.internal_name: options.TrapItems.option_no_traps}
def test_filler_has_no_traps_but_has_island(self):
- max_number_filler = 100
+ max_number_filler = 99
filler_generated = []
at_least_one_island = False
for i in range(0, max_iterations):
diff --git a/worlds/stardew_valley/test/TestItems.py b/worlds/stardew_valley/test/TestItems.py
index 38f59c74904f..48bc1b152138 100644
--- a/worlds/stardew_valley/test/TestItems.py
+++ b/worlds/stardew_valley/test/TestItems.py
@@ -1,14 +1,16 @@
-import itertools
-import math
import sys
-import unittest
import random
-from typing import Set
+import sys
-from BaseClasses import ItemClassification, MultiWorld
-from . import setup_solo_multiworld, SVTestCase, allsanity_options_without_mods
-from .. import ItemData, StardewValleyWorld
+from BaseClasses import MultiWorld, get_seed
+from . import setup_solo_multiworld, SVTestCase, allsanity_options_without_mods, get_minsanity_options
+from .. import StardewValleyWorld
from ..items import Group, item_table
+from ..options import Friendsanity, SeasonRandomization, Museumsanity, Shipsanity, Goal
+from ..strings.wallet_item_names import Wallet
+
+all_seasons = ["Spring", "Summer", "Fall", "Winter"]
+all_farms = ["Standard Farm", "Riverland Farm", "Forest Farm", "Hill-top Farm", "Wilderness Farm", "Four Corners Farm", "Beach Farm"]
class TestItems(SVTestCase):
@@ -33,20 +35,106 @@ def test_items_table_footprint_is_between_717000_and_737000(self):
def test_babies_come_in_all_shapes_and_sizes(self):
baby_permutations = set()
+ options = {Friendsanity.internal_name: Friendsanity.option_bachelors}
for attempt_number in range(50):
if len(baby_permutations) >= 4:
print(f"Already got all 4 baby permutations, breaking early [{attempt_number} generations]")
break
- seed = random.randrange(sys.maxsize)
- multiworld = setup_solo_multiworld(seed=seed)
+ seed = get_seed()
+ multiworld = setup_solo_multiworld(options, seed=seed, _cache={})
baby_items = [item for item in multiworld.get_items() if "Baby" in item.name]
self.assertEqual(len(baby_items), 2)
baby_permutations.add(f"{baby_items[0]} - {baby_items[1]}")
self.assertEqual(len(baby_permutations), 4)
def test_correct_number_of_stardrops(self):
- seed = random.randrange(sys.maxsize)
allsanity_options = allsanity_options_without_mods()
- multiworld = setup_solo_multiworld(allsanity_options, seed=seed)
+ multiworld = setup_solo_multiworld(allsanity_options)
stardrop_items = [item for item in multiworld.get_items() if "Stardrop" in item.name]
- self.assertEqual(len(stardrop_items), 5)
+ self.assertEqual(len(stardrop_items), 7)
+
+ def test_no_duplicate_rings(self):
+ allsanity_options = allsanity_options_without_mods()
+ multiworld = setup_solo_multiworld(allsanity_options)
+ ring_items = [item.name for item in multiworld.get_items() if Group.RING in item_table[item.name].groups]
+ self.assertEqual(len(ring_items), len(set(ring_items)))
+
+ def test_can_start_in_any_season(self):
+ starting_seasons_rolled = set()
+ options = {SeasonRandomization.internal_name: SeasonRandomization.option_randomized}
+ for attempt_number in range(50):
+ if len(starting_seasons_rolled) >= 4:
+ print(f"Already got all 4 starting seasons, breaking early [{attempt_number} generations]")
+ break
+ seed = get_seed()
+ multiworld = setup_solo_multiworld(options, seed=seed, _cache={})
+ starting_season_items = [item for item in multiworld.precollected_items[1] if item.name in all_seasons]
+ season_items = [item for item in multiworld.get_items() if item.name in all_seasons]
+ self.assertEqual(len(starting_season_items), 1)
+ self.assertEqual(len(season_items), 3)
+ starting_seasons_rolled.add(f"{starting_season_items[0]}")
+ self.assertEqual(len(starting_seasons_rolled), 4)
+
+ def test_can_start_on_any_farm(self):
+ starting_farms_rolled = set()
+ for attempt_number in range(60):
+ if len(starting_farms_rolled) >= 7:
+ print(f"Already got all 7 farm types, breaking early [{attempt_number} generations]")
+ break
+ seed = random.randrange(sys.maxsize)
+ multiworld = setup_solo_multiworld(seed=seed, _cache={})
+ starting_farm = multiworld.worlds[1].fill_slot_data()["farm_type"]
+ starting_farms_rolled.add(starting_farm)
+ self.assertEqual(len(starting_farms_rolled), 7)
+
+
+class TestMetalDetectors(SVTestCase):
+ def test_minsanity_1_metal_detector(self):
+ options = get_minsanity_options()
+ multiworld = setup_solo_multiworld(options)
+ items = [item.name for item in multiworld.get_items() if item.name == Wallet.metal_detector]
+ self.assertEquals(len(items), 1)
+
+ def test_museumsanity_2_metal_detector(self):
+ options = get_minsanity_options().copy()
+ options[Museumsanity.internal_name] = Museumsanity.option_all
+ multiworld = setup_solo_multiworld(options)
+ items = [item.name for item in multiworld.get_items() if item.name == Wallet.metal_detector]
+ self.assertEquals(len(items), 2)
+
+ def test_shipsanity_full_shipment_1_metal_detector(self):
+ options = get_minsanity_options().copy()
+ options[Shipsanity.internal_name] = Shipsanity.option_full_shipment
+ multiworld = setup_solo_multiworld(options)
+ items = [item.name for item in multiworld.get_items() if item.name == Wallet.metal_detector]
+ self.assertEquals(len(items), 1)
+
+ def test_shipsanity_everything_2_metal_detector(self):
+ options = get_minsanity_options().copy()
+ options[Shipsanity.internal_name] = Shipsanity.option_everything
+ multiworld = setup_solo_multiworld(options)
+ items = [item.name for item in multiworld.get_items() if item.name == Wallet.metal_detector]
+ self.assertEquals(len(items), 2)
+
+ def test_complete_collection_2_metal_detector(self):
+ options = get_minsanity_options().copy()
+ options[Goal.internal_name] = Goal.option_complete_collection
+ multiworld = setup_solo_multiworld(options)
+ items = [item.name for item in multiworld.get_items() if item.name == Wallet.metal_detector]
+ self.assertEquals(len(items), 2)
+
+ def test_perfection_2_metal_detector(self):
+ options = get_minsanity_options().copy()
+ options[Goal.internal_name] = Goal.option_perfection
+ multiworld = setup_solo_multiworld(options)
+ items = [item.name for item in multiworld.get_items() if item.name == Wallet.metal_detector]
+ self.assertEquals(len(items), 2)
+
+ def test_maxsanity_4_metal_detector(self):
+ options = get_minsanity_options().copy()
+ options[Museumsanity.internal_name] = Museumsanity.option_all
+ options[Shipsanity.internal_name] = Shipsanity.option_everything
+ options[Goal.internal_name] = Goal.option_perfection
+ multiworld = setup_solo_multiworld(options)
+ items = [item.name for item in multiworld.get_items() if item.name == Wallet.metal_detector]
+ self.assertEquals(len(items), 4)
diff --git a/worlds/stardew_valley/test/TestLogic.py b/worlds/stardew_valley/test/TestLogic.py
index 7965d05b57be..84d38ffeb449 100644
--- a/worlds/stardew_valley/test/TestLogic.py
+++ b/worlds/stardew_valley/test/TestLogic.py
@@ -1,11 +1,10 @@
-import unittest
+from unittest import TestCase
-from test.general import setup_solo_multiworld
-from .. import StardewValleyWorld, StardewLocation
-from ..data.bundle_data import BundleItem, all_bundle_items_except_money
-from ..stardew_rule import MISSING_ITEM, False_
+from . import setup_solo_multiworld, allsanity_options_with_mods
+from .assertion import RuleAssertMixin
+from ..data.bundle_data import all_bundle_items_except_money
-multi_world = setup_solo_multiworld(StardewValleyWorld)
+multi_world = setup_solo_multiworld(allsanity_options_with_mods(), _cache={})
world = multi_world.worlds[1]
logic = world.logic
@@ -18,85 +17,74 @@ def collect_all(mw):
collect_all(multi_world)
-class TestLogic(unittest.TestCase):
+class TestLogic(RuleAssertMixin, TestCase):
def test_given_bundle_item_then_is_available_in_logic(self):
for bundle_item in all_bundle_items_except_money:
- with self.subTest(msg=bundle_item.item.name):
- self.assertIn(bundle_item.item.name, logic.item_rules)
+ with self.subTest(msg=bundle_item.item_name):
+ self.assertIn(bundle_item.item_name, logic.registry.item_rules)
def test_given_item_rule_then_can_be_resolved(self):
- for item in logic.item_rules.keys():
+ for item in logic.registry.item_rules.keys():
with self.subTest(msg=item):
- rule = logic.item_rules[item]
- self.assertNotIn(MISSING_ITEM, repr(rule))
- self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve item rule for {item} {rule}")
+ rule = logic.registry.item_rules[item]
+ self.assert_rule_can_be_resolved(rule, multi_world.state)
def test_given_building_rule_then_can_be_resolved(self):
- for building in logic.building_rules.keys():
+ for building in logic.registry.building_rules.keys():
with self.subTest(msg=building):
- rule = logic.building_rules[building]
- self.assertNotIn(MISSING_ITEM, repr(rule))
- self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve building rule for {building} {rule}")
+ rule = logic.registry.building_rules[building]
+ self.assert_rule_can_be_resolved(rule, multi_world.state)
def test_given_quest_rule_then_can_be_resolved(self):
- for quest in logic.quest_rules.keys():
+ for quest in logic.registry.quest_rules.keys():
with self.subTest(msg=quest):
- rule = logic.quest_rules[quest]
- self.assertNotIn(MISSING_ITEM, repr(rule))
- self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve quest rule for {quest} {rule}")
+ rule = logic.registry.quest_rules[quest]
+ self.assert_rule_can_be_resolved(rule, multi_world.state)
def test_given_special_order_rule_then_can_be_resolved(self):
- for special_order in logic.special_order_rules.keys():
+ for special_order in logic.registry.special_order_rules.keys():
with self.subTest(msg=special_order):
- rule = logic.special_order_rules[special_order]
- self.assertNotIn(MISSING_ITEM, repr(rule))
- self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve special order rule for {special_order} {rule}")
+ rule = logic.registry.special_order_rules[special_order]
+ self.assert_rule_can_be_resolved(rule, multi_world.state)
def test_given_tree_fruit_rule_then_can_be_resolved(self):
- for tree_fruit in logic.tree_fruit_rules.keys():
+ for tree_fruit in logic.registry.tree_fruit_rules.keys():
with self.subTest(msg=tree_fruit):
- rule = logic.tree_fruit_rules[tree_fruit]
- self.assertNotIn(MISSING_ITEM, repr(rule))
- self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve tree fruit rule for {tree_fruit} {rule}")
+ rule = logic.registry.tree_fruit_rules[tree_fruit]
+ self.assert_rule_can_be_resolved(rule, multi_world.state)
def test_given_seed_rule_then_can_be_resolved(self):
- for seed in logic.seed_rules.keys():
+ for seed in logic.registry.seed_rules.keys():
with self.subTest(msg=seed):
- rule = logic.seed_rules[seed]
- self.assertNotIn(MISSING_ITEM, repr(rule))
- self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve seed rule for {seed} {rule}")
+ rule = logic.registry.seed_rules[seed]
+ self.assert_rule_can_be_resolved(rule, multi_world.state)
def test_given_crop_rule_then_can_be_resolved(self):
- for crop in logic.crop_rules.keys():
+ for crop in logic.registry.crop_rules.keys():
with self.subTest(msg=crop):
- rule = logic.crop_rules[crop]
- self.assertNotIn(MISSING_ITEM, repr(rule))
- self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve crop rule for {crop} {rule}")
+ rule = logic.registry.crop_rules[crop]
+ self.assert_rule_can_be_resolved(rule, multi_world.state)
def test_given_fish_rule_then_can_be_resolved(self):
- for fish in logic.fish_rules.keys():
+ for fish in logic.registry.fish_rules.keys():
with self.subTest(msg=fish):
- rule = logic.fish_rules[fish]
- self.assertNotIn(MISSING_ITEM, repr(rule))
- self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve fish rule for {fish} {rule}")
+ rule = logic.registry.fish_rules[fish]
+ self.assert_rule_can_be_resolved(rule, multi_world.state)
def test_given_museum_rule_then_can_be_resolved(self):
- for donation in logic.museum_rules.keys():
+ for donation in logic.registry.museum_rules.keys():
with self.subTest(msg=donation):
- rule = logic.museum_rules[donation]
- self.assertNotIn(MISSING_ITEM, repr(rule))
- self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve museum rule for {donation} {rule}")
+ rule = logic.registry.museum_rules[donation]
+ self.assert_rule_can_be_resolved(rule, multi_world.state)
def test_given_cooking_rule_then_can_be_resolved(self):
- for cooking_rule in logic.cooking_rules.keys():
+ for cooking_rule in logic.registry.cooking_rules.keys():
with self.subTest(msg=cooking_rule):
- rule = logic.cooking_rules[cooking_rule]
- self.assertNotIn(MISSING_ITEM, repr(rule))
- self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve cooking rule for {cooking_rule} {rule}")
+ rule = logic.registry.cooking_rules[cooking_rule]
+ self.assert_rule_can_be_resolved(rule, multi_world.state)
def test_given_location_rule_then_can_be_resolved(self):
for location in multi_world.get_locations(1):
with self.subTest(msg=location.name):
rule = location.access_rule
- self.assertNotIn(MISSING_ITEM, repr(rule))
- self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve location rule for {location} {rule}")
+ self.assert_rule_can_be_resolved(rule, multi_world.state)
diff --git a/worlds/stardew_valley/test/TestLogicSimplification.py b/worlds/stardew_valley/test/TestLogicSimplification.py
deleted file mode 100644
index 3f02643b83dc..000000000000
--- a/worlds/stardew_valley/test/TestLogicSimplification.py
+++ /dev/null
@@ -1,57 +0,0 @@
-import unittest
-from .. import True_
-from ..logic import Received, Has, False_, And, Or
-
-
-class TestSimplification(unittest.TestCase):
- def test_simplify_true_in_and(self):
- rules = {
- "Wood": True_(),
- "Rock": True_(),
- }
- summer = Received("Summer", 0, 1)
- self.assertEqual((Has("Wood", rules) & summer & Has("Rock", rules)).simplify(),
- summer)
-
- def test_simplify_false_in_or(self):
- rules = {
- "Wood": False_(),
- "Rock": False_(),
- }
- summer = Received("Summer", 0, 1)
- self.assertEqual((Has("Wood", rules) | summer | Has("Rock", rules)).simplify(),
- summer)
-
- def test_simplify_and_in_and(self):
- rule = And(And(Received('Summer', 0, 1), Received('Fall', 0, 1)),
- And(Received('Winter', 0, 1), Received('Spring', 0, 1)))
- self.assertEqual(rule.simplify(),
- And(Received('Summer', 0, 1), Received('Fall', 0, 1),
- Received('Winter', 0, 1), Received('Spring', 0, 1)))
-
- def test_simplify_duplicated_and(self):
- rule = And(And(Received('Summer', 0, 1), Received('Fall', 0, 1)),
- And(Received('Summer', 0, 1), Received('Fall', 0, 1)))
- self.assertEqual(rule.simplify(),
- And(Received('Summer', 0, 1), Received('Fall', 0, 1)))
-
- def test_simplify_or_in_or(self):
- rule = Or(Or(Received('Summer', 0, 1), Received('Fall', 0, 1)),
- Or(Received('Winter', 0, 1), Received('Spring', 0, 1)))
- self.assertEqual(rule.simplify(),
- Or(Received('Summer', 0, 1), Received('Fall', 0, 1), Received('Winter', 0, 1),
- Received('Spring', 0, 1)))
-
- def test_simplify_duplicated_or(self):
- rule = And(Or(Received('Summer', 0, 1), Received('Fall', 0, 1)),
- Or(Received('Summer', 0, 1), Received('Fall', 0, 1)))
- self.assertEqual(rule.simplify(),
- Or(Received('Summer', 0, 1), Received('Fall', 0, 1)))
-
- def test_simplify_true_in_or(self):
- rule = Or(True_(), Received('Summer', 0, 1))
- self.assertEqual(rule.simplify(), True_())
-
- def test_simplify_false_in_and(self):
- rule = And(False_(), Received('Summer', 0, 1))
- self.assertEqual(rule.simplify(), False_())
diff --git a/worlds/stardew_valley/test/TestMultiplePlayers.py b/worlds/stardew_valley/test/TestMultiplePlayers.py
new file mode 100644
index 000000000000..39be7d6f7ab2
--- /dev/null
+++ b/worlds/stardew_valley/test/TestMultiplePlayers.py
@@ -0,0 +1,92 @@
+from . import SVTestCase, setup_multiworld
+from .. import True_
+from ..options import FestivalLocations, StartingMoney
+from ..strings.festival_check_names import FestivalCheck
+
+
+def get_access_rule(multiworld, player: int, location_name: str):
+ return multiworld.get_location(location_name, player).access_rule
+
+
+class TestDifferentSettings(SVTestCase):
+
+ def test_different_festival_settings(self):
+ options_no_festivals = {FestivalLocations.internal_name: FestivalLocations.option_disabled}
+ options_easy_festivals = {FestivalLocations.internal_name: FestivalLocations.option_easy}
+ options_hard_festivals = {FestivalLocations.internal_name: FestivalLocations.option_hard}
+
+ multiplayer_options = [options_no_festivals, options_easy_festivals, options_hard_festivals]
+ multiworld = setup_multiworld(multiplayer_options)
+
+ self.check_location_rule(multiworld, 1, FestivalCheck.egg_hunt, False)
+ self.check_location_rule(multiworld, 2, FestivalCheck.egg_hunt, True, False)
+ self.check_location_rule(multiworld, 3, FestivalCheck.egg_hunt, True, True)
+
+ def test_different_money_settings(self):
+ options_no_festivals_unlimited_money = {FestivalLocations.internal_name: FestivalLocations.option_disabled,
+ StartingMoney.internal_name: -1}
+ options_no_festivals_limited_money = {FestivalLocations.internal_name: FestivalLocations.option_disabled,
+ StartingMoney.internal_name: 5000}
+ options_easy_festivals_unlimited_money = {FestivalLocations.internal_name: FestivalLocations.option_easy,
+ StartingMoney.internal_name: -1}
+ options_easy_festivals_limited_money = {FestivalLocations.internal_name: FestivalLocations.option_easy,
+ StartingMoney.internal_name: 5000}
+ options_hard_festivals_unlimited_money = {FestivalLocations.internal_name: FestivalLocations.option_hard,
+ StartingMoney.internal_name: -1}
+ options_hard_festivals_limited_money = {FestivalLocations.internal_name: FestivalLocations.option_hard,
+ StartingMoney.internal_name: 5000}
+
+ multiplayer_options = [options_no_festivals_unlimited_money, options_no_festivals_limited_money,
+ options_easy_festivals_unlimited_money, options_easy_festivals_limited_money,
+ options_hard_festivals_unlimited_money, options_hard_festivals_limited_money]
+ multiworld = setup_multiworld(multiplayer_options)
+
+ self.check_location_rule(multiworld, 1, FestivalCheck.rarecrow_4, False)
+ self.check_location_rule(multiworld, 2, FestivalCheck.rarecrow_4, False)
+
+ self.check_location_rule(multiworld, 3, FestivalCheck.rarecrow_4, True, True)
+ self.check_location_rule(multiworld, 4, FestivalCheck.rarecrow_4, True, False)
+
+ self.check_location_rule(multiworld, 5, FestivalCheck.rarecrow_4, True, True)
+ self.check_location_rule(multiworld, 6, FestivalCheck.rarecrow_4, True, False)
+
+ def test_money_rule_caching(self):
+ options_festivals_limited_money = {FestivalLocations.internal_name: FestivalLocations.option_easy,
+ StartingMoney.internal_name: 5000}
+ options_festivals_limited_money = {FestivalLocations.internal_name: FestivalLocations.option_easy,
+ StartingMoney.internal_name: 5000}
+
+ multiplayer_options = [options_festivals_limited_money, options_festivals_limited_money]
+ multiworld = setup_multiworld(multiplayer_options)
+
+ player_1_rarecrow_2 = get_access_rule(multiworld, 1, FestivalCheck.rarecrow_2)
+ player_1_rarecrow_4 = get_access_rule(multiworld, 1, FestivalCheck.rarecrow_4)
+ player_2_rarecrow_2 = get_access_rule(multiworld, 2, FestivalCheck.rarecrow_2)
+ player_2_rarecrow_4 = get_access_rule(multiworld, 2, FestivalCheck.rarecrow_4)
+
+ with self.subTest("Rules are not cached between players"):
+ self.assertNotEqual(id(player_1_rarecrow_2), id(player_2_rarecrow_2))
+ self.assertNotEqual(id(player_1_rarecrow_4), id(player_2_rarecrow_4))
+
+ with self.subTest("Rules are cached for the same player"):
+ self.assertEqual(id(player_1_rarecrow_2), id(player_1_rarecrow_4))
+ self.assertEqual(id(player_2_rarecrow_2), id(player_2_rarecrow_4))
+
+ def check_location_rule(self, multiworld, player: int, location_name: str, should_exist: bool, should_be_true: bool = False):
+ has = "has" if should_exist else "doesn't have"
+ rule = "without access rule" if should_be_true else f"with access rule"
+ rule_text = f" {rule}" if should_exist else ""
+ with self.subTest(f"Player {player} {has} {location_name}{rule_text}"):
+ locations = multiworld.get_locations(player)
+ locations_names = {location.name for location in locations}
+ if not should_exist:
+ self.assertNotIn(location_name, locations_names)
+ return None
+
+ self.assertIn(location_name, locations_names)
+ access_rule = get_access_rule(multiworld, player, location_name)
+ if should_be_true:
+ self.assertEqual(access_rule, True_())
+ else:
+ self.assertNotEqual(access_rule, True_())
+ return access_rule
diff --git a/worlds/stardew_valley/test/TestOptionFlags.py b/worlds/stardew_valley/test/TestOptionFlags.py
new file mode 100644
index 000000000000..05e52b40c4bd
--- /dev/null
+++ b/worlds/stardew_valley/test/TestOptionFlags.py
@@ -0,0 +1,105 @@
+from . import SVTestBase
+from .. import BuildingProgression
+from ..options import ToolProgression
+
+
+class TestBitFlagsVanilla(SVTestBase):
+ options = {ToolProgression.internal_name: ToolProgression.option_vanilla,
+ BuildingProgression.internal_name: BuildingProgression.option_vanilla}
+
+ def test_options_are_not_detected_as_progressive(self):
+ world_options = self.world.options
+ tool_progressive = world_options.tool_progression & ToolProgression.option_progressive
+ building_progressive = world_options.building_progression & BuildingProgression.option_progressive
+ self.assertFalse(tool_progressive)
+ self.assertFalse(building_progressive)
+
+ def test_tools_and_buildings_not_in_pool(self):
+ item_names = [item.name for item in self.multiworld.itempool]
+ self.assertNotIn("Progressive Coop", item_names)
+ self.assertNotIn("Progressive Pickaxe", item_names)
+
+
+class TestBitFlagsVanillaCheap(SVTestBase):
+ options = {ToolProgression.internal_name: ToolProgression.option_vanilla_cheap,
+ BuildingProgression.internal_name: BuildingProgression.option_vanilla_cheap}
+
+ def test_options_are_not_detected_as_progressive(self):
+ world_options = self.world.options
+ tool_progressive = world_options.tool_progression & ToolProgression.option_progressive
+ building_progressive = world_options.building_progression & BuildingProgression.option_progressive
+ self.assertFalse(tool_progressive)
+ self.assertFalse(building_progressive)
+
+ def test_tools_and_buildings_not_in_pool(self):
+ item_names = [item.name for item in self.multiworld.itempool]
+ self.assertNotIn("Progressive Coop", item_names)
+ self.assertNotIn("Progressive Pickaxe", item_names)
+
+
+class TestBitFlagsVanillaVeryCheap(SVTestBase):
+ options = {ToolProgression.internal_name: ToolProgression.option_vanilla_very_cheap,
+ BuildingProgression.internal_name: BuildingProgression.option_vanilla_very_cheap}
+
+ def test_options_are_not_detected_as_progressive(self):
+ world_options = self.world.options
+ tool_progressive = world_options.tool_progression & ToolProgression.option_progressive
+ building_progressive = world_options.building_progression & BuildingProgression.option_progressive
+ self.assertFalse(tool_progressive)
+ self.assertFalse(building_progressive)
+
+ def test_tools_and_buildings_not_in_pool(self):
+ item_names = [item.name for item in self.multiworld.itempool]
+ self.assertNotIn("Progressive Coop", item_names)
+ self.assertNotIn("Progressive Pickaxe", item_names)
+
+
+class TestBitFlagsProgressive(SVTestBase):
+ options = {ToolProgression.internal_name: ToolProgression.option_progressive,
+ BuildingProgression.internal_name: BuildingProgression.option_progressive}
+
+ def test_options_are_detected_as_progressive(self):
+ world_options = self.world.options
+ tool_progressive = world_options.tool_progression & ToolProgression.option_progressive
+ building_progressive = world_options.building_progression & BuildingProgression.option_progressive
+ self.assertTrue(tool_progressive)
+ self.assertTrue(building_progressive)
+
+ def test_tools_and_buildings_in_pool(self):
+ item_names = [item.name for item in self.multiworld.itempool]
+ self.assertIn("Progressive Coop", item_names)
+ self.assertIn("Progressive Pickaxe", item_names)
+
+
+class TestBitFlagsProgressiveCheap(SVTestBase):
+ options = {ToolProgression.internal_name: ToolProgression.option_progressive_cheap,
+ BuildingProgression.internal_name: BuildingProgression.option_progressive_cheap}
+
+ def test_options_are_detected_as_progressive(self):
+ world_options = self.world.options
+ tool_progressive = world_options.tool_progression & ToolProgression.option_progressive
+ building_progressive = world_options.building_progression & BuildingProgression.option_progressive
+ self.assertTrue(tool_progressive)
+ self.assertTrue(building_progressive)
+
+ def test_tools_and_buildings_in_pool(self):
+ item_names = [item.name for item in self.multiworld.itempool]
+ self.assertIn("Progressive Coop", item_names)
+ self.assertIn("Progressive Pickaxe", item_names)
+
+
+class TestBitFlagsProgressiveVeryCheap(SVTestBase):
+ options = {ToolProgression.internal_name: ToolProgression.option_progressive_very_cheap,
+ BuildingProgression.internal_name: BuildingProgression.option_progressive_very_cheap}
+
+ def test_options_are_detected_as_progressive(self):
+ world_options = self.world.options
+ tool_progressive = world_options.tool_progression & ToolProgression.option_progressive
+ building_progressive = world_options.building_progression & BuildingProgression.option_progressive
+ self.assertTrue(tool_progressive)
+ self.assertTrue(building_progressive)
+
+ def test_tools_and_buildings_in_pool(self):
+ item_names = [item.name for item in self.multiworld.itempool]
+ self.assertIn("Progressive Coop", item_names)
+ self.assertIn("Progressive Pickaxe", item_names)
diff --git a/worlds/stardew_valley/test/TestOptions.py b/worlds/stardew_valley/test/TestOptions.py
index ccffc2848a80..d13f9b8a051a 100644
--- a/worlds/stardew_valley/test/TestOptions.py
+++ b/worlds/stardew_valley/test/TestOptions.py
@@ -1,12 +1,10 @@
import itertools
-import unittest
-from random import random
-from typing import Dict
-from BaseClasses import ItemClassification, MultiWorld
from Options import NamedRange
-from . import setup_solo_multiworld, SVTestBase, SVTestCase, allsanity_options_without_mods, allsanity_options_with_mods
-from .. import StardewItem, items_by_group, Group, StardewValleyWorld
+from . import setup_solo_multiworld, SVTestCase, allsanity_options_without_mods, allsanity_options_with_mods
+from .assertion import WorldAssertMixin
+from .long.option_names import all_option_choices
+from .. import items_by_group, Group, StardewValleyWorld
from ..locations import locations_by_tag, LocationTags, location_table
from ..options import ExcludeGingerIsland, ToolProgression, Goal, SeasonRandomization, TrapItems, SpecialOrderLocations, ArcadeMachineLocations
from ..strings.goal_names import Goal as GoalName
@@ -18,60 +16,26 @@
TOOLS = {"Hoe", "Pickaxe", "Axe", "Watering Can", "Trash Can", "Fishing Rod"}
-def assert_can_win(tester: unittest.TestCase, multiworld: MultiWorld):
- for item in multiworld.get_items():
- multiworld.state.collect(item)
-
- tester.assertTrue(multiworld.find_item("Victory", 1).can_reach(multiworld.state))
-
-
-def basic_checks(tester: unittest.TestCase, multiworld: MultiWorld):
- tester.assertIn(StardewItem("Victory", ItemClassification.progression, None, 1), multiworld.get_items())
- assert_can_win(tester, multiworld)
- non_event_locations = [location for location in multiworld.get_locations() if not location.event]
- tester.assertEqual(len(multiworld.itempool), len(non_event_locations))
-
-
-def check_no_ginger_island(tester: unittest.TestCase, multiworld: MultiWorld):
- ginger_island_items = [item_data.name for item_data in items_by_group[Group.GINGER_ISLAND]]
- ginger_island_locations = [location_data.name for location_data in locations_by_tag[LocationTags.GINGER_ISLAND]]
- for item in multiworld.get_items():
- tester.assertNotIn(item.name, ginger_island_items)
- for location in multiworld.get_locations():
- tester.assertNotIn(location.name, ginger_island_locations)
-
-
-def get_option_choices(option) -> Dict[str, int]:
- if issubclass(option, NamedRange):
- return option.special_range_names
- elif option.options:
- return option.options
- return {}
-
-
-class TestGenerateDynamicOptions(SVTestCase):
+class TestGenerateDynamicOptions(WorldAssertMixin, SVTestCase):
def test_given_special_range_when_generate_then_basic_checks(self):
options = StardewValleyWorld.options_dataclass.type_hints
for option_name, option in options.items():
- if not isinstance(option, NamedRange):
+ if not issubclass(option, NamedRange):
continue
for value in option.special_range_names:
- with self.subTest(f"{option_name}: {value}"):
- choices = {option_name: option.special_range_names[value]}
- multiworld = setup_solo_multiworld(choices)
- basic_checks(self, multiworld)
+ world_options = {option_name: option.special_range_names[value]}
+ with self.solo_world_sub_test(f"{option_name}: {value}", world_options, dirty_state=True) as (multiworld, _):
+ self.assert_basic_checks(multiworld)
def test_given_choice_when_generate_then_basic_checks(self):
- seed = int(random() * pow(10, 18) - 1)
options = StardewValleyWorld.options_dataclass.type_hints
for option_name, option in options.items():
if not option.options:
continue
for value in option.options:
- with self.subTest(f"{option_name}: {value} [Seed: {seed}]"):
- world_options = {option_name: option.options[value]}
- multiworld = setup_solo_multiworld(world_options, seed)
- basic_checks(self, multiworld)
+ world_options = {option_name: option.options[value]}
+ with self.solo_world_sub_test(f"{option_name}: {value}", world_options, dirty_state=True) as (multiworld, _):
+ self.assert_basic_checks(multiworld)
class TestGoal(SVTestCase):
@@ -84,9 +48,8 @@ def test_given_goal_when_generate_then_victory_is_in_correct_location(self):
("complete_collection", GoalName.complete_museum),
("full_house", GoalName.full_house),
("perfection", GoalName.perfection)]:
- with self.subTest(msg=f"Goal: {goal}, Location: {location}"):
- world_options = {Goal.internal_name: Goal.options[goal]}
- multi_world = setup_solo_multiworld(world_options)
+ world_options = {Goal.internal_name: Goal.options[goal]}
+ with self.solo_world_sub_test(f"Goal: {goal}, Location: {location}", world_options) as (multi_world, _):
victory = multi_world.find_item("Victory", 1)
self.assertEqual(victory.name, location)
@@ -148,54 +111,45 @@ def test_given_progressive_when_generate_then_tool_upgrades_are_locations(self):
self.assertIn("Purchase Iridium Rod", locations)
-class TestGenerateAllOptionsWithExcludeGingerIsland(SVTestCase):
- def test_given_special_range_when_generate_exclude_ginger_island(self):
- options = StardewValleyWorld.options_dataclass.type_hints
- for option_name, option in options.items():
- if not isinstance(option, NamedRange) or option_name == ExcludeGingerIsland.internal_name:
- continue
- for value in option.special_range_names:
- with self.subTest(f"{option_name}: {value}"):
- multiworld = setup_solo_multiworld(
- {ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true,
- option_name: option.special_range_names[value]})
- check_no_ginger_island(self, multiworld)
+class TestGenerateAllOptionsWithExcludeGingerIsland(WorldAssertMixin, SVTestCase):
def test_given_choice_when_generate_exclude_ginger_island(self):
- seed = int(random() * pow(10, 18) - 1)
- options = StardewValleyWorld.options_dataclass.type_hints
- for option_name, option in options.items():
- if not option.options or option_name == ExcludeGingerIsland.internal_name:
+ for option, option_choice in all_option_choices:
+ if option is ExcludeGingerIsland:
continue
- for value in option.options:
- with self.subTest(f"{option_name}: {value} [Seed: {seed}]"):
- multiworld = setup_solo_multiworld(
- {ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true,
- option_name: option.options[value]}, seed)
- stardew_world: StardewValleyWorld = multiworld.worlds[self.player]
- if stardew_world.options.exclude_ginger_island != ExcludeGingerIsland.option_true:
- continue
- basic_checks(self, multiworld)
- check_no_ginger_island(self, multiworld)
+
+ world_options = {
+ ExcludeGingerIsland: ExcludeGingerIsland.option_true,
+ option: option_choice
+ }
+
+ with self.solo_world_sub_test(f"{option.internal_name}: {option_choice}", world_options, dirty_state=True) as (multiworld, stardew_world):
+
+ # Some options, like goals, will force Ginger island back in the game. We want to skip testing those.
+ if stardew_world.options.exclude_ginger_island != ExcludeGingerIsland.option_true:
+ continue
+
+ self.assert_basic_checks(multiworld)
+ self.assert_no_ginger_island_content(multiworld)
def test_given_island_related_goal_then_override_exclude_ginger_island(self):
- island_goals = [value for value in Goal.options if value in ["walnut_hunter", "perfection"]]
- island_option = ExcludeGingerIsland
- for goal in island_goals:
- for value in island_option.options:
- with self.subTest(f"Goal: {goal}, {island_option.internal_name}: {value}"):
- multiworld = setup_solo_multiworld(
- {Goal.internal_name: Goal.options[goal],
- island_option.internal_name: island_option.options[value]})
- stardew_world: StardewValleyWorld = multiworld.worlds[self.player]
- self.assertEqual(stardew_world.options.exclude_ginger_island, island_option.option_false)
- basic_checks(self, multiworld)
+ island_goals = ["greatest_walnut_hunter", "perfection"]
+ for goal, exclude_island in itertools.product(island_goals, ExcludeGingerIsland.options):
+ world_options = {
+ Goal: goal,
+ ExcludeGingerIsland: exclude_island
+ }
+
+ with self.solo_world_sub_test(f"Goal: {goal}, {ExcludeGingerIsland.internal_name}: {exclude_island}", world_options, dirty_state=True) \
+ as (multiworld, stardew_world):
+ self.assertEqual(stardew_world.options.exclude_ginger_island, ExcludeGingerIsland.option_false)
+ self.assert_basic_checks(multiworld)
class TestTraps(SVTestCase):
def test_given_no_traps_when_generate_then_no_trap_in_pool(self):
- world_options = allsanity_options_without_mods()
- world_options.update({TrapItems.internal_name: TrapItems.option_no_traps})
+ world_options = allsanity_options_without_mods().copy()
+ world_options[TrapItems.internal_name] = TrapItems.option_no_traps
multi_world = setup_solo_multiworld(world_options)
trap_items = [item_data.name for item_data in items_by_group[Group.TRAP]]
diff --git a/worlds/stardew_valley/test/TestOptionsPairs.py b/worlds/stardew_valley/test/TestOptionsPairs.py
new file mode 100644
index 000000000000..9109c39562ee
--- /dev/null
+++ b/worlds/stardew_valley/test/TestOptionsPairs.py
@@ -0,0 +1,56 @@
+from . import SVTestBase
+from .assertion import WorldAssertMixin
+from .. import options
+from ..options import Goal, QuestLocations
+
+
+class TestCrypticNoteNoQuests(WorldAssertMixin, SVTestBase):
+ options = {
+ Goal.internal_name: Goal.option_cryptic_note,
+ QuestLocations.internal_name: "none"
+ }
+
+ def test_given_option_pair_then_basic_checks(self):
+ self.assert_basic_checks(self.multiworld)
+
+
+class TestCompleteCollectionNoQuests(WorldAssertMixin, SVTestBase):
+ options = {
+ Goal.internal_name: Goal.option_complete_collection,
+ QuestLocations.internal_name: "none"
+ }
+
+ def test_given_option_pair_then_basic_checks(self):
+ self.assert_basic_checks(self.multiworld)
+
+
+class TestProtectorOfTheValleyNoQuests(WorldAssertMixin, SVTestBase):
+ options = {
+ Goal.internal_name: Goal.option_protector_of_the_valley,
+ QuestLocations.internal_name: "none"
+ }
+
+ def test_given_option_pair_then_basic_checks(self):
+ self.assert_basic_checks(self.multiworld)
+
+
+class TestCraftMasterNoQuests(WorldAssertMixin, SVTestBase):
+ options = {
+ Goal.internal_name: Goal.option_craft_master,
+ QuestLocations.internal_name: "none"
+ }
+
+ def test_given_option_pair_then_basic_checks(self):
+ self.assert_basic_checks(self.multiworld)
+
+
+class TestCraftMasterNoSpecialOrder(WorldAssertMixin, SVTestBase):
+ options = {
+ options.Goal.internal_name: Goal.option_craft_master,
+ options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_disabled,
+ options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true,
+ options.Craftsanity.internal_name: options.Craftsanity.option_none
+ }
+
+ def test_given_option_pair_then_basic_checks(self):
+ self.assert_basic_checks(self.multiworld)
diff --git a/worlds/stardew_valley/test/TestPresets.py b/worlds/stardew_valley/test/TestPresets.py
new file mode 100644
index 000000000000..2bb1c7fbaeaf
--- /dev/null
+++ b/worlds/stardew_valley/test/TestPresets.py
@@ -0,0 +1,21 @@
+import builtins
+import inspect
+
+from Options import PerGameCommonOptions, OptionSet
+from . import SVTestCase
+from .. import sv_options_presets, StardewValleyOptions
+
+
+class TestPresets(SVTestCase):
+ def test_all_presets_explicitly_set_all_options(self):
+ all_option_names = {option_key for option_key in StardewValleyOptions.type_hints}
+ omitted_option_names = {option_key for option_key in PerGameCommonOptions.type_hints}
+ mandatory_option_names = {option_key for option_key in all_option_names
+ if option_key not in omitted_option_names and
+ not issubclass(StardewValleyOptions.type_hints[option_key], OptionSet)}
+
+ for preset_name in sv_options_presets:
+ with self.subTest(f"{preset_name}"):
+ for option_name in mandatory_option_names:
+ with self.subTest(f"{preset_name} -> {option_name}"):
+ self.assertIn(option_name, sv_options_presets[preset_name])
\ No newline at end of file
diff --git a/worlds/stardew_valley/test/TestRegions.py b/worlds/stardew_valley/test/TestRegions.py
index 7ebbcece5c2c..0137bab9148b 100644
--- a/worlds/stardew_valley/test/TestRegions.py
+++ b/worlds/stardew_valley/test/TestRegions.py
@@ -1,11 +1,13 @@
import random
-import sys
import unittest
+from typing import Set
-from . import SVTestCase, setup_solo_multiworld
-from .. import options, StardewValleyWorld, StardewValleyOptions
+from BaseClasses import get_seed
+from . import SVTestCase, complete_options_with_default
from ..options import EntranceRandomization, ExcludeGingerIsland
-from ..regions import vanilla_regions, vanilla_connections, randomize_connections, RandomizationFlag
+from ..regions import vanilla_regions, vanilla_connections, randomize_connections, RandomizationFlag, create_final_connections_and_regions
+from ..strings.entrance_names import Entrance as EntranceName
+from ..strings.region_names import Region as RegionName
connections_by_name = {connection.name for connection in vanilla_connections}
regions_by_name = {region.name for region in vanilla_regions}
@@ -26,78 +28,118 @@ def test_connection_lead_somewhere(self):
f"{connection.name} is leading to {connection.destination} but it does not exist.")
-class TestEntranceRando(unittest.TestCase):
+def explore_connections_tree_up_to_blockers(blocked_entrances: Set[str], connections_by_name, regions_by_name):
+ explored_entrances = set()
+ explored_regions = set()
+ entrances_to_explore = set()
+ current_node_name = "Menu"
+ current_node = regions_by_name[current_node_name]
+ entrances_to_explore.update(current_node.exits)
+ while entrances_to_explore:
+ current_entrance_name = entrances_to_explore.pop()
+ current_entrance = connections_by_name[current_entrance_name]
+ current_node_name = current_entrance.destination
+
+ explored_entrances.add(current_entrance_name)
+ explored_regions.add(current_node_name)
+
+ if current_entrance_name in blocked_entrances:
+ continue
+
+ current_node = regions_by_name[current_node_name]
+ entrances_to_explore.update({entrance for entrance in current_node.exits if entrance not in explored_entrances})
+ return explored_regions
+
+
+class TestEntranceRando(SVTestCase):
def test_entrance_randomization(self):
- for option, flag in [(options.EntranceRandomization.option_pelican_town, RandomizationFlag.PELICAN_TOWN),
- (options.EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION),
- (options.EntranceRandomization.option_buildings, RandomizationFlag.BUILDINGS)]:
- # option = options.EntranceRandomization.option_buildings
- # flag = RandomizationFlag.BUILDINGS
- # for i in range(0, 100000):
- seed = random.randrange(sys.maxsize)
+ for option, flag in [(EntranceRandomization.option_pelican_town, RandomizationFlag.PELICAN_TOWN),
+ (EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION),
+ (EntranceRandomization.option_buildings, RandomizationFlag.BUILDINGS)]:
+ sv_options = complete_options_with_default({
+ EntranceRandomization.internal_name: option,
+ ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_false
+ })
+ seed = get_seed()
+ rand = random.Random(seed)
with self.subTest(flag=flag, msg=f"Seed: {seed}"):
- rand = random.Random(seed)
- world_options = {EntranceRandomization.internal_name: option,
- ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false}
- multiworld = setup_solo_multiworld(world_options)
- regions_by_name = {region.name: region for region in vanilla_regions}
-
- _, randomized_connections = randomize_connections(rand, multiworld.worlds[1].options, regions_by_name)
+ entrances, regions = create_final_connections_and_regions(sv_options)
+ _, randomized_connections = randomize_connections(rand, sv_options, regions, entrances)
for connection in vanilla_connections:
if flag in connection.flag:
connection_in_randomized = connection.name in randomized_connections
reverse_in_randomized = connection.reverse in randomized_connections
- self.assertTrue(connection_in_randomized,
- f"Connection {connection.name} should be randomized but it is not in the output. Seed = {seed}")
- self.assertTrue(reverse_in_randomized,
- f"Connection {connection.reverse} should be randomized but it is not in the output. Seed = {seed}")
+ self.assertTrue(connection_in_randomized, f"Connection {connection.name} should be randomized but it is not in the output.")
+ self.assertTrue(reverse_in_randomized, f"Connection {connection.reverse} should be randomized but it is not in the output.")
self.assertEqual(len(set(randomized_connections.values())), len(randomized_connections.values()),
- f"Connections are duplicated in randomization. Seed = {seed}")
+ f"Connections are duplicated in randomization.")
def test_entrance_randomization_without_island(self):
- for option, flag in [(options.EntranceRandomization.option_pelican_town, RandomizationFlag.PELICAN_TOWN),
- (options.EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION),
- (options.EntranceRandomization.option_buildings, RandomizationFlag.BUILDINGS)]:
- with self.subTest(option=option, flag=flag):
- seed = random.randrange(sys.maxsize)
- rand = random.Random(seed)
- world_options = {EntranceRandomization.internal_name: option,
- ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true}
- multiworld = setup_solo_multiworld(world_options)
- regions_by_name = {region.name: region for region in vanilla_regions}
-
- _, randomized_connections = randomize_connections(rand, multiworld.worlds[1].options, regions_by_name)
+ for option, flag in [(EntranceRandomization.option_pelican_town, RandomizationFlag.PELICAN_TOWN),
+ (EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION),
+ (EntranceRandomization.option_buildings, RandomizationFlag.BUILDINGS)]:
+
+ sv_options = complete_options_with_default({
+ EntranceRandomization.internal_name: option,
+ ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true
+ })
+ seed = get_seed()
+ rand = random.Random(seed)
+ with self.subTest(option=option, flag=flag, seed=seed):
+ entrances, regions = create_final_connections_and_regions(sv_options)
+ _, randomized_connections = randomize_connections(rand, sv_options, regions, entrances)
for connection in vanilla_connections:
if flag in connection.flag:
if RandomizationFlag.GINGER_ISLAND in connection.flag:
self.assertNotIn(connection.name, randomized_connections,
- f"Connection {connection.name} should not be randomized but it is in the output. Seed = {seed}")
+ f"Connection {connection.name} should not be randomized but it is in the output.")
self.assertNotIn(connection.reverse, randomized_connections,
- f"Connection {connection.reverse} should not be randomized but it is in the output. Seed = {seed}")
+ f"Connection {connection.reverse} should not be randomized but it is in the output.")
else:
self.assertIn(connection.name, randomized_connections,
- f"Connection {connection.name} should be randomized but it is not in the output. Seed = {seed}")
+ f"Connection {connection.name} should be randomized but it is not in the output.")
self.assertIn(connection.reverse, randomized_connections,
- f"Connection {connection.reverse} should be randomized but it is not in the output. Seed = {seed}")
+ f"Connection {connection.reverse} should be randomized but it is not in the output.")
self.assertEqual(len(set(randomized_connections.values())), len(randomized_connections.values()),
- f"Connections are duplicated in randomization. Seed = {seed}")
+ f"Connections are duplicated in randomization.")
+
+ def test_cannot_put_island_access_on_island(self):
+ sv_options = complete_options_with_default({
+ EntranceRandomization.internal_name: EntranceRandomization.option_buildings,
+ ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_false
+ })
+
+ for i in range(0, 100 if self.skip_long_tests else 10000):
+ seed = get_seed()
+ rand = random.Random(seed)
+ with self.subTest(msg=f"Seed: {seed}"):
+ entrances, regions = create_final_connections_and_regions(sv_options)
+ randomized_connections, randomized_data = randomize_connections(rand, sv_options, regions, entrances)
+ connections_by_name = {connection.name: connection for connection in randomized_connections}
+
+ blocked_entrances = {EntranceName.use_island_obelisk, EntranceName.boat_to_ginger_island}
+ required_regions = {RegionName.wizard_tower, RegionName.boat_tunnel}
+ self.assert_can_reach_any_region_before_blockers(required_regions, blocked_entrances, connections_by_name, regions)
+
+ def assert_can_reach_any_region_before_blockers(self, required_regions, blocked_entrances, connections_by_name, regions_by_name):
+ explored_regions = explore_connections_tree_up_to_blockers(blocked_entrances, connections_by_name, regions_by_name)
+ self.assertTrue(any(region in explored_regions for region in required_regions))
class TestEntranceClassifications(SVTestCase):
def test_non_progression_are_all_accessible_with_empty_inventory(self):
- for option, flag in [(options.EntranceRandomization.option_pelican_town, RandomizationFlag.PELICAN_TOWN),
- (options.EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION)]:
- seed = random.randrange(sys.maxsize)
- with self.subTest(flag=flag, msg=f"Seed: {seed}"):
- multiworld_options = {options.EntranceRandomization.internal_name: option}
- multiworld = setup_solo_multiworld(multiworld_options, seed)
- sv_world: StardewValleyWorld = multiworld.worlds[1]
+ for option, flag in [(EntranceRandomization.option_pelican_town, RandomizationFlag.PELICAN_TOWN),
+ (EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION)]:
+ world_options = {
+ EntranceRandomization.internal_name: option
+ }
+ with self.solo_world_sub_test(world_options=world_options, flag=flag) as (multiworld, sv_world):
ap_entrances = {entrance.name: entrance for entrance in multiworld.get_entrances()}
for randomized_entrance in sv_world.randomized_entrances:
if randomized_entrance in ap_entrances:
@@ -106,3 +148,16 @@ def test_non_progression_are_all_accessible_with_empty_inventory(self):
if sv_world.randomized_entrances[randomized_entrance] in ap_entrances:
ap_entrance_destination = multiworld.get_entrance(sv_world.randomized_entrances[randomized_entrance], 1)
self.assertTrue(ap_entrance_destination.access_rule(multiworld.state))
+
+ def test_no_ginger_island_entrances_when_excluded(self):
+ world_options = {
+ EntranceRandomization.internal_name: EntranceRandomization.option_disabled,
+ ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true
+ }
+ with self.solo_world_sub_test(world_options=world_options) as (multiworld, _):
+ ap_entrances = {entrance.name: entrance for entrance in multiworld.get_entrances()}
+ entrance_data_by_name = {entrance.name: entrance for entrance in vanilla_connections}
+ for entrance_name in ap_entrances:
+ entrance_data = entrance_data_by_name[entrance_name]
+ with self.subTest(f"{entrance_name}: {entrance_data.flag}"):
+ self.assertFalse(entrance_data.flag & RandomizationFlag.GINGER_ISLAND)
diff --git a/worlds/stardew_valley/test/TestRules.py b/worlds/stardew_valley/test/TestRules.py
index 0749b1a8f153..787e0ce39c3e 100644
--- a/worlds/stardew_valley/test/TestRules.py
+++ b/worlds/stardew_valley/test/TestRules.py
@@ -1,167 +1,167 @@
from collections import Counter
from . import SVTestBase
-from .. import options
+from .. import options, HasProgressionPercent
+from ..data.craftable_data import all_crafting_recipes_by_name
from ..locations import locations_by_tag, LocationTags, location_table
-from ..strings.animal_names import Animal
-from ..strings.animal_product_names import AnimalProduct
-from ..strings.artisan_good_names import ArtisanGood
-from ..strings.crop_names import Vegetable
+from ..options import ToolProgression, BuildingProgression, ExcludeGingerIsland, Chefsanity, Craftsanity, Shipsanity, SeasonRandomization, Friendsanity, \
+ FriendsanityHeartSize, BundleRandomization, SkillProgression
from ..strings.entrance_names import Entrance
-from ..strings.food_names import Meal
-from ..strings.ingredient_names import Ingredient
-from ..strings.machine_names import Machine
from ..strings.region_names import Region
-from ..strings.season_names import Season
-from ..strings.seed_names import Seed
+from ..strings.tool_names import Tool, ToolMaterial
class TestProgressiveToolsLogic(SVTestBase):
options = {
- options.ToolProgression.internal_name: options.ToolProgression.option_progressive,
- options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized,
+ ToolProgression.internal_name: ToolProgression.option_progressive,
+ SeasonRandomization.internal_name: SeasonRandomization.option_randomized,
}
- def setUp(self):
- super().setUp()
+ def test_sturgeon(self):
self.multiworld.state.prog_items = {1: Counter()}
- def test_sturgeon(self):
- self.assertFalse(self.world.logic.has("Sturgeon")(self.multiworld.state))
+ sturgeon_rule = self.world.logic.has("Sturgeon")
+ self.assert_rule_false(sturgeon_rule, self.multiworld.state)
summer = self.world.create_item("Summer")
- self.multiworld.state.collect(summer, event=True)
- self.assertFalse(self.world.logic.has("Sturgeon")(self.multiworld.state))
+ self.multiworld.state.collect(summer, event=False)
+ self.assert_rule_false(sturgeon_rule, self.multiworld.state)
fishing_rod = self.world.create_item("Progressive Fishing Rod")
- self.multiworld.state.collect(fishing_rod, event=True)
- self.multiworld.state.collect(fishing_rod, event=True)
- self.assertFalse(self.world.logic.has("Sturgeon")(self.multiworld.state))
+ self.multiworld.state.collect(fishing_rod, event=False)
+ self.multiworld.state.collect(fishing_rod, event=False)
+ self.assert_rule_false(sturgeon_rule, self.multiworld.state)
fishing_level = self.world.create_item("Fishing Level")
- self.multiworld.state.collect(fishing_level, event=True)
- self.assertFalse(self.world.logic.has("Sturgeon")(self.multiworld.state))
+ self.multiworld.state.collect(fishing_level, event=False)
+ self.assert_rule_false(sturgeon_rule, self.multiworld.state)
- self.multiworld.state.collect(fishing_level, event=True)
- self.multiworld.state.collect(fishing_level, event=True)
- self.multiworld.state.collect(fishing_level, event=True)
- self.multiworld.state.collect(fishing_level, event=True)
- self.multiworld.state.collect(fishing_level, event=True)
- self.assertTrue(self.world.logic.has("Sturgeon")(self.multiworld.state))
+ self.multiworld.state.collect(fishing_level, event=False)
+ self.multiworld.state.collect(fishing_level, event=False)
+ self.multiworld.state.collect(fishing_level, event=False)
+ self.multiworld.state.collect(fishing_level, event=False)
+ self.multiworld.state.collect(fishing_level, event=False)
+ self.assert_rule_true(sturgeon_rule, self.multiworld.state)
self.remove(summer)
- self.assertFalse(self.world.logic.has("Sturgeon")(self.multiworld.state))
+ self.assert_rule_false(sturgeon_rule, self.multiworld.state)
winter = self.world.create_item("Winter")
- self.multiworld.state.collect(winter, event=True)
- self.assertTrue(self.world.logic.has("Sturgeon")(self.multiworld.state))
+ self.multiworld.state.collect(winter, event=False)
+ self.assert_rule_true(sturgeon_rule, self.multiworld.state)
self.remove(fishing_rod)
- self.assertFalse(self.world.logic.has("Sturgeon")(self.multiworld.state))
+ self.assert_rule_false(sturgeon_rule, self.multiworld.state)
def test_old_master_cannoli(self):
- self.multiworld.state.collect(self.world.create_item("Progressive Axe"), event=True)
- self.multiworld.state.collect(self.world.create_item("Progressive Axe"), event=True)
- self.multiworld.state.collect(self.world.create_item("Summer"), event=True)
+ self.multiworld.state.prog_items = {1: Counter()}
+
+ self.multiworld.state.collect(self.world.create_item("Progressive Axe"), event=False)
+ self.multiworld.state.collect(self.world.create_item("Progressive Axe"), event=False)
+ self.multiworld.state.collect(self.world.create_item("Summer"), event=False)
+ self.collect_lots_of_money()
- self.assertFalse(self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state))
+ rule = self.world.logic.region.can_reach_location("Old Master Cannoli")
+ self.assert_rule_false(rule, self.multiworld.state)
fall = self.world.create_item("Fall")
- self.multiworld.state.collect(fall, event=True)
- self.assertFalse(self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state))
+ self.multiworld.state.collect(fall, event=False)
+ self.assert_rule_false(rule, self.multiworld.state)
tuesday = self.world.create_item("Traveling Merchant: Tuesday")
- self.multiworld.state.collect(tuesday, event=True)
- self.assertFalse(self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state))
+ self.multiworld.state.collect(tuesday, event=False)
+ self.assert_rule_false(rule, self.multiworld.state)
rare_seed = self.world.create_item("Rare Seed")
- self.multiworld.state.collect(rare_seed, event=True)
- self.assertTrue(self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state))
+ self.multiworld.state.collect(rare_seed, event=False)
+ self.assert_rule_true(rule, self.multiworld.state)
self.remove(fall)
- self.assertFalse(self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state))
+ self.assert_rule_false(rule, self.multiworld.state)
self.remove(tuesday)
green_house = self.world.create_item("Greenhouse")
- self.multiworld.state.collect(green_house, event=True)
- self.assertFalse(self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state))
+ self.multiworld.state.collect(green_house, event=False)
+ self.assert_rule_false(rule, self.multiworld.state)
friday = self.world.create_item("Traveling Merchant: Friday")
- self.multiworld.state.collect(friday, event=True)
+ self.multiworld.state.collect(friday, event=False)
self.assertTrue(self.multiworld.get_location("Old Master Cannoli", 1).access_rule(self.multiworld.state))
self.remove(green_house)
- self.assertFalse(self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state))
+ self.assert_rule_false(rule, self.multiworld.state)
self.remove(friday)
class TestBundlesLogic(SVTestBase):
options = {
+ BundleRandomization.internal_name: BundleRandomization.option_vanilla
}
def test_vault_2500g_bundle(self):
- self.assertTrue(self.world.logic.can_reach_location("2,500g Bundle")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach_location("2,500g Bundle")(self.multiworld.state))
+
+ self.collect_lots_of_money()
+ self.assertTrue(self.world.logic.region.can_reach_location("2,500g Bundle")(self.multiworld.state))
class TestBuildingLogic(SVTestBase):
options = {
- options.BuildingProgression.internal_name: options.BuildingProgression.option_progressive_early_shipping_bin
+ BuildingProgression.internal_name: BuildingProgression.option_progressive
}
def test_coop_blueprint(self):
- self.assertFalse(self.world.logic.can_reach_location("Coop Blueprint")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach_location("Coop Blueprint")(self.multiworld.state))
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.assertTrue(self.world.logic.can_reach_location("Coop Blueprint")(self.multiworld.state))
+ self.collect_lots_of_money()
+ self.assertTrue(self.world.logic.region.can_reach_location("Coop Blueprint")(self.multiworld.state))
def test_big_coop_blueprint(self):
- self.assertFalse(self.world.logic.can_reach_location("Big Coop Blueprint")(self.multiworld.state),
- f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}")
+ big_coop_blueprint_rule = self.world.logic.region.can_reach_location("Big Coop Blueprint")
+ self.assertFalse(big_coop_blueprint_rule(self.multiworld.state),
+ f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}")
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.assertFalse(self.world.logic.can_reach_location("Big Coop Blueprint")(self.multiworld.state),
- f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}")
+ self.collect_lots_of_money()
+ self.assertFalse(big_coop_blueprint_rule(self.multiworld.state),
+ f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}")
- self.multiworld.state.collect(self.world.create_item("Progressive Coop"), event=True)
- self.assertTrue(self.world.logic.can_reach_location("Big Coop Blueprint")(self.multiworld.state),
- f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}")
+ self.multiworld.state.collect(self.world.create_item("Can Construct Buildings"), event=True)
+ self.assertFalse(big_coop_blueprint_rule(self.multiworld.state),
+ f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}")
+
+ self.multiworld.state.collect(self.world.create_item("Progressive Coop"), event=False)
+ self.assertTrue(big_coop_blueprint_rule(self.multiworld.state),
+ f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}")
def test_deluxe_coop_blueprint(self):
- self.assertFalse(self.world.logic.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state))
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.assertFalse(self.world.logic.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state))
+ self.collect_lots_of_money()
+ self.multiworld.state.collect(self.world.create_item("Can Construct Buildings"), event=True)
+ self.assertFalse(self.world.logic.region.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state))
self.multiworld.state.collect(self.world.create_item("Progressive Coop"), event=True)
- self.assertFalse(self.world.logic.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state))
self.multiworld.state.collect(self.world.create_item("Progressive Coop"), event=True)
- self.assertTrue(self.world.logic.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state))
+ self.assertTrue(self.world.logic.region.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state))
def test_big_shed_blueprint(self):
- self.assertFalse(self.world.logic.can_reach_location("Big Shed Blueprint")(self.multiworld.state),
- f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}")
-
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
- self.assertFalse(self.world.logic.can_reach_location("Big Shed Blueprint")(self.multiworld.state),
- f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}")
+ big_shed_rule = self.world.logic.region.can_reach_location("Big Shed Blueprint")
+ self.assertFalse(big_shed_rule(self.multiworld.state),
+ f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}")
+
+ self.collect_lots_of_money()
+ self.assertFalse(big_shed_rule(self.multiworld.state),
+ f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}")
+
+ self.multiworld.state.collect(self.world.create_item("Can Construct Buildings"), event=True)
+ self.assertFalse(big_shed_rule(self.multiworld.state),
+ f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}")
self.multiworld.state.collect(self.world.create_item("Progressive Shed"), event=True)
- self.assertTrue(self.world.logic.can_reach_location("Big Shed Blueprint")(self.multiworld.state),
- f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}")
+ self.assertTrue(big_shed_rule(self.multiworld.state),
+ f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}")
class TestArcadeMachinesLogic(SVTestBase):
@@ -170,10 +170,10 @@ class TestArcadeMachinesLogic(SVTestBase):
}
def test_prairie_king(self):
- self.assertFalse(self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state))
- self.assertFalse(self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state))
- self.assertFalse(self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state))
- self.assertFalse(self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach("JotPK World 1")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach("JotPK World 2")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach("JotPK World 3")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state))
boots = self.world.create_item("JotPK: Progressive Boots")
gun = self.world.create_item("JotPK: Progressive Gun")
@@ -183,19 +183,19 @@ def test_prairie_king(self):
self.multiworld.state.collect(boots, event=True)
self.multiworld.state.collect(gun, event=True)
- self.assertTrue(self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state))
- self.assertFalse(self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state))
- self.assertFalse(self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state))
- self.assertFalse(self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state))
+ self.assertTrue(self.world.logic.region.can_reach("JotPK World 1")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach("JotPK World 2")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach("JotPK World 3")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state))
self.remove(boots)
self.remove(gun)
self.multiworld.state.collect(boots, event=True)
self.multiworld.state.collect(boots, event=True)
- self.assertTrue(self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state))
- self.assertFalse(self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state))
- self.assertFalse(self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state))
- self.assertFalse(self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state))
+ self.assertTrue(self.world.logic.region.can_reach("JotPK World 1")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach("JotPK World 2")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach("JotPK World 3")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state))
self.remove(boots)
self.remove(boots)
@@ -203,10 +203,10 @@ def test_prairie_king(self):
self.multiworld.state.collect(gun, event=True)
self.multiworld.state.collect(ammo, event=True)
self.multiworld.state.collect(life, event=True)
- self.assertTrue(self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state))
- self.assertTrue(self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state))
- self.assertFalse(self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state))
- self.assertFalse(self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state))
+ self.assertTrue(self.world.logic.region.can_reach("JotPK World 1")(self.multiworld.state))
+ self.assertTrue(self.world.logic.region.can_reach("JotPK World 2")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach("JotPK World 3")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state))
self.remove(boots)
self.remove(gun)
self.remove(ammo)
@@ -219,10 +219,10 @@ def test_prairie_king(self):
self.multiworld.state.collect(ammo, event=True)
self.multiworld.state.collect(life, event=True)
self.multiworld.state.collect(drop, event=True)
- self.assertTrue(self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state))
- self.assertTrue(self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state))
- self.assertTrue(self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state))
- self.assertFalse(self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state))
+ self.assertTrue(self.world.logic.region.can_reach("JotPK World 1")(self.multiworld.state))
+ self.assertTrue(self.world.logic.region.can_reach("JotPK World 2")(self.multiworld.state))
+ self.assertTrue(self.world.logic.region.can_reach("JotPK World 3")(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state))
self.remove(boots)
self.remove(gun)
self.remove(gun)
@@ -242,10 +242,10 @@ def test_prairie_king(self):
self.multiworld.state.collect(ammo, event=True)
self.multiworld.state.collect(life, event=True)
self.multiworld.state.collect(drop, event=True)
- self.assertTrue(self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state))
- self.assertTrue(self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state))
- self.assertTrue(self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state))
- self.assertTrue(self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state))
+ self.assertTrue(self.world.logic.region.can_reach("JotPK World 1")(self.multiworld.state))
+ self.assertTrue(self.world.logic.region.can_reach("JotPK World 2")(self.multiworld.state))
+ self.assertTrue(self.world.logic.region.can_reach("JotPK World 3")(self.multiworld.state))
+ self.assertTrue(self.world.logic.region.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state))
self.remove(boots)
self.remove(boots)
self.remove(gun)
@@ -261,116 +261,272 @@ def test_prairie_king(self):
class TestWeaponsLogic(SVTestBase):
options = {
- options.ToolProgression.internal_name: options.ToolProgression.option_progressive,
+ ToolProgression.internal_name: ToolProgression.option_progressive,
options.SkillProgression.internal_name: options.SkillProgression.option_progressive,
}
def test_mine(self):
- self.collect(self.world.create_item("Adventurer's Guild"))
self.multiworld.state.collect(self.world.create_item("Progressive Pickaxe"), event=True)
self.multiworld.state.collect(self.world.create_item("Progressive Pickaxe"), event=True)
self.multiworld.state.collect(self.world.create_item("Progressive Pickaxe"), event=True)
self.multiworld.state.collect(self.world.create_item("Progressive Pickaxe"), event=True)
self.collect([self.world.create_item("Combat Level")] * 10)
+ self.collect([self.world.create_item("Mining Level")] * 10)
self.collect([self.world.create_item("Progressive Mine Elevator")] * 24)
self.multiworld.state.collect(self.world.create_item("Bus Repair"), event=True)
self.multiworld.state.collect(self.world.create_item("Skull Key"), event=True)
- self.GiveItemAndCheckReachableMine("Rusty Sword", 1)
- self.GiveItemAndCheckReachableMine("Wooden Blade", 1)
- self.GiveItemAndCheckReachableMine("Elf Blade", 1)
+ self.GiveItemAndCheckReachableMine("Progressive Sword", 1)
+ self.GiveItemAndCheckReachableMine("Progressive Dagger", 1)
+ self.GiveItemAndCheckReachableMine("Progressive Club", 1)
- self.GiveItemAndCheckReachableMine("Silver Saber", 2)
- self.GiveItemAndCheckReachableMine("Crystal Dagger", 2)
+ self.GiveItemAndCheckReachableMine("Progressive Sword", 2)
+ self.GiveItemAndCheckReachableMine("Progressive Dagger", 2)
+ self.GiveItemAndCheckReachableMine("Progressive Club", 2)
- self.GiveItemAndCheckReachableMine("Claymore", 3)
- self.GiveItemAndCheckReachableMine("Obsidian Edge", 3)
- self.GiveItemAndCheckReachableMine("Bone Sword", 3)
+ self.GiveItemAndCheckReachableMine("Progressive Sword", 3)
+ self.GiveItemAndCheckReachableMine("Progressive Dagger", 3)
+ self.GiveItemAndCheckReachableMine("Progressive Club", 3)
- self.GiveItemAndCheckReachableMine("The Slammer", 4)
- self.GiveItemAndCheckReachableMine("Lava Katana", 4)
+ self.GiveItemAndCheckReachableMine("Progressive Sword", 4)
+ self.GiveItemAndCheckReachableMine("Progressive Dagger", 4)
+ self.GiveItemAndCheckReachableMine("Progressive Club", 4)
- self.GiveItemAndCheckReachableMine("Galaxy Sword", 5)
- self.GiveItemAndCheckReachableMine("Galaxy Hammer", 5)
- self.GiveItemAndCheckReachableMine("Galaxy Dagger", 5)
+ self.GiveItemAndCheckReachableMine("Progressive Sword", 5)
+ self.GiveItemAndCheckReachableMine("Progressive Dagger", 5)
+ self.GiveItemAndCheckReachableMine("Progressive Club", 5)
def GiveItemAndCheckReachableMine(self, item_name: str, reachable_level: int):
item = self.multiworld.create_item(item_name, self.player)
self.multiworld.state.collect(item, event=True)
+ rule = self.world.logic.mine.can_mine_in_the_mines_floor_1_40()
if reachable_level > 0:
- self.assertTrue(self.world.logic.can_mine_in_the_mines_floor_1_40()(self.multiworld.state))
+ self.assert_rule_true(rule, self.multiworld.state)
else:
- self.assertFalse(self.world.logic.can_mine_in_the_mines_floor_1_40()(self.multiworld.state))
+ self.assert_rule_false(rule, self.multiworld.state)
+ rule = self.world.logic.mine.can_mine_in_the_mines_floor_41_80()
if reachable_level > 1:
- self.assertTrue(self.world.logic.can_mine_in_the_mines_floor_41_80()(self.multiworld.state))
+ self.assert_rule_true(rule, self.multiworld.state)
else:
- self.assertFalse(self.world.logic.can_mine_in_the_mines_floor_41_80()(self.multiworld.state))
+ self.assert_rule_false(rule, self.multiworld.state)
+ rule = self.world.logic.mine.can_mine_in_the_mines_floor_81_120()
if reachable_level > 2:
- self.assertTrue(self.world.logic.can_mine_in_the_mines_floor_81_120()(self.multiworld.state))
+ self.assert_rule_true(rule, self.multiworld.state)
else:
- self.assertFalse(self.world.logic.can_mine_in_the_mines_floor_81_120()(self.multiworld.state))
+ self.assert_rule_false(rule, self.multiworld.state)
+ rule = self.world.logic.mine.can_mine_in_the_skull_cavern()
if reachable_level > 3:
- self.assertTrue(self.world.logic.can_mine_in_the_skull_cavern()(self.multiworld.state))
+ self.assert_rule_true(rule, self.multiworld.state)
else:
- self.assertFalse(self.world.logic.can_mine_in_the_skull_cavern()(self.multiworld.state))
+ self.assert_rule_false(rule, self.multiworld.state)
+ rule = self.world.logic.ability.can_mine_perfectly_in_the_skull_cavern()
if reachable_level > 4:
- self.assertTrue(self.world.logic.can_mine_perfectly_in_the_skull_cavern()(self.multiworld.state))
+ self.assert_rule_true(rule, self.multiworld.state)
else:
- self.assertFalse(self.world.logic.can_mine_perfectly_in_the_skull_cavern()(self.multiworld.state))
+ self.assert_rule_false(rule, self.multiworld.state)
- self.remove(item)
+class TestRecipeLearnLogic(SVTestBase):
+ options = {
+ BuildingProgression.internal_name: BuildingProgression.option_progressive,
+ options.Cropsanity.internal_name: options.Cropsanity.option_enabled,
+ options.Cooksanity.internal_name: options.Cooksanity.option_all,
+ Chefsanity.internal_name: Chefsanity.option_none,
+ ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true,
+ }
+
+ def test_can_learn_qos_recipe(self):
+ location = "Cook Radish Salad"
+ rule = self.world.logic.region.can_reach_location(location)
+ self.assert_rule_false(rule, self.multiworld.state)
-class TestRecipeLogic(SVTestBase):
+ self.multiworld.state.collect(self.world.create_item("Progressive House"), event=False)
+ self.multiworld.state.collect(self.world.create_item("Radish Seeds"), event=False)
+ self.multiworld.state.collect(self.world.create_item("Spring"), event=False)
+ self.multiworld.state.collect(self.world.create_item("Summer"), event=False)
+ self.collect_lots_of_money()
+ self.assert_rule_false(rule, self.multiworld.state)
+
+ self.multiworld.state.collect(self.world.create_item("The Queen of Sauce"), event=False)
+ self.assert_rule_true(rule, self.multiworld.state)
+
+
+class TestRecipeReceiveLogic(SVTestBase):
options = {
- options.BuildingProgression.internal_name: options.BuildingProgression.option_progressive,
- options.SkillProgression.internal_name: options.SkillProgression.option_progressive,
+ BuildingProgression.internal_name: BuildingProgression.option_progressive,
+ options.Cropsanity.internal_name: options.Cropsanity.option_enabled,
+ options.Cooksanity.internal_name: options.Cooksanity.option_all,
+ Chefsanity.internal_name: Chefsanity.option_all,
+ ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true,
+ }
+
+ def test_can_learn_qos_recipe(self):
+ location = "Cook Radish Salad"
+ rule = self.world.logic.region.can_reach_location(location)
+ self.assert_rule_false(rule, self.multiworld.state)
+
+ self.multiworld.state.collect(self.world.create_item("Progressive House"), event=False)
+ self.multiworld.state.collect(self.world.create_item("Radish Seeds"), event=False)
+ self.multiworld.state.collect(self.world.create_item("Summer"), event=False)
+ self.collect_lots_of_money()
+ self.assert_rule_false(rule, self.multiworld.state)
+
+ spring = self.world.create_item("Spring")
+ qos = self.world.create_item("The Queen of Sauce")
+ self.multiworld.state.collect(spring, event=False)
+ self.multiworld.state.collect(qos, event=False)
+ self.assert_rule_false(rule, self.multiworld.state)
+ self.multiworld.state.remove(spring)
+ self.multiworld.state.remove(qos)
+
+ self.multiworld.state.collect(self.world.create_item("Radish Salad Recipe"), event=False)
+ self.assert_rule_true(rule, self.multiworld.state)
+
+ def test_get_chefsanity_check_recipe(self):
+ location = "Radish Salad Recipe"
+ rule = self.world.logic.region.can_reach_location(location)
+ self.assert_rule_false(rule, self.multiworld.state)
+
+ self.multiworld.state.collect(self.world.create_item("Spring"), event=False)
+ self.collect_lots_of_money()
+ self.assert_rule_false(rule, self.multiworld.state)
+
+ seeds = self.world.create_item("Radish Seeds")
+ summer = self.world.create_item("Summer")
+ house = self.world.create_item("Progressive House")
+ self.multiworld.state.collect(seeds, event=False)
+ self.multiworld.state.collect(summer, event=False)
+ self.multiworld.state.collect(house, event=False)
+ self.assert_rule_false(rule, self.multiworld.state)
+ self.multiworld.state.remove(seeds)
+ self.multiworld.state.remove(summer)
+ self.multiworld.state.remove(house)
+
+ self.multiworld.state.collect(self.world.create_item("The Queen of Sauce"), event=False)
+ self.assert_rule_true(rule, self.multiworld.state)
+
+
+class TestCraftsanityLogic(SVTestBase):
+ options = {
+ BuildingProgression.internal_name: BuildingProgression.option_progressive,
options.Cropsanity.internal_name: options.Cropsanity.option_enabled,
+ Craftsanity.internal_name: Craftsanity.option_all,
+ ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true,
}
- # I wanted to make a test for different ways to obtain a pizza, but I'm stuck not knowing how to block the immediate purchase from Gus
- # def test_pizza(self):
- # world = self.world
- # logic = world.logic
- # multiworld = self.multiworld
- #
- # self.assertTrue(logic.has(Ingredient.wheat_flour)(multiworld.state))
- # self.assertTrue(logic.can_spend_money_at(Region.saloon, 150)(multiworld.state))
- # self.assertFalse(logic.has(Meal.pizza)(multiworld.state))
- #
- # self.assertFalse(logic.can_cook()(multiworld.state))
- # self.collect(world.create_item("Progressive House"))
- # self.assertTrue(logic.can_cook()(multiworld.state))
- # self.assertFalse(logic.has(Meal.pizza)(multiworld.state))
- #
- # self.assertFalse(logic.has(Seed.tomato)(multiworld.state))
- # self.collect(world.create_item(Seed.tomato))
- # self.assertTrue(logic.has(Seed.tomato)(multiworld.state))
- # self.assertFalse(logic.has(Meal.pizza)(multiworld.state))
- #
- # self.assertFalse(logic.has(Vegetable.tomato)(multiworld.state))
- # self.collect(world.create_item(Season.summer))
- # self.assertTrue(logic.has(Vegetable.tomato)(multiworld.state))
- # self.assertFalse(logic.has(Meal.pizza)(multiworld.state))
- #
- # self.assertFalse(logic.has(Animal.cow)(multiworld.state))
- # self.assertFalse(logic.has(AnimalProduct.cow_milk)(multiworld.state))
- # self.collect(world.create_item("Progressive Barn"))
- # self.assertTrue(logic.has(Animal.cow)(multiworld.state))
- # self.assertTrue(logic.has(AnimalProduct.cow_milk)(multiworld.state))
- # self.assertFalse(logic.has(Meal.pizza)(multiworld.state))
- #
- # self.assertFalse(logic.has(Machine.cheese_press)(self.multiworld.state))
- # self.assertFalse(logic.has(ArtisanGood.cheese)(self.multiworld.state))
- # self.collect(world.create_item(item) for item in ["Farming Level"] * 6)
- # self.collect(world.create_item(item) for item in ["Progressive Axe"] * 2)
- # self.assertTrue(logic.has(Machine.cheese_press)(self.multiworld.state))
- # self.assertTrue(logic.has(ArtisanGood.cheese)(self.multiworld.state))
- # self.assertTrue(logic.has(Meal.pizza)(self.multiworld.state))
+ def test_can_craft_recipe(self):
+ location = "Craft Marble Brazier"
+ rule = self.world.logic.region.can_reach_location(location)
+ self.collect([self.world.create_item("Progressive Pickaxe")] * 4)
+ self.collect([self.world.create_item("Progressive Fishing Rod")] * 4)
+ self.collect([self.world.create_item("Progressive Sword")] * 4)
+ self.collect([self.world.create_item("Progressive Mine Elevator")] * 24)
+ self.collect([self.world.create_item("Mining Level")] * 10)
+ self.collect([self.world.create_item("Combat Level")] * 10)
+ self.collect([self.world.create_item("Fishing Level")] * 10)
+ self.collect_all_the_money()
+ self.assert_rule_false(rule, self.multiworld.state)
+
+ self.multiworld.state.collect(self.world.create_item("Marble Brazier Recipe"), event=False)
+ self.assert_rule_true(rule, self.multiworld.state)
+
+ def test_can_learn_crafting_recipe(self):
+ location = "Marble Brazier Recipe"
+ rule = self.world.logic.region.can_reach_location(location)
+ self.assert_rule_false(rule, self.multiworld.state)
+
+ self.collect_lots_of_money()
+ self.assert_rule_true(rule, self.multiworld.state)
+
+ def test_can_craft_festival_recipe(self):
+ recipe = all_crafting_recipes_by_name["Jack-O-Lantern"]
+ self.multiworld.state.collect(self.world.create_item("Pumpkin Seeds"), event=False)
+ self.multiworld.state.collect(self.world.create_item("Torch Recipe"), event=False)
+ self.collect_lots_of_money()
+ rule = self.world.logic.crafting.can_craft(recipe)
+ self.assert_rule_false(rule, self.multiworld.state)
+
+ self.multiworld.state.collect(self.world.create_item("Fall"), event=False)
+ self.assert_rule_false(rule, self.multiworld.state)
+
+ self.multiworld.state.collect(self.world.create_item("Jack-O-Lantern Recipe"), event=False)
+ self.assert_rule_true(rule, self.multiworld.state)
+
+
+class TestCraftsanityWithFestivalsLogic(SVTestBase):
+ options = {
+ BuildingProgression.internal_name: BuildingProgression.option_progressive,
+ options.Cropsanity.internal_name: options.Cropsanity.option_enabled,
+ options.FestivalLocations.internal_name: options.FestivalLocations.option_easy,
+ Craftsanity.internal_name: Craftsanity.option_all,
+ ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true,
+ }
+
+ def test_can_craft_festival_recipe(self):
+ recipe = all_crafting_recipes_by_name["Jack-O-Lantern"]
+ self.multiworld.state.collect(self.world.create_item("Pumpkin Seeds"), event=False)
+ self.multiworld.state.collect(self.world.create_item("Fall"), event=False)
+ self.collect_lots_of_money()
+ rule = self.world.logic.crafting.can_craft(recipe)
+ self.assert_rule_false(rule, self.multiworld.state)
+
+ self.multiworld.state.collect(self.world.create_item("Jack-O-Lantern Recipe"), event=False)
+ self.assert_rule_false(rule, self.multiworld.state)
+
+ self.multiworld.state.collect(self.world.create_item("Torch Recipe"), event=False)
+ self.assert_rule_true(rule, self.multiworld.state)
+
+
+class TestNoCraftsanityLogic(SVTestBase):
+ options = {
+ BuildingProgression.internal_name: BuildingProgression.option_progressive,
+ SeasonRandomization.internal_name: SeasonRandomization.option_progressive,
+ options.Cropsanity.internal_name: options.Cropsanity.option_enabled,
+ options.FestivalLocations.internal_name: options.FestivalLocations.option_disabled,
+ Craftsanity.internal_name: Craftsanity.option_none,
+ ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true,
+ }
+
+ def test_can_craft_recipe(self):
+ recipe = all_crafting_recipes_by_name["Wood Floor"]
+ rule = self.world.logic.crafting.can_craft(recipe)
+ self.assert_rule_true(rule, self.multiworld.state)
+
+ def test_can_craft_festival_recipe(self):
+ recipe = all_crafting_recipes_by_name["Jack-O-Lantern"]
+ self.multiworld.state.collect(self.world.create_item("Pumpkin Seeds"), event=False)
+ self.collect_lots_of_money()
+ rule = self.world.logic.crafting.can_craft(recipe)
+ result = rule(self.multiworld.state)
+ self.assertFalse(result)
+
+ self.collect([self.world.create_item("Progressive Season")] * 2)
+ self.assert_rule_true(rule, self.multiworld.state)
+
+
+class TestNoCraftsanityWithFestivalsLogic(SVTestBase):
+ options = {
+ BuildingProgression.internal_name: BuildingProgression.option_progressive,
+ options.Cropsanity.internal_name: options.Cropsanity.option_enabled,
+ options.FestivalLocations.internal_name: options.FestivalLocations.option_easy,
+ Craftsanity.internal_name: Craftsanity.option_none,
+ ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true,
+ }
+
+ def test_can_craft_festival_recipe(self):
+ recipe = all_crafting_recipes_by_name["Jack-O-Lantern"]
+ self.multiworld.state.collect(self.world.create_item("Pumpkin Seeds"), event=False)
+ self.multiworld.state.collect(self.world.create_item("Fall"), event=False)
+ self.collect_lots_of_money()
+ rule = self.world.logic.crafting.can_craft(recipe)
+ self.assert_rule_false(rule, self.multiworld.state)
+
+ self.multiworld.state.collect(self.world.create_item("Jack-O-Lantern Recipe"), event=False)
+ self.assert_rule_true(rule, self.multiworld.state)
class TestDonationLogicAll(SVTestBase):
@@ -379,17 +535,17 @@ class TestDonationLogicAll(SVTestBase):
}
def test_cannot_make_any_donation_without_museum_access(self):
- guild_item = "Adventurer's Guild"
- swap_museum_and_guild(self.multiworld, self.player)
- collect_all_except(self.multiworld, guild_item)
+ railroad_item = "Railroad Boulder Removed"
+ swap_museum_and_bathhouse(self.multiworld, self.player)
+ collect_all_except(self.multiworld, railroad_item)
for donation in locations_by_tag[LocationTags.MUSEUM_DONATIONS]:
- self.assertFalse(self.world.logic.can_reach_location(donation.name)(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state))
- self.multiworld.state.collect(self.world.create_item(guild_item), event=True)
+ self.multiworld.state.collect(self.world.create_item(railroad_item), event=False)
for donation in locations_by_tag[LocationTags.MUSEUM_DONATIONS]:
- self.assertTrue(self.world.logic.can_reach_location(donation.name)(self.multiworld.state))
+ self.assertTrue(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state))
class TestDonationLogicRandomized(SVTestBase):
@@ -398,18 +554,19 @@ class TestDonationLogicRandomized(SVTestBase):
}
def test_cannot_make_any_donation_without_museum_access(self):
- guild_item = "Adventurer's Guild"
- swap_museum_and_guild(self.multiworld, self.player)
- collect_all_except(self.multiworld, guild_item)
- donation_locations = [location for location in self.multiworld.get_locations() if not location.event and LocationTags.MUSEUM_DONATIONS in location_table[location.name].tags]
+ railroad_item = "Railroad Boulder Removed"
+ swap_museum_and_bathhouse(self.multiworld, self.player)
+ collect_all_except(self.multiworld, railroad_item)
+ donation_locations = [location for location in self.multiworld.get_locations() if
+ not location.event and LocationTags.MUSEUM_DONATIONS in location_table[location.name].tags]
for donation in donation_locations:
- self.assertFalse(self.world.logic.can_reach_location(donation.name)(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state))
- self.multiworld.state.collect(self.world.create_item(guild_item), event=True)
+ self.multiworld.state.collect(self.world.create_item(railroad_item), event=False)
for donation in donation_locations:
- self.assertTrue(self.world.logic.can_reach_location(donation.name)(self.multiworld.state))
+ self.assertTrue(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state))
class TestDonationLogicMilestones(SVTestBase):
@@ -418,26 +575,74 @@ class TestDonationLogicMilestones(SVTestBase):
}
def test_cannot_make_any_donation_without_museum_access(self):
- guild_item = "Adventurer's Guild"
- swap_museum_and_guild(self.multiworld, self.player)
- collect_all_except(self.multiworld, guild_item)
+ railroad_item = "Railroad Boulder Removed"
+ swap_museum_and_bathhouse(self.multiworld, self.player)
+ collect_all_except(self.multiworld, railroad_item)
for donation in locations_by_tag[LocationTags.MUSEUM_MILESTONES]:
- self.assertFalse(self.world.logic.can_reach_location(donation.name)(self.multiworld.state))
+ self.assertFalse(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state))
- self.multiworld.state.collect(self.world.create_item(guild_item), event=True)
+ self.multiworld.state.collect(self.world.create_item(railroad_item), event=False)
for donation in locations_by_tag[LocationTags.MUSEUM_MILESTONES]:
- self.assertTrue(self.world.logic.can_reach_location(donation.name)(self.multiworld.state))
+ self.assertTrue(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state))
-def swap_museum_and_guild(multiworld, player):
+def swap_museum_and_bathhouse(multiworld, player):
museum_region = multiworld.get_region(Region.museum, player)
- guild_region = multiworld.get_region(Region.adventurer_guild, player)
+ bathhouse_region = multiworld.get_region(Region.bathhouse_entrance, player)
museum_entrance = multiworld.get_entrance(Entrance.town_to_museum, player)
- guild_entrance = multiworld.get_entrance(Entrance.mountain_to_adventurer_guild, player)
- museum_entrance.connect(guild_region)
- guild_entrance.connect(museum_region)
+ bathhouse_entrance = multiworld.get_entrance(Entrance.enter_bathhouse_entrance, player)
+ museum_entrance.connect(bathhouse_region)
+ bathhouse_entrance.connect(museum_region)
+
+
+class TestToolVanillaRequiresBlacksmith(SVTestBase):
+ options = {
+ options.EntranceRandomization: options.EntranceRandomization.option_buildings,
+ options.ToolProgression: options.ToolProgression.option_vanilla,
+ }
+ seed = 4111845104987680262
+
+ # Seed is hardcoded to make sure the ER is a valid roll that actually lock the blacksmith behind the Railroad Boulder Removed.
+
+ def test_cannot_get_any_tool_without_blacksmith_access(self):
+ railroad_item = "Railroad Boulder Removed"
+ place_region_at_entrance(self.multiworld, self.player, Region.blacksmith, Entrance.enter_bathhouse_entrance)
+ collect_all_except(self.multiworld, railroad_item)
+
+ for tool in [Tool.pickaxe, Tool.axe, Tool.hoe, Tool.trash_can, Tool.watering_can]:
+ for material in [ToolMaterial.copper, ToolMaterial.iron, ToolMaterial.gold, ToolMaterial.iridium]:
+ self.assert_rule_false(self.world.logic.tool.has_tool(tool, material), self.multiworld.state)
+
+ self.multiworld.state.collect(self.world.create_item(railroad_item), event=False)
+
+ for tool in [Tool.pickaxe, Tool.axe, Tool.hoe, Tool.trash_can, Tool.watering_can]:
+ for material in [ToolMaterial.copper, ToolMaterial.iron, ToolMaterial.gold, ToolMaterial.iridium]:
+ self.assert_rule_true(self.world.logic.tool.has_tool(tool, material), self.multiworld.state)
+
+ def test_cannot_get_fishing_rod_without_willy_access(self):
+ railroad_item = "Railroad Boulder Removed"
+ place_region_at_entrance(self.multiworld, self.player, Region.fish_shop, Entrance.enter_bathhouse_entrance)
+ collect_all_except(self.multiworld, railroad_item)
+
+ for fishing_rod_level in [3, 4]:
+ self.assert_rule_false(self.world.logic.tool.has_fishing_rod(fishing_rod_level), self.multiworld.state)
+
+ self.multiworld.state.collect(self.world.create_item(railroad_item), event=False)
+
+ for fishing_rod_level in [3, 4]:
+ self.assert_rule_true(self.world.logic.tool.has_fishing_rod(fishing_rod_level), self.multiworld.state)
+
+
+def place_region_at_entrance(multiworld, player, region, entrance):
+ region_to_place = multiworld.get_region(region, player)
+ entrance_to_place_region = multiworld.get_entrance(entrance, player)
+
+ entrance_to_switch = region_to_place.entrances[0]
+ region_to_switch = entrance_to_place_region.connected_region
+ entrance_to_switch.connect(region_to_switch)
+ entrance_to_place_region.connect(region_to_place)
def collect_all_except(multiworld, item_to_not_collect: str):
@@ -448,22 +653,19 @@ def collect_all_except(multiworld, item_to_not_collect: str):
class TestFriendsanityDatingRules(SVTestBase):
options = {
- options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized_not_winter,
- options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
- options.FriendsanityHeartSize.internal_name: 3
+ SeasonRandomization.internal_name: SeasonRandomization.option_randomized_not_winter,
+ Friendsanity.internal_name: Friendsanity.option_all_with_marriage,
+ FriendsanityHeartSize.internal_name: 3
}
def test_earning_dating_heart_requires_dating(self):
- month_name = "Month End"
- for i in range(12):
- month_item = self.world.create_item(month_name)
- self.multiworld.state.collect(month_item, event=True)
+ self.collect_all_the_money()
+ self.multiworld.state.collect(self.world.create_item("Fall"), event=False)
self.multiworld.state.collect(self.world.create_item("Beach Bridge"), event=False)
self.multiworld.state.collect(self.world.create_item("Progressive House"), event=False)
- self.multiworld.state.collect(self.world.create_item("Adventurer's Guild"), event=False)
- self.multiworld.state.collect(self.world.create_item("Galaxy Hammer"), event=False)
for i in range(3):
self.multiworld.state.collect(self.world.create_item("Progressive Pickaxe"), event=False)
+ self.multiworld.state.collect(self.world.create_item("Progressive Weapon"), event=False)
self.multiworld.state.collect(self.world.create_item("Progressive Axe"), event=False)
self.multiworld.state.collect(self.world.create_item("Progressive Barn"), event=False)
for i in range(10):
@@ -495,12 +697,102 @@ def assert_can_reach_heart_up_to(self, npc: str, max_reachable: int, step: int):
if i % step != 0 and i != 14:
continue
location = f"{prefix}{npc} {i}{suffix}"
- can_reach = self.world.logic.can_reach_location(location)(self.multiworld.state)
+ can_reach = self.world.logic.region.can_reach_location(location)(self.multiworld.state)
self.assertTrue(can_reach, f"Should be able to earn relationship up to {i} hearts")
for i in range(max_reachable + 1, 14 + 1):
if i % step != 0 and i != 14:
continue
location = f"{prefix}{npc} {i}{suffix}"
- can_reach = self.world.logic.can_reach_location(location)(self.multiworld.state)
+ can_reach = self.world.logic.region.can_reach_location(location)(self.multiworld.state)
self.assertFalse(can_reach, f"Should not be able to earn relationship up to {i} hearts")
+
+class TestShipsanityNone(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_none
+ }
+
+ def test_no_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event:
+ self.assertFalse("Shipsanity" in location.name)
+ self.assertNotIn(LocationTags.SHIPSANITY, location_table[location.name].tags)
+
+
+class TestShipsanityCrops(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_crops
+ }
+
+ def test_only_crop_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags:
+ self.assertIn(LocationTags.SHIPSANITY_CROP, location_table[location.name].tags)
+
+
+class TestShipsanityFish(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_fish
+ }
+
+ def test_only_fish_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags:
+ self.assertIn(LocationTags.SHIPSANITY_FISH, location_table[location.name].tags)
+
+
+class TestShipsanityFullShipment(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_full_shipment
+ }
+
+ def test_only_full_shipment_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags:
+ self.assertIn(LocationTags.SHIPSANITY_FULL_SHIPMENT, location_table[location.name].tags)
+ self.assertNotIn(LocationTags.SHIPSANITY_FISH, location_table[location.name].tags)
+
+
+class TestShipsanityFullShipmentWithFish(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_full_shipment_with_fish
+ }
+
+ def test_only_full_shipment_and_fish_shipsanity_locations(self):
+ for location in self.multiworld.get_locations(self.player):
+ if not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags:
+ self.assertTrue(LocationTags.SHIPSANITY_FULL_SHIPMENT in location_table[location.name].tags or
+ LocationTags.SHIPSANITY_FISH in location_table[location.name].tags)
+
+
+class TestShipsanityEverything(SVTestBase):
+ options = {
+ Shipsanity.internal_name: Shipsanity.option_everything,
+ BuildingProgression.internal_name: BuildingProgression.option_progressive
+ }
+
+ def test_all_shipsanity_locations_require_shipping_bin(self):
+ bin_name = "Shipping Bin"
+ collect_all_except(self.multiworld, bin_name)
+ shipsanity_locations = [location for location in self.multiworld.get_locations() if
+ not location.event and LocationTags.SHIPSANITY in location_table[location.name].tags]
+ bin_item = self.world.create_item(bin_name)
+ for location in shipsanity_locations:
+ with self.subTest(location.name):
+ self.remove(bin_item)
+ self.assertFalse(self.world.logic.region.can_reach_location(location.name)(self.multiworld.state))
+ self.multiworld.state.collect(bin_item, event=False)
+ shipsanity_rule = self.world.logic.region.can_reach_location(location.name)
+ self.assert_rule_true(shipsanity_rule, self.multiworld.state)
+ self.remove(bin_item)
+
+
+class TestVanillaSkillLogicSimplification(SVTestBase):
+ options = {
+ SkillProgression.internal_name: SkillProgression.option_vanilla,
+ ToolProgression.internal_name: ToolProgression.option_progressive,
+ }
+
+ def test_skill_logic_has_level_only_uses_one_has_progression_percent(self):
+ rule = self.multiworld.worlds[1].logic.skill.has_level("Farming", 8)
+ self.assertEqual(1, sum(1 for i in rule.current_rules if type(i) == HasProgressionPercent))
diff --git a/worlds/stardew_valley/test/TestStardewRule.py b/worlds/stardew_valley/test/TestStardewRule.py
new file mode 100644
index 000000000000..89317d90e4e2
--- /dev/null
+++ b/worlds/stardew_valley/test/TestStardewRule.py
@@ -0,0 +1,244 @@
+import unittest
+from unittest.mock import MagicMock, Mock
+
+from ..stardew_rule import Received, And, Or, HasProgressionPercent, false_, true_
+
+
+class TestSimplification(unittest.TestCase):
+ """
+ Those feature of simplifying the rules when they are built have proven to improve the fill speed considerably.
+ """
+
+ def test_simplify_and_and_and(self):
+ rule = And(Received('Summer', 0, 1), Received('Fall', 0, 1)) & And(Received('Winter', 0, 1), Received('Spring', 0, 1))
+
+ self.assertEqual(And(Received('Summer', 0, 1), Received('Fall', 0, 1), Received('Winter', 0, 1), Received('Spring', 0, 1)), rule)
+
+ def test_simplify_and_in_and(self):
+ rule = And(And(Received('Summer', 0, 1), Received('Fall', 0, 1)), And(Received('Winter', 0, 1), Received('Spring', 0, 1)))
+ self.assertEqual(And(Received('Summer', 0, 1), Received('Fall', 0, 1), Received('Winter', 0, 1), Received('Spring', 0, 1)), rule)
+
+ def test_simplify_duplicated_and(self):
+ # This only works because "Received"s are combinable.
+ rule = And(And(Received('Summer', 0, 1), Received('Fall', 0, 1)), And(Received('Summer', 0, 1), Received('Fall', 0, 1)))
+ self.assertEqual(And(Received('Summer', 0, 1), Received('Fall', 0, 1)), rule)
+
+ def test_simplify_or_or_or(self):
+ rule = Or(Received('Summer', 0, 1), Received('Fall', 0, 1)) | Or(Received('Winter', 0, 1), Received('Spring', 0, 1))
+ self.assertEqual(Or(Received('Summer', 0, 1), Received('Fall', 0, 1), Received('Winter', 0, 1), Received('Spring', 0, 1)), rule)
+
+ def test_simplify_or_in_or(self):
+ rule = Or(Or(Received('Summer', 0, 1), Received('Fall', 0, 1)), Or(Received('Winter', 0, 1), Received('Spring', 0, 1)))
+ self.assertEqual(Or(Received('Summer', 0, 1), Received('Fall', 0, 1), Received('Winter', 0, 1), Received('Spring', 0, 1)), rule)
+
+ def test_simplify_duplicated_or(self):
+ rule = Or(Or(Received('Summer', 0, 1), Received('Fall', 0, 1)), Or(Received('Summer', 0, 1), Received('Fall', 0, 1)))
+ self.assertEqual(Or(Received('Summer', 0, 1), Received('Fall', 0, 1)), rule)
+
+
+class TestHasProgressionPercentSimplification(unittest.TestCase):
+ def test_has_progression_percent_and_uses_max(self):
+ rule = HasProgressionPercent(1, 20) & HasProgressionPercent(1, 10)
+ self.assertEqual(rule, HasProgressionPercent(1, 20))
+
+ def test_has_progression_percent_or_uses_min(self):
+ rule = HasProgressionPercent(1, 20) | HasProgressionPercent(1, 10)
+ self.assertEqual(rule, HasProgressionPercent(1, 10))
+
+ def test_and_between_progression_percent_and_other_progression_percent_uses_max(self):
+ cases = [
+ And(HasProgressionPercent(1, 10)) & HasProgressionPercent(1, 20),
+ HasProgressionPercent(1, 10) & And(HasProgressionPercent(1, 20)),
+ And(HasProgressionPercent(1, 20)) & And(HasProgressionPercent(1, 10)),
+ ]
+ for i, case in enumerate(cases):
+ with self.subTest(f"{i} {repr(case)}"):
+ self.assertEqual(case, And(HasProgressionPercent(1, 20)))
+
+ def test_or_between_progression_percent_and_other_progression_percent_uses_max(self):
+ cases = [
+ Or(HasProgressionPercent(1, 10)) | HasProgressionPercent(1, 20),
+ HasProgressionPercent(1, 10) | Or(HasProgressionPercent(1, 20)),
+ Or(HasProgressionPercent(1, 20)) | Or(HasProgressionPercent(1, 10))
+ ]
+ for i, case in enumerate(cases):
+ with self.subTest(f"{i} {repr(case)}"):
+ self.assertEqual(case, Or(HasProgressionPercent(1, 10)))
+
+
+class TestEvaluateWhileSimplifying(unittest.TestCase):
+ def test_propagate_evaluate_while_simplifying(self):
+ expected_result = True
+ collection_state = MagicMock()
+ other_rule = MagicMock()
+ other_rule.evaluate_while_simplifying = Mock(return_value=(other_rule, expected_result))
+ rule = And(Or(other_rule))
+
+ _, actual_result = rule.evaluate_while_simplifying(collection_state)
+
+ other_rule.evaluate_while_simplifying.assert_called_with(collection_state)
+ self.assertEqual(expected_result, actual_result)
+
+ def test_return_complement_when_its_found(self):
+ expected_simplified = false_
+ expected_result = False
+ collection_state = MagicMock()
+ rule = And(expected_simplified)
+
+ actual_simplified, actual_result = rule.evaluate_while_simplifying(collection_state)
+
+ self.assertEqual(expected_result, actual_result)
+ self.assertEqual(expected_simplified, actual_simplified)
+
+ def test_short_circuit_when_complement_found(self):
+ collection_state = MagicMock()
+ other_rule = MagicMock()
+ rule = Or(true_, )
+
+ rule.evaluate_while_simplifying(collection_state)
+
+ other_rule.evaluate_while_simplifying.assert_not_called()
+
+ def test_short_circuit_when_combinable_rules_is_false(self):
+ collection_state = MagicMock()
+ other_rule = MagicMock()
+ rule = And(HasProgressionPercent(1, 10), other_rule)
+
+ rule.evaluate_while_simplifying(collection_state)
+
+ other_rule.evaluate_while_simplifying.assert_not_called()
+
+ def test_identity_is_removed_from_other_rules(self):
+ collection_state = MagicMock()
+ rule = Or(false_, HasProgressionPercent(1, 10))
+
+ rule.evaluate_while_simplifying(collection_state)
+
+ self.assertEqual(1, len(rule.current_rules))
+ self.assertIn(HasProgressionPercent(1, 10), rule.current_rules)
+
+ def test_complement_replaces_combinable_rules(self):
+ collection_state = MagicMock()
+ rule = Or(HasProgressionPercent(1, 10), true_)
+
+ rule.evaluate_while_simplifying(collection_state)
+
+ self.assertTrue(rule.current_rules)
+
+ def test_simplifying_to_complement_propagates_complement(self):
+ expected_simplified = true_
+ expected_result = True
+ collection_state = MagicMock()
+ rule = Or(Or(expected_simplified), HasProgressionPercent(1, 10))
+
+ actual_simplified, actual_result = rule.evaluate_while_simplifying(collection_state)
+
+ self.assertEqual(expected_result, actual_result)
+ self.assertEqual(expected_simplified, actual_simplified)
+ self.assertTrue(rule.current_rules)
+
+ def test_already_simplified_rules_are_not_simplified_again(self):
+ collection_state = MagicMock()
+ other_rule = MagicMock()
+ other_rule.evaluate_while_simplifying = Mock(return_value=(other_rule, False))
+ rule = Or(other_rule, HasProgressionPercent(1, 10))
+
+ rule.evaluate_while_simplifying(collection_state)
+ other_rule.assert_not_called()
+ other_rule.evaluate_while_simplifying.reset_mock()
+
+ rule.evaluate_while_simplifying(collection_state)
+ other_rule.assert_called_with(collection_state)
+ other_rule.evaluate_while_simplifying.assert_not_called()
+
+ def test_continue_simplification_after_short_circuited(self):
+ collection_state = MagicMock()
+ a_rule = MagicMock()
+ a_rule.evaluate_while_simplifying = Mock(return_value=(a_rule, False))
+ another_rule = MagicMock()
+ another_rule.evaluate_while_simplifying = Mock(return_value=(another_rule, False))
+ rule = And(a_rule, another_rule)
+
+ rule.evaluate_while_simplifying(collection_state)
+ # This test is completely messed up because sets are used internally and order of the rules cannot be ensured.
+ not_yet_simplified, already_simplified = (another_rule, a_rule) if a_rule.evaluate_while_simplifying.called else (a_rule, another_rule)
+ not_yet_simplified.evaluate_while_simplifying.assert_not_called()
+ already_simplified.return_value = True
+
+ rule.evaluate_while_simplifying(collection_state)
+ not_yet_simplified.evaluate_while_simplifying.assert_called_with(collection_state)
+
+
+class TestEvaluateWhileSimplifyingDoubleCalls(unittest.TestCase):
+ """
+ So, there is a situation where a rule kind of calls itself while it's being evaluated, because its evaluation triggers a region cache refresh.
+
+ The region cache check every entrance, so if a rule is also used in an entrances, it can be reevaluated.
+
+ For instance, but not limited to
+ Has Melon -> (Farm & Summer) | Greenhouse -> Greenhouse triggers an update of the region cache
+ -> Every entrance are evaluated, for instance "can start farming" -> Look that any crop can be grown (calls Has Melon).
+ """
+
+ def test_nested_call_in_the_internal_rule_being_evaluated_does_check_the_internal_rule(self):
+ collection_state = MagicMock()
+ internal_rule = MagicMock()
+ rule = Or(internal_rule)
+
+ called_once = False
+ internal_call_result = None
+
+ def first_call_to_internal_rule(state):
+ nonlocal internal_call_result
+ nonlocal called_once
+ if called_once:
+ return internal_rule, True
+ called_once = True
+
+ _, internal_call_result = rule.evaluate_while_simplifying(state)
+ internal_rule.evaluate_while_simplifying = Mock(return_value=(internal_rule, True))
+ return internal_rule, True
+
+ internal_rule.evaluate_while_simplifying = first_call_to_internal_rule
+
+ rule.evaluate_while_simplifying(collection_state)
+
+ self.assertTrue(called_once)
+ self.assertTrue(internal_call_result)
+
+ def test_nested_call_to_already_simplified_rule_does_not_steal_rule_to_simplify_from_parent_call(self):
+ collection_state = MagicMock()
+ an_internal_rule = MagicMock()
+ an_internal_rule.evaluate_while_simplifying = Mock(return_value=(an_internal_rule, True))
+ another_internal_rule = MagicMock()
+ another_internal_rule.evaluate_while_simplifying = Mock(return_value=(another_internal_rule, True))
+ rule = Or(an_internal_rule, another_internal_rule)
+
+ rule.evaluate_while_simplifying(collection_state)
+ # This test is completely messed up because sets are used internally and order of the rules cannot be ensured.
+ if an_internal_rule.evaluate_while_simplifying.called:
+ not_yet_simplified, already_simplified = another_internal_rule, an_internal_rule
+ else:
+ not_yet_simplified, already_simplified = an_internal_rule, another_internal_rule
+
+ called_once = False
+ internal_call_result = None
+
+ def call_to_already_simplified(state):
+ nonlocal internal_call_result
+ nonlocal called_once
+ if called_once:
+ return False
+ called_once = True
+
+ _, internal_call_result = rule.evaluate_while_simplifying(state)
+ return False
+
+ already_simplified.side_effect = call_to_already_simplified
+ not_yet_simplified.return_value = True
+
+ _, actual_result = rule.evaluate_while_simplifying(collection_state)
+
+ self.assertTrue(called_once)
+ self.assertTrue(internal_call_result)
+ self.assertTrue(actual_result)
diff --git a/worlds/stardew_valley/test/TestStartInventory.py b/worlds/stardew_valley/test/TestStartInventory.py
new file mode 100644
index 000000000000..826f49b1ac83
--- /dev/null
+++ b/worlds/stardew_valley/test/TestStartInventory.py
@@ -0,0 +1,41 @@
+from . import SVTestBase
+from .assertion import WorldAssertMixin
+from .. import options
+
+
+class TestStartInventoryAllsanity(WorldAssertMixin, SVTestBase):
+ options = {
+ "accessibility": "items",
+ options.Goal.internal_name: options.Goal.option_allsanity,
+ options.BundleRandomization.internal_name: options.BundleRandomization.option_remixed,
+ options.BundlePrice.internal_name: options.BundlePrice.option_minimum,
+ options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized,
+ options.Cropsanity.internal_name: options.Cropsanity.option_enabled,
+ options.ToolProgression.internal_name: options.ToolProgression.option_progressive_very_cheap,
+ options.ElevatorProgression.internal_name: options.ElevatorProgression.option_progressive_from_previous_floor,
+ options.SkillProgression.internal_name: options.SkillProgression.option_progressive,
+ options.BuildingProgression.internal_name: options.BuildingProgression.option_progressive_very_cheap,
+ options.FestivalLocations.internal_name: options.FestivalLocations.option_easy,
+ options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_disabled,
+ options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_only,
+ options.QuestLocations.internal_name: -1,
+ options.Fishsanity.internal_name: options.Fishsanity.option_only_easy_fish,
+ options.Museumsanity.internal_name: options.Museumsanity.option_randomized,
+ options.Monstersanity.internal_name: options.Monstersanity.option_one_per_category,
+ options.Shipsanity.internal_name: options.Shipsanity.option_crops,
+ options.Cooksanity.internal_name: options.Cooksanity.option_queen_of_sauce,
+ options.Chefsanity.internal_name: 0b1001,
+ options.Craftsanity.internal_name: options.Craftsanity.option_all,
+ options.Friendsanity.internal_name: options.Friendsanity.option_bachelors,
+ options.FriendsanityHeartSize.internal_name: 3,
+ options.NumberOfMovementBuffs.internal_name: 10,
+ options.NumberOfLuckBuffs.internal_name: 12,
+ options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false,
+ options.Mods.internal_name: ["Tractor Mod", "Bigger Backpack", "Luck Skill", "Magic", "Socializing Skill", "Archaeology", "Cooking Skill",
+ "Binning Skill"],
+ "start_inventory": {"Movement Speed Bonus": 2}
+ }
+
+ def test_start_inventory_movement_speed(self):
+ self.assert_basic_checks_with_subtests(self.multiworld)
+ self.assert_can_win(self.multiworld)
diff --git a/worlds/stardew_valley/test/__init__.py b/worlds/stardew_valley/test/__init__.py
index ba037f7a65da..5eddb7e280b0 100644
--- a/worlds/stardew_valley/test/__init__.py
+++ b/worlds/stardew_valley/test/__init__.py
@@ -1,140 +1,385 @@
import os
import unittest
from argparse import Namespace
-from typing import Dict, FrozenSet, Tuple, Any, ClassVar
+from contextlib import contextmanager
+from typing import Dict, ClassVar, Iterable, Hashable, Tuple, Optional, List, Union, Any
-from BaseClasses import MultiWorld
+from BaseClasses import MultiWorld, CollectionState, get_seed, Location
+from Options import VerifyKeys
from Utils import cache_argsless
-from test.TestBase import WorldTestBase
+from test.bases import WorldTestBase
from test.general import gen_steps, setup_solo_multiworld as setup_base_solo_multiworld
-from .. import StardewValleyWorld
-from ..mods.mod_data import ModNames
from worlds.AutoWorld import call_all
-from ..options import Cropsanity, SkillProgression, SpecialOrderLocations, Friendsanity, NumberOfLuckBuffs, SeasonRandomization, ToolProgression, \
- ElevatorProgression, Museumsanity, BackpackProgression, BuildingProgression, ArcadeMachineLocations, HelpWantedLocations, Fishsanity, NumberOfMovementBuffs, \
- BundleRandomization, BundlePrice, FestivalLocations, FriendsanityHeartSize, ExcludeGingerIsland, TrapItems, Goal, Mods
+from .assertion import RuleAssertMixin
+from .. import StardewValleyWorld, options
+from ..mods.mod_data import all_mods
+from ..options import StardewValleyOptions, StardewValleyOption
+DEFAULT_TEST_SEED = get_seed()
-class SVTestCase(unittest.TestCase):
- player: ClassVar[int] = 1
- """Set to False to not skip some 'extra' tests"""
- skip_extra_tests: bool = True
- """Set to False to run tests that take long"""
- skip_long_tests: bool = True
+# TODO is this caching really changing anything?
+@cache_argsless
+def disable_5_x_x_options():
+ return {
+ options.Monstersanity.internal_name: options.Monstersanity.option_none,
+ options.Shipsanity.internal_name: options.Shipsanity.option_none,
+ options.Cooksanity.internal_name: options.Cooksanity.option_none,
+ options.Chefsanity.internal_name: options.Chefsanity.option_none,
+ options.Craftsanity.internal_name: options.Craftsanity.option_none
+ }
-class SVTestBase(WorldTestBase, SVTestCase):
- game = "Stardew Valley"
- world: StardewValleyWorld
- def world_setup(self, *args, **kwargs):
- super().world_setup(*args, **kwargs)
- long_tests_key = "long"
- if long_tests_key in os.environ:
- self.skip_long_tests = not bool(os.environ[long_tests_key])
- if self.constructed:
- self.world = self.multiworld.worlds[self.player] # noqa
+@cache_argsless
+def default_4_x_x_options():
+ option_dict = default_options().copy()
+ option_dict.update(disable_5_x_x_options())
+ option_dict.update({
+ options.BundleRandomization.internal_name: options.BundleRandomization.option_shuffled,
+ })
+ return option_dict
- @property
- def run_default_tests(self) -> bool:
- # world_setup is overridden, so it'd always run default tests when importing SVTestBase
- is_not_stardew_test = type(self) is not SVTestBase
- should_run_default_tests = is_not_stardew_test and super().run_default_tests
- return should_run_default_tests
+
+@cache_argsless
+def default_options():
+ return {}
+
+
+@cache_argsless
+def get_minsanity_options():
+ return {
+ options.Goal.internal_name: options.Goal.option_bottom_of_the_mines,
+ options.BundleRandomization.internal_name: options.BundleRandomization.option_vanilla,
+ options.BundlePrice.internal_name: options.BundlePrice.option_very_cheap,
+ options.SeasonRandomization.internal_name: options.SeasonRandomization.option_disabled,
+ options.Cropsanity.internal_name: options.Cropsanity.option_disabled,
+ options.BackpackProgression.internal_name: options.BackpackProgression.option_vanilla,
+ options.ToolProgression.internal_name: options.ToolProgression.option_vanilla,
+ options.SkillProgression.internal_name: options.SkillProgression.option_vanilla,
+ options.BuildingProgression.internal_name: options.BuildingProgression.option_vanilla,
+ options.FestivalLocations.internal_name: options.FestivalLocations.option_disabled,
+ options.ElevatorProgression.internal_name: options.ElevatorProgression.option_vanilla,
+ options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_disabled,
+ options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_disabled,
+ options.QuestLocations.internal_name: -1,
+ options.Fishsanity.internal_name: options.Fishsanity.option_none,
+ options.Museumsanity.internal_name: options.Museumsanity.option_none,
+ options.Monstersanity.internal_name: options.Monstersanity.option_none,
+ options.Shipsanity.internal_name: options.Shipsanity.option_none,
+ options.Cooksanity.internal_name: options.Cooksanity.option_none,
+ options.Chefsanity.internal_name: options.Chefsanity.option_none,
+ options.Craftsanity.internal_name: options.Craftsanity.option_none,
+ options.Friendsanity.internal_name: options.Friendsanity.option_none,
+ options.FriendsanityHeartSize.internal_name: 8,
+ options.NumberOfMovementBuffs.internal_name: 0,
+ options.NumberOfLuckBuffs.internal_name: 0,
+ options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true,
+ options.TrapItems.internal_name: options.TrapItems.option_no_traps,
+ options.Mods.internal_name: frozenset(),
+ }
@cache_argsless
def minimal_locations_maximal_items():
min_max_options = {
- SeasonRandomization.internal_name: SeasonRandomization.option_randomized,
- Cropsanity.internal_name: Cropsanity.option_enabled,
- BackpackProgression.internal_name: BackpackProgression.option_vanilla,
- ToolProgression.internal_name: ToolProgression.option_vanilla,
- SkillProgression.internal_name: SkillProgression.option_vanilla,
- BuildingProgression.internal_name: BuildingProgression.option_vanilla,
- ElevatorProgression.internal_name: ElevatorProgression.option_vanilla,
- ArcadeMachineLocations.internal_name: ArcadeMachineLocations.option_disabled,
- SpecialOrderLocations.internal_name: SpecialOrderLocations.option_disabled,
- HelpWantedLocations.internal_name: 0,
- Fishsanity.internal_name: Fishsanity.option_none,
- Museumsanity.internal_name: Museumsanity.option_none,
- Friendsanity.internal_name: Friendsanity.option_none,
- NumberOfMovementBuffs.internal_name: 12,
- NumberOfLuckBuffs.internal_name: 12,
+ options.Goal.internal_name: options.Goal.option_craft_master,
+ options.BundleRandomization.internal_name: options.BundleRandomization.option_shuffled,
+ options.BundlePrice.internal_name: options.BundlePrice.option_expensive,
+ options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized,
+ options.Cropsanity.internal_name: options.Cropsanity.option_disabled,
+ options.BackpackProgression.internal_name: options.BackpackProgression.option_vanilla,
+ options.ToolProgression.internal_name: options.ToolProgression.option_vanilla,
+ options.SkillProgression.internal_name: options.SkillProgression.option_vanilla,
+ options.BuildingProgression.internal_name: options.BuildingProgression.option_vanilla,
+ options.FestivalLocations.internal_name: options.FestivalLocations.option_disabled,
+ options.ElevatorProgression.internal_name: options.ElevatorProgression.option_vanilla,
+ options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_disabled,
+ options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_disabled,
+ options.QuestLocations.internal_name: -1,
+ options.Fishsanity.internal_name: options.Fishsanity.option_none,
+ options.Museumsanity.internal_name: options.Museumsanity.option_none,
+ options.Monstersanity.internal_name: options.Monstersanity.option_none,
+ options.Shipsanity.internal_name: options.Shipsanity.option_none,
+ options.Cooksanity.internal_name: options.Cooksanity.option_none,
+ options.Chefsanity.internal_name: options.Chefsanity.option_none,
+ options.Craftsanity.internal_name: options.Craftsanity.option_none,
+ options.Friendsanity.internal_name: options.Friendsanity.option_none,
+ options.FriendsanityHeartSize.internal_name: 8,
+ options.NumberOfMovementBuffs.internal_name: 12,
+ options.NumberOfLuckBuffs.internal_name: 12,
+ options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true,
+ options.TrapItems.internal_name: options.TrapItems.option_nightmare,
+ options.Mods.internal_name: (),
}
return min_max_options
+@cache_argsless
+def minimal_locations_maximal_items_with_island():
+ min_max_options = minimal_locations_maximal_items().copy()
+ min_max_options.update({options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false})
+ return min_max_options
+
+
+@cache_argsless
+def allsanity_4_x_x_options_without_mods():
+ option_dict = {
+ options.Goal.internal_name: options.Goal.option_perfection,
+ options.BundleRandomization.internal_name: options.BundleRandomization.option_thematic,
+ options.BundlePrice.internal_name: options.BundlePrice.option_expensive,
+ options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized,
+ options.Cropsanity.internal_name: options.Cropsanity.option_enabled,
+ options.BackpackProgression.internal_name: options.BackpackProgression.option_progressive,
+ options.ToolProgression.internal_name: options.ToolProgression.option_progressive,
+ options.SkillProgression.internal_name: options.SkillProgression.option_progressive,
+ options.BuildingProgression.internal_name: options.BuildingProgression.option_progressive,
+ options.FestivalLocations.internal_name: options.FestivalLocations.option_hard,
+ options.ElevatorProgression.internal_name: options.ElevatorProgression.option_progressive,
+ options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_full_shuffling,
+ options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi,
+ options.QuestLocations.internal_name: 56,
+ options.Fishsanity.internal_name: options.Fishsanity.option_all,
+ options.Museumsanity.internal_name: options.Museumsanity.option_all,
+ options.Monstersanity.internal_name: options.Monstersanity.option_progressive_goals,
+ options.Shipsanity.internal_name: options.Shipsanity.option_everything,
+ options.Cooksanity.internal_name: options.Cooksanity.option_all,
+ options.Chefsanity.internal_name: options.Chefsanity.option_all,
+ options.Craftsanity.internal_name: options.Craftsanity.option_all,
+ options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
+ options.FriendsanityHeartSize.internal_name: 1,
+ options.NumberOfMovementBuffs.internal_name: 12,
+ options.NumberOfLuckBuffs.internal_name: 12,
+ options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false,
+ options.TrapItems.internal_name: options.TrapItems.option_nightmare,
+ }
+ option_dict.update(disable_5_x_x_options())
+ return option_dict
+
+
@cache_argsless
def allsanity_options_without_mods():
- allsanity = {
- Goal.internal_name: Goal.option_perfection,
- BundleRandomization.internal_name: BundleRandomization.option_shuffled,
- BundlePrice.internal_name: BundlePrice.option_expensive,
- SeasonRandomization.internal_name: SeasonRandomization.option_randomized,
- Cropsanity.internal_name: Cropsanity.option_enabled,
- BackpackProgression.internal_name: BackpackProgression.option_progressive,
- ToolProgression.internal_name: ToolProgression.option_progressive,
- SkillProgression.internal_name: SkillProgression.option_progressive,
- BuildingProgression.internal_name: BuildingProgression.option_progressive,
- FestivalLocations.internal_name: FestivalLocations.option_hard,
- ElevatorProgression.internal_name: ElevatorProgression.option_progressive,
- ArcadeMachineLocations.internal_name: ArcadeMachineLocations.option_full_shuffling,
- SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi,
- HelpWantedLocations.internal_name: 56,
- Fishsanity.internal_name: Fishsanity.option_all,
- Museumsanity.internal_name: Museumsanity.option_all,
- Friendsanity.internal_name: Friendsanity.option_all_with_marriage,
- FriendsanityHeartSize.internal_name: 1,
- NumberOfMovementBuffs.internal_name: 12,
- NumberOfLuckBuffs.internal_name: 12,
- ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_false,
- TrapItems.internal_name: TrapItems.option_nightmare,
+ return {
+ options.Goal.internal_name: options.Goal.option_perfection,
+ options.BundleRandomization.internal_name: options.BundleRandomization.option_thematic,
+ options.BundlePrice.internal_name: options.BundlePrice.option_expensive,
+ options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized,
+ options.Cropsanity.internal_name: options.Cropsanity.option_enabled,
+ options.BackpackProgression.internal_name: options.BackpackProgression.option_progressive,
+ options.ToolProgression.internal_name: options.ToolProgression.option_progressive,
+ options.SkillProgression.internal_name: options.SkillProgression.option_progressive,
+ options.BuildingProgression.internal_name: options.BuildingProgression.option_progressive,
+ options.FestivalLocations.internal_name: options.FestivalLocations.option_hard,
+ options.ElevatorProgression.internal_name: options.ElevatorProgression.option_progressive,
+ options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_full_shuffling,
+ options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi,
+ options.QuestLocations.internal_name: 56,
+ options.Fishsanity.internal_name: options.Fishsanity.option_all,
+ options.Museumsanity.internal_name: options.Museumsanity.option_all,
+ options.Monstersanity.internal_name: options.Monstersanity.option_progressive_goals,
+ options.Shipsanity.internal_name: options.Shipsanity.option_everything,
+ options.Cooksanity.internal_name: options.Cooksanity.option_all,
+ options.Chefsanity.internal_name: options.Chefsanity.option_all,
+ options.Craftsanity.internal_name: options.Craftsanity.option_all,
+ options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
+ options.FriendsanityHeartSize.internal_name: 1,
+ options.NumberOfMovementBuffs.internal_name: 12,
+ options.NumberOfLuckBuffs.internal_name: 12,
+ options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false,
+ options.TrapItems.internal_name: options.TrapItems.option_nightmare,
}
- return allsanity
@cache_argsless
def allsanity_options_with_mods():
- allsanity = {}
- allsanity.update(allsanity_options_without_mods())
- all_mods = (
- ModNames.deepwoods, ModNames.tractor, ModNames.big_backpack,
- ModNames.luck_skill, ModNames.magic, ModNames.socializing_skill, ModNames.archaeology,
- ModNames.cooking_skill, ModNames.binning_skill, ModNames.juna,
- ModNames.jasper, ModNames.alec, ModNames.yoba, ModNames.eugene,
- ModNames.wellwick, ModNames.ginger, ModNames.shiko, ModNames.delores,
- ModNames.ayeisha, ModNames.riley, ModNames.skull_cavern_elevator
- )
- allsanity.update({Mods.internal_name: all_mods})
+ allsanity = allsanity_options_without_mods().copy()
+ allsanity.update({options.Mods.internal_name: all_mods})
return allsanity
+class SVTestCase(unittest.TestCase):
+ # Set False to not skip some 'extra' tests
+ skip_base_tests: bool = True
+ # Set False to run tests that take long
+ skip_long_tests: bool = True
+
+ @classmethod
+ def setUpClass(cls) -> None:
+ super().setUpClass()
+ base_tests_key = "base"
+ if base_tests_key in os.environ:
+ cls.skip_base_tests = not bool(os.environ[base_tests_key])
+ long_tests_key = "long"
+ if long_tests_key in os.environ:
+ cls.skip_long_tests = not bool(os.environ[long_tests_key])
+
+ @contextmanager
+ def solo_world_sub_test(self, msg: Optional[str] = None,
+ /,
+ world_options: Optional[Dict[Union[str, StardewValleyOption], Any]] = None,
+ *,
+ seed=DEFAULT_TEST_SEED,
+ world_caching=True,
+ dirty_state=False,
+ **kwargs) -> Tuple[MultiWorld, StardewValleyWorld]:
+ if msg is not None:
+ msg += " "
+ else:
+ msg = ""
+ msg += f"[Seed = {seed}]"
+
+ with self.subTest(msg, **kwargs):
+ if world_caching:
+ multi_world = setup_solo_multiworld(world_options, seed)
+ if dirty_state:
+ original_state = multi_world.state.copy()
+ else:
+ multi_world = setup_solo_multiworld(world_options, seed, _cache={})
+
+ yield multi_world, multi_world.worlds[1]
+
+ if world_caching and dirty_state:
+ multi_world.state = original_state
+
+
+class SVTestBase(RuleAssertMixin, WorldTestBase, SVTestCase):
+ game = "Stardew Valley"
+ world: StardewValleyWorld
+ player: ClassVar[int] = 1
+
+ seed = DEFAULT_TEST_SEED
+
+ options = get_minsanity_options()
+
+ def world_setup(self, *args, **kwargs):
+ self.options = parse_class_option_keys(self.options)
+
+ super().world_setup(seed=self.seed)
+ if self.constructed:
+ self.world = self.multiworld.worlds[self.player] # noqa
+
+ @property
+ def run_default_tests(self) -> bool:
+ if self.skip_base_tests:
+ return False
+ # world_setup is overridden, so it'd always run default tests when importing SVTestBase
+ is_not_stardew_test = type(self) is not SVTestBase
+ should_run_default_tests = is_not_stardew_test and super().run_default_tests
+ return should_run_default_tests
+
+ def collect_lots_of_money(self):
+ self.multiworld.state.collect(self.world.create_item("Shipping Bin"), event=False)
+ for i in range(100):
+ self.multiworld.state.collect(self.world.create_item("Stardrop"), event=False)
+
+ def collect_all_the_money(self):
+ self.multiworld.state.collect(self.world.create_item("Shipping Bin"), event=False)
+ for i in range(1000):
+ self.multiworld.state.collect(self.world.create_item("Stardrop"), event=False)
+
+ def get_real_locations(self) -> List[Location]:
+ return [location for location in self.multiworld.get_locations(self.player) if not location.event]
+
+ def get_real_location_names(self) -> List[str]:
+ return [location.name for location in self.multiworld.get_locations(self.player) if not location.event]
+
+
pre_generated_worlds = {}
# Mostly a copy of test.general.setup_solo_multiworld, I just don't want to change the core.
-def setup_solo_multiworld(test_options=None, seed=None,
- _cache: Dict[FrozenSet[Tuple[str, Any]], MultiWorld] = {}) -> MultiWorld: # noqa
- if test_options is None:
- test_options = {}
+def setup_solo_multiworld(test_options: Optional[Dict[Union[str, StardewValleyOption], str]] = None,
+ seed=DEFAULT_TEST_SEED,
+ _cache: Dict[Hashable, MultiWorld] = {}, # noqa
+ _steps=gen_steps) -> MultiWorld:
+ test_options = parse_class_option_keys(test_options)
# Yes I reuse the worlds generated between tests, its speeds the execution by a couple seconds
- frozen_options = frozenset(test_options.items()).union({seed})
- if frozen_options in _cache:
- return _cache[frozen_options]
+ should_cache = "start_inventory" not in test_options
+ frozen_options = frozenset({})
+ if should_cache:
+ frozen_options = frozenset(test_options.items()).union({seed})
+ if frozen_options in _cache:
+ cached_multi_world = _cache[frozen_options]
+ print(f"Using cached solo multi world [Seed = {cached_multi_world.seed}]")
+ return cached_multi_world
- multiworld = setup_base_solo_multiworld(StardewValleyWorld, ())
- multiworld.set_seed(seed)
+ multiworld = setup_base_solo_multiworld(StardewValleyWorld, (), seed=seed)
# print(f"Seed: {multiworld.seed}") # Uncomment to print the seed for every test
+
args = Namespace()
for name, option in StardewValleyWorld.options_dataclass.type_hints.items():
- value = option(test_options[name]) if name in test_options else option.from_any(option.default)
+ value = option.from_any(test_options.get(name, option.default))
+
+ if issubclass(option, VerifyKeys):
+ # Values should already be verified, but just in case...
+ option.verify_keys(value.value)
+
setattr(args, name, {1: value})
multiworld.set_options(args)
- for step in gen_steps:
+
+ if "start_inventory" in test_options:
+ for item, amount in test_options["start_inventory"].items():
+ for _ in range(amount):
+ multiworld.push_precollected(multiworld.create_item(item, 1))
+
+ for step in _steps:
call_all(multiworld, step)
- _cache[frozen_options] = multiworld
+ if should_cache:
+ _cache[frozen_options] = multiworld
+
+ return multiworld
+
+
+def parse_class_option_keys(test_options: dict) -> dict:
+ """ Now the option class is allowed as key. """
+ parsed_options = {}
+
+ if test_options:
+ for option, value in test_options.items():
+ if hasattr(option, "internal_name"):
+ assert option.internal_name not in test_options, "Defined two times by class and internal_name"
+ parsed_options[option.internal_name] = value
+ else:
+ assert option in StardewValleyOptions.type_hints, \
+ f"All keys of world_options must be a possible Stardew Valley option, {option} is not."
+ parsed_options[option] = value
+
+ return parsed_options
+
+
+def complete_options_with_default(options_to_complete=None) -> StardewValleyOptions:
+ if options_to_complete is None:
+ options_to_complete = {}
+
+ for name, option in StardewValleyOptions.type_hints.items():
+ options_to_complete[name] = option.from_any(options_to_complete.get(name, option.default))
+
+ return StardewValleyOptions(**options_to_complete)
+
+
+def setup_multiworld(test_options: Iterable[Dict[str, int]] = None, seed=None) -> MultiWorld: # noqa
+ if test_options is None:
+ test_options = []
+
+ multiworld = MultiWorld(len(test_options))
+ multiworld.player_name = {}
+ multiworld.set_seed(seed)
+ multiworld.state = CollectionState(multiworld)
+ for i in range(1, len(test_options) + 1):
+ multiworld.game[i] = StardewValleyWorld.game
+ multiworld.player_name.update({i: f"Tester{i}"})
+ args = Namespace()
+ for name, option in StardewValleyWorld.options_dataclass.type_hints.items():
+ options = {}
+ for i in range(1, len(test_options) + 1):
+ player_options = test_options[i - 1]
+ value = option(player_options[name]) if name in player_options else option.from_any(option.default)
+ options.update({i: value})
+ setattr(args, name, options)
+ multiworld.set_options(args)
+
+ for step in gen_steps:
+ call_all(multiworld, step)
return multiworld
diff --git a/worlds/stardew_valley/test/assertion/__init__.py b/worlds/stardew_valley/test/assertion/__init__.py
new file mode 100644
index 000000000000..3a1420fe65e4
--- /dev/null
+++ b/worlds/stardew_valley/test/assertion/__init__.py
@@ -0,0 +1,5 @@
+from .goal_assert import *
+from .mod_assert import *
+from .option_assert import *
+from .rule_assert import *
+from .world_assert import *
diff --git a/worlds/stardew_valley/test/assertion/goal_assert.py b/worlds/stardew_valley/test/assertion/goal_assert.py
new file mode 100644
index 000000000000..2b2efbf2ec03
--- /dev/null
+++ b/worlds/stardew_valley/test/assertion/goal_assert.py
@@ -0,0 +1,55 @@
+from unittest import TestCase
+
+from BaseClasses import MultiWorld
+from .option_assert import get_stardew_options
+from ... import options, ExcludeGingerIsland
+
+
+def is_goal(multiworld: MultiWorld, goal: int) -> bool:
+ return get_stardew_options(multiworld).goal.value == goal
+
+
+def is_bottom_mines(multiworld: MultiWorld) -> bool:
+ return is_goal(multiworld, options.Goal.option_bottom_of_the_mines)
+
+
+def is_not_bottom_mines(multiworld: MultiWorld) -> bool:
+ return not is_bottom_mines(multiworld)
+
+
+def is_walnut_hunter(multiworld: MultiWorld) -> bool:
+ return is_goal(multiworld, options.Goal.option_greatest_walnut_hunter)
+
+
+def is_not_walnut_hunter(multiworld: MultiWorld) -> bool:
+ return not is_walnut_hunter(multiworld)
+
+
+def is_perfection(multiworld: MultiWorld) -> bool:
+ return is_goal(multiworld, options.Goal.option_perfection)
+
+
+def is_not_perfection(multiworld: MultiWorld) -> bool:
+ return not is_perfection(multiworld)
+
+
+class GoalAssertMixin(TestCase):
+
+ def assert_ginger_island_is_included(self, multiworld: MultiWorld):
+ self.assertEqual(get_stardew_options(multiworld).exclude_ginger_island, ExcludeGingerIsland.option_false)
+
+ def assert_walnut_hunter_world_is_valid(self, multiworld: MultiWorld):
+ if is_not_walnut_hunter(multiworld):
+ return
+
+ self.assert_ginger_island_is_included(multiworld)
+
+ def assert_perfection_world_is_valid(self, multiworld: MultiWorld):
+ if is_not_perfection(multiworld):
+ return
+
+ self.assert_ginger_island_is_included(multiworld)
+
+ def assert_goal_world_is_valid(self, multiworld: MultiWorld):
+ self.assert_walnut_hunter_world_is_valid(multiworld)
+ self.assert_perfection_world_is_valid(multiworld)
diff --git a/worlds/stardew_valley/test/assertion/mod_assert.py b/worlds/stardew_valley/test/assertion/mod_assert.py
new file mode 100644
index 000000000000..4f72c9a3977e
--- /dev/null
+++ b/worlds/stardew_valley/test/assertion/mod_assert.py
@@ -0,0 +1,26 @@
+from typing import Union, List
+from unittest import TestCase
+
+from BaseClasses import MultiWorld
+from ... import item_table, location_table
+from ...mods.mod_data import ModNames
+
+
+class ModAssertMixin(TestCase):
+ def assert_stray_mod_items(self, chosen_mods: Union[List[str], str], multiworld: MultiWorld):
+ if isinstance(chosen_mods, str):
+ chosen_mods = [chosen_mods]
+
+ if ModNames.jasper in chosen_mods:
+ # Jasper is a weird case because it shares NPC w/ SVE...
+ chosen_mods.append(ModNames.sve)
+
+ for multiworld_item in multiworld.get_items():
+ item = item_table[multiworld_item.name]
+ self.assertTrue(item.mod_name is None or item.mod_name in chosen_mods,
+ f"Item {item.name} has is from mod {item.mod_name}. Allowed mods are {chosen_mods}.")
+ for multiworld_location in multiworld.get_locations():
+ if multiworld_location.event:
+ continue
+ location = location_table[multiworld_location.name]
+ self.assertTrue(location.mod_name is None or location.mod_name in chosen_mods)
diff --git a/worlds/stardew_valley/test/assertion/option_assert.py b/worlds/stardew_valley/test/assertion/option_assert.py
new file mode 100644
index 000000000000..b384858f34f4
--- /dev/null
+++ b/worlds/stardew_valley/test/assertion/option_assert.py
@@ -0,0 +1,96 @@
+from unittest import TestCase
+
+from BaseClasses import MultiWorld
+from .world_assert import get_all_item_names, get_all_location_names
+from ... import StardewValleyWorld, options, item_table, Group, location_table, ExcludeGingerIsland
+from ...locations import LocationTags
+from ...strings.ap_names.transport_names import Transportation
+
+
+def get_stardew_world(multiworld: MultiWorld) -> StardewValleyWorld:
+ for world_key in multiworld.worlds:
+ world = multiworld.worlds[world_key]
+ if isinstance(world, StardewValleyWorld):
+ return world
+ raise ValueError("no stardew world in this multiworld")
+
+
+def get_stardew_options(multiworld: MultiWorld) -> options.StardewValleyOptions:
+ return get_stardew_world(multiworld).options
+
+
+class OptionAssertMixin(TestCase):
+
+ def assert_has_item(self, multiworld: MultiWorld, item: str):
+ all_item_names = set(get_all_item_names(multiworld))
+ self.assertIn(item, all_item_names)
+
+ def assert_has_not_item(self, multiworld: MultiWorld, item: str):
+ all_item_names = set(get_all_item_names(multiworld))
+ self.assertNotIn(item, all_item_names)
+
+ def assert_has_location(self, multiworld: MultiWorld, item: str):
+ all_location_names = set(get_all_location_names(multiworld))
+ self.assertIn(item, all_location_names)
+
+ def assert_has_not_location(self, multiworld: MultiWorld, item: str):
+ all_location_names = set(get_all_location_names(multiworld))
+ self.assertNotIn(item, all_location_names)
+
+ def assert_can_reach_island(self, multiworld: MultiWorld):
+ all_item_names = get_all_item_names(multiworld)
+ self.assertIn(Transportation.boat_repair, all_item_names)
+ self.assertIn(Transportation.island_obelisk, all_item_names)
+
+ def assert_cannot_reach_island(self, multiworld: MultiWorld):
+ all_item_names = get_all_item_names(multiworld)
+ self.assertNotIn(Transportation.boat_repair, all_item_names)
+ self.assertNotIn(Transportation.island_obelisk, all_item_names)
+
+ def assert_can_reach_island_if_should(self, multiworld: MultiWorld):
+ stardew_options = get_stardew_options(multiworld)
+ include_island = stardew_options.exclude_ginger_island.value == ExcludeGingerIsland.option_false
+ if include_island:
+ self.assert_can_reach_island(multiworld)
+ else:
+ self.assert_cannot_reach_island(multiworld)
+
+ def assert_cropsanity_same_number_items_and_locations(self, multiworld: MultiWorld):
+ is_cropsanity = get_stardew_options(multiworld).cropsanity.value == options.Cropsanity.option_enabled
+ if not is_cropsanity:
+ return
+
+ all_item_names = set(get_all_item_names(multiworld))
+ all_location_names = set(get_all_location_names(multiworld))
+ all_cropsanity_item_names = {item_name for item_name in all_item_names if Group.CROPSANITY in item_table[item_name].groups}
+ all_cropsanity_location_names = {location_name for location_name in all_location_names if LocationTags.CROPSANITY in location_table[location_name].tags}
+ self.assertEqual(len(all_cropsanity_item_names), len(all_cropsanity_location_names))
+
+ def assert_all_rarecrows_exist(self, multiworld: MultiWorld):
+ all_item_names = set(get_all_item_names(multiworld))
+ for rarecrow_number in range(1, 9):
+ self.assertIn(f"Rarecrow #{rarecrow_number}", all_item_names)
+
+ def assert_has_deluxe_scarecrow_recipe(self, multiworld: MultiWorld):
+ self.assert_has_item(multiworld, "Deluxe Scarecrow Recipe")
+
+ def assert_festivals_give_access_to_deluxe_scarecrow(self, multiworld: MultiWorld):
+ stardew_options = get_stardew_options(multiworld)
+ has_festivals = stardew_options.festival_locations.value != options.FestivalLocations.option_disabled
+ if not has_festivals:
+ return
+
+ self.assert_all_rarecrows_exist(multiworld)
+ self.assert_has_deluxe_scarecrow_recipe(multiworld)
+
+ def assert_has_festival_recipes(self, multiworld: MultiWorld):
+ stardew_options = get_stardew_options(multiworld)
+ has_festivals = stardew_options.festival_locations.value != options.FestivalLocations.option_disabled
+ festival_items = ["Tub o' Flowers Recipe", "Jack-O-Lantern Recipe"]
+ for festival_item in festival_items:
+ if has_festivals:
+ self.assert_has_item(multiworld, festival_item)
+ self.assert_has_location(multiworld, festival_item)
+ else:
+ self.assert_has_not_item(multiworld, festival_item)
+ self.assert_has_not_location(multiworld, festival_item)
diff --git a/worlds/stardew_valley/test/assertion/rule_assert.py b/worlds/stardew_valley/test/assertion/rule_assert.py
new file mode 100644
index 000000000000..f9b12394311a
--- /dev/null
+++ b/worlds/stardew_valley/test/assertion/rule_assert.py
@@ -0,0 +1,17 @@
+from unittest import TestCase
+
+from BaseClasses import CollectionState
+from .rule_explain import explain
+from ...stardew_rule import StardewRule, false_, MISSING_ITEM
+
+
+class RuleAssertMixin(TestCase):
+ def assert_rule_true(self, rule: StardewRule, state: CollectionState):
+ self.assertTrue(rule(state), explain(rule, state))
+
+ def assert_rule_false(self, rule: StardewRule, state: CollectionState):
+ self.assertFalse(rule(state), explain(rule, state, expected=False))
+
+ def assert_rule_can_be_resolved(self, rule: StardewRule, complete_state: CollectionState):
+ self.assertNotIn(MISSING_ITEM, repr(rule))
+ self.assertTrue(rule is false_ or rule(complete_state), explain(rule, complete_state))
diff --git a/worlds/stardew_valley/test/assertion/rule_explain.py b/worlds/stardew_valley/test/assertion/rule_explain.py
new file mode 100644
index 000000000000..f9bf97603404
--- /dev/null
+++ b/worlds/stardew_valley/test/assertion/rule_explain.py
@@ -0,0 +1,102 @@
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+from functools import cached_property, singledispatch
+from typing import Iterable
+
+from BaseClasses import CollectionState
+from worlds.generic.Rules import CollectionRule
+from ...stardew_rule import StardewRule, AggregatingStardewRule, Count, Has, TotalReceived, Received, Reach
+
+max_explanation_depth = 10
+
+
+@dataclass
+class RuleExplanation:
+ rule: StardewRule
+ state: CollectionState
+ expected: bool
+ sub_rules: Iterable[StardewRule] = field(default_factory=list)
+
+ def summary(self, depth=0):
+ return " " * depth + f"{str(self.rule)} -> {self.result}"
+
+ def __str__(self, depth=0):
+ if not self.sub_rules or depth >= max_explanation_depth:
+ return self.summary(depth)
+
+ return self.summary(depth) + "\n" + "\n".join(RuleExplanation.__str__(i, depth + 1)
+ if i.result is not self.expected else i.summary(depth + 1)
+ for i in sorted(self.explained_sub_rules, key=lambda x: x.result))
+
+ def __repr__(self, depth=0):
+ if not self.sub_rules or depth >= max_explanation_depth:
+ return self.summary(depth)
+
+ return self.summary(depth) + "\n" + "\n".join(RuleExplanation.__repr__(i, depth + 1)
+ for i in sorted(self.explained_sub_rules, key=lambda x: x.result))
+
+ @cached_property
+ def result(self):
+ return self.rule(self.state)
+
+ @cached_property
+ def explained_sub_rules(self):
+ return [_explain(i, self.state, self.expected) for i in self.sub_rules]
+
+
+def explain(rule: CollectionRule, state: CollectionState, expected: bool = True) -> RuleExplanation:
+ if isinstance(rule, StardewRule):
+ return _explain(rule, state, expected)
+ else:
+ return f"Value of rule {str(rule)} was not {str(expected)} in {str(state)}" # noqa
+
+
+@singledispatch
+def _explain(rule: StardewRule, state: CollectionState, expected: bool) -> RuleExplanation:
+ return RuleExplanation(rule, state, expected)
+
+
+@_explain.register
+def _(rule: AggregatingStardewRule, state: CollectionState, expected: bool) -> RuleExplanation:
+ return RuleExplanation(rule, state, expected, rule.original_rules)
+
+
+@_explain.register
+def _(rule: Count, state: CollectionState, expected: bool) -> RuleExplanation:
+ return RuleExplanation(rule, state, expected, rule.rules)
+
+
+@_explain.register
+def _(rule: Has, state: CollectionState, expected: bool) -> RuleExplanation:
+ return RuleExplanation(rule, state, expected, [rule.other_rules[rule.item]])
+
+
+@_explain.register
+def _(rule: TotalReceived, state: CollectionState, expected=True) -> RuleExplanation:
+ return RuleExplanation(rule, state, expected, [Received(i, rule.player, 1) for i in rule.items])
+
+
+@_explain.register
+def _(rule: Reach, state: CollectionState, expected=True) -> RuleExplanation:
+ access_rules = None
+ if rule.resolution_hint == 'Location':
+ spot = state.multiworld.get_location(rule.spot, rule.player)
+
+ if isinstance(spot.access_rule, StardewRule):
+ access_rules = [spot.access_rule, Reach(spot.parent_region.name, "Region", rule.player)]
+
+ elif rule.resolution_hint == 'Entrance':
+ spot = state.multiworld.get_entrance(rule.spot, rule.player)
+
+ if isinstance(spot.access_rule, StardewRule):
+ access_rules = [spot.access_rule, Reach(spot.parent_region.name, "Region", rule.player)]
+
+ else:
+ spot = state.multiworld.get_region(rule.spot, rule.player)
+ access_rules = [*(Reach(e.name, "Entrance", rule.player) for e in spot.entrances)]
+
+ if not access_rules:
+ return RuleExplanation(rule, state, expected)
+
+ return RuleExplanation(rule, state, expected, access_rules)
diff --git a/worlds/stardew_valley/test/assertion/world_assert.py b/worlds/stardew_valley/test/assertion/world_assert.py
new file mode 100644
index 000000000000..413517e1c912
--- /dev/null
+++ b/worlds/stardew_valley/test/assertion/world_assert.py
@@ -0,0 +1,83 @@
+from typing import List
+from unittest import TestCase
+
+from BaseClasses import MultiWorld, ItemClassification
+from .rule_assert import RuleAssertMixin
+from ... import StardewItem
+from ...items import items_by_group, Group
+from ...locations import LocationTags, locations_by_tag
+
+
+def get_all_item_names(multiworld: MultiWorld) -> List[str]:
+ return [item.name for item in multiworld.itempool]
+
+
+def get_all_location_names(multiworld: MultiWorld) -> List[str]:
+ return [location.name for location in multiworld.get_locations() if not location.event]
+
+
+class WorldAssertMixin(RuleAssertMixin, TestCase):
+
+ def assert_victory_exists(self, multiworld: MultiWorld):
+ self.assertIn(StardewItem("Victory", ItemClassification.progression, None, 1), multiworld.get_items())
+
+ def assert_can_reach_victory(self, multiworld: MultiWorld):
+ victory = multiworld.find_item("Victory", 1)
+ self.assert_rule_true(victory.access_rule, multiworld.state)
+
+ def assert_cannot_reach_victory(self, multiworld: MultiWorld):
+ victory = multiworld.find_item("Victory", 1)
+ self.assert_rule_false(victory.access_rule, multiworld.state)
+
+ def assert_item_was_necessary_for_victory(self, item: StardewItem, multiworld: MultiWorld):
+ self.assert_can_reach_victory(multiworld)
+ multiworld.state.remove(item)
+ self.assert_cannot_reach_victory(multiworld)
+ multiworld.state.collect(item, event=False)
+ self.assert_can_reach_victory(multiworld)
+
+ def assert_item_was_not_necessary_for_victory(self, item: StardewItem, multiworld: MultiWorld):
+ self.assert_can_reach_victory(multiworld)
+ multiworld.state.remove(item)
+ self.assert_can_reach_victory(multiworld)
+ multiworld.state.collect(item, event=False)
+ self.assert_can_reach_victory(multiworld)
+
+ def assert_can_win(self, multiworld: MultiWorld):
+ self.assert_victory_exists(multiworld)
+ self.assert_can_reach_victory(multiworld)
+
+ def assert_same_number_items_locations(self, multiworld: MultiWorld):
+ non_event_locations = [location for location in multiworld.get_locations() if not location.event]
+ self.assertEqual(len(multiworld.itempool), len(non_event_locations))
+
+ def assert_can_reach_everything(self, multiworld: MultiWorld):
+ for location in multiworld.get_locations():
+ self.assert_rule_true(location.access_rule, multiworld.state)
+
+ def assert_basic_checks(self, multiworld: MultiWorld):
+ self.assert_same_number_items_locations(multiworld)
+ non_event_items = [item for item in multiworld.get_items() if item.code]
+ for item in non_event_items:
+ multiworld.state.collect(item)
+ self.assert_can_win(multiworld)
+ self.assert_can_reach_everything(multiworld)
+
+ def assert_basic_checks_with_subtests(self, multiworld: MultiWorld):
+ with self.subTest("same_number_items_locations"):
+ self.assert_same_number_items_locations(multiworld)
+ non_event_items = [item for item in multiworld.get_items() if item.code]
+ for item in non_event_items:
+ multiworld.state.collect(item)
+ with self.subTest("can_win"):
+ self.assert_can_win(multiworld)
+ with self.subTest("can_reach_everything"):
+ self.assert_can_reach_everything(multiworld)
+
+ def assert_no_ginger_island_content(self, multiworld: MultiWorld):
+ ginger_island_items = [item_data.name for item_data in items_by_group[Group.GINGER_ISLAND]]
+ ginger_island_locations = [location_data.name for location_data in locations_by_tag[LocationTags.GINGER_ISLAND]]
+ for item in multiworld.get_items():
+ self.assertNotIn(item.name, ginger_island_items)
+ for location in multiworld.get_locations():
+ self.assertNotIn(location.name, ginger_island_locations)
diff --git a/worlds/stardew_valley/test/checks/goal_checks.py b/worlds/stardew_valley/test/checks/goal_checks.py
deleted file mode 100644
index d0f06a6caafa..000000000000
--- a/worlds/stardew_valley/test/checks/goal_checks.py
+++ /dev/null
@@ -1,55 +0,0 @@
-from BaseClasses import MultiWorld
-from .option_checks import get_stardew_options
-from ... import options
-from .. import SVTestBase
-
-
-def is_goal(multiworld: MultiWorld, goal: int) -> bool:
- return get_stardew_options(multiworld).goal.value == goal
-
-
-def is_bottom_mines(multiworld: MultiWorld) -> bool:
- return is_goal(multiworld, options.Goal.option_bottom_of_the_mines)
-
-
-def is_not_bottom_mines(multiworld: MultiWorld) -> bool:
- return not is_bottom_mines(multiworld)
-
-
-def is_walnut_hunter(multiworld: MultiWorld) -> bool:
- return is_goal(multiworld, options.Goal.option_greatest_walnut_hunter)
-
-
-def is_not_walnut_hunter(multiworld: MultiWorld) -> bool:
- return not is_walnut_hunter(multiworld)
-
-
-def is_perfection(multiworld: MultiWorld) -> bool:
- return is_goal(multiworld, options.Goal.option_perfection)
-
-
-def is_not_perfection(multiworld: MultiWorld) -> bool:
- return not is_perfection(multiworld)
-
-
-def assert_ginger_island_is_included(tester: SVTestBase, multiworld: MultiWorld):
- tester.assertEqual(get_stardew_options(multiworld).exclude_ginger_island, options.ExcludeGingerIsland.option_false)
-
-
-def assert_walnut_hunter_world_is_valid(tester: SVTestBase, multiworld: MultiWorld):
- if is_not_walnut_hunter(multiworld):
- return
-
- assert_ginger_island_is_included(tester, multiworld)
-
-
-def assert_perfection_world_is_valid(tester: SVTestBase, multiworld: MultiWorld):
- if is_not_perfection(multiworld):
- return
-
- assert_ginger_island_is_included(tester, multiworld)
-
-
-def assert_goal_world_is_valid(tester: SVTestBase, multiworld: MultiWorld):
- assert_walnut_hunter_world_is_valid(tester, multiworld)
- assert_perfection_world_is_valid(tester, multiworld)
diff --git a/worlds/stardew_valley/test/checks/option_checks.py b/worlds/stardew_valley/test/checks/option_checks.py
deleted file mode 100644
index c9d9860cf52b..000000000000
--- a/worlds/stardew_valley/test/checks/option_checks.py
+++ /dev/null
@@ -1,72 +0,0 @@
-from BaseClasses import MultiWorld
-from .world_checks import get_all_item_names, get_all_location_names
-from .. import SVTestBase
-from ... import StardewValleyWorld, options, item_table, Group, location_table
-from ...locations import LocationTags
-from ...strings.ap_names.transport_names import Transportation
-
-
-def get_stardew_world(multiworld: MultiWorld) -> StardewValleyWorld:
- for world_key in multiworld.worlds:
- world = multiworld.worlds[world_key]
- if isinstance(world, StardewValleyWorld):
- return world
- raise ValueError("no stardew world in this multiworld")
-
-
-def get_stardew_options(multiworld: MultiWorld) -> options.StardewValleyOptions:
- return get_stardew_world(multiworld).options
-
-
-def assert_can_reach_island(tester: SVTestBase, multiworld: MultiWorld):
- all_item_names = get_all_item_names(multiworld)
- tester.assertIn(Transportation.boat_repair, all_item_names)
- tester.assertIn(Transportation.island_obelisk, all_item_names)
-
-
-def assert_cannot_reach_island(tester: SVTestBase, multiworld: MultiWorld):
- all_item_names = get_all_item_names(multiworld)
- tester.assertNotIn(Transportation.boat_repair, all_item_names)
- tester.assertNotIn(Transportation.island_obelisk, all_item_names)
-
-
-def assert_can_reach_island_if_should(tester: SVTestBase, multiworld: MultiWorld):
- stardew_options = get_stardew_options(multiworld)
- include_island = stardew_options.exclude_ginger_island.value == options.ExcludeGingerIsland.option_false
- if include_island:
- assert_can_reach_island(tester, multiworld)
- else:
- assert_cannot_reach_island(tester, multiworld)
-
-
-def assert_cropsanity_same_number_items_and_locations(tester: SVTestBase, multiworld: MultiWorld):
- is_cropsanity = get_stardew_options(multiworld).cropsanity.value == options.Cropsanity.option_enabled
- if not is_cropsanity:
- return
-
- all_item_names = set(get_all_item_names(multiworld))
- all_location_names = set(get_all_location_names(multiworld))
- all_cropsanity_item_names = {item_name for item_name in all_item_names if Group.CROPSANITY in item_table[item_name].groups}
- all_cropsanity_location_names = {location_name for location_name in all_location_names if LocationTags.CROPSANITY in location_table[location_name].tags}
- tester.assertEqual(len(all_cropsanity_item_names), len(all_cropsanity_location_names))
-
-
-def assert_all_rarecrows_exist(tester: SVTestBase, multiworld: MultiWorld):
- all_item_names = set(get_all_item_names(multiworld))
- for rarecrow_number in range(1, 9):
- tester.assertIn(f"Rarecrow #{rarecrow_number}", all_item_names)
-
-
-def assert_has_deluxe_scarecrow_recipe(tester: SVTestBase, multiworld: MultiWorld):
- all_item_names = set(get_all_item_names(multiworld))
- tester.assertIn(f"Deluxe Scarecrow Recipe", all_item_names)
-
-
-def assert_festivals_give_access_to_deluxe_scarecrow(tester: SVTestBase, multiworld: MultiWorld):
- stardew_options = get_stardew_options(multiworld)
- has_festivals = stardew_options.festival_locations.value != options.FestivalLocations.option_disabled
- if not has_festivals:
- return
-
- assert_all_rarecrows_exist(tester, multiworld)
- assert_has_deluxe_scarecrow_recipe(tester, multiworld)
diff --git a/worlds/stardew_valley/test/checks/world_checks.py b/worlds/stardew_valley/test/checks/world_checks.py
deleted file mode 100644
index 9bd9fd614c26..000000000000
--- a/worlds/stardew_valley/test/checks/world_checks.py
+++ /dev/null
@@ -1,33 +0,0 @@
-import unittest
-from typing import List
-
-from BaseClasses import MultiWorld, ItemClassification
-from ... import StardewItem
-
-
-def get_all_item_names(multiworld: MultiWorld) -> List[str]:
- return [item.name for item in multiworld.itempool]
-
-
-def get_all_location_names(multiworld: MultiWorld) -> List[str]:
- return [location.name for location in multiworld.get_locations() if not location.event]
-
-
-def assert_victory_exists(tester: unittest.TestCase, multiworld: MultiWorld):
- tester.assertIn(StardewItem("Victory", ItemClassification.progression, None, 1), multiworld.get_items())
-
-
-def collect_all_then_assert_can_win(tester: unittest.TestCase, multiworld: MultiWorld):
- for item in multiworld.get_items():
- multiworld.state.collect(item)
- tester.assertTrue(multiworld.find_item("Victory", 1).can_reach(multiworld.state))
-
-
-def assert_can_win(tester: unittest.TestCase, multiworld: MultiWorld):
- assert_victory_exists(tester, multiworld)
- collect_all_then_assert_can_win(tester, multiworld)
-
-
-def assert_same_number_items_locations(tester: unittest.TestCase, multiworld: MultiWorld):
- non_event_locations = [location for location in multiworld.get_locations() if not location.event]
- tester.assertEqual(len(multiworld.itempool), len(non_event_locations))
\ No newline at end of file
diff --git a/worlds/stardew_valley/test/long/TestModsLong.py b/worlds/stardew_valley/test/long/TestModsLong.py
index 36a59ae854e5..9f76c10a9da4 100644
--- a/worlds/stardew_valley/test/long/TestModsLong.py
+++ b/worlds/stardew_valley/test/long/TestModsLong.py
@@ -1,57 +1,53 @@
import unittest
-from typing import List, Union
+from itertools import combinations, product
-from BaseClasses import MultiWorld
-from worlds.stardew_valley.mods.mod_data import all_mods
-from worlds.stardew_valley.test import setup_solo_multiworld
-from worlds.stardew_valley.test.TestOptions import basic_checks, SVTestCase
-from worlds.stardew_valley.items import item_table
-from worlds.stardew_valley.locations import location_table
-from worlds.stardew_valley.options import Mods
-from .option_names import options_to_include
+from BaseClasses import get_seed
+from .option_names import all_option_choices
+from .. import SVTestCase
+from ..assertion import WorldAssertMixin, ModAssertMixin
+from ... import options
+from ...mods.mod_data import all_mods, ModNames
+assert unittest
-def check_stray_mod_items(chosen_mods: Union[List[str], str], tester: unittest.TestCase, multiworld: MultiWorld):
- if isinstance(chosen_mods, str):
- chosen_mods = [chosen_mods]
- for multiworld_item in multiworld.get_items():
- item = item_table[multiworld_item.name]
- tester.assertTrue(item.mod_name is None or item.mod_name in chosen_mods)
- for multiworld_location in multiworld.get_locations():
- if multiworld_location.event:
- continue
- location = location_table[multiworld_location.name]
- tester.assertTrue(location.mod_name is None or location.mod_name in chosen_mods)
-
-class TestGenerateModsOptions(SVTestCase):
+class TestGenerateModsOptions(WorldAssertMixin, ModAssertMixin, SVTestCase):
def test_given_mod_pairs_when_generate_then_basic_checks(self):
if self.skip_long_tests:
return
- mods = list(all_mods)
- num_mods = len(mods)
- for mod1_index in range(0, num_mods):
- for mod2_index in range(mod1_index + 1, num_mods):
- mod1 = mods[mod1_index]
- mod2 = mods[mod2_index]
- mod_pair = (mod1, mod2)
- with self.subTest(f"Mods: {mod_pair}"):
- multiworld = setup_solo_multiworld({Mods: mod_pair})
- basic_checks(self, multiworld)
- check_stray_mod_items(list(mod_pair), self, multiworld)
+
+ for mod_pair in combinations(all_mods, 2):
+ world_options = {
+ options.Mods: frozenset(mod_pair)
+ }
+
+ with self.solo_world_sub_test(f"Mods: {mod_pair}", world_options, world_caching=False) as (multiworld, _):
+ self.assert_basic_checks(multiworld)
+ self.assert_stray_mod_items(list(mod_pair), multiworld)
def test_given_mod_names_when_generate_paired_with_other_options_then_basic_checks(self):
if self.skip_long_tests:
return
- num_options = len(options_to_include)
- for option_index in range(0, num_options):
- option = options_to_include[option_index]
- if not option.options:
- continue
- for value in option.options:
- for mod in all_mods:
- with self.subTest(f"{option.internal_name}: {value}, Mod: {mod}"):
- multiworld = setup_solo_multiworld({option.internal_name: option.options[value], Mods: mod})
- basic_checks(self, multiworld)
- check_stray_mod_items(mod, self, multiworld)
\ No newline at end of file
+
+ for mod, (option, value) in product(all_mods, all_option_choices):
+ world_options = {
+ option: value,
+ options.Mods: mod
+ }
+
+ with self.solo_world_sub_test(f"{option.internal_name}: {value}, Mod: {mod}", world_options, world_caching=False) as (multiworld, _):
+ self.assert_basic_checks(multiworld)
+ self.assert_stray_mod_items(mod, multiworld)
+
+ # @unittest.skip
+ def test_troubleshoot_option(self):
+ seed = get_seed(45949559493817417717)
+ world_options = {
+ options.ElevatorProgression: options.ElevatorProgression.option_vanilla,
+ options.Mods: ModNames.deepwoods
+ }
+
+ with self.solo_world_sub_test(world_options=world_options, seed=seed, world_caching=False) as (multiworld, _):
+ self.assert_basic_checks(multiworld)
+ self.assert_stray_mod_items(world_options[options.Mods], multiworld)
diff --git a/worlds/stardew_valley/test/long/TestOptionsLong.py b/worlds/stardew_valley/test/long/TestOptionsLong.py
index e3da6968ed43..ca9fc01b2922 100644
--- a/worlds/stardew_valley/test/long/TestOptionsLong.py
+++ b/worlds/stardew_valley/test/long/TestOptionsLong.py
@@ -1,41 +1,43 @@
-import unittest
-from typing import Dict
+from itertools import combinations
-from BaseClasses import MultiWorld
-from Options import NamedRange
-from .option_names import options_to_include
-from worlds.stardew_valley.test.checks.world_checks import assert_can_win, assert_same_number_items_locations
+from .option_names import all_option_choices
from .. import setup_solo_multiworld, SVTestCase
+from ..assertion.world_assert import WorldAssertMixin
+from ... import options
-def basic_checks(tester: unittest.TestCase, multiworld: MultiWorld):
- assert_can_win(tester, multiworld)
- assert_same_number_items_locations(tester, multiworld)
+class TestGenerateDynamicOptions(WorldAssertMixin, SVTestCase):
+ def test_given_option_pair_when_generate_then_basic_checks(self):
+ if self.skip_long_tests:
+ return
+ for (option1, option1_choice), (option2, option2_choice) in combinations(all_option_choices, 2):
+ if option1 is option2:
+ continue
-def get_option_choices(option) -> Dict[str, int]:
- if issubclass(option, NamedRange):
- return option.special_range_names
- elif option.options:
- return option.options
- return {}
+ world_options = {
+ option1: option1_choice,
+ option2: option2_choice
+ }
+ with self.solo_world_sub_test(f"{option1.internal_name}: {option1_choice}, {option2.internal_name}: {option2_choice}",
+ world_options,
+ world_caching=False) \
+ as (multiworld, _):
+ self.assert_basic_checks(multiworld)
-class TestGenerateDynamicOptions(SVTestCase):
- def test_given_option_pair_when_generate_then_basic_checks(self):
- if self.skip_long_tests:
- return
- num_options = len(options_to_include)
- for option1_index in range(0, num_options):
- for option2_index in range(option1_index + 1, num_options):
- option1 = options_to_include[option1_index]
- option2 = options_to_include[option2_index]
- option1_choices = get_option_choices(option1)
- option2_choices = get_option_choices(option2)
- for key1 in option1_choices:
- for key2 in option2_choices:
- with self.subTest(f"{option1.internal_name}: {key1}, {option2.internal_name}: {key2}"):
- choices = {option1.internal_name: option1_choices[key1],
- option2.internal_name: option2_choices[key2]}
- multiworld = setup_solo_multiworld(choices)
- basic_checks(self, multiworld)
\ No newline at end of file
+
+class TestDynamicOptionDebug(WorldAssertMixin, SVTestCase):
+
+ def test_option_pair_debug(self):
+ option_dict = {
+ options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi,
+ options.Monstersanity.internal_name: options.Monstersanity.option_one_per_monster,
+ }
+ for i in range(1):
+ # seed = int(random() * pow(10, 18) - 1)
+ seed = 823942126251776128
+ with self.subTest(f"Seed: {seed}"):
+ print(f"Seed: {seed}")
+ multiworld = setup_solo_multiworld(option_dict, seed)
+ self.assert_basic_checks(multiworld)
diff --git a/worlds/stardew_valley/test/long/TestPreRolledRandomness.py b/worlds/stardew_valley/test/long/TestPreRolledRandomness.py
new file mode 100644
index 000000000000..66bc5aeba8bb
--- /dev/null
+++ b/worlds/stardew_valley/test/long/TestPreRolledRandomness.py
@@ -0,0 +1,25 @@
+from BaseClasses import get_seed
+from .. import SVTestCase
+from ..assertion import WorldAssertMixin
+from ... import options
+
+
+class TestGeneratePreRolledRandomness(WorldAssertMixin, SVTestCase):
+ def test_given_pre_rolled_difficult_randomness_when_generate_then_basic_checks(self):
+ if self.skip_long_tests:
+ return
+ choices = {
+ options.EntranceRandomization.internal_name: options.EntranceRandomization.option_buildings,
+ options.BundleRandomization.internal_name: options.BundleRandomization.option_remixed,
+ options.BundlePrice.internal_name: options.BundlePrice.option_maximum
+ }
+
+ num_tests = 1000
+ for i in range(num_tests):
+ seed = get_seed() # Put seed in parameter to test
+ with self.solo_world_sub_test(f"Entrance Randomizer and Remixed Bundles",
+ choices,
+ seed=seed,
+ world_caching=False) \
+ as (multiworld, _):
+ self.assert_basic_checks(multiworld)
diff --git a/worlds/stardew_valley/test/long/TestRandomWorlds.py b/worlds/stardew_valley/test/long/TestRandomWorlds.py
index 1f1d59652c5e..f3702c05f42b 100644
--- a/worlds/stardew_valley/test/long/TestRandomWorlds.py
+++ b/worlds/stardew_valley/test/long/TestRandomWorlds.py
@@ -1,14 +1,11 @@
-from typing import Dict
import random
+from typing import Dict
-from BaseClasses import MultiWorld
+from BaseClasses import MultiWorld, get_seed
from Options import NamedRange, Range
from .option_names import options_to_include
from .. import setup_solo_multiworld, SVTestCase
-from ..checks.goal_checks import assert_perfection_world_is_valid, assert_goal_world_is_valid
-from ..checks.option_checks import assert_can_reach_island_if_should, assert_cropsanity_same_number_items_and_locations, \
- assert_festivals_give_access_to_deluxe_scarecrow
-from ..checks.world_checks import assert_same_number_items_locations, assert_victory_exists
+from ..assertion import GoalAssertMixin, OptionAssertMixin, WorldAssertMixin
def get_option_choices(option) -> Dict[str, int]:
@@ -27,10 +24,10 @@ def generate_random_multiworld(world_id: int):
return multiworld
-def generate_random_world_options(world_id: int) -> Dict[str, int]:
+def generate_random_world_options(seed: int) -> Dict[str, int]:
num_options = len(options_to_include)
world_options = dict()
- rng = random.Random(world_id)
+ rng = random.Random(seed)
for option_index in range(0, num_options):
option = options_to_include[option_index]
option_choices = get_option_choices(option)
@@ -57,42 +54,37 @@ def get_number_log_steps(number_worlds: int) -> int:
return 100
-def generate_many_worlds(number_worlds: int, start_index: int) -> Dict[int, MultiWorld]:
- num_steps = get_number_log_steps(number_worlds)
- log_step = number_worlds / num_steps
- multiworlds = dict()
- print(f"Generating {number_worlds} Solo Multiworlds [Start Seed: {start_index}] for Stardew Valley...")
- for world_number in range(0, number_worlds + 1):
- world_id = world_number + start_index
- multiworld = generate_random_multiworld(world_id)
- multiworlds[world_id] = multiworld
- if world_number > 0 and world_number % log_step == 0:
- print(f"Generated {world_number}/{number_worlds} worlds [{(world_number * 100) // number_worlds}%]")
- print(f"Finished generating {number_worlds} Solo Multiworlds for Stardew Valley")
- return multiworlds
+class TestGenerateManyWorlds(GoalAssertMixin, OptionAssertMixin, WorldAssertMixin, SVTestCase):
+ def test_generate_many_worlds_then_check_results(self):
+ if self.skip_long_tests:
+ return
+ number_worlds = 10 if self.skip_long_tests else 1000
+ seed = get_seed()
+ self.generate_and_check_many_worlds(number_worlds, seed)
+ def generate_and_check_many_worlds(self, number_worlds: int, seed: int):
+ num_steps = get_number_log_steps(number_worlds)
+ log_step = number_worlds / num_steps
-def check_every_multiworld_is_valid(tester: SVTestCase, multiworlds: Dict[int, MultiWorld]):
- for multiworld_id in multiworlds:
- multiworld = multiworlds[multiworld_id]
- with tester.subTest(f"Checking validity of world {multiworld_id}"):
- check_multiworld_is_valid(tester, multiworld_id, multiworld)
+ print(f"Generating {number_worlds} Solo Multiworlds [Start Seed: {seed}] for Stardew Valley...")
+ for world_number in range(0, number_worlds + 1):
+ world_seed = world_number + seed
+ world_options = generate_random_world_options(world_seed)
-def check_multiworld_is_valid(tester: SVTestCase, multiworld_id: int, multiworld: MultiWorld):
- assert_victory_exists(tester, multiworld)
- assert_same_number_items_locations(tester, multiworld)
- assert_goal_world_is_valid(tester, multiworld)
- assert_can_reach_island_if_should(tester, multiworld)
- assert_cropsanity_same_number_items_and_locations(tester, multiworld)
- assert_festivals_give_access_to_deluxe_scarecrow(tester, multiworld)
+ with self.solo_world_sub_test(f"Multiworld: {world_seed}", world_options, seed=world_seed, world_caching=False) as (multiworld, _):
+ self.assert_multiworld_is_valid(multiworld)
+ if world_number > 0 and world_number % log_step == 0:
+ print(f"Generated and Verified {world_number}/{number_worlds} worlds [{(world_number * 100) // number_worlds}%]")
-class TestGenerateManyWorlds(SVTestCase):
- def test_generate_many_worlds_then_check_results(self):
- if self.skip_long_tests:
- return
- number_worlds = 1000
- start_index = random.Random().randint(0, 9999999999)
- multiworlds = generate_many_worlds(number_worlds, start_index)
- check_every_multiworld_is_valid(self, multiworlds)
+ print(f"Finished generating and verifying {number_worlds} Solo Multiworlds for Stardew Valley")
+
+ def assert_multiworld_is_valid(self, multiworld: MultiWorld):
+ self.assert_victory_exists(multiworld)
+ self.assert_same_number_items_locations(multiworld)
+ self.assert_goal_world_is_valid(multiworld)
+ self.assert_can_reach_island_if_should(multiworld)
+ self.assert_cropsanity_same_number_items_and_locations(multiworld)
+ self.assert_festivals_give_access_to_deluxe_scarecrow(multiworld)
+ self.assert_has_festival_recipes(multiworld)
diff --git a/worlds/stardew_valley/test/long/option_names.py b/worlds/stardew_valley/test/long/option_names.py
index 649d0da5b33f..9f3cf98b872c 100644
--- a/worlds/stardew_valley/test/long/option_names.py
+++ b/worlds/stardew_valley/test/long/option_names.py
@@ -1,8 +1,30 @@
+from typing import Dict
+
+from Options import NamedRange
from ... import StardewValleyWorld
-options_to_exclude = ["profit_margin", "starting_money", "multiple_day_sleep_enabled", "multiple_day_sleep_cost",
+options_to_exclude = {"profit_margin", "starting_money", "multiple_day_sleep_enabled", "multiple_day_sleep_cost",
"experience_multiplier", "friendship_multiplier", "debris_multiplier",
- "quick_start", "gifting", "gift_tax", "progression_balancing", "accessibility", "start_inventory", "start_hints", "death_link"]
+ "quick_start", "gifting", "gift_tax",
+ "progression_balancing", "accessibility", "start_inventory", "start_hints", "death_link"}
-options_to_include = [option for option_name, option in StardewValleyWorld.options_dataclass.type_hints.items()
+options_to_include = [option
+ for option_name, option in StardewValleyWorld.options_dataclass.type_hints.items()
if option_name not in options_to_exclude]
+
+
+def get_option_choices(option) -> Dict[str, int]:
+ if issubclass(option, NamedRange):
+ return option.special_range_names
+ elif option.options:
+ return option.options
+ return {}
+
+
+all_option_choices = [(option, value)
+ for option in options_to_include
+ if option.options
+ for value in get_option_choices(option)
+ if option.default != get_option_choices(option)[value]]
+
+assert all_option_choices
diff --git a/worlds/stardew_valley/test/mods/TestBiggerBackpack.py b/worlds/stardew_valley/test/mods/TestBiggerBackpack.py
index bc81f21963d8..f6d312976c45 100644
--- a/worlds/stardew_valley/test/mods/TestBiggerBackpack.py
+++ b/worlds/stardew_valley/test/mods/TestBiggerBackpack.py
@@ -1,11 +1,13 @@
from .. import SVTestBase
-from ... import options
from ...mods.mod_data import ModNames
+from ...options import Mods, BackpackProgression
class TestBiggerBackpackVanilla(SVTestBase):
- options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_vanilla,
- options.Mods.internal_name: ModNames.big_backpack}
+ options = {
+ BackpackProgression.internal_name: BackpackProgression.option_vanilla,
+ Mods.internal_name: ModNames.big_backpack
+ }
def test_no_backpack(self):
with self.subTest(check="no items"):
@@ -20,8 +22,10 @@ def test_no_backpack(self):
class TestBiggerBackpackProgressive(SVTestBase):
- options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_progressive,
- options.Mods.internal_name: ModNames.big_backpack}
+ options = {
+ BackpackProgression.internal_name: BackpackProgression.option_progressive,
+ Mods.internal_name: ModNames.big_backpack
+ }
def test_backpack(self):
with self.subTest(check="has items"):
@@ -36,8 +40,10 @@ def test_backpack(self):
class TestBiggerBackpackEarlyProgressive(TestBiggerBackpackProgressive):
- options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_early_progressive,
- options.Mods.internal_name: ModNames.big_backpack}
+ options = {
+ BackpackProgression.internal_name: BackpackProgression.option_early_progressive,
+ Mods.internal_name: ModNames.big_backpack
+ }
def test_backpack(self):
super().test_backpack()
diff --git a/worlds/stardew_valley/test/mods/TestModFish.py b/worlds/stardew_valley/test/mods/TestModFish.py
new file mode 100644
index 000000000000..81ac6ac0fb99
--- /dev/null
+++ b/worlds/stardew_valley/test/mods/TestModFish.py
@@ -0,0 +1,226 @@
+import unittest
+from typing import Set
+
+from ...data.fish_data import get_fish_for_mods
+from ...mods.mod_data import ModNames
+from ...strings.fish_names import Fish, SVEFish
+
+no_mods: Set[str] = set()
+sve: Set[str] = {ModNames.sve}
+
+
+class TestGetFishForMods(unittest.TestCase):
+
+ def test_no_mods_all_vanilla_fish(self):
+ all_fish = get_fish_for_mods(no_mods)
+ fish_names = {fish.name for fish in all_fish}
+
+ self.assertIn(Fish.albacore, fish_names)
+ self.assertIn(Fish.anchovy, fish_names)
+ self.assertIn(Fish.blue_discus, fish_names)
+ self.assertIn(Fish.bream, fish_names)
+ self.assertIn(Fish.bullhead, fish_names)
+ self.assertIn(Fish.carp, fish_names)
+ self.assertIn(Fish.catfish, fish_names)
+ self.assertIn(Fish.chub, fish_names)
+ self.assertIn(Fish.dorado, fish_names)
+ self.assertIn(Fish.eel, fish_names)
+ self.assertIn(Fish.flounder, fish_names)
+ self.assertIn(Fish.ghostfish, fish_names)
+ self.assertIn(Fish.halibut, fish_names)
+ self.assertIn(Fish.herring, fish_names)
+ self.assertIn(Fish.ice_pip, fish_names)
+ self.assertIn(Fish.largemouth_bass, fish_names)
+ self.assertIn(Fish.lava_eel, fish_names)
+ self.assertIn(Fish.lingcod, fish_names)
+ self.assertIn(Fish.lionfish, fish_names)
+ self.assertIn(Fish.midnight_carp, fish_names)
+ self.assertIn(Fish.octopus, fish_names)
+ self.assertIn(Fish.perch, fish_names)
+ self.assertIn(Fish.pike, fish_names)
+ self.assertIn(Fish.pufferfish, fish_names)
+ self.assertIn(Fish.rainbow_trout, fish_names)
+ self.assertIn(Fish.red_mullet, fish_names)
+ self.assertIn(Fish.red_snapper, fish_names)
+ self.assertIn(Fish.salmon, fish_names)
+ self.assertIn(Fish.sandfish, fish_names)
+ self.assertIn(Fish.sardine, fish_names)
+ self.assertIn(Fish.scorpion_carp, fish_names)
+ self.assertIn(Fish.sea_cucumber, fish_names)
+ self.assertIn(Fish.shad, fish_names)
+ self.assertIn(Fish.slimejack, fish_names)
+ self.assertIn(Fish.smallmouth_bass, fish_names)
+ self.assertIn(Fish.squid, fish_names)
+ self.assertIn(Fish.stingray, fish_names)
+ self.assertIn(Fish.stonefish, fish_names)
+ self.assertIn(Fish.sturgeon, fish_names)
+ self.assertIn(Fish.sunfish, fish_names)
+ self.assertIn(Fish.super_cucumber, fish_names)
+ self.assertIn(Fish.tiger_trout, fish_names)
+ self.assertIn(Fish.tilapia, fish_names)
+ self.assertIn(Fish.tuna, fish_names)
+ self.assertIn(Fish.void_salmon, fish_names)
+ self.assertIn(Fish.walleye, fish_names)
+ self.assertIn(Fish.woodskip, fish_names)
+ self.assertIn(Fish.blob_fish, fish_names)
+ self.assertIn(Fish.midnight_squid, fish_names)
+ self.assertIn(Fish.spook_fish, fish_names)
+ self.assertIn(Fish.angler, fish_names)
+ self.assertIn(Fish.crimsonfish, fish_names)
+ self.assertIn(Fish.glacierfish, fish_names)
+ self.assertIn(Fish.legend, fish_names)
+ self.assertIn(Fish.mutant_carp, fish_names)
+ self.assertIn(Fish.ms_angler, fish_names)
+ self.assertIn(Fish.son_of_crimsonfish, fish_names)
+ self.assertIn(Fish.glacierfish_jr, fish_names)
+ self.assertIn(Fish.legend_ii, fish_names)
+ self.assertIn(Fish.radioactive_carp, fish_names)
+ self.assertIn(Fish.clam, fish_names)
+ self.assertIn(Fish.cockle, fish_names)
+ self.assertIn(Fish.crab, fish_names)
+ self.assertIn(Fish.crayfish, fish_names)
+ self.assertIn(Fish.lobster, fish_names)
+ self.assertIn(Fish.mussel, fish_names)
+ self.assertIn(Fish.oyster, fish_names)
+ self.assertIn(Fish.periwinkle, fish_names)
+ self.assertIn(Fish.shrimp, fish_names)
+ self.assertIn(Fish.snail, fish_names)
+
+ def test_no_mods_no_sve_fish(self):
+ all_fish = get_fish_for_mods(no_mods)
+ fish_names = {fish.name for fish in all_fish}
+
+ self.assertNotIn(SVEFish.baby_lunaloo, fish_names)
+ self.assertNotIn(SVEFish.bonefish, fish_names)
+ self.assertNotIn(SVEFish.bull_trout, fish_names)
+ self.assertNotIn(SVEFish.butterfish, fish_names)
+ self.assertNotIn(SVEFish.clownfish, fish_names)
+ self.assertNotIn(SVEFish.daggerfish, fish_names)
+ self.assertNotIn(SVEFish.frog, fish_names)
+ self.assertNotIn(SVEFish.gemfish, fish_names)
+ self.assertNotIn(SVEFish.goldenfish, fish_names)
+ self.assertNotIn(SVEFish.grass_carp, fish_names)
+ self.assertNotIn(SVEFish.king_salmon, fish_names)
+ self.assertNotIn(SVEFish.kittyfish, fish_names)
+ self.assertNotIn(SVEFish.lunaloo, fish_names)
+ self.assertNotIn(SVEFish.meteor_carp, fish_names)
+ self.assertNotIn(SVEFish.minnow, fish_names)
+ self.assertNotIn(SVEFish.puppyfish, fish_names)
+ self.assertNotIn(SVEFish.radioactive_bass, fish_names)
+ self.assertNotIn(SVEFish.seahorse, fish_names)
+ self.assertNotIn(SVEFish.shiny_lunaloo, fish_names)
+ self.assertNotIn(SVEFish.snatcher_worm, fish_names)
+ self.assertNotIn(SVEFish.starfish, fish_names)
+ self.assertNotIn(SVEFish.torpedo_trout, fish_names)
+ self.assertNotIn(SVEFish.undeadfish, fish_names)
+ self.assertNotIn(SVEFish.void_eel, fish_names)
+ self.assertNotIn(SVEFish.water_grub, fish_names)
+ self.assertNotIn(SVEFish.sea_sponge, fish_names)
+ self.assertNotIn(SVEFish.dulse_seaweed, fish_names)
+
+ def test_sve_all_vanilla_fish(self):
+ all_fish = get_fish_for_mods(no_mods)
+ fish_names = {fish.name for fish in all_fish}
+
+ self.assertIn(Fish.albacore, fish_names)
+ self.assertIn(Fish.anchovy, fish_names)
+ self.assertIn(Fish.blue_discus, fish_names)
+ self.assertIn(Fish.bream, fish_names)
+ self.assertIn(Fish.bullhead, fish_names)
+ self.assertIn(Fish.carp, fish_names)
+ self.assertIn(Fish.catfish, fish_names)
+ self.assertIn(Fish.chub, fish_names)
+ self.assertIn(Fish.dorado, fish_names)
+ self.assertIn(Fish.eel, fish_names)
+ self.assertIn(Fish.flounder, fish_names)
+ self.assertIn(Fish.ghostfish, fish_names)
+ self.assertIn(Fish.halibut, fish_names)
+ self.assertIn(Fish.herring, fish_names)
+ self.assertIn(Fish.ice_pip, fish_names)
+ self.assertIn(Fish.largemouth_bass, fish_names)
+ self.assertIn(Fish.lava_eel, fish_names)
+ self.assertIn(Fish.lingcod, fish_names)
+ self.assertIn(Fish.lionfish, fish_names)
+ self.assertIn(Fish.midnight_carp, fish_names)
+ self.assertIn(Fish.octopus, fish_names)
+ self.assertIn(Fish.perch, fish_names)
+ self.assertIn(Fish.pike, fish_names)
+ self.assertIn(Fish.pufferfish, fish_names)
+ self.assertIn(Fish.rainbow_trout, fish_names)
+ self.assertIn(Fish.red_mullet, fish_names)
+ self.assertIn(Fish.red_snapper, fish_names)
+ self.assertIn(Fish.salmon, fish_names)
+ self.assertIn(Fish.sandfish, fish_names)
+ self.assertIn(Fish.sardine, fish_names)
+ self.assertIn(Fish.scorpion_carp, fish_names)
+ self.assertIn(Fish.sea_cucumber, fish_names)
+ self.assertIn(Fish.shad, fish_names)
+ self.assertIn(Fish.slimejack, fish_names)
+ self.assertIn(Fish.smallmouth_bass, fish_names)
+ self.assertIn(Fish.squid, fish_names)
+ self.assertIn(Fish.stingray, fish_names)
+ self.assertIn(Fish.stonefish, fish_names)
+ self.assertIn(Fish.sturgeon, fish_names)
+ self.assertIn(Fish.sunfish, fish_names)
+ self.assertIn(Fish.super_cucumber, fish_names)
+ self.assertIn(Fish.tiger_trout, fish_names)
+ self.assertIn(Fish.tilapia, fish_names)
+ self.assertIn(Fish.tuna, fish_names)
+ self.assertIn(Fish.void_salmon, fish_names)
+ self.assertIn(Fish.walleye, fish_names)
+ self.assertIn(Fish.woodskip, fish_names)
+ self.assertIn(Fish.blob_fish, fish_names)
+ self.assertIn(Fish.midnight_squid, fish_names)
+ self.assertIn(Fish.spook_fish, fish_names)
+ self.assertIn(Fish.angler, fish_names)
+ self.assertIn(Fish.crimsonfish, fish_names)
+ self.assertIn(Fish.glacierfish, fish_names)
+ self.assertIn(Fish.legend, fish_names)
+ self.assertIn(Fish.mutant_carp, fish_names)
+ self.assertIn(Fish.ms_angler, fish_names)
+ self.assertIn(Fish.son_of_crimsonfish, fish_names)
+ self.assertIn(Fish.glacierfish_jr, fish_names)
+ self.assertIn(Fish.legend_ii, fish_names)
+ self.assertIn(Fish.radioactive_carp, fish_names)
+ self.assertIn(Fish.clam, fish_names)
+ self.assertIn(Fish.cockle, fish_names)
+ self.assertIn(Fish.crab, fish_names)
+ self.assertIn(Fish.crayfish, fish_names)
+ self.assertIn(Fish.lobster, fish_names)
+ self.assertIn(Fish.mussel, fish_names)
+ self.assertIn(Fish.oyster, fish_names)
+ self.assertIn(Fish.periwinkle, fish_names)
+ self.assertIn(Fish.shrimp, fish_names)
+ self.assertIn(Fish.snail, fish_names)
+
+ def test_sve_has_sve_fish(self):
+ all_fish = get_fish_for_mods(sve)
+ fish_names = {fish.name for fish in all_fish}
+
+ self.assertIn(SVEFish.baby_lunaloo, fish_names)
+ self.assertIn(SVEFish.bonefish, fish_names)
+ self.assertIn(SVEFish.bull_trout, fish_names)
+ self.assertIn(SVEFish.butterfish, fish_names)
+ self.assertIn(SVEFish.clownfish, fish_names)
+ self.assertIn(SVEFish.daggerfish, fish_names)
+ self.assertIn(SVEFish.frog, fish_names)
+ self.assertIn(SVEFish.gemfish, fish_names)
+ self.assertIn(SVEFish.goldenfish, fish_names)
+ self.assertIn(SVEFish.grass_carp, fish_names)
+ self.assertIn(SVEFish.king_salmon, fish_names)
+ self.assertIn(SVEFish.kittyfish, fish_names)
+ self.assertIn(SVEFish.lunaloo, fish_names)
+ self.assertIn(SVEFish.meteor_carp, fish_names)
+ self.assertIn(SVEFish.minnow, fish_names)
+ self.assertIn(SVEFish.puppyfish, fish_names)
+ self.assertIn(SVEFish.radioactive_bass, fish_names)
+ self.assertIn(SVEFish.seahorse, fish_names)
+ self.assertIn(SVEFish.shiny_lunaloo, fish_names)
+ self.assertIn(SVEFish.snatcher_worm, fish_names)
+ self.assertIn(SVEFish.starfish, fish_names)
+ self.assertIn(SVEFish.torpedo_trout, fish_names)
+ self.assertIn(SVEFish.undeadfish, fish_names)
+ self.assertIn(SVEFish.void_eel, fish_names)
+ self.assertIn(SVEFish.water_grub, fish_names)
+ self.assertIn(SVEFish.sea_sponge, fish_names)
+ self.assertIn(SVEFish.dulse_seaweed, fish_names)
diff --git a/worlds/stardew_valley/test/mods/TestModVillagers.py b/worlds/stardew_valley/test/mods/TestModVillagers.py
new file mode 100644
index 000000000000..3be437c3f737
--- /dev/null
+++ b/worlds/stardew_valley/test/mods/TestModVillagers.py
@@ -0,0 +1,132 @@
+import unittest
+from typing import Set
+
+from ...data.villagers_data import get_villagers_for_mods
+from ...mods.mod_data import ModNames
+from ...strings.villager_names import NPC, ModNPC
+
+no_mods: Set[str] = set()
+sve: Set[str] = {ModNames.sve}
+
+
+class TestGetVillagersForMods(unittest.TestCase):
+
+ def test_no_mods_all_vanilla_villagers(self):
+ villagers = get_villagers_for_mods(no_mods)
+ villager_names = {villager.name for villager in villagers}
+
+ self.assertIn(NPC.alex, villager_names)
+ self.assertIn(NPC.elliott, villager_names)
+ self.assertIn(NPC.harvey, villager_names)
+ self.assertIn(NPC.sam, villager_names)
+ self.assertIn(NPC.sebastian, villager_names)
+ self.assertIn(NPC.shane, villager_names)
+ self.assertIn(NPC.abigail, villager_names)
+ self.assertIn(NPC.emily, villager_names)
+ self.assertIn(NPC.haley, villager_names)
+ self.assertIn(NPC.leah, villager_names)
+ self.assertIn(NPC.maru, villager_names)
+ self.assertIn(NPC.penny, villager_names)
+ self.assertIn(NPC.caroline, villager_names)
+ self.assertIn(NPC.clint, villager_names)
+ self.assertIn(NPC.demetrius, villager_names)
+ self.assertIn(NPC.dwarf, villager_names)
+ self.assertIn(NPC.evelyn, villager_names)
+ self.assertIn(NPC.george, villager_names)
+ self.assertIn(NPC.gus, villager_names)
+ self.assertIn(NPC.jas, villager_names)
+ self.assertIn(NPC.jodi, villager_names)
+ self.assertIn(NPC.kent, villager_names)
+ self.assertIn(NPC.krobus, villager_names)
+ self.assertIn(NPC.leo, villager_names)
+ self.assertIn(NPC.lewis, villager_names)
+ self.assertIn(NPC.linus, villager_names)
+ self.assertIn(NPC.marnie, villager_names)
+ self.assertIn(NPC.pam, villager_names)
+ self.assertIn(NPC.pierre, villager_names)
+ self.assertIn(NPC.robin, villager_names)
+ self.assertIn(NPC.sandy, villager_names)
+ self.assertIn(NPC.vincent, villager_names)
+ self.assertIn(NPC.willy, villager_names)
+ self.assertIn(NPC.wizard, villager_names)
+
+ def test_no_mods_no_mod_villagers(self):
+ villagers = get_villagers_for_mods(no_mods)
+ villager_names = {villager.name for villager in villagers}
+
+ self.assertNotIn(ModNPC.alec, villager_names)
+ self.assertNotIn(ModNPC.ayeisha, villager_names)
+ self.assertNotIn(ModNPC.delores, villager_names)
+ self.assertNotIn(ModNPC.eugene, villager_names)
+ self.assertNotIn(ModNPC.jasper, villager_names)
+ self.assertNotIn(ModNPC.juna, villager_names)
+ self.assertNotIn(ModNPC.mr_ginger, villager_names)
+ self.assertNotIn(ModNPC.riley, villager_names)
+ self.assertNotIn(ModNPC.shiko, villager_names)
+ self.assertNotIn(ModNPC.wellwick, villager_names)
+ self.assertNotIn(ModNPC.yoba, villager_names)
+ self.assertNotIn(ModNPC.lance, villager_names)
+ self.assertNotIn(ModNPC.apples, villager_names)
+ self.assertNotIn(ModNPC.claire, villager_names)
+ self.assertNotIn(ModNPC.olivia, villager_names)
+ self.assertNotIn(ModNPC.sophia, villager_names)
+ self.assertNotIn(ModNPC.victor, villager_names)
+ self.assertNotIn(ModNPC.andy, villager_names)
+ self.assertNotIn(ModNPC.gunther, villager_names)
+ self.assertNotIn(ModNPC.martin, villager_names)
+ self.assertNotIn(ModNPC.marlon, villager_names)
+ self.assertNotIn(ModNPC.morgan, villager_names)
+ self.assertNotIn(ModNPC.morris, villager_names)
+ self.assertNotIn(ModNPC.scarlett, villager_names)
+ self.assertNotIn(ModNPC.susan, villager_names)
+ self.assertNotIn(ModNPC.goblin, villager_names)
+ self.assertNotIn(ModNPC.alecto, villager_names)
+
+ def test_sve_has_sve_villagers(self):
+ villagers = get_villagers_for_mods(sve)
+ villager_names = {villager.name for villager in villagers}
+
+ self.assertIn(ModNPC.lance, villager_names)
+ self.assertIn(ModNPC.apples, villager_names)
+ self.assertIn(ModNPC.claire, villager_names)
+ self.assertIn(ModNPC.olivia, villager_names)
+ self.assertIn(ModNPC.sophia, villager_names)
+ self.assertIn(ModNPC.victor, villager_names)
+ self.assertIn(ModNPC.andy, villager_names)
+ self.assertIn(ModNPC.gunther, villager_names)
+ self.assertIn(ModNPC.martin, villager_names)
+ self.assertIn(ModNPC.marlon, villager_names)
+ self.assertIn(ModNPC.morgan, villager_names)
+ self.assertIn(ModNPC.morris, villager_names)
+ self.assertIn(ModNPC.scarlett, villager_names)
+ self.assertIn(ModNPC.susan, villager_names)
+
+ def test_sve_has_no_other_mod_villagers(self):
+ villagers = get_villagers_for_mods(sve)
+ villager_names = {villager.name for villager in villagers}
+
+ self.assertNotIn(ModNPC.alec, villager_names)
+ self.assertNotIn(ModNPC.ayeisha, villager_names)
+ self.assertNotIn(ModNPC.delores, villager_names)
+ self.assertNotIn(ModNPC.eugene, villager_names)
+ self.assertNotIn(ModNPC.jasper, villager_names)
+ self.assertNotIn(ModNPC.juna, villager_names)
+ self.assertNotIn(ModNPC.mr_ginger, villager_names)
+ self.assertNotIn(ModNPC.riley, villager_names)
+ self.assertNotIn(ModNPC.shiko, villager_names)
+ self.assertNotIn(ModNPC.wellwick, villager_names)
+ self.assertNotIn(ModNPC.yoba, villager_names)
+ self.assertNotIn(ModNPC.goblin, villager_names)
+ self.assertNotIn(ModNPC.alecto, villager_names)
+
+ def test_no_mods_wizard_is_not_bachelor(self):
+ villagers = get_villagers_for_mods(no_mods)
+ villagers_by_name = {villager.name: villager for villager in villagers}
+ self.assertFalse(villagers_by_name[NPC.wizard].bachelor)
+ self.assertEqual(villagers_by_name[NPC.wizard].mod_name, ModNames.vanilla)
+
+ def test_sve_wizard_is_bachelor(self):
+ villagers = get_villagers_for_mods(sve)
+ villagers_by_name = {villager.name: villager for villager in villagers}
+ self.assertTrue(villagers_by_name[NPC.wizard].bachelor)
+ self.assertEqual(villagers_by_name[NPC.wizard].mod_name, ModNames.sve)
diff --git a/worlds/stardew_valley/test/mods/TestMods.py b/worlds/stardew_valley/test/mods/TestMods.py
index 9bdabaf73f14..57bca5f25645 100644
--- a/worlds/stardew_valley/test/mods/TestMods.py
+++ b/worlds/stardew_valley/test/mods/TestMods.py
@@ -1,69 +1,74 @@
-from typing import List, Union
-import unittest
import random
-import sys
-from BaseClasses import MultiWorld
-from ...mods.mod_data import all_mods
-from .. import setup_solo_multiworld, SVTestBase, SVTestCase, allsanity_options_without_mods
-from ..TestOptions import basic_checks
+from BaseClasses import get_seed
+from .. import setup_solo_multiworld, SVTestBase, SVTestCase, allsanity_options_without_mods, allsanity_options_with_mods, complete_options_with_default
+from ..assertion import ModAssertMixin, WorldAssertMixin
from ... import items, Group, ItemClassification
-from ...regions import RandomizationFlag, create_final_connections, randomize_connections, create_final_regions
-from ...items import item_table, items_by_group
-from ...locations import location_table
-from ...options import Mods, EntranceRandomization, Friendsanity, SeasonRandomization, SpecialOrderLocations, ExcludeGingerIsland, TrapItems
-
-
-def check_stray_mod_items(chosen_mods: Union[List[str], str], tester: unittest.TestCase, multiworld: MultiWorld):
- if isinstance(chosen_mods, str):
- chosen_mods = [chosen_mods]
- for multiworld_item in multiworld.get_items():
- item = item_table[multiworld_item.name]
- tester.assertTrue(item.mod_name is None or item.mod_name in chosen_mods)
- for multiworld_location in multiworld.get_locations():
- if multiworld_location.event:
- continue
- location = location_table[multiworld_location.name]
- tester.assertTrue(location.mod_name is None or location.mod_name in chosen_mods)
+from ... import options
+from ...items import items_by_group
+from ...mods.mod_data import all_mods
+from ...regions import RandomizationFlag, randomize_connections, create_final_connections_and_regions
-class TestGenerateModsOptions(SVTestCase):
+class TestGenerateModsOptions(WorldAssertMixin, ModAssertMixin, SVTestCase):
def test_given_single_mods_when_generate_then_basic_checks(self):
for mod in all_mods:
- with self.subTest(f"Mod: {mod}"):
- multi_world = setup_solo_multiworld({Mods: mod})
- basic_checks(self, multi_world)
- check_stray_mod_items(mod, self, multi_world)
+ with self.solo_world_sub_test(f"Mod: {mod}", {options.Mods: mod}, dirty_state=True) as (multi_world, _):
+ self.assert_basic_checks(multi_world)
+ self.assert_stray_mod_items(mod, multi_world)
def test_given_mod_names_when_generate_paired_with_entrance_randomizer_then_basic_checks(self):
- for option in EntranceRandomization.options:
+ for option in options.EntranceRandomization.options:
for mod in all_mods:
- with self.subTest(f"entrance_randomization: {option}, Mod: {mod}"):
- multiworld = setup_solo_multiworld({EntranceRandomization.internal_name: option, Mods: mod})
- basic_checks(self, multiworld)
- check_stray_mod_items(mod, self, multiworld)
- if self.skip_extra_tests:
- return # assume the rest will work as well
+ world_options = {
+ options.EntranceRandomization.internal_name: options.EntranceRandomization.options[option],
+ options.Mods: mod
+ }
+ with self.solo_world_sub_test(f"entrance_randomization: {option}, Mod: {mod}", world_options, dirty_state=True) as (multi_world, _):
+ self.assert_basic_checks(multi_world)
+ self.assert_stray_mod_items(mod, multi_world)
+
+ def test_allsanity_all_mods_when_generate_then_basic_checks(self):
+ with self.solo_world_sub_test(world_options=allsanity_options_with_mods(), dirty_state=True) as (multi_world, _):
+ self.assert_basic_checks(multi_world)
+
+ def test_allsanity_all_mods_exclude_island_when_generate_then_basic_checks(self):
+ world_options = allsanity_options_with_mods()
+ world_options.update({options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true})
+ with self.solo_world_sub_test(world_options=world_options, dirty_state=True) as (multi_world, _):
+ self.assert_basic_checks(multi_world)
+
+
+class TestBaseLocationDependencies(SVTestBase):
+ options = {
+ options.Mods.internal_name: all_mods,
+ options.ToolProgression.internal_name: options.ToolProgression.option_progressive,
+ options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized
+ }
class TestBaseItemGeneration(SVTestBase):
options = {
- Friendsanity.internal_name: Friendsanity.option_all_with_marriage,
- SeasonRandomization.internal_name: SeasonRandomization.option_progressive,
- SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi,
- Mods.internal_name: all_mods
+ options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
+ options.SeasonRandomization.internal_name: options.SeasonRandomization.option_progressive,
+ options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi,
+ options.Shipsanity.internal_name: options.Shipsanity.option_everything,
+ options.Chefsanity.internal_name: options.Chefsanity.option_all,
+ options.Craftsanity.internal_name: options.Craftsanity.option_all,
+ options.Mods.internal_name: all_mods
}
def test_all_progression_items_are_added_to_the_pool(self):
all_created_items = [item.name for item in self.multiworld.itempool]
# Ignore all the stuff that the algorithm chooses one of, instead of all, to fulfill logical progression
items_to_ignore = [event.name for event in items.events]
+ items_to_ignore.extend(deprecated.name for deprecated in items.items_by_group[Group.DEPRECATED])
items_to_ignore.extend(season.name for season in items.items_by_group[Group.SEASON])
items_to_ignore.extend(weapon.name for weapon in items.items_by_group[Group.WEAPON])
- items_to_ignore.extend(footwear.name for footwear in items.items_by_group[Group.FOOTWEAR])
items_to_ignore.extend(baby.name for baby in items.items_by_group[Group.BABY])
items_to_ignore.extend(resource_pack.name for resource_pack in items.items_by_group[Group.RESOURCE_PACK])
+ items_to_ignore.append("The Gateway Gazette")
progression_items = [item for item in items.all_items if item.classification is ItemClassification.progression
and item.name not in items_to_ignore]
for progression_item in progression_items:
@@ -73,21 +78,25 @@ def test_all_progression_items_are_added_to_the_pool(self):
class TestNoGingerIslandModItemGeneration(SVTestBase):
options = {
- Friendsanity.internal_name: Friendsanity.option_all_with_marriage,
- SeasonRandomization.internal_name: SeasonRandomization.option_progressive,
- ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true,
- Mods.internal_name: all_mods
+ options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
+ options.SeasonRandomization.internal_name: options.SeasonRandomization.option_progressive,
+ options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true,
+ options.Shipsanity.internal_name: options.Shipsanity.option_everything,
+ options.Chefsanity.internal_name: options.Chefsanity.option_all,
+ options.Craftsanity.internal_name: options.Craftsanity.option_all,
+ options.Mods.internal_name: all_mods
}
def test_all_progression_items_except_island_are_added_to_the_pool(self):
all_created_items = [item.name for item in self.multiworld.itempool]
# Ignore all the stuff that the algorithm chooses one of, instead of all, to fulfill logical progression
items_to_ignore = [event.name for event in items.events]
+ items_to_ignore.extend(deprecated.name for deprecated in items.items_by_group[Group.DEPRECATED])
items_to_ignore.extend(season.name for season in items.items_by_group[Group.SEASON])
items_to_ignore.extend(weapon.name for weapon in items.items_by_group[Group.WEAPON])
- items_to_ignore.extend(footwear.name for footwear in items.items_by_group[Group.FOOTWEAR])
items_to_ignore.extend(baby.name for baby in items.items_by_group[Group.BABY])
items_to_ignore.extend(resource_pack.name for resource_pack in items.items_by_group[Group.RESOURCE_PACK])
+ items_to_ignore.append("The Gateway Gazette")
progression_items = [item for item in items.all_items if item.classification is ItemClassification.progression
and item.name not in items_to_ignore]
for progression_item in progression_items:
@@ -101,44 +110,41 @@ def test_all_progression_items_except_island_are_added_to_the_pool(self):
class TestModEntranceRando(SVTestCase):
def test_mod_entrance_randomization(self):
-
- for option, flag in [(EntranceRandomization.option_pelican_town, RandomizationFlag.PELICAN_TOWN),
- (EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION),
- (EntranceRandomization.option_buildings, RandomizationFlag.BUILDINGS)]:
- with self.subTest(option=option, flag=flag):
- seed = random.randrange(sys.maxsize)
- rand = random.Random(seed)
- world_options = {EntranceRandomization.internal_name: option,
- ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_false,
- Mods.internal_name: all_mods}
- multiworld = setup_solo_multiworld(world_options)
- world = multiworld.worlds[1]
- final_regions = create_final_regions(world.options)
- final_connections = create_final_connections(world.options)
-
- regions_by_name = {region.name: region for region in final_regions}
- _, randomized_connections = randomize_connections(rand, world.options, regions_by_name)
-
- for connection in final_connections:
+ for option, flag in [(options.EntranceRandomization.option_pelican_town, RandomizationFlag.PELICAN_TOWN),
+ (options.EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION),
+ (options.EntranceRandomization.option_buildings, RandomizationFlag.BUILDINGS)]:
+ sv_options = complete_options_with_default({
+ options.EntranceRandomization.internal_name: option,
+ options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false,
+ options.Mods.internal_name: all_mods
+ })
+ seed = get_seed()
+ rand = random.Random(seed)
+ with self.subTest(option=option, flag=flag, seed=seed):
+ final_connections, final_regions = create_final_connections_and_regions(sv_options)
+
+ _, randomized_connections = randomize_connections(rand, sv_options, final_regions, final_connections)
+
+ for connection_name in final_connections:
+ connection = final_connections[connection_name]
if flag in connection.flag:
- connection_in_randomized = connection.name in randomized_connections
+ connection_in_randomized = connection_name in randomized_connections
reverse_in_randomized = connection.reverse in randomized_connections
- self.assertTrue(connection_in_randomized,
- f"Connection {connection.name} should be randomized but it is not in the output. Seed = {seed}")
- self.assertTrue(reverse_in_randomized,
- f"Connection {connection.reverse} should be randomized but it is not in the output. Seed = {seed}")
+ self.assertTrue(connection_in_randomized, f"Connection {connection_name} should be randomized but it is not in the output")
+ self.assertTrue(reverse_in_randomized, f"Connection {connection.reverse} should be randomized but it is not in the output.")
self.assertEqual(len(set(randomized_connections.values())), len(randomized_connections.values()),
- f"Connections are duplicated in randomization. Seed = {seed}")
+ f"Connections are duplicated in randomization.")
class TestModTraps(SVTestCase):
def test_given_traps_when_generate_then_all_traps_in_pool(self):
- for value in TrapItems.options:
+ for value in options.TrapItems.options:
if value == "no_traps":
continue
+
world_options = allsanity_options_without_mods()
- world_options.update({TrapItems.internal_name: TrapItems.options[value], Mods: "Magic"})
+ world_options.update({options.TrapItems.internal_name: options.TrapItems.options[value], options.Mods: "Magic"})
multi_world = setup_solo_multiworld(world_options)
trap_items = [item_data.name for item_data in items_by_group[Group.TRAP] if Group.DEPRECATED not in item_data.groups]
multiworld_items = [item.name for item in multi_world.get_items()]
diff --git a/worlds/stardew_valley/test/performance/TestPerformance.py b/worlds/stardew_valley/test/performance/TestPerformance.py
new file mode 100644
index 000000000000..0d453942c35f
--- /dev/null
+++ b/worlds/stardew_valley/test/performance/TestPerformance.py
@@ -0,0 +1,276 @@
+import os
+import time
+import unittest
+from dataclasses import dataclass
+from statistics import mean, median, variance, stdev
+from typing import List
+
+from BaseClasses import get_seed
+from Fill import distribute_items_restrictive, balance_multiworld_progression
+from worlds import AutoWorld
+from .. import SVTestCase, minimal_locations_maximal_items, setup_multiworld, default_4_x_x_options, \
+ allsanity_4_x_x_options_without_mods, default_options, allsanity_options_without_mods, allsanity_options_with_mods
+
+assert default_4_x_x_options
+assert allsanity_4_x_x_options_without_mods
+assert default_options
+assert allsanity_options_without_mods
+
+default_number_generations = 25
+acceptable_deviation = 4
+
+
+@dataclass
+class PerformanceResults:
+ case: SVTestCase
+
+ amount_of_players: int
+ results: List[float]
+ acceptable_mean: float
+
+ def __repr__(self):
+ size = size_name(self.amount_of_players)
+
+ total_time = sum(self.results)
+ mean_time = mean(self.results)
+ median_time = median(self.results)
+ stdev_time = stdev(self.results, mean_time)
+ variance_time = variance(self.results, mean_time)
+
+ return f"""Generated {len(self.results)} {size} multiworlds in {total_time:.2f} seconds. Average {mean_time:.2f} seconds (Acceptable: {self.acceptable_mean:.2f})
+Mean: {mean_time:.2f} Median: {median_time:.2f} Stdeviation: {stdev_time:.2f} Variance: {variance_time:.4f} Deviation percent: {stdev_time / mean_time:.2%}"""
+
+
+class SVPerformanceTestCase(SVTestCase):
+ acceptable_time_per_player: float
+ results: List[PerformanceResults]
+
+ # Set False to run tests that take long
+ skip_performance_tests: bool = True
+ # Set False to not call the fill in the tests"""
+ skip_fill: bool = True
+ # Set True to print results as CSV"""
+ csv: bool = False
+
+ @classmethod
+ def setUpClass(cls) -> None:
+ super().setUpClass()
+ performance_tests_key = "performance"
+ if performance_tests_key in os.environ:
+ cls.skip_performance_tests = not bool(os.environ[performance_tests_key])
+
+ fill_tests_key = "fill"
+ if fill_tests_key in os.environ:
+ cls.skip_fill = os.environ[fill_tests_key] != "True"
+
+ fixed_seed_key = "fixed_seed"
+ if fixed_seed_key in os.environ:
+ cls.fixed_seed = bool(os.environ[fixed_seed_key])
+ else:
+ cls.fixed_seed = False
+
+ number_generations_key = "number_gen"
+ if number_generations_key in os.environ:
+ cls.number_generations = int(os.environ[number_generations_key])
+ else:
+ cls.number_generations = default_number_generations
+
+ csv_key = "csv"
+ if csv_key in os.environ:
+ cls.csv = bool(os.environ[csv_key])
+
+ @classmethod
+ def tearDownClass(cls) -> None:
+ if cls.csv:
+ csved_results = (f"{type(result.case).__name__},{result.amount_of_players},{val:.6f}"
+ for result in cls.results for val in result.results)
+ for r in csved_results:
+ print(r)
+ else:
+ case = None
+ for result in cls.results:
+ if type(result.case) is not case:
+ case = type(result.case)
+ print(case.__name__)
+ print(result)
+ print()
+
+ super().tearDownClass()
+
+ def performance_test_multiworld(self, options):
+ amount_of_players = len(options)
+ acceptable_average_time = self.acceptable_time_per_player * amount_of_players
+ total_time = 0
+ all_times = []
+ seeds = [get_seed() for _ in range(self.number_generations)] if not self.fixed_seed else [87876703343494157696] * self.number_generations
+
+ for i, seed in enumerate(seeds):
+ with self.subTest(f"Seed: {seed}"):
+ time_before = time.time()
+
+ print("Starting world setup")
+ multiworld = setup_multiworld(options, seed)
+ if not self.skip_fill:
+ distribute_items_restrictive(multiworld)
+ AutoWorld.call_all(multiworld, 'post_fill')
+ if multiworld.players > 1:
+ balance_multiworld_progression(multiworld)
+
+ time_after = time.time()
+ elapsed_time = time_after - time_before
+ total_time += elapsed_time
+ all_times.append(elapsed_time)
+ print(f"Multiworld {i + 1}/{self.number_generations} [{seed}] generated in {elapsed_time:.4f} seconds")
+ # tester.assertLessEqual(elapsed_time, acceptable_average_time * acceptable_deviation)
+
+ self.results.append(PerformanceResults(self, amount_of_players, all_times, acceptable_average_time))
+ self.assertLessEqual(mean(all_times), acceptable_average_time)
+
+
+def size_name(number_players):
+ if number_players == 1:
+ return "solo"
+ elif number_players == 2:
+ return "duo"
+ elif number_players == 3:
+ return "trio"
+ return f"{number_players}-player"
+
+
+class TestDefaultOptions(SVPerformanceTestCase):
+ acceptable_time_per_player = 2
+ options = default_options()
+ results = []
+
+ def test_solo(self):
+ if self.skip_performance_tests:
+ return
+
+ number_players = 1
+ multiworld_options = [self.options] * number_players
+ self.performance_test_multiworld(multiworld_options)
+
+ def test_duo(self):
+ if self.skip_performance_tests:
+ return
+
+ number_players = 2
+ multiworld_options = [self.options] * number_players
+ self.performance_test_multiworld(multiworld_options)
+
+ def test_5_player(self):
+ if self.skip_performance_tests:
+ return
+
+ number_players = 5
+ multiworld_options = [self.options] * number_players
+ self.performance_test_multiworld(multiworld_options)
+
+ @unittest.skip
+ def test_10_player(self):
+ if self.skip_performance_tests:
+ return
+
+ number_players = 10
+ multiworld_options = [self.options] * number_players
+ self.performance_test_multiworld(multiworld_options)
+
+
+class TestMinLocationMaxItems(SVPerformanceTestCase):
+ acceptable_time_per_player = 0.3
+ options = minimal_locations_maximal_items()
+ results = []
+
+ def test_solo(self):
+ if self.skip_performance_tests:
+ return
+
+ number_players = 1
+ multiworld_options = [self.options] * number_players
+ self.performance_test_multiworld(multiworld_options)
+
+ def test_duo(self):
+ if self.skip_performance_tests:
+ return
+
+ number_players = 2
+ multiworld_options = [self.options] * number_players
+ self.performance_test_multiworld(multiworld_options)
+
+ def test_5_player(self):
+ if self.skip_performance_tests:
+ return
+
+ number_players = 5
+ multiworld_options = [self.options] * number_players
+ self.performance_test_multiworld(multiworld_options)
+
+ def test_10_player(self):
+ if self.skip_performance_tests:
+ return
+
+ number_players = 10
+ multiworld_options = [self.options] * number_players
+ self.performance_test_multiworld(multiworld_options)
+
+
+class TestAllsanityWithoutMods(SVPerformanceTestCase):
+ acceptable_time_per_player = 10
+ options = allsanity_options_without_mods()
+ results = []
+
+ def test_solo(self):
+ if self.skip_performance_tests:
+ return
+
+ number_players = 1
+ multiworld_options = [self.options] * number_players
+ self.performance_test_multiworld(multiworld_options)
+
+ def test_duo(self):
+ if self.skip_performance_tests:
+ return
+
+ number_players = 2
+ multiworld_options = [self.options] * number_players
+ self.performance_test_multiworld(multiworld_options)
+
+ @unittest.skip
+ def test_5_player(self):
+ if self.skip_performance_tests:
+ return
+
+ number_players = 5
+ multiworld_options = [self.options] * number_players
+ self.performance_test_multiworld(multiworld_options)
+
+ @unittest.skip
+ def test_10_player(self):
+ if self.skip_performance_tests:
+ return
+
+ number_players = 10
+ multiworld_options = [self.options] * number_players
+ self.performance_test_multiworld(multiworld_options)
+
+
+class TestAllsanityWithMods(SVPerformanceTestCase):
+ acceptable_time_per_player = 25
+ options = allsanity_options_with_mods()
+ results = []
+
+ def test_solo(self):
+ if self.skip_performance_tests:
+ return
+
+ number_players = 1
+ multiworld_options = [self.options] * number_players
+ self.performance_test_multiworld(multiworld_options)
+
+ def test_duo(self):
+ if self.skip_performance_tests:
+ return
+
+ number_players = 2
+ multiworld_options = [self.options] * number_players
+ self.performance_test_multiworld(multiworld_options)
diff --git a/worlds/stardew_valley/test/performance/__init__.py b/worlds/stardew_valley/test/performance/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/worlds/stardew_valley/test/stability/StabilityOutputScript.py b/worlds/stardew_valley/test/stability/StabilityOutputScript.py
new file mode 100644
index 000000000000..baf17dde8423
--- /dev/null
+++ b/worlds/stardew_valley/test/stability/StabilityOutputScript.py
@@ -0,0 +1,32 @@
+import argparse
+import json
+
+from ...test import setup_solo_multiworld, allsanity_options_with_mods
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--seed', help='Define seed number to generate.', type=int, required=True)
+
+ args = parser.parse_args()
+ seed = args.seed
+
+ multi_world = setup_solo_multiworld(
+ allsanity_options_with_mods(),
+ seed=seed
+ )
+
+ output = {
+ "bundles": {
+ bundle_room.name: {
+ bundle.name: str(bundle.items)
+ for bundle in bundle_room.bundles
+ }
+ for bundle_room in multi_world.worlds[1].modified_bundles
+ },
+ "items": [item.name for item in multi_world.get_items()],
+ "location_rules": {location.name: repr(location.access_rule) for location in multi_world.get_locations(1)}
+ }
+
+ print(json.dumps(output))
+else:
+ raise RuntimeError("Do not import this file, execute it in different python session so the PYTHONHASHSEED is different..")
diff --git a/worlds/stardew_valley/test/stability/TestStability.py b/worlds/stardew_valley/test/stability/TestStability.py
new file mode 100644
index 000000000000..48cd663cb301
--- /dev/null
+++ b/worlds/stardew_valley/test/stability/TestStability.py
@@ -0,0 +1,52 @@
+import json
+import re
+import subprocess
+import sys
+
+from BaseClasses import get_seed
+from .. import SVTestCase
+
+# at 0x102ca98a0>
+lambda_regex = re.compile(r"^ at (.*)>$")
+# Python 3.10.2\r\n
+python_version_regex = re.compile(r"^Python (\d+)\.(\d+)\.(\d+)\s*$")
+
+
+class TestGenerationIsStable(SVTestCase):
+ """Let it be known that I hate this tests, and if someone has a better idea than starting subprocesses, please fix this.
+ """
+
+ def test_all_locations_and_items_are_the_same_between_two_generations(self):
+ if self.skip_long_tests:
+ return
+
+ # seed = get_seed(33778671150797368040) # troubleshooting seed
+ seed = get_seed()
+
+ output_a = subprocess.check_output([sys.executable, '-m', 'worlds.stardew_valley.test.stability.StabilityOutputScript', '--seed', str(seed)])
+ output_b = subprocess.check_output([sys.executable, '-m', 'worlds.stardew_valley.test.stability.StabilityOutputScript', '--seed', str(seed)])
+
+ result_a = json.loads(output_a)
+ result_b = json.loads(output_b)
+
+ for i, ((room_a, bundles_a), (room_b, bundles_b)) in enumerate(zip(result_a["bundles"].items(), result_b["bundles"].items())):
+ self.assertEqual(room_a, room_b, f"Bundle rooms at index {i} is different between both executions. Seed={seed}")
+ for j, ((bundle_a, items_a), (bundle_b, items_b)) in enumerate(zip(bundles_a.items(), bundles_b.items())):
+ self.assertEqual(bundle_a, bundle_b, f"Bundle in room {room_a} at index {j} is different between both executions. Seed={seed}")
+ self.assertEqual(items_a, items_b, f"Items in bundle {bundle_a} are different between both executions. Seed={seed}")
+
+ for i, (item_a, item_b) in enumerate(zip(result_a["items"], result_b["items"])):
+ self.assertEqual(item_a, item_b, f"Item at index {i} is different between both executions. Seed={seed}")
+
+ for i, ((location_a, rule_a), (location_b, rule_b)) in enumerate(zip(result_a["location_rules"].items(), result_b["location_rules"].items())):
+ self.assertEqual(location_a, location_b, f"Location at index {i} is different between both executions. Seed={seed}")
+
+ match = lambda_regex.match(rule_a)
+ if match:
+ self.assertTrue(bool(lambda_regex.match(rule_b)),
+ f"Location rule of {location_a} at index {i} is different between both executions. Seed={seed}")
+ continue
+
+ # We check that the actual rule has the same order to make sure it is evaluated in the same order,
+ # so performance tests are repeatable as much as possible.
+ self.assertEqual(rule_a, rule_b, f"Location rule of {location_a} at index {i} is different between both executions. Seed={seed}")
diff --git a/worlds/stardew_valley/test/stability/__init__.py b/worlds/stardew_valley/test/stability/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/worlds/subnautica/__init__.py b/worlds/subnautica/__init__.py
index de4f4e33dc87..e9341ec3b9de 100644
--- a/worlds/subnautica/__init__.py
+++ b/worlds/subnautica/__init__.py
@@ -115,7 +115,7 @@ def create_items(self):
for i in range(item.count):
subnautica_item = self.create_item(item.name)
if item.name == "Neptune Launch Platform":
- self.multiworld.get_location("Aurora - Captain Data Terminal", self.player).place_locked_item(
+ self.get_location("Aurora - Captain Data Terminal").place_locked_item(
subnautica_item)
else:
pool.append(subnautica_item)
@@ -128,7 +128,7 @@ def create_items(self):
pool.append(self.create_item(name))
extras -= group_amount
- for item_name in self.multiworld.random.sample(
+ for item_name in self.random.sample(
# list of high-count important fragments as priority filler
[
"Cyclops Engine Fragment",
@@ -175,18 +175,6 @@ def create_item(self, name: str) -> SubnauticaItem:
item_table[item_id].classification,
item_id, player=self.player)
- def create_region(self, name: str, region_locations=None, exits=None):
- ret = Region(name, self.player, self.multiworld)
- if region_locations:
- for location in region_locations:
- loc_id = self.location_name_to_id.get(location, None)
- location = SubnauticaLocation(self.player, location, loc_id, ret)
- ret.locations.append(location)
- if exits:
- for region_exit in exits:
- ret.exits.append(Entrance(self.player, region_exit, ret))
- return ret
-
def get_filler_item_name(self) -> str:
return item_table[self.multiworld.random.choice(items_by_type[ItemType.resource])].name
diff --git a/worlds/subnautica/docs/en_Subnautica.md b/worlds/subnautica/docs/en_Subnautica.md
index 5e99208b5f44..50004de5a003 100644
--- a/worlds/subnautica/docs/en_Subnautica.md
+++ b/worlds/subnautica/docs/en_Subnautica.md
@@ -1,8 +1,8 @@
# Subnautica
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
diff --git a/worlds/subnautica/docs/setup_en.md b/worlds/subnautica/docs/setup_en.md
index 83f4186bdfaf..7fc637df2639 100644
--- a/worlds/subnautica/docs/setup_en.md
+++ b/worlds/subnautica/docs/setup_en.md
@@ -19,7 +19,7 @@
Use the connect form in Subnautica's main menu to enter your connection information to connect to an Archipelago multiworld.
Connection information consists of:
- Host: the full url that you're trying to connect to, such as `archipelago.gg:38281`.
- - PlayerName: your name in the multiworld. Can also be called "slot name" and is the name you entered when creating your settings.
+ - PlayerName: your name in the multiworld. Can also be called "slot name" and is the name you entered when creating your options.
- Password: optional password, leave blank if no password was set.
After the connection is made, start a new game. You should start to see Archipelago chat messages to appear, such as a message announcing that you joined the multiworld.
diff --git a/worlds/terraria/Rules.dsv b/worlds/terraria/Rules.dsv
index b511db54de99..38ca4e575f38 100644
--- a/worlds/terraria/Rules.dsv
+++ b/worlds/terraria/Rules.dsv
@@ -207,7 +207,7 @@ Clothier; Npc;
Dungeon; ; Skeletron;
Dungeon Heist; Achievement; Dungeon;
Bone; ; Dungeon | (@calamity & #Skeletron);
-Bewitching Table; Minions(1); Dungeon;
+Bewitching Table; Minions(1); Dungeon | (Witch Doctor & Wizard);
Mechanic; ; Dungeon;
Wire; ; Mechanic;
Decryption Computer; Calamity; Mysterious Circuitry & Dubious Plating & Wire;
@@ -385,7 +385,7 @@ Armored Digger; Calamity | Location | Item;
Temple Raider; Achievement; #Plantera;
Lihzahrd Temple; ; #Plantera | (Plantera & Actuator) | @pickaxe(210) | (@calamity & Hardmode Anvil & Soul of Light & Soul of Night);
Solar Eclipse; ; Lihzahrd Temple & Wall of Flesh;
-Broken Hero Sword; ; (Solar Eclipse & Plantera) | (@calamity & #Calamitas Clone);
+Broken Hero Sword; ; (Solar Eclipse & Plantera & @mech_boss(3)) | (@calamity & #Calamitas Clone);
Terra Blade; ; Hardmode Anvil & True Night's Edge & True Excalibur & Broken Hero Sword & (~@calamity | Living Shard);
Sword of the Hero; Achievement; Terra Blade;
Kill the Sun; Achievement; Solar Eclipse;
diff --git a/worlds/terraria/docs/en_Terraria.md b/worlds/terraria/docs/en_Terraria.md
index b0a8529ba76b..26428f75789d 100644
--- a/worlds/terraria/docs/en_Terraria.md
+++ b/worlds/terraria/docs/en_Terraria.md
@@ -1,14 +1,14 @@
# Terraria
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
Boss/event flags are randomized. So, defeating Empress of Light could give you Post-Skeletron, which allows you to enter
-the Dungeon, for example. In your player settings, you may also add item rewards and achievements to the pool.
+the Dungeon, for example. In your player options, you may also add item rewards and achievements to the pool.
## What Terraria items can appear in other players' worlds?
diff --git a/worlds/terraria/docs/setup_en.md b/worlds/terraria/docs/setup_en.md
index b69af591fa5c..55a4df1df30d 100644
--- a/worlds/terraria/docs/setup_en.md
+++ b/worlds/terraria/docs/setup_en.md
@@ -43,7 +43,7 @@ files are, and how they are used.
### Where do I get a YAML?
-You can use the [game settings page for Terraria](/games/Terraria/player-settings) here
+You can use the [game options page for Terraria](/games/Terraria/player-options) here
on the Archipelago website to generate a YAML using a graphical interface.
## Joining an Archipelago Game in Terraria
diff --git a/worlds/timespinner/Regions.py b/worlds/timespinner/Regions.py
index fc7535642949..f80babc0e6d4 100644
--- a/worlds/timespinner/Regions.py
+++ b/worlds/timespinner/Regions.py
@@ -247,13 +247,7 @@ def connect(world: MultiWorld, player: int, source: str, target: str,
sourceRegion = world.get_region(source, player)
targetRegion = world.get_region(target, player)
-
- connection = Entrance(player, "", sourceRegion)
-
- if rule:
- connection.access_rule = rule
- sourceRegion.exits.append(connection)
- connection.connect(targetRegion)
+ sourceRegion.connect(targetRegion, rule=rule)
def split_location_datas_per_region(locations: List[LocationData]) -> Dict[str, List[LocationData]]:
diff --git a/worlds/timespinner/docs/en_Timespinner.md b/worlds/timespinner/docs/en_Timespinner.md
index 6a9e7fa4c039..a5b1419b94a7 100644
--- a/worlds/timespinner/docs/en_Timespinner.md
+++ b/worlds/timespinner/docs/en_Timespinner.md
@@ -1,8 +1,8 @@
# Timespinner
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
diff --git a/worlds/timespinner/docs/setup_en.md b/worlds/timespinner/docs/setup_en.md
index c47c639cd20d..7ee51f91323a 100644
--- a/worlds/timespinner/docs/setup_en.md
+++ b/worlds/timespinner/docs/setup_en.md
@@ -33,8 +33,8 @@ randomized mode. For more info see the [ReadMe](https://github.com/Jarno458/TsRa
## Where do I get a config file?
-The [Player Settings](/games/Timespinner/player-settings) page on the website allows you to
-configure your personal settings and export them into a config file
+The [Player Options](/games/Timespinner/player-options) page on the website allows you to
+configure your personal options and export them into a config file
* The Timespinner Randomizer option "StinkyMaw" is currently always enabled for Archipelago generated seeds
* The Timespinner Randomizer options "ProgressiveVerticalMovement" & "ProgressiveKeycards" are currently not supported
diff --git a/worlds/tloz/ItemPool.py b/worlds/tloz/ItemPool.py
index 456598edecef..5b90e99722df 100644
--- a/worlds/tloz/ItemPool.py
+++ b/worlds/tloz/ItemPool.py
@@ -94,17 +94,17 @@ def get_pool_core(world):
# Starting Weapon
start_weapon_locations = starting_weapon_locations.copy()
final_starting_weapons = [weapon for weapon in starting_weapons
- if weapon not in world.multiworld.non_local_items[world.player]]
+ if weapon not in world.options.non_local_items]
if not final_starting_weapons:
final_starting_weapons = starting_weapons
starting_weapon = random.choice(final_starting_weapons)
- if world.multiworld.StartingPosition[world.player] == StartingPosition.option_safe:
+ if world.options.StartingPosition == StartingPosition.option_safe:
placed_items[start_weapon_locations[0]] = starting_weapon
- elif world.multiworld.StartingPosition[world.player] in \
+ elif world.options.StartingPosition in \
[StartingPosition.option_unsafe, StartingPosition.option_dangerous]:
- if world.multiworld.StartingPosition[world.player] == StartingPosition.option_dangerous:
+ if world.options.StartingPosition == StartingPosition.option_dangerous:
for location in dangerous_weapon_locations:
- if world.multiworld.ExpandedPool[world.player] or "Drop" not in location:
+ if world.options.ExpandedPool or "Drop" not in location:
start_weapon_locations.append(location)
placed_items[random.choice(start_weapon_locations)] = starting_weapon
else:
@@ -115,7 +115,7 @@ def get_pool_core(world):
# Triforce Fragments
fragment = "Triforce Fragment"
- if world.multiworld.ExpandedPool[world.player]:
+ if world.options.ExpandedPool:
possible_level_locations = [location for location in all_level_locations
if location not in level_locations[8]]
else:
@@ -125,15 +125,15 @@ def get_pool_core(world):
if location in possible_level_locations:
possible_level_locations.remove(location)
for level in range(1, 9):
- if world.multiworld.TriforceLocations[world.player] == TriforceLocations.option_vanilla:
+ if world.options.TriforceLocations == TriforceLocations.option_vanilla:
placed_items[f"Level {level} Triforce"] = fragment
- elif world.multiworld.TriforceLocations[world.player] == TriforceLocations.option_dungeons:
+ elif world.options.TriforceLocations == TriforceLocations.option_dungeons:
placed_items[possible_level_locations.pop(random.randint(0, len(possible_level_locations) - 1))] = fragment
else:
pool.append(fragment)
# Level 9 junk fill
- if world.multiworld.ExpandedPool[world.player] > 0:
+ if world.options.ExpandedPool > 0:
spots = random.sample(level_locations[8], len(level_locations[8]) // 2)
for spot in spots:
junk = random.choice(list(minor_items.keys()))
@@ -142,7 +142,7 @@ def get_pool_core(world):
# Finish Pool
final_pool = basic_pool
- if world.multiworld.ExpandedPool[world.player]:
+ if world.options.ExpandedPool:
final_pool = {
item: basic_pool.get(item, 0) + minor_items.get(item, 0) + take_any_items.get(item, 0)
for item in set(basic_pool) | set(minor_items) | set(take_any_items)
diff --git a/worlds/tloz/Locations.py b/worlds/tloz/Locations.py
index 3e46c4383373..5b30357c940c 100644
--- a/worlds/tloz/Locations.py
+++ b/worlds/tloz/Locations.py
@@ -105,6 +105,10 @@
"Level 7 Bomb Drop (Dodongos)", "Level 7 Rupee Drop (Goriyas North)"
]
+gleeok_locations = [
+ "Level 4 Boss", "Level 4 Triforce", "Level 8 Boss", "Level 8 Triforce"
+]
+
floor_location_game_offsets_early = {
"Level 1 Item (Bow)": 0x7F,
"Level 1 Item (Boomerang)": 0x44,
diff --git a/worlds/tloz/Options.py b/worlds/tloz/Options.py
index 96bd3e296dca..58a50ec35929 100644
--- a/worlds/tloz/Options.py
+++ b/worlds/tloz/Options.py
@@ -1,5 +1,6 @@
import typing
-from Options import Option, DefaultOnToggle, Choice
+from dataclasses import dataclass
+from Options import Option, DefaultOnToggle, Choice, PerGameCommonOptions
class ExpandedPool(DefaultOnToggle):
@@ -32,9 +33,8 @@ class StartingPosition(Choice):
option_dangerous = 2
option_very_dangerous = 3
-
-tloz_options: typing.Dict[str, type(Option)] = {
- "ExpandedPool": ExpandedPool,
- "TriforceLocations": TriforceLocations,
- "StartingPosition": StartingPosition
-}
+@dataclass
+class TlozOptions(PerGameCommonOptions):
+ ExpandedPool: ExpandedPool
+ TriforceLocations: TriforceLocations
+ StartingPosition: StartingPosition
diff --git a/worlds/tloz/Rules.py b/worlds/tloz/Rules.py
index 12bf466bce99..f8b21bff712c 100644
--- a/worlds/tloz/Rules.py
+++ b/worlds/tloz/Rules.py
@@ -1,7 +1,7 @@
from typing import TYPE_CHECKING
from worlds.generic.Rules import add_rule
-from .Locations import food_locations, shop_locations
+from .Locations import food_locations, shop_locations, gleeok_locations
from .ItemPool import dangerous_weapon_locations
from .Options import StartingPosition
@@ -11,6 +11,7 @@
def set_rules(tloz_world: "TLoZWorld"):
player = tloz_world.player
world = tloz_world.multiworld
+ options = tloz_world.options
# Boss events for a nicer spoiler log play through
for level in range(1, 9):
@@ -23,7 +24,7 @@ def set_rules(tloz_world: "TLoZWorld"):
# No dungeons without weapons except for the dangerous weapon locations if we're dangerous, no unsafe dungeons
for i, level in enumerate(tloz_world.levels[1:10]):
for location in level.locations:
- if world.StartingPosition[player] < StartingPosition.option_dangerous \
+ if options.StartingPosition < StartingPosition.option_dangerous \
or location.name not in dangerous_weapon_locations:
add_rule(world.get_location(location.name, player),
lambda state: state.has_group("weapons", player))
@@ -66,7 +67,7 @@ def set_rules(tloz_world: "TLoZWorld"):
lambda state: state.has("Recorder", player))
add_rule(world.get_location("Level 7 Boss", player),
lambda state: state.has("Recorder", player))
- if world.ExpandedPool[player]:
+ if options.ExpandedPool:
add_rule(world.get_location("Level 7 Key Drop (Stalfos)", player),
lambda state: state.has("Recorder", player))
add_rule(world.get_location("Level 7 Bomb Drop (Digdogger)", player),
@@ -75,13 +76,17 @@ def set_rules(tloz_world: "TLoZWorld"):
lambda state: state.has("Recorder", player))
for location in food_locations:
- if world.ExpandedPool[player] or "Drop" not in location:
+ if options.ExpandedPool or "Drop" not in location:
add_rule(world.get_location(location, player),
lambda state: state.has("Food", player))
+ for location in gleeok_locations:
+ add_rule(world.get_location(location, player),
+ lambda state: state.has_group("swords", player) or state.has("Magical Rod", player))
+
add_rule(world.get_location("Level 8 Item (Magical Key)", player),
lambda state: state.has("Bow", player) and state.has_group("arrows", player))
- if world.ExpandedPool[player]:
+ if options.ExpandedPool:
add_rule(world.get_location("Level 8 Bomb Drop (Darknuts North)", player),
lambda state: state.has("Bow", player) and state.has_group("arrows", player))
@@ -106,13 +111,13 @@ def set_rules(tloz_world: "TLoZWorld"):
for location in stepladder_locations:
add_rule(world.get_location(location, player),
lambda state: state.has("Stepladder", player))
- if world.ExpandedPool[player]:
+ if options.ExpandedPool:
for location in stepladder_locations_expanded:
add_rule(world.get_location(location, player),
lambda state: state.has("Stepladder", player))
# Don't allow Take Any Items until we can actually get in one
- if world.ExpandedPool[player]:
+ if options.ExpandedPool:
add_rule(world.get_location("Take Any Item Left", player),
lambda state: state.has_group("candles", player) or
state.has("Raft", player))
diff --git a/worlds/tloz/__init__.py b/worlds/tloz/__init__.py
index 6e8927c4e7b9..b2f23ae2ca91 100644
--- a/worlds/tloz/__init__.py
+++ b/worlds/tloz/__init__.py
@@ -13,7 +13,7 @@
from .Items import item_table, item_prices, item_game_ids
from .Locations import location_table, level_locations, major_locations, shop_locations, all_level_locations, \
standard_level_locations, shop_price_location_ids, secret_money_ids, location_ids, food_locations
-from .Options import tloz_options
+from .Options import TlozOptions
from .Rom import TLoZDeltaPatch, get_base_rom_path, first_quest_dungeon_items_early, first_quest_dungeon_items_late
from .Rules import set_rules
from worlds.AutoWorld import World, WebWorld
@@ -45,7 +45,7 @@ class DisplayMsgs(settings.Bool):
class TLoZWeb(WebWorld):
theme = "stone"
setup = Tutorial(
- "Multiworld Setup Tutorial",
+ "Multiworld Setup Guide",
"A guide to setting up The Legend of Zelda for Archipelago on your computer.",
"English",
"multiworld_en.md",
@@ -63,7 +63,8 @@ class TLoZWorld(World):
This randomizer shuffles all the items in the game around, leading to a new adventure
every time.
"""
- option_definitions = tloz_options
+ options_dataclass = TlozOptions
+ options: TlozOptions
settings: typing.ClassVar[TLoZSettings]
game = "The Legend of Zelda"
topology_present = False
@@ -132,7 +133,7 @@ def create_regions(self):
for i, level in enumerate(level_locations):
for location in level:
- if self.multiworld.ExpandedPool[self.player] or "Drop" not in location:
+ if self.options.ExpandedPool or "Drop" not in location:
self.levels[i + 1].locations.append(
self.create_location(location, self.location_name_to_id[location], self.levels[i + 1]))
@@ -144,7 +145,7 @@ def create_regions(self):
self.levels[level].locations.append(boss_event)
for location in major_locations:
- if self.multiworld.ExpandedPool[self.player] or "Take Any" not in location:
+ if self.options.ExpandedPool or "Take Any" not in location:
overworld.locations.append(
self.create_location(location, self.location_name_to_id[location], overworld))
@@ -179,7 +180,7 @@ def generate_basic(self):
self.multiworld.get_location("Zelda", self.player).place_locked_item(self.create_event("Rescued Zelda!"))
add_rule(self.multiworld.get_location("Zelda", self.player),
- lambda state: ganon in state.locations_checked)
+ lambda state: state.has("Triforce of Power", self.player))
self.multiworld.completion_condition[self.player] = lambda state: state.has("Rescued Zelda!", self.player)
def apply_base_patch(self, rom):
@@ -311,7 +312,7 @@ def get_filler_item_name(self) -> str:
return self.multiworld.random.choice(self.filler_items)
def fill_slot_data(self) -> Dict[str, Any]:
- if self.multiworld.ExpandedPool[self.player]:
+ if self.options.ExpandedPool:
take_any_left = self.multiworld.get_location("Take Any Item Left", self.player).item
take_any_middle = self.multiworld.get_location("Take Any Item Middle", self.player).item
take_any_right = self.multiworld.get_location("Take Any Item Right", self.player).item
diff --git a/worlds/tloz/docs/en_The Legend of Zelda.md b/worlds/tloz/docs/en_The Legend of Zelda.md
index 7c2e6deda5bd..96b613673f00 100644
--- a/worlds/tloz/docs/en_The Legend of Zelda.md
+++ b/worlds/tloz/docs/en_The Legend of Zelda.md
@@ -1,8 +1,8 @@
# The Legend of Zelda (NES)
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
@@ -41,7 +41,6 @@ filler and useful items will cost less, and uncategorized items will be in the m
- Pressing Select will cycle through your inventory.
- Shop purchases are tracked within sessions, indicated by the item being elevated from its normal position.
- What slots from a Take Any Cave have been chosen are similarly tracked.
--
## Local Unique Commands
diff --git a/worlds/tloz/docs/multiworld_en.md b/worlds/tloz/docs/multiworld_en.md
index df857f16df5b..366531e2e43a 100644
--- a/worlds/tloz/docs/multiworld_en.md
+++ b/worlds/tloz/docs/multiworld_en.md
@@ -39,8 +39,8 @@ guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
### Where do I get a config file?
-The Player Settings page on the website allows you to configure your personal settings and export a config file from
-them. Player settings page: [The Legend of Zelda Player Settings Page](/games/The%20Legend%20of%20Zelda/player-settings)
+The Player Options page on the website allows you to configure your personal options and export a config file from
+them. Player options page: [The Legend of Zelda Player Sptions Page](/games/The%20Legend%20of%20Zelda/player-options)
### Verifying your config file
@@ -49,8 +49,8 @@ validator page: [YAML Validation page](/check)
## Generating a Single-Player Game
-1. Navigate to the Player Settings page, configure your options, and click the "Generate Game" button.
- - Player Settings page: [The Legend of Zelda Player Settings Page](/games/The%20Legend%20of%20Zelda/player-settings)
+1. Navigate to the Player Options page, configure your options, and click the "Generate Game" button.
+ - Player Options page: [The Legend of Zelda Player Options Page](/games/The%20Legend%20of%20Zelda/player-options)
2. You will be presented with a "Seed Info" page.
3. Click the "Create New Room" link.
4. You will be presented with a server page, from which you can download your patch file.
diff --git a/worlds/tunic/__init__.py b/worlds/tunic/__init__.py
new file mode 100644
index 000000000000..3220c6c9347d
--- /dev/null
+++ b/worlds/tunic/__init__.py
@@ -0,0 +1,315 @@
+from typing import Dict, List, Any
+
+from BaseClasses import Region, Location, Item, Tutorial, ItemClassification
+from .items import item_name_to_id, item_table, item_name_groups, fool_tiers, filler_items, slot_data_item_names
+from .locations import location_table, location_name_groups, location_name_to_id, hexagon_locations
+from .rules import set_location_rules, set_region_rules, randomize_ability_unlocks, gold_hexagon
+from .er_rules import set_er_location_rules
+from .regions import tunic_regions
+from .er_scripts import create_er_regions
+from .er_data import portal_mapping
+from .options import TunicOptions
+from worlds.AutoWorld import WebWorld, World
+from decimal import Decimal, ROUND_HALF_UP
+
+
+class TunicWeb(WebWorld):
+ tutorials = [
+ Tutorial(
+ tutorial_name="Multiworld Setup Guide",
+ description="A guide to setting up the TUNIC Randomizer for Archipelago multiworld games.",
+ language="English",
+ file_name="setup_en.md",
+ link="setup/en",
+ authors=["SilentDestroyer"]
+ )
+ ]
+ theme = "grassFlowers"
+ game = "TUNIC"
+
+
+class TunicItem(Item):
+ game: str = "TUNIC"
+
+
+class TunicLocation(Location):
+ game: str = "TUNIC"
+
+
+class TunicWorld(World):
+ """
+ Explore a land filled with lost legends, ancient powers, and ferocious monsters in TUNIC, an isometric action game
+ about a small fox on a big adventure. Stranded on a mysterious beach, armed with only your own curiosity, you will
+ confront colossal beasts, collect strange and powerful items, and unravel long-lost secrets. Be brave, tiny fox!
+ """
+ game = "TUNIC"
+ web = TunicWeb()
+
+ options: TunicOptions
+ options_dataclass = TunicOptions
+ item_name_groups = item_name_groups
+ location_name_groups = location_name_groups
+
+ item_name_to_id = item_name_to_id
+ location_name_to_id = location_name_to_id
+
+ ability_unlocks: Dict[str, int]
+ slot_data_items: List[TunicItem]
+ tunic_portal_pairs: Dict[str, str]
+ er_portal_hints: Dict[int, str]
+
+ def generate_early(self) -> None:
+ # Universal tracker stuff, shouldn't do anything in standard gen
+ if hasattr(self.multiworld, "re_gen_passthrough"):
+ if "TUNIC" in self.multiworld.re_gen_passthrough:
+ passthrough = self.multiworld.re_gen_passthrough["TUNIC"]
+ self.options.start_with_sword.value = passthrough["start_with_sword"]
+ self.options.keys_behind_bosses.value = passthrough["keys_behind_bosses"]
+ self.options.sword_progression.value = passthrough["sword_progression"]
+ self.options.ability_shuffling.value = passthrough["ability_shuffling"]
+ self.options.logic_rules.value = passthrough["logic_rules"]
+ self.options.lanternless.value = passthrough["lanternless"]
+ self.options.maskless.value = passthrough["maskless"]
+ self.options.hexagon_quest.value = passthrough["hexagon_quest"]
+ self.options.entrance_rando.value = passthrough["entrance_rando"]
+ self.options.shuffle_ladders.value = passthrough["shuffle_ladders"]
+
+ def create_item(self, name: str) -> TunicItem:
+ item_data = item_table[name]
+ return TunicItem(name, item_data.classification, self.item_name_to_id[name], self.player)
+
+ def create_items(self) -> None:
+ keys_behind_bosses = self.options.keys_behind_bosses
+ hexagon_quest = self.options.hexagon_quest
+ sword_progression = self.options.sword_progression
+
+ tunic_items: List[TunicItem] = []
+ self.slot_data_items = []
+
+ items_to_create: Dict[str, int] = {item: data.quantity_in_item_pool for item, data in item_table.items()}
+
+ for money_fool in fool_tiers[self.options.fool_traps]:
+ items_to_create["Fool Trap"] += items_to_create[money_fool]
+ items_to_create[money_fool] = 0
+
+ if self.options.start_with_sword:
+ self.multiworld.push_precollected(self.create_item("Sword"))
+
+ if sword_progression:
+ items_to_create["Stick"] = 0
+ items_to_create["Sword"] = 0
+ else:
+ items_to_create["Sword Upgrade"] = 0
+
+ if self.options.laurels_location:
+ laurels = self.create_item("Hero's Laurels")
+ if self.options.laurels_location == "6_coins":
+ self.multiworld.get_location("Coins in the Well - 6 Coins", self.player).place_locked_item(laurels)
+ elif self.options.laurels_location == "10_coins":
+ self.multiworld.get_location("Coins in the Well - 10 Coins", self.player).place_locked_item(laurels)
+ elif self.options.laurels_location == "10_fairies":
+ self.multiworld.get_location("Secret Gathering Place - 10 Fairy Reward", self.player).place_locked_item(laurels)
+ self.slot_data_items.append(laurels)
+ items_to_create["Hero's Laurels"] = 0
+
+ if keys_behind_bosses:
+ for rgb_hexagon, location in hexagon_locations.items():
+ hex_item = self.create_item(gold_hexagon if hexagon_quest else rgb_hexagon)
+ self.multiworld.get_location(location, self.player).place_locked_item(hex_item)
+ self.slot_data_items.append(hex_item)
+ items_to_create[rgb_hexagon] = 0
+ items_to_create[gold_hexagon] -= 3
+
+ # Filler items in the item pool
+ available_filler: List[str] = [filler for filler in items_to_create if items_to_create[filler] > 0 and
+ item_table[filler].classification == ItemClassification.filler]
+
+ # Remove filler to make room for other items
+ def remove_filler(amount: int):
+ for _ in range(0, amount):
+ if not available_filler:
+ fill = "Fool Trap"
+ else:
+ fill = self.random.choice(available_filler)
+ if items_to_create[fill] == 0:
+ raise Exception("No filler items left to accommodate options selected. Turn down fool trap amount.")
+ items_to_create[fill] -= 1
+ if items_to_create[fill] == 0:
+ available_filler.remove(fill)
+
+ if self.options.shuffle_ladders:
+ ladder_count = 0
+ for item_name, item_data in item_table.items():
+ if item_data.item_group == "ladders":
+ items_to_create[item_name] = 1
+ ladder_count += 1
+ remove_filler(ladder_count)
+
+ if hexagon_quest:
+ # Calculate number of hexagons in item pool
+ hexagon_goal = self.options.hexagon_goal
+ extra_hexagons = self.options.extra_hexagon_percentage
+ items_to_create[gold_hexagon] += int((Decimal(100 + extra_hexagons) / 100 * hexagon_goal).to_integral_value(rounding=ROUND_HALF_UP))
+
+ # Replace pages and normal hexagons with filler
+ for replaced_item in list(filter(lambda item: "Pages" in item or item in hexagon_locations, items_to_create)):
+ filler_name = self.get_filler_item_name()
+ items_to_create[filler_name] += items_to_create[replaced_item]
+ if items_to_create[filler_name] >= 1 and filler_name not in available_filler:
+ available_filler.append(filler_name)
+ items_to_create[replaced_item] = 0
+
+ remove_filler(items_to_create[gold_hexagon])
+
+ if self.options.maskless:
+ mask_item = TunicItem("Scavenger Mask", ItemClassification.useful, self.item_name_to_id["Scavenger Mask"], self.player)
+ tunic_items.append(mask_item)
+ items_to_create["Scavenger Mask"] = 0
+
+ if self.options.lanternless:
+ lantern_item = TunicItem("Lantern", ItemClassification.useful, self.item_name_to_id["Lantern"], self.player)
+ tunic_items.append(lantern_item)
+ items_to_create["Lantern"] = 0
+
+ for item, quantity in items_to_create.items():
+ for i in range(0, quantity):
+ tunic_item: TunicItem = self.create_item(item)
+ if item in slot_data_item_names:
+ self.slot_data_items.append(tunic_item)
+ tunic_items.append(tunic_item)
+
+ self.multiworld.itempool += tunic_items
+
+ def create_regions(self) -> None:
+ self.tunic_portal_pairs = {}
+ self.er_portal_hints = {}
+ self.ability_unlocks = randomize_ability_unlocks(self.random, self.options)
+
+ # stuff for universal tracker support, can be ignored for standard gen
+ if hasattr(self.multiworld, "re_gen_passthrough"):
+ if "TUNIC" in self.multiworld.re_gen_passthrough:
+ passthrough = self.multiworld.re_gen_passthrough["TUNIC"]
+ self.ability_unlocks["Pages 24-25 (Prayer)"] = passthrough["Hexagon Quest Prayer"]
+ self.ability_unlocks["Pages 42-43 (Holy Cross)"] = passthrough["Hexagon Quest Holy Cross"]
+ self.ability_unlocks["Pages 52-53 (Icebolt)"] = passthrough["Hexagon Quest Icebolt"]
+
+ # ladder rando uses ER with vanilla connections, so that we're not managing more rules files
+ if self.options.entrance_rando or self.options.shuffle_ladders:
+ portal_pairs = create_er_regions(self)
+ if self.options.entrance_rando:
+ # these get interpreted by the game to tell it which entrances to connect
+ for portal1, portal2 in portal_pairs.items():
+ self.tunic_portal_pairs[portal1.scene_destination()] = portal2.scene_destination()
+ else:
+ # for non-ER, non-ladders
+ for region_name in tunic_regions:
+ region = Region(region_name, self.player, self.multiworld)
+ self.multiworld.regions.append(region)
+
+ for region_name, exits in tunic_regions.items():
+ region = self.multiworld.get_region(region_name, self.player)
+ region.add_exits(exits)
+
+ for location_name, location_id in self.location_name_to_id.items():
+ region = self.multiworld.get_region(location_table[location_name].region, self.player)
+ location = TunicLocation(self.player, location_name, location_id, region)
+ region.locations.append(location)
+
+ victory_region = self.multiworld.get_region("Spirit Arena", self.player)
+ victory_location = TunicLocation(self.player, "The Heir", None, victory_region)
+ victory_location.place_locked_item(TunicItem("Victory", ItemClassification.progression, None, self.player))
+ self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
+ victory_region.locations.append(victory_location)
+
+ def set_rules(self) -> None:
+ if self.options.entrance_rando or self.options.shuffle_ladders:
+ set_er_location_rules(self, self.ability_unlocks)
+ else:
+ set_region_rules(self, self.ability_unlocks)
+ set_location_rules(self, self.ability_unlocks)
+
+ def get_filler_item_name(self) -> str:
+ return self.random.choice(filler_items)
+
+ def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]):
+ if self.options.entrance_rando:
+ hint_data.update({self.player: {}})
+ # all state seems to have efficient paths
+ all_state = self.multiworld.get_all_state(True)
+ all_state.update_reachable_regions(self.player)
+ paths = all_state.path
+ portal_names = [portal.name for portal in portal_mapping]
+ for location in self.multiworld.get_locations(self.player):
+ # skipping event locations
+ if not location.address:
+ continue
+ path_to_loc = []
+ previous_name = "placeholder"
+ name, connection = paths[location.parent_region]
+ while connection != ("Menu", None):
+ name, connection = connection
+ # for LS entrances, we just want to give the portal name
+ if "(LS)" in name:
+ name, _ = name.split(" (LS) ")
+ # was getting some cases like Library Grave -> Library Grave -> other place
+ if name in portal_names and name != previous_name:
+ previous_name = name
+ path_to_loc.append(name)
+ hint_text = " -> ".join(reversed(path_to_loc))
+ if hint_text:
+ hint_data[self.player][location.address] = hint_text
+
+ def fill_slot_data(self) -> Dict[str, Any]:
+ slot_data: Dict[str, Any] = {
+ "seed": self.random.randint(0, 2147483647),
+ "start_with_sword": self.options.start_with_sword.value,
+ "keys_behind_bosses": self.options.keys_behind_bosses.value,
+ "sword_progression": self.options.sword_progression.value,
+ "ability_shuffling": self.options.ability_shuffling.value,
+ "hexagon_quest": self.options.hexagon_quest.value,
+ "fool_traps": self.options.fool_traps.value,
+ "logic_rules": self.options.logic_rules.value,
+ "lanternless": self.options.lanternless.value,
+ "maskless": self.options.maskless.value,
+ "entrance_rando": int(bool(self.options.entrance_rando.value)),
+ "shuffle_ladders": self.options.shuffle_ladders.value,
+ "Hexagon Quest Prayer": self.ability_unlocks["Pages 24-25 (Prayer)"],
+ "Hexagon Quest Holy Cross": self.ability_unlocks["Pages 42-43 (Holy Cross)"],
+ "Hexagon Quest Icebolt": self.ability_unlocks["Pages 52-53 (Icebolt)"],
+ "Hexagon Quest Goal": self.options.hexagon_goal.value,
+ "Entrance Rando": self.tunic_portal_pairs
+ }
+
+ for tunic_item in filter(lambda item: item.location is not None and item.code is not None, self.slot_data_items):
+ if tunic_item.name not in slot_data:
+ slot_data[tunic_item.name] = []
+ if tunic_item.name == gold_hexagon and len(slot_data[gold_hexagon]) >= 6:
+ continue
+ slot_data[tunic_item.name].extend([tunic_item.location.name, tunic_item.location.player])
+
+ for start_item in self.options.start_inventory_from_pool:
+ if start_item in slot_data_item_names:
+ if start_item not in slot_data:
+ slot_data[start_item] = []
+ for i in range(0, self.options.start_inventory_from_pool[start_item]):
+ slot_data[start_item].extend(["Your Pocket", self.player])
+
+ for plando_item in self.multiworld.plando_items[self.player]:
+ if plando_item["from_pool"]:
+ items_to_find = set()
+ for item_type in [key for key in ["item", "items"] if key in plando_item]:
+ for item in plando_item[item_type]:
+ items_to_find.add(item)
+ for item in items_to_find:
+ if item in slot_data_item_names:
+ slot_data[item] = []
+ for item_location in self.multiworld.find_item_locations(item, self.player):
+ slot_data[item].extend([item_location.name, item_location.player])
+
+ return slot_data
+
+ # for the universal tracker, doesn't get called in standard gen
+ @staticmethod
+ def interpret_slot_data(slot_data: Dict[str, Any]) -> Dict[str, Any]:
+ # returning slot_data so it regens, giving it back in multiworld.re_gen_passthrough
+ return slot_data
diff --git a/worlds/tunic/docs/en_TUNIC.md b/worlds/tunic/docs/en_TUNIC.md
new file mode 100644
index 000000000000..f1e0056041bb
--- /dev/null
+++ b/worlds/tunic/docs/en_TUNIC.md
@@ -0,0 +1,90 @@
+# TUNIC
+
+## Where is the options page?
+
+The [player options page for this game](../player-options) contains all the options you need to configure and export a config file.
+
+## I haven't played TUNIC before.
+
+**Play vanilla first.** It is **_heavily discouraged_** to play this randomizer before playing the vanilla game.
+It is recommended that you achieve both endings in the vanilla game before playing the randomizer.
+
+## What does randomization do to this game?
+
+In the TUNIC Randomizer, every item in the game is randomized. All chests, key item pickups, instruction manual pages, hero relics,
+and other unique items are shuffled.
+
+Ability shuffling is an option available from the options page to shuffle certain abilities (prayer, holy cross, and the icebolt combo),
+preventing them from being used until they are unlocked.
+
+Entrances can also be randomized, shuffling the connections between every door, teleporter, etc. in the game.
+
+Enemy randomization and other options are also available and can be turned on in the client mod.
+
+## What is the goal of TUNIC when randomized?
+The standard goal is the same as the vanilla game. Find the three hexagon keys, then Take Your
+Rightful Place or seek another path and Share Your Wisdom.
+
+Alternatively, Hexagon Quest is a mode that shuffles a certain number of Gold Questagons into the item pool, with the goal
+being to find the required amount of them and then Share Your Wisdom.
+
+## What items from TUNIC can appear in another player's world?
+Every item has a chance to appear in another player's world.
+
+## How many checks are in TUNIC?
+There are 302 checks located across the world of TUNIC.
+
+## What do items from other worlds look like in TUNIC?
+Items belonging to other TUNIC players will either appear as that item directly (if in a freestanding location) or in a
+chest with the original chest texture for that item.
+
+Items belonging to non-TUNIC players will either appear as a question-mark block (if in a freestanding location) or in a chest with
+a question mark symbol on it. Additionally, non-TUNIC items are color-coded by classification, with green for filler, blue for useful, and gold for progression.
+
+## Is there a tracker pack?
+There is a [tracker pack](https://github.com/SapphireSapphic/TunicTracker/releases/latest). It is compatible with both Poptracker and Emotracker. Using Poptracker, it will automatically track checked locations and important items received. It can also automatically tab between maps as you traverse the world. This tracker was originally created by SapphireSapphic and ScoutJD, and has been extensively updated by Br00ty.
+
+There is also a [standalone item tracker](https://github.com/radicoon/tunic-rando-tracker/releases/latest), which tracks what items you have received. It is great for adding an item overlay to streaming setups. This item tracker was created by Radicoon.
+
+There is an [entrance tracker](https://scipiowright.gitlab.io/tunic-tracker/) for the entrance randomizer. This is a manual tracker that runs in your browser. This tracker was created by ScipioWright, and is a fork of the Pokémon Tracker by [Sergi "Sekii" Santana](https://gitlab.com/Sekii/pokemon-tracker).
+
+You can also use the Universal Tracker (by Faris and qwint) to find a complete list of what checks are in logic with your current items. You can find it on the Archipelago Discord, in its post in the future-game-design channel. This tracker is an extension of the regular Archipelago Text Client.
+
+## What should I know regarding logic?
+- Nighttime is not considered in logic. Every check in the game is obtainable during the day.
+- The Cathedral is accessible during the day by using the Hero's Laurels to reach the Overworld fuse near the Swamp entrance.
+- The Secret Legend chest at the Cathedral can be obtained during the day by opening the Holy Cross door from the outside.
+
+For the Entrance Randomizer:
+- Activating a fuse to turn on a yellow teleporter pad also activates its counterpart in the Far Shore.
+- The West Garden fuse can be activated from below.
+- You can pray at the tree at the exterior of the Library.
+- The elevators in the Rooted Ziggurat only go down.
+- The portal in the trophy room of the Old House is active from the start.
+- The elevator in Cathedral is immediately usable without activating the fuse. Activating the fuse does nothing.
+
+## What item groups are there?
+Bombs, consumables (non-bomb ones), weapons, melee weapons (stick and sword), keys, hexagons, offerings, hero relics, cards, golden treasures, money, pages, and abilities (the three ability pages). There are also a few groups being used for singular items: laurels, orb, dagger, magic rod, holy cross, prayer, icebolt, and progressive sword.
+
+## What location groups are there?
+Holy cross (for all holy cross checks), fairies (for the two fairy checks), well (for the coin well checks), shop, bosses (for the bosses with checks associated with them), hero relic (for the 6 hero grave checks), and ladders (for the ladder items when you have shuffle ladders enabled).
+
+## Is Connection Plando supported?
+Yes. The host needs to enable it in their `host.yaml`, and the player's yaml needs to contain a plando_connections block.
+Example:
+```
+plando_connections:
+ - entrance: Stick House Entrance
+ exit: Stick House Exit
+ - entrance: Special Shop Exit
+ exit: Stairs to Top of the Mountain
+```
+Notes:
+- The Entrance Randomizer option must be enabled for it to work.
+- The `direction` field is not supported. Connections are always coupled.
+- For a list of entrance names, check `er_data.py` in the TUNIC world folder or generate a game with the Entrance Randomizer option enabled and check the spoiler log.
+- There is no limit to the number of Shops hard-coded into place.
+- If you have more than one shop in a scene, you may be wrong warped when exiting a shop.
+- If you have a shop in every scene, and you have an odd number of shops, it will error out.
+
+See the [Archipelago Plando Guide](../../../tutorial/Archipelago/plando/en) for more information on Plando and Connection Plando.
diff --git a/worlds/tunic/docs/setup_en.md b/worlds/tunic/docs/setup_en.md
new file mode 100644
index 000000000000..5ec41e8d526e
--- /dev/null
+++ b/worlds/tunic/docs/setup_en.md
@@ -0,0 +1,68 @@
+# TUNIC Setup Guide
+
+## Required Software
+
+- [TUNIC](https://tunicgame.com/) for PC (Steam Deck also supported)
+- [BepInEx (Unity IL2CPP)](https://github.com/BepInEx/BepInEx/releases/tag/v6.0.0-pre.1)
+- [TUNIC Randomizer Mod](https://github.com/silent-destroyer/tunic-randomizer/releases/latest)
+
+## Optional Software
+- [TUNIC Randomizer Map Tracker](https://github.com/SapphireSapphic/TunicTracker/releases/latest) (For use with EmoTracker/PopTracker)
+- [TUNIC Randomizer Item Auto-tracker](https://github.com/radicoon/tunic-rando-tracker/releases/latest)
+- [Archipelago Text Client](https://github.com/ArchipelagoMW/Archipelago/releases/latest)
+
+## Installation
+
+### Find Your Relevant Game Directories
+
+Find your TUNIC game installation directory:
+
+- **Steam**: Right click TUNIC in your Steam Library, then *Manage → Browse local files*.
+ - **Steam Deck**: Hold down the power button, tap "Switch to Desktop", then launch Steam from Desktop Mode to access the above option.
+- **PC Game Pass**: In the Xbox PC app, go to the TUNIC game page from your library, click the [...] button next to "Play", then
+*Manage → Files → Browse...*
+- **Other platforms**: Follow a similar pattern of steps as above to locate your specific game directory.
+
+### Install BepInEx
+
+BepInEx is a general purpose framework for modding Unity games, and is used to run the TUNIC Randomizer.
+
+Download [BepInEx](https://github.com/BepInEx/BepInEx/releases/download/v6.0.0-pre.1/BepInEx_UnityIL2CPP_x64_6.0.0-pre.1.zip).
+
+If playing on Steam Deck, follow this [guide to set up BepInEx via Proton](https://docs.bepinex.dev/articles/advanced/proton_wine.html).
+
+Extract the contents of the BepInEx .zip file into your TUNIC game directory:
+- **Steam**: Steam\steamapps\common\TUNIC
+- **PC Game Pass**: XboxGames\Tunic\Content
+- **Other platforms**: Place into the same folder that the Tunic_Data or Secret Legend_Data folder is found.
+
+Launch the game once and close it to finish the BepInEx installation.
+
+### Install The TUNIC Randomizer Mod
+
+Download the latest release of the [TUNIC Randomizer Mod](https://github.com/silent-destroyer/tunic-randomizer/releases/latest).
+
+Extract the contents of the downloaded .zip file, and find the folder labeled `Tunic Randomizer`.
+
+Copy the `Tunic Randomizer` folder into `BepInEx/plugins` in your TUNIC game installation directory.
+
+The filepath to the mod should look like `BepInEx/plugins/Tunic Randomizer/TunicRandomizer.dll`
+
+Launch the game, and if everything was installed correctly you should see `Randomizer Mod Ver. x.y.z` in the top left corner of the title screen!
+
+## Configure Archipelago Options
+
+### Configure Your YAML File
+
+Visit the [TUNIC options page](/games/Tunic/player-options) to generate a YAML with your selected options.
+
+### Configure Your Mod Settings
+Launch the game, and using the menu on the Title Screen select `Archipelago` under `Randomizer Mode`.
+
+Click the button labeled `Edit AP Config`, and fill in *Player*, *Hostname*, *Port*, and *Password* (if required) with the correct information for your room.
+
+Once you've input your information, click the `Close` button. If everything was configured properly, you should see `Status: Connected!` and your chosen game options will be shown under `World Settings`.
+
+An error message will display if the game fails to connect to the server.
+
+Be sure to also look at the in-game options menu for a variety of additional settings, such as enemy randomization!
\ No newline at end of file
diff --git a/worlds/tunic/er_data.py b/worlds/tunic/er_data.py
new file mode 100644
index 000000000000..d850a06dfa78
--- /dev/null
+++ b/worlds/tunic/er_data.py
@@ -0,0 +1,1079 @@
+from typing import Dict, NamedTuple, List, Tuple
+from enum import IntEnum
+
+
+class Portal(NamedTuple):
+ name: str # human-readable name
+ region: str # AP region
+ destination: str # vanilla destination scene
+ tag: str # vanilla tag
+
+ def scene(self) -> str: # the actual scene name in Tunic
+ return tunic_er_regions[self.region].game_scene
+
+ def scene_destination(self) -> str: # full, nonchanging name to interpret by the mod
+ return self.scene() + ", " + self.destination + self.tag
+
+ def destination_scene(self) -> str: # the vanilla connection
+ return self.destination + ", " + self.scene() + self.tag
+
+
+portal_mapping: List[Portal] = [
+ Portal(name="Stick House Entrance", region="Overworld",
+ destination="Sword Cave", tag="_"),
+ Portal(name="Windmill Entrance", region="Overworld",
+ destination="Windmill", tag="_"),
+ Portal(name="Well Ladder Entrance", region="Overworld Well Ladder",
+ destination="Sewer", tag="_entrance"),
+ Portal(name="Entrance to Well from Well Rail", region="Overworld Well to Furnace Rail",
+ destination="Sewer", tag="_west_aqueduct"),
+ Portal(name="Old House Door Entrance", region="Overworld Old House Door",
+ destination="Overworld Interiors", tag="_house"),
+ Portal(name="Old House Waterfall Entrance", region="Overworld",
+ destination="Overworld Interiors", tag="_under_checkpoint"),
+ Portal(name="Entrance to Furnace from Well Rail", region="Overworld Well to Furnace Rail",
+ destination="Furnace", tag="_gyro_upper_north"),
+ Portal(name="Entrance to Furnace under Windmill", region="Overworld",
+ destination="Furnace", tag="_gyro_upper_east"),
+ Portal(name="Entrance to Furnace near West Garden", region="Overworld to West Garden from Furnace",
+ destination="Furnace", tag="_gyro_west"),
+ Portal(name="Entrance to Furnace from Beach", region="Overworld Tunnel Turret",
+ destination="Furnace", tag="_gyro_lower"),
+ Portal(name="Caustic Light Cave Entrance", region="Overworld Swamp Lower Entry",
+ destination="Overworld Cave", tag="_"),
+ Portal(name="Swamp Upper Entrance", region="Overworld Swamp Upper Entry",
+ destination="Swamp Redux 2", tag="_wall"),
+ Portal(name="Swamp Lower Entrance", region="Overworld Swamp Lower Entry",
+ destination="Swamp Redux 2", tag="_conduit"),
+ Portal(name="Ruined Passage Not-Door Entrance", region="After Ruined Passage",
+ destination="Ruins Passage", tag="_east"),
+ Portal(name="Ruined Passage Door Entrance", region="Overworld Ruined Passage Door",
+ destination="Ruins Passage", tag="_west"),
+ Portal(name="Atoll Upper Entrance", region="Overworld to Atoll Upper",
+ destination="Atoll Redux", tag="_upper"),
+ Portal(name="Atoll Lower Entrance", region="Overworld Beach",
+ destination="Atoll Redux", tag="_lower"),
+ Portal(name="Special Shop Entrance", region="Overworld Special Shop Entry",
+ destination="ShopSpecial", tag="_"),
+ Portal(name="Maze Cave Entrance", region="Overworld Beach",
+ destination="Maze Room", tag="_"),
+ Portal(name="West Garden Entrance near Belltower", region="Overworld to West Garden Upper",
+ destination="Archipelagos Redux", tag="_upper"),
+ Portal(name="West Garden Entrance from Furnace", region="Overworld to West Garden from Furnace",
+ destination="Archipelagos Redux", tag="_lower"),
+ Portal(name="West Garden Laurels Entrance", region="Overworld West Garden Laurels Entry",
+ destination="Archipelagos Redux", tag="_lowest"),
+ Portal(name="Temple Door Entrance", region="Overworld Temple Door",
+ destination="Temple", tag="_main"),
+ Portal(name="Temple Rafters Entrance", region="Overworld after Temple Rafters",
+ destination="Temple", tag="_rafters"),
+ Portal(name="Ruined Shop Entrance", region="Overworld",
+ destination="Ruined Shop", tag="_"),
+ Portal(name="Patrol Cave Entrance", region="Overworld at Patrol Cave",
+ destination="PatrolCave", tag="_"),
+ Portal(name="Hourglass Cave Entrance", region="Overworld Beach",
+ destination="Town Basement", tag="_beach"),
+ Portal(name="Changing Room Entrance", region="Overworld",
+ destination="Changing Room", tag="_"),
+ Portal(name="Cube Cave Entrance", region="Overworld",
+ destination="CubeRoom", tag="_"),
+ Portal(name="Stairs from Overworld to Mountain", region="Upper Overworld",
+ destination="Mountain", tag="_"),
+ Portal(name="Overworld to Fortress", region="East Overworld",
+ destination="Fortress Courtyard", tag="_"),
+ Portal(name="Fountain HC Door Entrance", region="Overworld Fountain Cross Door",
+ destination="Town_FiligreeRoom", tag="_"),
+ Portal(name="Southeast HC Door Entrance", region="Overworld Southeast Cross Door",
+ destination="EastFiligreeCache", tag="_"),
+ Portal(name="Overworld to Quarry Connector", region="Overworld Quarry Entry",
+ destination="Darkwoods Tunnel", tag="_"),
+ Portal(name="Dark Tomb Main Entrance", region="Overworld",
+ destination="Crypt Redux", tag="_"),
+ Portal(name="Overworld to Forest Belltower", region="East Overworld",
+ destination="Forest Belltower", tag="_"),
+ Portal(name="Town to Far Shore", region="Overworld Town Portal",
+ destination="Transit", tag="_teleporter_town"),
+ Portal(name="Spawn to Far Shore", region="Overworld Spawn Portal",
+ destination="Transit", tag="_teleporter_starting island"),
+ Portal(name="Secret Gathering Place Entrance", region="Overworld",
+ destination="Waterfall", tag="_"),
+
+ Portal(name="Secret Gathering Place Exit", region="Secret Gathering Place",
+ destination="Overworld Redux", tag="_"),
+
+ Portal(name="Windmill Exit", region="Windmill",
+ destination="Overworld Redux", tag="_"),
+ Portal(name="Windmill Shop", region="Windmill",
+ destination="Shop", tag="_"),
+
+ Portal(name="Old House Door Exit", region="Old House Front",
+ destination="Overworld Redux", tag="_house"),
+ Portal(name="Old House to Glyph Tower", region="Old House Front",
+ destination="g_elements", tag="_"),
+ Portal(name="Old House Waterfall Exit", region="Old House Back",
+ destination="Overworld Redux", tag="_under_checkpoint"),
+
+ Portal(name="Glyph Tower Exit", region="Relic Tower",
+ destination="Overworld Interiors", tag="_"),
+
+ Portal(name="Changing Room Exit", region="Changing Room",
+ destination="Overworld Redux", tag="_"),
+
+ Portal(name="Fountain HC Room Exit", region="Fountain Cross Room",
+ destination="Overworld Redux", tag="_"),
+
+ Portal(name="Cube Cave Exit", region="Cube Cave",
+ destination="Overworld Redux", tag="_"),
+
+ Portal(name="Guard Patrol Cave Exit", region="Patrol Cave",
+ destination="Overworld Redux", tag="_"),
+
+ Portal(name="Ruined Shop Exit", region="Ruined Shop",
+ destination="Overworld Redux", tag="_"),
+
+ Portal(name="Furnace Exit towards Well", region="Furnace Fuse",
+ destination="Overworld Redux", tag="_gyro_upper_north"),
+ Portal(name="Furnace Exit to Dark Tomb", region="Furnace Walking Path",
+ destination="Crypt Redux", tag="_"),
+ Portal(name="Furnace Exit towards West Garden", region="Furnace Walking Path",
+ destination="Overworld Redux", tag="_gyro_west"),
+ Portal(name="Furnace Exit to Beach", region="Furnace Ladder Area",
+ destination="Overworld Redux", tag="_gyro_lower"),
+ Portal(name="Furnace Exit under Windmill", region="Furnace Ladder Area",
+ destination="Overworld Redux", tag="_gyro_upper_east"),
+
+ Portal(name="Stick House Exit", region="Stick House",
+ destination="Overworld Redux", tag="_"),
+
+ Portal(name="Ruined Passage Not-Door Exit", region="Ruined Passage",
+ destination="Overworld Redux", tag="_east"),
+ Portal(name="Ruined Passage Door Exit", region="Ruined Passage",
+ destination="Overworld Redux", tag="_west"),
+
+ Portal(name="Southeast HC Room Exit", region="Southeast Cross Room",
+ destination="Overworld Redux", tag="_"),
+
+ Portal(name="Caustic Light Cave Exit", region="Caustic Light Cave",
+ destination="Overworld Redux", tag="_"),
+
+ Portal(name="Maze Cave Exit", region="Maze Cave",
+ destination="Overworld Redux", tag="_"),
+
+ Portal(name="Hourglass Cave Exit", region="Hourglass Cave",
+ destination="Overworld Redux", tag="_beach"),
+
+ Portal(name="Special Shop Exit", region="Special Shop",
+ destination="Overworld Redux", tag="_"),
+
+ Portal(name="Temple Rafters Exit", region="Sealed Temple Rafters",
+ destination="Overworld Redux", tag="_rafters"),
+ Portal(name="Temple Door Exit", region="Sealed Temple",
+ destination="Overworld Redux", tag="_main"),
+
+ Portal(name="Well Ladder Exit", region="Beneath the Well Ladder Exit",
+ destination="Overworld Redux", tag="_entrance"),
+ Portal(name="Well to Well Boss", region="Beneath the Well Back",
+ destination="Sewer_Boss", tag="_"),
+ Portal(name="Well Exit towards Furnace", region="Beneath the Well Back",
+ destination="Overworld Redux", tag="_west_aqueduct"),
+
+ Portal(name="Well Boss to Well", region="Well Boss",
+ destination="Sewer", tag="_"),
+ Portal(name="Checkpoint to Dark Tomb", region="Dark Tomb Checkpoint",
+ destination="Crypt Redux", tag="_"),
+
+ Portal(name="Dark Tomb to Overworld", region="Dark Tomb Entry Point",
+ destination="Overworld Redux", tag="_"),
+ Portal(name="Dark Tomb to Furnace", region="Dark Tomb Dark Exit",
+ destination="Furnace", tag="_"),
+ Portal(name="Dark Tomb to Checkpoint", region="Dark Tomb Entry Point",
+ destination="Sewer_Boss", tag="_"),
+
+ Portal(name="West Garden Exit near Hero's Grave", region="West Garden",
+ destination="Overworld Redux", tag="_lower"),
+ Portal(name="West Garden to Magic Dagger House", region="West Garden",
+ destination="archipelagos_house", tag="_"),
+ Portal(name="West Garden Exit after Boss", region="West Garden after Boss",
+ destination="Overworld Redux", tag="_upper"),
+ Portal(name="West Garden Shop", region="West Garden",
+ destination="Shop", tag="_"),
+ Portal(name="West Garden Laurels Exit", region="West Garden Laurels Exit Region",
+ destination="Overworld Redux", tag="_lowest"),
+ Portal(name="West Garden Hero's Grave", region="West Garden Hero's Grave Region",
+ destination="RelicVoid", tag="_teleporter_relic plinth"),
+ Portal(name="West Garden to Far Shore", region="West Garden Portal",
+ destination="Transit", tag="_teleporter_archipelagos_teleporter"),
+
+ Portal(name="Magic Dagger House Exit", region="Magic Dagger House",
+ destination="Archipelagos Redux", tag="_"),
+
+ Portal(name="Atoll Upper Exit", region="Ruined Atoll",
+ destination="Overworld Redux", tag="_upper"),
+ Portal(name="Atoll Lower Exit", region="Ruined Atoll Lower Entry Area",
+ destination="Overworld Redux", tag="_lower"),
+ Portal(name="Atoll Shop", region="Ruined Atoll",
+ destination="Shop", tag="_"),
+ Portal(name="Atoll to Far Shore", region="Ruined Atoll Portal",
+ destination="Transit", tag="_teleporter_atoll"),
+ Portal(name="Atoll Statue Teleporter", region="Ruined Atoll Statue",
+ destination="Library Exterior", tag="_"),
+ Portal(name="Frog Stairs Eye Entrance", region="Ruined Atoll Frog Eye",
+ destination="Frog Stairs", tag="_eye"),
+ Portal(name="Frog Stairs Mouth Entrance", region="Ruined Atoll Frog Mouth",
+ destination="Frog Stairs", tag="_mouth"),
+
+ Portal(name="Frog Stairs Eye Exit", region="Frog Stairs Eye Exit",
+ destination="Atoll Redux", tag="_eye"),
+ Portal(name="Frog Stairs Mouth Exit", region="Frog Stairs Upper",
+ destination="Atoll Redux", tag="_mouth"),
+ Portal(name="Frog Stairs to Frog's Domain's Entrance", region="Frog Stairs to Frog's Domain",
+ destination="frog cave main", tag="_Entrance"),
+ Portal(name="Frog Stairs to Frog's Domain's Exit", region="Frog Stairs Lower",
+ destination="frog cave main", tag="_Exit"),
+
+ Portal(name="Frog's Domain Ladder Exit", region="Frog's Domain Entry",
+ destination="Frog Stairs", tag="_Entrance"),
+ Portal(name="Frog's Domain Orb Exit", region="Frog's Domain Back",
+ destination="Frog Stairs", tag="_Exit"),
+
+ Portal(name="Library Exterior Tree", region="Library Exterior Tree Region",
+ destination="Atoll Redux", tag="_"),
+ Portal(name="Library Exterior Ladder", region="Library Exterior Ladder Region",
+ destination="Library Hall", tag="_"),
+
+ Portal(name="Library Hall Bookshelf Exit", region="Library Hall Bookshelf",
+ destination="Library Exterior", tag="_"),
+ Portal(name="Library Hero's Grave", region="Library Hero's Grave Region",
+ destination="RelicVoid", tag="_teleporter_relic plinth"),
+ Portal(name="Library Hall to Rotunda", region="Library Hall to Rotunda",
+ destination="Library Rotunda", tag="_"),
+
+ Portal(name="Library Rotunda Lower Exit", region="Library Rotunda to Hall",
+ destination="Library Hall", tag="_"),
+ Portal(name="Library Rotunda Upper Exit", region="Library Rotunda to Lab",
+ destination="Library Lab", tag="_"),
+
+ Portal(name="Library Lab to Rotunda", region="Library Lab Lower",
+ destination="Library Rotunda", tag="_"),
+ Portal(name="Library to Far Shore", region="Library Portal",
+ destination="Transit", tag="_teleporter_library teleporter"),
+ Portal(name="Library Lab to Librarian Arena", region="Library Lab to Librarian",
+ destination="Library Arena", tag="_"),
+
+ Portal(name="Librarian Arena Exit", region="Library Arena",
+ destination="Library Lab", tag="_"),
+
+ Portal(name="Forest to Belltower", region="East Forest",
+ destination="Forest Belltower", tag="_"),
+ Portal(name="Forest Guard House 1 Lower Entrance", region="East Forest",
+ destination="East Forest Redux Laddercave", tag="_lower"),
+ Portal(name="Forest Guard House 1 Gate Entrance", region="East Forest",
+ destination="East Forest Redux Laddercave", tag="_gate"),
+ Portal(name="Forest Dance Fox Outside Doorway", region="East Forest Dance Fox Spot",
+ destination="East Forest Redux Laddercave", tag="_upper"),
+ Portal(name="Forest to Far Shore", region="East Forest Portal",
+ destination="Transit", tag="_teleporter_forest teleporter"),
+ Portal(name="Forest Guard House 2 Lower Entrance", region="Lower Forest",
+ destination="East Forest Redux Interior", tag="_lower"),
+ Portal(name="Forest Guard House 2 Upper Entrance", region="East Forest",
+ destination="East Forest Redux Interior", tag="_upper"),
+ Portal(name="Forest Grave Path Lower Entrance", region="East Forest",
+ destination="Sword Access", tag="_lower"),
+ Portal(name="Forest Grave Path Upper Entrance", region="East Forest",
+ destination="Sword Access", tag="_upper"),
+
+ Portal(name="Guard House 1 Dance Fox Exit", region="Guard House 1 West",
+ destination="East Forest Redux", tag="_upper"),
+ Portal(name="Guard House 1 Lower Exit", region="Guard House 1 West",
+ destination="East Forest Redux", tag="_lower"),
+ Portal(name="Guard House 1 Upper Forest Exit", region="Guard House 1 East",
+ destination="East Forest Redux", tag="_gate"),
+ Portal(name="Guard House 1 to Guard Captain Room", region="Guard House 1 East",
+ destination="Forest Boss Room", tag="_"),
+
+ Portal(name="Forest Grave Path Upper Exit", region="Forest Grave Path Upper",
+ destination="East Forest Redux", tag="_upper"),
+ Portal(name="Forest Grave Path Lower Exit", region="Forest Grave Path Main",
+ destination="East Forest Redux", tag="_lower"),
+ Portal(name="East Forest Hero's Grave", region="Forest Hero's Grave",
+ destination="RelicVoid", tag="_teleporter_relic plinth"),
+
+ Portal(name="Guard House 2 Lower Exit", region="Guard House 2 Lower",
+ destination="East Forest Redux", tag="_lower"),
+ Portal(name="Guard House 2 Upper Exit", region="Guard House 2 Upper",
+ destination="East Forest Redux", tag="_upper"),
+
+ Portal(name="Guard Captain Room Non-Gate Exit", region="Forest Boss Room",
+ destination="East Forest Redux Laddercave", tag="_"),
+ Portal(name="Guard Captain Room Gate Exit", region="Forest Boss Room",
+ destination="Forest Belltower", tag="_"),
+
+ Portal(name="Forest Belltower to Fortress", region="Forest Belltower Main",
+ destination="Fortress Courtyard", tag="_"),
+ Portal(name="Forest Belltower to Forest", region="Forest Belltower Lower",
+ destination="East Forest Redux", tag="_"),
+ Portal(name="Forest Belltower to Overworld", region="Forest Belltower Main",
+ destination="Overworld Redux", tag="_"),
+ Portal(name="Forest Belltower to Guard Captain Room", region="Forest Belltower Upper",
+ destination="Forest Boss Room", tag="_"),
+
+ Portal(name="Fortress Courtyard to Fortress Grave Path Lower", region="Fortress Courtyard",
+ destination="Fortress Reliquary", tag="_Lower"),
+ Portal(name="Fortress Courtyard to Fortress Grave Path Upper", region="Fortress Courtyard Upper",
+ destination="Fortress Reliquary", tag="_Upper"),
+ Portal(name="Fortress Courtyard to Fortress Interior", region="Fortress Courtyard",
+ destination="Fortress Main", tag="_Big Door"),
+ Portal(name="Fortress Courtyard to East Fortress", region="Fortress Courtyard Upper",
+ destination="Fortress East", tag="_"),
+ Portal(name="Fortress Courtyard to Beneath the Vault", region="Beneath the Vault Entry",
+ destination="Fortress Basement", tag="_"),
+ Portal(name="Fortress Courtyard to Forest Belltower", region="Fortress Exterior from East Forest",
+ destination="Forest Belltower", tag="_"),
+ Portal(name="Fortress Courtyard to Overworld", region="Fortress Exterior from Overworld",
+ destination="Overworld Redux", tag="_"),
+ Portal(name="Fortress Courtyard Shop", region="Fortress Exterior near cave",
+ destination="Shop", tag="_"),
+
+ Portal(name="Beneath the Vault to Fortress Interior", region="Beneath the Vault Back",
+ destination="Fortress Main", tag="_"),
+ Portal(name="Beneath the Vault to Fortress Courtyard", region="Beneath the Vault Ladder Exit",
+ destination="Fortress Courtyard", tag="_"),
+
+ Portal(name="Fortress Interior Main Exit", region="Eastern Vault Fortress",
+ destination="Fortress Courtyard", tag="_Big Door"),
+ Portal(name="Fortress Interior to Beneath the Earth", region="Eastern Vault Fortress",
+ destination="Fortress Basement", tag="_"),
+ Portal(name="Fortress Interior to Siege Engine Arena", region="Eastern Vault Fortress Gold Door",
+ destination="Fortress Arena", tag="_"),
+ Portal(name="Fortress Interior Shop", region="Eastern Vault Fortress",
+ destination="Shop", tag="_"),
+ Portal(name="Fortress Interior to East Fortress Upper", region="Eastern Vault Fortress",
+ destination="Fortress East", tag="_upper"),
+ Portal(name="Fortress Interior to East Fortress Lower", region="Eastern Vault Fortress",
+ destination="Fortress East", tag="_lower"),
+
+ Portal(name="East Fortress to Interior Lower", region="Fortress East Shortcut Lower",
+ destination="Fortress Main", tag="_lower"),
+ Portal(name="East Fortress to Courtyard", region="Fortress East Shortcut Upper",
+ destination="Fortress Courtyard", tag="_"),
+ Portal(name="East Fortress to Interior Upper", region="Fortress East Shortcut Upper",
+ destination="Fortress Main", tag="_upper"),
+
+ Portal(name="Fortress Grave Path Lower Exit", region="Fortress Grave Path",
+ destination="Fortress Courtyard", tag="_Lower"),
+ Portal(name="Fortress Hero's Grave", region="Fortress Hero's Grave Region",
+ destination="RelicVoid", tag="_teleporter_relic plinth"),
+ Portal(name="Fortress Grave Path Upper Exit", region="Fortress Grave Path Upper",
+ destination="Fortress Courtyard", tag="_Upper"),
+ Portal(name="Fortress Grave Path Dusty Entrance", region="Fortress Grave Path Dusty Entrance Region",
+ destination="Dusty", tag="_"),
+
+ Portal(name="Dusty Exit", region="Fortress Leaf Piles",
+ destination="Fortress Reliquary", tag="_"),
+
+ Portal(name="Siege Engine Arena to Fortress", region="Fortress Arena",
+ destination="Fortress Main", tag="_"),
+ Portal(name="Fortress to Far Shore", region="Fortress Arena Portal",
+ destination="Transit", tag="_teleporter_spidertank"),
+
+ Portal(name="Stairs to Top of the Mountain", region="Lower Mountain Stairs",
+ destination="Mountaintop", tag="_"),
+ Portal(name="Mountain to Quarry", region="Lower Mountain",
+ destination="Quarry Redux", tag="_"),
+ Portal(name="Mountain to Overworld", region="Lower Mountain",
+ destination="Overworld Redux", tag="_"),
+
+ Portal(name="Top of the Mountain Exit", region="Top of the Mountain",
+ destination="Mountain", tag="_"),
+
+ Portal(name="Quarry Connector to Overworld", region="Quarry Connector",
+ destination="Overworld Redux", tag="_"),
+ Portal(name="Quarry Connector to Quarry", region="Quarry Connector",
+ destination="Quarry Redux", tag="_"),
+
+ Portal(name="Quarry to Overworld Exit", region="Quarry Entry",
+ destination="Darkwoods Tunnel", tag="_"),
+ Portal(name="Quarry Shop", region="Quarry Entry",
+ destination="Shop", tag="_"),
+ Portal(name="Quarry to Monastery Front", region="Quarry Monastery Entry",
+ destination="Monastery", tag="_front"),
+ Portal(name="Quarry to Monastery Back", region="Monastery Rope",
+ destination="Monastery", tag="_back"),
+ Portal(name="Quarry to Mountain", region="Quarry Back",
+ destination="Mountain", tag="_"),
+ Portal(name="Quarry to Ziggurat", region="Lower Quarry Zig Door",
+ destination="ziggurat2020_0", tag="_"),
+ Portal(name="Quarry to Far Shore", region="Quarry Portal",
+ destination="Transit", tag="_teleporter_quarry teleporter"),
+
+ Portal(name="Monastery Rear Exit", region="Monastery Back",
+ destination="Quarry Redux", tag="_back"),
+ Portal(name="Monastery Front Exit", region="Monastery Front",
+ destination="Quarry Redux", tag="_front"),
+ Portal(name="Monastery Hero's Grave", region="Monastery Hero's Grave Region",
+ destination="RelicVoid", tag="_teleporter_relic plinth"),
+
+ Portal(name="Ziggurat Entry Hallway to Ziggurat Upper", region="Rooted Ziggurat Entry",
+ destination="ziggurat2020_1", tag="_"),
+ Portal(name="Ziggurat Entry Hallway to Quarry", region="Rooted Ziggurat Entry",
+ destination="Quarry Redux", tag="_"),
+
+ Portal(name="Ziggurat Upper to Ziggurat Entry Hallway", region="Rooted Ziggurat Upper Entry",
+ destination="ziggurat2020_0", tag="_"),
+ Portal(name="Ziggurat Upper to Ziggurat Tower", region="Rooted Ziggurat Upper Back",
+ destination="ziggurat2020_2", tag="_"),
+
+ Portal(name="Ziggurat Tower to Ziggurat Upper", region="Rooted Ziggurat Middle Top",
+ destination="ziggurat2020_1", tag="_"),
+ Portal(name="Ziggurat Tower to Ziggurat Lower", region="Rooted Ziggurat Middle Bottom",
+ destination="ziggurat2020_3", tag="_"),
+
+ Portal(name="Ziggurat Lower to Ziggurat Tower", region="Rooted Ziggurat Lower Front",
+ destination="ziggurat2020_2", tag="_"),
+ Portal(name="Ziggurat Portal Room Entrance", region="Rooted Ziggurat Portal Room Entrance",
+ destination="ziggurat2020_FTRoom", tag="_"),
+
+ Portal(name="Ziggurat Portal Room Exit", region="Rooted Ziggurat Portal Room Exit",
+ destination="ziggurat2020_3", tag="_"),
+ Portal(name="Ziggurat to Far Shore", region="Rooted Ziggurat Portal",
+ destination="Transit", tag="_teleporter_ziggurat teleporter"),
+
+ Portal(name="Swamp Lower Exit", region="Swamp Front",
+ destination="Overworld Redux", tag="_conduit"),
+ Portal(name="Swamp to Cathedral Main Entrance", region="Swamp to Cathedral Main Entrance Region",
+ destination="Cathedral Redux", tag="_main"),
+ Portal(name="Swamp to Cathedral Secret Legend Room Entrance", region="Swamp to Cathedral Treasure Room",
+ destination="Cathedral Redux", tag="_secret"),
+ Portal(name="Swamp to Gauntlet", region="Back of Swamp",
+ destination="Cathedral Arena", tag="_"),
+ Portal(name="Swamp Shop", region="Swamp Front",
+ destination="Shop", tag="_"),
+ Portal(name="Swamp Upper Exit", region="Back of Swamp Laurels Area",
+ destination="Overworld Redux", tag="_wall"),
+ Portal(name="Swamp Hero's Grave", region="Swamp Hero's Grave Region",
+ destination="RelicVoid", tag="_teleporter_relic plinth"),
+
+ Portal(name="Cathedral Main Exit", region="Cathedral",
+ destination="Swamp Redux 2", tag="_main"),
+ Portal(name="Cathedral Elevator", region="Cathedral",
+ destination="Cathedral Arena", tag="_"),
+ Portal(name="Cathedral Secret Legend Room Exit", region="Cathedral Secret Legend Room",
+ destination="Swamp Redux 2", tag="_secret"),
+
+ Portal(name="Gauntlet to Swamp", region="Cathedral Gauntlet Exit",
+ destination="Swamp Redux 2", tag="_"),
+ Portal(name="Gauntlet Elevator", region="Cathedral Gauntlet Checkpoint",
+ destination="Cathedral Redux", tag="_"),
+ Portal(name="Gauntlet Shop", region="Cathedral Gauntlet Checkpoint",
+ destination="Shop", tag="_"),
+
+ Portal(name="Hero's Grave to Fortress", region="Hero Relic - Fortress",
+ destination="Fortress Reliquary", tag="_teleporter_relic plinth"),
+ Portal(name="Hero's Grave to Monastery", region="Hero Relic - Quarry",
+ destination="Monastery", tag="_teleporter_relic plinth"),
+ Portal(name="Hero's Grave to West Garden", region="Hero Relic - West Garden",
+ destination="Archipelagos Redux", tag="_teleporter_relic plinth"),
+ Portal(name="Hero's Grave to East Forest", region="Hero Relic - East Forest",
+ destination="Sword Access", tag="_teleporter_relic plinth"),
+ Portal(name="Hero's Grave to Library", region="Hero Relic - Library",
+ destination="Library Hall", tag="_teleporter_relic plinth"),
+ Portal(name="Hero's Grave to Swamp", region="Hero Relic - Swamp",
+ destination="Swamp Redux 2", tag="_teleporter_relic plinth"),
+
+ Portal(name="Far Shore to West Garden", region="Far Shore to West Garden Region",
+ destination="Archipelagos Redux", tag="_teleporter_archipelagos_teleporter"),
+ Portal(name="Far Shore to Library", region="Far Shore to Library Region",
+ destination="Library Lab", tag="_teleporter_library teleporter"),
+ Portal(name="Far Shore to Quarry", region="Far Shore to Quarry Region",
+ destination="Quarry Redux", tag="_teleporter_quarry teleporter"),
+ Portal(name="Far Shore to East Forest", region="Far Shore to East Forest Region",
+ destination="East Forest Redux", tag="_teleporter_forest teleporter"),
+ Portal(name="Far Shore to Fortress", region="Far Shore to Fortress Region",
+ destination="Fortress Arena", tag="_teleporter_spidertank"),
+ Portal(name="Far Shore to Atoll", region="Far Shore",
+ destination="Atoll Redux", tag="_teleporter_atoll"),
+ Portal(name="Far Shore to Ziggurat", region="Far Shore",
+ destination="ziggurat2020_FTRoom", tag="_teleporter_ziggurat teleporter"),
+ Portal(name="Far Shore to Heir", region="Far Shore",
+ destination="Spirit Arena", tag="_teleporter_spirit arena"),
+ Portal(name="Far Shore to Town", region="Far Shore",
+ destination="Overworld Redux", tag="_teleporter_town"),
+ Portal(name="Far Shore to Spawn", region="Far Shore to Spawn Region",
+ destination="Overworld Redux", tag="_teleporter_starting island"),
+
+ Portal(name="Heir Arena Exit", region="Spirit Arena",
+ destination="Transit", tag="_teleporter_spirit arena"),
+
+ Portal(name="Purgatory Bottom Exit", region="Purgatory",
+ destination="Purgatory", tag="_bottom"),
+ Portal(name="Purgatory Top Exit", region="Purgatory",
+ destination="Purgatory", tag="_top"),
+]
+
+
+class RegionInfo(NamedTuple):
+ game_scene: str # the name of the scene in the actual game
+ dead_end: int = 0 # if a region has only one exit
+ hint: int = 0 # what kind of hint text you should have
+
+
+class DeadEnd(IntEnum):
+ free = 0 # not a dead end
+ all_cats = 1 # dead end in every logic category
+ restricted = 2 # dead end only in restricted
+ # there's no dead ends that are only in unrestricted
+
+
+# key is the AP region name. "Fake" in region info just means the mod won't receive that info at all
+tunic_er_regions: Dict[str, RegionInfo] = {
+ "Menu": RegionInfo("Fake", dead_end=DeadEnd.all_cats),
+ "Overworld": RegionInfo("Overworld Redux"), # main overworld, the central area
+ "Overworld Holy Cross": RegionInfo("Fake", dead_end=DeadEnd.all_cats), # main overworld holy cross checks
+ "Overworld Belltower": RegionInfo("Overworld Redux"), # the area with the belltower and chest
+ "Overworld Belltower at Bell": RegionInfo("Overworld Redux"), # being able to ring the belltower, basically
+ "Overworld Swamp Upper Entry": RegionInfo("Overworld Redux"), # upper swamp entry spot
+ "Overworld Swamp Lower Entry": RegionInfo("Overworld Redux"), # lower swamp entrance, rotating lights entrance
+ "After Ruined Passage": RegionInfo("Overworld Redux"), # just the door and chest
+ "Above Ruined Passage": RegionInfo("Overworld Redux"), # one ladder up from ruined passage
+ "East Overworld": RegionInfo("Overworld Redux"), # where the east forest and fortress entrances are
+ "Overworld Special Shop Entry": RegionInfo("Overworld Redux"), # special shop entry spot
+ "Upper Overworld": RegionInfo("Overworld Redux"), # where the mountain stairs are
+ "Overworld above Quarry Entrance": RegionInfo("Overworld Redux"), # top of the ladder where the chest is
+ "Overworld after Temple Rafters": RegionInfo("Overworld Redux"), # the ledge after the rafters exit, before ladder
+ "Overworld Quarry Entry": RegionInfo("Overworld Redux"), # at the top of the ladder, to darkwoods
+ "Overworld after Envoy": RegionInfo("Overworld Redux"), # after the envoy on the thin bridge to quarry
+ "Overworld at Patrol Cave": RegionInfo("Overworld Redux"), # right at the patrol cave entrance
+ "Overworld above Patrol Cave": RegionInfo("Overworld Redux"), # where the hook is, and one ladder up from patrol
+ "Overworld West Garden Laurels Entry": RegionInfo("Overworld Redux"), # west garden laurels entry
+ "Overworld to West Garden Upper": RegionInfo("Overworld Redux"), # usually leads to garden knight
+ "Overworld to West Garden from Furnace": RegionInfo("Overworld Redux"), # isolated stairway with one chest
+ "Overworld Well Ladder": RegionInfo("Overworld Redux"), # just the ladder entrance itself as a region
+ "Overworld Beach": RegionInfo("Overworld Redux"), # from the two turrets to invisble maze, and lower atoll entry
+ "Overworld Tunnel Turret": RegionInfo("Overworld Redux"), # the tunnel turret by the southwest beach ladder
+ "Overworld to Atoll Upper": RegionInfo("Overworld Redux"), # the little ledge before the ladder
+ "Overworld Well to Furnace Rail": RegionInfo("Overworld Redux"), # the rail hallway, bane of unrestricted logic
+ "Overworld Ruined Passage Door": RegionInfo("Overworld Redux"), # the small space betweeen the door and the portal
+ "Overworld Old House Door": RegionInfo("Overworld Redux"), # the too-small space between the door and the portal
+ "Overworld Southeast Cross Door": RegionInfo("Overworld Redux"), # the small space betweeen the door and the portal
+ "Overworld Fountain Cross Door": RegionInfo("Overworld Redux"), # the small space between the door and the portal
+ "Overworld Temple Door": RegionInfo("Overworld Redux"), # the small space betweeen the door and the portal
+ "Overworld Town Portal": RegionInfo("Overworld Redux"), # being able to go to or come from the portal
+ "Overworld Spawn Portal": RegionInfo("Overworld Redux"), # being able to go to or come from the portal
+ "Stick House": RegionInfo("Sword Cave", dead_end=DeadEnd.all_cats),
+ "Windmill": RegionInfo("Windmill"),
+ "Old House Back": RegionInfo("Overworld Interiors"), # part with the hc door
+ "Old House Front": RegionInfo("Overworld Interiors"), # part with the bedroom
+ "Relic Tower": RegionInfo("g_elements", dead_end=DeadEnd.all_cats),
+ "Furnace Fuse": RegionInfo("Furnace"), # top of the furnace
+ "Furnace Ladder Area": RegionInfo("Furnace"), # the two portals accessible by the ladder
+ "Furnace Walking Path": RegionInfo("Furnace"), # dark tomb to west garden
+ "Secret Gathering Place": RegionInfo("Waterfall", dead_end=DeadEnd.all_cats),
+ "Changing Room": RegionInfo("Changing Room", dead_end=DeadEnd.all_cats),
+ "Patrol Cave": RegionInfo("PatrolCave", dead_end=DeadEnd.all_cats),
+ "Ruined Shop": RegionInfo("Ruined Shop", dead_end=DeadEnd.all_cats),
+ "Ruined Passage": RegionInfo("Ruins Passage"),
+ "Special Shop": RegionInfo("ShopSpecial", dead_end=DeadEnd.all_cats),
+ "Caustic Light Cave": RegionInfo("Overworld Cave", dead_end=DeadEnd.all_cats),
+ "Maze Cave": RegionInfo("Maze Room", dead_end=DeadEnd.all_cats),
+ "Cube Cave": RegionInfo("CubeRoom", dead_end=DeadEnd.all_cats),
+ "Southeast Cross Room": RegionInfo("EastFiligreeCache", dead_end=DeadEnd.all_cats),
+ "Fountain Cross Room": RegionInfo("Town_FiligreeRoom", dead_end=DeadEnd.all_cats),
+ "Hourglass Cave": RegionInfo("Town Basement", dead_end=DeadEnd.all_cats),
+ "Hourglass Cave Tower": RegionInfo("Town Basement", dead_end=DeadEnd.all_cats), # top of the tower
+ "Sealed Temple": RegionInfo("Temple"),
+ "Sealed Temple Rafters": RegionInfo("Temple"),
+ "Forest Belltower Upper": RegionInfo("Forest Belltower"),
+ "Forest Belltower Main": RegionInfo("Forest Belltower"),
+ "Forest Belltower Lower": RegionInfo("Forest Belltower"),
+ "East Forest": RegionInfo("East Forest Redux"),
+ "East Forest Dance Fox Spot": RegionInfo("East Forest Redux"),
+ "East Forest Portal": RegionInfo("East Forest Redux"),
+ "Lower Forest": RegionInfo("East Forest Redux"), # bottom of the forest
+ "Guard House 1 East": RegionInfo("East Forest Redux Laddercave"),
+ "Guard House 1 West": RegionInfo("East Forest Redux Laddercave"),
+ "Guard House 2 Upper": RegionInfo("East Forest Redux Interior"),
+ "Guard House 2 Lower": RegionInfo("East Forest Redux Interior"),
+ "Forest Boss Room": RegionInfo("Forest Boss Room"),
+ "Forest Grave Path Main": RegionInfo("Sword Access"),
+ "Forest Grave Path Upper": RegionInfo("Sword Access"),
+ "Forest Grave Path by Grave": RegionInfo("Sword Access"),
+ "Forest Hero's Grave": RegionInfo("Sword Access"),
+ "Dark Tomb Entry Point": RegionInfo("Crypt Redux"), # both upper exits
+ "Dark Tomb Upper": RegionInfo("Crypt Redux"), # the part with the casket and the top of the ladder
+ "Dark Tomb Main": RegionInfo("Crypt Redux"),
+ "Dark Tomb Dark Exit": RegionInfo("Crypt Redux"),
+ "Dark Tomb Checkpoint": RegionInfo("Sewer_Boss"),
+ "Well Boss": RegionInfo("Sewer_Boss"),
+ "Beneath the Well Ladder Exit": RegionInfo("Sewer"), # just the ladder
+ "Beneath the Well Front": RegionInfo("Sewer"), # the front, to separate it from the weapon requirement in the mid
+ "Beneath the Well Main": RegionInfo("Sewer"), # the main section of it, requires a weapon
+ "Beneath the Well Back": RegionInfo("Sewer"), # the back two portals, and all 4 upper chests
+ "West Garden": RegionInfo("Archipelagos Redux"),
+ "Magic Dagger House": RegionInfo("archipelagos_house", dead_end=DeadEnd.all_cats),
+ "West Garden Portal": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted),
+ "West Garden Portal Item": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted),
+ "West Garden Laurels Exit Region": RegionInfo("Archipelagos Redux"),
+ "West Garden after Boss": RegionInfo("Archipelagos Redux"),
+ "West Garden Hero's Grave Region": RegionInfo("Archipelagos Redux"),
+ "Ruined Atoll": RegionInfo("Atoll Redux"),
+ "Ruined Atoll Lower Entry Area": RegionInfo("Atoll Redux"),
+ "Ruined Atoll Ladder Tops": RegionInfo("Atoll Redux"), # at the top of the 5 ladders in south Atoll
+ "Ruined Atoll Frog Mouth": RegionInfo("Atoll Redux"),
+ "Ruined Atoll Frog Eye": RegionInfo("Atoll Redux"),
+ "Ruined Atoll Portal": RegionInfo("Atoll Redux"),
+ "Ruined Atoll Statue": RegionInfo("Atoll Redux"),
+ "Frog Stairs Eye Exit": RegionInfo("Frog Stairs"),
+ "Frog Stairs Upper": RegionInfo("Frog Stairs"),
+ "Frog Stairs Lower": RegionInfo("Frog Stairs"),
+ "Frog Stairs to Frog's Domain": RegionInfo("Frog Stairs"),
+ "Frog's Domain Entry": RegionInfo("frog cave main"),
+ "Frog's Domain": RegionInfo("frog cave main"),
+ "Frog's Domain Back": RegionInfo("frog cave main"),
+ "Library Exterior Tree Region": RegionInfo("Library Exterior"),
+ "Library Exterior Ladder Region": RegionInfo("Library Exterior"),
+ "Library Hall Bookshelf": RegionInfo("Library Hall"),
+ "Library Hall": RegionInfo("Library Hall"),
+ "Library Hero's Grave Region": RegionInfo("Library Hall"),
+ "Library Hall to Rotunda": RegionInfo("Library Hall"),
+ "Library Rotunda to Hall": RegionInfo("Library Rotunda"),
+ "Library Rotunda": RegionInfo("Library Rotunda"),
+ "Library Rotunda to Lab": RegionInfo("Library Rotunda"),
+ "Library Lab": RegionInfo("Library Lab"),
+ "Library Lab Lower": RegionInfo("Library Lab"),
+ "Library Portal": RegionInfo("Library Lab"),
+ "Library Lab to Librarian": RegionInfo("Library Lab"),
+ "Library Arena": RegionInfo("Library Arena", dead_end=DeadEnd.all_cats),
+ "Fortress Exterior from East Forest": RegionInfo("Fortress Courtyard"),
+ "Fortress Exterior from Overworld": RegionInfo("Fortress Courtyard"),
+ "Fortress Exterior near cave": RegionInfo("Fortress Courtyard"), # where the shop and beneath the earth entry are
+ "Beneath the Vault Entry": RegionInfo("Fortress Courtyard"),
+ "Fortress Courtyard": RegionInfo("Fortress Courtyard"),
+ "Fortress Courtyard Upper": RegionInfo("Fortress Courtyard"),
+ "Beneath the Vault Ladder Exit": RegionInfo("Fortress Basement"),
+ "Beneath the Vault Front": RegionInfo("Fortress Basement"), # the vanilla entry point
+ "Beneath the Vault Back": RegionInfo("Fortress Basement"), # the vanilla exit point
+ "Eastern Vault Fortress": RegionInfo("Fortress Main"),
+ "Eastern Vault Fortress Gold Door": RegionInfo("Fortress Main"),
+ "Fortress East Shortcut Upper": RegionInfo("Fortress East"),
+ "Fortress East Shortcut Lower": RegionInfo("Fortress East"),
+ "Fortress Grave Path": RegionInfo("Fortress Reliquary"),
+ "Fortress Grave Path Upper": RegionInfo("Fortress Reliquary", dead_end=DeadEnd.restricted),
+ "Fortress Grave Path Dusty Entrance Region": RegionInfo("Fortress Reliquary"),
+ "Fortress Hero's Grave Region": RegionInfo("Fortress Reliquary"),
+ "Fortress Leaf Piles": RegionInfo("Dusty", dead_end=DeadEnd.all_cats),
+ "Fortress Arena": RegionInfo("Fortress Arena"),
+ "Fortress Arena Portal": RegionInfo("Fortress Arena"),
+ "Lower Mountain": RegionInfo("Mountain"),
+ "Lower Mountain Stairs": RegionInfo("Mountain"),
+ "Top of the Mountain": RegionInfo("Mountaintop", dead_end=DeadEnd.all_cats),
+ "Quarry Connector": RegionInfo("Darkwoods Tunnel"),
+ "Quarry Entry": RegionInfo("Quarry Redux"),
+ "Quarry": RegionInfo("Quarry Redux"),
+ "Quarry Portal": RegionInfo("Quarry Redux"),
+ "Quarry Back": RegionInfo("Quarry Redux"),
+ "Quarry Monastery Entry": RegionInfo("Quarry Redux"),
+ "Monastery Front": RegionInfo("Monastery"),
+ "Monastery Back": RegionInfo("Monastery"),
+ "Monastery Hero's Grave Region": RegionInfo("Monastery"),
+ "Monastery Rope": RegionInfo("Quarry Redux"),
+ "Lower Quarry": RegionInfo("Quarry Redux"),
+ "Even Lower Quarry": RegionInfo("Quarry Redux"),
+ "Lower Quarry Zig Door": RegionInfo("Quarry Redux"),
+ "Rooted Ziggurat Entry": RegionInfo("ziggurat2020_0"),
+ "Rooted Ziggurat Upper Entry": RegionInfo("ziggurat2020_1"),
+ "Rooted Ziggurat Upper Front": RegionInfo("ziggurat2020_1"),
+ "Rooted Ziggurat Upper Back": RegionInfo("ziggurat2020_1"), # after the administrator
+ "Rooted Ziggurat Middle Top": RegionInfo("ziggurat2020_2"),
+ "Rooted Ziggurat Middle Bottom": RegionInfo("ziggurat2020_2"),
+ "Rooted Ziggurat Lower Front": RegionInfo("ziggurat2020_3"), # the vanilla entry point side
+ "Rooted Ziggurat Lower Back": RegionInfo("ziggurat2020_3"), # the boss side
+ "Rooted Ziggurat Portal Room Entrance": RegionInfo("ziggurat2020_3"), # the door itself on the zig 3 side
+ "Rooted Ziggurat Portal": RegionInfo("ziggurat2020_FTRoom"),
+ "Rooted Ziggurat Portal Room Exit": RegionInfo("ziggurat2020_FTRoom"),
+ "Swamp Front": RegionInfo("Swamp Redux 2"), # from the main entry to the top of the ladder after south
+ "Swamp Mid": RegionInfo("Swamp Redux 2"), # from the bottom of the ladder to the cathedral door
+ "Swamp Ledge under Cathedral Door": RegionInfo("Swamp Redux 2"), # the ledge with the chest and secret door
+ "Swamp to Cathedral Treasure Room": RegionInfo("Swamp Redux 2"), # just the door
+ "Swamp to Cathedral Main Entrance Region": RegionInfo("Swamp Redux 2"), # just the door
+ "Back of Swamp": RegionInfo("Swamp Redux 2"), # the area with hero grave and gauntlet entrance
+ "Swamp Hero's Grave Region": RegionInfo("Swamp Redux 2"),
+ "Back of Swamp Laurels Area": RegionInfo("Swamp Redux 2"), # the spots you need laurels to traverse
+ "Cathedral": RegionInfo("Cathedral Redux"),
+ "Cathedral Secret Legend Room": RegionInfo("Cathedral Redux", dead_end=DeadEnd.all_cats),
+ "Cathedral Gauntlet Checkpoint": RegionInfo("Cathedral Arena"),
+ "Cathedral Gauntlet": RegionInfo("Cathedral Arena"),
+ "Cathedral Gauntlet Exit": RegionInfo("Cathedral Arena"),
+ "Far Shore": RegionInfo("Transit"),
+ "Far Shore to Spawn Region": RegionInfo("Transit"),
+ "Far Shore to East Forest Region": RegionInfo("Transit"),
+ "Far Shore to Quarry Region": RegionInfo("Transit"),
+ "Far Shore to Fortress Region": RegionInfo("Transit"),
+ "Far Shore to Library Region": RegionInfo("Transit"),
+ "Far Shore to West Garden Region": RegionInfo("Transit"),
+ "Hero Relic - Fortress": RegionInfo("RelicVoid", dead_end=DeadEnd.all_cats),
+ "Hero Relic - Quarry": RegionInfo("RelicVoid", dead_end=DeadEnd.all_cats),
+ "Hero Relic - West Garden": RegionInfo("RelicVoid", dead_end=DeadEnd.all_cats),
+ "Hero Relic - East Forest": RegionInfo("RelicVoid", dead_end=DeadEnd.all_cats),
+ "Hero Relic - Library": RegionInfo("RelicVoid", dead_end=DeadEnd.all_cats),
+ "Hero Relic - Swamp": RegionInfo("RelicVoid", dead_end=DeadEnd.all_cats),
+ "Purgatory": RegionInfo("Purgatory"),
+ "Shop": RegionInfo("Shop", dead_end=DeadEnd.all_cats),
+ "Spirit Arena": RegionInfo("Spirit Arena", dead_end=DeadEnd.all_cats),
+ "Spirit Arena Victory": RegionInfo("Spirit Arena", dead_end=DeadEnd.all_cats)
+}
+
+
+# the key is the region you have, the value is the regions you get for having that region
+# this is mostly so we don't have to do something overly complex to get this information
+# really want to get rid of this, but waiting on item plando being workable with ER
+dependent_regions_restricted: Dict[Tuple[str, ...], List[str]] = {
+ ("Overworld", "Overworld Belltower", "Overworld Belltower at Bell", "Overworld Swamp Upper Entry",
+ "Overworld Special Shop Entry", "Overworld West Garden Laurels Entry", "Overworld Southeast Cross Door",
+ "Overworld Temple Door", "Overworld Fountain Cross Door", "Overworld Town Portal", "Overworld Spawn Portal",
+ "Overworld Swamp Lower Entry", "After Ruined Passage", "Above Ruined Passage", "East Overworld", "Upper Overworld",
+ "Overworld after Temple Rafters", "Overworld Quarry Entry", "Overworld above Patrol Cave",
+ "Overworld at Patrol Cave", "Overworld to West Garden Upper", "Overworld Well Ladder", "Overworld Beach",
+ "Overworld to Atoll Upper", "Overworld above Quarry Entrance", "Overworld after Envoy", "Overworld Tunnel Turret"):
+ ["Overworld", "Overworld Belltower", "Overworld Belltower at Bell", "Overworld Swamp Upper Entry",
+ "Overworld Special Shop Entry", "Overworld West Garden Laurels Entry", "Overworld Ruined Passage Door",
+ "Overworld Southeast Cross Door", "Overworld Old House Door", "Overworld Temple Door",
+ "Overworld Fountain Cross Door", "Overworld Town Portal", "Overworld Spawn Portal",
+ "Overworld Swamp Lower Entry", "After Ruined Passage", "Above Ruined Passage", "East Overworld",
+ "Upper Overworld", "Overworld after Temple Rafters", "Overworld Quarry Entry", "Overworld above Patrol Cave",
+ "Overworld at Patrol Cave", "Overworld to West Garden Upper", "Overworld Well Ladder", "Overworld Beach",
+ "Overworld to Atoll Upper", "Overworld Temple Door", "Overworld above Quarry Entrance",
+ "Overworld after Envoy", "Overworld Tunnel Turret"],
+ ("Hourglass Cave",):
+ ["Hourglass Cave", "Hourglass Cave Tower"],
+ ("Old House Front",):
+ ["Old House Front", "Old House Back"],
+ ("Furnace Fuse", "Furnace Ladder Area", "Furnace Walking Path"):
+ ["Furnace Fuse", "Furnace Ladder Area", "Furnace Walking Path"],
+ ("Sealed Temple", "Sealed Temple Rafters"): ["Sealed Temple", "Sealed Temple Rafters"],
+ ("Forest Belltower Upper",):
+ ["Forest Belltower Upper", "Forest Belltower Main", "Forest Belltower Lower"],
+ ("Forest Belltower Main",):
+ ["Forest Belltower Main", "Forest Belltower Lower"],
+ ("East Forest", "East Forest Dance Fox Spot", "East Forest Portal", "Lower Forest"):
+ ["East Forest", "East Forest Dance Fox Spot", "East Forest Portal", "Lower Forest"],
+ ("Guard House 1 East", "Guard House 1 West"):
+ ["Guard House 1 East", "Guard House 1 West"],
+ ("Guard House 2 Upper", "Guard House 2 Lower"):
+ ["Guard House 2 Upper", "Guard House 2 Lower"],
+ ("Forest Grave Path Main", "Forest Grave Path Upper"):
+ ["Forest Grave Path Main", "Forest Grave Path Upper", "Forest Grave Path by Grave", "Forest Hero's Grave"],
+ ("Forest Grave Path by Grave", "Forest Hero's Grave"):
+ ["Forest Grave Path by Grave", "Forest Hero's Grave"],
+ ("Beneath the Well Front", "Beneath the Well Main", "Beneath the Well Back", "Beneath the Well Ladder Exit"):
+ ["Beneath the Well Front", "Beneath the Well Main", "Beneath the Well Back", "Beneath the Well Ladder Exit"],
+ ("Dark Tomb Entry Point", "Dark Tomb Main", "Dark Tomb Dark Exit", "Dark Tomb Upper"):
+ ["Dark Tomb Entry Point", "Dark Tomb Main", "Dark Tomb Dark Exit", "Dark Tomb Upper"],
+ ("Well Boss",):
+ ["Dark Tomb Checkpoint", "Well Boss"],
+ ("West Garden", "West Garden Laurels Exit Region", "West Garden after Boss", "West Garden Hero's Grave Region"):
+ ["West Garden", "West Garden Laurels Exit Region", "West Garden after Boss", "West Garden Hero's Grave Region"],
+ ("West Garden Portal", "West Garden Portal Item"): ["West Garden Portal", "West Garden Portal Item"],
+ ("Ruined Atoll", "Ruined Atoll Lower Entry Area", "Ruined Atoll Frog Mouth", "Ruined Atoll Portal",
+ "Ruined Atoll Statue", "Ruined Atoll Ladder Tops", "Ruined Atoll Frog Eye", "Ruined Atoll Ladder Tops"):
+ ["Ruined Atoll", "Ruined Atoll Lower Entry Area", "Ruined Atoll Frog Mouth", "Ruined Atoll Portal",
+ "Ruined Atoll Statue", "Ruined Atoll Ladder Tops", "Ruined Atoll Frog Eye", "Ruined Atoll Ladder Tops"],
+ ("Frog Stairs Upper", "Frog Stairs Lower", "Frog Stairs to Frog's Domain"):
+ ["Frog Stairs Upper", "Frog Stairs Lower", "Frog Stairs to Frog's Domain"],
+ ("Frog's Domain", "Frog's Domain Entry"):
+ ["Frog's Domain", "Frog's Domain Back", "Frog's Domain Entry"],
+ ("Library Exterior Ladder Region", "Library Exterior Tree Region"):
+ ["Library Exterior Ladder Region", "Library Exterior Tree Region"],
+ ("Library Hall", "Library Hero's Grave Region", "Library Hall Bookshelf", "Library Hall to Rotunda"):
+ ["Library Hall", "Library Hero's Grave Region", "Library Hall Bookshelf", "Library Hall to Rotunda"],
+ ("Library Rotunda to Hall", "Library Rotunda", "Library Rotunda to Lab"):
+ ["Library Rotunda to Hall", "Library Rotunda", "Library Rotunda to Lab"],
+ ("Library Lab", "Library Lab Lower", "Library Portal", "Library Lab to Librarian"):
+ ["Library Lab", "Library Lab Lower", "Library Portal", "Library Lab to Librarian"],
+ ("Fortress Courtyard Upper",):
+ ["Fortress Courtyard Upper", "Fortress Exterior from East Forest", "Fortress Exterior from Overworld",
+ "Fortress Exterior near cave", "Fortress Courtyard"],
+ ("Fortress Exterior from East Forest", "Fortress Exterior from Overworld",
+ "Fortress Exterior near cave", "Fortress Courtyard", "Beneath the Vault Entry"):
+ ["Fortress Exterior from East Forest", "Fortress Exterior from Overworld",
+ "Fortress Exterior near cave", "Fortress Courtyard", "Beneath the Vault Entry"],
+ ("Beneath the Vault Front", "Beneath the Vault Back", "Beneath the Vault Ladder Exit"):
+ ["Beneath the Vault Front", "Beneath the Vault Back", "Beneath the Vault Ladder Exit"],
+ ("Fortress East Shortcut Upper",):
+ ["Fortress East Shortcut Upper", "Fortress East Shortcut Lower"],
+ ("Eastern Vault Fortress",):
+ ["Eastern Vault Fortress", "Eastern Vault Fortress Gold Door"],
+ ("Fortress Grave Path", "Fortress Grave Path Dusty Entrance Region", "Fortress Hero's Grave Region"):
+ ["Fortress Grave Path", "Fortress Grave Path Dusty Entrance Region", "Fortress Hero's Grave Region"],
+ ("Fortress Arena", "Fortress Arena Portal"):
+ ["Fortress Arena", "Fortress Arena Portal"],
+ ("Lower Mountain", "Lower Mountain Stairs"):
+ ["Lower Mountain", "Lower Mountain Stairs"],
+ ("Monastery Front",):
+ ["Monastery Front", "Monastery Back", "Monastery Hero's Grave Region"],
+ ("Monastery Back", "Monastery Hero's Grave Region"):
+ ["Monastery Back", "Monastery Hero's Grave Region"],
+ ("Quarry", "Quarry Portal", "Lower Quarry", "Quarry Entry", "Quarry Back", "Quarry Monastery Entry",
+ "Even Lower Quarry"):
+ ["Quarry", "Quarry Portal", "Lower Quarry", "Quarry Entry", "Quarry Back", "Quarry Monastery Entry",
+ "Lower Quarry Zig Door", "Even Lower Quarry"],
+ ("Monastery Rope",): ["Monastery Rope", "Quarry", "Quarry Entry", "Quarry Back", "Quarry Portal", "Lower Quarry",
+ "Lower Quarry Zig Door", "Even Lower Quarry"],
+ ("Rooted Ziggurat Upper Entry", "Rooted Ziggurat Upper Front"):
+ ["Rooted Ziggurat Upper Entry", "Rooted Ziggurat Upper Front", "Rooted Ziggurat Upper Back"],
+ ("Rooted Ziggurat Middle Top",):
+ ["Rooted Ziggurat Middle Top", "Rooted Ziggurat Middle Bottom"],
+ ("Rooted Ziggurat Lower Front", "Rooted Ziggurat Lower Back", "Rooted Ziggurat Portal Room Entrance"):
+ ["Rooted Ziggurat Lower Front", "Rooted Ziggurat Lower Back", "Rooted Ziggurat Portal Room Entrance"],
+ ("Rooted Ziggurat Portal", "Rooted Ziggurat Portal Room Exit"):
+ ["Rooted Ziggurat Portal", "Rooted Ziggurat Portal Room Exit"],
+ ("Swamp Front", "Swamp Mid", "Swamp to Cathedral Treasure Room", "Swamp Ledge under Cathedral Door"):
+ ["Swamp Front", "Swamp Mid", "Swamp to Cathedral Treasure Room", "Swamp to Cathedral Main Entrance Region",
+ "Swamp Ledge under Cathedral Door"],
+ ("Back of Swamp", "Back of Swamp Laurels Area", "Swamp Hero's Grave Region"):
+ ["Back of Swamp", "Back of Swamp Laurels Area", "Swamp Hero's Grave Region"],
+ ("Cathedral Gauntlet Checkpoint",):
+ ["Cathedral Gauntlet Checkpoint", "Cathedral Gauntlet Exit", "Cathedral Gauntlet"],
+ ("Cathedral Gauntlet Exit",):
+ ["Cathedral Gauntlet Exit", "Cathedral Gauntlet"],
+ ("Far Shore", "Far Shore to Spawn Region", "Far Shore to East Forest Region", "Far Shore to Quarry Region",
+ "Far Shore to Fortress Region", "Far Shore to Library Region", "Far Shore to West Garden Region"):
+ ["Far Shore", "Far Shore to Spawn Region", "Far Shore to East Forest Region", "Far Shore to Quarry Region",
+ "Far Shore to Fortress Region", "Far Shore to Library Region", "Far Shore to West Garden Region"]
+}
+
+
+dependent_regions_nmg: Dict[Tuple[str, ...], List[str]] = {
+ ("Overworld", "Overworld Belltower", "Overworld Belltower at Bell", "Overworld Swamp Upper Entry",
+ "Overworld Special Shop Entry", "Overworld West Garden Laurels Entry", "Overworld Southeast Cross Door",
+ "Overworld Temple Door", "Overworld Fountain Cross Door", "Overworld Town Portal", "Overworld Spawn Portal",
+ "Overworld Ruined Passage Door", "Overworld Swamp Lower Entry", "After Ruined Passage", "Above Ruined Passage",
+ "East Overworld", "Upper Overworld", "Overworld after Temple Rafters", "Overworld Quarry Entry",
+ "Overworld above Patrol Cave", "Overworld at Patrol Cave", "Overworld to West Garden Upper",
+ "Overworld Well Ladder", "Overworld Beach", "Overworld to Atoll Upper", "Overworld above Quarry Entrance",
+ "Overworld after Envoy", "Overworld Tunnel Turret"):
+ ["Overworld", "Overworld Belltower", "Overworld Belltower at Bell", "Overworld Swamp Upper Entry",
+ "Overworld Special Shop Entry", "Overworld West Garden Laurels Entry", "Overworld Ruined Passage Door",
+ "Overworld Southeast Cross Door", "Overworld Old House Door", "Overworld Temple Door",
+ "Overworld Fountain Cross Door", "Overworld Town Portal", "Overworld Spawn Portal",
+ "Overworld Swamp Lower Entry", "After Ruined Passage", "Above Ruined Passage", "East Overworld",
+ "Upper Overworld", "Overworld after Temple Rafters", "Overworld Quarry Entry", "Overworld above Patrol Cave",
+ "Overworld at Patrol Cave", "Overworld to West Garden Upper", "Overworld Well Ladder", "Overworld Beach",
+ "Overworld to Atoll Upper", "Overworld above Quarry Entrance", "Overworld after Envoy",
+ "Overworld Tunnel Turret"],
+ # can laurels through the gate
+ ("Old House Front", "Old House Back"):
+ ["Old House Front", "Old House Back"],
+ ("Hourglass Cave",):
+ ["Hourglass Cave", "Hourglass Cave Tower"],
+ ("Furnace Fuse", "Furnace Ladder Area", "Furnace Walking Path"):
+ ["Furnace Fuse", "Furnace Ladder Area", "Furnace Walking Path"],
+ ("Sealed Temple", "Sealed Temple Rafters"): ["Sealed Temple", "Sealed Temple Rafters"],
+ ("Forest Belltower Upper",):
+ ["Forest Belltower Upper", "Forest Belltower Main", "Forest Belltower Lower"],
+ ("Forest Belltower Main",):
+ ["Forest Belltower Main", "Forest Belltower Lower"],
+ ("East Forest", "East Forest Dance Fox Spot", "East Forest Portal", "Lower Forest"):
+ ["East Forest", "East Forest Dance Fox Spot", "East Forest Portal", "Lower Forest"],
+ ("Guard House 1 East", "Guard House 1 West"):
+ ["Guard House 1 East", "Guard House 1 West"],
+ ("Guard House 2 Upper", "Guard House 2 Lower"):
+ ["Guard House 2 Upper", "Guard House 2 Lower"],
+ ("Forest Grave Path Main", "Forest Grave Path Upper", "Forest Grave Path by Grave", "Forest Hero's Grave"):
+ ["Forest Grave Path Main", "Forest Grave Path Upper", "Forest Grave Path by Grave", "Forest Hero's Grave"],
+ ("Beneath the Well Front", "Beneath the Well Main", "Beneath the Well Back", "Beneath the Well Ladder Exit"):
+ ["Beneath the Well Front", "Beneath the Well Main", "Beneath the Well Back", "Beneath the Well Ladder Exit"],
+ ("Dark Tomb Entry Point", "Dark Tomb Main", "Dark Tomb Dark Exit", "Dark Tomb Upper"):
+ ["Dark Tomb Entry Point", "Dark Tomb Main", "Dark Tomb Dark Exit", "Dark Tomb Upper"],
+ ("Dark Tomb Checkpoint", "Well Boss"):
+ ["Dark Tomb Checkpoint", "Well Boss"],
+ ("West Garden", "West Garden Laurels Exit Region", "West Garden after Boss", "West Garden Hero's Grave Region",
+ "West Garden Portal", "West Garden Portal Item"):
+ ["West Garden", "West Garden Laurels Exit Region", "West Garden after Boss", "West Garden Hero's Grave Region",
+ "West Garden Portal", "West Garden Portal Item"],
+ ("Ruined Atoll", "Ruined Atoll Lower Entry Area", "Ruined Atoll Frog Mouth", "Ruined Atoll Portal",
+ "Ruined Atoll Statue", "Ruined Atoll Ladder Tops", "Ruined Atoll Frog Eye", "Ruined Atoll Ladder Tops"):
+ ["Ruined Atoll", "Ruined Atoll Lower Entry Area", "Ruined Atoll Frog Mouth", "Ruined Atoll Portal",
+ "Ruined Atoll Statue", "Ruined Atoll Ladder Tops", "Ruined Atoll Frog Eye", "Ruined Atoll Ladder Tops"],
+ ("Frog Stairs Upper", "Frog Stairs Lower", "Frog Stairs to Frog's Domain"):
+ ["Frog Stairs Upper", "Frog Stairs Lower", "Frog Stairs to Frog's Domain"],
+ ("Frog's Domain", "Frog's Domain Entry"):
+ ["Frog's Domain", "Frog's Domain Back", "Frog's Domain Entry"],
+ ("Library Exterior Ladder Region", "Library Exterior Tree Region"):
+ ["Library Exterior Ladder Region", "Library Exterior Tree Region"],
+ ("Library Hall", "Library Hero's Grave Region", "Library Hall Bookshelf", "Library Hall to Rotunda"):
+ ["Library Hall", "Library Hero's Grave Region", "Library Hall Bookshelf", "Library Hall to Rotunda"],
+ ("Library Rotunda to Hall", "Library Rotunda", "Library Rotunda to Lab"):
+ ["Library Rotunda to Hall", "Library Rotunda", "Library Rotunda to Lab"],
+ ("Library Lab", "Library Lab Lower", "Library Portal", "Library Lab to Librarian"):
+ ["Library Lab", "Library Lab Lower", "Library Portal", "Library Lab to Librarian"],
+ ("Fortress Exterior from East Forest", "Fortress Exterior from Overworld",
+ "Fortress Exterior near cave", "Fortress Courtyard", "Fortress Courtyard Upper", "Beneath the Vault Entry"):
+ ["Fortress Exterior from East Forest", "Fortress Exterior from Overworld",
+ "Fortress Exterior near cave", "Fortress Courtyard", "Fortress Courtyard Upper", "Beneath the Vault Entry"],
+ ("Beneath the Vault Front", "Beneath the Vault Back", "Beneath the Vault Ladder Exit"):
+ ["Beneath the Vault Front", "Beneath the Vault Back", "Beneath the Vault Ladder Exit"],
+ ("Fortress East Shortcut Upper", "Fortress East Shortcut Lower"):
+ ["Fortress East Shortcut Upper", "Fortress East Shortcut Lower"],
+ ("Eastern Vault Fortress", "Eastern Vault Fortress Gold Door"):
+ ["Eastern Vault Fortress", "Eastern Vault Fortress Gold Door"],
+ ("Fortress Grave Path", "Fortress Grave Path Dusty Entrance Region", "Fortress Hero's Grave Region"):
+ ["Fortress Grave Path", "Fortress Grave Path Dusty Entrance Region", "Fortress Hero's Grave Region"],
+ ("Fortress Grave Path Upper",):
+ ["Fortress Grave Path Upper", "Fortress Grave Path", "Fortress Grave Path Dusty Entrance Region",
+ "Fortress Hero's Grave Region"],
+ ("Fortress Arena", "Fortress Arena Portal"):
+ ["Fortress Arena", "Fortress Arena Portal"],
+ ("Lower Mountain", "Lower Mountain Stairs"):
+ ["Lower Mountain", "Lower Mountain Stairs"],
+ ("Monastery Front", "Monastery Back", "Monastery Hero's Grave Region"):
+ ["Monastery Front", "Monastery Back", "Monastery Hero's Grave Region"],
+ ("Quarry", "Quarry Portal", "Lower Quarry", "Quarry Entry", "Quarry Back", "Quarry Monastery Entry",
+ "Even Lower Quarry"):
+ ["Quarry", "Quarry Portal", "Lower Quarry", "Quarry Entry", "Quarry Back", "Quarry Monastery Entry",
+ "Lower Quarry Zig Door", "Even Lower Quarry"],
+ ("Monastery Rope",): ["Monastery Rope", "Quarry", "Quarry Entry", "Quarry Back", "Quarry Portal", "Lower Quarry",
+ "Lower Quarry Zig Door", "Even Lower Quarry"],
+ ("Rooted Ziggurat Upper Entry", "Rooted Ziggurat Upper Front"):
+ ["Rooted Ziggurat Upper Entry", "Rooted Ziggurat Upper Front", "Rooted Ziggurat Upper Back"],
+ ("Rooted Ziggurat Middle Top",):
+ ["Rooted Ziggurat Middle Top", "Rooted Ziggurat Middle Bottom"],
+ ("Rooted Ziggurat Lower Front", "Rooted Ziggurat Lower Back", "Rooted Ziggurat Portal Room Entrance"):
+ ["Rooted Ziggurat Lower Front", "Rooted Ziggurat Lower Back", "Rooted Ziggurat Portal Room Entrance"],
+ ("Rooted Ziggurat Portal", "Rooted Ziggurat Portal Room Exit"):
+ ["Rooted Ziggurat Portal", "Rooted Ziggurat Portal Room Exit"],
+ ("Swamp Front", "Swamp Mid", "Swamp to Cathedral Treasure Room", "Swamp to Cathedral Main Entrance Region",
+ "Swamp Ledge under Cathedral Door"):
+ ["Swamp Front", "Swamp Mid", "Swamp to Cathedral Treasure Room", "Swamp to Cathedral Main Entrance Region",
+ "Swamp Ledge under Cathedral Door"],
+ ("Back of Swamp", "Back of Swamp Laurels Area", "Swamp Hero's Grave Region"):
+ ["Back of Swamp", "Back of Swamp Laurels Area", "Swamp Hero's Grave Region", "Swamp Front", "Swamp Mid",
+ "Swamp to Cathedral Treasure Room", "Swamp to Cathedral Main Entrance Region",
+ "Swamp Ledge under Cathedral Door"],
+ ("Cathedral Gauntlet Checkpoint",):
+ ["Cathedral Gauntlet Checkpoint", "Cathedral Gauntlet Exit", "Cathedral Gauntlet"],
+ ("Cathedral Gauntlet Exit",):
+ ["Cathedral Gauntlet Exit", "Cathedral Gauntlet"],
+ ("Far Shore", "Far Shore to Spawn Region", "Far Shore to East Forest Region", "Far Shore to Quarry Region",
+ "Far Shore to Fortress Region", "Far Shore to Library Region", "Far Shore to West Garden Region"):
+ ["Far Shore", "Far Shore to Spawn Region", "Far Shore to East Forest Region", "Far Shore to Quarry Region",
+ "Far Shore to Fortress Region", "Far Shore to Library Region", "Far Shore to West Garden Region"]
+}
+
+
+dependent_regions_ur: Dict[Tuple[str, ...], List[str]] = {
+ # can use ladder storage to get to the well rail
+ ("Overworld", "Overworld Belltower", "Overworld Belltower at Bell", "Overworld Swamp Upper Entry",
+ "Overworld Special Shop Entry", "Overworld West Garden Laurels Entry", "Overworld Southeast Cross Door",
+ "Overworld Temple Door", "Overworld Fountain Cross Door", "Overworld Town Portal", "Overworld Spawn Portal",
+ "Overworld Ruined Passage Door", "Overworld Swamp Lower Entry", "After Ruined Passage", "Above Ruined Passage",
+ "East Overworld", "Upper Overworld", "Overworld after Temple Rafters", "Overworld Quarry Entry",
+ "Overworld above Patrol Cave", "Overworld at Patrol Cave", "Overworld to West Garden Upper",
+ "Overworld Well Ladder", "Overworld Beach", "Overworld to Atoll Upper", "Overworld above Quarry Entrance",
+ "Overworld after Envoy", "Overworld Tunnel Turret"):
+ ["Overworld", "Overworld Belltower", "Overworld Belltower at Bell", "Overworld Swamp Upper Entry",
+ "Overworld Special Shop Entry", "Overworld West Garden Laurels Entry", "Overworld Southeast Cross Door",
+ "Overworld Temple Door", "Overworld Fountain Cross Door", "Overworld Town Portal", "Overworld Spawn Portal",
+ "Overworld Ruined Passage Door", "Overworld Swamp Lower Entry", "After Ruined Passage",
+ "Above Ruined Passage", "East Overworld", "Upper Overworld", "Overworld after Temple Rafters",
+ "Overworld Quarry Entry", "Overworld above Patrol Cave", "Overworld at Patrol Cave",
+ "Overworld to West Garden Upper", "Overworld Well Ladder", "Overworld Beach", "Overworld to Atoll Upper",
+ "Overworld above Quarry Entrance", "Overworld after Envoy", "Overworld Tunnel Turret"],
+ # can laurels through the gate
+ ("Old House Front", "Old House Back"):
+ ["Old House Front", "Old House Back"],
+ ("Hourglass Cave",):
+ ["Hourglass Cave", "Hourglass Cave Tower"],
+ ("Furnace Fuse", "Furnace Ladder Area", "Furnace Walking Path"):
+ ["Furnace Fuse", "Furnace Ladder Area", "Furnace Walking Path"],
+ ("Sealed Temple", "Sealed Temple Rafters"): ["Sealed Temple", "Sealed Temple Rafters"],
+ ("Forest Belltower Upper",):
+ ["Forest Belltower Upper", "Forest Belltower Main", "Forest Belltower Lower"],
+ ("Forest Belltower Main",):
+ ["Forest Belltower Main", "Forest Belltower Lower"],
+ ("East Forest", "East Forest Dance Fox Spot", "East Forest Portal", "Lower Forest"):
+ ["East Forest", "East Forest Dance Fox Spot", "East Forest Portal", "Lower Forest"],
+ ("Guard House 1 East", "Guard House 1 West"):
+ ["Guard House 1 East", "Guard House 1 West"],
+ ("Guard House 2 Upper", "Guard House 2 Lower"):
+ ["Guard House 2 Upper", "Guard House 2 Lower"],
+ # can use laurels, ice grapple, or ladder storage to traverse
+ ("Forest Grave Path Main", "Forest Grave Path Upper", "Forest Grave Path by Grave", "Forest Hero's Grave"):
+ ["Forest Grave Path Main", "Forest Grave Path Upper", "Forest Grave Path by Grave", "Forest Hero's Grave"],
+ ("Beneath the Well Front", "Beneath the Well Main", "Beneath the Well Back", "Beneath the Well Ladder Exit"):
+ ["Beneath the Well Front", "Beneath the Well Main", "Beneath the Well Back", "Beneath the Well Ladder Exit"],
+ ("Dark Tomb Entry Point", "Dark Tomb Main", "Dark Tomb Dark Exit", "Dark Tomb Upper"):
+ ["Dark Tomb Entry Point", "Dark Tomb Main", "Dark Tomb Dark Exit", "Dark Tomb Upper"],
+ ("Dark Tomb Checkpoint", "Well Boss"):
+ ["Dark Tomb Checkpoint", "Well Boss"],
+ # can ice grapple from portal area to the rest, and vice versa
+ ("West Garden", "West Garden Laurels Exit Region", "West Garden after Boss", "West Garden Hero's Grave Region",
+ "West Garden Portal", "West Garden Portal Item"):
+ ["West Garden", "West Garden Laurels Exit Region", "West Garden after Boss", "West Garden Hero's Grave Region",
+ "West Garden Portal", "West Garden Portal Item"],
+ ("Ruined Atoll", "Ruined Atoll Lower Entry Area", "Ruined Atoll Frog Mouth", "Ruined Atoll Portal",
+ "Ruined Atoll Statue", "Ruined Atoll Ladder Tops", "Ruined Atoll Frog Eye", "Ruined Atoll Ladder Tops"):
+ ["Ruined Atoll", "Ruined Atoll Lower Entry Area", "Ruined Atoll Frog Mouth", "Ruined Atoll Portal",
+ "Ruined Atoll Statue", "Ruined Atoll Ladder Tops", "Ruined Atoll Frog Eye", "Ruined Atoll Ladder Tops"],
+ ("Frog Stairs Upper", "Frog Stairs Lower", "Frog Stairs to Frog's Domain"):
+ ["Frog Stairs Upper", "Frog Stairs Lower", "Frog Stairs to Frog's Domain"],
+ ("Frog's Domain", "Frog's Domain Entry"):
+ ["Frog's Domain", "Frog's Domain Back", "Frog's Domain Entry"],
+ ("Library Exterior Ladder Region", "Library Exterior Tree Region"):
+ ["Library Exterior Ladder Region", "Library Exterior Tree Region"],
+ ("Library Hall", "Library Hero's Grave Region", "Library Hall Bookshelf", "Library Hall to Rotunda"):
+ ["Library Hall", "Library Hero's Grave Region", "Library Hall Bookshelf", "Library Hall to Rotunda"],
+ ("Library Rotunda to Hall", "Library Rotunda", "Library Rotunda to Lab"):
+ ["Library Rotunda to Hall", "Library Rotunda", "Library Rotunda to Lab"],
+ ("Library Lab", "Library Lab Lower", "Library Portal", "Library Lab to Librarian"):
+ ["Library Lab", "Library Lab Lower", "Library Portal", "Library Lab to Librarian"],
+ # can use ice grapple or ladder storage to get from any ladder to upper
+ ("Fortress Exterior from East Forest", "Fortress Exterior from Overworld",
+ "Fortress Exterior near cave", "Fortress Courtyard", "Fortress Courtyard Upper", "Beneath the Vault Entry"):
+ ["Fortress Exterior from East Forest", "Fortress Exterior from Overworld",
+ "Fortress Exterior near cave", "Fortress Courtyard", "Fortress Courtyard Upper", "Beneath the Vault Entry"],
+ ("Beneath the Vault Front", "Beneath the Vault Back", "Beneath the Vault Ladder Exit"):
+ ["Beneath the Vault Front", "Beneath the Vault Back", "Beneath the Vault Ladder Exit"],
+ # can ice grapple up
+ ("Fortress East Shortcut Upper", "Fortress East Shortcut Lower"):
+ ["Fortress East Shortcut Upper", "Fortress East Shortcut Lower"],
+ ("Eastern Vault Fortress", "Eastern Vault Fortress Gold Door"):
+ ["Eastern Vault Fortress", "Eastern Vault Fortress Gold Door"],
+ ("Fortress Grave Path", "Fortress Grave Path Dusty Entrance Region", "Fortress Hero's Grave Region"):
+ ["Fortress Grave Path", "Fortress Grave Path Dusty Entrance Region", "Fortress Hero's Grave Region"],
+ # can ice grapple down
+ ("Fortress Grave Path Upper",):
+ ["Fortress Grave Path Upper", "Fortress Grave Path", "Fortress Grave Path Dusty Entrance Region",
+ "Fortress Hero's Grave Region"],
+ ("Fortress Arena", "Fortress Arena Portal"):
+ ["Fortress Arena", "Fortress Arena Portal"],
+ ("Lower Mountain", "Lower Mountain Stairs"):
+ ["Lower Mountain", "Lower Mountain Stairs"],
+ ("Monastery Front", "Monastery Back", "Monastery Hero's Grave Region"):
+ ["Monastery Front", "Monastery Back", "Monastery Hero's Grave Region"],
+ # can use ladder storage at any of the Quarry ladders to get to Monastery Rope
+ ("Quarry", "Quarry Portal", "Lower Quarry", "Quarry Entry", "Quarry Back", "Quarry Monastery Entry",
+ "Monastery Rope", "Even Lower Quarry"):
+ ["Quarry", "Quarry Portal", "Lower Quarry", "Quarry Entry", "Quarry Back", "Quarry Monastery Entry",
+ "Monastery Rope", "Lower Quarry Zig Door", "Even Lower Quarry"],
+ ("Rooted Ziggurat Upper Entry", "Rooted Ziggurat Upper Front"):
+ ["Rooted Ziggurat Upper Entry", "Rooted Ziggurat Upper Front", "Rooted Ziggurat Upper Back"],
+ ("Rooted Ziggurat Middle Top",):
+ ["Rooted Ziggurat Middle Top", "Rooted Ziggurat Middle Bottom"],
+ ("Rooted Ziggurat Lower Front", "Rooted Ziggurat Lower Back", "Rooted Ziggurat Portal Room Entrance"):
+ ["Rooted Ziggurat Lower Front", "Rooted Ziggurat Lower Back", "Rooted Ziggurat Portal Room Entrance"],
+ ("Rooted Ziggurat Portal", "Rooted Ziggurat Portal Room Exit"):
+ ["Rooted Ziggurat Portal", "Rooted Ziggurat Portal Room Exit"],
+ ("Swamp Front", "Swamp Mid", "Swamp to Cathedral Treasure Room", "Swamp to Cathedral Main Entrance Region",
+ "Back of Swamp", "Back of Swamp Laurels Area", "Swamp Hero's Grave Region", "Swamp Ledge under Cathedral Door"):
+ ["Swamp Front", "Swamp Mid", "Swamp to Cathedral Treasure Room", "Swamp to Cathedral Main Entrance Region",
+ "Back of Swamp", "Back of Swamp Laurels Area", "Swamp Hero's Grave Region",
+ "Swamp Ledge under Cathedral Door"],
+ ("Cathedral Gauntlet Checkpoint",):
+ ["Cathedral Gauntlet Checkpoint", "Cathedral Gauntlet Exit", "Cathedral Gauntlet"],
+ ("Cathedral Gauntlet Exit",):
+ ["Cathedral Gauntlet Exit", "Cathedral Gauntlet"],
+ ("Far Shore", "Far Shore to Spawn Region", "Far Shore to East Forest Region", "Far Shore to Quarry Region",
+ "Far Shore to Fortress Region", "Far Shore to Library Region", "Far Shore to West Garden Region"):
+ ["Far Shore", "Far Shore to Spawn Region", "Far Shore to East Forest Region", "Far Shore to Quarry Region",
+ "Far Shore to Fortress Region", "Far Shore to Library Region", "Far Shore to West Garden Region"]
+}
diff --git a/worlds/tunic/er_rules.py b/worlds/tunic/er_rules.py
new file mode 100644
index 000000000000..96a3c39ad283
--- /dev/null
+++ b/worlds/tunic/er_rules.py
@@ -0,0 +1,1536 @@
+from typing import Dict, Set, List, Tuple, TYPE_CHECKING
+from worlds.generic.Rules import set_rule, forbid_item
+from .rules import has_ability, has_sword, has_stick, has_ice_grapple_logic, has_lantern, has_mask, can_ladder_storage
+from .er_data import Portal
+from .options import TunicOptions
+from BaseClasses import Region, CollectionState
+
+if TYPE_CHECKING:
+ from . import TunicWorld
+
+laurels = "Hero's Laurels"
+grapple = "Magic Orb"
+ice_dagger = "Magic Dagger"
+fire_wand = "Magic Wand"
+lantern = "Lantern"
+fairies = "Fairy"
+coins = "Golden Coin"
+prayer = "Pages 24-25 (Prayer)"
+holy_cross = "Pages 42-43 (Holy Cross)"
+icebolt = "Pages 52-53 (Icebolt)"
+key = "Key"
+house_key = "Old House Key"
+vault_key = "Fortress Vault Key"
+mask = "Scavenger Mask"
+red_hexagon = "Red Questagon"
+green_hexagon = "Green Questagon"
+blue_hexagon = "Blue Questagon"
+gold_hexagon = "Gold Questagon"
+
+
+def has_ladder(ladder: str, state: CollectionState, player: int, options: TunicOptions):
+ return not options.shuffle_ladders or state.has(ladder, player)
+
+
+def set_er_region_rules(world: "TunicWorld", ability_unlocks: Dict[str, int], regions: Dict[str, Region],
+ portal_pairs: Dict[Portal, Portal]) -> None:
+ player = world.player
+ options = world.options
+
+ regions["Menu"].connect(
+ connecting_region=regions["Overworld"])
+
+ # Overworld
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld Holy Cross"],
+ rule=lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+
+ # grapple on the west side, down the stairs from moss wall, across from ruined shop
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld Beach"],
+ rule=lambda state: has_ladder("Ladders in Overworld Town", state, player, options)
+ or state.has_any({laurels, grapple}, player))
+ regions["Overworld Beach"].connect(
+ connecting_region=regions["Overworld"],
+ rule=lambda state: has_ladder("Ladders in Overworld Town", state, player, options)
+ or state.has_any({laurels, grapple}, player))
+
+ regions["Overworld Beach"].connect(
+ connecting_region=regions["Overworld West Garden Laurels Entry"],
+ rule=lambda state: state.has(laurels, player))
+ regions["Overworld West Garden Laurels Entry"].connect(
+ connecting_region=regions["Overworld Beach"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["Overworld Beach"].connect(
+ connecting_region=regions["Overworld to Atoll Upper"],
+ rule=lambda state: has_ladder("Ladder to Ruined Atoll", state, player, options))
+ regions["Overworld to Atoll Upper"].connect(
+ connecting_region=regions["Overworld Beach"],
+ rule=lambda state: has_ladder("Ladder to Ruined Atoll", state, player, options))
+
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld to Atoll Upper"],
+ rule=lambda state: state.has(laurels, player))
+ regions["Overworld to Atoll Upper"].connect(
+ connecting_region=regions["Overworld"],
+ rule=lambda state: state.has_any({laurels, grapple}, player))
+
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld Belltower"],
+ rule=lambda state: state.has(laurels, player))
+ regions["Overworld Belltower"].connect(
+ connecting_region=regions["Overworld"])
+
+ regions["Overworld Belltower"].connect(
+ connecting_region=regions["Overworld to West Garden Upper"],
+ rule=lambda state: has_ladder("Ladders to West Bell", state, player, options))
+ regions["Overworld to West Garden Upper"].connect(
+ connecting_region=regions["Overworld Belltower"],
+ rule=lambda state: has_ladder("Ladders to West Bell", state, player, options))
+
+ regions["Overworld Belltower"].connect(
+ connecting_region=regions["Overworld Belltower at Bell"],
+ rule=lambda state: has_ladder("Ladders to West Bell", state, player, options))
+
+ # long dong, do not make a reverse connection here or to belltower
+ regions["Overworld above Patrol Cave"].connect(
+ connecting_region=regions["Overworld Belltower at Bell"],
+ rule=lambda state: options.logic_rules and state.has(fire_wand, player))
+
+ # nmg: can laurels through the ruined passage door
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld Ruined Passage Door"],
+ rule=lambda state: state.has(key, player, 2)
+ or (state.has(laurels, player) and options.logic_rules))
+ regions["Overworld Ruined Passage Door"].connect(
+ connecting_region=regions["Overworld"],
+ rule=lambda state: state.has(laurels, player) and options.logic_rules)
+
+ regions["Overworld"].connect(
+ connecting_region=regions["After Ruined Passage"],
+ rule=lambda state: has_ladder("Ladders near Weathervane", state, player, options)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+ regions["After Ruined Passage"].connect(
+ connecting_region=regions["Overworld"],
+ rule=lambda state: has_ladder("Ladders near Weathervane", state, player, options))
+
+ regions["Overworld"].connect(
+ connecting_region=regions["Above Ruined Passage"],
+ rule=lambda state: has_ladder("Ladders near Weathervane", state, player, options)
+ or state.has(laurels, player))
+ regions["Above Ruined Passage"].connect(
+ connecting_region=regions["Overworld"],
+ rule=lambda state: has_ladder("Ladders near Weathervane", state, player, options)
+ or state.has(laurels, player))
+
+ regions["After Ruined Passage"].connect(
+ connecting_region=regions["Above Ruined Passage"],
+ rule=lambda state: has_ladder("Ladders near Weathervane", state, player, options))
+ regions["Above Ruined Passage"].connect(
+ connecting_region=regions["After Ruined Passage"],
+ rule=lambda state: has_ladder("Ladders near Weathervane", state, player, options))
+
+ regions["Above Ruined Passage"].connect(
+ connecting_region=regions["East Overworld"],
+ rule=lambda state: has_ladder("Ladders near Weathervane", state, player, options)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+ regions["East Overworld"].connect(
+ connecting_region=regions["Above Ruined Passage"],
+ rule=lambda state: has_ladder("Ladders near Weathervane", state, player, options)
+ or state.has(laurels, player))
+
+ # nmg: ice grapple the slimes, works both ways consistently
+ regions["East Overworld"].connect(
+ connecting_region=regions["After Ruined Passage"],
+ rule=lambda state: has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+ regions["After Ruined Passage"].connect(
+ connecting_region=regions["East Overworld"],
+ rule=lambda state: has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+
+ regions["Overworld"].connect(
+ connecting_region=regions["East Overworld"],
+ rule=lambda state: has_ladder("Ladders near Overworld Checkpoint", state, player, options)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+ regions["East Overworld"].connect(
+ connecting_region=regions["Overworld"],
+ rule=lambda state: has_ladder("Ladders near Overworld Checkpoint", state, player, options))
+
+ regions["East Overworld"].connect(
+ connecting_region=regions["Overworld at Patrol Cave"])
+ regions["Overworld at Patrol Cave"].connect(
+ connecting_region=regions["East Overworld"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["Overworld at Patrol Cave"].connect(
+ connecting_region=regions["Overworld above Patrol Cave"],
+ rule=lambda state: has_ladder("Ladders near Patrol Cave", state, player, options)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+ regions["Overworld above Patrol Cave"].connect(
+ connecting_region=regions["Overworld at Patrol Cave"],
+ rule=lambda state: has_ladder("Ladders near Patrol Cave", state, player, options))
+
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld above Patrol Cave"],
+ rule=lambda state: has_ladder("Ladders near Overworld Checkpoint", state, player, options)
+ or state.has(grapple, player))
+ regions["Overworld above Patrol Cave"].connect(
+ connecting_region=regions["Overworld"],
+ rule=lambda state: has_ladder("Ladders near Overworld Checkpoint", state, player, options))
+
+ regions["East Overworld"].connect(
+ connecting_region=regions["Overworld above Patrol Cave"],
+ rule=lambda state: has_ladder("Ladders near Overworld Checkpoint", state, player, options)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+ regions["Overworld above Patrol Cave"].connect(
+ connecting_region=regions["East Overworld"],
+ rule=lambda state: has_ladder("Ladders near Overworld Checkpoint", state, player, options))
+
+ regions["Overworld above Patrol Cave"].connect(
+ connecting_region=regions["Upper Overworld"],
+ rule=lambda state: has_ladder("Ladders near Patrol Cave", state, player, options)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+ regions["Upper Overworld"].connect(
+ connecting_region=regions["Overworld above Patrol Cave"],
+ rule=lambda state: has_ladder("Ladders near Patrol Cave", state, player, options)
+ or state.has(grapple, player))
+
+ regions["Upper Overworld"].connect(
+ connecting_region=regions["Overworld above Quarry Entrance"],
+ rule=lambda state: state.has_any({grapple, laurels}, player))
+ regions["Overworld above Quarry Entrance"].connect(
+ connecting_region=regions["Upper Overworld"],
+ rule=lambda state: state.has_any({grapple, laurels}, player))
+
+ regions["Upper Overworld"].connect(
+ connecting_region=regions["Overworld after Temple Rafters"],
+ rule=lambda state: has_ladder("Ladder near Temple Rafters", state, player, options))
+ regions["Overworld after Temple Rafters"].connect(
+ connecting_region=regions["Upper Overworld"],
+ rule=lambda state: has_ladder("Ladder near Temple Rafters", state, player, options)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+
+ regions["Overworld above Quarry Entrance"].connect(
+ connecting_region=regions["Overworld"],
+ rule=lambda state: has_ladder("Ladders near Dark Tomb", state, player, options))
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld above Quarry Entrance"],
+ rule=lambda state: has_ladder("Ladders near Dark Tomb", state, player, options))
+
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld after Envoy"],
+ rule=lambda state: state.has_any({laurels, grapple}, player)
+ or state.has("Sword Upgrade", player, 4)
+ or options.logic_rules)
+ regions["Overworld after Envoy"].connect(
+ connecting_region=regions["Overworld"],
+ rule=lambda state: state.has_any({laurels, grapple}, player)
+ or state.has("Sword Upgrade", player, 4)
+ or options.logic_rules)
+
+ regions["Overworld after Envoy"].connect(
+ connecting_region=regions["Overworld Quarry Entry"],
+ rule=lambda state: has_ladder("Ladder to Quarry", state, player, options))
+ regions["Overworld Quarry Entry"].connect(
+ connecting_region=regions["Overworld after Envoy"],
+ rule=lambda state: has_ladder("Ladder to Quarry", state, player, options))
+
+ # ice grapple through the gate
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld Quarry Entry"],
+ rule=lambda state: has_ice_grapple_logic(False, state, player, options, ability_unlocks))
+ regions["Overworld Quarry Entry"].connect(
+ connecting_region=regions["Overworld"],
+ rule=lambda state: has_ice_grapple_logic(False, state, player, options, ability_unlocks))
+
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld Swamp Upper Entry"],
+ rule=lambda state: state.has(laurels, player))
+ regions["Overworld Swamp Upper Entry"].connect(
+ connecting_region=regions["Overworld"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld Swamp Lower Entry"],
+ rule=lambda state: has_ladder("Ladder to Swamp", state, player, options))
+ regions["Overworld Swamp Lower Entry"].connect(
+ connecting_region=regions["Overworld"],
+ rule=lambda state: has_ladder("Ladder to Swamp", state, player, options))
+
+ regions["East Overworld"].connect(
+ connecting_region=regions["Overworld Special Shop Entry"],
+ rule=lambda state: state.has(laurels, player))
+ regions["Overworld Special Shop Entry"].connect(
+ connecting_region=regions["East Overworld"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld Well Ladder"],
+ rule=lambda state: has_ladder("Ladders in Well", state, player, options))
+ regions["Overworld Well Ladder"].connect(
+ connecting_region=regions["Overworld"])
+
+ # nmg: can ice grapple through the door
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld Old House Door"],
+ rule=lambda state: state.has(house_key, player)
+ or has_ice_grapple_logic(False, state, player, options, ability_unlocks))
+
+ # not including ice grapple through this because it's very tedious to get an enemy here
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld Southeast Cross Door"],
+ rule=lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ regions["Overworld Southeast Cross Door"].connect(
+ connecting_region=regions["Overworld"],
+ rule=lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+
+ # not including ice grapple through this because we're not including it on the other door
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld Fountain Cross Door"],
+ rule=lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ regions["Overworld Fountain Cross Door"].connect(
+ connecting_region=regions["Overworld"])
+
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld Town Portal"],
+ rule=lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ regions["Overworld Town Portal"].connect(
+ connecting_region=regions["Overworld"])
+
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld Spawn Portal"],
+ rule=lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ regions["Overworld Spawn Portal"].connect(
+ connecting_region=regions["Overworld"])
+
+ # nmg: ice grapple through temple door
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld Temple Door"],
+ rule=lambda state: state.has_all({"Ring Eastern Bell", "Ring Western Bell"}, player)
+ or has_ice_grapple_logic(False, state, player, options, ability_unlocks))
+
+ regions["Overworld Temple Door"].connect(
+ connecting_region=regions["Overworld above Patrol Cave"],
+ rule=lambda state: state.has(grapple, player))
+
+ regions["Overworld Tunnel Turret"].connect(
+ connecting_region=regions["Overworld Beach"],
+ rule=lambda state: has_ladder("Ladders in Overworld Town", state, player, options)
+ or state.has(grapple, player))
+ regions["Overworld Beach"].connect(
+ connecting_region=regions["Overworld Tunnel Turret"],
+ rule=lambda state: has_ladder("Ladders in Overworld Town", state, player, options)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+
+ regions["Overworld"].connect(
+ connecting_region=regions["Overworld Tunnel Turret"],
+ rule=lambda state: state.has(laurels, player)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+ regions["Overworld Tunnel Turret"].connect(
+ connecting_region=regions["Overworld"],
+ rule=lambda state: state.has_any({grapple, laurels}, player))
+
+ # Overworld side areas
+ regions["Old House Front"].connect(
+ connecting_region=regions["Old House Back"])
+ # nmg: laurels through the gate
+ regions["Old House Back"].connect(
+ connecting_region=regions["Old House Front"],
+ rule=lambda state: state.has(laurels, player) and options.logic_rules)
+
+ regions["Sealed Temple"].connect(
+ connecting_region=regions["Sealed Temple Rafters"])
+ regions["Sealed Temple Rafters"].connect(
+ connecting_region=regions["Sealed Temple"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["Furnace Walking Path"].connect(
+ connecting_region=regions["Furnace Ladder Area"],
+ rule=lambda state: state.has(laurels, player))
+ regions["Furnace Ladder Area"].connect(
+ connecting_region=regions["Furnace Walking Path"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["Furnace Walking Path"].connect(
+ connecting_region=regions["Furnace Fuse"],
+ rule=lambda state: state.has(laurels, player))
+ regions["Furnace Fuse"].connect(
+ connecting_region=regions["Furnace Walking Path"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["Furnace Fuse"].connect(
+ connecting_region=regions["Furnace Ladder Area"],
+ rule=lambda state: state.has(laurels, player))
+ regions["Furnace Ladder Area"].connect(
+ connecting_region=regions["Furnace Fuse"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["Hourglass Cave"].connect(
+ connecting_region=regions["Hourglass Cave Tower"],
+ rule=lambda state: has_ladder("Ladders in Hourglass Cave", state, player, options))
+
+ # East Forest
+ regions["Forest Belltower Upper"].connect(
+ connecting_region=regions["Forest Belltower Main"])
+
+ regions["Forest Belltower Main"].connect(
+ connecting_region=regions["Forest Belltower Lower"],
+ rule=lambda state: has_ladder("Ladder to East Forest", state, player, options))
+
+ # nmg: ice grapple up to dance fox spot, and vice versa
+ regions["East Forest"].connect(
+ connecting_region=regions["East Forest Dance Fox Spot"],
+ rule=lambda state: state.has(laurels, player)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+ regions["East Forest Dance Fox Spot"].connect(
+ connecting_region=regions["East Forest"],
+ rule=lambda state: state.has(laurels, player)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+
+ regions["East Forest"].connect(
+ connecting_region=regions["East Forest Portal"],
+ rule=lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ regions["East Forest Portal"].connect(
+ connecting_region=regions["East Forest"])
+
+ regions["East Forest"].connect(
+ connecting_region=regions["Lower Forest"],
+ rule=lambda state: has_ladder("Ladders to Lower Forest", state, player, options)
+ or (state.has_all({grapple, fire_wand, ice_dagger}, player) # do ice slime, then go to the lower hook
+ and has_ability(state, player, icebolt, options, ability_unlocks)))
+ regions["Lower Forest"].connect(
+ connecting_region=regions["East Forest"],
+ rule=lambda state: has_ladder("Ladders to Lower Forest", state, player, options))
+
+ regions["Guard House 1 East"].connect(
+ connecting_region=regions["Guard House 1 West"])
+ regions["Guard House 1 West"].connect(
+ connecting_region=regions["Guard House 1 East"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["Guard House 2 Upper"].connect(
+ connecting_region=regions["Guard House 2 Lower"],
+ rule=lambda state: has_ladder("Ladders to Lower Forest", state, player, options))
+ regions["Guard House 2 Lower"].connect(
+ connecting_region=regions["Guard House 2 Upper"],
+ rule=lambda state: has_ladder("Ladders to Lower Forest", state, player, options))
+
+ # nmg: ice grapple from upper grave path exit to the rest of it
+ regions["Forest Grave Path Upper"].connect(
+ connecting_region=regions["Forest Grave Path Main"],
+ rule=lambda state: state.has(laurels, player)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+ regions["Forest Grave Path Main"].connect(
+ connecting_region=regions["Forest Grave Path Upper"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["Forest Grave Path Main"].connect(
+ connecting_region=regions["Forest Grave Path by Grave"])
+ # nmg: ice grapple or laurels through the gate
+ regions["Forest Grave Path by Grave"].connect(
+ connecting_region=regions["Forest Grave Path Main"],
+ rule=lambda state: has_ice_grapple_logic(False, state, player, options, ability_unlocks)
+ or (state.has(laurels, player) and options.logic_rules))
+
+ regions["Forest Grave Path by Grave"].connect(
+ connecting_region=regions["Forest Hero's Grave"],
+ rule=lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ regions["Forest Hero's Grave"].connect(
+ connecting_region=regions["Forest Grave Path by Grave"])
+
+ # Beneath the Well and Dark Tomb
+ # don't need the ladder when entering at the ladder spot
+ regions["Beneath the Well Ladder Exit"].connect(
+ connecting_region=regions["Beneath the Well Front"],
+ rule=lambda state: has_ladder("Ladders in Well", state, player, options))
+ regions["Beneath the Well Front"].connect(
+ connecting_region=regions["Beneath the Well Ladder Exit"],
+ rule=lambda state: has_ladder("Ladders in Well", state, player, options))
+
+ regions["Beneath the Well Front"].connect(
+ connecting_region=regions["Beneath the Well Main"],
+ rule=lambda state: has_stick(state, player) or state.has(fire_wand, player))
+ regions["Beneath the Well Main"].connect(
+ connecting_region=regions["Beneath the Well Front"],
+ rule=lambda state: has_stick(state, player) or state.has(fire_wand, player))
+
+ regions["Beneath the Well Main"].connect(
+ connecting_region=regions["Beneath the Well Back"],
+ rule=lambda state: has_ladder("Ladders in Well", state, player, options))
+ regions["Beneath the Well Back"].connect(
+ connecting_region=regions["Beneath the Well Main"],
+ rule=lambda state: has_ladder("Ladders in Well", state, player, options)
+ and (has_stick(state, player) or state.has(fire_wand, player)))
+
+ regions["Well Boss"].connect(
+ connecting_region=regions["Dark Tomb Checkpoint"])
+ # nmg: can laurels through the gate
+ regions["Dark Tomb Checkpoint"].connect(
+ connecting_region=regions["Well Boss"],
+ rule=lambda state: state.has(laurels, player) and options.logic_rules)
+
+ regions["Dark Tomb Entry Point"].connect(
+ connecting_region=regions["Dark Tomb Upper"],
+ rule=lambda state: has_lantern(state, player, options))
+ regions["Dark Tomb Upper"].connect(
+ connecting_region=regions["Dark Tomb Entry Point"])
+
+ regions["Dark Tomb Upper"].connect(
+ connecting_region=regions["Dark Tomb Main"],
+ rule=lambda state: has_ladder("Ladder in Dark Tomb", state, player, options))
+ regions["Dark Tomb Main"].connect(
+ connecting_region=regions["Dark Tomb Upper"],
+ rule=lambda state: has_ladder("Ladder in Dark Tomb", state, player, options))
+
+ regions["Dark Tomb Main"].connect(
+ connecting_region=regions["Dark Tomb Dark Exit"])
+ regions["Dark Tomb Dark Exit"].connect(
+ connecting_region=regions["Dark Tomb Main"],
+ rule=lambda state: has_lantern(state, player, options))
+
+ # West Garden
+ regions["West Garden Laurels Exit Region"].connect(
+ connecting_region=regions["West Garden"],
+ rule=lambda state: state.has(laurels, player))
+ regions["West Garden"].connect(
+ connecting_region=regions["West Garden Laurels Exit Region"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["West Garden after Boss"].connect(
+ connecting_region=regions["West Garden"],
+ rule=lambda state: state.has(laurels, player))
+ regions["West Garden"].connect(
+ connecting_region=regions["West Garden after Boss"],
+ rule=lambda state: state.has(laurels, player) or has_sword(state, player))
+
+ regions["West Garden"].connect(
+ connecting_region=regions["West Garden Hero's Grave Region"],
+ rule=lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ regions["West Garden Hero's Grave Region"].connect(
+ connecting_region=regions["West Garden"])
+
+ regions["West Garden Portal"].connect(
+ connecting_region=regions["West Garden Portal Item"],
+ rule=lambda state: state.has(laurels, player))
+ regions["West Garden Portal Item"].connect(
+ connecting_region=regions["West Garden Portal"],
+ rule=lambda state: state.has(laurels, player) and has_ability(state, player, prayer, options, ability_unlocks))
+
+ # nmg: can ice grapple to and from the item behind the magic dagger house
+ regions["West Garden Portal Item"].connect(
+ connecting_region=regions["West Garden"],
+ rule=lambda state: has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+ regions["West Garden"].connect(
+ connecting_region=regions["West Garden Portal Item"],
+ rule=lambda state: has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+
+ # Atoll and Frog's Domain
+ # nmg: ice grapple the bird below the portal
+ regions["Ruined Atoll"].connect(
+ connecting_region=regions["Ruined Atoll Lower Entry Area"],
+ rule=lambda state: state.has(laurels, player)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+ regions["Ruined Atoll Lower Entry Area"].connect(
+ connecting_region=regions["Ruined Atoll"],
+ rule=lambda state: state.has(laurels, player) or state.has(grapple, player))
+
+ regions["Ruined Atoll"].connect(
+ connecting_region=regions["Ruined Atoll Ladder Tops"],
+ rule=lambda state: has_ladder("Ladders in South Atoll", state, player, options))
+
+ regions["Ruined Atoll"].connect(
+ connecting_region=regions["Ruined Atoll Frog Mouth"],
+ rule=lambda state: state.has(laurels, player) or state.has(grapple, player))
+ regions["Ruined Atoll Frog Mouth"].connect(
+ connecting_region=regions["Ruined Atoll"],
+ rule=lambda state: state.has(laurels, player) or state.has(grapple, player))
+
+ regions["Ruined Atoll"].connect(
+ connecting_region=regions["Ruined Atoll Frog Eye"],
+ rule=lambda state: has_ladder("Ladders to Frog's Domain", state, player, options))
+ regions["Ruined Atoll Frog Eye"].connect(
+ connecting_region=regions["Ruined Atoll"],
+ rule=lambda state: has_ladder("Ladders to Frog's Domain", state, player, options))
+
+ regions["Ruined Atoll"].connect(
+ connecting_region=regions["Ruined Atoll Portal"],
+ rule=lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ regions["Ruined Atoll Portal"].connect(
+ connecting_region=regions["Ruined Atoll"])
+
+ regions["Ruined Atoll"].connect(
+ connecting_region=regions["Ruined Atoll Statue"],
+ rule=lambda state: has_ability(state, player, prayer, options, ability_unlocks)
+ and has_ladder("Ladders in South Atoll", state, player, options))
+ regions["Ruined Atoll Statue"].connect(
+ connecting_region=regions["Ruined Atoll"])
+
+ regions["Frog Stairs Eye Exit"].connect(
+ connecting_region=regions["Frog Stairs Upper"],
+ rule=lambda state: has_ladder("Ladders to Frog's Domain", state, player, options))
+ regions["Frog Stairs Upper"].connect(
+ connecting_region=regions["Frog Stairs Eye Exit"],
+ rule=lambda state: has_ladder("Ladders to Frog's Domain", state, player, options))
+
+ regions["Frog Stairs Upper"].connect(
+ connecting_region=regions["Frog Stairs Lower"],
+ rule=lambda state: has_ladder("Ladders to Frog's Domain", state, player, options))
+ regions["Frog Stairs Lower"].connect(
+ connecting_region=regions["Frog Stairs Upper"],
+ rule=lambda state: has_ladder("Ladders to Frog's Domain", state, player, options))
+
+ regions["Frog Stairs Lower"].connect(
+ connecting_region=regions["Frog Stairs to Frog's Domain"],
+ rule=lambda state: has_ladder("Ladders to Frog's Domain", state, player, options))
+ regions["Frog Stairs to Frog's Domain"].connect(
+ connecting_region=regions["Frog Stairs Lower"],
+ rule=lambda state: has_ladder("Ladders to Frog's Domain", state, player, options))
+
+ regions["Frog's Domain Entry"].connect(
+ connecting_region=regions["Frog's Domain"],
+ rule=lambda state: has_ladder("Ladders to Frog's Domain", state, player, options))
+
+ regions["Frog's Domain"].connect(
+ connecting_region=regions["Frog's Domain Back"],
+ rule=lambda state: state.has(grapple, player))
+
+ # Library
+ regions["Library Exterior Tree Region"].connect(
+ connecting_region=regions["Library Exterior Ladder Region"],
+ rule=lambda state: state.has_any({grapple, laurels}, player)
+ and has_ladder("Ladders in Library", state, player, options))
+ regions["Library Exterior Ladder Region"].connect(
+ connecting_region=regions["Library Exterior Tree Region"],
+ rule=lambda state: has_ability(state, player, prayer, options, ability_unlocks)
+ and (state.has(grapple, player) or (state.has(laurels, player)
+ and has_ladder("Ladders in Library", state, player, options))))
+
+ regions["Library Hall Bookshelf"].connect(
+ connecting_region=regions["Library Hall"],
+ rule=lambda state: has_ladder("Ladders in Library", state, player, options))
+ regions["Library Hall"].connect(
+ connecting_region=regions["Library Hall Bookshelf"],
+ rule=lambda state: has_ladder("Ladders in Library", state, player, options))
+
+ regions["Library Hall"].connect(
+ connecting_region=regions["Library Hero's Grave Region"],
+ rule=lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ regions["Library Hero's Grave Region"].connect(
+ connecting_region=regions["Library Hall"])
+
+ regions["Library Hall to Rotunda"].connect(
+ connecting_region=regions["Library Hall"],
+ rule=lambda state: has_ladder("Ladders in Library", state, player, options))
+ regions["Library Hall"].connect(
+ connecting_region=regions["Library Hall to Rotunda"],
+ rule=lambda state: has_ladder("Ladders in Library", state, player, options))
+
+ regions["Library Rotunda to Hall"].connect(
+ connecting_region=regions["Library Rotunda"],
+ rule=lambda state: has_ladder("Ladders in Library", state, player, options))
+ regions["Library Rotunda"].connect(
+ connecting_region=regions["Library Rotunda to Hall"],
+ rule=lambda state: has_ladder("Ladders in Library", state, player, options))
+
+ regions["Library Rotunda"].connect(
+ connecting_region=regions["Library Rotunda to Lab"],
+ rule=lambda state: has_ladder("Ladders in Library", state, player, options))
+ regions["Library Rotunda to Lab"].connect(
+ connecting_region=regions["Library Rotunda"],
+ rule=lambda state: has_ladder("Ladders in Library", state, player, options))
+
+ regions["Library Lab Lower"].connect(
+ connecting_region=regions["Library Lab"],
+ rule=lambda state: state.has_any({grapple, laurels}, player)
+ and has_ladder("Ladders in Library", state, player, options))
+ regions["Library Lab"].connect(
+ connecting_region=regions["Library Lab Lower"],
+ rule=lambda state: state.has(laurels, player)
+ and has_ladder("Ladders in Library", state, player, options))
+
+ regions["Library Lab"].connect(
+ connecting_region=regions["Library Portal"],
+ rule=lambda state: has_ability(state, player, prayer, options, ability_unlocks)
+ and has_ladder("Ladders in Library", state, player, options))
+ regions["Library Portal"].connect(
+ connecting_region=regions["Library Lab"],
+ rule=lambda state: has_ladder("Ladders in Library", state, player, options)
+ or state.has(laurels, player))
+
+ regions["Library Lab"].connect(
+ connecting_region=regions["Library Lab to Librarian"],
+ rule=lambda state: has_ladder("Ladders in Library", state, player, options))
+ regions["Library Lab to Librarian"].connect(
+ connecting_region=regions["Library Lab"],
+ rule=lambda state: has_ladder("Ladders in Library", state, player, options))
+
+ # Eastern Vault Fortress
+ regions["Fortress Exterior from East Forest"].connect(
+ connecting_region=regions["Fortress Exterior from Overworld"],
+ rule=lambda state: state.has(laurels, player) or state.has(grapple, player))
+ regions["Fortress Exterior from Overworld"].connect(
+ connecting_region=regions["Fortress Exterior from East Forest"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["Fortress Exterior near cave"].connect(
+ connecting_region=regions["Fortress Exterior from Overworld"],
+ rule=lambda state: state.has(laurels, player))
+ regions["Fortress Exterior from Overworld"].connect(
+ connecting_region=regions["Fortress Exterior near cave"],
+ rule=lambda state: state.has(laurels, player) or has_ability(state, player, prayer, options, ability_unlocks))
+
+ regions["Fortress Exterior near cave"].connect(
+ connecting_region=regions["Beneath the Vault Entry"],
+ rule=lambda state: has_ladder("Ladder to Beneath the Vault", state, player, options))
+ regions["Beneath the Vault Entry"].connect(
+ connecting_region=regions["Fortress Exterior near cave"],
+ rule=lambda state: has_ladder("Ladder to Beneath the Vault", state, player, options))
+
+ regions["Fortress Courtyard"].connect(
+ connecting_region=regions["Fortress Exterior from Overworld"],
+ rule=lambda state: state.has(laurels, player))
+ # nmg: can ice grapple an enemy in the courtyard
+ regions["Fortress Exterior from Overworld"].connect(
+ connecting_region=regions["Fortress Courtyard"],
+ rule=lambda state: state.has(laurels, player)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+
+ regions["Fortress Courtyard Upper"].connect(
+ connecting_region=regions["Fortress Courtyard"])
+ # nmg: can ice grapple to the upper ledge
+ regions["Fortress Courtyard"].connect(
+ connecting_region=regions["Fortress Courtyard Upper"],
+ rule=lambda state: has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+
+ regions["Fortress Courtyard Upper"].connect(
+ connecting_region=regions["Fortress Exterior from Overworld"])
+
+ regions["Beneath the Vault Ladder Exit"].connect(
+ connecting_region=regions["Beneath the Vault Front"],
+ rule=lambda state: has_ladder("Ladder to Beneath the Vault", state, player, options))
+ regions["Beneath the Vault Front"].connect(
+ connecting_region=regions["Beneath the Vault Ladder Exit"],
+ rule=lambda state: has_ladder("Ladder to Beneath the Vault", state, player, options))
+
+ regions["Beneath the Vault Front"].connect(
+ connecting_region=regions["Beneath the Vault Back"],
+ rule=lambda state: has_lantern(state, player, options))
+ regions["Beneath the Vault Back"].connect(
+ connecting_region=regions["Beneath the Vault Front"])
+
+ regions["Fortress East Shortcut Upper"].connect(
+ connecting_region=regions["Fortress East Shortcut Lower"])
+ # nmg: can ice grapple upwards
+ regions["Fortress East Shortcut Lower"].connect(
+ connecting_region=regions["Fortress East Shortcut Upper"],
+ rule=lambda state: has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+
+ # nmg: ice grapple through the big gold door, can do it both ways
+ regions["Eastern Vault Fortress"].connect(
+ connecting_region=regions["Eastern Vault Fortress Gold Door"],
+ rule=lambda state: state.has_all({"Activate Eastern Vault West Fuses",
+ "Activate Eastern Vault East Fuse"}, player)
+ or has_ice_grapple_logic(False, state, player, options, ability_unlocks))
+ regions["Eastern Vault Fortress Gold Door"].connect(
+ connecting_region=regions["Eastern Vault Fortress"],
+ rule=lambda state: has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+
+ regions["Fortress Grave Path"].connect(
+ connecting_region=regions["Fortress Grave Path Dusty Entrance Region"],
+ rule=lambda state: state.has(laurels, player))
+ regions["Fortress Grave Path Dusty Entrance Region"].connect(
+ connecting_region=regions["Fortress Grave Path"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["Fortress Grave Path"].connect(
+ connecting_region=regions["Fortress Hero's Grave Region"],
+ rule=lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ regions["Fortress Hero's Grave Region"].connect(
+ connecting_region=regions["Fortress Grave Path"])
+
+ # nmg: ice grapple from upper grave path to lower
+ regions["Fortress Grave Path Upper"].connect(
+ connecting_region=regions["Fortress Grave Path"],
+ rule=lambda state: has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+
+ regions["Fortress Arena"].connect(
+ connecting_region=regions["Fortress Arena Portal"],
+ rule=lambda state: state.has("Activate Eastern Vault West Fuses", player))
+ regions["Fortress Arena Portal"].connect(
+ connecting_region=regions["Fortress Arena"])
+
+ # Quarry
+ regions["Lower Mountain"].connect(
+ connecting_region=regions["Lower Mountain Stairs"],
+ rule=lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ regions["Lower Mountain Stairs"].connect(
+ connecting_region=regions["Lower Mountain"],
+ rule=lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+
+ regions["Quarry Entry"].connect(
+ connecting_region=regions["Quarry Portal"],
+ rule=lambda state: state.has("Activate Quarry Fuse", player))
+ regions["Quarry Portal"].connect(
+ connecting_region=regions["Quarry Entry"])
+
+ regions["Quarry Entry"].connect(
+ connecting_region=regions["Quarry"],
+ rule=lambda state: state.has(fire_wand, player) or has_sword(state, player))
+ regions["Quarry"].connect(
+ connecting_region=regions["Quarry Entry"])
+
+ regions["Quarry Back"].connect(
+ connecting_region=regions["Quarry"],
+ rule=lambda state: state.has(fire_wand, player) or has_sword(state, player))
+ regions["Quarry"].connect(
+ connecting_region=regions["Quarry Back"])
+
+ regions["Quarry Monastery Entry"].connect(
+ connecting_region=regions["Quarry"],
+ rule=lambda state: state.has(fire_wand, player) or has_sword(state, player))
+ regions["Quarry"].connect(
+ connecting_region=regions["Quarry Monastery Entry"])
+
+ regions["Quarry Monastery Entry"].connect(
+ connecting_region=regions["Quarry Back"],
+ rule=lambda state: state.has(laurels, player))
+ regions["Quarry Back"].connect(
+ connecting_region=regions["Quarry Monastery Entry"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["Monastery Rope"].connect(
+ connecting_region=regions["Quarry Back"])
+
+ regions["Quarry"].connect(
+ connecting_region=regions["Lower Quarry"],
+ rule=lambda state: has_mask(state, player, options))
+
+ # need the ladder, or you can ice grapple down in nmg
+ regions["Lower Quarry"].connect(
+ connecting_region=regions["Even Lower Quarry"],
+ rule=lambda state: has_ladder("Ladders in Lower Quarry", state, player, options)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+
+ # nmg: bring a scav over, then ice grapple through the door, only with ER on to avoid soft lock
+ regions["Even Lower Quarry"].connect(
+ connecting_region=regions["Lower Quarry Zig Door"],
+ rule=lambda state: state.has("Activate Quarry Fuse", player)
+ or (has_ice_grapple_logic(False, state, player, options, ability_unlocks) and options.entrance_rando))
+
+ # nmg: use ice grapple to get from the beginning of Quarry to the door without really needing mask only with ER on
+ regions["Quarry"].connect(
+ connecting_region=regions["Lower Quarry Zig Door"],
+ rule=lambda state: has_ice_grapple_logic(True, state, player, options, ability_unlocks)
+ and options.entrance_rando)
+
+ regions["Monastery Front"].connect(
+ connecting_region=regions["Monastery Back"])
+ # nmg: can laurels through the gate
+ regions["Monastery Back"].connect(
+ connecting_region=regions["Monastery Front"],
+ rule=lambda state: state.has(laurels, player) and options.logic_rules)
+
+ regions["Monastery Back"].connect(
+ connecting_region=regions["Monastery Hero's Grave Region"],
+ rule=lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ regions["Monastery Hero's Grave Region"].connect(
+ connecting_region=regions["Monastery Back"])
+
+ # Ziggurat
+ regions["Rooted Ziggurat Upper Entry"].connect(
+ connecting_region=regions["Rooted Ziggurat Upper Front"])
+
+ regions["Rooted Ziggurat Upper Front"].connect(
+ connecting_region=regions["Rooted Ziggurat Upper Back"],
+ rule=lambda state: state.has(laurels, player) or has_sword(state, player))
+ regions["Rooted Ziggurat Upper Back"].connect(
+ connecting_region=regions["Rooted Ziggurat Upper Front"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["Rooted Ziggurat Middle Top"].connect(
+ connecting_region=regions["Rooted Ziggurat Middle Bottom"])
+
+ regions["Rooted Ziggurat Lower Front"].connect(
+ connecting_region=regions["Rooted Ziggurat Lower Back"],
+ rule=lambda state: state.has(laurels, player)
+ or (has_sword(state, player) and has_ability(state, player, prayer, options, ability_unlocks)))
+ # unrestricted: use ladder storage to get to the front, get hit by one of the many enemies
+ # nmg: can ice grapple on the voidlings to the double admin fight, still need to pray at the fuse
+ regions["Rooted Ziggurat Lower Back"].connect(
+ connecting_region=regions["Rooted Ziggurat Lower Front"],
+ rule=lambda state: ((state.has(laurels, player)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+ and has_ability(state, player, prayer, options, ability_unlocks)
+ and has_sword(state, player))
+ or can_ladder_storage(state, player, options))
+
+ regions["Rooted Ziggurat Lower Back"].connect(
+ connecting_region=regions["Rooted Ziggurat Portal Room Entrance"],
+ rule=lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ regions["Rooted Ziggurat Portal Room Entrance"].connect(
+ connecting_region=regions["Rooted Ziggurat Lower Back"])
+
+ regions["Rooted Ziggurat Portal"].connect(
+ connecting_region=regions["Rooted Ziggurat Portal Room Exit"],
+ rule=lambda state: state.has("Activate Ziggurat Fuse", player))
+ regions["Rooted Ziggurat Portal Room Exit"].connect(
+ connecting_region=regions["Rooted Ziggurat Portal"],
+ rule=lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+
+ # Swamp and Cathedral
+ regions["Swamp Front"].connect(
+ connecting_region=regions["Swamp Mid"],
+ rule=lambda state: has_ladder("Ladders in Swamp", state, player, options)
+ or state.has(laurels, player)
+ or has_ice_grapple_logic(False, state, player, options, ability_unlocks)) # nmg: ice grapple through gate
+ regions["Swamp Mid"].connect(
+ connecting_region=regions["Swamp Front"],
+ rule=lambda state: has_ladder("Ladders in Swamp", state, player, options)
+ or state.has(laurels, player)
+ or has_ice_grapple_logic(False, state, player, options, ability_unlocks)) # nmg: ice grapple through gate
+
+ # nmg: ice grapple through cathedral door, can do it both ways
+ regions["Swamp Mid"].connect(
+ connecting_region=regions["Swamp to Cathedral Main Entrance Region"],
+ rule=lambda state: (has_ability(state, player, prayer, options, ability_unlocks)
+ and state.has(laurels, player))
+ or has_ice_grapple_logic(False, state, player, options, ability_unlocks))
+ regions["Swamp to Cathedral Main Entrance Region"].connect(
+ connecting_region=regions["Swamp Mid"],
+ rule=lambda state: has_ice_grapple_logic(False, state, player, options, ability_unlocks))
+
+ regions["Swamp Mid"].connect(
+ connecting_region=regions["Swamp Ledge under Cathedral Door"],
+ rule=lambda state: has_ladder("Ladders in Swamp", state, player, options))
+ regions["Swamp Ledge under Cathedral Door"].connect(
+ connecting_region=regions["Swamp Mid"],
+ rule=lambda state: has_ladder("Ladders in Swamp", state, player, options)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks)) # nmg: ice grapple the enemy at door
+
+ regions["Swamp Ledge under Cathedral Door"].connect(
+ connecting_region=regions["Swamp to Cathedral Treasure Room"],
+ rule=lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ regions["Swamp to Cathedral Treasure Room"].connect(
+ connecting_region=regions["Swamp Ledge under Cathedral Door"])
+
+ regions["Back of Swamp"].connect(
+ connecting_region=regions["Back of Swamp Laurels Area"],
+ rule=lambda state: state.has(laurels, player))
+ regions["Back of Swamp Laurels Area"].connect(
+ connecting_region=regions["Back of Swamp"],
+ rule=lambda state: state.has(laurels, player))
+
+ # nmg: can ice grapple down while you're on the pillars
+ regions["Back of Swamp Laurels Area"].connect(
+ connecting_region=regions["Swamp Mid"],
+ rule=lambda state: state.has(laurels, player)
+ and has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+
+ regions["Back of Swamp"].connect(
+ connecting_region=regions["Swamp Hero's Grave Region"],
+ rule=lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ regions["Swamp Hero's Grave Region"].connect(
+ connecting_region=regions["Back of Swamp"])
+
+ regions["Cathedral Gauntlet Checkpoint"].connect(
+ connecting_region=regions["Cathedral Gauntlet"])
+
+ regions["Cathedral Gauntlet"].connect(
+ connecting_region=regions["Cathedral Gauntlet Exit"],
+ rule=lambda state: state.has(laurels, player))
+ regions["Cathedral Gauntlet Exit"].connect(
+ connecting_region=regions["Cathedral Gauntlet"],
+ rule=lambda state: state.has(laurels, player))
+
+ # Far Shore
+ regions["Far Shore"].connect(
+ connecting_region=regions["Far Shore to Spawn Region"],
+ rule=lambda state: state.has(laurels, player))
+ regions["Far Shore to Spawn Region"].connect(
+ connecting_region=regions["Far Shore"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["Far Shore"].connect(
+ connecting_region=regions["Far Shore to East Forest Region"],
+ rule=lambda state: state.has(laurels, player))
+ regions["Far Shore to East Forest Region"].connect(
+ connecting_region=regions["Far Shore"],
+ rule=lambda state: state.has(laurels, player))
+
+ regions["Far Shore"].connect(
+ connecting_region=regions["Far Shore to West Garden Region"],
+ rule=lambda state: state.has("Activate West Garden Fuse", player))
+ regions["Far Shore to West Garden Region"].connect(
+ connecting_region=regions["Far Shore"])
+
+ regions["Far Shore"].connect(
+ connecting_region=regions["Far Shore to Quarry Region"],
+ rule=lambda state: state.has("Activate Quarry Fuse", player))
+ regions["Far Shore to Quarry Region"].connect(
+ connecting_region=regions["Far Shore"])
+
+ regions["Far Shore"].connect(
+ connecting_region=regions["Far Shore to Fortress Region"],
+ rule=lambda state: state.has("Activate Eastern Vault West Fuses", player))
+ regions["Far Shore to Fortress Region"].connect(
+ connecting_region=regions["Far Shore"])
+
+ regions["Far Shore"].connect(
+ connecting_region=regions["Far Shore to Library Region"],
+ rule=lambda state: state.has("Activate Library Fuse", player))
+ regions["Far Shore to Library Region"].connect(
+ connecting_region=regions["Far Shore"])
+
+ # Misc
+ regions["Spirit Arena"].connect(
+ connecting_region=regions["Spirit Arena Victory"],
+ rule=lambda state: (state.has(gold_hexagon, player, world.options.hexagon_goal.value) if
+ world.options.hexagon_quest else
+ state.has_all({red_hexagon, green_hexagon, blue_hexagon}, player)))
+
+ # connecting the regions portals are in to other portals you can access via ladder storage
+ # using has_stick instead of can_ladder_storage since it's already checking the logic rules
+ if options.logic_rules == "unrestricted":
+ def get_portal_info(portal_sd: str) -> (str, str):
+ for portal1, portal2 in portal_pairs.items():
+ if portal1.scene_destination() == portal_sd:
+ return portal1.name, portal2.region
+ if portal2.scene_destination() == portal_sd:
+ return portal2.name, portal1.region
+ raise Exception("no matches found in get_paired_region")
+
+ ladder_storages: List[Tuple[str, str, Set[str]]] = [
+ # LS from Overworld main
+ # The upper Swamp entrance
+ ("Overworld", "Overworld Redux, Swamp Redux 2_wall",
+ {"Ladders near Weathervane", "Ladder to Swamp", "Ladders in Overworld Town"}),
+ # Upper atoll entrance
+ ("Overworld", "Overworld Redux, Atoll Redux_upper",
+ {"Ladders near Weathervane", "Ladder to Swamp", "Ladders in Overworld Town"}),
+ # Furnace entrance, next to the sign that leads to West Garden
+ ("Overworld", "Overworld Redux, Furnace_gyro_west",
+ {"Ladders near Weathervane", "Ladder to Swamp", "Ladders in Overworld Town"}),
+ # Upper West Garden entry, by the belltower
+ ("Overworld", "Overworld Redux, Archipelagos Redux_upper",
+ {"Ladders near Weathervane", "Ladder to Swamp", "Ladders in Overworld Town"}),
+ # Ruined Passage
+ ("Overworld", "Overworld Redux, Ruins Passage_east",
+ {"Ladders near Weathervane", "Ladder to Swamp", "Ladders in Overworld Town"}),
+ # Well rail, west side. Can ls in town, get extra height by going over the portal pad
+ ("Overworld", "Overworld Redux, Sewer_west_aqueduct",
+ {"Ladders near Weathervane", "Ladder to Swamp", "Ladders in Overworld Town", "Ladder to Quarry"}),
+ # Well rail, east side. Need some height from the temple stairs
+ ("Overworld", "Overworld Redux, Furnace_gyro_upper_north",
+ {"Ladders near Weathervane", "Ladder to Swamp", "Ladders in Overworld Town", "Ladder to Quarry"}),
+ # Quarry entry
+ ("Overworld", "Overworld Redux, Darkwoods Tunnel_",
+ {"Ladders near Weathervane", "Ladder to Swamp", "Ladders in Overworld Town", "Ladders in Well"}),
+ # East Forest entry
+ ("Overworld", "Overworld Redux, Forest Belltower_",
+ {"Ladders near Weathervane", "Ladder to Swamp", "Ladders in Overworld Town", "Ladders in Well",
+ "Ladders near Patrol Cave", "Ladder to Quarry", "Ladders near Dark Tomb"}),
+ # Fortress entry
+ ("Overworld", "Overworld Redux, Fortress Courtyard_",
+ {"Ladders near Weathervane", "Ladder to Swamp", "Ladders in Overworld Town", "Ladders in Well",
+ "Ladders near Patrol Cave", "Ladder to Quarry", "Ladders near Dark Tomb"}),
+ # Patrol Cave entry
+ ("Overworld", "Overworld Redux, PatrolCave_",
+ {"Ladders near Weathervane", "Ladder to Swamp", "Ladders in Overworld Town", "Ladders in Well",
+ "Ladders near Overworld Checkpoint", "Ladder to Quarry", "Ladders near Dark Tomb"}),
+ # Special Shop entry, excluded in non-ER due to soft lock potential
+ ("Overworld", "Overworld Redux, ShopSpecial_",
+ {"Ladders near Weathervane", "Ladder to Swamp", "Ladders in Overworld Town", "Ladders in Well",
+ "Ladders near Overworld Checkpoint", "Ladders near Patrol Cave", "Ladder to Quarry",
+ "Ladders near Dark Tomb"}),
+ # Temple Rafters, excluded in non-ER + ladder rando due to soft lock potential
+ ("Overworld", "Overworld Redux, Temple_rafters",
+ {"Ladders near Weathervane", "Ladder to Swamp", "Ladders in Overworld Town", "Ladders in Well",
+ "Ladders near Overworld Checkpoint", "Ladders near Patrol Cave", "Ladder to Quarry",
+ "Ladders near Dark Tomb"}),
+ # Spot above the Quarry entrance,
+ # only gets you to the mountain stairs
+ ("Overworld above Quarry Entrance", "Overworld Redux, Mountain_",
+ {"Ladders near Dark Tomb"}),
+
+ # LS from the Overworld Beach
+ # West Garden entry by the Furnace
+ ("Overworld Beach", "Overworld Redux, Archipelagos Redux_lower",
+ {"Ladders in Overworld Town", "Ladder to Ruined Atoll"}),
+ # West Garden laurels entry
+ ("Overworld Beach", "Overworld Redux, Archipelagos Redux_lowest",
+ {"Ladders in Overworld Town", "Ladder to Ruined Atoll"}),
+ # Swamp lower entrance
+ ("Overworld Beach", "Overworld Redux, Swamp Redux 2_conduit",
+ {"Ladders in Overworld Town", "Ladder to Ruined Atoll"}),
+ # Rotating Lights entrance
+ ("Overworld Beach", "Overworld Redux, Overworld Cave_",
+ {"Ladders in Overworld Town", "Ladder to Ruined Atoll"}),
+ # Swamp upper entrance
+ ("Overworld Beach", "Overworld Redux, Swamp Redux 2_wall",
+ {"Ladder to Ruined Atoll"}),
+ # Furnace entrance, next to the sign that leads to West Garden
+ ("Overworld Beach", "Overworld Redux, Furnace_gyro_west",
+ {"Ladder to Ruined Atoll"}),
+ # Upper West Garden entry, by the belltower
+ ("Overworld Beach", "Overworld Redux, Archipelagos Redux_upper",
+ {"Ladder to Ruined Atoll"}),
+ # Ruined Passage
+ ("Overworld Beach", "Overworld Redux, Ruins Passage_east",
+ {"Ladder to Ruined Atoll"}),
+ # Well rail, west side. Can ls in town, get extra height by going over the portal pad
+ ("Overworld Beach", "Overworld Redux, Sewer_west_aqueduct",
+ {"Ladder to Ruined Atoll"}),
+ # Well rail, east side. Need some height from the temple stairs
+ ("Overworld Beach", "Overworld Redux, Furnace_gyro_upper_north",
+ {"Ladder to Ruined Atoll"}),
+ # Quarry entry
+ ("Overworld Beach", "Overworld Redux, Darkwoods Tunnel_",
+ {"Ladder to Ruined Atoll"}),
+
+ # LS from that low spot where you normally walk to swamp
+ # Only has low ones you can't get to from main Overworld
+ # West Garden main entry from swamp ladder
+ ("Overworld Swamp Lower Entry", "Overworld Redux, Archipelagos Redux_lower",
+ {"Ladder to Swamp"}),
+ # Maze Cave entry from swamp ladder
+ ("Overworld Swamp Lower Entry", "Overworld Redux, Maze Room_",
+ {"Ladder to Swamp"}),
+ # Hourglass Cave entry from swamp ladder
+ ("Overworld Swamp Lower Entry", "Overworld Redux, Town Basement_beach",
+ {"Ladder to Swamp"}),
+ # Lower Atoll entry from swamp ladder
+ ("Overworld Swamp Lower Entry", "Overworld Redux, Atoll Redux_lower",
+ {"Ladder to Swamp"}),
+ # Lowest West Garden entry from swamp ladder
+ ("Overworld Swamp Lower Entry", "Overworld Redux, Archipelagos Redux_lowest",
+ {"Ladder to Swamp"}),
+
+ # from the ladders by the belltower
+ # Ruined Passage
+ ("Overworld to West Garden Upper", "Overworld Redux, Ruins Passage_east",
+ {"Ladders to West Bell"}),
+ # Well rail, west side. Can ls in town, get extra height by going over the portal pad
+ ("Overworld to West Garden Upper", "Overworld Redux, Sewer_west_aqueduct",
+ {"Ladders to West Bell"}),
+ # Well rail, east side. Need some height from the temple stairs
+ ("Overworld to West Garden Upper", "Overworld Redux, Furnace_gyro_upper_north",
+ {"Ladders to West Bell"}),
+ # Quarry entry
+ ("Overworld to West Garden Upper", "Overworld Redux, Darkwoods Tunnel_",
+ {"Ladders to West Bell"}),
+ # East Forest entry
+ ("Overworld to West Garden Upper", "Overworld Redux, Forest Belltower_",
+ {"Ladders to West Bell"}),
+ # Fortress entry
+ ("Overworld to West Garden Upper", "Overworld Redux, Fortress Courtyard_",
+ {"Ladders to West Bell"}),
+ # Patrol Cave entry
+ ("Overworld to West Garden Upper", "Overworld Redux, PatrolCave_",
+ {"Ladders to West Bell"}),
+ # Special Shop entry, excluded in non-ER due to soft lock potential
+ ("Overworld to West Garden Upper", "Overworld Redux, ShopSpecial_",
+ {"Ladders to West Bell"}),
+ # Temple Rafters, excluded in non-ER and ladder rando due to soft lock potential
+ ("Overworld to West Garden Upper", "Overworld Redux, Temple_rafters",
+ {"Ladders to West Bell"}),
+
+ # In the furnace
+ # Furnace ladder to the fuse entrance
+ ("Furnace Ladder Area", "Furnace, Overworld Redux_gyro_upper_north", set()),
+ # Furnace ladder to Dark Tomb
+ ("Furnace Ladder Area", "Furnace, Crypt Redux_", set()),
+ # Furnace ladder to the West Garden connector
+ ("Furnace Ladder Area", "Furnace, Overworld Redux_gyro_west", set()),
+
+ # West Garden
+ # exit after Garden Knight
+ ("West Garden", "Archipelagos Redux, Overworld Redux_upper", set()),
+ # West Garden laurels exit
+ ("West Garden", "Archipelagos Redux, Overworld Redux_lowest", set()),
+
+ # Atoll, use the little ladder you fix at the beginning
+ ("Ruined Atoll", "Atoll Redux, Overworld Redux_lower", set()),
+ ("Ruined Atoll", "Atoll Redux, Frog Stairs_mouth", set()),
+ ("Ruined Atoll", "Atoll Redux, Frog Stairs_eye", set()),
+
+ # East Forest
+ # Entrance by the dancing fox holy cross spot
+ ("East Forest", "East Forest Redux, East Forest Redux Laddercave_upper", set()),
+
+ # From the west side of Guard House 1 to the east side
+ ("Guard House 1 West", "East Forest Redux Laddercave, East Forest Redux_gate", set()),
+ ("Guard House 1 West", "East Forest Redux Laddercave, Forest Boss Room_", set()),
+
+ # Upper exit from the Forest Grave Path, use LS at the ladder by the gate switch
+ ("Forest Grave Path Main", "Sword Access, East Forest Redux_upper", set()),
+
+ # Fortress Exterior
+ # shop, ls at the ladder by the telescope
+ ("Fortress Exterior from Overworld", "Fortress Courtyard, Shop_", set()),
+ # Fortress main entry and grave path lower entry, ls at the ladder by the telescope
+ ("Fortress Exterior from Overworld", "Fortress Courtyard, Fortress Main_Big Door", set()),
+ ("Fortress Exterior from Overworld", "Fortress Courtyard, Fortress Reliquary_Lower", set()),
+ # Upper exits from the courtyard. Use the ramp in the courtyard, then the blocks north of the first fuse
+ ("Fortress Exterior from Overworld", "Fortress Courtyard, Fortress Reliquary_Upper", set()),
+ ("Fortress Exterior from Overworld", "Fortress Courtyard, Fortress East_", set()),
+
+ # same as above, except from the east side of the area
+ ("Fortress Exterior from East Forest", "Fortress Courtyard, Overworld Redux_", set()),
+ ("Fortress Exterior from East Forest", "Fortress Courtyard, Shop_", set()),
+ ("Fortress Exterior from East Forest", "Fortress Courtyard, Fortress Main_Big Door", set()),
+ ("Fortress Exterior from East Forest", "Fortress Courtyard, Fortress Reliquary_Lower", set()),
+ ("Fortress Exterior from East Forest", "Fortress Courtyard, Fortress Reliquary_Upper", set()),
+ ("Fortress Exterior from East Forest", "Fortress Courtyard, Fortress East_", set()),
+
+ # same as above, except from the Beneath the Vault entrance ladder
+ ("Fortress Exterior near cave", "Fortress Courtyard, Overworld Redux_",
+ {"Ladder to Beneath the Vault"}),
+ ("Fortress Exterior near cave", "Fortress Courtyard, Fortress Main_Big Door",
+ {"Ladder to Beneath the Vault"}),
+ ("Fortress Exterior near cave", "Fortress Courtyard, Fortress Reliquary_Lower",
+ {"Ladder to Beneath the Vault"}),
+ ("Fortress Exterior near cave", "Fortress Courtyard, Fortress Reliquary_Upper",
+ {"Ladder to Beneath the Vault"}),
+ ("Fortress Exterior near cave", "Fortress Courtyard, Fortress East_",
+ {"Ladder to Beneath the Vault"}),
+
+ # ls at the ladder, need to gain a little height to get up the stairs
+ # excluded in non-ER due to soft lock potential
+ ("Lower Mountain", "Mountain, Mountaintop_", set()),
+
+ # Where the rope is behind Monastery. Connecting here since, if you have this region, you don't need a sword
+ ("Quarry Monastery Entry", "Quarry Redux, Monastery_back", set()),
+
+ # Swamp to Gauntlet
+ ("Swamp Mid", "Swamp Redux 2, Cathedral Arena_",
+ {"Ladders in Swamp"}),
+ # Swamp to Overworld upper
+ ("Swamp Mid", "Swamp Redux 2, Overworld Redux_wall",
+ {"Ladders in Swamp"}),
+ # Ladder by the hero grave
+ ("Back of Swamp", "Swamp Redux 2, Overworld Redux_conduit", set()),
+ ("Back of Swamp", "Swamp Redux 2, Shop_", set()),
+ # Need to put the cathedral HC code mid-flight
+ ("Back of Swamp", "Swamp Redux 2, Cathedral Redux_secret", set()),
+ ]
+
+ for region_name, scene_dest, ladders in ladder_storages:
+ portal_name, paired_region = get_portal_info(scene_dest)
+ # this is the only exception, requiring holy cross as well
+ if portal_name == "Swamp to Cathedral Secret Legend Room Entrance" and region_name == "Back of Swamp":
+ regions[region_name].connect(
+ regions[paired_region],
+ name=portal_name + " (LS) " + region_name,
+ rule=lambda state: has_stick(state, player)
+ and has_ability(state, player, holy_cross, options, ability_unlocks)
+ and (has_ladder("Ladders in Swamp", state, player, options)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks)
+ or not options.entrance_rando))
+ elif portal_name == "West Garden Exit after Boss" and not options.entrance_rando:
+ regions[region_name].connect(
+ regions[paired_region],
+ name=portal_name + " (LS) " + region_name,
+ rule=lambda state: has_stick(state, player)
+ and state.has_any(ladders, player)
+ and (state.has("Ladders to West Bell", player)))
+ # soft locked unless you have either ladder. if you have laurels, you use the other Entrance
+ elif portal_name in {"Furnace Exit towards West Garden", "Furnace Exit to Dark Tomb"} \
+ and not options.entrance_rando:
+ regions[region_name].connect(
+ regions[paired_region],
+ name=portal_name + " (LS) " + region_name,
+ rule=lambda state: has_stick(state, player)
+ and state.has_any({"Ladder in Dark Tomb", "Ladders to West Bell"}, player))
+ # soft locked for the same reasons as above
+ elif portal_name in {"Entrance to Furnace near West Garden", "West Garden Entrance from Furnace"} \
+ and not options.entrance_rando:
+ regions[region_name].connect(
+ regions[paired_region],
+ name=portal_name + " (LS) " + region_name,
+ rule=lambda state: has_stick(state, player)
+ and state.has_any(ladders, player)
+ and state.has_any({"Ladder in Dark Tomb", "Ladders to West Bell"}, player))
+ # soft locked if you can't get past garden knight backwards or up the belltower ladders
+ elif portal_name == "West Garden Entrance near Belltower" and not options.entrance_rando:
+ regions[region_name].connect(
+ regions[paired_region],
+ name=portal_name + " (LS) " + region_name,
+ rule=lambda state: has_stick(state, player) and state.has_any(ladders, player)
+ and state.has_any({"Ladders to West Bell", laurels}, player))
+ # soft locked if you can't get back out
+ elif portal_name == "Fortress Courtyard to Beneath the Vault" and not options.entrance_rando:
+ regions[region_name].connect(
+ regions[paired_region],
+ name=portal_name + " (LS) " + region_name,
+ rule=lambda state: has_stick(state, player)
+ and state.has("Ladder to Beneath the Vault", player)
+ and has_lantern(state, player, options))
+ elif portal_name == "Atoll Lower Entrance" and not options.entrance_rando:
+ regions[region_name].connect(
+ regions[paired_region],
+ name=portal_name + " (LS) " + region_name,
+ rule=lambda state: has_stick(state, player)
+ and state.has_any(ladders, player)
+ and (state.has_any({"Ladders in Overworld Town", grapple}, player)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks)))
+ elif portal_name == "Atoll Upper Entrance" and not options.entrance_rando:
+ regions[region_name].connect(
+ regions[paired_region],
+ name=portal_name + " (LS) " + region_name,
+ rule=lambda state: has_stick(state, player)
+ and state.has_any(ladders, player)
+ and state.has(grapple, player) or has_ability(state, player, prayer, options, ability_unlocks))
+ # soft lock potential
+ elif portal_name in {"Special Shop Entrance", "Stairs to Top of the Mountain", "Swamp Upper Entrance",
+ "Swamp Lower Entrance", "Caustic Light Cave Entrance"} and not options.entrance_rando:
+ continue
+ # soft lock if you don't have the ladder, I regret writing unrestricted logic
+ elif portal_name == "Temple Rafters Entrance" and not options.entrance_rando:
+ regions[region_name].connect(
+ regions[paired_region],
+ name=portal_name + " (LS) " + region_name,
+ rule=lambda state: has_stick(state, player)
+ and state.has_any(ladders, player)
+ and (state.has("Ladder near Temple Rafters", player)
+ or (state.has_all({laurels, grapple}, player)
+ and ((state.has("Ladders near Patrol Cave", player)
+ and (state.has("Ladders near Dark Tomb", player)
+ or state.has("Ladder to Quarry", player)
+ and (state.has(fire_wand, player) or has_sword(state, player))))
+ or state.has("Ladders near Overworld Checkpoint", player)
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks)))))
+ # if no ladder items are required, just do the basic stick only lambda
+ elif not ladders or not options.shuffle_ladders:
+ regions[region_name].connect(
+ regions[paired_region],
+ name=portal_name + " (LS) " + region_name,
+ rule=lambda state: has_stick(state, player))
+ # one ladder required
+ elif len(ladders) == 1:
+ ladder = ladders.pop()
+ regions[region_name].connect(
+ regions[paired_region],
+ name=portal_name + " (LS) " + region_name,
+ rule=lambda state: has_stick(state, player)
+ and state.has(ladder, player))
+ # if multiple ladders can be used
+ else:
+ regions[region_name].connect(
+ regions[paired_region],
+ name=portal_name + " (LS) " + region_name,
+ rule=lambda state: has_stick(state, player)
+ and state.has_any(ladders, player))
+
+
+def set_er_location_rules(world: "TunicWorld", ability_unlocks: Dict[str, int]) -> None:
+ player = world.player
+ multiworld = world.multiworld
+ options = world.options
+ forbid_item(multiworld.get_location("Secret Gathering Place - 20 Fairy Reward", player), fairies, player)
+
+ # Ability Shuffle Exclusive Rules
+ set_rule(multiworld.get_location("East Forest - Dancing Fox Spirit Holy Cross", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Forest Grave Path - Holy Cross Code by Grave", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("East Forest - Golden Obelisk Holy Cross", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Beneath the Well - [Powered Secret Room] Chest", player),
+ lambda state: state.has("Activate Furnace Fuse", player))
+ set_rule(multiworld.get_location("West Garden - [North] Behind Holy Cross Door", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Library Hall - Holy Cross Chest", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Eastern Vault Fortress - [West Wing] Candles Holy Cross", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("West Garden - [Central Highlands] Holy Cross (Blue Lines)", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Quarry - [Back Entrance] Bushes Holy Cross", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Cathedral - Secret Legend Trophy Chest", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Overworld - [Southwest] Flowers Holy Cross", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Overworld - [East] Weathervane Holy Cross", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Overworld - [Northeast] Flowers Holy Cross", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Overworld - [Southwest] Haiku Holy Cross", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Overworld - [Northwest] Golden Obelisk Page", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+
+ # Overworld
+ set_rule(multiworld.get_location("Overworld - [Southwest] Grapple Chest Over Walkway", player),
+ lambda state: state.has_any({grapple, laurels}, player))
+ set_rule(multiworld.get_location("Overworld - [Southwest] West Beach Guarded By Turret 2", player),
+ lambda state: state.has_any({grapple, laurels}, player))
+ set_rule(multiworld.get_location("Overworld - [Southwest] From West Garden", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Overworld - [Southeast] Page on Pillar by Swamp", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Overworld - [Southwest] Fountain Page", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Overworld - [Northwest] Page on Pillar by Dark Tomb", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Old House - Holy Cross Chest", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Overworld - [East] Grapple Chest", player),
+ lambda state: state.has(grapple, player))
+ set_rule(multiworld.get_location("Sealed Temple - Holy Cross Chest", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Caustic Light Cave - Holy Cross Chest", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Cube Cave - Holy Cross Chest", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Old House - Holy Cross Door Page", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Maze Cave - Maze Room Holy Cross", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Old House - Holy Cross Chest", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Patrol Cave - Holy Cross Chest", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Ruined Passage - Holy Cross Chest", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Hourglass Cave - Holy Cross Chest", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Secret Gathering Place - Holy Cross Chest", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Secret Gathering Place - 10 Fairy Reward", player),
+ lambda state: state.has(fairies, player, 10))
+ set_rule(multiworld.get_location("Secret Gathering Place - 20 Fairy Reward", player),
+ lambda state: state.has(fairies, player, 20))
+ set_rule(multiworld.get_location("Coins in the Well - 3 Coins", player), lambda state: state.has(coins, player, 3))
+ set_rule(multiworld.get_location("Coins in the Well - 6 Coins", player), lambda state: state.has(coins, player, 6))
+ set_rule(multiworld.get_location("Coins in the Well - 10 Coins", player),
+ lambda state: state.has(coins, player, 10))
+ set_rule(multiworld.get_location("Coins in the Well - 15 Coins", player),
+ lambda state: state.has(coins, player, 15))
+
+ # East Forest
+ set_rule(multiworld.get_location("East Forest - Lower Grapple Chest", player),
+ lambda state: state.has(grapple, player))
+ set_rule(multiworld.get_location("East Forest - Lower Dash Chest", player),
+ lambda state: state.has_all({grapple, laurels}, player))
+ set_rule(multiworld.get_location("East Forest - Ice Rod Grapple Chest", player), lambda state: (
+ state.has_all({grapple, ice_dagger, fire_wand}, player) and
+ has_ability(state, player, icebolt, options, ability_unlocks)))
+
+ # West Garden
+ set_rule(multiworld.get_location("West Garden - [North] Across From Page Pickup", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("West Garden - [West] In Flooded Walkway", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("West Garden - [West Lowlands] Tree Holy Cross Chest", player),
+ lambda state: state.has(laurels, player) and has_ability(state, player, holy_cross, options,
+ ability_unlocks))
+ set_rule(multiworld.get_location("West Garden - [East Lowlands] Page Behind Ice Dagger House", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("West Garden - [Central Lowlands] Below Left Walkway", player),
+ lambda state: state.has(laurels, player))
+
+ # Ruined Atoll
+ set_rule(multiworld.get_location("Ruined Atoll - [West] Near Kevin Block", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Ruined Atoll - [East] Locked Room Lower Chest", player),
+ lambda state: state.has_any({laurels, key}, player))
+ set_rule(multiworld.get_location("Ruined Atoll - [East] Locked Room Upper Chest", player),
+ lambda state: state.has_any({laurels, key}, player))
+
+ # Frog's Domain
+ set_rule(multiworld.get_location("Frog's Domain - Side Room Grapple Secret", player),
+ lambda state: state.has_any({grapple, laurels}, player))
+ set_rule(multiworld.get_location("Frog's Domain - Grapple Above Hot Tub", player),
+ lambda state: state.has_any({grapple, laurels}, player))
+ set_rule(multiworld.get_location("Frog's Domain - Escape Chest", player),
+ lambda state: state.has_any({grapple, laurels}, player))
+
+ # Eastern Vault Fortress
+ set_rule(multiworld.get_location("Fortress Arena - Hexagon Red", player),
+ lambda state: state.has(vault_key, player))
+
+ # Beneath the Vault
+ set_rule(multiworld.get_location("Beneath the Fortress - Bridge", player),
+ lambda state: state.has_group("melee weapons", player, 1) or state.has_any({laurels, fire_wand}, player))
+ set_rule(multiworld.get_location("Beneath the Fortress - Obscured Behind Waterfall", player),
+ lambda state: has_lantern(state, player, options))
+
+ # Quarry
+ set_rule(multiworld.get_location("Quarry - [Central] Above Ladder Dash Chest", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Quarry - [West] Upper Area Bombable Wall", player),
+ lambda state: has_mask(state, player, options))
+
+ # Ziggurat
+ set_rule(multiworld.get_location("Rooted Ziggurat Upper - Near Bridge Switch", player),
+ lambda state: has_sword(state, player) or state.has(fire_wand, player))
+ set_rule(multiworld.get_location("Rooted Ziggurat Lower - After Guarded Fuse", player),
+ lambda state: has_sword(state, player) and has_ability(state, player, prayer, options, ability_unlocks))
+
+ # Bosses
+ set_rule(multiworld.get_location("Fortress Arena - Siege Engine/Vault Key Pickup", player),
+ lambda state: has_sword(state, player))
+ # nmg - kill Librarian with a lure, or gun I guess
+ set_rule(multiworld.get_location("Librarian - Hexagon Green", player),
+ lambda state: (has_sword(state, player) or options.logic_rules)
+ and has_ladder("Ladders in Library", state, player, options))
+ # nmg - kill boss scav with orb + firecracker, or similar
+ set_rule(multiworld.get_location("Rooted Ziggurat Lower - Hexagon Blue", player),
+ lambda state: has_sword(state, player) or (state.has(grapple, player) and options.logic_rules))
+
+ # Swamp
+ set_rule(multiworld.get_location("Cathedral Gauntlet - Gauntlet Reward", player),
+ lambda state: state.has(fire_wand, player) and has_sword(state, player))
+ set_rule(multiworld.get_location("Swamp - [Entrance] Above Entryway", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Swamp - [South Graveyard] Upper Walkway Dash Chest", player),
+ lambda state: state.has(laurels, player))
+ # these two swamp checks really want you to kill the big skeleton first
+ set_rule(multiworld.get_location("Swamp - [South Graveyard] 4 Orange Skulls", player),
+ lambda state: has_sword(state, player))
+
+ # Hero's Grave and Far Shore
+ set_rule(multiworld.get_location("Hero's Grave - Tooth Relic", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Hero's Grave - Mushroom Relic", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Hero's Grave - Ash Relic", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Hero's Grave - Flowers Relic", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Hero's Grave - Effigy Relic", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Hero's Grave - Feathers Relic", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Far Shore - Secret Chest", player),
+ lambda state: state.has(laurels, player))
+
+ # Events
+ set_rule(multiworld.get_location("Eastern Bell", player),
+ lambda state: (has_stick(state, player) or state.has(fire_wand, player)))
+ set_rule(multiworld.get_location("Western Bell", player),
+ lambda state: (has_stick(state, player) or state.has(fire_wand, player)))
+ set_rule(multiworld.get_location("Furnace Fuse", player),
+ lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ set_rule(multiworld.get_location("South and West Fortress Exterior Fuses", player),
+ lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ set_rule(multiworld.get_location("Upper and Central Fortress Exterior Fuses", player),
+ lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ set_rule(multiworld.get_location("Beneath the Vault Fuse", player),
+ lambda state: state.has("Activate South and West Fortress Exterior Fuses", player))
+ set_rule(multiworld.get_location("Eastern Vault West Fuses", player),
+ lambda state: state.has("Activate Beneath the Vault Fuse", player))
+ set_rule(multiworld.get_location("Eastern Vault East Fuse", player),
+ lambda state: state.has_all({"Activate Upper and Central Fortress Exterior Fuses",
+ "Activate South and West Fortress Exterior Fuses"}, player))
+ set_rule(multiworld.get_location("Quarry Connector Fuse", player),
+ lambda state: has_ability(state, player, prayer, options, ability_unlocks) and state.has(grapple, player))
+ set_rule(multiworld.get_location("Quarry Fuse", player),
+ lambda state: state.has("Activate Quarry Connector Fuse", player))
+ set_rule(multiworld.get_location("Ziggurat Fuse", player),
+ lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ set_rule(multiworld.get_location("West Garden Fuse", player),
+ lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ set_rule(multiworld.get_location("Library Fuse", player),
+ lambda state: has_ability(state, player, prayer, options, ability_unlocks))
diff --git a/worlds/tunic/er_scripts.py b/worlds/tunic/er_scripts.py
new file mode 100644
index 000000000000..5d08188ace6e
--- /dev/null
+++ b/worlds/tunic/er_scripts.py
@@ -0,0 +1,535 @@
+from typing import Dict, List, Set, TYPE_CHECKING
+from BaseClasses import Region, ItemClassification, Item, Location
+from .locations import location_table
+from .er_data import Portal, tunic_er_regions, portal_mapping, \
+ dependent_regions_restricted, dependent_regions_nmg, dependent_regions_ur
+from .er_rules import set_er_region_rules
+from worlds.generic import PlandoConnection
+from random import Random
+
+if TYPE_CHECKING:
+ from . import TunicWorld
+
+
+class TunicERItem(Item):
+ game: str = "TUNIC"
+
+
+class TunicERLocation(Location):
+ game: str = "TUNIC"
+
+
+def create_er_regions(world: "TunicWorld") -> Dict[Portal, Portal]:
+ regions: Dict[str, Region] = {}
+ if world.options.entrance_rando:
+ portal_pairs: Dict[Portal, Portal] = pair_portals(world)
+
+ # output the entrances to the spoiler log here for convenience
+ for portal1, portal2 in portal_pairs.items():
+ world.multiworld.spoiler.set_entrance(portal1.name, portal2.name, "both", world.player)
+ else:
+ portal_pairs: Dict[Portal, Portal] = vanilla_portals()
+
+ for region_name, region_data in tunic_er_regions.items():
+ regions[region_name] = Region(region_name, world.player, world.multiworld)
+
+ set_er_region_rules(world, world.ability_unlocks, regions, portal_pairs)
+
+ for location_name, location_id in world.location_name_to_id.items():
+ region = regions[location_table[location_name].er_region]
+ location = TunicERLocation(world.player, location_name, location_id, region)
+ region.locations.append(location)
+
+ create_randomized_entrances(portal_pairs, regions)
+
+ for region in regions.values():
+ world.multiworld.regions.append(region)
+
+ place_event_items(world, regions)
+
+ victory_region = regions["Spirit Arena Victory"]
+ victory_location = TunicERLocation(world.player, "The Heir", None, victory_region)
+ victory_location.place_locked_item(TunicERItem("Victory", ItemClassification.progression, None, world.player))
+ world.multiworld.completion_condition[world.player] = lambda state: state.has("Victory", world.player)
+ victory_region.locations.append(victory_location)
+
+ return portal_pairs
+
+
+tunic_events: Dict[str, str] = {
+ "Eastern Bell": "Forest Belltower Upper",
+ "Western Bell": "Overworld Belltower at Bell",
+ "Furnace Fuse": "Furnace Fuse",
+ "South and West Fortress Exterior Fuses": "Fortress Exterior from Overworld",
+ "Upper and Central Fortress Exterior Fuses": "Fortress Courtyard Upper",
+ "Beneath the Vault Fuse": "Beneath the Vault Back",
+ "Eastern Vault West Fuses": "Eastern Vault Fortress",
+ "Eastern Vault East Fuse": "Eastern Vault Fortress",
+ "Quarry Connector Fuse": "Quarry Connector",
+ "Quarry Fuse": "Quarry",
+ "Ziggurat Fuse": "Rooted Ziggurat Lower Back",
+ "West Garden Fuse": "West Garden",
+ "Library Fuse": "Library Lab"
+}
+
+
+def place_event_items(world: "TunicWorld", regions: Dict[str, Region]) -> None:
+ for event_name, region_name in tunic_events.items():
+ region = regions[region_name]
+ location = TunicERLocation(world.player, event_name, None, region)
+ if event_name.endswith("Bell"):
+ location.place_locked_item(
+ TunicERItem("Ring " + event_name, ItemClassification.progression, None, world.player))
+ else:
+ location.place_locked_item(
+ TunicERItem("Activate " + event_name, ItemClassification.progression, None, world.player))
+ region.locations.append(location)
+
+
+def vanilla_portals() -> Dict[Portal, Portal]:
+ portal_pairs: Dict[Portal, Portal] = {}
+ portal_map = portal_mapping.copy()
+
+ while portal_map:
+ portal1 = portal_map[0]
+ portal2 = None
+ # portal2 scene destination tag is portal1's destination scene tag
+ portal2_sdt = portal1.destination_scene()
+
+ if portal2_sdt.startswith("Shop,"):
+ portal2 = Portal(name="Shop", region="Shop",
+ destination="Previous Region", tag="_")
+
+ elif portal2_sdt == "Purgatory, Purgatory_bottom":
+ portal2_sdt = "Purgatory, Purgatory_top"
+
+ for portal in portal_map:
+ if portal.scene_destination() == portal2_sdt:
+ portal2 = portal
+ break
+
+ portal_pairs[portal1] = portal2
+ portal_map.remove(portal1)
+ if not portal2_sdt.startswith("Shop,"):
+ portal_map.remove(portal2)
+
+ return portal_pairs
+
+
+# pairing off portals, starting with dead ends
+def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
+ # separate the portals into dead ends and non-dead ends
+ portal_pairs: Dict[Portal, Portal] = {}
+ dead_ends: List[Portal] = []
+ two_plus: List[Portal] = []
+ logic_rules = world.options.logic_rules.value
+ player_name = world.multiworld.get_player_name(world.player)
+
+ shop_scenes: Set[str] = set()
+ shop_count = 6
+ if world.options.fixed_shop.value:
+ shop_count = 1
+ shop_scenes.add("Overworld Redux")
+
+ if not logic_rules:
+ dependent_regions = dependent_regions_restricted
+ elif logic_rules == 1:
+ dependent_regions = dependent_regions_nmg
+ else:
+ dependent_regions = dependent_regions_ur
+
+ # create separate lists for dead ends and non-dead ends
+ if logic_rules:
+ for portal in portal_mapping:
+ if tunic_er_regions[portal.region].dead_end == 1:
+ dead_ends.append(portal)
+ else:
+ two_plus.append(portal)
+ else:
+ for portal in portal_mapping:
+ if tunic_er_regions[portal.region].dead_end:
+ dead_ends.append(portal)
+ else:
+ two_plus.append(portal)
+
+ connected_regions: Set[str] = set()
+ # make better start region stuff when/if implementing random start
+ start_region = "Overworld"
+ connected_regions.update(add_dependent_regions(start_region, logic_rules))
+
+ plando_connections = world.multiworld.plando_connections[world.player]
+
+ # universal tracker support stuff, don't need to care about region dependency
+ if hasattr(world.multiworld, "re_gen_passthrough"):
+ if "TUNIC" in world.multiworld.re_gen_passthrough:
+ plando_connections.clear()
+ # universal tracker stuff, won't do anything in normal gen
+ for portal1, portal2 in world.multiworld.re_gen_passthrough["TUNIC"]["Entrance Rando"].items():
+ portal_name1 = ""
+ portal_name2 = ""
+
+ for portal in portal_mapping:
+ if portal.scene_destination() == portal1:
+ portal_name1 = portal.name
+ # connected_regions.update(add_dependent_regions(portal.region, logic_rules))
+ if portal.scene_destination() == portal2:
+ portal_name2 = portal.name
+ # connected_regions.update(add_dependent_regions(portal.region, logic_rules))
+ # shops have special handling
+ if not portal_name2 and portal2 == "Shop, Previous Region_":
+ portal_name2 = "Shop Portal"
+ plando_connections.append(PlandoConnection(portal_name1, portal_name2, "both"))
+
+ non_dead_end_regions = set()
+ for region_name, region_info in tunic_er_regions.items():
+ if not region_info.dead_end:
+ non_dead_end_regions.add(region_name)
+ elif region_info.dead_end == 2 and logic_rules:
+ non_dead_end_regions.add(region_name)
+
+ if plando_connections:
+ for connection in plando_connections:
+ p_entrance = connection.entrance
+ p_exit = connection.exit
+
+ if p_entrance.startswith("Shop"):
+ p_entrance = p_exit
+ p_exit = "Shop Portal"
+
+ portal1 = None
+ portal2 = None
+
+ # search two_plus for both at once
+ for portal in two_plus:
+ if p_entrance == portal.name:
+ portal1 = portal
+ if p_exit == portal.name:
+ portal2 = portal
+
+ # search dead_ends individually since we can't really remove items from two_plus during the loop
+ if not portal1:
+ for portal in dead_ends:
+ if p_entrance == portal.name:
+ portal1 = portal
+ break
+ if not portal1:
+ raise Exception(f"Could not find entrance named {p_entrance} for "
+ f"plando connections in {player_name}'s YAML.")
+ dead_ends.remove(portal1)
+ else:
+ two_plus.remove(portal1)
+
+ if not portal2:
+ for portal in dead_ends:
+ if p_exit == portal.name:
+ portal2 = portal
+ break
+ if p_exit in ["Shop Portal", "Shop"]:
+ portal2 = Portal(name="Shop Portal", region=f"Shop",
+ destination="Previous Region", tag="_")
+ shop_count -= 1
+ if shop_count < 0:
+ shop_count += 2
+ for p in portal_mapping:
+ if p.name == p_entrance:
+ shop_scenes.add(p.scene())
+ break
+ else:
+ if not portal2:
+ raise Exception(f"Could not find entrance named {p_exit} for "
+ f"plando connections in {player_name}'s YAML.")
+ dead_ends.remove(portal2)
+ else:
+ two_plus.remove(portal2)
+
+ portal_pairs[portal1] = portal2
+
+ # update dependent regions based on the plando'd connections, to ensure the portals connect well, logically
+ for origins, destinations in dependent_regions.items():
+ if portal1.region in origins:
+ if portal2.region in non_dead_end_regions:
+ destinations.append(portal2.region)
+ if portal2.region in origins:
+ if portal1.region in non_dead_end_regions:
+ destinations.append(portal1.region)
+
+ # if we have plando connections, our connected regions may change somewhat
+ while True:
+ test1 = len(connected_regions)
+ for region in connected_regions.copy():
+ connected_regions.update(add_dependent_regions(region, logic_rules))
+ test2 = len(connected_regions)
+ if test1 == test2:
+ break
+
+ # need to plando fairy cave, or it could end up laurels locked
+ # fix this later to be random after adding some item logic to dependent regions
+ if world.options.laurels_location == "10_fairies" and not hasattr(world.multiworld, "re_gen_passthrough"):
+ portal1 = None
+ portal2 = None
+ for portal in two_plus:
+ if portal.scene_destination() == "Overworld Redux, Waterfall_":
+ portal1 = portal
+ break
+ for portal in dead_ends:
+ if portal.scene_destination() == "Waterfall, Overworld Redux_":
+ portal2 = portal
+ break
+ if not portal1:
+ raise Exception(f"Failed to do Laurels Location at 10 Fairies option. "
+ f"Did {player_name} plando connection the Secret Gathering Place Entrance?")
+ if not portal2:
+ raise Exception(f"Failed to do Laurels Location at 10 Fairies option. "
+ f"Did {player_name} plando connection the Secret Gathering Place Exit?")
+ portal_pairs[portal1] = portal2
+ two_plus.remove(portal1)
+ dead_ends.remove(portal2)
+
+ if world.options.fixed_shop and not hasattr(world.multiworld, "re_gen_passthrough"):
+ portal1 = None
+ for portal in two_plus:
+ if portal.scene_destination() == "Overworld Redux, Windmill_":
+ portal1 = portal
+ break
+ if not portal1:
+ raise Exception(f"Failed to do Fixed Shop option. "
+ f"Did {player_name} plando connection the Windmill Shop entrance?")
+
+ portal2 = Portal(name="Shop Portal", region="Shop", destination="Previous Region", tag="_")
+
+ portal_pairs[portal1] = portal2
+ two_plus.remove(portal1)
+
+ random_object: Random = world.random
+ if world.options.entrance_rando.value != 1:
+ random_object = Random(world.options.entrance_rando.value)
+ # we want to start by making sure every region is accessible
+ random_object.shuffle(two_plus)
+ check_success = 0
+ portal1 = None
+ portal2 = None
+ previous_conn_num = 0
+ fail_count = 0
+ while len(connected_regions) < len(non_dead_end_regions):
+ # if the connected regions length stays unchanged for too long, it's stuck in a loop
+ # should, hopefully, only ever occur if someone plandos connections poorly
+ if hasattr(world.multiworld, "re_gen_passthrough"):
+ break
+ if previous_conn_num == len(connected_regions):
+ fail_count += 1
+ if fail_count >= 500:
+ raise Exception(f"Failed to pair regions. Check plando connections for {player_name} for loops.")
+ else:
+ fail_count = 0
+ previous_conn_num = len(connected_regions)
+
+ # find a portal in an inaccessible region
+ if check_success == 0:
+ for portal in two_plus:
+ if portal.region in connected_regions:
+ # if there's risk of self-locking, start over
+ if gate_before_switch(portal, two_plus):
+ random_object.shuffle(two_plus)
+ break
+ portal1 = portal
+ two_plus.remove(portal)
+ check_success = 1
+ break
+
+ # then we find a portal in a connected region
+ if check_success == 1:
+ for portal in two_plus:
+ if portal.region not in connected_regions:
+ # if there's risk of self-locking, shuffle and try again
+ if gate_before_switch(portal, two_plus):
+ random_object.shuffle(two_plus)
+ break
+ portal2 = portal
+ two_plus.remove(portal)
+ check_success = 2
+ break
+
+ # once we have both portals, connect them and add the new region(s) to connected_regions
+ if check_success == 2:
+ connected_regions.update(add_dependent_regions(portal2.region, logic_rules))
+ portal_pairs[portal1] = portal2
+ check_success = 0
+ random_object.shuffle(two_plus)
+
+ # for universal tracker, we want to skip shop gen
+ if hasattr(world.multiworld, "re_gen_passthrough"):
+ if "TUNIC" in world.multiworld.re_gen_passthrough:
+ shop_count = 0
+
+ for i in range(shop_count):
+ portal1 = None
+ for portal in two_plus:
+ if portal.scene() not in shop_scenes:
+ shop_scenes.add(portal.scene())
+ portal1 = portal
+ two_plus.remove(portal)
+ break
+ if portal1 is None:
+ raise Exception("Too many shops in the pool, or something else went wrong.")
+ portal2 = Portal(name="Shop Portal", region="Shop", destination="Previous Region", tag="_")
+
+ portal_pairs[portal1] = portal2
+
+ # connect dead ends to random non-dead ends
+ # none of the key events are in dead ends, so we don't need to do gate_before_switch
+ while len(dead_ends) > 0:
+ if hasattr(world.multiworld, "re_gen_passthrough"):
+ break
+ portal1 = two_plus.pop()
+ portal2 = dead_ends.pop()
+ portal_pairs[portal1] = portal2
+
+ # then randomly connect the remaining portals to each other
+ # every region is accessible, so gate_before_switch is not necessary
+ while len(two_plus) > 1:
+ if hasattr(world.multiworld, "re_gen_passthrough"):
+ break
+ portal1 = two_plus.pop()
+ portal2 = two_plus.pop()
+ portal_pairs[portal1] = portal2
+
+ if len(two_plus) == 1:
+ raise Exception("two plus had an odd number of portals, investigate this. last portal is " + two_plus[0].name)
+
+ return portal_pairs
+
+
+# loop through our list of paired portals and make two-way connections
+def create_randomized_entrances(portal_pairs: Dict[Portal, Portal], regions: Dict[str, Region]) -> None:
+ for portal1, portal2 in portal_pairs.items():
+ region1 = regions[portal1.region]
+ region2 = regions[portal2.region]
+ region1.connect(connecting_region=region2, name=portal1.name)
+ # prevent the logic from thinking you can get to any shop-connected region from the shop
+ if portal2.name not in {"Shop", "Shop Portal"}:
+ region2.connect(connecting_region=region1, name=portal2.name)
+
+
+# loop through the static connections, return regions you can reach from this region
+# todo: refactor to take region_name and dependent_regions
+def add_dependent_regions(region_name: str, logic_rules: int) -> Set[str]:
+ region_set = set()
+ if not logic_rules:
+ regions_to_add = dependent_regions_restricted
+ elif logic_rules == 1:
+ regions_to_add = dependent_regions_nmg
+ else:
+ regions_to_add = dependent_regions_ur
+ for origin_regions, destination_regions in regions_to_add.items():
+ if region_name in origin_regions:
+ # if you matched something in the first set, you get the regions in its paired set
+ region_set.update(destination_regions)
+ return region_set
+ # if you didn't match anything in the first sets, just gives you the region
+ region_set = {region_name}
+ return region_set
+
+
+# we're checking if an event-locked portal is being placed before the regions where its key(s) is/are
+# doing this ensures the keys will not be locked behind the event-locked portal
+def gate_before_switch(check_portal: Portal, two_plus: List[Portal]) -> bool:
+ # the western belltower cannot be locked since you can access it with laurels
+ # so we only need to make sure the forest belltower isn't locked
+ if check_portal.scene_destination() == "Overworld Redux, Temple_main":
+ i = 0
+ for portal in two_plus:
+ if portal.region == "Forest Belltower Upper":
+ i += 1
+ break
+ if i == 1:
+ return True
+
+ # fortress big gold door needs 2 scenes and one of the two upper portals of the courtyard
+ elif check_portal.scene_destination() == "Fortress Main, Fortress Arena_":
+ i = j = k = 0
+ for portal in two_plus:
+ if portal.region == "Fortress Courtyard Upper":
+ i += 1
+ if portal.scene() == "Fortress Basement":
+ j += 1
+ if portal.region == "Eastern Vault Fortress":
+ k += 1
+ if i == 2 or j == 2 or k == 5:
+ return True
+
+ # fortress teleporter needs only the left fuses
+ elif check_portal.scene_destination() in {"Fortress Arena, Transit_teleporter_spidertank",
+ "Transit, Fortress Arena_teleporter_spidertank"}:
+ i = j = k = 0
+ for portal in two_plus:
+ if portal.scene() == "Fortress Courtyard":
+ i += 1
+ if portal.scene() == "Fortress Basement":
+ j += 1
+ if portal.region == "Eastern Vault Fortress":
+ k += 1
+ if i == 8 or j == 2 or k == 5:
+ return True
+
+ # Cathedral door needs Overworld and the front of Swamp
+ # Overworld is currently guaranteed, so no need to check it
+ elif check_portal.scene_destination() == "Swamp Redux 2, Cathedral Redux_main":
+ i = 0
+ for portal in two_plus:
+ if portal.region in {"Swamp Front", "Swamp to Cathedral Treasure Room",
+ "Swamp to Cathedral Main Entrance Region"}:
+ i += 1
+ if i == 4:
+ return True
+
+ # Zig portal room exit needs Zig 3 to be accessible to hit the fuse
+ elif check_portal.scene_destination() == "ziggurat2020_FTRoom, ziggurat2020_3_":
+ i = 0
+ for portal in two_plus:
+ if portal.scene() == "ziggurat2020_3":
+ i += 1
+ if i == 2:
+ return True
+
+ # Quarry teleporter needs you to hit the Darkwoods fuse
+ # Since it's physically in Quarry, we don't need to check for it
+ elif check_portal.scene_destination() in {"Quarry Redux, Transit_teleporter_quarry teleporter",
+ "Quarry Redux, ziggurat2020_0_"}:
+ i = 0
+ for portal in two_plus:
+ if portal.scene() == "Darkwoods Tunnel":
+ i += 1
+ if i == 2:
+ return True
+
+ # Same as above, but Quarry isn't guaranteed here
+ elif check_portal.scene_destination() == "Transit, Quarry Redux_teleporter_quarry teleporter":
+ i = j = 0
+ for portal in two_plus:
+ if portal.scene() == "Darkwoods Tunnel":
+ i += 1
+ if portal.scene() == "Quarry Redux":
+ j += 1
+ if i == 2 or j == 7:
+ return True
+
+ # Need Library fuse to use this teleporter
+ elif check_portal.scene_destination() == "Transit, Library Lab_teleporter_library teleporter":
+ i = 0
+ for portal in two_plus:
+ if portal.scene() == "Library Lab":
+ i += 1
+ if i == 3:
+ return True
+
+ # Need West Garden fuse to use this teleporter
+ elif check_portal.scene_destination() == "Transit, Archipelagos Redux_teleporter_archipelagos_teleporter":
+ i = 0
+ for portal in two_plus:
+ if portal.scene() == "Archipelagos Redux":
+ i += 1
+ if i == 6:
+ return True
+
+ # false means you're good to place the portal
+ return False
diff --git a/worlds/tunic/items.py b/worlds/tunic/items.py
new file mode 100644
index 000000000000..7483d55bf1cc
--- /dev/null
+++ b/worlds/tunic/items.py
@@ -0,0 +1,239 @@
+from itertools import groupby
+from typing import Dict, List, Set, NamedTuple
+from BaseClasses import ItemClassification
+
+
+class TunicItemData(NamedTuple):
+ classification: ItemClassification
+ quantity_in_item_pool: int
+ item_id_offset: int
+ item_group: str = ""
+
+
+item_base_id = 509342400
+
+item_table: Dict[str, TunicItemData] = {
+ "Firecracker x2": TunicItemData(ItemClassification.filler, 3, 0, "bombs"),
+ "Firecracker x3": TunicItemData(ItemClassification.filler, 3, 1, "bombs"),
+ "Firecracker x4": TunicItemData(ItemClassification.filler, 3, 2, "bombs"),
+ "Firecracker x5": TunicItemData(ItemClassification.filler, 1, 3, "bombs"),
+ "Firecracker x6": TunicItemData(ItemClassification.filler, 2, 4, "bombs"),
+ "Fire Bomb x2": TunicItemData(ItemClassification.filler, 2, 5, "bombs"),
+ "Fire Bomb x3": TunicItemData(ItemClassification.filler, 1, 6, "bombs"),
+ "Ice Bomb x2": TunicItemData(ItemClassification.filler, 2, 7, "bombs"),
+ "Ice Bomb x3": TunicItemData(ItemClassification.filler, 2, 8, "bombs"),
+ "Ice Bomb x5": TunicItemData(ItemClassification.filler, 1, 9, "bombs"),
+ "Lure": TunicItemData(ItemClassification.filler, 4, 10, "consumables"),
+ "Lure x2": TunicItemData(ItemClassification.filler, 1, 11, "consumables"),
+ "Pepper x2": TunicItemData(ItemClassification.filler, 4, 12, "consumables"),
+ "Ivy x3": TunicItemData(ItemClassification.filler, 2, 13, "consumables"),
+ "Effigy": TunicItemData(ItemClassification.useful, 12, 14, "money"),
+ "HP Berry": TunicItemData(ItemClassification.filler, 2, 15, "consumables"),
+ "HP Berry x2": TunicItemData(ItemClassification.filler, 4, 16, "consumables"),
+ "HP Berry x3": TunicItemData(ItemClassification.filler, 2, 17, "consumables"),
+ "MP Berry": TunicItemData(ItemClassification.filler, 4, 18, "consumables"),
+ "MP Berry x2": TunicItemData(ItemClassification.filler, 2, 19, "consumables"),
+ "MP Berry x3": TunicItemData(ItemClassification.filler, 7, 20, "consumables"),
+ "Fairy": TunicItemData(ItemClassification.progression, 20, 21),
+ "Stick": TunicItemData(ItemClassification.progression, 1, 22, "weapons"),
+ "Sword": TunicItemData(ItemClassification.progression, 3, 23, "weapons"),
+ "Sword Upgrade": TunicItemData(ItemClassification.progression, 4, 24, "weapons"),
+ "Magic Wand": TunicItemData(ItemClassification.progression, 1, 25, "weapons"),
+ "Magic Dagger": TunicItemData(ItemClassification.progression, 1, 26),
+ "Magic Orb": TunicItemData(ItemClassification.progression, 1, 27),
+ "Hero's Laurels": TunicItemData(ItemClassification.progression, 1, 28),
+ "Lantern": TunicItemData(ItemClassification.progression, 1, 29),
+ "Gun": TunicItemData(ItemClassification.useful, 1, 30, "weapons"),
+ "Shield": TunicItemData(ItemClassification.useful, 1, 31),
+ "Dath Stone": TunicItemData(ItemClassification.useful, 1, 32),
+ "Hourglass": TunicItemData(ItemClassification.useful, 1, 33),
+ "Old House Key": TunicItemData(ItemClassification.progression, 1, 34, "keys"),
+ "Key": TunicItemData(ItemClassification.progression, 2, 35, "keys"),
+ "Fortress Vault Key": TunicItemData(ItemClassification.progression, 1, 36, "keys"),
+ "Flask Shard": TunicItemData(ItemClassification.useful, 12, 37, "potions"),
+ "Potion Flask": TunicItemData(ItemClassification.useful, 5, 38, "potions"),
+ "Golden Coin": TunicItemData(ItemClassification.progression, 17, 39),
+ "Card Slot": TunicItemData(ItemClassification.useful, 4, 40),
+ "Red Questagon": TunicItemData(ItemClassification.progression_skip_balancing, 1, 41, "hexagons"),
+ "Green Questagon": TunicItemData(ItemClassification.progression_skip_balancing, 1, 42, "hexagons"),
+ "Blue Questagon": TunicItemData(ItemClassification.progression_skip_balancing, 1, 43, "hexagons"),
+ "Gold Questagon": TunicItemData(ItemClassification.progression_skip_balancing, 0, 44, "hexagons"),
+ "ATT Offering": TunicItemData(ItemClassification.useful, 4, 45, "offerings"),
+ "DEF Offering": TunicItemData(ItemClassification.useful, 4, 46, "offerings"),
+ "Potion Offering": TunicItemData(ItemClassification.useful, 3, 47, "offerings"),
+ "HP Offering": TunicItemData(ItemClassification.useful, 6, 48, "offerings"),
+ "MP Offering": TunicItemData(ItemClassification.useful, 3, 49, "offerings"),
+ "SP Offering": TunicItemData(ItemClassification.useful, 2, 50, "offerings"),
+ "Hero Relic - ATT": TunicItemData(ItemClassification.useful, 1, 51, "hero relics"),
+ "Hero Relic - DEF": TunicItemData(ItemClassification.useful, 1, 52, "hero relics"),
+ "Hero Relic - HP": TunicItemData(ItemClassification.useful, 1, 53, "hero relics"),
+ "Hero Relic - MP": TunicItemData(ItemClassification.useful, 1, 54, "hero relics"),
+ "Hero Relic - POTION": TunicItemData(ItemClassification.useful, 1, 55, "hero relics"),
+ "Hero Relic - SP": TunicItemData(ItemClassification.useful, 1, 56, "hero relics"),
+ "Orange Peril Ring": TunicItemData(ItemClassification.useful, 1, 57, "cards"),
+ "Tincture": TunicItemData(ItemClassification.useful, 1, 58, "cards"),
+ "Scavenger Mask": TunicItemData(ItemClassification.progression, 1, 59, "cards"),
+ "Cyan Peril Ring": TunicItemData(ItemClassification.useful, 1, 60, "cards"),
+ "Bracer": TunicItemData(ItemClassification.useful, 1, 61, "cards"),
+ "Dagger Strap": TunicItemData(ItemClassification.useful, 1, 62, "cards"),
+ "Inverted Ash": TunicItemData(ItemClassification.useful, 1, 63, "cards"),
+ "Lucky Cup": TunicItemData(ItemClassification.useful, 1, 64, "cards"),
+ "Magic Echo": TunicItemData(ItemClassification.useful, 1, 65, "cards"),
+ "Anklet": TunicItemData(ItemClassification.useful, 1, 66, "cards"),
+ "Muffling Bell": TunicItemData(ItemClassification.useful, 1, 67, "cards"),
+ "Glass Cannon": TunicItemData(ItemClassification.useful, 1, 68, "cards"),
+ "Perfume": TunicItemData(ItemClassification.useful, 1, 69, "cards"),
+ "Louder Echo": TunicItemData(ItemClassification.useful, 1, 70, "cards"),
+ "Aura's Gem": TunicItemData(ItemClassification.useful, 1, 71, "cards"),
+ "Bone Card": TunicItemData(ItemClassification.useful, 1, 72, "cards"),
+ "Mr Mayor": TunicItemData(ItemClassification.useful, 1, 73, "golden treasures"),
+ "Secret Legend": TunicItemData(ItemClassification.useful, 1, 74, "golden treasures"),
+ "Sacred Geometry": TunicItemData(ItemClassification.useful, 1, 75, "golden treasures"),
+ "Vintage": TunicItemData(ItemClassification.useful, 1, 76, "golden treasures"),
+ "Just Some Pals": TunicItemData(ItemClassification.useful, 1, 77, "golden treasures"),
+ "Regal Weasel": TunicItemData(ItemClassification.useful, 1, 78, "golden treasures"),
+ "Spring Falls": TunicItemData(ItemClassification.useful, 1, 79, "golden treasures"),
+ "Power Up": TunicItemData(ItemClassification.useful, 1, 80, "golden treasures"),
+ "Back To Work": TunicItemData(ItemClassification.useful, 1, 81, "golden treasures"),
+ "Phonomath": TunicItemData(ItemClassification.useful, 1, 82, "golden treasures"),
+ "Dusty": TunicItemData(ItemClassification.useful, 1, 83, "golden treasures"),
+ "Forever Friend": TunicItemData(ItemClassification.useful, 1, 84, "golden treasures"),
+ "Fool Trap": TunicItemData(ItemClassification.trap, 0, 85, "fool"),
+ "Money x1": TunicItemData(ItemClassification.filler, 3, 86, "money"),
+ "Money x10": TunicItemData(ItemClassification.filler, 1, 87, "money"),
+ "Money x15": TunicItemData(ItemClassification.filler, 10, 88, "money"),
+ "Money x16": TunicItemData(ItemClassification.filler, 1, 89, "money"),
+ "Money x20": TunicItemData(ItemClassification.filler, 17, 90, "money"),
+ "Money x25": TunicItemData(ItemClassification.filler, 14, 91, "money"),
+ "Money x30": TunicItemData(ItemClassification.filler, 4, 92, "money"),
+ "Money x32": TunicItemData(ItemClassification.filler, 4, 93, "money"),
+ "Money x40": TunicItemData(ItemClassification.filler, 3, 94, "money"),
+ "Money x48": TunicItemData(ItemClassification.filler, 1, 95, "money"),
+ "Money x50": TunicItemData(ItemClassification.filler, 7, 96, "money"),
+ "Money x64": TunicItemData(ItemClassification.filler, 1, 97, "money"),
+ "Money x100": TunicItemData(ItemClassification.filler, 5, 98, "money"),
+ "Money x128": TunicItemData(ItemClassification.useful, 3, 99, "money"),
+ "Money x200": TunicItemData(ItemClassification.useful, 1, 100, "money"),
+ "Money x255": TunicItemData(ItemClassification.useful, 1, 101, "money"),
+ "Pages 0-1": TunicItemData(ItemClassification.useful, 1, 102, "pages"),
+ "Pages 2-3": TunicItemData(ItemClassification.useful, 1, 103, "pages"),
+ "Pages 4-5": TunicItemData(ItemClassification.useful, 1, 104, "pages"),
+ "Pages 6-7": TunicItemData(ItemClassification.useful, 1, 105, "pages"),
+ "Pages 8-9": TunicItemData(ItemClassification.useful, 1, 106, "pages"),
+ "Pages 10-11": TunicItemData(ItemClassification.useful, 1, 107, "pages"),
+ "Pages 12-13": TunicItemData(ItemClassification.useful, 1, 108, "pages"),
+ "Pages 14-15": TunicItemData(ItemClassification.useful, 1, 109, "pages"),
+ "Pages 16-17": TunicItemData(ItemClassification.useful, 1, 110, "pages"),
+ "Pages 18-19": TunicItemData(ItemClassification.useful, 1, 111, "pages"),
+ "Pages 20-21": TunicItemData(ItemClassification.useful, 1, 112, "pages"),
+ "Pages 22-23": TunicItemData(ItemClassification.useful, 1, 113, "pages"),
+ "Pages 24-25 (Prayer)": TunicItemData(ItemClassification.progression, 1, 114, "pages"),
+ "Pages 26-27": TunicItemData(ItemClassification.useful, 1, 115, "pages"),
+ "Pages 28-29": TunicItemData(ItemClassification.useful, 1, 116, "pages"),
+ "Pages 30-31": TunicItemData(ItemClassification.useful, 1, 117, "pages"),
+ "Pages 32-33": TunicItemData(ItemClassification.useful, 1, 118, "pages"),
+ "Pages 34-35": TunicItemData(ItemClassification.useful, 1, 119, "pages"),
+ "Pages 36-37": TunicItemData(ItemClassification.useful, 1, 120, "pages"),
+ "Pages 38-39": TunicItemData(ItemClassification.useful, 1, 121, "pages"),
+ "Pages 40-41": TunicItemData(ItemClassification.useful, 1, 122, "pages"),
+ "Pages 42-43 (Holy Cross)": TunicItemData(ItemClassification.progression, 1, 123, "pages"),
+ "Pages 44-45": TunicItemData(ItemClassification.useful, 1, 124, "pages"),
+ "Pages 46-47": TunicItemData(ItemClassification.useful, 1, 125, "pages"),
+ "Pages 48-49": TunicItemData(ItemClassification.useful, 1, 126, "pages"),
+ "Pages 50-51": TunicItemData(ItemClassification.useful, 1, 127, "pages"),
+ "Pages 52-53 (Icebolt)": TunicItemData(ItemClassification.progression, 1, 128, "pages"),
+ "Pages 54-55": TunicItemData(ItemClassification.useful, 1, 129, "pages"),
+
+ "Ladders near Weathervane": TunicItemData(ItemClassification.progression, 0, 130, "ladders"),
+ "Ladders near Overworld Checkpoint": TunicItemData(ItemClassification.progression, 0, 131, "ladders"),
+ "Ladders near Patrol Cave": TunicItemData(ItemClassification.progression, 0, 132, "ladders"),
+ "Ladder near Temple Rafters": TunicItemData(ItemClassification.progression, 0, 133, "ladders"),
+ "Ladders near Dark Tomb": TunicItemData(ItemClassification.progression, 0, 134, "ladders"),
+ "Ladder to Quarry": TunicItemData(ItemClassification.progression, 0, 135, "ladders"),
+ "Ladders to West Bell": TunicItemData(ItemClassification.progression, 0, 136, "ladders"),
+ "Ladders in Overworld Town": TunicItemData(ItemClassification.progression, 0, 137, "ladders"),
+ "Ladder to Ruined Atoll": TunicItemData(ItemClassification.progression, 0, 138, "ladders"),
+ "Ladder to Swamp": TunicItemData(ItemClassification.progression, 0, 139, "ladders"),
+ "Ladders in Well": TunicItemData(ItemClassification.progression, 0, 140, "ladders"),
+ "Ladder in Dark Tomb": TunicItemData(ItemClassification.progression, 0, 141, "ladders"),
+ "Ladder to East Forest": TunicItemData(ItemClassification.progression, 0, 142, "ladders"),
+ "Ladders to Lower Forest": TunicItemData(ItemClassification.progression, 0, 143, "ladders"),
+ "Ladder to Beneath the Vault": TunicItemData(ItemClassification.progression, 0, 144, "ladders"),
+ "Ladders in Hourglass Cave": TunicItemData(ItemClassification.progression, 0, 145, "ladders"),
+ "Ladders in South Atoll": TunicItemData(ItemClassification.progression, 0, 146, "ladders"),
+ "Ladders to Frog's Domain": TunicItemData(ItemClassification.progression, 0, 147, "ladders"),
+ "Ladders in Library": TunicItemData(ItemClassification.progression, 0, 148, "ladders"),
+ "Ladders in Lower Quarry": TunicItemData(ItemClassification.progression, 0, 149, "ladders"),
+ "Ladders in Swamp": TunicItemData(ItemClassification.progression, 0, 150, "ladders"),
+}
+
+fool_tiers: List[List[str]] = [
+ [],
+ ["Money x1", "Money x10", "Money x15", "Money x16"],
+ ["Money x1", "Money x10", "Money x15", "Money x16", "Money x20"],
+ ["Money x1", "Money x10", "Money x15", "Money x16", "Money x20", "Money x25", "Money x30"],
+]
+
+slot_data_item_names = [
+ "Stick",
+ "Sword",
+ "Sword Upgrade",
+ "Magic Dagger",
+ "Magic Wand",
+ "Magic Orb",
+ "Hero's Laurels",
+ "Lantern",
+ "Gun",
+ "Scavenger Mask",
+ "Shield",
+ "Dath Stone",
+ "Hourglass",
+ "Old House Key",
+ "Fortress Vault Key",
+ "Hero Relic - ATT",
+ "Hero Relic - DEF",
+ "Hero Relic - POTION",
+ "Hero Relic - HP",
+ "Hero Relic - SP",
+ "Hero Relic - MP",
+ "Pages 24-25 (Prayer)",
+ "Pages 42-43 (Holy Cross)",
+ "Pages 52-53 (Icebolt)",
+ "Red Questagon",
+ "Green Questagon",
+ "Blue Questagon",
+ "Gold Questagon",
+]
+
+item_name_to_id: Dict[str, int] = {name: item_base_id + data.item_id_offset for name, data in item_table.items()}
+
+filler_items: List[str] = [name for name, data in item_table.items() if data.classification == ItemClassification.filler]
+
+
+def get_item_group(item_name: str) -> str:
+ return item_table[item_name].item_group
+
+
+item_name_groups: Dict[str, Set[str]] = {
+ group: set(item_names) for group, item_names in groupby(sorted(item_table, key=get_item_group), get_item_group) if group != ""
+}
+
+# extra groups for the purpose of aliasing items
+extra_groups: Dict[str, Set[str]] = {
+ "laurels": {"Hero's Laurels"},
+ "orb": {"Magic Orb"},
+ "dagger": {"Magic Dagger"},
+ "magic rod": {"Magic Wand"},
+ "holy cross": {"Pages 42-43 (Holy Cross)"},
+ "prayer": {"Pages 24-25 (Prayer)"},
+ "icebolt": {"Pages 52-53 (Icebolt)"},
+ "ice rod": {"Pages 52-53 (Icebolt)"},
+ "melee weapons": {"Stick", "Sword", "Sword Upgrade"},
+ "progressive sword": {"Sword Upgrade"},
+ "abilities": {"Pages 24-25 (Prayer)", "Pages 42-43 (Holy Cross)", "Pages 52-53 (Icebolt)"},
+ "questagons": {"Red Questagon", "Green Questagon", "Blue Questagon", "Gold Questagon"},
+ "ladder to atoll": {"Ladder to Ruined Atoll"}, # fuzzy matching made it hint Ladders in Well, now it won't
+ "ladders to bell": {"Ladders to West Bell"},
+}
+
+item_name_groups.update(extra_groups)
diff --git a/worlds/tunic/locations.py b/worlds/tunic/locations.py
new file mode 100644
index 000000000000..4d95e91cb3cc
--- /dev/null
+++ b/worlds/tunic/locations.py
@@ -0,0 +1,331 @@
+from typing import Dict, NamedTuple, Set, Optional, List
+
+
+class TunicLocationData(NamedTuple):
+ region: str
+ er_region: str # entrance rando region
+ location_group: Optional[str] = None
+ location_groups: Optional[List[str]] = None
+
+
+location_base_id = 509342400
+
+location_table: Dict[str, TunicLocationData] = {
+ "Beneath the Well - [Powered Secret Room] Chest": TunicLocationData("Beneath the Well", "Beneath the Well Back"),
+ "Beneath the Well - [Entryway] Chest": TunicLocationData("Beneath the Well", "Beneath the Well Main"),
+ "Beneath the Well - [Third Room] Beneath Platform Chest": TunicLocationData("Beneath the Well", "Beneath the Well Main"),
+ "Beneath the Well - [Third Room] Tentacle Chest": TunicLocationData("Beneath the Well", "Beneath the Well Main"),
+ "Beneath the Well - [Entryway] Obscured Behind Waterfall": TunicLocationData("Beneath the Well", "Beneath the Well Main"),
+ "Beneath the Well - [Save Room] Upper Floor Chest 1": TunicLocationData("Beneath the Well", "Beneath the Well Back"),
+ "Beneath the Well - [Save Room] Upper Floor Chest 2": TunicLocationData("Beneath the Well", "Beneath the Well Back"),
+ "Beneath the Well - [Second Room] Underwater Chest": TunicLocationData("Beneath the Well", "Beneath the Well Main"),
+ "Beneath the Well - [Back Corridor] Right Secret": TunicLocationData("Beneath the Well", "Beneath the Well Main"),
+ "Beneath the Well - [Back Corridor] Left Secret": TunicLocationData("Beneath the Well", "Beneath the Well Main"),
+ "Beneath the Well - [Second Room] Obscured Behind Waterfall": TunicLocationData("Beneath the Well", "Beneath the Well Main"),
+ "Beneath the Well - [Side Room] Chest By Pots": TunicLocationData("Beneath the Well", "Beneath the Well Back"),
+ "Beneath the Well - [Side Room] Chest By Phrends": TunicLocationData("Beneath the Well", "Beneath the Well Back"),
+ "Beneath the Well - [Second Room] Page": TunicLocationData("Beneath the Well", "Beneath the Well Main"),
+ "Dark Tomb Checkpoint - [Passage To Dark Tomb] Page Pickup": TunicLocationData("Overworld", "Dark Tomb Checkpoint"),
+ "Cathedral - [1F] Guarded By Lasers": TunicLocationData("Cathedral", "Cathedral"),
+ "Cathedral - [1F] Near Spikes": TunicLocationData("Cathedral", "Cathedral"),
+ "Cathedral - [2F] Bird Room": TunicLocationData("Cathedral", "Cathedral"),
+ "Cathedral - [2F] Entryway Upper Walkway": TunicLocationData("Cathedral", "Cathedral"),
+ "Cathedral - [1F] Library": TunicLocationData("Cathedral", "Cathedral"),
+ "Cathedral - [2F] Library": TunicLocationData("Cathedral", "Cathedral"),
+ "Cathedral - [2F] Guarded By Lasers": TunicLocationData("Cathedral", "Cathedral"),
+ "Cathedral - [2F] Bird Room Secret": TunicLocationData("Cathedral", "Cathedral"),
+ "Cathedral - [1F] Library Secret": TunicLocationData("Cathedral", "Cathedral"),
+ "Dark Tomb - Spike Maze Near Exit": TunicLocationData("Dark Tomb", "Dark Tomb Main"),
+ "Dark Tomb - 2nd Laser Room": TunicLocationData("Dark Tomb", "Dark Tomb Main"),
+ "Dark Tomb - 1st Laser Room": TunicLocationData("Dark Tomb", "Dark Tomb Main"),
+ "Dark Tomb - Spike Maze Upper Walkway": TunicLocationData("Dark Tomb", "Dark Tomb Main"),
+ "Dark Tomb - Skulls Chest": TunicLocationData("Dark Tomb", "Dark Tomb Upper"),
+ "Dark Tomb - Spike Maze Near Stairs": TunicLocationData("Dark Tomb", "Dark Tomb Main"),
+ "Dark Tomb - 1st Laser Room Obscured": TunicLocationData("Dark Tomb", "Dark Tomb Main"),
+ "Guardhouse 2 - Upper Floor": TunicLocationData("East Forest", "Guard House 2 Upper"),
+ "Guardhouse 2 - Bottom Floor Secret": TunicLocationData("East Forest", "Guard House 2 Lower"),
+ "Guardhouse 1 - Upper Floor Obscured": TunicLocationData("East Forest", "Guard House 1 East"),
+ "Guardhouse 1 - Upper Floor": TunicLocationData("East Forest", "Guard House 1 East"),
+ "East Forest - Dancing Fox Spirit Holy Cross": TunicLocationData("East Forest", "East Forest Dance Fox Spot", location_group="holy cross"),
+ "East Forest - Golden Obelisk Holy Cross": TunicLocationData("East Forest", "Lower Forest", location_group="holy cross"),
+ "East Forest - Ice Rod Grapple Chest": TunicLocationData("East Forest", "East Forest"),
+ "East Forest - Above Save Point": TunicLocationData("East Forest", "East Forest"),
+ "East Forest - Above Save Point Obscured": TunicLocationData("East Forest", "East Forest"),
+ "East Forest - From Guardhouse 1 Chest": TunicLocationData("East Forest", "East Forest Dance Fox Spot"),
+ "East Forest - Near Save Point": TunicLocationData("East Forest", "East Forest"),
+ "East Forest - Beneath Spider Chest": TunicLocationData("East Forest", "Lower Forest"),
+ "East Forest - Near Telescope": TunicLocationData("East Forest", "East Forest"),
+ "East Forest - Spider Chest": TunicLocationData("East Forest", "Lower Forest"),
+ "East Forest - Lower Dash Chest": TunicLocationData("East Forest", "Lower Forest"),
+ "East Forest - Lower Grapple Chest": TunicLocationData("East Forest", "Lower Forest"),
+ "East Forest - Bombable Wall": TunicLocationData("East Forest", "East Forest"),
+ "East Forest - Page On Teleporter": TunicLocationData("East Forest", "East Forest"),
+ "Forest Belltower - Near Save Point": TunicLocationData("East Forest", "Forest Belltower Lower"),
+ "Forest Belltower - After Guard Captain": TunicLocationData("East Forest", "Forest Belltower Upper"),
+ "Forest Belltower - Obscured Near Bell Top Floor": TunicLocationData("East Forest", "Forest Belltower Upper"),
+ "Forest Belltower - Obscured Beneath Bell Bottom Floor": TunicLocationData("East Forest", "Forest Belltower Main"),
+ "Forest Belltower - Page Pickup": TunicLocationData("East Forest", "Forest Belltower Main"),
+ "Forest Grave Path - Holy Cross Code by Grave": TunicLocationData("East Forest", "Forest Grave Path by Grave", location_group="holy cross"),
+ "Forest Grave Path - Above Gate": TunicLocationData("East Forest", "Forest Grave Path Main"),
+ "Forest Grave Path - Obscured Chest": TunicLocationData("East Forest", "Forest Grave Path Main"),
+ "Forest Grave Path - Upper Walkway": TunicLocationData("East Forest", "Forest Grave Path Upper"),
+ "Forest Grave Path - Sword Pickup": TunicLocationData("East Forest", "Forest Grave Path by Grave"),
+ "Hero's Grave - Tooth Relic": TunicLocationData("East Forest", "Hero Relic - East Forest", location_group="hero relic"),
+ "Fortress Courtyard - From East Belltower": TunicLocationData("East Forest", "Fortress Exterior from East Forest"),
+ "Fortress Leaf Piles - Secret Chest": TunicLocationData("Eastern Vault Fortress", "Fortress Leaf Piles"),
+ "Fortress Arena - Hexagon Red": TunicLocationData("Eastern Vault Fortress", "Fortress Arena"),
+ "Fortress Arena - Siege Engine/Vault Key Pickup": TunicLocationData("Eastern Vault Fortress", "Fortress Arena", location_group="bosses"),
+ "Fortress East Shortcut - Chest Near Slimes": TunicLocationData("Eastern Vault Fortress", "Fortress East Shortcut Lower"),
+ "Eastern Vault Fortress - [West Wing] Candles Holy Cross": TunicLocationData("Eastern Vault Fortress", "Eastern Vault Fortress", location_group="holy cross"),
+ "Eastern Vault Fortress - [West Wing] Dark Room Chest 1": TunicLocationData("Eastern Vault Fortress", "Eastern Vault Fortress"),
+ "Eastern Vault Fortress - [West Wing] Dark Room Chest 2": TunicLocationData("Eastern Vault Fortress", "Eastern Vault Fortress"),
+ "Eastern Vault Fortress - [East Wing] Bombable Wall": TunicLocationData("Eastern Vault Fortress", "Eastern Vault Fortress"),
+ "Eastern Vault Fortress - [West Wing] Page Pickup": TunicLocationData("Eastern Vault Fortress", "Eastern Vault Fortress"),
+ "Fortress Grave Path - Upper Walkway": TunicLocationData("Eastern Vault Fortress", "Fortress Grave Path Upper"),
+ "Fortress Grave Path - Chest Right of Grave": TunicLocationData("Eastern Vault Fortress", "Fortress Grave Path"),
+ "Fortress Grave Path - Obscured Chest Left of Grave": TunicLocationData("Eastern Vault Fortress", "Fortress Grave Path"),
+ "Hero's Grave - Flowers Relic": TunicLocationData("Eastern Vault Fortress", "Hero Relic - Fortress", location_group="hero relic"),
+ "Beneath the Fortress - Bridge": TunicLocationData("Beneath the Vault", "Beneath the Vault Back"),
+ "Beneath the Fortress - Cell Chest 1": TunicLocationData("Beneath the Vault", "Beneath the Vault Back"),
+ "Beneath the Fortress - Obscured Behind Waterfall": TunicLocationData("Beneath the Vault", "Beneath the Vault Front"),
+ "Beneath the Fortress - Back Room Chest": TunicLocationData("Beneath the Vault", "Beneath the Vault Back"),
+ "Beneath the Fortress - Cell Chest 2": TunicLocationData("Beneath the Vault", "Beneath the Vault Back"),
+ "Frog's Domain - Near Vault": TunicLocationData("Frog's Domain", "Frog's Domain"),
+ "Frog's Domain - Slorm Room": TunicLocationData("Frog's Domain", "Frog's Domain"),
+ "Frog's Domain - Escape Chest": TunicLocationData("Frog's Domain", "Frog's Domain Back"),
+ "Frog's Domain - Grapple Above Hot Tub": TunicLocationData("Frog's Domain", "Frog's Domain"),
+ "Frog's Domain - Above Vault": TunicLocationData("Frog's Domain", "Frog's Domain"),
+ "Frog's Domain - Main Room Top Floor": TunicLocationData("Frog's Domain", "Frog's Domain"),
+ "Frog's Domain - Main Room Bottom Floor": TunicLocationData("Frog's Domain", "Frog's Domain"),
+ "Frog's Domain - Side Room Secret Passage": TunicLocationData("Frog's Domain", "Frog's Domain"),
+ "Frog's Domain - Side Room Chest": TunicLocationData("Frog's Domain", "Frog's Domain"),
+ "Frog's Domain - Side Room Grapple Secret": TunicLocationData("Frog's Domain", "Frog's Domain"),
+ "Frog's Domain - Magic Orb Pickup": TunicLocationData("Frog's Domain", "Frog's Domain"),
+ "Librarian - Hexagon Green": TunicLocationData("Library", "Library Arena", location_group="bosses"),
+ "Library Hall - Holy Cross Chest": TunicLocationData("Library", "Library Hall", location_group="holy cross"),
+ "Library Lab - Chest By Shrine 2": TunicLocationData("Library", "Library Lab"),
+ "Library Lab - Chest By Shrine 1": TunicLocationData("Library", "Library Lab"),
+ "Library Lab - Chest By Shrine 3": TunicLocationData("Library", "Library Lab"),
+ "Library Lab - Behind Chalkboard by Fuse": TunicLocationData("Library", "Library Lab"),
+ "Library Lab - Page 3": TunicLocationData("Library", "Library Lab"),
+ "Library Lab - Page 1": TunicLocationData("Library", "Library Lab"),
+ "Library Lab - Page 2": TunicLocationData("Library", "Library Lab"),
+ "Hero's Grave - Mushroom Relic": TunicLocationData("Library", "Hero Relic - Library", location_group="hero relic"),
+ "Lower Mountain - Page Before Door": TunicLocationData("Overworld", "Lower Mountain"),
+ "Changing Room - Normal Chest": TunicLocationData("Overworld", "Changing Room"),
+ "Fortress Courtyard - Chest Near Cave": TunicLocationData("Overworld", "Fortress Exterior near cave"),
+ "Fortress Courtyard - Near Fuse": TunicLocationData("Overworld", "Fortress Exterior from Overworld"),
+ "Fortress Courtyard - Below Walkway": TunicLocationData("Overworld", "Fortress Exterior from Overworld"),
+ "Fortress Courtyard - Page Near Cave": TunicLocationData("Overworld", "Fortress Exterior near cave"),
+ "West Furnace - Lantern Pickup": TunicLocationData("Overworld", "Furnace Fuse"),
+ "Maze Cave - Maze Room Chest": TunicLocationData("Overworld", "Maze Cave"),
+ "Old House - Normal Chest": TunicLocationData("Overworld", "Old House Front"),
+ "Old House - Shield Pickup": TunicLocationData("Overworld", "Old House Front"),
+ "Overworld - [West] Obscured Behind Windmill": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [South] Beach Chest": TunicLocationData("Overworld", "Overworld Beach"),
+ "Overworld - [West] Obscured Near Well": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [Central] Bombable Wall": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [Northwest] Chest Near Turret": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [East] Chest Near Pots": TunicLocationData("Overworld", "East Overworld"),
+ "Overworld - [Northwest] Chest Near Golden Obelisk": TunicLocationData("Overworld", "Overworld above Quarry Entrance"),
+ "Overworld - [Southwest] South Chest Near Guard": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [Southwest] West Beach Guarded By Turret": TunicLocationData("Overworld", "Overworld Beach"),
+ "Overworld - [Southwest] Chest Guarded By Turret": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [Northwest] Shadowy Corner Chest": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [Southwest] Obscured In Tunnel To Beach": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [Southwest] Grapple Chest Over Walkway": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [Northwest] Chest Beneath Quarry Gate": TunicLocationData("Overworld", "Overworld after Envoy"),
+ "Overworld - [Southeast] Chest Near Swamp": TunicLocationData("Overworld", "Overworld Swamp Lower Entry"),
+ "Overworld - [Southwest] From West Garden": TunicLocationData("Overworld", "Overworld Beach"),
+ "Overworld - [East] Grapple Chest": TunicLocationData("Overworld", "Overworld above Patrol Cave"),
+ "Overworld - [Southwest] West Beach Guarded By Turret 2": TunicLocationData("Overworld", "Overworld Beach"),
+ "Overworld - [Southwest] Beach Chest Near Flowers": TunicLocationData("Overworld", "Overworld Beach"),
+ "Overworld - [Southwest] Bombable Wall Near Fountain": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [West] Chest After Bell": TunicLocationData("Overworld", "Overworld Belltower"),
+ "Overworld - [Southwest] Tunnel Guarded By Turret": TunicLocationData("Overworld", "Overworld Tunnel Turret"),
+ "Overworld - [East] Between Ladders Near Ruined Passage": TunicLocationData("Overworld", "Above Ruined Passage"),
+ "Overworld - [Northeast] Chest Above Patrol Cave": TunicLocationData("Overworld", "Upper Overworld"),
+ "Overworld - [Southwest] Beach Chest Beneath Guard": TunicLocationData("Overworld", "Overworld Beach"),
+ "Overworld - [Central] Chest Across From Well": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [Northwest] Chest Near Quarry Gate": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [East] Chest In Trees": TunicLocationData("Overworld", "Above Ruined Passage"),
+ "Overworld - [West] Chest Behind Moss Wall": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [South] Beach Page": TunicLocationData("Overworld", "Overworld Beach"),
+ "Overworld - [Southeast] Page on Pillar by Swamp": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [Southwest] Key Pickup": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [West] Key Pickup": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [East] Page Near Secret Shop": TunicLocationData("Overworld", "East Overworld"),
+ "Overworld - [Southwest] Fountain Page": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [Northwest] Page on Pillar by Dark Tomb": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [Northwest] Fire Wand Pickup": TunicLocationData("Overworld", "Upper Overworld"),
+ "Overworld - [West] Page On Teleporter": TunicLocationData("Overworld", "Overworld"),
+ "Overworld - [Northwest] Page By Well": TunicLocationData("Overworld", "Overworld"),
+ "Patrol Cave - Normal Chest": TunicLocationData("Overworld", "Patrol Cave"),
+ "Ruined Shop - Chest 1": TunicLocationData("Overworld", "Ruined Shop"),
+ "Ruined Shop - Chest 2": TunicLocationData("Overworld", "Ruined Shop"),
+ "Ruined Shop - Chest 3": TunicLocationData("Overworld", "Ruined Shop"),
+ "Ruined Passage - Page Pickup": TunicLocationData("Overworld", "Ruined Passage"),
+ "Shop - Potion 1": TunicLocationData("Overworld", "Shop", location_group="shop"),
+ "Shop - Potion 2": TunicLocationData("Overworld", "Shop", location_group="shop"),
+ "Shop - Coin 1": TunicLocationData("Overworld", "Shop", location_group="shop"),
+ "Shop - Coin 2": TunicLocationData("Overworld", "Shop", location_group="shop"),
+ "Special Shop - Secret Page Pickup": TunicLocationData("Overworld", "Special Shop"),
+ "Stick House - Stick Chest": TunicLocationData("Overworld", "Stick House"),
+ "Sealed Temple - Page Pickup": TunicLocationData("Overworld", "Sealed Temple"),
+ "Hourglass Cave - Hourglass Chest": TunicLocationData("Overworld", "Hourglass Cave"),
+ "Far Shore - Secret Chest": TunicLocationData("Overworld", "Far Shore"),
+ "Far Shore - Page Pickup": TunicLocationData("Overworld", "Far Shore to Spawn Region"),
+ "Coins in the Well - 10 Coins": TunicLocationData("Overworld", "Overworld", location_group="well"),
+ "Coins in the Well - 15 Coins": TunicLocationData("Overworld", "Overworld", location_group="well"),
+ "Coins in the Well - 3 Coins": TunicLocationData("Overworld", "Overworld", location_group="well"),
+ "Coins in the Well - 6 Coins": TunicLocationData("Overworld", "Overworld", location_group="well"),
+ "Secret Gathering Place - 20 Fairy Reward": TunicLocationData("Overworld", "Secret Gathering Place", location_group="fairies"),
+ "Secret Gathering Place - 10 Fairy Reward": TunicLocationData("Overworld", "Secret Gathering Place", location_group="fairies"),
+ "Overworld - [West] Moss Wall Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", location_group="holy cross"),
+ "Overworld - [Southwest] Flowers Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Beach", location_group="holy cross"),
+ "Overworld - [Southwest] Fountain Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", location_group="holy cross"),
+ "Overworld - [Northeast] Flowers Holy Cross": TunicLocationData("Overworld Holy Cross", "East Overworld", location_group="holy cross"),
+ "Overworld - [East] Weathervane Holy Cross": TunicLocationData("Overworld Holy Cross", "East Overworld", location_group="holy cross"),
+ "Overworld - [West] Windmill Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", location_group="holy cross"),
+ "Overworld - [Southwest] Haiku Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Beach", location_group="holy cross"),
+ "Overworld - [West] Windchimes Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", location_group="holy cross"),
+ "Overworld - [South] Starting Platform Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", location_group="holy cross"),
+ "Overworld - [Northwest] Golden Obelisk Page": TunicLocationData("Overworld Holy Cross", "Upper Overworld", location_group="holy cross"),
+ "Old House - Holy Cross Door Page": TunicLocationData("Overworld Holy Cross", "Old House Back", location_group="holy cross"),
+ "Cube Cave - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Cube Cave", location_group="holy cross"),
+ "Southeast Cross Door - Chest 3": TunicLocationData("Overworld Holy Cross", "Southeast Cross Room", location_group="holy cross"),
+ "Southeast Cross Door - Chest 2": TunicLocationData("Overworld Holy Cross", "Southeast Cross Room", location_group="holy cross"),
+ "Southeast Cross Door - Chest 1": TunicLocationData("Overworld Holy Cross", "Southeast Cross Room", location_group="holy cross"),
+ "Maze Cave - Maze Room Holy Cross": TunicLocationData("Overworld Holy Cross", "Maze Cave", location_group="holy cross"),
+ "Caustic Light Cave - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Caustic Light Cave", location_group="holy cross"),
+ "Old House - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Old House Front", location_group="holy cross"),
+ "Patrol Cave - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Patrol Cave", location_group="holy cross"),
+ "Ruined Passage - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Ruined Passage", location_group="holy cross"),
+ "Hourglass Cave - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Hourglass Cave Tower", location_group="holy cross"),
+ "Sealed Temple - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Sealed Temple", location_group="holy cross"),
+ "Fountain Cross Door - Page Pickup": TunicLocationData("Overworld Holy Cross", "Fountain Cross Room", location_group="holy cross"),
+ "Secret Gathering Place - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Secret Gathering Place", location_group="holy cross"),
+ "Top of the Mountain - Page At The Peak": TunicLocationData("Overworld Holy Cross", "Top of the Mountain", location_group="holy cross"),
+ "Monastery - Monastery Chest": TunicLocationData("Quarry", "Monastery Back"),
+ "Quarry - [Back Entrance] Bushes Holy Cross": TunicLocationData("Quarry Back", "Quarry Back", location_group="holy cross"),
+ "Quarry - [Back Entrance] Chest": TunicLocationData("Quarry Back", "Quarry Back"),
+ "Quarry - [Central] Near Shortcut Ladder": TunicLocationData("Quarry", "Quarry"),
+ "Quarry - [East] Near Telescope": TunicLocationData("Quarry", "Quarry"),
+ "Quarry - [East] Upper Floor": TunicLocationData("Quarry", "Quarry"),
+ "Quarry - [Central] Below Entry Walkway": TunicLocationData("Quarry", "Quarry"),
+ "Quarry - [East] Obscured Near Winding Staircase": TunicLocationData("Quarry", "Quarry"),
+ "Quarry - [East] Obscured Beneath Scaffolding": TunicLocationData("Quarry", "Quarry"),
+ "Quarry - [East] Obscured Near Telescope": TunicLocationData("Quarry", "Quarry"),
+ "Quarry - [Back Entrance] Obscured Behind Wall": TunicLocationData("Quarry Back", "Quarry Back"),
+ "Quarry - [Central] Obscured Below Entry Walkway": TunicLocationData("Quarry", "Quarry"),
+ "Quarry - [Central] Top Floor Overhang": TunicLocationData("Quarry", "Quarry"),
+ "Quarry - [East] Near Bridge": TunicLocationData("Quarry", "Quarry"),
+ "Quarry - [Central] Above Ladder": TunicLocationData("Quarry", "Quarry Monastery Entry"),
+ "Quarry - [Central] Obscured Behind Staircase": TunicLocationData("Quarry", "Quarry"),
+ "Quarry - [Central] Above Ladder Dash Chest": TunicLocationData("Quarry", "Quarry Monastery Entry"),
+ "Quarry - [West] Upper Area Bombable Wall": TunicLocationData("Quarry Back", "Quarry Back"),
+ "Quarry - [East] Bombable Wall": TunicLocationData("Quarry", "Quarry"),
+ "Hero's Grave - Ash Relic": TunicLocationData("Quarry", "Hero Relic - Quarry", location_group="hero relics"),
+ "Quarry - [West] Shooting Range Secret Path": TunicLocationData("Lower Quarry", "Lower Quarry"),
+ "Quarry - [West] Near Shooting Range": TunicLocationData("Lower Quarry", "Lower Quarry"),
+ "Quarry - [West] Below Shooting Range": TunicLocationData("Lower Quarry", "Lower Quarry"),
+ "Quarry - [Lowlands] Below Broken Ladder": TunicLocationData("Lower Quarry", "Even Lower Quarry"),
+ "Quarry - [West] Upper Area Near Waterfall": TunicLocationData("Lower Quarry", "Lower Quarry"),
+ "Quarry - [Lowlands] Upper Walkway": TunicLocationData("Lower Quarry", "Even Lower Quarry"),
+ "Quarry - [West] Lower Area Below Bridge": TunicLocationData("Lower Quarry", "Lower Quarry"),
+ "Quarry - [West] Lower Area Isolated Chest": TunicLocationData("Lower Quarry", "Lower Quarry"),
+ "Quarry - [Lowlands] Near Elevator": TunicLocationData("Lower Quarry", "Even Lower Quarry"),
+ "Quarry - [West] Lower Area After Bridge": TunicLocationData("Lower Quarry", "Lower Quarry"),
+ "Rooted Ziggurat Upper - Near Bridge Switch": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Upper Front"),
+ "Rooted Ziggurat Upper - Beneath Bridge To Administrator": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Upper Back"),
+ "Rooted Ziggurat Tower - Inside Tower": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Middle Top"),
+ "Rooted Ziggurat Lower - Near Corpses": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"),
+ "Rooted Ziggurat Lower - Spider Ambush": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"),
+ "Rooted Ziggurat Lower - Left Of Checkpoint Before Fuse": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"),
+ "Rooted Ziggurat Lower - After Guarded Fuse": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"),
+ "Rooted Ziggurat Lower - Guarded By Double Turrets": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"),
+ "Rooted Ziggurat Lower - After 2nd Double Turret Chest": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"),
+ "Rooted Ziggurat Lower - Guarded By Double Turrets 2": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"),
+ "Rooted Ziggurat Lower - Hexagon Blue": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Back", location_group="bosses"),
+ "Ruined Atoll - [West] Near Kevin Block": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
+ "Ruined Atoll - [South] Upper Floor On Power Line": TunicLocationData("Ruined Atoll", "Ruined Atoll Ladder Tops"),
+ "Ruined Atoll - [South] Chest Near Big Crabs": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
+ "Ruined Atoll - [North] Guarded By Bird": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
+ "Ruined Atoll - [Northeast] Chest Beneath Brick Walkway": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
+ "Ruined Atoll - [Northwest] Bombable Wall": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
+ "Ruined Atoll - [North] Obscured Beneath Bridge": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
+ "Ruined Atoll - [South] Upper Floor On Bricks": TunicLocationData("Ruined Atoll", "Ruined Atoll Ladder Tops"),
+ "Ruined Atoll - [South] Near Birds": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
+ "Ruined Atoll - [Northwest] Behind Envoy": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
+ "Ruined Atoll - [Southwest] Obscured Behind Fuse": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
+ "Ruined Atoll - [East] Locked Room Upper Chest": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
+ "Ruined Atoll - [North] From Lower Overworld Entrance": TunicLocationData("Ruined Atoll", "Ruined Atoll Lower Entry Area"),
+ "Ruined Atoll - [East] Locked Room Lower Chest": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
+ "Ruined Atoll - [Northeast] Chest On Brick Walkway": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
+ "Ruined Atoll - [Southeast] Chest Near Fuse": TunicLocationData("Ruined Atoll", "Ruined Atoll Ladder Tops"),
+ "Ruined Atoll - [Northeast] Key Pickup": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
+ "Cathedral Gauntlet - Gauntlet Reward": TunicLocationData("Swamp", "Cathedral Gauntlet"),
+ "Cathedral - Secret Legend Trophy Chest": TunicLocationData("Swamp", "Cathedral Secret Legend Room"),
+ "Swamp - [Upper Graveyard] Obscured Behind Hill": TunicLocationData("Swamp", "Swamp Mid"),
+ "Swamp - [South Graveyard] 4 Orange Skulls": TunicLocationData("Swamp", "Swamp Front"),
+ "Swamp - [Central] Near Ramps Up": TunicLocationData("Swamp", "Swamp Mid"),
+ "Swamp - [Upper Graveyard] Near Shield Fleemers": TunicLocationData("Swamp", "Swamp Mid"),
+ "Swamp - [South Graveyard] Obscured Behind Ridge": TunicLocationData("Swamp", "Swamp Mid"),
+ "Swamp - [South Graveyard] Obscured Beneath Telescope": TunicLocationData("Swamp", "Swamp Front"),
+ "Swamp - [Entrance] Above Entryway": TunicLocationData("Swamp", "Back of Swamp Laurels Area"),
+ "Swamp - [Central] South Secret Passage": TunicLocationData("Swamp", "Swamp Mid"),
+ "Swamp - [South Graveyard] Upper Walkway On Pedestal": TunicLocationData("Swamp", "Swamp Front"),
+ "Swamp - [South Graveyard] Guarded By Tentacles": TunicLocationData("Swamp", "Swamp Front"),
+ "Swamp - [Upper Graveyard] Near Telescope": TunicLocationData("Swamp", "Swamp Mid"),
+ "Swamp - [Outside Cathedral] Near Moonlight Bridge Door": TunicLocationData("Swamp", "Swamp Ledge under Cathedral Door"),
+ "Swamp - [Entrance] Obscured Inside Watchtower": TunicLocationData("Swamp", "Swamp Front"),
+ "Swamp - [Entrance] South Near Fence": TunicLocationData("Swamp", "Swamp Front"),
+ "Swamp - [South Graveyard] Guarded By Big Skeleton": TunicLocationData("Swamp", "Swamp Front"),
+ "Swamp - [South Graveyard] Chest Near Graves": TunicLocationData("Swamp", "Swamp Front"),
+ "Swamp - [Entrance] North Small Island": TunicLocationData("Swamp", "Swamp Front"),
+ "Swamp - [Outside Cathedral] Obscured Behind Memorial": TunicLocationData("Swamp", "Back of Swamp"),
+ "Swamp - [Central] Obscured Behind Northern Mountain": TunicLocationData("Swamp", "Swamp Mid"),
+ "Swamp - [South Graveyard] Upper Walkway Dash Chest": TunicLocationData("Swamp", "Swamp Mid"),
+ "Swamp - [South Graveyard] Above Big Skeleton": TunicLocationData("Swamp", "Swamp Front"),
+ "Swamp - [Central] Beneath Memorial": TunicLocationData("Swamp", "Swamp Mid"),
+ "Hero's Grave - Feathers Relic": TunicLocationData("Swamp", "Hero Relic - Swamp", location_group="hero relic"),
+ "West Furnace - Chest": TunicLocationData("West Garden", "Furnace Walking Path"),
+ "Overworld - [West] Near West Garden Entrance": TunicLocationData("West Garden", "Overworld to West Garden from Furnace"),
+ "West Garden - [Central Highlands] Holy Cross (Blue Lines)": TunicLocationData("West Garden", "West Garden", location_group="holy cross"),
+ "West Garden - [West Lowlands] Tree Holy Cross Chest": TunicLocationData("West Garden", "West Garden", location_group="holy cross"),
+ "West Garden - [Southeast Lowlands] Outside Cave": TunicLocationData("West Garden", "West Garden"),
+ "West Garden - [Central Lowlands] Chest Beneath Faeries": TunicLocationData("West Garden", "West Garden"),
+ "West Garden - [North] Behind Holy Cross Door": TunicLocationData("West Garden", "West Garden", location_group="holy cross"),
+ "West Garden - [Central Highlands] Top of Ladder Before Boss": TunicLocationData("West Garden", "West Garden"),
+ "West Garden - [Central Lowlands] Passage Beneath Bridge": TunicLocationData("West Garden", "West Garden"),
+ "West Garden - [North] Across From Page Pickup": TunicLocationData("West Garden", "West Garden"),
+ "West Garden - [Central Lowlands] Below Left Walkway": TunicLocationData("West Garden", "West Garden"),
+ "West Garden - [West] In Flooded Walkway": TunicLocationData("West Garden", "West Garden"),
+ "West Garden - [West] Past Flooded Walkway": TunicLocationData("West Garden", "West Garden"),
+ "West Garden - [North] Obscured Beneath Hero's Memorial": TunicLocationData("West Garden", "West Garden"),
+ "West Garden - [Central Lowlands] Chest Near Shortcut Bridge": TunicLocationData("West Garden", "West Garden"),
+ "West Garden - [West Highlands] Upper Left Walkway": TunicLocationData("West Garden", "West Garden"),
+ "West Garden - [Central Lowlands] Chest Beneath Save Point": TunicLocationData("West Garden", "West Garden"),
+ "West Garden - [Central Highlands] Behind Guard Captain": TunicLocationData("West Garden", "West Garden"),
+ "West Garden - [Central Highlands] After Garden Knight": TunicLocationData("Overworld", "West Garden after Boss", location_group="bosses"),
+ "West Garden - [South Highlands] Secret Chest Beneath Fuse": TunicLocationData("West Garden", "West Garden"),
+ "West Garden - [East Lowlands] Page Behind Ice Dagger House": TunicLocationData("West Garden", "West Garden Portal Item"),
+ "West Garden - [North] Page Pickup": TunicLocationData("West Garden", "West Garden"),
+ "West Garden House - [Southeast Lowlands] Ice Dagger Pickup": TunicLocationData("West Garden", "Magic Dagger House"),
+ "Hero's Grave - Effigy Relic": TunicLocationData("West Garden", "Hero Relic - West Garden", location_group="hero relic"),
+}
+
+hexagon_locations: Dict[str, str] = {
+ "Red Questagon": "Fortress Arena - Siege Engine/Vault Key Pickup",
+ "Green Questagon": "Librarian - Hexagon Green",
+ "Blue Questagon": "Rooted Ziggurat Lower - Hexagon Blue",
+}
+
+location_name_to_id: Dict[str, int] = {name: location_base_id + index for index, name in enumerate(location_table)}
+
+location_name_groups: Dict[str, Set[str]] = {}
+for loc_name, loc_data in location_table.items():
+ if loc_data.location_group:
+ if loc_data.location_group not in location_name_groups.keys():
+ location_name_groups[loc_data.location_group] = set()
+ location_name_groups[loc_data.location_group].add(loc_name)
diff --git a/worlds/tunic/options.py b/worlds/tunic/options.py
new file mode 100644
index 000000000000..38ddcbe8e40f
--- /dev/null
+++ b/worlds/tunic/options.py
@@ -0,0 +1,163 @@
+from dataclasses import dataclass
+
+from Options import DefaultOnToggle, Toggle, StartInventoryPool, Choice, Range, TextChoice, PerGameCommonOptions
+
+
+class SwordProgression(DefaultOnToggle):
+ """Adds four sword upgrades to the item pool that will progressively grant stronger melee weapons, including two new swords with increased range and attack power."""
+ internal_name = "sword_progression"
+ display_name = "Sword Progression"
+
+
+class StartWithSword(Toggle):
+ """Start with a sword in the player's inventory. Does not count towards Sword Progression."""
+ internal_name = "start_with_sword"
+ display_name = "Start With Sword"
+
+
+class KeysBehindBosses(Toggle):
+ """Places the three hexagon keys behind their respective boss fight in your world."""
+ internal_name = "keys_behind_bosses"
+ display_name = "Keys Behind Bosses"
+
+
+class AbilityShuffling(Toggle):
+ """Locks the usage of Prayer, Holy Cross*, and the Icebolt combo until the relevant pages of the manual have been found.
+ If playing Hexagon Quest, abilities are instead randomly unlocked after obtaining 25%, 50%, and 75% of the required Hexagon goal amount.
+ *Certain Holy Cross usages are still allowed, such as the free bomb codes, the seeking spell, and other player-facing codes.
+ """
+ internal_name = "ability_shuffling"
+ display_name = "Shuffle Abilities"
+
+
+class LogicRules(Choice):
+ """
+ Set which logic rules to use for your world.
+ Restricted: Standard logic, no glitches.
+ No Major Glitches: Sneaky Laurels zips, ice grapples through doors, shooting the west bell, and boss quick kills are included in logic.
+ * Ice grappling through the Ziggurat door is not in logic since you will get stuck in there without Prayer.
+ Unrestricted: Logic in No Major Glitches, as well as ladder storage to get to certain places early.
+ *Torch is given to the player at the start of the game due to the high softlock potential with various tricks. Using the torch is not required in logic.
+ *Using Ladder Storage to get to individual chests is not in logic to avoid tedium.
+ *Getting knocked out of the air by enemies during Ladder Storage to reach places is not in logic, except for in Rooted Ziggurat Lower. This is so you're not punished for playing with enemy rando on.
+ """
+ internal_name = "logic_rules"
+ display_name = "Logic Rules"
+ option_restricted = 0
+ option_no_major_glitches = 1
+ alias_nmg = 1
+ option_unrestricted = 2
+ alias_ur = 2
+ default = 0
+
+
+class Lanternless(Toggle):
+ """Choose whether you require the Lantern for dark areas.
+ When enabled, the Lantern is marked as Useful instead of Progression."""
+ internal_name = "lanternless"
+ display_name = "Lanternless"
+
+
+class Maskless(Toggle):
+ """Choose whether you require the Scavenger's Mask for Lower Quarry.
+ When enabled, the Scavenger's Mask is marked as Useful instead of Progression."""
+ internal_name = "maskless"
+ display_name = "Maskless"
+
+
+class FoolTraps(Choice):
+ """Replaces low-to-medium value money rewards in the item pool with fool traps, which cause random negative effects to the player."""
+ internal_name = "fool_traps"
+ display_name = "Fool Traps"
+ option_off = 0
+ option_normal = 1
+ option_double = 2
+ option_onslaught = 3
+ default = 1
+
+
+class HexagonQuest(Toggle):
+ """An alternate goal that shuffles Gold "Questagon" items into the item pool and allows the game to be completed after collecting the required number of them."""
+ internal_name = "hexagon_quest"
+ display_name = "Hexagon Quest"
+
+
+class HexagonGoal(Range):
+ """How many Gold Questagons are required to complete the game on Hexagon Quest."""
+ internal_name = "hexagon_goal"
+ display_name = "Gold Hexagons Required"
+ range_start = 15
+ range_end = 50
+ default = 20
+
+
+class ExtraHexagonPercentage(Range):
+ """How many extra Gold Questagons are shuffled into the item pool, taken as a percentage of the goal amount."""
+ internal_name = "extra_hexagon_percentage"
+ display_name = "Percentage of Extra Gold Hexagons"
+ range_start = 0
+ range_end = 100
+ default = 50
+
+
+class EntranceRando(TextChoice):
+ """
+ Randomize the connections between scenes.
+ If you set this to a value besides true or false, that value will be used as a custom seed.
+ A small, very lost fox on a big adventure.
+ """
+ internal_name = "entrance_rando"
+ display_name = "Entrance Rando"
+ alias_false = 0
+ option_no = 0
+ alias_true = 1
+ option_yes = 1
+ default = 0
+
+
+class FixedShop(Toggle):
+ """Forces the Windmill entrance to lead to a shop, and places only one other shop in the pool.
+ Has no effect if Entrance Rando is not enabled."""
+ internal_name = "fixed_shop"
+ display_name = "Fewer Shops in Entrance Rando"
+
+
+class LaurelsLocation(Choice):
+ """Force the Hero's Laurels to be placed at a location in your world.
+ For if you want to avoid or specify early or late Laurels.
+ If you use the 10 Fairies option in Entrance Rando, Secret Gathering Place will be at its vanilla entrance."""
+ internal_name = "laurels_location"
+ display_name = "Laurels Location"
+ option_anywhere = 0
+ option_6_coins = 1
+ option_10_coins = 2
+ option_10_fairies = 3
+ default = 0
+
+
+class ShuffleLadders(Toggle):
+ """Turns several ladders in the game into items that must be found before they can be climbed on.
+ Adds more layers of progression to the game by blocking access to many areas early on.
+ "Ladders were a mistake." —Andrew Shouldice"""
+ internal_name = "shuffle_ladders"
+ display_name = "Shuffle Ladders"
+
+
+@dataclass
+class TunicOptions(PerGameCommonOptions):
+ sword_progression: SwordProgression
+ start_with_sword: StartWithSword
+ keys_behind_bosses: KeysBehindBosses
+ ability_shuffling: AbilityShuffling
+ shuffle_ladders: ShuffleLadders
+ entrance_rando: EntranceRando
+ fixed_shop: FixedShop
+ logic_rules: LogicRules
+ fool_traps: FoolTraps
+ hexagon_quest: HexagonQuest
+ hexagon_goal: HexagonGoal
+ extra_hexagon_percentage: ExtraHexagonPercentage
+ lanternless: Lanternless
+ maskless: Maskless
+ laurels_location: LaurelsLocation
+ start_inventory_from_pool: StartInventoryPool
diff --git a/worlds/tunic/regions.py b/worlds/tunic/regions.py
new file mode 100644
index 000000000000..c30a44bb8ff6
--- /dev/null
+++ b/worlds/tunic/regions.py
@@ -0,0 +1,25 @@
+from typing import Dict, Set
+
+tunic_regions: Dict[str, Set[str]] = {
+ "Menu": {"Overworld"},
+ "Overworld": {"Overworld Holy Cross", "East Forest", "Dark Tomb", "Beneath the Well", "West Garden",
+ "Ruined Atoll", "Eastern Vault Fortress", "Beneath the Vault", "Quarry Back", "Quarry", "Swamp",
+ "Spirit Arena"},
+ "Overworld Holy Cross": set(),
+ "East Forest": set(),
+ "Dark Tomb": {"West Garden"},
+ "Beneath the Well": set(),
+ "West Garden": set(),
+ "Ruined Atoll": {"Frog's Domain", "Library"},
+ "Frog's Domain": set(),
+ "Library": set(),
+ "Eastern Vault Fortress": {"Beneath the Vault"},
+ "Beneath the Vault": {"Eastern Vault Fortress"},
+ "Quarry Back": {"Quarry"},
+ "Quarry": {"Lower Quarry"},
+ "Lower Quarry": {"Rooted Ziggurat"},
+ "Rooted Ziggurat": set(),
+ "Swamp": {"Cathedral"},
+ "Cathedral": set(),
+ "Spirit Arena": set()
+}
diff --git a/worlds/tunic/rules.py b/worlds/tunic/rules.py
new file mode 100644
index 000000000000..c82c5ca13339
--- /dev/null
+++ b/worlds/tunic/rules.py
@@ -0,0 +1,336 @@
+from random import Random
+from typing import Dict, TYPE_CHECKING
+
+from worlds.generic.Rules import set_rule, forbid_item
+from BaseClasses import CollectionState
+from .options import TunicOptions
+if TYPE_CHECKING:
+ from . import TunicWorld
+
+laurels = "Hero's Laurels"
+grapple = "Magic Orb"
+ice_dagger = "Magic Dagger"
+fire_wand = "Magic Wand"
+lantern = "Lantern"
+fairies = "Fairy"
+coins = "Golden Coin"
+prayer = "Pages 24-25 (Prayer)"
+holy_cross = "Pages 42-43 (Holy Cross)"
+icebolt = "Pages 52-53 (Icebolt)"
+key = "Key"
+house_key = "Old House Key"
+vault_key = "Fortress Vault Key"
+mask = "Scavenger Mask"
+red_hexagon = "Red Questagon"
+green_hexagon = "Green Questagon"
+blue_hexagon = "Blue Questagon"
+gold_hexagon = "Gold Questagon"
+
+
+def randomize_ability_unlocks(random: Random, options: TunicOptions) -> Dict[str, int]:
+ ability_requirement = [1, 1, 1]
+ if options.hexagon_quest.value:
+ hexagon_goal = options.hexagon_goal.value
+ # Set ability unlocks to 25, 50, and 75% of goal amount
+ ability_requirement = [hexagon_goal // 4, hexagon_goal // 2, hexagon_goal * 3 // 4]
+ abilities = [prayer, holy_cross, icebolt]
+ random.shuffle(abilities)
+ return dict(zip(abilities, ability_requirement))
+
+
+def has_ability(state: CollectionState, player: int, ability: str, options: TunicOptions,
+ ability_unlocks: Dict[str, int]) -> bool:
+ if not options.ability_shuffling:
+ return True
+ if options.hexagon_quest:
+ return state.has(gold_hexagon, player, ability_unlocks[ability])
+ return state.has(ability, player)
+
+
+# a check to see if you can whack things in melee at all
+def has_stick(state: CollectionState, player: int) -> bool:
+ return state.has("Stick", player) or state.has("Sword Upgrade", player, 1) or state.has("Sword", player)
+
+
+def has_sword(state: CollectionState, player: int) -> bool:
+ return state.has("Sword", player) or state.has("Sword Upgrade", player, 2)
+
+
+def has_ice_grapple_logic(long_range: bool, state: CollectionState, player: int, options: TunicOptions,
+ ability_unlocks: Dict[str, int]) -> bool:
+ if not options.logic_rules:
+ return False
+
+ if not long_range:
+ return state.has_all({ice_dagger, grapple}, player)
+ else:
+ return state.has_all({ice_dagger, fire_wand, grapple}, player) and \
+ has_ability(state, player, icebolt, options, ability_unlocks)
+
+
+def can_ladder_storage(state: CollectionState, player: int, options: TunicOptions) -> bool:
+ if options.logic_rules == "unrestricted" and has_stick(state, player):
+ return True
+ else:
+ return False
+
+
+def has_mask(state: CollectionState, player: int, options: TunicOptions) -> bool:
+ if options.maskless:
+ return True
+ else:
+ return state.has(mask, player)
+
+
+def has_lantern(state: CollectionState, player: int, options: TunicOptions) -> bool:
+ if options.lanternless:
+ return True
+ else:
+ return state.has(lantern, player)
+
+
+def set_region_rules(world: "TunicWorld", ability_unlocks: Dict[str, int]) -> None:
+ multiworld = world.multiworld
+ player = world.player
+ options = world.options
+
+ multiworld.get_entrance("Overworld -> Overworld Holy Cross", player).access_rule = \
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks)
+ multiworld.get_entrance("Overworld -> Beneath the Well", player).access_rule = \
+ lambda state: has_stick(state, player) or state.has(fire_wand, player)
+ multiworld.get_entrance("Overworld -> Dark Tomb", player).access_rule = \
+ lambda state: has_lantern(state, player, options)
+ multiworld.get_entrance("Overworld -> West Garden", player).access_rule = \
+ lambda state: state.has(laurels, player) \
+ or can_ladder_storage(state, player, options)
+ multiworld.get_entrance("Overworld -> Eastern Vault Fortress", player).access_rule = \
+ lambda state: state.has(laurels, player) \
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks) \
+ or can_ladder_storage(state, player, options)
+ # using laurels or ls to get in is covered by the -> Eastern Vault Fortress rules
+ multiworld.get_entrance("Overworld -> Beneath the Vault", player).access_rule = \
+ lambda state: has_lantern(state, player, options) and \
+ has_ability(state, player, prayer, options, ability_unlocks)
+ multiworld.get_entrance("Ruined Atoll -> Library", player).access_rule = \
+ lambda state: state.has_any({grapple, laurels}, player) and \
+ has_ability(state, player, prayer, options, ability_unlocks)
+ multiworld.get_entrance("Overworld -> Quarry", player).access_rule = \
+ lambda state: (has_sword(state, player) or state.has(fire_wand, player)) \
+ and (state.has_any({grapple, laurels}, player) or can_ladder_storage(state, player, options))
+ multiworld.get_entrance("Quarry Back -> Quarry", player).access_rule = \
+ lambda state: has_sword(state, player) or state.has(fire_wand, player)
+ multiworld.get_entrance("Quarry -> Lower Quarry", player).access_rule = \
+ lambda state: has_mask(state, player, options)
+ multiworld.get_entrance("Lower Quarry -> Rooted Ziggurat", player).access_rule = \
+ lambda state: state.has(grapple, player) and has_ability(state, player, prayer, options, ability_unlocks)
+ multiworld.get_entrance("Swamp -> Cathedral", player).access_rule = \
+ lambda state: state.has(laurels, player) and has_ability(state, player, prayer, options, ability_unlocks) \
+ or has_ice_grapple_logic(False, state, player, options, ability_unlocks)
+ multiworld.get_entrance("Overworld -> Spirit Arena", player).access_rule = \
+ lambda state: (state.has(gold_hexagon, player, options.hexagon_goal.value) if options.hexagon_quest.value
+ else state.has_all({red_hexagon, green_hexagon, blue_hexagon}, player)) and \
+ has_ability(state, player, prayer, options, ability_unlocks) and has_sword(state, player)
+
+
+def set_location_rules(world: "TunicWorld", ability_unlocks: Dict[str, int]) -> None:
+ multiworld = world.multiworld
+ player = world.player
+ options = world.options
+
+ forbid_item(multiworld.get_location("Secret Gathering Place - 20 Fairy Reward", player), fairies, player)
+
+ # Ability Shuffle Exclusive Rules
+ set_rule(multiworld.get_location("Far Shore - Page Pickup", player),
+ lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ set_rule(multiworld.get_location("Fortress Courtyard - Chest Near Cave", player),
+ lambda state: has_ability(state, player, prayer, options, ability_unlocks) or state.has(laurels, player)
+ or can_ladder_storage(state, player, options)
+ or (has_ice_grapple_logic(True, state, player, options, ability_unlocks)
+ and has_lantern(state, player, options)))
+ set_rule(multiworld.get_location("Fortress Courtyard - Page Near Cave", player),
+ lambda state: has_ability(state, player, prayer, options, ability_unlocks) or state.has(laurels, player)
+ or can_ladder_storage(state, player, options)
+ or (has_ice_grapple_logic(True, state, player, options, ability_unlocks)
+ and has_lantern(state, player, options)))
+ set_rule(multiworld.get_location("East Forest - Dancing Fox Spirit Holy Cross", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Forest Grave Path - Holy Cross Code by Grave", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("East Forest - Golden Obelisk Holy Cross", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Beneath the Well - [Powered Secret Room] Chest", player),
+ lambda state: has_ability(state, player, prayer, options, ability_unlocks))
+ set_rule(multiworld.get_location("West Garden - [North] Behind Holy Cross Door", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Library Hall - Holy Cross Chest", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Eastern Vault Fortress - [West Wing] Candles Holy Cross", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("West Garden - [Central Highlands] Holy Cross (Blue Lines)", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Quarry - [Back Entrance] Bushes Holy Cross", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("Cathedral - Secret Legend Trophy Chest", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks))
+
+ # Overworld
+ set_rule(multiworld.get_location("Overworld - [Southwest] Fountain Page", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Overworld - [Southwest] Grapple Chest Over Walkway", player),
+ lambda state: state.has_any({grapple, laurels}, player))
+ set_rule(multiworld.get_location("Overworld - [Southwest] West Beach Guarded By Turret 2", player),
+ lambda state: state.has_any({grapple, laurels}, player))
+ set_rule(multiworld.get_location("Far Shore - Secret Chest", player),
+ lambda state: state.has(laurels, player) and has_ability(state, player, prayer, options, ability_unlocks))
+ set_rule(multiworld.get_location("Overworld - [Southeast] Page on Pillar by Swamp", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Old House - Normal Chest", player),
+ lambda state: state.has(house_key, player)
+ or has_ice_grapple_logic(False, state, player, options, ability_unlocks)
+ or (state.has(laurels, player) and options.logic_rules))
+ set_rule(multiworld.get_location("Old House - Holy Cross Chest", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks) and
+ (state.has(house_key, player)
+ or has_ice_grapple_logic(False, state, player, options, ability_unlocks)
+ or (state.has(laurels, player) and options.logic_rules)))
+ set_rule(multiworld.get_location("Old House - Shield Pickup", player),
+ lambda state: state.has(house_key, player)
+ or has_ice_grapple_logic(False, state, player, options, ability_unlocks)
+ or (state.has(laurels, player) and options.logic_rules))
+ set_rule(multiworld.get_location("Overworld - [Northwest] Page on Pillar by Dark Tomb", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Overworld - [Southwest] From West Garden", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Overworld - [West] Chest After Bell", player),
+ lambda state: state.has(laurels, player)
+ or (has_lantern(state, player, options) and has_sword(state, player))
+ or can_ladder_storage(state, player, options))
+ set_rule(multiworld.get_location("Overworld - [Northwest] Chest Beneath Quarry Gate", player),
+ lambda state: state.has_any({grapple, laurels}, player) or options.logic_rules)
+ set_rule(multiworld.get_location("Overworld - [East] Grapple Chest", player),
+ lambda state: state.has(grapple, player))
+ set_rule(multiworld.get_location("Special Shop - Secret Page Pickup", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Sealed Temple - Holy Cross Chest", player),
+ lambda state: has_ability(state, player, holy_cross, options, ability_unlocks) and
+ (state.has(laurels, player)
+ or (has_lantern(state, player, options) and
+ (has_sword(state, player) or state.has(fire_wand, player)))
+ or has_ice_grapple_logic(False, state, player, options, ability_unlocks)))
+ set_rule(multiworld.get_location("Sealed Temple - Page Pickup", player),
+ lambda state: state.has(laurels, player)
+ or (has_lantern(state, player, options) and (has_sword(state, player) or state.has(fire_wand, player)))
+ or has_ice_grapple_logic(False, state, player, options, ability_unlocks))
+ set_rule(multiworld.get_location("West Furnace - Lantern Pickup", player),
+ lambda state: has_stick(state, player) or state.has_any({fire_wand, laurels}, player))
+
+ set_rule(multiworld.get_location("Secret Gathering Place - 10 Fairy Reward", player),
+ lambda state: state.has(fairies, player, 10))
+ set_rule(multiworld.get_location("Secret Gathering Place - 20 Fairy Reward", player),
+ lambda state: state.has(fairies, player, 20))
+ set_rule(multiworld.get_location("Coins in the Well - 3 Coins", player),
+ lambda state: state.has(coins, player, 3))
+ set_rule(multiworld.get_location("Coins in the Well - 6 Coins", player),
+ lambda state: state.has(coins, player, 6))
+ set_rule(multiworld.get_location("Coins in the Well - 10 Coins", player),
+ lambda state: state.has(coins, player, 10))
+ set_rule(multiworld.get_location("Coins in the Well - 15 Coins", player),
+ lambda state: state.has(coins, player, 15))
+
+ # East Forest
+ set_rule(multiworld.get_location("East Forest - Lower Grapple Chest", player),
+ lambda state: state.has(grapple, player))
+ set_rule(multiworld.get_location("East Forest - Lower Dash Chest", player),
+ lambda state: state.has_all({grapple, laurels}, player))
+ set_rule(multiworld.get_location("East Forest - Ice Rod Grapple Chest", player),
+ lambda state: state.has_all({grapple, ice_dagger, fire_wand}, player)
+ and has_ability(state, player, icebolt, options, ability_unlocks))
+
+ # West Garden
+ set_rule(multiworld.get_location("West Garden - [North] Across From Page Pickup", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("West Garden - [West] In Flooded Walkway", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("West Garden - [West Lowlands] Tree Holy Cross Chest", player),
+ lambda state: state.has(laurels, player)
+ and has_ability(state, player, holy_cross, options, ability_unlocks))
+ set_rule(multiworld.get_location("West Garden - [East Lowlands] Page Behind Ice Dagger House", player),
+ lambda state: (state.has(laurels, player) and has_ability(state, player, prayer, options, ability_unlocks))
+ or has_ice_grapple_logic(True, state, player, options, ability_unlocks))
+ set_rule(multiworld.get_location("West Garden - [Central Lowlands] Below Left Walkway", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("West Garden - [Central Highlands] After Garden Knight", player),
+ lambda state: state.has(laurels, player)
+ or (has_lantern(state, player, options) and has_sword(state, player))
+ or can_ladder_storage(state, player, options))
+
+ # Ruined Atoll
+ set_rule(multiworld.get_location("Ruined Atoll - [West] Near Kevin Block", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Ruined Atoll - [East] Locked Room Lower Chest", player),
+ lambda state: state.has_any({laurels, key}, player))
+ set_rule(multiworld.get_location("Ruined Atoll - [East] Locked Room Upper Chest", player),
+ lambda state: state.has_any({laurels, key}, player))
+ set_rule(multiworld.get_location("Librarian - Hexagon Green", player),
+ lambda state: has_sword(state, player) or options.logic_rules)
+
+ # Frog's Domain
+ set_rule(multiworld.get_location("Frog's Domain - Side Room Grapple Secret", player),
+ lambda state: state.has_any({grapple, laurels}, player))
+ set_rule(multiworld.get_location("Frog's Domain - Grapple Above Hot Tub", player),
+ lambda state: state.has_any({grapple, laurels}, player))
+ set_rule(multiworld.get_location("Frog's Domain - Escape Chest", player),
+ lambda state: state.has_any({grapple, laurels}, player))
+
+ # Eastern Vault Fortress
+ set_rule(multiworld.get_location("Fortress Leaf Piles - Secret Chest", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Fortress Arena - Siege Engine/Vault Key Pickup", player),
+ lambda state: has_sword(state, player) and
+ (has_ability(state, player, prayer, options, ability_unlocks)
+ or has_ice_grapple_logic(False, state, player, options, ability_unlocks)))
+ set_rule(multiworld.get_location("Fortress Arena - Hexagon Red", player),
+ lambda state: state.has(vault_key, player) and
+ (has_ability(state, player, prayer, options, ability_unlocks)
+ or has_ice_grapple_logic(False, state, player, options, ability_unlocks)))
+
+ # Beneath the Vault
+ set_rule(multiworld.get_location("Beneath the Fortress - Bridge", player),
+ lambda state: has_stick(state, player) or state.has_any({laurels, fire_wand}, player))
+ set_rule(multiworld.get_location("Beneath the Fortress - Obscured Behind Waterfall", player),
+ lambda state: has_stick(state, player) and has_lantern(state, player, options))
+
+ # Quarry
+ set_rule(multiworld.get_location("Quarry - [Central] Above Ladder Dash Chest", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Quarry - [West] Upper Area Bombable Wall", player),
+ lambda state: has_mask(state, player, options))
+ # nmg - kill boss scav with orb + firecracker, or similar
+ set_rule(multiworld.get_location("Rooted Ziggurat Lower - Hexagon Blue", player),
+ lambda state: has_sword(state, player) or (state.has(grapple, player) and options.logic_rules))
+
+ # Swamp
+ set_rule(multiworld.get_location("Cathedral Gauntlet - Gauntlet Reward", player),
+ lambda state: state.has(laurels, player) and state.has(fire_wand, player) and has_sword(state, player))
+ set_rule(multiworld.get_location("Swamp - [Entrance] Above Entryway", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Swamp - [South Graveyard] Upper Walkway Dash Chest", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Swamp - [Outside Cathedral] Obscured Behind Memorial", player),
+ lambda state: state.has(laurels, player))
+ set_rule(multiworld.get_location("Swamp - [South Graveyard] 4 Orange Skulls", player),
+ lambda state: has_sword(state, player))
+
+ # Hero's Grave
+ set_rule(multiworld.get_location("Hero's Grave - Tooth Relic", player),
+ lambda state: state.has(laurels, player) and has_ability(state, player, prayer, options, ability_unlocks))
+ set_rule(multiworld.get_location("Hero's Grave - Mushroom Relic", player),
+ lambda state: state.has(laurels, player) and has_ability(state, player, prayer, options, ability_unlocks))
+ set_rule(multiworld.get_location("Hero's Grave - Ash Relic", player),
+ lambda state: state.has(laurels, player) and has_ability(state, player, prayer, options, ability_unlocks))
+ set_rule(multiworld.get_location("Hero's Grave - Flowers Relic", player),
+ lambda state: state.has(laurels, player) and has_ability(state, player, prayer, options, ability_unlocks))
+ set_rule(multiworld.get_location("Hero's Grave - Effigy Relic", player),
+ lambda state: state.has(laurels, player) and has_ability(state, player, prayer, options, ability_unlocks))
+ set_rule(multiworld.get_location("Hero's Grave - Feathers Relic", player),
+ lambda state: state.has(laurels, player) and has_ability(state, player, prayer, options, ability_unlocks))
diff --git a/worlds/tunic/test/__init__.py b/worlds/tunic/test/__init__.py
new file mode 100644
index 000000000000..d7ae47f7d74c
--- /dev/null
+++ b/worlds/tunic/test/__init__.py
@@ -0,0 +1,6 @@
+from test.bases import WorldTestBase
+
+
+class TunicTestBase(WorldTestBase):
+ game = "TUNIC"
+ player: int = 1
\ No newline at end of file
diff --git a/worlds/tunic/test/test_access.py b/worlds/tunic/test/test_access.py
new file mode 100644
index 000000000000..1c4f06d50461
--- /dev/null
+++ b/worlds/tunic/test/test_access.py
@@ -0,0 +1,70 @@
+from . import TunicTestBase
+from .. import options
+
+
+class TestAccess(TunicTestBase):
+ # test whether you can get into the temple without laurels
+ def test_temple_access(self):
+ self.collect_all_but(["Hero's Laurels", "Lantern"])
+ self.assertFalse(self.can_reach_location("Sealed Temple - Page Pickup"))
+ self.collect_by_name(["Lantern"])
+ self.assertTrue(self.can_reach_location("Sealed Temple - Page Pickup"))
+
+ # test that the wells function properly. Since fairies is written the same way, that should succeed too
+ def test_wells(self):
+ self.collect_all_but(["Golden Coin"])
+ self.assertFalse(self.can_reach_location("Coins in the Well - 3 Coins"))
+ self.collect_by_name(["Golden Coin"])
+ self.assertTrue(self.can_reach_location("Coins in the Well - 3 Coins"))
+
+
+class TestStandardShuffle(TunicTestBase):
+ options = {options.AbilityShuffling.internal_name: options.AbilityShuffling.option_true}
+
+ # test that you need to get holy cross to open the hc door in overworld
+ def test_hc_door(self):
+ self.assertFalse(self.can_reach_location("Fountain Cross Door - Page Pickup"))
+ self.collect_by_name("Pages 42-43 (Holy Cross)")
+ self.assertTrue(self.can_reach_location("Fountain Cross Door - Page Pickup"))
+
+
+class TestHexQuestShuffle(TunicTestBase):
+ options = {options.HexagonQuest.internal_name: options.HexagonQuest.option_true,
+ options.AbilityShuffling.internal_name: options.AbilityShuffling.option_true}
+
+ # test that you need the gold questagons to open the hc door in overworld
+ def test_hc_door_hex_shuffle(self):
+ self.assertFalse(self.can_reach_location("Fountain Cross Door - Page Pickup"))
+ self.collect_by_name("Gold Questagon")
+ self.assertTrue(self.can_reach_location("Fountain Cross Door - Page Pickup"))
+
+
+class TestHexQuestNoShuffle(TunicTestBase):
+ options = {options.HexagonQuest.internal_name: options.HexagonQuest.option_true,
+ options.AbilityShuffling.internal_name: options.AbilityShuffling.option_false}
+
+ # test that you can get the item behind the overworld hc door with nothing and no ability shuffle
+ def test_hc_door_no_shuffle(self):
+ self.assertTrue(self.can_reach_location("Fountain Cross Door - Page Pickup"))
+
+
+class TestNormalGoal(TunicTestBase):
+ options = {options.HexagonQuest.internal_name: options.HexagonQuest.option_false}
+
+ # test that you need the three colored hexes to reach the Heir in standard
+ def test_normal_goal(self):
+ location = ["The Heir"]
+ items = [["Red Questagon", "Blue Questagon", "Green Questagon"]]
+ self.assertAccessDependency(location, items)
+
+
+class TestER(TunicTestBase):
+ options = {options.EntranceRando.internal_name: options.EntranceRando.option_yes,
+ options.AbilityShuffling.internal_name: options.AbilityShuffling.option_true,
+ options.HexagonQuest.internal_name: options.HexagonQuest.option_false}
+
+ def test_overworld_hc_chest(self):
+ # test to see that static connections are working properly -- this chest requires holy cross and is in Overworld
+ self.assertFalse(self.can_reach_location("Overworld - [Southwest] Flowers Holy Cross"))
+ self.collect_by_name(["Pages 42-43 (Holy Cross)"])
+ self.assertTrue(self.can_reach_location("Overworld - [Southwest] Flowers Holy Cross"))
diff --git a/worlds/undertale/__init__.py b/worlds/undertale/__init__.py
index 9e784a4a59a0..0694456a6b12 100644
--- a/worlds/undertale/__init__.py
+++ b/worlds/undertale/__init__.py
@@ -29,7 +29,7 @@ def data_path(file_name: str):
class UndertaleWeb(WebWorld):
tutorials = [Tutorial(
- "Multiworld Setup Tutorial",
+ "Multiworld Setup Guide",
"A guide to setting up the Archipelago Undertale software on your computer. This guide covers "
"single-player, multiworld, and related software.",
"English",
diff --git a/worlds/undertale/docs/en_Undertale.md b/worlds/undertale/docs/en_Undertale.md
index 7ff5d55edad9..02fc32f0abc6 100644
--- a/worlds/undertale/docs/en_Undertale.md
+++ b/worlds/undertale/docs/en_Undertale.md
@@ -1,8 +1,8 @@
# Undertale
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What is considered a location check in Undertale?
diff --git a/worlds/undertale/docs/setup_en.md b/worlds/undertale/docs/setup_en.md
index 3c20b614d3fa..f1f740959127 100644
--- a/worlds/undertale/docs/setup_en.md
+++ b/worlds/undertale/docs/setup_en.md
@@ -61,4 +61,4 @@ gameplay differences at the bottom.
### Where do I get a YAML file?
-You can customize your settings by visiting the [Undertale Player Settings Page](/games/Undertale/player-settings)
+You can customize your options by visiting the [Undertale Player Options Page](/games/Undertale/player-options)
diff --git a/worlds/v6/Options.py b/worlds/v6/Options.py
index 107fbab465e1..1950d1bcbd02 100644
--- a/worlds/v6/Options.py
+++ b/worlds/v6/Options.py
@@ -1,8 +1,10 @@
import typing
-from Options import Option, DeathLink, Range, Toggle
+from dataclasses import dataclass
+from Options import Option, DeathLink, Range, Toggle, PerGameCommonOptions
class DoorCost(Range):
"""Amount of Trinkets required to enter Areas. Set to 0 to disable artificial locks."""
+ display_name = "Door Cost"
range_start = 0
range_end = 3
default = 3
@@ -13,6 +15,7 @@ class AreaCostRandomizer(Toggle):
class DeathLinkAmnesty(Range):
"""Amount of Deaths to take before sending a DeathLink signal, for balancing difficulty"""
+ display_name = "Death Link Amnesty"
range_start = 0
range_end = 30
default = 15
@@ -25,11 +28,11 @@ class MusicRandomizer(Toggle):
"""Randomize Music"""
display_name = "Music Randomizer"
-v6_options: typing.Dict[str,type(Option)] = {
- "MusicRandomizer": MusicRandomizer,
- "AreaRandomizer": AreaRandomizer,
- "DoorCost": DoorCost,
- "AreaCostRandomizer": AreaCostRandomizer,
- "death_link": DeathLink,
- "DeathLinkAmnesty": DeathLinkAmnesty
-}
\ No newline at end of file
+@dataclass
+class V6Options(PerGameCommonOptions):
+ music_rando: MusicRandomizer
+ area_rando: AreaRandomizer
+ door_cost: DoorCost
+ area_cost: AreaCostRandomizer
+ death_link: DeathLink
+ death_link_amnesty: DeathLinkAmnesty
diff --git a/worlds/v6/Regions.py b/worlds/v6/Regions.py
index 5a8f0315f44a..f6e9ee753890 100644
--- a/worlds/v6/Regions.py
+++ b/worlds/v6/Regions.py
@@ -31,14 +31,3 @@ def create_regions(world: MultiWorld, player: int):
locWrp_names = ["Edge Games"]
regWrp.locations += [V6Location(player, loc_name, location_table[loc_name], regWrp) for loc_name in locWrp_names]
world.regions.append(regWrp)
-
-
-def connect_regions(world: MultiWorld, player: int, source: str, target: str, rule):
- sourceRegion = world.get_region(source, player)
- targetRegion = world.get_region(target, player)
-
- connection = Entrance(player,'', sourceRegion)
- connection.access_rule = rule
-
- sourceRegion.exits.append(connection)
- connection.connect(targetRegion)
\ No newline at end of file
diff --git a/worlds/v6/Rules.py b/worlds/v6/Rules.py
index ecb34f2f32ff..bf0d60499eb5 100644
--- a/worlds/v6/Rules.py
+++ b/worlds/v6/Rules.py
@@ -1,6 +1,6 @@
import typing
from ..generic.Rules import add_rule
-from .Regions import connect_regions, v6areas
+from .Regions import v6areas
def _has_trinket_range(state, player, start, end) -> bool:
@@ -10,34 +10,36 @@ def _has_trinket_range(state, player, start, end) -> bool:
return True
-def set_rules(world, player, area_connections: typing.Dict[int, int], area_cost_map: typing.Dict[int, int]):
+def set_rules(multiworld, options, player, area_connections: typing.Dict[int, int], area_cost_map: typing.Dict[int, int]):
areashuffle = list(range(len(v6areas)))
- if world.AreaRandomizer[player].value:
- world.random.shuffle(areashuffle)
+ if options.area_rando:
+ multiworld.random.shuffle(areashuffle)
area_connections.update({(index + 1): (value + 1) for index, value in enumerate(areashuffle)})
area_connections.update({0: 0})
- if world.AreaCostRandomizer[player].value:
- world.random.shuffle(areashuffle)
+ if options.area_cost:
+ multiworld.random.shuffle(areashuffle)
area_cost_map.update({(index + 1): (value + 1) for index, value in enumerate(areashuffle)})
area_cost_map.update({0: 0})
+ menu_region = multiworld.get_region("Menu", player)
for i in range(1, 5):
- connect_regions(world, player, "Menu", v6areas[area_connections[i] - 1],
- lambda state, i=i: _has_trinket_range(state, player,
- world.DoorCost[player].value * (area_cost_map[i] - 1),
- world.DoorCost[player].value * area_cost_map[i]))
+ target_region = multiworld.get_region(v6areas[area_connections[i] - 1], player)
+ menu_region.connect(connecting_region=target_region,
+ rule=lambda state, i=i: _has_trinket_range(state, player,
+ options.door_cost * (area_cost_map[i] - 1),
+ options.door_cost * area_cost_map[i]))
# Special Rule for V
- add_rule(world.get_location("V", player), lambda state: state.can_reach("Laboratory", 'Region', player) and
+ add_rule(multiworld.get_location("V", player), lambda state: state.can_reach("Laboratory", 'Region', player) and
state.can_reach("The Tower", 'Region', player) and
state.can_reach("Space Station 2", 'Region', player) and
state.can_reach("Warp Zone", 'Region', player))
# Special Rule for NPC Trinket
- add_rule(world.get_location("NPC Trinket", player),
+ add_rule(multiworld.get_location("NPC Trinket", player),
lambda state: state.can_reach("Laboratory", 'Region', player) or
(state.can_reach("The Tower", 'Region', player) and
state.can_reach("Space Station 2", 'Region', player) and
state.can_reach("Warp Zone", 'Region', player)))
- world.completion_condition[player] = lambda state: state.can_reach("V", 'Location', player)
+ multiworld.completion_condition[player] = lambda state: state.can_reach("V", 'Location', player)
diff --git a/worlds/v6/__init__.py b/worlds/v6/__init__.py
index 6ff7fba60c2d..30a76f82cce6 100644
--- a/worlds/v6/__init__.py
+++ b/worlds/v6/__init__.py
@@ -2,7 +2,7 @@
import os, json
from .Items import item_table, V6Item
from .Locations import location_table, V6Location
-from .Options import v6_options
+from .Options import V6Options
from .Rules import set_rules
from .Regions import create_regions
from BaseClasses import Item, ItemClassification, Tutorial
@@ -41,7 +41,7 @@ class V6World(World):
music_map: typing.Dict[int,int]
- option_definitions = v6_options
+ options_dataclass = V6Options
def create_regions(self):
create_regions(self.multiworld, self.player)
@@ -49,7 +49,7 @@ def create_regions(self):
def set_rules(self):
self.area_connections = {}
self.area_cost_map = {}
- set_rules(self.multiworld, self.player, self.area_connections, self.area_cost_map)
+ set_rules(self.multiworld, self.options, self.player, self.area_connections, self.area_cost_map)
def create_item(self, name: str) -> Item:
return V6Item(name, ItemClassification.progression, item_table[name], self.player)
@@ -61,7 +61,7 @@ def create_items(self):
def generate_basic(self):
musiclist_o = [1,2,3,4,9,12]
musiclist_s = musiclist_o.copy()
- if self.multiworld.MusicRandomizer[self.player].value:
+ if self.options.music_rando:
self.multiworld.random.shuffle(musiclist_s)
self.music_map = dict(zip(musiclist_o, musiclist_s))
@@ -69,10 +69,10 @@ def fill_slot_data(self):
return {
"MusicRando": self.music_map,
"AreaRando": self.area_connections,
- "DoorCost": self.multiworld.DoorCost[self.player].value,
+ "DoorCost": self.options.door_cost.value,
"AreaCostRando": self.area_cost_map,
- "DeathLink": self.multiworld.death_link[self.player].value,
- "DeathLink_Amnesty": self.multiworld.DeathLinkAmnesty[self.player].value
+ "DeathLink": self.options.death_link.value,
+ "DeathLink_Amnesty": self.options.death_link_amnesty.value
}
def generate_output(self, output_directory: str):
diff --git a/worlds/v6/docs/en_VVVVVV.md b/worlds/v6/docs/en_VVVVVV.md
index 5c2aa8fec957..c5790e01c5dd 100644
--- a/worlds/v6/docs/en_VVVVVV.md
+++ b/worlds/v6/docs/en_VVVVVV.md
@@ -1,9 +1,9 @@
# VVVVVV
-## Where is the settings page?
+## Where is the options page?
-The player settings page for this game contains all the options you need to configure and export a config file. Player
-settings page link: [VVVVVV Player Settings Page](../player-settings).
+The player options page for this game contains all the options you need to configure and export a config file. Player
+options page link: [VVVVVV Player Options Page](../player-options).
## What does randomization do to this game?
All 20 Trinkets are now Location Checks and may not actually contain Trinkets, but Items for different games.
diff --git a/worlds/v6/docs/setup_en.md b/worlds/v6/docs/setup_en.md
index 7adf5948c7e4..a23b6c5b252a 100644
--- a/worlds/v6/docs/setup_en.md
+++ b/worlds/v6/docs/setup_en.md
@@ -30,7 +30,7 @@ If everything worked out, you will see a textbox informing you the connection ha
# Playing offline
-To play offline, first generate a seed on the game's settings page.
+To play offline, first generate a seed on the game's options page.
Create a room and download the `.apv6` file, include the offline single-player launch option described above.
## Installation Troubleshooting
diff --git a/worlds/wargroove/docs/en_Wargroove.md b/worlds/wargroove/docs/en_Wargroove.md
index f08902535d4b..31fd8c81301c 100644
--- a/worlds/wargroove/docs/en_Wargroove.md
+++ b/worlds/wargroove/docs/en_Wargroove.md
@@ -1,8 +1,8 @@
# Wargroove (Steam, Windows)
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
diff --git a/worlds/wargroove/docs/wargroove_en.md b/worlds/wargroove/docs/wargroove_en.md
index 1954dc013924..9c2645178aa2 100644
--- a/worlds/wargroove/docs/wargroove_en.md
+++ b/worlds/wargroove/docs/wargroove_en.md
@@ -38,7 +38,7 @@ This should install the mod and campaign for you.
## Starting a Multiworld game
1. Start the Wargroove Client and connect to the server. Enter your username from your
-[settings file.](/games/Wargroove/player-settings)
+[options file.](/games/Wargroove/player-options)
2. Start Wargroove and play the Archipelago campaign by going to `Story->Campaign->Custom->Archipelago`.
## Ending a Multiworld game
diff --git a/worlds/witness/Options.py b/worlds/witness/Options.py
deleted file mode 100644
index 4c4b4f76267f..000000000000
--- a/worlds/witness/Options.py
+++ /dev/null
@@ -1,211 +0,0 @@
-from dataclasses import dataclass
-from Options import Toggle, DefaultOnToggle, Range, Choice, PerGameCommonOptions
-
-
-class DisableNonRandomizedPuzzles(Toggle):
- """Disables puzzles that cannot be randomized.
- This includes many puzzles that heavily involve the environment, such as Shadows, Monastery or Orchard.
- The lasers for those areas will activate as you solve optional puzzles, such as Discarded Panels.
- Additionally, the panels activating Monastery Laser and Jungle Popup Wall will be on from the start."""
- display_name = "Disable non randomized puzzles"
-
-
-class EarlyCaves(Choice):
- """Adds an item that opens the Caves Shortcuts to Swamp and Mountain,
- allowing early access to the Caves even if you are not playing a remote Door Shuffle mode.
- You can either add this item to the pool to be found on one of your randomized checks,
- or you can outright start with it and have immediate access to the Caves.
- If you choose "add_to_pool" and you are already playing a remote Door Shuffle mode, this setting will do nothing."""
- display_name = "Early Caves"
- option_off = 0
- option_add_to_pool = 1
- option_starting_inventory = 2
-
-
-class ShuffleSymbols(DefaultOnToggle):
- """You will need to unlock puzzle symbols as items to be able to solve the panels that contain those symbols.
- If you turn this off, there will be no progression items in the game unless you turn on door shuffle."""
- display_name = "Shuffle Symbols"
-
-
-class ShuffleLasers(Toggle):
- """If on, the 11 lasers are turned into items and will activate on their own upon receiving them.
- Note: There is a visual bug that can occur with the Desert Laser. It does not affect gameplay - The Laser can still
- be redirected as normal, for both applications of redirection."""
- display_name = "Shuffle Lasers"
-
-
-class ShuffleDoors(Choice):
- """If on, opening doors, moving bridges etc. will require a "key".
- If set to "panels", the panel on the door will be locked until receiving its corresponding key.
- If set to "doors", the door will open immediately upon receiving its key. Door panels are added as location checks.
- "Mixed" includes all doors from "doors", and all control panels (bridges, elevators etc.) from "panels"."""
- display_name = "Shuffle Doors"
- option_off = 0
- option_panels = 1
- option_doors = 2
- option_mixed = 3
-
-
-class DoorGroupings(Choice):
- """If set to "none", there will be one key for every door, resulting in up to 120 keys being added to the item pool.
- If set to "regional", all doors in the same general region will open at once with a single key,
- reducing the amount of door items and complexity."""
- display_name = "Door Groupings"
- option_off = 0
- option_regional = 1
-
-
-class ShuffleBoat(DefaultOnToggle):
- """If set, adds a "Boat" item to the item pool. Before receiving this item, you will not be able to use the boat."""
- display_name = "Shuffle Boat"
-
-
-class ShuffleDiscardedPanels(Toggle):
- """Add Discarded Panels into the location pool.
- Solving certain Discarded Panels may still be necessary to beat the game, even if this is off - The main example
- of this being the alternate activation triggers in disable_non_randomized."""
-
- display_name = "Shuffle Discarded Panels"
-
-
-class ShuffleVaultBoxes(Toggle):
- """Add Vault Boxes to the location pool."""
- display_name = "Shuffle Vault Boxes"
-
-
-class ShuffleEnvironmentalPuzzles(Choice):
- """
- Add Environmental/Obelisk Puzzles into the location pool.
- In "individual", every Environmental Puzzle sends an item.
- In "obelisk_sides", completing every puzzle on one side of an Obelisk sends an item.
- Note: In Obelisk Sides, any EPs excluded through another setting will be counted as pre-completed on their Obelisk.
- """
- display_name = "Shuffle Environmental Puzzles"
- option_off = 0
- option_individual = 1
- option_obelisk_sides = 2
-
-
-class ShuffleDog(Toggle):
- """Add petting the Town dog into the location pool."""
-
- display_name = "Pet the Dog"
-
-
-class EnvironmentalPuzzlesDifficulty(Choice):
- """
- When "Shuffle Environmental Puzzles" is on, this setting governs which EPs are eligible for the location pool.
- On "eclipse", every EP in the game is eligible, including the 1-hour-long "Theater Eclipse EP".
- On "tedious", Theater Eclipse EP is excluded from the location pool.
- On "normal", several other difficult or long EPs are excluded as well.
- """
- display_name = "Environmental Puzzles Difficulty"
- option_normal = 0
- option_tedious = 1
- option_eclipse = 2
-
-
-class ShufflePostgame(Toggle):
- """Adds locations into the pool that are guaranteed to become accessible after or at the same time as your goal.
- Use this if you don't play with release on victory. IMPORTANT NOTE: The possibility of your second
- "Progressive Dots" showing up in the Caves is ignored, they will still be considered "postgame" in base settings."""
- display_name = "Shuffle Postgame"
-
-
-class VictoryCondition(Choice):
- """Change the victory condition from the original game's ending (elevator) to beating the Challenge
- or solving the mountaintop box, either using the short solution
- (7 lasers or whatever you've changed it to) or the long solution (11 lasers or whatever you've changed it to)."""
- display_name = "Victory Condition"
- option_elevator = 0
- option_challenge = 1
- option_mountain_box_short = 2
- option_mountain_box_long = 3
-
-
-class PuzzleRandomization(Choice):
- """Puzzles in this randomizer are randomly generated. This setting changes the difficulty/types of puzzles."""
- display_name = "Puzzle Randomization"
- option_sigma_normal = 0
- option_sigma_expert = 1
- option_none = 2
-
-
-class MountainLasers(Range):
- """Sets the amount of beams required to enter the final area."""
- display_name = "Required Lasers for Mountain Entry"
- range_start = 1
- range_end = 7
- default = 7
-
-
-class ChallengeLasers(Range):
- """Sets the amount of beams required to enter the Caves through the Mountain Bottom Floor Discard."""
- display_name = "Required Lasers for Challenge"
- range_start = 1
- range_end = 11
- default = 11
-
-
-class ElevatorsComeToYou(Toggle):
- """If true, the Quarry Elevator, Bunker Elevator and Swamp Long Bridge will "come to you" if you approach them.
- This does actually affect logic as it allows unintended backwards / early access into these areas."""
- display_name = "All Bridges & Elevators come to you"
-
-
-class TrapPercentage(Range):
- """Replaces junk items with traps, at the specified rate."""
- display_name = "Trap Percentage"
- range_start = 0
- range_end = 100
- default = 20
-
-
-class PuzzleSkipAmount(Range):
- """Adds this number of Puzzle Skips into the pool, if there is room. Puzzle Skips let you skip one panel.
- Works on most panels in the game - The only big exception is The Challenge."""
- display_name = "Puzzle Skips"
- range_start = 0
- range_end = 30
- default = 10
-
-
-class HintAmount(Range):
- """Adds hints to Audio Logs. If set to a low amount, up to 2 additional duplicates of each hint will be added.
- Remaining Audio Logs will have junk hints."""
- display_name = "Hints on Audio Logs"
- range_start = 0
- range_end = 49
- default = 10
-
-
-class DeathLink(Toggle):
- """If on: Whenever you fail a puzzle (with some exceptions), everyone who is also on Death Link dies.
- The effect of a "death" in The Witness is a Power Surge."""
- display_name = "Death Link"
-
-
-@dataclass
-class TheWitnessOptions(PerGameCommonOptions):
- puzzle_randomization: PuzzleRandomization
- shuffle_symbols: ShuffleSymbols
- shuffle_doors: ShuffleDoors
- door_groupings: DoorGroupings
- shuffle_boat: ShuffleBoat
- shuffle_lasers: ShuffleLasers
- disable_non_randomized_puzzles: DisableNonRandomizedPuzzles
- shuffle_discarded_panels: ShuffleDiscardedPanels
- shuffle_vault_boxes: ShuffleVaultBoxes
- shuffle_EPs: ShuffleEnvironmentalPuzzles
- EP_difficulty: EnvironmentalPuzzlesDifficulty
- shuffle_postgame: ShufflePostgame
- victory_condition: VictoryCondition
- mountain_lasers: MountainLasers
- challenge_lasers: ChallengeLasers
- early_caves: EarlyCaves
- elevators_come_to_you: ElevatorsComeToYou
- trap_percentage: TrapPercentage
- puzzle_skip_amount: PuzzleSkipAmount
- hint_amount: HintAmount
- death_link: DeathLink
diff --git a/worlds/witness/WitnessItems.txt b/worlds/witness/WitnessItems.txt
index 750d6bd4ebec..28dc4a4d9784 100644
--- a/worlds/witness/WitnessItems.txt
+++ b/worlds/witness/WitnessItems.txt
@@ -16,6 +16,7 @@ Symbols:
72 - Colored Squares
80 - Arrows
200 - Progressive Dots - Dots,Full Dots
+210 - Progressive Symmetry - Symmetry,Colored Dots
260 - Progressive Stars - Stars,Stars + Same Colored Symbol
Useful:
@@ -29,22 +30,26 @@ Filler:
#503 - Energy Fill (Max) - 1
Traps:
-600 - Slowness - 8
+600 - Slowness - 6
610 - Power Surge - 2
+615 - Bonk - 1
Jokes:
650 - Functioning Brain
Doors:
1100 - Glass Factory Entry (Panel) - 0x01A54
-1101 - Tutorial Outpost Entry (Panel) - 0x0A171
-1102 - Tutorial Outpost Exit (Panel) - 0x04CA4
+1101 - Outside Tutorial Outpost Entry (Panel) - 0x0A171
+1102 - Outside Tutorial Outpost Exit (Panel) - 0x04CA4
1105 - Symmetry Island Lower (Panel) - 0x000B0
1107 - Symmetry Island Upper (Panel) - 0x1C349
+1108 - Desert Surface 3 Control (Panel) - 0x09FA0
+1109 - Desert Surface 8 Control (Panel) - 0x09F86
1110 - Desert Light Room Entry (Panel) - 0x0C339
1111 - Desert Flood Controls (Panel) - 0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B
1112 - Desert Light Control (Panel) - 0x09FAA
1113 - Desert Flood Room Entry (Panel) - 0x0A249
+1114 - Desert Elevator Room Hexagonal Control (Panel) - 0x0A015
1115 - Quarry Elevator Control (Panel) - 0x17CC4
1117 - Quarry Entry 1 (Panel) - 0x09E57
1118 - Quarry Entry 2 (Panel) - 0x17C09
@@ -69,6 +74,7 @@ Doors:
1167 - Town Maze Rooftop Bridge (Panel) - 0x2896A
1169 - Town Windmill Entry (Panel) - 0x17F5F
1172 - Town Cargo Box Entry (Panel) - 0x0A0C8
+1173 - Town Desert Laser Redirect Control (Panel) - 0x09F98
1182 - Windmill Turn Control (Panel) - 0x17D02
1184 - Theater Entry (Panel) - 0x17F89
1185 - Theater Video Input (Panel) - 0x00815
@@ -162,9 +168,9 @@ Doors:
1750 - Theater Entry (Door) - 0x17F88
1753 - Theater Exit Left (Door) - 0x0A16D
1756 - Theater Exit Right (Door) - 0x3CCDF
-1759 - Jungle Bamboo Laser Shortcut (Door) - 0x3873B
+1759 - Jungle Laser Shortcut (Door) - 0x3873B
1760 - Jungle Popup Wall (Door) - 0x1475B
-1762 - River Monastery Garden Shortcut (Door) - 0x0CF2A
+1762 - Jungle Monastery Garden Shortcut (Door) - 0x0CF2A
1765 - Bunker Entry (Door) - 0x0C2A4
1768 - Bunker Tinted Glass Door - 0x17C79
1771 - Bunker UV Room Entry (Door) - 0x0C2A3
@@ -189,7 +195,7 @@ Doors:
1828 - Mountain Floor 2 Exit (Door) - 0x09EDD
1831 - Mountain Floor 2 Staircase Far (Door) - 0x09E07
1834 - Mountain Bottom Floor Giant Puzzle Exit (Door) - 0x09F89
-1840 - Mountain Bottom Floor Final Room Entry (Door) - 0x0C141
+1840 - Mountain Bottom Floor Pillars Room Entry (Door) - 0x0C141
1843 - Mountain Bottom Floor Rock (Door) - 0x17F33
1846 - Caves Entry (Door) - 0x2D77D
1849 - Caves Pillar Door - 0x019A5
@@ -231,18 +237,18 @@ Doors:
1984 - Caves Shortcuts - 0x2D859,0x2D73F
1987 - Tunnels Doors - 0x27739,0x27263,0x09E87,0x0348A
-2000 - Desert Control Panels - 0x09FAA,0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B
+2000 - Desert Control Panels - 0x09FAA,0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B,0x0A015,0x09FA0,0x09F86
2005 - Quarry Stoneworks Control Panels - 0x03678,0x03676,0x03679,0x03675
2010 - Quarry Boathouse Control Panels - 0x03852,0x03858,0x275FA
-2015 - Town Control Panels - 0x2896A,0x334D8
+2015 - Town Control Panels - 0x2896A,0x334D8,0x09F98
2020 - Windmill & Theater Control Panels - 0x17D02,0x00815
2025 - Bunker Control Panels - 0x34BC5,0x34BC6,0x0A079
2030 - Swamp Control Panels - 0x00609,0x18488,0x181F5,0x17E2B,0x17C0A,0x17E07
2035 - Mountain & Caves Control Panels - 0x09ED8,0x09E86,0x09E39,0x09EEB,0x335AB,0x335AC,0x3369D
2100 - Symmetry Island Panels - 0x1C349,0x000B0
-2101 - Tutorial Outpost Panels - 0x0A171,0x04CA4
-2105 - Desert Panels - 0x09FAA,0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B,0x0C339,0x0A249
+2101 - Outside Tutorial Outpost Panels - 0x0A171,0x04CA4
+2105 - Desert Panels - 0x09FAA,0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B,0x0C339,0x0A249,0x0A015,0x09FA0,0x09F86
2110 - Quarry Outside Panels - 0x17C09,0x09E57,0x17CC4
2115 - Quarry Stoneworks Panels - 0x01E5A,0x01E59,0x03678,0x03676,0x03679,0x03675
2120 - Quarry Boathouse Panels - 0x03852,0x03858,0x275FA
@@ -250,6 +256,7 @@ Doors:
2125 - Monastery Panels - 0x09D9B,0x00C92,0x00B10
2130 - Town Church & RGB House Panels - 0x28998,0x28A0D,0x334D8
2135 - Town Maze Panels - 0x2896A,0x28A79
+2137 - Town Dockside House Panels - 0x0A0C8,0x09F98
2140 - Windmill & Theater Panels - 0x17D02,0x00815,0x17F5F,0x17F89,0x0A168,0x33AB2
2145 - Treehouse Panels - 0x0A182,0x0288C,0x02886,0x2700B,0x17CBC,0x037FF
2150 - Bunker Panels - 0x34BC5,0x34BC6,0x0A079,0x0A099,0x17C2E
@@ -258,6 +265,13 @@ Doors:
2165 - Caves Panels - 0x3369D,0x00FF8,0x0A16E,0x335AB,0x335AC
2170 - Tunnels Panels - 0x09E85,0x039B4
+2200 - Desert Obelisk Key - 0x0332B,0x03367,0x28B8A,0x037B6,0x037B2,0x000F7,0x3351D,0x0053C,0x00771,0x335C8,0x335C9,0x337F8,0x037BB,0x220E4,0x220E5,0x334B9,0x334BC,0x22106,0x0A14C,0x0A14D,0x00359
+2201 - Monastery Obelisk Key - 0x03ABC,0x03ABE,0x03AC0,0x03AC4,0x03AC5,0x03BE2,0x03BE3,0x0A409,0x006E5,0x006E6,0x006E7,0x034A7,0x034AD,0x034AF,0x03DAB,0x03DAC,0x03DAD,0x03E01,0x289F4,0x289F5,0x00263
+2202 - Treehouse Obelisk Key - 0x0053D,0x0053E,0x00769,0x33721,0x220A7,0x220BD,0x03B22,0x03B23,0x03B24,0x03B25,0x03A79,0x28ABD,0x28ABE,0x3388F,0x28B29,0x28B2A,0x018B6,0x033BE,0x033BF,0x033DD,0x033E5,0x28AE9,0x3348F,0x00097
+2203 - Mountainside Obelisk Key - 0x001A3,0x335AE,0x000D3,0x035F5,0x09D5D,0x09D5E,0x09D63,0x3370E,0x035DE,0x03601,0x03603,0x03D0D,0x3369A,0x336C8,0x33505,0x03A9E,0x016B2,0x3365F,0x03731,0x036CE,0x03C07,0x03A93,0x03AA6,0x3397C,0x0105D,0x0A304,0x035CB,0x035CF,0x00367
+2204 - Quarry Obelisk Key - 0x28A7B,0x005F6,0x00859,0x17CB9,0x28A4A,0x334B6,0x00614,0x0069D,0x28A4C,0x289CF,0x289D1,0x33692,0x03E77,0x03E7C,0x22073
+2205 - Town Obelisk Key - 0x035C7,0x01848,0x03D06,0x33530,0x33600,0x28A2F,0x28A37,0x334A3,0x3352F,0x33857,0x33879,0x03C19,0x28B30,0x035C9,0x03335,0x03412,0x038A6,0x038AA,0x03E3F,0x03E40,0x28B8E,0x28B91,0x03BCE,0x03BCF,0x03BD1,0x339B6,0x33A20,0x33A29,0x33A2A,0x33B06,0x0A16C
+
Lasers:
1500 - Symmetry Laser - 0x00509
1501 - Desert Laser - 0x012FB
diff --git a/worlds/witness/WitnessLogic.txt b/worlds/witness/WitnessLogic.txt
index acfbe8c14eb0..e3bacfb4b0e4 100644
--- a/worlds/witness/WitnessLogic.txt
+++ b/worlds/witness/WitnessLogic.txt
@@ -1,12 +1,14 @@
+==Tutorial (Inside)==
+
Menu (Menu) - Entry - True:
Entry (Entry):
-First Hallway (First Hallway) - Entry - True - First Hallway Room - 0x00064:
+Tutorial First Hallway (Tutorial First Hallway) - Entry - True - Tutorial First Hallway Room - 0x00064:
158000 - 0x00064 (Straight) - True - True
159510 - 0x01848 (EP) - 0x00064 - True
-First Hallway Room (First Hallway) - Tutorial - 0x00182:
+Tutorial First Hallway Room (Tutorial First Hallway) - Tutorial - 0x00182:
158001 - 0x00182 (Bend) - True - True
Tutorial (Tutorial) - Outside Tutorial - 0x03629:
@@ -23,6 +25,8 @@ Tutorial (Tutorial) - Outside Tutorial - 0x03629:
159513 - 0x33600 (Patio Flowers EP) - 0x0C373 - True
159517 - 0x3352F (Gate EP) - 0x03505 - True
+==Tutorial (Outside)==
+
Outside Tutorial (Outside Tutorial) - Outside Tutorial Path To Outpost - 0x03BA2 - Outside Tutorial Vault - 0x033D0:
158650 - 0x033D4 (Vault Panel) - True - Dots & Black/White Squares
Door - 0x033D0 (Vault Door) - 0x033D4
@@ -58,9 +62,23 @@ Outside Tutorial Outpost (Outside Tutorial) - Outside Tutorial - 0x04CA3:
Door - 0x04CA3 (Outpost Exit) - 0x04CA4
158600 - 0x17CFB (Discard) - True - Triangles
+Orchard (Orchard) - Main Island - True - Orchard Beyond First Gate - 0x03307:
+158071 - 0x00143 (Apple Tree 1) - True - True
+158072 - 0x0003B (Apple Tree 2) - 0x00143 - True
+158073 - 0x00055 (Apple Tree 3) - 0x0003B - True
+Door - 0x03307 (First Gate) - 0x00055
+
+Orchard Beyond First Gate (Orchard) - Orchard End - 0x03313:
+158074 - 0x032F7 (Apple Tree 4) - 0x00055 - True
+158075 - 0x032FF (Apple Tree 5) - 0x032F7 - True
+Door - 0x03313 (Second Gate) - 0x032FF
+
+Orchard End (Orchard):
+
Main Island (Main Island) - Outside Tutorial - True:
159801 - 0xFFD00 (Reached Independently) - True - True
-159550 - 0x28B91 (Thundercloud EP) - 0x09F98 & 0x012FB - True
+
+==Glass Factory==
Outside Glass Factory (Glass Factory) - Main Island - True - Inside Glass Factory - 0x01A29:
158027 - 0x01A54 (Entry Panel) - True - Symmetry
@@ -85,6 +103,8 @@ Door - 0x0D7ED (Back Wall) - 0x0005C
Inside Glass Factory Behind Back Wall (Glass Factory) - The Ocean - 0x17CC8:
158039 - 0x17CC8 (Boat Spawn) - 0x17CA6 | 0x17CDF | 0x09DB8 | 0x17C95 - Boat
+==Symmetry Island==
+
Outside Symmetry Island (Symmetry Island) - Main Island - True - Symmetry Island Lower - 0x17F3E:
158040 - 0x000B0 (Lower Panel) - 0x0343A - Dots
Door - 0x17F3E (Lower) - 0x000B0
@@ -128,20 +148,17 @@ Symmetry Island Upper (Symmetry Island):
Laser - 0x00509 (Laser) - 0x0360D
159001 - 0x03367 (Glass Factory Black Line EP) - True - True
-Orchard (Orchard) - Main Island - True - Orchard Beyond First Gate - 0x03307:
-158071 - 0x00143 (Apple Tree 1) - True - True
-158072 - 0x0003B (Apple Tree 2) - 0x00143 - True
-158073 - 0x00055 (Apple Tree 3) - 0x0003B - True
-Door - 0x03307 (First Gate) - 0x00055
-
-Orchard Beyond First Gate (Orchard) - Orchard End - 0x03313:
-158074 - 0x032F7 (Apple Tree 4) - 0x00055 - True
-158075 - 0x032FF (Apple Tree 5) - 0x032F7 - True
-Door - 0x03313 (Second Gate) - 0x032FF
+==Desert==
-Orchard End (Orchard):
+Desert Obelisk (Desert) - Entry - True:
+159700 - 0xFFE00 (Obelisk Side 1) - 0x0332B & 0x03367 & 0x28B8A - True
+159701 - 0xFFE01 (Obelisk Side 2) - 0x037B6 & 0x037B2 & 0x000F7 - True
+159702 - 0xFFE02 (Obelisk Side 3) - 0x3351D - True
+159703 - 0xFFE03 (Obelisk Side 4) - 0x0053C & 0x00771 & 0x335C8 & 0x335C9 & 0x337F8 & 0x037BB & 0x220E4 & 0x220E5 - True
+159704 - 0xFFE04 (Obelisk Side 5) - 0x334B9 & 0x334BC & 0x22106 & 0x0A14C & 0x0A14D - True
+159709 - 0x00359 (Obelisk) - True - True
-Desert Outside (Desert) - Main Island - True - Desert Floodlight Room - 0x09FEE - Desert Vault - 0x03444:
+Desert Outside (Desert) - Main Island - True - Desert Light Room - 0x09FEE - Desert Vault - 0x03444:
158652 - 0x0CC7B (Vault Panel) - True - Dots & Shapers & Rotated Shapers & Negative Shapers & Full Dots
Door - 0x03444 (Vault Door) - 0x0CC7B
158602 - 0x17CE7 (Discard) - True - Triangles
@@ -172,14 +189,14 @@ Laser - 0x012FB (Laser) - 0x03608
Desert Vault (Desert):
158653 - 0x0339E (Vault Box) - True - True
-Desert Floodlight Room (Desert) - Desert Pond Room - 0x0C2C3:
+Desert Light Room (Desert) - Desert Pond Room - 0x0C2C3:
158087 - 0x09FAA (Light Control) - True - True
158088 - 0x00422 (Light Room 1) - 0x09FAA - True
158089 - 0x006E3 (Light Room 2) - 0x09FAA - True
158090 - 0x0A02D (Light Room 3) - 0x09FAA & 0x00422 & 0x006E3 - True
Door - 0x0C2C3 (Pond Room Entry) - 0x0A02D
-Desert Pond Room (Desert) - Desert Water Levels Room - 0x0A24B:
+Desert Pond Room (Desert) - Desert Flood Room - 0x0A24B:
158091 - 0x00C72 (Pond Room 1) - True - True
158092 - 0x0129D (Pond Room 2) - 0x00C72 - True
158093 - 0x008BB (Pond Room 3) - 0x0129D - True
@@ -190,7 +207,7 @@ Door - 0x0A24B (Flood Room Entry) - 0x0A249
159043 - 0x0A14C (Pond Room Near Reflection EP) - True - True
159044 - 0x0A14D (Pond Room Far Reflection EP) - True - True
-Desert Water Levels Room (Desert) - Desert Elevator Room - 0x0C316:
+Desert Flood Room (Desert) - Desert Elevator Room - 0x0C316:
158097 - 0x1C2DF (Reduce Water Level Far Left) - True - True
158098 - 0x1831E (Reduce Water Level Far Right) - True - True
158099 - 0x1C260 (Reduce Water Level Near Left) - True - True
@@ -208,19 +225,29 @@ Desert Water Levels Room (Desert) - Desert Elevator Room - 0x0C316:
Door - 0x0C316 (Elevator Room Entry) - 0x18076
159034 - 0x337F8 (Flood Room EP) - 0x1C2DF - True
-Desert Elevator Room (Desert) - Desert Lowest Level Inbetween Shortcuts - 0x01317:
-158111 - 0x17C31 (Final Transparent) - True - True
-158113 - 0x012D7 (Final Hexagonal) - 0x17C31 & 0x0A015 - True
-158114 - 0x0A015 (Final Hexagonal Control) - 0x17C31 - True
-158115 - 0x0A15C (Final Bent 1) - True - True
-158116 - 0x09FFF (Final Bent 2) - 0x0A15C - True
-158117 - 0x0A15F (Final Bent 3) - 0x09FFF - True
+Desert Elevator Room (Desert) - Desert Behind Elevator - 0x01317:
+158111 - 0x17C31 (Elevator Room Transparent) - True - True
+158113 - 0x012D7 (Elevator Room Hexagonal) - 0x17C31 & 0x0A015 - True
+158114 - 0x0A015 (Elevator Room Hexagonal Control) - 0x17C31 - True
+158115 - 0x0A15C (Elevator Room Bent 1) - True - True
+158116 - 0x09FFF (Elevator Room Bent 2) - 0x0A15C - True
+158117 - 0x0A15F (Elevator Room Bent 3) - 0x09FFF - True
159035 - 0x037BB (Elevator EP) - 0x01317 - True
Door - 0x01317 (Elevator) - 0x03608
-Desert Lowest Level Inbetween Shortcuts (Desert):
+Desert Behind Elevator (Desert):
-Outside Quarry (Quarry) - Main Island - True - Quarry Between Entrys - 0x09D6F - Quarry Elevator - 0xFFD00 & 0xFFD01:
+==Quarry==
+
+Quarry Obelisk (Quarry) - Entry - True:
+159740 - 0xFFE40 (Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True
+159741 - 0xFFE41 (Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True
+159742 - 0xFFE42 (Obelisk Side 3) - 0x289CF & 0x289D1 - True
+159743 - 0xFFE43 (Obelisk Side 4) - 0x33692 - True
+159744 - 0xFFE44 (Obelisk Side 5) - 0x03E77 & 0x03E7C - True
+159749 - 0x22073 (Obelisk) - True - True
+
+Outside Quarry (Quarry) - Main Island - True - Quarry Between Entry Doors - 0x09D6F - Quarry Elevator - 0xFFD00 & 0xFFD01:
158118 - 0x09E57 (Entry 1 Panel) - True - Black/White Squares
158603 - 0x17CF0 (Discard) - True - Triangles
158702 - 0x03612 (Laser Panel) - 0x0A3D0 & 0x0367C - Eraser & Shapers
@@ -236,7 +263,7 @@ Quarry Elevator (Quarry) - Outside Quarry - 0x17CC4 - Quarry - 0x17CC4:
158120 - 0x17CC4 (Elevator Control) - 0x0367C - Dots & Eraser
159403 - 0x17CB9 (Railroad EP) - 0x17CC4 - True
-Quarry Between Entrys (Quarry) - Quarry - 0x17C07:
+Quarry Between Entry Doors (Quarry) - Quarry - 0x17C07:
158119 - 0x17C09 (Entry 2 Panel) - True - Shapers
Door - 0x17C07 (Entry 2) - 0x17C09
@@ -322,6 +349,8 @@ Door - 0x3865F (Second Barrier) - 0x38663
158169 - 0x0A3D0 (Back Second Row 3) - 0x0A3CC - Stars & Eraser & Shapers
159401 - 0x005F6 (Hook EP) - 0x275FA & 0x03852 & 0x3865F - True
+==Shadows==
+
Shadows (Shadows) - Main Island - True - Shadows Ledge - 0x19B24 - Shadows Laser Room - 0x194B2 | 0x19665:
158170 - 0x334DB (Door Timer Outside) - True - True
Door - 0x19B24 (Timed Door) - 0x334DB | 0x334DC
@@ -361,19 +390,18 @@ Shadows Laser Room (Shadows):
158703 - 0x19650 (Laser Panel) - 0x194B2 & 0x19665 - True
Laser - 0x181B3 (Laser) - 0x19650
-Treehouse Beach (Treehouse Beach) - Main Island - True:
-159200 - 0x0053D (Rock Shadow EP) - True - True
-159201 - 0x0053E (Sand Shadow EP) - True - True
-159212 - 0x220BD (Both Orange Bridges EP) - 0x17DA2 & 0x17DDB - True
+==Keep==
-Keep (Keep) - Main Island - True - Keep 2nd Maze - 0x01954 - Keep 2nd Pressure Plate - 0x01BEC:
+Outside Keep (Keep) - Main Island - True:
+159430 - 0x03E77 (Red Flowers EP) - True - True
+159431 - 0x03E7C (Purple Flowers EP) - True - True
+
+Keep (Keep) - Outside Keep - True - Keep 2nd Maze - 0x01954 - Keep 2nd Pressure Plate - 0x01BEC:
158193 - 0x00139 (Hedge Maze 1) - True - True
158197 - 0x0A3A8 (Reset Pressure Plates 1) - True - True
158198 - 0x033EA (Pressure Plates 1) - 0x0A3A8 - Dots
Door - 0x01954 (Hedge Maze 1 Exit) - 0x00139
Door - 0x01BEC (Pressure Plates 1 Exit) - 0x033EA
-159430 - 0x03E77 (Red Flowers EP) - True - True
-159431 - 0x03E7C (Purple Flowers EP) - True - True
Keep 2nd Maze (Keep) - Keep - 0x018CE - Keep 3rd Maze - 0x019D8:
Door - 0x018CE (Hedge Maze 2 Shortcut) - 0x00139
@@ -408,6 +436,22 @@ Door - 0x01D40 (Pressure Plates 4 Exit) - 0x01D3F
158205 - 0x09E49 (Shadows Shortcut Panel) - True - True
Door - 0x09E3D (Shadows Shortcut) - 0x09E49
+Keep Tower (Keep) - Keep - 0x04F8F:
+158206 - 0x0361B (Tower Shortcut Panel) - True - True
+Door - 0x04F8F (Tower Shortcut) - 0x0361B
+158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - True
+158705 - 0x03317 (Laser Panel Pressure Plates) - 0x033EA & 0x01BE9 & 0x01CD3 & 0x01D3F - Shapers & Black/White Squares & Colored Squares & Stars & Stars + Same Colored Symbol & Dots
+Laser - 0x014BB (Laser) - 0x0360E | 0x03317
+159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - True
+159241 - 0x033BF (Pressure Plates 2 EP) - 0x01BE9 - True
+159242 - 0x033DD (Pressure Plates 3 EP) - 0x01CD3 & 0x01CD5 - True
+159243 - 0x033E5 (Pressure Plates 4 Left Exit EP) - 0x01D3F - True
+159244 - 0x018B6 (Pressure Plates 4 Right Exit EP) - 0x01D3F - True
+159250 - 0x28AE9 (Path EP) - True - True
+159251 - 0x3348F (Hedges EP) - True - True
+
+==Shipwreck==
+
Shipwreck (Shipwreck) - Keep 3rd Pressure Plate - True - Shipwreck Vault - 0x17BB4:
158654 - 0x00AFB (Vault Panel) - True - Symmetry & Sound Dots & Colored Dots
Door - 0x17BB4 (Vault Door) - 0x00AFB
@@ -423,19 +467,16 @@ Door - 0x17BB4 (Vault Door) - 0x00AFB
Shipwreck Vault (Shipwreck):
158655 - 0x03535 (Vault Box) - True - True
-Keep Tower (Keep) - Keep - 0x04F8F:
-158206 - 0x0361B (Tower Shortcut Panel) - True - True
-Door - 0x04F8F (Tower Shortcut) - 0x0361B
-158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - True
-158705 - 0x03317 (Laser Panel Pressure Plates) - 0x033EA & 0x01BE9 & 0x01CD3 & 0x01D3F - Shapers & Black/White Squares & Colored Squares & Stars & Stars + Same Colored Symbol & Dots
-Laser - 0x014BB (Laser) - 0x0360E | 0x03317
-159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - True
-159241 - 0x033BF (Pressure Plates 2 EP) - 0x01BE9 - True
-159242 - 0x033DD (Pressure Plates 3 EP) - 0x01CD3 & 0x01CD5 - True
-159243 - 0x033E5 (Pressure Plates 4 Left Exit EP) - 0x01D3F - True
-159244 - 0x018B6 (Pressure Plates 4 Right Exit EP) - 0x01D3F - True
-159250 - 0x28AE9 (Path EP) - True - True
-159251 - 0x3348F (Hedges EP) - True - True
+==Monastery==
+
+Monastery Obelisk (Monastery) - Entry - True:
+159710 - 0xFFE10 (Obelisk Side 1) - 0x03ABC & 0x03ABE & 0x03AC0 & 0x03AC4 - True
+159711 - 0xFFE11 (Obelisk Side 2) - 0x03AC5 - True
+159712 - 0xFFE12 (Obelisk Side 3) - 0x03BE2 & 0x03BE3 & 0x0A409 - True
+159713 - 0xFFE13 (Obelisk Side 4) - 0x006E5 & 0x006E6 & 0x006E7 & 0x034A7 & 0x034AD & 0x034AF & 0x03DAB & 0x03DAC & 0x03DAD - True
+159714 - 0xFFE14 (Obelisk Side 5) - 0x03E01 - True
+159715 - 0xFFE15 (Obelisk Side 6) - 0x289F4 & 0x289F5 - True
+159719 - 0x00263 (Obelisk) - True - True
Outside Monastery (Monastery) - Main Island - True - Inside Monastery - 0x0C128 & 0x0C153 - Monastery Garden - 0x03750:
158207 - 0x03713 (Laser Shortcut Panel) - True - True
@@ -457,6 +498,9 @@ Laser - 0x17C65 (Laser) - 0x17CA4
159137 - 0x03DAC (Facade Left Stairs EP) - True - True
159138 - 0x03DAD (Facade Right Stairs EP) - True - True
159140 - 0x03E01 (Grass Stairs EP) - True - True
+159120 - 0x03BE2 (Garden Left EP) - 0x03750 - True
+159121 - 0x03BE3 (Garden Right EP) - True - True
+159122 - 0x0A409 (Wall EP) - True - True
Inside Monastery (Monastery):
158213 - 0x09D9B (Shutters Control) - True - Dots
@@ -470,11 +514,22 @@ Inside Monastery (Monastery):
Monastery Garden (Monastery):
-Town (Town) - Main Island - True - The Ocean - 0x0A054 - Town Maze Rooftop - 0x28AA2 - Town Church - True - Town Wooden Rooftop - 0x034F5 - RGB House - 0x28A61 - Windmill Interior - 0x1845B - Town Inside Cargo Box - 0x0A0C9:
+==Town==
+
+Town Obelisk (Town) - Entry - True:
+159750 - 0xFFE50 (Obelisk Side 1) - 0x035C7 - True
+159751 - 0xFFE51 (Obelisk Side 2) - 0x01848 & 0x03D06 & 0x33530 & 0x33600 & 0x28A2F & 0x28A37 & 0x334A3 & 0x3352F - True
+159752 - 0xFFE52 (Obelisk Side 3) - 0x33857 & 0x33879 & 0x03C19 - True
+159753 - 0xFFE53 (Obelisk Side 4) - 0x28B30 & 0x035C9 - True
+159754 - 0xFFE54 (Obelisk Side 5) - 0x03335 & 0x03412 & 0x038A6 & 0x038AA & 0x03E3F & 0x03E40 & 0x28B8E - True
+159755 - 0xFFE55 (Obelisk Side 6) - 0x28B91 & 0x03BCE & 0x03BCF & 0x03BD1 & 0x339B6 & 0x33A20 & 0x33A29 & 0x33A2A & 0x33B06 - True
+159759 - 0x0A16C (Obelisk) - True - True
+
+Town (Town) - Main Island - True - The Ocean - 0x0A054 - Town Maze Rooftop - 0x28AA2 - Town Church - True - Town Wooden Rooftop - 0x034F5 - Town RGB House - 0x28A61 - Town Inside Cargo Box - 0x0A0C9 - Outside Windmill - True:
158218 - 0x0A054 (Boat Spawn) - 0x17CA6 | 0x17CDF | 0x09DB8 | 0x17C95 - Boat
158219 - 0x0A0C8 (Cargo Box Entry Panel) - True - Black/White Squares & Shapers
Door - 0x0A0C9 (Cargo Box Entry) - 0x0A0C8
-158707 - 0x09F98 (Desert Laser Redirect) - True - True
+158707 - 0x09F98 (Desert Laser Redirect Control) - True - True
158220 - 0x18590 (Transparent) - True - Symmetry
158221 - 0x28AE3 (Vines) - 0x18590 - True
158222 - 0x28938 (Apple Tree) - 0x28AE3 - True
@@ -491,11 +546,6 @@ Door - 0x28A61 (RGB House Entry) - 0x28998
Door - 0x03BB0 (Church Entry) - 0x28A0D
158228 - 0x28A79 (Maze Panel) - True - True
Door - 0x28AA2 (Maze Stairs) - 0x28A79
-158241 - 0x17F5F (Windmill Entry Panel) - True - Dots
-Door - 0x1845B (Windmill Entry) - 0x17F5F
-159010 - 0x037B6 (Windmill First Blade EP) - 0x17D02 - True
-159011 - 0x037B2 (Windmill Second Blade EP) - 0x17D02 - True
-159012 - 0x000F7 (Windmill Third Blade EP) - 0x17D02 - True
159540 - 0x03335 (Tower Underside Third EP) - True - True
159541 - 0x03412 (Tower Underside Fourth EP) - True - True
159542 - 0x038A6 (Tower Underside First EP) - True - True
@@ -528,20 +578,26 @@ Town Church (Town):
158227 - 0x28A69 (Church Lattice) - 0x03BB0 - True
159553 - 0x03BD1 (Black Line Church EP) - True - True
-RGB House (Town) - RGB Room - 0x2897B:
+Town RGB House (Town RGB House) - Town RGB House Upstairs - 0x2897B:
158242 - 0x034E4 (Sound Room Left) - True - True
158243 - 0x034E3 (Sound Room Right) - True - Sound Dots
-Door - 0x2897B (RGB House Stairs) - 0x034E4 & 0x034E3
+Door - 0x2897B (Stairs) - 0x034E4 & 0x034E3
-RGB Room (Town):
+Town RGB House Upstairs (Town RGB House Upstairs):
158244 - 0x334D8 (RGB Control) - True - Rotated Shapers & Colored Squares
-158245 - 0x03C0C (RGB Room Left) - 0x334D8 - Colored Squares & Black/White Squares
-158246 - 0x03C08 (RGB Room Right) - 0x334D8 - Stars
+158245 - 0x03C0C (Left) - 0x334D8 - Colored Squares & Black/White Squares
+158246 - 0x03C08 (Right) - 0x334D8 - Stars
+
+Town Tower Bottom (Town Tower) - Town - True - Town Tower After First Door - 0x27799:
+Door - 0x27799 (First Door) - 0x28A69
-Town Tower (Town Tower) - Town - True - Town Tower Top - 0x27798 & 0x27799 & 0x2779A & 0x2779C:
+Town Tower After First Door (Town Tower) - Town Tower After Second Door - 0x27798:
Door - 0x27798 (Second Door) - 0x28ACC
+
+Town Tower After Second Door (Town Tower) - Town Tower After Third Door - 0x2779C:
Door - 0x2779C (Third Door) - 0x28AD9
-Door - 0x27799 (First Door) - 0x28A69
+
+Town Tower After Third Door (Town Tower) - Town Tower Top - 0x2779A:
Door - 0x2779A (Fourth Door) - 0x28B39
Town Tower Top (Town):
@@ -550,6 +606,15 @@ Laser - 0x032F9 (Laser) - 0x032F5
159422 - 0x33692 (Brown Bridge EP) - True - True
159551 - 0x03BCE (Black Line Tower EP) - True - True
+==Windmill & Theater==
+
+Outside Windmill (Windmill) - Windmill Interior - 0x1845B:
+159010 - 0x037B6 (First Blade EP) - 0x17D02 - True
+159011 - 0x037B2 (Second Blade EP) - 0x17D02 - True
+159012 - 0x000F7 (Third Blade EP) - 0x17D02 - True
+158241 - 0x17F5F (Entry Panel) - True - Dots
+Door - 0x1845B (Entry) - 0x17F5F
+
Windmill Interior (Windmill) - Theater - 0x17F88:
158247 - 0x17D02 (Turn Control) - True - Dots
158248 - 0x17F89 (Theater Entry Panel) - True - Black/White Squares
@@ -573,6 +638,8 @@ Door - 0x3CCDF (Exit Right) - 0x33AB2
159556 - 0x33A2A (Door EP) - 0x03553 - True
159558 - 0x33B06 (Church EP) - 0x0354E - True
+==Jungle==
+
Jungle (Jungle) - Main Island - True - The Ocean - 0x17CDF:
158251 - 0x17CDF (Shore Boat Spawn) - True - Boat
158609 - 0x17F9B (Discard) - True - Triangles
@@ -604,19 +671,18 @@ Door - 0x3873B (Laser Shortcut) - 0x337FA
159350 - 0x035CB (Bamboo CCW EP) - True - True
159351 - 0x035CF (Bamboo CW EP) - True - True
-Outside Jungle River (River) - Main Island - True - Monastery Garden - 0x0CF2A - River Vault - 0x15287:
+Outside Jungle River (Jungle) - Main Island - True - Monastery Garden - 0x0CF2A - Jungle Vault - 0x15287:
158267 - 0x17CAA (Monastery Garden Shortcut Panel) - True - True
Door - 0x0CF2A (Monastery Garden Shortcut) - 0x17CAA
158663 - 0x15ADD (Vault Panel) - True - Black/White Squares & Dots
Door - 0x15287 (Vault Door) - 0x15ADD
159110 - 0x03AC5 (Green Leaf Moss EP) - True - True
-159120 - 0x03BE2 (Monastery Garden Left EP) - 0x03750 - True
-159121 - 0x03BE3 (Monastery Garden Right EP) - True - True
-159122 - 0x0A409 (Monastery Wall EP) - True - True
-River Vault (River):
+Jungle Vault (Jungle):
158664 - 0x03702 (Vault Box) - True - True
+==Bunker==
+
Outside Bunker (Bunker) - Main Island - True - Bunker - 0x0C2A4:
158268 - 0x17C2E (Entry Panel) - True - Black/White Squares
Door - 0x0C2A4 (Entry) - 0x17C2E
@@ -650,9 +716,11 @@ Door - 0x0A08D (Elevator Room Entry) - 0x17E67
Bunker Elevator Section (Bunker) - Bunker Elevator - TrueOneWay:
159311 - 0x035F5 (Tinted Door EP) - 0x17C79 - True
-Bunker Elevator (Bunker) - Bunker Elevator Section - 0x0A079 - Bunker Green Room - 0x0A079 - Bunker Laser Platform - 0x0A079 - Outside Bunker - 0x0A079:
+Bunker Elevator (Bunker) - Bunker Elevator Section - 0x0A079 - Bunker Cyan Room - 0x0A079 - Bunker Green Room - 0x0A079 - Bunker Laser Platform - 0x0A079 - Outside Bunker - 0x0A079:
158286 - 0x0A079 (Elevator Control) - True - Colored Squares & Black/White Squares
+Bunker Cyan Room (Bunker) - Bunker Elevator - TrueOneWay:
+
Bunker Green Room (Bunker) - Bunker Elevator - TrueOneWay:
159310 - 0x000D3 (Green Room Flowers EP) - True - True
@@ -660,6 +728,8 @@ Bunker Laser Platform (Bunker) - Bunker Elevator - TrueOneWay:
158710 - 0x09DE0 (Laser Panel) - True - True
Laser - 0x0C2B2 (Laser) - 0x09DE0
+==Swamp==
+
Outside Swamp (Swamp) - Swamp Entry Area - 0x00C1C - Main Island - True:
158287 - 0x0056E (Entry Panel) - True - Shapers
Door - 0x00C1C (Entry) - 0x0056E
@@ -774,13 +844,29 @@ Laser - 0x00BF6 (Laser) - 0x03615
158342 - 0x17C02 (Laser Shortcut Right Panel) - 0x17C05 - Shapers & Negative Shapers & Rotated Shapers
Door - 0x2D880 (Laser Shortcut) - 0x17C02
-Treehouse Entry Area (Treehouse) - Treehouse Between Doors - 0x0C309 - The Ocean - 0x17C95:
+==Treehouse==
+
+Treehouse Obelisk (Treehouse) - Entry - True:
+159720 - 0xFFE20 (Obelisk Side 1) - 0x0053D & 0x0053E & 0x00769 - True
+159721 - 0xFFE21 (Obelisk Side 2) - 0x33721 & 0x220A7 & 0x220BD - True
+159722 - 0xFFE22 (Obelisk Side 3) - 0x03B22 & 0x03B23 & 0x03B24 & 0x03B25 & 0x03A79 & 0x28ABD & 0x28ABE - True
+159723 - 0xFFE23 (Obelisk Side 4) - 0x3388F & 0x28B29 & 0x28B2A - True
+159724 - 0xFFE24 (Obelisk Side 5) - 0x018B6 & 0x033BE & 0x033BF & 0x033DD & 0x033E5 - True
+159725 - 0xFFE25 (Obelisk Side 6) - 0x28AE9 & 0x3348F - True
+159729 - 0x00097 (Obelisk) - True - True
+
+Treehouse Beach (Treehouse Beach) - Main Island - True:
+159200 - 0x0053D (Rock Shadow EP) - True - True
+159201 - 0x0053E (Sand Shadow EP) - True - True
+159212 - 0x220BD (Both Orange Bridges EP) - 0x17DA2 & 0x17DDB - True
+
+Treehouse Entry Area (Treehouse) - Treehouse Between Entry Doors - 0x0C309 - The Ocean - 0x17C95:
158343 - 0x17C95 (Boat Spawn) - True - Boat
158344 - 0x0288C (First Door Panel) - True - Stars
Door - 0x0C309 (First Door) - 0x0288C
159210 - 0x33721 (Buoy EP) - 0x17C95 - True
-Treehouse Between Doors (Treehouse) - Treehouse Yellow Bridge - 0x0C310:
+Treehouse Between Entry Doors (Treehouse) - Treehouse Yellow Bridge - 0x0C310:
158345 - 0x02886 (Second Door Panel) - True - Stars
Door - 0x0C310 (Second Door) - 0x02886
@@ -809,7 +895,7 @@ Treehouse First Purple Bridge (Treehouse) - Treehouse Second Purple Bridge - 0x1
158360 - 0x17D2D (First Purple Bridge 4) - 0x17CE4 - Stars & Dots
158361 - 0x17D6C (First Purple Bridge 5) - 0x17D2D - Stars & Dots
-Treehouse Right Orange Bridge (Treehouse) - Treehouse Bridge Platform - 0x17DA2:
+Treehouse Right Orange Bridge (Treehouse) - Treehouse Drawbridge Platform - 0x17DA2:
158391 - 0x17D88 (Right Orange Bridge 1) - True - Stars
158392 - 0x17DB4 (Right Orange Bridge 2) - 0x17D88 - Stars
158393 - 0x17D8C (Right Orange Bridge 3) - 0x17DB4 - Stars
@@ -823,7 +909,7 @@ Treehouse Right Orange Bridge (Treehouse) - Treehouse Bridge Platform - 0x17DA2:
158401 - 0x17DB1 (Right Orange Bridge 11) - 0x17DB7 - Stars
158402 - 0x17DA2 (Right Orange Bridge 12) - 0x17DB1 - Stars
-Treehouse Bridge Platform (Treehouse) - Main Island - 0x0C32D:
+Treehouse Drawbridge Platform (Treehouse) - Main Island - 0x0C32D:
158404 - 0x037FF (Drawbridge Panel) - True - Stars
Door - 0x0C32D (Drawbridge) - 0x037FF
@@ -882,7 +968,19 @@ Treehouse Laser Room (Treehouse):
158403 - 0x17CBC (Laser House Door Timer Inside) - True - True
Laser - 0x028A4 (Laser) - 0x03613
+==Mountain (Outside)==
+
+Mountainside Obelisk (Mountainside) - Entry - True:
+159730 - 0xFFE30 (Obelisk Side 1) - 0x001A3 & 0x335AE - True
+159731 - 0xFFE31 (Obelisk Side 2) - 0x000D3 & 0x035F5 & 0x09D5D & 0x09D5E & 0x09D63 - True
+159732 - 0xFFE32 (Obelisk Side 3) - 0x3370E & 0x035DE & 0x03601 & 0x03603 & 0x03D0D & 0x3369A & 0x336C8 & 0x33505 - True
+159733 - 0xFFE33 (Obelisk Side 4) - 0x03A9E & 0x016B2 & 0x3365F & 0x03731 & 0x036CE & 0x03C07 & 0x03A93 - True
+159734 - 0xFFE34 (Obelisk Side 5) - 0x03AA6 & 0x3397C & 0x0105D & 0x0A304 - True
+159735 - 0xFFE35 (Obelisk Side 6) - 0x035CB & 0x035CF - True
+159739 - 0x00367 (Obelisk) - True - True
+
Mountainside (Mountainside) - Main Island - True - Mountaintop - True - Mountainside Vault - 0x00085:
+159550 - 0x28B91 (Thundercloud EP) - 0x09F98 & 0x012FB - True
158612 - 0x17C42 (Discard) - True - Triangles
158665 - 0x002A6 (Vault Panel) - True - Symmetry & Colored Dots & Black/White Squares & Dots
Door - 0x00085 (Vault Door) - 0x002A6
@@ -893,20 +991,22 @@ Door - 0x00085 (Vault Door) - 0x002A6
Mountainside Vault (Mountainside):
158666 - 0x03542 (Vault Box) - True - True
-Mountaintop (Mountaintop) - Mountain Top Layer - 0x17C34:
+Mountaintop (Mountaintop) - Mountain Floor 1 - 0x17C34:
158405 - 0x0042D (River Shape) - True - True
-158406 - 0x09F7F (Box Short) - 7 Lasers - True
+158406 - 0x09F7F (Box Short) - 7 Lasers + Redirect - True
158407 - 0x17C34 (Mountain Entry Panel) - 0x09F7F - Stars & Black/White Squares & Stars + Same Colored Symbol
-158800 - 0xFFF00 (Box Long) - 11 Lasers & 0x17C34 - True
+158800 - 0xFFF00 (Box Long) - 11 Lasers + Redirect & 0x17C34 - True
159300 - 0x001A3 (River Shape EP) - True - True
159320 - 0x3370E (Arch Black EP) - True - True
159324 - 0x336C8 (Arch White Right EP) - True - True
159326 - 0x3369A (Arch White Left EP) - True - True
-Mountain Top Layer (Mountain Floor 1) - Mountain Top Layer Bridge - 0x09E39:
+==Mountain (Inside)==
+
+Mountain Floor 1 (Mountain Floor 1) - Mountain Floor 1 Bridge - 0x09E39:
158408 - 0x09E39 (Light Bridge Controller) - True - Black/White Squares & Colored Squares & Eraser
-Mountain Top Layer Bridge (Mountain Floor 1) - Mountain Top Layer At Door - TrueOneWay:
+Mountain Floor 1 Bridge (Mountain Floor 1) - Mountain Floor 1 At Door - TrueOneWay:
158409 - 0x09E7A (Right Row 1) - True - Black/White Squares & Dots
158410 - 0x09E71 (Right Row 2) - 0x09E7A - Black/White Squares & Dots
158411 - 0x09E72 (Right Row 3) - 0x09E71 - Black/White Squares & Shapers & Dots
@@ -925,10 +1025,10 @@ Mountain Top Layer Bridge (Mountain Floor 1) - Mountain Top Layer At Door - True
158424 - 0x09EAD (Trash Pillar 1) - True - Black/White Squares & Shapers
158425 - 0x09EAF (Trash Pillar 2) - 0x09EAD - Black/White Squares & Shapers
-Mountain Top Layer At Door (Mountain Floor 1) - Mountain Floor 2 - 0x09E54:
+Mountain Floor 1 At Door (Mountain Floor 1) - Mountain Floor 2 - 0x09E54:
Door - 0x09E54 (Exit) - 0x09EAF & 0x09F6E & 0x09E6B & 0x09E7B
-Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near - 0x09FFB - Mountain Floor 2 Blue Bridge - 0x09E86 - Mountain Pink Bridge EP - TrueOneWay:
+Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near - 0x09FFB - Mountain Floor 2 Beyond Bridge - 0x09E86 - Mountain Floor 2 At Door - 0x09ED8 & 0x09E86 - Mountain Pink Bridge EP - TrueOneWay:
158426 - 0x09FD3 (Near Row 1) - True - Stars & Colored Squares & Stars + Same Colored Symbol
158427 - 0x09FD4 (Near Row 2) - 0x09FD3 - Stars & Colored Squares & Stars + Same Colored Symbol
158428 - 0x09FD6 (Near Row 3) - 0x09FD4 - Stars & Colored Squares & Stars + Same Colored Symbol
@@ -936,8 +1036,6 @@ Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near -
158430 - 0x09FD8 (Near Row 5) - 0x09FD7 - Colored Squares & Symmetry & Colored Dots
Door - 0x09FFB (Staircase Near) - 0x09FD8
-Mountain Floor 2 Blue Bridge (Mountain Floor 2) - Mountain Floor 2 Beyond Bridge - TrueOneWay - Mountain Floor 2 At Door - 0x09ED8:
-
Mountain Floor 2 At Door (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EDD:
Door - 0x09EDD (Elevator Room Entry) - 0x09ED8 & 0x09E86
@@ -959,10 +1057,10 @@ Mountain Floor 2 Light Bridge Room Far (Mountain Floor 2):
Mountain Floor 2 Elevator Room (Mountain Floor 2) - Mountain Floor 2 Elevator - TrueOneWay:
158613 - 0x17F93 (Elevator Discard) - True - Triangles
-Mountain Floor 2 Elevator (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EEB - Mountain Third Layer - 0x09EEB:
+Mountain Floor 2 Elevator (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EEB - Mountain Floor 3 - 0x09EEB:
158439 - 0x09EEB (Elevator Control Panel) - True - Dots
-Mountain Third Layer (Mountain Bottom Floor) - Mountain Floor 2 Elevator - TrueOneWay - Mountain Bottom Floor - 0x09F89 - Mountain Pink Bridge EP - TrueOneWay:
+Mountain Floor 3 (Mountain Bottom Floor) - Mountain Floor 2 Elevator - TrueOneWay - Mountain Bottom Floor - 0x09F89 - Mountain Pink Bridge EP - TrueOneWay:
158440 - 0x09FC1 (Giant Puzzle Bottom Left) - True - Shapers & Eraser
158441 - 0x09F8E (Giant Puzzle Bottom Right) - True - Shapers & Eraser
158442 - 0x09F01 (Giant Puzzle Top Right) - True - Rotated Shapers
@@ -972,13 +1070,32 @@ Mountain Third Layer (Mountain Bottom Floor) - Mountain Floor 2 Elevator - TrueO
159314 - 0x09D5E (Blue Bridge EP) - 0x09E86 & 0x09ED8 - True
Door - 0x09F89 (Exit) - 0x09FDA
-Mountain Bottom Floor (Mountain Bottom Floor) - Mountain Path to Caves - 0x17F33 - Final Room - 0x0C141:
+Mountain Bottom Floor (Mountain Bottom Floor) - Mountain Path to Caves - 0x17F33 - Mountain Bottom Floor Pillars Room - 0x0C141:
158614 - 0x17FA2 (Discard) - 0xFFF00 - Triangles
-158445 - 0x01983 (Final Room Entry Left) - True - Shapers & Stars
-158446 - 0x01987 (Final Room Entry Right) - True - Colored Squares & Dots
-Door - 0x0C141 (Final Room Entry) - 0x01983 & 0x01987
+158445 - 0x01983 (Pillars Room Entry Left) - True - Shapers & Stars
+158446 - 0x01987 (Pillars Room Entry Right) - True - Colored Squares & Dots
+Door - 0x0C141 (Pillars Room Entry) - 0x01983 & 0x01987
Door - 0x17F33 (Rock Open) - 0x17FA2 | 0x334E1
+Mountain Bottom Floor Pillars Room (Mountain Bottom Floor) - Elevator - 0x339BB & 0x33961:
+158522 - 0x0383A (Right Pillar 1) - True - Stars
+158523 - 0x09E56 (Right Pillar 2) - 0x0383A - Stars & Dots
+158524 - 0x09E5A (Right Pillar 3) - 0x09E56 - Dots & Full Dots
+158525 - 0x33961 (Right Pillar 4) - 0x09E5A - Dots & Symmetry
+158526 - 0x0383D (Left Pillar 1) - True - Dots
+158527 - 0x0383F (Left Pillar 2) - 0x0383D - Black/White Squares
+158528 - 0x03859 (Left Pillar 3) - 0x0383F - Shapers
+158529 - 0x339BB (Left Pillar 4) - 0x03859 - Black/White Squares & Stars & Symmetry
+
+Elevator (Mountain Bottom Floor):
+158530 - 0x3D9A6 (Elevator Door Closer Left) - True - True
+158531 - 0x3D9A7 (Elevator Door Close Right) - True - True
+158532 - 0x3C113 (Elevator Entry Left) - 0x3D9A6 | 0x3D9A7 - True
+158533 - 0x3C114 (Elevator Entry Right) - 0x3D9A6 | 0x3D9A7 - True
+158534 - 0x3D9AA (Back Wall Left) - 0x3D9A6 | 0x3D9A7 - True
+158535 - 0x3D9A8 (Back Wall Right) - 0x3D9A6 | 0x3D9A7 - True
+158536 - 0x3D9A9 (Elevator Start) - 0x3D9AA & 7 Lasers | 0x3D9A8 & 7 Lasers - True
+
Mountain Pink Bridge EP (Mountain Floor 2):
159312 - 0x09D63 (Pink Bridge EP) - 0x09E39 - True
@@ -987,7 +1104,9 @@ Mountain Path to Caves (Mountain Bottom Floor) - Caves - 0x2D77D:
Door - 0x2D77D (Caves Entry) - 0x00FF8
158448 - 0x334E1 (Rock Control) - True - True
-Caves (Caves) - Main Island - 0x2D73F | 0x2D859 - Path to Challenge - 0x019A5:
+==Caves==
+
+Caves (Caves) - Main Island - 0x2D73F | 0x2D859 - Caves Path to Challenge - 0x019A5:
158451 - 0x335AB (Elevator Inside Control) - True - Dots & Black/White Squares
158452 - 0x335AC (Elevator Upper Outside Control) - 0x335AB - Black/White Squares
158453 - 0x3369D (Elevator Lower Outside Control) - 0x335AB - Black/White Squares & Dots
@@ -1042,10 +1161,12 @@ Door - 0x2D73F (Mountain Shortcut Door) - 0x021D7
Door - 0x2D859 (Swamp Shortcut Door) - 0x17CF2
159341 - 0x3397C (Skylight EP) - True - True
-Path to Challenge (Caves) - Challenge - 0x0A19A:
+Caves Path to Challenge (Caves) - Challenge - 0x0A19A:
158477 - 0x0A16E (Challenge Entry Panel) - True - Stars & Shapers & Stars + Same Colored Symbol
Door - 0x0A19A (Challenge Entry) - 0x0A16E
+==Challenge==
+
Challenge (Challenge) - Tunnels - 0x0348A - Challenge Vault - 0x04D75:
158499 - 0x0A332 (Start Timer) - 11 Lasers - True
158500 - 0x0088E (Small Basic) - 0x0A332 - True
@@ -1074,7 +1195,9 @@ Door - 0x0348A (Tunnels Entry) - 0x039B4
Challenge Vault (Challenge):
158667 - 0x0356B (Vault Box) - 0x1C31A & 0x1C319 - True
-Tunnels (Tunnels) - Windmill Interior - 0x27739 - Desert Lowest Level Inbetween Shortcuts - 0x27263 - Town - 0x09E87:
+==Tunnels==
+
+Tunnels (Tunnels) - Windmill Interior - 0x27739 - Desert Behind Elevator - 0x27263 - Town - 0x09E87:
158668 - 0x2FAF6 (Vault Box) - True - True
158519 - 0x27732 (Theater Shortcut Panel) - True - True
Door - 0x27739 (Theater Shortcut) - 0x27732
@@ -1084,24 +1207,7 @@ Door - 0x27263 (Desert Shortcut) - 0x2773D
Door - 0x09E87 (Town Shortcut) - 0x09E85
159557 - 0x33A20 (Theater Flowers EP) - 0x03553 & Theater to Tunnels - True
-Final Room (Mountain Final Room) - Elevator - 0x339BB & 0x33961:
-158522 - 0x0383A (Right Pillar 1) - True - Stars
-158523 - 0x09E56 (Right Pillar 2) - 0x0383A - Stars & Dots
-158524 - 0x09E5A (Right Pillar 3) - 0x09E56 - Dots & Full Dots
-158525 - 0x33961 (Right Pillar 4) - 0x09E5A - Dots & Symmetry
-158526 - 0x0383D (Left Pillar 1) - True - Dots
-158527 - 0x0383F (Left Pillar 2) - 0x0383D - Black/White Squares
-158528 - 0x03859 (Left Pillar 3) - 0x0383F - Shapers
-158529 - 0x339BB (Left Pillar 4) - 0x03859 - Black/White Squares & Stars & Symmetry
-
-Elevator (Mountain Final Room):
-158530 - 0x3D9A6 (Elevator Door Closer Left) - True - True
-158531 - 0x3D9A7 (Elevator Door Close Right) - True - True
-158532 - 0x3C113 (Elevator Entry Left) - 0x3D9A6 | 0x3D9A7 - True
-158533 - 0x3C114 (Elevator Entry Right) - 0x3D9A6 | 0x3D9A7 - True
-158534 - 0x3D9AA (Back Wall Left) - 0x3D9A6 | 0x3D9A7 - True
-158535 - 0x3D9A8 (Back Wall Right) - 0x3D9A6 | 0x3D9A7 - True
-158536 - 0x3D9A9 (Elevator Start) - 0x3D9AA & 7 Lasers | 0x3D9A8 & 7 Lasers - True
+==Boat==
The Ocean (Boat) - Main Island - TrueOneWay - Swamp Near Boat - TrueOneWay - Treehouse Entry Area - TrueOneWay - Quarry Boathouse Behind Staircase - TrueOneWay - Inside Glass Factory Behind Back Wall - TrueOneWay:
159042 - 0x22106 (Desert EP) - True - True
@@ -1114,45 +1220,3 @@ The Ocean (Boat) - Main Island - TrueOneWay - Swamp Near Boat - TrueOneWay - Tre
159521 - 0x33879 (Tutorial Reflection EP) - True - True
159522 - 0x03C19 (Tutorial Moss EP) - True - True
159531 - 0x035C9 (Cargo Box EP) - 0x0A0C9 - True
-
-Obelisks (EPs) - Entry - True:
-159700 - 0xFFE00 (Desert Obelisk Side 1) - 0x0332B & 0x03367 & 0x28B8A - True
-159701 - 0xFFE01 (Desert Obelisk Side 2) - 0x037B6 & 0x037B2 & 0x000F7 - True
-159702 - 0xFFE02 (Desert Obelisk Side 3) - 0x3351D - True
-159703 - 0xFFE03 (Desert Obelisk Side 4) - 0x0053C & 0x00771 & 0x335C8 & 0x335C9 & 0x337F8 & 0x037BB & 0x220E4 & 0x220E5 - True
-159704 - 0xFFE04 (Desert Obelisk Side 5) - 0x334B9 & 0x334BC & 0x22106 & 0x0A14C & 0x0A14D - True
-159709 - 0x00359 (Desert Obelisk) - True - True
-159710 - 0xFFE10 (Monastery Obelisk Side 1) - 0x03ABC & 0x03ABE & 0x03AC0 & 0x03AC4 - True
-159711 - 0xFFE11 (Monastery Obelisk Side 2) - 0x03AC5 - True
-159712 - 0xFFE12 (Monastery Obelisk Side 3) - 0x03BE2 & 0x03BE3 & 0x0A409 - True
-159713 - 0xFFE13 (Monastery Obelisk Side 4) - 0x006E5 & 0x006E6 & 0x006E7 & 0x034A7 & 0x034AD & 0x034AF & 0x03DAB & 0x03DAC & 0x03DAD - True
-159714 - 0xFFE14 (Monastery Obelisk Side 5) - 0x03E01 - True
-159715 - 0xFFE15 (Monastery Obelisk Side 6) - 0x289F4 & 0x289F5 - True
-159719 - 0x00263 (Monastery Obelisk) - True - True
-159720 - 0xFFE20 (Treehouse Obelisk Side 1) - 0x0053D & 0x0053E & 0x00769 - True
-159721 - 0xFFE21 (Treehouse Obelisk Side 2) - 0x33721 & 0x220A7 & 0x220BD - True
-159722 - 0xFFE22 (Treehouse Obelisk Side 3) - 0x03B22 & 0x03B23 & 0x03B24 & 0x03B25 & 0x03A79 & 0x28ABD & 0x28ABE - True
-159723 - 0xFFE23 (Treehouse Obelisk Side 4) - 0x3388F & 0x28B29 & 0x28B2A - True
-159724 - 0xFFE24 (Treehouse Obelisk Side 5) - 0x018B6 & 0x033BE & 0x033BF & 0x033DD & 0x033E5 - True
-159725 - 0xFFE25 (Treehouse Obelisk Side 6) - 0x28AE9 & 0x3348F - True
-159729 - 0x00097 (Treehouse Obelisk) - True - True
-159730 - 0xFFE30 (River Obelisk Side 1) - 0x001A3 & 0x335AE - True
-159731 - 0xFFE31 (River Obelisk Side 2) - 0x000D3 & 0x035F5 & 0x09D5D & 0x09D5E & 0x09D63 - True
-159732 - 0xFFE32 (River Obelisk Side 3) - 0x3370E & 0x035DE & 0x03601 & 0x03603 & 0x03D0D & 0x3369A & 0x336C8 & 0x33505 - True
-159733 - 0xFFE33 (River Obelisk Side 4) - 0x03A9E & 0x016B2 & 0x3365F & 0x03731 & 0x036CE & 0x03C07 & 0x03A93 - True
-159734 - 0xFFE34 (River Obelisk Side 5) - 0x03AA6 & 0x3397C & 0x0105D & 0x0A304 - True
-159735 - 0xFFE35 (River Obelisk Side 6) - 0x035CB & 0x035CF - True
-159739 - 0x00367 (River Obelisk) - True - True
-159740 - 0xFFE40 (Quarry Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True
-159741 - 0xFFE41 (Quarry Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True
-159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 - True
-159743 - 0xFFE43 (Quarry Obelisk Side 4) - 0x33692 - True
-159744 - 0xFFE44 (Quarry Obelisk Side 5) - 0x03E77 & 0x03E7C - True
-159749 - 0x22073 (Quarry Obelisk) - True - True
-159750 - 0xFFE50 (Town Obelisk Side 1) - 0x035C7 - True
-159751 - 0xFFE51 (Town Obelisk Side 2) - 0x01848 & 0x03D06 & 0x33530 & 0x33600 & 0x28A2F & 0x28A37 & 0x334A3 & 0x3352F - True
-159752 - 0xFFE52 (Town Obelisk Side 3) - 0x33857 & 0x33879 & 0x03C19 - True
-159753 - 0xFFE53 (Town Obelisk Side 4) - 0x28B30 & 0x035C9 - True
-159754 - 0xFFE54 (Town Obelisk Side 5) - 0x03335 & 0x03412 & 0x038A6 & 0x038AA & 0x03E3F & 0x03E40 & 0x28B8E - True
-159755 - 0xFFE55 (Town Obelisk Side 6) - 0x28B91 & 0x03BCE & 0x03BCF & 0x03BD1 & 0x339B6 & 0x33A20 & 0x33A29 & 0x33A2A & 0x33B06 - True
-159759 - 0x0A16C (Town Obelisk) - True - True
diff --git a/worlds/witness/WitnessLogicExpert.txt b/worlds/witness/WitnessLogicExpert.txt
index b1d9b8e30e40..b01d5551ec55 100644
--- a/worlds/witness/WitnessLogicExpert.txt
+++ b/worlds/witness/WitnessLogicExpert.txt
@@ -1,12 +1,14 @@
+==Tutorial (Inside)==
+
Menu (Menu) - Entry - True:
Entry (Entry):
-First Hallway (First Hallway) - Entry - True - First Hallway Room - 0x00064:
+Tutorial First Hallway (Tutorial First Hallway) - Entry - True - Tutorial First Hallway Room - 0x00064:
158000 - 0x00064 (Straight) - True - True
159510 - 0x01848 (EP) - 0x00064 - True
-First Hallway Room (First Hallway) - Tutorial - 0x00182:
+Tutorial First Hallway Room (Tutorial First Hallway) - Tutorial - 0x00182:
158001 - 0x00182 (Bend) - True - True
Tutorial (Tutorial) - Outside Tutorial - True:
@@ -23,6 +25,8 @@ Tutorial (Tutorial) - Outside Tutorial - True:
159513 - 0x33600 (Patio Flowers EP) - 0x0C373 - True
159517 - 0x3352F (Gate EP) - 0x03505 - True
+==Tutorial (Outside)==
+
Outside Tutorial (Outside Tutorial) - Outside Tutorial Path To Outpost - 0x03BA2 - Outside Tutorial Vault - 0x033D0:
158650 - 0x033D4 (Vault Panel) - True - Dots & Full Dots & Squares & Black/White Squares
Door - 0x033D0 (Vault Door) - 0x033D4
@@ -58,9 +62,23 @@ Outside Tutorial Outpost (Outside Tutorial) - Outside Tutorial - 0x04CA3:
Door - 0x04CA3 (Outpost Exit) - 0x04CA4
158600 - 0x17CFB (Discard) - True - Arrows
+Orchard (Orchard) - Main Island - True - Orchard Beyond First Gate - 0x03307:
+158071 - 0x00143 (Apple Tree 1) - True - True
+158072 - 0x0003B (Apple Tree 2) - 0x00143 - True
+158073 - 0x00055 (Apple Tree 3) - 0x0003B - True
+Door - 0x03307 (First Gate) - 0x00055
+
+Orchard Beyond First Gate (Orchard) - Orchard End - 0x03313:
+158074 - 0x032F7 (Apple Tree 4) - 0x00055 - True
+158075 - 0x032FF (Apple Tree 5) - 0x032F7 - True
+Door - 0x03313 (Second Gate) - 0x032FF
+
+Orchard End (Orchard):
+
Main Island (Main Island) - Outside Tutorial - True:
159801 - 0xFFD00 (Reached Independently) - True - True
-159550 - 0x28B91 (Thundercloud EP) - 0x09F98 & 0x012FB - True
+
+==Glass Factory==
Outside Glass Factory (Glass Factory) - Main Island - True - Inside Glass Factory - 0x01A29:
158027 - 0x01A54 (Entry Panel) - True - Symmetry
@@ -85,6 +103,8 @@ Door - 0x0D7ED (Back Wall) - 0x0005C
Inside Glass Factory Behind Back Wall (Glass Factory) - The Ocean - 0x17CC8:
158039 - 0x17CC8 (Boat Spawn) - 0x17CA6 | 0x17CDF | 0x09DB8 | 0x17C95 - Boat
+==Symmetry Island==
+
Outside Symmetry Island (Symmetry Island) - Main Island - True - Symmetry Island Lower - 0x17F3E:
158040 - 0x000B0 (Lower Panel) - 0x0343A - Triangles
Door - 0x17F3E (Lower) - 0x000B0
@@ -128,20 +148,17 @@ Symmetry Island Upper (Symmetry Island):
Laser - 0x00509 (Laser) - 0x0360D
159001 - 0x03367 (Glass Factory Black Line EP) - True - True
-Orchard (Orchard) - Main Island - True - Orchard Beyond First Gate - 0x03307:
-158071 - 0x00143 (Apple Tree 1) - True - True
-158072 - 0x0003B (Apple Tree 2) - 0x00143 - True
-158073 - 0x00055 (Apple Tree 3) - 0x0003B - True
-Door - 0x03307 (First Gate) - 0x00055
-
-Orchard Beyond First Gate (Orchard) - Orchard End - 0x03313:
-158074 - 0x032F7 (Apple Tree 4) - 0x00055 - True
-158075 - 0x032FF (Apple Tree 5) - 0x032F7 - True
-Door - 0x03313 (Second Gate) - 0x032FF
+==Desert==
-Orchard End (Orchard):
+Desert Obelisk (Desert) - Entry - True:
+159700 - 0xFFE00 (Obelisk Side 1) - 0x0332B & 0x03367 & 0x28B8A - True
+159701 - 0xFFE01 (Obelisk Side 2) - 0x037B6 & 0x037B2 & 0x000F7 - True
+159702 - 0xFFE02 (Obelisk Side 3) - 0x3351D - True
+159703 - 0xFFE03 (Obelisk Side 4) - 0x0053C & 0x00771 & 0x335C8 & 0x335C9 & 0x337F8 & 0x037BB & 0x220E4 & 0x220E5 - True
+159704 - 0xFFE04 (Obelisk Side 5) - 0x334B9 & 0x334BC & 0x22106 & 0x0A14C & 0x0A14D - True
+159709 - 0x00359 (Obelisk) - True - True
-Desert Outside (Desert) - Main Island - True - Desert Floodlight Room - 0x09FEE - Desert Vault - 0x03444:
+Desert Outside (Desert) - Main Island - True - Desert Light Room - 0x09FEE - Desert Vault - 0x03444:
158652 - 0x0CC7B (Vault Panel) - True - Dots & Full Dots & Stars & Stars + Same Colored Symbol & Eraser & Triangles & Shapers & Negative Shapers & Colored Squares
Door - 0x03444 (Vault Door) - 0x0CC7B
158602 - 0x17CE7 (Discard) - True - Arrows
@@ -172,14 +189,14 @@ Laser - 0x012FB (Laser) - 0x03608
Desert Vault (Desert):
158653 - 0x0339E (Vault Box) - True - True
-Desert Floodlight Room (Desert) - Desert Pond Room - 0x0C2C3:
+Desert Light Room (Desert) - Desert Pond Room - 0x0C2C3:
158087 - 0x09FAA (Light Control) - True - True
158088 - 0x00422 (Light Room 1) - 0x09FAA - True
158089 - 0x006E3 (Light Room 2) - 0x09FAA - True
158090 - 0x0A02D (Light Room 3) - 0x09FAA & 0x00422 & 0x006E3 - True
Door - 0x0C2C3 (Pond Room Entry) - 0x0A02D
-Desert Pond Room (Desert) - Desert Water Levels Room - 0x0A24B:
+Desert Pond Room (Desert) - Desert Flood Room - 0x0A24B:
158091 - 0x00C72 (Pond Room 1) - True - True
158092 - 0x0129D (Pond Room 2) - 0x00C72 - True
158093 - 0x008BB (Pond Room 3) - 0x0129D - True
@@ -190,7 +207,7 @@ Door - 0x0A24B (Flood Room Entry) - 0x0A249
159043 - 0x0A14C (Pond Room Near Reflection EP) - True - True
159044 - 0x0A14D (Pond Room Far Reflection EP) - True - True
-Desert Water Levels Room (Desert) - Desert Elevator Room - 0x0C316:
+Desert Flood Room (Desert) - Desert Elevator Room - 0x0C316:
158097 - 0x1C2DF (Reduce Water Level Far Left) - True - True
158098 - 0x1831E (Reduce Water Level Far Right) - True - True
158099 - 0x1C260 (Reduce Water Level Near Left) - True - True
@@ -208,19 +225,29 @@ Desert Water Levels Room (Desert) - Desert Elevator Room - 0x0C316:
Door - 0x0C316 (Elevator Room Entry) - 0x18076
159034 - 0x337F8 (Flood Room EP) - 0x1C2DF - True
-Desert Elevator Room (Desert) - Desert Lowest Level Inbetween Shortcuts - 0x01317:
-158111 - 0x17C31 (Final Transparent) - True - True
-158113 - 0x012D7 (Final Hexagonal) - 0x17C31 & 0x0A015 - True
-158114 - 0x0A015 (Final Hexagonal Control) - 0x17C31 - True
-158115 - 0x0A15C (Final Bent 1) - True - True
-158116 - 0x09FFF (Final Bent 2) - 0x0A15C - True
-158117 - 0x0A15F (Final Bent 3) - 0x09FFF - True
+Desert Elevator Room (Desert) - Desert Behind Elevator - 0x01317:
+158111 - 0x17C31 (Elevator Room Transparent) - True - True
+158113 - 0x012D7 (Elevator Room Hexagonal) - 0x17C31 & 0x0A015 - True
+158114 - 0x0A015 (Elevator Room Hexagonal Control) - 0x17C31 - True
+158115 - 0x0A15C (Elevator Room Bent 1) - True - True
+158116 - 0x09FFF (Elevator Room Bent 2) - 0x0A15C - True
+158117 - 0x0A15F (Elevator Room Bent 3) - 0x09FFF - True
159035 - 0x037BB (Elevator EP) - 0x01317 - True
Door - 0x01317 (Elevator) - 0x03608
-Desert Lowest Level Inbetween Shortcuts (Desert):
+Desert Behind Elevator (Desert):
-Outside Quarry (Quarry) - Main Island - True - Quarry Between Entrys - 0x09D6F - Quarry Elevator - 0xFFD00 & 0xFFD01:
+==Quarry==
+
+Quarry Obelisk (Quarry) - Entry - True:
+159740 - 0xFFE40 (Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True
+159741 - 0xFFE41 (Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True
+159742 - 0xFFE42 (Obelisk Side 3) - 0x289CF & 0x289D1 - True
+159743 - 0xFFE43 (Obelisk Side 4) - 0x33692 - True
+159744 - 0xFFE44 (Obelisk Side 5) - 0x03E77 & 0x03E7C - True
+159749 - 0x22073 (Obelisk) - True - True
+
+Outside Quarry (Quarry) - Main Island - True - Quarry Between Entry Doors - 0x09D6F - Quarry Elevator - 0xFFD00 & 0xFFD01:
158118 - 0x09E57 (Entry 1 Panel) - True - Squares & Black/White Squares & Triangles
158603 - 0x17CF0 (Discard) - True - Arrows
158702 - 0x03612 (Laser Panel) - 0x0A3D0 & 0x0367C - Eraser & Triangles & Stars & Stars + Same Colored Symbol
@@ -236,7 +263,7 @@ Quarry Elevator (Quarry) - Outside Quarry - 0x17CC4 - Quarry - 0x17CC4:
158120 - 0x17CC4 (Elevator Control) - 0x0367C - Dots & Eraser
159403 - 0x17CB9 (Railroad EP) - 0x17CC4 - True
-Quarry Between Entrys (Quarry) - Quarry - 0x17C07:
+Quarry Between Entry Doors (Quarry) - Quarry - 0x17C07:
158119 - 0x17C09 (Entry 2 Panel) - True - Shapers & Triangles
Door - 0x17C07 (Entry 2) - 0x17C09
@@ -322,6 +349,8 @@ Door - 0x3865F (Second Barrier) - 0x38663
158169 - 0x0A3D0 (Back Second Row 3) - 0x0A3CC - Stars & Eraser & Shapers & Negative Shapers & Stars + Same Colored Symbol
159401 - 0x005F6 (Hook EP) - 0x275FA & 0x03852 & 0x3865F - True
+==Shadows==
+
Shadows (Shadows) - Main Island - True - Shadows Ledge - 0x19B24 - Shadows Laser Room - 0x194B2 | 0x19665:
158170 - 0x334DB (Door Timer Outside) - True - True
Door - 0x19B24 (Timed Door) - 0x334DB | 0x334DC
@@ -361,19 +390,18 @@ Shadows Laser Room (Shadows):
158703 - 0x19650 (Laser Panel) - 0x194B2 & 0x19665 - True
Laser - 0x181B3 (Laser) - 0x19650
-Treehouse Beach (Treehouse Beach) - Main Island - True:
-159200 - 0x0053D (Rock Shadow EP) - True - True
-159201 - 0x0053E (Sand Shadow EP) - True - True
-159212 - 0x220BD (Both Orange Bridges EP) - 0x17DA2 & 0x17DDB - True
+==Keep==
-Keep (Keep) - Main Island - True - Keep 2nd Maze - 0x01954 - Keep 2nd Pressure Plate - 0x01BEC:
+Outside Keep (Keep) - Main Island - True:
+159430 - 0x03E77 (Red Flowers EP) - True - True
+159431 - 0x03E7C (Purple Flowers EP) - True - True
+
+Keep (Keep) - Outside Keep - True - Keep 2nd Maze - 0x01954 - Keep 2nd Pressure Plate - 0x01BEC:
158193 - 0x00139 (Hedge Maze 1) - True - True
158197 - 0x0A3A8 (Reset Pressure Plates 1) - True - True
158198 - 0x033EA (Pressure Plates 1) - 0x0A3A8 - Colored Squares & Triangles & Stars & Stars + Same Colored Symbol
Door - 0x01954 (Hedge Maze 1 Exit) - 0x00139
Door - 0x01BEC (Pressure Plates 1 Exit) - 0x033EA
-159430 - 0x03E77 (Red Flowers EP) - True - True
-159431 - 0x03E7C (Purple Flowers EP) - True - True
Keep 2nd Maze (Keep) - Keep - 0x018CE - Keep 3rd Maze - 0x019D8:
Door - 0x018CE (Hedge Maze 2 Shortcut) - 0x00139
@@ -408,6 +436,22 @@ Door - 0x01D40 (Pressure Plates 4 Exit) - 0x01D3F
158205 - 0x09E49 (Shadows Shortcut Panel) - True - True
Door - 0x09E3D (Shadows Shortcut) - 0x09E49
+Keep Tower (Keep) - Keep - 0x04F8F:
+158206 - 0x0361B (Tower Shortcut Panel) - True - True
+Door - 0x04F8F (Tower Shortcut) - 0x0361B
+158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - True
+158705 - 0x03317 (Laser Panel Pressure Plates) - 0x033EA & 0x01BE9 & 0x01CD3 & 0x01D3F - Shapers & Rotated Shapers & Triangles & Stars & Stars + Same Colored Symbol & Colored Squares & Black/White Squares
+Laser - 0x014BB (Laser) - 0x0360E | 0x03317
+159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - True
+159241 - 0x033BF (Pressure Plates 2 EP) - 0x01BE9 - True
+159242 - 0x033DD (Pressure Plates 3 EP) - 0x01CD3 & 0x01CD5 - True
+159243 - 0x033E5 (Pressure Plates 4 Left Exit EP) - 0x01D3F - True
+159244 - 0x018B6 (Pressure Plates 4 Right Exit EP) - 0x01D3F - True
+159250 - 0x28AE9 (Path EP) - True - True
+159251 - 0x3348F (Hedges EP) - True - True
+
+==Shipwreck==
+
Shipwreck (Shipwreck) - Keep 3rd Pressure Plate - True - Shipwreck Vault - 0x17BB4:
158654 - 0x00AFB (Vault Panel) - True - Symmetry & Sound Dots & Colored Dots
Door - 0x17BB4 (Vault Door) - 0x00AFB
@@ -423,19 +467,16 @@ Door - 0x17BB4 (Vault Door) - 0x00AFB
Shipwreck Vault (Shipwreck):
158655 - 0x03535 (Vault Box) - True - True
-Keep Tower (Keep) - Keep - 0x04F8F:
-158206 - 0x0361B (Tower Shortcut Panel) - True - True
-Door - 0x04F8F (Tower Shortcut) - 0x0361B
-158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - True
-158705 - 0x03317 (Laser Panel Pressure Plates) - 0x033EA & 0x01BE9 & 0x01CD3 & 0x01D3F - Shapers & Rotated Shapers & Triangles & Stars & Stars + Same Colored Symbol & Colored Squares & Black/White Squares
-Laser - 0x014BB (Laser) - 0x0360E | 0x03317
-159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - True
-159241 - 0x033BF (Pressure Plates 2 EP) - 0x01BE9 - True
-159242 - 0x033DD (Pressure Plates 3 EP) - 0x01CD3 & 0x01CD5 - True
-159243 - 0x033E5 (Pressure Plates 4 Left Exit EP) - 0x01D3F - True
-159244 - 0x018B6 (Pressure Plates 4 Right Exit EP) - 0x01D3F - True
-159250 - 0x28AE9 (Path EP) - True - True
-159251 - 0x3348F (Hedges EP) - True - True
+==Monastery==
+
+Monastery Obelisk (Monastery) - Entry - True:
+159710 - 0xFFE10 (Obelisk Side 1) - 0x03ABC & 0x03ABE & 0x03AC0 & 0x03AC4 - True
+159711 - 0xFFE11 (Obelisk Side 2) - 0x03AC5 - True
+159712 - 0xFFE12 (Obelisk Side 3) - 0x03BE2 & 0x03BE3 & 0x0A409 - True
+159713 - 0xFFE13 (Obelisk Side 4) - 0x006E5 & 0x006E6 & 0x006E7 & 0x034A7 & 0x034AD & 0x034AF & 0x03DAB & 0x03DAC & 0x03DAD - True
+159714 - 0xFFE14 (Obelisk Side 5) - 0x03E01 - True
+159715 - 0xFFE15 (Obelisk Side 6) - 0x289F4 & 0x289F5 - True
+159719 - 0x00263 (Obelisk) - True - True
Outside Monastery (Monastery) - Main Island - True - Inside Monastery - 0x0C128 & 0x0C153 - Monastery Garden - 0x03750:
158207 - 0x03713 (Laser Shortcut Panel) - True - True
@@ -457,6 +498,9 @@ Laser - 0x17C65 (Laser) - 0x17CA4
159137 - 0x03DAC (Facade Left Stairs EP) - True - True
159138 - 0x03DAD (Facade Right Stairs EP) - True - True
159140 - 0x03E01 (Grass Stairs EP) - True - True
+159120 - 0x03BE2 (Garden Left EP) - 0x03750 - True
+159121 - 0x03BE3 (Garden Right EP) - True - True
+159122 - 0x0A409 (Wall EP) - True - True
Inside Monastery (Monastery):
158213 - 0x09D9B (Shutters Control) - True - Dots
@@ -470,11 +514,22 @@ Inside Monastery (Monastery):
Monastery Garden (Monastery):
-Town (Town) - Main Island - True - The Ocean - 0x0A054 - Town Maze Rooftop - 0x28AA2 - Town Church - True - Town Wooden Rooftop - 0x034F5 - RGB House - 0x28A61 - Windmill Interior - 0x1845B - Town Inside Cargo Box - 0x0A0C9:
+==Town==
+
+Town Obelisk (Town) - Entry - True:
+159750 - 0xFFE50 (Obelisk Side 1) - 0x035C7 - True
+159751 - 0xFFE51 (Obelisk Side 2) - 0x01848 & 0x03D06 & 0x33530 & 0x33600 & 0x28A2F & 0x28A37 & 0x334A3 & 0x3352F - True
+159752 - 0xFFE52 (Obelisk Side 3) - 0x33857 & 0x33879 & 0x03C19 - True
+159753 - 0xFFE53 (Obelisk Side 4) - 0x28B30 & 0x035C9 - True
+159754 - 0xFFE54 (Obelisk Side 5) - 0x03335 & 0x03412 & 0x038A6 & 0x038AA & 0x03E3F & 0x03E40 & 0x28B8E - True
+159755 - 0xFFE55 (Obelisk Side 6) - 0x28B91 & 0x03BCE & 0x03BCF & 0x03BD1 & 0x339B6 & 0x33A20 & 0x33A29 & 0x33A2A & 0x33B06 - True
+159759 - 0x0A16C (Obelisk) - True - True
+
+Town (Town) - Main Island - True - The Ocean - 0x0A054 - Town Maze Rooftop - 0x28AA2 - Town Church - True - Town Wooden Rooftop - 0x034F5 - Town RGB House - 0x28A61 - Town Inside Cargo Box - 0x0A0C9 - Outside Windmill - True:
158218 - 0x0A054 (Boat Spawn) - 0x17CA6 | 0x17CDF | 0x09DB8 | 0x17C95 - Boat
158219 - 0x0A0C8 (Cargo Box Entry Panel) - True - Squares & Black/White Squares & Shapers & Triangles
Door - 0x0A0C9 (Cargo Box Entry) - 0x0A0C8
-158707 - 0x09F98 (Desert Laser Redirect) - True - True
+158707 - 0x09F98 (Desert Laser Redirect Control) - True - True
158220 - 0x18590 (Transparent) - True - Symmetry
158221 - 0x28AE3 (Vines) - 0x18590 - True
158222 - 0x28938 (Apple Tree) - 0x28AE3 - True
@@ -491,11 +546,6 @@ Door - 0x28A61 (RGB House Entry) - 0x28A0D
Door - 0x03BB0 (Church Entry) - 0x03C08
158228 - 0x28A79 (Maze Panel) - True - True
Door - 0x28AA2 (Maze Stairs) - 0x28A79
-158241 - 0x17F5F (Windmill Entry Panel) - True - Dots
-Door - 0x1845B (Windmill Entry) - 0x17F5F
-159010 - 0x037B6 (Windmill First Blade EP) - 0x17D02 - True
-159011 - 0x037B2 (Windmill Second Blade EP) - 0x17D02 - True
-159012 - 0x000F7 (Windmill Third Blade EP) - 0x17D02 - True
159540 - 0x03335 (Tower Underside Third EP) - True - True
159541 - 0x03412 (Tower Underside Fourth EP) - True - True
159542 - 0x038A6 (Tower Underside First EP) - True - True
@@ -528,20 +578,26 @@ Town Church (Town):
158227 - 0x28A69 (Church Lattice) - 0x03BB0 - True
159553 - 0x03BD1 (Black Line Church EP) - True - True
-RGB House (Town) - RGB Room - 0x2897B:
+Town RGB House (Town RGB House) - Town RGB House Upstairs - 0x2897B:
158242 - 0x034E4 (Sound Room Left) - True - True
158243 - 0x034E3 (Sound Room Right) - True - Sound Dots
-Door - 0x2897B (RGB House Stairs) - 0x034E4 & 0x034E3
+Door - 0x2897B (Stairs) - 0x034E4 & 0x034E3
-RGB Room (Town):
+Town RGB House Upstairs (Town RGB House Upstairs):
158244 - 0x334D8 (RGB Control) - True - Rotated Shapers & Squares & Colored Squares & Triangles
-158245 - 0x03C0C (RGB Room Left) - 0x334D8 - Squares & Colored Squares & Black/White Squares & Eraser
-158246 - 0x03C08 (RGB Room Right) - 0x334D8 & 0x03C0C - Symmetry & Dots & Colored Dots & Triangles
+158245 - 0x03C0C (Left) - 0x334D8 - Squares & Colored Squares & Black/White Squares & Eraser
+158246 - 0x03C08 (Right) - 0x334D8 & 0x03C0C - Symmetry & Dots & Colored Dots & Triangles
+
+Town Tower Bottom (Town Tower) - Town - True - Town Tower After First Door - 0x27799:
+Door - 0x27799 (First Door) - 0x28A69
-Town Tower (Town Tower) - Town - True - Town Tower Top - 0x27798 & 0x27799 & 0x2779A & 0x2779C:
+Town Tower After First Door (Town Tower) - Town Tower After Second Door - 0x27798:
Door - 0x27798 (Second Door) - 0x28ACC
+
+Town Tower After Second Door (Town Tower) - Town Tower After Third Door - 0x2779C:
Door - 0x2779C (Third Door) - 0x28AD9
-Door - 0x27799 (First Door) - 0x28A69
+
+Town Tower After Third Door (Town Tower) - Town Tower Top - 0x2779A:
Door - 0x2779A (Fourth Door) - 0x28B39
Town Tower Top (Town):
@@ -550,6 +606,15 @@ Laser - 0x032F9 (Laser) - 0x032F5
159422 - 0x33692 (Brown Bridge EP) - True - True
159551 - 0x03BCE (Black Line Tower EP) - True - True
+==Windmill & Theater==
+
+Outside Windmill (Windmill) - Windmill Interior - 0x1845B:
+159010 - 0x037B6 (First Blade EP) - 0x17D02 - True
+159011 - 0x037B2 (Second Blade EP) - 0x17D02 - True
+159012 - 0x000F7 (Third Blade EP) - 0x17D02 - True
+158241 - 0x17F5F (Entry Panel) - True - Dots
+Door - 0x1845B (Entry) - 0x17F5F
+
Windmill Interior (Windmill) - Theater - 0x17F88:
158247 - 0x17D02 (Turn Control) - True - Dots
158248 - 0x17F89 (Theater Entry Panel) - True - Squares & Black/White Squares & Eraser & Triangles
@@ -573,6 +638,8 @@ Door - 0x3CCDF (Exit Right) - 0x33AB2
159556 - 0x33A2A (Door EP) - 0x03553 - True
159558 - 0x33B06 (Church EP) - 0x0354E - True
+==Jungle==
+
Jungle (Jungle) - Main Island - True - The Ocean - 0x17CDF:
158251 - 0x17CDF (Shore Boat Spawn) - True - Boat
158609 - 0x17F9B (Discard) - True - Arrows
@@ -604,19 +671,18 @@ Door - 0x3873B (Laser Shortcut) - 0x337FA
159350 - 0x035CB (Bamboo CCW EP) - True - True
159351 - 0x035CF (Bamboo CW EP) - True - True
-Outside Jungle River (River) - Main Island - True - Monastery Garden - 0x0CF2A - River Vault - 0x15287:
+Outside Jungle River (Jungle) - Main Island - True - Monastery Garden - 0x0CF2A - Jungle Vault - 0x15287:
158267 - 0x17CAA (Monastery Garden Shortcut Panel) - True - True
Door - 0x0CF2A (Monastery Garden Shortcut) - 0x17CAA
158663 - 0x15ADD (Vault Panel) - True - Black/White Squares & Dots
Door - 0x15287 (Vault Door) - 0x15ADD
159110 - 0x03AC5 (Green Leaf Moss EP) - True - True
-159120 - 0x03BE2 (Monastery Garden Left EP) - 0x03750 - True
-159121 - 0x03BE3 (Monastery Garden Right EP) - True - True
-159122 - 0x0A409 (Monastery Wall EP) - True - True
-River Vault (River):
+Jungle Vault (Jungle):
158664 - 0x03702 (Vault Box) - True - True
+==Bunker==
+
Outside Bunker (Bunker) - Main Island - True - Bunker - 0x0C2A4:
158268 - 0x17C2E (Entry Panel) - True - Squares & Black/White Squares
Door - 0x0C2A4 (Entry) - 0x17C2E
@@ -650,9 +716,11 @@ Door - 0x0A08D (Elevator Room Entry) - 0x17E67
Bunker Elevator Section (Bunker) - Bunker Elevator - TrueOneWay:
159311 - 0x035F5 (Tinted Door EP) - 0x17C79 - True
-Bunker Elevator (Bunker) - Bunker Elevator Section - 0x0A079 - Bunker Green Room - 0x0A079 - Bunker Laser Platform - 0x0A079 - Outside Bunker - 0x0A079:
+Bunker Elevator (Bunker) - Bunker Elevator Section - 0x0A079 - Bunker Cyan Room - 0x0A079 - Bunker Green Room - 0x0A079 - Bunker Laser Platform - 0x0A079 - Outside Bunker - 0x0A079:
158286 - 0x0A079 (Elevator Control) - True - Colored Squares & Black/White Squares
+Bunker Cyan Room (Bunker) - Bunker Elevator - TrueOneWay:
+
Bunker Green Room (Bunker) - Bunker Elevator - TrueOneWay:
159310 - 0x000D3 (Green Room Flowers EP) - True - True
@@ -660,6 +728,8 @@ Bunker Laser Platform (Bunker) - Bunker Elevator - TrueOneWay:
158710 - 0x09DE0 (Laser Panel) - True - True
Laser - 0x0C2B2 (Laser) - 0x09DE0
+==Swamp==
+
Outside Swamp (Swamp) - Swamp Entry Area - 0x00C1C - Main Island - True:
158287 - 0x0056E (Entry Panel) - True - Rotated Shapers & Black/White Squares & Triangles
Door - 0x00C1C (Entry) - 0x0056E
@@ -774,13 +844,29 @@ Laser - 0x00BF6 (Laser) - 0x03615
158342 - 0x17C02 (Laser Shortcut Right Panel) - 0x17C05 - Shapers & Negative Shapers & Stars & Stars + Same Colored Symbol
Door - 0x2D880 (Laser Shortcut) - 0x17C02
-Treehouse Entry Area (Treehouse) - Treehouse Between Doors - 0x0C309 - The Ocean - 0x17C95:
+==Treehouse==
+
+Treehouse Obelisk (Treehouse) - Entry - True:
+159720 - 0xFFE20 (Obelisk Side 1) - 0x0053D & 0x0053E & 0x00769 - True
+159721 - 0xFFE21 (Obelisk Side 2) - 0x33721 & 0x220A7 & 0x220BD - True
+159722 - 0xFFE22 (Obelisk Side 3) - 0x03B22 & 0x03B23 & 0x03B24 & 0x03B25 & 0x03A79 & 0x28ABD & 0x28ABE - True
+159723 - 0xFFE23 (Obelisk Side 4) - 0x3388F & 0x28B29 & 0x28B2A - True
+159724 - 0xFFE24 (Obelisk Side 5) - 0x018B6 & 0x033BE & 0x033BF & 0x033DD & 0x033E5 - True
+159725 - 0xFFE25 (Obelisk Side 6) - 0x28AE9 & 0x3348F - True
+159729 - 0x00097 (Obelisk) - True - True
+
+Treehouse Beach (Treehouse Beach) - Main Island - True:
+159200 - 0x0053D (Rock Shadow EP) - True - True
+159201 - 0x0053E (Sand Shadow EP) - True - True
+159212 - 0x220BD (Both Orange Bridges EP) - 0x17DA2 & 0x17DDB - True
+
+Treehouse Entry Area (Treehouse) - Treehouse Between Entry Doors - 0x0C309 - The Ocean - 0x17C95:
158343 - 0x17C95 (Boat Spawn) - True - Boat
158344 - 0x0288C (First Door Panel) - True - Stars & Stars + Same Colored Symbol & Triangles
Door - 0x0C309 (First Door) - 0x0288C
159210 - 0x33721 (Buoy EP) - 0x17C95 - True
-Treehouse Between Doors (Treehouse) - Treehouse Yellow Bridge - 0x0C310:
+Treehouse Between Entry Doors (Treehouse) - Treehouse Yellow Bridge - 0x0C310:
158345 - 0x02886 (Second Door Panel) - True - Stars & Stars + Same Colored Symbol & Triangles
Door - 0x0C310 (Second Door) - 0x02886
@@ -809,7 +895,7 @@ Treehouse First Purple Bridge (Treehouse) - Treehouse Second Purple Bridge - 0x1
158360 - 0x17D2D (First Purple Bridge 4) - 0x17CE4 - Stars & Dots & Full Dots
158361 - 0x17D6C (First Purple Bridge 5) - 0x17D2D - Stars & Dots & Full Dots
-Treehouse Right Orange Bridge (Treehouse) - Treehouse Bridge Platform - 0x17DA2:
+Treehouse Right Orange Bridge (Treehouse) - Treehouse Drawbridge Platform - 0x17DA2:
158391 - 0x17D88 (Right Orange Bridge 1) - True - Stars & Stars + Same Colored Symbol & Triangles
158392 - 0x17DB4 (Right Orange Bridge 2) - 0x17D88 - Stars & Stars + Same Colored Symbol & Triangles
158393 - 0x17D8C (Right Orange Bridge 3) - 0x17DB4 - Stars & Stars + Same Colored Symbol & Triangles
@@ -823,7 +909,7 @@ Treehouse Right Orange Bridge (Treehouse) - Treehouse Bridge Platform - 0x17DA2:
158401 - 0x17DB1 (Right Orange Bridge 11) - 0x17DB7 - Stars & Stars + Same Colored Symbol & Triangles
158402 - 0x17DA2 (Right Orange Bridge 12) - 0x17DB1 - Stars & Stars + Same Colored Symbol & Triangles
-Treehouse Bridge Platform (Treehouse) - Main Island - 0x0C32D:
+Treehouse Drawbridge Platform (Treehouse) - Main Island - 0x0C32D:
158404 - 0x037FF (Drawbridge Panel) - True - Stars
Door - 0x0C32D (Drawbridge) - 0x037FF
@@ -882,7 +968,19 @@ Treehouse Laser Room (Treehouse):
158403 - 0x17CBC (Laser House Door Timer Inside) - True - True
Laser - 0x028A4 (Laser) - 0x03613
+==Mountain (Outside)==
+
+Mountainside Obelisk (Mountainside) - Entry - True:
+159730 - 0xFFE30 (Obelisk Side 1) - 0x001A3 & 0x335AE - True
+159731 - 0xFFE31 (Obelisk Side 2) - 0x000D3 & 0x035F5 & 0x09D5D & 0x09D5E & 0x09D63 - True
+159732 - 0xFFE32 (Obelisk Side 3) - 0x3370E & 0x035DE & 0x03601 & 0x03603 & 0x03D0D & 0x3369A & 0x336C8 & 0x33505 - True
+159733 - 0xFFE33 (Obelisk Side 4) - 0x03A9E & 0x016B2 & 0x3365F & 0x03731 & 0x036CE & 0x03C07 & 0x03A93 - True
+159734 - 0xFFE34 (Obelisk Side 5) - 0x03AA6 & 0x3397C & 0x0105D & 0x0A304 - True
+159735 - 0xFFE35 (Obelisk Side 6) - 0x035CB & 0x035CF - True
+159739 - 0x00367 (Obelisk) - True - True
+
Mountainside (Mountainside) - Main Island - True - Mountaintop - True - Mountainside Vault - 0x00085:
+159550 - 0x28B91 (Thundercloud EP) - 0x09F98 & 0x012FB - True
158612 - 0x17C42 (Discard) - True - Arrows
158665 - 0x002A6 (Vault Panel) - True - Symmetry & Colored Squares & Triangles & Stars & Stars + Same Colored Symbol
Door - 0x00085 (Vault Door) - 0x002A6
@@ -893,20 +991,22 @@ Door - 0x00085 (Vault Door) - 0x002A6
Mountainside Vault (Mountainside):
158666 - 0x03542 (Vault Box) - True - True
-Mountaintop (Mountaintop) - Mountain Top Layer - 0x17C34:
+Mountaintop (Mountaintop) - Mountain Floor 1 - 0x17C34:
158405 - 0x0042D (River Shape) - True - True
-158406 - 0x09F7F (Box Short) - 7 Lasers - True
+158406 - 0x09F7F (Box Short) - 7 Lasers + Redirect - True
158407 - 0x17C34 (Mountain Entry Panel) - 0x09F7F - Stars & Black/White Squares & Stars + Same Colored Symbol & Triangles
-158800 - 0xFFF00 (Box Long) - 11 Lasers & 0x17C34 - True
+158800 - 0xFFF00 (Box Long) - 11 Lasers + Redirect & 0x17C34 - True
159300 - 0x001A3 (River Shape EP) - True - True
159320 - 0x3370E (Arch Black EP) - True - True
159324 - 0x336C8 (Arch White Right EP) - True - True
159326 - 0x3369A (Arch White Left EP) - True - True
-Mountain Top Layer (Mountain Floor 1) - Mountain Top Layer Bridge - 0x09E39:
+==Mountain (Inside)==
+
+Mountain Floor 1 (Mountain Floor 1) - Mountain Floor 1 Bridge - 0x09E39:
158408 - 0x09E39 (Light Bridge Controller) - True - Eraser & Triangles
-Mountain Top Layer Bridge (Mountain Floor 1) - Mountain Top Layer At Door - TrueOneWay:
+Mountain Floor 1 Bridge (Mountain Floor 1) - Mountain Floor 1 At Door - TrueOneWay:
158409 - 0x09E7A (Right Row 1) - True - Black/White Squares & Dots & Stars & Stars + Same Colored Symbol
158410 - 0x09E71 (Right Row 2) - 0x09E7A - Black/White Squares & Triangles
158411 - 0x09E72 (Right Row 3) - 0x09E71 - Black/White Squares & Shapers & Stars & Stars + Same Colored Symbol
@@ -925,10 +1025,10 @@ Mountain Top Layer Bridge (Mountain Floor 1) - Mountain Top Layer At Door - True
158424 - 0x09EAD (Trash Pillar 1) - True - Rotated Shapers & Stars
158425 - 0x09EAF (Trash Pillar 2) - 0x09EAD - Rotated Shapers & Triangles
-Mountain Top Layer At Door (Mountain Floor 1) - Mountain Floor 2 - 0x09E54:
+Mountain Floor 1 At Door (Mountain Floor 1) - Mountain Floor 2 - 0x09E54:
Door - 0x09E54 (Exit) - 0x09EAF & 0x09F6E & 0x09E6B & 0x09E7B
-Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near - 0x09FFB - Mountain Floor 2 Blue Bridge - 0x09E86 - Mountain Pink Bridge EP - TrueOneWay:
+Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near - 0x09FFB - Mountain Floor 2 Beyond Bridge - 0x09E86 - Mountain Floor 2 At Door - 0x09ED8 & 0x09E86 - Mountain Pink Bridge EP - TrueOneWay:
158426 - 0x09FD3 (Near Row 1) - True - Stars & Colored Squares & Stars + Same Colored Symbol
158427 - 0x09FD4 (Near Row 2) - 0x09FD3 - Stars & Triangles & Stars + Same Colored Symbol
158428 - 0x09FD6 (Near Row 3) - 0x09FD4 - Stars & Shapers & Negative Shapers & Stars + Same Colored Symbol
@@ -936,8 +1036,6 @@ Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near -
158430 - 0x09FD8 (Near Row 5) - 0x09FD7 - Stars & Stars + Same Colored Symbol & Rotated Shapers & Eraser
Door - 0x09FFB (Staircase Near) - 0x09FD8
-Mountain Floor 2 Blue Bridge (Mountain Floor 2) - Mountain Floor 2 Beyond Bridge - TrueOneWay - Mountain Floor 2 At Door - 0x09ED8:
-
Mountain Floor 2 At Door (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EDD:
Door - 0x09EDD (Elevator Room Entry) - 0x09ED8 & 0x09E86
@@ -959,10 +1057,10 @@ Mountain Floor 2 Light Bridge Room Far (Mountain Floor 2):
Mountain Floor 2 Elevator Room (Mountain Floor 2) - Mountain Floor 2 Elevator - TrueOneWay:
158613 - 0x17F93 (Elevator Discard) - True - Arrows
-Mountain Floor 2 Elevator (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EEB - Mountain Third Layer - 0x09EEB:
+Mountain Floor 2 Elevator (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EEB - Mountain Floor 3 - 0x09EEB:
158439 - 0x09EEB (Elevator Control Panel) - True - Dots
-Mountain Third Layer (Mountain Bottom Floor) - Mountain Floor 2 Elevator - TrueOneWay - Mountain Bottom Floor - 0x09F89 - Mountain Pink Bridge EP - TrueOneWay:
+Mountain Floor 3 (Mountain Bottom Floor) - Mountain Floor 2 Elevator - TrueOneWay - Mountain Bottom Floor - 0x09F89 - Mountain Pink Bridge EP - TrueOneWay:
158440 - 0x09FC1 (Giant Puzzle Bottom Left) - True - Shapers & Eraser & Negative Shapers
158441 - 0x09F8E (Giant Puzzle Bottom Right) - True - Shapers & Eraser & Negative Shapers
158442 - 0x09F01 (Giant Puzzle Top Right) - True - Shapers & Eraser & Negative Shapers
@@ -972,13 +1070,32 @@ Mountain Third Layer (Mountain Bottom Floor) - Mountain Floor 2 Elevator - TrueO
159314 - 0x09D5E (Blue Bridge EP) - 0x09E86 & 0x09ED8 - True
Door - 0x09F89 (Exit) - 0x09FDA
-Mountain Bottom Floor (Mountain Bottom Floor) - Mountain Path to Caves - 0x17F33 - Final Room - 0x0C141:
+Mountain Bottom Floor (Mountain Bottom Floor) - Mountain Path to Caves - 0x17F33 - Mountain Bottom Floor Pillars Room - 0x0C141:
158614 - 0x17FA2 (Discard) - 0xFFF00 - Arrows
-158445 - 0x01983 (Final Room Entry Left) - True - Shapers & Stars
-158446 - 0x01987 (Final Room Entry Right) - True - Squares & Colored Squares & Dots
-Door - 0x0C141 (Final Room Entry) - 0x01983 & 0x01987
+158445 - 0x01983 (Pillars Room Entry Left) - True - Shapers & Stars
+158446 - 0x01987 (Pillars Room Entry Right) - True - Squares & Colored Squares & Dots
+Door - 0x0C141 (Pillars Room Entry) - 0x01983 & 0x01987
Door - 0x17F33 (Rock Open) - 0x17FA2 | 0x334E1
+Mountain Bottom Floor Pillars Room (Mountain Bottom Floor) - Elevator - 0x339BB & 0x33961:
+158522 - 0x0383A (Right Pillar 1) - True - Stars & Eraser & Triangles & Stars + Same Colored Symbol
+158523 - 0x09E56 (Right Pillar 2) - 0x0383A - Dots & Full Dots & Triangles & Symmetry
+158524 - 0x09E5A (Right Pillar 3) - 0x09E56 - Dots & Shapers & Stars & Negative Shapers & Stars + Same Colored Symbol & Symmetry
+158525 - 0x33961 (Right Pillar 4) - 0x09E5A - Eraser & Symmetry & Stars & Stars + Same Colored Symbol & Negative Shapers & Shapers
+158526 - 0x0383D (Left Pillar 1) - True - Stars & Black/White Squares & Stars + Same Colored Symbol
+158527 - 0x0383F (Left Pillar 2) - 0x0383D - Triangles & Symmetry
+158528 - 0x03859 (Left Pillar 3) - 0x0383F - Symmetry & Shapers & Black/White Squares
+158529 - 0x339BB (Left Pillar 4) - 0x03859 - Symmetry & Black/White Squares & Stars & Stars + Same Colored Symbol & Triangles & Colored Dots
+
+Elevator (Mountain Bottom Floor):
+158530 - 0x3D9A6 (Elevator Door Closer Left) - True - True
+158531 - 0x3D9A7 (Elevator Door Close Right) - True - True
+158532 - 0x3C113 (Elevator Entry Left) - 0x3D9A6 | 0x3D9A7 - True
+158533 - 0x3C114 (Elevator Entry Right) - 0x3D9A6 | 0x3D9A7 - True
+158534 - 0x3D9AA (Back Wall Left) - 0x3D9A6 | 0x3D9A7 - True
+158535 - 0x3D9A8 (Back Wall Right) - 0x3D9A6 | 0x3D9A7 - True
+158536 - 0x3D9A9 (Elevator Start) - 0x3D9AA & 7 Lasers | 0x3D9A8 & 7 Lasers - True
+
Mountain Pink Bridge EP (Mountain Floor 2):
159312 - 0x09D63 (Pink Bridge EP) - 0x09E39 - True
@@ -987,7 +1104,9 @@ Mountain Path to Caves (Mountain Bottom Floor) - Caves - 0x2D77D:
Door - 0x2D77D (Caves Entry) - 0x00FF8
158448 - 0x334E1 (Rock Control) - True - True
-Caves (Caves) - Main Island - 0x2D73F | 0x2D859 - Path to Challenge - 0x019A5:
+==Caves==
+
+Caves (Caves) - Main Island - 0x2D73F | 0x2D859 - Caves Path to Challenge - 0x019A5:
158451 - 0x335AB (Elevator Inside Control) - True - Dots & Squares & Black/White Squares
158452 - 0x335AC (Elevator Upper Outside Control) - 0x335AB - Squares & Black/White Squares
158453 - 0x3369D (Elevator Lower Outside Control) - 0x335AB - Squares & Black/White Squares & Dots
@@ -1042,10 +1161,12 @@ Door - 0x2D73F (Mountain Shortcut Door) - 0x021D7
Door - 0x2D859 (Swamp Shortcut Door) - 0x17CF2
159341 - 0x3397C (Skylight EP) - True - True
-Path to Challenge (Caves) - Challenge - 0x0A19A:
+Caves Path to Challenge (Caves) - Challenge - 0x0A19A:
158477 - 0x0A16E (Challenge Entry Panel) - True - Stars & Arrows & Stars + Same Colored Symbol
Door - 0x0A19A (Challenge Entry) - 0x0A16E
+==Challenge==
+
Challenge (Challenge) - Tunnels - 0x0348A - Challenge Vault - 0x04D75:
158499 - 0x0A332 (Start Timer) - 11 Lasers - True
158500 - 0x0088E (Small Basic) - 0x0A332 - True
@@ -1074,7 +1195,9 @@ Door - 0x0348A (Tunnels Entry) - 0x039B4
Challenge Vault (Challenge):
158667 - 0x0356B (Vault Box) - 0x1C31A & 0x1C319 - True
-Tunnels (Tunnels) - Windmill Interior - 0x27739 - Desert Lowest Level Inbetween Shortcuts - 0x27263 - Town - 0x09E87:
+==Tunnels==
+
+Tunnels (Tunnels) - Windmill Interior - 0x27739 - Desert Behind Elevator - 0x27263 - Town - 0x09E87:
158668 - 0x2FAF6 (Vault Box) - True - True
158519 - 0x27732 (Theater Shortcut Panel) - True - True
Door - 0x27739 (Theater Shortcut) - 0x27732
@@ -1084,24 +1207,7 @@ Door - 0x27263 (Desert Shortcut) - 0x2773D
Door - 0x09E87 (Town Shortcut) - 0x09E85
159557 - 0x33A20 (Theater Flowers EP) - 0x03553 & Theater to Tunnels - True
-Final Room (Mountain Final Room) - Elevator - 0x339BB & 0x33961:
-158522 - 0x0383A (Right Pillar 1) - True - Stars & Eraser & Triangles & Stars + Same Colored Symbol
-158523 - 0x09E56 (Right Pillar 2) - 0x0383A - Dots & Full Dots & Triangles & Symmetry
-158524 - 0x09E5A (Right Pillar 3) - 0x09E56 - Dots & Shapers & Stars & Negative Shapers & Stars + Same Colored Symbol & Symmetry
-158525 - 0x33961 (Right Pillar 4) - 0x09E5A - Eraser & Symmetry & Stars & Stars + Same Colored Symbol & Negative Shapers & Shapers
-158526 - 0x0383D (Left Pillar 1) - True - Stars & Black/White Squares & Stars + Same Colored Symbol
-158527 - 0x0383F (Left Pillar 2) - 0x0383D - Triangles & Symmetry
-158528 - 0x03859 (Left Pillar 3) - 0x0383F - Symmetry & Shapers & Black/White Squares
-158529 - 0x339BB (Left Pillar 4) - 0x03859 - Symmetry & Black/White Squares & Stars & Stars + Same Colored Symbol & Triangles & Colored Dots
-
-Elevator (Mountain Final Room):
-158530 - 0x3D9A6 (Elevator Door Closer Left) - True - True
-158531 - 0x3D9A7 (Elevator Door Close Right) - True - True
-158532 - 0x3C113 (Elevator Entry Left) - 0x3D9A6 | 0x3D9A7 - True
-158533 - 0x3C114 (Elevator Entry Right) - 0x3D9A6 | 0x3D9A7 - True
-158534 - 0x3D9AA (Back Wall Left) - 0x3D9A6 | 0x3D9A7 - True
-158535 - 0x3D9A8 (Back Wall Right) - 0x3D9A6 | 0x3D9A7 - True
-158536 - 0x3D9A9 (Elevator Start) - 0x3D9AA & 7 Lasers | 0x3D9A8 & 7 Lasers - True
+==Boat==
The Ocean (Boat) - Main Island - TrueOneWay - Swamp Near Boat - TrueOneWay - Treehouse Entry Area - TrueOneWay - Quarry Boathouse Behind Staircase - TrueOneWay - Inside Glass Factory Behind Back Wall - TrueOneWay:
159042 - 0x22106 (Desert EP) - True - True
@@ -1114,45 +1220,3 @@ The Ocean (Boat) - Main Island - TrueOneWay - Swamp Near Boat - TrueOneWay - Tre
159521 - 0x33879 (Tutorial Reflection EP) - True - True
159522 - 0x03C19 (Tutorial Moss EP) - True - True
159531 - 0x035C9 (Cargo Box EP) - 0x0A0C9 - True
-
-Obelisks (EPs) - Entry - True:
-159700 - 0xFFE00 (Desert Obelisk Side 1) - 0x0332B & 0x03367 & 0x28B8A - True
-159701 - 0xFFE01 (Desert Obelisk Side 2) - 0x037B6 & 0x037B2 & 0x000F7 - True
-159702 - 0xFFE02 (Desert Obelisk Side 3) - 0x3351D - True
-159703 - 0xFFE03 (Desert Obelisk Side 4) - 0x0053C & 0x00771 & 0x335C8 & 0x335C9 & 0x337F8 & 0x037BB & 0x220E4 & 0x220E5 - True
-159704 - 0xFFE04 (Desert Obelisk Side 5) - 0x334B9 & 0x334BC & 0x22106 & 0x0A14C & 0x0A14D - True
-159709 - 0x00359 (Desert Obelisk) - True - True
-159710 - 0xFFE10 (Monastery Obelisk Side 1) - 0x03ABC & 0x03ABE & 0x03AC0 & 0x03AC4 - True
-159711 - 0xFFE11 (Monastery Obelisk Side 2) - 0x03AC5 - True
-159712 - 0xFFE12 (Monastery Obelisk Side 3) - 0x03BE2 & 0x03BE3 & 0x0A409 - True
-159713 - 0xFFE13 (Monastery Obelisk Side 4) - 0x006E5 & 0x006E6 & 0x006E7 & 0x034A7 & 0x034AD & 0x034AF & 0x03DAB & 0x03DAC & 0x03DAD - True
-159714 - 0xFFE14 (Monastery Obelisk Side 5) - 0x03E01 - True
-159715 - 0xFFE15 (Monastery Obelisk Side 6) - 0x289F4 & 0x289F5 - True
-159719 - 0x00263 (Monastery Obelisk) - True - True
-159720 - 0xFFE20 (Treehouse Obelisk Side 1) - 0x0053D & 0x0053E & 0x00769 - True
-159721 - 0xFFE21 (Treehouse Obelisk Side 2) - 0x33721 & 0x220A7 & 0x220BD - True
-159722 - 0xFFE22 (Treehouse Obelisk Side 3) - 0x03B22 & 0x03B23 & 0x03B24 & 0x03B25 & 0x03A79 & 0x28ABD & 0x28ABE - True
-159723 - 0xFFE23 (Treehouse Obelisk Side 4) - 0x3388F & 0x28B29 & 0x28B2A - True
-159724 - 0xFFE24 (Treehouse Obelisk Side 5) - 0x018B6 & 0x033BE & 0x033BF & 0x033DD & 0x033E5 - True
-159725 - 0xFFE25 (Treehouse Obelisk Side 6) - 0x28AE9 & 0x3348F - True
-159729 - 0x00097 (Treehouse Obelisk) - True - True
-159730 - 0xFFE30 (River Obelisk Side 1) - 0x001A3 & 0x335AE - True
-159731 - 0xFFE31 (River Obelisk Side 2) - 0x000D3 & 0x035F5 & 0x09D5D & 0x09D5E & 0x09D63 - True
-159732 - 0xFFE32 (River Obelisk Side 3) - 0x3370E & 0x035DE & 0x03601 & 0x03603 & 0x03D0D & 0x3369A & 0x336C8 & 0x33505 - True
-159733 - 0xFFE33 (River Obelisk Side 4) - 0x03A9E & 0x016B2 & 0x3365F & 0x03731 & 0x036CE & 0x03C07 & 0x03A93 - True
-159734 - 0xFFE34 (River Obelisk Side 5) - 0x03AA6 & 0x3397C & 0x0105D & 0x0A304 - True
-159735 - 0xFFE35 (River Obelisk Side 6) - 0x035CB & 0x035CF - True
-159739 - 0x00367 (River Obelisk) - True - True
-159740 - 0xFFE40 (Quarry Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True
-159741 - 0xFFE41 (Quarry Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True
-159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 - True
-159743 - 0xFFE43 (Quarry Obelisk Side 4) - 0x33692 - True
-159744 - 0xFFE44 (Quarry Obelisk Side 5) - 0x03E77 & 0x03E7C - True
-159749 - 0x22073 (Quarry Obelisk) - True - True
-159750 - 0xFFE50 (Town Obelisk Side 1) - 0x035C7 - True
-159751 - 0xFFE51 (Town Obelisk Side 2) - 0x01848 & 0x03D06 & 0x33530 & 0x33600 & 0x28A2F & 0x28A37 & 0x334A3 & 0x3352F - True
-159752 - 0xFFE52 (Town Obelisk Side 3) - 0x33857 & 0x33879 & 0x03C19 - True
-159753 - 0xFFE53 (Town Obelisk Side 4) - 0x28B30 & 0x035C9 - True
-159754 - 0xFFE54 (Town Obelisk Side 5) - 0x03335 & 0x03412 & 0x038A6 & 0x038AA & 0x03E3F & 0x03E40 & 0x28B8E - True
-159755 - 0xFFE55 (Town Obelisk Side 6) - 0x28B91 & 0x03BCE & 0x03BCF & 0x03BD1 & 0x339B6 & 0x33A20 & 0x33A29 & 0x33A2A & 0x33B06 - True
-159759 - 0x0A16C (Town Obelisk) - True - True
diff --git a/worlds/witness/WitnessLogicVanilla.txt b/worlds/witness/WitnessLogicVanilla.txt
index 779ead6bde4b..62c38d412427 100644
--- a/worlds/witness/WitnessLogicVanilla.txt
+++ b/worlds/witness/WitnessLogicVanilla.txt
@@ -1,12 +1,14 @@
+==Tutorial (Inside)==
+
Menu (Menu) - Entry - True:
Entry (Entry):
-First Hallway (First Hallway) - Entry - True - First Hallway Room - 0x00064:
+Tutorial First Hallway (Tutorial First Hallway) - Entry - True - Tutorial First Hallway Room - 0x00064:
158000 - 0x00064 (Straight) - True - True
159510 - 0x01848 (EP) - 0x00064 - True
-First Hallway Room (First Hallway) - Tutorial - 0x00182:
+Tutorial First Hallway Room (Tutorial First Hallway) - Tutorial - 0x00182:
158001 - 0x00182 (Bend) - True - True
Tutorial (Tutorial) - Outside Tutorial - 0x03629:
@@ -23,6 +25,8 @@ Tutorial (Tutorial) - Outside Tutorial - 0x03629:
159513 - 0x33600 (Patio Flowers EP) - 0x0C373 - True
159517 - 0x3352F (Gate EP) - 0x03505 - True
+==Tutorial (Outside)==
+
Outside Tutorial (Outside Tutorial) - Outside Tutorial Path To Outpost - 0x03BA2 - Outside Tutorial Vault - 0x033D0:
158650 - 0x033D4 (Vault Panel) - True - Dots & Black/White Squares
Door - 0x033D0 (Vault Door) - 0x033D4
@@ -58,9 +62,23 @@ Outside Tutorial Outpost (Outside Tutorial) - Outside Tutorial - 0x04CA3:
Door - 0x04CA3 (Outpost Exit) - 0x04CA4
158600 - 0x17CFB (Discard) - True - Triangles
+Orchard (Orchard) - Main Island - True - Orchard Beyond First Gate - 0x03307:
+158071 - 0x00143 (Apple Tree 1) - True - True
+158072 - 0x0003B (Apple Tree 2) - 0x00143 - True
+158073 - 0x00055 (Apple Tree 3) - 0x0003B - True
+Door - 0x03307 (First Gate) - 0x00055
+
+Orchard Beyond First Gate (Orchard) - Orchard End - 0x03313:
+158074 - 0x032F7 (Apple Tree 4) - 0x00055 - True
+158075 - 0x032FF (Apple Tree 5) - 0x032F7 - True
+Door - 0x03313 (Second Gate) - 0x032FF
+
+Orchard End (Orchard):
+
Main Island (Main Island) - Outside Tutorial - True:
159801 - 0xFFD00 (Reached Independently) - True - True
-159550 - 0x28B91 (Thundercloud EP) - 0x09F98 & 0x012FB - True
+
+==Glass Factory==
Outside Glass Factory (Glass Factory) - Main Island - True - Inside Glass Factory - 0x01A29:
158027 - 0x01A54 (Entry Panel) - True - Symmetry
@@ -85,6 +103,8 @@ Door - 0x0D7ED (Back Wall) - 0x0005C
Inside Glass Factory Behind Back Wall (Glass Factory) - The Ocean - 0x17CC8:
158039 - 0x17CC8 (Boat Spawn) - 0x17CA6 | 0x17CDF | 0x09DB8 | 0x17C95 - Boat
+==Symmetry Island==
+
Outside Symmetry Island (Symmetry Island) - Main Island - True - Symmetry Island Lower - 0x17F3E:
158040 - 0x000B0 (Lower Panel) - 0x0343A - Dots
Door - 0x17F3E (Lower) - 0x000B0
@@ -128,20 +148,17 @@ Symmetry Island Upper (Symmetry Island):
Laser - 0x00509 (Laser) - 0x0360D
159001 - 0x03367 (Glass Factory Black Line EP) - True - True
-Orchard (Orchard) - Main Island - True - Orchard Beyond First Gate - 0x03307:
-158071 - 0x00143 (Apple Tree 1) - True - True
-158072 - 0x0003B (Apple Tree 2) - 0x00143 - True
-158073 - 0x00055 (Apple Tree 3) - 0x0003B - True
-Door - 0x03307 (First Gate) - 0x00055
-
-Orchard Beyond First Gate (Orchard) - Orchard End - 0x03313:
-158074 - 0x032F7 (Apple Tree 4) - 0x00055 - True
-158075 - 0x032FF (Apple Tree 5) - 0x032F7 - True
-Door - 0x03313 (Second Gate) - 0x032FF
+==Desert==
-Orchard End (Orchard):
+Desert Obelisk (Desert) - Entry - True:
+159700 - 0xFFE00 (Obelisk Side 1) - 0x0332B & 0x03367 & 0x28B8A - True
+159701 - 0xFFE01 (Obelisk Side 2) - 0x037B6 & 0x037B2 & 0x000F7 - True
+159702 - 0xFFE02 (Obelisk Side 3) - 0x3351D - True
+159703 - 0xFFE03 (Obelisk Side 4) - 0x0053C & 0x00771 & 0x335C8 & 0x335C9 & 0x337F8 & 0x037BB & 0x220E4 & 0x220E5 - True
+159704 - 0xFFE04 (Obelisk Side 5) - 0x334B9 & 0x334BC & 0x22106 & 0x0A14C & 0x0A14D - True
+159709 - 0x00359 (Obelisk) - True - True
-Desert Outside (Desert) - Main Island - True - Desert Floodlight Room - 0x09FEE - Desert Vault - 0x03444:
+Desert Outside (Desert) - Main Island - True - Desert Light Room - 0x09FEE - Desert Vault - 0x03444:
158652 - 0x0CC7B (Vault Panel) - True - Dots & Shapers & Rotated Shapers & Negative Shapers & Full Dots
Door - 0x03444 (Vault Door) - 0x0CC7B
158602 - 0x17CE7 (Discard) - True - Triangles
@@ -172,14 +189,14 @@ Laser - 0x012FB (Laser) - 0x03608
Desert Vault (Desert):
158653 - 0x0339E (Vault Box) - True - True
-Desert Floodlight Room (Desert) - Desert Pond Room - 0x0C2C3:
+Desert Light Room (Desert) - Desert Pond Room - 0x0C2C3:
158087 - 0x09FAA (Light Control) - True - True
158088 - 0x00422 (Light Room 1) - 0x09FAA - True
158089 - 0x006E3 (Light Room 2) - 0x09FAA - True
158090 - 0x0A02D (Light Room 3) - 0x09FAA & 0x00422 & 0x006E3 - True
Door - 0x0C2C3 (Pond Room Entry) - 0x0A02D
-Desert Pond Room (Desert) - Desert Water Levels Room - 0x0A24B:
+Desert Pond Room (Desert) - Desert Flood Room - 0x0A24B:
158091 - 0x00C72 (Pond Room 1) - True - True
158092 - 0x0129D (Pond Room 2) - 0x00C72 - True
158093 - 0x008BB (Pond Room 3) - 0x0129D - True
@@ -190,7 +207,7 @@ Door - 0x0A24B (Flood Room Entry) - 0x0A249
159043 - 0x0A14C (Pond Room Near Reflection EP) - True - True
159044 - 0x0A14D (Pond Room Far Reflection EP) - True - True
-Desert Water Levels Room (Desert) - Desert Elevator Room - 0x0C316:
+Desert Flood Room (Desert) - Desert Elevator Room - 0x0C316:
158097 - 0x1C2DF (Reduce Water Level Far Left) - True - True
158098 - 0x1831E (Reduce Water Level Far Right) - True - True
158099 - 0x1C260 (Reduce Water Level Near Left) - True - True
@@ -208,19 +225,29 @@ Desert Water Levels Room (Desert) - Desert Elevator Room - 0x0C316:
Door - 0x0C316 (Elevator Room Entry) - 0x18076
159034 - 0x337F8 (Flood Room EP) - 0x1C2DF - True
-Desert Elevator Room (Desert) - Desert Lowest Level Inbetween Shortcuts - 0x01317:
-158111 - 0x17C31 (Final Transparent) - True - True
-158113 - 0x012D7 (Final Hexagonal) - 0x17C31 & 0x0A015 - True
-158114 - 0x0A015 (Final Hexagonal Control) - 0x17C31 - True
-158115 - 0x0A15C (Final Bent 1) - True - True
-158116 - 0x09FFF (Final Bent 2) - 0x0A15C - True
-158117 - 0x0A15F (Final Bent 3) - 0x09FFF - True
+Desert Elevator Room (Desert) - Desert Behind Elevator - 0x01317:
+158111 - 0x17C31 (Elevator Room Transparent) - True - True
+158113 - 0x012D7 (Elevator Room Hexagonal) - 0x17C31 & 0x0A015 - True
+158114 - 0x0A015 (Elevator Room Hexagonal Control) - 0x17C31 - True
+158115 - 0x0A15C (Elevator Room Bent 1) - True - True
+158116 - 0x09FFF (Elevator Room Bent 2) - 0x0A15C - True
+158117 - 0x0A15F (Elevator Room Bent 3) - 0x09FFF - True
159035 - 0x037BB (Elevator EP) - 0x01317 - True
Door - 0x01317 (Elevator) - 0x03608
-Desert Lowest Level Inbetween Shortcuts (Desert):
+Desert Behind Elevator (Desert):
-Outside Quarry (Quarry) - Main Island - True - Quarry Between Entrys - 0x09D6F - Quarry Elevator - 0xFFD00 & 0xFFD01:
+==Quarry==
+
+Quarry Obelisk (Quarry) - Entry - True:
+159740 - 0xFFE40 (Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True
+159741 - 0xFFE41 (Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True
+159742 - 0xFFE42 (Obelisk Side 3) - 0x289CF & 0x289D1 - True
+159743 - 0xFFE43 (Obelisk Side 4) - 0x33692 - True
+159744 - 0xFFE44 (Obelisk Side 5) - 0x03E77 & 0x03E7C - True
+159749 - 0x22073 (Obelisk) - True - True
+
+Outside Quarry (Quarry) - Main Island - True - Quarry Between Entry Doors - 0x09D6F - Quarry Elevator - 0xFFD00 & 0xFFD01:
158118 - 0x09E57 (Entry 1 Panel) - True - Black/White Squares
158603 - 0x17CF0 (Discard) - True - Triangles
158702 - 0x03612 (Laser Panel) - 0x0A3D0 & 0x0367C - Eraser & Shapers
@@ -236,7 +263,7 @@ Quarry Elevator (Quarry) - Outside Quarry - 0x17CC4 - Quarry - 0x17CC4:
158120 - 0x17CC4 (Elevator Control) - 0x0367C - Dots & Eraser
159403 - 0x17CB9 (Railroad EP) - 0x17CC4 - True
-Quarry Between Entrys (Quarry) - Quarry - 0x17C07:
+Quarry Between Entry Doors (Quarry) - Quarry - 0x17C07:
158119 - 0x17C09 (Entry 2 Panel) - True - Shapers
Door - 0x17C07 (Entry 2) - 0x17C09
@@ -322,6 +349,8 @@ Door - 0x3865F (Second Barrier) - 0x38663
158169 - 0x0A3D0 (Back Second Row 3) - 0x0A3CC - Stars & Eraser & Shapers
159401 - 0x005F6 (Hook EP) - 0x275FA & 0x03852 & 0x3865F - True
+==Shadows==
+
Shadows (Shadows) - Main Island - True - Shadows Ledge - 0x19B24 - Shadows Laser Room - 0x194B2 | 0x19665:
158170 - 0x334DB (Door Timer Outside) - True - True
Door - 0x19B24 (Timed Door) - 0x334DB | 0x334DC
@@ -361,19 +390,18 @@ Shadows Laser Room (Shadows):
158703 - 0x19650 (Laser Panel) - 0x194B2 & 0x19665 - True
Laser - 0x181B3 (Laser) - 0x19650
-Treehouse Beach (Treehouse Beach) - Main Island - True:
-159200 - 0x0053D (Rock Shadow EP) - True - True
-159201 - 0x0053E (Sand Shadow EP) - True - True
-159212 - 0x220BD (Both Orange Bridges EP) - 0x17DA2 & 0x17DDB - True
+==Keep==
-Keep (Keep) - Main Island - True - Keep 2nd Maze - 0x01954 - Keep 2nd Pressure Plate - 0x01BEC:
+Outside Keep (Keep) - Main Island - True:
+159430 - 0x03E77 (Red Flowers EP) - True - True
+159431 - 0x03E7C (Purple Flowers EP) - True - True
+
+Keep (Keep) - Outside Keep - True - Keep 2nd Maze - 0x01954 - Keep 2nd Pressure Plate - 0x01BEC:
158193 - 0x00139 (Hedge Maze 1) - True - True
158197 - 0x0A3A8 (Reset Pressure Plates 1) - True - True
158198 - 0x033EA (Pressure Plates 1) - 0x0A3A8 - Dots
Door - 0x01954 (Hedge Maze 1 Exit) - 0x00139
Door - 0x01BEC (Pressure Plates 1 Exit) - 0x033EA
-159430 - 0x03E77 (Red Flowers EP) - True - True
-159431 - 0x03E7C (Purple Flowers EP) - True - True
Keep 2nd Maze (Keep) - Keep - 0x018CE - Keep 3rd Maze - 0x019D8:
Door - 0x018CE (Hedge Maze 2 Shortcut) - 0x00139
@@ -408,6 +436,22 @@ Door - 0x01D40 (Pressure Plates 4 Exit) - 0x01D3F
158205 - 0x09E49 (Shadows Shortcut Panel) - True - True
Door - 0x09E3D (Shadows Shortcut) - 0x09E49
+Keep Tower (Keep) - Keep - 0x04F8F:
+158206 - 0x0361B (Tower Shortcut Panel) - True - True
+Door - 0x04F8F (Tower Shortcut) - 0x0361B
+158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - True
+158705 - 0x03317 (Laser Panel Pressure Plates) - 0x033EA & 0x01BE9 & 0x01CD3 & 0x01D3F - Dots & Shapers & Black/White Squares & Rotated Shapers
+Laser - 0x014BB (Laser) - 0x0360E | 0x03317
+159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - True
+159241 - 0x033BF (Pressure Plates 2 EP) - 0x01BE9 & 0x01BEA - True
+159242 - 0x033DD (Pressure Plates 3 EP) - 0x01CD3 & 0x01CD5 - True
+159243 - 0x033E5 (Pressure Plates 4 Left Exit EP) - 0x01D3F - True
+159244 - 0x018B6 (Pressure Plates 4 Right Exit EP) - 0x01D3F - True
+159250 - 0x28AE9 (Path EP) - True - True
+159251 - 0x3348F (Hedges EP) - True - True
+
+==Shipwreck==
+
Shipwreck (Shipwreck) - Keep 3rd Pressure Plate - True - Shipwreck Vault - 0x17BB4:
158654 - 0x00AFB (Vault Panel) - True - Symmetry & Sound Dots & Colored Dots
Door - 0x17BB4 (Vault Door) - 0x00AFB
@@ -423,19 +467,16 @@ Door - 0x17BB4 (Vault Door) - 0x00AFB
Shipwreck Vault (Shipwreck):
158655 - 0x03535 (Vault Box) - True - True
-Keep Tower (Keep) - Keep - 0x04F8F:
-158206 - 0x0361B (Tower Shortcut Panel) - True - True
-Door - 0x04F8F (Tower Shortcut) - 0x0361B
-158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - True
-158705 - 0x03317 (Laser Panel Pressure Plates) - 0x033EA & 0x01BE9 & 0x01CD3 & 0x01D3F - Dots & Shapers & Black/White Squares & Rotated Shapers
-Laser - 0x014BB (Laser) - 0x0360E | 0x03317
-159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - True
-159241 - 0x033BF (Pressure Plates 2 EP) - 0x01BE9 & 0x01BEA - True
-159242 - 0x033DD (Pressure Plates 3 EP) - 0x01CD3 & 0x01CD5 - True
-159243 - 0x033E5 (Pressure Plates 4 Left Exit EP) - 0x01D3F - True
-159244 - 0x018B6 (Pressure Plates 4 Right Exit EP) - 0x01D3F - True
-159250 - 0x28AE9 (Path EP) - True - True
-159251 - 0x3348F (Hedges EP) - True - True
+==Monastery==
+
+Monastery Obelisk (Monastery) - Entry - True:
+159710 - 0xFFE10 (Obelisk Side 1) - 0x03ABC & 0x03ABE & 0x03AC0 & 0x03AC4 - True
+159711 - 0xFFE11 (Obelisk Side 2) - 0x03AC5 - True
+159712 - 0xFFE12 (Obelisk Side 3) - 0x03BE2 & 0x03BE3 & 0x0A409 - True
+159713 - 0xFFE13 (Obelisk Side 4) - 0x006E5 & 0x006E6 & 0x006E7 & 0x034A7 & 0x034AD & 0x034AF & 0x03DAB & 0x03DAC & 0x03DAD - True
+159714 - 0xFFE14 (Obelisk Side 5) - 0x03E01 - True
+159715 - 0xFFE15 (Obelisk Side 6) - 0x289F4 & 0x289F5 - True
+159719 - 0x00263 (Obelisk) - True - True
Outside Monastery (Monastery) - Main Island - True - Inside Monastery - 0x0C128 & 0x0C153 - Monastery Garden - 0x03750:
158207 - 0x03713 (Laser Shortcut Panel) - True - True
@@ -457,6 +498,9 @@ Laser - 0x17C65 (Laser) - 0x17CA4
159137 - 0x03DAC (Facade Left Stairs EP) - True - True
159138 - 0x03DAD (Facade Right Stairs EP) - True - True
159140 - 0x03E01 (Grass Stairs EP) - True - True
+159120 - 0x03BE2 (Garden Left EP) - 0x03750 - True
+159121 - 0x03BE3 (Garden Right EP) - True - True
+159122 - 0x0A409 (Wall EP) - True - True
Inside Monastery (Monastery):
158213 - 0x09D9B (Shutters Control) - True - Dots
@@ -470,11 +514,22 @@ Inside Monastery (Monastery):
Monastery Garden (Monastery):
-Town (Town) - Main Island - True - The Ocean - 0x0A054 - Town Maze Rooftop - 0x28AA2 - Town Church - True - Town Wooden Rooftop - 0x034F5 - RGB House - 0x28A61 - Windmill Interior - 0x1845B - Town Inside Cargo Box - 0x0A0C9:
+==Town==
+
+Town Obelisk (Town) - Entry - True:
+159750 - 0xFFE50 (Obelisk Side 1) - 0x035C7 - True
+159751 - 0xFFE51 (Obelisk Side 2) - 0x01848 & 0x03D06 & 0x33530 & 0x33600 & 0x28A2F & 0x28A37 & 0x334A3 & 0x3352F - True
+159752 - 0xFFE52 (Obelisk Side 3) - 0x33857 & 0x33879 & 0x03C19 - True
+159753 - 0xFFE53 (Obelisk Side 4) - 0x28B30 & 0x035C9 - True
+159754 - 0xFFE54 (Obelisk Side 5) - 0x03335 & 0x03412 & 0x038A6 & 0x038AA & 0x03E3F & 0x03E40 & 0x28B8E - True
+159755 - 0xFFE55 (Obelisk Side 6) - 0x28B91 & 0x03BCE & 0x03BCF & 0x03BD1 & 0x339B6 & 0x33A20 & 0x33A29 & 0x33A2A & 0x33B06 - True
+159759 - 0x0A16C (Obelisk) - True - True
+
+Town (Town) - Main Island - True - The Ocean - 0x0A054 - Town Maze Rooftop - 0x28AA2 - Town Church - True - Town Wooden Rooftop - 0x034F5 - Town RGB House - 0x28A61 - Town Inside Cargo Box - 0x0A0C9 - Outside Windmill - True:
158218 - 0x0A054 (Boat Spawn) - 0x17CA6 | 0x17CDF | 0x09DB8 | 0x17C95 - Boat
158219 - 0x0A0C8 (Cargo Box Entry Panel) - True - Black/White Squares & Shapers
Door - 0x0A0C9 (Cargo Box Entry) - 0x0A0C8
-158707 - 0x09F98 (Desert Laser Redirect) - True - True
+158707 - 0x09F98 (Desert Laser Redirect Control) - True - True
158220 - 0x18590 (Transparent) - True - Symmetry
158221 - 0x28AE3 (Vines) - 0x18590 - True
158222 - 0x28938 (Apple Tree) - 0x28AE3 - True
@@ -491,11 +546,6 @@ Door - 0x28A61 (RGB House Entry) - 0x28998
Door - 0x03BB0 (Church Entry) - 0x28A0D
158228 - 0x28A79 (Maze Panel) - True - True
Door - 0x28AA2 (Maze Stairs) - 0x28A79
-158241 - 0x17F5F (Windmill Entry Panel) - True - Dots
-Door - 0x1845B (Windmill Entry) - 0x17F5F
-159010 - 0x037B6 (Windmill First Blade EP) - 0x17D02 - True
-159011 - 0x037B2 (Windmill Second Blade EP) - 0x17D02 - True
-159012 - 0x000F7 (Windmill Third Blade EP) - 0x17D02 - True
159540 - 0x03335 (Tower Underside Third EP) - True - True
159541 - 0x03412 (Tower Underside Fourth EP) - True - True
159542 - 0x038A6 (Tower Underside First EP) - True - True
@@ -528,20 +578,26 @@ Town Church (Town):
158227 - 0x28A69 (Church Lattice) - 0x03BB0 - True
159553 - 0x03BD1 (Black Line Church EP) - True - True
-RGB House (Town) - RGB Room - 0x2897B:
+Town RGB House (Town RGB House) - Town RGB House Upstairs - 0x2897B:
158242 - 0x034E4 (Sound Room Left) - True - True
158243 - 0x034E3 (Sound Room Right) - True - Sound Dots
-Door - 0x2897B (RGB House Stairs) - 0x034E4 & 0x034E3
+Door - 0x2897B (Stairs) - 0x034E4 & 0x034E3
-RGB Room (Town):
+Town RGB House Upstairs (Town RGB House Upstairs):
158244 - 0x334D8 (RGB Control) - True - Rotated Shapers & Colored Squares
-158245 - 0x03C0C (RGB Room Left) - 0x334D8 - Colored Squares & Black/White Squares
-158246 - 0x03C08 (RGB Room Right) - 0x334D8 - Stars
+158245 - 0x03C0C (Left) - 0x334D8 - Colored Squares & Black/White Squares
+158246 - 0x03C08 (Right) - 0x334D8 - Stars
+
+Town Tower Bottom (Town Tower) - Town - True - Town Tower After First Door - 0x27799:
+Door - 0x27799 (First Door) - 0x28A69
-Town Tower (Town Tower) - Town - True - Town Tower Top - 0x27798 & 0x27799 & 0x2779A & 0x2779C:
+Town Tower After First Door (Town Tower) - Town Tower After Second Door - 0x27798:
Door - 0x27798 (Second Door) - 0x28ACC
+
+Town Tower After Second Door (Town Tower) - Town Tower After Third Door - 0x2779C:
Door - 0x2779C (Third Door) - 0x28AD9
-Door - 0x27799 (First Door) - 0x28A69
+
+Town Tower After Third Door (Town Tower) - Town Tower Top - 0x2779A:
Door - 0x2779A (Fourth Door) - 0x28B39
Town Tower Top (Town):
@@ -550,6 +606,15 @@ Laser - 0x032F9 (Laser) - 0x032F5
159422 - 0x33692 (Brown Bridge EP) - True - True
159551 - 0x03BCE (Black Line Tower EP) - True - True
+==Windmill & Theater==
+
+Outside Windmill (Windmill) - Windmill Interior - 0x1845B:
+159010 - 0x037B6 (First Blade EP) - 0x17D02 - True
+159011 - 0x037B2 (Second Blade EP) - 0x17D02 - True
+159012 - 0x000F7 (Third Blade EP) - 0x17D02 - True
+158241 - 0x17F5F (Entry Panel) - True - Dots
+Door - 0x1845B (Entry) - 0x17F5F
+
Windmill Interior (Windmill) - Theater - 0x17F88:
158247 - 0x17D02 (Turn Control) - True - Dots
158248 - 0x17F89 (Theater Entry Panel) - True - Black/White Squares
@@ -573,6 +638,8 @@ Door - 0x3CCDF (Exit Right) - 0x33AB2
159556 - 0x33A2A (Door EP) - 0x03553 - True
159558 - 0x33B06 (Church EP) - 0x0354E - True
+==Jungle==
+
Jungle (Jungle) - Main Island - True - The Ocean - 0x17CDF:
158251 - 0x17CDF (Shore Boat Spawn) - True - Boat
158609 - 0x17F9B (Discard) - True - Triangles
@@ -604,19 +671,18 @@ Door - 0x3873B (Laser Shortcut) - 0x337FA
159350 - 0x035CB (Bamboo CCW EP) - True - True
159351 - 0x035CF (Bamboo CW EP) - True - True
-Outside Jungle River (River) - Main Island - True - Monastery Garden - 0x0CF2A - River Vault - 0x15287:
+Outside Jungle River (Jungle) - Main Island - True - Monastery Garden - 0x0CF2A - Jungle Vault - 0x15287:
158267 - 0x17CAA (Monastery Garden Shortcut Panel) - True - True
Door - 0x0CF2A (Monastery Garden Shortcut) - 0x17CAA
158663 - 0x15ADD (Vault Panel) - True - Black/White Squares & Dots
Door - 0x15287 (Vault Door) - 0x15ADD
159110 - 0x03AC5 (Green Leaf Moss EP) - True - True
-159120 - 0x03BE2 (Monastery Garden Left EP) - 0x03750 - True
-159121 - 0x03BE3 (Monastery Garden Right EP) - True - True
-159122 - 0x0A409 (Monastery Wall EP) - True - True
-River Vault (River):
+Jungle Vault (Jungle):
158664 - 0x03702 (Vault Box) - True - True
+==Bunker==
+
Outside Bunker (Bunker) - Main Island - True - Bunker - 0x0C2A4:
158268 - 0x17C2E (Entry Panel) - True - Black/White Squares
Door - 0x0C2A4 (Entry) - 0x17C2E
@@ -650,9 +716,11 @@ Door - 0x0A08D (Elevator Room Entry) - 0x17E67
Bunker Elevator Section (Bunker) - Bunker Elevator - TrueOneWay:
159311 - 0x035F5 (Tinted Door EP) - 0x17C79 - True
-Bunker Elevator (Bunker) - Bunker Elevator Section - 0x0A079 - Bunker Green Room - 0x0A079 - Bunker Laser Platform - 0x0A079 - Outside Bunker - 0x0A079:
+Bunker Elevator (Bunker) - Bunker Elevator Section - 0x0A079 - Bunker Cyan Room - 0x0A079 - Bunker Green Room - 0x0A079 - Bunker Laser Platform - 0x0A079 - Outside Bunker - 0x0A079:
158286 - 0x0A079 (Elevator Control) - True - Colored Squares & Black/White Squares
+Bunker Cyan Room (Bunker) - Bunker Elevator - TrueOneWay:
+
Bunker Green Room (Bunker) - Bunker Elevator - TrueOneWay:
159310 - 0x000D3 (Green Room Flowers EP) - True - True
@@ -660,6 +728,8 @@ Bunker Laser Platform (Bunker) - Bunker Elevator - TrueOneWay:
158710 - 0x09DE0 (Laser Panel) - True - True
Laser - 0x0C2B2 (Laser) - 0x09DE0
+==Swamp==
+
Outside Swamp (Swamp) - Swamp Entry Area - 0x00C1C - Main Island - True:
158287 - 0x0056E (Entry Panel) - True - Shapers
Door - 0x00C1C (Entry) - 0x0056E
@@ -774,13 +844,29 @@ Laser - 0x00BF6 (Laser) - 0x03615
158342 - 0x17C02 (Laser Shortcut Right Panel) - 0x17C05 - Shapers & Negative Shapers & Rotated Shapers
Door - 0x2D880 (Laser Shortcut) - 0x17C02
-Treehouse Entry Area (Treehouse) - Treehouse Between Doors - 0x0C309 - The Ocean - 0x17C95:
+==Treehouse==
+
+Treehouse Obelisk (Treehouse) - Entry - True:
+159720 - 0xFFE20 (Obelisk Side 1) - 0x0053D & 0x0053E & 0x00769 - True
+159721 - 0xFFE21 (Obelisk Side 2) - 0x33721 & 0x220A7 & 0x220BD - True
+159722 - 0xFFE22 (Obelisk Side 3) - 0x03B22 & 0x03B23 & 0x03B24 & 0x03B25 & 0x03A79 & 0x28ABD & 0x28ABE - True
+159723 - 0xFFE23 (Obelisk Side 4) - 0x3388F & 0x28B29 & 0x28B2A - True
+159724 - 0xFFE24 (Obelisk Side 5) - 0x018B6 & 0x033BE & 0x033BF & 0x033DD & 0x033E5 - True
+159725 - 0xFFE25 (Obelisk Side 6) - 0x28AE9 & 0x3348F - True
+159729 - 0x00097 (Obelisk) - True - True
+
+Treehouse Beach (Treehouse Beach) - Main Island - True:
+159200 - 0x0053D (Rock Shadow EP) - True - True
+159201 - 0x0053E (Sand Shadow EP) - True - True
+159212 - 0x220BD (Both Orange Bridges EP) - 0x17DA2 & 0x17DDB - True
+
+Treehouse Entry Area (Treehouse) - Treehouse Between Entry Doors - 0x0C309 - The Ocean - 0x17C95:
158343 - 0x17C95 (Boat Spawn) - True - Boat
158344 - 0x0288C (First Door Panel) - True - Stars
Door - 0x0C309 (First Door) - 0x0288C
159210 - 0x33721 (Buoy EP) - 0x17C95 - True
-Treehouse Between Doors (Treehouse) - Treehouse Yellow Bridge - 0x0C310:
+Treehouse Between Entry Doors (Treehouse) - Treehouse Yellow Bridge - 0x0C310:
158345 - 0x02886 (Second Door Panel) - True - Stars
Door - 0x0C310 (Second Door) - 0x02886
@@ -809,7 +895,7 @@ Treehouse First Purple Bridge (Treehouse) - Treehouse Second Purple Bridge - 0x1
158360 - 0x17D2D (First Purple Bridge 4) - 0x17CE4 - Stars & Dots
158361 - 0x17D6C (First Purple Bridge 5) - 0x17D2D - Stars & Dots
-Treehouse Right Orange Bridge (Treehouse) - Treehouse Bridge Platform - 0x17DA2:
+Treehouse Right Orange Bridge (Treehouse) - Treehouse Drawbridge Platform - 0x17DA2:
158391 - 0x17D88 (Right Orange Bridge 1) - True - Stars
158392 - 0x17DB4 (Right Orange Bridge 2) - 0x17D88 - Stars
158393 - 0x17D8C (Right Orange Bridge 3) - 0x17DB4 - Stars
@@ -823,7 +909,7 @@ Treehouse Right Orange Bridge (Treehouse) - Treehouse Bridge Platform - 0x17DA2:
158401 - 0x17DB1 (Right Orange Bridge 11) - 0x17DB7 - Stars
158402 - 0x17DA2 (Right Orange Bridge 12) - 0x17DB1 - Stars
-Treehouse Bridge Platform (Treehouse) - Main Island - 0x0C32D:
+Treehouse Drawbridge Platform (Treehouse) - Main Island - 0x0C32D:
158404 - 0x037FF (Drawbridge Panel) - True - Stars
Door - 0x0C32D (Drawbridge) - 0x037FF
@@ -882,7 +968,19 @@ Treehouse Laser Room (Treehouse):
158403 - 0x17CBC (Laser House Door Timer Inside) - True - True
Laser - 0x028A4 (Laser) - 0x03613
+==Mountain (Outside)==
+
+Mountainside Obelisk (Mountainside) - Entry - True:
+159730 - 0xFFE30 (Obelisk Side 1) - 0x001A3 & 0x335AE - True
+159731 - 0xFFE31 (Obelisk Side 2) - 0x000D3 & 0x035F5 & 0x09D5D & 0x09D5E & 0x09D63 - True
+159732 - 0xFFE32 (Obelisk Side 3) - 0x3370E & 0x035DE & 0x03601 & 0x03603 & 0x03D0D & 0x3369A & 0x336C8 & 0x33505 - True
+159733 - 0xFFE33 (Obelisk Side 4) - 0x03A9E & 0x016B2 & 0x3365F & 0x03731 & 0x036CE & 0x03C07 & 0x03A93 - True
+159734 - 0xFFE34 (Obelisk Side 5) - 0x03AA6 & 0x3397C & 0x0105D & 0x0A304 - True
+159735 - 0xFFE35 (Obelisk Side 6) - 0x035CB & 0x035CF - True
+159739 - 0x00367 (Obelisk) - True - True
+
Mountainside (Mountainside) - Main Island - True - Mountaintop - True - Mountainside Vault - 0x00085:
+159550 - 0x28B91 (Thundercloud EP) - 0x09F98 & 0x012FB - True
158612 - 0x17C42 (Discard) - True - Triangles
158665 - 0x002A6 (Vault Panel) - True - Symmetry & Colored Dots & Black/White Squares
Door - 0x00085 (Vault Door) - 0x002A6
@@ -893,20 +991,22 @@ Door - 0x00085 (Vault Door) - 0x002A6
Mountainside Vault (Mountainside):
158666 - 0x03542 (Vault Box) - True - True
-Mountaintop (Mountaintop) - Mountain Top Layer - 0x17C34:
+Mountaintop (Mountaintop) - Mountain Floor 1 - 0x17C34:
158405 - 0x0042D (River Shape) - True - True
-158406 - 0x09F7F (Box Short) - 7 Lasers - True
+158406 - 0x09F7F (Box Short) - 7 Lasers + Redirect - True
158407 - 0x17C34 (Mountain Entry Panel) - 0x09F7F - Black/White Squares
-158800 - 0xFFF00 (Box Long) - 11 Lasers & 0x17C34 - True
+158800 - 0xFFF00 (Box Long) - 11 Lasers + Redirect & 0x17C34 - True
159300 - 0x001A3 (River Shape EP) - True - True
159320 - 0x3370E (Arch Black EP) - True - True
159324 - 0x336C8 (Arch White Right EP) - True - True
159326 - 0x3369A (Arch White Left EP) - True - True
-Mountain Top Layer (Mountain Floor 1) - Mountain Top Layer Bridge - 0x09E39:
+==Mountain (Inside)==
+
+Mountain Floor 1 (Mountain Floor 1) - Mountain Floor 1 Bridge - 0x09E39:
158408 - 0x09E39 (Light Bridge Controller) - True - Black/White Squares & Rotated Shapers
-Mountain Top Layer Bridge (Mountain Floor 1) - Mountain Top Layer At Door - TrueOneWay:
+Mountain Floor 1 Bridge (Mountain Floor 1) - Mountain Floor 1 At Door - TrueOneWay:
158409 - 0x09E7A (Right Row 1) - True - Black/White Squares & Dots
158410 - 0x09E71 (Right Row 2) - 0x09E7A - Black/White Squares & Dots
158411 - 0x09E72 (Right Row 3) - 0x09E71 - Black/White Squares & Shapers
@@ -925,10 +1025,10 @@ Mountain Top Layer Bridge (Mountain Floor 1) - Mountain Top Layer At Door - True
158424 - 0x09EAD (Trash Pillar 1) - True - Black/White Squares & Shapers
158425 - 0x09EAF (Trash Pillar 2) - 0x09EAD - Black/White Squares & Shapers
-Mountain Top Layer At Door (Mountain Floor 1) - Mountain Floor 2 - 0x09E54:
+Mountain Floor 1 At Door (Mountain Floor 1) - Mountain Floor 2 - 0x09E54:
Door - 0x09E54 (Exit) - 0x09EAF & 0x09F6E & 0x09E6B & 0x09E7B
-Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near - 0x09FFB - Mountain Floor 2 Blue Bridge - 0x09E86 - Mountain Pink Bridge EP - TrueOneWay:
+Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near - 0x09FFB - Mountain Floor 2 Beyond Bridge - 0x09E86 - Mountain Floor 2 At Door - 0x09ED8 & 0x09E86 - Mountain Pink Bridge EP - TrueOneWay:
158426 - 0x09FD3 (Near Row 1) - True - Colored Squares
158427 - 0x09FD4 (Near Row 2) - 0x09FD3 - Colored Squares & Dots
158428 - 0x09FD6 (Near Row 3) - 0x09FD4 - Stars & Colored Squares & Stars + Same Colored Symbol
@@ -936,8 +1036,6 @@ Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near -
158430 - 0x09FD8 (Near Row 5) - 0x09FD7 - Colored Squares
Door - 0x09FFB (Staircase Near) - 0x09FD8
-Mountain Floor 2 Blue Bridge (Mountain Floor 2) - Mountain Floor 2 Beyond Bridge - TrueOneWay - Mountain Floor 2 At Door - 0x09ED8:
-
Mountain Floor 2 At Door (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EDD:
Door - 0x09EDD (Elevator Room Entry) - 0x09ED8 & 0x09E86
@@ -959,10 +1057,10 @@ Mountain Floor 2 Light Bridge Room Far (Mountain Floor 2):
Mountain Floor 2 Elevator Room (Mountain Floor 2) - Mountain Floor 2 Elevator - TrueOneWay:
158613 - 0x17F93 (Elevator Discard) - True - Triangles
-Mountain Floor 2 Elevator (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EEB - Mountain Third Layer - 0x09EEB:
+Mountain Floor 2 Elevator (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EEB - Mountain Floor 3 - 0x09EEB:
158439 - 0x09EEB (Elevator Control Panel) - True - Dots
-Mountain Third Layer (Mountain Bottom Floor) - Mountain Floor 2 Elevator - TrueOneWay - Mountain Bottom Floor - 0x09F89 - Mountain Pink Bridge EP - TrueOneWay:
+Mountain Floor 3 (Mountain Bottom Floor) - Mountain Floor 2 Elevator - TrueOneWay - Mountain Bottom Floor - 0x09F89 - Mountain Pink Bridge EP - TrueOneWay:
158440 - 0x09FC1 (Giant Puzzle Bottom Left) - True - Shapers & Eraser
158441 - 0x09F8E (Giant Puzzle Bottom Right) - True - Rotated Shapers & Eraser
158442 - 0x09F01 (Giant Puzzle Top Right) - True - Shapers & Eraser
@@ -972,13 +1070,32 @@ Mountain Third Layer (Mountain Bottom Floor) - Mountain Floor 2 Elevator - TrueO
159314 - 0x09D5E (Blue Bridge EP) - 0x09E86 & 0x09ED8 - True
Door - 0x09F89 (Exit) - 0x09FDA
-Mountain Bottom Floor (Mountain Bottom Floor) - Mountain Path to Caves - 0x17F33 - Final Room - 0x0C141:
+Mountain Bottom Floor (Mountain Bottom Floor) - Mountain Path to Caves - 0x17F33 - Mountain Bottom Floor Pillars Room - 0x0C141:
158614 - 0x17FA2 (Discard) - 0xFFF00 - Triangles
-158445 - 0x01983 (Final Room Entry Left) - True - Shapers & Stars
-158446 - 0x01987 (Final Room Entry Right) - True - Colored Squares & Dots
-Door - 0x0C141 (Final Room Entry) - 0x01983 & 0x01987
+158445 - 0x01983 (Pillars Room Entry Left) - True - Shapers & Stars
+158446 - 0x01987 (Pillars Room Entry Right) - True - Colored Squares & Dots
+Door - 0x0C141 (Pillars Room Entry) - 0x01983 & 0x01987
Door - 0x17F33 (Rock Open) - 0x17FA2 | 0x334E1
+Mountain Bottom Floor Pillars Room (Mountain Bottom Floor) - Elevator - 0x339BB & 0x33961:
+158522 - 0x0383A (Right Pillar 1) - True - Stars
+158523 - 0x09E56 (Right Pillar 2) - 0x0383A - Stars & Dots
+158524 - 0x09E5A (Right Pillar 3) - 0x09E56 - Dots & Full Dots
+158525 - 0x33961 (Right Pillar 4) - 0x09E5A - Dots & Symmetry
+158526 - 0x0383D (Left Pillar 1) - True - Dots & Full Dots
+158527 - 0x0383F (Left Pillar 2) - 0x0383D - Black/White Squares
+158528 - 0x03859 (Left Pillar 3) - 0x0383F - Shapers
+158529 - 0x339BB (Left Pillar 4) - 0x03859 - Black/White Squares & Stars & Symmetry
+
+Elevator (Mountain Bottom Floor):
+158530 - 0x3D9A6 (Elevator Door Closer Left) - True - True
+158531 - 0x3D9A7 (Elevator Door Close Right) - True - True
+158532 - 0x3C113 (Elevator Entry Left) - 0x3D9A6 | 0x3D9A7 - True
+158533 - 0x3C114 (Elevator Entry Right) - 0x3D9A6 | 0x3D9A7 - True
+158534 - 0x3D9AA (Back Wall Left) - 0x3D9A6 | 0x3D9A7 - True
+158535 - 0x3D9A8 (Back Wall Right) - 0x3D9A6 | 0x3D9A7 - True
+158536 - 0x3D9A9 (Elevator Start) - 0x3D9AA & 7 Lasers | 0x3D9A8 & 7 Lasers - True
+
Mountain Pink Bridge EP (Mountain Floor 2):
159312 - 0x09D63 (Pink Bridge EP) - 0x09E39 - True
@@ -987,7 +1104,9 @@ Mountain Path to Caves (Mountain Bottom Floor) - Caves - 0x2D77D:
Door - 0x2D77D (Caves Entry) - 0x00FF8
158448 - 0x334E1 (Rock Control) - True - True
-Caves (Caves) - Main Island - 0x2D73F | 0x2D859 - Path to Challenge - 0x019A5:
+==Caves==
+
+Caves (Caves) - Main Island - 0x2D73F | 0x2D859 - Caves Path to Challenge - 0x019A5:
158451 - 0x335AB (Elevator Inside Control) - True - Dots & Black/White Squares
158452 - 0x335AC (Elevator Upper Outside Control) - 0x335AB - Black/White Squares
158453 - 0x3369D (Elevator Lower Outside Control) - 0x335AB - Black/White Squares & Dots
@@ -1042,10 +1161,12 @@ Door - 0x2D73F (Mountain Shortcut Door) - 0x021D7
Door - 0x2D859 (Swamp Shortcut Door) - 0x17CF2
159341 - 0x3397C (Skylight EP) - True - True
-Path to Challenge (Caves) - Challenge - 0x0A19A:
+Caves Path to Challenge (Caves) - Challenge - 0x0A19A:
158477 - 0x0A16E (Challenge Entry Panel) - True - Stars & Shapers & Stars + Same Colored Symbol
Door - 0x0A19A (Challenge Entry) - 0x0A16E
+==Challenge==
+
Challenge (Challenge) - Tunnels - 0x0348A - Challenge Vault - 0x04D75:
158499 - 0x0A332 (Start Timer) - 11 Lasers - True
158500 - 0x0088E (Small Basic) - 0x0A332 - True
@@ -1074,7 +1195,9 @@ Door - 0x0348A (Tunnels Entry) - 0x039B4
Challenge Vault (Challenge):
158667 - 0x0356B (Vault Box) - 0x1C31A & 0x1C319 - True
-Tunnels (Tunnels) - Windmill Interior - 0x27739 - Desert Lowest Level Inbetween Shortcuts - 0x27263 - Town - 0x09E87:
+==Tunnels==
+
+Tunnels (Tunnels) - Windmill Interior - 0x27739 - Desert Behind Elevator - 0x27263 - Town - 0x09E87:
158668 - 0x2FAF6 (Vault Box) - True - True
158519 - 0x27732 (Theater Shortcut Panel) - True - True
Door - 0x27739 (Theater Shortcut) - 0x27732
@@ -1084,24 +1207,7 @@ Door - 0x27263 (Desert Shortcut) - 0x2773D
Door - 0x09E87 (Town Shortcut) - 0x09E85
159557 - 0x33A20 (Theater Flowers EP) - 0x03553 & Theater to Tunnels - True
-Final Room (Mountain Final Room) - Elevator - 0x339BB & 0x33961:
-158522 - 0x0383A (Right Pillar 1) - True - Stars
-158523 - 0x09E56 (Right Pillar 2) - 0x0383A - Stars & Dots
-158524 - 0x09E5A (Right Pillar 3) - 0x09E56 - Dots & Full Dots
-158525 - 0x33961 (Right Pillar 4) - 0x09E5A - Dots & Symmetry
-158526 - 0x0383D (Left Pillar 1) - True - Dots & Full Dots
-158527 - 0x0383F (Left Pillar 2) - 0x0383D - Black/White Squares
-158528 - 0x03859 (Left Pillar 3) - 0x0383F - Shapers
-158529 - 0x339BB (Left Pillar 4) - 0x03859 - Black/White Squares & Stars & Symmetry
-
-Elevator (Mountain Final Room):
-158530 - 0x3D9A6 (Elevator Door Closer Left) - True - True
-158531 - 0x3D9A7 (Elevator Door Close Right) - True - True
-158532 - 0x3C113 (Elevator Entry Left) - 0x3D9A6 | 0x3D9A7 - True
-158533 - 0x3C114 (Elevator Entry Right) - 0x3D9A6 | 0x3D9A7 - True
-158534 - 0x3D9AA (Back Wall Left) - 0x3D9A6 | 0x3D9A7 - True
-158535 - 0x3D9A8 (Back Wall Right) - 0x3D9A6 | 0x3D9A7 - True
-158536 - 0x3D9A9 (Elevator Start) - 0x3D9AA & 7 Lasers | 0x3D9A8 & 7 Lasers - True
+==Boat==
The Ocean (Boat) - Main Island - TrueOneWay - Swamp Near Boat - TrueOneWay - Treehouse Entry Area - TrueOneWay - Quarry Boathouse Behind Staircase - TrueOneWay - Inside Glass Factory Behind Back Wall - TrueOneWay:
159042 - 0x22106 (Desert EP) - True - True
@@ -1114,45 +1220,3 @@ The Ocean (Boat) - Main Island - TrueOneWay - Swamp Near Boat - TrueOneWay - Tre
159521 - 0x33879 (Tutorial Reflection EP) - True - True
159522 - 0x03C19 (Tutorial Moss EP) - True - True
159531 - 0x035C9 (Cargo Box EP) - 0x0A0C9 - True
-
-Obelisks (EPs) - Entry - True:
-159700 - 0xFFE00 (Desert Obelisk Side 1) - 0x0332B & 0x03367 & 0x28B8A - True
-159701 - 0xFFE01 (Desert Obelisk Side 2) - 0x037B6 & 0x037B2 & 0x000F7 - True
-159702 - 0xFFE02 (Desert Obelisk Side 3) - 0x3351D - True
-159703 - 0xFFE03 (Desert Obelisk Side 4) - 0x0053C & 0x00771 & 0x335C8 & 0x335C9 & 0x337F8 & 0x037BB & 0x220E4 & 0x220E5 - True
-159704 - 0xFFE04 (Desert Obelisk Side 5) - 0x334B9 & 0x334BC & 0x22106 & 0x0A14C & 0x0A14D - True
-159709 - 0x00359 (Desert Obelisk) - True - True
-159710 - 0xFFE10 (Monastery Obelisk Side 1) - 0x03ABC & 0x03ABE & 0x03AC0 & 0x03AC4 - True
-159711 - 0xFFE11 (Monastery Obelisk Side 2) - 0x03AC5 - True
-159712 - 0xFFE12 (Monastery Obelisk Side 3) - 0x03BE2 & 0x03BE3 & 0x0A409 - True
-159713 - 0xFFE13 (Monastery Obelisk Side 4) - 0x006E5 & 0x006E6 & 0x006E7 & 0x034A7 & 0x034AD & 0x034AF & 0x03DAB & 0x03DAC & 0x03DAD - True
-159714 - 0xFFE14 (Monastery Obelisk Side 5) - 0x03E01 - True
-159715 - 0xFFE15 (Monastery Obelisk Side 6) - 0x289F4 & 0x289F5 - True
-159719 - 0x00263 (Monastery Obelisk) - True - True
-159720 - 0xFFE20 (Treehouse Obelisk Side 1) - 0x0053D & 0x0053E & 0x00769 - True
-159721 - 0xFFE21 (Treehouse Obelisk Side 2) - 0x33721 & 0x220A7 & 0x220BD - True
-159722 - 0xFFE22 (Treehouse Obelisk Side 3) - 0x03B22 & 0x03B23 & 0x03B24 & 0x03B25 & 0x03A79 & 0x28ABD & 0x28ABE - True
-159723 - 0xFFE23 (Treehouse Obelisk Side 4) - 0x3388F & 0x28B29 & 0x28B2A - True
-159724 - 0xFFE24 (Treehouse Obelisk Side 5) - 0x018B6 & 0x033BE & 0x033BF & 0x033DD & 0x033E5 - True
-159725 - 0xFFE25 (Treehouse Obelisk Side 6) - 0x28AE9 & 0x3348F - True
-159729 - 0x00097 (Treehouse Obelisk) - True - True
-159730 - 0xFFE30 (River Obelisk Side 1) - 0x001A3 & 0x335AE - True
-159731 - 0xFFE31 (River Obelisk Side 2) - 0x000D3 & 0x035F5 & 0x09D5D & 0x09D5E & 0x09D63 - True
-159732 - 0xFFE32 (River Obelisk Side 3) - 0x3370E & 0x035DE & 0x03601 & 0x03603 & 0x03D0D & 0x3369A & 0x336C8 & 0x33505 - True
-159733 - 0xFFE33 (River Obelisk Side 4) - 0x03A9E & 0x016B2 & 0x3365F & 0x03731 & 0x036CE & 0x03C07 & 0x03A93 - True
-159734 - 0xFFE34 (River Obelisk Side 5) - 0x03AA6 & 0x3397C & 0x0105D & 0x0A304 - True
-159735 - 0xFFE35 (River Obelisk Side 6) - 0x035CB & 0x035CF - True
-159739 - 0x00367 (River Obelisk) - True - True
-159740 - 0xFFE40 (Quarry Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True
-159741 - 0xFFE41 (Quarry Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True
-159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 - True
-159743 - 0xFFE43 (Quarry Obelisk Side 4) - 0x33692 - True
-159744 - 0xFFE44 (Quarry Obelisk Side 5) - 0x03E77 & 0x03E7C - True
-159749 - 0x22073 (Quarry Obelisk) - True - True
-159750 - 0xFFE50 (Town Obelisk Side 1) - 0x035C7 - True
-159751 - 0xFFE51 (Town Obelisk Side 2) - 0x01848 & 0x03D06 & 0x33530 & 0x33600 & 0x28A2F & 0x28A37 & 0x334A3 & 0x3352F - True
-159752 - 0xFFE52 (Town Obelisk Side 3) - 0x33857 & 0x33879 & 0x03C19 - True
-159753 - 0xFFE53 (Town Obelisk Side 4) - 0x28B30 & 0x035C9 - True
-159754 - 0xFFE54 (Town Obelisk Side 5) - 0x03335 & 0x03412 & 0x038A6 & 0x038AA & 0x03E3F & 0x03E40 & 0x28B8E - True
-159755 - 0xFFE55 (Town Obelisk Side 6) - 0x28B91 & 0x03BCE & 0x03BCF & 0x03BD1 & 0x339B6 & 0x33A20 & 0x33A29 & 0x33A2A & 0x33B06 - True
-159759 - 0x0A16C (Town Obelisk) - True - True
diff --git a/worlds/witness/__init__.py b/worlds/witness/__init__.py
index 6360c33aefb9..88de0f3134f2 100644
--- a/worlds/witness/__init__.py
+++ b/worlds/witness/__init__.py
@@ -2,21 +2,23 @@
Archipelago init file for The Witness
"""
import dataclasses
-from typing import Dict, Optional
+from typing import Dict, Optional, cast
from BaseClasses import Region, Location, MultiWorld, Item, Entrance, Tutorial, CollectionState
from Options import PerGameCommonOptions, Toggle
-from .hints import get_always_hint_locations, get_always_hint_items, get_priority_hint_locations, \
- get_priority_hint_items, make_hints, generate_joke_hints
+from .presets import witness_option_presets
from worlds.AutoWorld import World, WebWorld
from .player_logic import WitnessPlayerLogic
-from .static_logic import StaticWitnessLogic
+from .static_logic import StaticWitnessLogic, ItemCategory, DoorItemDefinition
+from .hints import get_always_hint_locations, get_always_hint_items, get_priority_hint_locations, \
+ get_priority_hint_items, make_always_and_priority_hints, generate_joke_hints, make_area_hints, get_hintable_areas, \
+ make_extra_location_hints, create_all_hints, make_laser_hints, make_compact_hint_data, CompactItemData
from .locations import WitnessPlayerLocations, StaticWitnessLocations
from .items import WitnessItem, StaticWitnessItems, WitnessPlayerItems, ItemData
from .regions import WitnessRegions
from .rules import set_rules
-from .Options import TheWitnessOptions
-from .utils import get_audio_logs
+from .options import TheWitnessOptions
+from .utils import get_audio_logs, get_laser_shuffle
from logging import warning, error
@@ -31,6 +33,8 @@ class WitnessWebWorld(WebWorld):
["NewSoupVi", "Jarno"]
)]
+ options_presets = witness_option_presets
+
class WitnessWorld(World):
"""
@@ -40,11 +44,6 @@ class WitnessWorld(World):
"""
game = "The Witness"
topology_present = False
- data_version = 14
-
- StaticWitnessLogic()
- StaticWitnessLocations()
- StaticWitnessItems()
web = WitnessWebWorld()
options_dataclass = TheWitnessOptions
@@ -55,8 +54,9 @@ class WitnessWorld(World):
}
location_name_to_id = StaticWitnessLocations.ALL_LOCATIONS_TO_ID
item_name_groups = StaticWitnessItems.item_groups
+ location_name_groups = StaticWitnessLocations.AREA_LOCATION_GROUPS
- required_client_version = (0, 4, 4)
+ required_client_version = (0, 4, 5)
def __init__(self, multiworld: "MultiWorld", player: int):
super().__init__(multiworld, player)
@@ -66,7 +66,8 @@ def __init__(self, multiworld: "MultiWorld", player: int):
self.items = None
self.regio = None
- self.log_ids_to_hints = None
+ self.log_ids_to_hints: Dict[int, CompactItemData] = dict()
+ self.laser_ids_to_hints: Dict[int, CompactItemData] = dict()
self.items_placed_early = []
self.own_itempool = []
@@ -81,17 +82,58 @@ def _get_slot_data(self):
'symbols_not_in_the_game': self.items.get_symbol_ids_not_in_pool(),
'disabled_entities': [int(h, 16) for h in self.player_logic.COMPLETELY_DISABLED_ENTITIES],
'log_ids_to_hints': self.log_ids_to_hints,
+ 'laser_ids_to_hints': self.laser_ids_to_hints,
'progressive_item_lists': self.items.get_progressive_item_ids_in_pool(),
'obelisk_side_id_to_EPs': StaticWitnessLogic.OBELISK_SIDE_ID_TO_EP_HEXES,
'precompleted_puzzles': [int(h, 16) for h in self.player_logic.EXCLUDED_LOCATIONS],
'entity_to_name': StaticWitnessLogic.ENTITY_ID_TO_NAME,
}
+ def determine_sufficient_progression(self):
+ """
+ Determine whether there are enough progression items in this world to consider it "interactive".
+ In the case of singleplayer, this just outputs a warning.
+ In the case of multiplayer, the requirements are a bit stricter and an Exception is raised.
+ """
+
+ # A note on Obelisk Keys:
+ # Obelisk Keys are never relevant in singleplayer, because the locations they lock are irrelevant to in-game
+ # progress and irrelevant to all victory conditions. Thus, I consider them "fake progression" for singleplayer.
+ # However, those locations could obviously contain big items needed for other players, so I consider
+ # "Obelisk Keys only" valid for multiworld.
+
+ # A note on Laser Shuffle:
+ # In singleplayer, I don't mind "Ice Rod Hunt" type gameplay, so "laser shuffle only" is valid.
+ # However, I do not want to allow "Ice Rod Hunt" style gameplay in multiworld, so "laser shuffle only" is
+ # not considered interactive enough for multiworld.
+
+ interacts_sufficiently_with_multiworld = (
+ self.options.shuffle_symbols
+ or self.options.shuffle_doors
+ or self.options.obelisk_keys and self.options.shuffle_EPs
+ )
+
+ has_locally_relevant_progression = (
+ self.options.shuffle_symbols
+ or self.options.shuffle_doors
+ or self.options.shuffle_lasers
+ or self.options.shuffle_boat
+ or self.options.early_caves == "add_to_pool" and self.options.victory_condition == "challenge"
+ )
+
+ if not has_locally_relevant_progression and self.multiworld.players == 1:
+ warning(f"{self.multiworld.get_player_name(self.player)}'s Witness world doesn't have any progression"
+ f" items. Please turn on Symbol Shuffle, Door Shuffle or Laser Shuffle if that doesn't seem right.")
+ elif not interacts_sufficiently_with_multiworld and self.multiworld.players > 1:
+ raise Exception(f"{self.multiworld.get_player_name(self.player)}'s Witness world doesn't have enough"
+ f" progression items that can be placed in other players' worlds. Please turn on Symbol"
+ f" Shuffle, Door Shuffle or Obelisk Keys.")
+
def generate_early(self):
- disabled_locations = self.multiworld.exclude_locations[self.player].value
+ disabled_locations = self.options.exclude_locations.value
self.player_logic = WitnessPlayerLogic(
- self, disabled_locations, self.multiworld.start_inventory[self.player].value
+ self, disabled_locations, self.options.start_inventory.value
)
self.locat: WitnessPlayerLocations = WitnessPlayerLocations(self, self.player_logic)
@@ -102,14 +144,10 @@ def generate_early(self):
self.log_ids_to_hints = dict()
- if not (self.options.shuffle_symbols or self.options.shuffle_doors or self.options.shuffle_lasers):
- if self.multiworld.players == 1:
- warning(f"{self.multiworld.get_player_name(self.player)}'s Witness world doesn't have any progression"
- f" items. Please turn on Symbol Shuffle, Door Shuffle or Laser Shuffle if that doesn't"
- f" seem right.")
- else:
- raise Exception(f"{self.multiworld.get_player_name(self.player)}'s Witness world doesn't have any"
- f" progression items. Please turn on Symbol Shuffle, Door Shuffle or Laser Shuffle.")
+ self.determine_sufficient_progression()
+
+ if self.options.shuffle_lasers == "local":
+ self.options.local_items.value |= self.item_name_groups["Lasers"]
def create_regions(self):
self.regio.create_regions(self, self.player_logic)
@@ -144,7 +182,7 @@ def create_regions(self):
early_items = [item for item in self.items.get_early_items() if item in self.items.get_mandatory_items()]
if early_items:
random_early_item = self.random.choice(early_items)
- if self.options.puzzle_randomization == 1:
+ if self.options.puzzle_randomization == "sigma_expert":
# In Expert, only tag the item as early, rather than forcing it onto the gate.
self.multiworld.local_early_items[self.player][random_early_item] = 1
else:
@@ -167,16 +205,17 @@ def create_regions(self):
# Adjust the needed size for sphere 1 based on how restrictive the settings are in terms of items
needed_size = 3
- needed_size += self.options.puzzle_randomization == 1
+ needed_size += self.options.puzzle_randomization == "sigma_expert"
needed_size += self.options.shuffle_symbols
needed_size += self.options.shuffle_doors > 0
# Then, add checks in order until the required amount of sphere 1 checks is met.
extra_checks = [
- ("First Hallway Room", "First Hallway Bend"),
- ("First Hallway", "First Hallway Straight"),
- ("Desert Outside", "Desert Surface 3"),
+ ("Tutorial First Hallway Room", "Tutorial First Hallway Bend"),
+ ("Tutorial First Hallway", "Tutorial First Hallway Straight"),
+ ("Desert Outside", "Desert Surface 1"),
+ ("Desert Outside", "Desert Surface 2"),
]
for i in range(num_early_locs, needed_size):
@@ -253,32 +292,48 @@ def create_items(self):
self.own_itempool += new_items
self.multiworld.itempool += new_items
if self.items.item_data[item_name].local_only:
- self.multiworld.local_items[self.player].value.add(item_name)
+ self.options.local_items.value.add(item_name)
def fill_slot_data(self) -> dict:
+ already_hinted_locations = set()
+
+ # Laser hints
+
+ if self.options.laser_hints:
+ laser_hints = make_laser_hints(self, StaticWitnessItems.item_groups["Lasers"])
+
+ for item_name, hint in laser_hints.items():
+ item_def = cast(DoorItemDefinition, StaticWitnessLogic.all_items[item_name])
+ self.laser_ids_to_hints[int(item_def.panel_id_hexes[0], 16)] = make_compact_hint_data(hint, self.player)
+ already_hinted_locations.add(hint.location)
+
+ # Audio Log Hints
+
hint_amount = self.options.hint_amount.value
credits_hint = (
- "This Randomizer is brought to you by",
- "NewSoupVi, Jarno, blastron,",
- "jbzdarkid, sigma144, IHNN, oddGarrett, Exempt-Medic.", -1
+ "This Randomizer is brought to you by\n"
+ "NewSoupVi, Jarno, blastron,\n"
+ "jbzdarkid, sigma144, IHNN, oddGarrett, Exempt-Medic.", -1, -1
)
audio_logs = get_audio_logs().copy()
- if hint_amount != 0:
- generated_hints = make_hints(self, hint_amount, self.own_itempool)
+ if hint_amount:
+ area_hints = round(self.options.area_hint_percentage / 100 * hint_amount)
+
+ generated_hints = create_all_hints(self, hint_amount, area_hints, already_hinted_locations)
self.random.shuffle(audio_logs)
duplicates = min(3, len(audio_logs) // hint_amount)
- for _ in range(0, hint_amount):
- hint = generated_hints.pop(0)
+ for hint in generated_hints:
+ compact_hint_data = make_compact_hint_data(hint, self.player)
for _ in range(0, duplicates):
audio_log = audio_logs.pop()
- self.log_ids_to_hints[int(audio_log, 16)] = hint
+ self.log_ids_to_hints[int(audio_log, 16)] = compact_hint_data
if audio_logs:
audio_log = audio_logs.pop()
@@ -290,7 +345,7 @@ def fill_slot_data(self) -> dict:
audio_log = audio_logs.pop()
self.log_ids_to_hints[int(audio_log, 16)] = joke_hints.pop()
- # generate hints done
+ # Options for the client & auto-tracker
slot_data = self._get_slot_data()
diff --git a/worlds/witness/docs/en_The Witness.md b/worlds/witness/docs/en_The Witness.md
index 4d00ecaae451..6882ed3fdedf 100644
--- a/worlds/witness/docs/en_The Witness.md
+++ b/worlds/witness/docs/en_The Witness.md
@@ -1,8 +1,8 @@
# The Witness
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
@@ -16,7 +16,7 @@ Panels with puzzle symbols on them are now locked initially.
## What is a "check" in The Witness?
Solving the last panel in a row of panels or an important standalone panel will count as a check, and send out an item.
-It is also possible to add Environmental Puzzles into the location pool via the "Shuffle Environmental Puzzles" setting.
+It is also possible to add Environmental Puzzles into the location pool via the "Shuffle Environmental Puzzles" option.
## What "items" can you unlock in The Witness?
@@ -25,7 +25,7 @@ This includes symbols such as "Dots", "Black/White Squares", "Colored Squares",
Alternatively (or additionally), you can play "Door shuffle", where some doors won't open until you receive their "key".
-Receiving lasers as items is also a possible setting.
+You can also set lasers to be items you can receive.
## What else can I find in the world?
diff --git a/worlds/witness/hints.py b/worlds/witness/hints.py
index d238aa4adfb6..6ebf8eeec00d 100644
--- a/worlds/witness/hints.py
+++ b/worlds/witness/hints.py
@@ -1,81 +1,91 @@
-from typing import Tuple, List, TYPE_CHECKING
-
-from BaseClasses import Item
+import logging
+from dataclasses import dataclass
+from typing import Tuple, List, TYPE_CHECKING, Set, Dict, Optional, Union
+from BaseClasses import Item, ItemClassification, Location, LocationProgressType, CollectionState
+from . import StaticWitnessLogic
+from .utils import weighted_sample
if TYPE_CHECKING:
from . import WitnessWorld
+CompactItemData = Tuple[str, Union[str, int], int]
+
joke_hints = [
- "Quaternions break my brain",
- "Eclipse has nothing, but you should do it anyway.",
- "Beep",
- "Putting in custom subtitles shouldn't have been as hard as it was...",
- "BK mode is right around the corner.",
- "You can do it!",
- "I believe in you!",
- "The person playing is cute. <3",
- "dash dot, dash dash dash,\ndash, dot dot dot dot, dot dot,\ndash dot, dash dash dot",
- "When you think about it, there are actually a lot of bubbles in a stream.",
- "Never gonna give you up\nNever gonna let you down\nNever gonna run around and desert you",
- "Thanks to the Archipelago developers for making this possible.",
+ "Have you tried Adventure?\n...Holy crud, that game is 17 years older than me.",
+ "Have you tried A Link to the Past?\nThe Archipelago game that started it all!",
+ "Waiting to get your items?\nTry BK Sudoku! Make progress even while stuck.",
+ "Have you tried Blasphemous?\nYou haven't? Blasphemy!\n...Sorry. You should try it, though!",
+ "Have you tried Bumper Stickers?\nDecades after its inception, people are still inventing unique twists on the match-3 genre.",
+ "Have you tried Bumper Stickers?\nMaybe after spending so much time on this island, you are longing for a simpler puzzle game.",
+ "Have you tried Celeste 64?\nYou need smol low-poly Madeline in your life. TRUST ME.",
"Have you tried ChecksFinder?\nIf you like puzzles, you might enjoy it!",
+ "Have you tried Clique?\nIt's certainly a lot less complicated than this game!",
"Have you tried Dark Souls III?\nA tough game like this feels better when friends are helping you!",
"Have you tried Donkey Kong Country 3?\nA legendary game from a golden age of platformers!",
+ "Have you tried DLC Quest?\nI know you all like parody games.\nI got way too many requests to make a randomizer for \"The Looker\".",
+ "Have you tried Doom?\nI wonder if a smart fridge can connect to Archipelago.",
+ "Have you tried Doom II?\nGot a good game on your hands? Just make it bigger and better.",
"Have you tried Factorio?\nAlone in an unknown multiworld. Sound familiar?",
"Have you tried Final Fantasy?\nExperience a classic game improved to fit modern standards!",
+ "Have you tried Final Fantasy Mystic Quest?\nApparently, it was made in an attempt to simplify Final Fantasy for the western market.\nThey were right, I suck at RPGs.",
+ "Have you tried Heretic?\nWait, there is a Doom Engine game where you can look UP AND DOWN???",
"Have you tried Hollow Knight?\nAnother independent hit revolutionising a genre!",
- "Have you tried A Link to the Past?\nThe Archipelago game that started it all!",
+ "Have you tried Hylics 2?\nStop motion might just be the epitome of unique art styles.",
+ "Have you tried Kirby's Dream Land 3?\nAll good things must come to an end, including Nintendo's SNES library.\nWent out with a bang though!",
+ "Have you tried Kingdom Hearts II?\nI'll wait for you to name a more epic crossover.",
+ "Have you tried Link's Awakening DX?\nHopefully, Link won't be obsessed with circles when he wakes up.",
+ "Have you tried Landstalker?\nThe Witness player's greatest fear: A diagonal movement grid...\nWait, I guess we have the Monastery puzzles.",
+ "Have you tried Lingo?\nIt's an open world puzzle game. It features puzzle panels with non-verbally explained mechanics.\nIf you like this game, you'll like Lingo too.",
+ "(Middle Yellow)\nYOU AILED OVERNIGHT\nH--- --- ----- -----?",
+ "Have you tried Lufia II?\nRoguelites are not just a 2010s phenomenon, turns out.",
"Have you tried Meritous?\nYou should know that obscure games are often groundbreaking!",
+ "Have you tried The Messenger?\nOld ideas made new again. It's how all art is made.",
+ "Have you tried Minecraft?\nI have recently learned this is a question that needs to be asked.",
+ "Have you tried Mega Man Battle Network 3?\nIt's a Mega Man RPG. How could you not want to try that?",
+ "Have you tried Muse Dash?\nRhythm game with cute girls!\n(Maybe skip if you don't like the Jungle panels)",
+ "Have you tried Noita?\nIf you like punishing yourself, you will like it.",
"Have you tried Ocarina of Time?\nOne of the biggest randomizers, big inspiration for this one's features!",
+ "Have you tried Overcooked 2?\nWhen you're done relaxing with puzzles, use your energy to yell at your friends.",
+ "Have you tried Pokemon Emerald?\nI'm going to say it: 10/10, just the right amount of water.",
+ "Have you tried Pokemon Red&Blue?\nA cute pet collecting game that fascinated an entire generation.",
"Have you tried Raft?\nHaven't you always wanted to explore the ocean surrounding this island?",
- "Have you tried Risk of Rain 2?\nI haven't either. But I hear it's incredible!",
"Have you tried Rogue Legacy?\nAfter solving so many puzzles it's the perfect way to rest your \"thinking\" brain.",
- "Have you tried Secret of Evermore?\nI haven't either. But I hear it's great!",
- "Have you tried Slay the Spire?\nExperience the thrill of combat without needing fast fingers!",
- "Have you tried SMZ3?\nWhy play one incredible game when you can play 2 at once?",
+ "Have you tried Risk of Rain 2?\nI haven't either. But I hear it's incredible!",
+ "Have you tried Sonic Adventure 2?\nIf the silence on this island is getting to you, there aren't many games more energetic.",
"Have you tried Starcraft 2?\nUse strategy and management to crush your enemies!",
- "Have you tried Super Mario 64?\n3-dimensional games like this owe everything to that game.",
+ "Have you tried Shivers?\nWitness 2 should totally feature a haunted museum.",
"Have you tried Super Metroid?\nA classic game, yet still one of the best in the genre.",
- "Have you tried Timespinner?\nEveryone who plays it ends up loving it!",
- "Have you tried VVVVVV?\nExperience the essence of gaming distilled into its purest form!",
- "Have you tried The Witness?\nOh. I guess you already have. Thanks for playing!",
+ "Have you tried Super Mario 64?\n3-dimensional games like this owe everything to that game.",
"Have you tried Super Mario World?\nI don't think I need to tell you that it is beloved by many.",
- "Have you tried Overcooked 2?\nWhen you're done relaxing with puzzles, use your energy to yell at your friends.",
- "Have you tried Zillion?\nMe neither. But it looks fun. So, let's try something new together?",
- "Have you tried Hylics 2?\nStop motion might just be the epitome of unique art styles.",
- "Have you tried Pokemon Red&Blue?\nA cute pet collecting game that fascinated an entire generation.",
- "Have you tried Lufia II?\nRoguelites are not just a 2010s phenomenon, turns out.",
- "Have you tried Minecraft?\nI have recently learned this is a question that needs to be asked.",
- "Have you tried Subnautica?\nIf you like this game's lonely atmosphere, I would suggest you try it.",
-
- "Have you tried Sonic Adventure 2?\nIf the silence on this island is getting to you, "
- "there aren't many games more energetic.",
-
- "Waiting to get your items?\nTry BK Sudoku! Make progress even while stuck.",
-
- "Have you tried Adventure?\n...Holy crud, that game is 17 years older than me.",
- "Have you tried Muse Dash?\nRhythm game with cute girls!\n(Maybe skip if you don't like the Jungle panels)",
- "Have you tried Clique?\nIt's certainly a lot less complicated than this game!",
- "Have you tried Bumper Stickers?\nDecades after its inception, people are still inventing unique twists on the match-3 genre.",
- "Have you tried DLC Quest?\nI know you all like parody games.\nI got way too many requests to make a randomizer for \"The Looker\".",
- "Have you tried Doom?\nI wonder if a smart fridge can connect to Archipelago.",
- "Have you tried Kingdom Hearts II?\nI'll wait for you to name a more epic crossover.",
- "Have you tried Link's Awakening DX?\nHopefully, Link won't be obsessed with circles when he wakes up.",
- "Have you tried The Messenger?\nOld ideas made new again. It's how all art is made.",
- "Have you tried Mega Man Battle Network 3?\nIt's a Mega Man RPG. How could you not want to try that?",
- "Have you tried Noita?\nIf you like punishing yourself, you will like it.",
+ "Have you tried SMZ3?\nWhy play one incredible game when you can play 2 at once?",
+ "Have you tried Secret of Evermore?\nI haven't either. But I hear it's great!",
+ "Have you tried Slay the Spire?\nExperience the thrill of combat without needing fast fingers!",
"Have you tried Stardew Valley?\nThe Farming game that gave a damn. It's so easy to lose hours and days to it...",
+ "Have you tried Subnautica?\nIf you like this game's lonely atmosphere, I would suggest you try it.",
+ "Have you tried Terraria?\nA prime example of a survival sandbox game that beats the \"Wide as an ocean, deep as a puddle\" allegations.",
+ "Have you tried Timespinner?\nEveryone who plays it ends up loving it!",
"Have you tried The Legend of Zelda?\nIn some sense, it was the starting point of \"adventure\" in video games.",
+ "Have you tried TUNC?\nWhat? No, I'm pretty sure I spelled that right.",
+ "Have you tried TUNIC?\nRemember what discovering your first Environmental Puzzle was like?\nTUNIC will make you feel like that at least 5 times over.",
"Have you tried Undertale?\nI hope I'm not the 10th person to ask you that. But it's, like, really good.",
+ "Have you tried VVVVVV?\nExperience the essence of gaming distilled into its purest form!",
"Have you tried Wargroove?\nI'm glad that for every abandoned series, enough people are yearning for its return that one of them will know how to code.",
- "Have you tried Blasphemous?\nYou haven't? Blasphemy!\n...Sorry. You should try it, though!",
- "Have you tried Doom II?\nGot a good game on your hands? Just make it bigger and better.",
- "Have you tried Lingo?\nIt's an open world puzzle game. It features panels with non-verbally explained mechanics.\nIf you like this game, you'll like Lingo too.",
- "(Middle Yellow)\nYOU AILED OVERNIGHT\nH--- --- ----- -----?",
- "Have you tried Bumper Stickers?\nMaybe after spending so much time on this island, you are longing for a simpler puzzle game.",
- "Have you tried Pokemon Emerald?\nI'm going to say it: 10/10, just the right amount of water.",
- "Have you tried Terraria?\nA prime example of a survival sandbox game that beats the \"Wide as an ocean, deep as a puddle\" allegations.",
-
+ "Have you tried The Witness?\nOh. I guess you already have. Thanks for playing!",
+ "Have you tried Zillion?\nMe neither. But it looks fun. So, let's try something new together?",
+ "Have you tried Zork: Grand Inquisitor?\nThis 1997 game uses Z-Vision technology to simulate 3D environments.\nCome on, I know you wanna find out what \"Z-Vision\" is.",
+
+ "Quaternions break my brain",
+ "Eclipse has nothing, but you should do it anyway.",
+ "Beep",
+ "Putting in custom subtitles shouldn't have been as hard as it was...",
+ "BK mode is right around the corner.",
+ "You can do it!",
+ "I believe in you!",
+ "The person playing is cute. <3",
+ "dash dot, dash dash dash,\ndash, dot dot dot dot, dot dot,\ndash dot, dash dash dot",
+ "When you think about it, there are actually a lot of bubbles in a stream.",
+ "Never gonna give you up\nNever gonna let you down\nNever gonna run around and desert you",
+ "Thanks to the Archipelago developers for making this possible.",
"One day I was fascinated by the subject of generation of waves by wind.",
"I don't like sandwiches. Why would you think I like sandwiches? Have you ever seen me with a sandwich?",
"Where are you right now?\nI'm at soup!\nWhat do you mean you're at soup?",
@@ -148,19 +158,55 @@
"You don't have Boat? Invisible boat time!\nYou do have boat? Boat clipping time!",
"Cet indice est en français. Nous nous excusons de tout inconvénients engendrés par cela.",
"How many of you have personally witnessed a total solar eclipse?",
- "In the Treehouse area, you will find \n[Error: Data not found] progression items.",
+ "In the Treehouse area, you will find 69 progression items.\nNice.\n(Source: Just trust me)",
"Lingo\nLingoing\nLingone",
"The name of the captain was Albert Einstein.",
"Panel impossible Sigma plz fix",
"Welcome Back! (:",
"R R R U L L U L U R U R D R D R U U",
"Have you tried checking your tracker?",
-
- "Hints suggested by:\nIHNN, Beaker, MrPokemon11, Ember, TheM8, NewSoupVi, Jasper Bird, T1mshady,"
+ "Lines are drawn on grids\nAll symbols must be obeyed\nIt's snowing on Mt. Fuji",
+ "If you're BK, you could try today's Wittle:\nhttps://www.fourisland.com/wittle/",
+ "They say that plundering Outside Ganon's Castle is a foolish choice.",
+ "You should try to BLJ. Maybe that'll get you through that door.",
+ "Error: Witness Randomizer disconnected from Archipelago.\n(lmao gottem)",
+ "You have found: One (1) Audio Log!\nSeries of 49! Collect them all!",
+ "In the Town area, you will find 1 good boi.\nGo pet him.",
+ "If you're ever stuck on a panel, feel free to ask Rever.\nSurely you'll understand his drawing!",
+ "[This hint has been removed as part of the Witness Protection Program]",
+ "Panel Diddle",
+ "Witness AP when",
+ "This game is my favorite walking simulator.",
+ "Did you hear that? It said --\n\nCosmic background radiation is a riot!",
+ "Well done solving those puzzles.\nPray return to the Waking Sands.",
+ "Having trouble finding your checks?\nTry the PopTracker pack!\nIt's got auto-tracking and a detailed map.",
+
+ "Hints suggested by:\nIHNN, Beaker, MrPokemon11, Ember, TheM8, NewSoupVi, Jasper Bird, T1mshady, "
"KF, Yoshi348, Berserker, BowlinJim, oddGarrett, Pink Switch, Rever, Ishigh, snolid.",
]
+@dataclass
+class WitnessLocationHint:
+ location: Location
+ hint_came_from_location: bool
+
+ # If a hint gets added to a set twice, but once as an item hint and once as a location hint, those are the same
+ def __hash__(self):
+ return hash(self.location)
+
+ def __eq__(self, other):
+ return self.location == other.location
+
+
+@dataclass
+class WitnessWordedHint:
+ wording: str
+ location: Optional[Location] = None
+ area: Optional[str] = None
+ area_amount: Optional[int] = None
+
+
def get_always_hint_items(world: "WitnessWorld") -> List[str]:
always = [
"Boat",
@@ -173,22 +219,22 @@ def get_always_hint_items(world: "WitnessWorld") -> List[str]:
wincon = world.options.victory_condition
if discards:
- if difficulty == 1:
+ if difficulty == "sigma_expert":
always.append("Arrows")
else:
always.append("Triangles")
- if wincon == 0:
- always += ["Mountain Bottom Floor Final Room Entry (Door)", "Mountain Bottom Floor Doors"]
+ if wincon == "elevator":
+ always += ["Mountain Bottom Floor Pillars Room Entry (Door)", "Mountain Bottom Floor Doors"]
- if wincon == 1:
+ if wincon == "challenge":
always += ["Challenge Entry (Panel)", "Caves Panels"]
return always
-def get_always_hint_locations(_: "WitnessWorld") -> List[str]:
- return [
+def get_always_hint_locations(world: "WitnessWorld") -> List[str]:
+ always = [
"Challenge Vault Box",
"Mountain Bottom Floor Discard",
"Theater Eclipse EP",
@@ -196,6 +242,18 @@ def get_always_hint_locations(_: "WitnessWorld") -> List[str]:
"Mountainside Cloud Cycle EP",
]
+ # Add Obelisk Sides that contain EPs that are meant to be hinted, if they are necessary to complete the Obelisk Side
+ if "0x339B6" not in world.player_logic.COMPLETELY_DISABLED_ENTITIES:
+ always.append("Town Obelisk Side 6") # Eclipse EP
+
+ if "0x3388F" not in world.player_logic.COMPLETELY_DISABLED_ENTITIES:
+ always.append("Treehouse Obelisk Side 4") # Couch EP
+
+ if "0x335AE" not in world.player_logic.COMPLETELY_DISABLED_ENTITIES:
+ always.append("Mountainside Obelisk Side 1") # Cloud Cycle EP.
+
+ return always
+
def get_priority_hint_items(world: "WitnessWorld") -> List[str]:
priority = {
@@ -217,9 +275,8 @@ def get_priority_hint_items(world: "WitnessWorld") -> List[str]:
"Eraser",
"Black/White Squares",
"Colored Squares",
- "Colored Dots",
"Sound Dots",
- "Symmetry"
+ "Progressive Symmetry"
]
priority.update(world.random.sample(symbols, 5))
@@ -249,12 +306,14 @@ def get_priority_hint_items(world: "WitnessWorld") -> List[str]:
return sorted(priority)
-def get_priority_hint_locations(_: "WitnessWorld") -> List[str]:
- return [
+def get_priority_hint_locations(world: "WitnessWorld") -> List[str]:
+ priority = [
+ "Tutorial Patio Floor",
+ "Tutorial Patio Flowers EP",
"Swamp Purple Underwater",
"Shipwreck Vault Box",
- "Town RGB Room Left",
- "Town RGB Room Right",
+ "Town RGB House Upstairs Left",
+ "Town RGB House Upstairs Right",
"Treehouse Green Bridge 7",
"Treehouse Green Bridge Discard",
"Shipwreck Discard",
@@ -265,9 +324,40 @@ def get_priority_hint_locations(_: "WitnessWorld") -> List[str]:
"Boat Shipwreck Green EP",
"Quarry Stoneworks Control Room Left",
]
+
+ # Add Obelisk Sides that contain EPs that are meant to be hinted, if they are necessary to complete the Obelisk Side
+ if "0x33A20" not in world.player_logic.COMPLETELY_DISABLED_ENTITIES:
+ priority.append("Town Obelisk Side 6") # Theater Flowers EP
+
+ if "0x28B29" not in world.player_logic.COMPLETELY_DISABLED_ENTITIES:
+ priority.append("Treehouse Obelisk Side 4") # Shipwreck Green EP
+
+ if "0x33600" not in world.player_logic.COMPLETELY_DISABLED_ENTITIES:
+ priority.append("Town Obelisk Side 2") # Tutorial Patio Flowers EP.
+
+ return priority
+
+def word_direct_hint(world: "WitnessWorld", hint: WitnessLocationHint):
+ location_name = hint.location.name
+ if hint.location.player != world.player:
+ location_name += " (" + world.multiworld.get_player_name(hint.location.player) + ")"
+
+ item = hint.location.item
+ item_name = item.name
+ if item.player != world.player:
+ item_name += " (" + world.multiworld.get_player_name(item.player) + ")"
+
+ if hint.hint_came_from_location:
+ hint_text = f"{location_name} contains {item_name}."
+ else:
+ hint_text = f"{item_name} can be found at {location_name}."
+
+ return WitnessWordedHint(hint_text, hint.location)
+
+
+def hint_from_item(world: "WitnessWorld", item_name: str, own_itempool: List[Item]) -> Optional[WitnessLocationHint]:
-def make_hint_from_item(world: "WitnessWorld", item_name: str, own_itempool: List[Item]):
locations = [item.location for item in own_itempool if item.name == item_name and item.location]
if not locations:
@@ -279,28 +369,39 @@ def make_hint_from_item(world: "WitnessWorld", item_name: str, own_itempool: Lis
if location_obj.player != world.player:
location_name += " (" + world.multiworld.get_player_name(location_obj.player) + ")"
- return location_name, item_name, location_obj.address if (location_obj.player == world.player) else -1
+ return WitnessLocationHint(location_obj, False)
-def make_hint_from_location(world: "WitnessWorld", location: str):
+def hint_from_location(world: "WitnessWorld", location: str) -> Optional[WitnessLocationHint]:
location_obj = world.multiworld.get_location(location, world.player)
item_obj = world.multiworld.get_location(location, world.player).item
item_name = item_obj.name
if item_obj.player != world.player:
item_name += " (" + world.multiworld.get_player_name(item_obj.player) + ")"
- return location, item_name, location_obj.address if (location_obj.player == world.player) else -1
+ return WitnessLocationHint(location_obj, True)
-def make_hints(world: "WitnessWorld", hint_amount: int, own_itempool: List[Item]):
- hints = list()
+def get_items_and_locations_in_random_order(world: "WitnessWorld", own_itempool: List[Item]):
+ prog_items_in_this_world = sorted(
+ item.name for item in own_itempool
+ if item.advancement and item.code and item.location
+ )
+ locations_in_this_world = sorted(
+ location.name for location in world.multiworld.get_locations(world.player)
+ if location.address and location.progress_type != LocationProgressType.EXCLUDED
+ )
- prog_items_in_this_world = {
- item.name for item in own_itempool if item.advancement and item.code and item.location
- }
- loc_in_this_world = {
- location.name for location in world.multiworld.get_locations(world.player) if location.address
- }
+ world.random.shuffle(prog_items_in_this_world)
+ world.random.shuffle(locations_in_this_world)
+
+ return prog_items_in_this_world, locations_in_this_world
+
+
+def make_always_and_priority_hints(world: "WitnessWorld", own_itempool: List[Item],
+ already_hinted_locations: Set[Location]
+ ) -> Tuple[List[WitnessLocationHint], List[WitnessLocationHint]]:
+ prog_items_in_this_world, loc_in_this_world = get_items_and_locations_in_random_order(world, own_itempool)
always_locations = [
location for location in get_always_hint_locations(world)
@@ -319,105 +420,350 @@ def make_hints(world: "WitnessWorld", hint_amount: int, own_itempool: List[Item]
if item in prog_items_in_this_world
]
- always_hint_pairs = dict()
+ # Get always and priority location/item hints
+ always_location_hints = {hint_from_location(world, location) for location in always_locations}
+ always_item_hints = {hint_from_item(world, item, own_itempool) for item in always_items}
+ priority_location_hints = {hint_from_location(world, location) for location in priority_locations}
+ priority_item_hints = {hint_from_item(world, item, own_itempool) for item in priority_items}
- for item in always_items:
- hint_pair = make_hint_from_item(world, item, own_itempool)
+ # Combine the sets. This will get rid of duplicates
+ always_hints_set = always_item_hints | always_location_hints
+ priority_hints_set = priority_item_hints | priority_location_hints
- if not hint_pair or hint_pair[2] == 158007: # Tutorial Gate Open
- continue
+ # Make sure priority hints doesn't contain any hints that are already always hints.
+ priority_hints_set -= always_hints_set
- always_hint_pairs[hint_pair[0]] = (hint_pair[1], True, hint_pair[2])
+ always_generator = [hint for hint in always_hints_set if hint and hint.location not in already_hinted_locations]
+ priority_generator = [hint for hint in priority_hints_set if hint and hint.location not in already_hinted_locations]
- for location in always_locations:
- hint_pair = make_hint_from_location(world, location)
- always_hint_pairs[hint_pair[0]] = (hint_pair[1], False, hint_pair[2])
+ # Convert both hint types to list and then shuffle. Also, get rid of None and Tutorial Gate Open.
+ always_hints = sorted(always_generator, key=lambda h: h.location)
+ priority_hints = sorted(priority_generator, key=lambda h: h.location)
+ world.random.shuffle(always_hints)
+ world.random.shuffle(priority_hints)
- priority_hint_pairs = dict()
+ return always_hints, priority_hints
- for item in priority_items:
- hint_pair = make_hint_from_item(world, item, own_itempool)
- if not hint_pair or hint_pair[2] == 158007: # Tutorial Gate Open
- continue
+def make_extra_location_hints(world: "WitnessWorld", hint_amount: int, own_itempool: List[Item],
+ already_hinted_locations: Set[Location], hints_to_use_first: List[WitnessLocationHint],
+ unhinted_locations_for_hinted_areas: Dict[str, Set[Location]]) -> List[WitnessWordedHint]:
+ prog_items_in_this_world, locations_in_this_world = get_items_and_locations_in_random_order(world, own_itempool)
- priority_hint_pairs[hint_pair[0]] = (hint_pair[1], True, hint_pair[2])
+ next_random_hint_is_location = world.random.randrange(0, 2)
- for location in priority_locations:
- hint_pair = make_hint_from_location(world, location)
- priority_hint_pairs[hint_pair[0]] = (hint_pair[1], False, hint_pair[2])
+ hints = []
- already_hinted_locations = set()
+ # This is a way to reverse a Dict[a,List[b]] to a Dict[b,a]
+ area_reverse_lookup = {v: k for k, l in unhinted_locations_for_hinted_areas.items() for v in l}
- for loc, item in always_hint_pairs.items():
- if loc in already_hinted_locations:
+ while len(hints) < hint_amount:
+ if not prog_items_in_this_world and not locations_in_this_world and not hints_to_use_first:
+ player_name = world.multiworld.get_player_name(world.player)
+ logging.warning(f"Ran out of items/locations to hint for player {player_name}.")
+ break
+
+ if hints_to_use_first:
+ location_hint = hints_to_use_first.pop()
+ elif next_random_hint_is_location and locations_in_this_world:
+ location_hint = hint_from_location(world, locations_in_this_world.pop())
+ elif not next_random_hint_is_location and prog_items_in_this_world:
+ location_hint = hint_from_item(world, prog_items_in_this_world.pop(), own_itempool)
+ # The list that the hint was supposed to be taken from was empty.
+ # Try the other list, which has to still have something, as otherwise, all lists would be empty,
+ # which would have triggered the guard condition above.
+ else:
+ next_random_hint_is_location = not next_random_hint_is_location
continue
- if item[1]:
- hints.append((f"{item[0]} can be found at {loc}.", item[2]))
- else:
- hints.append((f"{loc} contains {item[0]}.", item[2]))
+ if not location_hint or location_hint.location in already_hinted_locations:
+ continue
- already_hinted_locations.add(loc)
+ # Don't hint locations in areas that are almost fully hinted out already
+ if location_hint.location in area_reverse_lookup:
+ area = area_reverse_lookup[location_hint.location]
+ if len(unhinted_locations_for_hinted_areas[area]) == 1:
+ continue
+ del area_reverse_lookup[location_hint.location]
+ unhinted_locations_for_hinted_areas[area] -= {location_hint.location}
- world.random.shuffle(hints) # shuffle always hint order in case of low hint amount
+ hints.append(word_direct_hint(world, location_hint))
+ already_hinted_locations.add(location_hint.location)
- remaining_hints = hint_amount - len(hints)
- priority_hint_amount = int(max(0.0, min(len(priority_hint_pairs) / 2, remaining_hints / 2)))
+ next_random_hint_is_location = not next_random_hint_is_location
- prog_items_in_this_world = sorted(prog_items_in_this_world)
- locations_in_this_world = sorted(loc_in_this_world)
+ return hints
- world.random.shuffle(prog_items_in_this_world)
- world.random.shuffle(locations_in_this_world)
- priority_hint_list = list(priority_hint_pairs.items())
- world.random.shuffle(priority_hint_list)
- for _ in range(0, priority_hint_amount):
- next_priority_hint = priority_hint_list.pop()
- loc = next_priority_hint[0]
- item = next_priority_hint[1]
+def generate_joke_hints(world: "WitnessWorld", amount: int) -> List[Tuple[str, int, int]]:
+ return [(x, -1, -1) for x in world.random.sample(joke_hints, amount)]
- if loc in already_hinted_locations:
- continue
- if item[1]:
- hints.append((f"{item[0]} can be found at {loc}.", item[2]))
- else:
- hints.append((f"{loc} contains {item[0]}.", item[2]))
+def choose_areas(world: "WitnessWorld", amount: int, locations_per_area: Dict[str, List[Location]],
+ already_hinted_locations: Set[Location]) -> Tuple[List[str], Dict[str, Set[Location]]]:
+ """
+ Choose areas to hint.
+ This takes into account that some areas may already have had items hinted in them through location hints.
+ When this happens, they are made less likely to receive an area hint.
+ """
- already_hinted_locations.add(loc)
+ unhinted_locations_per_area = dict()
+ unhinted_location_percentage_per_area = dict()
- next_random_hint_is_item = world.random.randrange(0, 2)
+ for area_name, locations in locations_per_area.items():
+ not_yet_hinted_locations = sum(location not in already_hinted_locations for location in locations)
+ unhinted_locations_per_area[area_name] = {loc for loc in locations if loc not in already_hinted_locations}
+ unhinted_location_percentage_per_area[area_name] = not_yet_hinted_locations / len(locations)
- while len(hints) < hint_amount:
- if next_random_hint_is_item:
- if not prog_items_in_this_world:
- next_random_hint_is_item = not next_random_hint_is_item
- continue
+ items_per_area = {area_name: [location.item for location in locations]
+ for area_name, locations in locations_per_area.items()}
- hint = make_hint_from_item(world, prog_items_in_this_world.pop(), own_itempool)
+ areas = sorted(area for area in items_per_area if unhinted_location_percentage_per_area[area])
+ weights = [unhinted_location_percentage_per_area[area] for area in areas]
- if not hint or hint[0] in already_hinted_locations:
- continue
+ amount = min(amount, len(weights))
+
+ hinted_areas = weighted_sample(world.random, areas, weights, amount)
+
+ return hinted_areas, unhinted_locations_per_area
+
+
+def get_hintable_areas(world: "WitnessWorld") -> Tuple[Dict[str, List[Location]], Dict[str, List[Item]]]:
+ potential_areas = list(StaticWitnessLogic.ALL_AREAS_BY_NAME.keys())
+
+ locations_per_area = dict()
+ items_per_area = dict()
+
+ for area in potential_areas:
+ regions = [
+ world.regio.created_regions[region]
+ for region in StaticWitnessLogic.ALL_AREAS_BY_NAME[area]["regions"]
+ if region in world.regio.created_regions
+ ]
+ locations = [location for region in regions for location in region.get_locations() if location.address]
+
+ if locations:
+ locations_per_area[area] = locations
+ items_per_area[area] = [location.item for location in locations]
+
+ return locations_per_area, items_per_area
+
+
+def word_area_hint(world: "WitnessWorld", hinted_area: str, corresponding_items: List[Item]) -> Tuple[str, int]:
+ """
+ Word the hint for an area using natural sounding language.
+ This takes into account how much progression there is, how much of it is local/non-local, and whether there are
+ any local lasers to be found in this area.
+ """
+
+ local_progression = sum(item.player == world.player and item.advancement for item in corresponding_items)
+ non_local_progression = sum(item.player != world.player and item.advancement for item in corresponding_items)
- hints.append((f"{hint[1]} can be found at {hint[0]}.", hint[2]))
+ laser_names = {"Symmetry Laser", "Desert Laser", "Quarry Laser", "Shadows Laser", "Town Laser", "Monastery Laser",
+ "Jungle Laser", "Bunker Laser", "Swamp Laser", "Treehouse Laser", "Keep Laser", }
- already_hinted_locations.add(hint[0])
+ local_lasers = sum(
+ item.player == world.player and item.name in laser_names
+ for item in corresponding_items
+ )
+
+ total_progression = non_local_progression + local_progression
+
+ player_count = world.multiworld.players
+
+ area_progression_word = "Both" if total_progression == 2 else "All"
+
+ if not total_progression:
+ hint_string = f"In the {hinted_area} area, you will find no progression items."
+
+ elif total_progression == 1:
+ hint_string = f"In the {hinted_area} area, you will find 1 progression item."
+
+ if player_count > 1:
+ if local_lasers:
+ hint_string += "\nThis item is a laser for this world."
+ elif non_local_progression:
+ other_player_str = "the other player" if player_count == 2 else "another player"
+ hint_string += f"\nThis item is for {other_player_str}."
+ else:
+ hint_string += "\nThis item is for this world."
else:
- hint = make_hint_from_location(world, locations_in_this_world.pop())
+ if local_lasers:
+ hint_string += "\nThis item is a laser."
+
+ else:
+ hint_string = f"In the {hinted_area} area, you will find {total_progression} progression items."
+
+ if local_lasers == total_progression:
+ sentence_end = (" for this world." if player_count > 1 else ".")
+ hint_string += f"\nAll of them are lasers" + sentence_end
+
+ elif player_count > 1:
+ if local_progression and non_local_progression:
+ if non_local_progression == 1:
+ other_player_str = "the other player" if player_count == 2 else "another player"
+ hint_string += f"\nOne of them is for {other_player_str}."
+ else:
+ other_player_str = "the other player" if player_count == 2 else "other players"
+ hint_string += f"\n{non_local_progression} of them are for {other_player_str}."
+ elif non_local_progression:
+ other_players_str = "the other player" if player_count == 2 else "other players"
+ hint_string += f"\n{area_progression_word} of them are for {other_players_str}."
+ elif local_progression:
+ hint_string += f"\n{area_progression_word} of them are for this world."
+
+ if local_lasers == 1:
+ if not non_local_progression:
+ hint_string += "\nAlso, one of them is a laser."
+ else:
+ hint_string += "\nAlso, one of them is a laser for this world."
+ elif local_lasers:
+ if not non_local_progression:
+ hint_string += f"\nAlso, {local_lasers} of them are lasers."
+ else:
+ hint_string += f"\nAlso, {local_lasers} of them are lasers for this world."
- if hint[0] in already_hinted_locations:
- continue
+ else:
+ if local_lasers == 1:
+ hint_string += "\nOne of them is a laser."
+ elif local_lasers:
+ hint_string += f"\n{local_lasers} of them are lasers."
- hints.append((f"{hint[0]} contains {hint[1]}.", hint[2]))
+ return hint_string, total_progression
- already_hinted_locations.add(hint[0])
- next_random_hint_is_item = not next_random_hint_is_item
+def make_area_hints(world: "WitnessWorld", amount: int, already_hinted_locations: Set[Location]
+ ) -> Tuple[List[WitnessWordedHint], Dict[str, Set[Location]]]:
+ locs_per_area, items_per_area = get_hintable_areas(world)
- return hints
+ hinted_areas, unhinted_locations_per_area = choose_areas(world, amount, locs_per_area, already_hinted_locations)
+
+ hints = []
+
+ for hinted_area in hinted_areas:
+ hint_string, prog_amount = word_area_hint(world, hinted_area, items_per_area[hinted_area])
+
+ hints.append(WitnessWordedHint(hint_string, None, f"hinted_area:{hinted_area}", prog_amount))
+
+ if len(hinted_areas) < amount:
+ player_name = world.multiworld.get_player_name(world.player)
+ logging.warning(f"Was not able to make {amount} area hints for player {player_name}. "
+ f"Made {len(hinted_areas)} instead, and filled the rest with random location hints.")
+
+ return hints, unhinted_locations_per_area
+
+
+def create_all_hints(world: "WitnessWorld", hint_amount: int, area_hints: int,
+ already_hinted_locations: Set[Location]) -> List[WitnessWordedHint]:
+ generated_hints: List[WitnessWordedHint] = []
+
+ state = CollectionState(world.multiworld)
+
+ # Keep track of already hinted locations. Consider early Tutorial as "already hinted"
+
+ already_hinted_locations |= {
+ loc for loc in world.multiworld.get_reachable_locations(state, world.player)
+ if loc.address and StaticWitnessLogic.ENTITIES_BY_NAME[loc.name]["area"]["name"] == "Tutorial (Inside)"
+ }
+
+ intended_location_hints = hint_amount - area_hints
+
+ # First, make always and priority hints.
+
+ always_hints, priority_hints = make_always_and_priority_hints(
+ world, world.own_itempool, already_hinted_locations
+ )
+
+ generated_always_hints = len(always_hints)
+ possible_priority_hints = len(priority_hints)
+
+ # Make as many always hints as possible
+ always_hints_to_use = min(intended_location_hints, generated_always_hints)
+
+ # Make up to half of the rest of the location hints priority hints, using up to half of the possibly priority hints
+ remaining_location_hints = intended_location_hints - always_hints_to_use
+ priority_hints_to_use = int(max(0.0, min(possible_priority_hints / 2, remaining_location_hints / 2)))
+
+ for _ in range(always_hints_to_use):
+ location_hint = always_hints.pop()
+ generated_hints.append(word_direct_hint(world, location_hint))
+ already_hinted_locations.add(location_hint.location)
+
+ for _ in range(priority_hints_to_use):
+ location_hint = priority_hints.pop()
+ generated_hints.append(word_direct_hint(world, location_hint))
+ already_hinted_locations.add(location_hint.location)
+
+ location_hints_created_in_round_1 = len(generated_hints)
+
+ unhinted_locations_per_area: Dict[str, Set[Location]] = dict()
+
+ # Then, make area hints.
+ if area_hints:
+ generated_area_hints, unhinted_locations_per_area = make_area_hints(world, area_hints, already_hinted_locations)
+ generated_hints += generated_area_hints
+
+ # If we don't have enough hints yet, recalculate always and priority hints, then fill with random hints
+ if len(generated_hints) < hint_amount:
+ remaining_needed_location_hints = hint_amount - len(generated_hints)
+
+ # Save old values for used always and priority hints for later calculations
+ amt_of_used_always_hints = always_hints_to_use
+ amt_of_used_priority_hints = priority_hints_to_use
+
+ # Recalculate how many always hints and priority hints are supposed to be used
+ intended_location_hints = remaining_needed_location_hints + location_hints_created_in_round_1
+
+ always_hints_to_use = min(intended_location_hints, generated_always_hints)
+ priority_hints_to_use = int(max(0.0, min(possible_priority_hints / 2, remaining_location_hints / 2)))
+
+ # If we now need more always hints and priority hints than we thought previously, make some more.
+ more_always_hints = always_hints_to_use - amt_of_used_always_hints
+ more_priority_hints = priority_hints_to_use - amt_of_used_priority_hints
+
+ extra_always_and_priority_hints: List[WitnessLocationHint] = []
+
+ for _ in range(more_always_hints):
+ extra_always_and_priority_hints.append(always_hints.pop())
+
+ for _ in range(more_priority_hints):
+ extra_always_and_priority_hints.append(priority_hints.pop())
+
+ generated_hints += make_extra_location_hints(
+ world, hint_amount - len(generated_hints), world.own_itempool, already_hinted_locations,
+ extra_always_and_priority_hints, unhinted_locations_per_area
+ )
+
+ # If we still don't have enough for whatever reason, throw a warning, proceed with the lower amount
+ if len(generated_hints) != hint_amount:
+ player_name = world.multiworld.get_player_name(world.player)
+ logging.warning(f"Couldn't generate {hint_amount} hints for player {player_name}. "
+ f"Generated {len(generated_hints)} instead.")
+
+ return generated_hints
+
+
+def make_compact_hint_data(hint: WitnessWordedHint, local_player_number: int) -> CompactItemData:
+ location = hint.location
+ area_amount = hint.area_amount
+
+ # None if junk hint, address if location hint, area string if area hint
+ arg_1 = location.address if location else (hint.area if hint.area else None)
+
+ # self.player if junk hint, player if location hint, progression amount if area hint
+ arg_2 = area_amount if area_amount is not None else (location.player if location else local_player_number)
+
+ return hint.wording, arg_1, arg_2
+
+
+def make_laser_hints(world: "WitnessWorld", laser_names: List[str]) -> Dict[str, WitnessWordedHint]:
+ laser_hints_by_name = dict()
+
+ for item_name in laser_names:
+ location_hint = hint_from_item(world, item_name, world.own_itempool)
+ if not location_hint:
+ continue
+ laser_hints_by_name[item_name] = word_direct_hint(world, location_hint)
-def generate_joke_hints(world: "WitnessWorld", amount: int) -> List[Tuple[str, int]]:
- return [(x, -1) for x in world.random.sample(joke_hints, amount)]
+ return laser_hints_by_name
diff --git a/worlds/witness/items.py b/worlds/witness/items.py
index a8c889de937a..6802fd2a21b5 100644
--- a/worlds/witness/items.py
+++ b/worlds/witness/items.py
@@ -112,30 +112,12 @@ def __init__(self, world: "WitnessWorld", logic: WitnessPlayerLogic, locat: Witn
or name in logic.PROG_ITEMS_ACTUALLY_IN_THE_GAME
}
- # Adjust item classifications based on game settings.
- eps_shuffled = self._world.options.shuffle_EPs
- come_to_you = self._world.options.elevators_come_to_you
- difficulty = self._world.options.puzzle_randomization
+ # Downgrade door items
for item_name, item_data in self.item_data.items():
- if not eps_shuffled and item_name in {"Monastery Garden Entry (Door)",
- "Monastery Shortcuts",
- "Quarry Boathouse Hook Control (Panel)",
- "Windmill Turn Control (Panel)"}:
- # Downgrade doors that only gate progress in EP shuffle.
- item_data.classification = ItemClassification.useful
- elif not come_to_you and not eps_shuffled and item_name in {"Quarry Elevator Control (Panel)",
- "Swamp Long Bridge (Panel)"}:
- # These Bridges/Elevators are not logical access because they may leave you stuck.
- item_data.classification = ItemClassification.useful
- elif item_name in {"River Monastery Garden Shortcut (Door)",
- "Monastery Laser Shortcut (Door)",
- "Orchard Second Gate (Door)",
- "Jungle Bamboo Laser Shortcut (Door)",
- "Caves Elevator Controls (Panel)"}:
- # Downgrade doors that don't gate progress.
- item_data.classification = ItemClassification.useful
- elif item_name == "Keep Pressure Plates 2 Exit (Door)" and not (difficulty == "none" and eps_shuffled):
- # PP2EP requires the door in vanilla puzzles, otherwise it's unnecessary
+ if not isinstance(item_data.definition, DoorItemDefinition):
+ continue
+
+ if all(not self._logic.solvability_guaranteed(e_hex) for e_hex in item_data.definition.panel_id_hexes):
item_data.classification = ItemClassification.useful
# Build the mandatory item list.
@@ -194,9 +176,14 @@ def get_filler_items(self, quantity: int) -> Dict[str, int]:
# Read trap configuration data.
trap_weight = self._world.options.trap_percentage / 100
- filler_weight = 1 - trap_weight
+ trap_items = self._world.options.trap_weights.value
+
+ if not sum(trap_items.values()):
+ trap_weight = 0
# Add filler items to the list.
+ filler_weight = 1 - trap_weight
+
filler_items: Dict[str, float]
filler_items = {name: data.definition.weight if isinstance(data.definition, WeightedItemDefinition) else 1
for (name, data) in self.item_data.items() if data.definition.category is ItemCategory.FILLER}
@@ -205,8 +192,6 @@ def get_filler_items(self, quantity: int) -> Dict[str, int]:
# Add trap items.
if trap_weight > 0:
- trap_items = {name: data.definition.weight if isinstance(data.definition, WeightedItemDefinition) else 1
- for (name, data) in self.item_data.items() if data.definition.category is ItemCategory.TRAP}
filler_items.update({name: base_weight * trap_weight / sum(trap_items.values())
for name, base_weight in trap_items.items() if base_weight > 0})
@@ -228,7 +213,7 @@ def get_early_items(self) -> List[str]:
output = {"Dots", "Black/White Squares", "Symmetry", "Shapers", "Stars"}
if self._world.options.shuffle_discarded_panels:
- if self._world.options.puzzle_randomization == 1:
+ if self._world.options.puzzle_randomization == "sigma_expert":
output.add("Arrows")
else:
output.add("Triangles")
@@ -285,3 +270,6 @@ def get_progressive_item_ids_in_pool(self) -> Dict[int, List[int]]:
output[item.ap_code] = [StaticWitnessItems.item_data[child_item].ap_code
for child_item in item.definition.child_item_names]
return output
+
+
+StaticWitnessItems()
diff --git a/worlds/witness/locations.py b/worlds/witness/locations.py
index d20be2794056..cd6d71f46911 100644
--- a/worlds/witness/locations.py
+++ b/worlds/witness/locations.py
@@ -55,8 +55,8 @@ class StaticWitnessLocations:
"Desert Light Room 3",
"Desert Pond Room 5",
"Desert Flood Room 6",
- "Desert Final Hexagonal",
- "Desert Final Bent 3",
+ "Desert Elevator Room Hexagonal",
+ "Desert Elevator Room Bent 3",
"Desert Laser Panel",
"Quarry Entry 1 Panel",
@@ -110,13 +110,13 @@ class StaticWitnessLocations:
"Town Red Rooftop 5",
"Town Wooden Roof Lower Row 5",
"Town Wooden Rooftop",
- "Town Windmill Entry Panel",
+ "Windmill Entry Panel",
"Town RGB House Entry Panel",
"Town Laser Panel",
- "Town RGB Room Left",
- "Town RGB Room Right",
- "Town Sound Room Right",
+ "Town RGB House Upstairs Left",
+ "Town RGB House Upstairs Right",
+ "Town RGB House Sound Room Right",
"Windmill Theater Entry Panel",
"Theater Exit Left Panel",
@@ -134,8 +134,8 @@ class StaticWitnessLocations:
"Jungle Popup Wall 6",
"Jungle Laser Panel",
- "River Vault Box",
- "River Monastery Garden Shortcut Panel",
+ "Jungle Vault Box",
+ "Jungle Monastery Garden Shortcut Panel",
"Bunker Entry Panel",
"Bunker Intro Left 5",
@@ -177,7 +177,7 @@ class StaticWitnessLocations:
"Mountainside Vault Box",
"Mountaintop River Shape",
- "First Hallway EP",
+ "Tutorial First Hallway EP",
"Tutorial Cloud EP",
"Tutorial Patio Flowers EP",
"Tutorial Gate EP",
@@ -185,7 +185,7 @@ class StaticWitnessLocations:
"Outside Tutorial Town Sewer EP",
"Outside Tutorial Path EP",
"Outside Tutorial Tractor EP",
- "Main Island Thundercloud EP",
+ "Mountainside Thundercloud EP",
"Glass Factory Vase EP",
"Symmetry Island Glass Factory Black Line Reflection EP",
"Symmetry Island Glass Factory Black Line EP",
@@ -242,9 +242,9 @@ class StaticWitnessLocations:
"Monastery Left Shutter EP",
"Monastery Middle Shutter EP",
"Monastery Right Shutter EP",
- "Town Windmill First Blade EP",
- "Town Windmill Second Blade EP",
- "Town Windmill Third Blade EP",
+ "Windmill First Blade EP",
+ "Windmill Second Blade EP",
+ "Windmill Third Blade EP",
"Town Tower Underside Third EP",
"Town Tower Underside Fourth EP",
"Town Tower Underside First EP",
@@ -268,10 +268,10 @@ class StaticWitnessLocations:
"Jungle Tree Halo EP",
"Jungle Bamboo CCW EP",
"Jungle Bamboo CW EP",
- "River Green Leaf Moss EP",
- "River Monastery Garden Left EP",
- "River Monastery Garden Right EP",
- "River Monastery Wall EP",
+ "Jungle Green Leaf Moss EP",
+ "Monastery Garden Left EP",
+ "Monastery Garden Right EP",
+ "Monastery Wall EP",
"Bunker Tinted Door EP",
"Bunker Green Room Flowers EP",
"Swamp Purple Sand Middle EP",
@@ -330,12 +330,12 @@ class StaticWitnessLocations:
"Treehouse Obelisk Side 4",
"Treehouse Obelisk Side 5",
"Treehouse Obelisk Side 6",
- "River Obelisk Side 1",
- "River Obelisk Side 2",
- "River Obelisk Side 3",
- "River Obelisk Side 4",
- "River Obelisk Side 5",
- "River Obelisk Side 6",
+ "Mountainside Obelisk Side 1",
+ "Mountainside Obelisk Side 2",
+ "Mountainside Obelisk Side 3",
+ "Mountainside Obelisk Side 4",
+ "Mountainside Obelisk Side 5",
+ "Mountainside Obelisk Side 6",
"Quarry Obelisk Side 1",
"Quarry Obelisk Side 2",
"Quarry Obelisk Side 3",
@@ -407,13 +407,13 @@ class StaticWitnessLocations:
"Mountain Floor 2 Elevator Discard",
"Mountain Bottom Floor Giant Puzzle",
- "Mountain Bottom Floor Final Room Entry Left",
- "Mountain Bottom Floor Final Room Entry Right",
+ "Mountain Bottom Floor Pillars Room Entry Left",
+ "Mountain Bottom Floor Pillars Room Entry Right",
"Mountain Bottom Floor Caves Entry Panel",
- "Mountain Final Room Left Pillar 4",
- "Mountain Final Room Right Pillar 4",
+ "Mountain Bottom Floor Left Pillar 4",
+ "Mountain Bottom Floor Right Pillar 4",
"Challenge Vault Box",
"Theater Challenge Video",
@@ -438,12 +438,12 @@ class StaticWitnessLocations:
"Treehouse Obelisk Side 4",
"Treehouse Obelisk Side 5",
"Treehouse Obelisk Side 6",
- "River Obelisk Side 1",
- "River Obelisk Side 2",
- "River Obelisk Side 3",
- "River Obelisk Side 4",
- "River Obelisk Side 5",
- "River Obelisk Side 6",
+ "Mountainside Obelisk Side 1",
+ "Mountainside Obelisk Side 2",
+ "Mountainside Obelisk Side 3",
+ "Mountainside Obelisk Side 4",
+ "Mountainside Obelisk Side 5",
+ "Mountainside Obelisk Side 6",
"Quarry Obelisk Side 1",
"Quarry Obelisk Side 2",
"Quarry Obelisk Side 3",
@@ -459,6 +459,8 @@ class StaticWitnessLocations:
ALL_LOCATIONS_TO_ID = dict()
+ AREA_LOCATION_GROUPS = dict()
+
@staticmethod
def get_id(chex: str):
"""
@@ -491,6 +493,10 @@ def __init__(self):
for key, item in all_loc_to_id.items():
self.ALL_LOCATIONS_TO_ID[key] = item
+ for loc in all_loc_to_id:
+ area = StaticWitnessLogic.ENTITIES_BY_NAME[loc]["area"]["name"]
+ self.AREA_LOCATION_GROUPS.setdefault(area, []).append(loc)
+
class WitnessPlayerLocations:
"""
@@ -509,9 +515,9 @@ def __init__(self, world: "WitnessWorld", player_logic: WitnessPlayerLogic):
if world.options.shuffle_vault_boxes:
self.PANEL_TYPES_TO_SHUFFLE.add("Vault")
- if world.options.shuffle_EPs == 1:
+ if world.options.shuffle_EPs == "individual":
self.PANEL_TYPES_TO_SHUFFLE.add("EP")
- elif world.options.shuffle_EPs == 2:
+ elif world.options.shuffle_EPs == "obelisk_sides":
self.PANEL_TYPES_TO_SHUFFLE.add("Obelisk Side")
for obelisk_loc in StaticWitnessLocations.OBELISK_SIDES:
@@ -543,7 +549,7 @@ def __init__(self, world: "WitnessWorld", player_logic: WitnessPlayerLogic):
)
event_locations = {
- p for p in player_logic.EVENT_PANELS
+ p for p in player_logic.USED_EVENT_NAMES_BY_HEX
}
self.EVENT_LOCATION_TABLE = {
@@ -563,3 +569,6 @@ def add_location_late(self, entity_name: str):
entity_hex = StaticWitnessLogic.ENTITIES_BY_NAME[entity_name]["entity_hex"]
self.CHECK_LOCATION_TABLE[entity_hex] = entity_name
self.CHECK_PANELHEX_TO_ID[entity_hex] = StaticWitnessLocations.get_id(entity_hex)
+
+
+StaticWitnessLocations()
diff --git a/worlds/witness/options.py b/worlds/witness/options.py
new file mode 100644
index 000000000000..b66308df432a
--- /dev/null
+++ b/worlds/witness/options.py
@@ -0,0 +1,333 @@
+from dataclasses import dataclass
+
+from schema import Schema, And, Optional
+
+from Options import Toggle, DefaultOnToggle, Range, Choice, PerGameCommonOptions, OptionDict
+
+from .static_logic import WeightedItemDefinition, ItemCategory, StaticWitnessLogic
+
+
+class DisableNonRandomizedPuzzles(Toggle):
+ """
+ Disables puzzles that cannot be randomized.
+ This includes many puzzles that heavily involve the environment, such as Shadows, Monastery or Orchard.
+
+ The lasers for those areas will activate as you solve optional puzzles, such as Discarded Panels.
+ Additionally, the panel activating the Jungle Popup Wall will be on from the start.
+ """
+ display_name = "Disable non randomized puzzles"
+
+
+class EarlyCaves(Choice):
+ """
+ Adds an item that opens the Caves Shortcuts to Swamp and Mountain, allowing early access to the Caves even if you are not playing a remote Door Shuffle mode.
+ You can either add this item to the pool to be found in the multiworld, or you can outright start with it and have immediate access to the Caves.
+
+ If you choose "Add To Pool" and you are already playing a remote Door Shuffle mode, this option will do nothing.
+ """
+ display_name = "Early Caves"
+ option_off = 0
+ alias_false = 0
+ option_add_to_pool = 1
+ option_starting_inventory = 2
+ alias_true = 2
+ alias_on = 2
+
+
+class ShuffleSymbols(DefaultOnToggle):
+ """
+ If on, you will need to unlock puzzle symbols as items to be able to solve the panels that contain those symbols.
+
+ Please note that there is no minimum set of progression items in this randomizer.
+ If you turn this option off and don't turn on door shuffle or obelisk keys, there will be no progression items, which will disallow you from adding your yaml to a multiworld generation.
+ """
+ display_name = "Shuffle Symbols"
+
+
+class ShuffleLasers(Choice):
+ """
+ If on, the 11 lasers are turned into items and will activate on their own upon receiving them.
+ """
+ display_name = "Shuffle Lasers"
+ option_off = 0
+ alias_false = 0
+ option_local = 1
+ option_anywhere = 2
+ alias_true = 2
+ alias_on = 2
+
+
+class ShuffleDoors(Choice):
+ """
+ If on, opening doors, moving bridges etc. will require a "key".
+ If set to "panels", the panel on the door will be locked until receiving its corresponding key.
+ If set to "doors", the door will open immediately upon receiving its key. Door panels are added as location checks.
+ "Mixed" includes all doors from "doors", and all control panels (bridges, elevators etc.) from "panels".
+ """
+ display_name = "Shuffle Doors"
+ option_off = 0
+ option_panels = 1
+ option_doors = 2
+ option_mixed = 3
+
+
+class DoorGroupings(Choice):
+ """
+ If set to "none", there will be one key for each door, potentially resulting in upwards of 120 keys being added to the item pool.
+ If set to "regional", all doors in the same general region will open at once with a single key, reducing the amount of door items and complexity.
+ """
+ display_name = "Door Groupings"
+ option_off = 0
+ option_regional = 1
+
+
+class ShuffleBoat(DefaultOnToggle):
+ """
+ If on, adds a "Boat" item to the item pool. Before receiving this item, you will not be able to use the boat.
+ """
+ display_name = "Shuffle Boat"
+
+
+class ShuffleDiscardedPanels(Toggle):
+ """
+ Adds Discarded Panels into the location pool.
+
+ Even if this is off, solving certain Discarded Panels may still be necessary to beat the game - The main example of this being the alternate activation triggers in "Disable non randomized puzzles".
+ """
+ display_name = "Shuffle Discarded Panels"
+
+
+class ShuffleVaultBoxes(Toggle):
+ """
+ Adds Vault Boxes to the location pool.
+ """
+ display_name = "Shuffle Vault Boxes"
+
+
+class ShuffleEnvironmentalPuzzles(Choice):
+ """
+ Adds Environmental/Obelisk Puzzles into the location pool.
+ If set to "individual", every Environmental Puzzle sends an item.
+ If set to "Obelisk Sides", completing every puzzle on one side of an Obelisk sends an item.
+
+ Note: In Obelisk Sides, any EPs excluded through another option will be pre-completed on their Obelisk.
+ """
+ display_name = "Shuffle Environmental Puzzles"
+ option_off = 0
+ option_individual = 1
+ option_obelisk_sides = 2
+
+
+class ShuffleDog(Toggle):
+ """
+ Adds petting the Town dog into the location pool.
+ """
+ display_name = "Pet the Dog"
+
+
+class EnvironmentalPuzzlesDifficulty(Choice):
+ """
+ When "Shuffle Environmental Puzzles" is on, this setting governs which EPs are eligible for the location pool.
+ If set to "eclipse", every EP in the game is eligible, including the 1-hour-long "Theater Eclipse EP".
+ If set to "tedious", Theater Eclipse EP is excluded from the location pool.
+ If set to "normal", several other difficult or long EPs are excluded as well.
+ """
+ display_name = "Environmental Puzzles Difficulty"
+ option_normal = 0
+ option_tedious = 1
+ option_eclipse = 2
+
+
+class ObeliskKeys(DefaultOnToggle):
+ """
+ Add one Obelisk Key item per Obelisk, locking you out of solving any of the associated Environmental Puzzles.
+
+ Does nothing if "Shuffle Environmental Puzzles" is set to "off".
+ """
+ display_name = "Obelisk Keys"
+
+
+class ShufflePostgame(Toggle):
+ """
+ Adds locations into the pool that are guaranteed to become accessible after or at the same time as your goal.
+ Use this if you don't play with release on victory.
+ """
+ display_name = "Shuffle Postgame"
+
+
+class VictoryCondition(Choice):
+ """
+ Set the victory condition for this world.
+ Elevator: Start the elevator at the bottom of the mountain (requires Mountain Lasers).
+ Challenge: Beat the secret Challenge (requires Challenge Lasers).
+ Mountain Box Short: Input the short solution to the Mountaintop Box (requires Mountain Lasers).
+ Mountain Box Long: Input the long solution to the Mountaintop Box (requires Challenge Lasers).
+
+ It is important to note that while the Mountain Box requires Desert Laser to be redirected in Town for that laser
+ to count, the laser locks on the Elevator and Challenge Timer panels do not.
+ """
+ display_name = "Victory Condition"
+ option_elevator = 0
+ option_challenge = 1
+ option_mountain_box_short = 2
+ option_mountain_box_long = 3
+
+
+class PuzzleRandomization(Choice):
+ """
+ Puzzles in this randomizer are randomly generated. This option changes the difficulty/types of puzzles.
+ """
+ display_name = "Puzzle Randomization"
+ option_sigma_normal = 0
+ option_sigma_expert = 1
+ option_none = 2
+
+
+class MountainLasers(Range):
+ """
+ Sets the number of lasers required to enter the Mountain.
+ If set to a higher number than 7, the mountaintop box will be slightly rotated to make it possible to solve without the hatch being opened.
+ This change will also be applied logically to the long solution ("Challenge Lasers" option).
+ """
+ display_name = "Required Lasers for Mountain Entry"
+ range_start = 1
+ range_end = 11
+ default = 7
+
+
+class ChallengeLasers(Range):
+ """
+ Sets the number of lasers required to enter the Caves through the Mountain Bottom Floor Discard and to unlock the Challenge Timer Panel.
+ """
+ display_name = "Required Lasers for Challenge"
+ range_start = 1
+ range_end = 11
+ default = 11
+
+
+class ElevatorsComeToYou(Toggle):
+ """
+ If on, the Quarry Elevator, Bunker Elevator and Swamp Long Bridge will "come to you" if you approach them.
+ This does actually affect logic as it allows unintended backwards / early access into these areas.
+ """
+ display_name = "All Bridges & Elevators come to you"
+
+
+class TrapPercentage(Range):
+ """
+ Replaces junk items with traps, at the specified rate.
+ """
+ display_name = "Trap Percentage"
+ range_start = 0
+ range_end = 100
+ default = 20
+
+
+class TrapWeights(OptionDict):
+ """
+ Specify the weights determining how many copies of each trap item will be in your itempool.
+ If you don't want a specific type of trap, you can set the weight for it to 0 (Do not delete the entry outright!).
+ If you set all trap weights to 0, you will get no traps, bypassing the "Trap Percentage" option.
+ """
+ display_name = "Trap Weights"
+ schema = Schema({
+ trap_name: And(int, lambda n: n >= 0)
+ for trap_name, item_definition in StaticWitnessLogic.all_items.items()
+ if isinstance(item_definition, WeightedItemDefinition) and item_definition.category is ItemCategory.TRAP
+ })
+ default = {
+ trap_name: item_definition.weight
+ for trap_name, item_definition in StaticWitnessLogic.all_items.items()
+ if isinstance(item_definition, WeightedItemDefinition) and item_definition.category is ItemCategory.TRAP
+ }
+
+
+class PuzzleSkipAmount(Range):
+ """
+ Adds this many Puzzle Skips into the pool, if there is room. Puzzle Skips let you skip one panel.
+ """
+ display_name = "Puzzle Skips"
+ range_start = 0
+ range_end = 30
+ default = 10
+
+
+class HintAmount(Range):
+ """
+ Adds hints to Audio Logs. If set to a low amount, up to 2 additional duplicates of each hint will be added.
+ Remaining Audio Logs will have junk hints.
+ """
+ display_name = "Hints on Audio Logs"
+ range_start = 0
+ range_end = 49
+ default = 12
+
+
+class AreaHintPercentage(Range):
+ """
+ There are two types of hints for The Witness.
+ "Location hints" hint one location in your world or one location containing an item for your world.
+ "Area hints" tell you some general info about the items you can find in one of the main geographic areas on the island.
+ Use this option to specify how many of your hints you want to be area hints. The rest will be location hints.
+ """
+ display_name = "Area Hint Percentage"
+ range_start = 0
+ range_end = 100
+ default = 33
+
+
+class LaserHints(Toggle):
+ """
+ If on, lasers will tell you where their items are if you walk close to them in-game.
+ Only applies if Laser Shuffle is enabled.
+ """
+ display_name = "Laser Hints"
+
+
+class DeathLink(Toggle):
+ """
+ If on, whenever you fail a puzzle (with some exceptions), you and everyone who is also on Death Link dies.
+ The effect of a "death" in The Witness is a Bonk Trap.
+ """
+ display_name = "Death Link"
+
+
+class DeathLinkAmnesty(Range):
+ """
+ The number of panel fails to allow before sending a death through Death Link.
+ 0 means every panel fail will send a death, 1 means every other panel fail will send a death, etc.
+ """
+ display_name = "Death Link Amnesty"
+ range_start = 0
+ range_end = 5
+ default = 1
+
+
+@dataclass
+class TheWitnessOptions(PerGameCommonOptions):
+ puzzle_randomization: PuzzleRandomization
+ shuffle_symbols: ShuffleSymbols
+ shuffle_doors: ShuffleDoors
+ door_groupings: DoorGroupings
+ shuffle_boat: ShuffleBoat
+ shuffle_lasers: ShuffleLasers
+ disable_non_randomized_puzzles: DisableNonRandomizedPuzzles
+ shuffle_discarded_panels: ShuffleDiscardedPanels
+ shuffle_vault_boxes: ShuffleVaultBoxes
+ obelisk_keys: ObeliskKeys
+ shuffle_EPs: ShuffleEnvironmentalPuzzles
+ EP_difficulty: EnvironmentalPuzzlesDifficulty
+ shuffle_postgame: ShufflePostgame
+ victory_condition: VictoryCondition
+ mountain_lasers: MountainLasers
+ challenge_lasers: ChallengeLasers
+ early_caves: EarlyCaves
+ elevators_come_to_you: ElevatorsComeToYou
+ trap_percentage: TrapPercentage
+ trap_weights: TrapWeights
+ puzzle_skip_amount: PuzzleSkipAmount
+ hint_amount: HintAmount
+ area_hint_percentage: AreaHintPercentage
+ laser_hints: LaserHints
+ death_link: DeathLink
+ death_link_amnesty: DeathLinkAmnesty
diff --git a/worlds/witness/player_logic.py b/worlds/witness/player_logic.py
index e1ef1ae4319e..6bc263b9cc68 100644
--- a/worlds/witness/player_logic.py
+++ b/worlds/witness/player_logic.py
@@ -63,26 +63,30 @@ def reduce_req_within_region(self, panel_hex: str) -> FrozenSet[FrozenSet[str]]:
if panel_hex in self.DOOR_ITEMS_BY_ID:
door_items = frozenset({frozenset([item]) for item in self.DOOR_ITEMS_BY_ID[panel_hex]})
- all_options = set()
+ all_options: Set[FrozenSet[str]] = set()
for dependentItem in door_items:
self.PROG_ITEMS_ACTUALLY_IN_THE_GAME_NO_MULTI.update(dependentItem)
for items_option in these_items:
all_options.add(items_option.union(dependentItem))
- # 0x28A0D depends on another entity for *non-power* reasons -> This dependency needs to be preserved,
- # except in Expert, where that dependency doesn't exist, but now there *is* a power dependency.
- # In the future, it would be wise to make a distinction between "power dependencies" and other dependencies.
- if panel_hex == "0x28A0D" and not any("0x28998" in option for option in these_panels):
- these_items = all_options
+ # If this entity is not an EP, and it has an associated door item, ignore the original power dependencies
+ if StaticWitnessLogic.ENTITIES_BY_HEX[panel_hex]["entityType"] != "EP":
+ # 0x28A0D depends on another entity for *non-power* reasons -> This dependency needs to be preserved,
+ # except in Expert, where that dependency doesn't exist, but now there *is* a power dependency.
+ # In the future, it'd be wise to make a distinction between "power dependencies" and other dependencies.
+ if panel_hex == "0x28A0D" and not any("0x28998" in option for option in these_panels):
+ these_items = all_options
- # Another dependency that is not power-based: The Symmetry Island Upper Panel latches
- elif panel_hex == "0x1C349":
- these_items = all_options
+ # Another dependency that is not power-based: The Symmetry Island Upper Panel latches
+ elif panel_hex == "0x1C349":
+ these_items = all_options
+
+ else:
+ return frozenset(all_options)
- # For any other door entity, we just return a set with the item that opens it & disregard power dependencies
else:
- return frozenset(all_options)
+ these_items = all_options
disabled_eps = {eHex for eHex in self.COMPLETELY_DISABLED_ENTITIES
if self.REFERENCE_LOGIC.ENTITIES_BY_HEX[eHex]["entityType"] == "EP"}
@@ -101,9 +105,13 @@ def reduce_req_within_region(self, panel_hex: str) -> FrozenSet[FrozenSet[str]]:
for option_entity in option:
dep_obj = self.REFERENCE_LOGIC.ENTITIES_BY_HEX.get(option_entity)
- if option_entity in self.EVENT_NAMES_BY_HEX:
+ if option_entity in self.ALWAYS_EVENT_NAMES_BY_HEX:
new_items = frozenset({frozenset([option_entity])})
- elif option_entity in {"7 Lasers", "11 Lasers", "PP2 Weirdness", "Theater to Tunnels"}:
+ elif (panel_hex, option_entity) in self.CONDITIONAL_EVENTS:
+ new_items = frozenset({frozenset([option_entity])})
+ self.USED_EVENT_NAMES_BY_HEX[option_entity] = self.CONDITIONAL_EVENTS[(panel_hex, option_entity)]
+ elif option_entity in {"7 Lasers", "11 Lasers", "7 Lasers + Redirect", "11 Lasers + Redirect",
+ "PP2 Weirdness", "Theater to Tunnels"}:
new_items = frozenset({frozenset([option_entity])})
else:
new_items = self.reduce_req_within_region(option_entity)
@@ -169,14 +177,11 @@ def make_single_adjustment(self, adj_type: str, line: str):
if adj_type == "Event Items":
line_split = line.split(" - ")
new_event_name = line_split[0]
- hex_set = line_split[1].split(",")
-
- for entity, event_name in self.EVENT_NAMES_BY_HEX.items():
- if event_name == new_event_name:
- self.DONT_MAKE_EVENTS.add(entity)
+ entity_hex = line_split[1]
+ dependent_hex_set = line_split[2].split(",")
- for hex_code in hex_set:
- self.EVENT_NAMES_BY_HEX[hex_code] = new_event_name
+ for dependent_hex in dependent_hex_set:
+ self.CONDITIONAL_EVENTS[(entity_hex, dependent_hex)] = new_event_name
return
@@ -252,51 +257,101 @@ def make_single_adjustment(self, adj_type: str, line: str):
line = self.REFERENCE_LOGIC.ENTITIES_BY_HEX[line]["checkName"]
self.ADDED_CHECKS.add(line)
+ @staticmethod
+ def handle_postgame(world: "WitnessWorld"):
+ # In shuffle_postgame, panels that become accessible "after or at the same time as the goal" are disabled.
+ # This has a lot of complicated considerations, which I'll try my best to explain.
+ postgame_adjustments = []
+
+ # Make some quick references to some options
+ doors = world.options.shuffle_doors >= 2 # "Panels" mode has no overarching region accessibility implications.
+ early_caves = world.options.early_caves
+ victory = world.options.victory_condition
+ mnt_lasers = world.options.mountain_lasers
+ chal_lasers = world.options.challenge_lasers
+
+ # Goal is "short box" but short box requires more lasers than long box
+ reverse_shortbox_goal = victory == "mountain_box_short" and mnt_lasers > chal_lasers
+
+ # Goal is "short box", and long box requires at least as many lasers as short box (as god intended)
+ proper_shortbox_goal = victory == "mountain_box_short" and chal_lasers >= mnt_lasers
+
+ # Goal is "long box", but short box requires at least as many lasers than long box.
+ reverse_longbox_goal = victory == "mountain_box_long" and mnt_lasers >= chal_lasers
+
+ # If goal is shortbox or "reverse longbox", you will never enter the mountain from the top before winning.
+ mountain_enterable_from_top = not (victory == "mountain_box_short" or reverse_longbox_goal)
+
+ # Caves & Challenge should never have anything if doors are vanilla - definitionally "post-game"
+ # This is technically imprecise, but it matches player expectations better.
+ if not (early_caves or doors):
+ postgame_adjustments.append(get_caves_exclusion_list())
+ postgame_adjustments.append(get_beyond_challenge_exclusion_list())
+
+ # If Challenge is the goal, some panels on the way need to be left on, as well as Challenge Vault box itself
+ if not victory == "challenge":
+ postgame_adjustments.append(get_path_to_challenge_exclusion_list())
+ postgame_adjustments.append(get_challenge_vault_box_exclusion_list())
+
+ # Challenge can only have something if the goal is not challenge or longbox itself.
+ # In case of shortbox, it'd have to be a "reverse shortbox" situation where shortbox requires *more* lasers.
+ # In that case, it'd also have to be a doors mode, but that's already covered by the previous block.
+ if not (victory == "elevator" or reverse_shortbox_goal):
+ postgame_adjustments.append(get_beyond_challenge_exclusion_list())
+ if not victory == "challenge":
+ postgame_adjustments.append(get_challenge_vault_box_exclusion_list())
+
+ # Mountain can't be reached if the goal is shortbox (or "reverse long box")
+ if not mountain_enterable_from_top:
+ postgame_adjustments.append(get_mountain_upper_exclusion_list())
+
+ # Same goes for lower mountain, but that one *can* be reached in remote doors modes.
+ if not doors:
+ postgame_adjustments.append(get_mountain_lower_exclusion_list())
+
+ # The Mountain Bottom Floor Discard is a bit complicated, so we handle it separately. ("it" == the Discard)
+ # In Elevator Goal, it is definitionally in the post-game, unless remote doors is played.
+ # In Challenge Goal, it is before the Challenge, so it is not post-game.
+ # In Short Box Goal, you can win before turning it on, UNLESS Short Box requires MORE lasers than long box.
+ # In Long Box Goal, it is always in the post-game because solving long box is what turns it on.
+ if not ((victory == "elevator" and doors) or victory == "challenge" or (reverse_shortbox_goal and doors)):
+ # We now know Bottom Floor Discard is in the post-game.
+ # This has different consequences depending on whether remote doors is being played.
+ # If doors are vanilla, Bottom Floor Discard locks a door to an area, which has to be disabled as well.
+ if doors:
+ postgame_adjustments.append(get_bottom_floor_discard_exclusion_list())
+ else:
+ postgame_adjustments.append(get_bottom_floor_discard_nondoors_exclusion_list())
+
+ # In Challenge goal + early_caves + vanilla doors, you could find something important on Bottom Floor Discard,
+ # including the Caves Shortcuts themselves if playing "early_caves: start_inventory".
+ # This is another thing that was deemed "unfun" more than fitting the actual definition of post-game.
+ if victory == "challenge" and early_caves and not doors:
+ postgame_adjustments.append(get_bottom_floor_discard_nondoors_exclusion_list())
+
+ # If we have a proper short box goal, long box will never be activated first.
+ if proper_shortbox_goal:
+ postgame_adjustments.append(["Disabled Locations:", "0xFFF00 (Mountain Box Long)"])
+
+ return postgame_adjustments
+
def make_options_adjustments(self, world: "WitnessWorld"):
"""Makes logic adjustments based on options"""
adjustment_linesets_in_order = []
- # Postgame
+ # Make condensed references to some options
- doors = world.options.shuffle_doors >= 2
+ doors = world.options.shuffle_doors >= 2 # "Panels" mode has no overarching region accessibility implications.
lasers = world.options.shuffle_lasers
- early_caves = world.options.early_caves > 0
victory = world.options.victory_condition
mnt_lasers = world.options.mountain_lasers
chal_lasers = world.options.challenge_lasers
- mountain_enterable_from_top = victory == 0 or victory == 1 or (victory == 3 and chal_lasers > mnt_lasers)
-
+ # Exclude panels from the post-game if shuffle_postgame is false.
if not world.options.shuffle_postgame:
- if not (early_caves or doors):
- adjustment_linesets_in_order.append(get_caves_exclusion_list())
- if not victory == 1:
- adjustment_linesets_in_order.append(get_path_to_challenge_exclusion_list())
- adjustment_linesets_in_order.append(get_challenge_vault_box_exclusion_list())
- adjustment_linesets_in_order.append(get_beyond_challenge_exclusion_list())
-
- if not ((doors or early_caves) and (victory == 0 or (victory == 2 and mnt_lasers > chal_lasers))):
- adjustment_linesets_in_order.append(get_beyond_challenge_exclusion_list())
- if not victory == 1:
- adjustment_linesets_in_order.append(get_challenge_vault_box_exclusion_list())
-
- if not (doors or mountain_enterable_from_top):
- adjustment_linesets_in_order.append(get_mountain_lower_exclusion_list())
-
- if not mountain_enterable_from_top:
- adjustment_linesets_in_order.append(get_mountain_upper_exclusion_list())
-
- if not ((victory == 0 and doors) or victory == 1 or (victory == 2 and mnt_lasers > chal_lasers and doors)):
- if doors:
- adjustment_linesets_in_order.append(get_bottom_floor_discard_exclusion_list())
- else:
- adjustment_linesets_in_order.append(get_bottom_floor_discard_nondoors_exclusion_list())
-
- if victory == 2 and chal_lasers >= mnt_lasers:
- adjustment_linesets_in_order.append(["Disabled Locations:", "0xFFF00 (Mountain Box Long)"])
+ adjustment_linesets_in_order += self.handle_postgame(world)
# Exclude Discards / Vaults
-
if not world.options.shuffle_discarded_panels:
# In disable_non_randomized, the discards are needed for alternate activation triggers, UNLESS both
# (remote) doors and lasers are shuffled.
@@ -308,21 +363,24 @@ def make_options_adjustments(self, world: "WitnessWorld"):
if not world.options.shuffle_vault_boxes:
adjustment_linesets_in_order.append(get_vault_exclusion_list())
- if not victory == 1:
+ if not victory == "challenge":
adjustment_linesets_in_order.append(get_challenge_vault_box_exclusion_list())
# Victory Condition
- if victory == 0:
+ if victory == "elevator":
self.VICTORY_LOCATION = "0x3D9A9"
- elif victory == 1:
+ elif victory == "challenge":
self.VICTORY_LOCATION = "0x0356B"
- elif victory == 2:
+ elif victory == "mountain_box_short":
self.VICTORY_LOCATION = "0x09F7F"
- elif victory == 3:
+ elif victory == "mountain_box_long":
self.VICTORY_LOCATION = "0xFFF00"
- if chal_lasers <= 7:
+ # Long box can usually only be solved by opening Mountain Entry. However, if it requires 7 lasers or less
+ # (challenge_lasers <= 7), you can now solve it without opening Mountain Entry first.
+ # Furthermore, if the user sets mountain_lasers > 7, the box is rotated to not require Mountain Entry either.
+ if chal_lasers <= 7 or mnt_lasers > 7:
adjustment_linesets_in_order.append([
"Requirement Changes:",
"0xFFF00 - 11 Lasers - True",
@@ -334,36 +392,36 @@ def make_options_adjustments(self, world: "WitnessWorld"):
if world.options.shuffle_symbols:
adjustment_linesets_in_order.append(get_symbol_shuffle_list())
- if world.options.EP_difficulty == 0:
+ if world.options.EP_difficulty == "normal":
adjustment_linesets_in_order.append(get_ep_easy())
- elif world.options.EP_difficulty == 1:
+ elif world.options.EP_difficulty == "tedious":
adjustment_linesets_in_order.append(get_ep_no_eclipse())
- if world.options.door_groupings == 1:
- if world.options.shuffle_doors == 1:
+ if world.options.door_groupings == "regional":
+ if world.options.shuffle_doors == "panels":
adjustment_linesets_in_order.append(get_simple_panels())
- elif world.options.shuffle_doors == 2:
+ elif world.options.shuffle_doors == "doors":
adjustment_linesets_in_order.append(get_simple_doors())
- elif world.options.shuffle_doors == 3:
+ elif world.options.shuffle_doors == "mixed":
adjustment_linesets_in_order.append(get_simple_doors())
adjustment_linesets_in_order.append(get_simple_additional_panels())
else:
- if world.options.shuffle_doors == 1:
+ if world.options.shuffle_doors == "panels":
adjustment_linesets_in_order.append(get_complex_door_panels())
adjustment_linesets_in_order.append(get_complex_additional_panels())
- elif world.options.shuffle_doors == 2:
+ elif world.options.shuffle_doors == "doors":
adjustment_linesets_in_order.append(get_complex_doors())
- elif world.options.shuffle_doors == 3:
+ elif world.options.shuffle_doors == "mixed":
adjustment_linesets_in_order.append(get_complex_doors())
adjustment_linesets_in_order.append(get_complex_additional_panels())
if world.options.shuffle_boat:
adjustment_linesets_in_order.append(get_boat())
- if world.options.early_caves == 2:
+ if world.options.early_caves == "starting_inventory":
adjustment_linesets_in_order.append(get_early_caves_start_list())
- if world.options.early_caves == 1 and not doors:
+ if world.options.early_caves == "add_to_pool" and not doors:
adjustment_linesets_in_order.append(get_early_caves_list())
if world.options.elevators_come_to_you:
@@ -375,6 +433,9 @@ def make_options_adjustments(self, world: "WitnessWorld"):
if lasers:
adjustment_linesets_in_order.append(get_laser_shuffle())
+ if world.options.shuffle_EPs and world.options.obelisk_keys:
+ adjustment_linesets_in_order.append(get_obelisk_keys())
+
if world.options.shuffle_EPs == "obelisk_sides":
ep_gen = ((ep_hex, ep_obj) for (ep_hex, ep_obj) in self.REFERENCE_LOGIC.ENTITIES_BY_HEX.items()
if ep_obj["entityType"] == "EP")
@@ -383,14 +444,12 @@ def make_options_adjustments(self, world: "WitnessWorld"):
obelisk = self.REFERENCE_LOGIC.ENTITIES_BY_HEX[self.REFERENCE_LOGIC.EP_TO_OBELISK_SIDE[ep_hex]]
obelisk_name = obelisk["checkName"]
ep_name = self.REFERENCE_LOGIC.ENTITIES_BY_HEX[ep_hex]["checkName"]
- self.EVENT_NAMES_BY_HEX[ep_hex] = f"{obelisk_name} - {ep_name}"
+ self.ALWAYS_EVENT_NAMES_BY_HEX[ep_hex] = f"{obelisk_name} - {ep_name}"
else:
adjustment_linesets_in_order.append(["Disabled Locations:"] + get_ep_obelisks()[1:])
- if world.options.shuffle_EPs == 0:
- adjustment_linesets_in_order.append(["Irrelevant Locations:"] + get_ep_all_individual()[1:])
-
- yaml_disabled_eps = []
+ if not world.options.shuffle_EPs:
+ adjustment_linesets_in_order.append(["Disabled Locations:"] + get_ep_all_individual()[1:])
for yaml_disabled_location in self.YAML_DISABLED_LOCATIONS:
if yaml_disabled_location not in self.REFERENCE_LOGIC.ENTITIES_BY_NAME:
@@ -398,14 +457,12 @@ def make_options_adjustments(self, world: "WitnessWorld"):
loc_obj = self.REFERENCE_LOGIC.ENTITIES_BY_NAME[yaml_disabled_location]
- if loc_obj["entityType"] == "EP" and world.options.shuffle_EPs != 0:
- yaml_disabled_eps.append(loc_obj["entity_hex"])
+ if loc_obj["entityType"] == "EP":
+ self.COMPLETELY_DISABLED_ENTITIES.add(loc_obj["entity_hex"])
- if loc_obj["entityType"] in {"EP", "General", "Vault", "Discard"}:
+ elif loc_obj["entityType"] in {"General", "Vault", "Discard"}:
self.EXCLUDED_LOCATIONS.add(loc_obj["entity_hex"])
- adjustment_linesets_in_order.append(["Disabled Locations:"] + yaml_disabled_eps)
-
for adjustment_lineset in adjustment_linesets_in_order:
current_adjustment_type = None
@@ -455,7 +512,8 @@ def make_dependency_reduced_checklist(self):
for option in connection[1]:
individual_entity_requirements = []
for entity in option:
- if entity in self.EVENT_NAMES_BY_HEX or entity not in self.REFERENCE_LOGIC.ENTITIES_BY_HEX:
+ if (entity in self.ALWAYS_EVENT_NAMES_BY_HEX
+ or entity not in self.REFERENCE_LOGIC.ENTITIES_BY_HEX):
individual_entity_requirements.append(frozenset({frozenset({entity})}))
else:
entity_req = self.reduce_req_within_region(entity)
@@ -472,6 +530,72 @@ def make_dependency_reduced_checklist(self):
self.CONNECTIONS_BY_REGION_NAME[region] = new_connections
+ def solvability_guaranteed(self, entity_hex: str):
+ return not (
+ entity_hex in self.ENTITIES_WITHOUT_ENSURED_SOLVABILITY
+ or entity_hex in self.COMPLETELY_DISABLED_ENTITIES
+ or entity_hex in self.IRRELEVANT_BUT_NOT_DISABLED_ENTITIES
+ )
+
+ def determine_unrequired_entities(self, world: "WitnessWorld"):
+ """Figure out which major items are actually useless in this world's settings"""
+
+ # Gather quick references to relevant options
+ eps_shuffled = world.options.shuffle_EPs
+ come_to_you = world.options.elevators_come_to_you
+ difficulty = world.options.puzzle_randomization
+ discards_shuffled = world.options.shuffle_discarded_panels
+ boat_shuffled = world.options.shuffle_boat
+ symbols_shuffled = world.options.shuffle_symbols
+ disable_non_randomized = world.options.disable_non_randomized_puzzles
+ postgame_included = world.options.shuffle_postgame
+ goal = world.options.victory_condition
+ doors = world.options.shuffle_doors
+ shortbox_req = world.options.mountain_lasers
+ longbox_req = world.options.challenge_lasers
+
+ # Make some helper booleans so it is easier to follow what's going on
+ mountain_upper_is_in_postgame = (
+ goal == "mountain_box_short"
+ or goal == "mountain_box_long" and longbox_req <= shortbox_req
+ )
+ mountain_upper_included = postgame_included or not mountain_upper_is_in_postgame
+ remote_doors = doors >= 2
+ door_panels = doors == "panels" or doors == "mixed"
+
+ # It is easier to think about when these items *are* required, so we make that dict first
+ # If the entity is disabled anyway, we don't need to consider that case
+ is_item_required_dict = {
+ "0x03750": eps_shuffled, # Monastery Garden Entry Door
+ "0x275FA": eps_shuffled, # Boathouse Hook Control
+ "0x17D02": eps_shuffled, # Windmill Turn Control
+ "0x0368A": symbols_shuffled or door_panels, # Quarry Stoneworks Stairs Door
+ "0x3865F": symbols_shuffled or door_panels or eps_shuffled, # Quarry Boathouse 2nd Barrier
+ "0x17CC4": come_to_you or eps_shuffled, # Quarry Elevator Panel
+ "0x17E2B": come_to_you and boat_shuffled or eps_shuffled, # Swamp Long Bridge
+ "0x0CF2A": False, # Jungle Monastery Garden Shortcut
+ "0x17CAA": remote_doors, # Jungle Monastery Garden Shortcut Panel
+ "0x0364E": False, # Monastery Laser Shortcut Door
+ "0x03713": remote_doors, # Monastery Laser Shortcut Panel
+ "0x03313": False, # Orchard Second Gate
+ "0x337FA": remote_doors, # Jungle Bamboo Laser Shortcut Panel
+ "0x3873B": False, # Jungle Bamboo Laser Shortcut Door
+ "0x335AB": False, # Caves Elevator Controls
+ "0x335AC": False, # Caves Elevator Controls
+ "0x3369D": False, # Caves Elevator Controls
+ "0x01BEA": difficulty == "none" and eps_shuffled, # Keep PP2
+ "0x0A0C9": eps_shuffled or discards_shuffled or disable_non_randomized, # Cargo Box Entry Door
+ "0x09EEB": discards_shuffled or mountain_upper_included, # Mountain Floor 2 Elevator Control Panel
+ "0x09EDD": mountain_upper_included, # Mountain Floor 2 Exit Door
+ "0x17CAB": symbols_shuffled or not disable_non_randomized or "0x17CAB" not in self.DOOR_ITEMS_BY_ID,
+ # Jungle Popup Wall Panel
+ }
+
+ # Now, return the keys of the dict entries where the result is False to get unrequired major items
+ self.ENTITIES_WITHOUT_ENSURED_SOLVABILITY |= {
+ item_name for item_name, is_required in is_item_required_dict.items() if not is_required
+ }
+
def make_event_item_pair(self, panel: str):
"""
Makes a pair of an event panel and its event item
@@ -479,21 +603,23 @@ def make_event_item_pair(self, panel: str):
action = " Opened" if self.REFERENCE_LOGIC.ENTITIES_BY_HEX[panel]["entityType"] == "Door" else " Solved"
name = self.REFERENCE_LOGIC.ENTITIES_BY_HEX[panel]["checkName"] + action
- if panel not in self.EVENT_NAMES_BY_HEX:
+ if panel not in self.USED_EVENT_NAMES_BY_HEX:
warning("Panel \"" + name + "\" does not have an associated event name.")
- self.EVENT_NAMES_BY_HEX[panel] = name + " Event"
- pair = (name, self.EVENT_NAMES_BY_HEX[panel])
+ self.USED_EVENT_NAMES_BY_HEX[panel] = name + " Event"
+ pair = (name, self.USED_EVENT_NAMES_BY_HEX[panel])
return pair
def make_event_panel_lists(self):
- self.EVENT_NAMES_BY_HEX[self.VICTORY_LOCATION] = "Victory"
+ self.ALWAYS_EVENT_NAMES_BY_HEX[self.VICTORY_LOCATION] = "Victory"
- for event_hex, event_name in self.EVENT_NAMES_BY_HEX.items():
- if event_hex in self.COMPLETELY_DISABLED_ENTITIES or event_hex in self.IRRELEVANT_BUT_NOT_DISABLED_ENTITIES:
- continue
- self.EVENT_PANELS.add(event_hex)
+ self.USED_EVENT_NAMES_BY_HEX.update(self.ALWAYS_EVENT_NAMES_BY_HEX)
- for panel in self.EVENT_PANELS:
+ self.USED_EVENT_NAMES_BY_HEX = {
+ event_hex: event_name for event_hex, event_name in self.USED_EVENT_NAMES_BY_HEX.items()
+ if self.solvability_guaranteed(event_hex)
+ }
+
+ for panel in self.USED_EVENT_NAMES_BY_HEX:
pair = self.make_event_item_pair(panel)
self.EVENT_ITEM_PAIRS[pair[0]] = pair[1]
@@ -506,6 +632,8 @@ def __init__(self, world: "WitnessWorld", disabled_locations: Set[str], start_in
self.IRRELEVANT_BUT_NOT_DISABLED_ENTITIES = set()
+ self.ENTITIES_WITHOUT_ENSURED_SOLVABILITY = set()
+
self.THEORETICAL_ITEMS = set()
self.THEORETICAL_ITEMS_NO_MULTI = set()
self.MULTI_AMOUNTS = defaultdict(lambda: 1)
@@ -515,31 +643,29 @@ def __init__(self, world: "WitnessWorld", disabled_locations: Set[str], start_in
self.DOOR_ITEMS_BY_ID: Dict[str, List[str]] = {}
self.STARTING_INVENTORY = set()
- self.DIFFICULTY = world.options.puzzle_randomization.value
+ self.DIFFICULTY = world.options.puzzle_randomization
- if self.DIFFICULTY == 0:
+ if self.DIFFICULTY == "sigma_normal":
self.REFERENCE_LOGIC = StaticWitnessLogic.sigma_normal
- elif self.DIFFICULTY == 1:
+ elif self.DIFFICULTY == "sigma_expert":
self.REFERENCE_LOGIC = StaticWitnessLogic.sigma_expert
- elif self.DIFFICULTY == 2:
+ elif self.DIFFICULTY == "none":
self.REFERENCE_LOGIC = StaticWitnessLogic.vanilla
- self.CONNECTIONS_BY_REGION_NAME = copy.copy(self.REFERENCE_LOGIC.STATIC_CONNECTIONS_BY_REGION_NAME)
- self.DEPENDENT_REQUIREMENTS_BY_HEX = copy.copy(self.REFERENCE_LOGIC.STATIC_DEPENDENT_REQUIREMENTS_BY_HEX)
+ self.CONNECTIONS_BY_REGION_NAME = copy.deepcopy(self.REFERENCE_LOGIC.STATIC_CONNECTIONS_BY_REGION_NAME)
+ self.DEPENDENT_REQUIREMENTS_BY_HEX = copy.deepcopy(self.REFERENCE_LOGIC.STATIC_DEPENDENT_REQUIREMENTS_BY_HEX)
self.REQUIREMENTS_BY_HEX = dict()
# Determining which panels need to be events is a difficult process.
# At the end, we will have EVENT_ITEM_PAIRS for all the necessary ones.
- self.EVENT_PANELS = set()
self.EVENT_ITEM_PAIRS = dict()
- self.DONT_MAKE_EVENTS = set()
self.COMPLETELY_DISABLED_ENTITIES = set()
self.PRECOMPLETED_LOCATIONS = set()
self.EXCLUDED_LOCATIONS = set()
self.ADDED_CHECKS = set()
self.VICTORY_LOCATION = "0x0356B"
- self.EVENT_NAMES_BY_HEX = {
+ self.ALWAYS_EVENT_NAMES_BY_HEX = {
"0x00509": "+1 Laser (Symmetry Laser)",
"0x012FB": "+1 Laser (Desert Laser)",
"0x09F98": "Desert Laser Redirection",
@@ -552,10 +678,14 @@ def __init__(self, world: "WitnessWorld", disabled_locations: Set[str], start_in
"0x0C2B2": "+1 Laser (Bunker Laser)",
"0x00BF6": "+1 Laser (Swamp Laser)",
"0x028A4": "+1 Laser (Treehouse Laser)",
- "0x09F7F": "Mountain Entry",
+ "0x17C34": "Mountain Entry",
"0xFFF00": "Bottom Floor Discard Turns On",
}
+ self.USED_EVENT_NAMES_BY_HEX = {}
+ self.CONDITIONAL_EVENTS = {}
+
self.make_options_adjustments(world)
+ self.determine_unrequired_entities(world)
self.make_dependency_reduced_checklist()
self.make_event_panel_lists()
diff --git a/worlds/witness/presets.py b/worlds/witness/presets.py
new file mode 100644
index 000000000000..2a53484a4c77
--- /dev/null
+++ b/worlds/witness/presets.py
@@ -0,0 +1,113 @@
+from typing import Any, Dict
+
+from .options import *
+
+witness_option_presets: Dict[str, Dict[str, Any]] = {
+ # Great for short syncs & scratching that "speedrun with light routing elements" itch.
+ "Short & Dense": {
+ "progression_balancing": 30,
+
+ "puzzle_randomization": PuzzleRandomization.option_sigma_normal,
+
+ "shuffle_symbols": False,
+ "shuffle_doors": ShuffleDoors.option_panels,
+ "door_groupings": DoorGroupings.option_off,
+ "shuffle_boat": True,
+ "shuffle_lasers": ShuffleLasers.option_local,
+ "obelisk_keys": ObeliskKeys.option_false,
+
+ "disable_non_randomized_puzzles": True,
+ "shuffle_discarded_panels": False,
+ "shuffle_vault_boxes": False,
+ "shuffle_EPs": ShuffleEnvironmentalPuzzles.option_off,
+ "EP_difficulty": EnvironmentalPuzzlesDifficulty.option_normal,
+ "shuffle_postgame": False,
+
+ "victory_condition": VictoryCondition.option_mountain_box_short,
+ "mountain_lasers": 7,
+ "challenge_lasers": 11,
+
+ "early_caves": EarlyCaves.option_off,
+ "elevators_come_to_you": False,
+
+ "trap_percentage": TrapPercentage.default,
+ "puzzle_skip_amount": PuzzleSkipAmount.default,
+ "hint_amount": HintAmount.default,
+ "area_hint_percentage": AreaHintPercentage.default,
+ "laser_hints": LaserHints.default,
+ "death_link": DeathLink.default,
+ "death_link_amnesty": DeathLinkAmnesty.default,
+ },
+
+ # For relative beginners who want to move to the next step.
+ "Advanced, But Chill": {
+ "progression_balancing": 30,
+
+ "puzzle_randomization": PuzzleRandomization.option_sigma_normal,
+
+ "shuffle_symbols": True,
+ "shuffle_doors": ShuffleDoors.option_doors,
+ "door_groupings": DoorGroupings.option_regional,
+ "shuffle_boat": True,
+ "shuffle_lasers": ShuffleLasers.option_off,
+ "obelisk_keys": ObeliskKeys.option_false,
+
+ "disable_non_randomized_puzzles": False,
+ "shuffle_discarded_panels": True,
+ "shuffle_vault_boxes": True,
+ "shuffle_EPs": ShuffleEnvironmentalPuzzles.option_obelisk_sides,
+ "EP_difficulty": EnvironmentalPuzzlesDifficulty.option_normal,
+ "shuffle_postgame": False,
+
+ "victory_condition": VictoryCondition.option_mountain_box_long,
+ "mountain_lasers": 6,
+ "challenge_lasers": 9,
+
+ "early_caves": EarlyCaves.option_off,
+ "elevators_come_to_you": False,
+
+ "trap_percentage": TrapPercentage.default,
+ "puzzle_skip_amount": 15,
+ "hint_amount": HintAmount.default,
+ "area_hint_percentage": AreaHintPercentage.default,
+ "laser_hints": LaserHints.default,
+ "death_link": DeathLink.default,
+ "death_link_amnesty": DeathLinkAmnesty.default,
+ },
+
+ # Allsanity but without the BS (no expert, no tedious EPs).
+ "Nice Allsanity": {
+ "progression_balancing": 50,
+
+ "puzzle_randomization": PuzzleRandomization.option_sigma_normal,
+
+ "shuffle_symbols": True,
+ "shuffle_doors": ShuffleDoors.option_mixed,
+ "door_groupings": DoorGroupings.option_off,
+ "shuffle_boat": True,
+ "shuffle_lasers": ShuffleLasers.option_anywhere,
+ "obelisk_keys": ObeliskKeys.option_true,
+
+ "disable_non_randomized_puzzles": False,
+ "shuffle_discarded_panels": True,
+ "shuffle_vault_boxes": True,
+ "shuffle_EPs": ShuffleEnvironmentalPuzzles.option_individual,
+ "EP_difficulty": EnvironmentalPuzzlesDifficulty.option_normal,
+ "shuffle_postgame": False,
+
+ "victory_condition": VictoryCondition.option_challenge,
+ "mountain_lasers": 6,
+ "challenge_lasers": 9,
+
+ "early_caves": EarlyCaves.option_off,
+ "elevators_come_to_you": True,
+
+ "trap_percentage": TrapPercentage.default,
+ "puzzle_skip_amount": 15,
+ "hint_amount": HintAmount.default,
+ "area_hint_percentage": AreaHintPercentage.default,
+ "laser_hints": LaserHints.default,
+ "death_link": DeathLink.default,
+ "death_link_amnesty": DeathLinkAmnesty.default,
+ },
+}
diff --git a/worlds/witness/regions.py b/worlds/witness/regions.py
index e09702480515..350017c6943a 100644
--- a/worlds/witness/regions.py
+++ b/worlds/witness/regions.py
@@ -129,19 +129,20 @@ def create_regions(self, world: "WitnessWorld", player_logic: WitnessPlayerLogic
regions_to_check.add(target.name)
reachable_regions.add(target.name)
- final_regions_list = [v for k, v in regions_by_name.items() if k in reachable_regions]
+ self.created_regions = {k: v for k, v in regions_by_name.items() if k in reachable_regions}
- world.multiworld.regions += final_regions_list
+ world.multiworld.regions += self.created_regions.values()
def __init__(self, locat: WitnessPlayerLocations, world: "WitnessWorld"):
- difficulty = world.options.puzzle_randomization.value
+ difficulty = world.options.puzzle_randomization
- if difficulty == 0:
+ if difficulty == "sigma_normal":
self.reference_logic = StaticWitnessLogic.sigma_normal
- elif difficulty == 1:
+ elif difficulty == "sigma_expert":
self.reference_logic = StaticWitnessLogic.sigma_expert
- elif difficulty == 2:
+ elif difficulty == "none":
self.reference_logic = StaticWitnessLogic.vanilla
self.locat = locat
self.created_entrances: Dict[Tuple[str, str], List[Entrance]] = KeyedDefaultDict(lambda _: [])
+ self.created_regions: Dict[str, Region] = dict()
diff --git a/worlds/witness/rules.py b/worlds/witness/rules.py
index 75c662ac0f26..8636829a4ef1 100644
--- a/worlds/witness/rules.py
+++ b/worlds/witness/rules.py
@@ -29,8 +29,9 @@
]
-def _has_laser(laser_hex: str, world: "WitnessWorld", player: int) -> Callable[[CollectionState], bool]:
- if laser_hex == "0x012FB":
+def _has_laser(laser_hex: str, world: "WitnessWorld", player: int,
+ redirect_required: bool) -> Callable[[CollectionState], bool]:
+ if laser_hex == "0x012FB" and redirect_required:
return lambda state: (
_can_solve_panel(laser_hex, world, world.player, world.player_logic, world.locat)(state)
and state.has("Desert Laser Redirection", player)
@@ -39,11 +40,11 @@ def _has_laser(laser_hex: str, world: "WitnessWorld", player: int) -> Callable[[
return _can_solve_panel(laser_hex, world, world.player, world.player_logic, world.locat)
-def _has_lasers(amount: int, world: "WitnessWorld") -> Callable[[CollectionState], bool]:
+def _has_lasers(amount: int, world: "WitnessWorld", redirect_required: bool) -> Callable[[CollectionState], bool]:
laser_lambdas = []
for laser_hex in laser_hexes:
- has_laser_lambda = _has_laser(laser_hex, world, world.player)
+ has_laser_lambda = _has_laser(laser_hex, world, world.player, redirect_required)
laser_lambdas.append(has_laser_lambda)
@@ -155,15 +156,21 @@ def _has_item(item: str, world: "WitnessWorld", player: int,
return lambda state: state.can_reach(item, "Region", player)
if item == "7 Lasers":
laser_req = world.options.mountain_lasers.value
- return _has_lasers(laser_req, world)
+ return _has_lasers(laser_req, world, False)
+ if item == "7 Lasers + Redirect":
+ laser_req = world.options.mountain_lasers.value
+ return _has_lasers(laser_req, world, True)
if item == "11 Lasers":
laser_req = world.options.challenge_lasers.value
- return _has_lasers(laser_req, world)
+ return _has_lasers(laser_req, world, False)
+ if item == "11 Lasers + Redirect":
+ laser_req = world.options.challenge_lasers.value
+ return _has_lasers(laser_req, world, True)
elif item == "PP2 Weirdness":
return lambda state: _can_do_expert_pp2(state, world)
elif item == "Theater to Tunnels":
return lambda state: _can_do_theater_to_tunnels(state, world)
- if item in player_logic.EVENT_PANELS:
+ if item in player_logic.USED_EVENT_NAMES_BY_HEX:
return _can_solve_panel(item, world, player, player_logic, locat)
prog_item = StaticWitnessLogic.get_parent_progressive_item(item)
diff --git a/worlds/witness/settings/Door_Shuffle/Complex_Additional_Panels.txt b/worlds/witness/settings/Door_Shuffle/Complex_Additional_Panels.txt
index 79bda7ea2281..b84370908524 100644
--- a/worlds/witness/settings/Door_Shuffle/Complex_Additional_Panels.txt
+++ b/worlds/witness/settings/Door_Shuffle/Complex_Additional_Panels.txt
@@ -1,4 +1,7 @@
Items:
+Desert Surface 3 Control (Panel)
+Desert Surface 8 Control (Panel)
+Desert Elevator Room Hexagonal Control (Panel)
Desert Flood Controls (Panel)
Desert Light Control (Panel)
Quarry Elevator Control (Panel)
@@ -10,6 +13,7 @@ Quarry Boathouse Hook Control (Panel)
Monastery Shutters Control (Panel)
Town Maze Rooftop Bridge (Panel)
Town RGB Control (Panel)
+Town Desert Laser Redirect Control (Panel)
Windmill Turn Control (Panel)
Theater Video Input (Panel)
Bunker Drop-Down Door Controls (Panel)
diff --git a/worlds/witness/settings/Door_Shuffle/Complex_Door_Panels.txt b/worlds/witness/settings/Door_Shuffle/Complex_Door_Panels.txt
index 472403962065..70223bd74924 100644
--- a/worlds/witness/settings/Door_Shuffle/Complex_Door_Panels.txt
+++ b/worlds/witness/settings/Door_Shuffle/Complex_Door_Panels.txt
@@ -1,7 +1,7 @@
Items:
Glass Factory Entry (Panel)
-Tutorial Outpost Entry (Panel)
-Tutorial Outpost Exit (Panel)
+Outside Tutorial Outpost Entry (Panel)
+Outside Tutorial Outpost Exit (Panel)
Symmetry Island Lower (Panel)
Symmetry Island Upper (Panel)
Desert Light Room Entry (Panel)
diff --git a/worlds/witness/settings/Door_Shuffle/Complex_Doors.txt b/worlds/witness/settings/Door_Shuffle/Complex_Doors.txt
index 2f2b32171079..87ec69f59c81 100644
--- a/worlds/witness/settings/Door_Shuffle/Complex_Doors.txt
+++ b/worlds/witness/settings/Door_Shuffle/Complex_Doors.txt
@@ -58,9 +58,9 @@ Town Tower Third (Door)
Theater Entry (Door)
Theater Exit Left (Door)
Theater Exit Right (Door)
-Jungle Bamboo Laser Shortcut (Door)
+Jungle Laser Shortcut (Door)
Jungle Popup Wall (Door)
-River Monastery Garden Shortcut (Door)
+Jungle Monastery Garden Shortcut (Door)
Bunker Entry (Door)
Bunker Tinted Glass Door
Bunker UV Room Entry (Door)
@@ -85,7 +85,7 @@ Mountain Floor 2 Staircase Near (Door)
Mountain Floor 2 Exit (Door)
Mountain Floor 2 Staircase Far (Door)
Mountain Bottom Floor Giant Puzzle Exit (Door)
-Mountain Bottom Floor Final Room Entry (Door)
+Mountain Bottom Floor Pillars Room Entry (Door)
Mountain Bottom Floor Rock (Door)
Caves Entry (Door)
Caves Pillar Door
@@ -143,8 +143,8 @@ Town Wooden Roof Lower Row 5
Town RGB House Entry Panel
Town Church Entry Panel
Town Maze Panel
-Town Windmill Entry Panel
-Town Sound Room Right
+Windmill Entry Panel
+Town RGB House Sound Room Right
Town Red Rooftop 5
Town Church Lattice
Town Tall Hexagonal
@@ -154,7 +154,7 @@ Theater Exit Left Panel
Theater Exit Right Panel
Jungle Laser Shortcut Panel
Jungle Popup Wall Control
-River Monastery Garden Shortcut Panel
+Jungle Monastery Garden Shortcut Panel
Bunker Entry Panel
Bunker Tinted Glass Door Panel
Bunker Glass Room 3
@@ -186,8 +186,8 @@ Mountain Floor 2 Light Bridge Controller Near
Mountain Floor 2 Light Bridge Controller Far
Mountain Floor 2 Far Row 6
Mountain Bottom Floor Giant Puzzle
-Mountain Bottom Floor Final Room Entry Left
-Mountain Bottom Floor Final Room Entry Right
+Mountain Bottom Floor Pillars Room Entry Left
+Mountain Bottom Floor Pillars Room Entry Right
Mountain Bottom Floor Discard
Mountain Bottom Floor Rock Control
Mountain Bottom Floor Caves Entry Panel
diff --git a/worlds/witness/settings/Door_Shuffle/Obelisk_Keys.txt b/worlds/witness/settings/Door_Shuffle/Obelisk_Keys.txt
new file mode 100644
index 000000000000..9ebcc9fa2ffa
--- /dev/null
+++ b/worlds/witness/settings/Door_Shuffle/Obelisk_Keys.txt
@@ -0,0 +1,7 @@
+Items:
+Desert Obelisk Key
+Monastery Obelisk Key
+Treehouse Obelisk Key
+Mountainside Obelisk Key
+Quarry Obelisk Key
+Town Obelisk Key
\ No newline at end of file
diff --git a/worlds/witness/settings/Door_Shuffle/Simple_Doors.txt b/worlds/witness/settings/Door_Shuffle/Simple_Doors.txt
index 91a7132ec113..2059f43af62c 100644
--- a/worlds/witness/settings/Door_Shuffle/Simple_Doors.txt
+++ b/worlds/witness/settings/Door_Shuffle/Simple_Doors.txt
@@ -76,8 +76,8 @@ Town Wooden Roof Lower Row 5
Town RGB House Entry Panel
Town Church Entry Panel
Town Maze Panel
-Town Windmill Entry Panel
-Town Sound Room Right
+Windmill Entry Panel
+Town RGB House Sound Room Right
Town Red Rooftop 5
Town Church Lattice
Town Tall Hexagonal
@@ -87,7 +87,7 @@ Theater Exit Left Panel
Theater Exit Right Panel
Jungle Laser Shortcut Panel
Jungle Popup Wall Control
-River Monastery Garden Shortcut Panel
+Jungle Monastery Garden Shortcut Panel
Bunker Entry Panel
Bunker Tinted Glass Door Panel
Bunker Glass Room 3
@@ -119,8 +119,8 @@ Mountain Floor 2 Light Bridge Controller Near
Mountain Floor 2 Light Bridge Controller Far
Mountain Floor 2 Far Row 6
Mountain Bottom Floor Giant Puzzle
-Mountain Bottom Floor Final Room Entry Left
-Mountain Bottom Floor Final Room Entry Right
+Mountain Bottom Floor Pillars Room Entry Left
+Mountain Bottom Floor Pillars Room Entry Right
Mountain Bottom Floor Discard
Mountain Bottom Floor Rock Control
Mountain Bottom Floor Caves Entry Panel
diff --git a/worlds/witness/settings/Door_Shuffle/Simple_Panels.txt b/worlds/witness/settings/Door_Shuffle/Simple_Panels.txt
index 79da154491b7..23501d20d3a7 100644
--- a/worlds/witness/settings/Door_Shuffle/Simple_Panels.txt
+++ b/worlds/witness/settings/Door_Shuffle/Simple_Panels.txt
@@ -1,6 +1,6 @@
Items:
Symmetry Island Panels
-Tutorial Outpost Panels
+Outside Tutorial Outpost Panels
Desert Panels
Quarry Outside Panels
Quarry Stoneworks Panels
@@ -10,7 +10,7 @@ Monastery Panels
Town Church & RGB House Panels
Town Maze Panels
Windmill & Theater Panels
-Town Cargo Box Entry (Panel)
+Town Dockside House Panels
Treehouse Panels
Bunker Panels
Swamp Panels
diff --git a/worlds/witness/settings/EP_Shuffle/EP_Sides.txt b/worlds/witness/settings/EP_Shuffle/EP_Sides.txt
index 82ab63329500..d561ffdc183f 100644
--- a/worlds/witness/settings/EP_Shuffle/EP_Sides.txt
+++ b/worlds/witness/settings/EP_Shuffle/EP_Sides.txt
@@ -16,12 +16,12 @@ Added Locations:
0xFFE23 (Treehouse Obelisk Side 4)
0xFFE24 (Treehouse Obelisk Side 5)
0xFFE25 (Treehouse Obelisk Side 6)
-0xFFE30 (River Obelisk Side 1)
-0xFFE31 (River Obelisk Side 2)
-0xFFE32 (River Obelisk Side 3)
-0xFFE33 (River Obelisk Side 4)
-0xFFE34 (River Obelisk Side 5)
-0xFFE35 (River Obelisk Side 6)
+0xFFE30 (Mountainside Obelisk Side 1)
+0xFFE31 (Mountainside Obelisk Side 2)
+0xFFE32 (Mountainside Obelisk Side 3)
+0xFFE33 (Mountainside Obelisk Side 4)
+0xFFE34 (Mountainside Obelisk Side 5)
+0xFFE35 (Mountainside Obelisk Side 6)
0xFFE40 (Quarry Obelisk Side 1)
0xFFE41 (Quarry Obelisk Side 2)
0xFFE42 (Quarry Obelisk Side 3)
diff --git a/worlds/witness/settings/Exclusions/Disable_Unrandomized.txt b/worlds/witness/settings/Exclusions/Disable_Unrandomized.txt
index 2419bde06c14..09c366cfaabd 100644
--- a/worlds/witness/settings/Exclusions/Disable_Unrandomized.txt
+++ b/worlds/witness/settings/Exclusions/Disable_Unrandomized.txt
@@ -1,9 +1,9 @@
Event Items:
-Monastery Laser Activation - 0x00A5B,0x17CE7,0x17FA9
-Bunker Laser Activation - 0x00061,0x17D01,0x17C42
-Shadows Laser Activation - 0x00021,0x17D28,0x17C71
-Town Tower 4th Door Opens - 0x17CFB,0x3C12B,0x17CF7
-Jungle Popup Wall Lifts - 0x17FA0,0x17D27,0x17F9B,0x17CAB
+Monastery Laser Activation - 0x17C65 - 0x00A5B,0x17CE7,0x17FA9
+Bunker Laser Activation - 0x0C2B2 - 0x00061,0x17D01,0x17C42
+Shadows Laser Activation - 0x181B3 - 0x00021,0x17D28,0x17C71
+Town Tower 4th Door Opens - 0x2779A - 0x17CFB,0x3C12B,0x17CF7
+Jungle Popup Wall Lifts - 0x1475B - 0x17FA0,0x17D27,0x17F9B,0x17CAB
Requirement Changes:
0x17C65 - 0x00A5B | 0x17CE7 | 0x17FA9
diff --git a/worlds/witness/settings/Exclusions/Vaults.txt b/worlds/witness/settings/Exclusions/Vaults.txt
index f23a13183326..d9e5d28cd694 100644
--- a/worlds/witness/settings/Exclusions/Vaults.txt
+++ b/worlds/witness/settings/Exclusions/Vaults.txt
@@ -8,9 +8,9 @@ Disabled Locations:
0x00AFB (Shipwreck Vault)
0x03535 (Shipwreck Vault Box)
0x17BB4 (Shipwreck Vault Door)
-0x15ADD (River Vault)
-0x03702 (River Vault Box)
-0x15287 (River Vault Door)
+0x15ADD (Jungle Vault)
+0x03702 (Jungle Vault Box)
+0x15287 (Jungle Vault Door)
0x002A6 (Mountainside Vault)
0x03542 (Mountainside Vault Box)
0x00085 (Mountainside Vault Door)
diff --git a/worlds/witness/settings/Postgame/Mountain_Lower.txt b/worlds/witness/settings/Postgame/Mountain_Lower.txt
index 354e3feb82c3..aecddec5adde 100644
--- a/worlds/witness/settings/Postgame/Mountain_Lower.txt
+++ b/worlds/witness/settings/Postgame/Mountain_Lower.txt
@@ -7,9 +7,9 @@ Disabled Locations:
0x09EFF (Giant Puzzle Top Left)
0x09FDA (Giant Puzzle)
0x09F89 (Exit Door)
-0x01983 (Final Room Entry Left)
-0x01987 (Final Room Entry Right)
-0x0C141 (Final Room Entry Door)
+0x01983 (Pillars Room Entry Left)
+0x01987 (Pillars Room Entry Right)
+0x0C141 (Pillars Room Entry Door)
0x0383A (Right Pillar 1)
0x09E56 (Right Pillar 2)
0x09E5A (Right Pillar 3)
diff --git a/worlds/witness/settings/Symbol_Shuffle.txt b/worlds/witness/settings/Symbol_Shuffle.txt
index 3d0342f5e2a9..253fe98bad42 100644
--- a/worlds/witness/settings/Symbol_Shuffle.txt
+++ b/worlds/witness/settings/Symbol_Shuffle.txt
@@ -1,9 +1,8 @@
Items:
Arrows
Progressive Dots
-Colored Dots
Sound Dots
-Symmetry
+Progressive Symmetry
Triangles
Eraser
Shapers
diff --git a/worlds/witness/static_logic.py b/worlds/witness/static_logic.py
index 29c171d45c33..3efab4915e69 100644
--- a/worlds/witness/static_logic.py
+++ b/worlds/witness/static_logic.py
@@ -56,6 +56,11 @@ def read_logic_file(self, lines):
"""
current_region = dict()
+ current_area = {
+ "name": "Misc",
+ "regions": [],
+ }
+ self.ALL_AREAS_BY_NAME["Misc"] = current_area
for line in lines:
if line == "" or line[0] == "#":
@@ -67,6 +72,16 @@ def read_logic_file(self, lines):
region_name = current_region["name"]
self.ALL_REGIONS_BY_NAME[region_name] = current_region
self.STATIC_CONNECTIONS_BY_REGION_NAME[region_name] = new_region_and_connections[1]
+ current_area["regions"].append(region_name)
+ continue
+
+ if line[0] == "=":
+ area_name = line[2:-2]
+ current_area = {
+ "name": area_name,
+ "regions": [],
+ }
+ self.ALL_AREAS_BY_NAME[area_name] = current_area
continue
line_split = line.split(" - ")
@@ -88,7 +103,8 @@ def read_logic_file(self, lines):
"entity_hex": entity_hex,
"region": None,
"id": None,
- "entityType": location_id
+ "entityType": location_id,
+ "area": current_area,
}
self.ENTITIES_BY_NAME[self.ENTITIES_BY_HEX[entity_hex]["checkName"]] = self.ENTITIES_BY_HEX[entity_hex]
@@ -109,7 +125,6 @@ def read_logic_file(self, lines):
"Laser",
"Laser Hedges",
"Laser Pressure Plates",
- "Desert Laser Redirect"
}
is_vault_or_video = "Vault" in entity_name or "Video" in entity_name
@@ -121,7 +136,6 @@ def read_logic_file(self, lines):
location_type = "Laser"
elif "Obelisk Side" in entity_name:
location_type = "Obelisk Side"
- full_entity_name = entity_name
elif "EP" in entity_name:
location_type = "EP"
else:
@@ -152,7 +166,8 @@ def read_logic_file(self, lines):
"entity_hex": entity_hex,
"region": current_region,
"id": int(location_id),
- "entityType": location_type
+ "entityType": location_type,
+ "area": current_area,
}
self.ENTITY_ID_TO_NAME[entity_hex] = full_entity_name
@@ -168,6 +183,7 @@ def __init__(self, lines=None):
# All regions with a list of panels in them and the connections to other regions, before logic adjustments
self.ALL_REGIONS_BY_NAME = dict()
+ self.ALL_AREAS_BY_NAME = dict()
self.STATIC_CONNECTIONS_BY_REGION_NAME = dict()
self.ENTITIES_BY_HEX = dict()
@@ -189,6 +205,7 @@ class StaticWitnessLogic:
_progressive_lookup: Dict[str, str] = {}
ALL_REGIONS_BY_NAME = dict()
+ ALL_AREAS_BY_NAME = dict()
STATIC_CONNECTIONS_BY_REGION_NAME = dict()
OBELISK_SIDE_ID_TO_EP_HEXES = dict()
@@ -266,6 +283,7 @@ def __init__(self):
self.parse_items()
self.ALL_REGIONS_BY_NAME.update(self.sigma_normal.ALL_REGIONS_BY_NAME)
+ self.ALL_AREAS_BY_NAME.update(self.sigma_normal.ALL_AREAS_BY_NAME)
self.STATIC_CONNECTIONS_BY_REGION_NAME.update(self.sigma_normal.STATIC_CONNECTIONS_BY_REGION_NAME)
self.ENTITIES_BY_HEX.update(self.sigma_normal.ENTITIES_BY_HEX)
@@ -277,3 +295,6 @@ def __init__(self):
self.EP_TO_OBELISK_SIDE.update(self.sigma_normal.EP_TO_OBELISK_SIDE)
self.ENTITY_ID_TO_NAME.update(self.sigma_normal.ENTITY_ID_TO_NAME)
+
+
+StaticWitnessLogic()
diff --git a/worlds/witness/utils.py b/worlds/witness/utils.py
index fbb670fd0877..43e039475d80 100644
--- a/worlds/witness/utils.py
+++ b/worlds/witness/utils.py
@@ -2,6 +2,21 @@
from math import floor
from typing import List, Collection, FrozenSet, Tuple, Dict, Any, Set
from pkgutil import get_data
+from random import random
+
+
+def weighted_sample(world_random: random, population: List, weights: List[float], k: int):
+ positions = range(len(population))
+ indices = []
+ while True:
+ needed = k - len(indices)
+ if not needed:
+ break
+ for i in world_random.choices(positions, weights, k=needed):
+ if weights[i]:
+ weights[i] = 0.0
+ indices.append(i)
+ return [population[i] for i in indices]
def build_weighted_int_list(inputs: Collection[float], total: int) -> List[int]:
@@ -162,6 +177,10 @@ def get_ep_obelisks() -> List[str]:
return get_adjustment_file("settings/EP_Shuffle/EP_Sides.txt")
+def get_obelisk_keys() -> List[str]:
+ return get_adjustment_file("settings/Door_Shuffle/Obelisk_Keys.txt")
+
+
def get_ep_easy() -> List[str]:
return get_adjustment_file("settings/EP_Shuffle/EP_Easy.txt")
diff --git a/worlds/yoshisisland/Client.py b/worlds/yoshisisland/Client.py
new file mode 100644
index 000000000000..1aff36c553c7
--- /dev/null
+++ b/worlds/yoshisisland/Client.py
@@ -0,0 +1,145 @@
+import logging
+import struct
+import typing
+import time
+from struct import pack
+
+from NetUtils import ClientStatus, color
+from worlds.AutoSNIClient import SNIClient
+
+if typing.TYPE_CHECKING:
+ from SNIClient import SNIContext
+
+snes_logger = logging.getLogger("SNES")
+
+ROM_START = 0x000000
+WRAM_START = 0xF50000
+WRAM_SIZE = 0x20000
+SRAM_START = 0xE00000
+
+YOSHISISLAND_ROMHASH_START = 0x007FC0
+ROMHASH_SIZE = 0x15
+
+ITEMQUEUE_HIGH = WRAM_START + 0x1465
+ITEM_RECEIVED = WRAM_START + 0x1467
+DEATH_RECEIVED = WRAM_START + 0x7E23B0
+GAME_MODE = WRAM_START + 0x0118
+YOSHI_STATE = SRAM_START + 0x00AC
+DEATHLINK_ADDR = ROM_START + 0x06FC8C
+DEATHMUSIC_FLAG = WRAM_START + 0x004F
+DEATHFLAG = WRAM_START + 0x00DB
+DEATHLINKRECV = WRAM_START + 0x00E0
+GOALFLAG = WRAM_START + 0x14B6
+
+VALID_GAME_STATES = [0x0F, 0x10, 0x2C]
+
+
+class YoshisIslandSNIClient(SNIClient):
+ game = "Yoshi's Island"
+ patch_suffix = ".apyi"
+
+ async def deathlink_kill_player(self, ctx: "SNIContext") -> None:
+ from SNIClient import DeathState, snes_buffered_write, snes_flush_writes, snes_read
+ game_state = await snes_read(ctx, GAME_MODE, 0x1)
+ if game_state[0] != 0x0F:
+ return
+
+ yoshi_state = await snes_read(ctx, YOSHI_STATE, 0x1)
+ if yoshi_state[0] != 0x00:
+ return
+
+ snes_buffered_write(ctx, WRAM_START + 0x026A, bytes([0x01]))
+ snes_buffered_write(ctx, WRAM_START + 0x00E0, bytes([0x01]))
+ await snes_flush_writes(ctx)
+ ctx.death_state = DeathState.dead
+ ctx.last_death_link = time.time()
+
+ async def validate_rom(self, ctx: "SNIContext") -> bool:
+ from SNIClient import snes_read
+
+ rom_name = await snes_read(ctx, YOSHISISLAND_ROMHASH_START, ROMHASH_SIZE)
+ if rom_name is None or rom_name[:7] != b"YOSHIAP":
+ return False
+
+ ctx.game = self.game
+ ctx.items_handling = 0b111 # remote items
+ ctx.rom = rom_name
+
+ death_link = await snes_read(ctx, DEATHLINK_ADDR, 1)
+ if death_link:
+ await ctx.update_death_link(bool(death_link[0] & 0b1))
+ return True
+
+ async def game_watcher(self, ctx: "SNIContext") -> None:
+ from SNIClient import snes_buffered_write, snes_flush_writes, snes_read
+
+ game_mode = await snes_read(ctx, GAME_MODE, 0x1)
+ item_received = await snes_read(ctx, ITEM_RECEIVED, 0x1)
+ game_music = await snes_read(ctx, DEATHMUSIC_FLAG, 0x1)
+ goal_flag = await snes_read(ctx, GOALFLAG, 0x1)
+
+ if "DeathLink" in ctx.tags and ctx.last_death_link + 1 < time.time():
+ death_flag = await snes_read(ctx, DEATHFLAG, 0x1)
+ deathlink_death = await snes_read(ctx, DEATHLINKRECV, 0x1)
+ currently_dead = (game_music[0] == 0x07 or game_mode[0] == 0x12 or
+ (death_flag[0] == 0x00 and game_mode[0] == 0x11)) and deathlink_death[0] == 0x00
+ await ctx.handle_deathlink_state(currently_dead)
+
+ if game_mode is None:
+ return
+ elif goal_flag[0] != 0x00:
+ await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}])
+ ctx.finished_game = True
+ elif game_mode[0] not in VALID_GAME_STATES:
+ return
+ elif item_received[0] > 0x00:
+ return
+
+ from .Rom import item_values
+ rom = await snes_read(ctx, YOSHISISLAND_ROMHASH_START, ROMHASH_SIZE)
+ if rom != ctx.rom:
+ ctx.rom = None
+ return
+
+ new_checks = []
+ from .Rom import location_table
+
+ location_ram_data = await snes_read(ctx, WRAM_START + 0x1440, 0x80)
+ for loc_id, loc_data in location_table.items():
+ if loc_id not in ctx.locations_checked:
+ data = location_ram_data[loc_data[0] - 0x1440]
+ masked_data = data & (1 << loc_data[1])
+ bit_set = masked_data != 0
+ invert_bit = ((len(loc_data) >= 3) and loc_data[2])
+ if bit_set != invert_bit:
+ new_checks.append(loc_id)
+
+ for new_check_id in new_checks:
+ ctx.locations_checked.add(new_check_id)
+ location = ctx.location_names[new_check_id]
+ total_locations = len(ctx.missing_locations) + len(ctx.checked_locations)
+ snes_logger.info(f"New Check: {location} ({len(ctx.locations_checked)}/{total_locations})")
+ await ctx.send_msgs([{"cmd": "LocationChecks", "locations": [new_check_id]}])
+
+ recv_count = await snes_read(ctx, ITEMQUEUE_HIGH, 2)
+ recv_index = struct.unpack("H", recv_count)[0]
+ if recv_index < len(ctx.items_received):
+ item = ctx.items_received[recv_index]
+ recv_index += 1
+ logging.info("Received %s from %s (%s) (%d/%d in list)" % (
+ color(ctx.item_names[item.item], "red", "bold"),
+ color(ctx.player_names[item.player], "yellow"),
+ ctx.location_names[item.location], recv_index, len(ctx.items_received)))
+
+ snes_buffered_write(ctx, ITEMQUEUE_HIGH, pack("H", recv_index))
+ if item.item in item_values:
+ item_count = await snes_read(ctx, WRAM_START + item_values[item.item][0], 0x1)
+ increment = item_values[item.item][1]
+ new_item_count = item_count[0]
+ if increment > 1:
+ new_item_count = increment
+ else:
+ new_item_count += increment
+
+ snes_buffered_write(ctx, WRAM_START + item_values[item.item][0], bytes([new_item_count]))
+ await snes_flush_writes(ctx)
diff --git a/worlds/yoshisisland/Items.py b/worlds/yoshisisland/Items.py
new file mode 100644
index 000000000000..c97678ed4ed4
--- /dev/null
+++ b/worlds/yoshisisland/Items.py
@@ -0,0 +1,122 @@
+from typing import Dict, Set, Tuple, NamedTuple, Optional
+from BaseClasses import ItemClassification
+
+class ItemData(NamedTuple):
+ category: str
+ code: Optional[int]
+ classification: ItemClassification
+ amount: Optional[int] = 1
+
+item_table: Dict[str, ItemData] = {
+ "! Switch": ItemData("Items", 0x302050, ItemClassification.progression),
+ "Dashed Platform": ItemData("Items", 0x302051, ItemClassification.progression),
+ "Dashed Stairs": ItemData("Items", 0x302052, ItemClassification.progression),
+ "Beanstalk": ItemData("Items", 0x302053, ItemClassification.progression),
+ "Helicopter Morph": ItemData("Morphs", 0x302054, ItemClassification.progression),
+ "Spring Ball": ItemData("Items", 0x302055, ItemClassification.progression),
+ "Large Spring Ball": ItemData("Items", 0x302056, ItemClassification.progression),
+ "Arrow Wheel": ItemData("Items", 0x302057, ItemClassification.progression),
+ "Vanishing Arrow Wheel": ItemData("Items", 0x302058, ItemClassification.progression),
+ "Mole Tank Morph": ItemData("Morphs", 0x302059, ItemClassification.progression),
+ "Watermelon": ItemData("Items", 0x30205A, ItemClassification.progression),
+ "Ice Melon": ItemData("Items", 0x30205B, ItemClassification.progression),
+ "Fire Melon": ItemData("Items", 0x30205C, ItemClassification.progression),
+ "Super Star": ItemData("Items", 0x30205D, ItemClassification.progression),
+ "Car Morph": ItemData("Morphs", 0x30205E, ItemClassification.progression),
+ "Flashing Eggs": ItemData("Items", 0x30205F, ItemClassification.progression),
+ "Giant Eggs": ItemData("Items", 0x302060, ItemClassification.progression),
+ "Egg Launcher": ItemData("Items", 0x302061, ItemClassification.progression),
+ "Egg Plant": ItemData("Items", 0x302062, ItemClassification.progression),
+ "Submarine Morph": ItemData("Morphs", 0x302063, ItemClassification.progression),
+ "Chomp Rock": ItemData("Items", 0x302064, ItemClassification.progression),
+ "Poochy": ItemData("Items", 0x302065, ItemClassification.progression),
+ "Platform Ghost": ItemData("Items", 0x302066, ItemClassification.progression),
+ "Skis": ItemData("Items", 0x302067, ItemClassification.progression),
+ "Train Morph": ItemData("Morphs", 0x302068, ItemClassification.progression),
+ "Key": ItemData("Items", 0x302069, ItemClassification.progression),
+ "Middle Ring": ItemData("Items", 0x30206A, ItemClassification.progression),
+ "Bucket": ItemData("Items", 0x30206B, ItemClassification.progression),
+ "Tulip": ItemData("Items", 0x30206C, ItemClassification.progression),
+ "Egg Capacity Upgrade": ItemData("Items", 0x30206D, ItemClassification.progression, 5),
+ "Secret Lens": ItemData("Items", 0x302081, ItemClassification.progression),
+
+ "World 1 Gate": ItemData("Gates", 0x30206E, ItemClassification.progression),
+ "World 2 Gate": ItemData("Gates", 0x30206F, ItemClassification.progression),
+ "World 3 Gate": ItemData("Gates", 0x302070, ItemClassification.progression),
+ "World 4 Gate": ItemData("Gates", 0x302071, ItemClassification.progression),
+ "World 5 Gate": ItemData("Gates", 0x302072, ItemClassification.progression),
+ "World 6 Gate": ItemData("Gates", 0x302073, ItemClassification.progression),
+
+ "Extra 1": ItemData("Panels", 0x302074, ItemClassification.progression),
+ "Extra 2": ItemData("Panels", 0x302075, ItemClassification.progression),
+ "Extra 3": ItemData("Panels", 0x302076, ItemClassification.progression),
+ "Extra 4": ItemData("Panels", 0x302077, ItemClassification.progression),
+ "Extra 5": ItemData("Panels", 0x302078, ItemClassification.progression),
+ "Extra 6": ItemData("Panels", 0x302079, ItemClassification.progression),
+ "Extra Panels": ItemData("Panels", 0x30207A, ItemClassification.progression),
+
+ "Bonus 1": ItemData("Panels", 0x30207B, ItemClassification.progression),
+ "Bonus 2": ItemData("Panels", 0x30207C, ItemClassification.progression),
+ "Bonus 3": ItemData("Panels", 0x30207D, ItemClassification.progression),
+ "Bonus 4": ItemData("Panels", 0x30207E, ItemClassification.progression),
+ "Bonus 5": ItemData("Panels", 0x30207F, ItemClassification.progression),
+ "Bonus 6": ItemData("Panels", 0x302080, ItemClassification.progression),
+ "Bonus Panels": ItemData("Panels", 0x302082, ItemClassification.progression),
+
+ "Anytime Egg": ItemData("Consumable", 0x302083, ItemClassification.useful, 0),
+ "Anywhere Pow": ItemData("Consumable", 0x302084, ItemClassification.filler, 0),
+ "Winged Cloud Maker": ItemData("Consumable", 0x302085, ItemClassification.filler, 0),
+ "Pocket Melon": ItemData("Consumable", 0x302086, ItemClassification.filler, 0),
+ "Pocket Fire Melon": ItemData("Consumable", 0x302087, ItemClassification.filler, 0),
+ "Pocket Ice Melon": ItemData("Consumable", 0x302088, ItemClassification.filler, 0),
+ "Magnifying Glass": ItemData("Consumable", 0x302089, ItemClassification.filler, 0),
+ "+10 Stars": ItemData("Consumable", 0x30208A, ItemClassification.useful, 0),
+ "+20 Stars": ItemData("Consumable", 0x30208B, ItemClassification.useful, 0),
+ "1-Up": ItemData("Lives", 0x30208C, ItemClassification.filler, 0),
+ "2-Up": ItemData("Lives", 0x30208D, ItemClassification.filler, 0),
+ "3-Up": ItemData("Lives", 0x30208E, ItemClassification.filler, 0),
+ "10-Up": ItemData("Lives", 0x30208F, ItemClassification.filler, 5),
+ "Bonus Consumables": ItemData("Events", None, ItemClassification.progression, 0),
+ "Bandit Consumables": ItemData("Events", None, ItemClassification.progression, 0),
+ "Bandit Watermelons": ItemData("Events", None, ItemClassification.progression, 0),
+
+ "Fuzzy Trap": ItemData("Traps", 0x302090, ItemClassification.trap, 0),
+ "Reversal Trap": ItemData("Traps", 0x302091, ItemClassification.trap, 0),
+ "Darkness Trap": ItemData("Traps", 0x302092, ItemClassification.trap, 0),
+ "Freeze Trap": ItemData("Traps", 0x302093, ItemClassification.trap, 0),
+
+ "Boss Clear": ItemData("Events", None, ItemClassification.progression, 0),
+ "Piece of Luigi": ItemData("Items", 0x302095, ItemClassification.progression, 0),
+ "Saved Baby Luigi": ItemData("Events", None, ItemClassification.progression, 0)
+}
+
+filler_items: Tuple[str, ...] = (
+ "Anytime Egg",
+ "Anywhere Pow",
+ "Winged Cloud Maker",
+ "Pocket Melon",
+ "Pocket Fire Melon",
+ "Pocket Ice Melon",
+ "Magnifying Glass",
+ "+10 Stars",
+ "+20 Stars",
+ "1-Up",
+ "2-Up",
+ "3-Up"
+)
+
+trap_items: Tuple[str, ...] = (
+ "Fuzzy Trap",
+ "Reversal Trap",
+ "Darkness Trap",
+ "Freeze Trap"
+)
+
+def get_item_names_per_category() -> Dict[str, Set[str]]:
+ categories: Dict[str, Set[str]] = {}
+
+ for name, data in item_table.items():
+ if data.category != "Events":
+ categories.setdefault(data.category, set()).add(name)
+
+ return categories
diff --git a/worlds/yoshisisland/Locations.py b/worlds/yoshisisland/Locations.py
new file mode 100644
index 000000000000..bc0855260eb4
--- /dev/null
+++ b/worlds/yoshisisland/Locations.py
@@ -0,0 +1,355 @@
+from typing import List, Optional, NamedTuple, TYPE_CHECKING
+
+from .Options import PlayerGoal, MinigameChecks
+from worlds.generic.Rules import CollectionRule
+
+if TYPE_CHECKING:
+ from . import YoshisIslandWorld
+from .level_logic import YoshiLogic
+
+
+class LocationData(NamedTuple):
+ region: str
+ name: str
+ code: Optional[int]
+ LevelID: int
+ rule: CollectionRule = lambda state: True
+
+
+def get_locations(world: Optional["YoshisIslandWorld"]) -> List[LocationData]:
+ if world:
+ logic = YoshiLogic(world)
+
+ location_table: List[LocationData] = [
+ LocationData("1-1", "Make Eggs, Throw Eggs: Red Coins", 0x305020, 0x00),
+ LocationData("1-1", "Make Eggs, Throw Eggs: Flowers", 0x305021, 0x00),
+ LocationData("1-1", "Make Eggs, Throw Eggs: Stars", 0x305022, 0x00),
+ LocationData("1-1", "Make Eggs, Throw Eggs: Level Clear", 0x305023, 0x00),
+
+ LocationData("1-2", "Watch Out Below!: Red Coins", 0x305024, 0x01),
+ LocationData("1-2", "Watch Out Below!: Flowers", 0x305025, 0x01),
+ LocationData("1-2", "Watch Out Below!: Stars", 0x305026, 0x01),
+ LocationData("1-2", "Watch Out Below!: Level Clear", 0x305027, 0x01),
+
+ LocationData("1-3", "The Cave Of Chomp Rock: Red Coins", 0x305028, 0x02),
+ LocationData("1-3", "The Cave Of Chomp Rock: Flowers", 0x305029, 0x02),
+ LocationData("1-3", "The Cave Of Chomp Rock: Stars", 0x30502A, 0x02),
+ LocationData("1-3", "The Cave Of Chomp Rock: Level Clear", 0x30502B, 0x02),
+
+ LocationData("1-4", "Burt The Bashful's Fort: Red Coins", 0x30502C, 0x03),
+ LocationData("1-4", "Burt The Bashful's Fort: Flowers", 0x30502D, 0x03),
+ LocationData("1-4", "Burt The Bashful's Fort: Stars", 0x30502E, 0x03),
+ LocationData("1-4", "Burt The Bashful's Fort: Level Clear", 0x30502F, 0x03, lambda state: logic._14CanFightBoss(state)),
+ LocationData("Burt The Bashful's Boss Room", "Burt The Bashful's Boss Room", None, 0x03, lambda state: logic._14Boss(state)),
+
+ LocationData("1-5", "Hop! Hop! Donut Lifts: Red Coins", 0x305031, 0x04),
+ LocationData("1-5", "Hop! Hop! Donut Lifts: Flowers", 0x305032, 0x04),
+ LocationData("1-5", "Hop! Hop! Donut Lifts: Stars", 0x305033, 0x04),
+ LocationData("1-5", "Hop! Hop! Donut Lifts: Level Clear", 0x305034, 0x04),
+
+ LocationData("1-6", "Shy-Guys On Stilts: Red Coins", 0x305035, 0x05),
+ LocationData("1-6", "Shy-Guys On Stilts: Flowers", 0x305036, 0x05),
+ LocationData("1-6", "Shy-Guys On Stilts: Stars", 0x305037, 0x05),
+ LocationData("1-6", "Shy-Guys On Stilts: Level Clear", 0x305038, 0x05),
+
+ LocationData("1-7", "Touch Fuzzy Get Dizzy: Red Coins", 0x305039, 0x06),
+ LocationData("1-7", "Touch Fuzzy Get Dizzy: Flowers", 0x30503A, 0x06),
+ LocationData("1-7", "Touch Fuzzy Get Dizzy: Stars", 0x30503B, 0x06),
+ LocationData("1-7", "Touch Fuzzy Get Dizzy: Level Clear", 0x30503C, 0x06),
+ LocationData("1-7", "Touch Fuzzy Get Dizzy: Gather Coins", None, 0x06, lambda state: logic._17Game(state)),
+
+ LocationData("1-8", "Salvo The Slime's Castle: Red Coins", 0x30503D, 0x07),
+ LocationData("1-8", "Salvo The Slime's Castle: Flowers", 0x30503E, 0x07),
+ LocationData("1-8", "Salvo The Slime's Castle: Stars", 0x30503F, 0x07),
+ LocationData("1-8", "Salvo The Slime's Castle: Level Clear", 0x305040, 0x07, lambda state: logic._18CanFightBoss(state)),
+ LocationData("Salvo The Slime's Boss Room", "Salvo The Slime's Boss Room", None, 0x07, lambda state: logic._18Boss(state)),
+
+ LocationData("1-Bonus", "Flip Cards", None, 0x09),
+ ############################################################################################
+ LocationData("2-1", "Visit Koopa And Para-Koopa: Red Coins", 0x305041, 0x0C),
+ LocationData("2-1", "Visit Koopa And Para-Koopa: Flowers", 0x305042, 0x0C),
+ LocationData("2-1", "Visit Koopa And Para-Koopa: Stars", 0x305043, 0x0C),
+ LocationData("2-1", "Visit Koopa And Para-Koopa: Level Clear", 0x305044, 0x0C),
+
+ LocationData("2-2", "The Baseball Boys: Red Coins", 0x305045, 0x0D),
+ LocationData("2-2", "The Baseball Boys: Flowers", 0x305046, 0x0D),
+ LocationData("2-2", "The Baseball Boys: Stars", 0x305047, 0x0D),
+ LocationData("2-2", "The Baseball Boys: Level Clear", 0x305048, 0x0D),
+
+ LocationData("2-3", "What's Gusty Taste Like?: Red Coins", 0x305049, 0x0E),
+ LocationData("2-3", "What's Gusty Taste Like?: Flowers", 0x30504A, 0x0E),
+ LocationData("2-3", "What's Gusty Taste Like?: Stars", 0x30504B, 0x0E),
+ LocationData("2-3", "What's Gusty Taste Like?: Level Clear", 0x30504C, 0x0E),
+
+ LocationData("2-4", "Bigger Boo's Fort: Red Coins", 0x30504D, 0x0F),
+ LocationData("2-4", "Bigger Boo's Fort: Flowers", 0x30504E, 0x0F),
+ LocationData("2-4", "Bigger Boo's Fort: Stars", 0x30504F, 0x0F),
+ LocationData("2-4", "Bigger Boo's Fort: Level Clear", 0x305050, 0x0F, lambda state: logic._24CanFightBoss(state)),
+ LocationData("Bigger Boo's Boss Room", "Bigger Boo's Boss Room", None, 0x0F, lambda state: logic._24Boss(state)),
+
+ LocationData("2-5", "Watch Out For Lakitu: Red Coins", 0x305051, 0x10),
+ LocationData("2-5", "Watch Out For Lakitu: Flowers", 0x305052, 0x10),
+ LocationData("2-5", "Watch Out For Lakitu: Stars", 0x305053, 0x10),
+ LocationData("2-5", "Watch Out For Lakitu: Level Clear", 0x305054, 0x10),
+
+ LocationData("2-6", "The Cave Of The Mystery Maze: Red Coins", 0x305055, 0x11),
+ LocationData("2-6", "The Cave Of The Mystery Maze: Flowers", 0x305056, 0x11),
+ LocationData("2-6", "The Cave Of The Mystery Maze: Stars", 0x305057, 0x11),
+ LocationData("2-6", "The Cave Of The Mystery Maze: Level Clear", 0x305058, 0x11),
+ LocationData("2-6", "The Cave Of the Mystery Maze: Seed Spitting Contest", None, 0x11, lambda state: logic._26Game(state)),
+
+ LocationData("2-7", "Lakitu's Wall: Red Coins", 0x305059, 0x12),
+ LocationData("2-7", "Lakitu's Wall: Flowers", 0x30505A, 0x12),
+ LocationData("2-7", "Lakitu's Wall: Stars", 0x30505B, 0x12),
+ LocationData("2-7", "Lakitu's Wall: Level Clear", 0x30505C, 0x12),
+ LocationData("2-7", "Lakitu's Wall: Gather Coins", None, 0x12, lambda state: logic._27Game(state)),
+
+ LocationData("2-8", "The Potted Ghost's Castle: Red Coins", 0x30505D, 0x13),
+ LocationData("2-8", "The Potted Ghost's Castle: Flowers", 0x30505E, 0x13),
+ LocationData("2-8", "The Potted Ghost's Castle: Stars", 0x30505F, 0x13),
+ LocationData("2-8", "The Potted Ghost's Castle: Level Clear", 0x305060, 0x13, lambda state: logic._28CanFightBoss(state)),
+ LocationData("Roger The Ghost's Boss Room", "Roger The Ghost's Boss Room", None, 0x13, lambda state: logic._28Boss(state)),
+ ###############################################################################################
+ LocationData("3-1", "Welcome To Monkey World!: Red Coins", 0x305061, 0x18),
+ LocationData("3-1", "Welcome To Monkey World!: Flowers", 0x305062, 0x18),
+ LocationData("3-1", "Welcome To Monkey World!: Stars", 0x305063, 0x18),
+ LocationData("3-1", "Welcome To Monkey World!: Level Clear", 0x305064, 0x18),
+
+ LocationData("3-2", "Jungle Rhythm...: Red Coins", 0x305065, 0x19),
+ LocationData("3-2", "Jungle Rhythm...: Flowers", 0x305066, 0x19),
+ LocationData("3-2", "Jungle Rhythm...: Stars", 0x305067, 0x19),
+ LocationData("3-2", "Jungle Rhythm...: Level Clear", 0x305068, 0x19),
+
+ LocationData("3-3", "Nep-Enuts' Domain: Red Coins", 0x305069, 0x1A),
+ LocationData("3-3", "Nep-Enuts' Domain: Flowers", 0x30506A, 0x1A),
+ LocationData("3-3", "Nep-Enuts' Domain: Stars", 0x30506B, 0x1A),
+ LocationData("3-3", "Nep-Enuts' Domain: Level Clear", 0x30506C, 0x1A),
+
+ LocationData("3-4", "Prince Froggy's Fort: Red Coins", 0x30506D, 0x1B),
+ LocationData("3-4", "Prince Froggy's Fort: Flowers", 0x30506E, 0x1B),
+ LocationData("3-4", "Prince Froggy's Fort: Stars", 0x30506F, 0x1B),
+ LocationData("3-4", "Prince Froggy's Fort: Level Clear", 0x305070, 0x1B, lambda state: logic._34CanFightBoss(state)),
+ LocationData("Prince Froggy's Boss Room", "Prince Froggy's Boss Room", None, 0x1B, lambda state: logic._34Boss(state)),
+
+ LocationData("3-5", "Jammin' Through The Trees: Red Coins", 0x305071, 0x1C),
+ LocationData("3-5", "Jammin' Through The Trees: Flowers", 0x305072, 0x1C),
+ LocationData("3-5", "Jammin' Through The Trees: Stars", 0x305073, 0x1C),
+ LocationData("3-5", "Jammin' Through The Trees: Level Clear", 0x305074, 0x1C),
+
+ LocationData("3-6", "The Cave Of Harry Hedgehog: Red Coins", 0x305075, 0x1D),
+ LocationData("3-6", "The Cave Of Harry Hedgehog: Flowers", 0x305076, 0x1D),
+ LocationData("3-6", "The Cave Of Harry Hedgehog: Stars", 0x305077, 0x1D),
+ LocationData("3-6", "The Cave Of Harry Hedgehog: Level Clear", 0x305078, 0x1D),
+
+ LocationData("3-7", "Monkeys' Favorite Lake: Red Coins", 0x305079, 0x1E),
+ LocationData("3-7", "Monkeys' Favorite Lake: Flowers", 0x30507A, 0x1E),
+ LocationData("3-7", "Monkeys' Favorite Lake: Stars", 0x30507B, 0x1E),
+ LocationData("3-7", "Monkeys' Favorite Lake: Level Clear", 0x30507C, 0x1E),
+
+ LocationData("3-8", "Naval Piranha's Castle: Red Coins", 0x30507D, 0x1F),
+ LocationData("3-8", "Naval Piranha's Castle: Flowers", 0x30507E, 0x1F),
+ LocationData("3-8", "Naval Piranha's Castle: Stars", 0x30507F, 0x1F),
+ LocationData("3-8", "Naval Piranha's Castle: Level Clear", 0x305080, 0x1F, lambda state: logic._38CanFightBoss(state)),
+ LocationData("Naval Piranha's Boss Room", "Naval Piranha's Boss Room", None, 0x1F, lambda state: logic._38Boss(state)),
+
+ LocationData("3-Bonus", "Drawing Lots", None, 0x21),
+ ##############################################################################################
+ LocationData("4-1", "GO! GO! MARIO!!: Red Coins", 0x305081, 0x24),
+ LocationData("4-1", "GO! GO! MARIO!!: Flowers", 0x305082, 0x24),
+ LocationData("4-1", "GO! GO! MARIO!!: Stars", 0x305083, 0x24),
+ LocationData("4-1", "GO! GO! MARIO!!: Level Clear", 0x305084, 0x24),
+
+ LocationData("4-2", "The Cave Of The Lakitus: Red Coins", 0x305085, 0x25),
+ LocationData("4-2", "The Cave Of The Lakitus: Flowers", 0x305086, 0x25),
+ LocationData("4-2", "The Cave Of The Lakitus: Stars", 0x305087, 0x25),
+ LocationData("4-2", "The Cave Of The Lakitus: Level Clear", 0x305088, 0x25),
+
+ LocationData("4-3", "Don't Look Back!: Red Coins", 0x305089, 0x26),
+ LocationData("4-3", "Don't Look Back!: Flowers", 0x30508A, 0x26),
+ LocationData("4-3", "Don't Look Back!: Stars", 0x30508B, 0x26),
+ LocationData("4-3", "Don't Look Back!: Level Clear", 0x30508C, 0x26),
+
+ LocationData("4-4", "Marching Milde's Fort: Red Coins", 0x30508D, 0x27),
+ LocationData("4-4", "Marching Milde's Fort: Flowers", 0x30508E, 0x27),
+ LocationData("4-4", "Marching Milde's Fort: Stars", 0x30508F, 0x27),
+ LocationData("4-4", "Marching Milde's Fort: Level Clear", 0x305090, 0x27, lambda state: logic._44CanFightBoss(state)),
+ LocationData("Marching Milde's Boss Room", "Marching Milde's Boss Room", None, 0x27, lambda state: logic._44Boss(state)),
+
+ LocationData("4-5", "Chomp Rock Zone: Red Coins", 0x305091, 0x28),
+ LocationData("4-5", "Chomp Rock Zone: Flowers", 0x305092, 0x28),
+ LocationData("4-5", "Chomp Rock Zone: Stars", 0x305093, 0x28),
+ LocationData("4-5", "Chomp Rock Zone: Level Clear", 0x305094, 0x28),
+
+ LocationData("4-6", "Lake Shore Paradise: Red Coins", 0x305095, 0x29),
+ LocationData("4-6", "Lake Shore Paradise: Flowers", 0x305096, 0x29),
+ LocationData("4-6", "Lake Shore Paradise: Stars", 0x305097, 0x29),
+ LocationData("4-6", "Lake Shore Paradise: Level Clear", 0x305098, 0x29),
+
+ LocationData("4-7", "Ride Like The Wind: Red Coins", 0x305099, 0x2A),
+ LocationData("4-7", "Ride Like The Wind: Flowers", 0x30509A, 0x2A),
+ LocationData("4-7", "Ride Like The Wind: Stars", 0x30509B, 0x2A),
+ LocationData("4-7", "Ride Like The Wind: Level Clear", 0x30509C, 0x2A),
+ LocationData("4-7", "Ride Like The Wind: Gather Coins", None, 0x2A, lambda state: logic._47Game(state)),
+
+ LocationData("4-8", "Hookbill The Koopa's Castle: Red Coins", 0x30509D, 0x2B),
+ LocationData("4-8", "Hookbill The Koopa's Castle: Flowers", 0x30509E, 0x2B),
+ LocationData("4-8", "Hookbill The Koopa's Castle: Stars", 0x30509F, 0x2B),
+ LocationData("4-8", "Hookbill The Koopa's Castle: Level Clear", 0x3050A0, 0x2B, lambda state: logic._48CanFightBoss(state)),
+ LocationData("Hookbill The Koopa's Boss Room", "Hookbill The Koopa's Boss Room", None, 0x2B, lambda state: logic._48Boss(state)),
+
+ LocationData("4-Bonus", "Match Cards", None, 0x2D),
+ ######################################################################################################
+ LocationData("5-1", "BLIZZARD!!!: Red Coins", 0x3050A1, 0x30),
+ LocationData("5-1", "BLIZZARD!!!: Flowers", 0x3050A2, 0x30),
+ LocationData("5-1", "BLIZZARD!!!: Stars", 0x3050A3, 0x30),
+ LocationData("5-1", "BLIZZARD!!!: Level Clear", 0x3050A4, 0x30),
+
+ LocationData("5-2", "Ride The Ski Lifts: Red Coins", 0x3050A5, 0x31),
+ LocationData("5-2", "Ride The Ski Lifts: Flowers", 0x3050A6, 0x31),
+ LocationData("5-2", "Ride The Ski Lifts: Stars", 0x3050A7, 0x31),
+ LocationData("5-2", "Ride The Ski Lifts: Level Clear", 0x3050A8, 0x31),
+
+ LocationData("5-3", "Danger - Icy Conditions Ahead: Red Coins", 0x3050A9, 0x32),
+ LocationData("5-3", "Danger - Icy Conditions Ahead: Flowers", 0x3050AA, 0x32),
+ LocationData("5-3", "Danger - Icy Conditions Ahead: Stars", 0x3050AB, 0x32),
+ LocationData("5-3", "Danger - Icy Conditions Ahead: Level Clear", 0x3050AC, 0x32),
+
+ LocationData("5-4", "Sluggy The Unshaven's Fort: Red Coins", 0x3050AD, 0x33),
+ LocationData("5-4", "Sluggy The Unshaven's Fort: Flowers", 0x3050AE, 0x33),
+ LocationData("5-4", "Sluggy The Unshaven's Fort: Stars", 0x3050AF, 0x33),
+ LocationData("5-4", "Sluggy The Unshaven's Fort: Level Clear", 0x3050B0, 0x33, lambda state: logic._54CanFightBoss(state)),
+ LocationData("Sluggy The Unshaven's Boss Room", "Sluggy The Unshaven's Boss Room", None, 0x33, lambda state: logic._54Boss(state)),
+
+ LocationData("5-5", "Goonie Rides!: Red Coins", 0x3050B1, 0x34),
+ LocationData("5-5", "Goonie Rides!: Flowers", 0x3050B2, 0x34),
+ LocationData("5-5", "Goonie Rides!: Stars", 0x3050B3, 0x34),
+ LocationData("5-5", "Goonie Rides!: Level Clear", 0x3050B4, 0x34),
+
+ LocationData("5-6", "Welcome To Cloud World: Red Coins", 0x3050B5, 0x35),
+ LocationData("5-6", "Welcome To Cloud World: Flowers", 0x3050B6, 0x35),
+ LocationData("5-6", "Welcome To Cloud World: Stars", 0x3050B7, 0x35),
+ LocationData("5-6", "Welcome To Cloud World: Level Clear", 0x3050B8, 0x35),
+
+ LocationData("5-7", "Shifting Platforms Ahead: Red Coins", 0x3050B9, 0x36),
+ LocationData("5-7", "Shifting Platforms Ahead: Flowers", 0x3050BA, 0x36),
+ LocationData("5-7", "Shifting Platforms Ahead: Stars", 0x3050BB, 0x36),
+ LocationData("5-7", "Shifting Platforms Ahead: Level Clear", 0x3050BC, 0x36),
+
+ LocationData("5-8", "Raphael The Raven's Castle: Red Coins", 0x3050BD, 0x37),
+ LocationData("5-8", "Raphael The Raven's Castle: Flowers", 0x3050BE, 0x37),
+ LocationData("5-8", "Raphael The Raven's Castle: Stars", 0x3050BF, 0x37),
+ LocationData("5-8", "Raphael The Raven's Castle: Level Clear", 0x3050C0, 0x37, lambda state: logic._58CanFightBoss(state)),
+ LocationData("Raphael The Raven's Boss Room", "Raphael The Raven's Boss Room", None, 0x37, lambda state: logic._58Boss(state)),
+ ######################################################################################################
+
+ LocationData("6-1", "Scary Skeleton Goonies!: Red Coins", 0x3050C1, 0x3C),
+ LocationData("6-1", "Scary Skeleton Goonies!: Flowers", 0x3050C2, 0x3C),
+ LocationData("6-1", "Scary Skeleton Goonies!: Stars", 0x3050C3, 0x3C),
+ LocationData("6-1", "Scary Skeleton Goonies!: Level Clear", 0x3050C4, 0x3C),
+
+ LocationData("6-2", "The Cave Of The Bandits: Red Coins", 0x3050C5, 0x3D),
+ LocationData("6-2", "The Cave Of The Bandits: Flowers", 0x3050C6, 0x3D),
+ LocationData("6-2", "The Cave Of The Bandits: Stars", 0x3050C7, 0x3D),
+ LocationData("6-2", "The Cave Of The Bandits: Level Clear", 0x3050C8, 0x3D),
+
+ LocationData("6-3", "Beware The Spinning Logs: Red Coins", 0x3050C9, 0x3E),
+ LocationData("6-3", "Beware The Spinning Logs: Flowers", 0x3050CA, 0x3E),
+ LocationData("6-3", "Beware The Spinning Logs: Stars", 0x3050CB, 0x3E),
+ LocationData("6-3", "Beware The Spinning Logs: Level Clear", 0x3050CC, 0x3E),
+
+ LocationData("6-4", "Tap-Tap The Red Nose's Fort: Red Coins", 0x3050CD, 0x3F),
+ LocationData("6-4", "Tap-Tap The Red Nose's Fort: Flowers", 0x3050CE, 0x3F),
+ LocationData("6-4", "Tap-Tap The Red Nose's Fort: Stars", 0x3050CF, 0x3F),
+ LocationData("6-4", "Tap-Tap The Red Nose's Fort: Level Clear", 0x3050D0, 0x3F, lambda state: logic._64CanFightBoss(state)),
+ LocationData("Tap-Tap The Red Nose's Boss Room", "Tap-Tap The Red Nose's Boss Room", None, 0x3F, lambda state: logic._64Boss(state)),
+
+ LocationData("6-5", "The Very Loooooong Cave: Red Coins", 0x3050D1, 0x40),
+ LocationData("6-5", "The Very Loooooong Cave: Flowers", 0x3050D2, 0x40),
+ LocationData("6-5", "The Very Loooooong Cave: Stars", 0x3050D3, 0x40),
+ LocationData("6-5", "The Very Loooooong Cave: Level Clear", 0x3050D4, 0x40),
+
+ LocationData("6-6", "The Deep, Underground Maze: Red Coins", 0x3050D5, 0x41),
+ LocationData("6-6", "The Deep, Underground Maze: Flowers", 0x3050D6, 0x41),
+ LocationData("6-6", "The Deep, Underground Maze: Stars", 0x3050D7, 0x41),
+ LocationData("6-6", "The Deep, Underground Maze: Level Clear", 0x3050D8, 0x41),
+
+ LocationData("6-7", "KEEP MOVING!!!!: Red Coins", 0x3050D9, 0x42),
+ LocationData("6-7", "KEEP MOVING!!!!: Flowers", 0x3050DA, 0x42),
+ LocationData("6-7", "KEEP MOVING!!!!: Stars", 0x3050DB, 0x42),
+ LocationData("6-7", "KEEP MOVING!!!!: Level Clear", 0x3050DC, 0x42),
+
+ LocationData("6-8", "King Bowser's Castle: Red Coins", 0x3050DD, 0x43),
+ LocationData("6-8", "King Bowser's Castle: Flowers", 0x3050DE, 0x43),
+ LocationData("6-8", "King Bowser's Castle: Stars", 0x3050DF, 0x43)
+ ]
+
+ if not world or world.options.extras_enabled:
+ location_table += [
+ LocationData("1-Extra", "Poochy Ain't Stupid: Red Coins", 0x3050E0, 0x08),
+ LocationData("1-Extra", "Poochy Ain't Stupid: Flowers", 0x3050E1, 0x08),
+ LocationData("1-Extra", "Poochy Ain't Stupid: Stars", 0x3050E2, 0x08),
+ LocationData("1-Extra", "Poochy Ain't Stupid: Level Clear", 0x3050E3, 0x08),
+
+ LocationData("2-Extra", "Hit That Switch!!: Red Coins", 0x3050E4, 0x14),
+ LocationData("2-Extra", "Hit That Switch!!: Flowers", 0x3050E5, 0x14),
+ LocationData("2-Extra", "Hit That Switch!!: Stars", 0x3050E6, 0x14),
+ LocationData("2-Extra", "Hit That Switch!!: Level Clear", 0x3050E7, 0x14),
+
+ LocationData("3-Extra", "More Monkey Madness: Red Coins", 0x3050E8, 0x20),
+ LocationData("3-Extra", "More Monkey Madness: Flowers", 0x3050E9, 0x20),
+ LocationData("3-Extra", "More Monkey Madness: Stars", 0x3050EA, 0x20),
+ LocationData("3-Extra", "More Monkey Madness: Level Clear", 0x3050EB, 0x20),
+
+ LocationData("4-Extra", "The Impossible? Maze: Red Coins", 0x3050EC, 0x2C),
+ LocationData("4-Extra", "The Impossible? Maze: Flowers", 0x3050ED, 0x2C),
+ LocationData("4-Extra", "The Impossible? Maze: Stars", 0x3050EE, 0x2C),
+ LocationData("4-Extra", "The Impossible? Maze: Level Clear", 0x3050EF, 0x2C),
+
+ LocationData("5-Extra", "Kamek's Revenge: Red Coins", 0x3050F0, 0x38),
+ LocationData("5-Extra", "Kamek's Revenge: Flowers", 0x3050F1, 0x38),
+ LocationData("5-Extra", "Kamek's Revenge: Stars", 0x3050F2, 0x38),
+ LocationData("5-Extra", "Kamek's Revenge: Level Clear", 0x3050F3, 0x38),
+
+ LocationData("6-Extra", "Castles - Masterpiece Set: Red Coins", 0x3050F4, 0x44),
+ LocationData("6-Extra", "Castles - Masterpiece Set: Flowers", 0x3050F5, 0x44),
+ LocationData("6-Extra", "Castles - Masterpiece Set: Stars", 0x3050F6, 0x44),
+ LocationData("6-Extra", "Castles - Masterpiece Set: Level Clear", 0x3050F7, 0x44),
+ ]
+
+ if not world or world.options.minigame_checks in {MinigameChecks.option_bandit_games, MinigameChecks.option_both}:
+ location_table += [
+ LocationData("1-3", "The Cave Of Chomp Rock: Bandit Game", 0x3050F8, 0x02, lambda state: logic._13Game(state)),
+ LocationData("1-7", "Touch Fuzzy Get Dizzy: Bandit Game", 0x3050F9, 0x06, lambda state: logic._17Game(state)),
+ LocationData("2-1", "Visit Koopa And Para-Koopa: Bandit Game", 0x3050FA, 0x0C, lambda state: logic._21Game(state)),
+ LocationData("2-3", "What's Gusty Taste Like?: Bandit Game", 0x3050FB, 0x0E, lambda state: logic._23Game(state)),
+ LocationData("2-6", "The Cave Of The Mystery Maze: Bandit Game", 0x3050FC, 0x11, lambda state: logic._26Game(state)),
+ LocationData("2-7", "Lakitu's Wall: Bandit Game", 0x3050FD, 0x12, lambda state: logic._27Game(state)),
+ LocationData("3-2", "Jungle Rhythm...: Bandit Game", 0x3050FE, 0x19, lambda state: logic._32Game(state)),
+ LocationData("3-7", "Monkeys' Favorite Lake: Bandit Game", 0x3050FF, 0x1E, lambda state: logic._37Game(state)),
+ LocationData("4-2", "The Cave Of The Lakitus: Bandit Game", 0x305100, 0x25, lambda state: logic._42Game(state)),
+ LocationData("4-6", "Lake Shore Paradise: Bandit Game", 0x305101, 0x29, lambda state: logic._46Game(state)),
+ LocationData("4-7", "Ride Like The Wind: Bandit Game", 0x305102, 0x2A, lambda state: logic._47Game(state)),
+ LocationData("5-1", "BLIZZARD!!!: Bandit Game", 0x305103, 0x30, lambda state: logic._51Game(state)),
+ LocationData("6-1", "Scary Skeleton Goonies!: Bandit Game", 0x305104, 0x3C, lambda state: logic._61Game(state)),
+ LocationData("6-7", "KEEP MOVING!!!!: Bandit Game", 0x305105, 0x42, lambda state: logic._67Game(state)),
+ ]
+
+ if not world or world.options.minigame_checks in {MinigameChecks.option_bonus_games, MinigameChecks.option_both}:
+ location_table += [
+ LocationData("1-Bonus", "Flip Cards: Victory", 0x305106, 0x09),
+ LocationData("2-Bonus", "Scratch And Match: Victory", 0x305107, 0x15),
+ LocationData("3-Bonus", "Drawing Lots: Victory", 0x305108, 0x21),
+ LocationData("4-Bonus", "Match Cards: Victory", 0x305109, 0x2D),
+ LocationData("5-Bonus", "Roulette: Victory", 0x30510A, 0x39),
+ LocationData("6-Bonus", "Slot Machine: Victory", 0x30510B, 0x45),
+ ]
+ if not world or world.options.goal == PlayerGoal.option_luigi_hunt:
+ location_table += [
+ LocationData("Overworld", "Reconstituted Luigi", None, 0x00, lambda state: logic.reconstitute_luigi(state)),
+ ]
+ if not world or world.options.goal == PlayerGoal.option_bowser:
+ location_table += [
+ LocationData("Bowser's Room", "King Bowser's Castle: Level Clear", None, 0x43, lambda state: logic._68Clear(state)),
+ ]
+
+ return location_table
diff --git a/worlds/yoshisisland/Options.py b/worlds/yoshisisland/Options.py
new file mode 100644
index 000000000000..d02999309f61
--- /dev/null
+++ b/worlds/yoshisisland/Options.py
@@ -0,0 +1,296 @@
+from dataclasses import dataclass
+from Options import Toggle, DefaultOnToggle, DeathLink, Choice, Range, PerGameCommonOptions
+
+
+class ExtrasEnabled(Toggle):
+ """If enabled, the more difficult Extra stages will be added into logic. Otherwise, they will be inaccessible."""
+ display_name = "Include Extra Stages"
+
+
+class SplitExtras(Toggle):
+ """If enabled, Extra stages will be unlocked individually. Otherwise, there will be a single 'Extra Panels' item that unlocks all of them."""
+ display_name = "Split Extra Stages"
+
+
+class SplitBonus(Toggle):
+ """If enabled, Bonus Games will be unlocked individually. Otherwise, there will be a single 'Bonus Panels' item that unlocks all of them."""
+ display_name = "Split Bonus Games"
+
+
+class ObjectVis(Choice):
+ """This will determine the default visibility of objects revealed by the Magnifying Glass.
+ Strict Logic will expect the Secret Lens or a Magnifying Glass to interact with hidden clouds containing stars if they are not set to visible by default."""
+ display_name = "Hidden Object Visibility"
+ option_none = 0
+ option_coins_only = 1
+ option_clouds_only = 2
+ option_full = 3
+ default = 1
+
+
+class SoftlockPrevention(DefaultOnToggle):
+ """If enabled, hold R + X to warp to the last used Middle Ring, or the start of the level if none have been activated."""
+ display_name = "Softlock Prevention Code"
+
+
+class StageLogic(Choice):
+ """This determines what logic mode the stages will use.
+ Strict: Best for casual players or those new to playing Yoshi's Island in AP. Level requirements won't expect anything too difficult of the player.
+ Loose: Recommended for veterans of the original game. Won't expect anything too difficult, but may expect unusual platforming or egg throws.
+ Expert: Logic may expect advanced knowledge or memorization of level layouts, as well as jumps the player may only have one chance to make without restarting."""
+ display_name = "Stage Logic"
+ option_strict = 0
+ option_loose = 1
+ option_expert = 2
+ # option_glitched = 3
+ default = 0
+
+
+class ShuffleMiddleRings(Toggle):
+ """If enabled, Middle Rings will be added to the item pool."""
+ display_name = "Shuffle Middle Rings"
+
+
+class ShuffleSecretLens(Toggle):
+ """If enabled, the Secret Lens will be added to the item pool.
+ The Secret Lens will act as a permanent Magnifying Glass."""
+ display_name = "Add Secret Lens"
+
+
+class DisableAutoScrollers(Toggle):
+ """If enabled, will disable autoscrolling during levels, except during levels which cannot function otherwise."""
+ display_name = "Disable Autoscrolling"
+
+
+class ItemLogic(Toggle):
+ """This will enable logic to expect consumables to be used from the inventory in place of some major items.
+ Logic will expect you to have access to an Overworld bonus game, or a bandit game to get the necessary items.
+ Logic will NOT expect grinding end-of-level bonus games, or any inventory consumables received from checks.
+ Casual logic will only expect consumables from Overworld games; Loose and Expert may expect them from bandit games."""
+ display_name = "Consumable Logic"
+
+
+class MinigameChecks(Choice):
+ """This will set minigame victories to give Archipelago checks.
+ This will not randomize minigames amongst themselves, and is compatible with item logic.
+ Bonus games will be expected to be cleared from the Overworld, not the end of levels.
+ Additionally, 1-Up bonus games will accept any profit as a victory."""
+ display_name = "Minigame Reward Checks"
+ option_none = 0
+ option_bandit_games = 1
+ option_bonus_games = 2
+ option_both = 3
+ default = 0
+
+
+class StartingWorld(Choice):
+ """This sets which world you start in. Other worlds can be accessed by receiving a Gate respective to that world."""
+ display_name = "Starting World"
+ option_world_1 = 0
+ option_world_2 = 1
+ option_world_3 = 2
+ option_world_4 = 3
+ option_world_5 = 4
+ option_world_6 = 5
+ default = 0
+
+
+class StartingLives(Range):
+ """This sets the amount of lives Yoshi will have upon loading the game."""
+ display_name = "Starting Life Count"
+ range_start = 1
+ range_end = 999
+ default = 3
+
+
+class PlayerGoal(Choice):
+ """This sets the goal. Bowser goal requires defeating Bowser at the end of 6-8, while Luigi Hunt requires collecting all required Luigi Pieces."""
+ display_name = "Goal"
+ option_bowser = 0
+ option_luigi_hunt = 1
+ default = 0
+
+
+class LuigiPiecesReq(Range):
+ """This will set how many Luigi Pieces are required to trigger a victory."""
+ display_name = "Luigi Pieces Required"
+ range_start = 1
+ range_end = 100
+ default = 25
+
+
+class LuigiPiecesAmt(Range):
+ """This will set how many Luigi Pieces are in the item pool.
+ If the number in the pool is lower than the number required,
+ the amount in the pool will be randomized, with the minimum being the amount required."""
+ display_name = "Amount of Luigi Pieces"
+ range_start = 1
+ range_end = 100
+ default = 50
+
+
+class FinalLevelBosses(Range):
+ """This sets how many bosses need to be defeated to access 6-8.
+ You can check this in-game by pressing SELECT while in any level."""
+ display_name = "Bosses Required for 6-8 Unlock"
+ range_start = 0
+ range_end = 11
+ default = 5
+
+
+class FinalBossBosses(Range):
+ """This sets how many bosses need to be defeated to access the boss of 6-8.
+ You can check this in-game by pressing SELECT while in any level."""
+ display_name = "Bosses Required for 6-8 Clear"
+ range_start = 0
+ range_end = 11
+ default = 0
+
+
+class BowserDoor(Choice):
+ """This will set which route you take through 6-8.
+ Manual: You go through the door that you hit with an egg, as normal.
+ Doors: Route will be forced to be the door chosen here, regardless of which door you hit.
+ Gauntlet: You will be forced to go through all 4 routes in order before the final hallway."""
+ display_name = "Bowser's Castle Doors"
+ option_manual = 0
+ option_door_1 = 1
+ option_door_2 = 2
+ option_door_3 = 3
+ option_door_4 = 4
+ option_gauntlet = 5
+ default = 0
+
+
+class BossShuffle(Toggle):
+ """This whill shuffle which boss each boss door will lead to. Each boss can only appear once, and Baby Bowser is left alone."""
+ display_name = "Boss Shuffle"
+
+
+class LevelShuffle(Choice):
+ """Disabled: All levels will appear in their normal location.
+ Bosses Guranteed: All worlds will have a boss on -4 and -8.
+ Full: Worlds may have more than 2 or no bosses in them.
+ Regardless of the setting, 6-8 and Extra stages are not shuffled."""
+ display_name = "Level Shuffle"
+ option_disabled = 0
+ option_bosses_guranteed = 1
+ option_full = 2
+ default = 0
+
+
+class YoshiColors(Choice):
+ """Sets the Yoshi color for each level.
+ Normal will use the vanilla colors.
+ Random order will generate a random order of colors that will be used in each level. The stage 1 color will be used for Extra stages, and 6-8.
+ Random color will generate a random color for each stage.
+ Singularity will use a single color defined under 'Singularity Yoshi Color' for use in all stages."""
+ display_name = "Yoshi Colors"
+ option_normal = 0
+ option_random_order = 1
+ option_random_color = 2
+ option_singularity = 3
+ default = 0
+
+
+class SinguColor(Choice):
+ """Sets which color Yoshi will be if Yoshi Colors is set to singularity."""
+ display_name = "Singularity Yoshi Color"
+ option_green = 0
+ option_pink = 1
+ option_cyan = 3
+ option_yellow = 2
+ option_purple = 4
+ option_brown = 5
+ option_red = 6
+ option_blue = 7
+ default = 0
+
+
+class BabySound(Choice):
+ """Change the sound that Baby Mario makes when not on Yoshi."""
+ display_name = "Mario Sound Effect"
+ option_normal = 0
+ option_disabled = 1
+ option_random_sound_effect = 2
+ default = 0
+
+
+class TrapsEnabled(Toggle):
+ """Will place traps into the item pool.
+ Traps have a variety of negative effects, and will only replace filler items."""
+ display_name = "Traps Enabled"
+
+
+class TrapPercent(Range):
+ """Percentage of the item pool that becomes replaced with traps."""
+ display_name = "Trap Chance"
+ range_start = 0
+ range_end = 100
+ default = 10
+
+# class EnableScrets(Range):
+ # """This sets the amount of lives Yoshi will have upon loading the game."""
+ # display_name = "Starting Life Count"
+ # range_start = 1
+ # range_end = 255
+ # default = 3
+
+# class BackgroundColors(Range):
+ # """This sets the amount of lives Yoshi will have upon loading the game."""
+ # display_name = "Starting Life Count"
+ # range_start = 1
+ # range_end = 255
+ # default = 3
+
+# class Foreground Colors(Range):
+ # """This sets the amount of lives Yoshi will have upon loading the game."""
+ # display_name = "Starting Life Count"
+ # range_start = 1
+ # range_end = 255
+ # default = 3
+
+# class Music Shuffle(Range):
+ # """This sets the amount of lives Yoshi will have upon loading the game."""
+ # display_name = "Starting Life Count"
+ # range_start = 1
+ # range_end = 255
+ # default = 3
+
+# class Star Loss Rate(Range):
+ # """This sets the amount of lives Yoshi will have upon loading the game."""
+ # display_name = "Starting Life Count"
+ # range_start = 1
+ # range_end = 255
+ # default = 3
+
+
+@dataclass
+class YoshisIslandOptions(PerGameCommonOptions):
+ starting_world: StartingWorld
+ starting_lives: StartingLives
+ goal: PlayerGoal
+ luigi_pieces_required: LuigiPiecesReq
+ luigi_pieces_in_pool: LuigiPiecesAmt
+ extras_enabled: ExtrasEnabled
+ minigame_checks: MinigameChecks
+ split_extras: SplitExtras
+ split_bonus: SplitBonus
+ hidden_object_visibility: ObjectVis
+ add_secretlens: ShuffleSecretLens
+ shuffle_midrings: ShuffleMiddleRings
+ stage_logic: StageLogic
+ item_logic: ItemLogic
+ disable_autoscroll: DisableAutoScrollers
+ softlock_prevention: SoftlockPrevention
+ castle_open_condition: FinalLevelBosses
+ castle_clear_condition: FinalBossBosses
+ bowser_door_mode: BowserDoor
+ level_shuffle: LevelShuffle
+ boss_shuffle: BossShuffle
+ yoshi_colors: YoshiColors
+ yoshi_singularity_color: SinguColor
+ baby_mario_sound: BabySound
+ traps_enabled: TrapsEnabled
+ trap_percent: TrapPercent
+ death_link: DeathLink
diff --git a/worlds/yoshisisland/Regions.py b/worlds/yoshisisland/Regions.py
new file mode 100644
index 000000000000..59e93cfe7979
--- /dev/null
+++ b/worlds/yoshisisland/Regions.py
@@ -0,0 +1,248 @@
+from typing import List, Dict, TYPE_CHECKING
+from BaseClasses import Region, Location
+from .Locations import LocationData
+from .Options import MinigameChecks
+from .level_logic import YoshiLogic
+from .setup_bosses import BossReqs
+if TYPE_CHECKING:
+ from . import YoshisIslandWorld
+
+
+class YoshisIslandLocation(Location):
+ game: str = "Yoshi's Island"
+ level_id: int
+
+ def __init__(self, player: int, name: str = " ", address: int = None, parent=None, level_id: int = None):
+ super().__init__(player, name, address, parent)
+ self.level_id = level_id
+
+
+def init_areas(world: "YoshisIslandWorld", locations: List[LocationData]) -> None:
+ multiworld = world.multiworld
+ player = world.player
+ logic = YoshiLogic(world)
+
+ locations_per_region = get_locations_per_region(locations)
+
+ regions = [
+ create_region(world, player, locations_per_region, "Menu"),
+ create_region(world, player, locations_per_region, "Overworld"),
+ create_region(world, player, locations_per_region, "World 1"),
+ create_region(world, player, locations_per_region, "World 2"),
+ create_region(world, player, locations_per_region, "World 3"),
+ create_region(world, player, locations_per_region, "World 4"),
+ create_region(world, player, locations_per_region, "World 5"),
+ create_region(world, player, locations_per_region, "World 6"),
+
+ create_region(world, player, locations_per_region, "1-1"),
+ create_region(world, player, locations_per_region, "1-2"),
+ create_region(world, player, locations_per_region, "1-3"),
+ create_region(world, player, locations_per_region, "1-4"),
+ create_region(world, player, locations_per_region, "Burt The Bashful's Boss Room"),
+ create_region(world, player, locations_per_region, "1-5"),
+ create_region(world, player, locations_per_region, "1-6"),
+ create_region(world, player, locations_per_region, "1-7"),
+ create_region(world, player, locations_per_region, "1-8"),
+ create_region(world, player, locations_per_region, "Salvo The Slime's Boss Room"),
+
+ create_region(world, player, locations_per_region, "2-1"),
+ create_region(world, player, locations_per_region, "2-2"),
+ create_region(world, player, locations_per_region, "2-3"),
+ create_region(world, player, locations_per_region, "2-4"),
+ create_region(world, player, locations_per_region, "Bigger Boo's Boss Room"),
+ create_region(world, player, locations_per_region, "2-5"),
+ create_region(world, player, locations_per_region, "2-6"),
+ create_region(world, player, locations_per_region, "2-7"),
+ create_region(world, player, locations_per_region, "2-8"),
+ create_region(world, player, locations_per_region, "Roger The Ghost's Boss Room"),
+
+ create_region(world, player, locations_per_region, "3-1"),
+ create_region(world, player, locations_per_region, "3-2"),
+ create_region(world, player, locations_per_region, "3-3"),
+ create_region(world, player, locations_per_region, "3-4"),
+ create_region(world, player, locations_per_region, "Prince Froggy's Boss Room"),
+ create_region(world, player, locations_per_region, "3-5"),
+ create_region(world, player, locations_per_region, "3-6"),
+ create_region(world, player, locations_per_region, "3-7"),
+ create_region(world, player, locations_per_region, "3-8"),
+ create_region(world, player, locations_per_region, "Naval Piranha's Boss Room"),
+
+ create_region(world, player, locations_per_region, "4-1"),
+ create_region(world, player, locations_per_region, "4-2"),
+ create_region(world, player, locations_per_region, "4-3"),
+ create_region(world, player, locations_per_region, "4-4"),
+ create_region(world, player, locations_per_region, "Marching Milde's Boss Room"),
+ create_region(world, player, locations_per_region, "4-5"),
+ create_region(world, player, locations_per_region, "4-6"),
+ create_region(world, player, locations_per_region, "4-7"),
+ create_region(world, player, locations_per_region, "4-8"),
+ create_region(world, player, locations_per_region, "Hookbill The Koopa's Boss Room"),
+
+ create_region(world, player, locations_per_region, "5-1"),
+ create_region(world, player, locations_per_region, "5-2"),
+ create_region(world, player, locations_per_region, "5-3"),
+ create_region(world, player, locations_per_region, "5-4"),
+ create_region(world, player, locations_per_region, "Sluggy The Unshaven's Boss Room"),
+ create_region(world, player, locations_per_region, "5-5"),
+ create_region(world, player, locations_per_region, "5-6"),
+ create_region(world, player, locations_per_region, "5-7"),
+ create_region(world, player, locations_per_region, "5-8"),
+ create_region(world, player, locations_per_region, "Raphael The Raven's Boss Room"),
+
+ create_region(world, player, locations_per_region, "6-1"),
+ create_region(world, player, locations_per_region, "6-2"),
+ create_region(world, player, locations_per_region, "6-3"),
+ create_region(world, player, locations_per_region, "6-4"),
+ create_region(world, player, locations_per_region, "Tap-Tap The Red Nose's Boss Room"),
+ create_region(world, player, locations_per_region, "6-5"),
+ create_region(world, player, locations_per_region, "6-6"),
+ create_region(world, player, locations_per_region, "6-7"),
+ create_region(world, player, locations_per_region, "6-8"),
+ create_region(world, player, locations_per_region, "Bowser's Room"),
+ ]
+
+ if world.options.extras_enabled:
+ regions.insert(68, create_region(world, player, locations_per_region, "6-Extra"))
+ regions.insert(58, create_region(world, player, locations_per_region, "5-Extra"))
+ regions.insert(48, create_region(world, player, locations_per_region, "4-Extra"))
+ regions.insert(38, create_region(world, player, locations_per_region, "3-Extra"))
+ regions.insert(28, create_region(world, player, locations_per_region, "2-Extra"))
+ regions.insert(18, create_region(world, player, locations_per_region, "1-Extra"))
+
+ if world.options.minigame_checks in {MinigameChecks.option_bonus_games, MinigameChecks.option_both}:
+ regions.insert(74, create_region(world, player, locations_per_region, "6-Bonus"))
+ regions.insert(63, create_region(world, player, locations_per_region, "5-Bonus"))
+ regions.insert(52, create_region(world, player, locations_per_region, "4-Bonus"))
+ regions.insert(41, create_region(world, player, locations_per_region, "3-Bonus"))
+ regions.insert(29, create_region(world, player, locations_per_region, "2-Bonus"))
+ regions.insert(19, create_region(world, player, locations_per_region, "1-Bonus"))
+
+ multiworld.regions += regions
+
+ connect_starting_region(world)
+
+ bosses = BossReqs(world)
+
+ multiworld.get_region("Overworld", player).add_exits(
+ ["World 1", "World 2", "World 3", "World 4", "World 5", "World 6"],
+ {
+ "World 1": lambda state: state.has("World 1 Gate", player),
+ "World 2": lambda state: state.has("World 2 Gate", player),
+ "World 3": lambda state: state.has("World 3 Gate", player),
+ "World 4": lambda state: state.has("World 4 Gate", player),
+ "World 5": lambda state: state.has("World 5 Gate", player),
+ "World 6": lambda state: state.has("World 6 Gate", player)
+ }
+ )
+
+ for cur_world in range(1, 7):
+ for cur_level in range(8):
+ if cur_world != 6 or cur_level != 7:
+ multiworld.get_region(f"World {cur_world}", player).add_exits(
+ [world.level_location_list[(cur_world - 1) * 8 + cur_level]]
+ )
+
+ multiworld.get_region("1-4", player).add_exits([world.boss_order[0]],{world.boss_order[0]: lambda state: logic._14Clear(state)})
+ multiworld.get_region("1-8", player).add_exits([world.boss_order[1]],{world.boss_order[1]: lambda state: logic._18Clear(state)})
+ multiworld.get_region("2-4", player).add_exits([world.boss_order[2]],{world.boss_order[2]: lambda state: logic._24Clear(state)})
+ multiworld.get_region("2-8", player).add_exits([world.boss_order[3]],{world.boss_order[3]: lambda state: logic._28Clear(state)})
+ multiworld.get_region("3-4", player).add_exits([world.boss_order[4]],{world.boss_order[4]: lambda state: logic._34Clear(state)})
+ multiworld.get_region("3-8", player).add_exits([world.boss_order[5]],{world.boss_order[5]: lambda state: logic._38Clear(state)})
+ multiworld.get_region("4-4", player).add_exits([world.boss_order[6]],{world.boss_order[6]: lambda state: logic._44Clear(state)})
+ multiworld.get_region("4-8", player).add_exits([world.boss_order[7]],{world.boss_order[7]: lambda state: logic._48Clear(state)})
+ multiworld.get_region("5-4", player).add_exits([world.boss_order[8]],{world.boss_order[8]: lambda state: logic._54Clear(state)})
+ multiworld.get_region("5-8", player).add_exits([world.boss_order[9]],{world.boss_order[9]: lambda state: logic._58Clear(state)})
+ multiworld.get_region("World 6", player).add_exits(["6-8"],{"6-8": lambda state: bosses.castle_access(state)})
+ multiworld.get_region("6-4", player).add_exits([world.boss_order[10]],{world.boss_order[10]: lambda state: logic._64Clear(state)})
+ multiworld.get_region("6-8", player).add_exits(["Bowser's Room"],{"Bowser's Room": lambda state: bosses.castle_clear(state)})
+
+ if world.options.extras_enabled:
+ multiworld.get_region("World 1", player).add_exits(
+ ["1-Extra"],
+ {"1-Extra": lambda state: state.has_any({"Extra Panels", "Extra 1"}, player)}
+ )
+ multiworld.get_region("World 2", player).add_exits(
+ ["2-Extra"],
+ {"2-Extra": lambda state: state.has_any({"Extra Panels", "Extra 2"}, player)}
+ )
+ multiworld.get_region(
+ "World 3", player).add_exits(["3-Extra"],
+ {"3-Extra": lambda state: state.has_any({"Extra Panels", "Extra 3"}, player)}
+ )
+ multiworld.get_region("World 4", player).add_exits(
+ ["4-Extra"],
+ {"4-Extra": lambda state: state.has_any({"Extra Panels", "Extra 4"}, player)}
+ )
+ multiworld.get_region("World 5", player).add_exits(
+ ["5-Extra"],
+ {"5-Extra": lambda state: state.has_any({"Extra Panels", "Extra 5"}, player)}
+ )
+ multiworld.get_region("World 6", player).add_exits(
+ ["6-Extra"],
+ {"6-Extra": lambda state: state.has_any({"Extra Panels", "Extra 6"}, player)}
+ )
+
+ if world.options.minigame_checks in {MinigameChecks.option_bonus_games, MinigameChecks.option_both}:
+ multiworld.get_region("World 1", player).add_exits(
+ ["1-Bonus"],
+ {"1-Bonus": lambda state: state.has_any({"Bonus Panels", "Bonus 1"}, player)}
+ )
+ multiworld.get_region("World 2", player).add_exits(
+ ["2-Bonus"],
+ {"2-Bonus": lambda state: state.has_any({"Bonus Panels", "Bonus 2"}, player)}
+ )
+ multiworld.get_region("World 3", player).add_exits(
+ ["3-Bonus"],
+ {"3-Bonus": lambda state: state.has_any({"Bonus Panels", "Bonus 3"}, player)}
+ )
+ multiworld.get_region("World 4", player).add_exits(
+ ["4-Bonus"],
+ {"4-Bonus": lambda state: state.has_any({"Bonus Panels", "Bonus 4"}, player)}
+ )
+ multiworld.get_region("World 5", player).add_exits(
+ ["5-Bonus"],
+ {"5-Bonus": lambda state: state.has_any({"Bonus Panels", "Bonus 5"}, player)}
+ )
+ multiworld.get_region("World 6", player).add_exits(
+ ["6-Bonus"],
+ {"6-Bonus": lambda state: state.has_any({"Bonus Panels", "Bonus 6"}, player)}
+ )
+
+
+def create_location(player: int, location_data: LocationData, region: Region) -> Location:
+ location = YoshisIslandLocation(player, location_data.name, location_data.code, region)
+ location.access_rule = location_data.rule
+ location.level_id = location_data.LevelID
+
+ return location
+
+
+def create_region(world: "YoshisIslandWorld", player: int, locations_per_region: Dict[str, List[LocationData]], name: str) -> Region:
+ region = Region(name, player, world.multiworld)
+
+ if name in locations_per_region:
+ for location_data in locations_per_region[name]:
+ location = create_location(player, location_data, region)
+ region.locations.append(location)
+
+ return region
+
+def connect_starting_region(world: "YoshisIslandWorld") -> None:
+ multiworld = world.multiworld
+ player = world.player
+ menu = multiworld.get_region("Menu", player)
+ world_main = multiworld.get_region("Overworld", player)
+
+ starting_region = multiworld.get_region(f"World {world.options.starting_world + 1}", player)
+
+ menu.connect(world_main, "Start Game")
+ world_main.connect(starting_region, "Overworld")
+
+
+def get_locations_per_region(locations: List[LocationData]) -> Dict[str, List[LocationData]]:
+ per_region: Dict[str, List[LocationData]] = {}
+
+ for location in locations:
+ per_region.setdefault(location.region, []).append(location)
+
+ return per_region
diff --git a/worlds/yoshisisland/Rom.py b/worlds/yoshisisland/Rom.py
new file mode 100644
index 000000000000..fa3006afcf9f
--- /dev/null
+++ b/worlds/yoshisisland/Rom.py
@@ -0,0 +1,1230 @@
+import hashlib
+import os
+import Utils
+from worlds.Files import APDeltaPatch
+from settings import get_settings
+from typing import TYPE_CHECKING
+
+from .Options import YoshiColors, BowserDoor, PlayerGoal, MinigameChecks
+
+if TYPE_CHECKING:
+ from . import YoshisIslandWorld
+USHASH = "cb472164c5a71ccd3739963390ec6a50"
+
+item_values = {
+ 0x302050: [0x1467, 0x01], # ! Switch
+ 0x302051: [0x1467, 0x02], # Dashed Platform
+ 0x302052: [0x1467, 0x03], # Dashed Stairs
+ 0x302053: [0x1467, 0x04], # Beanstalk
+ 0x302054: [0x1467, 0x05], # Helicopter
+ 0x302059: [0x1467, 0x06], # Mole Tank
+ 0x302068: [0x1467, 0x07], # Train
+ 0x30205E: [0x1467, 0x08], # Car
+ 0x302063: [0x1467, 0x09], # Submarine
+ 0x302055: [0x1467, 0x0A], # Spring Ball
+ 0x302056: [0x1467, 0x0B], # Large Spring Ball
+ 0x302057: [0x1467, 0x0C], # Arrow Wheel
+ 0x302058: [0x1467, 0x0D], # Vanishing Arrow Wheel
+ 0x30205A: [0x1467, 0x0E], # Watermelon
+ 0x30205B: [0x1467, 0x0F], # Ice Melon
+ 0x30205C: [0x1467, 0x10], # Fire Melon
+ 0x30205D: [0x1467, 0x11], # Super Star
+ 0x30205F: [0x1467, 0x12], # Flashing Eggs
+ 0x302060: [0x1467, 0x13], # Giant Eggs
+ 0x302061: [0x1467, 0x14], # Egg Launcher
+ 0x302062: [0x1467, 0x15], # Egg Plant
+ 0x302064: [0x1467, 0x16], # Chomp Rock
+ 0x302065: [0x1467, 0x17], # Poochy
+ 0x302066: [0x1467, 0x18], # Platform Ghost
+ 0x302067: [0x1467, 0x19], # Skis
+ 0x302069: [0x1467, 0x1A], # Key
+ 0x30206A: [0x1467, 0x1B], # Middle Ring
+ 0x30206B: [0x1467, 0x1C], # Bucket
+ 0x30206C: [0x1467, 0x1D], # Tulip
+ 0x302081: [0x1467, 0x1E], # Secret Lens
+
+ 0x30206D: [0x1467, 0x1F], # Egg Capacity Upgrade
+
+ 0x30206E: [0x1467, 0x20], # World 1 Gate
+ 0x30206F: [0x1467, 0x21], # World 2 Gate
+ 0x302070: [0x1467, 0x22], # World 3 Gate
+ 0x302071: [0x1467, 0x23], # World 4 Gate
+ 0x302072: [0x1467, 0x24], # World 5 Gate
+ 0x302073: [0x1467, 0x25], # World 6 Gate
+
+ 0x302074: [0x1467, 0x26], # Extra 1
+ 0x302075: [0x1467, 0x27], # Extra 2
+ 0x302076: [0x1467, 0x28], # Extra 3
+ 0x302077: [0x1467, 0x29], # Extra 4
+ 0x302078: [0x1467, 0x2A], # Extra 5
+ 0x302079: [0x1467, 0x2B], # Extra 6
+ 0x30207A: [0x1467, 0x2C], # Extra Panels
+
+ 0x30207B: [0x1467, 0x2D], # Bonus 1
+ 0x30207C: [0x1467, 0x2E], # Bonus 2
+ 0x30207D: [0x1467, 0x2F], # Bonus 3
+ 0x30207E: [0x1467, 0x30], # Bonus 4
+ 0x30207F: [0x1467, 0x31], # Bonus 5
+ 0x302080: [0x1467, 0x32], # Bonus 6
+ 0x302082: [0x1467, 0x33], # Bonus Panels
+
+ 0x302083: [0x1467, 0x34], # Anytime Egg
+ 0x302084: [0x1467, 0x35], # Anywhere Pow
+ 0x302085: [0x1467, 0x36], # Cloud
+ 0x302086: [0x1467, 0x37], # Pocket Melon
+ 0x302088: [0x1467, 0x38], # Ice Melon
+ 0x302087: [0x1467, 0x39], # Fire Melon
+ 0x302089: [0x1467, 0x3A], # Magnifying Glass
+ 0x30208A: [0x1467, 0x3B], # 10 Stars
+ 0x30208B: [0x1467, 0x3C], # 20 Stars
+
+ 0x30208C: [0x1467, 0x3D], # 1up
+ 0x30208D: [0x1467, 0x3E], # 2up
+ 0x30208E: [0x1467, 0x3F], # 3up
+ 0x30208F: [0x1467, 0x40], # 10up
+
+ 0x302090: [0x1467, 0x41], # Fuzzy Trap
+ 0x302093: [0x1467, 0x42], # Freeze Trap
+ 0x302091: [0x1467, 0x43], # Reverse Trap
+ 0x302092: [0x1467, 0x44], # Dark Trap
+ 0x302094: [0x1467, 0x00], # Boss clear, local handling
+
+ 0x302095: [0x1467, 0x45] # Luigi Piece
+
+}
+
+location_table = {
+ # 1-1
+ 0x305020: [0x146D, 0], # Red Coins
+ 0x305021: [0x146D, 1], # Flowers
+ 0x305022: [0x146D, 2], # Stars
+ 0x305023: [0x146D, 3], # Level Clear
+ # 1-2
+ 0x305024: [0x146E, 0],
+ 0x305025: [0x146E, 1],
+ 0x305026: [0x146E, 2],
+ 0x305027: [0x146E, 3],
+ # 1-3
+ 0x305028: [0x146F, 0],
+ 0x305029: [0x146F, 1],
+ 0x30502A: [0x146F, 2],
+ 0x30502B: [0x146F, 3],
+ 0x3050F8: [0x146F, 4],
+ # 1-4
+ 0x30502C: [0x1470, 0],
+ 0x30502D: [0x1470, 1],
+ 0x30502E: [0x1470, 2],
+ 0x30502F: [0x1470, 3],
+ # 1-5
+ 0x305031: [0x1471, 0],
+ 0x305032: [0x1471, 1],
+ 0x305033: [0x1471, 2],
+ 0x305034: [0x1471, 3],
+ # 1-6
+ 0x305035: [0x1472, 0],
+ 0x305036: [0x1472, 1],
+ 0x305037: [0x1472, 2],
+ 0x305038: [0x1472, 3],
+ # 1-7
+ 0x305039: [0x1473, 0],
+ 0x30503A: [0x1473, 1],
+ 0x30503B: [0x1473, 2],
+ 0x30503C: [0x1473, 3],
+ 0x3050F9: [0x1473, 4],
+ # 1-8
+ 0x30503D: [0x1474, 0],
+ 0x30503E: [0x1474, 1],
+ 0x30503F: [0x1474, 2],
+ 0x305040: [0x1474, 3],
+ # 1-E
+ 0x3050E0: [0x1475, 0],
+ 0x3050E1: [0x1475, 1],
+ 0x3050E2: [0x1475, 2],
+ 0x3050E3: [0x1475, 3],
+ # 1-B
+ 0x305106: [0x1476, 4],
+ ######################
+ # 2-1
+ 0x305041: [0x1479, 0],
+ 0x305042: [0x1479, 1],
+ 0x305043: [0x1479, 2],
+ 0x305044: [0x1479, 3],
+ 0x3050FA: [0x1479, 4],
+ # 2-2
+ 0x305045: [0x147A, 0],
+ 0x305046: [0x147A, 1],
+ 0x305047: [0x147A, 2],
+ 0x305048: [0x147A, 3],
+ # 2-3
+ 0x305049: [0x147B, 0],
+ 0x30504A: [0x147B, 1],
+ 0x30504B: [0x147B, 2],
+ 0x30504C: [0x147B, 3],
+ 0x3050FB: [0x147B, 4],
+ # 2-4
+ 0x30504D: [0x147C, 0],
+ 0x30504E: [0x147C, 1],
+ 0x30504F: [0x147C, 2],
+ 0x305050: [0x147C, 3],
+ # 2-5
+ 0x305051: [0x147D, 0],
+ 0x305052: [0x147D, 1],
+ 0x305053: [0x147D, 2],
+ 0x305054: [0x147D, 3],
+ # 2-6
+ 0x305055: [0x147E, 0],
+ 0x305056: [0x147E, 1],
+ 0x305057: [0x147E, 2],
+ 0x305058: [0x147E, 3],
+ 0x3050FC: [0x147E, 4],
+ # 2-7
+ 0x305059: [0x147F, 0],
+ 0x30505A: [0x147F, 1],
+ 0x30505B: [0x147F, 2],
+ 0x30505C: [0x147F, 3],
+ 0x3050FD: [0x147F, 4],
+ # 2-8
+ 0x30505D: [0x1480, 0],
+ 0x30505E: [0x1480, 1],
+ 0x30505F: [0x1480, 2],
+ 0x305060: [0x1480, 3],
+ # 2-E
+ 0x3050E4: [0x1481, 0],
+ 0x3050E5: [0x1481, 1],
+ 0x3050E6: [0x1481, 2],
+ 0x3050E7: [0x1481, 3],
+ # 2-B
+ 0x305107: [0x1482, 4],
+ ######################
+ # 3-1
+ 0x305061: [0x1485, 0],
+ 0x305062: [0x1485, 1],
+ 0x305063: [0x1485, 2],
+ 0x305064: [0x1485, 3],
+ # 3-2
+ 0x305065: [0x1486, 0],
+ 0x305066: [0x1486, 1],
+ 0x305067: [0x1486, 2],
+ 0x305068: [0x1486, 3],
+ 0x3050FE: [0x1486, 4],
+ # 3-3
+ 0x305069: [0x1487, 0],
+ 0x30506A: [0x1487, 1],
+ 0x30506B: [0x1487, 2],
+ 0x30506C: [0x1487, 3],
+ # 3-4
+ 0x30506D: [0x1488, 0],
+ 0x30506E: [0x1488, 1],
+ 0x30506F: [0x1488, 2],
+ 0x305070: [0x1488, 3],
+ # 3-5
+ 0x305071: [0x1489, 0],
+ 0x305072: [0x1489, 1],
+ 0x305073: [0x1489, 2],
+ 0x305074: [0x1489, 3],
+ # 3-6
+ 0x305075: [0x148A, 0],
+ 0x305076: [0x148A, 1],
+ 0x305077: [0x148A, 2],
+ 0x305078: [0x148A, 3],
+ # 3-7
+ 0x305079: [0x148B, 0],
+ 0x30507A: [0x148B, 1],
+ 0x30507B: [0x148B, 2],
+ 0x30507C: [0x148B, 3],
+ 0x3050FF: [0x148B, 4],
+ # 3-8
+ 0x30507D: [0x148C, 0],
+ 0x30507E: [0x148C, 1],
+ 0x30507F: [0x148C, 2],
+ 0x305080: [0x148C, 3],
+ # 3-E
+ 0x3050E8: [0x148D, 0],
+ 0x3050E9: [0x148D, 1],
+ 0x3050EA: [0x148D, 2],
+ 0x3050EB: [0x148D, 3],
+ # 3-B
+ 0x305108: [0x148E, 4],
+ ######################
+ # 4-1
+ 0x305081: [0x1491, 0],
+ 0x305082: [0x1491, 1],
+ 0x305083: [0x1491, 2],
+ 0x305084: [0x1491, 3],
+ # 4-2
+ 0x305085: [0x1492, 0],
+ 0x305086: [0x1492, 1],
+ 0x305087: [0x1492, 2],
+ 0x305088: [0x1492, 3],
+ 0x305100: [0x1492, 4],
+ # 4-3
+ 0x305089: [0x1493, 0],
+ 0x30508A: [0x1493, 1],
+ 0x30508B: [0x1493, 2],
+ 0x30508C: [0x1493, 3],
+ # 4-4
+ 0x30508D: [0x1494, 0],
+ 0x30508E: [0x1494, 1],
+ 0x30508F: [0x1494, 2],
+ 0x305090: [0x1494, 3],
+ # 4-5
+ 0x305091: [0x1495, 0],
+ 0x305092: [0x1495, 1],
+ 0x305093: [0x1495, 2],
+ 0x305094: [0x1495, 3],
+ # 4-6
+ 0x305095: [0x1496, 0],
+ 0x305096: [0x1496, 1],
+ 0x305097: [0x1496, 2],
+ 0x305098: [0x1496, 3],
+ 0x305101: [0x1496, 4],
+ # 4-7
+ 0x305099: [0x1497, 0],
+ 0x30509A: [0x1497, 1],
+ 0x30509B: [0x1497, 2],
+ 0x30509C: [0x1497, 3],
+ 0x305102: [0x1497, 4],
+ # 4-8
+ 0x30509D: [0x1498, 0],
+ 0x30509E: [0x1498, 1],
+ 0x30509F: [0x1498, 2],
+ 0x3050A0: [0x1498, 3],
+ # 4-E
+ 0x3050EC: [0x1499, 0],
+ 0x3050ED: [0x1499, 1],
+ 0x3050EE: [0x1499, 2],
+ 0x3050EF: [0x1499, 3],
+ # 4-B
+ 0x305109: [0x149A, 4],
+ ######################
+ # 5-1
+ 0x3050A1: [0x149D, 0],
+ 0x3050A2: [0x149D, 1],
+ 0x3050A3: [0x149D, 2],
+ 0x3050A4: [0x149D, 3],
+ 0x305103: [0x149D, 4],
+ # 5-2
+ 0x3050A5: [0x149E, 0],
+ 0x3050A6: [0x149E, 1],
+ 0x3050A7: [0x149E, 2],
+ 0x3050A8: [0x149E, 3],
+ # 5-3
+ 0x3050A9: [0x149F, 0],
+ 0x3050AA: [0x149F, 1],
+ 0x3050AB: [0x149F, 2],
+ 0x3050AC: [0x149F, 3],
+ # 5-4
+ 0x3050AD: [0x14A0, 0],
+ 0x3050AE: [0x14A0, 1],
+ 0x3050AF: [0x14A0, 2],
+ 0x3050B0: [0x14A0, 3],
+ # 5-5
+ 0x3050B1: [0x14A1, 0],
+ 0x3050B2: [0x14A1, 1],
+ 0x3050B3: [0x14A1, 2],
+ 0x3050B4: [0x14A1, 3],
+ # 5-6
+ 0x3050B5: [0x14A2, 0],
+ 0x3050B6: [0x14A2, 1],
+ 0x3050B7: [0x14A2, 2],
+ 0x3050B8: [0x14A2, 3],
+ # 5-7
+ 0x3050B9: [0x14A3, 0],
+ 0x3050BA: [0x14A3, 1],
+ 0x3050BB: [0x14A3, 2],
+ 0x3050BC: [0x14A3, 3],
+ # 5-8
+ 0x3050BD: [0x14A4, 0],
+ 0x3050BE: [0x14A4, 1],
+ 0x3050BF: [0x14A4, 2],
+ 0x3050C0: [0x14A4, 3],
+ # 5-E
+ 0x3050F0: [0x14A5, 0],
+ 0x3050F1: [0x14A5, 1],
+ 0x3050F2: [0x14A5, 2],
+ 0x3050F3: [0x14A5, 3],
+ # 5-B
+ 0x30510A: [0x14A6, 4],
+ #######################
+ # 6-1
+ 0x3050C1: [0x14A9, 0],
+ 0x3050C2: [0x14A9, 1],
+ 0x3050C3: [0x14A9, 2],
+ 0x3050C4: [0x14A9, 3],
+ 0x305104: [0x14A9, 4],
+ # 6-2
+ 0x3050C5: [0x14AA, 0],
+ 0x3050C6: [0x14AA, 1],
+ 0x3050C7: [0x14AA, 2],
+ 0x3050C8: [0x14AA, 3],
+ # 6-3
+ 0x3050C9: [0x14AB, 0],
+ 0x3050CA: [0x14AB, 1],
+ 0x3050CB: [0x14AB, 2],
+ 0x3050CC: [0x14AB, 3],
+ # 6-4
+ 0x3050CD: [0x14AC, 0],
+ 0x3050CE: [0x14AC, 1],
+ 0x3050CF: [0x14AC, 2],
+ 0x3050D0: [0x14AC, 3],
+ # 6-5
+ 0x3050D1: [0x14AD, 0],
+ 0x3050D2: [0x14AD, 1],
+ 0x3050D3: [0x14AD, 2],
+ 0x3050D4: [0x14AD, 3],
+ # 6-6
+ 0x3050D5: [0x14AE, 0],
+ 0x3050D6: [0x14AE, 1],
+ 0x3050D7: [0x14AE, 2],
+ 0x3050D8: [0x14AE, 3],
+ # 6-7
+ 0x3050D9: [0x14AF, 0],
+ 0x3050DA: [0x14AF, 1],
+ 0x3050DB: [0x14AF, 2],
+ 0x3050DC: [0x14AF, 3],
+ 0x305105: [0x14AF, 4],
+ # 6-8
+ 0x3050DD: [0x14B0, 0],
+ 0x3050DE: [0x14B0, 1],
+ 0x3050DF: [0x14B0, 2],
+ # 6-E
+ 0x3050F4: [0x14B1, 0],
+ 0x3050F5: [0x14B1, 1],
+ 0x3050F6: [0x14B1, 2],
+ 0x3050F7: [0x14B1, 3],
+ # 6-B
+ 0x30510B: [0x14B2, 4]
+}
+
+class LocalRom(object):
+
+ def __init__(self, file: str) -> None:
+ self.name = None
+ self.hash = hash
+ self.orig_buffer = None
+
+ with open(file, "rb") as stream:
+ self.buffer = Utils.read_snes_rom(stream)
+
+ def read_bit(self, address: int, bit_number: int) -> bool:
+ bitflag = 1 << bit_number
+ return (self.buffer[address] & bitflag) != 0
+
+ def read_byte(self, address: int) -> int:
+ return self.buffer[address]
+
+ def read_bytes(self, startaddress: int, length: int) -> bytes:
+ return self.buffer[startaddress:startaddress + length]
+
+ def write_byte(self, address: int, value: int) -> None:
+ self.buffer[address] = value
+
+ def write_bytes(self, startaddress: int, values: bytearray) -> None:
+ self.buffer[startaddress:startaddress + len(values)] = values
+
+ def write_to_file(self, file: str) -> None:
+ with open(file, "wb") as outfile:
+ outfile.write(self.buffer)
+
+ def read_from_file(self, file: str) -> None:
+ with open(file, "rb") as stream:
+ self.buffer = bytearray(stream.read())
+
+def handle_items(rom: LocalRom) -> None:
+ rom.write_bytes(0x0077B0, bytearray([0xE2, 0x20, 0xAD, 0x40, 0x14, 0xC2, 0x20, 0xF0, 0x08, 0xBD, 0x82, 0x71, 0x18, 0x5C, 0x3B, 0xB6]))
+ rom.write_bytes(0x0077C0, bytearray([0x0E, 0x5C, 0x97, 0xB6, 0x0E, 0xA0, 0xFF, 0xAD, 0x74, 0x79, 0x29, 0x01, 0x00, 0xD0, 0x02, 0xA0]))
+ rom.write_bytes(0x0077D0, bytearray([0x05, 0x98, 0x9D, 0xA2, 0x74, 0x6B, 0xE2, 0x20, 0xBD, 0x60, 0x73, 0xDA, 0xC2, 0x20, 0xA2, 0x00]))
+ rom.write_bytes(0x0077E0, bytearray([0xDF, 0x70, 0xAF, 0x09, 0xF0, 0x08, 0xE8, 0xE8, 0xE0, 0x08, 0xF0, 0x23, 0x80, 0xF2, 0xE2, 0x20]))
+ rom.write_bytes(0x0077F0, bytearray([0x8A, 0x4A, 0xAA, 0xBF, 0x78, 0xAF, 0x09, 0xAA, 0xBD, 0x40, 0x14, 0xC2, 0x20, 0xF0, 0x08, 0xFA]))
+ rom.write_bytes(0x007800, bytearray([0xA0, 0x05, 0x98, 0x9D, 0xA2, 0x74, 0x60, 0xFA, 0x22, 0xC5, 0xF7, 0x00, 0xEA, 0xEA, 0x60, 0xFA]))
+ rom.write_bytes(0x007810, bytearray([0x60, 0x22, 0x23, 0xAF, 0x03, 0x20, 0xD6, 0xF7, 0x6B, 0x20, 0x2F, 0xF8, 0xE2, 0x20, 0xC9, 0x00]))
+ rom.write_bytes(0x007820, bytearray([0xD0, 0x03, 0xC2, 0x20, 0x6B, 0xC2, 0x20, 0xBD, 0x60, 0x73, 0x38, 0x5C, 0xB1, 0xC9, 0x03, 0xDA]))
+ rom.write_bytes(0x007830, bytearray([0xBD, 0x60, 0x73, 0xA2, 0x00, 0xDF, 0x7C, 0xAF, 0x09, 0xF0, 0x08, 0xE8, 0xE8, 0xE0, 0x0A, 0xF0]))
+ rom.write_bytes(0x007840, bytearray([0x13, 0x80, 0xF2, 0xE2, 0x20, 0x8A, 0x4A, 0xAA, 0xBF, 0x86, 0xAF, 0x09, 0xAA, 0xBD, 0x40, 0x14]))
+ rom.write_bytes(0x007850, bytearray([0xFA, 0xC2, 0x20, 0x60, 0xA9, 0x01, 0x00, 0xFA, 0x60, 0x20, 0x2F, 0xF8, 0xE2, 0x20, 0xC9, 0x00]))
+ rom.write_bytes(0x007860, bytearray([0xC2, 0x20, 0xD0, 0x06, 0x22, 0xC5, 0xF7, 0x00, 0x80, 0x04, 0x22, 0xCF, 0xF7, 0x00, 0xA5, 0x14]))
+ rom.write_bytes(0x007870, bytearray([0x29, 0x0F, 0x00, 0x5C, 0x9A, 0xC9, 0x03, 0x5A, 0xE2, 0x10, 0x20, 0x2F, 0xF8, 0xC2, 0x10, 0x7A]))
+ rom.write_bytes(0x007880, bytearray([0xE2, 0x20, 0xC9, 0x00, 0xC2, 0x20, 0xD0, 0x08, 0xAD, 0x74, 0x79, 0x29, 0x01, 0x00, 0xF0, 0x04]))
+ rom.write_bytes(0x007890, bytearray([0x22, 0x3C, 0xAA, 0x03, 0xE2, 0x10, 0x5C, 0x47, 0xC9, 0x03, 0x22, 0x23, 0xAF, 0x03, 0xBD, 0x60]))
+ rom.write_bytes(0x0078A0, bytearray([0x73, 0xC9, 0x6F, 0x00, 0xF0, 0x07, 0xE2, 0x20, 0xAD, 0x4A, 0x14, 0x80, 0x05, 0xE2, 0x20, 0xAD]))
+ rom.write_bytes(0x0078B0, bytearray([0x49, 0x14, 0xC2, 0x20, 0xD0, 0x06, 0x22, 0xC5, 0xF7, 0x00, 0x80, 0x04, 0x22, 0xCF, 0xF7, 0x00]))
+ rom.write_bytes(0x0078C0, bytearray([0x5C, 0x2D, 0x83, 0x05, 0xBD, 0x60, 0x73, 0xC9, 0x6F, 0x00, 0xF0, 0x07, 0xE2, 0x20, 0xAD, 0x4A]))
+ rom.write_bytes(0x0078D0, bytearray([0x14, 0x80, 0x05, 0xE2, 0x20, 0xAD, 0x49, 0x14, 0xC2, 0x20, 0xD0, 0x04, 0x5C, 0xA0, 0x83, 0x05]))
+ rom.write_bytes(0x0078E0, bytearray([0xAD, 0xAC, 0x60, 0x0D, 0xAE, 0x60, 0x5C, 0x84, 0x83, 0x05, 0x22, 0x52, 0xAA, 0x03, 0xBD, 0x60]))
+ rom.write_bytes(0x0078F0, bytearray([0x73, 0xC9, 0x1E, 0x01, 0xE2, 0x20, 0xF0, 0x05, 0xAD, 0x4C, 0x14, 0x80, 0x03, 0xAD, 0x4B, 0x14]))
+ rom.write_bytes(0x007900, bytearray([0xEA, 0xC2, 0x20, 0xF0, 0x08, 0x22, 0xCF, 0xF7, 0x00, 0x5C, 0xA6, 0xF0, 0x05, 0x22, 0xC5, 0xF7]))
+ rom.write_bytes(0x007910, bytearray([0x00, 0x5C, 0xA6, 0xF0, 0x05, 0xE2, 0x20, 0xAD, 0x1C, 0x01, 0xC9, 0x0E, 0xC2, 0x20, 0xF0, 0x18]))
+ rom.write_bytes(0x007920, bytearray([0x20, 0x59, 0xF9, 0xE2, 0x20, 0xC9, 0x00, 0xF0, 0x04, 0xA9, 0x10, 0x80, 0x2A, 0xA9, 0x02, 0x9D]))
+ rom.write_bytes(0x007930, bytearray([0x00, 0x6F, 0xC2, 0x20, 0x22, 0xC5, 0xF7, 0x00, 0xA2, 0x0A, 0xA9, 0x2F, 0xCE, 0x5C, 0x22, 0x80]))
+ rom.write_bytes(0x007940, bytearray([0x04, 0x20, 0x59, 0xF9, 0xE2, 0x20, 0xC9, 0x00, 0xC2, 0x20, 0xF0, 0x0A, 0xAD, 0x0E, 0x30, 0x29]))
+ rom.write_bytes(0x007950, bytearray([0x03, 0x00, 0x5C, 0x2E, 0x80, 0x04, 0x6B, 0x80, 0xD6, 0xDA, 0xBD, 0x60, 0x73, 0xA2, 0x00, 0xDF]))
+ rom.write_bytes(0x007960, bytearray([0x8B, 0xAF, 0x09, 0xF0, 0x04, 0xE8, 0xE8, 0x80, 0xF6, 0xE2, 0x20, 0x8A, 0x4A, 0xAA, 0xBF, 0x91]))
+ rom.write_bytes(0x007970, bytearray([0xAF, 0x09, 0xAA, 0xBD, 0x40, 0x14, 0xFA, 0xC2, 0x20, 0x60, 0x22, 0x2E, 0xAA, 0x03, 0xE2, 0x20]))
+ rom.write_bytes(0x007980, bytearray([0xAD, 0x50, 0x14, 0xC2, 0x20, 0xD0, 0x06, 0x22, 0xC5, 0xF7, 0x00, 0x80, 0x04, 0x22, 0xCF, 0xF7]))
+ rom.write_bytes(0x007990, bytearray([0x00, 0x5C, 0x05, 0x99, 0x02, 0x69, 0x20, 0x00, 0xC9, 0x20, 0x01, 0xB0, 0x0D, 0xE2, 0x20, 0xAD]))
+ rom.write_bytes(0x0079A0, bytearray([0x50, 0x14, 0xC2, 0x20, 0xF0, 0x04, 0x5C, 0x3E, 0x99, 0x02, 0x5C, 0x8C, 0x99, 0x02, 0x22, 0x23]))
+ rom.write_bytes(0x0079B0, bytearray([0xAF, 0x03, 0xE2, 0x20, 0xAD, 0x1C, 0x01, 0xC9, 0x02, 0xC2, 0x20, 0xD0, 0x18, 0x20, 0x59, 0xF9]))
+ rom.write_bytes(0x0079C0, bytearray([0xE2, 0x20, 0xC9, 0x00, 0xD0, 0x13, 0xC2, 0x20, 0x22, 0xC5, 0xF7, 0x00, 0xE2, 0x20, 0xA9, 0x02]))
+ rom.write_bytes(0x0079D0, bytearray([0x9D, 0x00, 0x6F, 0xC2, 0x20, 0x5C, 0x35, 0x80, 0x04, 0xC2, 0x20, 0x22, 0xCF, 0xF7, 0x00, 0x80]))
+ rom.write_bytes(0x0079E0, bytearray([0xF2, 0xE2, 0x20, 0xAD, 0x4E, 0x14, 0xC2, 0x20, 0xF0, 0x07, 0xA9, 0x14, 0x00, 0x5C, 0x9E, 0xF1]))
+ rom.write_bytes(0x0079F0, bytearray([0x07, 0xA9, 0x0E, 0x00, 0x80, 0xF7, 0xBD, 0x60, 0x73, 0xDA, 0xA2, 0x00, 0xDF, 0x94, 0xAF, 0x09]))
+ rom.write_bytes(0x007A00, bytearray([0xF0, 0x11, 0xE0, 0x08, 0xF0, 0x04, 0xE8, 0xE8, 0x80, 0xF2, 0xFA, 0x22, 0x57, 0xF9, 0x0C, 0x5C]))
+ rom.write_bytes(0x007A10, bytearray([0xBD, 0xBE, 0x03, 0x8A, 0x4A, 0xE2, 0x20, 0xAA, 0xBF, 0x9E, 0xAF, 0x09, 0xAA, 0xBD, 0x40, 0x14]))
+ rom.write_bytes(0x007A20, bytearray([0xC9, 0x00, 0xC2, 0x20, 0xF0, 0x02, 0x80, 0xE2, 0x4C, 0x61, 0xFF, 0x00, 0x9D, 0x00, 0x6F, 0x74]))
+ rom.write_bytes(0x007A30, bytearray([0x78, 0x74, 0x18, 0x74, 0x76, 0x9E, 0x36, 0x7A, 0x9E, 0x38, 0x7A, 0x9E, 0x38, 0x7D, 0xBC, 0xC2]))
+ rom.write_bytes(0x007A40, bytearray([0x77, 0xB9, 0xB5, 0xBE, 0x9D, 0x20, 0x72, 0xA9, 0x00, 0xFC, 0x9D, 0x22, 0x72, 0xA9, 0x40, 0x00]))
+ rom.write_bytes(0x007A50, bytearray([0x9D, 0x42, 0x75, 0xA9, 0x90, 0x00, 0x22, 0xD2, 0x85, 0x00, 0x6B, 0x5A, 0xE2, 0x20, 0xAD, 0x51]))
+ rom.write_bytes(0x007A60, bytearray([0x14, 0xC9, 0x00, 0xC2, 0x20, 0xF0, 0x0D, 0x22, 0xCF, 0xF7, 0x00, 0x7A, 0x9B, 0xAD, 0x30, 0x00]))
+ rom.write_bytes(0x007A70, bytearray([0x5C, 0x62, 0xB7, 0x03, 0x22, 0xC5, 0xF7, 0x00, 0x7A, 0x9B, 0xA9, 0x03, 0x00, 0x5C, 0x62, 0xB7]))
+ rom.write_bytes(0x007A80, bytearray([0x03, 0x22, 0x23, 0xAF, 0x03, 0xE2, 0x20, 0xAD, 0x53, 0x14, 0xF0, 0x07, 0xC2, 0x20, 0x22, 0xCF]))
+ rom.write_bytes(0x007A90, bytearray([0xF7, 0x00, 0x6B, 0xC2, 0x20, 0x22, 0xC5, 0xF7, 0x00, 0x6B, 0xE2, 0x20, 0xAD, 0x53, 0x14, 0xF0]))
+ rom.write_bytes(0x007AA0, bytearray([0x07, 0xC2, 0x20, 0x22, 0x78, 0xBA, 0x07, 0x6B, 0xC2, 0x20, 0x6B, 0xC9, 0x06, 0x00, 0xB0, 0x0F]))
+ rom.write_bytes(0x007AB0, bytearray([0xE2, 0x20, 0xAD, 0x54, 0x14, 0xC9, 0x00, 0xC2, 0x20, 0xF0, 0x04, 0x5C, 0x94, 0x81, 0x07, 0x5C]))
+ rom.write_bytes(0x007AC0, bytearray([0xFB, 0x81, 0x07, 0x22, 0x23, 0xAF, 0x03, 0xE2, 0x20, 0xAD, 0x54, 0x14, 0xC2, 0x20, 0xF0, 0x08]))
+ rom.write_bytes(0x007AD0, bytearray([0x22, 0xCF, 0xF7, 0x00, 0x5C, 0xF7, 0x80, 0x07, 0x22, 0xC5, 0xF7, 0x00, 0x5C, 0xF7, 0x80, 0x07]))
+ rom.write_bytes(0x007AE0, bytearray([0x5A, 0xE2, 0x20, 0xAD, 0x55, 0x14, 0xC2, 0x20, 0xF0, 0x06, 0x22, 0xCF, 0xF7, 0x00, 0x80, 0x06]))
+ rom.write_bytes(0x007AF0, bytearray([0x22, 0xC5, 0xF7, 0x00, 0x80, 0x04, 0x22, 0x65, 0xC3, 0x0E, 0x7A, 0x5C, 0xFA, 0xBE, 0x0E, 0xE2]))
+ rom.write_bytes(0x007B00, bytearray([0x20, 0xAD, 0x56, 0x14, 0xC2, 0x20, 0xF0, 0x0A, 0x22, 0xCF, 0xF7, 0x00, 0x22, 0xB7, 0xA5, 0x03]))
+ rom.write_bytes(0x007B10, bytearray([0x80, 0x04, 0x22, 0xC5, 0xF7, 0x00, 0x5C, 0x3D, 0x96, 0x07, 0xBD, 0x02, 0x79, 0x85, 0x0E, 0xE2]))
+ rom.write_bytes(0x007B20, bytearray([0x20, 0xAD, 0x57, 0x14, 0xC2, 0x20, 0xF0, 0x05, 0x22, 0xCF, 0xF7, 0x00, 0x6B, 0x22, 0xC5, 0xF7]))
+ rom.write_bytes(0x007B30, bytearray([0x00, 0x6B, 0xE2, 0x20, 0xAD, 0x57, 0x14, 0xC2, 0x20, 0xD0, 0x0C, 0xAD, 0x74, 0x79, 0x29, 0x01]))
+ rom.write_bytes(0x007B40, bytearray([0x00, 0xD0, 0x04, 0x5C, 0x4A, 0xF3, 0x06, 0xBD, 0xD6, 0x79, 0x38, 0xFD, 0xE2, 0x70, 0x5C, 0x45]))
+ rom.write_bytes(0x007B50, bytearray([0xF2, 0x06, 0xAD, 0xAA, 0x60, 0x48, 0x30, 0x0E, 0xE2, 0x20, 0xAD, 0x57, 0x14, 0xC2, 0x20, 0xF0]))
+ rom.write_bytes(0x007B60, bytearray([0x05, 0x68, 0x5C, 0xA0, 0xF3, 0x06, 0x68, 0x5C, 0xE6, 0xF3, 0x06, 0xBD, 0x02, 0x79, 0x85, 0x0E]))
+ rom.write_bytes(0x007B70, bytearray([0xE2, 0x20, 0xAD, 0x57, 0x14, 0xC2, 0x20, 0xD0, 0x08, 0x22, 0xC5, 0xF7, 0x00, 0x5C, 0x35, 0xE5]))
+ rom.write_bytes(0x007B80, bytearray([0x06, 0x22, 0xCF, 0xF7, 0x00, 0x5C, 0x35, 0xE5, 0x06, 0xE2, 0x20, 0xAD, 0x57, 0x14, 0xC2, 0x20]))
+ rom.write_bytes(0x007B90, bytearray([0xD0, 0x0C, 0xAD, 0x74, 0x79, 0x29, 0x01, 0x00, 0xD0, 0x04, 0x5C, 0x48, 0xF3, 0x06, 0xBD, 0x36]))
+ rom.write_bytes(0x007BA0, bytearray([0x7A, 0x38, 0xE9, 0x08, 0x00, 0x5C, 0x63, 0xE8, 0x06, 0xAD, 0xAA, 0x60, 0x30, 0x0D, 0xE2, 0x20]))
+ rom.write_bytes(0x007BB0, bytearray([0xAD, 0x57, 0x14, 0xC2, 0x20, 0xF0, 0x04, 0x5C, 0x99, 0xE8, 0x06, 0x5C, 0xF1, 0xE8, 0x06, 0x9C]))
+ rom.write_bytes(0x007BC0, bytearray([0xB0, 0x61, 0x9C, 0x8C, 0x0C, 0xE2, 0x20, 0xAD, 0x58, 0x14, 0xC2, 0x20, 0xF0, 0x07, 0x9C, 0x8E]))
+ rom.write_bytes(0x007BD0, bytearray([0x0C, 0x5C, 0x9D, 0xA4, 0x02, 0xA9, 0x00, 0x00, 0x8F, 0xAE, 0x00, 0x70, 0x8F, 0xAC, 0x00, 0x70]))
+ rom.write_bytes(0x007BE0, bytearray([0xE2, 0x20, 0xA9, 0xFE, 0x9D, 0x78, 0x79, 0x8F, 0x49, 0x00, 0x7E, 0xC2, 0x20, 0x5C, 0x9D, 0xA4]))
+ rom.write_bytes(0x007BF0, bytearray([0x02, 0xE2, 0x20, 0xAF, 0x49, 0x00, 0x7E, 0xC2, 0x20, 0xF0, 0x0D, 0xA9, 0x00, 0x00, 0x9D, 0xD8]))
+ rom.write_bytes(0x007C00, bytearray([0x79, 0x9D, 0x78, 0x79, 0x8F, 0x49, 0x00, 0x7E, 0xBD, 0x16, 0x7C, 0x18, 0x5C, 0x51, 0xA3, 0x02]))
+ rom.write_bytes(0x007C10, bytearray([0xE2, 0x20, 0xAD, 0x59, 0x14, 0xC2, 0x20, 0xD0, 0x0D, 0x22, 0xC5, 0xF7, 0x00, 0xBD, 0x38, 0x7D]))
+ rom.write_bytes(0x007C20, bytearray([0xF0, 0x0A, 0x5C, 0x4F, 0xA0, 0x02, 0x22, 0xCF, 0xF7, 0x00, 0x80, 0xF1, 0x5C, 0x59, 0xA0, 0x02]))
+ rom.write_bytes(0x007C30, bytearray([0xE2, 0x20, 0xAD, 0x59, 0x14, 0xC2, 0x20, 0xF0, 0x09, 0xBB, 0x22, 0x87, 0xBF, 0x03, 0x5C, 0x8D]))
+ rom.write_bytes(0x007C40, bytearray([0xA3, 0x02, 0x5C, 0x81, 0xA3, 0x02, 0xE2, 0x20, 0xAD, 0x5A, 0x14, 0xC2, 0x20, 0xF0, 0x09, 0xB5]))
+ rom.write_bytes(0x007C50, bytearray([0x76, 0x29, 0xFF, 0x00, 0x5C, 0x9D, 0x93, 0x02, 0x8D, 0x04, 0x30, 0xA9, 0x00, 0x00, 0x8D, 0x08]))
+ rom.write_bytes(0x007C60, bytearray([0x30, 0x5C, 0xA5, 0x93, 0x02, 0xE2, 0x20, 0xAD, 0x5A, 0x14, 0xC2, 0x20, 0xD0, 0x01, 0x6B, 0x22]))
+ rom.write_bytes(0x007C70, bytearray([0x23, 0xAF, 0x03, 0x5C, 0xDA, 0x93, 0x02, 0xE2, 0x20, 0xAD, 0x5B, 0x14, 0xC2, 0x20, 0xF0, 0x09]))
+ rom.write_bytes(0x007C80, bytearray([0x9B, 0xBD, 0xD6, 0x79, 0x0A, 0x5C, 0xCA, 0xC4, 0x05, 0x6B, 0xE2, 0x20, 0xAD, 0x5B, 0x14, 0xC2]))
+ rom.write_bytes(0x007C90, bytearray([0x20, 0xF0, 0x09, 0x9B, 0xBD, 0xD6, 0x79, 0x0A, 0x5C, 0xC1, 0xC8, 0x05, 0x6B, 0x22, 0x52, 0xAA]))
+ rom.write_bytes(0x007CA0, bytearray([0x03, 0xE2, 0x20, 0xAD, 0x5B, 0x14, 0xC2, 0x20, 0xF0, 0x0A, 0xA0, 0x00, 0x22, 0xD1, 0xF7, 0x00]))
+ rom.write_bytes(0x007CB0, bytearray([0x5C, 0xD9, 0xC4, 0x05, 0x22, 0xC5, 0xF7, 0x00, 0x5C, 0x70, 0xC5, 0x05, 0x22, 0x23, 0xAF, 0x03]))
+ rom.write_bytes(0x007CC0, bytearray([0xE2, 0x20, 0xAD, 0x5C, 0x14, 0xC2, 0x20, 0xF0, 0x0A, 0xA0, 0x00, 0x22, 0xD1, 0xF7, 0x00, 0x5C]))
+ rom.write_bytes(0x007CD0, bytearray([0x24, 0xC9, 0x0C, 0x22, 0xC5, 0xF7, 0x00, 0x80, 0xF6, 0xE2, 0x20, 0xAD, 0x5C, 0x14, 0xC2, 0x20]))
+ rom.write_bytes(0x007CE0, bytearray([0xF0, 0x08, 0x8A, 0x8D, 0x02, 0x30, 0x5C, 0x4D, 0xCD, 0x0C, 0xFA, 0x5C, 0x3A, 0xCD, 0x0C, 0x48]))
+ rom.write_bytes(0x007CF0, bytearray([0xDA, 0xE2, 0x20, 0xAD, 0x5D, 0x14, 0xF0, 0x33, 0xAA, 0x4C, 0x53, 0xFF, 0xFF, 0x18, 0x4C, 0x71]))
+ rom.write_bytes(0x007D00, bytearray([0xFF, 0x8D, 0x5E, 0x14, 0xC2, 0x20, 0xFA, 0x68, 0x1A, 0x1A, 0xC9, 0x0E, 0x00, 0x90, 0x06, 0x80]))
+ rom.write_bytes(0x007D10, bytearray([0x16, 0x5C, 0x15, 0xBF, 0x03, 0xE2, 0x20, 0x48, 0xBD, 0x60, 0x73, 0xC9, 0x27, 0xF0, 0x12, 0x68]))
+ rom.write_bytes(0x007D20, bytearray([0xCD, 0x5E, 0x14, 0xC2, 0x20, 0x90, 0xEA, 0x5C, 0xE5, 0xFA, 0x0B, 0x1A, 0x8D, 0x5D, 0x14, 0x80]))
+ rom.write_bytes(0x007D30, bytearray([0xC0, 0x68, 0xC2, 0x20, 0xEE, 0xCC, 0x00, 0xEE, 0xCC, 0x00, 0x80, 0xD5, 0xA8, 0x5C, 0x20, 0xBF]))
+ rom.write_bytes(0x007D40, bytearray([0x03, 0x8B, 0xA9, 0x03, 0x8D, 0x4B, 0x09, 0x8D, 0x01, 0x21, 0x22, 0x39, 0xB4, 0x00, 0x22, 0x79]))
+ rom.write_bytes(0x007D50, bytearray([0x82, 0x10, 0xDA, 0xAD, 0x0E, 0x03, 0x4A, 0xAA, 0xBF, 0xF3, 0xFE, 0x06, 0xAA, 0xAD, 0x1A, 0x02]))
+ rom.write_bytes(0x007D60, bytearray([0x9F, 0x00, 0x7C, 0x70, 0x9C, 0x22, 0x02, 0xAF, 0x83, 0xFC, 0x0D, 0xAA, 0xBF, 0xB2, 0xAF, 0x09]))
+ rom.write_bytes(0x007D70, bytearray([0x0C, 0xCE, 0x00, 0xAD, 0x60, 0x14, 0x0C, 0xCE, 0x00, 0x5A, 0xC2, 0x10, 0xA2, 0xAA, 0xAF, 0xAD]))
+ rom.write_bytes(0x007D80, bytearray([0xCE, 0x00, 0x89, 0x01, 0xF0, 0x06, 0xA0, 0x22, 0x02, 0x20, 0xCA, 0xFD, 0x89, 0x02, 0xF0, 0x06]))
+ rom.write_bytes(0x007D90, bytearray([0xA0, 0x2E, 0x02, 0x20, 0xCA, 0xFD, 0x89, 0x04, 0xF0, 0x06, 0xA0, 0x3A, 0x02, 0x20, 0xCA, 0xFD]))
+ rom.write_bytes(0x007DA0, bytearray([0x89, 0x08, 0xF0, 0x06, 0xA0, 0x46, 0x02, 0x20, 0xCA, 0xFD, 0x89, 0x10, 0xF0, 0x06, 0xA0, 0x52]))
+ rom.write_bytes(0x007DB0, bytearray([0x02, 0x20, 0xCA, 0xFD, 0x89, 0x20, 0xF0, 0x06, 0xA0, 0x5E, 0x02, 0x20, 0xCA, 0xFD, 0x9C, 0x65]))
+ rom.write_bytes(0x007DC0, bytearray([0x02, 0xE2, 0x10, 0x7A, 0xFA, 0xAB, 0x5C, 0xB6, 0xA5, 0x17, 0xC2, 0x20, 0x48, 0xA9, 0x07, 0x00]))
+ rom.write_bytes(0x007DD0, bytearray([0xDA, 0x54, 0x00, 0x09, 0xFA, 0x68, 0xE2, 0x20, 0x60, 0xDA, 0x5A, 0x8B, 0xAD, 0x0E, 0x03, 0xC2]))
+ rom.write_bytes(0x007DE0, bytearray([0x20, 0xC2, 0x10, 0xAA, 0xBF, 0x07, 0xFF, 0x06, 0xA8, 0xE2, 0x20, 0xA9, 0x00, 0xEB, 0xA9, 0x7F]))
+ rom.write_bytes(0x007DF0, bytearray([0xA2, 0xC0, 0x14, 0x54, 0x70, 0x7E, 0xA2, 0xC0, 0x14, 0xA0, 0x40, 0x14, 0xA9, 0x00, 0xEB, 0xA9]))
+ rom.write_bytes(0x007E00, bytearray([0x7F, 0x54, 0x7E, 0x7E, 0xE2, 0x10, 0xAB, 0x7A, 0xFA, 0xA9, 0x1E, 0x8D, 0x18, 0x01, 0xAF, 0x83]))
+ rom.write_bytes(0x007E10, bytearray([0xFC, 0x0D, 0xDA, 0xAA, 0xBF, 0xB8, 0xAF, 0x09, 0x8D, 0x18, 0x02, 0xAF, 0x88, 0xFC, 0x0D, 0x49]))
+ rom.write_bytes(0x007E20, bytearray([0x01, 0x8D, 0x5A, 0x14, 0xFA, 0x5C, 0x58, 0x99, 0x17, 0xAE, 0x15, 0x11, 0xAD, 0x60, 0x14, 0x89]))
+ rom.write_bytes(0x007E30, bytearray([0x01, 0xD0, 0x0D, 0xAF, 0x83, 0xFC, 0x0D, 0xF0, 0x07, 0x9E, 0x10, 0x00, 0x5C, 0xB1, 0xD8, 0x17]))
+ rom.write_bytes(0x007E40, bytearray([0xFE, 0x10, 0x00, 0x80, 0xF7, 0xA9, 0xF0, 0x85, 0x4D, 0x8D, 0x63, 0x14, 0xA9, 0x80, 0x8D, 0x20]))
+ rom.write_bytes(0x007E50, bytearray([0x02, 0x8D, 0x4A, 0x00, 0x5C, 0x59, 0xC1, 0x01, 0xE2, 0x20, 0xAD, 0x61, 0x14, 0x89, 0x01, 0xF0]))
+ rom.write_bytes(0x007E60, bytearray([0x08, 0x48, 0xA9, 0x09, 0x8F, 0x17, 0x03, 0x17, 0x68, 0x89, 0x02, 0xF0, 0x08, 0x48, 0xA9, 0x09]))
+ rom.write_bytes(0x007E70, bytearray([0x8F, 0x23, 0x03, 0x17, 0x68, 0x89, 0x04, 0xF0, 0x08, 0x48, 0xA9, 0x09, 0x8F, 0x2F, 0x03, 0x17]))
+ rom.write_bytes(0x007E80, bytearray([0x68, 0x89, 0x08, 0xF0, 0x08, 0x48, 0xA9, 0x09, 0x8F, 0x3B, 0x03, 0x17, 0x68, 0x89, 0x10, 0xF0]))
+ rom.write_bytes(0x007E90, bytearray([0x08, 0x48, 0xA9, 0x09, 0x8F, 0x47, 0x03, 0x17, 0x68, 0x89, 0x20, 0xF0, 0x08, 0x48, 0xA9, 0x09]))
+ rom.write_bytes(0x007EA0, bytearray([0x8F, 0x53, 0x03, 0x17, 0x68, 0xAD, 0x62, 0x14, 0x89, 0x01, 0xF0, 0x08, 0x48, 0xA9, 0x0A, 0x8F]))
+ rom.write_bytes(0x007EB0, bytearray([0x18, 0x03, 0x17, 0x68, 0x89, 0x02, 0xF0, 0x08, 0x48, 0xA9, 0x0A, 0x8F, 0x24, 0x03, 0x17, 0x68]))
+ rom.write_bytes(0x007EC0, bytearray([0x89, 0x04, 0xF0, 0x08, 0x48, 0xA9, 0x0A, 0x8F, 0x30, 0x03, 0x17, 0x68, 0x89, 0x08, 0xF0, 0x08]))
+ rom.write_bytes(0x007ED0, bytearray([0x48, 0xA9, 0x0A, 0x8F, 0x3C, 0x03, 0x17, 0x68, 0x89, 0x10, 0xF0, 0x08, 0x48, 0xA9, 0x0A, 0x8F]))
+ rom.write_bytes(0x007EE0, bytearray([0x48, 0x03, 0x17, 0x68, 0x89, 0x20, 0xF0, 0x08, 0x48, 0xA9, 0x0A, 0x8F, 0x54, 0x03, 0x17, 0x68]))
+ rom.write_bytes(0x007EF0, bytearray([0xC2, 0x20, 0x5C, 0x26, 0xDB, 0x17, 0xAD, 0x63, 0x14, 0xF0, 0x0E, 0xA9, 0x00, 0x8D, 0x63, 0x14]))
+ rom.write_bytes(0x007F00, bytearray([0xA9, 0x20, 0x8D, 0x18, 0x01, 0x5C, 0x04, 0xA9, 0x17, 0xA9, 0x25, 0x80, 0xF5, 0xAD, 0x06, 0x7E]))
+ rom.write_bytes(0x007F10, bytearray([0xD0, 0x15, 0xE2, 0x20, 0xAD, 0x64, 0x14, 0xD0, 0x0E, 0xAF, 0x84, 0xFC, 0x0D, 0x89, 0x01, 0xD0]))
+ rom.write_bytes(0x007F20, bytearray([0x06, 0xC2, 0x20, 0x5C, 0x49, 0xEA, 0x0C, 0xC2, 0x20, 0x5C, 0x47, 0xEA, 0x0C, 0xAD, 0x06, 0x7E]))
+ rom.write_bytes(0x007F30, bytearray([0xD0, 0x15, 0xE2, 0x20, 0xAD, 0x64, 0x14, 0xD0, 0x0E, 0xAF, 0x84, 0xFC, 0x0D, 0x89, 0x02, 0xD0]))
+ rom.write_bytes(0x007F40, bytearray([0x06, 0xC2, 0x20, 0x5C, 0x91, 0xC0, 0x03, 0xC2, 0x20, 0x5C, 0xCC, 0xC0, 0x03]))
+ rom.write_bytes(0x007F53, bytearray([0xBF, 0xA3, 0xAF, 0x09, 0xE0, 0x06, 0xF0, 0x03, 0x4C, 0xFD, 0xFC, 0x4C, 0x01, 0xFD]))
+ rom.write_bytes(0x007F61, bytearray([0xAF, 0xAE, 0x00, 0x70, 0xD0, 0x07, 0xFA, 0xA9, 0x0E, 0x00, 0x4C, 0x2C, 0xFA, 0x4C, 0x26, 0xFA]))
+ rom.write_bytes(0x007F71, bytearray([0x6D, 0xCC, 0x00, 0xC9, 0x0E, 0x90, 0x02, 0xA9, 0x0E, 0x4C, 0x01, 0xFD]))
+
+ rom.write_bytes(0x077F82, bytearray([0xE2, 0x20, 0xAD, 0x40, 0x14, 0xD0, 0x08, 0xC2, 0x20, 0x22, 0xC5, 0xF7, 0x00, 0x80]))
+ rom.write_bytes(0x077F90, bytearray([0x06, 0xA0, 0x00, 0x22, 0xD1, 0xF7, 0x00, 0xC2, 0x20, 0x20, 0x1A, 0xB6, 0x60, 0xE2, 0x20, 0xAD]))
+ rom.write_bytes(0x077FA0, bytearray([0x55, 0x14, 0xC2, 0x20, 0xD0, 0x03, 0x4C, 0xFD, 0xBE, 0x20, 0xBB, 0xBF, 0x4C, 0xFD, 0xBE]))
+
+ rom.write_bytes(0x01FEEE, bytearray([0xB9, 0x00]))
+ rom.write_bytes(0x01FEF0, bytearray([0x6F, 0x48, 0xDA, 0xBD, 0x60, 0x73, 0xA2, 0x00, 0xDF, 0x70, 0xAF, 0x09, 0xF0, 0x08, 0xE8, 0xE8]))
+ rom.write_bytes(0x01FF00, bytearray([0xE0, 0x08, 0xF0, 0x1A, 0x80, 0xF2, 0x8A, 0x4A, 0xE2, 0x20, 0xAA, 0xBF, 0x78, 0xAF, 0x09, 0xAA]))
+ rom.write_bytes(0x01FF10, bytearray([0xBD, 0x40, 0x14, 0xC2, 0x20, 0xD0, 0x07, 0xFA, 0x68, 0xC9, 0x00, 0x00, 0x80, 0x05, 0xFA, 0x68]))
+ rom.write_bytes(0x01FF20, bytearray([0xC9, 0x10, 0x00, 0x5C, 0x34, 0xC3, 0x03, 0xAE, 0x12, 0x98, 0xE2, 0x20, 0xAD, 0x5E, 0x14, 0xC9]))
+ rom.write_bytes(0x01FF30, bytearray([0x0E, 0xF0, 0x08, 0x3A, 0x3A, 0xA8, 0xC2, 0x20, 0x4C, 0x15, 0xBF, 0x98, 0x80, 0xF8]))
+
+ rom.write_bytes(0x02FFC0, bytearray([0x0C, 0xA6, 0x12, 0x6B, 0xBD, 0x60, 0x73, 0xC9, 0x1E, 0x01, 0xE2, 0x20, 0xF0, 0x05, 0xAD, 0x4C]))
+ rom.write_bytes(0x02FFD0, bytearray([0x14, 0x80, 0x03, 0xAD, 0x4B, 0x14, 0xC9, 0x00, 0xC2, 0x20, 0xF0, 0x03, 0x20, 0xF6, 0xF1, 0x4C]))
+ rom.write_bytes(0x02FFE0, bytearray([0xB0, 0xF0]))
+
+ rom.write_bytes(0x017FD7, bytearray([0xE2, 0x20, 0xAD, 0x4D, 0x14, 0xC2, 0x20, 0xF0, 0x10]))
+ rom.write_bytes(0x017FE0, bytearray([0xBD, 0x60, 0x73, 0xC9, 0xA9, 0x01, 0xF0, 0x04, 0xA9, 0x04, 0x00, 0x60, 0xA9, 0x0A, 0x00, 0x60]))
+ rom.write_bytes(0x017FF0, bytearray([0x68, 0x4C, 0x90, 0xAD]))
+
+ rom.write_bytes(0x03FF48, bytearray([0xE2, 0x20, 0xAD, 0x56, 0x14, 0xC2, 0x20, 0xD0]))
+ rom.write_bytes(0x03FF50, bytearray([0x03, 0x4C, 0x5B, 0x96, 0x20, 0x3D, 0x9D, 0x4C, 0x4F, 0x96]))
+
+
+def Item_Data(rom: LocalRom) -> None:
+ rom.write_bytes(0x04AF70, bytearray([0xBB, 0x00, 0xBA, 0x00, 0xC7, 0x00, 0xC8, 0x00, 0x01, 0x02, 0x03, 0x03, 0xB1, 0x00, 0xB0, 0x00]))
+ rom.write_bytes(0x04AF80, bytearray([0xB2, 0x00, 0xAF, 0x00, 0xB4, 0x00, 0x04, 0x05, 0x06, 0x07, 0x08, 0x07, 0x00, 0x05, 0x00, 0x09]))
+ rom.write_bytes(0x04AF90, bytearray([0x00, 0x0D, 0x0E, 0x0F, 0x22, 0x00, 0x26, 0x00, 0x29, 0x00, 0x2A, 0x00, 0x2B, 0x00, 0x11, 0x12]))
+ rom.write_bytes(0x04AFA0, bytearray([0x12, 0x12, 0x12, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]))
+ rom.write_bytes(0x04AFB0, bytearray([0x01, 0x01, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A]))
+
+
+def Server_Data(rom: LocalRom) -> None:
+ rom.write_bytes(0x037EAA, bytearray([0x00, 0x00, 0x01, 0x02, 0x03, 0x04]))
+ rom.write_bytes(0x037EB0, bytearray([0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14]))
+ rom.write_bytes(0x037EC0, bytearray([0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x24, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x01]))
+ rom.write_bytes(0x037ED0, bytearray([0x02, 0x04, 0x08, 0x10, 0x20, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30]))
+ rom.write_bytes(0x037EE0, bytearray([0x31, 0x32, 0x33, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0xFF, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39]))
+ rom.write_bytes(0x037EF0, bytearray([0x3A, 0x3B, 0x3C, 0x02, 0x6A, 0xD2, 0x04, 0x03, 0x06, 0x07, 0x08, 0x09, 0x05, 0x01, 0x02, 0x3D]))
+ rom.write_bytes(0x037F00, bytearray([0x3E, 0x3F, 0x40, 0x01, 0x02, 0x03, 0x0A, 0x80, 0x7E, 0x00, 0x7F, 0x80, 0x7F]))
+
+
+def Menu_Data(rom: LocalRom) -> None:
+ rom.write_bytes(0x115348, bytearray([0x80, 0x80, 0x4E, 0x80, 0x80, 0x4E, 0x80, 0x80]))
+ rom.write_bytes(0x115350, bytearray([0x4E, 0x80, 0x80, 0x4E, 0x80, 0x80, 0x4E, 0x80, 0x80, 0x4E, 0x80, 0x80, 0x4E, 0x80, 0x80, 0x4E]))
+ rom.write_bytes(0x115360, bytearray([0x80, 0x80, 0x4E, 0x80, 0x80, 0x4E, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03]))
+ rom.write_bytes(0x115370, bytearray([0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x08, 0x08]))
+ rom.write_bytes(0x115380, bytearray([0x08, 0x09, 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x24, 0x2C, 0x00, 0x06, 0x1E, 0x00, 0x06, 0x24, 0x00]))
+ rom.write_bytes(0x115390, bytearray([0x02, 0x24, 0x00, 0x0E, 0x04, 0x00, 0x18, 0x26, 0x00, 0x26, 0x1A, 0x00, 0x04, 0x22, 0x00, 0x24]))
+ rom.write_bytes(0x1153A0, bytearray([0x18, 0x00, 0x24, 0x02, 0x00, 0x16, 0x24, 0x00, 0x00, 0x2C, 0x00, 0x2A, 0x2C, 0x00, 0x2C, 0x18]))
+ rom.write_bytes(0x1153B0, bytearray([0x00, 0x10, 0x18, 0x00, 0x0A, 0x18, 0x00, 0x24, 0x24, 0x00, 0x0A, 0x08, 0x00, 0x0C, 0x08, 0x00]))
+ rom.write_bytes(0x1153C0, bytearray([0x08, 0x16, 0x00, 0x08, 0x1E, 0x00, 0x04, 0x14, 0x00, 0x1E, 0x0E, 0x00, 0x1E, 0x0C, 0x00, 0x24]))
+ rom.write_bytes(0x1153D0, bytearray([0x14, 0x00, 0x14, 0x30, 0x00, 0x18, 0x22, 0x00, 0x02, 0x04, 0x00, 0x26, 0x16, 0x00, 0x24, 0x16]))
+ rom.write_bytes(0x1153E0, bytearray([0x00, 0x5C, 0x38, 0x60, 0x4E, 0x28, 0x1A, 0x16, 0x1C, 0x04, 0x14, 0x36, 0x36, 0x36, 0x80, 0x80]))
+ rom.write_bytes(0x1153F0, bytearray([0x34, 0x81, 0x81, 0x4E, 0x4E, 0x4E, 0x5C, 0x38, 0x60, 0x4E, 0x04, 0x16, 0x08, 0x00, 0x22, 0x36]))
+ rom.write_bytes(0x115400, bytearray([0x36, 0x36, 0x36, 0x80, 0x80, 0x34, 0x81, 0x81, 0x4E, 0x4E, 0x4E, 0x50, 0x52, 0x54, 0x56, 0x58]))
+ rom.write_bytes(0x115410, bytearray([0x5A, 0x5C, 0x5E, 0x60, 0x62, 0x50, 0x52, 0x54, 0x09, 0x15, 0x21, 0x2D, 0x39, 0x45, 0x0C, 0x03]))
+ rom.write_bytes(0x115420, bytearray([0x07, 0x0F, 0x13, 0x1B, 0x1F, 0x27, 0x2B, 0x33, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x02, 0x04, 0x04]))
+ rom.write_bytes(0x115430, bytearray([0x06, 0x06, 0x08, 0x08, 0x0A, 0x41, 0x00, 0x3C, 0x00, 0x33, 0x00, 0x25, 0x00, 0x1B, 0x00, 0x14]))
+ rom.write_bytes(0x115440, bytearray([0x00, 0x0B, 0x00, 0x02, 0x00, 0xF6, 0x3F, 0xEC, 0x3F, 0xDC, 0x3F]))
+
+ rom.write_bytes(0x082660, bytearray([0x07]))
+ rom.write_bytes(0x082667, bytearray([0x05]))
+ rom.write_bytes(0x082677, bytearray([0x0A, 0x03, 0x05]))
+ rom.write_bytes(0x082688, bytearray([0x00]))
+
+ rom.write_bytes(0x11548E, bytearray([0x60, 0x3d, 0x66, 0x3b, 0x60, 0x3f, 0x60, 0x39, 0x66, 0x39, 0x66, 0x3d, 0x66, 0x3f, 0x60, 0x3b]))
+ rom.write_bytes(0x11549E, bytearray([0x02, 0x06, 0x04, 0x00, 0x01, 0x03, 0x05, 0x07]))
+
+
+def CodeHandler(rom: LocalRom) -> None:
+ rom.write_bytes(0x073637, bytearray([0x5C, 0xB0, 0xF7, 0x00])) # Check ! Switch
+ rom.write_bytes(0x07360B, bytearray([0x20, 0x82, 0xFF])) # Flash ! Switch
+
+ rom.write_bytes(0x01C2F3, bytearray([0x22, 0x11, 0xF8, 0x00])) # Check visibility of winged clouds
+ rom.write_bytes(0x01C32E, bytearray([0x5C, 0xEE, 0xFE, 0x03])) # Check items in winged clouds
+
+ rom.write_bytes(0x01C9AD, bytearray([0x5C, 0x19, 0xF8, 0x00])) # Check transformations
+ rom.write_bytes(0x01C995, bytearray([0x5C, 0x59, 0xF8, 0x00])) # Flash transformations
+ rom.write_bytes(0x01C943, bytearray([0x5C, 0x77, 0xF8, 0x00])) # Fixes a bug where transformation bubbles flashing would displace the sprite
+
+ rom.write_bytes(0x028329, bytearray([0x5C, 0x9A, 0xF8, 0x00])) # Flash Spring Ball
+ rom.write_bytes(0x02837E, bytearray([0x5C, 0xC4, 0xF8, 0x00])) # Check Spring Ball
+
+ rom.write_bytes(0x02F0A2, bytearray([0x5C, 0xEA, 0xF8, 0x00])) # Flash Arrow Wheel
+ rom.write_bytes(0x02F0AD, bytearray([0x4C, 0xC4, 0xFF])) # Check Arrow Wheel
+
+ rom.write_bytes(0x02001D, bytearray([0x5C, 0x15, 0xF9, 0x00])) # Check Melon
+ rom.write_bytes(0x020028, bytearray([0x5C, 0x41, 0xF9, 0x00])) # Secondary check for melon used to overwrite visibility on the ground
+ rom.write_bytes(0x020031, bytearray([0x5C, 0xAE, 0xF9, 0x00])) # Check for melons that are spawned by objects which skips the initial check
+ rom.write_bytes(0x012DF7, bytearray([0x20, 0xD7, 0xFF])) # Check for monkeys holding melons
+ rom.write_bytes(0x012E07, bytearray([0x20, 0xD7, 0xFF])) # Check for monkeys holding melons
+ rom.write_bytes(0x03F17D, bytearray([0x5C, 0xE1, 0xF9, 0x00])) # Fixes a bug where balloons with ice melons will write to yoshi's mouth before deactivating the melon.
+
+ rom.write_bytes(0x011901, bytearray([0x5C, 0x7A, 0xF9, 0x00])) # Flash Super Star
+ rom.write_bytes(0x01192A, bytearray([0x5C, 0x95, 0xF9, 0x00])) # Check Super Star
+
+ rom.write_bytes(0x01BEB9, bytearray([0x5C, 0xF6, 0xF9, 0x00])) # Check egg-type items
+ rom.write_bytes(0x01B75E, bytearray([0x5C, 0x5B, 0xFA, 0x00])) # Flash flashing eggs and force them to purple
+
+ rom.write_bytes(0x03BA31, bytearray([0x22, 0x81, 0xFA, 0x00])) # Flash Arrow Cloud
+ rom.write_bytes(0x03BA35, bytearray([0x22, 0x9A, 0xFA, 0x00])) # Check Arrow Cloud
+ rom.write_bytes(0x03BA3D, bytearray([0x22, 0x81, 0xFA, 0x00])) # Flash Arrow Cloud, rotating
+ rom.write_bytes(0x03BA5A, bytearray([0x22, 0x9A, 0xFA, 0x00])) # Check Arrow Cloud, rotating
+
+ rom.write_bytes(0x03818F, bytearray([0x5C, 0xAB, 0xFA, 0x00])) # Check Egg Plant
+ rom.write_bytes(0x0380F3, bytearray([0x5C, 0xC3, 0xFA, 0x00])) # Flash Egg Plant
+
+ rom.write_bytes(0x073EF6, bytearray([0x5C, 0xE0, 0xFA, 0x00])) # Flash Chomp Rock
+ rom.write_bytes(0x073EFA, bytearray([0x4C, 0x9D, 0xFF])) # Check Chomp Rock
+
+ rom.write_bytes(0x039639, bytearray([0x5C, 0xFF, 0xFA, 0x00])) # Flash Poochy
+ rom.write_bytes(0x03964C, bytearray([0x4C, 0x48, 0xFF])) # Check Poochy
+
+ rom.write_bytes(0x0370C2, bytearray([0x22, 0x1A, 0xFB, 0x00, 0xEA])) # Flash Platform Ghosts
+ rom.write_bytes(0x03723F, bytearray([0x5C, 0x32, 0xFB, 0x00])) # Fixes a bug where the eyes would assign to a random sprite while flashing
+ rom.write_bytes(0x03739B, bytearray([0x5C, 0x52, 0xFB, 0x00])) # Check Vertical Platform Ghost
+ rom.write_bytes(0x036530, bytearray([0x5C, 0x6B, 0xFB, 0x00])) # Flash horizontal ghost
+ rom.write_bytes(0x03685C, bytearray([0x5C, 0x89, 0xFB, 0x00])) # Fix flashing horizontal ghost
+ rom.write_bytes(0x036894, bytearray([0x5C, 0xA9, 0xFB, 0x00])) # Check horizontal ghost
+
+ rom.write_bytes(0x012497, bytearray([0x5C, 0xBF, 0xFB, 0x00])) # Check Skis
+ rom.write_bytes(0x01234D, bytearray([0x5C, 0xF1, 0xFB, 0x00])) # Allow ski doors to be re-entered
+
+ rom.write_bytes(0x01204A, bytearray([0x5C, 0x10, 0xFC, 0x00])) # Flash Key
+ rom.write_bytes(0x012388, bytearray([0x5C, 0x30, 0xFC, 0x00])) # Check Key
+
+ rom.write_bytes(0x011398, bytearray([0x5C, 0x46, 0xFC, 0x00])) # Flash MidRing
+ rom.write_bytes(0x0113D6, bytearray([0x5C, 0x65, 0xFC, 0x00])) # Check MidRing
+
+ rom.write_bytes(0x02C4C6, bytearray([0x5C, 0x77, 0xFC, 0x00])) # Check Bucket w/ Item
+ rom.write_bytes(0x02C8BD, bytearray([0x5C, 0x8A, 0xFC, 0x00])) # Check Bucket, ridable
+ rom.write_bytes(0x02C4D5, bytearray([0x5C, 0x9D, 0xFC, 0x00])) # Flash Bucket
+
+ rom.write_bytes(0x064920, bytearray([0x5C, 0xBC, 0xFC, 0x00])) # Flash Tulip
+ rom.write_bytes(0x064D49, bytearray([0x5C, 0xD9, 0xFC, 0x00])) # Check Tulip
+
+ rom.write_bytes(0x01BEC7, bytearray([0x5C, 0xEF, 0xFC, 0x00])) # Check Egg Capacity
+ rom.write_bytes(0x01BF12, bytearray([0x4C, 0x27, 0xFF])) # Set current egg max
+ rom.write_bytes(0x01BF1A, bytearray([0x5C, 0x3C, 0xFD, 0x00])) # Cap eggs
+
+ rom.write_bytes(0x0BA5AE, bytearray([0x5C, 0x41, 0xFD, 0x00])) # Unlock Levels
+
+ rom.write_bytes(0x0B9953, bytearray([0x5C, 0xD9, 0xFD, 0x00])) # File initialization
+
+ rom.write_bytes(0x0BD8AB, bytearray([0x5C, 0x29, 0xFE, 0x00])) # Prevent the world 1 tab from being drawn without it being unlocked
+
+ rom.write_bytes(0x00C155, bytearray([0x5C, 0x45, 0xFE, 0x00])) # Save between levels
+
+ rom.write_bytes(0x0BDB20, bytearray([0x5C, 0x58, 0xFE, 0x00])) # Unlock extra and bonus stages
+
+ rom.write_bytes(0x0BA8FF, bytearray([0x5C, 0xF6, 0xFE, 0x00])) # Skip the score animation if coming from start-select, but still save
+
+ rom.write_bytes(0x0BA8A9, bytearray([0x80, 0x46])) # Prevent unlocking new levels
+
+ rom.write_bytes(0x066A42, bytearray([0x5C, 0x0D, 0xFF, 0x00])) # Coin visibility
+ rom.write_bytes(0x01C08C, bytearray([0x5C, 0x2D, 0xFF, 0x00])) # Cloud visibility
+
+ rom.write_bytes(0x00C0D9, bytearray([0x5C, 0xB8, 0xF3, 0x0B])) # Receive item from server
+
+ rom.write_bytes(0x00C153, bytearray([0xEA, 0xEA])) # Always enable Start/Select
+
+ rom.write_bytes(0x00C18B, bytearray([0x5C, 0x1B, 0xF5, 0x0B])) # Enable traps
+
+ rom.write_bytes(0x01B365, bytearray([0x5C, 0x86, 0xF5, 0x0B])) # Red Coin checks
+ rom.write_bytes(0x0734C6, bytearray([0x5C, 0xCE, 0xF5, 0x0B])) # Flower checks
+ rom.write_bytes(0x00C0DE, bytearray([0x5C, 0xF5, 0xF5, 0x0B])) # Star checks
+ rom.write_bytes(0x00B580, bytearray([0x5C, 0xB1, 0xF5, 0x0B])) # Level Clear checks
+
+ rom.write_bytes(0x0B9937, bytearray([0x5C, 0x23, 0xF6, 0x0B])) # Load AP data
+ rom.write_bytes(0x0BE14A, bytearray([0x5C, 0x58, 0xF6, 0x0B])) # Save AP data
+
+ rom.write_bytes(0x00D09F, bytearray([0x5C, 0x8C, 0xF6, 0x0B])) # Clear Menu
+ rom.write_bytes(0x00BCB5, bytearray([0x5C, 0xAD, 0xF6, 0x0B])) # Clear Score for menu
+ rom.write_bytes(0x00D072, bytearray([0x5C, 0xC3, 0xF6, 0x0B])) # Loads the data for the AP menu
+ rom.write_bytes(0x00D07A, bytearray([0x5C, 0x5A, 0xF7, 0x0B])) # Draw the AP menu over the pause menu
+ rom.write_bytes(0x00D17A, bytearray([0x5C, 0xDA, 0xF7, 0x0B])) # Skip the flower counter in the AP menu
+ rom.write_bytes(0x00D0DE, bytearray([0x5C, 0xF1, 0xF7, 0x0B])) # Skip the coin counter in the AP menu
+ rom.write_bytes(0x00CFB4, bytearray([0x5C, 0x06, 0xF8, 0x0B])) # Get the number of bosses required to unlock 6-8
+ rom.write_bytes(0x00CFD0, bytearray([0x5C, 0x2B, 0xF8, 0x0B])) # Get bosses for 6-8 clear
+ rom.write_bytes(0x00D203, bytearray([0x5C, 0xF0, 0xF8, 0x0B])) # Wipe total score line
+ rom.write_bytes(0x00D277, bytearray([0x5C, 0x04, 0xF9, 0x0B])) # Wipe high score line
+ rom.write_bytes(0x00C104, bytearray([0x5C, 0x18, 0xF9, 0x0B])) # Replace the pause menu with AP menu when SELECT is pressed
+ rom.write_bytes(0x00C137, bytearray([0x5C, 0x31, 0xF9, 0x0B])) # Prevent accidentally quitting out of a stage while opening the AP menu
+ rom.write_bytes(0x00CE48, bytearray([0x5C, 0x42, 0xF9, 0x0B])) # When closing the AP menu, reset the AP menu flag so the normal menu can be opened.
+
+ rom.write_bytes(0x0BA5B6, bytearray([0x5C, 0x4E, 0xF9, 0x0B])) # Unlock 6-8 if the current number of defeated bosses is higher than the number of bosses required. If 6-8 is marked 'cleared', skip boss checks
+ rom.write_bytes(0x01209E, bytearray([0x5C, 0x92, 0xF9, 0x0B])) # Write a flag to check bosses if setting up the final boss door
+ rom.write_bytes(0x0123AA, bytearray([0x5C, 0xA3, 0xF9, 0x0B])) # If the boss check flag is set, read the number of bosses before opening door
+
+ rom.write_bytes(0x015F7A, bytearray([0x5C, 0xCA, 0xF9, 0x0B])) # Write Boss Clears
+
+ rom.write_bytes(0x0BE16E, bytearray([0x80, 0x12])) # Disable overworld bandit code
+
+ rom.write_bytes(0x083015, bytearray([0x5C, 0x26, 0xFA, 0x0B])) # Flip Cards
+ rom.write_bytes(0x0839B6, bytearray([0x5C, 0x18, 0xFA, 0x0B])) # Scratch Cards
+ rom.write_bytes(0x085094, bytearray([0x5C, 0x31, 0xFA, 0x0B])) # Draw Lots
+ rom.write_bytes(0x0852C5, bytearray([0x5C, 0x3D, 0xFA, 0x0B])) # Match Cards
+ rom.write_bytes(0x0845EA, bytearray([0x5C, 0x48, 0xFA, 0x0B])) # Roulette
+ rom.write_bytes(0x083E0A, bytearray([0x5C, 0x53, 0xFA, 0x0B])) # Slots
+
+ rom.write_bytes(0x01D845, bytearray([0x5C, 0x76, 0xF9, 0x0B])) # Check setting for disabled autoscrolls
+
+ rom.write_bytes(0x0BDAC2, bytearray([0x80, 0x0E])) # Prevent extra and bonus stages from auto-unlocking at 100 points
+ rom.write_bytes(0x0BA720, bytearray([0xA9, 0x00, 0x00])) # Always read level scores as 0. This stops extras and bonus from trying to unlock
+
+ rom.write_bytes(0x0BA720, bytearray([0xA9, 0x00, 0x00])) # Always read level scores as 0. This stops extras and bonus from trying to unlock
+
+ rom.write_bytes(0x03FE85, bytearray([0x5C, 0x09, 0xFB, 0x0B])) # Decrement the key counter when unlocking the 6-4 cork
+
+ rom.write_bytes(0x06F1B4, bytearray([0x5C, 0x22, 0xFB, 0x0B])) # Mark the goal and bowser clear after defeating bowser
+
+ rom.write_bytes(0x005FE2, bytearray([0x5C, 0x9C, 0xFB, 0x0B])) # Flag red coins as checked if the last one came from a pole
+
+ rom.write_bytes(0x01C2E1, bytearray([0x80])) # Makes hidden clouds not flash
+ rom.write_bytes(0x0120C0, bytearray([0x80])) # Prevents bandit game doors from sealing
+
+ rom.write_bytes(0x0382A7, bytearray([0x5C, 0xC2, 0xFB, 0x0B])) # Make cactus eggplants check the eggplant item correctly
+
+ rom.write_bytes(0x025E71, bytearray([0x5C, 0xFA, 0xFB, 0x0B])) # Write the stored reverse value
+
+ rom.write_bytes(0x00B587, bytearray([0x5C, 0x24, 0xFC, 0x0B])) # Store the reverse value and zero it
+
+ rom.write_bytes(0x0B9932, bytearray([0x5C, 0x96, 0xFA, 0x0B])) # Get 16 bit life count
+
+ rom.write_bytes(0x00C288, bytearray([0x00]))
+ rom.write_bytes(0x00C28B, bytearray([0x80])) # Disable baby mario tutorial text
+
+ rom.write_bytes(0x01141F, bytearray([0x80])) # Disable Middle Ring tutorial
+
+ rom.write_bytes(0x073534, bytearray([0x80])) # Disable Flower tutorial
+
+ rom.write_bytes(0x065B24, bytearray([0x5C, 0x45, 0xFC, 0x0B])) # Fix boss cutscenes
+
+ rom.write_bytes(0x011507, bytearray([0x5C, 0x70, 0xFC, 0x0B])) # Fix Hookbill middle ring during boss shuffle
+
+ rom.write_bytes(0x019E98, bytearray([0x5C, 0xB4, 0xFC, 0x0B])) # Flag red coins as checked if the last one was eaten
+
+ rom.write_bytes(0x011AB6, bytearray([0x5C, 0xD7, 0xFC, 0x0B])) # Check egg refills for how many eggs to spawn
+
+ rom.write_bytes(0x00DCA6, bytearray([0x5C, 0x00, 0xFD, 0x0B])) # Check egg refill pause use
+
+ rom.write_bytes(0x0BE06B, bytearray([0x5C, 0x56, 0xFD, 0x0B])) # Get level from shuffled order
+
+ rom.write_bytes(0x00C14B, bytearray([0xAE, 0x7C, 0x02, 0x8E, 0x1A, 0x02])) # Return to the original list when exiting a level
+
+ rom.write_bytes(0x00BEA8, bytearray([0x5C, 0x3F, 0xFE, 0x0B])) # Save the original level when beating a shuffled one.
+
+ rom.write_bytes(0x00E702, bytearray([0xAD, 0x7C, 0x02, 0x8D, 0x1A, 0x02, 0x80, 0x05])) # Save the original level when leaving through death
+
+ rom.write_bytes(0x0BE72A, bytearray([0x7C])) # Load yoshi colors by slot number not level number
+
+ rom.write_bytes(0x003346, bytearray([0x22, 0x54, 0xFE, 0x0B, 0xEA, 0xEA])) # Fix World 6 levels using weird tilesets
+
+ rom.write_bytes(0x003A37, bytearray([0x22, 0x54, 0xFE, 0x0B, 0xEA, 0xEA])) # Fix World 6 levels using weird tilesets
+
+ rom.write_bytes(0x0B87D5, bytearray([0x5C, 0x67, 0xFE, 0x0B]))
+
+ rom.write_bytes(0x07081F, bytearray([0x80])) # Fix for weird falling chomps. Why does this even read the world number?????
+
+ rom.write_bytes(0x0BC0B2, bytearray([0x5C, 0xD0, 0xED, 0x01])) # Load randomized yoshi colors on the world map
+
+ rom.write_bytes(0x0BC6F7, bytearray([0x5C, 0x04, 0xEE, 0x01])) # Load selected yoshi color on the world map
+
+ rom.write_bytes(0x0BC0AB, bytearray([0x80])) # Skip special color check for world 6; Levels handle this anyway
+
+
+def write_lives(rom: LocalRom) -> None:
+ rom.write_bytes(0x05FA96, bytearray([0xC2, 0x20, 0xAF, 0x89, 0xFC, 0x0D, 0x8D, 0x79, 0x03, 0xE2, 0x20, 0x5C, 0x37, 0x99, 0x17]))
+ rom.write_bytes(0x05FABF, bytearray([0x48, 0xE2, 0x20, 0xAD, 0xCC, 0x00, 0xF0, 0x06, 0xCE, 0xCC, 0x00, 0xCE, 0xCC, 0x00, 0xC2, 0x20, 0x68, 0x22, 0x87, 0xBF, 0x03, 0x5C, 0x89, 0xFE, 0x07]))
+
+
+def bonus_checks(rom: LocalRom) -> None:
+ rom.write_bytes(0x082156, bytearray([0x5C, 0x5F, 0xFA, 0x0B])) # Write bonus check
+
+
+def bandit_checks(rom: LocalRom) -> None:
+ rom.write_bytes(0x08C9E4, bytearray([0x5C, 0xF3, 0xF9, 0x0B])) # Write Bandit Checks
+
+
+def Handle_Locations(rom: LocalRom) -> None:
+ rom.write_bytes(0x05F3B8, bytearray([0xAD, 0x67, 0x14, 0xF0, 0x59, 0xDA, 0xC9, 0x1F]))
+ rom.write_bytes(0x05F3C0, bytearray([0xF0, 0x16, 0xC9, 0x20, 0xB0, 0x27, 0xAA, 0xBF, 0xAA, 0xFE, 0x06, 0xAA, 0xA9, 0x01, 0x9D, 0x40]))
+ rom.write_bytes(0x05F3D0, bytearray([0x14, 0xA9, 0x43, 0x8D, 0x53, 0x00, 0x80, 0x67, 0xAD, 0x5D, 0x14, 0xD0, 0x01, 0x1A, 0xC9, 0x06]))
+ rom.write_bytes(0x05F3E0, bytearray([0xF0, 0x04, 0x1A, 0x8D, 0x5D, 0x14, 0xA9, 0x03, 0x8D, 0x53, 0x00, 0x80, 0x52, 0xC9, 0x26, 0xB0]))
+ rom.write_bytes(0x05F3F0, bytearray([0x27, 0xA2, 0x00, 0xDF, 0xC9, 0xFE, 0x06, 0xF0, 0x03, 0xE8, 0x80, 0xF7, 0xBF, 0xCF, 0xFE, 0x06]))
+ rom.write_bytes(0x05F400, bytearray([0x8D, 0x4C, 0x00, 0xAD, 0x60, 0x14, 0x0C, 0x4C, 0x00, 0xAD, 0x4C, 0x00, 0x8D, 0x60, 0x14, 0xA9]))
+ rom.write_bytes(0x05F410, bytearray([0x97, 0x8D, 0x53, 0x00, 0x80, 0x29, 0x80, 0x70, 0xC9, 0x2D, 0xB0, 0x25, 0xA2, 0x00, 0xDF, 0xD5]))
+ rom.write_bytes(0x05F420, bytearray([0xFE, 0x06, 0xF0, 0x03, 0xE8, 0x80, 0xF7, 0xBF, 0xE3, 0xFE, 0x06, 0x8D, 0xCF, 0x00, 0xAD, 0x61]))
+ rom.write_bytes(0x05F430, bytearray([0x14, 0x0C, 0xCF, 0x00, 0xAD, 0xCF, 0x00, 0x8D, 0x61, 0x14, 0xA9, 0x95, 0x8D, 0x53, 0x00, 0x80]))
+ rom.write_bytes(0x05F440, bytearray([0x78, 0xC9, 0x34, 0xB0, 0x25, 0xA2, 0x00, 0xDF, 0xDC, 0xFE, 0x06, 0xF0, 0x03, 0xE8, 0x80, 0xF7]))
+ rom.write_bytes(0x05F450, bytearray([0xBF, 0xE3, 0xFE, 0x06, 0x8D, 0xCF, 0x00, 0xAD, 0x62, 0x14, 0x0C, 0xCF, 0x00, 0xAD, 0xCF, 0x00]))
+ rom.write_bytes(0x05F460, bytearray([0x8D, 0x62, 0x14, 0xA9, 0x95, 0x8D, 0x53, 0x00, 0x80, 0x4F, 0xC9, 0x3D, 0xB0, 0x1C, 0xA2, 0x00]))
+ rom.write_bytes(0x05F470, bytearray([0xDF, 0xEA, 0xFE, 0x06, 0xF0, 0x03, 0xE8, 0x80, 0xF7, 0xBF, 0xF6, 0xFE, 0x06, 0x22, 0xA6, 0x9C]))
+ rom.write_bytes(0x05F480, bytearray([0x10, 0xA9, 0x36, 0x8D, 0x53, 0x00, 0x80, 0x31, 0x80, 0x64, 0xC9, 0x41, 0xB0, 0x2D, 0xA2, 0x00]))
+ rom.write_bytes(0x05F490, bytearray([0xDF, 0xFF, 0xFE, 0x06, 0xF0, 0x03, 0xE8, 0x80, 0xF7, 0xA9, 0x00, 0xEB, 0xBF, 0x03, 0xFF, 0x06]))
+ rom.write_bytes(0x05F4A0, bytearray([0xAA, 0x18, 0xC2, 0x20, 0x6D, 0x79, 0x03, 0x8D, 0x79, 0x03, 0xE2, 0x20, 0xA9, 0x08, 0x22, 0xD2]))
+ rom.write_bytes(0x05F4B0, bytearray([0x85, 0x00, 0xCA, 0xE0, 0x00, 0xF0, 0x02, 0x80, 0xF5, 0x80, 0x51, 0xC9, 0x41, 0xF0, 0x1E, 0xC9]))
+ rom.write_bytes(0x05F4C0, bytearray([0x42, 0xF0, 0x2D, 0xC9, 0x43, 0xF0, 0x3A, 0xC2, 0x20, 0x5C, 0xFB, 0xB3, 0x21, 0x77, 0x14, 0xE2]))
+ rom.write_bytes(0x05F4D0, bytearray([0x20, 0xA9, 0x01, 0x8D, 0x7D, 0x02, 0xA9, 0x2E, 0x8D, 0x53, 0x00, 0x80, 0x2F, 0xA9, 0x01, 0x8D]))
+ rom.write_bytes(0x05F4E0, bytearray([0x68, 0x14, 0xC2, 0x20, 0xA9, 0x00, 0x04, 0x8D, 0x69, 0x14, 0xE2, 0x20, 0x80, 0x1E, 0x80, 0x22]))
+ rom.write_bytes(0x05F4F0, bytearray([0xC2, 0x20, 0xA9, 0x2C, 0x01, 0x8D, 0xCC, 0x0C, 0xE2, 0x20, 0xA9, 0xA0, 0x8D, 0x53, 0x00, 0x80]))
+ rom.write_bytes(0x05F500, bytearray([0x0B, 0xA9, 0x15, 0x8D, 0x53, 0x00, 0xA9, 0x05, 0x8F, 0xED, 0x61, 0x04, 0xFA, 0xA9, 0x00, 0x8D]))
+ rom.write_bytes(0x05F510, bytearray([0x67, 0x14, 0xA9, 0x10, 0x8D, 0x83, 0x0B, 0x5C, 0xDE, 0xC0, 0x01, 0xE2, 0x20, 0xAD, 0x7D, 0x02]))
+ rom.write_bytes(0x05F520, bytearray([0xF0, 0x25, 0xC2, 0x20, 0xAD, 0x7E, 0x02, 0xE2, 0x20, 0xF0, 0x12, 0xA9, 0x02, 0x8D, 0x00, 0x02]))
+ rom.write_bytes(0x05F530, bytearray([0xC2, 0x20, 0xAD, 0x7E, 0x02, 0x3A, 0x8D, 0x7E, 0x02, 0xE2, 0x20, 0x80, 0x0A, 0xA9, 0x0F, 0x8D]))
+ rom.write_bytes(0x05F540, bytearray([0x00, 0x02, 0xA9, 0x00, 0x8D, 0x7D, 0x02, 0xAD, 0x68, 0x14, 0xF0, 0x32, 0xC2, 0x20, 0xAD, 0x69]))
+ rom.write_bytes(0x05F550, bytearray([0x14, 0xF0, 0x1B, 0x3A, 0x8D, 0x69, 0x14, 0xE2, 0x20, 0x4C, 0x40, 0xFD, 0xE8, 0x1F, 0x70, 0xAD]))
+ rom.write_bytes(0x05F560, bytearray([0xD0, 0x00, 0xD0, 0x08, 0xEE, 0xD0, 0x00, 0xA9, 0x21, 0x8D, 0x53, 0x00, 0x80, 0x10, 0xE2, 0x20]))
+ rom.write_bytes(0x05F570, bytearray([0xA9, 0x22, 0x8D, 0x53, 0x00, 0xA9, 0x00, 0x8D, 0x68, 0x14, 0x8F, 0xE8, 0x1F, 0x70, 0x22, 0x59]))
+ rom.write_bytes(0x05F580, bytearray([0x82, 0x00, 0x5C, 0x8F, 0xC1, 0x01, 0xAC, 0xB4, 0x03, 0xC0, 0x14, 0x30, 0x20, 0x48, 0xDA, 0xE2]))
+ rom.write_bytes(0x05F590, bytearray([0x20, 0xAE, 0x1A, 0x02, 0xBD, 0x6D, 0x14, 0x8D, 0xD1, 0x00, 0xA9, 0x01, 0x0C, 0xD1, 0x00, 0xAD]))
+ rom.write_bytes(0x05F5A0, bytearray([0xD1, 0x00, 0x9D, 0x6D, 0x14, 0xC2, 0x20, 0xFA, 0x68, 0x5C, 0x6C, 0xB3, 0x03, 0x5C, 0x6D, 0xB3]))
+ rom.write_bytes(0x05F5B0, bytearray([0x03, 0xAE, 0x1A, 0x02, 0xBD, 0x6D, 0x14, 0x8D, 0xD1, 0x00, 0xA9, 0x08, 0x0C, 0xD1, 0x00, 0xAD]))
+ rom.write_bytes(0x05F5C0, bytearray([0xD1, 0x00, 0x9D, 0x6D, 0x14, 0xAE, 0x57, 0x0B, 0xE0, 0x0D, 0x5C, 0x85, 0xB5, 0x01, 0xA0, 0x05]))
+ rom.write_bytes(0x05F5D0, bytearray([0x8C, 0xB8, 0x03, 0x08, 0xE2, 0x20, 0xDA, 0x48, 0xAE, 0x1A, 0x02, 0xBD, 0x6D, 0x14, 0x8D, 0xD1]))
+ rom.write_bytes(0x05F5E0, bytearray([0x00, 0xA9, 0x02, 0x0C, 0xD1, 0x00, 0xAD, 0xD1, 0x00, 0x9D, 0x6D, 0x14, 0x68, 0xFA, 0xC2, 0x20]))
+ rom.write_bytes(0x05F5F0, bytearray([0x28, 0x5C, 0xCB, 0xB4, 0x0E, 0xC2, 0x20, 0xAD, 0xB6, 0x03, 0xC9, 0x2C, 0x01, 0x90, 0x18, 0xE2]))
+ rom.write_bytes(0x05F600, bytearray([0x20, 0xDA, 0xAE, 0x1A, 0x02, 0xBD, 0x6D, 0x14, 0x8D, 0xD1, 0x00, 0xA9, 0x04, 0x0C, 0xD1, 0x00]))
+ rom.write_bytes(0x05F610, bytearray([0xAD, 0xD1, 0x00, 0x9D, 0x6D, 0x14, 0xFA, 0x9C, 0x84, 0x0B, 0xE2, 0x20, 0xAD, 0x0F, 0x0D, 0x5C]))
+ rom.write_bytes(0x05F620, bytearray([0xE4, 0xC0, 0x01, 0xC2, 0x20, 0x48, 0xE2, 0x20, 0xA9, 0x1F, 0x8D, 0x18, 0x01, 0xDA, 0x5A, 0x8B]))
+ rom.write_bytes(0x05F630, bytearray([0x4C, 0xB2, 0xFA, 0xC2, 0x20, 0xC2, 0x10, 0xAA, 0xBF, 0x07, 0xFF, 0x06, 0xAA, 0xE2, 0x20, 0xA9]))
+ rom.write_bytes(0x05F640, bytearray([0x00, 0xEB, 0xA9, 0x7F, 0xA0, 0x40, 0x14, 0x54, 0x7E, 0x70, 0xE2, 0x10, 0xAB, 0x7A, 0xFA, 0xC2]))
+ rom.write_bytes(0x05F650, bytearray([0x20, 0x68, 0xE2, 0x20, 0x5C, 0x3C, 0x99, 0x17, 0xC2, 0x20, 0x48, 0xC2, 0x10, 0xDA, 0x5A, 0x8B]))
+ rom.write_bytes(0x05F660, bytearray([0xAD, 0x0E, 0x03, 0x29, 0x0F, 0x00, 0xAA, 0xBF, 0x07, 0xFF, 0x06, 0xA8, 0xE2, 0x20, 0xA9, 0x00]))
+ rom.write_bytes(0x05F670, bytearray([0xEB, 0xA9, 0x7F, 0xA2, 0x40, 0x14, 0x54, 0x70, 0x7E, 0xAB, 0x7A, 0xFA, 0xE2, 0x10, 0xC2, 0x20]))
+ rom.write_bytes(0x05F680, bytearray([0x68, 0xE2, 0x20, 0xAD, 0x3D, 0x09, 0x29, 0x20, 0x5C, 0x4F, 0xE1, 0x17, 0xE2, 0x20, 0xAD, 0xD2]))
+ rom.write_bytes(0x05F690, bytearray([0x00, 0xC2, 0x20, 0xD0, 0x09, 0xA9, 0xC1, 0xB1, 0x85, 0x10, 0x5C, 0xA4, 0xD0, 0x01, 0xA9, 0x00]))
+ rom.write_bytes(0x05F6A0, bytearray([0x00, 0x85, 0x10, 0x85, 0x12, 0x85, 0x14, 0x85, 0x16, 0x5C, 0xB3, 0xD0, 0x01, 0xE2, 0x20, 0xAD]))
+ rom.write_bytes(0x05F6B0, bytearray([0xD2, 0x00, 0xC2, 0x20, 0xD0, 0x09, 0xA9, 0x6F, 0x01, 0x05, 0x02, 0x5C, 0xBA, 0xBC, 0x01, 0x5C]))
+ rom.write_bytes(0x05F6C0, bytearray([0xBC, 0xBC, 0x01, 0xE2, 0x20, 0xAD, 0xD2, 0x00, 0xC2, 0x20, 0xD0, 0x0B, 0xBF, 0xED, 0xB7, 0x01]))
+ rom.write_bytes(0x05F6D0, bytearray([0x29, 0xFF, 0x00, 0x5C, 0x79, 0xD0, 0x01, 0xBF, 0x48, 0xD3, 0x22, 0x29, 0xFF, 0x00, 0xC9, 0x80]))
+ rom.write_bytes(0x05F6E0, bytearray([0x00, 0xF0, 0x04, 0x5C, 0x79, 0xD0, 0x01, 0xBF, 0x66, 0xD3, 0x22, 0xDA, 0xAA, 0xAD, 0xD3, 0x00]))
+ rom.write_bytes(0x05F6F0, bytearray([0x29, 0xFF, 0x00, 0xC9, 0x01, 0x00, 0xF0, 0x21, 0xC9, 0x02, 0x00, 0xF0, 0x38, 0xBD, 0x40, 0x14]))
+ rom.write_bytes(0x05F700, bytearray([0x29, 0xFF, 0x00, 0xF0, 0x0C, 0xFA, 0xBF, 0x87, 0xD3, 0x22, 0x29, 0xFF, 0x00, 0x5C, 0x79, 0xD0]))
+ rom.write_bytes(0x05F710, bytearray([0x01, 0xFA, 0xA9, 0x4E, 0x00, 0x5C, 0x79, 0xD0, 0x01, 0xBD, 0x4A, 0x14, 0x29, 0xFF, 0x00, 0xF0]))
+ rom.write_bytes(0x05F720, bytearray([0x0C, 0xFA, 0xBF, 0xA5, 0xD3, 0x22, 0x29, 0xFF, 0x00, 0x5C, 0x79, 0xD0, 0x01, 0xFA, 0xA9, 0x4E]))
+ rom.write_bytes(0x05F730, bytearray([0x00, 0x5C, 0x79, 0xD0, 0x01, 0xE0, 0x09, 0xD0, 0x05, 0xAD, 0x64, 0x14, 0x80, 0x03, 0xBD, 0x54]))
+ rom.write_bytes(0x05F740, bytearray([0x14, 0x29, 0xFF, 0x00, 0xF0, 0x0C, 0xFA, 0xBF, 0xC3, 0xD3, 0x22, 0x29, 0xFF, 0x00, 0x5C, 0x79]))
+ rom.write_bytes(0x05F750, bytearray([0xD0, 0x01, 0xFA, 0xA9, 0x4E, 0x00, 0x5C, 0x79, 0xD0, 0x01, 0xE2, 0x20, 0xAD, 0xD2, 0x00, 0xC2]))
+ rom.write_bytes(0x05F760, bytearray([0x20, 0xD0, 0x08, 0xBF, 0x5F, 0xB8, 0x01, 0x5C, 0x7E, 0xD0, 0x01, 0xAD, 0xD3, 0x00, 0x29, 0xFF]))
+ rom.write_bytes(0x05F770, bytearray([0x00, 0xC9, 0x01, 0x00, 0xF0, 0x3C, 0xC9, 0x02, 0x00, 0xF0, 0x4B, 0xBF, 0x5F, 0xB8, 0x01, 0x05]))
+ rom.write_bytes(0x05F780, bytearray([0x18, 0x99, 0xA1, 0xB1, 0xBF, 0xDD, 0xB8, 0x01, 0x05, 0x18, 0x99, 0xE1, 0xB1, 0xFA, 0xC8, 0xC8]))
+ rom.write_bytes(0x05F790, bytearray([0xE8, 0xE0, 0x1D, 0x90, 0x19, 0xEE, 0xD3, 0x00, 0xA0, 0x00, 0xA2, 0x00, 0xAD, 0xD3, 0x00, 0x29]))
+ rom.write_bytes(0x05F7A0, bytearray([0xFF, 0x00, 0xC9, 0x03, 0x00, 0xD0, 0x07, 0x9C, 0xD3, 0x00, 0x5C, 0x94, 0xD0, 0x01, 0x5C, 0x71]))
+ rom.write_bytes(0x05F7B0, bytearray([0xD0, 0x01, 0xBF, 0x5F, 0xB8, 0x01, 0x05, 0x18, 0x99, 0x21, 0xB2, 0xBF, 0xDD, 0xB8, 0x01, 0x05]))
+ rom.write_bytes(0x05F7C0, bytearray([0x18, 0x99, 0x61, 0xB2, 0x80, 0xC7, 0xBF, 0x5F, 0xB8, 0x01, 0x05, 0x18, 0x99, 0xA1, 0xB2, 0xBF]))
+ rom.write_bytes(0x05F7D0, bytearray([0xDD, 0xB8, 0x01, 0x05, 0x18, 0x99, 0xE1, 0xB2, 0x80, 0xB3, 0xE2, 0x20, 0xAD, 0xD2, 0x00, 0xC2]))
+ rom.write_bytes(0x05F7E0, bytearray([0x20, 0xD0, 0x0A, 0x64, 0x18, 0xAF, 0xB8, 0x03, 0x00, 0x5C, 0x80, 0xD1, 0x01, 0x5C, 0x02, 0xD2]))
+ rom.write_bytes(0x05F7F0, bytearray([0x01, 0xE2, 0x20, 0xAD, 0xD2, 0x00, 0xC2, 0x20, 0xD0, 0x08, 0x64, 0x18, 0xA0, 0x00, 0x5C, 0xE2]))
+ rom.write_bytes(0x05F800, bytearray([0xD0, 0x01, 0x5C, 0x02, 0xD2, 0x01, 0xAD, 0xD2, 0x00, 0x29, 0xFF, 0x00, 0xD0, 0x08, 0xBF, 0x35]))
+ rom.write_bytes(0x05F810, bytearray([0xB8, 0x01, 0x5C, 0xB8, 0xCF, 0x01, 0xBF, 0xE1, 0xD3, 0x22, 0x29, 0xFF, 0x00, 0xC9, 0x80, 0x00]))
+ rom.write_bytes(0x05F820, bytearray([0xF0, 0x2E, 0xC9, 0x81, 0x00, 0xF0, 0x47, 0x5C, 0xB8, 0xCF, 0x01, 0xAD, 0xD2, 0x00, 0x29, 0xFF]))
+ rom.write_bytes(0x05F830, bytearray([0x00, 0xD0, 0x08, 0xBF, 0x4A, 0xB8, 0x01, 0x5C, 0xD4, 0xCF, 0x01, 0xBF, 0xF6, 0xD3, 0x22, 0x29]))
+ rom.write_bytes(0x05F840, bytearray([0xFF, 0x00, 0x4C, 0xB6, 0xFD, 0xF0, 0x18, 0xC9, 0x81, 0x00, 0xF0, 0x30, 0x5C, 0xD4, 0xCF, 0x01]))
+ rom.write_bytes(0x05F850, bytearray([0xDA, 0xE2, 0x20, 0xAD, 0xB3, 0x14, 0xAA, 0xC2, 0x20, 0x20, 0x8A, 0xF8, 0xFA, 0x80, 0xC8, 0xDA]))
+ rom.write_bytes(0x05F860, bytearray([0xE2, 0x20, 0xAD, 0xB3, 0x14, 0xAA, 0xC2, 0x20, 0x20, 0xBD, 0xF8, 0xFA, 0x80, 0xDE, 0xDA, 0xE2]))
+ rom.write_bytes(0x05F870, bytearray([0x20, 0xAF, 0x85, 0xFC, 0x0D, 0xAA, 0x20, 0x8A, 0xF8, 0xFA, 0x80, 0xAB, 0xDA, 0xE2, 0x20, 0xAF]))
+ rom.write_bytes(0x05F880, bytearray([0x86, 0xFC, 0x0D, 0xAA, 0x20, 0xBD, 0xF8, 0xFA, 0x80, 0xC2, 0xE2, 0x20, 0xC9, 0x0A, 0xB0, 0x1F]))
+ rom.write_bytes(0x05F890, bytearray([0xAD, 0xD5, 0x00, 0xD0, 0x0D, 0xBF, 0x0B, 0xD4, 0x22, 0xC2, 0x20, 0xA9, 0x50, 0x00, 0xEE, 0xD5]))
+ rom.write_bytes(0x05F8A0, bytearray([0x00, 0x60, 0xBF, 0x0B, 0xD4, 0x22, 0x9C, 0xD4, 0x00, 0x9C, 0xD5, 0x00, 0xC2, 0x20, 0x60, 0xAD]))
+ rom.write_bytes(0x05F8B0, bytearray([0xD4, 0x00, 0xD0, 0xEE, 0xEE, 0xD4, 0x00, 0xC2, 0x20, 0xA9, 0x52, 0x00, 0x60, 0xE2, 0x20, 0xC9]))
+ rom.write_bytes(0x05F8C0, bytearray([0x0A, 0xB0, 0x1F, 0xAD, 0xD6, 0x00, 0xD0, 0x0D, 0xBF, 0x0B, 0xD4, 0x22, 0xC2, 0x20, 0xA9, 0x50]))
+ rom.write_bytes(0x05F8D0, bytearray([0x00, 0xEE, 0xD6, 0x00, 0x60, 0xBF, 0x0B, 0xD4, 0x22, 0x9C, 0xD7, 0x00, 0x9C, 0xD6, 0x00, 0xC2]))
+ rom.write_bytes(0x05F8E0, bytearray([0x20, 0x60, 0xAD, 0xD7, 0x00, 0xD0, 0xEE, 0xEE, 0xD7, 0x00, 0xC2, 0x20, 0xA9, 0x52, 0x00, 0x60]))
+ rom.write_bytes(0x05F8F0, bytearray([0xAD, 0xD2, 0x00, 0x29, 0xFF, 0x00, 0xF0, 0x04, 0x5C, 0x74, 0xD2, 0x01, 0x64, 0x18, 0xA0, 0x00]))
+ rom.write_bytes(0x05F900, bytearray([0x5C, 0x07, 0xD2, 0x01, 0xAD, 0xD2, 0x00, 0x29, 0xFF, 0x00, 0xF0, 0x04, 0x5C, 0x74, 0xD2, 0x01]))
+ rom.write_bytes(0x05F910, bytearray([0xAF, 0x7C, 0x02, 0x00, 0x5C, 0x7B, 0xD2, 0x01, 0xA5, 0x38, 0x89, 0x20, 0xD0, 0x0A, 0x29, 0x10]))
+ rom.write_bytes(0x05F920, bytearray([0xF0, 0x02, 0xA9, 0x01, 0x5C, 0x08, 0xC1, 0x01, 0xEE, 0xD2, 0x00, 0x64, 0x38, 0x5C, 0x08, 0xC1]))
+ rom.write_bytes(0x05F930, bytearray([0x01, 0xAD, 0xD2, 0x00, 0xD0, 0x08, 0xA5, 0x38, 0x29, 0x20, 0x5C, 0x3B, 0xC1, 0x01, 0xA9, 0x00]))
+ rom.write_bytes(0x05F940, bytearray([0x80, 0xF8, 0xAD, 0x10, 0x0B, 0x49, 0x01, 0x9C, 0xD2, 0x00, 0x5C, 0x4D, 0xCE, 0x01, 0x9C, 0x01]))
+ rom.write_bytes(0x05F950, bytearray([0x02, 0xAD, 0x5E, 0x02, 0xF0, 0x16, 0xAD, 0xB0, 0x14, 0x89, 0x08, 0xD0, 0x15, 0xAD, 0xB3, 0x14]))
+ rom.write_bytes(0x05F960, bytearray([0xCF, 0x85, 0xFC, 0x0D, 0x90, 0x06, 0xA9, 0x80, 0x8F, 0x65, 0x02, 0x7E, 0xC2, 0x20, 0x5C, 0xBB]))
+ rom.write_bytes(0x05F970, bytearray([0xA5, 0x17, 0xA9, 0x01, 0x80, 0xF2, 0xE2, 0x20, 0xAF, 0x87, 0xFC, 0x0D, 0xC2, 0x20, 0xF0, 0x0D]))
+ rom.write_bytes(0x05F980, bytearray([0x4C, 0xBF, 0xFA, 0x8D, 0x1C, 0x0C, 0x8D, 0x1E, 0x0C, 0x5C, 0x4E, 0xD8, 0x03, 0xB9, 0x04, 0x0C]))
+ rom.write_bytes(0x05F990, bytearray([0x80, 0xF1, 0xE2, 0x20, 0xA9, 0x01, 0x8D, 0xD8, 0x00, 0xC2, 0x20, 0x22, 0xBE, 0xAE, 0x03, 0x5C]))
+ rom.write_bytes(0x05F9A0, bytearray([0xA2, 0xA0, 0x02, 0xE2, 0x20, 0xAD, 0xD8, 0x00, 0xD0, 0x0F, 0xC2, 0x20, 0xA9, 0x02, 0x00, 0x9D]))
+ rom.write_bytes(0x05F9B0, bytearray([0x96, 0x7A, 0xFE, 0x78, 0x79, 0x5C, 0xAF, 0xA3, 0x02, 0xAD, 0xB3, 0x14, 0xCF, 0x86, 0xFC, 0x0D]))
+ rom.write_bytes(0x05F9C0, bytearray([0xC2, 0x20, 0xB0, 0xE8, 0xC2, 0x20, 0x5C, 0x81, 0xA3, 0x02, 0xE2, 0x20, 0xDA, 0xAE, 0x1A, 0x02]))
+ rom.write_bytes(0x05F9D0, bytearray([0xBD, 0x6D, 0x14, 0x89, 0x20, 0xF0, 0x0D, 0xFA, 0xC2, 0x20, 0xAD, 0x02, 0x74, 0xC9, 0x32, 0x00]))
+ rom.write_bytes(0x05F9E0, bytearray([0x5C, 0x80, 0xDF, 0x02, 0x18, 0x69, 0x20, 0x9D, 0x6D, 0x14, 0xAD, 0xB3, 0x14, 0x1A, 0x8D, 0xB3]))
+ rom.write_bytes(0x05F9F0, bytearray([0x14, 0x80, 0xE4, 0xE2, 0x20, 0xDA, 0xAE, 0x1A, 0x02, 0xBD, 0x6D, 0x14, 0x8D, 0xD1, 0x00, 0xA9]))
+ rom.write_bytes(0x05FA00, bytearray([0x10, 0x0C, 0xD1, 0x00, 0xAD, 0xD1, 0x00, 0x9D, 0x6D, 0x14, 0xFA, 0xC2, 0x20, 0xA9, 0x36, 0x00]))
+ rom.write_bytes(0x05FA10, bytearray([0x22, 0xD2, 0x85, 0x00, 0x5C, 0xEB, 0xC9, 0x11, 0xB9, 0xE4, 0xB9, 0xC0, 0x00, 0xF0, 0x03, 0xEE]))
+ rom.write_bytes(0x05FA20, bytearray([0xD9, 0x00, 0x5C, 0xBB, 0xB9, 0x10, 0xA9, 0x06, 0x85, 0x4D, 0xEE, 0xD9, 0x00, 0x5C, 0x19, 0xB0]))
+ rom.write_bytes(0x05FA30, bytearray([0x10, 0xA9, 0x05, 0x00, 0x85, 0x4D, 0xEE, 0xD9, 0x00, 0x5C, 0x9A, 0xD0, 0x10, 0xA9, 0x06, 0x85]))
+ rom.write_bytes(0x05FA40, bytearray([0x4D, 0xEE, 0xD9, 0x00, 0x5C, 0xC9, 0xD2, 0x10, 0xA9, 0x05, 0x85, 0x4D, 0xEE, 0xD9, 0x00, 0x5C]))
+ rom.write_bytes(0x05FA50, bytearray([0xEE, 0xC5, 0x10, 0xA9, 0x05, 0x00, 0x85, 0x4D, 0xEE, 0xD9, 0x00, 0x5C, 0x0F, 0xBE, 0x10, 0xDA]))
+ rom.write_bytes(0x05FA60, bytearray([0xE2, 0x20, 0xAD, 0xD9, 0x00, 0xF0, 0x26, 0xA2, 0x00, 0xAD, 0x1A, 0x02, 0xDF, 0x18, 0xD4, 0x22]))
+ rom.write_bytes(0x05FA70, bytearray([0xF0, 0x07, 0xE8, 0xE0, 0x06, 0xF0, 0x16, 0x80, 0xF3, 0xAE, 0x1A, 0x02, 0xBD, 0x6D, 0x14, 0x8D]))
+ rom.write_bytes(0x05FA80, bytearray([0xD1, 0x00, 0xA9, 0x10, 0x0C, 0xD1, 0x00, 0xAD, 0xD1, 0x00, 0x9D, 0x6D, 0x14, 0xFA, 0x22, 0x67]))
+ rom.write_bytes(0x05FA90, bytearray([0xFA, 0x04, 0x5C, 0x5A, 0xA1, 0x10]))
+
+ rom.write_bytes(0x05FAB2, bytearray([0xA9, 0x00, 0xEB, 0xAD, 0x0E, 0x03, 0xC2, 0x20, 0xC2, 0x10, 0x4C, 0x37, 0xF6]))
+ rom.write_bytes(0x05FABF, bytearray([0xE2]))
+ rom.write_bytes(0x05FAC0, bytearray([0x20, 0xAD, 0x1A, 0x02, 0xDA, 0xA2, 0x00, 0x00, 0xDF, 0x1E, 0xD4, 0x22, 0xF0, 0x11, 0xE8, 0xE0]))
+ rom.write_bytes(0x05FAD0, bytearray([0x01, 0x00, 0xF0, 0x02, 0x80, 0xF2, 0xFA, 0xC2, 0x20, 0xA9, 0x00, 0x00, 0x4C, 0x83, 0xF9, 0xFA]))
+ rom.write_bytes(0x05FAE0, bytearray([0xC2, 0x20, 0x4C, 0x8D, 0xF9]))
+ rom.write_bytes(0x05FAE5, bytearray([0x48, 0xE2, 0x20, 0xAD, 0x5D, 0x14, 0xC9, 0x01, 0xF0, 0x07]))
+ rom.write_bytes(0x05FAEF, bytearray([0xC2, 0x20, 0x68, 0x5C, 0xCE, 0xBE, 0x03, 0xAD, 0xCC, 0x00, 0xD0, 0xF4, 0xAF, 0xFA, 0x1D, 0x70]))
+ rom.write_bytes(0x05FAFF, bytearray([0xF0, 0xEE, 0xA9, 0x00, 0x8F, 0xFA, 0x1D, 0x70, 0x80, 0xE6]))
+ rom.write_bytes(0x05FB09, bytearray([0x48, 0xE2, 0x20, 0xAD, 0xCC, 0x00, 0xF0, 0x06, 0xCE, 0xCC, 0x00, 0xCE, 0xCC, 0x00, 0xC2, 0x20, 0x68, 0x22, 0x87, 0xBF, 0x03, 0x5C, 0x89, 0xFE, 0x07]))
+ rom.write_bytes(0x05FB22, bytearray([0xA0, 0x0A, 0x8C, 0x4D, 0x00, 0xE2, 0x20, 0xA9, 0x08, 0x0C, 0xB0, 0x14, 0x8D, 0xB6, 0x14, 0xC2, 0x20, 0x5C, 0xB9, 0xF1, 0x0D, 0x0D, 0xA8, 0xE2]))
+ rom.write_bytes(0x05FB3A, bytearray([0x20, 0xA9, 0x08, 0x0C, 0xB0, 0x14, 0xA9, 0x00, 0xEB, 0xA9, 0x7F, 0xA2, 0x40, 0x14, 0x54, 0x70, 0x7E, 0xAB, 0x7A, 0xFA, 0x1A, 0xEE, 0x14, 0xC2, 0x20, 0x68, 0x5C, 0xB9, 0xF1, 0x0D]))
+ rom.write_bytes(0x05FB58, bytearray([0x4C, 0xDD, 0xFB, 0x04, 0xAF, 0xAC, 0x00, 0x70]))
+ rom.write_bytes(0x05FB60, bytearray([0xD0, 0x2C, 0xAD, 0x35, 0x00, 0xC9, 0x50, 0xD0, 0x25, 0xAD, 0xDA, 0x00, 0xC9, 0x80, 0xF0, 0x11]))
+ rom.write_bytes(0x05FB70, bytearray([0xC9, 0x00, 0xF0, 0x21, 0xC9, 0x2A, 0xF0, 0x1D, 0xC9, 0x54, 0xF0, 0x19, 0xEE, 0xDA, 0x00, 0x80]))
+ rom.write_bytes(0x05FB80, bytearray([0x10, 0xA9, 0x2F, 0x8D, 0x53, 0x00, 0xA9, 0x11, 0x8D, 0x18, 0x01, 0xEE, 0xDB, 0x00, 0x9C, 0xDA]))
+ rom.write_bytes(0x05FB90, bytearray([0x00, 0x5C, 0x93, 0xC1, 0x01, 0xA9, 0x28, 0x8D, 0x53, 0x00, 0x80, 0xE0]))
+ rom.write_bytes(0x05FB9C, bytearray([0xA9, 0x93, 0x00, 0xEE]))
+ rom.write_bytes(0x05FBA0, bytearray([0xB4, 0x03, 0xAC, 0xB4, 0x03, 0xC0, 0x14, 0x00, 0x90, 0x14, 0xE2, 0x20, 0xDA, 0xAE, 0x1A, 0x02]))
+ rom.write_bytes(0x05FBB0, bytearray([0xBD, 0x6D, 0x14, 0x09, 0x01, 0x9D, 0x6D, 0x14, 0xFA, 0xC2, 0x20, 0xA9, 0x94, 0x00, 0x5C, 0xF1, 0xDF, 0x00]))
+ rom.write_bytes(0x05FBC2, bytearray([0x48, 0xC9, 0x06, 0x00, 0xB0, 0x10, 0xE2, 0x20, 0xAD, 0x54, 0x14, 0xC9, 0x00, 0xC2, 0x20, 0xF0]))
+ rom.write_bytes(0x05FBD2, bytearray([0x05, 0x68, 0x5C, 0xAC, 0x82, 0x07, 0x68, 0x5C, 0xFB, 0x81, 0x07, 0xAD, 0x6A, 0x02, 0xF0, 0x11]))
+ rom.write_bytes(0x05FBE2, bytearray([0xC2, 0x20, 0xA9, 0x0E, 0x00, 0x22, 0xE2, 0xF6, 0x04, 0xA9, 0x00, 0x00, 0x8D, 0x6A, 0x02, 0xE2]))
+ rom.write_bytes(0x05FBF2, bytearray([0x20, 0x22, 0x28, 0xFD, 0x04, 0x4C, 0x5C, 0xFB, 0xAF, 0xB0, 0x23, 0x7E, 0xF0, 0x18, 0xAF, 0xAC]))
+ rom.write_bytes(0x05FC02, bytearray([0x00, 0x70, 0x29, 0xFF, 0x00, 0xD0, 0x0F, 0xAF, 0xB0, 0x23, 0x7E, 0x8F, 0xEC, 0x61, 0x04, 0xA9]))
+ rom.write_bytes(0x05FC12, bytearray([0x00, 0x00, 0x8F, 0xB0, 0x23, 0x7E, 0xBD, 0xD0, 0x61, 0xF0, 0x03, 0xDE, 0xD0, 0x61, 0x5C, 0x79]))
+ rom.write_bytes(0x05FC22, bytearray([0xDE, 0x04, 0x48, 0xC2, 0x20, 0xAF, 0xEC, 0x61, 0x04, 0xD0, 0x0B, 0xE2, 0x20, 0x68, 0x22, 0xCE]))
+ rom.write_bytes(0x05FC32, bytearray([0xC0, 0x01, 0x5C, 0x8B, 0xB5, 0x01, 0x8F, 0xB0, 0x23, 0x7E, 0xA9, 0x00, 0x00, 0x8F, 0xEC, 0x61]))
+ rom.write_bytes(0x05FC42, bytearray([0x04, 0x80, 0xE8, 0x48, 0xDA, 0xE2, 0x20, 0x4C, 0xA5, 0xFC, 0xA2, 0x00, 0xDF, 0x1F, 0xD4, 0x22]))
+ rom.write_bytes(0x05FC52, bytearray([0xF0, 0x03, 0xE8, 0x80, 0xF7, 0xBF, 0x8D, 0xFC, 0x0D, 0xAA, 0xBF, 0x2A, 0xD4, 0x22, 0x8D, 0xDC]))
+ rom.write_bytes(0x05FC62, bytearray([0x00, 0xC2, 0x20, 0xFA, 0x68, 0x0D, 0xDC, 0x00, 0x95, 0x76, 0x5C, 0x29, 0xDB, 0x0C, 0xE2, 0x20]))
+ rom.write_bytes(0x05FC72, bytearray([0xAD, 0x48, 0x0B, 0xF0, 0x23, 0xAF, 0xBE, 0x03, 0x02, 0xC9, 0x02, 0xD0, 0x1B, 0xAD, 0x1A, 0x02]))
+ rom.write_bytes(0x05FC82, bytearray([0xA2, 0x00, 0xDF, 0x1F, 0xD4, 0x22, 0xF0, 0x03, 0xE8, 0x80, 0xF7, 0x8A, 0x0A, 0xAA, 0xC2, 0x20]))
+ rom.write_bytes(0x05FC92, bytearray([0xBF, 0x35, 0xD4, 0x22, 0x8F, 0xBE, 0x03, 0x02, 0xC2, 0x20, 0xEE, 0xAC, 0x03, 0xC2, 0x10, 0x5C]))
+ rom.write_bytes(0x05FCA2, bytearray([0x0C, 0x95, 0x02, 0xAD, 0x1A, 0x02, 0xC9, 0x43, 0xF0, 0x03, 0x4C, 0x4C, 0xFC, 0xA9, 0x0A, 0x4C]))
+ rom.write_bytes(0x05FCB2, bytearray([0x60, 0xFC, 0xAC, 0xB4, 0x03, 0xC0, 0x14, 0x30, 0x14, 0x1A, 0xE2, 0x20, 0xDA, 0x48, 0xAE, 0x1A]))
+ rom.write_bytes(0x05FCC2, bytearray([0x02, 0xBD, 0x6D, 0x14, 0x09, 0x01, 0x9D, 0x6D, 0x14, 0x68, 0xFA, 0xC2, 0x20, 0x22, 0xD2, 0x85]))
+ rom.write_bytes(0x05FCD2, bytearray([0x00, 0x5C, 0xA4, 0x9E, 0x03, 0xE2, 0x20, 0xAD, 0xF6, 0x7D, 0xC9, 0x0C, 0xB0, 0x1A, 0xAD, 0xCC]))
+ rom.write_bytes(0x05FCE2, bytearray([0x00, 0xAD, 0x5E, 0x14, 0x38, 0xED, 0xCC, 0x00, 0x3A, 0x3A, 0x8D, 0xDE, 0x00, 0xAD, 0xF6, 0x7D]))
+ rom.write_bytes(0x05FCF2, bytearray([0x38, 0xED, 0xCC, 0x00, 0x18, 0xCD, 0xDE, 0x00, 0xC2, 0x20, 0x5C, 0xBC, 0x9A, 0x02, 0xE2, 0x20]))
+ rom.write_bytes(0x05FD02, bytearray([0xAD, 0x5D, 0x14, 0xF0, 0x33, 0xAA, 0xBF, 0xA3, 0xAF, 0x09, 0x18, 0x6D, 0xCC, 0x00, 0x8D, 0x5E]))
+ rom.write_bytes(0x05FD12, bytearray([0x14, 0xAD, 0xF6, 0x7D, 0xC9, 0x0C, 0xB0, 0x1A, 0xAD, 0xCC, 0x00, 0xAD, 0x5E, 0x14, 0x38, 0xED]))
+ rom.write_bytes(0x05FD22, bytearray([0xCC, 0x00, 0x3A, 0x3A, 0x8D, 0xDE, 0x00, 0xAD, 0xF6, 0x7D, 0x38, 0xED, 0xCC, 0x00, 0x18, 0xCD]))
+ rom.write_bytes(0x05FD32, bytearray([0xDE, 0x00, 0xC2, 0x20, 0x5C, 0xAC, 0xDC, 0x01, 0x1A, 0x8D, 0x5D, 0x14, 0x80, 0xC0, 0xA9, 0x00]))
+ rom.write_bytes(0x05FD42, bytearray([0x8F, 0xE8, 0x1F, 0x70, 0xAD, 0xAC, 0x60, 0xC9, 0x00, 0xD0, 0x06, 0xA9, 0x01, 0x8F, 0xE8, 0x1F]))
+ rom.write_bytes(0x05FD52, bytearray([0x70, 0x4C, 0x5F, 0xF5, 0xDA, 0xAD, 0x1A, 0x02, 0x8D, 0x7C, 0x02, 0xAD, 0x12, 0x11, 0xC9, 0x08]))
+ rom.write_bytes(0x05FD62, bytearray([0xB0, 0x1D, 0xAD, 0x18, 0x02, 0x4A, 0xAA, 0xA9, 0x00, 0xE0, 0x00, 0xF0, 0x06, 0x18, 0x69, 0x08]))
+ rom.write_bytes(0x05FD72, bytearray([0xCA, 0x80, 0xF6, 0x18, 0x6D, 0x12, 0x11, 0xAA, 0xBF, 0x4B, 0xD4, 0x22, 0x8D, 0x1A, 0x02, 0xFA]))
+ rom.write_bytes(0x05FD82, bytearray([0xA9, 0x02, 0x8D, 0x13, 0x11, 0x5C, 0x70, 0xE0, 0x17, 0xAC, 0x7C, 0x02, 0x8C, 0x1A, 0x02, 0xB9]))
+ rom.write_bytes(0x05FD92, bytearray([0x22, 0x02, 0x5C, 0xA7, 0x82, 0x10, 0xAD, 0x7C, 0x02, 0x8D, 0x1A, 0x02, 0xC2, 0x20, 0xE2, 0x10]))
+ rom.write_bytes(0x05FDA2, bytearray([0x5C, 0xBC, 0xB2, 0x01, 0xC9, 0x45, 0xB0, 0x03, 0x8D, 0x7C, 0x02, 0x8D, 0x1A, 0x02, 0x29, 0x07]))
+ rom.write_bytes(0x05FDB2, bytearray([0x5C, 0x3A, 0x81, 0x10, 0xC9, 0x82, 0x00, 0xF0, 0x2E, 0xC9, 0x83, 0x00, 0xF0, 0x40, 0xC9, 0x84]))
+ rom.write_bytes(0x05FDC2, bytearray([0x00, 0xF0, 0x0B, 0xC9, 0x85, 0x00, 0xF0, 0x0E, 0xC9, 0x80, 0x00, 0x4C, 0x45, 0xF8, 0xE2, 0x20]))
+ rom.write_bytes(0x05FDD2, bytearray([0xAF, 0x99, 0xFC, 0x0D, 0x80, 0x16, 0xDA, 0xE2, 0x20, 0xAD, 0xE3, 0x00, 0xD0, 0x4E, 0x9C, 0xE1]))
+ rom.write_bytes(0x05FDE2, bytearray([0x00, 0xAF, 0x99, 0xFC, 0x0D, 0x80, 0x25, 0xE2, 0x20, 0xAD, 0xB5, 0x14, 0xC9, 0x64, 0xC2, 0x20]))
+ rom.write_bytes(0x05FDF2, bytearray([0xB0, 0x06, 0xA9, 0x4E, 0x00, 0x4C, 0x4C, 0xF8, 0xA9, 0x52, 0x00, 0x4C, 0x4C, 0xF8, 0xDA, 0xE2]))
+ rom.write_bytes(0x05FE02, bytearray([0x20, 0xAD, 0xE3, 0x00, 0xD0, 0x26, 0x9C, 0xE1, 0x00, 0xAD, 0xB5, 0x14, 0xC9, 0x0A, 0x90, 0x08]))
+ rom.write_bytes(0x05FE12, bytearray([0x38, 0xE9, 0x0A, 0xEE, 0xE1, 0x00, 0x80, 0xF4, 0x8D, 0xE2, 0x00, 0xEE, 0xE3, 0x00, 0xAD, 0xE1]))
+ rom.write_bytes(0x05FE22, bytearray([0x00, 0xAA, 0xBF, 0x0B, 0xD4, 0x22, 0xC2, 0x20, 0xFA, 0x4C, 0x46, 0xF8, 0x9C, 0xE3, 0x00, 0xAD]))
+ rom.write_bytes(0x05FE32, bytearray([0xE2, 0x00, 0xAA, 0xBF, 0x0B, 0xD4, 0x22, 0xC2, 0x20, 0xFA, 0x4C, 0x4C, 0xF8, 0x22, 0xB7, 0xB2]))
+ rom.write_bytes(0x05FE42, bytearray([0x01, 0xEA, 0xEA, 0xEA, 0xAE, 0x7C, 0x02, 0x8E, 0x1A, 0x02, 0xEA, 0xEA, 0xEA, 0xEA, 0x5C, 0xAC]))
+ rom.write_bytes(0x05FE52, bytearray([0xBE, 0x01, 0xE2, 0x20, 0xAD, 0x1A, 0x02, 0xc9, 0x3C, 0xC2, 0x20, 0xB0, 0x04, 0xA9, 0x02, 0x00]))
+ rom.write_bytes(0x05FE62, bytearray([0x6B, 0xA9, 0x00, 0x00, 0x6B, 0xAD, 0x18, 0x01, 0xC9, 0x19, 0xD0, 0x3A, 0xC2, 0x20, 0x48, 0xA9]))
+ rom.write_bytes(0x05FE72, bytearray([0x00, 0x00, 0xE2, 0x20, 0xAF, 0x9A, 0xFC, 0x0D, 0xD0, 0x05, 0xA9, 0x08, 0x0C, 0xB0, 0x14, 0xC2]))
+ rom.write_bytes(0x05FE82, bytearray([0x10, 0xDA, 0x5A, 0x8B, 0xAD, 0x0E, 0x03, 0xC2, 0x20, 0xAA, 0xBF, 0x07, 0xFF, 0x06, 0xA8, 0xE2]))
+ rom.write_bytes(0x05FE92, bytearray([0x20, 0xA9, 0x00, 0xEB, 0xA9, 0x7F, 0xA2, 0x40, 0x14, 0x54, 0x70, 0x7E, 0xAB, 0x7A, 0xFA, 0xC2]))
+ rom.write_bytes(0x05FEA2, bytearray([0x20, 0x68, 0xE2, 0x20, 0xE2, 0x10, 0x22, 0x4B, 0x82, 0x00, 0x5C, 0xD9, 0x87, 0x17]))
+
+ rom.write_bytes(0x00EDD0, bytearray([0xda, 0xa2, 0x00, 0x00, 0xe2, 0x20, 0xc9, 0x00, 0xf0, 0x0b, 0x48, 0x8a, 0x18, 0x69, 0x0c, 0xaa]))
+ rom.write_bytes(0x00EDE0, bytearray([0x68, 0x3a, 0x3a, 0x80, 0xf1, 0x98, 0x4a, 0x8f, 0x80, 0x24, 0x7e, 0x18, 0x8a, 0x6f, 0x80, 0x24]))
+ rom.write_bytes(0x00EDF0, bytearray([0x7e, 0xaa, 0xbf, 0x00, 0x80, 0x02, 0x0a, 0xaa, 0xc2, 0x20, 0xbf, 0x8e, 0xd4, 0x22, 0xfa, 0x18]))
+ rom.write_bytes(0x00EE00, bytearray([0x5c, 0xb6, 0xc0, 0x17, 0xda, 0xe2, 0x20, 0xa2, 0x00, 0x00, 0xdf, 0x4b, 0xd4, 0x22, 0xf0, 0x0e]))
+ rom.write_bytes(0x00EE10, bytearray([0xe8, 0xe0, 0x30, 0x00, 0xb0, 0x02, 0x80, 0xf2, 0xa9, 0x00, 0xeb, 0xad, 0x1a, 0x02, 0xaa, 0xbf]))
+ rom.write_bytes(0x00EE20, bytearray([0x00, 0x80, 0x02, 0xaa, 0xbf, 0x9e, 0xd4, 0x22, 0xc2, 0x20, 0x29, 0xff, 0x00, 0xfa, 0x5c, 0xfd]))
+ rom.write_bytes(0x00EE30, bytearray([0xc6, 0x17]))
+
+
+def ExtendedItemHandler(rom: LocalRom) -> None:
+ rom.write_bytes(0x10B3FB, bytearray([0xE2, 0x20, 0xC9, 0x45, 0xB0]))
+ rom.write_bytes(0x10B400, bytearray([0x0C, 0xC2, 0x20, 0xA9, 0x10, 0x03, 0x8D, 0x7E, 0x02, 0x5C, 0xCF, 0xF4, 0x0B, 0xAD, 0x0F, 0x0B]))
+ rom.write_bytes(0x10B410, bytearray([0xD0, 0x38, 0xEE, 0xB5, 0x14, 0xA9, 0x18, 0x8D, 0x53, 0x00, 0xAF, 0x9A, 0xFC, 0x0D, 0xF0, 0x09]))
+ rom.write_bytes(0x10B420, bytearray([0xAD, 0xB5, 0x14, 0xCF, 0x99, 0xFC, 0x0D, 0xB0, 0x04, 0x5C, 0x0A, 0xF5, 0x0B, 0xAD, 0xB6, 0x14]))
+ rom.write_bytes(0x10B430, bytearray([0xD0, 0xF7, 0xA9, 0x01, 0x8D, 0xB6, 0x14, 0xA9, 0x0A, 0x8D, 0x18, 0x02, 0xA9, 0x16, 0x8D, 0x18]))
+ rom.write_bytes(0x10B440, bytearray([0x01, 0xA9, 0x97, 0x8D, 0x53, 0x00, 0x5C, 0x0A, 0xF5, 0x0B, 0xFA, 0x5C, 0x10, 0xF5, 0x0B]))
+
+
+def patch_rom(world: "YoshisIslandWorld", rom: LocalRom, player: int) -> None:
+ handle_items(rom) # Implement main item functionality
+ Item_Data(rom) # Pointers necessary for item functionality
+ write_lives(rom) # Writes the number of lives as set in AP
+ CodeHandler(rom) # Jumps to my code
+ Server_Data(rom) # Pointers mostly related to receiving items
+ Menu_Data(rom) # Data related to the AP menu
+ Handle_Locations(rom)
+ ExtendedItemHandler(rom)
+ rom.write_bytes(0x11544B, bytearray(world.global_level_list))
+ rom.write_bytes(0x11547A, bytearray([0x43]))
+
+ rom.write_bytes(0x06FC89, world.starting_lives)
+ rom.write_bytes(0x03464F, ([world.baby_mario_sfx]))
+ rom.write_bytes(0x06FC83, ([world.options.starting_world.value]))
+ rom.write_bytes(0x06FC84, ([world.options.hidden_object_visibility.value]))
+ rom.write_bytes(0x06FC88, ([world.options.shuffle_midrings.value]))
+ rom.write_bytes(0x06FC85, ([world.options.castle_open_condition.value]))
+ rom.write_bytes(0x06FC86, ([world.options.castle_clear_condition.value]))
+ rom.write_bytes(0x06FC87, ([world.options.disable_autoscroll.value]))
+ rom.write_bytes(0x06FC8B, ([world.options.minigame_checks.value]))
+ rom.write_byte(0x06FC8C, world.options.death_link.value)
+ rom.write_bytes(0x06FC8D, bytearray(world.boss_room_id))
+ rom.write_bytes(0x06FC99, bytearray([world.options.luigi_pieces_required.value]))
+ rom.write_bytes(0x06FC9A, bytearray([world.options.goal.value]))
+
+ if world.options.yoshi_colors != YoshiColors.option_normal:
+ rom.write_bytes(0x113A33, bytearray(world.bowser_text))
+
+ rom.write_bytes(0x0A060C, bytearray(world.boss_burt_data))
+ rom.write_bytes(0x0A8666, bytearray(world.boss_slime_data))
+ rom.write_bytes(0x0A9D90, bytearray(world.boss_boo_data))
+ rom.write_bytes(0x0074EA, bytearray(world.boss_pot_data))
+ rom.write_bytes(0x08DC0A, bytearray(world.boss_frog_data))
+ rom.write_bytes(0x0A4440, bytearray(world.boss_plant_data))
+ rom.write_bytes(0x0968A2, bytearray(world.boss_milde_data))
+ rom.write_bytes(0x0B3E10, bytearray(world.boss_koop_data))
+ rom.write_bytes(0x0B4BD0, bytearray(world.boss_slug_data))
+ rom.write_bytes(0x0B6BBA, bytearray(world.boss_raph_data))
+ rom.write_bytes(0x087BED, bytearray(world.boss_tap_data))
+
+ rom.write_bytes(0x07A00D, ([world.tap_tap_room]))
+ rom.write_bytes(0x079DF2, ([world.tap_tap_room]))
+ rom.write_bytes(0x079CCF, ([world.tap_tap_room]))
+ rom.write_bytes(0x079C4D, ([world.tap_tap_room]))
+
+ rom.write_bytes(0x045A2E, bytearray(world.Stage11StageGFX))
+ rom.write_bytes(0x045A31, bytearray(world.Stage12StageGFX))
+ rom.write_bytes(0x045A34, bytearray(world.Stage13StageGFX))
+ rom.write_bytes(0x045A37, bytearray(world.Stage14StageGFX))
+ rom.write_bytes(0x045A3A, bytearray(world.Stage15StageGFX))
+ rom.write_bytes(0x045A3D, bytearray(world.Stage16StageGFX))
+ rom.write_bytes(0x045A40, bytearray(world.Stage17StageGFX))
+ rom.write_bytes(0x045A43, bytearray(world.Stage18StageGFX))
+
+ rom.write_bytes(0x045A52, bytearray(world.Stage21StageGFX))
+ rom.write_bytes(0x045A55, bytearray(world.Stage22StageGFX))
+ rom.write_bytes(0x045A58, bytearray(world.Stage23StageGFX))
+ rom.write_bytes(0x045A5B, bytearray(world.Stage24StageGFX))
+ rom.write_bytes(0x045A5E, bytearray(world.Stage25StageGFX))
+ rom.write_bytes(0x045A61, bytearray(world.Stage26StageGFX))
+ rom.write_bytes(0x045A64, bytearray(world.Stage27StageGFX))
+ rom.write_bytes(0x045A67, bytearray(world.Stage28StageGFX))
+
+ rom.write_bytes(0x045A76, bytearray(world.Stage31StageGFX))
+ rom.write_bytes(0x045A79, bytearray(world.Stage32StageGFX))
+ rom.write_bytes(0x045A7C, bytearray(world.Stage33StageGFX))
+ rom.write_bytes(0x045A7F, bytearray(world.Stage34StageGFX))
+ rom.write_bytes(0x045A82, bytearray(world.Stage35StageGFX))
+ rom.write_bytes(0x045A85, bytearray(world.Stage36StageGFX))
+ rom.write_bytes(0x045A88, bytearray(world.Stage37StageGFX))
+ rom.write_bytes(0x045A8B, bytearray(world.Stage38StageGFX))
+
+ rom.write_bytes(0x045A9A, bytearray(world.Stage41StageGFX))
+ rom.write_bytes(0x045A9D, bytearray(world.Stage42StageGFX))
+ rom.write_bytes(0x045AA0, bytearray(world.Stage43StageGFX))
+ rom.write_bytes(0x045AA3, bytearray(world.Stage44StageGFX))
+ rom.write_bytes(0x045AA6, bytearray(world.Stage45StageGFX))
+ rom.write_bytes(0x045AA9, bytearray(world.Stage46StageGFX))
+ rom.write_bytes(0x045AAC, bytearray(world.Stage47StageGFX))
+ rom.write_bytes(0x045AAF, bytearray(world.Stage48StageGFX))
+
+ rom.write_bytes(0x045ABE, bytearray(world.Stage51StageGFX))
+ rom.write_bytes(0x045AC1, bytearray(world.Stage52StageGFX))
+ rom.write_bytes(0x045AC4, bytearray(world.Stage53StageGFX))
+ rom.write_bytes(0x045AC7, bytearray(world.Stage54StageGFX))
+ rom.write_bytes(0x045ACA, bytearray(world.Stage55StageGFX))
+ rom.write_bytes(0x045ACD, bytearray(world.Stage56StageGFX))
+ rom.write_bytes(0x045AD0, bytearray(world.Stage57StageGFX))
+ rom.write_bytes(0x045AD3, bytearray(world.Stage58StageGFX))
+
+ rom.write_bytes(0x045AE2, bytearray(world.Stage61StageGFX))
+ rom.write_bytes(0x045AE5, bytearray(world.Stage62StageGFX))
+ rom.write_bytes(0x045AE8, bytearray(world.Stage63StageGFX))
+ rom.write_bytes(0x045AEB, bytearray(world.Stage64StageGFX))
+ rom.write_bytes(0x045AEE, bytearray(world.Stage65StageGFX))
+ rom.write_bytes(0x045AF1, bytearray(world.Stage66StageGFX))
+ rom.write_bytes(0x045AF4, bytearray(world.Stage67StageGFX))
+
+ rom.write_bytes(0x0BDBAF, bytearray(world.level_gfx_table))
+ rom.write_bytes(0x0BDC4F, bytearray(world.palette_panel_list))
+
+ if world.options.yoshi_colors == YoshiColors.option_random_order:
+ rom.write_bytes(0x010000, ([world.leader_color]))
+ rom.write_bytes(0x010008, ([world.leader_color]))
+ rom.write_bytes(0x010009, ([world.leader_color]))
+ rom.write_bytes(0x010001, bytearray(world.color_order))
+ rom.write_bytes(0x01000C, ([world.leader_color]))
+ rom.write_bytes(0x010014, ([world.leader_color]))
+ rom.write_bytes(0x010015, ([world.leader_color]))
+ rom.write_bytes(0x01000D, bytearray(world.color_order))
+ rom.write_bytes(0x010018, ([world.leader_color]))
+ rom.write_bytes(0x010020, ([world.leader_color]))
+ rom.write_bytes(0x010021, ([world.leader_color]))
+ rom.write_bytes(0x01001A, bytearray(world.color_order))
+ rom.write_bytes(0x010024, ([world.leader_color]))
+ rom.write_bytes(0x01002C, ([world.leader_color]))
+ rom.write_bytes(0x01002D, ([world.leader_color]))
+ rom.write_bytes(0x010025, bytearray(world.color_order))
+ rom.write_bytes(0x010030, ([world.leader_color]))
+ rom.write_bytes(0x010038, ([world.leader_color]))
+ rom.write_bytes(0x010039, ([world.leader_color]))
+ rom.write_bytes(0x010031, bytearray(world.color_order))
+ rom.write_bytes(0x01003C, ([world.leader_color]))
+ rom.write_bytes(0x010044, ([world.leader_color]))
+ rom.write_bytes(0x010045, ([world.leader_color]))
+ rom.write_bytes(0x01003D, bytearray(world.color_order))
+ rom.write_bytes(0x010043, ([world.leader_color]))
+ elif world.options.yoshi_colors in {YoshiColors.option_random_color, YoshiColors.option_singularity}:
+ rom.write_bytes(0x010000, bytearray(world.level_colors))
+
+ if world.options.minigame_checks in {MinigameChecks.option_bonus_games, MinigameChecks.option_both}:
+ bonus_checks(rom)
+
+ if world.options.minigame_checks in {MinigameChecks.option_bandit_games, MinigameChecks.option_both}:
+ bandit_checks(rom)
+
+ rom.write_bytes(0x00BF2C, bytearray(world.world_bonus))
+
+ if world.options.softlock_prevention:
+ rom.write_bytes(0x00C18F, bytearray([0x5C, 0x58, 0xFB, 0x0B])) # R + X Code
+
+ if world.options.bowser_door_mode != BowserDoor.option_manual:
+ rom.write_bytes(0x07891F, bytearray(world.castle_door)) # 1 Entry
+ rom.write_bytes(0x078923, bytearray(world.castle_door)) # 2 Entry
+ rom.write_bytes(0x078927, bytearray(world.castle_door)) # 3 Entry
+ rom.write_bytes(0x07892B, bytearray(world.castle_door)) # 4 Entry
+
+ if world.options.bowser_door_mode == BowserDoor.option_gauntlet:
+ rom.write_bytes(0x0AF517, bytearray([0xC6, 0x07, 0x7A, 0x00])) # Door 2
+ rom.write_bytes(0x0AF6B7, bytearray([0xCD, 0x05, 0x5B, 0x00])) # Door 3
+ rom.write_bytes(0x0AF8F2, bytearray([0xD3, 0x00, 0x77, 0x06])) # Door 4
+
+ if world.options.goal == PlayerGoal.option_luigi_hunt:
+ rom.write_bytes(0x1153F6, bytearray([0x16, 0x28, 0x10, 0x0C, 0x10, 0x4E, 0x1E, 0x10, 0x08, 0x04, 0x08, 0x24, 0x36, 0x82, 0x83, 0x83, 0x34, 0x84, 0x85, 0x85])) # Luigi piece clear text
+ rom.write_bytes(0x06FC86, bytearray([0xFF])) # Boss clear goal = 255, renders bowser inaccessible
+
+ from Main import __version__
+ rom.name = bytearray(f'YOSHIAP{__version__.replace(".", "")[0:3]}_{player}_{world.multiworld.seed:11}\0', "utf8")[:21]
+ rom.name.extend([0] * (21 - len(rom.name)))
+ rom.write_bytes(0x007FC0, rom.name)
+
+
+class YoshisIslandDeltaPatch(APDeltaPatch):
+ hash = USHASH
+ game: str = "Yoshi's Island"
+ patch_file_ending = ".apyi"
+
+ @classmethod
+ def get_source_data(cls) -> bytes:
+ return get_base_rom_bytes()
+
+
+def get_base_rom_bytes(file_name: str = "") -> bytes:
+ base_rom_bytes = getattr(get_base_rom_bytes, "base_rom_bytes", None)
+ if not base_rom_bytes:
+ file_name = get_base_rom_path(file_name)
+ base_rom_bytes = bytes(Utils.read_snes_rom(open(file_name, "rb")))
+
+ basemd5 = hashlib.md5()
+ basemd5.update(base_rom_bytes)
+ if USHASH != basemd5.hexdigest():
+ raise Exception("Supplied Base Rom does not match known MD5 for US(1.0) release. "
+ "Get the correct game and version, then dump it")
+ get_base_rom_bytes.base_rom_bytes = base_rom_bytes
+ return base_rom_bytes
+
+
+def get_base_rom_path(file_name: str = "") -> str:
+ if not file_name:
+ file_name = get_settings()["yoshisisland_options"]["rom_file"]
+ if not os.path.exists(file_name):
+ file_name = Utils.user_path(file_name)
+ return file_name
diff --git a/worlds/yoshisisland/Rules.py b/worlds/yoshisisland/Rules.py
new file mode 100644
index 000000000000..09f6eaced07c
--- /dev/null
+++ b/worlds/yoshisisland/Rules.py
@@ -0,0 +1,612 @@
+from .level_logic import YoshiLogic
+from worlds.generic.Rules import set_rule
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from . import YoshisIslandWorld
+
+
+def set_easy_rules(world: "YoshisIslandWorld") -> None:
+ logic = YoshiLogic(world)
+ player = world.player
+
+ set_rule(world.multiworld.get_location("Make Eggs, Throw Eggs: Red Coins", player), lambda state: state.has_all({"Dashed Stairs", "Beanstalk"}, player))
+ set_rule(world.multiworld.get_location("Make Eggs, Throw Eggs: Flowers", player), lambda state: state.has_all({"Dashed Stairs", "Beanstalk"}, player))
+ set_rule(world.multiworld.get_location("Make Eggs, Throw Eggs: Stars", player), lambda state: state.has_all({"Tulip", "Beanstalk", "Dashed Stairs"}, player))
+ set_rule(world.multiworld.get_location("Make Eggs, Throw Eggs: Level Clear", player), lambda state: state.has("Beanstalk", player))
+
+ set_rule(world.multiworld.get_location("Watch Out Below!: Red Coins", player), lambda state: state.has_all({"Large Spring Ball", "Helicopter Morph"}, player))
+ set_rule(world.multiworld.get_location("Watch Out Below!: Flowers", player), lambda state: state.has_all({"Large Spring Ball", "Helicopter Morph"}, player))
+ set_rule(world.multiworld.get_location("Watch Out Below!: Stars", player), lambda state: state.has("Large Spring Ball", player) and logic.has_midring(state))
+ set_rule(world.multiworld.get_location("Watch Out Below!: Level Clear", player), lambda state: state.has("Large Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("The Cave Of Chomp Rock: Red Coins", player), lambda state: state.has("Chomp Rock", player))
+ set_rule(world.multiworld.get_location("The Cave Of Chomp Rock: Flowers", player), lambda state: state.has("Chomp Rock", player))
+
+ set_rule(world.multiworld.get_location("Burt The Bashful's Fort: Red Coins", player), lambda state: state.has("Spring Ball", player))
+ set_rule(world.multiworld.get_location("Burt The Bashful's Fort: Flowers", player), lambda state: state.has_all({"Spring Ball", "Key"}, player) and (state.has("Egg Capacity Upgrade", player, 3) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Burt The Bashful's Fort: Stars", player), lambda state: state.has("Spring Ball", player) and (logic.has_midring(state) or state.has("Key", player)))
+
+ set_rule(world.multiworld.get_location("Hop! Hop! Donut Lifts: Stars", player), lambda state: logic.has_midring(state) or logic.cansee_clouds(state))
+
+ set_rule(world.multiworld.get_location("Shy-Guys On Stilts: Red Coins", player), lambda state: state.has_all({"Large Spring Ball", "Flashing Eggs", "Mole Tank Morph", "! Switch"}, player))
+ set_rule(world.multiworld.get_location("Shy-Guys On Stilts: Flowers", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Shy-Guys On Stilts: Stars", player), lambda state: (logic.has_midring(state) and state.has("Tulip", player) or logic.has_midring(state) and state.has("Beanstalk", player)) and state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Shy-Guys On Stilts: Level Clear", player), lambda state: state.has_all({"Large Spring Ball", "Beanstalk"}, player))
+
+ set_rule(world.multiworld.get_location("Touch Fuzzy Get Dizzy: Red Coins", player), lambda state: state.has_all({"Flashing Eggs", "Spring Ball", "Chomp Rock", "Beanstalk"}, player))
+ set_rule(world.multiworld.get_location("Touch Fuzzy Get Dizzy: Stars", player), lambda state: logic.has_midring(state) or (logic.cansee_clouds and state.has_all({"Spring Ball", "Chomp Rock", "Beanstalk"}, player)))
+
+ set_rule(world.multiworld.get_location("Salvo The Slime's Castle: Red Coins", player), lambda state: state.has("Platform Ghost", player))
+ set_rule(world.multiworld.get_location("Salvo The Slime's Castle: Flowers", player), lambda state: state.has("Platform Ghost", player))
+ set_rule(world.multiworld.get_location("Salvo The Slime's Castle: Stars", player), lambda state: logic.has_midring(state) and (state.has("Platform Ghost", player) or state.has_all({"Arrow Wheel", "Key"}, player)))
+
+ set_rule(world.multiworld.get_location("Visit Koopa And Para-Koopa: Red Coins", player), lambda state: state.has_all({"Poochy", "Large Spring Ball", "Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("Visit Koopa And Para-Koopa: Flowers", player), lambda state: state.has_all({"Super Star", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("Visit Koopa And Para-Koopa: Stars", player), lambda state: state.has("Large Spring Ball", player) and logic.has_midring(state))
+ set_rule(world.multiworld.get_location("Visit Koopa And Para-Koopa: Level Clear", player), lambda state: state.has("Large Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("The Baseball Boys: Red Coins", player), lambda state: state.has_all({"Beanstalk", "Super Star", "Egg Launcher", "Large Spring Ball", "Mole Tank Morph"}, player))
+ set_rule(world.multiworld.get_location("The Baseball Boys: Flowers", player), lambda state: state.has_all({"Beanstalk", "Super Star", "Egg Launcher", "Large Spring Ball", "Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("The Baseball Boys: Stars", player), lambda state: (logic.has_midring(state) and (state.has("Tulip", player))) and state.has_all({"Beanstalk", "Super Star", "Large Spring Ball", "Egg Launcher"}, player))
+ set_rule(world.multiworld.get_location("The Baseball Boys: Level Clear", player), lambda state: state.has_all({"Beanstalk", "Super Star", "Egg Launcher", "Large Spring Ball"}, player))
+
+ set_rule(world.multiworld.get_location("What's Gusty Taste Like?: Red Coins", player), lambda state: state.has("! Switch", player))
+ set_rule(world.multiworld.get_location("What's Gusty Taste Like?: Flowers", player), lambda state: state.has_any({"Large Spring Ball", "Super Star"}, player))
+ set_rule(world.multiworld.get_location("What's Gusty Taste Like?: Level Clear", player), lambda state: state.has_any({"Large Spring Ball", "Super Star"}, player))
+
+ set_rule(world.multiworld.get_location("Bigger Boo's Fort: Red Coins", player), lambda state: state.has_all({"! Switch", "Key", "Dashed Stairs"}, player))
+ set_rule(world.multiworld.get_location("Bigger Boo's Fort: Flowers", player), lambda state: state.has_all({"! Switch", "Key", "Dashed Stairs"}, player))
+ set_rule(world.multiworld.get_location("Bigger Boo's Fort: Stars", player), lambda state: state.has_all({"! Switch", "Key", "Dashed Stairs"}, player) and logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("Watch Out For Lakitu: Red Coins", player), lambda state: state.has("Chomp Rock", player))
+ set_rule(world.multiworld.get_location("Watch Out For Lakitu: Flowers", player), lambda state: state.has_all({"Key", "Train Morph", "Chomp Rock"}, player))
+ set_rule(world.multiworld.get_location("Watch Out For Lakitu: Level Clear", player), lambda state: state.has("Chomp Rock", player))
+
+ set_rule(world.multiworld.get_location("The Cave Of The Mystery Maze: Red Coins", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("The Cave Of The Mystery Maze: Flowers", player), lambda state: state.has_all({"Large Spring Ball", "Egg Launcher"}, player))
+ set_rule(world.multiworld.get_location("The Cave Of The Mystery Maze: Stars", player), lambda state: state.has("Large Spring Ball", player) and logic.has_midring(state))
+ set_rule(world.multiworld.get_location("The Cave Of The Mystery Maze: Level Clear", player), lambda state: state.has("Large Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("Lakitu's Wall: Red Coins", player), lambda state: (state.has_any({"Dashed Platform", "Giant Eggs"}, player) or logic.combat_item(state)) and state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Lakitu's Wall: Flowers", player), lambda state: state.has_all({"Large Spring Ball", "! Switch"}, player) and (logic.combat_item(state) or state.has("Giant Eggs", player)))
+ set_rule(world.multiworld.get_location("Lakitu's Wall: Stars", player), lambda state: state.has("Giant Eggs", player) and logic.has_midring(state))
+ set_rule(world.multiworld.get_location("Lakitu's Wall: Level Clear", player), lambda state: state.has_all({"Large Spring Ball", "Car Morph"}, player))
+
+ set_rule(world.multiworld.get_location("The Potted Ghost's Castle: Red Coins", player), lambda state: state.has_all({"Arrow Wheel", "Key"}, player) and (state.has("Egg Capacity Upgrade", player, 1)))
+ set_rule(world.multiworld.get_location("The Potted Ghost's Castle: Flowers", player), lambda state: state.has_all({"Arrow Wheel", "Train Morph", "Key"}, player) and (state.has("Egg Capacity Upgrade", player, 1)))
+ set_rule(world.multiworld.get_location("The Potted Ghost's Castle: Stars", player), lambda state: state.has_all({"Arrow Wheel", "Key"}, player) and logic.has_midring(state) and (state.has("Egg Capacity Upgrade", player, 1)))
+
+ set_rule(world.multiworld.get_location("Welcome To Monkey World!: Stars", player), lambda state: logic.has_midring(state))
+ set_rule(world.multiworld.get_location("Jungle Rhythm...: Red Coins", player), lambda state: state.has_all({"Dashed Stairs", "Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("Jungle Rhythm...: Flowers", player), lambda state: state.has_all({"Dashed Stairs", "Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("Jungle Rhythm...: Stars", player), lambda state: logic.has_midring(state) and state.has("Tulip", player))
+ set_rule(world.multiworld.get_location("Jungle Rhythm...: Level Clear", player), lambda state: state.has_all({"Dashed Stairs", "Spring Ball"}, player))
+
+ set_rule(world.multiworld.get_location("Nep-Enuts' Domain: Red Coins", player), lambda state: state.has_all({"Submarine Morph", "Helicopter Morph"}, player))
+ set_rule(world.multiworld.get_location("Nep-Enuts' Domain: Flowers", player), lambda state: state.has_all({"Submarine Morph", "Helicopter Morph"}, player))
+ set_rule(world.multiworld.get_location("Nep-Enuts' Domain: Stars", player), lambda state: logic.has_midring(state) or state.has_all({"Submarine Morph", "Helicopter Morph"}, player))
+ set_rule(world.multiworld.get_location("Nep-Enuts' Domain: Level Clear", player), lambda state: state.has_all({"Submarine Morph", "Helicopter Morph"}, player))
+
+ set_rule(world.multiworld.get_location("Prince Froggy's Fort: Red Coins", player), lambda state: state.has("Submarine Morph", player))
+ set_rule(world.multiworld.get_location("Prince Froggy's Fort: Flowers", player), lambda state: (state.has("Egg Capacity Upgrade", player, 5) or logic.combat_item(state)) and (state.has("Dashed Platform", player)))
+ set_rule(world.multiworld.get_location("Prince Froggy's Fort: Stars", player), lambda state: logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("Jammin' Through The Trees: Flowers", player), lambda state: state.has("Watermelon", player) or logic.melon_item(state))
+ set_rule(world.multiworld.get_location("Jammin' Through The Trees: Stars", player), lambda state: ((logic.has_midring(state) or state.has("Tulip", player)) and logic.cansee_clouds(state)) or logic.has_midring(state) and state.has("Tulip", player))
+
+ set_rule(world.multiworld.get_location("The Cave Of Harry Hedgehog: Red Coins", player), lambda state: state.has_all({"Chomp Rock", "Beanstalk", "Mole Tank Morph", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("The Cave Of Harry Hedgehog: Flowers", player), lambda state: state.has_all({"Chomp Rock", "Beanstalk", "Mole Tank Morph", "Large Spring Ball", "! Switch"}, player))
+ set_rule(world.multiworld.get_location("The Cave Of Harry Hedgehog: Stars", player), lambda state: state.has_all({"Tulip", "Large Spring Ball", "Dashed Stairs", "Chomp Rock", "Beanstalk", "Mole Tank Morph"}, player) and logic.has_midring(state))
+ set_rule(world.multiworld.get_location("The Cave Of Harry Hedgehog: Level Clear", player), lambda state: state.has_all({"Chomp Rock", "Large Spring Ball", "Key"}, player))
+
+ set_rule(world.multiworld.get_location("Monkeys' Favorite Lake: Red Coins", player), lambda state: state.has_all({"! Switch", "Submarine Morph", "Large Spring Ball", "Beanstalk"}, player))
+ set_rule(world.multiworld.get_location("Monkeys' Favorite Lake: Flowers", player), lambda state: state.has_all({"Beanstalk", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("Monkeys' Favorite Lake: Stars", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Monkeys' Favorite Lake: Level Clear", player), lambda state: state.has("Large Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("Naval Piranha's Castle: Red Coins", player), lambda state: (state.has("Egg Capacity Upgrade", player, 3) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Naval Piranha's Castle: Flowers", player), lambda state: (state.has("Egg Capacity Upgrade", player, 3) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Naval Piranha's Castle: Stars", player), lambda state: logic.has_midring(state) and state.has("Tulip", player))
+
+ set_rule(world.multiworld.get_location("GO! GO! MARIO!!: Red Coins", player), lambda state: state.has("Super Star", player))
+ set_rule(world.multiworld.get_location("GO! GO! MARIO!!: Flowers", player), lambda state: state.has("Super Star", player))
+ set_rule(world.multiworld.get_location("GO! GO! MARIO!!: Stars", player), lambda state: logic.has_midring(state) or (state.has("Tulip", player) and logic.cansee_clouds(state)))
+ set_rule(world.multiworld.get_location("GO! GO! MARIO!!: Level Clear", player), lambda state: state.has("Super Star", player))
+
+ set_rule(world.multiworld.get_location("The Cave Of The Lakitus: Red Coins", player), lambda state: state.has_all({"Large Spring Ball", "! Switch", "Egg Launcher"}, player))
+ set_rule(world.multiworld.get_location("The Cave Of The Lakitus: Flowers", player), lambda state: state.has_all({"Large Spring Ball", "Egg Launcher"}, player))
+ set_rule(world.multiworld.get_location("The Cave Of The Lakitus: Stars", player), lambda state: state.has_all({"Large Spring Ball", "Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("The Cave Of The Lakitus: Level Clear", player), lambda state: state.has("Large Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("Don't Look Back!: Red Coins", player), lambda state: state.has_all({"Helicopter Morph", "! Switch", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("Don't Look Back!: Flowers", player), lambda state: state.has_all({"! Switch", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("Don't Look Back!: Stars", player), lambda state: (logic.has_midring(state) and state.has("Tulip", player)) and state.has("! Switch", player))
+ set_rule(world.multiworld.get_location("Don't Look Back!: Level Clear", player), lambda state: state.has("! Switch", player))
+
+ set_rule(world.multiworld.get_location("Marching Milde's Fort: Red Coins", player), lambda state: state.has_all({"Dashed Stairs", "Vanishing Arrow Wheel", "Arrow Wheel", "Bucket", "Key"}, player) and (state.has("Egg Capacity Upgrade", player, 1) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Marching Milde's Fort: Flowers", player), lambda state: state.has_all({"Dashed Stairs", "Vanishing Arrow Wheel", "Arrow Wheel", "Bucket"}, player) and (state.has("Egg Capacity Upgrade", player, 1) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Marching Milde's Fort: Stars", player), lambda state: state.has("Dashed Stairs", player) and (logic.has_midring(state) or state.has("Vanishing Arrow Wheel", player) or logic.cansee_clouds(state)))
+
+ set_rule(world.multiworld.get_location("Chomp Rock Zone: Red Coins", player), lambda state: state.has_all({"Large Spring Ball", "Chomp Rock"}, player))
+ set_rule(world.multiworld.get_location("Chomp Rock Zone: Flowers", player), lambda state: state.has_all({"Chomp Rock", "! Switch", "Spring Ball", "Dashed Platform"}, player))
+ set_rule(world.multiworld.get_location("Chomp Rock Zone: Stars", player), lambda state: state.has_all({"Chomp Rock", "! Switch", "Spring Ball", "Dashed Platform"}, player))
+
+ set_rule(world.multiworld.get_location("Lake Shore Paradise: Red Coins", player), lambda state: state.has_any({"Large Spring Ball", "Spring Ball"}, player) and (state.has("Egg Plant", player) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Lake Shore Paradise: Flowers", player), lambda state: state.has_any({"Large Spring Ball", "Spring Ball"}, player) and (state.has("Egg Plant", player) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Lake Shore Paradise: Stars", player), lambda state: (logic.has_midring(state) or (state.has("Tulip", player) and logic.cansee_clouds(state))) and (state.has("Egg Plant", player) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Lake Shore Paradise: Level Clear", player), lambda state: state.has("Egg Plant", player) or logic.combat_item(state))
+
+ set_rule(world.multiworld.get_location("Ride Like The Wind: Red Coins", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Ride Like The Wind: Flowers", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Ride Like The Wind: Stars", player), lambda state: (logic.has_midring(state) and state.has("Helicopter Morph", player)) and state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Ride Like The Wind: Level Clear", player), lambda state: state.has("Large Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("Hookbill The Koopa's Castle: Red Coins", player), lambda state: state.has_all({"Dashed Stairs", "Vanishing Arrow Wheel", "Key"}, player))
+ set_rule(world.multiworld.get_location("Hookbill The Koopa's Castle: Flowers", player), lambda state: state.has_all({"Dashed Stairs", "Vanishing Arrow Wheel", "Key"}, player))
+ set_rule(world.multiworld.get_location("Hookbill The Koopa's Castle: Stars", player), lambda state: logic.has_midring(state) and (state.has_any({"Dashed Stairs", "Vanishing Arrow Wheel"}, player)))
+
+ set_rule(world.multiworld.get_location("BLIZZARD!!!: Red Coins", player), lambda state: state.has_all({"Helicopter Morph", "Dashed Stairs"}, player))
+ set_rule(world.multiworld.get_location("BLIZZARD!!!: Stars", player), lambda state: logic.cansee_clouds(state) or ((logic.has_midring(state) and state.has("Dashed Stairs", player)) or state.has("Tulip", player)))
+
+ set_rule(world.multiworld.get_location("Ride The Ski Lifts: Stars", player), lambda state: logic.has_midring(state) or state.has("Super Star", player))
+
+ set_rule(world.multiworld.get_location("Danger - Icy Conditions Ahead: Red Coins", player), lambda state: (state.has("Fire Melon", player) or logic.melon_item(state)) and (state.has_all({"Bucket", "Spring Ball", "Super Star", "Skis", "Dashed Platform"}, player)))
+ set_rule(world.multiworld.get_location("Danger - Icy Conditions Ahead: Flowers", player), lambda state: (state.has("Fire Melon", player) or logic.melon_item(state)) and state.has_all({"Spring Ball", "Skis", "Dashed Platform"}, player))
+ set_rule(world.multiworld.get_location("Danger - Icy Conditions Ahead: Stars", player), lambda state: (logic.has_midring(state) and (state.has("Fire Melon", player) or logic.melon_item(state))) and state.has("Spring Ball", player))
+ set_rule(world.multiworld.get_location("Danger - Icy Conditions Ahead: Level Clear", player), lambda state: state.has_all({"Spring Ball", "Skis", "Dashed Platform"}, player))
+
+ set_rule(world.multiworld.get_location("Sluggy The Unshaven's Fort: Red Coins", player), lambda state: state.has_all({"Dashed Stairs", "Dashed Platform", "Platform Ghost"}, player))
+ set_rule(world.multiworld.get_location("Sluggy The Unshaven's Fort: Flowers", player), lambda state: state.has_all({"Dashed Stairs", "Platform Ghost", "Dashed Platform"}, player))
+ set_rule(world.multiworld.get_location("Sluggy The Unshaven's Fort: Stars", player), lambda state: ((state.has_all({"Dashed Stairs", "Platform Ghost"}, player)) and logic.has_midring(state)) or (logic.cansee_clouds(state) and state.has("Dashed Stairs", player) and state.has("Dashed Platform", player)))
+
+ set_rule(world.multiworld.get_location("Goonie Rides!: Red Coins", player), lambda state: state.has_all({"Helicopter Morph", "! Switch"}, player))
+ set_rule(world.multiworld.get_location("Goonie Rides!: Flowers", player), lambda state: state.has_all({"Helicopter Morph", "! Switch"}, player))
+ set_rule(world.multiworld.get_location("Goonie Rides!: Stars", player), lambda state: logic.has_midring(state))
+ set_rule(world.multiworld.get_location("Goonie Rides!: Level Clear", player), lambda state: state.has_all({"Helicopter Morph", "! Switch"}, player))
+
+ set_rule(world.multiworld.get_location("Welcome To Cloud World: Stars", player), lambda state: logic.has_midring(state) or state.has("Tulip", player))
+
+ set_rule(world.multiworld.get_location("Shifting Platforms Ahead: Red Coins", player), lambda state: state.has("Egg Capacity Upgrade", player, 1) or logic.combat_item(state))
+ set_rule(world.multiworld.get_location("Shifting Platforms Ahead: Flowers", player), lambda state: state.has("Egg Capacity Upgrade", player, 1) or logic.combat_item(state))
+ set_rule(world.multiworld.get_location("Shifting Platforms Ahead: Stars", player), lambda state: logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("Raphael The Raven's Castle: Red Coins", player), lambda state: state.has_all({"Arrow Wheel", "Train Morph"}, player))
+ set_rule(world.multiworld.get_location("Raphael The Raven's Castle: Flowers", player), lambda state: state.has_all({"Arrow Wheel", "Train Morph"}, player))
+ set_rule(world.multiworld.get_location("Raphael The Raven's Castle: Stars", player), lambda state: logic.has_midring(state) and state.has("Arrow Wheel", player))
+
+ set_rule(world.multiworld.get_location("Scary Skeleton Goonies!: Red Coins", player), lambda state: state.has_all({"Dashed Platform", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("Scary Skeleton Goonies!: Flowers", player), lambda state: state.has_all({"Dashed Platform", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("Scary Skeleton Goonies!: Stars", player), lambda state: state.has("Dashed Platform", player) and logic.has_midring(state))
+ set_rule(world.multiworld.get_location("Scary Skeleton Goonies!: Level Clear", player), lambda state: state.has_all({"Dashed Platform", "Large Spring Ball"}, player))
+
+ set_rule(world.multiworld.get_location("The Cave Of The Bandits: Red Coins", player), lambda state: state.has("Super Star", player))
+ set_rule(world.multiworld.get_location("The Cave Of The Bandits: Flowers", player), lambda state: state.has("Super Star", player))
+ set_rule(world.multiworld.get_location("The Cave Of The Bandits: Stars", player), lambda state: logic.cansee_clouds(state) or logic.has_midring(state))
+ set_rule(world.multiworld.get_location("The Cave Of The Bandits: Level Clear", player), lambda state: state.has("Super Star", player))
+
+ set_rule(world.multiworld.get_location("Tap-Tap The Red Nose's Fort: Red Coins", player), lambda state: state.has_all({"Spring Ball", "Large Spring Ball", "Egg Plant", "Key"}, player) and (state.has("Egg Capacity Upgrade", player, 3) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Tap-Tap The Red Nose's Fort: Flowers", player), lambda state: state.has_all({"Spring Ball", "Large Spring Ball", "Egg Plant", "Key"}, player) and (state.has("Egg Capacity Upgrade", player, 3) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Tap-Tap The Red Nose's Fort: Stars", player), lambda state: logic.has_midring(state) and state.has_all({"Spring Ball", "Large Spring Ball", "Egg Plant", "Key"}, player))
+
+ set_rule(world.multiworld.get_location("The Very Loooooong Cave: Red Coins", player), lambda state: state.has("Chomp Rock", player) and (state.has("Egg Capacity Upgrade", player, 3) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("The Very Loooooong Cave: Flowers", player), lambda state: state.has("Chomp Rock", player) and (state.has("Egg Capacity Upgrade", player, 3) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("The Very Loooooong Cave: Stars", player), lambda state: state.has("Chomp Rock", player) and (state.has("Egg Capacity Upgrade", player, 3) or logic.combat_item(state)) and logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("The Deep, Underground Maze: Red Coins", player), lambda state: state.has_all({"Chomp Rock", "Key", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("The Deep, Underground Maze: Flowers", player), lambda state: state.has_all({"Chomp Rock", "Key", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("The Deep, Underground Maze: Stars", player), lambda state: state.has_all({"Chomp Rock", "Tulip", "Key"}, player) or (logic.has_midring(state) and state.has_all({"Key", "Chomp Rock", "Large Spring Ball"}, player)))
+ set_rule(world.multiworld.get_location("The Deep, Underground Maze: Level Clear", player), lambda state: state.has_all({"Chomp Rock", "Key", "Large Spring Ball", "Dashed Platform"}, player))
+
+ set_rule(world.multiworld.get_location("KEEP MOVING!!!!: Red Coins", player), lambda state: (state.has("Egg Capacity Upgrade", player, 1) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("KEEP MOVING!!!!: Flowers", player), lambda state: state.has("Egg Plant", player))
+ set_rule(world.multiworld.get_location("KEEP MOVING!!!!: Stars", player), lambda state: logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("King Bowser's Castle: Red Coins", player), lambda state: state.has_all({"Helicopter Morph", "Egg Plant"}, player) and logic._68CollectibleRoute(state))
+ set_rule(world.multiworld.get_location("King Bowser's Castle: Flowers", player), lambda state: state.has_all({"Helicopter Morph", "Egg Plant"}, player) and logic._68CollectibleRoute(state))
+ set_rule(world.multiworld.get_location("King Bowser's Castle: Stars", player), lambda state: state.has_all({"Helicopter Morph", "Egg Plant"}, player) and logic._68Route(state))
+
+ set_easy_extra_rules(world)
+
+
+def set_easy_extra_rules(world: "YoshisIslandWorld") -> None:
+ player = world.player
+ logic = YoshiLogic(world)
+ if not world.options.extras_enabled:
+ return
+ set_rule(world.multiworld.get_location("Poochy Ain't Stupid: Red Coins", player), lambda state: state.has("Poochy", player))
+ set_rule(world.multiworld.get_location("Poochy Ain't Stupid: Flowers", player), lambda state: state.has("Poochy", player))
+ set_rule(world.multiworld.get_location("Poochy Ain't Stupid: Stars", player), lambda state: state.has("Poochy", player))
+ set_rule(world.multiworld.get_location("Poochy Ain't Stupid: Level Clear", player), lambda state: state.has("Poochy", player))
+
+ set_rule(world.multiworld.get_location("Hit That Switch!!: Red Coins", player), lambda state: state.has_all({"Large Spring Ball", "! Switch"}, player))
+ set_rule(world.multiworld.get_location("Hit That Switch!!: Flowers", player), lambda state: state.has_all({"Large Spring Ball", "! Switch"}, player))
+ set_rule(world.multiworld.get_location("Hit That Switch!!: Level Clear", player), lambda state: state.has_all({"Large Spring Ball", "! Switch"}, player))
+
+ set_rule(world.multiworld.get_location("The Impossible? Maze: Red Coins", player), lambda state: state.has_all({"Spring Ball", "Large Spring Ball", "Mole Tank Morph", "Helicopter Morph", "Flashing Eggs"}, player))
+ set_rule(world.multiworld.get_location("The Impossible? Maze: Flowers", player), lambda state: state.has_all({"Spring Ball", "Large Spring Ball", "Mole Tank Morph", "Helicopter Morph"}, player))
+ set_rule(world.multiworld.get_location("The Impossible? Maze: Stars", player), lambda state: state.has_all({"Spring Ball", "Large Spring Ball", "Mole Tank Morph"}, player))
+ set_rule(world.multiworld.get_location("The Impossible? Maze: Level Clear", player), lambda state: state.has_all({"Spring Ball", "Large Spring Ball", "Mole Tank Morph", "Helicopter Morph"}, player))
+
+ set_rule(world.multiworld.get_location("Kamek's Revenge: Red Coins", player), lambda state: state.has_all({"Key", "Skis", "Helicopter Morph", "! Switch"}, player) and logic.has_midring(state))
+ set_rule(world.multiworld.get_location("Kamek's Revenge: Flowers", player), lambda state: state.has_all({"Key", "Skis", "Helicopter Morph", "! Switch"}, player) and logic.has_midring(state))
+ set_rule(world.multiworld.get_location("Kamek's Revenge: Stars", player), lambda state: state.has("! Switch", player) and logic.has_midring(state))
+ set_rule(world.multiworld.get_location("Kamek's Revenge: Level Clear", player), lambda state: state.has_all({"Key", "Skis", "! Switch", "Helicopter Morph"}, player))
+
+ set_rule(world.multiworld.get_location("Castles - Masterpiece Set: Red Coins", player), lambda state: (state.has("Egg Capacity Upgrade", player, 2) or logic.combat_item(state)) and state.has(("Large Spring Ball"), player))
+ set_rule(world.multiworld.get_location("Castles - Masterpiece Set: Flowers", player), lambda state: (state.has("Egg Capacity Upgrade", player, 2) or logic.combat_item(state)) and state.has(("Large Spring Ball"), player))
+ set_rule(world.multiworld.get_location("Castles - Masterpiece Set: Stars", player), lambda state: logic.has_midring(state) and state.has(("Large Spring Ball"), player))
+ set_rule(world.multiworld.get_location("Castles - Masterpiece Set: Level Clear", player), lambda state: (state.has("Egg Capacity Upgrade", player, 2) or logic.combat_item(state)) and state.has(("Large Spring Ball"), player))
+
+
+def set_normal_rules(world: "YoshisIslandWorld") -> None:
+ logic = YoshiLogic(world)
+ player = world.player
+
+ set_rule(world.multiworld.get_location("Make Eggs, Throw Eggs: Red Coins", player), lambda state: state.has("Dashed Stairs", player))
+ set_rule(world.multiworld.get_location("Make Eggs, Throw Eggs: Flowers", player), lambda state: state.has("Dashed Stairs", player))
+ set_rule(world.multiworld.get_location("Make Eggs, Throw Eggs: Stars", player), lambda state: state.has_any({"Tulip", "Dashed Stairs"}, player))
+
+ set_rule(world.multiworld.get_location("Watch Out Below!: Red Coins", player), lambda state: state.has("Helicopter Morph", player))
+ set_rule(world.multiworld.get_location("Watch Out Below!: Flowers", player), lambda state: state.has("Helicopter Morph", player))
+ set_rule(world.multiworld.get_location("Watch Out Below!: Stars", player), lambda state: logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("Burt The Bashful's Fort: Red Coins", player), lambda state: state.has("Spring Ball", player))
+ set_rule(world.multiworld.get_location("Burt The Bashful's Fort: Flowers", player), lambda state: state.has_all({"Spring Ball", "Key"}, player) and (state.has("Egg Capacity Upgrade", player, 3) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Burt The Bashful's Fort: Stars", player), lambda state: state.has("Spring Ball", player))
+ set_rule(world.multiworld.get_location("Burt The Bashful's Fort: Level Clear", player), lambda state: logic._14CanFightBoss(state))
+
+
+ set_rule(world.multiworld.get_location("Shy-Guys On Stilts: Red Coins", player), lambda state: state.has_all({"Large Spring Ball", "Flashing Eggs", "Mole Tank Morph", "! Switch"}, player))
+ set_rule(world.multiworld.get_location("Shy-Guys On Stilts: Flowers", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Shy-Guys On Stilts: Stars", player), lambda state: (logic.has_midring(state) and state.has_any(["Tulip", "Beanstalk"], player)) or (state.has_all(["Tulip", "Beanstalk", "Large Spring Ball"], player)))
+ set_rule(world.multiworld.get_location("Shy-Guys On Stilts: Level Clear", player), lambda state: state.has_all({"Large Spring Ball", "Beanstalk"}, player))
+
+ set_rule(world.multiworld.get_location("Touch Fuzzy Get Dizzy: Red Coins", player), lambda state: state.has_all({"Flashing Eggs", "Spring Ball", "Chomp Rock", "Beanstalk"}, player))
+ set_rule(world.multiworld.get_location("Touch Fuzzy Get Dizzy: Stars", player), lambda state: logic.has_midring(state) or (logic.cansee_clouds and state.has_all({"Spring Ball", "Chomp Rock", "Beanstalk"}, player)))
+
+ set_rule(world.multiworld.get_location("Salvo The Slime's Castle: Red Coins", player), lambda state: state.has("Platform Ghost", player))
+ set_rule(world.multiworld.get_location("Salvo The Slime's Castle: Flowers", player), lambda state: state.has("Platform Ghost", player))
+ set_rule(world.multiworld.get_location("Salvo The Slime's Castle: Stars", player), lambda state: logic.has_midring(state) and (state.has("Platform Ghost", player) or state.has_all({"Arrow Wheel", "Key"}, player)))
+
+ set_rule(world.multiworld.get_location("Visit Koopa And Para-Koopa: Red Coins", player), lambda state: state.has_all({"Poochy", "Large Spring Ball", "Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("Visit Koopa And Para-Koopa: Flowers", player), lambda state: state.has_all({"Super Star", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("Visit Koopa And Para-Koopa: Stars", player), lambda state: state.has("Large Spring Ball", player) and logic.has_midring(state))
+ set_rule(world.multiworld.get_location("Visit Koopa And Para-Koopa: Level Clear", player), lambda state: state.has("Large Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("The Baseball Boys: Red Coins", player), lambda state: state.has_all({"Beanstalk", "Super Star", "Large Spring Ball", "Mole Tank Morph"}, player))
+ set_rule(world.multiworld.get_location("The Baseball Boys: Flowers", player), lambda state: state.has_all({"Super Star", "Large Spring Ball", "Beanstalk", "Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("The Baseball Boys: Stars", player), lambda state: (logic.has_midring(state) or (state.has("Tulip", player))) and state.has_all({"Beanstalk", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("The Baseball Boys: Level Clear", player), lambda state: state.has_all({"Beanstalk", "Super Star", "Large Spring Ball"}, player))
+
+ set_rule(world.multiworld.get_location("What's Gusty Taste Like?: Red Coins", player), lambda state: state.has("! Switch", player))
+
+ set_rule(world.multiworld.get_location("Bigger Boo's Fort: Red Coins", player), lambda state: state.has_all({"! Switch", "Key", "Dashed Stairs"}, player))
+ set_rule(world.multiworld.get_location("Bigger Boo's Fort: Flowers", player), lambda state: state.has_all({"! Switch", "Key", "Dashed Stairs"}, player))
+ set_rule(world.multiworld.get_location("Bigger Boo's Fort: Stars", player), lambda state: state.has_all({"! Switch", "Dashed Stairs"}, player))
+
+ set_rule(world.multiworld.get_location("Watch Out For Lakitu: Flowers", player), lambda state: state.has_all({"Key", "Train Morph"}, player))
+
+ set_rule(world.multiworld.get_location("The Cave Of The Mystery Maze: Red Coins", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("The Cave Of The Mystery Maze: Flowers", player), lambda state: state.has_all({"Large Spring Ball", "Egg Launcher"}, player))
+ set_rule(world.multiworld.get_location("The Cave Of The Mystery Maze: Stars", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("The Cave Of The Mystery Maze: Level Clear", player), lambda state: state.has("Large Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("Lakitu's Wall: Flowers", player), lambda state: state.has_all({"Large Spring Ball", "! Switch"}, player) and (logic.combat_item(state) or state.has("Giant Eggs", player)))
+ set_rule(world.multiworld.get_location("Lakitu's Wall: Stars", player), lambda state: state.has("Giant Eggs", player) or logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("The Potted Ghost's Castle: Red Coins", player), lambda state: state.has_all({"Arrow Wheel", "Key"}, player))
+ set_rule(world.multiworld.get_location("The Potted Ghost's Castle: Flowers", player), lambda state: state.has_all({"Arrow Wheel", "Key", "Train Morph"}, player))
+ set_rule(world.multiworld.get_location("The Potted Ghost's Castle: Stars", player), lambda state: state.has("Arrow Wheel", player) and (logic.has_midring(state) or state.has("Key", player)))
+
+ set_rule(world.multiworld.get_location("Welcome To Monkey World!: Stars", player), lambda state: logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("Jungle Rhythm...: Red Coins", player), lambda state: state.has("Dashed Stairs", player))
+ set_rule(world.multiworld.get_location("Jungle Rhythm...: Flowers", player), lambda state: state.has("Dashed Stairs", player))
+ set_rule(world.multiworld.get_location("Jungle Rhythm...: Stars", player), lambda state: logic.has_midring(state) and state.has("Tulip", player))
+ set_rule(world.multiworld.get_location("Jungle Rhythm...: Level Clear", player), lambda state: state.has("Dashed Stairs", player))
+
+ set_rule(world.multiworld.get_location("Nep-Enuts' Domain: Red Coins", player), lambda state: state.has_all({"Submarine Morph", "Helicopter Morph"}, player))
+ set_rule(world.multiworld.get_location("Nep-Enuts' Domain: Flowers", player), lambda state: state.has_all({"Submarine Morph", "Helicopter Morph"}, player))
+ set_rule(world.multiworld.get_location("Nep-Enuts' Domain: Level Clear", player), lambda state: state.has_all({"Submarine Morph", "Helicopter Morph"}, player))
+
+ set_rule(world.multiworld.get_location("Prince Froggy's Fort: Red Coins", player), lambda state: state.has("Submarine Morph", player))
+ set_rule(world.multiworld.get_location("Prince Froggy's Fort: Flowers", player), lambda state: (state.has("Egg Capacity Upgrade", player, 5) or logic.combat_item(state)) and (state.has("Dashed Platform", player) or logic.has_midring(state)))
+ set_rule(world.multiworld.get_location("Prince Froggy's Fort: Stars", player), lambda state: logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("Jammin' Through The Trees: Flowers", player), lambda state: state.has("Watermelon", player) or logic.melon_item(state))
+
+ set_rule(world.multiworld.get_location("The Cave Of Harry Hedgehog: Red Coins", player), lambda state: state.has_any({"Dashed Stairs", "Beanstalk"}, player) and state.has_all({"Mole Tank Morph", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("The Cave Of Harry Hedgehog: Flowers", player), lambda state: state.has_any({"Dashed Stairs", "Beanstalk"}, player) and state.has_all({"! Switch", "Mole Tank Morph", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("The Cave Of Harry Hedgehog: Stars", player), lambda state: (state.has_any({"Dashed Stairs", "Beanstalk"}, player) and state.has_all({"Mole Tank Morph", "Large Spring Ball"}, player)) and (logic.has_midring(state) or state.has("Tulip", player)))
+ set_rule(world.multiworld.get_location("The Cave Of Harry Hedgehog: Level Clear", player), lambda state: state.has_all({"Large Spring Ball", "Key"}, player))
+
+ set_rule(world.multiworld.get_location("Monkeys' Favorite Lake: Red Coins", player), lambda state: state.has_all({"! Switch", "Submarine Morph", "Large Spring Ball", "Beanstalk"}, player))
+ set_rule(world.multiworld.get_location("Monkeys' Favorite Lake: Flowers", player), lambda state: state.has("Beanstalk", player))
+
+ set_rule(world.multiworld.get_location("Naval Piranha's Castle: Red Coins", player), lambda state: (state.has("Egg Capacity Upgrade", player, 1) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Naval Piranha's Castle: Flowers", player), lambda state: (state.has("Egg Capacity Upgrade", player, 1) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Naval Piranha's Castle: Stars", player), lambda state: (logic.has_midring(state) and state.has("Tulip", player)) and state.has("Egg Capacity Upgrade", player, 1))
+
+ set_rule(world.multiworld.get_location("GO! GO! MARIO!!: Red Coins", player), lambda state: state.has("Super Star", player))
+ set_rule(world.multiworld.get_location("GO! GO! MARIO!!: Flowers", player), lambda state: state.has("Super Star", player))
+ set_rule(world.multiworld.get_location("GO! GO! MARIO!!: Stars", player), lambda state: state.has("Super Star", player))
+ set_rule(world.multiworld.get_location("GO! GO! MARIO!!: Level Clear", player), lambda state: state.has("Super Star", player))
+
+ set_rule(world.multiworld.get_location("The Cave Of The Lakitus: Red Coins", player), lambda state: state.has_all({"Large Spring Ball", "! Switch", "Egg Launcher"}, player))
+ set_rule(world.multiworld.get_location("The Cave Of The Lakitus: Flowers", player), lambda state: state.has_all({"Large Spring Ball", "Egg Launcher"}, player))
+ set_rule(world.multiworld.get_location("The Cave Of The Lakitus: Stars", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("The Cave Of The Lakitus: Level Clear", player), lambda state: state.has("Large Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("Don't Look Back!: Red Coins", player), lambda state: state.has_all({"Helicopter Morph", "! Switch", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("Don't Look Back!: Flowers", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Don't Look Back!: Stars", player), lambda state: (logic.has_midring(state) or state.has("Tulip", player)) and state.has("! Switch", player))
+ set_rule(world.multiworld.get_location("Don't Look Back!: Level Clear", player), lambda state: state.has("! Switch", player))
+
+ set_rule(world.multiworld.get_location("Marching Milde's Fort: Red Coins", player), lambda state: state.has_all({"Dashed Stairs", "Vanishing Arrow Wheel", "Arrow Wheel", "Bucket", "Key"}, player))
+ set_rule(world.multiworld.get_location("Marching Milde's Fort: Flowers", player), lambda state: state.has_all({"Dashed Stairs", "Vanishing Arrow Wheel", "Arrow Wheel", "Bucket"}, player))
+ set_rule(world.multiworld.get_location("Marching Milde's Fort: Stars", player), lambda state: state.has("Dashed Stairs", player))
+
+ set_rule(world.multiworld.get_location("Chomp Rock Zone: Red Coins", player), lambda state: state.has_all({"Large Spring Ball", "Chomp Rock"}, player))
+ set_rule(world.multiworld.get_location("Chomp Rock Zone: Flowers", player), lambda state: state.has_all({"Chomp Rock", "! Switch", "Dashed Platform"}, player))
+ set_rule(world.multiworld.get_location("Chomp Rock Zone: Stars", player), lambda state: state.has_all({"Chomp Rock", "! Switch", "Dashed Platform"}, player))
+
+ set_rule(world.multiworld.get_location("Lake Shore Paradise: Red Coins", player), lambda state: state.has("Egg Plant", player) or logic.combat_item(state))
+ set_rule(world.multiworld.get_location("Lake Shore Paradise: Flowers", player), lambda state: state.has("Egg Plant", player) or logic.combat_item(state))
+ set_rule(world.multiworld.get_location("Lake Shore Paradise: Stars", player), lambda state: (logic.has_midring(state) or (state.has("Tulip", player) and logic.cansee_clouds(state))) and (state.has("Egg Plant", player) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Lake Shore Paradise: Level Clear", player), lambda state: state.has("Egg Plant", player) or logic.combat_item(state))
+
+ set_rule(world.multiworld.get_location("Ride Like The Wind: Red Coins", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Ride Like The Wind: Flowers", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Ride Like The Wind: Stars", player), lambda state: (logic.has_midring(state) or state.has("Helicopter Morph", player)) and state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Ride Like The Wind: Level Clear", player), lambda state: state.has("Large Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("Hookbill The Koopa's Castle: Red Coins", player), lambda state: state.has_all({"Dashed Stairs", "Vanishing Arrow Wheel", "Key"}, player))
+ set_rule(world.multiworld.get_location("Hookbill The Koopa's Castle: Flowers", player), lambda state: state.has_all({"Dashed Stairs", "Vanishing Arrow Wheel", "Key"}, player))
+ set_rule(world.multiworld.get_location("Hookbill The Koopa's Castle: Stars", player), lambda state: logic.has_midring(state) or (state.has_any({"Dashed Stairs", "Vanishing Arrow Wheel"}, player)))
+
+ set_rule(world.multiworld.get_location("BLIZZARD!!!: Red Coins", player), lambda state: state.has_any({"Dashed Stairs", "Ice Melon"}, player) and (state.has("Egg Capacity Upgrade", player, 1) or logic.combat_item(state) or state.has("Helicopter Morph", player)))
+
+ set_rule(world.multiworld.get_location("Danger - Icy Conditions Ahead: Red Coins", player), lambda state: (state.has("Fire Melon", player) or logic.melon_item(state)) and (state.has_all({"Spring Ball", "Skis"}, player)) and (state.has("Super Star", player) or logic.melon_item(state)))
+ set_rule(world.multiworld.get_location("Danger - Icy Conditions Ahead: Flowers", player), lambda state: (state.has("Fire Melon", player) or logic.melon_item(state)) and state.has_all({"Spring Ball", "Skis"}, player))
+ set_rule(world.multiworld.get_location("Danger - Icy Conditions Ahead: Stars", player), lambda state: (logic.has_midring(state) and (state.has("Fire Melon", player) or logic.melon_item(state))) or (logic.has_midring(state) and (state.has_all({"Tulip", "Dashed Platform"}, player))))
+ set_rule(world.multiworld.get_location("Danger - Icy Conditions Ahead: Level Clear", player), lambda state: state.has_all({"Spring Ball", "Skis"}, player))
+
+ set_rule(world.multiworld.get_location("Sluggy The Unshaven's Fort: Red Coins", player), lambda state: state.has_all({"Dashed Stairs", "Dashed Platform", "Platform Ghost"}, player))
+ set_rule(world.multiworld.get_location("Sluggy The Unshaven's Fort: Flowers", player), lambda state: state.has_all({"Dashed Stairs", "Platform Ghost", "Dashed Platform"}, player))
+ set_rule(world.multiworld.get_location("Sluggy The Unshaven's Fort: Stars", player), lambda state: ((state.has_all({"Dashed Stairs", "Platform Ghost"}, player))) or (logic.cansee_clouds(state) and state.has("Dashed Stairs", player)))
+
+ set_rule(world.multiworld.get_location("Goonie Rides!: Red Coins", player), lambda state: state.has("Helicopter Morph", player))
+ set_rule(world.multiworld.get_location("Goonie Rides!: Flowers", player), lambda state: state.has_all({"Helicopter Morph", "! Switch"}, player))
+
+ set_rule(world.multiworld.get_location("Shifting Platforms Ahead: Stars", player), lambda state: logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("Raphael The Raven's Castle: Red Coins", player), lambda state: state.has_all({"Arrow Wheel", "Train Morph"}, player))
+ set_rule(world.multiworld.get_location("Raphael The Raven's Castle: Flowers", player), lambda state: state.has_all({"Arrow Wheel", "Train Morph"}, player))
+ set_rule(world.multiworld.get_location("Raphael The Raven's Castle: Stars", player), lambda state: logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("Scary Skeleton Goonies!: Red Coins", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Scary Skeleton Goonies!: Flowers", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Scary Skeleton Goonies!: Stars", player), lambda state: logic.has_midring(state))
+ set_rule(world.multiworld.get_location("Scary Skeleton Goonies!: Level Clear", player), lambda state: state.has("Large Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("The Cave Of The Bandits: Red Coins", player), lambda state: state.has("Super Star", player))
+ set_rule(world.multiworld.get_location("The Cave Of The Bandits: Flowers", player), lambda state: state.has("Super Star", player))
+ set_rule(world.multiworld.get_location("The Cave Of The Bandits: Level Clear", player), lambda state: state.has("Super Star", player))
+
+ set_rule(world.multiworld.get_location("Tap-Tap The Red Nose's Fort: Red Coins", player), lambda state: state.has_all({"Large Spring Ball", "Egg Plant", "Key"}, player) and (state.has("Egg Capacity Upgrade", player, 2) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Tap-Tap The Red Nose's Fort: Flowers", player), lambda state: state.has_all({"Large Spring Ball", "Egg Plant", "Key"}, player) and (state.has("Egg Capacity Upgrade", player, 2) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Tap-Tap The Red Nose's Fort: Stars", player), lambda state: logic.has_midring(state) and state.has_all({"Spring Ball", "Egg Plant", "Key"}, player))
+
+ set_rule(world.multiworld.get_location("The Very Loooooong Cave: Red Coins", player), lambda state: state.has("Chomp Rock", player) and (state.has("Egg Capacity Upgrade", player, 2) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("The Very Loooooong Cave: Flowers", player), lambda state: state.has("Chomp Rock", player) and (state.has("Egg Capacity Upgrade", player, 2) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("The Very Loooooong Cave: Stars", player), lambda state: state.has("Chomp Rock", player) and (state.has("Egg Capacity Upgrade", player, 2) or logic.combat_item(state)) and logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("The Deep, Underground Maze: Red Coins", player), lambda state: state.has_all({"Chomp Rock", "Key", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("The Deep, Underground Maze: Flowers", player), lambda state: state.has_all({"Chomp Rock", "Key", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("The Deep, Underground Maze: Stars", player), lambda state: state.has_all({"Chomp Rock", "Tulip", "Key"}, player) or (logic.has_midring(state) and state.has_all({"Key", "Chomp Rock", "Large Spring Ball"}, player)))
+ set_rule(world.multiworld.get_location("The Deep, Underground Maze: Level Clear", player), lambda state: state.has_all({"Key", "Large Spring Ball", "Dashed Platform"}, player) and (logic.combat_item(state) or state.has("Chomp Rock", player)))
+
+ set_rule(world.multiworld.get_location("KEEP MOVING!!!!: Stars", player), lambda state: logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("King Bowser's Castle: Red Coins", player), lambda state: state.has_all({"Helicopter Morph", "Egg Plant"}, player) and logic._68CollectibleRoute(state))
+ set_rule(world.multiworld.get_location("King Bowser's Castle: Flowers", player), lambda state: state.has_all({"Helicopter Morph", "Egg Plant"}, player) and logic._68CollectibleRoute(state))
+ set_rule(world.multiworld.get_location("King Bowser's Castle: Stars", player), lambda state: state.has_all({"Helicopter Morph", "Egg Plant"}, player) and logic._68Route(state))
+
+ set_normal_extra_rules(world)
+
+
+def set_normal_extra_rules(world: "YoshisIslandWorld") -> None:
+ player = world.player
+ logic = YoshiLogic(world)
+ if not world.options.extras_enabled:
+ return
+
+ set_rule(world.multiworld.get_location("Poochy Ain't Stupid: Red Coins", player), lambda state: state.has("Poochy", player))
+ set_rule(world.multiworld.get_location("Poochy Ain't Stupid: Flowers", player), lambda state: state.has("Poochy", player))
+ set_rule(world.multiworld.get_location("Poochy Ain't Stupid: Stars", player), lambda state: state.has("Poochy", player))
+ set_rule(world.multiworld.get_location("Poochy Ain't Stupid: Level Clear", player), lambda state: state.has("Poochy", player))
+
+ set_rule(world.multiworld.get_location("Hit That Switch!!: Red Coins", player), lambda state: state.has_all({"Large Spring Ball", "! Switch"}, player))
+ set_rule(world.multiworld.get_location("Hit That Switch!!: Flowers", player), lambda state: state.has_all({"Large Spring Ball", "! Switch"}, player))
+ set_rule(world.multiworld.get_location("Hit That Switch!!: Level Clear", player), lambda state: state.has_all({"Large Spring Ball", "! Switch"}, player))
+
+ set_rule(world.multiworld.get_location("The Impossible? Maze: Red Coins", player), lambda state: state.has_all({"Spring Ball", "Large Spring Ball", "Mole Tank Morph", "Helicopter Morph", "Flashing Eggs"}, player))
+ set_rule(world.multiworld.get_location("The Impossible? Maze: Flowers", player), lambda state: state.has_all({"Spring Ball", "Large Spring Ball", "Mole Tank Morph", "Helicopter Morph"}, player))
+ set_rule(world.multiworld.get_location("The Impossible? Maze: Stars", player), lambda state: state.has_all({"Spring Ball", "Large Spring Ball", "Mole Tank Morph"}, player))
+ set_rule(world.multiworld.get_location("The Impossible? Maze: Level Clear", player), lambda state: state.has_all({"Spring Ball", "Large Spring Ball", "Mole Tank Morph", "Helicopter Morph"}, player))
+
+ set_rule(world.multiworld.get_location("Kamek's Revenge: Red Coins", player), lambda state: state.has_all({"Key", "Skis", "Helicopter Morph", "! Switch"}, player) and logic.has_midring(state))
+ set_rule(world.multiworld.get_location("Kamek's Revenge: Flowers", player), lambda state: state.has_all({"Key", "Skis", "Helicopter Morph", "! Switch"}, player) and logic.has_midring(state))
+ set_rule(world.multiworld.get_location("Kamek's Revenge: Stars", player), lambda state: state.has("! Switch", player) or logic.has_midring(state))
+ set_rule(world.multiworld.get_location("Kamek's Revenge: Level Clear", player), lambda state: state.has_all({"Key", "Skis", "Helicopter Morph"}, player))
+
+ set_rule(world.multiworld.get_location("Castles - Masterpiece Set: Red Coins", player), lambda state: (state.has("Egg Capacity Upgrade", player, 1) or logic.combat_item(state)) and state.has(("Large Spring Ball"), player))
+ set_rule(world.multiworld.get_location("Castles - Masterpiece Set: Flowers", player), lambda state: (state.has("Egg Capacity Upgrade", player, 1) or logic.combat_item(state)) and state.has(("Large Spring Ball"), player))
+ set_rule(world.multiworld.get_location("Castles - Masterpiece Set: Stars", player), lambda state: logic.has_midring(state) or state.has(("Large Spring Ball"), player))
+ set_rule(world.multiworld.get_location("Castles - Masterpiece Set: Level Clear", player), lambda state: (state.has("Egg Capacity Upgrade", player, 1) or logic.combat_item(state)) and state.has(("Large Spring Ball"), player))
+
+
+def set_hard_rules(world: "YoshisIslandWorld"):
+ logic = YoshiLogic(world)
+ player = world.player
+
+ set_rule(world.multiworld.get_location("Burt The Bashful's Fort: Red Coins", player), lambda state: state.has("Spring Ball", player))
+ set_rule(world.multiworld.get_location("Burt The Bashful's Fort: Flowers", player), lambda state: state.has_all({"Spring Ball", "Key"}, player) and (state.has("Egg Capacity Upgrade", player, 3) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Burt The Bashful's Fort: Stars", player), lambda state: state.has("Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("Shy-Guys On Stilts: Red Coins", player), lambda state: state.has_all({"Large Spring Ball", "Flashing Eggs", "Mole Tank Morph", "! Switch"}, player))
+ set_rule(world.multiworld.get_location("Shy-Guys On Stilts: Level Clear", player), lambda state: state.has("Large Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("Touch Fuzzy Get Dizzy: Red Coins", player), lambda state: state.has_all({"Flashing Eggs", "Spring Ball", "Chomp Rock", "Beanstalk"}, player))
+
+ set_rule(world.multiworld.get_location("Salvo The Slime's Castle: Red Coins", player), lambda state: state.has("Platform Ghost", player))
+ set_rule(world.multiworld.get_location("Salvo The Slime's Castle: Flowers", player), lambda state: state.has("Platform Ghost", player))
+
+ set_rule(world.multiworld.get_location("Visit Koopa And Para-Koopa: Red Coins", player), lambda state: state.has("Large Spring Ball", player) and (state.has("Poochy", player) or logic.melon_item(state)))
+ set_rule(world.multiworld.get_location("Visit Koopa And Para-Koopa: Flowers", player), lambda state: state.has_all({"Super Star", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("Visit Koopa And Para-Koopa: Stars", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Visit Koopa And Para-Koopa: Level Clear", player), lambda state: state.has("Large Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("The Baseball Boys: Red Coins", player), lambda state: state.has("Mole Tank Morph", player) and (state.has_any({"Ice Melon", "Large Spring Ball"}, player) or logic.melon_item(state)))
+ set_rule(world.multiworld.get_location("The Baseball Boys: Flowers", player), lambda state: (state.has_any({"Ice Melon", "Large Spring Ball"}, player) or logic.melon_item(state)))
+ set_rule(world.multiworld.get_location("The Baseball Boys: Level Clear", player), lambda state: (state.has_any({"Ice Melon", "Large Spring Ball"}, player) or logic.melon_item(state)))
+
+ set_rule(world.multiworld.get_location("What's Gusty Taste Like?: Red Coins", player), lambda state: state.has("! Switch", player))
+
+ set_rule(world.multiworld.get_location("Bigger Boo's Fort: Red Coins", player), lambda state: state.has_all({"! Switch", "Key"}, player))
+ set_rule(world.multiworld.get_location("Bigger Boo's Fort: Flowers", player), lambda state: state.has_all({"! Switch", "Key"}, player))
+ set_rule(world.multiworld.get_location("Bigger Boo's Fort: Stars", player), lambda state: state.has("! Switch", player))
+
+ set_rule(world.multiworld.get_location("Watch Out For Lakitu: Flowers", player), lambda state: state.has_all({"Key", "Train Morph"}, player))
+
+ set_rule(world.multiworld.get_location("The Cave Of The Mystery Maze: Red Coins", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("The Cave Of The Mystery Maze: Flowers", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("The Cave Of The Mystery Maze: Level Clear", player), lambda state: state.has("Large Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("Lakitu's Wall: Flowers", player), lambda state: state.has("! Switch", player))
+ set_rule(world.multiworld.get_location("Lakitu's Wall: Stars", player), lambda state: state.has("Giant Eggs", player) or logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("The Potted Ghost's Castle: Red Coins", player), lambda state: state.has_all({"Arrow Wheel", "Key"}, player))
+ set_rule(world.multiworld.get_location("The Potted Ghost's Castle: Flowers", player), lambda state: state.has_all({"Arrow Wheel", "Key", "Train Morph"}, player))
+ set_rule(world.multiworld.get_location("The Potted Ghost's Castle: Stars", player), lambda state: state.has("Arrow Wheel", player))
+
+ set_rule(world.multiworld.get_location("Welcome To Monkey World!: Stars", player), lambda state: logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("Jungle Rhythm...: Red Coins", player), lambda state: state.has("Dashed Stairs", player))
+ set_rule(world.multiworld.get_location("Jungle Rhythm...: Flowers", player), lambda state: state.has("Dashed Stairs", player))
+ set_rule(world.multiworld.get_location("Jungle Rhythm...: Stars", player), lambda state: logic.has_midring(state) and state.has("Tulip", player))
+ set_rule(world.multiworld.get_location("Jungle Rhythm...: Level Clear", player), lambda state: state.has("Dashed Stairs", player))
+
+ set_rule(world.multiworld.get_location("Nep-Enuts' Domain: Red Coins", player), lambda state: state.has_all({"Submarine Morph", "Helicopter Morph"}, player))
+ set_rule(world.multiworld.get_location("Nep-Enuts' Domain: Flowers", player), lambda state: state.has_all({"Submarine Morph", "Helicopter Morph"}, player))
+ set_rule(world.multiworld.get_location("Nep-Enuts' Domain: Level Clear", player), lambda state: state.has_all({"Submarine Morph", "Helicopter Morph"}, player))
+
+ set_rule(world.multiworld.get_location("Prince Froggy's Fort: Red Coins", player), lambda state: state.has("Submarine Morph", player))
+ set_rule(world.multiworld.get_location("Prince Froggy's Fort: Flowers", player), lambda state: (state.has("Egg Capacity Upgrade", player, 5) or logic.combat_item(state)))
+
+ set_rule(world.multiworld.get_location("The Cave Of Harry Hedgehog: Red Coins", player), lambda state: state.has("Mole Tank Morph", player))
+ set_rule(world.multiworld.get_location("The Cave Of Harry Hedgehog: Flowers", player), lambda state: state.has_all({"Mole Tank Morph", "! Switch"}, player))
+ set_rule(world.multiworld.get_location("The Cave Of Harry Hedgehog: Stars", player), lambda state: logic.has_midring(state) or state.has("Tulip", player))
+ set_rule(world.multiworld.get_location("The Cave Of Harry Hedgehog: Level Clear", player), lambda state: state.has_all({"Large Spring Ball", "Key"}, player))
+
+ set_rule(world.multiworld.get_location("Monkeys' Favorite Lake: Red Coins", player), lambda state: state.has_all({"! Switch", "Submarine Morph"}, player))
+
+ set_rule(world.multiworld.get_location("GO! GO! MARIO!!: Red Coins", player), lambda state: state.has("Super Star", player))
+ set_rule(world.multiworld.get_location("GO! GO! MARIO!!: Flowers", player), lambda state: state.has("Super Star", player))
+ set_rule(world.multiworld.get_location("GO! GO! MARIO!!: Level Clear", player), lambda state: state.has("Super Star", player))
+
+ set_rule(world.multiworld.get_location("The Cave Of The Lakitus: Red Coins", player), lambda state: state.has_all({"! Switch", "Egg Launcher"}, player))
+ set_rule(world.multiworld.get_location("The Cave Of The Lakitus: Flowers", player), lambda state: state.has("Egg Launcher", player))
+
+ set_rule(world.multiworld.get_location("Don't Look Back!: Red Coins", player), lambda state: state.has_all({"Helicopter Morph", "Large Spring Ball"}, player))
+ set_rule(world.multiworld.get_location("Don't Look Back!: Flowers", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Don't Look Back!: Stars", player), lambda state: logic.has_midring(state) or state.has("Tulip", player))
+
+ set_rule(world.multiworld.get_location("Marching Milde's Fort: Red Coins", player), lambda state: state.has_all({"Dashed Stairs", "Vanishing Arrow Wheel", "Arrow Wheel", "Bucket", "Key"}, player))
+ set_rule(world.multiworld.get_location("Marching Milde's Fort: Flowers", player), lambda state: state.has_all({"Dashed Stairs", "Vanishing Arrow Wheel", "Arrow Wheel", "Bucket"}, player))
+ set_rule(world.multiworld.get_location("Marching Milde's Fort: Stars", player), lambda state: state.has("Dashed Stairs", player))
+
+ set_rule(world.multiworld.get_location("Chomp Rock Zone: Red Coins", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Chomp Rock Zone: Flowers", player), lambda state: state.has_all({"Chomp Rock", "! Switch"}, player))
+ set_rule(world.multiworld.get_location("Chomp Rock Zone: Stars", player), lambda state: state.has_all({"Chomp Rock", "! Switch"}, player))
+
+ set_rule(world.multiworld.get_location("Lake Shore Paradise: Stars", player), lambda state: (logic.has_midring(state) or (state.has("Tulip", player) and logic.cansee_clouds(state))))
+
+ set_rule(world.multiworld.get_location("Ride Like The Wind: Red Coins", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Ride Like The Wind: Flowers", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Ride Like The Wind: Stars", player), lambda state: (logic.has_midring(state) or state.has("Helicopter Morph", player)) and state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Ride Like The Wind: Level Clear", player), lambda state: state.has("Large Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("Hookbill The Koopa's Castle: Red Coins", player), lambda state: state.has_all({"Key", "Dashed Stairs"}, player))
+ set_rule(world.multiworld.get_location("Hookbill The Koopa's Castle: Flowers", player), lambda state: state.has_all({"Dashed Stairs", "Key"}, player))
+
+ set_rule(world.multiworld.get_location("Danger - Icy Conditions Ahead: Red Coins", player), lambda state: (state.has("Fire Melon", player) or logic.melon_item(state)) and (state.has_all({"Spring Ball", "Skis"}, player)) and (state.has("Super Star", player) or logic.melon_item(state)))
+ set_rule(world.multiworld.get_location("Danger - Icy Conditions Ahead: Flowers", player), lambda state: (state.has("Fire Melon", player) or logic.melon_item(state)) and state.has_all({"Spring Ball", "Skis"}, player))
+ set_rule(world.multiworld.get_location("Danger - Icy Conditions Ahead: Stars", player), lambda state: (logic.has_midring(state) and (state.has("Fire Melon", player) or logic.melon_item(state))) or (logic.has_midring(state) and (state.has_all({"Tulip", "Dashed Platform"}, player))))
+ set_rule(world.multiworld.get_location("Danger - Icy Conditions Ahead: Level Clear", player), lambda state: state.has_all({"Spring Ball", "Skis"}, player))
+
+ set_rule(world.multiworld.get_location("Sluggy The Unshaven's Fort: Red Coins", player), lambda state: state.has_all({"Dashed Stairs", "Dashed Platform", "Platform Ghost"}, player))
+ set_rule(world.multiworld.get_location("Sluggy The Unshaven's Fort: Flowers", player), lambda state: state.has_all({"Dashed Stairs", "Platform Ghost"}, player))
+
+ set_rule(world.multiworld.get_location("Shifting Platforms Ahead: Stars", player), lambda state: logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("Raphael The Raven's Castle: Red Coins", player), lambda state: state.has_all({"Arrow Wheel", "Train Morph"}, player))
+ set_rule(world.multiworld.get_location("Raphael The Raven's Castle: Flowers", player), lambda state: state.has_all({"Arrow Wheel", "Train Morph"}, player))
+
+ set_rule(world.multiworld.get_location("Scary Skeleton Goonies!: Red Coins", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Scary Skeleton Goonies!: Flowers", player), lambda state: state.has("Large Spring Ball", player))
+ set_rule(world.multiworld.get_location("Scary Skeleton Goonies!: Stars", player), lambda state: logic.has_midring(state))
+ set_rule(world.multiworld.get_location("Scary Skeleton Goonies!: Level Clear", player), lambda state: state.has("Large Spring Ball", player))
+
+ set_rule(world.multiworld.get_location("The Cave Of The Bandits: Red Coins", player), lambda state: state.has("Super Star", player))
+ set_rule(world.multiworld.get_location("The Cave Of The Bandits: Flowers", player), lambda state: state.has("Super Star", player))
+ set_rule(world.multiworld.get_location("The Cave Of The Bandits: Level Clear", player), lambda state: state.has("Super Star", player))
+
+ set_rule(world.multiworld.get_location("Tap-Tap The Red Nose's Fort: Red Coins", player), lambda state: state.has_all({"Egg Plant", "Key"}, player) and (state.has("Egg Capacity Upgrade", player, 1) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Tap-Tap The Red Nose's Fort: Flowers", player), lambda state: state.has_all({"Egg Plant", "Key"}, player) and (state.has("Egg Capacity Upgrade", player, 1) or logic.combat_item(state)))
+ set_rule(world.multiworld.get_location("Tap-Tap The Red Nose's Fort: Stars", player), lambda state: state.has("Egg Plant", player) and state.has("Key", player))
+
+ set_rule(world.multiworld.get_location("The Very Loooooong Cave: Stars", player), lambda state: logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("The Deep, Underground Maze: Red Coins", player), lambda state: state.has_all({"Key", "Large Spring Ball"}, player) and (logic.combat_item(state) or state.has("Chomp Rock", player)))
+ set_rule(world.multiworld.get_location("The Deep, Underground Maze: Flowers", player), lambda state: state.has_all({"Key", "Large Spring Ball"}, player) and (logic.combat_item(state) or state.has("Chomp Rock", player)))
+ set_rule(world.multiworld.get_location("The Deep, Underground Maze: Stars", player), lambda state: state.has_all({"Chomp Rock", "Key"}, player))
+ set_rule(world.multiworld.get_location("The Deep, Underground Maze: Level Clear", player), lambda state: state.has_all({"Key", "Large Spring Ball", "Dashed Platform"}, player) and (logic.combat_item(state) or state.has("Chomp Rock", player)))
+
+ set_rule(world.multiworld.get_location("KEEP MOVING!!!!: Stars", player), lambda state: logic.has_midring(state))
+
+ set_rule(world.multiworld.get_location("King Bowser's Castle: Red Coins", player), lambda state: state.has("Helicopter Morph", player) and logic._68CollectibleRoute(state))
+ set_rule(world.multiworld.get_location("King Bowser's Castle: Flowers", player), lambda state: state.has("Helicopter Morph", player) and logic._68CollectibleRoute(state))
+ set_rule(world.multiworld.get_location("King Bowser's Castle: Stars", player), lambda state: state.has("Helicopter Morph", player) and logic._68Route(state))
+
+ set_hard_extra_rules(world)
+
+
+def set_hard_extra_rules(world: "YoshisIslandWorld") -> None:
+ player = world.player
+ logic = YoshiLogic(world)
+ if not world.options.extras_enabled:
+ return
+ set_rule(world.multiworld.get_location("Poochy Ain't Stupid: Red Coins", player), lambda state: state.has("Poochy", player))
+ set_rule(world.multiworld.get_location("Poochy Ain't Stupid: Flowers", player), lambda state: state.has("Poochy", player))
+ set_rule(world.multiworld.get_location("Poochy Ain't Stupid: Stars", player), lambda state: state.has("Poochy", player))
+ set_rule(world.multiworld.get_location("Poochy Ain't Stupid: Level Clear", player), lambda state: state.has("Poochy", player))
+
+ set_rule(world.multiworld.get_location("Hit That Switch!!: Red Coins", player), lambda state: state.has_all({"Large Spring Ball", "! Switch"}, player))
+ set_rule(world.multiworld.get_location("Hit That Switch!!: Flowers", player), lambda state: state.has_all({"Large Spring Ball", "! Switch"}, player))
+ set_rule(world.multiworld.get_location("Hit That Switch!!: Level Clear", player), lambda state: state.has_all({"Large Spring Ball", "! Switch"}, player))
+
+ set_rule(world.multiworld.get_location("The Impossible? Maze: Red Coins", player), lambda state: state.has_all({"Spring Ball", "Large Spring Ball", "Mole Tank Morph", "Helicopter Morph", "Flashing Eggs"}, player))
+ set_rule(world.multiworld.get_location("The Impossible? Maze: Flowers", player), lambda state: state.has_all({"Spring Ball", "Large Spring Ball", "Mole Tank Morph", "Helicopter Morph"}, player))
+ set_rule(world.multiworld.get_location("The Impossible? Maze: Stars", player), lambda state: state.has_all({"Spring Ball", "Large Spring Ball", "Mole Tank Morph"}, player))
+ set_rule(world.multiworld.get_location("The Impossible? Maze: Level Clear", player), lambda state: state.has_all({"Spring Ball", "Large Spring Ball", "Mole Tank Morph", "Helicopter Morph"}, player))
+
+ set_rule(world.multiworld.get_location("Kamek's Revenge: Red Coins", player), lambda state: state.has_all({"Key", "Skis", "Helicopter Morph"}, player))
+ set_rule(world.multiworld.get_location("Kamek's Revenge: Flowers", player), lambda state: state.has_all({"Key", "Skis", "Helicopter Morph", "! Switch"}, player))
+ set_rule(world.multiworld.get_location("Kamek's Revenge: Stars", player), lambda state: state.has("! Switch", player) or logic.has_midring(state))
+ set_rule(world.multiworld.get_location("Kamek's Revenge: Level Clear", player), lambda state: state.has_all({"Key", "Skis", "Helicopter Morph"}, player))
+
+ set_rule(world.multiworld.get_location("Castles - Masterpiece Set: Red Coins", player), lambda state: state.has(("Large Spring Ball"), player))
+ set_rule(world.multiworld.get_location("Castles - Masterpiece Set: Flowers", player), lambda state: state.has(("Large Spring Ball"), player))
+ set_rule(world.multiworld.get_location("Castles - Masterpiece Set: Stars", player), lambda state: True)
+ set_rule(world.multiworld.get_location("Castles - Masterpiece Set: Level Clear", player), lambda state: state.has(("Large Spring Ball"), player))
diff --git a/worlds/yoshisisland/__init__.py b/worlds/yoshisisland/__init__.py
new file mode 100644
index 000000000000..b5d7e137b5f3
--- /dev/null
+++ b/worlds/yoshisisland/__init__.py
@@ -0,0 +1,388 @@
+import base64
+import os
+import typing
+import threading
+
+from typing import List, Set, TextIO, Dict
+from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification
+from worlds.AutoWorld import World, WebWorld
+import settings
+from .Items import get_item_names_per_category, item_table, filler_items, trap_items
+from .Locations import get_locations
+from .Regions import init_areas
+from .Options import YoshisIslandOptions, PlayerGoal, ObjectVis, StageLogic, MinigameChecks
+from .setup_game import setup_gamevars
+from .Client import YoshisIslandSNIClient
+from .Rules import set_easy_rules, set_normal_rules, set_hard_rules
+from .Rom import LocalRom, patch_rom, get_base_rom_path, YoshisIslandDeltaPatch, USHASH
+
+
+class YoshisIslandSettings(settings.Group):
+ class RomFile(settings.SNESRomPath):
+ """File name of the Yoshi's Island 1.0 US rom"""
+ description = "Yoshi's Island ROM File"
+ copy_to = "Super Mario World 2 - Yoshi's Island (U).sfc"
+ md5s = [USHASH]
+
+ rom_file: RomFile = RomFile(RomFile.copy_to)
+
+
+class YoshisIslandWeb(WebWorld):
+ theme = "ocean"
+
+ setup_en = Tutorial(
+ "Multiworld Setup Guide",
+ "A guide to setting up the Yoshi's Island randomizer"
+ "and connecting to an Archipelago server.",
+ "English",
+ "setup_en.md",
+ "setup/en",
+ ["Pink Switch"]
+ )
+
+ tutorials = [setup_en]
+
+
+class YoshisIslandWorld(World):
+ """
+ Yoshi's Island is a 2D platforming game.
+ During a delivery, Bowser's evil ward, Kamek, attacked the stork, kidnapping Luigi and dropping Mario onto Yoshi's Island.
+ As Yoshi, you must run, jump, and throw eggs to escort the baby Mario across the island to defeat Bowser and reunite the two brothers with their parents.
+ """
+ game = "Yoshi's Island"
+ option_definitions = YoshisIslandOptions
+ required_client_version = (0, 4, 4)
+
+ item_name_to_id = {item: item_table[item].code for item in item_table}
+ location_name_to_id = {location.name: location.code for location in get_locations(None)}
+ item_name_groups = get_item_names_per_category()
+
+ web = YoshisIslandWeb()
+ settings: typing.ClassVar[YoshisIslandSettings]
+ # topology_present = True
+
+ options_dataclass = YoshisIslandOptions
+ options: YoshisIslandOptions
+
+ locked_locations: List[str]
+ set_req_bosses: str
+ lives_high: int
+ lives_low: int
+ castle_bosses: int
+ bowser_bosses: int
+ baby_mario_sfx: int
+ leader_color: int
+ boss_order: list
+ boss_burt: int
+ luigi_count: int
+
+ rom_name: bytearray
+
+ def __init__(self, multiworld: MultiWorld, player: int):
+ self.rom_name_available_event = threading.Event()
+ super().__init__(multiworld, player)
+ self.locked_locations = []
+
+ @classmethod
+ def stage_assert_generate(cls, multiworld: MultiWorld) -> None:
+ rom_file = get_base_rom_path()
+ if not os.path.exists(rom_file):
+ raise FileNotFoundError(rom_file)
+
+ def fill_slot_data(self) -> Dict[str, List[int]]:
+ return {
+ "world_1": self.world_1_stages,
+ "world_2": self.world_2_stages,
+ "world_3": self.world_3_stages,
+ "world_4": self.world_4_stages,
+ "world_5": self.world_5_stages,
+ "world_6": self.world_6_stages
+ }
+
+ def write_spoiler_header(self, spoiler_handle: TextIO) -> None:
+ spoiler_handle.write(f"Burt The Bashful's Boss Door: {self.boss_order[0]}\n")
+ spoiler_handle.write(f"Salvo The Slime's Boss Door: {self.boss_order[1]}\n")
+ spoiler_handle.write(f"Bigger Boo's Boss Door: {self.boss_order[2]}\n")
+ spoiler_handle.write(f"Roger The Ghost's Boss Door: {self.boss_order[3]}\n")
+ spoiler_handle.write(f"Prince Froggy's Boss Door: {self.boss_order[4]}\n")
+ spoiler_handle.write(f"Naval Piranha's Boss Door: {self.boss_order[5]}\n")
+ spoiler_handle.write(f"Marching Milde's Boss Door: {self.boss_order[6]}\n")
+ spoiler_handle.write(f"Hookbill The Koopa's Boss Door: {self.boss_order[7]}\n")
+ spoiler_handle.write(f"Sluggy The Unshaven's Boss Door: {self.boss_order[8]}\n")
+ spoiler_handle.write(f"Raphael The Raven's Boss Door: {self.boss_order[9]}\n")
+ spoiler_handle.write(f"Tap-Tap The Red Nose's Boss Door: {self.boss_order[10]}\n")
+ spoiler_handle.write(f"\nLevels:\n1-1: {self.level_name_list[0]}\n")
+ spoiler_handle.write(f"1-2: {self.level_name_list[1]}\n")
+ spoiler_handle.write(f"1-3: {self.level_name_list[2]}\n")
+ spoiler_handle.write(f"1-4: {self.level_name_list[3]}\n")
+ spoiler_handle.write(f"1-5: {self.level_name_list[4]}\n")
+ spoiler_handle.write(f"1-6: {self.level_name_list[5]}\n")
+ spoiler_handle.write(f"1-7: {self.level_name_list[6]}\n")
+ spoiler_handle.write(f"1-8: {self.level_name_list[7]}\n")
+
+ spoiler_handle.write(f"\n2-1: {self.level_name_list[8]}\n")
+ spoiler_handle.write(f"2-2: {self.level_name_list[9]}\n")
+ spoiler_handle.write(f"2-3: {self.level_name_list[10]}\n")
+ spoiler_handle.write(f"2-4: {self.level_name_list[11]}\n")
+ spoiler_handle.write(f"2-5: {self.level_name_list[12]}\n")
+ spoiler_handle.write(f"2-6: {self.level_name_list[13]}\n")
+ spoiler_handle.write(f"2-7: {self.level_name_list[14]}\n")
+ spoiler_handle.write(f"2-8: {self.level_name_list[15]}\n")
+
+ spoiler_handle.write(f"\n3-1: {self.level_name_list[16]}\n")
+ spoiler_handle.write(f"3-2: {self.level_name_list[17]}\n")
+ spoiler_handle.write(f"3-3: {self.level_name_list[18]}\n")
+ spoiler_handle.write(f"3-4: {self.level_name_list[19]}\n")
+ spoiler_handle.write(f"3-5: {self.level_name_list[20]}\n")
+ spoiler_handle.write(f"3-6: {self.level_name_list[21]}\n")
+ spoiler_handle.write(f"3-7: {self.level_name_list[22]}\n")
+ spoiler_handle.write(f"3-8: {self.level_name_list[23]}\n")
+
+ spoiler_handle.write(f"\n4-1: {self.level_name_list[24]}\n")
+ spoiler_handle.write(f"4-2: {self.level_name_list[25]}\n")
+ spoiler_handle.write(f"4-3: {self.level_name_list[26]}\n")
+ spoiler_handle.write(f"4-4: {self.level_name_list[27]}\n")
+ spoiler_handle.write(f"4-5: {self.level_name_list[28]}\n")
+ spoiler_handle.write(f"4-6: {self.level_name_list[29]}\n")
+ spoiler_handle.write(f"4-7: {self.level_name_list[30]}\n")
+ spoiler_handle.write(f"4-8: {self.level_name_list[31]}\n")
+
+ spoiler_handle.write(f"\n5-1: {self.level_name_list[32]}\n")
+ spoiler_handle.write(f"5-2: {self.level_name_list[33]}\n")
+ spoiler_handle.write(f"5-3: {self.level_name_list[34]}\n")
+ spoiler_handle.write(f"5-4: {self.level_name_list[35]}\n")
+ spoiler_handle.write(f"5-5: {self.level_name_list[36]}\n")
+ spoiler_handle.write(f"5-6: {self.level_name_list[37]}\n")
+ spoiler_handle.write(f"5-7: {self.level_name_list[38]}\n")
+ spoiler_handle.write(f"5-8: {self.level_name_list[39]}\n")
+
+ spoiler_handle.write(f"\n6-1: {self.level_name_list[40]}\n")
+ spoiler_handle.write(f"6-2: {self.level_name_list[41]}\n")
+ spoiler_handle.write(f"6-3: {self.level_name_list[42]}\n")
+ spoiler_handle.write(f"6-4: {self.level_name_list[43]}\n")
+ spoiler_handle.write(f"6-5: {self.level_name_list[44]}\n")
+ spoiler_handle.write(f"6-6: {self.level_name_list[45]}\n")
+ spoiler_handle.write(f"6-7: {self.level_name_list[46]}\n")
+ spoiler_handle.write("6-8: King Bowser's Castle")
+
+ def create_item(self, name: str) -> Item:
+ data = item_table[name]
+ return Item(name, data.classification, data.code, self.player)
+
+ def create_regions(self) -> None:
+ init_areas(self, get_locations(self))
+
+ def get_filler_item_name(self) -> str:
+ trap_chance: int = self.options.trap_percent.value
+
+ if self.random.random() < (trap_chance / 100) and self.options.traps_enabled:
+ return self.random.choice(trap_items)
+ else:
+ return self.random.choice(filler_items)
+
+ def set_rules(self) -> None:
+ rules_per_difficulty = {
+ 0: set_easy_rules,
+ 1: set_normal_rules,
+ 2: set_hard_rules
+ }
+
+ rules_per_difficulty[self.options.stage_logic.value](self)
+ self.multiworld.completion_condition[self.player] = lambda state: state.has("Saved Baby Luigi", self.player)
+ self.get_location("Burt The Bashful's Boss Room").place_locked_item(self.create_item("Boss Clear"))
+ self.get_location("Salvo The Slime's Boss Room").place_locked_item(self.create_item("Boss Clear"))
+ self.get_location("Bigger Boo's Boss Room", ).place_locked_item(self.create_item("Boss Clear"))
+ self.get_location("Roger The Ghost's Boss Room").place_locked_item(self.create_item("Boss Clear"))
+ self.get_location("Prince Froggy's Boss Room").place_locked_item(self.create_item("Boss Clear"))
+ self.get_location("Naval Piranha's Boss Room").place_locked_item(self.create_item("Boss Clear"))
+ self.get_location("Marching Milde's Boss Room").place_locked_item(self.create_item("Boss Clear"))
+ self.get_location("Hookbill The Koopa's Boss Room").place_locked_item(self.create_item("Boss Clear"))
+ self.get_location("Sluggy The Unshaven's Boss Room").place_locked_item(self.create_item("Boss Clear"))
+ self.get_location("Raphael The Raven's Boss Room").place_locked_item(self.create_item("Boss Clear"))
+ self.get_location("Tap-Tap The Red Nose's Boss Room").place_locked_item(self.create_item("Boss Clear"))
+
+ if self.options.goal == PlayerGoal.option_luigi_hunt:
+ self.get_location("Reconstituted Luigi").place_locked_item(self.create_item("Saved Baby Luigi"))
+ else:
+ self.get_location("King Bowser's Castle: Level Clear").place_locked_item(
+ self.create_item("Saved Baby Luigi")
+ )
+
+ self.get_location("Touch Fuzzy Get Dizzy: Gather Coins").place_locked_item(
+ self.create_item("Bandit Consumables")
+ )
+ self.get_location("The Cave Of the Mystery Maze: Seed Spitting Contest").place_locked_item(
+ self.create_item("Bandit Watermelons")
+ )
+ self.get_location("Lakitu's Wall: Gather Coins").place_locked_item(self.create_item("Bandit Consumables"))
+ self.get_location("Ride Like The Wind: Gather Coins").place_locked_item(self.create_item("Bandit Consumables"))
+
+ def generate_early(self) -> None:
+ setup_gamevars(self)
+
+ def get_excluded_items(self) -> Set[str]:
+ excluded_items: Set[str] = set()
+
+ starting_gate = ["World 1 Gate", "World 2 Gate", "World 3 Gate",
+ "World 4 Gate", "World 5 Gate", "World 6 Gate"]
+
+ excluded_items.add(starting_gate[self.options.starting_world])
+
+ if not self.options.shuffle_midrings:
+ excluded_items.add("Middle Ring")
+
+ if not self.options.add_secretlens:
+ excluded_items.add("Secret Lens")
+
+ if not self.options.extras_enabled:
+ excluded_items.add("Extra Panels")
+ excluded_items.add("Extra 1")
+ excluded_items.add("Extra 2")
+ excluded_items.add("Extra 3")
+ excluded_items.add("Extra 4")
+ excluded_items.add("Extra 5")
+ excluded_items.add("Extra 6")
+
+ if self.options.split_extras:
+ excluded_items.add("Extra Panels")
+ else:
+ excluded_items.add("Extra 1")
+ excluded_items.add("Extra 2")
+ excluded_items.add("Extra 3")
+ excluded_items.add("Extra 4")
+ excluded_items.add("Extra 5")
+ excluded_items.add("Extra 6")
+
+ if self.options.split_bonus:
+ excluded_items.add("Bonus Panels")
+ else:
+ excluded_items.add("Bonus 1")
+ excluded_items.add("Bonus 2")
+ excluded_items.add("Bonus 3")
+ excluded_items.add("Bonus 4")
+ excluded_items.add("Bonus 5")
+ excluded_items.add("Bonus 6")
+
+ return excluded_items
+
+ def create_item_with_correct_settings(self, name: str) -> Item:
+ data = item_table[name]
+ item = Item(name, data.classification, data.code, self.player)
+
+ if not item.advancement:
+ return item
+
+ if name == "Car Morph" and self.options.stage_logic != StageLogic.option_strict:
+ item.classification = ItemClassification.useful
+
+ secret_lens_visibility_check = (
+ self.options.hidden_object_visibility >= ObjectVis.option_clouds_only
+ or self.options.stage_logic != StageLogic.option_strict
+ )
+ if name == "Secret Lens" and secret_lens_visibility_check:
+ item.classification = ItemClassification.useful
+
+ is_bonus_location = name in {"Bonus 1", "Bonus 2", "Bonus 3", "Bonus 4", "Bonus 5", "Bonus 6", "Bonus Panels"}
+ bonus_games_disabled = (
+ self.options.minigame_checks not in {MinigameChecks.option_bonus_games, MinigameChecks.option_both}
+ )
+ if is_bonus_location and bonus_games_disabled:
+ item.classification = ItemClassification.useful
+
+ if name in {"Bonus 1", "Bonus 3", "Bonus 4", "Bonus Panels"} and self.options.item_logic:
+ item.classification = ItemClassification.progression
+
+ if name == "Piece of Luigi" and self.options.goal == PlayerGoal.option_luigi_hunt:
+ if self.luigi_count >= self.options.luigi_pieces_required:
+ item.classification = ItemClassification.useful
+ else:
+ item.classification = ItemClassification.progression_skip_balancing
+ self.luigi_count += 1
+
+ return item
+
+ def generate_filler(self, pool: List[Item]) -> None:
+ if self.options.goal == PlayerGoal.option_luigi_hunt:
+ for _ in range(self.options.luigi_pieces_in_pool.value):
+ item = self.create_item_with_correct_settings("Piece of Luigi")
+ pool.append(item)
+
+ for _ in range(len(self.multiworld.get_unfilled_locations(self.player)) - len(pool) - 16):
+ item = self.create_item_with_correct_settings(self.get_filler_item_name())
+ pool.append(item)
+
+ def get_item_pool(self, excluded_items: Set[str]) -> List[Item]:
+ pool: List[Item] = []
+
+ for name, data in item_table.items():
+ if name not in excluded_items:
+ for _ in range(data.amount):
+ item = self.create_item_with_correct_settings(name)
+ pool.append(item)
+
+ return pool
+
+ def create_items(self) -> None:
+ self.luigi_count = 0
+
+ if self.options.minigame_checks in {MinigameChecks.option_bonus_games, MinigameChecks.option_both}:
+ self.multiworld.get_location("Flip Cards", self.player).place_locked_item(
+ self.create_item("Bonus Consumables"))
+ self.multiworld.get_location("Drawing Lots", self.player).place_locked_item(
+ self.create_item("Bonus Consumables"))
+ self.multiworld.get_location("Match Cards", self.player).place_locked_item(
+ self.create_item("Bonus Consumables"))
+
+ pool = self.get_item_pool(self.get_excluded_items())
+
+ self.generate_filler(pool)
+
+ self.multiworld.itempool += pool
+
+ def generate_output(self, output_directory: str) -> None:
+ rompath = "" # if variable is not declared finally clause may fail
+ try:
+ world = self.multiworld
+ player = self.player
+ rom = LocalRom(get_base_rom_path())
+ patch_rom(self, rom, self.player)
+
+ rompath = os.path.join(output_directory, f"{self.multiworld.get_out_file_name_base(self.player)}.sfc")
+ rom.write_to_file(rompath)
+ self.rom_name = rom.name
+
+ patch = YoshisIslandDeltaPatch(os.path.splitext(rompath)[0] + YoshisIslandDeltaPatch.patch_file_ending,
+ player=player, player_name=world.player_name[player], patched_path=rompath)
+ patch.write()
+ finally:
+ self.rom_name_available_event.set()
+ if os.path.exists(rompath):
+ os.unlink(rompath)
+
+ def modify_multidata(self, multidata: dict) -> None:
+ # wait for self.rom_name to be available.
+ self.rom_name_available_event.wait()
+ rom_name = getattr(self, "rom_name", None)
+ if rom_name:
+ new_name = base64.b64encode(bytes(self.rom_name)).decode()
+ multidata["connect_names"][new_name] = multidata["connect_names"][self.multiworld.player_name[self.player]]
+
+ def extend_hint_information(self, hint_data: typing.Dict[int, typing.Dict[int, str]]) -> None:
+ world_names = [f"World {i}" for i in range(1, 7)]
+ world_stages = [
+ self.world_1_stages, self.world_2_stages, self.world_3_stages,
+ self.world_4_stages, self.world_5_stages, self.world_6_stages
+ ]
+
+ stage_pos_data = {}
+ for loc in self.multiworld.get_locations(self.player):
+ if loc.address is None:
+ continue
+
+ level_id = getattr(loc, "level_id")
+ for level, stages in zip(world_names, world_stages):
+ if level_id in stages:
+ stage_pos_data[loc.address] = level
+ break
+
+ hint_data[self.player] = stage_pos_data
diff --git a/worlds/yoshisisland/docs/en_Yoshi's Island.md b/worlds/yoshisisland/docs/en_Yoshi's Island.md
new file mode 100644
index 000000000000..8cd825cc7f34
--- /dev/null
+++ b/worlds/yoshisisland/docs/en_Yoshi's Island.md
@@ -0,0 +1,71 @@
+# Yoshi's Island
+
+## Where is the settings page?
+
+The [player options page for this game](../player-options) contains all the options you need to configure and export a config file.
+
+## What does randomization do to this game?
+
+Certain interactable objects within levels will be unable to be used until the corresponding item is found. If the item is not in the player's posession, the object will flash and will not function. Objects include:
+- Spring Ball
+- Large Spring Ball
+- ! Switch
+- Dashed Platform
+- Dashed Stairs
+- Beanstalk
+- Arrow Wheel
+- Vanishing Arrow Wheel
+- Ice, fire, and normal watermelons
+- Super Star
+- Flashing Eggs
+- Giant Eggs
+- Egg Launcher
+- Egg Refill Plant
+- Chomp Rock
+- Poochy
+- Transformation Morphs
+- Skis
+- Platform Ghost
+- Middle Rings
+- Buckets
+- Tulips
+
+Yoshi will start out being able to carry only one egg, and 5 capacity upgrades can be found to bring the total up to 6.
+The player will start with all levels unlocked in their starting world, and can collect 'World Gates' to unlock levels from other worlds.
+Extra and Bonus stages will also start out locked, and require respective items to access them. 6-8 is locked, and will be unlocked
+upon reaching the number of boss clears defined by the player.
+Other checks will grant the player extra lives, consumables for use in the inventory, or traps.
+
+Additionally, the player is able to randomize the bosses found at the end of boss stages, the order of stages,
+the world they start in, the starting amount of lives, route through 6-8, and the color of Yoshi for each stage.
+
+## What is the goal of Yoshi's Island when randomized?
+
+The player can choose one of two goals:
+- Bowser: Defeat a pre-defined number of bosses, and defeat Bowser at the end of 6-8.
+- Luigi Hunt: Collect a pre-defined number of 'Pieces of Luigi' within levels.
+
+## What items and locations get shuffled?
+
+Locations consist of 'level objectives', that being:
+- Beating the stage
+- Collecting 20 red coins.
+- Collecting 5 flowers.
+- Collecting 30 stars.
+
+Checks will be sent immediately upon achieving that objective, regardless of if the stage is cleared or not.
+Additional checks can be placed on Bandit mini-battles, or overworld minigames.
+
+
+## Which items can be in another player's world?
+
+Any shuffled item can be in other players' worlds.
+
+## What does another world's item look like in Yoshi's Island
+
+Items do not have an appearance in Yoshi's Island
+
+## When the player receives an item, what happens?
+
+When the player recieves an item, a fanfare or sound will be heard to reflect the item received. Most items, aside from Egg Capacity and level unlocks, can be checked on the menu by pressing SELECT.
+If an item is in the queue and has not been received, checks will not be processed.
diff --git a/worlds/yoshisisland/docs/setup_en.md b/worlds/yoshisisland/docs/setup_en.md
new file mode 100644
index 000000000000..30aadbfa604d
--- /dev/null
+++ b/worlds/yoshisisland/docs/setup_en.md
@@ -0,0 +1,123 @@
+# Yoshi's Island Archipelago Randomizer Setup Guide
+
+## Required Software
+
+- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases).
+
+
+- Hardware or software capable of loading and playing SNES ROM files
+ - An emulator capable of connecting to SNI such as:
+ - snes9x-rr from: [snes9x rr](https://github.com/gocha/snes9x-rr/releases),
+ - BizHawk from: [TASVideos](https://tasvideos.org/BizHawk)
+ - snes9x-nwa from: [snes9x nwa](https://github.com/Skarsnik/snes9x-emunwa/releases)
+
+ NOTE: RetroArch and FXPakPro are not currently supported.
+- Your legally obtained Yoshi's Island English 1.0 ROM file, probably named `Super Mario World 2 - Yoshi's Island (U).sfc`
+
+
+## Installation Procedures
+
+### Windows Setup
+
+1. Download and install Archipelago from the link above, making sure to install the most recent version.
+2. During generation/patching, you will be asked to locate your base ROM file. This is your Yoshi's Island ROM file.
+3. If you are using an emulator, you should assign your Lua capable emulator as your default program for launching ROM
+ files.
+ 1. Extract your emulator's folder to your Desktop, or somewhere you will remember.
+ 2. Right-click on a ROM file and select **Open with...**
+ 3. Check the box next to **Always use this app to open .sfc files**
+ 4. Scroll to the bottom of the list and click the grey text **Look for another App on this PC**
+ 5. Browse for your emulator's `.exe` file and click **Open**. This file should be located inside the folder you
+ extracted in step one.
+
+## Create a Config (.yaml) File
+
+### What is a config file and why do I need one?
+
+See the guide on setting up a basic YAML at the Archipelago setup
+guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
+
+### Where do I get a config file?
+
+The Player Options page on the website allows you to configure your personal settings and export a config file from
+them.
+
+### Verifying your config file
+
+If you would like to validate your config file to make sure it works, you may do so on the YAML Validator page. YAML
+validator page: [YAML Validation page](/mysterycheck)
+
+## Joining a MultiWorld Game
+
+### Obtain your patch file and create your ROM
+
+When you join a multiworld game, you will be asked to provide your config file to whomever is hosting. Once that is done,
+the host will provide you with either a link to download your patch file, or with a zip file containing everyone's patch
+files. Your patch file should have a `.apyi` extension.
+
+Put your patch file on your desktop or somewhere convenient, and double click it. This should automatically launch the
+client, and will also create your ROM in the same place as your patch file.
+
+### Connect to the client
+
+#### With an emulator
+
+When the client launched automatically, SNI should have also automatically launched in the background. If this is its
+first time launching, you may be prompted to allow it to communicate through the Windows Firewall.
+
+##### snes9x-rr
+
+1. Load your ROM file if it hasn't already been loaded.
+2. Click on the File menu and hover on **Lua Scripting**
+3. Click on **New Lua Script Window...**
+4. In the new window, click **Browse...**
+5. Select the connector lua file included with your client
+ - Look in the Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the
+ emulator is 64-bit or 32-bit.
+6. If you see an error while loading the script that states `socket.dll missing` or similar, navigate to the folder of
+the lua you are using in your file explorer and copy the `socket.dll` to the base folder of your snes9x install.
+
+##### BizHawk
+
+1. Ensure you have the BSNES core loaded. This is done with the main menubar, under:
+ - (≤ 2.8) `Config` 〉 `Cores` 〉 `SNES` 〉 `BSNES`
+ - (≥ 2.9) `Config` 〉 `Preferred Cores` 〉 `SNES` 〉 `BSNESv115+`
+2. Load your ROM file if it hasn't already been loaded.
+ If you changed your core preference after loading the ROM, don't forget to reload it (default hotkey: Ctrl+R).
+3. Drag+drop the `Connector.lua` file included with your client onto the main EmuHawk window.
+ - Look in the Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the
+ emulator is 64-bit or 32-bit. Please note the most recent versions of BizHawk are 64-bit only.
+ - You could instead open the Lua Console manually, click `Script` 〉 `Open Script`, and navigate to `Connector.lua`
+ with the file picker.
+
+
+
+### Connect to the Archipelago Server
+
+The patch file which launched your client should have automatically connected you to the AP Server. There are a few
+reasons this may not happen however, including if the game is hosted on the website but was generated elsewhere. If the
+client window shows "Server Status: Not Connected", simply ask the host for the address of the server, and copy/paste it
+into the "Server" input field then press enter.
+
+The client will attempt to reconnect to the new server address, and should momentarily show "Server Status: Connected".
+
+### Play the game
+
+When the client shows both SNES Device and Server as connected, you're ready to begin playing. Congratulations on
+successfully joining a multiworld game!
+
+## Hosting a MultiWorld game
+
+The recommended way to host a game is to use our hosting service. The process is relatively simple:
+
+1. Collect config files from your players.
+2. Create a zip file containing your players' config files.
+3. Upload that zip file to the Generate page above.
+ - Generate page: [WebHost Seed Generation Page](/generate)
+4. Wait a moment while the seed is generated.
+5. When the seed is generated, you will be redirected to a "Seed Info" page.
+6. Click "Create New Room". This will take you to the server page. Provide the link to this page to your players, so
+ they may download their patch files from there.
+7. Note that a link to a MultiWorld Tracker is at the top of the room page. The tracker shows the progress of all
+ players in the game. Any observers may also be given the link to this page.
+8. Once all players have joined, you may begin playing.
diff --git a/worlds/yoshisisland/level_logic.py b/worlds/yoshisisland/level_logic.py
new file mode 100644
index 000000000000..094e5efed12d
--- /dev/null
+++ b/worlds/yoshisisland/level_logic.py
@@ -0,0 +1,482 @@
+from BaseClasses import CollectionState
+from typing import TYPE_CHECKING
+
+from .Options import StageLogic, BowserDoor, ObjectVis
+
+if TYPE_CHECKING:
+ from . import YoshisIslandWorld
+
+
+class YoshiLogic:
+ player: int
+ game_logic: str
+ midring_start: bool
+ clouds_always_visible: bool
+ consumable_logic: bool
+ luigi_pieces: int
+
+ def __init__(self, world: "YoshisIslandWorld") -> None:
+ self.player = world.player
+ self.boss_order = world.boss_order
+ self.luigi_pieces = world.options.luigi_pieces_required.value
+
+ if world.options.stage_logic == StageLogic.option_strict:
+ self.game_logic = "Easy"
+ elif world.options.stage_logic == StageLogic.option_loose:
+ self.game_logic = "Normal"
+ else:
+ self.game_logic = "Hard"
+
+ self.midring_start = not world.options.shuffle_midrings
+ self.consumable_logic = not world.options.item_logic
+
+ self.clouds_always_visible = world.options.hidden_object_visibility >= ObjectVis.option_clouds_only
+
+ self.bowser_door = world.options.bowser_door_mode.value
+ if self.bowser_door == BowserDoor.option_door_4:
+ self.bowser_door = BowserDoor.option_door_3
+
+ def has_midring(self, state: CollectionState) -> bool:
+ return self.midring_start or state.has("Middle Ring", self.player)
+
+ def reconstitute_luigi(self, state: CollectionState) -> bool:
+ return state.has("Piece of Luigi", self.player, self.luigi_pieces)
+
+ def bandit_bonus(self, state: CollectionState) -> bool:
+ return state.has("Bandit Consumables", self.player) or state.has("Bandit Watermelons", self.player)
+
+ def item_bonus(self, state: CollectionState) -> bool:
+ return state.has("Bonus Consumables", self.player)
+
+ def combat_item(self, state: CollectionState) -> bool:
+ if not self.consumable_logic:
+ return False
+ else:
+ if self.game_logic == "Easy":
+ return self.item_bonus(state)
+ else:
+ return self.bandit_bonus(state) or self.item_bonus(state)
+
+ def melon_item(self, state: CollectionState) -> bool:
+ if not self.consumable_logic:
+ return False
+ else:
+ if self.game_logic == "Easy":
+ return self.item_bonus(state)
+ else:
+ return state.has("Bandit Watermelons", self.player) or self.item_bonus(state)
+
+ def default_vis(self, state: CollectionState) -> bool:
+ if self.clouds_always_visible:
+ return True
+ else:
+ return False
+
+ def cansee_clouds(self, state: CollectionState) -> bool:
+ if self.game_logic != "Easy":
+ return True
+ else:
+ return self.default_vis(state) or state.has("Secret Lens", self.player) or self.combat_item(state)
+
+ def bowserdoor_1(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Egg Plant", "! Switch"}, self.player) and state.has("Egg Capacity Upgrade", self.player, 2)
+ elif self.game_logic == "Normal":
+ return state.has("Egg Plant", self.player) and state.has("Egg Capacity Upgrade", self.player, 1)
+ else:
+ return state.has("Egg Plant", self.player)
+
+ def bowserdoor_2(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return ((state.has("Egg Capacity Upgrade", self.player, 3) and state.has("Egg Plant", self.player)) or self.combat_item(state)) and state.has("Key", self.player)
+ elif self.game_logic == "Normal":
+ return ((state.has("Egg Capacity Upgrade", self.player, 2) and state.has("Egg Plant", self.player)) or self.combat_item(state)) and state.has("Key", self.player)
+ else:
+ return ((state.has("Egg Capacity Upgrade", self.player, 1) and state.has("Egg Plant", self.player)) or self.combat_item(state)) and state.has("Key", self.player)
+
+ def bowserdoor_3(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return True
+ elif self.game_logic == "Normal":
+ return True
+ else:
+ return True
+
+ def bowserdoor_4(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return True
+ elif self.game_logic == "Normal":
+ return True
+ else:
+ return True
+
+ def _68Route(self, state: CollectionState) -> bool:
+ if self.bowser_door == 0:
+ return True
+ elif self.bowser_door == 1:
+ return self.bowserdoor_1(state)
+ elif self.bowser_door == 2:
+ return self.bowserdoor_2(state)
+ elif self.bowser_door == 3:
+ return True
+ elif self.bowser_door == 4:
+ return True
+ elif self.bowser_door == 5:
+ return self.bowserdoor_1(state) and self.bowserdoor_2(state) and self.bowserdoor_3(state)
+
+ def _68CollectibleRoute(self, state: CollectionState) -> bool:
+ if self.bowser_door == 0:
+ return True
+ elif self.bowser_door == 1:
+ return self.bowserdoor_1(state)
+ elif self.bowser_door == 2:
+ return self.bowserdoor_2(state)
+ elif self.bowser_door == 3:
+ return True
+ elif self.bowser_door == 4:
+ return True
+ elif self.bowser_door == 5:
+ return self.bowserdoor_1(state)
+
+
+##############################################################################
+ def _13Game(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has("Key", self.player)
+ elif self.game_logic == "Normal":
+ return state.has("Key", self.player)
+ else:
+ return state.has("Key", self.player)
+##############################################################################
+ def _14Clear(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Spring Ball", "Key"}, self.player)
+ elif self.game_logic == "Normal":
+ return state.has_all({"Spring Ball", "Key"}, self.player)
+ else:
+ return state.has_all({"Spring Ball", "Key"}, self.player)
+
+ def _14Boss(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has("Egg Plant", self.player)
+ elif self.game_logic == "Normal":
+ return state.has("Egg Plant", self.player)
+ else:
+ return (state.has("Egg Capacity Upgrade", self.player, 5) or state.has("Egg Plant", self.player))
+
+ def _14CanFightBoss(self, state: CollectionState) -> bool:
+ if state.can_reach(self.boss_order[0], "Location", self.player):
+ return True
+##############################################################################
+ def _17Game(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has("Key", self.player)
+ elif self.game_logic == "Normal":
+ return state.has("Key", self.player)
+ else:
+ return state.has("Key", self.player)
+##############################################################################
+ def _18Clear(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Key", "Arrow Wheel"}, self.player)
+ elif self.game_logic == "Normal":
+ return state.has_all({"Key", "Arrow Wheel"}, self.player)
+ else:
+ return state.has_all({"Key", "Arrow Wheel"}, self.player)
+
+ def _18Boss(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return True
+ elif self.game_logic == "Normal":
+ return True
+ else:
+ return True
+
+ def _18CanFightBoss(self, state: CollectionState) -> bool:
+ if state.can_reach(self.boss_order[1], "Location", self.player):
+ return True
+##############################################################################
+ def _21Game(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Poochy", "Large Spring Ball", "Key"}, self.player)
+ elif self.game_logic == "Normal":
+ return state.has_all({"Poochy", "Large Spring Ball", "Key"}, self.player)
+ else:
+ return state.has_all({"Poochy", "Large Spring Ball", "Key"}, self.player)
+##############################################################################
+ def _23Game(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Mole Tank Morph", "Key"}, self.player)
+ elif self.game_logic == "Normal":
+ return state.has_all({"Mole Tank Morph", "Key"}, self.player)
+ else:
+ return state.has_all({"Mole Tank Morph", "Key"}, self.player)
+##############################################################################
+ def _24Clear(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"! Switch", "Key", "Dashed Stairs"}, self.player)
+ elif self.game_logic == "Normal":
+ return state.has_all({"! Switch", "Dashed Stairs"}, self.player)
+ else:
+ return state.has("! Switch", self.player)
+
+ def _24Boss(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return True
+ elif self.game_logic == "Normal":
+ return True
+ else:
+ return True
+
+ def _24CanFightBoss(self, state: CollectionState) -> bool:
+ if state.can_reach(self.boss_order[2], "Location", self.player):
+ return True
+##############################################################################
+ def _26Game(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Large Spring Ball", "Key"}, self.player)
+ elif self.game_logic == "Normal":
+ return state.has_all({"Large Spring Ball", "Key"}, self.player)
+ else:
+ return state.has_all({"Large Spring Ball", "Key"}, self.player)
+##############################################################################
+ def _27Game(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has("Key", self.player)
+ elif self.game_logic == "Normal":
+ return state.has("Key", self.player)
+ else:
+ return state.has("Key", self.player)
+##############################################################################
+ def _28Clear(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Arrow Wheel", "Key"}, self.player) and (state.has("Egg Capacity Upgrade", self.player, 1))
+ elif self.game_logic == "Normal":
+ return state.has_all({"Arrow Wheel", "Key"}, self.player)
+ else:
+ return state.has_all({"Arrow Wheel", "Key"}, self.player)
+
+ def _28Boss(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return True
+ elif self.game_logic == "Normal":
+ return True
+ else:
+ return True
+
+ def _28CanFightBoss(self, state: CollectionState) -> bool:
+ if state.can_reach(self.boss_order[3], "Location", self.player):
+ return True
+##############################################################################
+ def _32Game(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Dashed Stairs", "Spring Ball", "Key"}, self.player)
+ elif self.game_logic == "Normal":
+ return state.has_all({"Dashed Stairs", "Key"}, self.player)
+ else:
+ return state.has_all({"Dashed Stairs", "Key"}, self.player)
+##############################################################################
+ def _34Clear(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has("Dashed Platform", self.player)
+ elif self.game_logic == "Normal":
+ return (state.has("Dashed Platform", self.player) or self.has_midring(state))
+ else:
+ return True
+
+ def _34Boss(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has("Giant Eggs", self.player)
+ elif self.game_logic == "Normal":
+ return True
+ else:
+ return True
+
+ def _34CanFightBoss(self, state: CollectionState) -> bool:
+ if state.can_reach(self.boss_order[4], "Location", self.player):
+ return True
+##############################################################################
+ def _37Game(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Key", "Large Spring Ball"}, self.player)
+ elif self.game_logic == "Normal":
+ return state.has("Key", self.player)
+ else:
+ return state.has("Key", self.player)
+##############################################################################
+ def _38Clear(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return (state.has("Egg Capacity Upgrade", self.player, 3) or self.combat_item(state))
+ elif self.game_logic == "Normal":
+ return (state.has("Egg Capacity Upgrade", self.player, 1) or self.combat_item(state))
+ else:
+ return True
+
+ def _38Boss(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return True
+ elif self.game_logic == "Normal":
+ return True
+ else:
+ return True
+
+ def _38CanFightBoss(self, state: CollectionState) -> bool:
+ if state.can_reach(self.boss_order[5], "Location", self.player):
+ return True
+##############################################################################
+ def _42Game(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Large Spring Ball", "Key"}, self.player)
+ elif self.game_logic == "Normal":
+ return state.has_all({"Large Spring Ball", "Key"}, self.player)
+ else:
+ return state.has("Key", self.player)
+##############################################################################
+ def _44Clear(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Dashed Stairs", "Vanishing Arrow Wheel", "Arrow Wheel", "Bucket", "Key"}, self.player) and (state.has("Egg Capacity Upgrade", self.player, 1) or self.combat_item(state))
+ elif self.game_logic == "Normal":
+ return state.has_all({"Dashed Stairs", "Vanishing Arrow Wheel", "Arrow Wheel", "Bucket", "Key"}, self.player)
+ else:
+ return state.has_all({"Dashed Stairs", "Vanishing Arrow Wheel", "Arrow Wheel", "Bucket", "Key"}, self.player)
+
+ def _44Boss(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return True
+ elif self.game_logic == "Normal":
+ return True
+ else:
+ return True
+
+ def _44CanFightBoss(self, state: CollectionState) -> bool:
+ if state.can_reach(self.boss_order[6], "Location", self.player):
+ return True
+########################################################################################################
+
+ def _46Game(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Key", "Large Spring Ball"}, self.player)
+ elif self.game_logic == "Normal":
+ return state.has_all({"Key", "Large Spring Ball"}, self.player)
+ else:
+ return state.has_all({"Key", "Large Spring Ball"}, self.player)
+
+ def _47Game(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Key", "Large Spring Ball"}, self.player)
+ elif self.game_logic == "Normal":
+ return state.has_all({"Key", "Large Spring Ball"}, self.player)
+ else:
+ return state.has_all({"Key", "Large Spring Ball"}, self.player)
+##############################################################################
+ def _48Clear(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return (state.has_all({"Dashed Stairs", "Vanishing Arrow Wheel", "Key", "Large Spring Ball"}, self.player))
+ elif self.game_logic == "Normal":
+ return (state.has_all({"Dashed Stairs", "Vanishing Arrow Wheel", "Key", "Large Spring Ball"}, self.player))
+ else:
+ return (state.has_all({"Key", "Large Spring Ball"}, self.player))
+
+ def _48Boss(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return (state.has("Egg Capacity Upgrade", self.player, 3))
+ elif self.game_logic == "Normal":
+ return (state.has("Egg Capacity Upgrade", self.player, 2))
+ else:
+ return (state.has("Egg Capacity Upgrade", self.player, 1))
+
+ def _48CanFightBoss(self, state: CollectionState) -> bool:
+ if state.can_reach(self.boss_order[7], "Location", self.player):
+ return True
+######################################################################################################################
+ def _51Game(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has("Key", self.player)
+ elif self.game_logic == "Normal":
+ return state.has("Key", self.player)
+ else:
+ return state.has("Key", self.player)
+##############################################################################
+ def _54Clear(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return (state.has_all({"Dashed Stairs", "Platform Ghost", "Dashed Platform"}, self.player))
+ elif self.game_logic == "Normal":
+ return (state.has_all({"Dashed Stairs", "Platform Ghost", "Dashed Platform"}, self.player))
+ else:
+ return (state.has_all({"Dashed Stairs", "Platform Ghost"}, self.player))
+
+ def _54Boss(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return (state.has("Egg Capacity Upgrade", self.player, 2) and state.has("Egg Plant", self.player))
+ elif self.game_logic == "Normal":
+ return ((state.has("Egg Capacity Upgrade", self.player, 1) and state.has("Egg Plant", self.player)) or (state.has("Egg Capacity Upgrade", self.player, 5) and self.has_midring(state)))
+ else:
+ return ((state.has("Egg Plant", self.player)) or (state.has("Egg Capacity Upgrade", self.player, 3) and self.has_midring(state)))
+
+ def _54CanFightBoss(self, state: CollectionState) -> bool:
+ if state.can_reach(self.boss_order[8], "Location", self.player):
+ return True
+###################################################################################################
+ def _58Clear(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Arrow Wheel", "Large Spring Ball"}, self.player)
+ elif self.game_logic == "Normal":
+ return state.has_all({"Arrow Wheel", "Large Spring Ball"}, self.player)
+ else:
+ return state.has_all({"Arrow Wheel", "Large Spring Ball"}, self.player)
+
+ def _58Boss(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return True
+ elif self.game_logic == "Normal":
+ return True
+ else:
+ return True
+
+ def _58CanFightBoss(self, state: CollectionState) -> bool:
+ if state.can_reach(self.boss_order[9], "Location", self.player):
+ return True
+##############################################################################
+ def _61Game(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Dashed Platform", "Key", "Beanstalk"}, self.player)
+ elif self.game_logic == "Normal":
+ return state.has_all({"Dashed Platform", "Key", "Beanstalk"}, self.player)
+ else:
+ return state.has("Key", self.player)
+##############################################################################
+ def _64Clear(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Spring Ball", "Large Spring Ball", "Egg Plant", "Key"}, self.player) and (state.has("Egg Capacity Upgrade", self.player, 3) or self.combat_item(state))
+ elif self.game_logic == "Normal":
+ return state.has_all({"Large Spring Ball", "Egg Plant", "Key"}, self.player) and (state.has("Egg Capacity Upgrade", self.player, 2) or self.combat_item(state))
+ else:
+ return state.has_all({"Egg Plant", "Key"}, self.player) and (state.has("Egg Capacity Upgrade", self.player, 1) or self.combat_item(state))
+
+ def _64Boss(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has("Egg Plant", self.player)
+ elif self.game_logic == "Normal":
+ return state.has("Egg Plant", self.player)
+ else:
+ return True
+
+ def _64CanFightBoss(self, state: CollectionState) -> bool:
+ if state.can_reach(self.boss_order[10], "Location", self.player):
+ return True
+##############################################################################
+ def _67Game(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has("Key", self.player)
+ elif self.game_logic == "Normal":
+ return state.has("Key", self.player)
+ else:
+ return state.has("Key", self.player)
+##############################################################################
+ def _68Clear(self, state: CollectionState) -> bool:
+ if self.game_logic == "Easy":
+ return state.has_all({"Helicopter Morph", "Egg Plant", "Giant Eggs"}, self.player) and self._68Route(state)
+ elif self.game_logic == "Normal":
+ return state.has_all({"Helicopter Morph", "Egg Plant", "Giant Eggs"}, self.player) and self._68Route(state)
+ else:
+ return state.has_all({"Helicopter Morph", "Giant Eggs"}, self.player) and self._68Route(state)
diff --git a/worlds/yoshisisland/setup_bosses.py b/worlds/yoshisisland/setup_bosses.py
new file mode 100644
index 000000000000..bbefdd31a05c
--- /dev/null
+++ b/worlds/yoshisisland/setup_bosses.py
@@ -0,0 +1,19 @@
+from BaseClasses import CollectionState
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from . import YoshisIslandWorld
+
+
+class BossReqs:
+ player: int
+
+ def __init__(self, world: "YoshisIslandWorld") -> None:
+ self.player = world.player
+ self.castle_unlock = world.options.castle_open_condition.value
+ self.boss_unlock = world.options.castle_clear_condition.value
+
+ def castle_access(self, state: CollectionState) -> bool:
+ return state.has("Boss Clear", self.player, self.castle_unlock)
+
+ def castle_clear(self, state: CollectionState) -> bool:
+ return state.has("Boss Clear", self.player, self.boss_unlock)
diff --git a/worlds/yoshisisland/setup_game.py b/worlds/yoshisisland/setup_game.py
new file mode 100644
index 000000000000..000420a95b07
--- /dev/null
+++ b/worlds/yoshisisland/setup_game.py
@@ -0,0 +1,460 @@
+import struct
+from typing import TYPE_CHECKING
+
+from .Options import YoshiColors, BabySound, LevelShuffle
+
+if TYPE_CHECKING:
+ from . import YoshisIslandWorld
+
+
+def setup_gamevars(world: "YoshisIslandWorld") -> None:
+ if world.options.luigi_pieces_in_pool < world.options.luigi_pieces_required:
+ world.options.luigi_pieces_in_pool.value = world.random.randint(world.options.luigi_pieces_required.value, 100)
+ world.starting_lives = struct.pack("H", world.options.starting_lives)
+
+ world.level_colors = []
+ world.color_order = []
+ for i in range(72):
+ world.level_colors.append(world.random.randint(0, 7))
+ if world.options.yoshi_colors == YoshiColors.option_singularity:
+ singularity_color = world.options.yoshi_singularity_color.value
+ for i in range(len(world.level_colors)):
+ world.level_colors[i] = singularity_color
+ elif world.options.yoshi_colors == YoshiColors.option_random_order:
+ world.leader_color = world.random.randint(0, 7)
+ for i in range(7):
+ world.color_order.append(world.random.randint(0, 7))
+
+ bonus_valid = [0x00, 0x02, 0x04, 0x06, 0x08, 0x0A]
+
+ world.world_bonus = []
+ for i in range(12):
+ world.world_bonus.append(world.random.choice(bonus_valid))
+
+ safe_baby_sounds = [0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
+ 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A,
+ 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,
+ 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x52, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62,
+ 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
+ 0x73, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B,
+ 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2]
+
+ if world.options.baby_mario_sound == BabySound.option_random_sound_effect:
+ world.baby_mario_sfx = world.random.choice(safe_baby_sounds)
+ elif world.options.baby_mario_sound == BabySound.option_disabled:
+ world.baby_mario_sfx = 0x42
+ else:
+ world.baby_mario_sfx = 0x44
+
+ boss_list = ["Burt The Bashful's Boss Room", "Salvo The Slime's Boss Room",
+ "Bigger Boo's Boss Room", "Roger The Ghost's Boss Room",
+ "Prince Froggy's Boss Room", "Naval Piranha's Boss Room",
+ "Marching Milde's Boss Room", "Hookbill The Koopa's Boss Room",
+ "Sluggy The Unshaven's Boss Room", "Raphael The Raven's Boss Room",
+ "Tap-Tap The Red Nose's Boss Room"]
+
+ world.boss_order = []
+
+ if world.options.boss_shuffle:
+ world.random.shuffle(boss_list)
+ world.boss_order = boss_list
+
+ burt_pointers = [0x3D, 0x05, 0x63, 0x00]
+ slime_pointers = [0x70, 0x04, 0x78, 0x00]
+ boo_pointers = [0x74, 0xBB, 0x7A, 0x00]
+ pot_pointers = [0xCF, 0x04, 0x4D, 0x00]
+ frog_pointers = [0xBF, 0x12, 0x62, 0x04]
+ plant_pointers = [0x7F, 0x0D, 0x42, 0x00]
+ milde_pointers = [0x82, 0x06, 0x64, 0x00]
+ koop_pointers = [0x86, 0x0D, 0x78, 0x00]
+ slug_pointers = [0x8A, 0x09, 0x7A, 0x00]
+ raph_pointers = [0xC4, 0x03, 0x4B, 0x05]
+ tap_pointers = [0xCC, 0x49, 0x64, 0x02]
+
+ boss_data_list = [
+ burt_pointers,
+ slime_pointers,
+ boo_pointers,
+ pot_pointers,
+ frog_pointers,
+ plant_pointers,
+ milde_pointers,
+ koop_pointers,
+ slug_pointers,
+ raph_pointers,
+ tap_pointers
+ ]
+
+ boss_levels = [0x03, 0x07, 0x0F, 0x13, 0x1B, 0x1F, 0x27, 0x2B, 0x33, 0x37, 0x3F]
+
+ boss_room_idlist = {
+ "Burt The Bashful's Boss Room": 0,
+ "Salvo The Slime's Boss Room": 1,
+ "Bigger Boo's Boss Room": 2,
+ "Roger The Ghost's Boss Room": 3,
+ "Prince Froggy's Boss Room": 4,
+ "Naval Piranha's Boss Room": 5,
+ "Marching Milde's Boss Room": 6,
+ "Hookbill The Koopa's Boss Room": 7,
+ "Sluggy The Unshaven's Boss Room": 8,
+ "Raphael The Raven's Boss Room": 9,
+ "Tap-Tap The Red Nose's Boss Room": 10,
+ }
+
+ boss_check_list = {
+ "Burt The Bashful's Boss Room": "Burt The Bashful Defeated",
+ "Salvo The Slime's Boss Room": "Salvo The Slime Defeated",
+ "Bigger Boo's Boss Room": "Bigger Boo Defeated",
+ "Roger The Ghost's Boss Room": "Roger The Ghost Defeated",
+ "Prince Froggy's Boss Room": "Prince Froggy Defeated",
+ "Naval Piranha's Boss Room": "Naval Piranha Defeated",
+ "Marching Milde's Boss Room": "Marching Milde Defeated",
+ "Hookbill The Koopa's Boss Room": "Hookbill The Koopa Defeated",
+ "Sluggy The Unshaven's Boss Room": "Sluggy The Unshaven Defeated",
+ "Raphael The Raven's Boss Room": "Raphael The Raven Defeated",
+ "Tap-Tap The Red Nose's Boss Room": "Tap-Tap The Red Nose Defeated",
+ }
+
+ world.boss_room_id = [boss_room_idlist[roomnum] for roomnum in world.boss_order]
+ world.tap_tap_room = boss_levels[world.boss_room_id.index(10)]
+ world.boss_ap_loc = [boss_check_list[roomnum] for roomnum in world.boss_order]
+
+ world.boss_burt_data = boss_data_list[world.boss_room_id[0]]
+
+ world.boss_slime_data = boss_data_list[world.boss_room_id[1]]
+
+ world.boss_boo_data = boss_data_list[world.boss_room_id[2]]
+
+ world.boss_pot_data = boss_data_list[world.boss_room_id[3]]
+
+ world.boss_frog_data = boss_data_list[world.boss_room_id[4]]
+
+ world.boss_plant_data = boss_data_list[world.boss_room_id[5]]
+
+ world.boss_milde_data = boss_data_list[world.boss_room_id[6]]
+
+ world.boss_koop_data = boss_data_list[world.boss_room_id[7]]
+
+ world.boss_slug_data = boss_data_list[world.boss_room_id[8]]
+
+ world.boss_raph_data = boss_data_list[world.boss_room_id[9]]
+
+ world.boss_tap_data = boss_data_list[world.boss_room_id[10]]
+
+ world.global_level_list = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42]
+ level_id_list = {
+ 0x00: "1-1",
+ 0x01: "1-2",
+ 0x02: "1-3",
+ 0x03: "1-4",
+ 0x04: "1-5",
+ 0x05: "1-6",
+ 0x06: "1-7",
+ 0x07: "1-8",
+ 0x0C: "2-1",
+ 0x0D: "2-2",
+ 0x0E: "2-3",
+ 0x0F: "2-4",
+ 0x10: "2-5",
+ 0x11: "2-6",
+ 0x12: "2-7",
+ 0x13: "2-8",
+ 0x18: "3-1",
+ 0x19: "3-2",
+ 0x1A: "3-3",
+ 0x1B: "3-4",
+ 0x1C: "3-5",
+ 0x1D: "3-6",
+ 0x1E: "3-7",
+ 0x1F: "3-8",
+ 0x24: "4-1",
+ 0x25: "4-2",
+ 0x26: "4-3",
+ 0x27: "4-4",
+ 0x28: "4-5",
+ 0x29: "4-6",
+ 0x2A: "4-7",
+ 0x2B: "4-8",
+ 0x30: "5-1",
+ 0x31: "5-2",
+ 0x32: "5-3",
+ 0x33: "5-4",
+ 0x34: "5-5",
+ 0x35: "5-6",
+ 0x36: "5-7",
+ 0x37: "5-8",
+ 0x3C: "6-1",
+ 0x3D: "6-2",
+ 0x3E: "6-3",
+ 0x3F: "6-4",
+ 0x40: "6-5",
+ 0x41: "6-6",
+ 0x42: "6-7"
+ }
+
+ level_names = {
+ 0x00: "Make Eggs, Throw Eggs",
+ 0x01: "Watch Out Below!",
+ 0x02: "The Cave Of Chomp Rock",
+ 0x03: "Burt The Bashful's Fort",
+ 0x04: "Hop! Hop! Donut Lifts",
+ 0x05: "Shy-Guys On Stilts",
+ 0x06: "Touch Fuzzy Get Dizzy",
+ 0x07: "Salvo The Slime's Castle",
+ 0x0C: "Visit Koopa And Para-Koopa",
+ 0x0D: "The Baseball Boys",
+ 0x0E: "What's Gusty Taste Like?",
+ 0x0F: "Bigger Boo's Fort",
+ 0x10: "Watch Out For Lakitu",
+ 0x11: "The Cave Of The Mystery Maze",
+ 0x12: "Lakitu's Wall",
+ 0x13: "The Potted Ghost's Castle",
+ 0x18: "Welcome To Monkey World!",
+ 0x19: "Jungle Rhythm...",
+ 0x1A: "Nep-Enuts' Domain",
+ 0x1B: "Prince Froggy's Fort",
+ 0x1C: "Jammin' Through The Trees",
+ 0x1D: "The Cave Of Harry Hedgehog",
+ 0x1E: "Monkeys' Favorite Lake",
+ 0x1F: "Naval Piranha's Castle",
+ 0x24: "GO! GO! MARIO!!",
+ 0x25: "The Cave Of The Lakitus",
+ 0x26: "Don't Look Back!",
+ 0x27: "Marching Milde's Fort",
+ 0x28: "Chomp Rock Zone",
+ 0x29: "Lake Shore Paradise",
+ 0x2A: "Ride Like The Wind",
+ 0x2B: "Hookbill The Koopa's Castle",
+ 0x30: "BLIZZARD!!!",
+ 0x31: "Ride The Ski Lifts",
+ 0x32: "Danger - Icy Conditions Ahead",
+ 0x33: "Sluggy The Unshaven's Fort",
+ 0x34: "Goonie Rides!",
+ 0x35: "Welcome To Cloud World",
+ 0x36: "Shifting Platforms Ahead",
+ 0x37: "Raphael The Raven's Castle",
+ 0x3C: "Scary Skeleton Goonies!",
+ 0x3D: "The Cave Of The Bandits",
+ 0x3E: "Beware The Spinning Logs",
+ 0x3F: "Tap-Tap The Red Nose's Fort",
+ 0x40: "The Very Loooooong Cave",
+ 0x41: "The Deep, Underground Maze",
+ 0x42: "KEEP MOVING!!!!"
+ }
+
+ world_1_offsets = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00]
+ world_2_offsets = [0x01, 0x01, 0x00, 0x00, 0x00, 0x00]
+ world_3_offsets = [0x01, 0x01, 0x01, 0x00, 0x00, 0x00]
+ world_4_offsets = [0x01, 0x01, 0x01, 0x01, 0x00, 0x00]
+ world_5_offsets = [0x01, 0x01, 0x01, 0x01, 0x01, 0x00]
+ easy_start_lv = [0x02, 0x04, 0x06, 0x0E, 0x10, 0x18, 0x1C, 0x28,
+ 0x30, 0x31, 0x35, 0x36, 0x3E, 0x40, 0x42]
+ norm_start_lv = [0x00, 0x01, 0x02, 0x04, 0x06, 0x0E, 0x10, 0x12, 0x18, 0x1A,
+ 0x1C, 0x1E, 0x28, 0x30, 0x31, 0x34, 0x35, 0x36, 0x3D, 0x3E, 0x40, 0x42]
+ hard_start_lv = [0x00, 0x01, 0x02, 0x04, 0x06, 0x0D, 0x0E, 0x10, 0x11, 0x12, 0x18, 0x1A, 0x1C,
+ 0x1E, 0x24, 0x25, 0x26, 0x28, 0x29, 0x30, 0x31, 0x34, 0x35, 0x36, 0x3D, 0x3E,
+ 0x40, 0x42]
+ diff_index = [easy_start_lv, norm_start_lv, hard_start_lv]
+ diff_level = diff_index[world.options.stage_logic.value]
+ boss_lv = [0x03, 0x07, 0x0F, 0x13, 0x1B, 0x1F, 0x27, 0x2B, 0x33, 0x37, 0x3F]
+ world.world_start_lv = [0, 8, 16, 24, 32, 40]
+ if not world.options.shuffle_midrings:
+ easy_start_lv.extend([0x1A, 0x24, 0x34])
+ norm_start_lv.extend([0x24, 0x3C])
+ hard_start_lv.extend([0x1D, 0x3C])
+
+ if world.options.level_shuffle != LevelShuffle.option_bosses_guranteed:
+ hard_start_lv.extend([0x07, 0x1B, 0x1F, 0x2B, 0x33, 0x37])
+ if not world.options.shuffle_midrings:
+ easy_start_lv.extend([0x1B])
+ norm_start_lv.extend([0x1B, 0x2B, 0x37])
+
+ starting_level = world.random.choice(diff_level)
+
+ starting_level_entrance = world.world_start_lv[world.options.starting_world.value]
+ if world.options.level_shuffle:
+ world.global_level_list.remove(starting_level)
+ world.random.shuffle(world.global_level_list)
+ if world.options.level_shuffle == LevelShuffle.option_bosses_guranteed:
+ for i in range(11):
+ world.global_level_list = [item for item in world.global_level_list
+ if item not in boss_lv]
+ world.random.shuffle(boss_lv)
+
+ world.global_level_list.insert(3 - world_1_offsets[world.options.starting_world.value], boss_lv[0]) # 1 if starting world is 1, 0 otherwise
+ world.global_level_list.insert(7 - world_1_offsets[world.options.starting_world.value], boss_lv[1])
+ world.global_level_list.insert(11 - world_2_offsets[world.options.starting_world.value], boss_lv[2])
+ world.global_level_list.insert(15 - world_2_offsets[world.options.starting_world.value], boss_lv[3])
+ world.global_level_list.insert(19 - world_3_offsets[world.options.starting_world.value], boss_lv[4])
+ world.global_level_list.insert(23 - world_3_offsets[world.options.starting_world.value], boss_lv[5])
+ world.global_level_list.insert(27 - world_4_offsets[world.options.starting_world.value], boss_lv[6])
+ world.global_level_list.insert(31 - world_4_offsets[world.options.starting_world.value], boss_lv[7])
+ world.global_level_list.insert(35 - world_5_offsets[world.options.starting_world.value], boss_lv[8])
+ world.global_level_list.insert(39 - world_5_offsets[world.options.starting_world.value], boss_lv[9])
+ world.global_level_list.insert(43 - 1, boss_lv[10])
+ world.global_level_list.insert(starting_level_entrance, starting_level)
+ world.level_location_list = [level_id_list[LevelID] for LevelID in world.global_level_list]
+ world.level_name_list = [level_names[LevelID] for LevelID in world.global_level_list]
+
+ level_panel_dict = {
+ 0x00: [0x04, 0x04, 0x53],
+ 0x01: [0x20, 0x04, 0x53],
+ 0x02: [0x3C, 0x04, 0x53],
+ 0x03: [0x58, 0x04, 0x53],
+ 0x04: [0x74, 0x04, 0x53],
+ 0x05: [0x90, 0x04, 0x53],
+ 0x06: [0xAC, 0x04, 0x53],
+ 0x07: [0xC8, 0x04, 0x53],
+ 0x0C: [0x04, 0x24, 0x53],
+ 0x0D: [0x20, 0x24, 0x53],
+ 0x0E: [0x3C, 0x24, 0x53],
+ 0x0F: [0x58, 0x24, 0x53],
+ 0x10: [0x74, 0x24, 0x53],
+ 0x11: [0x90, 0x24, 0x53],
+ 0x12: [0xAC, 0x24, 0x53],
+ 0x13: [0xC8, 0x24, 0x53],
+ 0x18: [0x04, 0x44, 0x53],
+ 0x19: [0x20, 0x44, 0x53],
+ 0x1A: [0x3C, 0x44, 0x53],
+ 0x1B: [0x58, 0x44, 0x53],
+ 0x1C: [0x74, 0x44, 0x53],
+ 0x1D: [0x90, 0x44, 0x53],
+ 0x1E: [0xAC, 0x44, 0x53],
+ 0x1F: [0xC8, 0x44, 0x53],
+ 0x24: [0x04, 0x64, 0x53],
+ 0x25: [0x20, 0x64, 0x53],
+ 0x26: [0x3C, 0x64, 0x53],
+ 0x27: [0x58, 0x64, 0x53],
+ 0x28: [0x74, 0x64, 0x53],
+ 0x29: [0x90, 0x64, 0x53],
+ 0x2A: [0xAC, 0x64, 0x53],
+ 0x2B: [0xC8, 0x64, 0x53],
+ 0x30: [0x04, 0x04, 0x53],
+ 0x31: [0x20, 0x04, 0x53],
+ 0x32: [0x3C, 0x04, 0x53],
+ 0x33: [0x58, 0x04, 0x53],
+ 0x34: [0x74, 0x04, 0x53],
+ 0x35: [0x90, 0x04, 0x53],
+ 0x36: [0xAC, 0x04, 0x53],
+ 0x37: [0xC8, 0x04, 0x53],
+ 0x3C: [0x04, 0x24, 0x53],
+ 0x3D: [0x20, 0x24, 0x53],
+ 0x3E: [0x3C, 0x24, 0x53],
+ 0x3F: [0x58, 0x24, 0x53],
+ 0x40: [0x74, 0x24, 0x53],
+ 0x41: [0x90, 0x24, 0x53],
+ 0x42: [0xAC, 0x24, 0x53],
+ }
+ panel_palette_1 = [0x00, 0x03, 0x04, 0x05, 0x0C, 0x10, 0x12, 0x13, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
+ 0x24, 0x26, 0x27, 0x29, 0x2A, 0x2B, 0x30, 0x32, 0x34,
+ 0x35, 0x37, 0x3C, 0x3D, 0x40, 0x41] # 000C
+ panel_palette_2 = [0x01, 0x02, 0x06, 0x07, 0x0D, 0x0E, 0x0F, 0x11, 0x18, 0x1E, 0x1F, 0x25, 0x28,
+ 0x31, 0x33, 0x36, 0x3E, 0x3F, 0x42] # 0010
+
+ stage_number = 0
+ world_number = 1
+ for i in range(47):
+ stage_number += 1
+ if stage_number >= 9:
+ world_number += 1
+ stage_number = 1
+ for _ in range(3):
+ setattr(world, f"Stage{world_number}{stage_number}StageGFX",
+ level_panel_dict[world.global_level_list[i]])
+
+ world.level_gfx_table = []
+ world.palette_panel_list = []
+
+ for i in range(47):
+ if world.global_level_list[i] >= 0x30:
+ world.level_gfx_table.append(0x15)
+ else:
+ world.level_gfx_table.append(0x11)
+
+ if world.global_level_list[i] in panel_palette_1:
+ world.palette_panel_list.extend([0x00, 0x0C])
+ elif world.global_level_list[i] in panel_palette_2:
+ world.palette_panel_list.extend([0x00, 0x10])
+
+ world.palette_panel_list[16:16] = [0x00, 0x0c, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x18]
+ world.palette_panel_list[40:40] = [0x00, 0x0c, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x18]
+ world.palette_panel_list[64:64] = [0x00, 0x0c, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x18]
+ world.palette_panel_list[88:88] = [0x00, 0x0c, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x18]
+ world.palette_panel_list[112:112] = [0x00, 0x0c, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x18]
+
+ world.level_gfx_table.insert(8, 0x15)
+ world.level_gfx_table.insert(8, 0x15)
+ world.level_gfx_table.insert(8, 0x15)
+ world.level_gfx_table.insert(8, 0x11)
+
+ world.level_gfx_table.insert(20, 0x15)
+ world.level_gfx_table.insert(20, 0x15)
+ world.level_gfx_table.insert(20, 0x15)
+ world.level_gfx_table.insert(20, 0x11)
+
+ world.level_gfx_table.insert(32, 0x15)
+ world.level_gfx_table.insert(32, 0x15)
+ world.level_gfx_table.insert(32, 0x15)
+ world.level_gfx_table.insert(32, 0x11)
+
+ world.level_gfx_table.insert(44, 0x15)
+ world.level_gfx_table.insert(44, 0x15)
+ world.level_gfx_table.insert(44, 0x15)
+ world.level_gfx_table.insert(44, 0x11)
+
+ world.level_gfx_table.insert(56, 0x15)
+ world.level_gfx_table.insert(56, 0x15)
+ world.level_gfx_table.insert(56, 0x15)
+ world.level_gfx_table.insert(56, 0x15)
+
+ castle_door_dict = {
+ 0: [0xB8, 0x05, 0x77, 0x00],
+ 1: [0xB8, 0x05, 0x77, 0x00],
+ 2: [0xC6, 0x07, 0x7A, 0x00],
+ 3: [0xCD, 0x05, 0x5B, 0x00],
+ 4: [0xD3, 0x00, 0x77, 0x06],
+ 5: [0xB8, 0x05, 0x77, 0x00],
+ }
+
+ world.castle_door = castle_door_dict[world.options.bowser_door_mode.value]
+
+ world.world_1_stages = world.global_level_list[0:8]
+ world.world_2_stages = world.global_level_list[8:16]
+ world.world_3_stages = world.global_level_list[16:24]
+ world.world_4_stages = world.global_level_list[24:32]
+ world.world_5_stages = world.global_level_list[32:40]
+ world.world_6_stages = world.global_level_list[40:47]
+
+ world.world_1_stages.extend([0x08, 0x09])
+ world.world_2_stages.extend([0x14, 0x15])
+ world.world_3_stages.extend([0x20, 0x21])
+ world.world_4_stages.extend([0x2C, 0x2D])
+ world.world_5_stages.extend([0x38, 0x39])
+ world.world_6_stages.extend([0x43, 0x44, 0x45])
+
+ bowser_text_table = {
+ 0: [0xDE, 0xEE, 0xDC, 0xDC, 0xE5], # Gween
+ 1: [0xE7, 0xE0, 0xE5, 0xE2, 0xD0], # Pink
+ 3: [0xEB, 0xDF, 0xF0, 0xD8, 0xE5], # Thyan
+ 2: [0xF0, 0xDC, 0xEE, 0xEE, 0xE6], # Yewow
+ 4: [0xE7, 0xEC, 0xDF, 0xE7, 0xE3], # puhpl
+ 5: [0xD9, 0xEE, 0xE6, 0xEE, 0xE5], # Bwown
+ 6: [0xEE, 0xDC, 0xDB, 0xD0, 0xD0], # Wed
+ 7: [0xD9, 0xEE, 0xEC, 0xDC, 0xD0], # Bwue
+ }
+
+ if world.options.yoshi_colors == YoshiColors.option_random_order:
+ world.bowser_text = bowser_text_table[world.leader_color]
+ else:
+ world.bowser_text = bowser_text_table[world.level_colors[67]]
diff --git a/worlds/zillion/__init__.py b/worlds/zillion/__init__.py
index 3f441d12ab34..b4e382e097d2 100644
--- a/worlds/zillion/__init__.py
+++ b/worlds/zillion/__init__.py
@@ -4,20 +4,22 @@
import settings
import threading
import typing
-from typing import Any, Dict, List, Literal, Set, Tuple, Optional, cast
+from typing import Any, Dict, List, Set, Tuple, Optional
import os
import logging
from BaseClasses import ItemClassification, LocationProgressType, \
MultiWorld, Item, CollectionState, Entrance, Tutorial
+
+from .gen_data import GenData
from .logic import cs_to_zz_locs
from .region import ZillionLocation, ZillionRegion
-from .options import ZillionOptions, ZillionStartChar, validate
-from .id_maps import item_name_to_id as _item_name_to_id, \
+from .options import ZillionOptions, validate
+from .id_maps import ZillionSlotInfo, get_slot_info, item_name_to_id as _item_name_to_id, \
loc_name_to_id as _loc_name_to_id, make_id_to_others, \
zz_reg_name_to_reg_name, base_id
from .item import ZillionItem
-from .patch import ZillionDeltaPatch, get_base_rom_path
+from .patch import ZillionPatch
from zilliandomizer.randomizer import Randomizer as ZzRandomizer
from zilliandomizer.system import System
@@ -25,7 +27,7 @@
from zilliandomizer.logic_components.locations import Location as ZzLocation, Req
from zilliandomizer.options import Chars
-from ..AutoWorld import World, WebWorld
+from worlds.AutoWorld import World, WebWorld
class ZillionSettings(settings.Group):
@@ -33,8 +35,8 @@ class RomFile(settings.UserFilePath):
"""File name of the Zillion US rom"""
description = "Zillion US ROM File"
copy_to = "Zillion (UE) [!].sms"
- assert ZillionDeltaPatch.hash
- md5s = [ZillionDeltaPatch.hash]
+ assert ZillionPatch.hash
+ md5s = [ZillionPatch.hash]
class RomStart(str):
"""
@@ -134,14 +136,6 @@ 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)
self.id_to_zz_item = id_to_zz_item
- @classmethod
- def stage_assert_generate(cls, multiworld: MultiWorld) -> None:
- """Checks that a game is capable of generating, usually checks for some base file like a ROM.
- Not run for unittests since they don't produce output"""
- rom_file = get_base_rom_path()
- if not os.path.exists(rom_file):
- raise FileNotFoundError(rom_file)
-
def generate_early(self) -> None:
if not hasattr(self.multiworld, "zillion_logic_cache"):
setattr(self.multiworld, "zillion_logic_cache", {})
@@ -225,7 +219,7 @@ def access_rule_wrapped(zz_loc_local: ZzLocation,
loc.access_rule = access_rule
if not (limited_skill >= zz_loc.req):
loc.progress_type = LocationProgressType.EXCLUDED
- self.multiworld.exclude_locations[p].value.add(loc.name)
+ self.options.exclude_locations.value.add(loc.name)
here.locations.append(loc)
self.my_locations.append(loc)
@@ -288,15 +282,15 @@ def stage_generate_basic(multiworld: MultiWorld, *args: Any) -> None:
if group["game"] == "Zillion":
assert "item_pool" in group
item_pool = group["item_pool"]
- to_stay: Literal['Apple', 'Champ', 'JJ'] = "JJ"
+ to_stay: Chars = "JJ"
if "JJ" in item_pool:
assert "players" in group
group_players = group["players"]
- start_chars = cast(Dict[int, ZillionStartChar], getattr(multiworld, "start_char"))
- players_start_chars = [
- (player, start_chars[player].current_option_name)
- for player in group_players
- ]
+ players_start_chars: List[Tuple[int, Chars]] = []
+ for player in group_players:
+ z_world = multiworld.worlds[player]
+ assert isinstance(z_world, ZillionWorld)
+ players_start_chars.append((player, z_world.options.start_char.get_char()))
start_char_counts = Counter(sc for _, sc in players_start_chars)
# majority rules
if start_char_counts["Apple"] > start_char_counts["Champ"]:
@@ -304,14 +298,16 @@ def stage_generate_basic(multiworld: MultiWorld, *args: Any) -> None:
elif start_char_counts["Champ"] > start_char_counts["Apple"]:
to_stay = "Champ"
else: # equal
- choices: Tuple[Literal['Apple', 'Champ', 'JJ'], ...] = ("Apple", "Champ")
+ choices: Tuple[Chars, ...] = ("Apple", "Champ")
to_stay = multiworld.random.choice(choices)
for p, sc in players_start_chars:
if sc != to_stay:
group_players.remove(p)
assert "world" in group
- cast(ZillionWorld, group["world"])._make_item_maps(to_stay)
+ group_world = group["world"]
+ assert isinstance(group_world, ZillionWorld)
+ group_world._make_item_maps(to_stay)
def post_fill(self) -> None:
"""Optional Method that is called after regular fill. Can be used to do adjustments before output generation.
@@ -319,27 +315,28 @@ def post_fill(self) -> None:
self.zz_system.post_fill()
- def finalize_item_locations(self) -> None:
+ def finalize_item_locations(self) -> GenData:
"""
sync zilliandomizer item locations with AP item locations
+
+ return the data needed to generate output
"""
- rom_dir_name = os.path.dirname(get_base_rom_path())
- self.zz_system.make_patcher(rom_dir_name)
- assert self.zz_system.randomizer and self.zz_system.patcher, "generate_early hasn't been called"
- zz_options = self.zz_system.randomizer.options
+
+ assert self.zz_system.randomizer, "generate_early hasn't been called"
# debug_zz_loc_ids: Dict[str, int] = {}
empty = zz_items[4]
multi_item = empty # a different patcher method differentiates empty from ap multi item
multi_items: Dict[str, Tuple[str, str]] = {} # zz_loc_name to (item_name, player_name)
- for loc in self.multiworld.get_locations(self.player):
- z_loc = cast(ZillionLocation, loc)
+ for z_loc in self.multiworld.get_locations(self.player):
+ assert isinstance(z_loc, ZillionLocation)
# debug_zz_loc_ids[z_loc.zz_loc.name] = id(z_loc.zz_loc)
if z_loc.item is None:
self.logger.warn("generate_output location has no item - is that ok?")
z_loc.zz_loc.item = empty
elif z_loc.item.player == self.player:
- z_item = cast(ZillionItem, z_loc.item)
+ z_item = z_loc.item
+ assert isinstance(z_item, ZillionItem)
z_loc.zz_loc.item = z_item.zz_item
else: # another player's item
# print(f"put multi item in {z_loc.zz_loc.name}")
@@ -368,47 +365,32 @@ def finalize_item_locations(self) -> None:
f"in world {self.player} didn't get an item"
)
- zz_patcher = self.zz_system.patcher
-
- 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()
- rm = self.zz_system.resource_managers
- assert rm, "missing resource_managers from generate_early"
- zz_patcher.all_fixes_and_options(zz_options, rm)
- zz_patcher.set_external_item_interface(zz_options.start_char, zz_options.max_level)
- zz_patcher.set_multiworld_items(multi_items)
game_id = self.multiworld.player_name[self.player].encode() + b'\x00' + self.multiworld.seed_name[-6:].encode()
- zz_patcher.set_rom_to_ram_data(game_id)
- def generate_output(self, output_directory: str) -> None:
- """This method gets called from a threadpool, do not use world.random here.
- If you need any last-second randomization, use MultiWorld.per_slot_randoms[slot] instead."""
- self.finalize_item_locations()
+ return GenData(multi_items, self.zz_system.get_game(), game_id)
- assert self.zz_system.patcher, "didn't get patcher from finalize_item_locations"
- # original_rom_bytes = self.zz_patcher.rom
- patched_rom_bytes = self.zz_system.patcher.get_patched_bytes()
+ def generate_output(self, output_directory: str) -> None:
+ """This method gets called from a threadpool, do not use multiworld.random here.
+ If you need any last-second randomization, use self.random instead."""
+ try:
+ gen_data = self.finalize_item_locations()
+ except BaseException:
+ raise
+ finally:
+ self.slot_data_ready.set()
out_file_base = self.multiworld.get_out_file_name_base(self.player)
- filename = os.path.join(
- output_directory,
- f'{out_file_base}{ZillionDeltaPatch.result_file_ending}'
- )
- with open(filename, "wb") as binary_file:
- binary_file.write(patched_rom_bytes)
- patch = ZillionDeltaPatch(
- os.path.splitext(filename)[0] + ZillionDeltaPatch.patch_file_ending,
- player=self.player,
- player_name=self.multiworld.player_name[self.player],
- patched_path=filename
- )
+ patch_file_name = os.path.join(output_directory, f"{out_file_base}{ZillionPatch.patch_file_ending}")
+ patch = ZillionPatch(patch_file_name,
+ player=self.player,
+ player_name=self.multiworld.player_name[self.player],
+ gen_data_str=gen_data.to_json())
patch.write()
- os.remove(filename)
- def fill_slot_data(self) -> Dict[str, Any]: # json of WebHostLib.models.Slot
+ self.logger.debug(f"Zillion player {self.player} finished generate_output")
+
+ def fill_slot_data(self) -> ZillionSlotInfo: # 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."""
@@ -418,25 +400,10 @@ def fill_slot_data(self) -> Dict[str, Any]: # json of WebHostLib.models.Slot
# TODO: tell client which canisters are keywords
# so it can open and get those when restoring doors
- assert self.zz_system.randomizer, "didn't get randomizer from generate_early"
-
- rescues: Dict[str, Any] = {}
self.slot_data_ready.wait()
- zz_patcher = self.zz_system.patcher
- assert zz_patcher, "didn't get patcher from generate_output"
- for i in (0, 1):
- if i in zz_patcher.rescue_locations:
- ri = zz_patcher.rescue_locations[i]
- rescues[str(i)] = {
- "start_char": ri.start_char,
- "room_code": ri.room_code,
- "mask": ri.mask
- }
- return {
- "start_char": self.zz_system.randomizer.options.start_char,
- "rescues": rescues,
- "loc_mem_to_id": zz_patcher.loc_memory_to_loc_id
- }
+ assert self.zz_system.randomizer, "didn't get randomizer from generate_early"
+ game = self.zz_system.get_game()
+ return get_slot_info(game.regions, game.char_order[0], game.loc_name_2_pretty)
# def modify_multidata(self, multidata: Dict[str, Any]) -> None:
# """For deeper modification of server multidata."""
diff --git a/worlds/zillion/client.py b/worlds/zillion/client.py
index b10507aaf885..5c2e11453036 100644
--- a/worlds/zillion/client.py
+++ b/worlds/zillion/client.py
@@ -1,5 +1,7 @@
import asyncio
import base64
+import io
+import pkgutil
import platform
from typing import Any, ClassVar, Coroutine, Dict, List, Optional, Protocol, Tuple, cast
@@ -10,14 +12,13 @@
import colorama
-from zilliandomizer.zri.memory import Memory
+from zilliandomizer.zri.memory import Memory, RescueInfo
from zilliandomizer.zri import events
from zilliandomizer.utils.loc_name_maps import id_to_loc
from zilliandomizer.options import Chars
-from zilliandomizer.patch import RescueInfo
from .id_maps import loc_name_to_id, make_id_to_others
-from .config import base_id, zillion_map
+from .config import base_id
class ZillionCommandProcessor(ClientCommandProcessor):
@@ -138,7 +139,9 @@ def run_gui(self) -> None:
from kvui import GameManager
from kivy.core.text import Label as CoreLabel
from kivy.graphics import Ellipse, Color, Rectangle
+ from kivy.graphics.texture import Texture
from kivy.uix.layout import Layout
+ from kivy.uix.image import CoreImage
from kivy.uix.widget import Widget
class ZillionManager(GameManager):
@@ -150,12 +153,21 @@ class ZillionManager(GameManager):
class MapPanel(Widget):
MAP_WIDTH: ClassVar[int] = 281
- _number_textures: List[Any] = []
+ map_background: CoreImage
+ _number_textures: List[Texture] = []
rooms: List[List[int]] = []
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
+ FILE_NAME = "empty-zillion-map-row-col-labels-281.png"
+ image_file_data = pkgutil.get_data(__name__, FILE_NAME)
+ if not image_file_data:
+ raise FileNotFoundError(f"{__name__=} {FILE_NAME=}")
+ data = io.BytesIO(image_file_data)
+ self.map_background = CoreImage(data, ext="png")
+ assert self.map_background.texture.size[0] == ZillionManager.MapPanel.MAP_WIDTH
+
self.rooms = [[0 for _ in range(8)] for _ in range(16)]
self._make_numbers()
@@ -176,10 +188,9 @@ def update_map(self, *args: Any) -> None:
with self.canvas:
Color(1, 1, 1, 1)
- Rectangle(source=zillion_map,
+ Rectangle(texture=self.map_background.texture,
pos=self.pos,
- size=(ZillionManager.MapPanel.MAP_WIDTH,
- int(ZillionManager.MapPanel.MAP_WIDTH * 1.456))) # aspect ratio of that image
+ size=self.map_background.texture.size)
for y in range(16):
for x in range(8):
num = self.rooms[15 - y][x]
@@ -194,7 +205,7 @@ def update_map(self, *args: Any) -> None:
def build(self) -> Layout:
container = super().build()
- self.map_widget = ZillionManager.MapPanel(size_hint_x=None, width=0)
+ self.map_widget = ZillionManager.MapPanel(size_hint_x=None, width=ZillionManager.MapPanel.MAP_WIDTH)
self.main_area_container.add_widget(self.map_widget)
return container
diff --git a/worlds/zillion/config.py b/worlds/zillion/config.py
index ca02f9a99f41..e08c4f4278ed 100644
--- a/worlds/zillion/config.py
+++ b/worlds/zillion/config.py
@@ -1,4 +1 @@
-import os
-
base_id = 8675309
-zillion_map = os.path.join(os.path.dirname(__file__), "empty-zillion-map-row-col-labels-281.png")
diff --git a/worlds/zillion/docs/en_Zillion.md b/worlds/zillion/docs/en_Zillion.md
index 06a11b7d7993..697a9b7dadbe 100644
--- a/worlds/zillion/docs/en_Zillion.md
+++ b/worlds/zillion/docs/en_Zillion.md
@@ -4,9 +4,9 @@ Zillion is a metroidvania-style game released in 1987 for the 8-bit Sega Master
It's based on the anime Zillion (赤い光弾ジリオン, Akai Koudan Zillion).
-## Where is the settings page?
+## Where is the options page?
-The [player settings page for this game](../player-settings) contains all the options you need to configure and export a config file.
+The [player options page for this game](../player-options) contains all the options you need to configure and export a config file.
## What changes are made to this game?
diff --git a/worlds/zillion/docs/setup_en.md b/worlds/zillion/docs/setup_en.md
index 79f7912dd4fd..c8e29fc36cde 100644
--- a/worlds/zillion/docs/setup_en.md
+++ b/worlds/zillion/docs/setup_en.md
@@ -47,7 +47,7 @@ guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
### Where do I get a config file?
-The [player settings page](/games/Zillion/player-settings) on the website allows you to configure your personal settings and export a config file from
+The [player options page](/games/Zillion/player-options) on the website allows you to configure your personal options and export a config file from
them.
### Verifying your config file
@@ -56,7 +56,7 @@ If you would like to validate your config file to make sure it works, you may do
## Generating a Single-Player Game
-1. Navigate to the [player settings page](/games/Zillion/player-settings), configure your options, and click the "Generate Game" button.
+1. Navigate to the [player options page](/games/Zillion/player-options), configure your options, and click the "Generate Game" button.
2. A "Seed Info" page will appear.
3. Click the "Create New Room" link.
4. A server page will appear. Download your patch file from this page.
diff --git a/worlds/zillion/gen_data.py b/worlds/zillion/gen_data.py
new file mode 100644
index 000000000000..aa24ff8961b3
--- /dev/null
+++ b/worlds/zillion/gen_data.py
@@ -0,0 +1,35 @@
+from dataclasses import dataclass
+import json
+from typing import Dict, Tuple
+
+from zilliandomizer.game import Game as ZzGame
+
+
+@dataclass
+class GenData:
+ """ data passed from generation to patcher """
+
+ multi_items: Dict[str, Tuple[str, str]]
+ """ zz_loc_name to (item_name, player_name) """
+ zz_game: ZzGame
+ game_id: bytes
+ """ the byte string used to detect the rom """
+
+ def to_json(self) -> str:
+ """ serialized data from generation needed to patch rom """
+ jsonable = {
+ "multi_items": self.multi_items,
+ "zz_game": self.zz_game.to_jsonable(),
+ "game_id": list(self.game_id)
+ }
+ return json.dumps(jsonable)
+
+ @staticmethod
+ def from_json(gen_data_str: str) -> "GenData":
+ """ the reverse of `to_json` """
+ from_json = json.loads(gen_data_str)
+ return GenData(
+ from_json["multi_items"],
+ ZzGame.from_jsonable(from_json["zz_game"]),
+ bytes(from_json["game_id"])
+ )
diff --git a/worlds/zillion/id_maps.py b/worlds/zillion/id_maps.py
index bc9caeeece2e..32d71fc79b30 100644
--- a/worlds/zillion/id_maps.py
+++ b/worlds/zillion/id_maps.py
@@ -1,10 +1,22 @@
-from typing import Dict, Tuple
-from zilliandomizer.logic_components.items import Item as ZzItem, \
- item_name_to_id as zz_item_name_to_zz_id, items as zz_items, \
- item_name_to_item as zz_item_name_to_zz_item
+from collections import defaultdict
+from typing import Dict, Iterable, Mapping, Tuple, TypedDict
+
+from zilliandomizer.logic_components.items import (
+ Item as ZzItem,
+ KEYWORD,
+ NORMAL,
+ RESCUE,
+ item_name_to_id as zz_item_name_to_zz_id,
+ items as zz_items,
+ item_name_to_item as zz_item_name_to_zz_item,
+)
+from zilliandomizer.logic_components.regions import RegionData
+from zilliandomizer.low_resources.item_rooms import item_room_codes
from zilliandomizer.options import Chars
from zilliandomizer.utils.loc_name_maps import loc_to_id as pretty_loc_name_to_id
-from zilliandomizer.utils import parse_reg_name
+from zilliandomizer.utils import parse_loc_name, parse_reg_name
+from zilliandomizer.zri.memory import RescueInfo
+
from .config import base_id as base_id
item_name_to_id = {
@@ -91,3 +103,56 @@ def zz_reg_name_to_reg_name(zz_reg_name: str) -> str:
end = zz_reg_name[5:]
return f"{make_room_name(row, col)} {end.upper()}"
return zz_reg_name
+
+
+class ClientRescue(TypedDict):
+ start_char: Chars
+ room_code: int
+ mask: int
+
+
+class ZillionSlotInfo(TypedDict):
+ start_char: Chars
+ rescues: Dict[str, ClientRescue]
+ loc_mem_to_id: Dict[int, int]
+ """ memory location of canister to Archipelago location id number """
+
+
+def get_slot_info(regions: Iterable[RegionData],
+ start_char: Chars,
+ loc_name_to_pretty: Mapping[str, str]) -> ZillionSlotInfo:
+ items_placed_in_map_index: Dict[int, int] = defaultdict(int)
+ rescue_locations: Dict[int, RescueInfo] = {}
+ loc_memory_to_loc_id: Dict[int, int] = {}
+ for region in regions:
+ for loc in region.locations:
+ assert loc.item, ("There should be an item placed in every location before "
+ f"writing slot info. {loc.name} is missing item.")
+ if loc.item.code in {KEYWORD, NORMAL, RESCUE}:
+ row, col, _y, _x = parse_loc_name(loc.name)
+ map_index = row * 8 + col
+ item_no = items_placed_in_map_index[map_index]
+ room_code = item_room_codes[map_index]
+
+ r = room_code
+ m = 1 << item_no
+ if loc.item.code == RESCUE:
+ rescue_locations[loc.item.id] = RescueInfo(start_char, r, m)
+ loc_memory = (r << 7) | m
+ loc_memory_to_loc_id[loc_memory] = pretty_loc_name_to_id[loc_name_to_pretty[loc.name]]
+ items_placed_in_map_index[map_index] += 1
+
+ rescues: Dict[str, ClientRescue] = {}
+ for i in (0, 1):
+ if i in rescue_locations:
+ ri = rescue_locations[i]
+ rescues[str(i)] = {
+ "start_char": ri.start_char,
+ "room_code": ri.room_code,
+ "mask": ri.mask
+ }
+ return {
+ "start_char": start_char,
+ "rescues": rescues,
+ "loc_mem_to_id": loc_memory_to_loc_id
+ }
diff --git a/worlds/zillion/logic.py b/worlds/zillion/logic.py
index 305546c78b62..dcbc6131f1a9 100644
--- a/worlds/zillion/logic.py
+++ b/worlds/zillion/logic.py
@@ -1,9 +1,11 @@
-from typing import Dict, FrozenSet, Tuple, cast, List, Counter as _Counter
+from typing import Dict, FrozenSet, Tuple, List, Counter as _Counter
+
from BaseClasses import CollectionState
+
+from zilliandomizer.logic_components.items import Item, items
from zilliandomizer.logic_components.locations import Location
from zilliandomizer.randomizer import Randomizer
-from zilliandomizer.logic_components.items import Item, items
-from .region import ZillionLocation
+
from .item import ZillionItem
from .id_maps import item_name_to_id
@@ -18,11 +20,12 @@ def set_randomizer_locs(cs: CollectionState, p: int, zz_r: Randomizer) -> int:
returns a hash of the player and of the set locations with their items
"""
+ from . import ZillionWorld
z_world = cs.multiworld.worlds[p]
- my_locations = cast(List[ZillionLocation], getattr(z_world, "my_locations"))
+ assert isinstance(z_world, ZillionWorld)
_hash = p
- for z_loc in my_locations:
+ for z_loc in z_world.my_locations:
zz_name = z_loc.zz_loc.name
zz_item = z_loc.item.zz_item \
if isinstance(z_loc.item, ZillionItem) and z_loc.item.player == p \
diff --git a/worlds/zillion/options.py b/worlds/zillion/options.py
index cb861e962128..97f8b817f77c 100644
--- a/worlds/zillion/options.py
+++ b/worlds/zillion/options.py
@@ -1,13 +1,14 @@
from collections import Counter
from dataclasses import dataclass
-from typing import Dict, Tuple
+from typing import ClassVar, Dict, Tuple
from typing_extensions import TypeGuard # remove when Python >= 3.10
from Options import DefaultOnToggle, NamedRange, PerGameCommonOptions, Range, Toggle, Choice
-from zilliandomizer.options import \
- Options as ZzOptions, char_to_gun, char_to_jump, ID, \
- VBLR as ZzVBLR, chars, Chars, ItemCounts as ZzItemCounts
+from zilliandomizer.options import (
+ Options as ZzOptions, char_to_gun, char_to_jump, ID,
+ VBLR as ZzVBLR, Chars, ItemCounts as ZzItemCounts
+)
from zilliandomizer.options.parsing import validate as zz_validate
@@ -107,6 +108,15 @@ class ZillionStartChar(Choice):
display_name = "start character"
default = "random"
+ _name_capitalization: ClassVar[Dict[int, Chars]] = {
+ option_jj: "JJ",
+ option_apple: "Apple",
+ option_champ: "Champ",
+ }
+
+ def get_char(self) -> Chars:
+ return ZillionStartChar._name_capitalization[self.value]
+
class ZillionIDCardCount(Range):
"""
@@ -348,16 +358,6 @@ def validate(options: ZillionOptions) -> "Tuple[ZzOptions, Counter[str]]":
# that should be all of the level requirements met
- name_capitalization: Dict[str, Chars] = {
- "jj": "JJ",
- "apple": "Apple",
- "champ": "Champ",
- }
-
- start_char = options.start_char
- start_char_name = name_capitalization[start_char.current_key]
- assert start_char_name in chars
-
starting_cards = options.starting_cards
room_gen = options.room_gen
@@ -371,7 +371,7 @@ def validate(options: ZillionOptions) -> "Tuple[ZzOptions, Counter[str]]":
max_level.value,
False, # tutorial
skill,
- start_char_name,
+ options.start_char.get_char(),
floppy_req.value,
options.continues.value,
bool(options.randomize_alarms.value),
diff --git a/worlds/zillion/patch.py b/worlds/zillion/patch.py
index 148caac9fb7b..6bc6d04dd663 100644
--- a/worlds/zillion/patch.py
+++ b/worlds/zillion/patch.py
@@ -1,22 +1,53 @@
-from typing import BinaryIO, Optional, cast
-import Utils
-from worlds.Files import APDeltaPatch
import os
+from typing import Any, BinaryIO, Optional, cast
+import zipfile
+
+from typing_extensions import override
+
+import Utils
+from worlds.Files import APAutoPatchInterface
+
+from zilliandomizer.patch import Patcher
+
+from .gen_data import GenData
USHASH = 'd4bf9e7bcf9a48da53785d2ae7bc4270'
-class ZillionDeltaPatch(APDeltaPatch):
+class ZillionPatch(APAutoPatchInterface):
hash = USHASH
game = "Zillion"
patch_file_ending = ".apzl"
result_file_ending = ".sms"
+ gen_data_str: str
+ """ JSON encoded """
+
+ def __init__(self, *args: Any, gen_data_str: str = "", **kwargs: Any) -> None:
+ super().__init__(*args, **kwargs)
+ self.gen_data_str = gen_data_str
+
@classmethod
def get_source_data(cls) -> bytes:
with open(get_base_rom_path(), "rb") as stream:
return read_rom(stream)
+ @override
+ def write_contents(self, opened_zipfile: zipfile.ZipFile) -> None:
+ super().write_contents(opened_zipfile)
+ opened_zipfile.writestr("gen_data.json",
+ self.gen_data_str,
+ compress_type=zipfile.ZIP_DEFLATED)
+
+ @override
+ def read_contents(self, opened_zipfile: zipfile.ZipFile) -> None:
+ super().read_contents(opened_zipfile)
+ self.gen_data_str = opened_zipfile.read("gen_data.json").decode()
+
+ def patch(self, target: str) -> None:
+ self.read()
+ write_rom_from_gen_data(self.gen_data_str, target)
+
def get_base_rom_path(file_name: Optional[str] = None) -> str:
options = Utils.get_options()
@@ -32,3 +63,21 @@ def read_rom(stream: BinaryIO) -> bytes:
data = stream.read()
# I'm not aware of any sms header.
return data
+
+
+def write_rom_from_gen_data(gen_data_str: str, output_rom_file_name: str) -> None:
+ """ take the output of `GenData.to_json`, and create rom from it """
+ gen_data = GenData.from_json(gen_data_str)
+
+ base_rom_path = get_base_rom_path()
+ zz_patcher = Patcher(base_rom_path)
+
+ zz_patcher.write_locations(gen_data.zz_game.regions, gen_data.zz_game.char_order[0])
+ zz_patcher.all_fixes_and_options(gen_data.zz_game)
+ zz_patcher.set_external_item_interface(gen_data.zz_game.char_order[0], gen_data.zz_game.options.max_level)
+ zz_patcher.set_multiworld_items(gen_data.multi_items)
+ zz_patcher.set_rom_to_ram_data(gen_data.game_id)
+
+ patched_rom_bytes = zz_patcher.get_patched_bytes()
+ with open(output_rom_file_name, "wb") as binary_file:
+ binary_file.write(patched_rom_bytes)
diff --git a/worlds/zillion/requirements.txt b/worlds/zillion/requirements.txt
index c8944925acac..3a784846a891 100644
--- a/worlds/zillion/requirements.txt
+++ b/worlds/zillion/requirements.txt
@@ -1,2 +1,2 @@
-zilliandomizer @ git+https://github.com/beauxq/zilliandomizer@ae00a4b186be897c7cfaf429a0e0ff83c4ecf28c#0.6.0
+zilliandomizer @ git+https://github.com/beauxq/zilliandomizer@b36a23b5a138c78732ac8efb5b5ca8b0be07dcff#0.7.0
typing-extensions>=4.7, <5
diff --git a/worlds/zork_grand_inquisitor/LICENSE b/worlds/zork_grand_inquisitor/LICENSE
new file mode 100644
index 000000000000..a94ca6bf9177
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Serpent.AI
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/worlds/zork_grand_inquisitor/__init__.py b/worlds/zork_grand_inquisitor/__init__.py
new file mode 100644
index 000000000000..4da257e47bd0
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/__init__.py
@@ -0,0 +1,17 @@
+import worlds.LauncherComponents as LauncherComponents
+
+from .world import ZorkGrandInquisitorWorld
+
+
+def launch_client() -> None:
+ from .client import main
+ LauncherComponents.launch_subprocess(main, name="ZorkGrandInquisitorClient")
+
+
+LauncherComponents.components.append(
+ LauncherComponents.Component(
+ "Zork Grand Inquisitor Client",
+ func=launch_client,
+ component_type=LauncherComponents.Type.CLIENT
+ )
+)
diff --git a/worlds/zork_grand_inquisitor/client.py b/worlds/zork_grand_inquisitor/client.py
new file mode 100644
index 000000000000..11d6b7f8f183
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/client.py
@@ -0,0 +1,188 @@
+import asyncio
+
+import CommonClient
+import NetUtils
+import Utils
+
+from typing import Any, Dict, List, Optional, Set, Tuple
+
+from .data_funcs import item_names_to_id, location_names_to_id, id_to_items, id_to_locations, id_to_goals
+from .enums import ZorkGrandInquisitorItems, ZorkGrandInquisitorLocations
+from .game_controller import GameController
+
+
+class ZorkGrandInquisitorCommandProcessor(CommonClient.ClientCommandProcessor):
+ def _cmd_zork(self) -> None:
+ """Attach to an open Zork Grand Inquisitor process."""
+ result: bool = self.ctx.game_controller.open_process_handle()
+
+ if result:
+ self.ctx.process_attached_at_least_once = True
+ self.output("Successfully attached to Zork Grand Inquisitor process.")
+ else:
+ self.output("Failed to attach to Zork Grand Inquisitor process.")
+
+ def _cmd_brog(self) -> None:
+ """List received Brog items."""
+ self.ctx.game_controller.list_received_brog_items()
+
+ def _cmd_griff(self) -> None:
+ """List received Griff items."""
+ self.ctx.game_controller.list_received_griff_items()
+
+ def _cmd_lucy(self) -> None:
+ """List received Lucy items."""
+ self.ctx.game_controller.list_received_lucy_items()
+
+ def _cmd_hotspots(self) -> None:
+ """List received Hotspots."""
+ self.ctx.game_controller.list_received_hotspots()
+
+
+class ZorkGrandInquisitorContext(CommonClient.CommonContext):
+ tags: Set[str] = {"AP"}
+ game: str = "Zork Grand Inquisitor"
+ command_processor: CommonClient.ClientCommandProcessor = ZorkGrandInquisitorCommandProcessor
+ items_handling: int = 0b111
+ want_slot_data: bool = True
+
+ item_name_to_id: Dict[str, int] = item_names_to_id()
+ location_name_to_id: Dict[str, int] = location_names_to_id()
+
+ id_to_items: Dict[int, ZorkGrandInquisitorItems] = id_to_items()
+ id_to_locations: Dict[int, ZorkGrandInquisitorLocations] = id_to_locations()
+
+ game_controller: GameController
+
+ controller_task: Optional[asyncio.Task]
+
+ process_attached_at_least_once: bool
+ can_display_process_message: bool
+
+ def __init__(self, server_address: Optional[str], password: Optional[str]) -> None:
+ super().__init__(server_address, password)
+
+ self.game_controller = GameController(logger=CommonClient.logger)
+
+ self.controller_task = None
+
+ self.process_attached_at_least_once = False
+ self.can_display_process_message = True
+
+ def run_gui(self) -> None:
+ from kvui import GameManager
+
+ class TextManager(GameManager):
+ logging_pairs: List[Tuple[str, str]] = [("Client", "Archipelago")]
+ base_title: str = "Archipelago Zork Grand Inquisitor Client"
+
+ self.ui = TextManager(self)
+ self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI")
+
+ async def server_auth(self, password_requested: bool = False):
+ if password_requested and not self.password:
+ await super().server_auth(password_requested)
+
+ await self.get_username()
+ await self.send_connect()
+
+ def on_package(self, cmd: str, _args: Any) -> None:
+ if cmd == "Connected":
+ self.game = self.slot_info[self.slot].game
+
+ # Options
+ self.game_controller.option_goal = id_to_goals()[_args["slot_data"]["goal"]]
+ self.game_controller.option_deathsanity = _args["slot_data"]["deathsanity"] == 1
+
+ self.game_controller.option_grant_missable_location_checks = (
+ _args["slot_data"]["grant_missable_location_checks"] == 1
+ )
+
+ async def controller(self):
+ while not self.exit_event.is_set():
+ await asyncio.sleep(0.1)
+
+ # Enqueue Received Item Delta
+ network_item: NetUtils.NetworkItem
+ for network_item in self.items_received:
+ item: ZorkGrandInquisitorItems = self.id_to_items[network_item.item]
+
+ if item not in self.game_controller.received_items:
+ if item not in self.game_controller.received_items_queue:
+ self.game_controller.received_items_queue.append(item)
+
+ # Game Controller Update
+ if self.game_controller.is_process_running():
+ self.game_controller.update()
+ self.can_display_process_message = True
+ else:
+ process_message: str
+
+ if self.process_attached_at_least_once:
+ process_message = (
+ "Lost connection to Zork Grand Inquisitor process. Please restart the game and use the /zork "
+ "command to reattach."
+ )
+ else:
+ process_message = (
+ "Please use the /zork command to attach to a running Zork Grand Inquisitor process."
+ )
+
+ if self.can_display_process_message:
+ CommonClient.logger.info(process_message)
+ self.can_display_process_message = False
+
+ # Send Checked Locations
+ checked_location_ids: List[int] = list()
+
+ while len(self.game_controller.completed_locations_queue) > 0:
+ location: ZorkGrandInquisitorLocations = self.game_controller.completed_locations_queue.popleft()
+ location_id: int = self.location_name_to_id[location.value]
+
+ checked_location_ids.append(location_id)
+
+ await self.send_msgs([
+ {
+ "cmd": "LocationChecks",
+ "locations": checked_location_ids
+ }
+ ])
+
+ # Check for Goal Completion
+ if self.game_controller.goal_completed:
+ await self.send_msgs([
+ {
+ "cmd": "StatusUpdate",
+ "status": CommonClient.ClientStatus.CLIENT_GOAL
+ }
+ ])
+
+
+def main() -> None:
+ Utils.init_logging("ZorkGrandInquisitorClient", exception_logger="Client")
+
+ async def _main():
+ ctx: ZorkGrandInquisitorContext = ZorkGrandInquisitorContext(None, None)
+
+ ctx.server_task = asyncio.create_task(CommonClient.server_loop(ctx), name="server loop")
+ ctx.controller_task = asyncio.create_task(ctx.controller(), name="ZorkGrandInquisitorController")
+
+ if CommonClient.gui_enabled:
+ ctx.run_gui()
+
+ ctx.run_cli()
+
+ await ctx.exit_event.wait()
+ await ctx.shutdown()
+
+ import colorama
+
+ colorama.init()
+
+ asyncio.run(_main())
+
+ colorama.deinit()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/worlds/zork_grand_inquisitor/data/__init__.py b/worlds/zork_grand_inquisitor/data/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/worlds/zork_grand_inquisitor/data/entrance_rule_data.py b/worlds/zork_grand_inquisitor/data/entrance_rule_data.py
new file mode 100644
index 000000000000..f48be5eb6b6a
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/data/entrance_rule_data.py
@@ -0,0 +1,419 @@
+from typing import Dict, Tuple, Union
+
+from ..enums import ZorkGrandInquisitorEvents, ZorkGrandInquisitorItems, ZorkGrandInquisitorRegions
+
+
+entrance_rule_data: Dict[
+ Tuple[
+ ZorkGrandInquisitorRegions,
+ ZorkGrandInquisitorRegions,
+ ],
+ Union[
+ Tuple[
+ Tuple[
+ Union[
+ ZorkGrandInquisitorEvents,
+ ZorkGrandInquisitorItems,
+ ZorkGrandInquisitorRegions,
+ ],
+ ...,
+ ],
+ ...,
+ ],
+ None,
+ ],
+] = {
+ (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.DM_LAIR): (
+ (
+ ZorkGrandInquisitorItems.SWORD,
+ ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE,
+ ),
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.GUE_TECH): (
+ (
+ ZorkGrandInquisitorItems.SPELL_REZROV,
+ ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.HADES_SHORE): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.PORT_FOOZLE): None,
+ (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS): (
+ (
+ ZorkGrandInquisitorItems.SUBWAY_TOKEN,
+ ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.CROSSROADS): None,
+ (ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR): (
+ (
+ ZorkGrandInquisitorEvents.DOOR_SMOKED_CIGAR,
+ ZorkGrandInquisitorEvents.DOOR_DRANK_MEAD,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.HADES_SHORE): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, ZorkGrandInquisitorRegions.DM_LAIR): None,
+ (ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, ZorkGrandInquisitorRegions.WALKING_CASTLE): (
+ (
+ ZorkGrandInquisitorItems.HOTSPOT_BLINDS,
+ ZorkGrandInquisitorEvents.KNOWS_OBIDIL,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, ZorkGrandInquisitorRegions.WHITE_HOUSE): (
+ (
+ ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR,
+ ZorkGrandInquisitorItems.SPELL_NARWILE,
+ ZorkGrandInquisitorEvents.KNOWS_YASTARD,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO, ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON): (
+ (
+ ZorkGrandInquisitorItems.TOTEM_GRIFF,
+ ZorkGrandInquisitorItems.HOTSPOT_DRAGON_CLAW,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO, ZorkGrandInquisitorRegions.HADES_BEYOND_GATES): None,
+ (ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON, ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO): None,
+ (ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON, ZorkGrandInquisitorRegions.ENDGAME): (
+ (
+ ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN,
+ ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS,
+ ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH,
+ ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4,
+ ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY,
+ ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS,
+ ZorkGrandInquisitorRegions.WHITE_HOUSE,
+ ZorkGrandInquisitorItems.TOTEM_BROG, # Needed here since White House is not broken down in 2 regions
+ ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH,
+ ZorkGrandInquisitorItems.BROGS_GRUE_EGG,
+ ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT,
+ ZorkGrandInquisitorItems.BROGS_PLANK,
+ ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.GUE_TECH, ZorkGrandInquisitorRegions.CROSSROADS): None,
+ (ZorkGrandInquisitorRegions.GUE_TECH, ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY): (
+ (
+ ZorkGrandInquisitorItems.SPELL_IGRAM,
+ ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.GUE_TECH, ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE): (
+ (ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_DOOR,),
+ ),
+ (ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, ZorkGrandInquisitorRegions.GUE_TECH): None,
+ (ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE): (
+ (
+ ZorkGrandInquisitorItems.STUDENT_ID,
+ ZorkGrandInquisitorItems.HOTSPOT_STUDENT_ID_MACHINE,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, ZorkGrandInquisitorRegions.CROSSROADS): (
+ (ZorkGrandInquisitorItems.MAP,),
+ ),
+ (ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, ZorkGrandInquisitorRegions.DM_LAIR): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, ZorkGrandInquisitorRegions.GUE_TECH): None,
+ (ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, ZorkGrandInquisitorRegions.HADES_SHORE): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.HADES, ZorkGrandInquisitorRegions.HADES_BEYOND_GATES): (
+ (
+ ZorkGrandInquisitorEvents.KNOWS_SNAVIG,
+ ZorkGrandInquisitorItems.TOTEM_BROG, # Visually hiding this totem is tied to owning it; no choice
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.HADES, ZorkGrandInquisitorRegions.HADES_SHORE): (
+ (ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,),
+ ),
+ (ZorkGrandInquisitorRegions.HADES_BEYOND_GATES, ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO): (
+ (
+ ZorkGrandInquisitorItems.SPELL_NARWILE,
+ ZorkGrandInquisitorEvents.KNOWS_YASTARD,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.HADES_BEYOND_GATES, ZorkGrandInquisitorRegions.HADES): None,
+ (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.CROSSROADS): (
+ (ZorkGrandInquisitorItems.MAP,),
+ ),
+ (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.DM_LAIR): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.HADES): (
+ (
+ ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER,
+ ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS,
+ ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS): None,
+ (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM): (
+ (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM,),
+ ),
+ (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): (
+ (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY,),
+ ),
+ (ZorkGrandInquisitorRegions.MENU, ZorkGrandInquisitorRegions.PORT_FOOZLE): None,
+ (ZorkGrandInquisitorRegions.MONASTERY, ZorkGrandInquisitorRegions.HADES_SHORE): (
+ (
+ ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL,
+ ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS,
+ ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.MONASTERY, ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT): (
+ (
+ ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_HALL_OF_INQUISITION,
+ ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS,
+ ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.MONASTERY, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): None,
+ (ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, ZorkGrandInquisitorRegions.MONASTERY): None,
+ (ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST): (
+ (
+ ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER,
+ ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT,
+ ZorkGrandInquisitorItems.LARGE_TELEGRAPH_HAMMER,
+ ZorkGrandInquisitorItems.SPELL_NARWILE,
+ ZorkGrandInquisitorEvents.KNOWS_YASTARD,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.PORT_FOOZLE, ZorkGrandInquisitorRegions.CROSSROADS): (
+ (
+ ZorkGrandInquisitorEvents.LANTERN_DALBOZ_ACCESSIBLE,
+ ZorkGrandInquisitorItems.ROPE,
+ ZorkGrandInquisitorItems.HOTSPOT_WELL,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.PORT_FOOZLE, ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP): (
+ (
+ ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE,
+ ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP, ZorkGrandInquisitorRegions.PORT_FOOZLE): None,
+ (ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST, ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT): None,
+ (ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST, ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN): (
+ (
+ ZorkGrandInquisitorItems.TOTEM_LUCY,
+ ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, ZorkGrandInquisitorRegions.ENDGAME): (
+ (
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4,
+ ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY,
+ ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS,
+ ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON,
+ ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN,
+ ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS,
+ ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH,
+ ZorkGrandInquisitorRegions.WHITE_HOUSE,
+ ZorkGrandInquisitorItems.TOTEM_BROG, # Needed here since White House is not broken down in 2 regions
+ ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH,
+ ZorkGrandInquisitorItems.BROGS_GRUE_EGG,
+ ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT,
+ ZorkGrandInquisitorItems.BROGS_PLANK,
+ ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST): None,
+ (ZorkGrandInquisitorRegions.SPELL_LAB, ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE): None,
+ (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.CROSSROADS): (
+ (ZorkGrandInquisitorItems.MAP,),
+ ),
+ (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.DM_LAIR): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY): None,
+ (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.HADES_SHORE): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.SPELL_LAB): (
+ (
+ ZorkGrandInquisitorItems.SWORD,
+ ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE,
+ ZorkGrandInquisitorEvents.DAM_DESTROYED,
+ ZorkGrandInquisitorItems.SPELL_GOLGATEM,
+ ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): (
+ (
+ ZorkGrandInquisitorItems.MAP,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, ZorkGrandInquisitorRegions.CROSSROADS): None,
+ (ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, ZorkGrandInquisitorRegions.HADES_SHORE): (
+ (
+ ZorkGrandInquisitorItems.SPELL_KENDALL,
+ ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM): (
+ (
+ ZorkGrandInquisitorItems.SPELL_KENDALL,
+ ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): (
+ (
+ ZorkGrandInquisitorItems.SPELL_KENDALL,
+ ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, ZorkGrandInquisitorRegions.HADES_SHORE): (
+ (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES,),
+ ),
+ (ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS): None,
+ (ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): (
+ (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY,),
+ ),
+ (ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, ZorkGrandInquisitorRegions.HADES_SHORE): (
+ (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES,),
+ ),
+ (ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, ZorkGrandInquisitorRegions.MONASTERY): (
+ (
+ ZorkGrandInquisitorItems.SWORD,
+ ZorkGrandInquisitorEvents.ROPE_GLORFABLE,
+ ZorkGrandInquisitorItems.HOTSPOT_MONASTERY_VENT,
+ ),
+ ),
+ (ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS): None,
+ (ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM): (
+ (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM,),
+ ),
+ (ZorkGrandInquisitorRegions.WALKING_CASTLE, ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR): None,
+ (ZorkGrandInquisitorRegions.WHITE_HOUSE, ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR): None,
+ (ZorkGrandInquisitorRegions.WHITE_HOUSE, ZorkGrandInquisitorRegions.ENDGAME): (
+ (
+ ZorkGrandInquisitorItems.TOTEM_BROG, # Needed here since White House is not broken down in 2 regions
+ ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH,
+ ZorkGrandInquisitorItems.BROGS_GRUE_EGG,
+ ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT,
+ ZorkGrandInquisitorItems.BROGS_PLANK,
+ ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE,
+ ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON,
+ ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN,
+ ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS,
+ ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH,
+ ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4,
+ ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY,
+ ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS,
+ ),
+ ),
+}
diff --git a/worlds/zork_grand_inquisitor/data/item_data.py b/worlds/zork_grand_inquisitor/data/item_data.py
new file mode 100644
index 000000000000..c312bbce3d09
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/data/item_data.py
@@ -0,0 +1,792 @@
+from typing import Dict, NamedTuple, Optional, Tuple, Union
+
+from BaseClasses import ItemClassification
+
+from ..enums import ZorkGrandInquisitorItems, ZorkGrandInquisitorTags
+
+
+class ZorkGrandInquisitorItemData(NamedTuple):
+ statemap_keys: Optional[Tuple[int, ...]]
+ archipelago_id: Optional[int]
+ classification: ItemClassification
+ tags: Tuple[ZorkGrandInquisitorTags, ...]
+ maximum_quantity: Optional[int] = 1
+
+
+ITEM_OFFSET = 9758067000
+
+item_data: Dict[ZorkGrandInquisitorItems, ZorkGrandInquisitorItemData] = {
+ # Inventory Items
+ ZorkGrandInquisitorItems.BROGS_BICKERING_TORCH: ZorkGrandInquisitorItemData(
+ statemap_keys=(67,), # Extinguished = 103
+ archipelago_id=ITEM_OFFSET + 0,
+ classification=ItemClassification.filler,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH: ZorkGrandInquisitorItemData(
+ statemap_keys=(68,), # Extinguished = 104
+ archipelago_id=ITEM_OFFSET + 1,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.BROGS_GRUE_EGG: ZorkGrandInquisitorItemData(
+ statemap_keys=(70,), # Boiled = 71
+ archipelago_id=ITEM_OFFSET + 2,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.BROGS_PLANK: ZorkGrandInquisitorItemData(
+ statemap_keys=(69,),
+ archipelago_id=ITEM_OFFSET + 3,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.FLATHEADIA_FUDGE: ZorkGrandInquisitorItemData(
+ statemap_keys=(54,),
+ archipelago_id=ITEM_OFFSET + 4,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP: ZorkGrandInquisitorItemData(
+ statemap_keys=(86,),
+ archipelago_id=ITEM_OFFSET + 5,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH: ZorkGrandInquisitorItemData(
+ statemap_keys=(84,),
+ archipelago_id=ITEM_OFFSET + 6,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT: ZorkGrandInquisitorItemData(
+ statemap_keys=(9,),
+ archipelago_id=ITEM_OFFSET + 7,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN: ZorkGrandInquisitorItemData(
+ statemap_keys=(16,),
+ archipelago_id=ITEM_OFFSET + 8,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.HAMMER: ZorkGrandInquisitorItemData(
+ statemap_keys=(23,),
+ archipelago_id=ITEM_OFFSET + 9,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.HUNGUS_LARD: ZorkGrandInquisitorItemData(
+ statemap_keys=(55,),
+ archipelago_id=ITEM_OFFSET + 10,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.JAR_OF_HOTBUGS: ZorkGrandInquisitorItemData(
+ statemap_keys=(56,),
+ archipelago_id=ITEM_OFFSET + 11,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.LANTERN: ZorkGrandInquisitorItemData(
+ statemap_keys=(4,),
+ archipelago_id=ITEM_OFFSET + 12,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.LARGE_TELEGRAPH_HAMMER: ZorkGrandInquisitorItemData(
+ statemap_keys=(88,),
+ archipelago_id=ITEM_OFFSET + 13,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1: ZorkGrandInquisitorItemData(
+ statemap_keys=(116,), # With fly = 120
+ archipelago_id=ITEM_OFFSET + 14,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2: ZorkGrandInquisitorItemData(
+ statemap_keys=(117,), # With fly = 121
+ archipelago_id=ITEM_OFFSET + 15,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3: ZorkGrandInquisitorItemData(
+ statemap_keys=(118,), # With fly = 122
+ archipelago_id=ITEM_OFFSET + 16,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4: ZorkGrandInquisitorItemData(
+ statemap_keys=(119,), # With fly = 123
+ archipelago_id=ITEM_OFFSET + 17,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.MAP: ZorkGrandInquisitorItemData(
+ statemap_keys=(6,),
+ archipelago_id=ITEM_OFFSET + 18,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.MEAD_LIGHT: ZorkGrandInquisitorItemData(
+ statemap_keys=(2,),
+ archipelago_id=ITEM_OFFSET + 19,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.MOSS_OF_MAREILON: ZorkGrandInquisitorItemData(
+ statemap_keys=(57,),
+ archipelago_id=ITEM_OFFSET + 20,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.MUG: ZorkGrandInquisitorItemData(
+ statemap_keys=(35,),
+ archipelago_id=ITEM_OFFSET + 21,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.OLD_SCRATCH_CARD: ZorkGrandInquisitorItemData(
+ statemap_keys=(17,),
+ archipelago_id=ITEM_OFFSET + 22,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.PERMA_SUCK_MACHINE: ZorkGrandInquisitorItemData(
+ statemap_keys=(36,),
+ archipelago_id=ITEM_OFFSET + 23,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.PLASTIC_SIX_PACK_HOLDER: ZorkGrandInquisitorItemData(
+ statemap_keys=(3,),
+ archipelago_id=ITEM_OFFSET + 24,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS: ZorkGrandInquisitorItemData(
+ statemap_keys=(5827,),
+ archipelago_id=ITEM_OFFSET + 25,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.PROZORK_TABLET: ZorkGrandInquisitorItemData(
+ statemap_keys=(65,),
+ archipelago_id=ITEM_OFFSET + 26,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.QUELBEE_HONEYCOMB: ZorkGrandInquisitorItemData(
+ statemap_keys=(53,),
+ archipelago_id=ITEM_OFFSET + 27,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.ROPE: ZorkGrandInquisitorItemData(
+ statemap_keys=(83,),
+ archipelago_id=ITEM_OFFSET + 28,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.SCROLL_FRAGMENT_ANS: ZorkGrandInquisitorItemData(
+ statemap_keys=(101,), # SNA = 41
+ archipelago_id=ITEM_OFFSET + 29,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.SCROLL_FRAGMENT_GIV: ZorkGrandInquisitorItemData(
+ statemap_keys=(102,), # VIG = 48
+ archipelago_id=ITEM_OFFSET + 30,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.SHOVEL: ZorkGrandInquisitorItemData(
+ statemap_keys=(49,),
+ archipelago_id=ITEM_OFFSET + 31,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.SNAPDRAGON: ZorkGrandInquisitorItemData(
+ statemap_keys=(50,),
+ archipelago_id=ITEM_OFFSET + 32,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.STUDENT_ID: ZorkGrandInquisitorItemData(
+ statemap_keys=(39,),
+ archipelago_id=ITEM_OFFSET + 33,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.SUBWAY_TOKEN: ZorkGrandInquisitorItemData(
+ statemap_keys=(20,),
+ archipelago_id=ITEM_OFFSET + 34,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.SWORD: ZorkGrandInquisitorItemData(
+ statemap_keys=(21,),
+ archipelago_id=ITEM_OFFSET + 35,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.ZIMDOR_SCROLL: ZorkGrandInquisitorItemData(
+ statemap_keys=(25,),
+ archipelago_id=ITEM_OFFSET + 36,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ ZorkGrandInquisitorItems.ZORK_ROCKS: ZorkGrandInquisitorItemData(
+ statemap_keys=(37,),
+ archipelago_id=ITEM_OFFSET + 37,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,),
+ ),
+ # Hotspots
+ ZorkGrandInquisitorItems.HOTSPOT_666_MAILBOX: ZorkGrandInquisitorItemData(
+ statemap_keys=(9116,),
+ archipelago_id=ITEM_OFFSET + 100 + 0,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS: ZorkGrandInquisitorItemData(
+ statemap_keys=(15434, 15436, 15438, 15440),
+ archipelago_id=ITEM_OFFSET + 100 + 1,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_BLANK_SCROLL_BOX: ZorkGrandInquisitorItemData(
+ statemap_keys=(12096,),
+ archipelago_id=ITEM_OFFSET + 100 + 2,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_BLINDS: ZorkGrandInquisitorItemData(
+ statemap_keys=(4799,),
+ archipelago_id=ITEM_OFFSET + 100 + 3,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS: ZorkGrandInquisitorItemData(
+ statemap_keys=(12691, 12692, 12693, 12694, 12695, 12696, 12697, 12698, 12699, 12700, 12701),
+ archipelago_id=ITEM_OFFSET + 100 + 4,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT: ZorkGrandInquisitorItemData(
+ statemap_keys=(12702,),
+ archipelago_id=ITEM_OFFSET + 100 + 5,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_VACUUM_SLOT: ZorkGrandInquisitorItemData(
+ statemap_keys=(12909,),
+ archipelago_id=ITEM_OFFSET + 100 + 6,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_CHANGE_MACHINE_SLOT: ZorkGrandInquisitorItemData(
+ statemap_keys=(12900,),
+ archipelago_id=ITEM_OFFSET + 100 + 7,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR: ZorkGrandInquisitorItemData(
+ statemap_keys=(5010,),
+ archipelago_id=ITEM_OFFSET + 100 + 8,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT: ZorkGrandInquisitorItemData(
+ statemap_keys=(9539,),
+ archipelago_id=ITEM_OFFSET + 100 + 9,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER: ZorkGrandInquisitorItemData(
+ statemap_keys=(19712,),
+ archipelago_id=ITEM_OFFSET + 100 + 10,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT: ZorkGrandInquisitorItemData(
+ statemap_keys=(2586,),
+ archipelago_id=ITEM_OFFSET + 100 + 11,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_DENTED_LOCKER: ZorkGrandInquisitorItemData(
+ statemap_keys=(11878,),
+ archipelago_id=ITEM_OFFSET + 100 + 12,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_DIRT_MOUND: ZorkGrandInquisitorItemData(
+ statemap_keys=(11751,),
+ archipelago_id=ITEM_OFFSET + 100 + 13,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH: ZorkGrandInquisitorItemData(
+ statemap_keys=(15147, 15153),
+ archipelago_id=ITEM_OFFSET + 100 + 14,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_DRAGON_CLAW: ZorkGrandInquisitorItemData(
+ statemap_keys=(1705,),
+ archipelago_id=ITEM_OFFSET + 100 + 15,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS: ZorkGrandInquisitorItemData(
+ statemap_keys=(1425, 1426),
+ archipelago_id=ITEM_OFFSET + 100 + 16,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE: ZorkGrandInquisitorItemData(
+ statemap_keys=(13106,),
+ archipelago_id=ITEM_OFFSET + 100 + 17,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS: ZorkGrandInquisitorItemData(
+ statemap_keys=(13219, 13220, 13221, 13222),
+ archipelago_id=ITEM_OFFSET + 100 + 18,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS: ZorkGrandInquisitorItemData(
+ statemap_keys=(14327, 14332, 14337, 14342),
+ archipelago_id=ITEM_OFFSET + 100 + 19,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_COIN_SLOT: ZorkGrandInquisitorItemData(
+ statemap_keys=(12528,),
+ archipelago_id=ITEM_OFFSET + 100 + 20,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_DOORS: ZorkGrandInquisitorItemData(
+ statemap_keys=(12523, 12524, 12525),
+ archipelago_id=ITEM_OFFSET + 100 + 21,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_GLASS_CASE: ZorkGrandInquisitorItemData(
+ statemap_keys=(13002,),
+ archipelago_id=ITEM_OFFSET + 100 + 22,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL: ZorkGrandInquisitorItemData(
+ statemap_keys=(10726,),
+ archipelago_id=ITEM_OFFSET + 100 + 23,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_DOOR: ZorkGrandInquisitorItemData(
+ statemap_keys=(12280,),
+ archipelago_id=ITEM_OFFSET + 100 + 24,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_GRASS: ZorkGrandInquisitorItemData(
+ statemap_keys=(
+ 17694,
+ 17695,
+ 17696,
+ 17697,
+ 18200,
+ 17703,
+ 17704,
+ 17705,
+ 17710,
+ 17711,
+ 17712,
+ 17713,
+ 17714,
+ 17715,
+ 17716,
+ 17722,
+ 17723,
+ 17724,
+ 17725,
+ 17726,
+ 17727
+ ),
+ archipelago_id=ITEM_OFFSET + 100 + 25,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS: ZorkGrandInquisitorItemData(
+ statemap_keys=(8448, 8449, 8450, 8451, 8452, 8453, 8454, 8455, 8456, 8457, 8458, 8459),
+ archipelago_id=ITEM_OFFSET + 100 + 26,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER: ZorkGrandInquisitorItemData(
+ statemap_keys=(8446,),
+ archipelago_id=ITEM_OFFSET + 100 + 27,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_HARRY: ZorkGrandInquisitorItemData(
+ statemap_keys=(4260,),
+ archipelago_id=ITEM_OFFSET + 100 + 28,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY: ZorkGrandInquisitorItemData(
+ statemap_keys=(18026,),
+ archipelago_id=ITEM_OFFSET + 100 + 29,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH: ZorkGrandInquisitorItemData(
+ statemap_keys=(17623,),
+ archipelago_id=ITEM_OFFSET + 100 + 30,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR: ZorkGrandInquisitorItemData(
+ statemap_keys=(13140,),
+ archipelago_id=ITEM_OFFSET + 100 + 31,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR: ZorkGrandInquisitorItemData(
+ statemap_keys=(10441,),
+ archipelago_id=ITEM_OFFSET + 100 + 32,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_LOUDSPEAKER_VOLUME_BUTTONS: ZorkGrandInquisitorItemData(
+ statemap_keys=(19632, 19627),
+ archipelago_id=ITEM_OFFSET + 100 + 33,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_DOOR: ZorkGrandInquisitorItemData(
+ statemap_keys=(3025,),
+ archipelago_id=ITEM_OFFSET + 100 + 34,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG: ZorkGrandInquisitorItemData(
+ statemap_keys=(3036,),
+ archipelago_id=ITEM_OFFSET + 100 + 35,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_MIRROR: ZorkGrandInquisitorItemData(
+ statemap_keys=(5031,),
+ archipelago_id=ITEM_OFFSET + 100 + 36,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_MONASTERY_VENT: ZorkGrandInquisitorItemData(
+ statemap_keys=(13597,),
+ archipelago_id=ITEM_OFFSET + 100 + 37,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_MOSSY_GRATE: ZorkGrandInquisitorItemData(
+ statemap_keys=(13390,),
+ archipelago_id=ITEM_OFFSET + 100 + 38,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR: ZorkGrandInquisitorItemData(
+ statemap_keys=(2455, 2447),
+ archipelago_id=ITEM_OFFSET + 100 + 39,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS: ZorkGrandInquisitorItemData(
+ statemap_keys=(12389, 12390),
+ archipelago_id=ITEM_OFFSET + 100 + 40,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE: ZorkGrandInquisitorItemData(
+ statemap_keys=(4302,),
+ archipelago_id=ITEM_OFFSET + 100 + 41,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE: ZorkGrandInquisitorItemData(
+ statemap_keys=(16383, 16384),
+ archipelago_id=ITEM_OFFSET + 100 + 42,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE: ZorkGrandInquisitorItemData(
+ statemap_keys=(2769,),
+ archipelago_id=ITEM_OFFSET + 100 + 43,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON: ZorkGrandInquisitorItemData(
+ statemap_keys=(4149,),
+ archipelago_id=ITEM_OFFSET + 100 + 44,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_BUTTONS: ZorkGrandInquisitorItemData(
+ statemap_keys=(12584, 12585, 12586, 12587),
+ archipelago_id=ITEM_OFFSET + 100 + 45,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_COIN_SLOT: ZorkGrandInquisitorItemData(
+ statemap_keys=(12574,),
+ archipelago_id=ITEM_OFFSET + 100 + 46,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_SOUVENIR_COIN_SLOT: ZorkGrandInquisitorItemData(
+ statemap_keys=(13412,),
+ archipelago_id=ITEM_OFFSET + 100 + 47,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER: ZorkGrandInquisitorItemData(
+ statemap_keys=(12170,),
+ archipelago_id=ITEM_OFFSET + 100 + 48,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM: ZorkGrandInquisitorItemData(
+ statemap_keys=(16382,),
+ archipelago_id=ITEM_OFFSET + 100 + 49,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM: ZorkGrandInquisitorItemData(
+ statemap_keys=(4209,),
+ archipelago_id=ITEM_OFFSET + 100 + 50,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_STUDENT_ID_MACHINE: ZorkGrandInquisitorItemData(
+ statemap_keys=(11973,),
+ archipelago_id=ITEM_OFFSET + 100 + 51,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT: ZorkGrandInquisitorItemData(
+ statemap_keys=(13168,),
+ archipelago_id=ITEM_OFFSET + 100 + 52,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY: ZorkGrandInquisitorItemData(
+ statemap_keys=(15396,),
+ archipelago_id=ITEM_OFFSET + 100 + 53,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH: ZorkGrandInquisitorItemData(
+ statemap_keys=(9706,),
+ archipelago_id=ITEM_OFFSET + 100 + 54,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS: ZorkGrandInquisitorItemData(
+ statemap_keys=(9728, 9729, 9730),
+ archipelago_id=ITEM_OFFSET + 100 + 55,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ ZorkGrandInquisitorItems.HOTSPOT_WELL: ZorkGrandInquisitorItemData(
+ statemap_keys=(10314,),
+ archipelago_id=ITEM_OFFSET + 100 + 56,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.HOTSPOT,),
+ ),
+ # Spells
+ ZorkGrandInquisitorItems.SPELL_GLORF: ZorkGrandInquisitorItemData(
+ statemap_keys=(202,),
+ archipelago_id=ITEM_OFFSET + 200 + 0,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.SPELL,),
+ ),
+ ZorkGrandInquisitorItems.SPELL_GOLGATEM: ZorkGrandInquisitorItemData(
+ statemap_keys=(192,),
+ archipelago_id=ITEM_OFFSET + 200 + 1,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.SPELL,),
+ ),
+ ZorkGrandInquisitorItems.SPELL_IGRAM: ZorkGrandInquisitorItemData(
+ statemap_keys=(199,),
+ archipelago_id=ITEM_OFFSET + 200 + 2,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.SPELL,),
+ ),
+ ZorkGrandInquisitorItems.SPELL_KENDALL: ZorkGrandInquisitorItemData(
+ statemap_keys=(196,),
+ archipelago_id=ITEM_OFFSET + 200 + 3,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.SPELL,),
+ ),
+ ZorkGrandInquisitorItems.SPELL_NARWILE: ZorkGrandInquisitorItemData(
+ statemap_keys=(197,),
+ archipelago_id=ITEM_OFFSET + 200 + 4,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.SPELL,),
+ ),
+ ZorkGrandInquisitorItems.SPELL_REZROV: ZorkGrandInquisitorItemData(
+ statemap_keys=(195,),
+ archipelago_id=ITEM_OFFSET + 200 + 5,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.SPELL,),
+ ),
+ ZorkGrandInquisitorItems.SPELL_THROCK: ZorkGrandInquisitorItemData(
+ statemap_keys=(200,),
+ archipelago_id=ITEM_OFFSET + 200 + 6,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.SPELL,),
+ ),
+ ZorkGrandInquisitorItems.SPELL_VOXAM: ZorkGrandInquisitorItemData(
+ statemap_keys=(191,),
+ archipelago_id=ITEM_OFFSET + 200 + 7,
+ classification=ItemClassification.useful,
+ tags=(ZorkGrandInquisitorTags.SPELL,),
+ ),
+ # Subway Destinations
+ ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM: ZorkGrandInquisitorItemData(
+ statemap_keys=(13757, 13297, 13486, 13625),
+ archipelago_id=ITEM_OFFSET + 300 + 0,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.SUBWAY_DESTINATION,),
+ ),
+ ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES: ZorkGrandInquisitorItemData(
+ statemap_keys=(13758, 13309, 13498, 13637),
+ archipelago_id=ITEM_OFFSET + 300 + 1,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.SUBWAY_DESTINATION,),
+ ),
+ ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY: ZorkGrandInquisitorItemData(
+ statemap_keys=(13759, 13316, 13505, 13644),
+ archipelago_id=ITEM_OFFSET + 300 + 2,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.SUBWAY_DESTINATION,),
+ ),
+ # Teleporter Destinations
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR: ZorkGrandInquisitorItemData(
+ statemap_keys=(2203,),
+ archipelago_id=ITEM_OFFSET + 400 + 0,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.TELEPORTER_DESTINATION,),
+ ),
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH: ZorkGrandInquisitorItemData(
+ statemap_keys=(7132,),
+ archipelago_id=ITEM_OFFSET + 400 + 1,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.TELEPORTER_DESTINATION,),
+ ),
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES: ZorkGrandInquisitorItemData(
+ statemap_keys=(7119,),
+ archipelago_id=ITEM_OFFSET + 400 + 2,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.TELEPORTER_DESTINATION,),
+ ),
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY: ZorkGrandInquisitorItemData(
+ statemap_keys=(7148,),
+ archipelago_id=ITEM_OFFSET + 400 + 3,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.TELEPORTER_DESTINATION,),
+ ),
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB: ZorkGrandInquisitorItemData(
+ statemap_keys=(16545,),
+ archipelago_id=ITEM_OFFSET + 400 + 4,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.TELEPORTER_DESTINATION,),
+ ),
+ # Totemizer Destinations
+ ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_HALL_OF_INQUISITION: ZorkGrandInquisitorItemData(
+ statemap_keys=(9660,),
+ archipelago_id=ITEM_OFFSET + 500 + 0,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.TOTEMIZER_DESTINATION,),
+ ),
+ ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_INFINITY: ZorkGrandInquisitorItemData(
+ statemap_keys=(9666,),
+ archipelago_id=ITEM_OFFSET + 500 + 1,
+ classification=ItemClassification.filler,
+ tags=(ZorkGrandInquisitorTags.TOTEMIZER_DESTINATION,),
+ ),
+ ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL: ZorkGrandInquisitorItemData(
+ statemap_keys=(9668,),
+ archipelago_id=ITEM_OFFSET + 500 + 2,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.TOTEMIZER_DESTINATION,),
+ ),
+ ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_SURFACE_OF_MERZ: ZorkGrandInquisitorItemData(
+ statemap_keys=(9662,),
+ archipelago_id=ITEM_OFFSET + 500 + 3,
+ classification=ItemClassification.filler,
+ tags=(ZorkGrandInquisitorTags.TOTEMIZER_DESTINATION,),
+ ),
+ # Totems
+ ZorkGrandInquisitorItems.TOTEM_BROG: ZorkGrandInquisitorItemData(
+ statemap_keys=(4853,),
+ archipelago_id=ITEM_OFFSET + 600 + 0,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.TOTEM,),
+ ),
+ ZorkGrandInquisitorItems.TOTEM_GRIFF: ZorkGrandInquisitorItemData(
+ statemap_keys=(4315,),
+ archipelago_id=ITEM_OFFSET + 600 + 1,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.TOTEM,),
+ ),
+ ZorkGrandInquisitorItems.TOTEM_LUCY: ZorkGrandInquisitorItemData(
+ statemap_keys=(5223,),
+ archipelago_id=ITEM_OFFSET + 600 + 2,
+ classification=ItemClassification.progression,
+ tags=(ZorkGrandInquisitorTags.TOTEM,),
+ ),
+ # Filler
+ ZorkGrandInquisitorItems.FILLER_INQUISITION_PROPAGANDA_FLYER: ZorkGrandInquisitorItemData(
+ statemap_keys=None,
+ archipelago_id=ITEM_OFFSET + 700 + 0,
+ classification=ItemClassification.filler,
+ tags=(ZorkGrandInquisitorTags.FILLER,),
+ maximum_quantity=None,
+ ),
+ ZorkGrandInquisitorItems.FILLER_UNREADABLE_SPELL_SCROLL: ZorkGrandInquisitorItemData(
+ statemap_keys=None,
+ archipelago_id=ITEM_OFFSET + 700 + 1,
+ classification=ItemClassification.filler,
+ tags=(ZorkGrandInquisitorTags.FILLER,),
+ maximum_quantity=None,
+ ),
+ ZorkGrandInquisitorItems.FILLER_MAGIC_CONTRABAND: ZorkGrandInquisitorItemData(
+ statemap_keys=None,
+ archipelago_id=ITEM_OFFSET + 700 + 2,
+ classification=ItemClassification.filler,
+ tags=(ZorkGrandInquisitorTags.FILLER,),
+ maximum_quantity=None,
+ ),
+ ZorkGrandInquisitorItems.FILLER_FROBOZZ_ELECTRIC_GADGET: ZorkGrandInquisitorItemData(
+ statemap_keys=None,
+ archipelago_id=ITEM_OFFSET + 700 + 3,
+ classification=ItemClassification.filler,
+ tags=(ZorkGrandInquisitorTags.FILLER,),
+ maximum_quantity=None,
+ ),
+ ZorkGrandInquisitorItems.FILLER_NONSENSICAL_INQUISITION_PAPERWORK: ZorkGrandInquisitorItemData(
+ statemap_keys=None,
+ archipelago_id=ITEM_OFFSET + 700 + 4,
+ classification=ItemClassification.filler,
+ tags=(ZorkGrandInquisitorTags.FILLER,),
+ maximum_quantity=None,
+ ),
+}
diff --git a/worlds/zork_grand_inquisitor/data/location_data.py b/worlds/zork_grand_inquisitor/data/location_data.py
new file mode 100644
index 000000000000..8b4e57392de8
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/data/location_data.py
@@ -0,0 +1,1535 @@
+from typing import Dict, NamedTuple, Optional, Tuple, Union
+
+from ..enums import (
+ ZorkGrandInquisitorEvents,
+ ZorkGrandInquisitorItems,
+ ZorkGrandInquisitorLocations,
+ ZorkGrandInquisitorRegions,
+ ZorkGrandInquisitorTags,
+)
+
+
+class ZorkGrandInquisitorLocationData(NamedTuple):
+ game_state_trigger: Optional[
+ Tuple[
+ Union[
+ Tuple[str, str],
+ Tuple[int, int],
+ Tuple[int, Tuple[int, ...]],
+ ],
+ ...,
+ ]
+ ]
+ archipelago_id: Optional[int]
+ region: ZorkGrandInquisitorRegions
+ tags: Optional[Tuple[ZorkGrandInquisitorTags, ...]] = None
+ requirements: Optional[
+ Tuple[
+ Union[
+ Union[
+ ZorkGrandInquisitorItems,
+ ZorkGrandInquisitorEvents,
+ ],
+ Tuple[
+ Union[
+ ZorkGrandInquisitorItems,
+ ZorkGrandInquisitorEvents,
+ ],
+ ...,
+ ],
+ ],
+ ...,
+ ]
+ ] = None
+ event_item_name: Optional[str] = None
+
+
+LOCATION_OFFSET = 9758067000
+
+location_data: Dict[
+ Union[ZorkGrandInquisitorLocations, ZorkGrandInquisitorEvents], ZorkGrandInquisitorLocationData
+] = {
+ ZorkGrandInquisitorLocations.ALARM_SYSTEM_IS_DOWN: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "tr2m"),),
+ archipelago_id=LOCATION_OFFSET + 0,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.ARREST_THE_VANDAL: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((10789, 1),),
+ archipelago_id=LOCATION_OFFSET + 1,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE,
+ ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.ARTIFACTS_EXPLAINED: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((11787, 1), (11788, 1), (11789, 1)),
+ archipelago_id=LOCATION_OFFSET + 2,
+ region=ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.A_BIG_FAT_SASSY_2_HEADED_MONSTER: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((8929, 1),),
+ archipelago_id=LOCATION_OFFSET + 3,
+ region=ZorkGrandInquisitorRegions.HADES,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorEvents.KNOWS_OBIDIL,),
+ ),
+ ZorkGrandInquisitorLocations.A_LETTER_FROM_THE_WHITE_HOUSE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((9124, 1),),
+ archipelago_id=LOCATION_OFFSET + 4,
+ region=ZorkGrandInquisitorRegions.HADES,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorEvents.WHITE_HOUSE_LETTER_MAILABLE,
+ ZorkGrandInquisitorItems.HOTSPOT_666_MAILBOX,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.A_SMALLWAY: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((11777, 1),),
+ archipelago_id=LOCATION_OFFSET + 5,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS,
+ ZorkGrandInquisitorItems.SPELL_IGRAM,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.BEAUTIFUL_THATS_PLENTY: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((13278, 1),),
+ archipelago_id=LOCATION_OFFSET + 6,
+ region=ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.HOTSPOT_MOSSY_GRATE,
+ ZorkGrandInquisitorItems.SPELL_THROCK,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.BEBURTT_DEMYSTIFIED: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((16315, 1),),
+ archipelago_id=LOCATION_OFFSET + 7,
+ region=ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorEvents.DUNCE_LOCKER_OPENABLE,
+ ZorkGrandInquisitorItems.SPELL_KENDALL,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.BETTER_SPELL_MANUFACTURING_IN_UNDER_10_MINUTES: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "th3x"),),
+ archipelago_id=LOCATION_OFFSET + 8,
+ region=ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorEvents.DUNCE_LOCKER_OPENABLE,),
+ ),
+ ZorkGrandInquisitorLocations.BOING_BOING_BOING: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4220, 1),),
+ archipelago_id=LOCATION_OFFSET + 9,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorItems.HAMMER,
+ ZorkGrandInquisitorItems.SNAPDRAGON,
+ ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.BONK: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((19491, 1),),
+ archipelago_id=LOCATION_OFFSET + 10,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorItems.HAMMER,
+ ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.BRAVE_SOULS_WANTED: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "us2g"),),
+ archipelago_id=LOCATION_OFFSET + 11,
+ region=ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.BROG_DO_GOOD: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((2644, 1),),
+ archipelago_id=LOCATION_OFFSET + 12,
+ region=ZorkGrandInquisitorRegions.WHITE_HOUSE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.TOTEM_BROG,
+ ZorkGrandInquisitorItems.BROGS_GRUE_EGG,
+ ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT,
+ ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH,
+ )
+ ),
+ ZorkGrandInquisitorLocations.BROG_EAT_ROCKS: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((2629, 1),),
+ archipelago_id=LOCATION_OFFSET + 13,
+ region=ZorkGrandInquisitorRegions.WHITE_HOUSE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.TOTEM_BROG,
+ ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH,
+ )
+ ),
+ ZorkGrandInquisitorLocations.BROG_KNOW_DUMB_THAT_DUMB: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((2650, 1),),
+ archipelago_id=LOCATION_OFFSET + 14,
+ region=ZorkGrandInquisitorRegions.WHITE_HOUSE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.TOTEM_BROG,
+ ZorkGrandInquisitorItems.BROGS_GRUE_EGG,
+ ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH,
+ )
+ ),
+ ZorkGrandInquisitorLocations.BROG_MUCH_BETTER_AT_THIS_GAME: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((15715, 1),),
+ archipelago_id=LOCATION_OFFSET + 15,
+ region=ZorkGrandInquisitorRegions.WHITE_HOUSE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.TOTEM_BROG,
+ ZorkGrandInquisitorItems.BROGS_GRUE_EGG,
+ ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT,
+ ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH,
+ ZorkGrandInquisitorItems.BROGS_PLANK,
+ ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE,
+ )
+ ),
+ ZorkGrandInquisitorLocations.CASTLE_WATCHING_A_FIELD_GUIDE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "dv1t"),),
+ archipelago_id=LOCATION_OFFSET + 16,
+ region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.CAVES_NOTES: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "th3y"),),
+ archipelago_id=LOCATION_OFFSET + 17,
+ region=ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorEvents.DUNCE_LOCKER_OPENABLE,),
+ ),
+ ZorkGrandInquisitorLocations.CLOSING_THE_TIME_TUNNELS: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((9543, 1),),
+ archipelago_id=LOCATION_OFFSET + 18,
+ region=ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.CRISIS_AVERTED: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((11769, 1),),
+ archipelago_id=LOCATION_OFFSET + 19,
+ region=ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorEvents.ZORK_ROCKS_ACTIVATED,
+ ZorkGrandInquisitorItems.SPELL_IGRAM,
+ ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS,
+ ZorkGrandInquisitorItems.HOTSPOT_DENTED_LOCKER,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.CUT_THAT_OUT_YOU_LITTLE_CREEP: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((19350, 1),),
+ archipelago_id=LOCATION_OFFSET + 20,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.DENIED_BY_THE_LAKE_MONSTER: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((17632, 1),),
+ archipelago_id=LOCATION_OFFSET + 21,
+ region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorItems.HOTSPOT_BLINDS,
+ ZorkGrandInquisitorItems.SPELL_GOLGATEM,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.DESPERATELY_SEEKING_TUTOR: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "tr2q"),),
+ archipelago_id=LOCATION_OFFSET + 22,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.DONT_EVEN_START_WITH_US_SPARKY: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "hp5e"), (8919, 2), (9, 100)),
+ archipelago_id=LOCATION_OFFSET + 23,
+ region=ZorkGrandInquisitorRegions.HADES,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorItems.SWORD,),
+ ),
+ ZorkGrandInquisitorLocations.DOOOOOOWN: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((3619, 3600),),
+ archipelago_id=LOCATION_OFFSET + 24,
+ region=ZorkGrandInquisitorRegions.WHITE_HOUSE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.TOTEM_GRIFF,
+ ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.DOWN: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((3619, 5300),),
+ archipelago_id=LOCATION_OFFSET + 25,
+ region=ZorkGrandInquisitorRegions.WHITE_HOUSE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.TOTEM_LUCY,
+ ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.DRAGON_ARCHIPELAGO_TIME_TUNNEL: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((9216, 1),),
+ archipelago_id=LOCATION_OFFSET + 26,
+ region=ZorkGrandInquisitorRegions.HADES_BEYOND_GATES,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorItems.SPELL_NARWILE,),
+ ),
+ ZorkGrandInquisitorLocations.DUNCE_LOCKER: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((11851, 1),),
+ archipelago_id=LOCATION_OFFSET + 27,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,
+ ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT,
+ ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.EGGPLANTS: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((3816, 11000),),
+ archipelago_id=LOCATION_OFFSET + 28,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.ELSEWHERE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "pc1e"),),
+ archipelago_id=LOCATION_OFFSET + 29,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.EMERGENCY_MAGICATRONIC_MESSAGE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((11784, 1),),
+ archipelago_id=LOCATION_OFFSET + 30,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ ),
+ ZorkGrandInquisitorLocations.ENJOY_YOUR_TRIP: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((13743, 1),),
+ archipelago_id=LOCATION_OFFSET + 31,
+ region=ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorItems.SPELL_KENDALL,),
+ ),
+ ZorkGrandInquisitorLocations.FAT_LOT_OF_GOOD_THATLL_DO_YA: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((16368, 1),),
+ archipelago_id=LOCATION_OFFSET + 32,
+ region=ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(ZorkGrandInquisitorItems.SPELL_IGRAM,),
+ ),
+ ZorkGrandInquisitorLocations.FIRE_FIRE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((10277, 1),),
+ archipelago_id=LOCATION_OFFSET + 33,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE,
+ ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.FLOOD_CONTROL_DAM_3_THE_NOT_REMOTELY_BORING_TALE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "ue1h"),),
+ archipelago_id=LOCATION_OFFSET + 34,
+ region=ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.FLYING_SNAPDRAGON: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4222, 1),),
+ archipelago_id=LOCATION_OFFSET + 35,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.SPELL_THROCK,
+ ZorkGrandInquisitorItems.SNAPDRAGON,
+ ZorkGrandInquisitorItems.HAMMER,
+ ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.FROBUARY_3_UNDERGROUNDHOG_DAY: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "dw2g"),),
+ archipelago_id=LOCATION_OFFSET + 36,
+ region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.GETTING_SOME_CHANGE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((12892, 1),),
+ archipelago_id=LOCATION_OFFSET + 37,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorEvents.ZORKMID_BILL_ACCESSIBLE,
+ ZorkGrandInquisitorItems.HOTSPOT_CHANGE_MACHINE_SLOT,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.GO_AWAY: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((10654, 1),),
+ archipelago_id=LOCATION_OFFSET + 38,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.GUE_TECH_DEANS_LIST: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "tr2k"),),
+ archipelago_id=LOCATION_OFFSET + 39,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.GUE_TECH_ENTRANCE_EXAM: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((11082, 1), (11307, 1), (11536, 1)),
+ archipelago_id=LOCATION_OFFSET + 40,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.GUE_TECH_HEALTH_MEMO: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "tr2j"),),
+ archipelago_id=LOCATION_OFFSET + 41,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.GUE_TECH_MAGEMEISTERS: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "tr2n"),),
+ archipelago_id=LOCATION_OFFSET + 42,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.HAVE_A_HELL_OF_A_DAY: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((8443, 1),),
+ archipelago_id=LOCATION_OFFSET + 43,
+ region=ZorkGrandInquisitorRegions.HADES_SHORE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER,
+ ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS,
+ )
+ ),
+ ZorkGrandInquisitorLocations.HELLO_THIS_IS_SHONA_FROM_GURTH_PUBLISHING: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4698, 1),),
+ archipelago_id=LOCATION_OFFSET + 44,
+ region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.HELP_ME_CANT_BREATHE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((10421, 1),),
+ archipelago_id=LOCATION_OFFSET + 45,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH,
+ ZorkGrandInquisitorItems.PLASTIC_SIX_PACK_HOLDER,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.HEY_FREE_DIRT: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((11747, 1),),
+ archipelago_id=LOCATION_OFFSET + 46,
+ region=ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.HOTSPOT_DIRT_MOUND,
+ ZorkGrandInquisitorItems.SHOVEL,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.HI_MY_NAME_IS_DOUG: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4698, 2),),
+ archipelago_id=LOCATION_OFFSET + 47,
+ region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.HMMM_INFORMATIVE_YET_DEEPLY_DISTURBING: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "mt2h"),),
+ archipelago_id=LOCATION_OFFSET + 48,
+ region=ZorkGrandInquisitorRegions.MONASTERY,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.HOLD_ON_FOR_AN_IMPORTANT_MESSAGE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4698, 5),),
+ archipelago_id=LOCATION_OFFSET + 49,
+ region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.HOW_TO_HYPNOTIZE_YOURSELF: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "uh1e"),),
+ archipelago_id=LOCATION_OFFSET + 50,
+ region=ZorkGrandInquisitorRegions.HADES_SHORE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.HOW_TO_WIN_AT_DOUBLE_FANUCCI: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "th3s"),),
+ archipelago_id=LOCATION_OFFSET + 51,
+ region=ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorEvents.DALBOZ_LOCKER_OPENABLE,),
+ ),
+ ZorkGrandInquisitorLocations.IMBUE_BEBURTT: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((194, 1),),
+ archipelago_id=LOCATION_OFFSET + 52,
+ region=ZorkGrandInquisitorRegions.SPELL_LAB,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.HOTSPOT_BLANK_SCROLL_BOX,
+ ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.IM_COMPLETELY_NUDE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((19344, 1),),
+ archipelago_id=LOCATION_OFFSET + 53,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.INTO_THE_FOLIAGE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((13060, 1),),
+ archipelago_id=LOCATION_OFFSET + 54,
+ region=ZorkGrandInquisitorRegions.CROSSROADS,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.SWORD,
+ ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.INVISIBLE_FLOWERS: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((12967, 1),),
+ archipelago_id=LOCATION_OFFSET + 55,
+ region=ZorkGrandInquisitorRegions.CROSSROADS,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorItems.SPELL_IGRAM,),
+ ),
+ ZorkGrandInquisitorLocations.IN_CASE_OF_ADVENTURE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((12931, 1),),
+ archipelago_id=LOCATION_OFFSET + 56,
+ region=ZorkGrandInquisitorRegions.CROSSROADS,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.HAMMER,
+ ZorkGrandInquisitorItems.HOTSPOT_GLASS_CASE,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.IN_MAGIC_WE_TRUST: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((13062, 1),),
+ archipelago_id=LOCATION_OFFSET + 57,
+ region=ZorkGrandInquisitorRegions.CROSSROADS,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.SPELL_REZROV,
+ ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.ITS_ONE_OF_THOSE_ADVENTURERS_AGAIN: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "pe3j"),),
+ archipelago_id=LOCATION_OFFSET + 58,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.I_DONT_THINK_YOU_WOULDVE_WANTED_THAT_TO_WORK_ANYWAY: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((3816, 1008),),
+ archipelago_id=LOCATION_OFFSET + 59,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorItems.SPELL_THROCK,
+ ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.I_DONT_WANT_NO_TROUBLE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((10694, 1),),
+ archipelago_id=LOCATION_OFFSET + 60,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.I_HOPE_YOU_CAN_CLIMB_UP_THERE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((9637, 1),),
+ archipelago_id=LOCATION_OFFSET + 61,
+ region=ZorkGrandInquisitorRegions.SUBWAY_MONASTERY,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.SWORD,
+ ZorkGrandInquisitorEvents.ROPE_GLORFABLE,
+ ZorkGrandInquisitorItems.HOTSPOT_MONASTERY_VENT,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.I_LIKE_YOUR_STYLE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((16374, 1),),
+ archipelago_id=LOCATION_OFFSET + 62,
+ region=ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.SWORD,
+ ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE,
+ ZorkGrandInquisitorEvents.DAM_DESTROYED,
+ ZorkGrandInquisitorItems.SPELL_GOLGATEM,
+ ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.I_SPIT_ON_YOUR_FILTHY_COINAGE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "tp1e"), (9, 87), (1011, 1)),
+ archipelago_id=LOCATION_OFFSET + 63,
+ region=ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,),
+ ),
+ ZorkGrandInquisitorLocations.LIT_SUNFLOWERS: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4129, 1),),
+ archipelago_id=LOCATION_OFFSET + 64,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorItems.SPELL_THROCK,),
+ ),
+ ZorkGrandInquisitorLocations.MAGIC_FOREVER: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "pc1e"), (10304, 1), (5221, 1)),
+ archipelago_id=LOCATION_OFFSET + 65,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorEvents.LANTERN_DALBOZ_ACCESSIBLE,
+ ZorkGrandInquisitorItems.ROPE,
+ ZorkGrandInquisitorItems.HOTSPOT_WELL,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.MAILED_IT_TO_HELL: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((2498, (1, 2)),),
+ archipelago_id=LOCATION_OFFSET + 66,
+ region=ZorkGrandInquisitorRegions.WHITE_HOUSE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ (ZorkGrandInquisitorItems.TOTEM_GRIFF, ZorkGrandInquisitorItems.TOTEM_LUCY),
+ ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_DOOR,
+ ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.MAKE_LOVE_NOT_WAR: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((8623, 21),),
+ archipelago_id=LOCATION_OFFSET + 67,
+ region=ZorkGrandInquisitorRegions.HADES_SHORE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorEvents.CHARON_CALLED,
+ ZorkGrandInquisitorItems.SWORD,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.MEAD_LIGHT: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((10485, 1),),
+ archipelago_id=LOCATION_OFFSET + 68,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorItems.MEAD_LIGHT,
+ ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.MIKES_PANTS: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "tr2p"),),
+ archipelago_id=LOCATION_OFFSET + 69,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.MUSHROOM_HAMMERED: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4217, 1),),
+ archipelago_id=LOCATION_OFFSET + 70,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorItems.HAMMER,
+ ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.NATIONAL_TREASURE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((14318, 1),),
+ archipelago_id=LOCATION_OFFSET + 71,
+ region=ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.SPELL_REZROV,
+ ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS,
+ ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.NATURAL_AND_SUPERNATURAL_CREATURES_OF_QUENDOR: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "dv1p"),),
+ archipelago_id=LOCATION_OFFSET + 72,
+ region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.NOOOOOOOOOOOOO: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((12706, 1),),
+ archipelago_id=LOCATION_OFFSET + 73,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,
+ ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT,
+ ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.NOTHIN_LIKE_A_GOOD_STOGIE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4237, 1),),
+ archipelago_id=LOCATION_OFFSET + 74,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE,
+ ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.NOW_YOU_LOOK_LIKE_US_WHICH_IS_AN_IMPROVEMENT: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((8935, 1),),
+ archipelago_id=LOCATION_OFFSET + 75,
+ region=ZorkGrandInquisitorRegions.HADES,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorEvents.KNOWS_SNAVIG,),
+ ),
+ ZorkGrandInquisitorLocations.NO_AUTOGRAPHS: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((10476, 1),),
+ archipelago_id=LOCATION_OFFSET + 76,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR,),
+ ),
+ ZorkGrandInquisitorLocations.NO_BONDAGE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "pe2e"), (10262, 2), (15150, 83)),
+ archipelago_id=LOCATION_OFFSET + 77,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorItems.ROPE,
+ ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.OBIDIL_DRIED_UP: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((12164, 1),),
+ archipelago_id=LOCATION_OFFSET + 78,
+ region=ZorkGrandInquisitorRegions.SPELL_LAB,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorEvents.HAS_REPAIRABLE_OBIDIL,
+ ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.OH_DEAR_GOD_ITS_A_DRAGON: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((1300, 1),),
+ archipelago_id=LOCATION_OFFSET + 79,
+ region=ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN,
+ ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.OH_VERY_FUNNY_GUYS: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((2448, 1),),
+ archipelago_id=LOCATION_OFFSET + 80,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.TOTEM_BROG,
+ ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.OH_WOW_TALK_ABOUT_DEJA_VU: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4869, 1),),
+ archipelago_id=LOCATION_OFFSET + 81,
+ region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.FLATHEADIA_FUDGE,
+ ZorkGrandInquisitorItems.HUNGUS_LARD,
+ ZorkGrandInquisitorItems.JAR_OF_HOTBUGS,
+ ZorkGrandInquisitorItems.QUELBEE_HONEYCOMB,
+ ZorkGrandInquisitorItems.MOSS_OF_MAREILON,
+ ZorkGrandInquisitorItems.MUG,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.OLD_SCRATCH_WINNER: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4512, 32),),
+ archipelago_id=LOCATION_OFFSET + 82,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE, # This can be done anywhere if the item requirement is met
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorItems.OLD_SCRATCH_CARD,),
+ ),
+ ZorkGrandInquisitorLocations.ONLY_YOU_CAN_PREVENT_FOOZLE_FIRES: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "pe5n"),),
+ archipelago_id=LOCATION_OFFSET + 83,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.OPEN_THE_GATES_OF_HELL: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((8730, 1),),
+ archipelago_id=LOCATION_OFFSET + 84,
+ region=ZorkGrandInquisitorRegions.HADES,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorEvents.KNOWS_SNAVIG,),
+ ),
+ ZorkGrandInquisitorLocations.OUTSMART_THE_QUELBEES: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4241, 1),),
+ archipelago_id=LOCATION_OFFSET + 85,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.HUNGUS_LARD,
+ ZorkGrandInquisitorItems.SWORD,
+ ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.PERMASEAL: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "mt1g"),),
+ archipelago_id=LOCATION_OFFSET + 86,
+ region=ZorkGrandInquisitorRegions.MONASTERY,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.PLANETFALL: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "pp1j"),),
+ archipelago_id=LOCATION_OFFSET + 87,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.PLEASE_DONT_THROCK_THE_GRASS: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "te1g"),),
+ archipelago_id=LOCATION_OFFSET + 88,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.PORT_FOOZLE_TIME_TUNNEL: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((9404, 1),),
+ archipelago_id=LOCATION_OFFSET + 89,
+ region=ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER,
+ ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT,
+ ZorkGrandInquisitorItems.LARGE_TELEGRAPH_HAMMER,
+ ZorkGrandInquisitorItems.SPELL_NARWILE,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.PROZORKED: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4115, 1),),
+ archipelago_id=LOCATION_OFFSET + 90,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.PROZORK_TABLET,
+ ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.REASSEMBLE_SNAVIG: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4512, 98),),
+ archipelago_id=LOCATION_OFFSET + 91,
+ region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.SCROLL_FRAGMENT_ANS,
+ ZorkGrandInquisitorItems.SCROLL_FRAGMENT_GIV,
+ ZorkGrandInquisitorItems.HOTSPOT_MIRROR,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.RESTOCKED_ON_GRUESDAY: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "tr2h"),),
+ archipelago_id=LOCATION_OFFSET + 92,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.RIGHT_HELLO_YES_UH_THIS_IS_SNEFFLE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4698, 3),),
+ archipelago_id=LOCATION_OFFSET + 93,
+ region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.RIGHT_UH_SORRY_ITS_ME_AGAIN_SNEFFLE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4698, 4),),
+ archipelago_id=LOCATION_OFFSET + 94,
+ region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.SNAVIG_REPAIRED: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((201, 1),),
+ archipelago_id=LOCATION_OFFSET + 95,
+ region=ZorkGrandInquisitorRegions.SPELL_LAB,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorEvents.HAS_REPAIRABLE_SNAVIG,
+ ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.SOUVENIR: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((13408, 1),),
+ archipelago_id=LOCATION_OFFSET + 96,
+ region=ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,
+ ZorkGrandInquisitorItems.HOTSPOT_SOUVENIR_COIN_SLOT,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.STRAIGHT_TO_HELL: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((9719, 1),),
+ archipelago_id=LOCATION_OFFSET + 97,
+ region=ZorkGrandInquisitorRegions.MONASTERY,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS,
+ ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL,
+ ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((14511, 1), (14524, 5)),
+ archipelago_id=LOCATION_OFFSET + 98,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4,
+ ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY,
+ ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.SUCKING_ROCKS: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((12859, 1),),
+ archipelago_id=LOCATION_OFFSET + 99,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorEvents.ZORK_ROCKS_SUCKABLE,
+ ZorkGrandInquisitorItems.PERMA_SUCK_MACHINE,
+ ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_VACUUM_SLOT,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.TALK_TO_ME_GRAND_INQUISITOR: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((10299, 1),),
+ archipelago_id=LOCATION_OFFSET + 100,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL,),
+ ),
+ ZorkGrandInquisitorLocations.TAMING_YOUR_SNAPDRAGON: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "dv1h"),),
+ archipelago_id=LOCATION_OFFSET + 101,
+ region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.THAR_SHE_BLOWS: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((1311, 1), (1312, 1)),
+ archipelago_id=LOCATION_OFFSET + 102,
+ region=ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN,
+ ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS,
+ ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.THATS_A_ROPE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((10486, 1),),
+ archipelago_id=LOCATION_OFFSET + 103,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorItems.ROPE,
+ ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.THATS_IT_JUST_KEEP_HITTING_THOSE_BUTTONS: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((13805, 1),),
+ archipelago_id=LOCATION_OFFSET + 104,
+ region=ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ ),
+ ZorkGrandInquisitorLocations.THATS_STILL_A_ROPE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "tp1e"), (9, 83), (1011, 1)),
+ archipelago_id=LOCATION_OFFSET + 105,
+ region=ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(ZorkGrandInquisitorEvents.ROPE_GLORFABLE,),
+ ),
+ ZorkGrandInquisitorLocations.THATS_THE_SPIRIT: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((10341, 95),),
+ archipelago_id=LOCATION_OFFSET + 106,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorItems.HOTSPOT_LOUDSPEAKER_VOLUME_BUTTONS,),
+ ),
+ ZorkGrandInquisitorLocations.THE_ALCHEMICAL_DEBACLE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((9459, 1),),
+ archipelago_id=LOCATION_OFFSET + 107,
+ region=ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.THE_ENDLESS_FIRE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((9473, 1),),
+ archipelago_id=LOCATION_OFFSET + 108,
+ region=ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.THE_FLATHEADIAN_FUDGE_FIASCO: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((9520, 1),),
+ archipelago_id=LOCATION_OFFSET + 109,
+ region=ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.THE_PERILS_OF_MAGIC: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "me1j"),),
+ archipelago_id=LOCATION_OFFSET + 110,
+ region=ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.THE_UNDERGROUND_UNDERGROUND: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((13167, 1),),
+ archipelago_id=LOCATION_OFFSET + 111,
+ region=ZorkGrandInquisitorRegions.CROSSROADS,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.SUBWAY_TOKEN,
+ ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.THIS_DOESNT_LOOK_ANYTHING_LIKE_THE_BROCHURE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "cd60"), (1524, 1)),
+ archipelago_id=LOCATION_OFFSET + 112,
+ region=ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorItems.TOTEM_LUCY,),
+ ),
+ ZorkGrandInquisitorLocations.THROCKED_MUSHROOM_HAMMERED: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4219, 1),),
+ archipelago_id=LOCATION_OFFSET + 113,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.HAMMER,
+ ZorkGrandInquisitorItems.SPELL_THROCK,
+ ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.TIME_TRAVEL_FOR_DUMMIES: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "th3z"),),
+ archipelago_id=LOCATION_OFFSET + 114,
+ region=ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorEvents.DUNCE_LOCKER_OPENABLE,),
+ ),
+ ZorkGrandInquisitorLocations.TOTEMIZED_DAILY_BILLBOARD: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "px1h"),),
+ archipelago_id=LOCATION_OFFSET + 115,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.UH_OH_BROG_CANT_SWIM: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "cd60"), (1520, 1)),
+ archipelago_id=LOCATION_OFFSET + 116,
+ region=ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorItems.TOTEM_BROG,),
+ ),
+ ZorkGrandInquisitorLocations.UMBRELLA_FLOWERS: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((12926, 1),),
+ archipelago_id=LOCATION_OFFSET + 117,
+ region=ZorkGrandInquisitorRegions.CROSSROADS,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorEvents.KNOWS_BEBURTT,),
+ ),
+ ZorkGrandInquisitorLocations.UP: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((3619, 5200),),
+ archipelago_id=LOCATION_OFFSET + 118,
+ region=ZorkGrandInquisitorRegions.WHITE_HOUSE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.TOTEM_LUCY,
+ ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.USELESS_BUT_FUN: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((14321, 1),),
+ archipelago_id=LOCATION_OFFSET + 119,
+ region=ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(ZorkGrandInquisitorItems.SPELL_GOLGATEM,),
+ ),
+ ZorkGrandInquisitorLocations.UUUUUP: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((3619, 3500),),
+ archipelago_id=LOCATION_OFFSET + 120,
+ region=ZorkGrandInquisitorRegions.WHITE_HOUSE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.TOTEM_GRIFF,
+ ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.VOYAGE_OF_CAPTAIN_ZAHAB: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "uh1h"),),
+ archipelago_id=LOCATION_OFFSET + 121,
+ region=ZorkGrandInquisitorRegions.HADES_SHORE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.WANT_SOME_RYE_COURSE_YA_DO: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4034, 1),),
+ archipelago_id=LOCATION_OFFSET + 122,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorEvents.DOOR_SMOKED_CIGAR,
+ ZorkGrandInquisitorItems.MEAD_LIGHT,
+ ZorkGrandInquisitorItems.ZIMDOR_SCROLL,
+ ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.WE_DONT_SERVE_YOUR_KIND_HERE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((2461, 1),),
+ archipelago_id=LOCATION_OFFSET + 123,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.TOTEM_GRIFF,
+ ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((15472, 1),),
+ archipelago_id=LOCATION_OFFSET + 124,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4,
+ ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY,
+ ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.WHAT_ARE_YOU_STUPID: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((10484, 1),),
+ archipelago_id=LOCATION_OFFSET + 125,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorItems.PLASTIC_SIX_PACK_HOLDER,
+ ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.WHITE_HOUSE_TIME_TUNNEL: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((4983, 1),),
+ archipelago_id=LOCATION_OFFSET + 126,
+ region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR,
+ ZorkGrandInquisitorItems.SPELL_NARWILE,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.WOW_IVE_NEVER_GONE_INSIDE_HIM_BEFORE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "dc10"), (1596, 1)),
+ archipelago_id=LOCATION_OFFSET + 127,
+ region=ZorkGrandInquisitorRegions.WALKING_CASTLE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.YAD_GOHDNUORGREDNU_3_YRAUBORF: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "dm2g"),),
+ archipelago_id=LOCATION_OFFSET + 128,
+ region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(ZorkGrandInquisitorItems.HOTSPOT_MIRROR,),
+ ),
+ ZorkGrandInquisitorLocations.YOUR_PUNY_WEAPONS_DONT_PHASE_ME_BABY: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "dg4e"), (4266, 1), (9, 21), (4035, 1)),
+ archipelago_id=LOCATION_OFFSET + 129,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorItems.SWORD,
+ ZorkGrandInquisitorItems.HOTSPOT_HARRY,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.YOU_DONT_GO_MESSING_WITH_A_MANS_ZIPPER: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((16405, 1),),
+ archipelago_id=LOCATION_OFFSET + 130,
+ region=ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(ZorkGrandInquisitorItems.SPELL_REZROV,),
+ ),
+ ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((16342, 1),),
+ archipelago_id=LOCATION_OFFSET + 131,
+ region=ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ requirements=(
+ ZorkGrandInquisitorItems.SWORD,
+ ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.YOU_ONE_OF_THEM_AGITATORS_AINT_YA: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((10586, 1),),
+ archipelago_id=LOCATION_OFFSET + 132,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE,),
+ ),
+ ZorkGrandInquisitorLocations.YOU_WANT_A_PIECE_OF_ME_DOCK_BOY: ZorkGrandInquisitorLocationData(
+ game_state_trigger=((15151, 1),),
+ archipelago_id=LOCATION_OFFSET + 133,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH,),
+ ),
+ # Deathsanity
+ ZorkGrandInquisitorLocations.DEATH_ARRESTED_WITH_JACK: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "gjde"), (2201, 1)),
+ archipelago_id=LOCATION_OFFSET + 200 + 0,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE,
+ ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.DEATH_ATTACKED_THE_QUELBEES: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "gjde"), (2201, 20)),
+ archipelago_id=LOCATION_OFFSET + 200 + 1,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorItems.SWORD,
+ ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.DEATH_CLIMBED_OUT_OF_THE_WELL: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "gjde"), (2201, 21)),
+ archipelago_id=LOCATION_OFFSET + 200 + 2,
+ region=ZorkGrandInquisitorRegions.CROSSROADS,
+ tags=(ZorkGrandInquisitorTags.DEATHSANITY,),
+ ),
+ ZorkGrandInquisitorLocations.DEATH_EATEN_BY_A_GRUE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "gjde"), (2201, 18)),
+ archipelago_id=LOCATION_OFFSET + 200 + 3,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorItems.ROPE,
+ ZorkGrandInquisitorItems.HOTSPOT_WELL,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.DEATH_JUMPED_IN_BOTTOMLESS_PIT: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "gjde"), (2201, 3)),
+ archipelago_id=LOCATION_OFFSET + 200 + 4,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.DEATHSANITY,),
+ ),
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "gjde"), (2201, 37)),
+ archipelago_id=LOCATION_OFFSET + 200 + 5,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN,
+ tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4,
+ ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY,
+ ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.DEATH_LOST_SOUL_TO_OLD_SCRATCH: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "gjde"), (2201, 23)),
+ archipelago_id=LOCATION_OFFSET + 200 + 6,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(ZorkGrandInquisitorItems.OLD_SCRATCH_CARD,),
+ ),
+ ZorkGrandInquisitorLocations.DEATH_OUTSMARTED_BY_THE_QUELBEES: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "gjde"), (2201, 29)),
+ archipelago_id=LOCATION_OFFSET + 200 + 7,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorItems.HUNGUS_LARD,
+ ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.DEATH_SLICED_UP_BY_THE_INVISIBLE_GUARD: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "gjde"), (2201, 30)),
+ archipelago_id=LOCATION_OFFSET + 200 + 8,
+ region=ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE,
+ tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE),
+ ),
+ ZorkGrandInquisitorLocations.DEATH_STEPPED_INTO_THE_INFINITE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "gjde"), (2201, 4)),
+ archipelago_id=LOCATION_OFFSET + 200 + 9,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorItems.SPELL_IGRAM,
+ ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "gjde"), (2201, 11)),
+ archipelago_id=LOCATION_OFFSET + 200 + 10,
+ region=ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON,
+ tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(
+ ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN,
+ ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS,
+ ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.DEATH_THROCKED_THE_GRASS: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "gjde"), (2201, 34)),
+ archipelago_id=LOCATION_OFFSET + 200 + 11,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.DEATHSANITY,),
+ requirements=(
+ ZorkGrandInquisitorItems.SPELL_THROCK,
+ ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_GRASS,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.DEATH_TOTEMIZED: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "gjde"), (2201, (9, 32, 33))),
+ archipelago_id=LOCATION_OFFSET + 200 + 12,
+ region=ZorkGrandInquisitorRegions.MONASTERY,
+ tags=(ZorkGrandInquisitorTags.DEATHSANITY,),
+ requirements=(
+ ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS,
+ ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH,
+ ),
+ ),
+ ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "gjde"), (2201, (5, 6, 7, 8, 13))),
+ archipelago_id=LOCATION_OFFSET + 200 + 13,
+ region=ZorkGrandInquisitorRegions.MONASTERY,
+ tags=(ZorkGrandInquisitorTags.DEATHSANITY,),
+ requirements=(ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH,),
+ ),
+ ZorkGrandInquisitorLocations.DEATH_YOURE_NOT_CHARON: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "gjde"), (2201, 10)),
+ archipelago_id=LOCATION_OFFSET + 200 + 14,
+ region=ZorkGrandInquisitorRegions.HADES,
+ tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(ZorkGrandInquisitorEvents.KNOWS_SNAVIG,),
+ ),
+ ZorkGrandInquisitorLocations.DEATH_ZORK_ROCKS_EXPLODED: ZorkGrandInquisitorLocationData(
+ game_state_trigger=(("location", "gjde"), (2201, 19)),
+ archipelago_id=LOCATION_OFFSET + 200 + 15,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE),
+ requirements=(ZorkGrandInquisitorEvents.ZORK_ROCKS_ACTIVATED,),
+ ),
+ # Events
+ ZorkGrandInquisitorEvents.CHARON_CALLED: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.HADES_SHORE,
+ requirements=(
+ ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER,
+ ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.CHARON_CALLED.value,
+ ),
+ ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ requirements=(
+ ZorkGrandInquisitorItems.LANTERN,
+ ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE.value,
+ ),
+ ZorkGrandInquisitorEvents.DALBOZ_LOCKER_OPENABLE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ requirements=(
+ ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,
+ ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT,
+ ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.DALBOZ_LOCKER_OPENABLE.value,
+ ),
+ ZorkGrandInquisitorEvents.DAM_DESTROYED: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM,
+ requirements=(
+ ZorkGrandInquisitorItems.SPELL_REZROV,
+ ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS,
+ ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.DAM_DESTROYED.value,
+ ),
+ ZorkGrandInquisitorEvents.DOOR_DRANK_MEAD: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ requirements=(
+ ZorkGrandInquisitorEvents.DOOR_SMOKED_CIGAR,
+ ZorkGrandInquisitorItems.MEAD_LIGHT,
+ ZorkGrandInquisitorItems.ZIMDOR_SCROLL,
+ ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.DOOR_DRANK_MEAD.value,
+ ),
+ ZorkGrandInquisitorEvents.DOOR_SMOKED_CIGAR: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.DM_LAIR,
+ requirements=(
+ ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE,
+ ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.DOOR_SMOKED_CIGAR.value,
+ ),
+ ZorkGrandInquisitorEvents.DUNCE_LOCKER_OPENABLE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ requirements=(
+ ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,
+ ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT,
+ ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.DUNCE_LOCKER_OPENABLE.value,
+ ),
+ ZorkGrandInquisitorEvents.HAS_REPAIRABLE_OBIDIL: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ requirements=(
+ ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,
+ ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_COIN_SLOT,
+ ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_DOORS,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.HAS_REPAIRABLE_OBIDIL.value,
+ ),
+ ZorkGrandInquisitorEvents.HAS_REPAIRABLE_SNAVIG: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ requirements=(
+ ZorkGrandInquisitorItems.SCROLL_FRAGMENT_ANS,
+ ZorkGrandInquisitorItems.SCROLL_FRAGMENT_GIV,
+ ZorkGrandInquisitorItems.HOTSPOT_MIRROR,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.HAS_REPAIRABLE_SNAVIG.value,
+ ),
+ ZorkGrandInquisitorEvents.KNOWS_BEBURTT: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.SPELL_LAB,
+ requirements=(
+ ZorkGrandInquisitorItems.HOTSPOT_BLANK_SCROLL_BOX,
+ ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.KNOWS_BEBURTT.value,
+ ),
+ ZorkGrandInquisitorEvents.KNOWS_OBIDIL: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.SPELL_LAB,
+ requirements=(
+ ZorkGrandInquisitorEvents.HAS_REPAIRABLE_OBIDIL,
+ ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.KNOWS_OBIDIL.value,
+ ),
+ ZorkGrandInquisitorEvents.KNOWS_SNAVIG: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.SPELL_LAB,
+ requirements=(
+ ZorkGrandInquisitorEvents.HAS_REPAIRABLE_SNAVIG,
+ ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.KNOWS_SNAVIG.value,
+ ),
+ ZorkGrandInquisitorEvents.KNOWS_YASTARD: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ requirements=(
+ ZorkGrandInquisitorItems.FLATHEADIA_FUDGE,
+ ZorkGrandInquisitorItems.HUNGUS_LARD,
+ ZorkGrandInquisitorItems.JAR_OF_HOTBUGS,
+ ZorkGrandInquisitorItems.QUELBEE_HONEYCOMB,
+ ZorkGrandInquisitorItems.MOSS_OF_MAREILON,
+ ZorkGrandInquisitorItems.MUG,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.KNOWS_YASTARD.value,
+ ),
+ ZorkGrandInquisitorEvents.LANTERN_DALBOZ_ACCESSIBLE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ requirements=(
+ ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE,
+ ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.LANTERN_DALBOZ_ACCESSIBLE.value,
+ ),
+ ZorkGrandInquisitorEvents.ROPE_GLORFABLE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.CROSSROADS,
+ requirements=(ZorkGrandInquisitorItems.SPELL_GLORF,),
+ event_item_name=ZorkGrandInquisitorEvents.ROPE_GLORFABLE.value,
+ ),
+ ZorkGrandInquisitorEvents.VICTORY: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.ENDGAME,
+ event_item_name=ZorkGrandInquisitorEvents.VICTORY.value,
+ ),
+ ZorkGrandInquisitorEvents.WHITE_HOUSE_LETTER_MAILABLE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.WHITE_HOUSE,
+ requirements=(
+ (ZorkGrandInquisitorItems.TOTEM_GRIFF, ZorkGrandInquisitorItems.TOTEM_LUCY),
+ ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG,
+ ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_DOOR,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.WHITE_HOUSE_LETTER_MAILABLE.value,
+ ),
+ ZorkGrandInquisitorEvents.ZORKMID_BILL_ACCESSIBLE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ requirements=(ZorkGrandInquisitorItems.OLD_SCRATCH_CARD,),
+ event_item_name=ZorkGrandInquisitorEvents.ZORKMID_BILL_ACCESSIBLE.value,
+ ),
+ ZorkGrandInquisitorEvents.ZORK_ROCKS_ACTIVATED: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ requirements=(
+ ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,
+ ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_COIN_SLOT,
+ ZorkGrandInquisitorItems.ZORK_ROCKS,
+ ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_BUTTONS,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.ZORK_ROCKS_ACTIVATED.value,
+ ),
+ ZorkGrandInquisitorEvents.ZORK_ROCKS_SUCKABLE: ZorkGrandInquisitorLocationData(
+ game_state_trigger=None,
+ archipelago_id=None,
+ region=ZorkGrandInquisitorRegions.GUE_TECH,
+ requirements=(
+ ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,
+ ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT,
+ ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS,
+ ),
+ event_item_name=ZorkGrandInquisitorEvents.ZORK_ROCKS_SUCKABLE.value,
+ ),
+}
diff --git a/worlds/zork_grand_inquisitor/data/missable_location_grant_conditions_data.py b/worlds/zork_grand_inquisitor/data/missable_location_grant_conditions_data.py
new file mode 100644
index 000000000000..ef6eacb78ceb
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/data/missable_location_grant_conditions_data.py
@@ -0,0 +1,200 @@
+from typing import Dict, NamedTuple, Optional, Tuple
+
+from ..enums import ZorkGrandInquisitorItems, ZorkGrandInquisitorLocations
+
+
+class ZorkGrandInquisitorMissableLocationGrantConditionsData(NamedTuple):
+ location_condition: ZorkGrandInquisitorLocations
+ item_conditions: Optional[Tuple[ZorkGrandInquisitorItems, ...]]
+
+
+missable_location_grant_conditions_data: Dict[
+ ZorkGrandInquisitorLocations, ZorkGrandInquisitorMissableLocationGrantConditionsData
+] = {
+ ZorkGrandInquisitorLocations.BOING_BOING_BOING:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.FLYING_SNAPDRAGON,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.BONK:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.PROZORKED,
+ item_conditions=(ZorkGrandInquisitorItems.HAMMER,),
+ )
+ ,
+ ZorkGrandInquisitorLocations.DEATH_ARRESTED_WITH_JACK:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.ARREST_THE_VANDAL,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.DEATH_ATTACKED_THE_QUELBEES:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.OUTSMART_THE_QUELBEES,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.DEATH_EATEN_BY_A_GRUE:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.MAGIC_FOREVER,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.DEATH_LOST_SOUL_TO_OLD_SCRATCH:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.OLD_SCRATCH_WINNER,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.DEATH_OUTSMARTED_BY_THE_QUELBEES:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.OUTSMART_THE_QUELBEES,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.DEATH_SLICED_UP_BY_THE_INVISIBLE_GUARD:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.DEATH_STEPPED_INTO_THE_INFINITE:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.A_SMALLWAY,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.THAR_SHE_BLOWS,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.DEATH_YOURE_NOT_CHARON:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.OPEN_THE_GATES_OF_HELL,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.DEATH_ZORK_ROCKS_EXPLODED:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.CRISIS_AVERTED,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.DENIED_BY_THE_LAKE_MONSTER:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.WOW_IVE_NEVER_GONE_INSIDE_HIM_BEFORE,
+ item_conditions=(ZorkGrandInquisitorItems.SPELL_GOLGATEM,),
+ )
+ ,
+ ZorkGrandInquisitorLocations.EMERGENCY_MAGICATRONIC_MESSAGE:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.ARTIFACTS_EXPLAINED,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.FAT_LOT_OF_GOOD_THATLL_DO_YA:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS,
+ item_conditions=(ZorkGrandInquisitorItems.SPELL_IGRAM,),
+ )
+ ,
+ ZorkGrandInquisitorLocations.I_DONT_THINK_YOU_WOULDVE_WANTED_THAT_TO_WORK_ANYWAY:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.PROZORKED,
+ item_conditions=(ZorkGrandInquisitorItems.SPELL_THROCK,),
+ )
+ ,
+ ZorkGrandInquisitorLocations.I_SPIT_ON_YOUR_FILTHY_COINAGE:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS,
+ item_conditions=(ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,),
+ )
+ ,
+ ZorkGrandInquisitorLocations.MEAD_LIGHT:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.FIRE_FIRE,
+ item_conditions=(ZorkGrandInquisitorItems.MEAD_LIGHT,),
+ )
+ ,
+ ZorkGrandInquisitorLocations.MUSHROOM_HAMMERED:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.THROCKED_MUSHROOM_HAMMERED,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.NO_AUTOGRAPHS:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.FIRE_FIRE,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.NO_BONDAGE:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.HELP_ME_CANT_BREATHE,
+ item_conditions=(ZorkGrandInquisitorItems.ROPE,),
+ )
+ ,
+ ZorkGrandInquisitorLocations.TALK_TO_ME_GRAND_INQUISITOR:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.FIRE_FIRE,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.THATS_A_ROPE:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.FIRE_FIRE,
+ item_conditions=(ZorkGrandInquisitorItems.ROPE,),
+ )
+ ,
+ ZorkGrandInquisitorLocations.THATS_IT_JUST_KEEP_HITTING_THOSE_BUTTONS:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.ENJOY_YOUR_TRIP,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.THATS_STILL_A_ROPE:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS,
+ item_conditions=(ZorkGrandInquisitorItems.SPELL_GLORF,),
+ )
+ ,
+ ZorkGrandInquisitorLocations.WHAT_ARE_YOU_STUPID:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.FIRE_FIRE,
+ item_conditions=(ZorkGrandInquisitorItems.PLASTIC_SIX_PACK_HOLDER,),
+ )
+ ,
+ ZorkGrandInquisitorLocations.YAD_GOHDNUORGREDNU_3_YRAUBORF:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.REASSEMBLE_SNAVIG,
+ item_conditions=None,
+ )
+ ,
+ ZorkGrandInquisitorLocations.YOUR_PUNY_WEAPONS_DONT_PHASE_ME_BABY:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.WANT_SOME_RYE_COURSE_YA_DO,
+ item_conditions=(ZorkGrandInquisitorItems.SWORD, ZorkGrandInquisitorItems.HOTSPOT_HARRY),
+ )
+ ,
+ ZorkGrandInquisitorLocations.YOU_DONT_GO_MESSING_WITH_A_MANS_ZIPPER:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS,
+ item_conditions=(ZorkGrandInquisitorItems.SPELL_REZROV,),
+ )
+ ,
+ ZorkGrandInquisitorLocations.YOU_WANT_A_PIECE_OF_ME_DOCK_BOY:
+ ZorkGrandInquisitorMissableLocationGrantConditionsData(
+ location_condition=ZorkGrandInquisitorLocations.HELP_ME_CANT_BREATHE,
+ item_conditions=None,
+ )
+ ,
+}
diff --git a/worlds/zork_grand_inquisitor/data/region_data.py b/worlds/zork_grand_inquisitor/data/region_data.py
new file mode 100644
index 000000000000..1aed160f3088
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/data/region_data.py
@@ -0,0 +1,183 @@
+from typing import Dict, NamedTuple, Optional, Tuple
+
+from ..enums import ZorkGrandInquisitorRegions
+
+
+class ZorkGrandInquisitorRegionData(NamedTuple):
+ exits: Optional[Tuple[ZorkGrandInquisitorRegions, ...]]
+
+
+region_data: Dict[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegionData] = {
+ ZorkGrandInquisitorRegions.CROSSROADS: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.DM_LAIR,
+ ZorkGrandInquisitorRegions.GUE_TECH,
+ ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE,
+ ZorkGrandInquisitorRegions.HADES_SHORE,
+ ZorkGrandInquisitorRegions.PORT_FOOZLE,
+ ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE,
+ ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS,
+ ZorkGrandInquisitorRegions.SUBWAY_MONASTERY,
+ )
+ ),
+ ZorkGrandInquisitorRegions.DM_LAIR: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.CROSSROADS,
+ ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE,
+ ZorkGrandInquisitorRegions.HADES_SHORE,
+ ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE,
+ ZorkGrandInquisitorRegions.SUBWAY_MONASTERY,
+ )
+ ),
+ ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.DM_LAIR,
+ ZorkGrandInquisitorRegions.WALKING_CASTLE,
+ ZorkGrandInquisitorRegions.WHITE_HOUSE,
+ )
+ ),
+ ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON,
+ ZorkGrandInquisitorRegions.HADES_BEYOND_GATES,
+ )
+ ),
+ ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO,
+ ZorkGrandInquisitorRegions.ENDGAME,
+ )
+ ),
+ ZorkGrandInquisitorRegions.ENDGAME: ZorkGrandInquisitorRegionData(exits=None),
+ ZorkGrandInquisitorRegions.GUE_TECH: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.CROSSROADS,
+ ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY,
+ ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE,
+ )
+ ),
+ ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.GUE_TECH,
+ ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE,
+ )
+ ),
+ ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.CROSSROADS,
+ ZorkGrandInquisitorRegions.DM_LAIR,
+ ZorkGrandInquisitorRegions.GUE_TECH,
+ ZorkGrandInquisitorRegions.HADES_SHORE,
+ ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE,
+ ZorkGrandInquisitorRegions.SUBWAY_MONASTERY,
+ )
+ ),
+ ZorkGrandInquisitorRegions.HADES: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.HADES_BEYOND_GATES,
+ ZorkGrandInquisitorRegions.HADES_SHORE,
+ )
+ ),
+ ZorkGrandInquisitorRegions.HADES_BEYOND_GATES: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO,
+ ZorkGrandInquisitorRegions.HADES,
+ )
+ ),
+ ZorkGrandInquisitorRegions.HADES_SHORE: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.CROSSROADS,
+ ZorkGrandInquisitorRegions.DM_LAIR,
+ ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE,
+ ZorkGrandInquisitorRegions.HADES,
+ ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE,
+ ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS,
+ ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM,
+ ZorkGrandInquisitorRegions.SUBWAY_MONASTERY,
+ )
+ ),
+ ZorkGrandInquisitorRegions.MENU: ZorkGrandInquisitorRegionData(
+ exits=(ZorkGrandInquisitorRegions.PORT_FOOZLE,)
+ ),
+ ZorkGrandInquisitorRegions.MONASTERY: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.HADES_SHORE,
+ ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT,
+ ZorkGrandInquisitorRegions.SUBWAY_MONASTERY,
+ )
+ ),
+ ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.MONASTERY,
+ ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST,
+ )
+ ),
+ ZorkGrandInquisitorRegions.PORT_FOOZLE: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.CROSSROADS,
+ ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP,
+ )
+ ),
+ ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP: ZorkGrandInquisitorRegionData(
+ exits=(ZorkGrandInquisitorRegions.PORT_FOOZLE,)
+ ),
+ ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT,
+ ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN,
+ )
+ ),
+ ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.ENDGAME,
+ ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST,
+ )
+ ),
+ ZorkGrandInquisitorRegions.SPELL_LAB: ZorkGrandInquisitorRegionData(
+ exits=(ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE,)
+ ),
+ ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.CROSSROADS,
+ ZorkGrandInquisitorRegions.DM_LAIR,
+ ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE,
+ ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY,
+ ZorkGrandInquisitorRegions.HADES_SHORE,
+ ZorkGrandInquisitorRegions.SPELL_LAB,
+ ZorkGrandInquisitorRegions.SUBWAY_MONASTERY,
+ )
+ ),
+ ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.CROSSROADS,
+ ZorkGrandInquisitorRegions.HADES_SHORE,
+ ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM,
+ ZorkGrandInquisitorRegions.SUBWAY_MONASTERY,
+ )
+ ),
+ ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.HADES_SHORE,
+ ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS,
+ ZorkGrandInquisitorRegions.SUBWAY_MONASTERY,
+ )
+ ),
+ ZorkGrandInquisitorRegions.SUBWAY_MONASTERY: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.HADES_SHORE,
+ ZorkGrandInquisitorRegions.MONASTERY,
+ ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS,
+ ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM,
+ )
+ ),
+ ZorkGrandInquisitorRegions.WALKING_CASTLE: ZorkGrandInquisitorRegionData(
+ exits=(ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,)
+ ),
+ ZorkGrandInquisitorRegions.WHITE_HOUSE: ZorkGrandInquisitorRegionData(
+ exits=(
+ ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,
+ ZorkGrandInquisitorRegions.ENDGAME,
+ )
+ ),
+}
diff --git a/worlds/zork_grand_inquisitor/data_funcs.py b/worlds/zork_grand_inquisitor/data_funcs.py
new file mode 100644
index 000000000000..9ea806e8aa4d
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/data_funcs.py
@@ -0,0 +1,247 @@
+from typing import Dict, Set, Tuple, Union
+
+from .data.entrance_rule_data import entrance_rule_data
+from .data.item_data import item_data, ZorkGrandInquisitorItemData
+from .data.location_data import location_data, ZorkGrandInquisitorLocationData
+
+from .enums import (
+ ZorkGrandInquisitorEvents,
+ ZorkGrandInquisitorGoals,
+ ZorkGrandInquisitorItems,
+ ZorkGrandInquisitorLocations,
+ ZorkGrandInquisitorRegions,
+ ZorkGrandInquisitorTags,
+)
+
+
+def item_names_to_id() -> Dict[str, int]:
+ return {item.value: data.archipelago_id for item, data in item_data.items()}
+
+
+def item_names_to_item() -> Dict[str, ZorkGrandInquisitorItems]:
+ return {item.value: item for item in item_data}
+
+
+def location_names_to_id() -> Dict[str, int]:
+ return {
+ location.value: data.archipelago_id
+ for location, data in location_data.items()
+ if data.archipelago_id is not None
+ }
+
+
+def location_names_to_location() -> Dict[str, ZorkGrandInquisitorLocations]:
+ return {
+ location.value: location
+ for location, data in location_data.items()
+ if data.archipelago_id is not None
+ }
+
+
+def id_to_goals() -> Dict[int, ZorkGrandInquisitorGoals]:
+ return {goal.value: goal for goal in ZorkGrandInquisitorGoals}
+
+
+def id_to_items() -> Dict[int, ZorkGrandInquisitorItems]:
+ return {data.archipelago_id: item for item, data in item_data.items()}
+
+
+def id_to_locations() -> Dict[int, ZorkGrandInquisitorLocations]:
+ return {
+ data.archipelago_id: location
+ for location, data in location_data.items()
+ if data.archipelago_id is not None
+ }
+
+
+def item_groups() -> Dict[str, Set[str]]:
+ groups: Dict[str, Set[str]] = dict()
+
+ item: ZorkGrandInquisitorItems
+ data: ZorkGrandInquisitorItemData
+ for item, data in item_data.items():
+ if data.tags is not None:
+ for tag in data.tags:
+ groups.setdefault(tag.value, set()).add(item.value)
+
+ return {k: v for k, v in groups.items() if len(v)}
+
+
+def items_with_tag(tag: ZorkGrandInquisitorTags) -> Set[ZorkGrandInquisitorItems]:
+ items: Set[ZorkGrandInquisitorItems] = set()
+
+ item: ZorkGrandInquisitorItems
+ data: ZorkGrandInquisitorItemData
+ for item, data in item_data.items():
+ if data.tags is not None and tag in data.tags:
+ items.add(item)
+
+ return items
+
+
+def game_id_to_items() -> Dict[int, ZorkGrandInquisitorItems]:
+ mapping: Dict[int, ZorkGrandInquisitorItems] = dict()
+
+ item: ZorkGrandInquisitorItems
+ data: ZorkGrandInquisitorItemData
+ for item, data in item_data.items():
+ if data.statemap_keys is not None:
+ for key in data.statemap_keys:
+ mapping[key] = item
+
+ return mapping
+
+
+def location_groups() -> Dict[str, Set[str]]:
+ groups: Dict[str, Set[str]] = dict()
+
+ tag: ZorkGrandInquisitorTags
+ for tag in ZorkGrandInquisitorTags:
+ groups[tag.value] = set()
+
+ location: ZorkGrandInquisitorLocations
+ data: ZorkGrandInquisitorLocationData
+ for location, data in location_data.items():
+ if data.tags is not None:
+ for tag in data.tags:
+ groups[tag.value].add(location.value)
+
+ return {k: v for k, v in groups.items() if len(v)}
+
+
+def locations_by_region(include_deathsanity: bool = False) -> Dict[
+ ZorkGrandInquisitorRegions, Set[ZorkGrandInquisitorLocations]
+]:
+ mapping: Dict[ZorkGrandInquisitorRegions, Set[ZorkGrandInquisitorLocations]] = dict()
+
+ region: ZorkGrandInquisitorRegions
+ for region in ZorkGrandInquisitorRegions:
+ mapping[region] = set()
+
+ location: ZorkGrandInquisitorLocations
+ data: ZorkGrandInquisitorLocationData
+ for location, data in location_data.items():
+ if not include_deathsanity and ZorkGrandInquisitorTags.DEATHSANITY in (
+ data.tags or tuple()
+ ):
+ continue
+
+ mapping[data.region].add(location)
+
+ return mapping
+
+
+def locations_with_tag(tag: ZorkGrandInquisitorTags) -> Set[ZorkGrandInquisitorLocations]:
+ location: ZorkGrandInquisitorLocations
+ data: ZorkGrandInquisitorLocationData
+
+ return {location for location, data in location_data.items() if data.tags is not None and tag in data.tags}
+
+
+def location_access_rule_for(location: ZorkGrandInquisitorLocations, player: int) -> str:
+ data: ZorkGrandInquisitorLocationData = location_data[location]
+
+ if data.requirements is None:
+ return "lambda state: True"
+
+ lambda_string: str = "lambda state: "
+
+ i: int
+ requirement: Union[
+ Tuple[
+ Union[
+ ZorkGrandInquisitorEvents,
+ ZorkGrandInquisitorItems,
+ ],
+ ...,
+ ],
+ ZorkGrandInquisitorEvents,
+ ZorkGrandInquisitorItems
+ ]
+
+ for i, requirement in enumerate(data.requirements):
+ if isinstance(requirement, tuple):
+ lambda_string += "("
+
+ ii: int
+ sub_requirement: Union[ZorkGrandInquisitorEvents, ZorkGrandInquisitorItems]
+ for ii, sub_requirement in enumerate(requirement):
+ lambda_string += f"state.has(\"{sub_requirement.value}\", {player})"
+
+ if ii < len(requirement) - 1:
+ lambda_string += " or "
+
+ lambda_string += ")"
+ else:
+ lambda_string += f"state.has(\"{requirement.value}\", {player})"
+
+ if i < len(data.requirements) - 1:
+ lambda_string += " and "
+
+ return lambda_string
+
+
+def entrance_access_rule_for(
+ region_origin: ZorkGrandInquisitorRegions,
+ region_destination: ZorkGrandInquisitorRegions,
+ player: int
+) -> str:
+ data: Union[
+ Tuple[
+ Tuple[
+ Union[
+ ZorkGrandInquisitorEvents,
+ ZorkGrandInquisitorItems,
+ ZorkGrandInquisitorRegions,
+ ],
+ ...,
+ ],
+ ...,
+ ],
+ None,
+ ] = entrance_rule_data[(region_origin, region_destination)]
+
+ if data is None:
+ return "lambda state: True"
+
+ lambda_string: str = "lambda state: "
+
+ i: int
+ requirement_group: Tuple[
+ Union[
+ ZorkGrandInquisitorEvents,
+ ZorkGrandInquisitorItems,
+ ZorkGrandInquisitorRegions,
+ ],
+ ...,
+ ]
+ for i, requirement_group in enumerate(data):
+ lambda_string += "("
+
+ ii: int
+ requirement: Union[
+ ZorkGrandInquisitorEvents,
+ ZorkGrandInquisitorItems,
+ ZorkGrandInquisitorRegions,
+ ]
+ for ii, requirement in enumerate(requirement_group):
+ requirement_type: Union[
+ ZorkGrandInquisitorEvents,
+ ZorkGrandInquisitorItems,
+ ZorkGrandInquisitorRegions,
+ ] = type(requirement)
+
+ if requirement_type in (ZorkGrandInquisitorEvents, ZorkGrandInquisitorItems):
+ lambda_string += f"state.has(\"{requirement.value}\", {player})"
+ elif requirement_type == ZorkGrandInquisitorRegions:
+ lambda_string += f"state.can_reach(\"{requirement.value}\", \"Region\", {player})"
+
+ if ii < len(requirement_group) - 1:
+ lambda_string += " and "
+
+ lambda_string += ")"
+
+ if i < len(data) - 1:
+ lambda_string += " or "
+
+ return lambda_string
diff --git a/worlds/zork_grand_inquisitor/docs/en_Zork Grand Inquisitor.md b/worlds/zork_grand_inquisitor/docs/en_Zork Grand Inquisitor.md
new file mode 100644
index 000000000000..d5821914beca
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/docs/en_Zork Grand Inquisitor.md
@@ -0,0 +1,102 @@
+# Zork Grand Inquisitor
+
+## Where is the options page?
+
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
+configuration file.
+
+## Is a tracker available for this game?
+
+Yes! You can download the latest PopTracker pack for Zork Grand Inquisitor [here](https://github.com/SerpentAI/ZorkGrandInquisitorAPTracker/releases/latest).
+
+## What does randomization do to this game?
+
+A majority of inventory items you can normally pick up are completely removed from the game (e.g. the lantern won't be
+in the crate, the mead won't be at the fish market, etc.). Instead, these items will be distributed in the multiworld.
+This means that you can expect to access areas and be in a position to solve certain puzzles in a completely different
+order than you normally would.
+
+Subway, teleporter and totemizer destinations are initially locked and need to be unlocked by receiving the
+corresponding item in the multiworld. This alone enables creative routing in a game that would otherwise be rather
+linear. The Crossroads destination is always unlocked for both the subway and teleporter to prevent softlocks. Until you
+receive your first totemizer destination, it will be locked to Newark, New Jersey.
+
+Important hotspots are also randomized. This means that you will be unable to interact with certain objects until you
+receive the corresponding item in the multiworld. This can be a bit confusing at first, but it adds depth to the
+randomization and makes the game more interesting to play.
+
+You can travel back to the surface without dying by looking inside the bucket. This will work as long as the rope is
+still attached to the well.
+
+Attempting to cast VOXAM will teleport you back to the Crossroads. Fast Travel!
+
+## What item types are distributed in the multiworld?
+
+- Inventory items
+- Pouch of Zorkmids
+- Spells
+- Totems
+- Subway destinations
+- Teleporter destinations
+- Totemizer destinations
+- Hotspots (with option to start with the items enabling them instead if you prefer not playing with the randomization
+ of hotspots)
+
+## When the player receives an item, what happens?
+
+- **Inventory items**: Directly added to the player's inventory.
+- **Pouch of Zorkmids**: Appears on the inventory screen. The player can then pick up Zorkmid coins from it.
+- **Spells**: Learned and directly added to the spell book.
+- **Totems**: Appears on the inventory screen.
+- **Subway destinations**: The destination button on the subway map becomes functional.
+- **Teleporter destinations**: The destination can show up on the teleporter screen.
+- **Totemizer destinations**: The destination button on the panel becomes functional.
+- **Hotspots**: The hotspot becomes interactable.
+
+## What is considered a location check in Zork Grand Inquisitor?
+
+- Solving puzzles
+- Accessing certain areas for the first time
+- Triggering certain interactions, even if they aren't puzzles per se
+- Dying in unique ways (Optional; Deathsanity option)
+
+## The location check names are fun but don't always convey well what's needed to unlock them. Is there a guide?
+
+Yes! You can find a complete guide for the location checks [here](https://gist.github.com/nbrochu/f7bed7a1fef4e2beb67ad6ddbf18b970).
+
+## What is the victory condition?
+
+Victory is achieved when the 3 artifacts of magic are retrieved and placed inside the walking castle.
+
+## Can I use the save system without a problem?
+
+Absolutely! The save system is fully supported (and its use is in fact strongly encouraged!). You can save and load your
+game as you normally would and the client will automatically sync your items and hotspots with what you should have in
+that game state.
+
+Depending on how your game progresses, there's a chance that certain location checks might become missable. This
+presents an excellent opportunity to utilize the save system. Simply make it a habit to save before undertaking
+irreversible actions, ensuring you can revert to a previous state if necessary. If you prefer not to depend on the save
+system for accessing missable location checks, there's an option to automatically unlock them as they become
+unavailable.
+
+## Unique Local Commands
+The following commands are only available when using the Zork Grand Inquisitor Client to play the game with Archipelago.
+
+- `/zork` Attempts to attach to a running instance of Zork Grand Inquisitor. If successful, the client will then be able
+ to read and control the state of the game.
+- `/brog` Lists received items for Brog.
+- `/griff` Lists received items for Griff.
+- `/lucy` Lists received items for Lucy.
+- `/hotspots` Lists received hotspots.
+
+## Known issues
+
+- You will get a second rope right after using GLORF (one in your inventory and one on your cursor). This is a harmless
+ side effect that will go away after you store it in your inventory as duplicates are actively removed.
+- After climbing up to the Monastery for the first time, a rope will forever remain in place in the vent. When you come
+ back to the Monastery, you will be able to climb up without needing to combine the sword and rope again. However, when
+ arriving at the top, you will receive a duplicate sword on a rope. This is a harmless side effect that will go away
+ after you store it in your inventory as duplicates are actively removed.
+- Since the client is reading and manipulating the game's memory, rare game crashes can happen. If you encounter one,
+ simply restart the game, load your latest save and use the `/zork` command again in the client. Nothing will be lost.
diff --git a/worlds/zork_grand_inquisitor/docs/setup_en.md b/worlds/zork_grand_inquisitor/docs/setup_en.md
new file mode 100644
index 000000000000..f9078c6d39ba
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/docs/setup_en.md
@@ -0,0 +1,42 @@
+# Zork Grand Inquisitor Randomizer Setup Guide
+
+## Requirements
+
+- Windows OS (Hard required. Client is using memory reading / writing through Win32 API)
+- A copy of Zork Grand Inquisitor. Only the GOG version is supported. The Steam version can work with some tinkering but
+ is not officially supported.
+- ScummVM 2.7.1 64-bit (Important: Will not work with any other version. [Direct Download](https://downloads.scummvm.org/frs/scummvm/2.7.1/scummvm-2.7.1-win32-x86_64.zip))
+- Archipelago 0.4.4+
+
+## Game Setup Instructions
+
+No game modding is required to play Zork Grand Inquisitor with Archipelago. The client does all the work by attaching to
+the game process and reading and manipulating the game state in real-time.
+
+This being said, the game does need to be played through ScummVM 2.7.1, so some configuration is required around that.
+
+### GOG
+
+- Open the directory where you installed Zork Grand Inquisitor. You should see a `Launch Zork Grand Inquisitor`
+ shortcut.
+- Open the `scummvm` directory. Delete the entire contents of that directory.
+- Still inside the `scummvm` directory, unzip the contents of the ScummVM 2.7.1 zip file you downloaded earlier.
+- Go back to the directory where you installed Zork Grand Inquisitor.
+- Verify that the game still launches when using the `Launch Zork Grand Inquisitor` shortcut.
+- Your game is now ready to be played with Archipelago. From now on, you can use the `Launch Zork Grand Inquisitor`
+ shortcut to launch the game.
+
+## Joining a Multiworld Game
+
+- Launch Zork Grand Inquisitor and start a new game.
+- Open the Archipelago Launcher and click `Zork Grand Inquisitor Client`.
+- Using the `Zork Grand Inquisitor Client`:
+ - Enter the room's hostname and port number (e.g. `archipelago.gg:54321`) in the top box and press `Connect`.
+ - Input your player name at the bottom when prompted and press `Enter`.
+ - You should now be connected to the Archipelago room.
+ - Next, input `/zork` at the bottom and press `Enter`. This will attach the client to the game process.
+ - If the command is successful, you are now ready to play Zork Grand Inquisitor with Archipelago.
+
+## Continuing a Multiworld Game
+
+- Perform the same steps as above, but instead of starting a new game, load your latest save file.
diff --git a/worlds/zork_grand_inquisitor/enums.py b/worlds/zork_grand_inquisitor/enums.py
new file mode 100644
index 000000000000..ecbb38a949b4
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/enums.py
@@ -0,0 +1,350 @@
+import enum
+
+
+class ZorkGrandInquisitorEvents(enum.Enum):
+ CHARON_CALLED = "Event: Charon Called"
+ CIGAR_ACCESSIBLE = "Event: Cigar Accessible"
+ DALBOZ_LOCKER_OPENABLE = "Event: Dalboz Locker Openable"
+ DAM_DESTROYED = "Event: Dam Destroyed"
+ DOOR_DRANK_MEAD = "Event: Door Drank Mead"
+ DOOR_SMOKED_CIGAR = "Event: Door Smoked Cigar"
+ DUNCE_LOCKER_OPENABLE = "Event: Dunce Locker Openable"
+ HAS_REPAIRABLE_OBIDIL = "Event: Has Repairable OBIDIL"
+ HAS_REPAIRABLE_SNAVIG = "Event: Has Repairable SNAVIG"
+ KNOWS_BEBURTT = "Event: Knows BEBURTT"
+ KNOWS_OBIDIL = "Event: Knows OBIDIL"
+ KNOWS_SNAVIG = "Event: Knows SNAVIG"
+ KNOWS_YASTARD = "Event: Knows YASTARD"
+ LANTERN_DALBOZ_ACCESSIBLE = "Event: Lantern (Dalboz) Accessible"
+ ROPE_GLORFABLE = "Event: Rope GLORFable"
+ VICTORY = "Victory"
+ WHITE_HOUSE_LETTER_MAILABLE = "Event: White House Letter Mailable"
+ ZORKMID_BILL_ACCESSIBLE = "Event: 500 Zorkmid Bill Accessible"
+ ZORK_ROCKS_ACTIVATED = "Event: Zork Rocks Activated"
+ ZORK_ROCKS_SUCKABLE = "Event: Zork Rocks Suckable"
+
+
+class ZorkGrandInquisitorGoals(enum.Enum):
+ THREE_ARTIFACTS = 0
+
+
+class ZorkGrandInquisitorItems(enum.Enum):
+ BROGS_BICKERING_TORCH = "Brog's Bickering Torch"
+ BROGS_FLICKERING_TORCH = "Brog's Flickering Torch"
+ BROGS_GRUE_EGG = "Brog's Grue Egg"
+ BROGS_PLANK = "Brog's Plank"
+ FILLER_FROBOZZ_ELECTRIC_GADGET = "Frobozz Electric Gadget"
+ FILLER_INQUISITION_PROPAGANDA_FLYER = "Inquisition Propaganda Flyer"
+ FILLER_MAGIC_CONTRABAND = "Magic Contraband"
+ FILLER_NONSENSICAL_INQUISITION_PAPERWORK = "Nonsensical Inquisition Paperwork"
+ FILLER_UNREADABLE_SPELL_SCROLL = "Unreadable Spell Scroll"
+ FLATHEADIA_FUDGE = "Flatheadia Fudge"
+ GRIFFS_AIR_PUMP = "Griff's Air Pump"
+ GRIFFS_DRAGON_TOOTH = "Griff's Dragon Tooth"
+ GRIFFS_INFLATABLE_RAFT = "Griff's Inflatable Raft"
+ GRIFFS_INFLATABLE_SEA_CAPTAIN = "Griff's Inflatable Sea Captain"
+ HAMMER = "Hammer"
+ HOTSPOT_666_MAILBOX = "Hotspot: 666 Mailbox"
+ HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS = "Hotspot: Alpine's Quandry Card Slots"
+ HOTSPOT_BLANK_SCROLL_BOX = "Hotspot: Blank Scroll Box"
+ HOTSPOT_BLINDS = "Hotspot: Blinds"
+ HOTSPOT_CANDY_MACHINE_BUTTONS = "Hotspot: Candy Machine Buttons"
+ HOTSPOT_CANDY_MACHINE_COIN_SLOT = "Hotspot: Candy Machine Coin Slot"
+ HOTSPOT_CANDY_MACHINE_VACUUM_SLOT = "Hotspot: Candy Machine Vacuum Slot"
+ HOTSPOT_CHANGE_MACHINE_SLOT = "Hotspot: Change Machine Slot"
+ HOTSPOT_CLOSET_DOOR = "Hotspot: Closet Door"
+ HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT = "Hotspot: Closing the Time Tunnels Hammer Slot"
+ HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER = "Hotspot: Closing the Time Tunnels Lever"
+ HOTSPOT_COOKING_POT = "Hotspot: Cooking Pot"
+ HOTSPOT_DENTED_LOCKER = "Hotspot: Dented Locker"
+ HOTSPOT_DIRT_MOUND = "Hotspot: Dirt Mound"
+ HOTSPOT_DOCK_WINCH = "Hotspot: Dock Winch"
+ HOTSPOT_DRAGON_CLAW = "Hotspot: Dragon Claw"
+ HOTSPOT_DRAGON_NOSTRILS = "Hotspot: Dragon Nostrils"
+ HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE = "Hotspot: Dungeon Master's Lair Entrance"
+ HOTSPOT_FLOOD_CONTROL_BUTTONS = "Hotspot: Flood Control Buttons"
+ HOTSPOT_FLOOD_CONTROL_DOORS = "Hotspot: Flood Control Doors"
+ HOTSPOT_FROZEN_TREAT_MACHINE_COIN_SLOT = "Hotspot: Frozen Treat Machine Coin Slot"
+ HOTSPOT_FROZEN_TREAT_MACHINE_DOORS = "Hotspot: Frozen Treat Machine Doors"
+ HOTSPOT_GLASS_CASE = "Hotspot: Glass Case"
+ HOTSPOT_GRAND_INQUISITOR_DOLL = "Hotspot: Grand Inquisitor Doll"
+ HOTSPOT_GUE_TECH_DOOR = "Hotspot: GUE Tech Door"
+ HOTSPOT_GUE_TECH_GRASS = "Hotspot: GUE Tech Grass"
+ HOTSPOT_HADES_PHONE_BUTTONS = "Hotspot: Hades Phone Buttons"
+ HOTSPOT_HADES_PHONE_RECEIVER = "Hotspot: Hades Phone Receiver"
+ HOTSPOT_HARRY = "Hotspot: Harry"
+ HOTSPOT_HARRYS_ASHTRAY = "Hotspot: Harry's Ashtray"
+ HOTSPOT_HARRYS_BIRD_BATH = "Hotspot: Harry's Bird Bath"
+ HOTSPOT_IN_MAGIC_WE_TRUST_DOOR = "Hotspot: In Magic We Trust Door"
+ HOTSPOT_JACKS_DOOR = "Hotspot: Jack's Door"
+ HOTSPOT_LOUDSPEAKER_VOLUME_BUTTONS = "Hotspot: Loudspeaker Volume Buttons"
+ HOTSPOT_MAILBOX_DOOR = "Hotspot: Mailbox Door"
+ HOTSPOT_MAILBOX_FLAG = "Hotspot: Mailbox Flag"
+ HOTSPOT_MIRROR = "Hotspot: Mirror"
+ HOTSPOT_MONASTERY_VENT = "Hotspot: Monastery Vent"
+ HOTSPOT_MOSSY_GRATE = "Hotspot: Mossy Grate"
+ HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR = "Hotspot: Port Foozle Past Tavern Door"
+ HOTSPOT_PURPLE_WORDS = "Hotspot: Purple Words"
+ HOTSPOT_QUELBEE_HIVE = "Hotspot: Quelbee Hive"
+ HOTSPOT_ROPE_BRIDGE = "Hotspot: Rope Bridge"
+ HOTSPOT_SKULL_CAGE = "Hotspot: Skull Cage"
+ HOTSPOT_SNAPDRAGON = "Hotspot: Snapdragon"
+ HOTSPOT_SODA_MACHINE_BUTTONS = "Hotspot: Soda Machine Buttons"
+ HOTSPOT_SODA_MACHINE_COIN_SLOT = "Hotspot: Soda Machine Coin Slot"
+ HOTSPOT_SOUVENIR_COIN_SLOT = "Hotspot: Souvenir Coin Slot"
+ HOTSPOT_SPELL_CHECKER = "Hotspot: Spell Checker"
+ HOTSPOT_SPELL_LAB_CHASM = "Hotspot: Spell Lab Chasm"
+ HOTSPOT_SPRING_MUSHROOM = "Hotspot: Spring Mushroom"
+ HOTSPOT_STUDENT_ID_MACHINE = "Hotspot: Student ID Machine"
+ HOTSPOT_SUBWAY_TOKEN_SLOT = "Hotspot: Subway Token Slot"
+ HOTSPOT_TAVERN_FLY = "Hotspot: Tavern Fly"
+ HOTSPOT_TOTEMIZER_SWITCH = "Hotspot: Totemizer Switch"
+ HOTSPOT_TOTEMIZER_WHEELS = "Hotspot: Totemizer Wheels"
+ HOTSPOT_WELL = "Hotspot: Well"
+ HUNGUS_LARD = "Hungus Lard"
+ JAR_OF_HOTBUGS = "Jar of Hotbugs"
+ LANTERN = "Lantern"
+ LARGE_TELEGRAPH_HAMMER = "Large Telegraph Hammer"
+ LUCYS_PLAYING_CARD_1 = "Lucy's Playing Card: 1 Pip"
+ LUCYS_PLAYING_CARD_2 = "Lucy's Playing Card: 2 Pips"
+ LUCYS_PLAYING_CARD_3 = "Lucy's Playing Card: 3 Pips"
+ LUCYS_PLAYING_CARD_4 = "Lucy's Playing Card: 4 Pips"
+ MAP = "Map"
+ MEAD_LIGHT = "Mead Light"
+ MOSS_OF_MAREILON = "Moss of Mareilon"
+ MUG = "Mug"
+ OLD_SCRATCH_CARD = "Old Scratch Card"
+ PERMA_SUCK_MACHINE = "Perma-Suck Machine"
+ PLASTIC_SIX_PACK_HOLDER = "Plastic Six-Pack Holder"
+ POUCH_OF_ZORKMIDS = "Pouch of Zorkmids"
+ PROZORK_TABLET = "Prozork Tablet"
+ QUELBEE_HONEYCOMB = "Quelbee Honeycomb"
+ ROPE = "Rope"
+ SCROLL_FRAGMENT_ANS = "Scroll Fragment: ANS"
+ SCROLL_FRAGMENT_GIV = "Scroll Fragment: GIV"
+ SHOVEL = "Shovel"
+ SNAPDRAGON = "Snapdragon"
+ SPELL_GLORF = "Spell: GLORF"
+ SPELL_GOLGATEM = "Spell: GOLGATEM"
+ SPELL_IGRAM = "Spell: IGRAM"
+ SPELL_KENDALL = "Spell: KENDALL"
+ SPELL_NARWILE = "Spell: NARWILE"
+ SPELL_REZROV = "Spell: REZROV"
+ SPELL_THROCK = "Spell: THROCK"
+ SPELL_VOXAM = "Spell: VOXAM"
+ STUDENT_ID = "Student ID"
+ SUBWAY_DESTINATION_FLOOD_CONTROL_DAM = "Subway Destination: Flood Control Dam #3"
+ SUBWAY_DESTINATION_HADES = "Subway Destination: Hades"
+ SUBWAY_DESTINATION_MONASTERY = "Subway Destination: Monastery"
+ SUBWAY_TOKEN = "Subway Token"
+ SWORD = "Sword"
+ TELEPORTER_DESTINATION_DM_LAIR = "Teleporter Destination: Dungeon Master's Lair"
+ TELEPORTER_DESTINATION_GUE_TECH = "Teleporter Destination: GUE Tech"
+ TELEPORTER_DESTINATION_HADES = "Teleporter Destination: Hades"
+ TELEPORTER_DESTINATION_MONASTERY = "Teleporter Destination: Monastery Station"
+ TELEPORTER_DESTINATION_SPELL_LAB = "Teleporter Destination: Spell Lab"
+ TOTEM_BROG = "Totem: Brog"
+ TOTEM_GRIFF = "Totem: Griff"
+ TOTEM_LUCY = "Totem: Lucy"
+ TOTEMIZER_DESTINATION_HALL_OF_INQUISITION = "Totemizer Destination: Hall of Inquisition"
+ TOTEMIZER_DESTINATION_INFINITY = "Totemizer Destination: Infinity"
+ TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL = "Totemizer Destination: Straight to Hell"
+ TOTEMIZER_DESTINATION_SURFACE_OF_MERZ = "Totemizer Destination: Surface of Merz"
+ ZIMDOR_SCROLL = "ZIMDOR Scroll"
+ ZORK_ROCKS = "Zork Rocks"
+
+
+class ZorkGrandInquisitorLocations(enum.Enum):
+ ALARM_SYSTEM_IS_DOWN = "Alarm System is Down"
+ ARREST_THE_VANDAL = "Arrest the Vandal!"
+ ARTIFACTS_EXPLAINED = "Artifacts, Explained"
+ A_BIG_FAT_SASSY_2_HEADED_MONSTER = "A Big, Fat, SASSY 2-Headed Monster"
+ A_LETTER_FROM_THE_WHITE_HOUSE = "A Letter from the White House"
+ A_SMALLWAY = "A Smallway"
+ BEAUTIFUL_THATS_PLENTY = "Beautiful, That's Plenty!"
+ BEBURTT_DEMYSTIFIED = "BEBURTT, Demystified"
+ BETTER_SPELL_MANUFACTURING_IN_UNDER_10_MINUTES = "Better Spell Manufacturing in Under 10 Minutes"
+ BOING_BOING_BOING = "Boing, Boing, Boing"
+ BONK = "Bonk!"
+ BRAVE_SOULS_WANTED = "Brave Souls Wanted"
+ BROG_DO_GOOD = "Brog Do Good!"
+ BROG_EAT_ROCKS = "Brog Eat Rocks"
+ BROG_KNOW_DUMB_THAT_DUMB = "Brog Know Dumb. That Dumb"
+ BROG_MUCH_BETTER_AT_THIS_GAME = "Brog Much Better at This Game"
+ CASTLE_WATCHING_A_FIELD_GUIDE = "Castle Watching: A Field Guide"
+ CAVES_NOTES = "Cave's Notes"
+ CLOSING_THE_TIME_TUNNELS = "Closing the Time Tunnels"
+ CRISIS_AVERTED = "Crisis Averted"
+ CUT_THAT_OUT_YOU_LITTLE_CREEP = "Cut That Out You Little Creep!"
+ DEATH_ARRESTED_WITH_JACK = "Death: Arrested With Jack"
+ DEATH_ATTACKED_THE_QUELBEES = "Death: Attacked the Quelbees"
+ DEATH_CLIMBED_OUT_OF_THE_WELL = "Death: Climbed Out of the Well"
+ DEATH_EATEN_BY_A_GRUE = "Death: Eaten by a Grue"
+ DEATH_JUMPED_IN_BOTTOMLESS_PIT = "Death: Jumped in Bottomless Pit"
+ DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER = "Death: Lost Game of Strip Grue, Fire, Water"
+ DEATH_LOST_SOUL_TO_OLD_SCRATCH = "Death: Lost Soul to Old Scratch"
+ DEATH_OUTSMARTED_BY_THE_QUELBEES = "Death: Outsmarted by the Quelbees"
+ DEATH_SLICED_UP_BY_THE_INVISIBLE_GUARD = "Death: Sliced up by the Invisible Guard"
+ DEATH_STEPPED_INTO_THE_INFINITE = "Death: Step Into the Infinite"
+ DEATH_SWALLOWED_BY_A_DRAGON = "Death: Swallowed by a Dragon"
+ DEATH_THROCKED_THE_GRASS = "Death: THROCKed the Grass"
+ DEATH_TOTEMIZED = "Death: Totemized?"
+ DEATH_TOTEMIZED_PERMANENTLY = "Death: Totemized... Permanently"
+ DEATH_YOURE_NOT_CHARON = "Death: You're Not Charon!?"
+ DEATH_ZORK_ROCKS_EXPLODED = "Death: Zork Rocks Exploded"
+ DENIED_BY_THE_LAKE_MONSTER = "Denied by the Lake Monster"
+ DESPERATELY_SEEKING_TUTOR = "Desperately Seeking Tutor"
+ DONT_EVEN_START_WITH_US_SPARKY = "Don't Even Start With Us, Sparky"
+ DOOOOOOWN = "Doooooown"
+ DOWN = "Down"
+ DRAGON_ARCHIPELAGO_TIME_TUNNEL = "Dragon Archipelago Time Tunnel"
+ DUNCE_LOCKER = "Dunce Locker"
+ EGGPLANTS = "Eggplants"
+ ELSEWHERE = "Elsewhere"
+ EMERGENCY_MAGICATRONIC_MESSAGE = "Emergency Magicatronic Message"
+ ENJOY_YOUR_TRIP = "Enjoy Your Trip!"
+ FAT_LOT_OF_GOOD_THATLL_DO_YA = "Fat Lot of Good That'll Do Ya"
+ FIRE_FIRE = "Fire! Fire!"
+ FLOOD_CONTROL_DAM_3_THE_NOT_REMOTELY_BORING_TALE = "Flood Control Dam #3: The Not Remotely Boring Tale"
+ FLYING_SNAPDRAGON = "Flying Snapdragon"
+ FROBUARY_3_UNDERGROUNDHOG_DAY = "Frobruary 3 - Undergroundhog Day"
+ GETTING_SOME_CHANGE = "Getting Some Change"
+ GO_AWAY = "GO AWAY!"
+ GUE_TECH_DEANS_LIST = "GUE Tech Dean's List"
+ GUE_TECH_ENTRANCE_EXAM = "GUE Tech Entrance Exam"
+ GUE_TECH_HEALTH_MEMO = "GUE Tech Health Memo"
+ GUE_TECH_MAGEMEISTERS = "GUE Tech Magemeisters"
+ HAVE_A_HELL_OF_A_DAY = "Have a Hell of a Day!"
+ HELLO_THIS_IS_SHONA_FROM_GURTH_PUBLISHING = "Hello, This is Shona from Gurth Publishing"
+ HELP_ME_CANT_BREATHE = "Help... Me. Can't... Breathe"
+ HEY_FREE_DIRT = "Hey, Free Dirt!"
+ HI_MY_NAME_IS_DOUG = "Hi, My Name is Doug"
+ HMMM_INFORMATIVE_YET_DEEPLY_DISTURBING = "Hmmm. Informative. Yet Deeply Disturbing"
+ HOLD_ON_FOR_AN_IMPORTANT_MESSAGE = "Hold on for an Important Message"
+ HOW_TO_HYPNOTIZE_YOURSELF = "How to Hypnotize Yourself"
+ HOW_TO_WIN_AT_DOUBLE_FANUCCI = "How to Win at Double Fanucci"
+ IMBUE_BEBURTT = "Imbue BEBURTT"
+ IM_COMPLETELY_NUDE = "I'm Completely Nude"
+ INTO_THE_FOLIAGE = "Into the Foliage"
+ INVISIBLE_FLOWERS = "Invisible Flowers"
+ IN_CASE_OF_ADVENTURE = "In Case of Adventure, Break Glass!"
+ IN_MAGIC_WE_TRUST = "In Magic We Trust"
+ ITS_ONE_OF_THOSE_ADVENTURERS_AGAIN = "It's One of Those Adventurers Again..."
+ I_DONT_THINK_YOU_WOULDVE_WANTED_THAT_TO_WORK_ANYWAY = "I Don't Think You Would've Wanted That to Work Anyway"
+ I_DONT_WANT_NO_TROUBLE = "I Don't Want No Trouble!"
+ I_HOPE_YOU_CAN_CLIMB_UP_THERE = "I Hope You Can Climb Up There With All This Junk"
+ I_LIKE_YOUR_STYLE = "I Like Your Style!"
+ I_SPIT_ON_YOUR_FILTHY_COINAGE = "I Spit on Your Filthy Coinage"
+ LIT_SUNFLOWERS = "Lit Sunflowers"
+ MAGIC_FOREVER = "Magic Forever!"
+ MAILED_IT_TO_HELL = "Mailed it to Hell"
+ MAKE_LOVE_NOT_WAR = "Make Love, Not War"
+ MEAD_LIGHT = "Mead Light?"
+ MIKES_PANTS = "Mike's Pants"
+ MUSHROOM_HAMMERED = "Mushroom, Hammered"
+ NATIONAL_TREASURE = "300 Year Old National Treasure"
+ NATURAL_AND_SUPERNATURAL_CREATURES_OF_QUENDOR = "Natural and Supernatural Creatures of Quendor"
+ NOOOOOOOOOOOOO = "NOOOOOOOOOOOOO!"
+ NOTHIN_LIKE_A_GOOD_STOGIE = "Nothin' Like a Good Stogie"
+ NOW_YOU_LOOK_LIKE_US_WHICH_IS_AN_IMPROVEMENT = "Now You Look Like Us, Which is an Improvement"
+ NO_AUTOGRAPHS = "No Autographs"
+ NO_BONDAGE = "No Bondage"
+ OBIDIL_DRIED_UP = "OBIDIL, Dried Up"
+ OH_DEAR_GOD_ITS_A_DRAGON = "Oh Dear God, It's a Dragon!"
+ OH_VERY_FUNNY_GUYS = "Oh, Very Funny Guys"
+ OH_WOW_TALK_ABOUT_DEJA_VU = "Oh, Wow! Talk About Deja Vu"
+ OLD_SCRATCH_WINNER = "Old Scratch Winner!"
+ ONLY_YOU_CAN_PREVENT_FOOZLE_FIRES = "Only You Can Prevent Foozle Fires"
+ OPEN_THE_GATES_OF_HELL = "Open the Gates of Hell"
+ OUTSMART_THE_QUELBEES = "Outsmart the Quelbees"
+ PERMASEAL = "PermaSeal"
+ PLANETFALL = "Planetfall"
+ PLEASE_DONT_THROCK_THE_GRASS = "Please Don't THROCK the Grass"
+ PORT_FOOZLE_TIME_TUNNEL = "Port Foozle Time Tunnel"
+ PROZORKED = "Prozorked"
+ REASSEMBLE_SNAVIG = "Reassemble SNAVIG"
+ RESTOCKED_ON_GRUESDAY = "Restocked on Gruesday"
+ RIGHT_HELLO_YES_UH_THIS_IS_SNEFFLE = "Right. Hello. Yes. Uh, This is Sneffle"
+ RIGHT_UH_SORRY_ITS_ME_AGAIN_SNEFFLE = "Right. Uh, Sorry. It's Me Again. Sneffle"
+ SNAVIG_REPAIRED = "SNAVIG, Repaired"
+ SOUVENIR = "Souvenir"
+ STRAIGHT_TO_HELL = "Straight to Hell"
+ STRIP_GRUE_FIRE_WATER = "Strip Grue, Fire, Water"
+ SUCKING_ROCKS = "Sucking Rocks"
+ TALK_TO_ME_GRAND_INQUISITOR = "Talk to Me Grand Inquisitor"
+ TAMING_YOUR_SNAPDRAGON = "Taming Your Snapdragon"
+ THAR_SHE_BLOWS = "Thar She Blows!"
+ THATS_A_ROPE = "That's a Rope"
+ THATS_IT_JUST_KEEP_HITTING_THOSE_BUTTONS = "That's it! Just Keep Hitting Those Buttons"
+ THATS_STILL_A_ROPE = "That's Still a Rope"
+ THATS_THE_SPIRIT = "That's the Spirit!"
+ THE_ALCHEMICAL_DEBACLE = "The Alchemical Debacle"
+ THE_ENDLESS_FIRE = "The Endless Fire"
+ THE_FLATHEADIAN_FUDGE_FIASCO = "The Flatheadian Fudge Fiasco"
+ THE_PERILS_OF_MAGIC = "The Perils of Magic"
+ THE_UNDERGROUND_UNDERGROUND = "The Underground Underground"
+ THIS_DOESNT_LOOK_ANYTHING_LIKE_THE_BROCHURE = "This Doesn't Look Anything Like the Brochure"
+ THROCKED_MUSHROOM_HAMMERED = "THROCKed Mushroom, Hammered"
+ TIME_TRAVEL_FOR_DUMMIES = "Time Travel for Dummies"
+ TOTEMIZED_DAILY_BILLBOARD = "Totemized Daily Billboard Functioning Correctly"
+ UH_OH_BROG_CANT_SWIM = "Uh-Oh. Brog Can't Swim"
+ UMBRELLA_FLOWERS = "Umbrella Flowers"
+ UP = "Up"
+ USELESS_BUT_FUN = "Useless, But Fun"
+ UUUUUP = "Uuuuup"
+ VOYAGE_OF_CAPTAIN_ZAHAB = "Voyage of Captain Zahab"
+ WANT_SOME_RYE_COURSE_YA_DO = "Want Some Rye? Course Ya Do!"
+ WE_DONT_SERVE_YOUR_KIND_HERE = "We Don't Serve Your Kind Here"
+ WE_GOT_A_HIGH_ROLLER = "We Got a High Roller!"
+ WHAT_ARE_YOU_STUPID = "What Are You, Stupid?"
+ WHITE_HOUSE_TIME_TUNNEL = "White House Time Tunnel"
+ WOW_IVE_NEVER_GONE_INSIDE_HIM_BEFORE = "Wow! I've Never Gone Inside Him Before!"
+ YAD_GOHDNUORGREDNU_3_YRAUBORF = "yaD gohdnuorgrednU - 3 yrauborF"
+ YOUR_PUNY_WEAPONS_DONT_PHASE_ME_BABY = "Your Puny Weapons Don't Phase Me, Baby!"
+ YOU_DONT_GO_MESSING_WITH_A_MANS_ZIPPER = "You Don't Go Messing With a Man's Zipper"
+ YOU_GAINED_86_EXPERIENCE_POINTS = "You Gained 86 Experience Points"
+ YOU_ONE_OF_THEM_AGITATORS_AINT_YA = "You One of Them Agitators, Ain't Ya?"
+ YOU_WANT_A_PIECE_OF_ME_DOCK_BOY = "You Want a Piece of Me, Dock Boy? or Girl"
+
+
+class ZorkGrandInquisitorRegions(enum.Enum):
+ CROSSROADS = "Crossroads"
+ DM_LAIR = "Dungeon Master's Lair"
+ DM_LAIR_INTERIOR = "Dungeon Master's Lair - Interior"
+ DRAGON_ARCHIPELAGO = "Dragon Archipelago"
+ DRAGON_ARCHIPELAGO_DRAGON = "Dragon Archipelago - Dragon"
+ ENDGAME = "Endgame"
+ GUE_TECH = "GUE Tech"
+ GUE_TECH_HALLWAY = "GUE Tech - Hallway"
+ GUE_TECH_OUTSIDE = "GUE Tech - Outside"
+ HADES = "Hades"
+ HADES_BEYOND_GATES = "Hades - Beyond Gates"
+ HADES_SHORE = "Hades - Shore"
+ MENU = "Menu"
+ MONASTERY = "Monastery"
+ MONASTERY_EXHIBIT = "Monastery - Exhibit"
+ PORT_FOOZLE = "Port Foozle"
+ PORT_FOOZLE_JACKS_SHOP = "Port Foozle - Jack's Shop"
+ PORT_FOOZLE_PAST = "Port Foozle Past"
+ PORT_FOOZLE_PAST_TAVERN = "Port Foozle Past - Tavern"
+ SPELL_LAB = "Spell Lab"
+ SPELL_LAB_BRIDGE = "Spell Lab - Bridge"
+ SUBWAY_CROSSROADS = "Subway Platform - Crossroads"
+ SUBWAY_FLOOD_CONTROL_DAM = "Subway Platform - Flood Control Dam #3"
+ SUBWAY_MONASTERY = "Subway Platform - Monastery"
+ WALKING_CASTLE = "Walking Castle"
+ WHITE_HOUSE = "White House"
+
+
+class ZorkGrandInquisitorTags(enum.Enum):
+ CORE = "Core"
+ DEATHSANITY = "Deathsanity"
+ FILLER = "Filler"
+ HOTSPOT = "Hotspot"
+ INVENTORY_ITEM = "Inventory Item"
+ MISSABLE = "Missable"
+ SPELL = "Spell"
+ SUBWAY_DESTINATION = "Subway Destination"
+ TELEPORTER_DESTINATION = "Teleporter Destination"
+ TOTEMIZER_DESTINATION = "Totemizer Destination"
+ TOTEM = "Totem"
diff --git a/worlds/zork_grand_inquisitor/game_controller.py b/worlds/zork_grand_inquisitor/game_controller.py
new file mode 100644
index 000000000000..7a60a1460829
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/game_controller.py
@@ -0,0 +1,1388 @@
+import collections
+import functools
+import logging
+
+from typing import Dict, Optional, Set, Tuple, Union
+
+from .data.item_data import item_data, ZorkGrandInquisitorItemData
+from .data.location_data import location_data, ZorkGrandInquisitorLocationData
+
+from .data.missable_location_grant_conditions_data import (
+ missable_location_grant_conditions_data,
+ ZorkGrandInquisitorMissableLocationGrantConditionsData,
+)
+
+from .data_funcs import game_id_to_items, items_with_tag, locations_with_tag
+
+from .enums import (
+ ZorkGrandInquisitorGoals,
+ ZorkGrandInquisitorItems,
+ ZorkGrandInquisitorLocations,
+ ZorkGrandInquisitorTags,
+)
+
+from .game_state_manager import GameStateManager
+
+
+class GameController:
+ logger: Optional[logging.Logger]
+
+ game_state_manager: GameStateManager
+
+ received_items: Set[ZorkGrandInquisitorItems]
+ completed_locations: Set[ZorkGrandInquisitorLocations]
+
+ completed_locations_queue: collections.deque
+ received_items_queue: collections.deque
+
+ all_hotspot_items: Set[ZorkGrandInquisitorItems]
+
+ game_id_to_items: Dict[int, ZorkGrandInquisitorItems]
+
+ possible_inventory_items: Set[ZorkGrandInquisitorItems]
+
+ available_inventory_slots: Set[int]
+
+ goal_completed: bool
+
+ option_goal: Optional[ZorkGrandInquisitorGoals]
+ option_deathsanity: Optional[bool]
+ option_grant_missable_location_checks: Optional[bool]
+
+ def __init__(self, logger=None) -> None:
+ self.logger = logger
+
+ self.game_state_manager = GameStateManager()
+
+ self.received_items = set()
+ self.completed_locations = set()
+
+ self.completed_locations_queue = collections.deque()
+ self.received_items_queue = collections.deque()
+
+ self.all_hotspot_items = (
+ items_with_tag(ZorkGrandInquisitorTags.HOTSPOT)
+ | items_with_tag(ZorkGrandInquisitorTags.SUBWAY_DESTINATION)
+ | items_with_tag(ZorkGrandInquisitorTags.TOTEMIZER_DESTINATION)
+ )
+
+ self.game_id_to_items = game_id_to_items()
+
+ self.possible_inventory_items = (
+ items_with_tag(ZorkGrandInquisitorTags.INVENTORY_ITEM)
+ | items_with_tag(ZorkGrandInquisitorTags.SPELL)
+ | items_with_tag(ZorkGrandInquisitorTags.TOTEM)
+ )
+
+ self.available_inventory_slots = set()
+
+ self.goal_completed = False
+
+ self.option_goal = None
+ self.option_deathsanity = None
+ self.option_grant_missable_location_checks = None
+
+ @functools.cached_property
+ def brog_items(self) -> Set[ZorkGrandInquisitorItems]:
+ return {
+ ZorkGrandInquisitorItems.BROGS_BICKERING_TORCH,
+ ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH,
+ ZorkGrandInquisitorItems.BROGS_GRUE_EGG,
+ ZorkGrandInquisitorItems.BROGS_PLANK,
+ }
+
+ @functools.cached_property
+ def griff_items(self) -> Set[ZorkGrandInquisitorItems]:
+ return {
+ ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP,
+ ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN,
+ }
+
+ @functools.cached_property
+ def lucy_items(self) -> Set[ZorkGrandInquisitorItems]:
+ return {
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4,
+ }
+
+ @property
+ def totem_items(self) -> Set[ZorkGrandInquisitorItems]:
+ return self.brog_items | self.griff_items | self.lucy_items
+
+ @functools.cached_property
+ def missable_locations(self) -> Set[ZorkGrandInquisitorLocations]:
+ return locations_with_tag(ZorkGrandInquisitorTags.MISSABLE)
+
+ def log(self, message) -> None:
+ if self.logger:
+ self.logger.info(message)
+
+ def log_debug(self, message) -> None:
+ if self.logger:
+ self.logger.debug(message)
+
+ def open_process_handle(self) -> bool:
+ return self.game_state_manager.open_process_handle()
+
+ def close_process_handle(self) -> bool:
+ return self.game_state_manager.close_process_handle()
+
+ def is_process_running(self) -> bool:
+ return self.game_state_manager.is_process_running
+
+ def list_received_brog_items(self) -> None:
+ self.log("Received Brog Items:")
+
+ self._process_received_items()
+ received_brog_items: Set[ZorkGrandInquisitorItems] = self.received_items & self.brog_items
+
+ if not len(received_brog_items):
+ self.log(" Nothing")
+ return
+
+ for item in sorted(i.value for i in received_brog_items):
+ self.log(f" {item}")
+
+ def list_received_griff_items(self) -> None:
+ self.log("Received Griff Items:")
+
+ self._process_received_items()
+ received_griff_items: Set[ZorkGrandInquisitorItems] = self.received_items & self.griff_items
+
+ if not len(received_griff_items):
+ self.log(" Nothing")
+ return
+
+ for item in sorted(i.value for i in received_griff_items):
+ self.log(f" {item}")
+
+ def list_received_lucy_items(self) -> None:
+ self.log("Received Lucy Items:")
+
+ self._process_received_items()
+ received_lucy_items: Set[ZorkGrandInquisitorItems] = self.received_items & self.lucy_items
+
+ if not len(received_lucy_items):
+ self.log(" Nothing")
+ return
+
+ for item in sorted(i.value for i in received_lucy_items):
+ self.log(f" {item}")
+
+ def list_received_hotspots(self) -> None:
+ self.log("Received Hotspots:")
+
+ self._process_received_items()
+
+ hotspot_items: Set[ZorkGrandInquisitorItems] = items_with_tag(ZorkGrandInquisitorTags.HOTSPOT)
+ received_hotspots: Set[ZorkGrandInquisitorItems] = self.received_items & hotspot_items
+
+ if not len(received_hotspots):
+ self.log(" Nothing")
+ return
+
+ for item in sorted(i.value for i in received_hotspots):
+ self.log(f" {item}")
+
+ def update(self) -> None:
+ if self.game_state_manager.is_process_still_running():
+ try:
+ self.game_state_manager.refresh_game_location()
+
+ self._apply_permanent_game_state()
+ self._apply_conditional_game_state()
+
+ self._apply_permanent_game_flags()
+
+ self._check_for_completed_locations()
+
+ if self.option_grant_missable_location_checks:
+ self._check_for_missable_locations_to_grant()
+
+ self._process_received_items()
+
+ self._manage_hotspots()
+ self._manage_items()
+
+ self._apply_conditional_teleports()
+
+ self._check_for_victory()
+ except Exception as e:
+ self.log_debug(e)
+
+ def _apply_permanent_game_state(self) -> None:
+ self._write_game_state_value_for(10934, 1) # Rope Taken
+ self._write_game_state_value_for(10418, 1) # Mead Light Taken
+ self._write_game_state_value_for(10275, 0) # Lantern in Crate
+ self._write_game_state_value_for(13929, 1) # Great Underground Door Open
+ self._write_game_state_value_for(13968, 1) # Subway Token Taken
+ self._write_game_state_value_for(12930, 1) # Hammer Taken
+ self._write_game_state_value_for(12935, 1) # Griff Totem Taken
+ self._write_game_state_value_for(12948, 1) # ZIMDOR Scroll Taken
+ self._write_game_state_value_for(4058, 1) # Shovel Taken
+ self._write_game_state_value_for(4059, 1) # THROCK Scroll Taken
+ self._write_game_state_value_for(11758, 1) # KENDALL Scroll Taken
+ self._write_game_state_value_for(16959, 1) # Old Scratch Card Taken
+ self._write_game_state_value_for(12840, 0) # Zork Rocks in Perma-Suck Machine
+ self._write_game_state_value_for(11886, 1) # Student ID Taken
+ self._write_game_state_value_for(16279, 1) # Prozork Tablet Taken
+ self._write_game_state_value_for(13260, 1) # GOLGATEM Scroll Taken
+ self._write_game_state_value_for(4834, 1) # Flatheadia Fudge Taken
+ self._write_game_state_value_for(4746, 1) # Jar of Hotbugs Taken
+ self._write_game_state_value_for(4755, 1) # Hungus Lard Taken
+ self._write_game_state_value_for(4758, 1) # Mug Taken
+ self._write_game_state_value_for(3716, 1) # NARWILE Scroll Taken
+ self._write_game_state_value_for(17147, 1) # Lucy Totem Taken
+ self._write_game_state_value_for(9818, 1) # Middle Telegraph Hammer Taken
+ self._write_game_state_value_for(3766, 0) # ANS Scroll in Window
+ self._write_game_state_value_for(4980, 0) # ANS Scroll in Window
+ self._write_game_state_value_for(3768, 0) # GIV Scroll in Window
+ self._write_game_state_value_for(4978, 0) # GIV Scroll in Window
+ self._write_game_state_value_for(3765, 0) # SNA Scroll in Window
+ self._write_game_state_value_for(4979, 0) # SNA Scroll in Window
+ self._write_game_state_value_for(3767, 0) # VIG Scroll in Window
+ self._write_game_state_value_for(4977, 0) # VIG Scroll in Window
+ self._write_game_state_value_for(15065, 1) # Brog's Bickering Torch Taken
+ self._write_game_state_value_for(15088, 1) # Brog's Flickering Torch Taken
+ self._write_game_state_value_for(2628, 4) # Brog's Grue Eggs Taken
+ self._write_game_state_value_for(2971, 1) # Brog's Plank Taken
+ self._write_game_state_value_for(1340, 1) # Griff's Inflatable Sea Captain Taken
+ self._write_game_state_value_for(1341, 1) # Griff's Inflatable Raft Taken
+ self._write_game_state_value_for(1477, 1) # Griff's Air Pump Taken
+ self._write_game_state_value_for(1814, 1) # Griff's Dragon Tooth Taken
+ self._write_game_state_value_for(15403, 0) # Lucy's Cards Taken
+ self._write_game_state_value_for(15404, 1) # Lucy's Cards Taken
+ self._write_game_state_value_for(15405, 4) # Lucy's Cards Taken
+ self._write_game_state_value_for(5222, 1) # User Has Spell Book
+ self._write_game_state_value_for(13930, 1) # Skip Well Cutscenes
+ self._write_game_state_value_for(19057, 1) # Skip Well Cutscenes
+ self._write_game_state_value_for(13934, 1) # Skip Well Cutscenes
+ self._write_game_state_value_for(13935, 1) # Skip Well Cutscenes
+ self._write_game_state_value_for(13384, 1) # Skip Meanwhile... Cutscene
+ self._write_game_state_value_for(8620, 1) # First Coin Paid to Charon
+ self._write_game_state_value_for(8731, 1) # First Coin Paid to Charon
+
+ def _apply_conditional_game_state(self):
+ # Can teleport to Dungeon Master's Lair
+ if self._player_has(ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR):
+ self._write_game_state_value_for(2203, 1)
+ else:
+ self._write_game_state_value_for(2203, 0)
+
+ # Can teleport to GUE Tech
+ if self._player_has(ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH):
+ self._write_game_state_value_for(7132, 1)
+ else:
+ self._write_game_state_value_for(7132, 0)
+
+ # Can Teleport to Spell Lab
+ if self._player_has(ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB):
+ self._write_game_state_value_for(16545, 1)
+ else:
+ self._write_game_state_value_for(16545, 0)
+
+ # Can Teleport to Hades
+ if self._player_has(ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES):
+ self._write_game_state_value_for(7119, 1)
+ else:
+ self._write_game_state_value_for(7119, 0)
+
+ # Can Teleport to Monastery Station
+ if self._player_has(ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY):
+ self._write_game_state_value_for(7148, 1)
+ else:
+ self._write_game_state_value_for(7148, 0)
+
+ # Initial Totemizer Destination
+ should_force_initial_totemizer_destination: bool = True
+
+ if self._player_has(ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_HALL_OF_INQUISITION):
+ should_force_initial_totemizer_destination = False
+ elif self._player_has(ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL):
+ should_force_initial_totemizer_destination = False
+ elif self._player_has(ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_INFINITY):
+ should_force_initial_totemizer_destination = False
+ elif self._player_has(ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_SURFACE_OF_MERZ):
+ should_force_initial_totemizer_destination = False
+
+ if should_force_initial_totemizer_destination:
+ self._write_game_state_value_for(9617, 2)
+
+ # Pouch of Zorkmids
+ if self._player_has(ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS):
+ self._write_game_state_value_for(5827, 1)
+ else:
+ self._write_game_state_value_for(5827, 0)
+
+ # Brog Torches
+ if self._player_is_brog() and self._player_has(ZorkGrandInquisitorItems.BROGS_BICKERING_TORCH):
+ self._write_game_state_value_for(10999, 1)
+ else:
+ self._write_game_state_value_for(10999, 0)
+
+ if self._player_is_brog() and self._player_has(ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH):
+ self._write_game_state_value_for(10998, 1)
+ else:
+ self._write_game_state_value_for(10998, 0)
+
+ # Monastery Rope
+ if ZorkGrandInquisitorLocations.I_HOPE_YOU_CAN_CLIMB_UP_THERE in self.completed_locations:
+ self._write_game_state_value_for(9637, 1)
+
+ def _apply_permanent_game_flags(self) -> None:
+ self._write_game_flags_value_for(9437, 2) # Monastery Exhibit Door to Outside
+ self._write_game_flags_value_for(3074, 2) # White House Door
+ self._write_game_flags_value_for(13005, 2) # Map
+ self._write_game_flags_value_for(13006, 2) # Sword
+ self._write_game_flags_value_for(13007, 2) # Sword
+ self._write_game_flags_value_for(13389, 2) # Moss of Mareilon
+ self._write_game_flags_value_for(4301, 2) # Quelbee Honeycomb
+ self._write_game_flags_value_for(12895, 2) # Change Machine Money
+ self._write_game_flags_value_for(4150, 2) # Prozorked Snapdragon
+ self._write_game_flags_value_for(13413, 2) # Letter Opener
+ self._write_game_flags_value_for(15403, 2) # Lucy's Cards
+
+ def _check_for_completed_locations(self) -> None:
+ location: ZorkGrandInquisitorLocations
+ data: ZorkGrandInquisitorLocationData
+ for location, data in location_data.items():
+ if location in self.completed_locations or not isinstance(
+ location, ZorkGrandInquisitorLocations
+ ):
+ continue
+
+ is_location_completed: bool = True
+
+ trigger: [Union[str, int]]
+ value: Union[str, int, Tuple[int, ...]]
+ for trigger, value in data.game_state_trigger:
+ if trigger == "location":
+ if not self._player_is_at(value):
+ is_location_completed = False
+ break
+ elif isinstance(trigger, int):
+ if isinstance(value, int):
+ if self._read_game_state_value_for(trigger) != value:
+ is_location_completed = False
+ break
+ elif isinstance(value, tuple):
+ if self._read_game_state_value_for(trigger) not in value:
+ is_location_completed = False
+ break
+ else:
+ is_location_completed = False
+ break
+ else:
+ is_location_completed = False
+ break
+
+ if is_location_completed:
+ self.completed_locations.add(location)
+ self.completed_locations_queue.append(location)
+
+ def _check_for_missable_locations_to_grant(self) -> None:
+ missable_location: ZorkGrandInquisitorLocations
+ for missable_location in self.missable_locations:
+ if missable_location in self.completed_locations:
+ continue
+
+ data: ZorkGrandInquisitorLocationData = location_data[missable_location]
+
+ if ZorkGrandInquisitorTags.DEATHSANITY in data.tags and not self.option_deathsanity:
+ continue
+
+ condition_data: ZorkGrandInquisitorMissableLocationGrantConditionsData = (
+ missable_location_grant_conditions_data.get(missable_location)
+ )
+
+ if condition_data is None:
+ self.log_debug(f"Missable Location {missable_location.value} has no grant conditions")
+ continue
+
+ if condition_data.location_condition in self.completed_locations:
+ grant_location: bool = True
+
+ item: ZorkGrandInquisitorItems
+ for item in condition_data.item_conditions or tuple():
+ if self._player_doesnt_have(item):
+ grant_location = False
+ break
+
+ if grant_location:
+ self.completed_locations_queue.append(missable_location)
+
+ def _process_received_items(self) -> None:
+ while len(self.received_items_queue) > 0:
+ item: ZorkGrandInquisitorItems = self.received_items_queue.popleft()
+ data: ZorkGrandInquisitorItemData = item_data[item]
+
+ if ZorkGrandInquisitorTags.FILLER in data.tags:
+ continue
+
+ self.received_items.add(item)
+
+ def _manage_hotspots(self) -> None:
+ hotspot_item: ZorkGrandInquisitorItems
+ for hotspot_item in self.all_hotspot_items:
+ data: ZorkGrandInquisitorItemData = item_data[hotspot_item]
+
+ if hotspot_item not in self.received_items:
+ key: int
+ for key in data.statemap_keys:
+ self._write_game_flags_value_for(key, 2)
+ else:
+ if hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_666_MAILBOX:
+ if self.game_state_manager.game_location == "hp5g":
+ if self._read_game_state_value_for(9113) == 0:
+ self._write_game_flags_value_for(9116, 0)
+ else:
+ self._write_game_flags_value_for(9116, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS:
+ if self.game_state_manager.game_location == "qb2g":
+ if self._read_game_state_value_for(15433) == 0:
+ self._write_game_flags_value_for(15434, 0)
+ else:
+ self._write_game_flags_value_for(15434, 2)
+
+ if self._read_game_state_value_for(15435) == 0:
+ self._write_game_flags_value_for(15436, 0)
+ else:
+ self._write_game_flags_value_for(15436, 2)
+
+ if self._read_game_state_value_for(15437) == 0:
+ self._write_game_flags_value_for(15438, 0)
+ else:
+ self._write_game_flags_value_for(15438, 2)
+
+ if self._read_game_state_value_for(15439) == 0:
+ self._write_game_flags_value_for(15440, 0)
+ else:
+ self._write_game_flags_value_for(15440, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_BLANK_SCROLL_BOX:
+ if self.game_state_manager.game_location == "tp2g":
+ if self._read_game_state_value_for(12095) == 1:
+ self._write_game_flags_value_for(9115, 2)
+ else:
+ self._write_game_flags_value_for(9115, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_BLINDS:
+ if self.game_state_manager.game_location == "dv1e":
+ if self._read_game_state_value_for(4743) == 0:
+ self._write_game_flags_value_for(4799, 0)
+ else:
+ self._write_game_flags_value_for(4799, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS:
+ if self.game_state_manager.game_location == "tr5g":
+ key: int
+ for key in data.statemap_keys:
+ self._write_game_flags_value_for(key, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT:
+ if self.game_state_manager.game_location == "tr5g":
+ self._write_game_flags_value_for(12702, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_VACUUM_SLOT:
+ if self.game_state_manager.game_location == "tr5m":
+ self._write_game_flags_value_for(12909, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_CHANGE_MACHINE_SLOT:
+ if self.game_state_manager.game_location == "tr5j":
+ if self._read_game_state_value_for(12892) == 0:
+ self._write_game_flags_value_for(12900, 0)
+ else:
+ self._write_game_flags_value_for(12900, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR:
+ if self.game_state_manager.game_location == "dw1e":
+ if self._read_game_state_value_for(4983) == 0:
+ self._write_game_flags_value_for(5010, 0)
+ else:
+ self._write_game_flags_value_for(5010, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT:
+ if self.game_state_manager.game_location == "me2j":
+ if self._read_game_state_value_for(9491) == 2:
+ self._write_game_flags_value_for(9539, 0)
+ else:
+ self._write_game_flags_value_for(9539, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER:
+ if self.game_state_manager.game_location == "me2j":
+ if self._read_game_state_value_for(9546) == 2 or self._read_game_state_value_for(9419) == 1:
+ self._write_game_flags_value_for(19712, 2)
+ else:
+ self._write_game_flags_value_for(19712, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT:
+ if self.game_state_manager.game_location == "sg1f":
+ self._write_game_flags_value_for(2586, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_DENTED_LOCKER:
+ if self.game_state_manager.game_location == "th3j":
+ five_is_open: bool = self._read_game_state_value_for(11847) == 1
+ six_is_open: bool = self._read_game_state_value_for(11840) == 1
+ seven_is_open: bool = self._read_game_state_value_for(11841) == 1
+ eight_is_open: bool = self._read_game_state_value_for(11848) == 1
+
+ rocks_in_six: bool = self._read_game_state_value_for(11769) == 1
+ six_blasted: bool = self._read_game_state_value_for(11770) == 1
+
+ if five_is_open or six_is_open or seven_is_open or eight_is_open or rocks_in_six or six_blasted:
+ self._write_game_flags_value_for(11878, 2)
+ else:
+ self._write_game_flags_value_for(11878, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_DIRT_MOUND:
+ if self.game_state_manager.game_location == "te5e":
+ if self._read_game_state_value_for(11747) == 0:
+ self._write_game_flags_value_for(11751, 0)
+ else:
+ self._write_game_flags_value_for(11751, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH:
+ if self.game_state_manager.game_location == "pe2e":
+ self._write_game_flags_value_for(15147, 0)
+ self._write_game_flags_value_for(15153, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_DRAGON_CLAW:
+ if self.game_state_manager.game_location == "cd70":
+ self._write_game_flags_value_for(1705, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS:
+ if self.game_state_manager.game_location == "cd3h":
+ raft_in_left: bool = self._read_game_state_value_for(1301) == 1
+ raft_in_right: bool = self._read_game_state_value_for(1304) == 1
+ raft_inflated: bool = self._read_game_state_value_for(1379) == 1
+
+ captain_in_left: bool = self._read_game_state_value_for(1374) == 1
+ captain_in_right: bool = self._read_game_state_value_for(1381) == 1
+ captain_inflated: bool = self._read_game_state_value_for(1378) == 1
+
+ left_inflated: bool = (raft_in_left and raft_inflated) or (captain_in_left and captain_inflated)
+
+ right_inflated: bool = (raft_in_right and raft_inflated) or (
+ captain_in_right and captain_inflated
+ )
+
+ if left_inflated:
+ self._write_game_flags_value_for(1425, 2)
+ else:
+ self._write_game_flags_value_for(1425, 0)
+
+ if right_inflated:
+ self._write_game_flags_value_for(1426, 2)
+ else:
+ self._write_game_flags_value_for(1426, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE:
+ if self.game_state_manager.game_location == "uc3e":
+ if self._read_game_state_value_for(13060) == 0:
+ self._write_game_flags_value_for(13106, 0)
+ else:
+ self._write_game_flags_value_for(13106, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS:
+ if self.game_state_manager.game_location == "ue1e":
+ if self._read_game_state_value_for(14318) == 0:
+ self._write_game_flags_value_for(13219, 0)
+ self._write_game_flags_value_for(13220, 0)
+ self._write_game_flags_value_for(13221, 0)
+ self._write_game_flags_value_for(13222, 0)
+ else:
+ self._write_game_flags_value_for(13219, 2)
+ self._write_game_flags_value_for(13220, 2)
+ self._write_game_flags_value_for(13221, 2)
+ self._write_game_flags_value_for(13222, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS:
+ if self.game_state_manager.game_location == "ue1e":
+ if self._read_game_state_value_for(14318) == 0:
+ self._write_game_flags_value_for(14327, 0)
+ self._write_game_flags_value_for(14332, 0)
+ self._write_game_flags_value_for(14337, 0)
+ self._write_game_flags_value_for(14342, 0)
+ else:
+ self._write_game_flags_value_for(14327, 2)
+ self._write_game_flags_value_for(14332, 2)
+ self._write_game_flags_value_for(14337, 2)
+ self._write_game_flags_value_for(14342, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_COIN_SLOT:
+ if self.game_state_manager.game_location == "tr5e":
+ self._write_game_flags_value_for(12528, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_DOORS:
+ if self.game_state_manager.game_location == "tr5e":
+ if self._read_game_state_value_for(12220) == 0:
+ self._write_game_flags_value_for(12523, 2)
+ self._write_game_flags_value_for(12524, 2)
+ self._write_game_flags_value_for(12525, 2)
+ else:
+ self._write_game_flags_value_for(12523, 0)
+ self._write_game_flags_value_for(12524, 0)
+ self._write_game_flags_value_for(12525, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_GLASS_CASE:
+ if self.game_state_manager.game_location == "uc1g":
+ if self._read_game_state_value_for(12931) == 1 or self._read_game_state_value_for(12929) == 1:
+ self._write_game_flags_value_for(13002, 2)
+ else:
+ self._write_game_flags_value_for(13002, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL:
+ if self.game_state_manager.game_location == "pe5e":
+ if self._read_game_state_value_for(10277) == 0:
+ self._write_game_flags_value_for(10726, 0)
+ else:
+ self._write_game_flags_value_for(10726, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_DOOR:
+ if self.game_state_manager.game_location == "tr1k":
+ if self._read_game_state_value_for(12212) == 0:
+ self._write_game_flags_value_for(12280, 0)
+ else:
+ self._write_game_flags_value_for(12280, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_GRASS:
+ if self.game_state_manager.game_location in ("te10", "te1g", "te20", "te30", "te40"):
+ key: int
+ for key in data.statemap_keys:
+ self._write_game_flags_value_for(key, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS:
+ if self.game_state_manager.game_location == "hp1e":
+ if self._read_game_state_value_for(8431) == 1:
+ key: int
+ for key in data.statemap_keys:
+ self._write_game_flags_value_for(key, 0)
+ else:
+ key: int
+ for key in data.statemap_keys:
+ self._write_game_flags_value_for(key, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER:
+ if self.game_state_manager.game_location == "hp1e":
+ if self._read_game_state_value_for(8431) == 1:
+ self._write_game_flags_value_for(8446, 2)
+ else:
+ self._write_game_flags_value_for(8446, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_HARRY:
+ if self.game_state_manager.game_location == "dg4e":
+ if self._read_game_state_value_for(4237) == 1 and self._read_game_state_value_for(4034) == 1:
+ self._write_game_flags_value_for(4260, 2)
+ else:
+ self._write_game_flags_value_for(4260, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY:
+ if self.game_state_manager.game_location == "dg4h":
+ if self._read_game_state_value_for(4279) == 1:
+ self._write_game_flags_value_for(18026, 2)
+ else:
+ self._write_game_flags_value_for(18026, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH:
+ if self.game_state_manager.game_location == "dg4g":
+ if self._read_game_state_value_for(4034) == 1:
+ self._write_game_flags_value_for(17623, 2)
+ else:
+ self._write_game_flags_value_for(17623, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR:
+ if self.game_state_manager.game_location == "uc4e":
+ if self._read_game_state_value_for(13062) == 1:
+ self._write_game_flags_value_for(13140, 2)
+ else:
+ self._write_game_flags_value_for(13140, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR:
+ if self.game_state_manager.game_location == "pe1e":
+ if self._read_game_state_value_for(10451) == 1:
+ self._write_game_flags_value_for(10441, 2)
+ else:
+ self._write_game_flags_value_for(10441, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_LOUDSPEAKER_VOLUME_BUTTONS:
+ if self.game_state_manager.game_location == "pe2j":
+ self._write_game_flags_value_for(19632, 0)
+ self._write_game_flags_value_for(19627, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_DOOR:
+ if self.game_state_manager.game_location == "sw4e":
+ if self._read_game_state_value_for(2989) == 1:
+ self._write_game_flags_value_for(3025, 2)
+ else:
+ self._write_game_flags_value_for(3025, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG:
+ if self.game_state_manager.game_location == "sw4e":
+ self._write_game_flags_value_for(3036, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_MIRROR:
+ if self.game_state_manager.game_location == "dw1f":
+ self._write_game_flags_value_for(5031, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_MONASTERY_VENT:
+ if self.game_state_manager.game_location == "um1e":
+ if self._read_game_state_value_for(9637) == 0:
+ self._write_game_flags_value_for(13597, 0)
+ else:
+ self._write_game_flags_value_for(13597, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_MOSSY_GRATE:
+ if self.game_state_manager.game_location == "ue2g":
+ if self._read_game_state_value_for(13278) == 0:
+ self._write_game_flags_value_for(13390, 0)
+ else:
+ self._write_game_flags_value_for(13390, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR:
+ if self.game_state_manager.game_location == "qe1e":
+ if self._player_is_brog():
+ self._write_game_flags_value_for(2447, 0)
+ elif self._player_is_griff():
+ self._write_game_flags_value_for(2455, 0)
+ elif self._player_is_lucy():
+ if self._read_game_state_value_for(2457) == 0:
+ self._write_game_flags_value_for(2455, 0)
+ else:
+ self._write_game_flags_value_for(2455, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS:
+ if self.game_state_manager.game_location == "tr3h":
+ if self._read_game_state_value_for(11777) == 1:
+ self._write_game_flags_value_for(12389, 2)
+ else:
+ self._write_game_flags_value_for(12389, 0)
+
+ self._write_game_state_value_for(12390, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE:
+ if self.game_state_manager.game_location == "dg4f":
+ if self._read_game_state_value_for(4241) == 1:
+ self._write_game_flags_value_for(4302, 2)
+ else:
+ self._write_game_flags_value_for(4302, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE:
+ if self.game_state_manager.game_location == "tp1e":
+ if self._read_game_state_value_for(16342) == 1:
+ self._write_game_flags_value_for(16383, 2)
+ self._write_game_flags_value_for(16384, 2)
+ else:
+ self._write_game_flags_value_for(16383, 0)
+ self._write_game_flags_value_for(16384, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE:
+ if self.game_state_manager.game_location == "sg6e":
+ if self._read_game_state_value_for(15715) == 1:
+ self._write_game_flags_value_for(2769, 2)
+ else:
+ self._write_game_flags_value_for(2769, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON:
+ if self.game_state_manager.game_location == "dg2f":
+ if self._read_game_state_value_for(4114) == 1 or self._read_game_state_value_for(4115) == 1:
+ self._write_game_flags_value_for(4149, 2)
+ else:
+ self._write_game_flags_value_for(4149, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_BUTTONS:
+ if self.game_state_manager.game_location == "tr5f":
+ self._write_game_flags_value_for(12584, 0)
+ self._write_game_flags_value_for(12585, 0)
+ self._write_game_flags_value_for(12586, 0)
+ self._write_game_flags_value_for(12587, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_COIN_SLOT:
+ if self.game_state_manager.game_location == "tr5f":
+ self._write_game_flags_value_for(12574, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SOUVENIR_COIN_SLOT:
+ if self.game_state_manager.game_location == "ue2j":
+ if self._read_game_state_value_for(13408) == 1:
+ self._write_game_flags_value_for(13412, 2)
+ else:
+ self._write_game_flags_value_for(13412, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER:
+ if self.game_state_manager.game_location == "tp4g":
+ self._write_game_flags_value_for(12170, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM:
+ if self.game_state_manager.game_location == "tp1e":
+ if self._read_game_state_value_for(16342) == 1 and self._read_game_state_value_for(16374) == 0:
+ self._write_game_flags_value_for(16382, 0)
+ else:
+ self._write_game_flags_value_for(16382, 2)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM:
+ if self.game_state_manager.game_location == "dg3e":
+ self._write_game_flags_value_for(4209, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_STUDENT_ID_MACHINE:
+ if self.game_state_manager.game_location == "th3r":
+ self._write_game_flags_value_for(11973, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT:
+ if self.game_state_manager.game_location == "uc6e":
+ self._write_game_flags_value_for(13168, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY:
+ if self.game_state_manager.game_location == "qb2e":
+ if self._read_game_state_value_for(15395) == 1:
+ self._write_game_flags_value_for(15396, 2)
+ else:
+ self._write_game_flags_value_for(15396, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH:
+ if self.game_state_manager.game_location == "mt2e":
+ self._write_game_flags_value_for(9706, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS:
+ if self.game_state_manager.game_location == "mt2g":
+ self._write_game_flags_value_for(9728, 0)
+ self._write_game_flags_value_for(9729, 0)
+ self._write_game_flags_value_for(9730, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_WELL:
+ if self.game_state_manager.game_location == "pc1e":
+ self._write_game_flags_value_for(10314, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM:
+ if self.game_state_manager.game_location == "us2e":
+ self._write_game_flags_value_for(13757, 0)
+ elif self.game_state_manager.game_location == "ue2e":
+ self._write_game_flags_value_for(13297, 0)
+ elif self.game_state_manager.game_location == "uh2e":
+ self._write_game_flags_value_for(13486, 0)
+ elif self.game_state_manager.game_location == "um2e":
+ self._write_game_flags_value_for(13625, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES:
+ if self.game_state_manager.game_location == "us2e":
+ self._write_game_flags_value_for(13758, 0)
+ elif self.game_state_manager.game_location == "ue2e":
+ self._write_game_flags_value_for(13309, 0)
+ elif self.game_state_manager.game_location == "uh2e":
+ self._write_game_flags_value_for(13498, 0)
+ elif self.game_state_manager.game_location == "um2e":
+ self._write_game_flags_value_for(13637, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY:
+ if self.game_state_manager.game_location == "us2e":
+ self._write_game_flags_value_for(13759, 0)
+ elif self.game_state_manager.game_location == "ue2e":
+ self._write_game_flags_value_for(13316, 0)
+ elif self.game_state_manager.game_location == "uh2e":
+ self._write_game_flags_value_for(13505, 0)
+ elif self.game_state_manager.game_location == "um2e":
+ self._write_game_flags_value_for(13644, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_HALL_OF_INQUISITION:
+ if self.game_state_manager.game_location == "mt1f":
+ self._write_game_flags_value_for(9660, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_INFINITY:
+ if self.game_state_manager.game_location == "mt1f":
+ self._write_game_flags_value_for(9666, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL:
+ if self.game_state_manager.game_location == "mt1f":
+ self._write_game_flags_value_for(9668, 0)
+ elif hotspot_item == ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_SURFACE_OF_MERZ:
+ if self.game_state_manager.game_location == "mt1f":
+ self._write_game_flags_value_for(9662, 0)
+
+ def _manage_items(self) -> None:
+ if self._player_is_afgncaap():
+ self.available_inventory_slots = self._determine_available_inventory_slots()
+
+ received_inventory_items: Set[ZorkGrandInquisitorItems]
+ received_inventory_items = self.received_items & self.possible_inventory_items
+
+ received_inventory_items = self._filter_received_inventory_items(received_inventory_items)
+ elif self._player_is_totem():
+ self.available_inventory_slots = self._determine_available_inventory_slots(is_totem=True)
+
+ received_inventory_items: Set[ZorkGrandInquisitorItems]
+
+ if self._player_is_brog():
+ received_inventory_items = self.received_items & self.brog_items
+ received_inventory_items = self._filter_received_brog_inventory_items(received_inventory_items)
+ elif self._player_is_griff():
+ received_inventory_items = self.received_items & self.griff_items
+ received_inventory_items = self._filter_received_griff_inventory_items(received_inventory_items)
+ elif self._player_is_lucy():
+ received_inventory_items = self.received_items & self.lucy_items
+ received_inventory_items = self._filter_received_lucy_inventory_items(received_inventory_items)
+ else:
+ return None
+ else:
+ return None
+
+ game_state_inventory_items: Set[ZorkGrandInquisitorItems] = self._determine_game_state_inventory()
+
+ inventory_items_to_remove: Set[ZorkGrandInquisitorItems]
+ inventory_items_to_remove = game_state_inventory_items - received_inventory_items
+
+ inventory_items_to_add: Set[ZorkGrandInquisitorItems]
+ inventory_items_to_add = received_inventory_items - game_state_inventory_items
+
+ item: ZorkGrandInquisitorItems
+ for item in inventory_items_to_remove:
+ self._remove_from_inventory(item)
+
+ item: ZorkGrandInquisitorItems
+ for item in inventory_items_to_add:
+ self._add_to_inventory(item)
+
+ # Item Deduplication (Just in Case)
+ seen_items: Set[int] = set()
+
+ i: int
+ for i in range(151, 171):
+ item: int = self._read_game_state_value_for(i)
+
+ if item in seen_items:
+ self._write_game_state_value_for(i, 0)
+ else:
+ seen_items.add(item)
+
+ def _apply_conditional_teleports(self) -> None:
+ if self._player_is_at("uw1x"):
+ self.game_state_manager.set_game_location("uw10", 0)
+
+ if self._player_is_at("uw1k") and self._read_game_state_value_for(13938) == 0:
+ self.game_state_manager.set_game_location("pc10", 250)
+
+ if self._player_is_at("ue1q"):
+ self.game_state_manager.set_game_location("ue1e", 0)
+
+ if self._player_is_at("ej10"):
+ self.game_state_manager.set_game_location("uc10", 1200)
+
+ if self._read_game_state_value_for(9) == 224:
+ self._write_game_state_value_for(9, 0)
+ self.game_state_manager.set_game_location("uc10", 1200)
+
+ def _check_for_victory(self) -> None:
+ if self.option_goal == ZorkGrandInquisitorGoals.THREE_ARTIFACTS:
+ coconut_is_placed = self._read_game_state_value_for(2200) == 1
+ cube_is_placed = self._read_game_state_value_for(2322) == 1
+ skull_is_placed = self._read_game_state_value_for(2321) == 1
+
+ self.goal_completed = coconut_is_placed and cube_is_placed and skull_is_placed
+
+ def _determine_game_state_inventory(self) -> Set[ZorkGrandInquisitorItems]:
+ game_state_inventory: Set[ZorkGrandInquisitorItems] = set()
+
+ # Item on Cursor
+ item_on_cursor: int = self._read_game_state_value_for(9)
+
+ if item_on_cursor != 0:
+ if item_on_cursor in self.game_id_to_items:
+ game_state_inventory.add(self.game_id_to_items[item_on_cursor])
+
+ # Item in Inspector
+ item_in_inspector: int = self._read_game_state_value_for(4512)
+
+ if item_in_inspector != 0:
+ if item_in_inspector in self.game_id_to_items:
+ game_state_inventory.add(self.game_id_to_items[item_in_inspector])
+
+ # Items in Inventory Slots
+ i: int
+ for i in range(151, 171):
+ if self._read_game_state_value_for(i) != 0:
+ if self._read_game_state_value_for(i) in self.game_id_to_items:
+ game_state_inventory.add(
+ self.game_id_to_items[self._read_game_state_value_for(i)]
+ )
+
+ # Pouch of Zorkmids
+ if self._read_game_state_value_for(5827) == 1:
+ game_state_inventory.add(ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS)
+
+ # Spells
+ i: int
+ for i in range(191, 203):
+ if self._read_game_state_value_for(i) == 1:
+ if i in self.game_id_to_items:
+ game_state_inventory.add(self.game_id_to_items[i])
+
+ # Totems
+ if self._read_game_state_value_for(4853) == 1:
+ game_state_inventory.add(ZorkGrandInquisitorItems.TOTEM_BROG)
+
+ if self._read_game_state_value_for(4315) == 1:
+ game_state_inventory.add(ZorkGrandInquisitorItems.TOTEM_GRIFF)
+
+ if self._read_game_state_value_for(5223) == 1:
+ game_state_inventory.add(ZorkGrandInquisitorItems.TOTEM_LUCY)
+
+ return game_state_inventory
+
+ def _add_to_inventory(self, item: ZorkGrandInquisitorItems) -> None:
+ if item == ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS:
+ return None
+
+ data: ZorkGrandInquisitorItemData = item_data[item]
+
+ if ZorkGrandInquisitorTags.INVENTORY_ITEM in data.tags:
+ if len(self.available_inventory_slots): # Inventory slot overflow protection
+ inventory_slot: int = self.available_inventory_slots.pop()
+ self._write_game_state_value_for(inventory_slot, data.statemap_keys[0])
+ elif ZorkGrandInquisitorTags.SPELL in data.tags:
+ self._write_game_state_value_for(data.statemap_keys[0], 1)
+ elif ZorkGrandInquisitorTags.TOTEM in data.tags:
+ self._write_game_state_value_for(data.statemap_keys[0], 1)
+
+ def _remove_from_inventory(self, item: ZorkGrandInquisitorItems) -> None:
+ if item == ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS:
+ return None
+
+ data: ZorkGrandInquisitorItemData = item_data[item]
+
+ if ZorkGrandInquisitorTags.INVENTORY_ITEM in data.tags:
+ inventory_slot: Optional[int] = self._inventory_slot_for(item)
+
+ if inventory_slot is None:
+ return None
+
+ self._write_game_state_value_for(inventory_slot, 0)
+
+ if inventory_slot != 9:
+ self.available_inventory_slots.add(inventory_slot)
+ elif ZorkGrandInquisitorTags.SPELL in data.tags:
+ self._write_game_state_value_for(data.statemap_keys[0], 0)
+ elif ZorkGrandInquisitorTags.TOTEM in data.tags:
+ self._write_game_state_value_for(data.statemap_keys[0], 0)
+
+ def _determine_available_inventory_slots(self, is_totem: bool = False) -> Set[int]:
+ available_inventory_slots: Set[int] = set()
+
+ inventory_slot_range_end: int = 171
+
+ if is_totem:
+ if self._player_is_brog():
+ inventory_slot_range_end = 161
+ elif self._player_is_griff():
+ inventory_slot_range_end = 160
+ elif self._player_is_lucy():
+ inventory_slot_range_end = 157
+
+ i: int
+ for i in range(151, inventory_slot_range_end):
+ if self._read_game_state_value_for(i) == 0:
+ available_inventory_slots.add(i)
+
+ return available_inventory_slots
+
+ def _inventory_slot_for(self, item) -> Optional[int]:
+ data: ZorkGrandInquisitorItemData = item_data[item]
+
+ if ZorkGrandInquisitorTags.INVENTORY_ITEM in data.tags:
+ i: int
+ for i in range(151, 171):
+ if self._read_game_state_value_for(i) == data.statemap_keys[0]:
+ return i
+
+ if self._read_game_state_value_for(9) == data.statemap_keys[0]:
+ return 9
+
+ if self._read_game_state_value_for(4512) == data.statemap_keys[0]:
+ return 4512
+
+ return None
+
+ def _filter_received_inventory_items(
+ self, received_inventory_items: Set[ZorkGrandInquisitorItems]
+ ) -> Set[ZorkGrandInquisitorItems]:
+ to_filter_inventory_items: Set[ZorkGrandInquisitorItems] = self.totem_items
+
+ inventory_item_values: Set[int] = set()
+
+ i: int
+ for i in range(151, 171):
+ inventory_item_values.add(self._read_game_state_value_for(i))
+
+ cursor_item_value: int = self._read_game_state_value_for(9)
+ inspector_item_value: int = self._read_game_state_value_for(4512)
+
+ inventory_item_values.add(cursor_item_value)
+ inventory_item_values.add(inspector_item_value)
+
+ item: ZorkGrandInquisitorItems
+ for item in received_inventory_items:
+ if item == ZorkGrandInquisitorItems.FLATHEADIA_FUDGE:
+ if self._read_game_state_value_for(4766) == 1:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(4869) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.HUNGUS_LARD:
+ if self._read_game_state_value_for(4870) == 1:
+ to_filter_inventory_items.add(item)
+ elif (
+ self._read_game_state_value_for(4244) == 1
+ and self._read_game_state_value_for(4309) == 0
+ ):
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.JAR_OF_HOTBUGS:
+ if self._read_game_state_value_for(4750) == 1:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(4869) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.LANTERN:
+ if self._read_game_state_value_for(10477) == 1:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(5221) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.LARGE_TELEGRAPH_HAMMER:
+ if self._read_game_state_value_for(9491) == 3:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.MAP:
+ if self._read_game_state_value_for(16618) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.MEAD_LIGHT:
+ if 105 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(17620) > 0:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(4034) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.MOSS_OF_MAREILON:
+ if self._read_game_state_value_for(4763) == 1:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(4869) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.MUG:
+ if self._read_game_state_value_for(4772) == 1:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(4869) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.OLD_SCRATCH_CARD:
+ if 32 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(12892) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.PERMA_SUCK_MACHINE:
+ if self._read_game_state_value_for(12218) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.PLASTIC_SIX_PACK_HOLDER:
+ if self._read_game_state_value_for(15150) == 3:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(10421) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.PROZORK_TABLET:
+ if self._read_game_state_value_for(4115) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.QUELBEE_HONEYCOMB:
+ if self._read_game_state_value_for(4769) == 1:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(4869) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.ROPE:
+ if 22 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif 111 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif (
+ self._read_game_state_value_for(10304) == 1
+ and not self._read_game_state_value_for(13938) == 1
+ ):
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15150) == 83:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.SCROLL_FRAGMENT_ANS:
+ if 41 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif 98 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(201) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.SCROLL_FRAGMENT_GIV:
+ if 48 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif 98 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(201) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.SNAPDRAGON:
+ if self._read_game_state_value_for(4199) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.STUDENT_ID:
+ if self._read_game_state_value_for(11838) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.SUBWAY_TOKEN:
+ if self._read_game_state_value_for(13167) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.SWORD:
+ if 22 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif 100 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif 111 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.ZIMDOR_SCROLL:
+ if 105 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(17620) == 3:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(4034) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.ZORK_ROCKS:
+ if self._read_game_state_value_for(12486) == 1:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(12487) == 1:
+ to_filter_inventory_items.add(item)
+ elif 52 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(11769) == 1:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(11840) == 1:
+ to_filter_inventory_items.add(item)
+
+ return received_inventory_items - to_filter_inventory_items
+
+ def _filter_received_brog_inventory_items(
+ self, received_inventory_items: Set[ZorkGrandInquisitorItems]
+ ) -> Set[ZorkGrandInquisitorItems]:
+ to_filter_inventory_items: Set[ZorkGrandInquisitorItems] = set()
+
+ inventory_item_values: Set[int] = set()
+
+ i: int
+ for i in range(151, 161):
+ inventory_item_values.add(self._read_game_state_value_for(i))
+
+ cursor_item_value: int = self._read_game_state_value_for(9)
+ inspector_item_value: int = self._read_game_state_value_for(2194)
+
+ inventory_item_values.add(cursor_item_value)
+ inventory_item_values.add(inspector_item_value)
+
+ item: ZorkGrandInquisitorItems
+ for item in received_inventory_items:
+ if item == ZorkGrandInquisitorItems.BROGS_BICKERING_TORCH:
+ if 103 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH:
+ if 104 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.BROGS_GRUE_EGG:
+ if self._read_game_state_value_for(2577) == 1:
+ to_filter_inventory_items.add(item)
+ elif 71 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(2641) == 1:
+ to_filter_inventory_items.add(item)
+
+ return received_inventory_items - to_filter_inventory_items
+
+ def _filter_received_griff_inventory_items(
+ self, received_inventory_items: Set[ZorkGrandInquisitorItems]
+ ) -> Set[ZorkGrandInquisitorItems]:
+ to_filter_inventory_items: Set[ZorkGrandInquisitorItems] = set()
+
+ inventory_item_values: Set[int] = set()
+
+ i: int
+ for i in range(151, 160):
+ inventory_item_values.add(self._read_game_state_value_for(i))
+
+ cursor_item_value: int = self._read_game_state_value_for(9)
+ inspector_item_value: int = self._read_game_state_value_for(4512)
+
+ inventory_item_values.add(cursor_item_value)
+ inventory_item_values.add(inspector_item_value)
+
+ item: ZorkGrandInquisitorItems
+ for item in received_inventory_items:
+ if item == ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT:
+ if self._read_game_state_value_for(1301) == 1:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(1304) == 1:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(16562) == 1:
+ to_filter_inventory_items.add(item)
+ if item == ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN:
+ if self._read_game_state_value_for(1374) == 1:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(1381) == 1:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(16562) == 1:
+ to_filter_inventory_items.add(item)
+
+ return received_inventory_items - to_filter_inventory_items
+
+ def _filter_received_lucy_inventory_items(
+ self, received_inventory_items: Set[ZorkGrandInquisitorItems]
+ ) -> Set[ZorkGrandInquisitorItems]:
+ to_filter_inventory_items: Set[ZorkGrandInquisitorItems] = set()
+
+ inventory_item_values: Set[int] = set()
+
+ i: int
+ for i in range(151, 157):
+ inventory_item_values.add(self._read_game_state_value_for(i))
+
+ cursor_item_value: int = self._read_game_state_value_for(9)
+ inspector_item_value: int = self._read_game_state_value_for(2198)
+
+ inventory_item_values.add(cursor_item_value)
+ inventory_item_values.add(inspector_item_value)
+
+ item: ZorkGrandInquisitorItems
+ for item in received_inventory_items:
+ if item == ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1:
+ if 120 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15433) == 1:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15435) == 1:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15437) == 1:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15439) == 1:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15472) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2:
+ if 121 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15433) == 2:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15435) == 2:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15437) == 2:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15439) == 2:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15472) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3:
+ if 122 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15433) == 3:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15435) == 3:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15437) == 3:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15439) == 3:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15472) == 1:
+ to_filter_inventory_items.add(item)
+ elif item == ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4:
+ if 123 in inventory_item_values:
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15433) in (4, 5):
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15435) in (4, 5):
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15437) in (4, 5):
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15439) in (4, 5):
+ to_filter_inventory_items.add(item)
+ elif self._read_game_state_value_for(15472) == 1:
+ to_filter_inventory_items.add(item)
+
+ return received_inventory_items - to_filter_inventory_items
+
+ def _read_game_state_value_for(self, key: int) -> Optional[int]:
+ try:
+ return self.game_state_manager.read_game_state_value_for(key)
+ except Exception as e:
+ self.log_debug(f"Exception: {e} while trying to read game state key '{key}'")
+ raise e
+
+ def _write_game_state_value_for(self, key: int, value: int) -> Optional[bool]:
+ try:
+ return self.game_state_manager.write_game_state_value_for(key, value)
+ except Exception as e:
+ self.log_debug(f"Exception: {e} while trying to write '{key} = {value}' to game state")
+ raise e
+
+ def _read_game_flags_value_for(self, key: int) -> Optional[int]:
+ try:
+ return self.game_state_manager.read_game_flags_value_for(key)
+ except Exception as e:
+ self.log_debug(f"Exception: {e} while trying to read game flags key '{key}'")
+ raise e
+
+ def _write_game_flags_value_for(self, key: int, value: int) -> Optional[bool]:
+ try:
+ return self.game_state_manager.write_game_flags_value_for(key, value)
+ except Exception as e:
+ self.log_debug(f"Exception: {e} while trying to write '{key} = {value}' to game flags")
+ raise e
+
+ def _player_has(self, item: ZorkGrandInquisitorItems) -> bool:
+ return item in self.received_items
+
+ def _player_doesnt_have(self, item: ZorkGrandInquisitorItems) -> bool:
+ return item not in self.received_items
+
+ def _player_is_at(self, game_location: str) -> bool:
+ return self.game_state_manager.game_location == game_location
+
+ def _player_is_afgncaap(self) -> bool:
+ return self._read_game_state_value_for(1596) == 1
+
+ def _player_is_totem(self) -> bool:
+ return self._player_is_brog() or self._player_is_griff() or self._player_is_lucy()
+
+ def _player_is_brog(self) -> bool:
+ return self._read_game_state_value_for(1520) == 1
+
+ def _player_is_griff(self) -> bool:
+ return self._read_game_state_value_for(1296) == 1
+
+ def _player_is_lucy(self) -> bool:
+ return self._read_game_state_value_for(1524) == 1
diff --git a/worlds/zork_grand_inquisitor/game_state_manager.py b/worlds/zork_grand_inquisitor/game_state_manager.py
new file mode 100644
index 000000000000..25b35969bf5e
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/game_state_manager.py
@@ -0,0 +1,370 @@
+from typing import Optional, Tuple
+
+from pymem import Pymem
+from pymem.process import close_handle
+
+
+class GameStateManager:
+ process_name = "scummvm.exe"
+
+ process: Optional[Pymem]
+ is_process_running: bool
+
+ script_manager_struct_address: int
+ render_manager_struct_address: int
+
+ game_location: Optional[str]
+ game_location_offset: Optional[int]
+
+ def __init__(self) -> None:
+ self.process = None
+ self.is_process_running = False
+
+ self.script_manager_struct_address = 0x0
+ self.render_manager_struct_address = 0x0
+
+ self.game_location = None
+ self.game_location_offset = None
+
+ @property
+ def game_state_storage_pointer_address(self) -> int:
+ return self.script_manager_struct_address + 0x88
+
+ @property
+ def game_state_storage_address(self) -> int:
+ return self.process.read_longlong(self.game_state_storage_pointer_address)
+
+ @property
+ def game_state_hashmap_size_address(self) -> int:
+ return self.script_manager_struct_address + 0x90
+
+ @property
+ def game_state_key_count_address(self) -> int:
+ return self.script_manager_struct_address + 0x94
+
+ @property
+ def game_state_deleted_key_count_address(self) -> int:
+ return self.script_manager_struct_address + 0x98
+
+ @property
+ def game_flags_storage_pointer_address(self) -> int:
+ return self.script_manager_struct_address + 0x120
+
+ @property
+ def game_flags_storage_address(self) -> int:
+ return self.process.read_longlong(self.game_flags_storage_pointer_address)
+
+ @property
+ def game_flags_hashmap_size_address(self) -> int:
+ return self.script_manager_struct_address + 0x128
+
+ @property
+ def game_flags_key_count_address(self) -> int:
+ return self.script_manager_struct_address + 0x12C
+
+ @property
+ def game_flags_deleted_key_count_address(self) -> int:
+ return self.script_manager_struct_address + 0x130
+
+ @property
+ def current_location_address(self) -> int:
+ return self.script_manager_struct_address + 0x400
+
+ @property
+ def current_location_offset_address(self) -> int:
+ return self.script_manager_struct_address + 0x404
+
+ @property
+ def next_location_address(self) -> int:
+ return self.script_manager_struct_address + 0x408
+
+ @property
+ def next_location_offset_address(self) -> int:
+ return self.script_manager_struct_address + 0x40C
+
+ @property
+ def panorama_reversed_address(self) -> int:
+ return self.render_manager_struct_address + 0x1C
+
+ def open_process_handle(self) -> bool:
+ try:
+ self.process = Pymem(self.process_name)
+ self.is_process_running = True
+
+ self.script_manager_struct_address = self._resolve_address(0x5276600, (0xC8, 0x0))
+ self.render_manager_struct_address = self._resolve_address(0x5276600, (0xD0, 0x120))
+ except Exception:
+ return False
+
+ return True
+
+ def close_process_handle(self) -> bool:
+ if close_handle(self.process.process_handle):
+ self.is_process_running = False
+ self.process = None
+
+ self.script_manager_struct_address = 0x0
+ self.render_manager_struct_address = 0x0
+
+ return True
+
+ return False
+
+ def is_process_still_running(self) -> bool:
+ try:
+ self.process.read_int(self.process.base_address)
+ except Exception:
+ self.is_process_running = False
+ self.process = None
+
+ self.script_manager_struct_address = 0x0
+ self.render_manager_struct_address = 0x0
+
+ return False
+
+ return True
+
+ def read_game_state_value_for(self, key: int) -> Optional[int]:
+ return self.read_statemap_value_for(key, scope="game_state")
+
+ def read_game_flags_value_for(self, key: int) -> Optional[int]:
+ return self.read_statemap_value_for(key, scope="game_flags")
+
+ def read_statemap_value_for(self, key: int, scope: str = "game_state") -> Optional[int]:
+ if self.is_process_running:
+ offset: int
+
+ address: int
+ address_value: int
+
+ if scope == "game_state":
+ offset = self._get_game_state_address_read_offset_for(key)
+
+ address = self.game_state_storage_address + offset
+ address_value = self.process.read_longlong(address)
+ elif scope == "game_flags":
+ offset = self._get_game_flags_address_read_offset_for(key)
+
+ address = self.game_flags_storage_address + offset
+ address_value = self.process.read_longlong(address)
+ else:
+ raise ValueError(f"Invalid scope: {scope}")
+
+ if address_value == 0:
+ return 0
+
+ statemap_value: int = self.process.read_int(address_value + 0x0)
+ statemap_key: int = self.process.read_int(address_value + 0x4)
+
+ assert statemap_key == key
+
+ return statemap_value
+
+ return None
+
+ def write_game_state_value_for(self, key: int, value: int) -> Optional[bool]:
+ return self.write_statemap_value_for(key, value, scope="game_state")
+
+ def write_game_flags_value_for(self, key: int, value: int) -> Optional[bool]:
+ return self.write_statemap_value_for(key, value, scope="game_flags")
+
+ def write_statemap_value_for(self, key: int, value: int, scope: str = "game_state") -> Optional[bool]:
+ if self.is_process_running:
+ offset: int
+ is_existing_node: bool
+ is_reused_dummy_node: bool
+
+ key_count_address: int
+ deleted_key_count_address: int
+
+ storage_address: int
+
+ if scope == "game_state":
+ offset, is_existing_node, is_reused_dummy_node = self._get_game_state_address_write_offset_for(key)
+
+ key_count_address = self.game_state_key_count_address
+ deleted_key_count_address = self.game_state_deleted_key_count_address
+
+ storage_address = self.game_state_storage_address
+ elif scope == "game_flags":
+ offset, is_existing_node, is_reused_dummy_node = self._get_game_flags_address_write_offset_for(key)
+
+ key_count_address = self.game_flags_key_count_address
+ deleted_key_count_address = self.game_flags_deleted_key_count_address
+
+ storage_address = self.game_flags_storage_address
+ else:
+ raise ValueError(f"Invalid scope: {scope}")
+
+ statemap_key_count: int = self.process.read_int(key_count_address)
+ statemap_deleted_key_count: int = self.process.read_int(deleted_key_count_address)
+
+ if value == 0:
+ if not is_existing_node:
+ return False
+
+ self.process.write_longlong(storage_address + offset, 1)
+
+ self.process.write_int(key_count_address, statemap_key_count - 1)
+ self.process.write_int(deleted_key_count_address, statemap_deleted_key_count + 1)
+ else:
+ if is_existing_node:
+ address_value: int = self.process.read_longlong(storage_address + offset)
+ self.process.write_int(address_value + 0x0, value)
+ else:
+ write_address: int = self.process.allocate(0x8)
+
+ self.process.write_int(write_address + 0x0, value)
+ self.process.write_int(write_address + 0x4, key)
+
+ self.process.write_longlong(storage_address + offset, write_address)
+
+ self.process.write_int(key_count_address, statemap_key_count + 1)
+
+ if is_reused_dummy_node:
+ self.process.write_int(deleted_key_count_address, statemap_deleted_key_count - 1)
+
+ return True
+
+ return None
+
+ def refresh_game_location(self) -> Optional[bool]:
+ if self.is_process_running:
+ game_location_bytes: bytes = self.process.read_bytes(self.current_location_address, 4)
+
+ self.game_location = game_location_bytes.decode("ascii")
+ self.game_location_offset = self.process.read_int(self.current_location_offset_address)
+
+ return True
+
+ return None
+
+ def set_game_location(self, game_location: str, offset: int) -> Optional[bool]:
+ if self.is_process_running:
+ game_location_bytes: bytes = game_location.encode("ascii")
+
+ self.process.write_bytes(self.next_location_address, game_location_bytes, 4)
+ self.process.write_int(self.next_location_offset_address, offset)
+
+ return True
+
+ return None
+
+ def set_panorama_reversed(self, is_reversed: bool) -> Optional[bool]:
+ if self.is_process_running:
+ self.process.write_int(self.panorama_reversed_address, 1 if is_reversed else 0)
+
+ return True
+
+ return None
+
+ def _resolve_address(self, base_offset: int, offsets: Tuple[int, ...]):
+ address: int = self.process.read_longlong(self.process.base_address + base_offset)
+
+ for offset in offsets[:-1]:
+ address = self.process.read_longlong(address + offset)
+
+ return address + offsets[-1]
+
+ def _get_game_state_address_read_offset_for(self, key: int):
+ return self._get_statemap_address_read_offset_for(key, scope="game_state")
+
+ def _get_game_flags_address_read_offset_for(self, key: int):
+ return self._get_statemap_address_read_offset_for(key, scope="game_flags")
+
+ def _get_statemap_address_read_offset_for(self, key: int, scope: str = "game_state") -> int:
+ hashmap_size_address: int
+ storage_address: int
+
+ if scope == "game_state":
+ hashmap_size_address = self.game_state_hashmap_size_address
+ storage_address = self.game_state_storage_address
+ elif scope == "game_flags":
+ hashmap_size_address = self.game_flags_hashmap_size_address
+ storage_address = self.game_flags_storage_address
+ else:
+ raise ValueError(f"Invalid scope: {scope}")
+
+ statemap_hashmap_size: int = self.process.read_int(hashmap_size_address)
+
+ perturb: int = key
+ perturb_shift: int = 0x5
+
+ index: int = key & statemap_hashmap_size
+ offset: int = index * 0x8
+
+ while True:
+ offset_value: int = self.process.read_longlong(storage_address + offset)
+
+ if offset_value == 0: # Null Pointer
+ break
+ elif offset_value == 1: # Dummy Node
+ pass
+ elif offset_value > 1: # Existing Node
+ if self.process.read_int(offset_value + 0x4) == key:
+ break
+
+ index = ((0x5 * index) + perturb + 0x1) & statemap_hashmap_size
+ offset = index * 0x8
+
+ perturb >>= perturb_shift
+
+ return offset
+
+ def _get_game_state_address_write_offset_for(self, key: int) -> Tuple[int, bool, bool]:
+ return self._get_statemap_address_write_offset_for(key, scope="game_state")
+
+ def _get_game_flags_address_write_offset_for(self, key: int) -> Tuple[int, bool, bool]:
+ return self._get_statemap_address_write_offset_for(key, scope="game_flags")
+
+ def _get_statemap_address_write_offset_for(self, key: int, scope: str = "game_state") -> Tuple[int, bool, bool]:
+ hashmap_size_address: int
+ storage_address: int
+
+ if scope == "game_state":
+ hashmap_size_address = self.game_state_hashmap_size_address
+ storage_address = self.game_state_storage_address
+ elif scope == "game_flags":
+ hashmap_size_address = self.game_flags_hashmap_size_address
+ storage_address = self.game_flags_storage_address
+ else:
+ raise ValueError(f"Invalid scope: {scope}")
+
+ statemap_hashmap_size: int = self.process.read_int(hashmap_size_address)
+
+ perturb: int = key
+ perturb_shift: int = 0x5
+
+ index: int = key & statemap_hashmap_size
+ offset: int = index * 0x8
+
+ node_found: bool = False
+
+ dummy_node_found: bool = False
+ dummy_node_offset: Optional[int] = None
+
+ while True:
+ offset_value: int = self.process.read_longlong(storage_address + offset)
+
+ if offset_value == 0: # Null Pointer
+ break
+ elif offset_value == 1: # Dummy Node
+ if not dummy_node_found:
+ dummy_node_offset = offset
+ dummy_node_found = True
+ elif offset_value > 1: # Existing Node
+ if self.process.read_int(offset_value + 0x4) == key:
+ node_found = True
+ break
+
+ index = ((0x5 * index) + perturb + 0x1) & statemap_hashmap_size
+ offset = index * 0x8
+
+ perturb >>= perturb_shift
+
+ if not node_found and dummy_node_found: # We should reuse the dummy node
+ return dummy_node_offset, False, True
+ elif not node_found and not dummy_node_found: # We should allocate a new node
+ return offset, False, False
+
+ return offset, True, False # We should update the existing node
diff --git a/worlds/zork_grand_inquisitor/options.py b/worlds/zork_grand_inquisitor/options.py
new file mode 100644
index 000000000000..f06415199934
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/options.py
@@ -0,0 +1,61 @@
+from dataclasses import dataclass
+
+from Options import Choice, DefaultOnToggle, PerGameCommonOptions, Toggle
+
+
+class Goal(Choice):
+ """
+ Determines the victory condition
+
+ Three Artifacts: Retrieve the three artifacts of magic and place them in the walking castle
+ """
+ display_name: str = "Goal"
+
+ default: int = 0
+ option_three_artifacts: int = 0
+
+
+class QuickPortFoozle(DefaultOnToggle):
+ """If true, the items needed to go down the well will be found in early locations for a smoother early game"""
+
+ display_name: str = "Quick Port Foozle"
+
+
+class StartWithHotspotItems(DefaultOnToggle):
+ """
+ If true, the player will be given all the hotspot items at the start of the game, effectively removing the need
+ to enable the important hotspots in the game before interacting with them. Recommended for beginners
+
+ Note: The spots these hotspot items would have occupied in the item pool will instead be filled with junk items.
+ Expect a higher volume of filler items if you enable this option
+ """
+
+ display_name: str = "Start with Hotspot Items"
+
+
+class Deathsanity(Toggle):
+ """If true, adds 16 player death locations to the world"""
+
+ display_name: str = "Deathsanity"
+
+
+class GrantMissableLocationChecks(Toggle):
+ """
+ If true, performing an irreversible action will grant the locations checks that would have become unobtainable as a
+ result of that action when you meet the item requirements
+
+ Otherwise, the player is expected to potentially have to use the save system to reach those location checks. If you
+ don't like the idea of rarely having to reload an earlier save to get a location check, make sure this option is
+ enabled
+ """
+
+ display_name: str = "Grant Missable Checks"
+
+
+@dataclass
+class ZorkGrandInquisitorOptions(PerGameCommonOptions):
+ goal: Goal
+ quick_port_foozle: QuickPortFoozle
+ start_with_hotspot_items: StartWithHotspotItems
+ deathsanity: Deathsanity
+ grant_missable_location_checks: GrantMissableLocationChecks
diff --git a/worlds/zork_grand_inquisitor/requirements.txt b/worlds/zork_grand_inquisitor/requirements.txt
new file mode 100644
index 000000000000..fe25267f6705
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/requirements.txt
@@ -0,0 +1 @@
+Pymem>=1.13.0
\ No newline at end of file
diff --git a/worlds/zork_grand_inquisitor/test/__init__.py b/worlds/zork_grand_inquisitor/test/__init__.py
new file mode 100644
index 000000000000..c8ceda43a7bf
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/test/__init__.py
@@ -0,0 +1,5 @@
+from test.bases import WorldTestBase
+
+
+class ZorkGrandInquisitorTestBase(WorldTestBase):
+ game = "Zork Grand Inquisitor"
diff --git a/worlds/zork_grand_inquisitor/test/test_access.py b/worlds/zork_grand_inquisitor/test/test_access.py
new file mode 100644
index 000000000000..63a5f8c9ab1d
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/test/test_access.py
@@ -0,0 +1,2927 @@
+from typing import List
+
+from . import ZorkGrandInquisitorTestBase
+
+from ..enums import (
+ ZorkGrandInquisitorEvents,
+ ZorkGrandInquisitorItems,
+ ZorkGrandInquisitorLocations,
+ ZorkGrandInquisitorRegions,
+)
+
+
+class AccessTestRegions(ZorkGrandInquisitorTestBase):
+ options = {
+ "start_with_hotspot_items": "false",
+ }
+
+ def test_access_crossroads_to_dm_lair_sword(self) -> None:
+ self._go_to_crossroads()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.DM_LAIR.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SWORD.value,
+ ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.DM_LAIR.value))
+
+ def test_access_crossroads_to_dm_lair_teleporter(self) -> None:
+ self._go_to_crossroads()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.DM_LAIR.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.DM_LAIR.value))
+
+ def test_access_crossroads_to_gue_tech(self) -> None:
+ self._go_to_crossroads()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SPELL_REZROV.value,
+ ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH.value))
+
+ def test_access_crossroads_to_gue_tech_outside(self) -> None:
+ self._go_to_crossroads()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE.value))
+
+ def test_access_crossroads_to_hades_shore(self) -> None:
+ self._go_to_crossroads()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ def test_access_crossroads_to_port_foozle(self) -> None:
+ self._go_to_crossroads()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.PORT_FOOZLE.value))
+
+ def test_access_crossroads_to_spell_lab_bridge(self) -> None:
+ self._go_to_crossroads()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE.value))
+
+ def test_access_crossroads_to_subway_crossroads(self) -> None:
+ self._go_to_crossroads()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SUBWAY_TOKEN.value,
+ ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS.value))
+
+ def test_access_crossroads_to_subway_monastery(self) -> None:
+ self._go_to_crossroads()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_MONASTERY.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_MONASTERY.value))
+
+ def test_access_dm_lair_to_crossroads(self) -> None:
+ self._go_to_dm_lair()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.CROSSROADS.value))
+
+ def test_access_dm_lair_to_dm_lair_interior(self) -> None:
+ self._go_to_dm_lair()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY.value,
+ ZorkGrandInquisitorItems.MEAD_LIGHT.value,
+ ZorkGrandInquisitorItems.ZIMDOR_SCROLL.value,
+ ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR.value))
+
+ def test_access_dm_lair_to_gue_tech_outside(self) -> None:
+ self._go_to_dm_lair()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH.value))
+
+ def test_access_dm_lair_to_hades_shore(self) -> None:
+ self._go_to_dm_lair()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ def test_access_dm_lair_to_spell_lab_bridge(self) -> None:
+ self._go_to_dm_lair()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE.value))
+
+ def test_access_dm_lair_to_subway_monastery(self) -> None:
+ self._go_to_dm_lair()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_MONASTERY.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_MONASTERY.value))
+
+ def test_access_dm_lair_interior_to_dm_lair(self) -> None:
+ self._go_to_dm_lair_interior()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.DM_LAIR.value))
+
+ def test_access_dm_lair_interior_to_walking_castle(self) -> None:
+ self._go_to_dm_lair_interior()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.WALKING_CASTLE.value))
+
+ self._obtain_obidil()
+
+ self.collect_by_name(ZorkGrandInquisitorItems.HOTSPOT_BLINDS.value)
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.WALKING_CASTLE.value))
+
+ def test_access_dm_lair_interior_to_white_house(self) -> None:
+ self._go_to_dm_lair_interior()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.WHITE_HOUSE.value))
+
+ self._obtain_yastard()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR.value,
+ ZorkGrandInquisitorItems.SPELL_NARWILE.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.WHITE_HOUSE.value))
+
+ def test_access_dragon_archipelago_to_dragon_archipelago_dragon(self) -> None:
+ self._go_to_dragon_archipelago()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.TOTEM_GRIFF.value,
+ ZorkGrandInquisitorItems.HOTSPOT_DRAGON_CLAW.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON.value))
+
+ def test_access_dragon_archipelago_to_hades_beyond_gates(self) -> None:
+ self._go_to_dragon_archipelago()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_BEYOND_GATES.value))
+
+ def test_access_dragon_archipelago_dragon_to_dragon_archipelago(self) -> None:
+ self._go_to_dragon_archipelago_dragon()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO.value))
+
+ def test_access_dragon_archipelago_dragon_to_endgame(self) -> None:
+ self._go_to_dragon_archipelago_dragon()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.ENDGAME.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP.value,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT.value,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN.value,
+ ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS.value,
+ ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH.value,
+ )
+ )
+
+ self._go_to_port_foozle_past_tavern()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1.value,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2.value,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3.value,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4.value,
+ ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY.value,
+ ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS.value,
+ )
+ )
+
+ self._go_to_white_house()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.TOTEM_BROG.value,
+ ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH.value,
+ ZorkGrandInquisitorItems.BROGS_GRUE_EGG.value,
+ ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT.value,
+ ZorkGrandInquisitorItems.BROGS_PLANK.value,
+ ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.ENDGAME.value))
+
+ def test_access_gue_tech_to_crossroads(self) -> None:
+ self._go_to_gue_tech()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.CROSSROADS.value))
+
+ def test_access_gue_tech_to_gue_tech_hallway(self) -> None:
+ self._go_to_gue_tech()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SPELL_IGRAM.value,
+ ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY.value))
+
+ def test_access_gue_tech_to_gue_tech_outside(self) -> None:
+ self._go_to_gue_tech()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE.value))
+
+ self.collect_by_name(ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_DOOR.value)
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE.value))
+
+ def test_access_gue_tech_hallway_to_gue_tech(self) -> None:
+ self._go_to_gue_tech_hallway()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH.value))
+
+ def test_access_gue_tech_hallway_to_spell_lab_bridge(self) -> None:
+ self._go_to_gue_tech_hallway()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.STUDENT_ID.value,
+ ZorkGrandInquisitorItems.HOTSPOT_STUDENT_ID_MACHINE.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE.value))
+
+ def test_access_gue_tech_outside_to_crossroads(self) -> None:
+ self._go_to_gue_tech_outside()
+
+ # Direct connection requires the map but indirect connection is free
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.CROSSROADS.value))
+
+ def test_access_gue_tech_outside_to_dm_lair(self) -> None:
+ self._go_to_gue_tech_outside()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.DM_LAIR.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.DM_LAIR.value))
+
+ def test_access_gue_tech_outside_to_gue_tech(self) -> None:
+ self._go_to_gue_tech_outside()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH.value))
+
+ def test_access_gue_tech_outside_to_hades_shore(self) -> None:
+ self._go_to_gue_tech_outside()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ def test_access_gue_tech_outside_to_spell_lab_bridge(self) -> None:
+ self._go_to_gue_tech_outside()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE.value))
+
+ def test_access_gue_tech_outside_to_subway_monastery(self) -> None:
+ self._go_to_gue_tech_outside()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_MONASTERY.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_MONASTERY.value))
+
+ def test_access_hades_to_hades_beyond_gates(self) -> None:
+ self._go_to_hades()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_BEYOND_GATES.value))
+
+ self._obtain_snavig()
+
+ self.collect_by_name(ZorkGrandInquisitorItems.TOTEM_BROG.value)
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_BEYOND_GATES.value))
+
+ def test_access_hades_to_hades_shore(self) -> None:
+ self._go_to_hades()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ def test_access_hades_beyond_gates_to_dragon_archipelago(self) -> None:
+ self._go_to_hades_beyond_gates()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO.value))
+
+ self._obtain_yastard()
+
+ self.collect_by_name(ZorkGrandInquisitorItems.SPELL_NARWILE.value)
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO.value))
+
+ def test_access_hades_beyond_gates_to_hades(self) -> None:
+ self._go_to_hades_beyond_gates()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.HADES.value))
+
+ def test_access_hades_shore_to_crossroads(self) -> None:
+ self._go_to_hades_shore()
+
+ # Direct connection requires the map but indirect connection is free
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.CROSSROADS.value))
+
+ def test_access_hades_shore_to_dm_lair(self) -> None:
+ self._go_to_hades_shore()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.DM_LAIR.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.DM_LAIR.value))
+
+ def test_access_hades_shore_to_gue_tech_outside(self) -> None:
+ self._go_to_hades_shore()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE.value))
+
+ def test_access_hades_shore_to_hades(self) -> None:
+ self._go_to_hades_shore()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.HADES.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER.value,
+ ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS.value,
+ ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.HADES.value))
+
+ def test_access_hades_shore_to_spell_lab_bridge(self) -> None:
+ self._go_to_hades_shore()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE.value))
+
+ def test_access_hades_shore_to_subway_crossroads(self) -> None:
+ self._go_to_hades_shore()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS.value))
+
+ def test_access_hades_shore_to_subway_flood_control_dam(self) -> None:
+ self._go_to_hades_shore()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM.value))
+
+ self.collect_by_name(ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM.value)
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM.value))
+
+ def test_access_hades_shore_to_subway_monastery(self) -> None:
+ self._go_to_hades_shore()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_MONASTERY.value))
+
+ self.collect_by_name(ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY.value)
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_MONASTERY.value))
+
+ def test_access_monastery_to_hades_shore(self) -> None:
+ self._go_to_monastery()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL.value,
+ ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS.value,
+ ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ def test_access_monastery_to_monastery_exhibit(self) -> None:
+ self._go_to_monastery()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_HALL_OF_INQUISITION.value,
+ ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS.value,
+ ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT.value))
+
+ def test_access_monastery_to_subway_monastery(self) -> None:
+ self._go_to_monastery()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_MONASTERY.value))
+
+ def test_access_monastery_exhibit_to_monastery(self) -> None:
+ self._go_to_monastery_exhibit()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.MONASTERY.value))
+
+ def test_access_monastery_exhibit_to_port_foozle_past(self) -> None:
+ self._go_to_monastery_exhibit()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST.value))
+
+ self._obtain_yastard()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER.value,
+ ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT.value,
+ ZorkGrandInquisitorItems.LARGE_TELEGRAPH_HAMMER.value,
+ ZorkGrandInquisitorItems.SPELL_NARWILE.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST.value))
+
+ def test_access_port_foozle_to_crossroads(self) -> None:
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.CROSSROADS.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR.value,
+ ZorkGrandInquisitorItems.LANTERN.value,
+ ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL.value,
+ ZorkGrandInquisitorItems.ROPE.value,
+ ZorkGrandInquisitorItems.HOTSPOT_WELL.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.CROSSROADS.value))
+
+ def test_access_port_foozle_to_port_foozle_jacks_shop(self) -> None:
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR.value,
+ ZorkGrandInquisitorItems.LANTERN.value,
+ ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP.value))
+
+ def test_access_port_foozle_jacks_shop_to_port_foozle(self) -> None:
+ self._go_to_port_foozle_jacks_shop()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.PORT_FOOZLE.value))
+
+ def test_access_port_foozle_past_to_monastery_exhibit(self) -> None:
+ self._go_to_port_foozle_past()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT.value))
+
+ def test_access_port_foozle_past_to_port_foozle_past_tavern(self) -> None:
+ self._go_to_port_foozle_past()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.TOTEM_LUCY.value,
+ ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN.value))
+
+ def test_access_port_foozle_past_tavern_to_endgame(self) -> None:
+ self._go_to_port_foozle_past_tavern()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.ENDGAME.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1.value,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2.value,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3.value,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4.value,
+ ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY.value,
+ ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS.value,
+ )
+ )
+
+ self._go_to_dragon_archipelago_dragon()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP.value,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT.value,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN.value,
+ ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS.value,
+ ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH.value,
+ )
+ )
+
+ self._go_to_white_house()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.TOTEM_BROG.value,
+ ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH.value,
+ ZorkGrandInquisitorItems.BROGS_GRUE_EGG.value,
+ ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT.value,
+ ZorkGrandInquisitorItems.BROGS_PLANK.value,
+ ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.ENDGAME.value))
+
+ def test_access_port_foozle_past_tavern_to_port_foozle_past(self) -> None:
+ self._go_to_port_foozle_past_tavern()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST.value))
+
+ def test_access_spell_lab_to_spell_lab_bridge(self) -> None:
+ self._go_to_spell_lab()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE.value))
+
+ def test_access_spell_lab_bridge_to_crossroads(self) -> None:
+ self._go_to_spell_lab_bridge()
+
+ # Direct connection requires the map but indirect connection is free
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.CROSSROADS.value))
+
+ def test_access_spell_lab_bridge_to_dm_lair(self) -> None:
+ self._go_to_spell_lab_bridge()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.DM_LAIR.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.DM_LAIR.value))
+
+ def test_access_spell_lab_bridge_to_gue_tech_outside(self) -> None:
+ self._go_to_spell_lab_bridge()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE.value))
+
+ def test_access_spell_lab_bridge_to_gue_tech_hallway(self) -> None:
+ self._go_to_spell_lab_bridge()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY.value))
+
+ def test_access_spell_lab_bridge_to_hades_shore(self) -> None:
+ self._go_to_spell_lab_bridge()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ def test_access_spell_lab_bridge_to_spell_lab(self) -> None:
+ self._go_to_spell_lab_bridge()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.SPELL_LAB.value))
+
+ self._go_to_subway_flood_control_dam()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SPELL_REZROV.value,
+ ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS.value,
+ ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS.value,
+ ZorkGrandInquisitorItems.SWORD.value,
+ ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE.value,
+ ZorkGrandInquisitorItems.SPELL_GOLGATEM.value,
+ ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SPELL_LAB.value))
+
+ def test_access_spell_lab_bridge_to_subway_monastery(self) -> None:
+ self._go_to_spell_lab_bridge()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_MONASTERY.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_MONASTERY.value))
+
+ def test_access_subway_crossroads_to_crossroads(self) -> None:
+ self._go_to_subway_crossroads()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.CROSSROADS.value))
+
+ def test_access_subway_crossroads_to_hades_shore(self) -> None:
+ self._go_to_subway_crossroads()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SPELL_KENDALL.value,
+ ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ def test_access_subway_crossroads_to_subway_flood_control_dam(self) -> None:
+ self._go_to_subway_crossroads()
+
+ self.assertFalse(
+ self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM.value)
+ )
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SPELL_KENDALL.value,
+ ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM.value,
+ )
+ )
+
+ self.assertTrue(
+ self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM.value)
+ )
+
+ def test_access_subway_crossroads_to_subway_monastery(self) -> None:
+ self._go_to_subway_crossroads()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_MONASTERY.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SPELL_KENDALL.value,
+ ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_MONASTERY.value))
+
+ def test_access_subway_flood_control_dam_to_hades_shore(self) -> None:
+ self._go_to_subway_flood_control_dam()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ self.collect_by_name(ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES.value)
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ def test_access_subway_flood_control_dam_to_subway_crossroads(self) -> None:
+ self._go_to_subway_flood_control_dam()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS.value))
+
+ def test_access_subway_flood_control_dam_to_subway_monastery(self) -> None:
+ self._go_to_subway_flood_control_dam()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_MONASTERY.value))
+
+ self.collect_by_name(ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY.value)
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_MONASTERY.value))
+
+ def test_access_subway_monastery_to_hades_shore(self) -> None:
+ self._go_to_subway_monastery()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ self.collect_by_name(ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES.value)
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.HADES_SHORE.value))
+
+ def test_access_subway_monastery_to_monastery(self) -> None:
+ self._go_to_subway_monastery()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.MONASTERY.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SWORD.value,
+ ZorkGrandInquisitorItems.SPELL_GLORF.value,
+ ZorkGrandInquisitorItems.HOTSPOT_MONASTERY_VENT.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.MONASTERY.value))
+
+ def test_access_subway_monastery_to_subway_crossroads(self) -> None:
+ self._go_to_subway_monastery()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS.value))
+
+ def test_access_subway_monastery_to_subway_flood_control_dam(self) -> None:
+ self._go_to_subway_monastery()
+
+ self.assertFalse(
+ self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM.value)
+ )
+
+ self.collect_by_name(ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM.value)
+
+ self.assertTrue(
+ self.can_reach_region(ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM.value)
+ )
+
+ def test_access_walking_castle_to_dm_lair_interior(self) -> None:
+ self._go_to_walking_castle()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR.value))
+
+ def test_access_white_house_to_dm_lair_interior(self) -> None:
+ self._go_to_white_house()
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR.value))
+
+ def test_access_white_house_to_endgame(self) -> None:
+ self._go_to_white_house()
+
+ self.assertFalse(self.can_reach_region(ZorkGrandInquisitorRegions.ENDGAME.value))
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.TOTEM_BROG.value,
+ ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH.value,
+ ZorkGrandInquisitorItems.BROGS_GRUE_EGG.value,
+ ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT.value,
+ ZorkGrandInquisitorItems.BROGS_PLANK.value,
+ ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE.value,
+ )
+ )
+
+ self._go_to_dragon_archipelago_dragon()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP.value,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT.value,
+ ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN.value,
+ ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS.value,
+ ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH.value,
+ )
+ )
+
+ self._go_to_port_foozle_past_tavern()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1.value,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2.value,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3.value,
+ ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4.value,
+ ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY.value,
+ ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS.value,
+ )
+ )
+
+ self.assertTrue(self.can_reach_region(ZorkGrandInquisitorRegions.ENDGAME.value))
+
+ def _go_to_crossroads(self) -> None:
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.LANTERN.value,
+ ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR.value,
+ ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL.value,
+ ZorkGrandInquisitorItems.ROPE.value,
+ ZorkGrandInquisitorItems.HOTSPOT_WELL.value,
+ )
+ )
+
+ def _go_to_dm_lair(self) -> None:
+ self._go_to_crossroads()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SWORD.value,
+ ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE.value,
+ )
+ )
+
+ def _go_to_dm_lair_interior(self) -> None:
+ self._go_to_dm_lair()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY.value,
+ ZorkGrandInquisitorItems.MEAD_LIGHT.value,
+ ZorkGrandInquisitorItems.ZIMDOR_SCROLL.value,
+ ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH.value,
+ )
+ )
+
+ def _go_to_dragon_archipelago(self) -> None:
+ self._go_to_hades_beyond_gates()
+ self._obtain_yastard()
+
+ self.collect_by_name(ZorkGrandInquisitorItems.SPELL_NARWILE.value)
+
+ def _go_to_dragon_archipelago_dragon(self) -> None:
+ self._go_to_dragon_archipelago()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.TOTEM_GRIFF.value,
+ ZorkGrandInquisitorItems.HOTSPOT_DRAGON_CLAW.value,
+ )
+ )
+
+ def _go_to_gue_tech(self) -> None:
+ self._go_to_crossroads()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SPELL_REZROV.value,
+ ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR.value,
+ )
+ )
+
+ def _go_to_gue_tech_hallway(self) -> None:
+ self._go_to_gue_tech()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SPELL_IGRAM.value,
+ ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS.value,
+ )
+ )
+
+ def _go_to_gue_tech_outside(self) -> None:
+ self._go_to_crossroads()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH.value,
+ )
+ )
+
+ def _go_to_hades(self) -> None:
+ self._go_to_hades_shore()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER.value,
+ ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS.value,
+ ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS.value,
+ )
+ )
+
+ def _go_to_hades_beyond_gates(self) -> None:
+ self._go_to_hades()
+ self._obtain_snavig()
+
+ self.collect_by_name(ZorkGrandInquisitorItems.TOTEM_BROG.value)
+
+ def _go_to_hades_shore(self) -> None:
+ self._go_to_crossroads()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES.value,
+ )
+ )
+
+ def _go_to_monastery(self) -> None:
+ self._go_to_subway_monastery()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SWORD.value,
+ ZorkGrandInquisitorItems.SPELL_GLORF.value,
+ ZorkGrandInquisitorItems.HOTSPOT_MONASTERY_VENT.value,
+ )
+ )
+
+ def _go_to_monastery_exhibit(self) -> None:
+ self._go_to_monastery()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_HALL_OF_INQUISITION.value,
+ ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS.value,
+ ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH.value,
+ )
+ )
+
+ def _go_to_port_foozle_jacks_shop(self) -> None:
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR.value,
+ ZorkGrandInquisitorItems.LANTERN.value,
+ ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL.value,
+ )
+ )
+
+ def _go_to_port_foozle_past(self) -> None:
+ self._go_to_monastery_exhibit()
+
+ self._obtain_yastard()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER.value,
+ ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT.value,
+ ZorkGrandInquisitorItems.LARGE_TELEGRAPH_HAMMER.value,
+ ZorkGrandInquisitorItems.SPELL_NARWILE.value,
+ )
+ )
+
+ def _go_to_port_foozle_past_tavern(self) -> None:
+ self._go_to_port_foozle_past()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.TOTEM_LUCY.value,
+ ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR.value,
+ )
+ )
+
+ def _go_to_spell_lab(self) -> None:
+ self._go_to_subway_flood_control_dam()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SPELL_REZROV.value,
+ ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS.value,
+ ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS.value,
+ )
+ )
+
+ self._go_to_spell_lab_bridge()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SWORD.value,
+ ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE.value,
+ ZorkGrandInquisitorItems.SPELL_GOLGATEM.value,
+ ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM.value,
+ )
+ )
+
+ def _go_to_spell_lab_bridge(self) -> None:
+ self._go_to_crossroads()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB.value,
+ )
+ )
+
+ def _go_to_subway_crossroads(self) -> None:
+ self._go_to_crossroads()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SUBWAY_TOKEN.value,
+ ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT.value,
+ )
+ )
+
+ def _go_to_subway_flood_control_dam(self) -> None:
+ self._go_to_subway_crossroads()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SPELL_KENDALL.value,
+ ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM.value,
+ )
+ )
+
+ def _go_to_subway_monastery(self) -> None:
+ self._go_to_crossroads()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.MAP.value,
+ ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY.value,
+ )
+ )
+
+ def _go_to_white_house(self) -> None:
+ self._go_to_dm_lair_interior()
+
+ self._obtain_yastard()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR.value,
+ ZorkGrandInquisitorItems.SPELL_NARWILE.value,
+ )
+ )
+
+ def _go_to_walking_castle(self) -> None:
+ self._go_to_dm_lair_interior()
+
+ self._obtain_obidil()
+ self.collect_by_name(ZorkGrandInquisitorItems.HOTSPOT_BLINDS.value)
+
+ def _obtain_obidil(self) -> None:
+ self._go_to_crossroads()
+ self._go_to_gue_tech()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS.value,
+ ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_COIN_SLOT.value,
+ ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_DOORS.value,
+ )
+ )
+
+ self._go_to_subway_flood_control_dam()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SPELL_REZROV.value,
+ ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS.value,
+ ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS.value,
+ )
+ )
+
+ self._go_to_spell_lab_bridge()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SWORD.value,
+ ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE.value,
+ ZorkGrandInquisitorItems.SPELL_GOLGATEM.value,
+ ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM.value,
+ ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER.value,
+ )
+ )
+
+ def _obtain_snavig(self) -> None:
+ self._go_to_crossroads()
+ self._go_to_dm_lair_interior()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SCROLL_FRAGMENT_ANS.value,
+ ZorkGrandInquisitorItems.SCROLL_FRAGMENT_GIV.value,
+ ZorkGrandInquisitorItems.HOTSPOT_MIRROR.value,
+ )
+ )
+
+ self._go_to_subway_flood_control_dam()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SPELL_REZROV.value,
+ ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS.value,
+ ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS.value,
+ )
+ )
+
+ self._go_to_spell_lab_bridge()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.SWORD.value,
+ ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE.value,
+ ZorkGrandInquisitorItems.SPELL_GOLGATEM.value,
+ ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM.value,
+ ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER.value,
+ )
+ )
+
+ def _obtain_yastard(self) -> None:
+ self._go_to_crossroads()
+ self._go_to_dm_lair_interior()
+
+ self.collect_by_name(
+ (
+ ZorkGrandInquisitorItems.FLATHEADIA_FUDGE.value,
+ ZorkGrandInquisitorItems.HUNGUS_LARD.value,
+ ZorkGrandInquisitorItems.JAR_OF_HOTBUGS.value,
+ ZorkGrandInquisitorItems.QUELBEE_HONEYCOMB.value,
+ ZorkGrandInquisitorItems.MOSS_OF_MAREILON.value,
+ ZorkGrandInquisitorItems.MUG.value,
+ )
+ )
+
+
+class AccessTestLocations(ZorkGrandInquisitorTestBase):
+ options = {
+ "deathsanity": "true",
+ "start_with_hotspot_items": "false",
+ }
+
+ def test_access_locations_requiring_brogs_flickering_torch(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.BROG_DO_GOOD.value,
+ ZorkGrandInquisitorLocations.BROG_EAT_ROCKS.value,
+ ZorkGrandInquisitorLocations.BROG_KNOW_DUMB_THAT_DUMB.value,
+ ZorkGrandInquisitorLocations.BROG_MUCH_BETTER_AT_THIS_GAME.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH.value,)]
+ )
+
+ def test_access_locations_requiring_brogs_grue_egg(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.BROG_DO_GOOD.value,
+ ZorkGrandInquisitorLocations.BROG_KNOW_DUMB_THAT_DUMB.value,
+ ZorkGrandInquisitorLocations.BROG_MUCH_BETTER_AT_THIS_GAME.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.BROGS_GRUE_EGG.value,)]
+ )
+
+ def test_access_locations_requiring_brogs_plank(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.BROG_MUCH_BETTER_AT_THIS_GAME.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.BROGS_PLANK.value,)]
+ )
+
+ def test_access_locations_requiring_flatheadia_fudge(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.OH_WOW_TALK_ABOUT_DEJA_VU.value,
+ ZorkGrandInquisitorEvents.KNOWS_YASTARD.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.FLATHEADIA_FUDGE.value,)]
+ )
+
+ def test_access_locations_requiring_griffs_air_pump(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.OH_DEAR_GOD_ITS_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.THAR_SHE_BLOWS.value,
+ ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP.value,)]
+ )
+
+ def test_access_locations_requiring_griffs_dragon_tooth(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.THAR_SHE_BLOWS.value,
+ ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH.value,)]
+ )
+
+ def test_access_locations_requiring_griffs_inflatable_raft(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.OH_DEAR_GOD_ITS_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.THAR_SHE_BLOWS.value,
+ ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT.value,)]
+ )
+
+ def test_access_locations_requiring_griffs_inflatable_sea_captain(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.OH_DEAR_GOD_ITS_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.THAR_SHE_BLOWS.value,
+ ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN.value,)]
+ )
+
+ def test_access_locations_requiring_hammer(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.BOING_BOING_BOING.value,
+ ZorkGrandInquisitorLocations.BONK.value,
+ ZorkGrandInquisitorLocations.FLYING_SNAPDRAGON.value,
+ ZorkGrandInquisitorLocations.IN_CASE_OF_ADVENTURE.value,
+ ZorkGrandInquisitorLocations.MUSHROOM_HAMMERED.value,
+ ZorkGrandInquisitorLocations.THROCKED_MUSHROOM_HAMMERED.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HAMMER.value,)]
+ )
+
+ def test_access_locations_requiring_hungus_lard(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.OH_WOW_TALK_ABOUT_DEJA_VU.value,
+ ZorkGrandInquisitorLocations.OUTSMART_THE_QUELBEES.value,
+ ZorkGrandInquisitorLocations.DEATH_OUTSMARTED_BY_THE_QUELBEES.value,
+ ZorkGrandInquisitorEvents.KNOWS_YASTARD.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HUNGUS_LARD.value,)]
+ )
+
+ def test_access_locations_requiring_jar_of_hotbugs(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.OH_WOW_TALK_ABOUT_DEJA_VU.value,
+ ZorkGrandInquisitorEvents.KNOWS_YASTARD.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.JAR_OF_HOTBUGS.value,)]
+ )
+
+ def test_access_locations_requiring_lantern(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.LANTERN.value,)]
+ )
+
+ def test_access_locations_requiring_large_telegraph_hammer(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.PORT_FOOZLE_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.OH_VERY_FUNNY_GUYS.value,
+ ZorkGrandInquisitorLocations.WE_DONT_SERVE_YOUR_KIND_HERE.value,
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER.value,
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.LARGE_TELEGRAPH_HAMMER.value,)]
+ )
+
+ def test_access_locations_requiring_lucys_playing_cards(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER.value,
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1.value,)]
+ )
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2.value,)]
+ )
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3.value,)]
+ )
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4.value,)]
+ )
+
+ def test_access_locations_requiring_map(self) -> None:
+ locations: List[str] = list()
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.MAP.value,)]
+ )
+
+ def test_access_locations_requiring_mead_light(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.MEAD_LIGHT.value,
+ ZorkGrandInquisitorLocations.WANT_SOME_RYE_COURSE_YA_DO.value,
+ ZorkGrandInquisitorEvents.DOOR_DRANK_MEAD.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.MEAD_LIGHT.value,)]
+ )
+
+ def test_access_locations_requiring_moss_of_mareilon(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.OH_WOW_TALK_ABOUT_DEJA_VU.value,
+ ZorkGrandInquisitorEvents.KNOWS_YASTARD.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.MOSS_OF_MAREILON.value,)]
+ )
+
+ def test_access_locations_requiring_mug(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.OH_WOW_TALK_ABOUT_DEJA_VU.value,
+ ZorkGrandInquisitorEvents.KNOWS_YASTARD.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.MUG.value,)]
+ )
+
+ def test_access_locations_requiring_old_scratch_card(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DEATH_LOST_SOUL_TO_OLD_SCRATCH.value,
+ ZorkGrandInquisitorLocations.OLD_SCRATCH_WINNER.value,
+ ZorkGrandInquisitorEvents.ZORKMID_BILL_ACCESSIBLE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.OLD_SCRATCH_CARD.value,)]
+ )
+
+ def test_access_locations_requiring_perma_suck_machine(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.SUCKING_ROCKS.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.PERMA_SUCK_MACHINE.value,)]
+ )
+
+ def test_access_locations_requiring_plastic_six_pack_holder(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.HELP_ME_CANT_BREATHE.value,
+ ZorkGrandInquisitorLocations.WHAT_ARE_YOU_STUPID.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.PLASTIC_SIX_PACK_HOLDER.value,)]
+ )
+
+ def test_access_locations_requiring_pouch_of_zorkmids(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.A_BIG_FAT_SASSY_2_HEADED_MONSTER.value,
+ ZorkGrandInquisitorLocations.A_LETTER_FROM_THE_WHITE_HOUSE.value,
+ ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.DEATH_YOURE_NOT_CHARON.value,
+ ZorkGrandInquisitorLocations.DONT_EVEN_START_WITH_US_SPARKY.value,
+ ZorkGrandInquisitorLocations.DRAGON_ARCHIPELAGO_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.DUNCE_LOCKER.value,
+ ZorkGrandInquisitorLocations.I_SPIT_ON_YOUR_FILTHY_COINAGE.value,
+ ZorkGrandInquisitorLocations.NOOOOOOOOOOOOO.value,
+ ZorkGrandInquisitorLocations.NOW_YOU_LOOK_LIKE_US_WHICH_IS_AN_IMPROVEMENT.value,
+ ZorkGrandInquisitorLocations.OH_DEAR_GOD_ITS_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.OPEN_THE_GATES_OF_HELL.value,
+ ZorkGrandInquisitorLocations.SOUVENIR.value,
+ ZorkGrandInquisitorLocations.THAR_SHE_BLOWS.value,
+ ZorkGrandInquisitorLocations.THIS_DOESNT_LOOK_ANYTHING_LIKE_THE_BROCHURE.value,
+ ZorkGrandInquisitorLocations.UH_OH_BROG_CANT_SWIM.value,
+ ZorkGrandInquisitorEvents.DALBOZ_LOCKER_OPENABLE.value,
+ ZorkGrandInquisitorEvents.DUNCE_LOCKER_OPENABLE.value,
+ ZorkGrandInquisitorEvents.HAS_REPAIRABLE_OBIDIL.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ZorkGrandInquisitorEvents.ZORK_ROCKS_ACTIVATED.value,
+ ZorkGrandInquisitorEvents.ZORK_ROCKS_SUCKABLE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS.value,)]
+ )
+
+ def test_access_locations_requiring_prozork_tablet(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.PROZORKED.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.PROZORK_TABLET.value,)]
+ )
+
+ def test_access_locations_requiring_quelbee_honeycomb(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.OH_WOW_TALK_ABOUT_DEJA_VU.value,
+ ZorkGrandInquisitorEvents.KNOWS_YASTARD.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.QUELBEE_HONEYCOMB.value,)]
+ )
+
+ def test_access_locations_requiring_rope(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.ALARM_SYSTEM_IS_DOWN.value,
+ ZorkGrandInquisitorLocations.ARTIFACTS_EXPLAINED.value,
+ ZorkGrandInquisitorLocations.A_BIG_FAT_SASSY_2_HEADED_MONSTER.value,
+ ZorkGrandInquisitorLocations.A_LETTER_FROM_THE_WHITE_HOUSE.value,
+ ZorkGrandInquisitorLocations.A_SMALLWAY.value,
+ ZorkGrandInquisitorLocations.BEAUTIFUL_THATS_PLENTY.value,
+ ZorkGrandInquisitorLocations.BEBURTT_DEMYSTIFIED.value,
+ ZorkGrandInquisitorLocations.BETTER_SPELL_MANUFACTURING_IN_UNDER_10_MINUTES.value,
+ ZorkGrandInquisitorLocations.BOING_BOING_BOING.value,
+ ZorkGrandInquisitorLocations.BONK.value,
+ ZorkGrandInquisitorLocations.BRAVE_SOULS_WANTED.value,
+ ZorkGrandInquisitorLocations.BROG_DO_GOOD.value,
+ ZorkGrandInquisitorLocations.BROG_EAT_ROCKS.value,
+ ZorkGrandInquisitorLocations.BROG_KNOW_DUMB_THAT_DUMB.value,
+ ZorkGrandInquisitorLocations.BROG_MUCH_BETTER_AT_THIS_GAME.value,
+ ZorkGrandInquisitorLocations.CASTLE_WATCHING_A_FIELD_GUIDE.value,
+ ZorkGrandInquisitorLocations.CAVES_NOTES.value,
+ ZorkGrandInquisitorLocations.CLOSING_THE_TIME_TUNNELS.value,
+ ZorkGrandInquisitorLocations.CRISIS_AVERTED.value,
+ ZorkGrandInquisitorLocations.DEATH_ATTACKED_THE_QUELBEES.value,
+ ZorkGrandInquisitorLocations.DEATH_CLIMBED_OUT_OF_THE_WELL.value,
+ ZorkGrandInquisitorLocations.DEATH_EATEN_BY_A_GRUE.value,
+ ZorkGrandInquisitorLocations.DEATH_JUMPED_IN_BOTTOMLESS_PIT.value,
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.DEATH_OUTSMARTED_BY_THE_QUELBEES.value,
+ ZorkGrandInquisitorLocations.DEATH_SLICED_UP_BY_THE_INVISIBLE_GUARD.value,
+ ZorkGrandInquisitorLocations.DEATH_STEPPED_INTO_THE_INFINITE.value,
+ ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.DEATH_THROCKED_THE_GRASS.value,
+ ZorkGrandInquisitorLocations.DEATH_TOTEMIZED.value,
+ ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY.value,
+ ZorkGrandInquisitorLocations.DEATH_YOURE_NOT_CHARON.value,
+ ZorkGrandInquisitorLocations.DEATH_ZORK_ROCKS_EXPLODED.value,
+ ZorkGrandInquisitorLocations.DENIED_BY_THE_LAKE_MONSTER.value,
+ ZorkGrandInquisitorLocations.DESPERATELY_SEEKING_TUTOR.value,
+ ZorkGrandInquisitorLocations.DONT_EVEN_START_WITH_US_SPARKY.value,
+ ZorkGrandInquisitorLocations.DOOOOOOWN.value,
+ ZorkGrandInquisitorLocations.DOWN.value,
+ ZorkGrandInquisitorLocations.DRAGON_ARCHIPELAGO_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.DUNCE_LOCKER.value,
+ ZorkGrandInquisitorLocations.EGGPLANTS.value,
+ ZorkGrandInquisitorLocations.EMERGENCY_MAGICATRONIC_MESSAGE.value,
+ ZorkGrandInquisitorLocations.ENJOY_YOUR_TRIP.value,
+ ZorkGrandInquisitorLocations.FAT_LOT_OF_GOOD_THATLL_DO_YA.value,
+ ZorkGrandInquisitorLocations.FLOOD_CONTROL_DAM_3_THE_NOT_REMOTELY_BORING_TALE.value,
+ ZorkGrandInquisitorLocations.FLYING_SNAPDRAGON.value,
+ ZorkGrandInquisitorLocations.FROBUARY_3_UNDERGROUNDHOG_DAY.value,
+ ZorkGrandInquisitorLocations.GETTING_SOME_CHANGE.value,
+ ZorkGrandInquisitorLocations.GUE_TECH_DEANS_LIST.value,
+ ZorkGrandInquisitorLocations.GUE_TECH_ENTRANCE_EXAM.value,
+ ZorkGrandInquisitorLocations.GUE_TECH_HEALTH_MEMO.value,
+ ZorkGrandInquisitorLocations.GUE_TECH_MAGEMEISTERS.value,
+ ZorkGrandInquisitorLocations.HAVE_A_HELL_OF_A_DAY.value,
+ ZorkGrandInquisitorLocations.HELLO_THIS_IS_SHONA_FROM_GURTH_PUBLISHING.value,
+ ZorkGrandInquisitorLocations.HEY_FREE_DIRT.value,
+ ZorkGrandInquisitorLocations.HI_MY_NAME_IS_DOUG.value,
+ ZorkGrandInquisitorLocations.HMMM_INFORMATIVE_YET_DEEPLY_DISTURBING.value,
+ ZorkGrandInquisitorLocations.HOLD_ON_FOR_AN_IMPORTANT_MESSAGE.value,
+ ZorkGrandInquisitorLocations.HOW_TO_HYPNOTIZE_YOURSELF.value,
+ ZorkGrandInquisitorLocations.HOW_TO_WIN_AT_DOUBLE_FANUCCI.value,
+ ZorkGrandInquisitorLocations.I_DONT_THINK_YOU_WOULDVE_WANTED_THAT_TO_WORK_ANYWAY.value,
+ ZorkGrandInquisitorLocations.I_SPIT_ON_YOUR_FILTHY_COINAGE.value,
+ ZorkGrandInquisitorLocations.IMBUE_BEBURTT.value,
+ ZorkGrandInquisitorLocations.INTO_THE_FOLIAGE.value,
+ ZorkGrandInquisitorLocations.IN_CASE_OF_ADVENTURE.value,
+ ZorkGrandInquisitorLocations.IN_MAGIC_WE_TRUST.value,
+ ZorkGrandInquisitorLocations.INVISIBLE_FLOWERS.value,
+ ZorkGrandInquisitorLocations.I_HOPE_YOU_CAN_CLIMB_UP_THERE.value,
+ ZorkGrandInquisitorLocations.I_LIKE_YOUR_STYLE.value,
+ ZorkGrandInquisitorLocations.LIT_SUNFLOWERS.value,
+ ZorkGrandInquisitorLocations.MAGIC_FOREVER.value,
+ ZorkGrandInquisitorLocations.MAILED_IT_TO_HELL.value,
+ ZorkGrandInquisitorLocations.MAKE_LOVE_NOT_WAR.value,
+ ZorkGrandInquisitorLocations.MIKES_PANTS.value,
+ ZorkGrandInquisitorLocations.MUSHROOM_HAMMERED.value,
+ ZorkGrandInquisitorLocations.NATIONAL_TREASURE.value,
+ ZorkGrandInquisitorLocations.NATURAL_AND_SUPERNATURAL_CREATURES_OF_QUENDOR.value,
+ ZorkGrandInquisitorLocations.NO_BONDAGE.value,
+ ZorkGrandInquisitorLocations.NOOOOOOOOOOOOO.value,
+ ZorkGrandInquisitorLocations.NOTHIN_LIKE_A_GOOD_STOGIE.value,
+ ZorkGrandInquisitorLocations.NOW_YOU_LOOK_LIKE_US_WHICH_IS_AN_IMPROVEMENT.value,
+ ZorkGrandInquisitorLocations.OBIDIL_DRIED_UP.value,
+ ZorkGrandInquisitorLocations.OH_DEAR_GOD_ITS_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.OH_VERY_FUNNY_GUYS.value,
+ ZorkGrandInquisitorLocations.OH_WOW_TALK_ABOUT_DEJA_VU.value,
+ ZorkGrandInquisitorLocations.OPEN_THE_GATES_OF_HELL.value,
+ ZorkGrandInquisitorLocations.OUTSMART_THE_QUELBEES.value,
+ ZorkGrandInquisitorLocations.PERMASEAL.value,
+ ZorkGrandInquisitorLocations.PLEASE_DONT_THROCK_THE_GRASS.value,
+ ZorkGrandInquisitorLocations.PORT_FOOZLE_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.PROZORKED.value,
+ ZorkGrandInquisitorLocations.REASSEMBLE_SNAVIG.value,
+ ZorkGrandInquisitorLocations.RESTOCKED_ON_GRUESDAY.value,
+ ZorkGrandInquisitorLocations.RIGHT_HELLO_YES_UH_THIS_IS_SNEFFLE.value,
+ ZorkGrandInquisitorLocations.RIGHT_UH_SORRY_ITS_ME_AGAIN_SNEFFLE.value,
+ ZorkGrandInquisitorLocations.SNAVIG_REPAIRED.value,
+ ZorkGrandInquisitorLocations.SOUVENIR.value,
+ ZorkGrandInquisitorLocations.STRAIGHT_TO_HELL.value,
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.SUCKING_ROCKS.value,
+ ZorkGrandInquisitorLocations.TAMING_YOUR_SNAPDRAGON.value,
+ ZorkGrandInquisitorLocations.THAR_SHE_BLOWS.value,
+ ZorkGrandInquisitorLocations.THATS_A_ROPE.value,
+ ZorkGrandInquisitorLocations.THATS_IT_JUST_KEEP_HITTING_THOSE_BUTTONS.value,
+ ZorkGrandInquisitorLocations.THATS_STILL_A_ROPE.value,
+ ZorkGrandInquisitorLocations.THE_ALCHEMICAL_DEBACLE.value,
+ ZorkGrandInquisitorLocations.THE_ENDLESS_FIRE.value,
+ ZorkGrandInquisitorLocations.THE_FLATHEADIAN_FUDGE_FIASCO.value,
+ ZorkGrandInquisitorLocations.THE_PERILS_OF_MAGIC.value,
+ ZorkGrandInquisitorLocations.THE_UNDERGROUND_UNDERGROUND.value,
+ ZorkGrandInquisitorLocations.THIS_DOESNT_LOOK_ANYTHING_LIKE_THE_BROCHURE.value,
+ ZorkGrandInquisitorLocations.THROCKED_MUSHROOM_HAMMERED.value,
+ ZorkGrandInquisitorLocations.TIME_TRAVEL_FOR_DUMMIES.value,
+ ZorkGrandInquisitorLocations.UH_OH_BROG_CANT_SWIM.value,
+ ZorkGrandInquisitorLocations.UMBRELLA_FLOWERS.value,
+ ZorkGrandInquisitorLocations.UP.value,
+ ZorkGrandInquisitorLocations.USELESS_BUT_FUN.value,
+ ZorkGrandInquisitorLocations.UUUUUP.value,
+ ZorkGrandInquisitorLocations.VOYAGE_OF_CAPTAIN_ZAHAB.value,
+ ZorkGrandInquisitorLocations.WANT_SOME_RYE_COURSE_YA_DO.value,
+ ZorkGrandInquisitorLocations.WE_DONT_SERVE_YOUR_KIND_HERE.value,
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER.value,
+ ZorkGrandInquisitorLocations.WHITE_HOUSE_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.WOW_IVE_NEVER_GONE_INSIDE_HIM_BEFORE.value,
+ ZorkGrandInquisitorLocations.YAD_GOHDNUORGREDNU_3_YRAUBORF.value,
+ ZorkGrandInquisitorLocations.YOU_DONT_GO_MESSING_WITH_A_MANS_ZIPPER.value,
+ ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS.value,
+ ZorkGrandInquisitorLocations.YOUR_PUNY_WEAPONS_DONT_PHASE_ME_BABY.value,
+ ZorkGrandInquisitorEvents.CHARON_CALLED.value,
+ ZorkGrandInquisitorEvents.DAM_DESTROYED.value,
+ ZorkGrandInquisitorEvents.DOOR_DRANK_MEAD.value,
+ ZorkGrandInquisitorEvents.DOOR_SMOKED_CIGAR.value,
+ ZorkGrandInquisitorEvents.DALBOZ_LOCKER_OPENABLE.value,
+ ZorkGrandInquisitorEvents.DUNCE_LOCKER_OPENABLE.value,
+ ZorkGrandInquisitorEvents.HAS_REPAIRABLE_OBIDIL.value,
+ ZorkGrandInquisitorEvents.HAS_REPAIRABLE_SNAVIG.value,
+ ZorkGrandInquisitorEvents.KNOWS_BEBURTT.value,
+ ZorkGrandInquisitorEvents.KNOWS_OBIDIL.value,
+ ZorkGrandInquisitorEvents.KNOWS_SNAVIG.value,
+ ZorkGrandInquisitorEvents.KNOWS_YASTARD.value,
+ ZorkGrandInquisitorEvents.ROPE_GLORFABLE.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ZorkGrandInquisitorEvents.WHITE_HOUSE_LETTER_MAILABLE.value,
+ ZorkGrandInquisitorEvents.ZORK_ROCKS_ACTIVATED.value,
+ ZorkGrandInquisitorEvents.ZORK_ROCKS_SUCKABLE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.ROPE.value,)]
+ )
+
+ def test_access_locations_requiring_scroll_fragment_ans(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.REASSEMBLE_SNAVIG.value,
+ ZorkGrandInquisitorEvents.HAS_REPAIRABLE_SNAVIG.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.SCROLL_FRAGMENT_ANS.value,)]
+ )
+
+ def test_access_locations_requiring_scroll_fragment_giv(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.REASSEMBLE_SNAVIG.value,
+ ZorkGrandInquisitorEvents.HAS_REPAIRABLE_SNAVIG.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.SCROLL_FRAGMENT_GIV.value,)]
+ )
+
+ def test_access_locations_requiring_shovel(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.HEY_FREE_DIRT.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.SHOVEL.value,)]
+ )
+
+ def test_access_locations_requiring_snapdragon(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.BOING_BOING_BOING.value,
+ ZorkGrandInquisitorLocations.FLYING_SNAPDRAGON.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.SNAPDRAGON.value,)]
+ )
+
+ def test_access_locations_requiring_student_id(self) -> None:
+ locations: List[str] = list()
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.STUDENT_ID.value,)]
+ )
+
+ def test_access_locations_requiring_subway_token(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.THE_UNDERGROUND_UNDERGROUND.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.SUBWAY_TOKEN.value,)]
+ )
+
+ def test_access_locations_requiring_sword(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.CLOSING_THE_TIME_TUNNELS.value,
+ ZorkGrandInquisitorLocations.DEATH_ATTACKED_THE_QUELBEES.value,
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.DEATH_TOTEMIZED.value,
+ ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY.value,
+ ZorkGrandInquisitorLocations.DONT_EVEN_START_WITH_US_SPARKY.value,
+ ZorkGrandInquisitorLocations.HMMM_INFORMATIVE_YET_DEEPLY_DISTURBING.value,
+ ZorkGrandInquisitorLocations.I_HOPE_YOU_CAN_CLIMB_UP_THERE.value,
+ ZorkGrandInquisitorLocations.I_LIKE_YOUR_STYLE.value,
+ ZorkGrandInquisitorLocations.IMBUE_BEBURTT.value,
+ ZorkGrandInquisitorLocations.INTO_THE_FOLIAGE.value,
+ ZorkGrandInquisitorLocations.MAKE_LOVE_NOT_WAR.value,
+ ZorkGrandInquisitorLocations.OBIDIL_DRIED_UP.value,
+ ZorkGrandInquisitorLocations.OH_VERY_FUNNY_GUYS.value,
+ ZorkGrandInquisitorLocations.OUTSMART_THE_QUELBEES.value,
+ ZorkGrandInquisitorLocations.PERMASEAL.value,
+ ZorkGrandInquisitorLocations.PORT_FOOZLE_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.SNAVIG_REPAIRED.value,
+ ZorkGrandInquisitorLocations.STRAIGHT_TO_HELL.value,
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.THE_ALCHEMICAL_DEBACLE.value,
+ ZorkGrandInquisitorLocations.THE_ENDLESS_FIRE.value,
+ ZorkGrandInquisitorLocations.THE_FLATHEADIAN_FUDGE_FIASCO.value,
+ ZorkGrandInquisitorLocations.THE_PERILS_OF_MAGIC.value,
+ ZorkGrandInquisitorLocations.WE_DONT_SERVE_YOUR_KIND_HERE.value,
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER.value,
+ ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS.value,
+ ZorkGrandInquisitorLocations.YOUR_PUNY_WEAPONS_DONT_PHASE_ME_BABY.value,
+ ZorkGrandInquisitorEvents.KNOWS_BEBURTT.value,
+ ZorkGrandInquisitorEvents.KNOWS_OBIDIL.value,
+ ZorkGrandInquisitorEvents.KNOWS_SNAVIG.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.SWORD.value,)]
+ )
+
+ def test_access_locations_requiring_zimdor_scroll(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.WANT_SOME_RYE_COURSE_YA_DO.value,
+ ZorkGrandInquisitorEvents.DOOR_DRANK_MEAD.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.ZIMDOR_SCROLL.value,)]
+ )
+
+ def test_access_locations_requiring_zork_rocks(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorEvents.ZORK_ROCKS_ACTIVATED.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.ZORK_ROCKS.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_666_mailbox(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.A_LETTER_FROM_THE_WHITE_HOUSE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_666_MAILBOX.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_alpines_quandry_card_slots(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_blank_scroll_box(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.IMBUE_BEBURTT.value,
+ ZorkGrandInquisitorEvents.KNOWS_BEBURTT.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_BLANK_SCROLL_BOX.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_blinds(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DENIED_BY_THE_LAKE_MONSTER.value,
+ ZorkGrandInquisitorLocations.WOW_IVE_NEVER_GONE_INSIDE_HIM_BEFORE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_BLINDS.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_candy_machine_buttons(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DUNCE_LOCKER.value,
+ ZorkGrandInquisitorLocations.NOOOOOOOOOOOOO.value,
+ ZorkGrandInquisitorEvents.DALBOZ_LOCKER_OPENABLE.value,
+ ZorkGrandInquisitorEvents.DUNCE_LOCKER_OPENABLE.value,
+ ZorkGrandInquisitorEvents.ZORK_ROCKS_SUCKABLE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_candy_machine_coin_slot(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DUNCE_LOCKER.value,
+ ZorkGrandInquisitorLocations.NOOOOOOOOOOOOO.value,
+ ZorkGrandInquisitorEvents.DALBOZ_LOCKER_OPENABLE.value,
+ ZorkGrandInquisitorEvents.DUNCE_LOCKER_OPENABLE.value,
+ ZorkGrandInquisitorEvents.ZORK_ROCKS_SUCKABLE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_candy_machine_vacuum_slot(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.SUCKING_ROCKS.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_VACUUM_SLOT.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_change_machine_slot(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.GETTING_SOME_CHANGE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_CHANGE_MACHINE_SLOT.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_closet_door(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.BROG_DO_GOOD.value,
+ ZorkGrandInquisitorLocations.BROG_EAT_ROCKS.value,
+ ZorkGrandInquisitorLocations.BROG_KNOW_DUMB_THAT_DUMB.value,
+ ZorkGrandInquisitorLocations.BROG_MUCH_BETTER_AT_THIS_GAME.value,
+ ZorkGrandInquisitorLocations.DOOOOOOWN.value,
+ ZorkGrandInquisitorLocations.DOWN.value,
+ ZorkGrandInquisitorLocations.UP.value,
+ ZorkGrandInquisitorLocations.UUUUUP.value,
+ ZorkGrandInquisitorLocations.MAILED_IT_TO_HELL.value,
+ ZorkGrandInquisitorLocations.WHITE_HOUSE_TIME_TUNNEL.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ZorkGrandInquisitorEvents.WHITE_HOUSE_LETTER_MAILABLE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_closing_the_time_tunnels_hammer_slot(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.OH_VERY_FUNNY_GUYS.value,
+ ZorkGrandInquisitorLocations.PORT_FOOZLE_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.WE_DONT_SERVE_YOUR_KIND_HERE.value,
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_closing_the_time_tunnels_lever(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.OH_VERY_FUNNY_GUYS.value,
+ ZorkGrandInquisitorLocations.PORT_FOOZLE_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.WE_DONT_SERVE_YOUR_KIND_HERE.value,
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_cooking_pot(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.BROG_DO_GOOD.value,
+ ZorkGrandInquisitorLocations.BROG_MUCH_BETTER_AT_THIS_GAME.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_dented_locker(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.CRISIS_AVERTED.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_DENTED_LOCKER.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_dirt_mound(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.HEY_FREE_DIRT.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_DIRT_MOUND.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_dock_winch(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.HELP_ME_CANT_BREATHE.value,
+ ZorkGrandInquisitorLocations.NO_BONDAGE.value,
+ ZorkGrandInquisitorLocations.YOU_WANT_A_PIECE_OF_ME_DOCK_BOY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_dragon_claw(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.OH_DEAR_GOD_ITS_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.THAR_SHE_BLOWS.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_DRAGON_CLAW.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_dragon_nostrils(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.OH_DEAR_GOD_ITS_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.THAR_SHE_BLOWS.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_dungeon_masters_lair_entrance(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.INTO_THE_FOLIAGE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_flood_control_buttons(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.NATIONAL_TREASURE.value,
+ ZorkGrandInquisitorEvents.DAM_DESTROYED.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_flood_control_doors(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.NATIONAL_TREASURE.value,
+ ZorkGrandInquisitorEvents.DAM_DESTROYED.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_frozen_treat_machine_coin_slot(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorEvents.HAS_REPAIRABLE_OBIDIL.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_COIN_SLOT.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_frozen_treat_machine_doors(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorEvents.HAS_REPAIRABLE_OBIDIL.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_DOORS.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_glass_case(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.IN_CASE_OF_ADVENTURE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_GLASS_CASE.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_grand_inquisitor_doll(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.ARREST_THE_VANDAL.value,
+ ZorkGrandInquisitorLocations.DEATH_ARRESTED_WITH_JACK.value,
+ ZorkGrandInquisitorLocations.FIRE_FIRE.value,
+ ZorkGrandInquisitorLocations.PLANETFALL.value,
+ ZorkGrandInquisitorLocations.TALK_TO_ME_GRAND_INQUISITOR.value,
+ ZorkGrandInquisitorEvents.LANTERN_DALBOZ_ACCESSIBLE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_gue_tech_door(self) -> None:
+ locations: List[str] = list()
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_DOOR.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_gue_tech_grass(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DEATH_THROCKED_THE_GRASS.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_GRASS.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_hades_phone_buttons(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.A_BIG_FAT_SASSY_2_HEADED_MONSTER.value,
+ ZorkGrandInquisitorLocations.A_LETTER_FROM_THE_WHITE_HOUSE.value,
+ ZorkGrandInquisitorLocations.DEATH_YOURE_NOT_CHARON.value,
+ ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.DONT_EVEN_START_WITH_US_SPARKY.value,
+ ZorkGrandInquisitorLocations.DRAGON_ARCHIPELAGO_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.HAVE_A_HELL_OF_A_DAY.value,
+ ZorkGrandInquisitorLocations.NOW_YOU_LOOK_LIKE_US_WHICH_IS_AN_IMPROVEMENT.value,
+ ZorkGrandInquisitorLocations.OH_DEAR_GOD_ITS_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.OPEN_THE_GATES_OF_HELL.value,
+ ZorkGrandInquisitorLocations.THAR_SHE_BLOWS.value,
+ ZorkGrandInquisitorLocations.THIS_DOESNT_LOOK_ANYTHING_LIKE_THE_BROCHURE.value,
+ ZorkGrandInquisitorLocations.UH_OH_BROG_CANT_SWIM.value,
+ ZorkGrandInquisitorEvents.CHARON_CALLED.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_hades_phone_receiver(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.A_BIG_FAT_SASSY_2_HEADED_MONSTER.value,
+ ZorkGrandInquisitorLocations.A_LETTER_FROM_THE_WHITE_HOUSE.value,
+ ZorkGrandInquisitorLocations.DEATH_YOURE_NOT_CHARON.value,
+ ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.DONT_EVEN_START_WITH_US_SPARKY.value,
+ ZorkGrandInquisitorLocations.DRAGON_ARCHIPELAGO_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.HAVE_A_HELL_OF_A_DAY.value,
+ ZorkGrandInquisitorLocations.NOW_YOU_LOOK_LIKE_US_WHICH_IS_AN_IMPROVEMENT.value,
+ ZorkGrandInquisitorLocations.OH_DEAR_GOD_ITS_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.OPEN_THE_GATES_OF_HELL.value,
+ ZorkGrandInquisitorLocations.THAR_SHE_BLOWS.value,
+ ZorkGrandInquisitorLocations.THIS_DOESNT_LOOK_ANYTHING_LIKE_THE_BROCHURE.value,
+ ZorkGrandInquisitorLocations.UH_OH_BROG_CANT_SWIM.value,
+ ZorkGrandInquisitorEvents.CHARON_CALLED.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_harry(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.YOUR_PUNY_WEAPONS_DONT_PHASE_ME_BABY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_HARRY.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_harrys_ashtray(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.NOTHIN_LIKE_A_GOOD_STOGIE.value,
+ ZorkGrandInquisitorEvents.DOOR_SMOKED_CIGAR.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_harrys_bird_bath(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.WANT_SOME_RYE_COURSE_YA_DO.value,
+ ZorkGrandInquisitorEvents.DOOR_DRANK_MEAD.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_in_magic_we_trust_door(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.IN_MAGIC_WE_TRUST.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_jacks_door(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.MEAD_LIGHT.value,
+ ZorkGrandInquisitorLocations.NO_AUTOGRAPHS.value,
+ ZorkGrandInquisitorLocations.THATS_A_ROPE.value,
+ ZorkGrandInquisitorLocations.WHAT_ARE_YOU_STUPID.value,
+ ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_loudspeaker_volume_buttons(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.THATS_THE_SPIRIT.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_LOUDSPEAKER_VOLUME_BUTTONS.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_mailbox_door(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.MAILED_IT_TO_HELL.value,
+ ZorkGrandInquisitorEvents.WHITE_HOUSE_LETTER_MAILABLE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_DOOR.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_mailbox_flag(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DOOOOOOWN.value,
+ ZorkGrandInquisitorLocations.DOWN.value,
+ ZorkGrandInquisitorLocations.MAILED_IT_TO_HELL.value,
+ ZorkGrandInquisitorLocations.UP.value,
+ ZorkGrandInquisitorLocations.UUUUUP.value,
+ ZorkGrandInquisitorEvents.WHITE_HOUSE_LETTER_MAILABLE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_mirror(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.REASSEMBLE_SNAVIG.value,
+ ZorkGrandInquisitorLocations.YAD_GOHDNUORGREDNU_3_YRAUBORF.value,
+ ZorkGrandInquisitorEvents.HAS_REPAIRABLE_SNAVIG.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_MIRROR.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_monastery_vent(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.CLOSING_THE_TIME_TUNNELS.value,
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.DEATH_TOTEMIZED.value,
+ ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY.value,
+ ZorkGrandInquisitorLocations.HMMM_INFORMATIVE_YET_DEEPLY_DISTURBING.value,
+ ZorkGrandInquisitorLocations.I_HOPE_YOU_CAN_CLIMB_UP_THERE.value,
+ ZorkGrandInquisitorLocations.OH_VERY_FUNNY_GUYS.value,
+ ZorkGrandInquisitorLocations.PERMASEAL.value,
+ ZorkGrandInquisitorLocations.PORT_FOOZLE_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.STRAIGHT_TO_HELL.value,
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.THE_ALCHEMICAL_DEBACLE.value,
+ ZorkGrandInquisitorLocations.THE_ENDLESS_FIRE.value,
+ ZorkGrandInquisitorLocations.THE_FLATHEADIAN_FUDGE_FIASCO.value,
+ ZorkGrandInquisitorLocations.THE_PERILS_OF_MAGIC.value,
+ ZorkGrandInquisitorLocations.WE_DONT_SERVE_YOUR_KIND_HERE.value,
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_MONASTERY_VENT.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_mossy_grate(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.BEAUTIFUL_THATS_PLENTY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_MOSSY_GRATE.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_port_foozle_past_tavern_door(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.OH_VERY_FUNNY_GUYS.value,
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.WE_DONT_SERVE_YOUR_KIND_HERE.value,
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_purple_words(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.A_SMALLWAY.value,
+ ZorkGrandInquisitorLocations.CRISIS_AVERTED.value,
+ ZorkGrandInquisitorLocations.DEATH_STEPPED_INTO_THE_INFINITE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_quelbee_hive(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DEATH_ATTACKED_THE_QUELBEES.value,
+ ZorkGrandInquisitorLocations.DEATH_OUTSMARTED_BY_THE_QUELBEES.value,
+ ZorkGrandInquisitorLocations.OUTSMART_THE_QUELBEES.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_rope_bridge(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.I_LIKE_YOUR_STYLE.value,
+ ZorkGrandInquisitorLocations.IMBUE_BEBURTT.value,
+ ZorkGrandInquisitorLocations.OBIDIL_DRIED_UP.value,
+ ZorkGrandInquisitorLocations.SNAVIG_REPAIRED.value,
+ ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS.value,
+ ZorkGrandInquisitorEvents.KNOWS_BEBURTT.value,
+ ZorkGrandInquisitorEvents.KNOWS_OBIDIL.value,
+ ZorkGrandInquisitorEvents.KNOWS_SNAVIG.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_skull_cage(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.BROG_MUCH_BETTER_AT_THIS_GAME.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_snapdragon(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.BONK.value,
+ ZorkGrandInquisitorLocations.I_DONT_THINK_YOU_WOULDVE_WANTED_THAT_TO_WORK_ANYWAY.value,
+ ZorkGrandInquisitorLocations.PROZORKED.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_soda_machine_buttons(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorEvents.ZORK_ROCKS_ACTIVATED.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_BUTTONS.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_soda_machine_coin_slot(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorEvents.ZORK_ROCKS_ACTIVATED.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_COIN_SLOT.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_souvenir_coin_slot(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.SOUVENIR.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_SOUVENIR_COIN_SLOT.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_spell_checker(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.IMBUE_BEBURTT.value,
+ ZorkGrandInquisitorLocations.OBIDIL_DRIED_UP.value,
+ ZorkGrandInquisitorLocations.SNAVIG_REPAIRED.value,
+ ZorkGrandInquisitorEvents.KNOWS_BEBURTT.value,
+ ZorkGrandInquisitorEvents.KNOWS_OBIDIL.value,
+ ZorkGrandInquisitorEvents.KNOWS_SNAVIG.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_spell_lab_chasm(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.I_LIKE_YOUR_STYLE.value,
+ ZorkGrandInquisitorLocations.IMBUE_BEBURTT.value,
+ ZorkGrandInquisitorLocations.OBIDIL_DRIED_UP.value,
+ ZorkGrandInquisitorLocations.SNAVIG_REPAIRED.value,
+ ZorkGrandInquisitorEvents.KNOWS_BEBURTT.value,
+ ZorkGrandInquisitorEvents.KNOWS_OBIDIL.value,
+ ZorkGrandInquisitorEvents.KNOWS_SNAVIG.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_spring_mushroom(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.BOING_BOING_BOING.value,
+ ZorkGrandInquisitorLocations.FLYING_SNAPDRAGON.value,
+ ZorkGrandInquisitorLocations.MUSHROOM_HAMMERED.value,
+ ZorkGrandInquisitorLocations.THROCKED_MUSHROOM_HAMMERED.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_student_id_machine(self) -> None:
+ locations: List[str] = list()
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_STUDENT_ID_MACHINE.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_subway_token_slot(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.THE_UNDERGROUND_UNDERGROUND.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_tavern_fly(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_totemizer_switch(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.CLOSING_THE_TIME_TUNNELS.value,
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.DEATH_TOTEMIZED.value,
+ ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY.value,
+ ZorkGrandInquisitorLocations.OH_VERY_FUNNY_GUYS.value,
+ ZorkGrandInquisitorLocations.PORT_FOOZLE_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.STRAIGHT_TO_HELL.value,
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.THE_ALCHEMICAL_DEBACLE.value,
+ ZorkGrandInquisitorLocations.THE_ENDLESS_FIRE.value,
+ ZorkGrandInquisitorLocations.THE_FLATHEADIAN_FUDGE_FIASCO.value,
+ ZorkGrandInquisitorLocations.THE_PERILS_OF_MAGIC.value,
+ ZorkGrandInquisitorLocations.WE_DONT_SERVE_YOUR_KIND_HERE.value,
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_totemizer_wheels(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.CLOSING_THE_TIME_TUNNELS.value,
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.DEATH_TOTEMIZED.value,
+ ZorkGrandInquisitorLocations.OH_VERY_FUNNY_GUYS.value,
+ ZorkGrandInquisitorLocations.PORT_FOOZLE_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.STRAIGHT_TO_HELL.value,
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.THE_ALCHEMICAL_DEBACLE.value,
+ ZorkGrandInquisitorLocations.THE_ENDLESS_FIRE.value,
+ ZorkGrandInquisitorLocations.THE_FLATHEADIAN_FUDGE_FIASCO.value,
+ ZorkGrandInquisitorLocations.THE_PERILS_OF_MAGIC.value,
+ ZorkGrandInquisitorLocations.WE_DONT_SERVE_YOUR_KIND_HERE.value,
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS.value,)]
+ )
+
+ def test_access_locations_requiring_hotspot_well(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.ALARM_SYSTEM_IS_DOWN.value,
+ ZorkGrandInquisitorLocations.ARTIFACTS_EXPLAINED.value,
+ ZorkGrandInquisitorLocations.A_BIG_FAT_SASSY_2_HEADED_MONSTER.value,
+ ZorkGrandInquisitorLocations.A_LETTER_FROM_THE_WHITE_HOUSE.value,
+ ZorkGrandInquisitorLocations.A_SMALLWAY.value,
+ ZorkGrandInquisitorLocations.BEAUTIFUL_THATS_PLENTY.value,
+ ZorkGrandInquisitorLocations.BEBURTT_DEMYSTIFIED.value,
+ ZorkGrandInquisitorLocations.BETTER_SPELL_MANUFACTURING_IN_UNDER_10_MINUTES.value,
+ ZorkGrandInquisitorLocations.BOING_BOING_BOING.value,
+ ZorkGrandInquisitorLocations.BONK.value,
+ ZorkGrandInquisitorLocations.BRAVE_SOULS_WANTED.value,
+ ZorkGrandInquisitorLocations.BROG_DO_GOOD.value,
+ ZorkGrandInquisitorLocations.BROG_EAT_ROCKS.value,
+ ZorkGrandInquisitorLocations.BROG_KNOW_DUMB_THAT_DUMB.value,
+ ZorkGrandInquisitorLocations.BROG_MUCH_BETTER_AT_THIS_GAME.value,
+ ZorkGrandInquisitorLocations.CASTLE_WATCHING_A_FIELD_GUIDE.value,
+ ZorkGrandInquisitorLocations.CAVES_NOTES.value,
+ ZorkGrandInquisitorLocations.CLOSING_THE_TIME_TUNNELS.value,
+ ZorkGrandInquisitorLocations.CRISIS_AVERTED.value,
+ ZorkGrandInquisitorLocations.DEATH_ATTACKED_THE_QUELBEES.value,
+ ZorkGrandInquisitorLocations.DEATH_CLIMBED_OUT_OF_THE_WELL.value,
+ ZorkGrandInquisitorLocations.DEATH_EATEN_BY_A_GRUE.value,
+ ZorkGrandInquisitorLocations.DEATH_JUMPED_IN_BOTTOMLESS_PIT.value,
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.DEATH_OUTSMARTED_BY_THE_QUELBEES.value,
+ ZorkGrandInquisitorLocations.DEATH_SLICED_UP_BY_THE_INVISIBLE_GUARD.value,
+ ZorkGrandInquisitorLocations.DEATH_STEPPED_INTO_THE_INFINITE.value,
+ ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.DEATH_THROCKED_THE_GRASS.value,
+ ZorkGrandInquisitorLocations.DEATH_TOTEMIZED.value,
+ ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY.value,
+ ZorkGrandInquisitorLocations.DEATH_YOURE_NOT_CHARON.value,
+ ZorkGrandInquisitorLocations.DEATH_ZORK_ROCKS_EXPLODED.value,
+ ZorkGrandInquisitorLocations.DENIED_BY_THE_LAKE_MONSTER.value,
+ ZorkGrandInquisitorLocations.DESPERATELY_SEEKING_TUTOR.value,
+ ZorkGrandInquisitorLocations.DONT_EVEN_START_WITH_US_SPARKY.value,
+ ZorkGrandInquisitorLocations.DOOOOOOWN.value,
+ ZorkGrandInquisitorLocations.DOWN.value,
+ ZorkGrandInquisitorLocations.DRAGON_ARCHIPELAGO_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.DUNCE_LOCKER.value,
+ ZorkGrandInquisitorLocations.EGGPLANTS.value,
+ ZorkGrandInquisitorLocations.EMERGENCY_MAGICATRONIC_MESSAGE.value,
+ ZorkGrandInquisitorLocations.ENJOY_YOUR_TRIP.value,
+ ZorkGrandInquisitorLocations.FAT_LOT_OF_GOOD_THATLL_DO_YA.value,
+ ZorkGrandInquisitorLocations.FLOOD_CONTROL_DAM_3_THE_NOT_REMOTELY_BORING_TALE.value,
+ ZorkGrandInquisitorLocations.FLYING_SNAPDRAGON.value,
+ ZorkGrandInquisitorLocations.FROBUARY_3_UNDERGROUNDHOG_DAY.value,
+ ZorkGrandInquisitorLocations.GETTING_SOME_CHANGE.value,
+ ZorkGrandInquisitorLocations.GUE_TECH_DEANS_LIST.value,
+ ZorkGrandInquisitorLocations.GUE_TECH_ENTRANCE_EXAM.value,
+ ZorkGrandInquisitorLocations.GUE_TECH_HEALTH_MEMO.value,
+ ZorkGrandInquisitorLocations.GUE_TECH_MAGEMEISTERS.value,
+ ZorkGrandInquisitorLocations.HAVE_A_HELL_OF_A_DAY.value,
+ ZorkGrandInquisitorLocations.HELLO_THIS_IS_SHONA_FROM_GURTH_PUBLISHING.value,
+ ZorkGrandInquisitorLocations.HEY_FREE_DIRT.value,
+ ZorkGrandInquisitorLocations.HI_MY_NAME_IS_DOUG.value,
+ ZorkGrandInquisitorLocations.HMMM_INFORMATIVE_YET_DEEPLY_DISTURBING.value,
+ ZorkGrandInquisitorLocations.HOLD_ON_FOR_AN_IMPORTANT_MESSAGE.value,
+ ZorkGrandInquisitorLocations.HOW_TO_HYPNOTIZE_YOURSELF.value,
+ ZorkGrandInquisitorLocations.HOW_TO_WIN_AT_DOUBLE_FANUCCI.value,
+ ZorkGrandInquisitorLocations.I_DONT_THINK_YOU_WOULDVE_WANTED_THAT_TO_WORK_ANYWAY.value,
+ ZorkGrandInquisitorLocations.I_SPIT_ON_YOUR_FILTHY_COINAGE.value,
+ ZorkGrandInquisitorLocations.IMBUE_BEBURTT.value,
+ ZorkGrandInquisitorLocations.INTO_THE_FOLIAGE.value,
+ ZorkGrandInquisitorLocations.IN_CASE_OF_ADVENTURE.value,
+ ZorkGrandInquisitorLocations.IN_MAGIC_WE_TRUST.value,
+ ZorkGrandInquisitorLocations.INVISIBLE_FLOWERS.value,
+ ZorkGrandInquisitorLocations.I_HOPE_YOU_CAN_CLIMB_UP_THERE.value,
+ ZorkGrandInquisitorLocations.I_LIKE_YOUR_STYLE.value,
+ ZorkGrandInquisitorLocations.LIT_SUNFLOWERS.value,
+ ZorkGrandInquisitorLocations.MAGIC_FOREVER.value,
+ ZorkGrandInquisitorLocations.MAILED_IT_TO_HELL.value,
+ ZorkGrandInquisitorLocations.MAKE_LOVE_NOT_WAR.value,
+ ZorkGrandInquisitorLocations.MIKES_PANTS.value,
+ ZorkGrandInquisitorLocations.MUSHROOM_HAMMERED.value,
+ ZorkGrandInquisitorLocations.NATIONAL_TREASURE.value,
+ ZorkGrandInquisitorLocations.NATURAL_AND_SUPERNATURAL_CREATURES_OF_QUENDOR.value,
+ ZorkGrandInquisitorLocations.NOOOOOOOOOOOOO.value,
+ ZorkGrandInquisitorLocations.NOTHIN_LIKE_A_GOOD_STOGIE.value,
+ ZorkGrandInquisitorLocations.NOW_YOU_LOOK_LIKE_US_WHICH_IS_AN_IMPROVEMENT.value,
+ ZorkGrandInquisitorLocations.OBIDIL_DRIED_UP.value,
+ ZorkGrandInquisitorLocations.OH_DEAR_GOD_ITS_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.OH_VERY_FUNNY_GUYS.value,
+ ZorkGrandInquisitorLocations.OH_WOW_TALK_ABOUT_DEJA_VU.value,
+ ZorkGrandInquisitorLocations.OPEN_THE_GATES_OF_HELL.value,
+ ZorkGrandInquisitorLocations.OUTSMART_THE_QUELBEES.value,
+ ZorkGrandInquisitorLocations.PERMASEAL.value,
+ ZorkGrandInquisitorLocations.PLEASE_DONT_THROCK_THE_GRASS.value,
+ ZorkGrandInquisitorLocations.PORT_FOOZLE_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.PROZORKED.value,
+ ZorkGrandInquisitorLocations.REASSEMBLE_SNAVIG.value,
+ ZorkGrandInquisitorLocations.RESTOCKED_ON_GRUESDAY.value,
+ ZorkGrandInquisitorLocations.RIGHT_HELLO_YES_UH_THIS_IS_SNEFFLE.value,
+ ZorkGrandInquisitorLocations.RIGHT_UH_SORRY_ITS_ME_AGAIN_SNEFFLE.value,
+ ZorkGrandInquisitorLocations.SNAVIG_REPAIRED.value,
+ ZorkGrandInquisitorLocations.SOUVENIR.value,
+ ZorkGrandInquisitorLocations.STRAIGHT_TO_HELL.value,
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.SUCKING_ROCKS.value,
+ ZorkGrandInquisitorLocations.TAMING_YOUR_SNAPDRAGON.value,
+ ZorkGrandInquisitorLocations.THAR_SHE_BLOWS.value,
+ ZorkGrandInquisitorLocations.THATS_IT_JUST_KEEP_HITTING_THOSE_BUTTONS.value,
+ ZorkGrandInquisitorLocations.THATS_STILL_A_ROPE.value,
+ ZorkGrandInquisitorLocations.THE_ALCHEMICAL_DEBACLE.value,
+ ZorkGrandInquisitorLocations.THE_ENDLESS_FIRE.value,
+ ZorkGrandInquisitorLocations.THE_FLATHEADIAN_FUDGE_FIASCO.value,
+ ZorkGrandInquisitorLocations.THE_PERILS_OF_MAGIC.value,
+ ZorkGrandInquisitorLocations.THE_UNDERGROUND_UNDERGROUND.value,
+ ZorkGrandInquisitorLocations.THIS_DOESNT_LOOK_ANYTHING_LIKE_THE_BROCHURE.value,
+ ZorkGrandInquisitorLocations.THROCKED_MUSHROOM_HAMMERED.value,
+ ZorkGrandInquisitorLocations.TIME_TRAVEL_FOR_DUMMIES.value,
+ ZorkGrandInquisitorLocations.UH_OH_BROG_CANT_SWIM.value,
+ ZorkGrandInquisitorLocations.UMBRELLA_FLOWERS.value,
+ ZorkGrandInquisitorLocations.UP.value,
+ ZorkGrandInquisitorLocations.USELESS_BUT_FUN.value,
+ ZorkGrandInquisitorLocations.UUUUUP.value,
+ ZorkGrandInquisitorLocations.VOYAGE_OF_CAPTAIN_ZAHAB.value,
+ ZorkGrandInquisitorLocations.WANT_SOME_RYE_COURSE_YA_DO.value,
+ ZorkGrandInquisitorLocations.WE_DONT_SERVE_YOUR_KIND_HERE.value,
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER.value,
+ ZorkGrandInquisitorLocations.WHITE_HOUSE_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.WOW_IVE_NEVER_GONE_INSIDE_HIM_BEFORE.value,
+ ZorkGrandInquisitorLocations.YAD_GOHDNUORGREDNU_3_YRAUBORF.value,
+ ZorkGrandInquisitorLocations.YOU_DONT_GO_MESSING_WITH_A_MANS_ZIPPER.value,
+ ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS.value,
+ ZorkGrandInquisitorLocations.YOUR_PUNY_WEAPONS_DONT_PHASE_ME_BABY.value,
+ ZorkGrandInquisitorEvents.CHARON_CALLED.value,
+ ZorkGrandInquisitorEvents.DAM_DESTROYED.value,
+ ZorkGrandInquisitorEvents.DOOR_DRANK_MEAD.value,
+ ZorkGrandInquisitorEvents.DOOR_SMOKED_CIGAR.value,
+ ZorkGrandInquisitorEvents.DALBOZ_LOCKER_OPENABLE.value,
+ ZorkGrandInquisitorEvents.DUNCE_LOCKER_OPENABLE.value,
+ ZorkGrandInquisitorEvents.HAS_REPAIRABLE_OBIDIL.value,
+ ZorkGrandInquisitorEvents.HAS_REPAIRABLE_SNAVIG.value,
+ ZorkGrandInquisitorEvents.KNOWS_BEBURTT.value,
+ ZorkGrandInquisitorEvents.KNOWS_OBIDIL.value,
+ ZorkGrandInquisitorEvents.KNOWS_SNAVIG.value,
+ ZorkGrandInquisitorEvents.KNOWS_YASTARD.value,
+ ZorkGrandInquisitorEvents.ROPE_GLORFABLE.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ZorkGrandInquisitorEvents.WHITE_HOUSE_LETTER_MAILABLE.value,
+ ZorkGrandInquisitorEvents.ZORK_ROCKS_ACTIVATED.value,
+ ZorkGrandInquisitorEvents.ZORK_ROCKS_SUCKABLE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.HOTSPOT_WELL.value,)]
+ )
+
+ def test_access_locations_requiring_spell_glorf(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorEvents.ROPE_GLORFABLE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.SPELL_GLORF.value,)]
+ )
+
+ def test_access_locations_requiring_spell_golgatem(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DENIED_BY_THE_LAKE_MONSTER.value,
+ ZorkGrandInquisitorLocations.I_LIKE_YOUR_STYLE.value,
+ ZorkGrandInquisitorLocations.IMBUE_BEBURTT.value,
+ ZorkGrandInquisitorLocations.OBIDIL_DRIED_UP.value,
+ ZorkGrandInquisitorLocations.SNAVIG_REPAIRED.value,
+ ZorkGrandInquisitorLocations.USELESS_BUT_FUN.value,
+ ZorkGrandInquisitorEvents.KNOWS_BEBURTT.value,
+ ZorkGrandInquisitorEvents.KNOWS_OBIDIL.value,
+ ZorkGrandInquisitorEvents.KNOWS_SNAVIG.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.SPELL_GOLGATEM.value,)]
+ )
+
+ def test_access_locations_requiring_spell_igram(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.A_SMALLWAY.value,
+ ZorkGrandInquisitorLocations.CRISIS_AVERTED.value,
+ ZorkGrandInquisitorLocations.DEATH_STEPPED_INTO_THE_INFINITE.value,
+ ZorkGrandInquisitorLocations.FAT_LOT_OF_GOOD_THATLL_DO_YA.value,
+ ZorkGrandInquisitorLocations.INVISIBLE_FLOWERS.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.SPELL_IGRAM.value,)]
+ )
+
+ def test_access_locations_requiring_spell_kendall(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.BEBURTT_DEMYSTIFIED.value,
+ ZorkGrandInquisitorLocations.ENJOY_YOUR_TRIP.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.SPELL_KENDALL.value,)]
+ )
+
+ def test_access_locations_requiring_spell_narwile(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.BROG_DO_GOOD.value,
+ ZorkGrandInquisitorLocations.BROG_EAT_ROCKS.value,
+ ZorkGrandInquisitorLocations.BROG_KNOW_DUMB_THAT_DUMB.value,
+ ZorkGrandInquisitorLocations.BROG_MUCH_BETTER_AT_THIS_GAME.value,
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.DOOOOOOWN.value,
+ ZorkGrandInquisitorLocations.DOWN.value,
+ ZorkGrandInquisitorLocations.DRAGON_ARCHIPELAGO_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.MAILED_IT_TO_HELL.value,
+ ZorkGrandInquisitorLocations.OH_DEAR_GOD_ITS_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.OH_VERY_FUNNY_GUYS.value,
+ ZorkGrandInquisitorLocations.PORT_FOOZLE_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.THAR_SHE_BLOWS.value,
+ ZorkGrandInquisitorLocations.THIS_DOESNT_LOOK_ANYTHING_LIKE_THE_BROCHURE.value,
+ ZorkGrandInquisitorLocations.UH_OH_BROG_CANT_SWIM.value,
+ ZorkGrandInquisitorLocations.UP.value,
+ ZorkGrandInquisitorLocations.UUUUUP.value,
+ ZorkGrandInquisitorLocations.WE_DONT_SERVE_YOUR_KIND_HERE.value,
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER.value,
+ ZorkGrandInquisitorLocations.WHITE_HOUSE_TIME_TUNNEL.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ZorkGrandInquisitorEvents.WHITE_HOUSE_LETTER_MAILABLE.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.SPELL_NARWILE.value,)]
+ )
+
+ def test_access_locations_requiring_spell_rezrov(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.IN_MAGIC_WE_TRUST.value,
+ ZorkGrandInquisitorLocations.NATIONAL_TREASURE.value,
+ ZorkGrandInquisitorLocations.YOU_DONT_GO_MESSING_WITH_A_MANS_ZIPPER.value,
+ ZorkGrandInquisitorEvents.DAM_DESTROYED.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.SPELL_REZROV.value,)]
+ )
+
+ def test_access_locations_requiring_spell_throck(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.BEAUTIFUL_THATS_PLENTY.value,
+ ZorkGrandInquisitorLocations.DEATH_THROCKED_THE_GRASS.value,
+ ZorkGrandInquisitorLocations.FLYING_SNAPDRAGON.value,
+ ZorkGrandInquisitorLocations.I_DONT_THINK_YOU_WOULDVE_WANTED_THAT_TO_WORK_ANYWAY.value,
+ ZorkGrandInquisitorLocations.LIT_SUNFLOWERS.value,
+ ZorkGrandInquisitorLocations.THROCKED_MUSHROOM_HAMMERED.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.SPELL_THROCK.value,)]
+ )
+
+ def test_access_locations_requiring_subway_destination_flood_control_dam(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.BEAUTIFUL_THATS_PLENTY.value,
+ ZorkGrandInquisitorLocations.FLOOD_CONTROL_DAM_3_THE_NOT_REMOTELY_BORING_TALE.value,
+ ZorkGrandInquisitorLocations.NATIONAL_TREASURE.value,
+ ZorkGrandInquisitorLocations.SOUVENIR.value,
+ ZorkGrandInquisitorLocations.USELESS_BUT_FUN.value,
+ ZorkGrandInquisitorEvents.DAM_DESTROYED.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM.value,)]
+ )
+
+ def test_access_locations_requiring_subway_destination_hades(self) -> None:
+ locations: List[str] = list()
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES.value,)]
+ )
+
+ def test_access_locations_requiring_subway_destination_monastery(self) -> None:
+ locations: List[str] = list()
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY.value,)]
+ )
+
+ def test_access_locations_requiring_teleporter_destination_dm_lair(self) -> None:
+ locations: List[str] = list()
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR.value,)]
+ )
+
+ def test_access_locations_requiring_teleporter_destination_gue_tech(self) -> None:
+ locations: List[str] = list()
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH.value,)]
+ )
+
+ def test_access_locations_requiring_teleporter_destination_hades(self) -> None:
+ locations: List[str] = list()
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES.value,)]
+ )
+
+ def test_access_locations_requiring_teleporter_destination_monastery(self) -> None:
+ locations: List[str] = list()
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY.value,)]
+ )
+
+ def test_access_locations_requiring_teleporter_destination_spell_lab(self) -> None:
+ locations: List[str] = list()
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB.value,)]
+ )
+
+ def test_access_locations_requiring_totemizer_destination_hall_of_inquisition(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.CLOSING_THE_TIME_TUNNELS.value,
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.OH_VERY_FUNNY_GUYS.value,
+ ZorkGrandInquisitorLocations.PORT_FOOZLE_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.THE_ALCHEMICAL_DEBACLE.value,
+ ZorkGrandInquisitorLocations.THE_ENDLESS_FIRE.value,
+ ZorkGrandInquisitorLocations.THE_FLATHEADIAN_FUDGE_FIASCO.value,
+ ZorkGrandInquisitorLocations.THE_PERILS_OF_MAGIC.value,
+ ZorkGrandInquisitorLocations.WE_DONT_SERVE_YOUR_KIND_HERE.value,
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_HALL_OF_INQUISITION.value,)]
+ )
+
+ def test_access_locations_requiring_totemizer_destination_straight_to_hell(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.STRAIGHT_TO_HELL.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL.value,)]
+ )
+
+ def test_access_locations_requiring_totem_brog(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.BROG_DO_GOOD.value,
+ ZorkGrandInquisitorLocations.BROG_EAT_ROCKS.value,
+ ZorkGrandInquisitorLocations.BROG_KNOW_DUMB_THAT_DUMB.value,
+ ZorkGrandInquisitorLocations.BROG_MUCH_BETTER_AT_THIS_GAME.value,
+ ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.DRAGON_ARCHIPELAGO_TIME_TUNNEL.value,
+ ZorkGrandInquisitorLocations.OH_DEAR_GOD_ITS_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.OH_VERY_FUNNY_GUYS.value,
+ ZorkGrandInquisitorLocations.THAR_SHE_BLOWS.value,
+ ZorkGrandInquisitorLocations.THIS_DOESNT_LOOK_ANYTHING_LIKE_THE_BROCHURE.value,
+ ZorkGrandInquisitorLocations.UH_OH_BROG_CANT_SWIM.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.TOTEM_BROG.value,)]
+ )
+
+ def test_access_locations_requiring_totem_griff(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.DOOOOOOWN.value,
+ ZorkGrandInquisitorLocations.OH_DEAR_GOD_ITS_A_DRAGON.value,
+ ZorkGrandInquisitorLocations.THAR_SHE_BLOWS.value,
+ ZorkGrandInquisitorLocations.UUUUUP.value,
+ ZorkGrandInquisitorLocations.WE_DONT_SERVE_YOUR_KIND_HERE.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.TOTEM_GRIFF.value,)]
+ )
+
+ def test_access_locations_requiring_totem_lucy(self) -> None:
+ locations: List[str] = [
+ ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.DOWN.value,
+ ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER.value,
+ ZorkGrandInquisitorLocations.THIS_DOESNT_LOOK_ANYTHING_LIKE_THE_BROCHURE.value,
+ ZorkGrandInquisitorLocations.UP.value,
+ ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER.value,
+ ZorkGrandInquisitorEvents.VICTORY.value,
+ ]
+
+ self.assertAccessDependency(
+ locations, [(ZorkGrandInquisitorItems.TOTEM_LUCY.value,)]
+ )
diff --git a/worlds/zork_grand_inquisitor/test/test_data_funcs.py b/worlds/zork_grand_inquisitor/test/test_data_funcs.py
new file mode 100644
index 000000000000..9d8d5a4ba356
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/test/test_data_funcs.py
@@ -0,0 +1,132 @@
+import unittest
+
+from ..data_funcs import location_access_rule_for, entrance_access_rule_for
+from ..enums import ZorkGrandInquisitorLocations, ZorkGrandInquisitorRegions
+
+
+class DataFuncsTest(unittest.TestCase):
+ def test_location_access_rule_for(self) -> None:
+ # No Requirements
+ self.assertEqual(
+ "lambda state: True",
+ location_access_rule_for(ZorkGrandInquisitorLocations.ALARM_SYSTEM_IS_DOWN, 1),
+ )
+
+ # Single Item Requirement
+ self.assertEqual(
+ 'lambda state: state.has("Sword", 1)',
+ location_access_rule_for(ZorkGrandInquisitorLocations.DONT_EVEN_START_WITH_US_SPARKY, 1),
+ )
+
+ self.assertEqual(
+ 'lambda state: state.has("Spell: NARWILE", 1)',
+ location_access_rule_for(ZorkGrandInquisitorLocations.DRAGON_ARCHIPELAGO_TIME_TUNNEL, 1),
+ )
+
+ # Single Event Requirement
+ self.assertEqual(
+ 'lambda state: state.has("Event: Knows OBIDIL", 1)',
+ location_access_rule_for(ZorkGrandInquisitorLocations.A_BIG_FAT_SASSY_2_HEADED_MONSTER, 1),
+ )
+
+ self.assertEqual(
+ 'lambda state: state.has("Event: Dunce Locker Openable", 1)',
+ location_access_rule_for(ZorkGrandInquisitorLocations.BETTER_SPELL_MANUFACTURING_IN_UNDER_10_MINUTES, 1),
+ )
+
+ # Multiple Item Requirements
+ self.assertEqual(
+ 'lambda state: state.has("Hotspot: Purple Words", 1) and state.has("Spell: IGRAM", 1)',
+ location_access_rule_for(ZorkGrandInquisitorLocations.A_SMALLWAY, 1),
+ )
+
+ self.assertEqual(
+ 'lambda state: state.has("Hotspot: Mossy Grate", 1) and state.has("Spell: THROCK", 1)',
+ location_access_rule_for(ZorkGrandInquisitorLocations.BEAUTIFUL_THATS_PLENTY, 1),
+ )
+
+ # Multiple Item Requirements OR
+ self.assertEqual(
+ 'lambda state: (state.has("Totem: Griff", 1) or state.has("Totem: Lucy", 1)) and state.has("Hotspot: Mailbox Door", 1) and state.has("Hotspot: Mailbox Flag", 1)',
+ location_access_rule_for(ZorkGrandInquisitorLocations.MAILED_IT_TO_HELL, 1),
+ )
+
+ # Multiple Mixed Requirements
+ self.assertEqual(
+ 'lambda state: state.has("Event: Cigar Accessible", 1) and state.has("Hotspot: Grand Inquisitor Doll", 1)',
+ location_access_rule_for(ZorkGrandInquisitorLocations.ARREST_THE_VANDAL, 1),
+ )
+
+ self.assertEqual(
+ 'lambda state: state.has("Sword", 1) and state.has("Event: Rope GLORFable", 1) and state.has("Hotspot: Monastery Vent", 1)',
+ location_access_rule_for(ZorkGrandInquisitorLocations.I_HOPE_YOU_CAN_CLIMB_UP_THERE, 1),
+ )
+
+ def test_entrance_access_rule_for(self) -> None:
+ # No Requirements
+ self.assertEqual(
+ "lambda state: True",
+ entrance_access_rule_for(
+ ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.PORT_FOOZLE, 1
+ ),
+ )
+
+ self.assertEqual(
+ "lambda state: True",
+ entrance_access_rule_for(
+ ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.CROSSROADS, 1
+ ),
+ )
+
+ # Single Requirement
+ self.assertEqual(
+ 'lambda state: (state.has("Map", 1))',
+ entrance_access_rule_for(
+ ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, ZorkGrandInquisitorRegions.CROSSROADS, 1
+ ),
+ )
+
+ self.assertEqual(
+ 'lambda state: (state.has("Map", 1))',
+ entrance_access_rule_for(
+ ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.CROSSROADS, 1
+ ),
+ )
+
+ # Multiple Requirements AND
+ self.assertEqual(
+ 'lambda state: (state.has("Spell: REZROV", 1) and state.has("Hotspot: In Magic We Trust Door", 1))',
+ entrance_access_rule_for(
+ ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.GUE_TECH, 1
+ ),
+ )
+
+ self.assertEqual(
+ 'lambda state: (state.has("Event: Door Smoked Cigar", 1) and state.has("Event: Door Drank Mead", 1))',
+ entrance_access_rule_for(
+ ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, 1
+ ),
+ )
+
+ self.assertEqual(
+ 'lambda state: (state.has("Hotspot: Closet Door", 1) and state.has("Spell: NARWILE", 1) and state.has("Event: Knows YASTARD", 1))',
+ entrance_access_rule_for(
+ ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, ZorkGrandInquisitorRegions.WHITE_HOUSE, 1
+ ),
+ )
+
+ # Multiple Requirements AND + OR
+ self.assertEqual(
+ 'lambda state: (state.has("Sword", 1) and state.has("Hotspot: Dungeon Master\'s Lair Entrance", 1)) or (state.has("Map", 1) and state.has("Teleporter Destination: Dungeon Master\'s Lair", 1))',
+ entrance_access_rule_for(
+ ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.DM_LAIR, 1
+ ),
+ )
+
+ # Multiple Requirements Regions
+ self.assertEqual(
+ 'lambda state: (state.has("Griff\'s Air Pump", 1) and state.has("Griff\'s Inflatable Raft", 1) and state.has("Griff\'s Inflatable Sea Captain", 1) and state.has("Hotspot: Dragon Nostrils", 1) and state.has("Griff\'s Dragon Tooth", 1) and state.can_reach("Port Foozle Past - Tavern", "Region", 1) and state.has("Lucy\'s Playing Card: 1 Pip", 1) and state.has("Lucy\'s Playing Card: 2 Pips", 1) and state.has("Lucy\'s Playing Card: 3 Pips", 1) and state.has("Lucy\'s Playing Card: 4 Pips", 1) and state.has("Hotspot: Tavern Fly", 1) and state.has("Hotspot: Alpine\'s Quandry Card Slots", 1) and state.can_reach("White House", "Region", 1) and state.has("Totem: Brog", 1) and state.has("Brog\'s Flickering Torch", 1) and state.has("Brog\'s Grue Egg", 1) and state.has("Hotspot: Cooking Pot", 1) and state.has("Brog\'s Plank", 1) and state.has("Hotspot: Skull Cage", 1))',
+ entrance_access_rule_for(
+ ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON, ZorkGrandInquisitorRegions.ENDGAME, 1
+ ),
+ )
diff --git a/worlds/zork_grand_inquisitor/test/test_locations.py b/worlds/zork_grand_inquisitor/test/test_locations.py
new file mode 100644
index 000000000000..fa576dd510dc
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/test/test_locations.py
@@ -0,0 +1,49 @@
+from typing import Dict, Set
+
+from . import ZorkGrandInquisitorTestBase
+
+from ..data_funcs import location_names_to_location, locations_with_tag
+from ..enums import ZorkGrandInquisitorLocations, ZorkGrandInquisitorTags
+
+
+class LocationsTestNoDeathsanity(ZorkGrandInquisitorTestBase):
+ options = {
+ "deathsanity": "false",
+ }
+
+ def test_correct_locations_exist(self) -> None:
+ expected_locations: Set[ZorkGrandInquisitorLocations] = locations_with_tag(
+ ZorkGrandInquisitorTags.CORE
+ )
+
+ self._assert_expected_locations_exist(expected_locations)
+
+ def _assert_expected_locations_exist(self, expected_locations: Set[ZorkGrandInquisitorLocations]) -> None:
+ location_name_to_location: Dict[str, ZorkGrandInquisitorLocations] = location_names_to_location()
+
+ for location_object in self.multiworld.get_locations(1):
+ location: ZorkGrandInquisitorLocations = location_name_to_location.get(
+ location_object.name
+ )
+
+ if location is None:
+ continue
+
+ self.assertIn(location, expected_locations)
+
+ expected_locations.remove(location)
+
+ self.assertEqual(0, len(expected_locations))
+
+
+class LocationsTestDeathsanity(LocationsTestNoDeathsanity):
+ options = {
+ "deathsanity": "true",
+ }
+
+ def test_correct_locations_exist(self) -> None:
+ expected_locations: Set[ZorkGrandInquisitorLocations] = (
+ locations_with_tag(ZorkGrandInquisitorTags.CORE) | locations_with_tag(ZorkGrandInquisitorTags.DEATHSANITY)
+ )
+
+ self._assert_expected_locations_exist(expected_locations)
diff --git a/worlds/zork_grand_inquisitor/world.py b/worlds/zork_grand_inquisitor/world.py
new file mode 100644
index 000000000000..2dc634e47d8d
--- /dev/null
+++ b/worlds/zork_grand_inquisitor/world.py
@@ -0,0 +1,206 @@
+from typing import Any, Dict, List, Set, Tuple
+
+from BaseClasses import Item, ItemClassification, Location, Region, Tutorial
+
+from worlds.AutoWorld import WebWorld, World
+
+from .data.item_data import item_data, ZorkGrandInquisitorItemData
+from .data.location_data import location_data, ZorkGrandInquisitorLocationData
+from .data.region_data import region_data
+
+from .data_funcs import (
+ item_names_to_id,
+ item_names_to_item,
+ location_names_to_id,
+ item_groups,
+ items_with_tag,
+ location_groups,
+ locations_by_region,
+ location_access_rule_for,
+ entrance_access_rule_for,
+)
+
+from .enums import (
+ ZorkGrandInquisitorEvents,
+ ZorkGrandInquisitorItems,
+ ZorkGrandInquisitorLocations,
+ ZorkGrandInquisitorRegions,
+ ZorkGrandInquisitorTags,
+)
+
+from .options import ZorkGrandInquisitorOptions
+
+
+class ZorkGrandInquisitorItem(Item):
+ game = "Zork Grand Inquisitor"
+
+
+class ZorkGrandInquisitorLocation(Location):
+ game = "Zork Grand Inquisitor"
+
+
+class ZorkGrandInquisitorWebWorld(WebWorld):
+ theme: str = "stone"
+
+ tutorials: List[Tutorial] = [
+ Tutorial(
+ "Multiworld Setup Guide",
+ "A guide to setting up the Zork Grand Inquisitor randomizer connected to an Archipelago Multiworld",
+ "English",
+ "setup_en.md",
+ "setup/en",
+ ["Serpent.AI"],
+ )
+ ]
+
+
+class ZorkGrandInquisitorWorld(World):
+ """
+ Zork: Grand Inquisitor is a 1997 point-and-click adventure game for PC.
+ Magic has been banned from the great Underground Empire of Zork. By edict of the Grand Inquisitor Mir Yannick, the
+ Empire has been sealed off and the practice of mystic arts declared punishable by "Totemization" (a very bad thing).
+ The only way to restore magic to the kingdom is to find three hidden artifacts: The Coconut of Quendor, The Cube of
+ Foundation, and The Skull of Yoruk.
+ """
+
+ options_dataclass = ZorkGrandInquisitorOptions
+ options: ZorkGrandInquisitorOptions
+
+ game = "Zork Grand Inquisitor"
+
+ item_name_to_id = item_names_to_id()
+ location_name_to_id = location_names_to_id()
+
+ item_name_groups = item_groups()
+ location_name_groups = location_groups()
+
+ required_client_version: Tuple[int, int, int] = (0, 4, 4)
+
+ web = ZorkGrandInquisitorWebWorld()
+
+ item_name_to_item: Dict[str, ZorkGrandInquisitorItems] = item_names_to_item()
+
+ def create_regions(self) -> None:
+ deathsanity: bool = bool(self.options.deathsanity)
+
+ region_mapping: Dict[ZorkGrandInquisitorRegions, Region] = dict()
+
+ region_enum_item: ZorkGrandInquisitorRegions
+ for region_enum_item in region_data.keys():
+ region_mapping[region_enum_item] = Region(region_enum_item.value, self.player, self.multiworld)
+
+ region_locations_mapping: Dict[ZorkGrandInquisitorRegions, Set[ZorkGrandInquisitorLocations]]
+ region_locations_mapping = locations_by_region(include_deathsanity=deathsanity)
+
+ region_enum_item: ZorkGrandInquisitorRegions
+ region: Region
+ for region_enum_item, region in region_mapping.items():
+ regions_locations: Set[ZorkGrandInquisitorLocations] = region_locations_mapping[region_enum_item]
+
+ # Locations
+ location_enum_item: ZorkGrandInquisitorLocations
+ for location_enum_item in regions_locations:
+ data: ZorkGrandInquisitorLocationData = location_data[location_enum_item]
+
+ location: ZorkGrandInquisitorLocation = ZorkGrandInquisitorLocation(
+ self.player,
+ location_enum_item.value,
+ data.archipelago_id,
+ region_mapping[data.region],
+ )
+
+ location.event = isinstance(location_enum_item, ZorkGrandInquisitorEvents)
+
+ if location.event:
+ location.place_locked_item(
+ ZorkGrandInquisitorItem(
+ data.event_item_name,
+ ItemClassification.progression,
+ None,
+ self.player,
+ )
+ )
+
+ location_access_rule: str = location_access_rule_for(location_enum_item, self.player)
+
+ if location_access_rule != "lambda state: True":
+ location.access_rule = eval(location_access_rule)
+
+ region.locations.append(location)
+
+ # Connections
+ region_exit: ZorkGrandInquisitorRegions
+ for region_exit in region_data[region_enum_item].exits or tuple():
+ entrance_access_rule: str = entrance_access_rule_for(region_enum_item, region_exit, self.player)
+
+ if entrance_access_rule == "lambda state: True":
+ region.connect(region_mapping[region_exit])
+ else:
+ region.connect(region_mapping[region_exit], rule=eval(entrance_access_rule))
+
+ self.multiworld.regions.append(region)
+
+ def create_items(self) -> None:
+ quick_port_foozle: bool = bool(self.options.quick_port_foozle)
+ start_with_hotspot_items: bool = bool(self.options.start_with_hotspot_items)
+
+ item_pool: List[ZorkGrandInquisitorItem] = list()
+
+ item: ZorkGrandInquisitorItems
+ data: ZorkGrandInquisitorItemData
+ for item, data in item_data.items():
+ tags: Tuple[ZorkGrandInquisitorTags, ...] = data.tags or tuple()
+
+ if ZorkGrandInquisitorTags.FILLER in tags:
+ continue
+ elif ZorkGrandInquisitorTags.HOTSPOT in tags and start_with_hotspot_items:
+ continue
+
+ item_pool.append(self.create_item(item.value))
+
+ total_locations: int = len(self.multiworld.get_unfilled_locations(self.player))
+ item_pool += [self.create_filler() for _ in range(total_locations - len(item_pool))]
+
+ self.multiworld.itempool += item_pool
+
+ if quick_port_foozle:
+ self.multiworld.early_items[self.player][ZorkGrandInquisitorItems.ROPE.value] = 1
+ self.multiworld.early_items[self.player][ZorkGrandInquisitorItems.LANTERN.value] = 1
+
+ if not start_with_hotspot_items:
+ self.multiworld.early_items[self.player][ZorkGrandInquisitorItems.HOTSPOT_WELL.value] = 1
+ self.multiworld.early_items[self.player][ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR.value] = 1
+
+ self.multiworld.early_items[self.player][
+ ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL.value
+ ] = 1
+
+ if start_with_hotspot_items:
+ item: ZorkGrandInquisitorItems
+ for item in items_with_tag(ZorkGrandInquisitorTags.HOTSPOT):
+ self.multiworld.push_precollected(self.create_item(item.value))
+
+ def create_item(self, name: str) -> ZorkGrandInquisitorItem:
+ data: ZorkGrandInquisitorItemData = item_data[self.item_name_to_item[name]]
+
+ return ZorkGrandInquisitorItem(
+ name,
+ data.classification,
+ data.archipelago_id,
+ self.player,
+ )
+
+ def generate_basic(self) -> None:
+ self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
+
+ def fill_slot_data(self) -> Dict[str, Any]:
+ return self.options.as_dict(
+ "goal",
+ "quick_port_foozle",
+ "start_with_hotspot_items",
+ "deathsanity",
+ "grant_missable_location_checks",
+ )
+
+ def get_filler_item_name(self) -> str:
+ return self.random.choice(list(self.item_name_groups["Filler"]))
diff --git a/worlds_disabled/README.md b/worlds_disabled/README.md
index b891bc71d4ba..a7bffe222b14 100644
--- a/worlds_disabled/README.md
+++ b/worlds_disabled/README.md
@@ -3,3 +3,11 @@
This folder is for already merged worlds that are unmaintained and currently broken. If you are interested in fixing and
stepping up as maintainer for any of these worlds, please review the [world maintainer](/docs/world%20maintainer.md)
documentation.
+
+## Information for Disabled Worlds
+
+For each disabled world, a README file can be found detailing when the world was disabled and the reasons that it
+was disabled. In order to be considered for reactivation, these concerns should be handled at a bare minimum. However,
+each world may have additional issues that also need to be handled, such as deprecated API calls or missing components.
+
+
diff --git a/worlds_disabled/oribf/README.md b/worlds_disabled/oribf/README.md
new file mode 100644
index 000000000000..0c78c23bea0d
--- /dev/null
+++ b/worlds_disabled/oribf/README.md
@@ -0,0 +1,7 @@
+### Ori and the Blind Forest
+
+This world was disabled for the following reasons:
+
+* Missing client
+* Unmaintained
+* Outdated, fails tests as of Jun 29, 2023