Skip to content

Pokemon R/B: The Big Door Shuffle Update #2861

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2c5c9f5
Pokémon R/B: Decoupled entrances and door shuffle re-attempts
Alchav Jan 27, 2024
f0161a3
Decoupled working
Alchav Feb 5, 2024
ccbb617
Rewrite everything
Alchav Feb 6, 2024
813b044
Rename Full to Interiors and add new Full door shuffle
Alchav Feb 16, 2024
6e0b269
Fix search_for_exit
Alchav Feb 16, 2024
0513cd3
Tweak option descriptions
Alchav Feb 16, 2024
da5d3ce
Shuffle Safari Zone connections
Alchav Feb 17, 2024
6cb7157
No longer force some connections to be coupled on Decoupled
Alchav Feb 17, 2024
ef520ae
Raise exception instead of breakpoint
Alchav Feb 17, 2024
92dca77
Update Town Map when using Simple Door Shuffle
Alchav Feb 18, 2024
f7def7b
Clarify purpose of Town Map changes
Alchav Feb 18, 2024
6d360f7
More finagling to make sure Rock Tunnel works
Alchav Feb 19, 2024
bdafcd6
Yet more finagling to make Rock Tunnel work
Alchav Feb 19, 2024
1d67169
Fix Fly intervention
Alchav Feb 19, 2024
b73c062
Remove remnants of old rock tunnel entrance handling
Alchav Feb 19, 2024
b78a368
Tweak Decoupled dead end rate
Alchav Feb 19, 2024
480b53d
Type Chart crash fix
Alchav Feb 19, 2024
2418929
Remove unnecessary things
Alchav Feb 21, 2024
7a2cc3f
Hopefully final tweaks for Decoupled
Alchav Feb 24, 2024
73418ae
Auto Level Scaling
Alchav Feb 24, 2024
57c83a8
Merge branch 'main' into pokemon-rb-door-shuffle-updates
Alchav Feb 24, 2024
2105b16
Removed dummied code
Alchav Feb 24, 2024
758c868
Warp Tile Shuffle aliases
Alchav Feb 24, 2024
bf746f5
Duplicate item reference fix
Alchav Feb 26, 2024
c9727e4
Fix stage fill hook
Alchav Feb 28, 2024
2e5d9e6
Fix door-blocking NPCs sending you to elevators bug
Alchav Feb 28, 2024
6692555
Remove accidentally-committed elevator test code
Alchav Feb 29, 2024
9608f3a
indentation fix
Alchav Mar 1, 2024
cb2dec5
Set all but one of each mon to useful after fill
Alchav Mar 2, 2024
1081b78
Merge branch 'main' into pokemon-rb-door-shuffle-updates
Alchav Mar 4, 2024
9cf4251
Fix Route 4 and Route 24 connections
Alchav Mar 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 19 additions & 32 deletions worlds/pokemon_rb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -555,23 +559,6 @@ def number_of_zones(mon):
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

def create_regions(self):
if (self.multiworld.old_man[self.player] == "vanilla" or
self.multiworld.door_shuffle[self.player] in ("full", "insanity")):
Expand Down
Binary file modified worlds/pokemon_rb/basepatch_blue.bsdiff4
Binary file not shown.
Binary file modified worlds/pokemon_rb/basepatch_red.bsdiff4
Binary file not shown.
2 changes: 2 additions & 0 deletions worlds/pokemon_rb/docs/en_Pokemon Red and Blue.md
Original file line number Diff line number Diff line change
Expand Up @@ -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?

Expand Down
25 changes: 13 additions & 12 deletions worlds/pokemon_rb/level_scaling.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()

70 changes: 36 additions & 34 deletions worlds/pokemon_rb/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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):
Expand Down
Loading