Merge remote-tracking branch 'origin/OverworldShuffle' into OverworldShuffle

This commit is contained in:
2021-08-02 19:41:32 -07:00
24 changed files with 389 additions and 225 deletions

View File

@@ -70,6 +70,7 @@ class World(object):
self.customitemarray = customitemarray
self.can_take_damage = True
self.hints = hints.copy()
self.prizes = {}
self.dynamic_regions = []
self.dynamic_locations = []
self.spoiler = Spoiler(self)
@@ -122,6 +123,7 @@ class World(object):
set_player_attr('compassshuffle', False)
set_player_attr('keyshuffle', False)
set_player_attr('bigkeyshuffle', False)
set_player_attr('bomblogic', False)
set_player_attr('difficulty_requirements', None)
set_player_attr('boss_shuffle', 'none')
set_player_attr('enemy_shuffle', 'none')
@@ -149,6 +151,7 @@ class World(object):
set_player_attr('standardize_palettes', 'standardize')
set_player_attr('force_fix', {'gt': False, 'sw': False, 'pod': False, 'tr': False})
set_player_attr('owswaps', [[],[],[]])
set_player_attr('prizes', {'pull': [0, 0, 0], 'crab': [0, 0], 'stun': 0, 'fish': 0})
def get_name_string_for_object(self, obj):
return obj.name if self.players == 1 else f'{obj.name} ({self.get_player_names(obj.player)})'
@@ -687,6 +690,140 @@ class CollectionState(object):
return True
return False
def can_farm_rupees(self, player):
tree_pulls = ['Lost Woods East Area',
'Snitch Lady (East)',
'Turtle Rock Area',
'Pyramid Area',
'Hype Cave Area',
'Dark South Pass Area',
'Bumper Cave Area']
pre_aga_tree_pulls = ['Hyrule Castle Courtyard', 'Mountain Entry Area']
post_aga_tree_pulls = ['Statues Area', 'Eastern Palace Area']
rupee_farms = ['Archery Game', '50 Rupee Cave', '20 Rupee Cave']
def can_reach_non_bunny(regionname):
region = self.world.get_region(regionname, player)
return region.can_reach(self) and ((self.world.mode[player] != 'inverted' and region.is_light_world) or (self.world.mode[player] == 'inverted' and region.is_dark_world) or self.has('Pearl', player))
for region in rupee_farms:
if can_reach_non_bunny(region):
return True
if any(i in [0xda, 0xdb] for i in self.world.prizes[player]['pull']):
for region in tree_pulls:
if can_reach_non_bunny(region):
return True
if not self.has('Beat Agahnim 1', player):
for region in pre_aga_tree_pulls:
if can_reach_non_bunny(region):
return True
else:
for region in post_aga_tree_pulls:
if can_reach_non_bunny(region):
return True
return False
def can_farm_bombs(self, player):
if self.world.mode[player] == 'standard' and not self.has('Zelda Delivered', player):
return True
bush_bombs = ['Flute Boy Approach Area',
'Kakariko Area',
'Village of Outcasts Area',
'Forgotten Forest Area',
'Bat Cave Ledge',
'East Dark Death Mountain (Bottom)']
rock_bombs = ['Links House Area',
'Dark Chapel Area',
'Wooden Bridge Area',
'Ice Cave Area',
'Eastern Nook Area',
'West Death Mountain (Bottom)',
'Kakariko Fortune Area',
'Skull Woods Forest',
'Catfish Area',
'Dark Fortune Area',
'Qirn Jump Area',
'Shield Shop Area',
'Palace of Darkness Nook Area',
'Swamp Nook Area',
'Dark South Pass Area']
bonk_bombs = ['Kakariko Fortune Area', 'Dark Graveyard Area'] #TODO: Flute Boy Approach Area and Bonk Rock Ledge are available post-Aga
bomb_caves = ['Graveyard Cave', 'Light World Bomb Hut']
tree_pulls = ['Lost Woods East Area',
'Snitch Lady (East)',
'Turtle Rock Area',
'Pyramid Area',
'Hype Cave Area',
'Dark South Pass Area',
'Bumper Cave Area']
pre_aga_tree_pulls = ['Hyrule Castle Courtyard', 'Mountain Entry Area']
post_aga_tree_pulls = ['Statues Area', 'Eastern Palace Area']
bush_crabs = ['Lost Woods East Area', 'Mountain Entry Area']
pre_aga_bush_crabs = ['Lumberjack Area', 'South Pass Area']
rock_crabs = ['Desert Pass Area']
def can_reach_non_bunny(regionname):
region = self.world.get_region(regionname, player)
return region.can_reach(self) and ((self.world.mode[player] != 'inverted' and region.is_light_world) or (self.world.mode[player] == 'inverted' and region.is_dark_world) or self.has('Pearl', player))
# bomb pickups
for region in bush_bombs + bomb_caves:
if can_reach_non_bunny(region):
return True
if self.can_lift_rocks(player):
for region in rock_bombs:
if can_reach_non_bunny(region):
return True
if self.has_Boots(player):
for region in bonk_bombs:
if can_reach_non_bunny(region):
return True
# tree pulls
if any(i in [0xdc, 0xdd, 0xde] for i in self.world.prizes[player]['pull']):
for region in tree_pulls:
if can_reach_non_bunny(region):
return True
if not self.has('Beat Agahnim 1', player):
for region in pre_aga_tree_pulls:
if can_reach_non_bunny(region):
return True
else:
for region in post_aga_tree_pulls:
if can_reach_non_bunny(region):
return True
# bush crabs (final item isn't considered)
if self.world.enemy_shuffle[player] != 'none':
if self.world.prizes[player]['crab'][0] in [0xdc, 0xdd, 0xde]:
for region in bush_crabs:
if can_reach_non_bunny(region):
return True
if not self.has('Beat Agahnim 1', player):
for region in pre_aga_bush_crabs:
if can_reach_non_bunny(region):
return True
if self.can_lift_rocks(player) and self.world.prizes[player]['crab'][0] in [0xdc, 0xdd, 0xde]:
for region in rock_crabs:
if can_reach_non_bunny(region):
return True
# stun prize
if self.can_stun_enemies(player) and self.world.prizes[player]['stun'] in [0xdc, 0xdd, 0xde]:
return True
# bomb purchases
if self.can_farm_rupees(player) and (self.can_buy_unlimited('Bombs (10)', player) or self.can_reach('Big Bomb Shop', None, player)):
return True
return False
def item_count(self, item, player):
return self.prog_items[item, player]
@@ -743,10 +880,21 @@ class CollectionState(object):
or self.can_shoot_arrows(player)
or self.has('Fire Rod', player)))
# Check if the player starts without bombs
def can_stun_enemies(self, player):
if self.world.difficulty_adjustments[player] == 'expert':
return False
elif self.world.difficulty_adjustments[player] == 'hard':
return self.has('Hookshot', player)
else:
return self.has('Hookshot', player) \
or self.has('Blue Boomerang', player) \
or self.has('Red Boomerang', player)
# In the future, this can be used to check if the player starts without bombs
def can_use_bombs(self, player):
StartingBombs = True
return (self.bomb_mode_check(player, 1) and (StartingBombs or self.has('Bomb Upgrade (+10)', player)))
if self.world.swords[player] == 'bombs':
return self.has_bomb_level(player, 1)
return (not self.world.bomblogic[player] or self.has('Bomb Upgrade (+10)', player)) and self.can_farm_bombs(player)
def can_hit_crystal(self, player):
return (self.can_use_bombs(player)
@@ -778,6 +926,7 @@ class CollectionState(object):
def can_get_good_bee(self, player):
cave = self.world.get_region('Good Bee Cave', player)
return (
self.can_use_bombs(player) and
self.has_bottle(player) and
self.has('Bug Catching Net', player) and
(self.has_Boots(player) or (self.can_use_medallions(player) and self.has('Quake', player))) and
@@ -2273,6 +2422,7 @@ class Spoiler(object):
'logic': self.world.logic,
'mode': self.world.mode,
'retro': self.world.retro,
'bomblogic': self.world.bomblogic,
'weapons': self.world.swords,
'goal': self.world.goal,
'ow_shuffle': self.world.owShuffle,
@@ -2388,6 +2538,7 @@ class Spoiler(object):
outfile.write('Experimental:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['experimental'][player] else 'No'))
outfile.write('Key Drops shuffled:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['keydropshuffle'][player] else 'No'))
outfile.write('Shopsanity:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['shopsanity'][player] else 'No'))
outfile.write('Bomblogic:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['bomblogic'][player] else 'No'))
if self.doors:
outfile.write('\n\nDoors:\n\n')
outfile.write('\n'.join(