Merge branch 'OverworldShuffleDev' into OverworldShuffle

This commit is contained in:
codemann8
2021-07-29 03:52:30 -05:00
9 changed files with 150 additions and 35 deletions

View File

@@ -70,6 +70,7 @@ class World(object):
self.customitemarray = customitemarray self.customitemarray = customitemarray
self.can_take_damage = True self.can_take_damage = True
self.hints = hints.copy() self.hints = hints.copy()
self.prizes = {}
self.dynamic_regions = [] self.dynamic_regions = []
self.dynamic_locations = [] self.dynamic_locations = []
self.spoiler = Spoiler(self) self.spoiler = Spoiler(self)
@@ -148,6 +149,7 @@ class World(object):
set_player_attr('standardize_palettes', 'standardize') set_player_attr('standardize_palettes', 'standardize')
set_player_attr('force_fix', {'gt': False, 'sw': False, 'pod': False, 'tr': False}) set_player_attr('force_fix', {'gt': False, 'sw': False, 'pod': False, 'tr': False})
set_player_attr('owswaps', [[],[],[]]) 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): 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)})' return obj.name if self.players == 1 else f'{obj.name} ({self.get_player_names(obj.player)})'
@@ -674,7 +676,6 @@ class CollectionState(object):
return False return False
def can_farm_rupees(self, player): def can_farm_rupees(self, player):
# TODO: Possibly use tree pulls also in the future, bush crabs also if enemizer is disabled
tree_pulls = ['Lost Woods East Area', tree_pulls = ['Lost Woods East Area',
'Snitch Lady (East)', 'Snitch Lady (East)',
'Turtle Rock Area', 'Turtle Rock Area',
@@ -694,6 +695,19 @@ class CollectionState(object):
for region in rupee_farms: for region in rupee_farms:
if can_reach_non_bunny(region): if can_reach_non_bunny(region):
return True 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 return False
def can_farm_bombs(self, player): def can_farm_bombs(self, player):
@@ -724,7 +738,6 @@ class CollectionState(object):
bonk_bombs = ['Kakariko Fortune Area', 'Dark Graveyard Area'] #TODO: Flute Boy Approach Area and Bonk Rock Ledge are available post-Aga 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'] bomb_caves = ['Graveyard Cave', 'Light World Bomb Hut']
# TODO: Possibly use tree pulls also in the future, bush crabs also if enemizer is disabled
tree_pulls = ['Lost Woods East Area', tree_pulls = ['Lost Woods East Area',
'Snitch Lady (East)', 'Snitch Lady (East)',
'Turtle Rock Area', 'Turtle Rock Area',
@@ -735,10 +748,15 @@ class CollectionState(object):
pre_aga_tree_pulls = ['Hyrule Castle Courtyard', 'Mountain Entry Area'] pre_aga_tree_pulls = ['Hyrule Castle Courtyard', 'Mountain Entry Area']
post_aga_tree_pulls = ['Statues Area', 'Eastern Palace 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): def can_reach_non_bunny(regionname):
region = self.world.get_region(regionname, player) 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)) 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: for region in bush_bombs + bomb_caves:
if can_reach_non_bunny(region): if can_reach_non_bunny(region):
return True return True
@@ -753,7 +771,41 @@ class CollectionState(object):
if can_reach_non_bunny(region): if can_reach_non_bunny(region):
return True return True
if self.can_farm_rupees(player) and self.can_buy_unlimited('Bombs (10)', player): # 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 True
return False return False
@@ -813,6 +865,16 @@ class CollectionState(object):
or self.has('Fire Rod', player) or self.has('Fire Rod', player)
) )
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 # In the future, this can be used to check if the player starts without bombs
def can_use_bombs(self, player): def can_use_bombs(self, player):
return (not self.world.bomblogic[player] or self.has('Bomb Upgrade (+10)', player)) and self.can_farm_bombs(player) return (not self.world.bomblogic[player] or self.has('Bomb Upgrade (+10)', player)) and self.can_farm_bombs(player)

View File

@@ -1,5 +1,9 @@
# Changelog # Changelog
### 0.1.7.1
- Improved bomb logic to consider tree pulls, bush crabs, and stun prize
- Fixed Mystery to use new updated OW mode terminology
### 0.1.7.0 ### 0.1.7.0
- Expanded new DR bomb logic to all modes (bomb usage in logic only if there is an unlimited supply of bombs available) - Expanded new DR bomb logic to all modes (bomb usage in logic only if there is an unlimited supply of bombs available)
- ~~Merged DR v0.5.0.1 - Bomblogic mode / Enemizer fixes~~ - ~~Merged DR v0.5.0.1 - Bomblogic mode / Enemizer fixes~~

30
Fill.py
View File

@@ -712,3 +712,33 @@ def balance_money_progression(world):
unchecked_locations.remove(location) unchecked_locations.remove(location)
if location.item.name.startswith('Rupee'): if location.item.name.startswith('Rupee'):
wallet[location.item.player] += rupee_chart[location.item.name] wallet[location.item.player] += rupee_chart[location.item.name]
def set_prize_drops(world, player):
prizes = [0xD8, 0xD8, 0xD8, 0xD8, 0xD9, 0xD8, 0xD8, 0xD9, 0xDA, 0xD9, 0xDA, 0xDB, 0xDA, 0xD9, 0xDA, 0xDA, 0xE0, 0xDF, 0xDF, 0xDA, 0xE0, 0xDF, 0xD8, 0xDF,
0xDC, 0xDC, 0xDC, 0xDD, 0xDC, 0xDC, 0xDE, 0xDC, 0xE1, 0xD8, 0xE1, 0xE2, 0xE1, 0xD8, 0xE1, 0xE2, 0xDF, 0xD9, 0xD8, 0xE1, 0xDF, 0xDC, 0xD9, 0xD8,
0xD8, 0xE3, 0xE0, 0xDB, 0xDE, 0xD8, 0xDB, 0xE2, 0xD9, 0xDA, 0xDB, 0xD9, 0xDB, 0xD9, 0xDB]
# randomize last 7 slots
new_prizes = random.sample(prizes, 7)
if world.difficulty_adjustments[player] in ['hard', 'expert']:
prize_replacements = {0xE0: 0xDF, # Fairy -> heart
0xE3: 0xD8} # Big magic -> small magic
new_prizes = [prize_replacements.get(prize, prize) for prize in new_prizes]
if world.retro[player]:
prize_replacements = {0xE1: 0xDA, #5 Arrows -> Blue Rupee
0xE2: 0xDB} #10 Arrows -> Red Rupee
new_prizes = [prize_replacements.get(prize, prize) for prize in new_prizes]
# write tree pull prizes
world.prizes[player]['pull'] = [ new_prizes.pop(), new_prizes.pop(), new_prizes.pop() ]
# rupee crab prizes
world.prizes[player]['crab'] = [ new_prizes.pop(), new_prizes.pop() ]
# stunned enemy prize
world.prizes[player]['stun'] = new_prizes.pop()
# saved fish prize
world.prizes[player]['fish'] = new_prizes.pop()

View File

@@ -25,7 +25,7 @@ from RoomData import create_rooms
from Rules import set_rules from Rules import set_rules
from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive
from Fill import distribute_items_cutoff, distribute_items_staleness, distribute_items_restrictive, flood_items from Fill import distribute_items_cutoff, distribute_items_staleness, distribute_items_restrictive, flood_items
from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations, set_prize_drops
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops
from Utils import output_path, parse_player_names from Utils import output_path, parse_player_names
@@ -195,6 +195,8 @@ def main(args, seed=None, fish=None):
else: else:
lock_shop_locations(world, player) lock_shop_locations(world, player)
for player in range(1, world.players + 1):
set_prize_drops(world, player)
logger.info(world.fish.translate("cli","cli","placing.dungeon.prizes")) logger.info(world.fish.translate("cli","cli","placing.dungeon.prizes"))
@@ -414,6 +416,7 @@ def copy_world(world):
ret.standardize_palettes = world.standardize_palettes.copy() ret.standardize_palettes = world.standardize_palettes.copy()
ret.owswaps = world.owswaps.copy() ret.owswaps = world.owswaps.copy()
ret.owflutespots = world.owflutespots.copy() ret.owflutespots = world.owflutespots.copy()
ret.prizes = world.prizes.copy()
for player in range(1, world.players + 1): for player in range(1, world.players + 1):
create_regions(ret, player) create_regions(ret, player)

View File

@@ -135,8 +135,8 @@ def roll_settings(weights):
ret.ow_shuffle = overworld_shuffle if overworld_shuffle != 'none' else 'vanilla' ret.ow_shuffle = overworld_shuffle if overworld_shuffle != 'none' else 'vanilla'
overworld_swap = get_choice('overworld_swap') overworld_swap = get_choice('overworld_swap')
ret.ow_swap = overworld_swap if overworld_swap != 'none' else 'vanilla' ret.ow_swap = overworld_swap if overworld_swap != 'none' else 'vanilla'
ret.ow_keepsimilar = get_choice('ow_keepsimilar') ret.ow_keepsimilar = get_choice('overworld_keepsimilar')
overworld_flute = get_choice('overworld_flute') overworld_flute = get_choice('flute_shuffle')
ret.ow_swap = overworld_flute if overworld_flute != 'none' else 'vanilla' ret.ow_swap = overworld_flute if overworld_flute != 'none' else 'vanilla'
entrance_shuffle = get_choice('entrance_shuffle') entrance_shuffle = get_choice('entrance_shuffle')
ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla' ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla'

View File

@@ -2,7 +2,7 @@ import RaceRandom as random, logging, copy
from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSlot from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSlot
from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OpenStd, parallel_links, IsParallel from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OpenStd, parallel_links, IsParallel
__version__ = '0.1.7.0-u' __version__ = '0.1.7.1-u'
def link_overworld(world, player): def link_overworld(world, player):
# setup mandatory connections # setup mandatory connections

37
Rom.py
View File

@@ -1066,9 +1066,13 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x178000 + i, random.randint(0, 255)) rom.write_byte(0x178000 + i, random.randint(0, 255))
# shuffle prize packs # shuffle prize packs
prizes = [0xD8, 0xD8, 0xD8, 0xD8, 0xD9, 0xD8, 0xD8, 0xD9, 0xDA, 0xD9, 0xDA, 0xDB, 0xDA, 0xD9, 0xDA, 0xDA, 0xE0, 0xDF, 0xDF, 0xDA, 0xE0, 0xDF, 0xD8, 0xDF, pack_prizes = [0xD8, 0xD8, 0xD8, 0xD8, 0xD9, 0xD8, 0xD8, 0xD9,
0xDC, 0xDC, 0xDC, 0xDD, 0xDC, 0xDC, 0xDE, 0xDC, 0xE1, 0xD8, 0xE1, 0xE2, 0xE1, 0xD8, 0xE1, 0xE2, 0xDF, 0xD9, 0xD8, 0xE1, 0xDF, 0xDC, 0xD9, 0xD8, 0xDA, 0xD9, 0xDA, 0xDB, 0xDA, 0xD9, 0xDA, 0xDA,
0xD8, 0xE3, 0xE0, 0xDB, 0xDE, 0xD8, 0xDB, 0xE2, 0xD9, 0xDA, 0xDB, 0xD9, 0xDB, 0xD9, 0xDB] 0xE0, 0xDF, 0xDF, 0xDA, 0xE0, 0xDF, 0xD8, 0xDF,
0xDC, 0xDC, 0xDC, 0xDD, 0xDC, 0xDC, 0xDE, 0xDC,
0xE1, 0xD8, 0xE1, 0xE2, 0xE1, 0xD8, 0xE1, 0xE2,
0xDF, 0xD9, 0xD8, 0xE1, 0xDF, 0xDC, 0xD9, 0xD8,
0xD8, 0xE3, 0xE0, 0xDB, 0xDE, 0xD8, 0xDB, 0xE2]
dig_prizes = [0xB2, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, dig_prizes = [0xB2, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
@@ -1080,44 +1084,41 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
def chunk(l,n): def chunk(l,n):
return [l[i:i+n] for i in range(0, len(l), n)] return [l[i:i+n] for i in range(0, len(l), n)]
# randomize last 7 slots
prizes [-7:] = random.sample(prizes, 7)
#shuffle order of 7 main packs #shuffle order of 7 main packs
packs = chunk(prizes[:56], 8) packs = chunk(pack_prizes, 8)
random.shuffle(packs) random.shuffle(packs)
prizes[:56] = [drop for pack in packs for drop in pack] pack_prizes = [drop for pack in packs for drop in pack]
if world.difficulty_adjustments[player] in ['hard', 'expert']: if world.difficulty_adjustments[player] in ['hard', 'expert']:
prize_replacements = {0xE0: 0xDF, # Fairy -> heart prize_replacements = {0xE0: 0xDF, # Fairy -> heart
0xE3: 0xD8} # Big magic -> small magic 0xE3: 0xD8} # Big magic -> small magic
prizes = [prize_replacements.get(prize, prize) for prize in prizes] pack_prizes = [prize_replacements.get(prize, prize) for prize in pack_prizes]
dig_prizes = [prize_replacements.get(prize, prize) for prize in dig_prizes] dig_prizes = [prize_replacements.get(prize, prize) for prize in dig_prizes]
if world.retro[player]: if world.retro[player]:
prize_replacements = {0xE1: 0xDA, #5 Arrows -> Blue Rupee prize_replacements = {0xE1: 0xDA, #5 Arrows -> Blue Rupee
0xE2: 0xDB} #10 Arrows -> Red Rupee 0xE2: 0xDB} #10 Arrows -> Red Rupee
prizes = [prize_replacements.get(prize, prize) for prize in prizes] pack_prizes = [prize_replacements.get(prize, prize) for prize in pack_prizes]
dig_prizes = [prize_replacements.get(prize, prize) for prize in dig_prizes] dig_prizes = [prize_replacements.get(prize, prize) for prize in dig_prizes]
rom.write_bytes(0x180100, dig_prizes) rom.write_bytes(0x180100, dig_prizes)
# write tree pull prizes # write tree pull prizes
rom.write_byte(0xEFBD4, prizes.pop()) rom.write_byte(0xEFBD4, world.prizes[player]['pull'][0])
rom.write_byte(0xEFBD5, prizes.pop()) rom.write_byte(0xEFBD5, world.prizes[player]['pull'][1])
rom.write_byte(0xEFBD6, prizes.pop()) rom.write_byte(0xEFBD6, world.prizes[player]['pull'][2])
# rupee crab prizes # rupee crab prizes
rom.write_byte(0x329C8, prizes.pop()) # first prize rom.write_byte(0x329C8, world.prizes[player]['crab'][0]) # first prize
rom.write_byte(0x329C4, prizes.pop()) # final prize rom.write_byte(0x329C4, world.prizes[player]['crab'][1]) # final prize
# stunned enemy prize # stunned enemy prize
rom.write_byte(0x37993, prizes.pop()) rom.write_byte(0x37993, world.prizes[player]['stun'])
# saved fish prize # saved fish prize
rom.write_byte(0xE82CC, prizes.pop()) rom.write_byte(0xE82CC, world.prizes[player]['fish'])
# fill enemy prize packs # fill enemy prize packs
rom.write_bytes(0x37A78, prizes) rom.write_bytes(0x37A78, pack_prizes)
# set bonk prizes # set bonk prizes
bonk_prizes = [0x79, 0xE3, 0x79, 0xAC, 0xAC, 0xE0, 0xDC, 0xAC, 0xE3, 0xE3, 0xDA, 0xE3, 0xDA, 0xD8, 0xAC, 0xAC, 0xE3, 0xD8, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xDC, 0xDB, 0xE3, 0xDA, 0x79, 0x79, 0xE3, 0xE3, bonk_prizes = [0x79, 0xE3, 0x79, 0xAC, 0xAC, 0xE0, 0xDC, 0xAC, 0xE3, 0xE3, 0xDA, 0xE3, 0xDA, 0xD8, 0xAC, 0xAC, 0xE3, 0xD8, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xDC, 0xDB, 0xE3, 0xDA, 0x79, 0x79, 0xE3, 0xE3,

View File

@@ -124,3 +124,18 @@ divisor_lookup = {
# 0xf0: 0xb53, 0xf1: 0xb53, 0xf2: 0xba0, 0xf3: 0xba0, 0xf4: 0xba5, 0xf5: 0xba5, 0xf6: 0xbac, 0xf7: 0xbac, # 0xf0: 0xb53, 0xf1: 0xb53, 0xf2: 0xba0, 0xf3: 0xba0, 0xf4: 0xba5, 0xf5: 0xba5, 0xf6: 0xbac, 0xf7: 0xbac,
# 0xf8: 0xbac, 0xf9: 0xbba, 0xfa: 0xbc1, 0xfb: 0xbcc, 0xfc: 0xbd7, 0xfd: 0xbd7, 0xfe: 0xbba, 0xff: 0xbe3 # 0xf8: 0xbac, 0xf9: 0xbba, 0xfa: 0xbc1, 0xfb: 0xbcc, 0xfc: 0xbd7, 0xfd: 0xbd7, 0xfe: 0xbba, 0xff: 0xbe3
# } # }
prize_lookup = {
0xd8: 'Small Magic Refill',
0xd9: 'Rupee (1)',
0xda: 'Rupees (5)',
0xdb: 'Rupees (20)',
0xdc: 'Bomb (1)',
0xdd: 'Bombs (4)',
0xde: 'Bombs (8)',
0xdf: 'Heart',
0xe0: 'Fairy',
0xe1: 'Arrows (5)',
0xe2: 'Arrows (10)',
0xe3: 'Full Magic Refill'
}

View File

@@ -1,16 +1,16 @@
description: Example door rando weights description: Example door rando weights
ow_shuffle: overworld_shuffle:
vanilla: 0 vanilla: 0
parallel: 2 parallel: 2
full: 2 full: 2
ow_keepsimilar: overworld_keepsimilar:
on: 1 on: 1
off: 1 off: 1
ow_swap: overworld_swap:
vanilla: 0 vanilla: 0
mixed: 2 mixed: 2
crossed: 2 crossed: 2
ow_fluteshuffle: flute_shuffle:
vanilla: 0 vanilla: 0
balanced: 1 balanced: 1
random: 1 random: 1