From c14ad502cd022de0c51e5faba245e17b68823e62 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 29 Jul 2021 03:40:45 -0500 Subject: [PATCH] Adding tree pulls, bush crabs, and stun prizes to logic consideration --- BaseClasses.py | 80 ++++++++++++++++++++++++++++++++++++++++++++------ Fill.py | 30 +++++++++++++++++++ Main.py | 5 +++- Rom.py | 37 +++++++++++------------ Tables.py | 15 ++++++++++ 5 files changed, 139 insertions(+), 28 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 49bac1de..09ff5b3b 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -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) @@ -148,6 +149,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)})' @@ -674,7 +676,6 @@ class CollectionState(object): return False 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', 'Snitch Lady (East)', 'Turtle Rock Area', @@ -684,16 +685,29 @@ class CollectionState(object): '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): @@ -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 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', 'Snitch Lady (East)', 'Turtle Rock Area', @@ -734,15 +747,20 @@ class CollectionState(object): '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): @@ -753,7 +771,41 @@ class CollectionState(object): if can_reach_non_bunny(region): 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 False @@ -813,6 +865,16 @@ class CollectionState(object): 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 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) diff --git a/Fill.py b/Fill.py index de59a2b5..3e046600 100644 --- a/Fill.py +++ b/Fill.py @@ -712,3 +712,33 @@ def balance_money_progression(world): unchecked_locations.remove(location) if location.item.name.startswith('Rupee'): 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() \ No newline at end of file diff --git a/Main.py b/Main.py index 05090de7..715b0ace 100644 --- a/Main.py +++ b/Main.py @@ -25,7 +25,7 @@ from RoomData import create_rooms from Rules import set_rules 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 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 Utils import output_path, parse_player_names @@ -195,6 +195,8 @@ def main(args, seed=None, fish=None): else: 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")) @@ -414,6 +416,7 @@ def copy_world(world): ret.standardize_palettes = world.standardize_palettes.copy() ret.owswaps = world.owswaps.copy() ret.owflutespots = world.owflutespots.copy() + ret.prizes = world.prizes.copy() for player in range(1, world.players + 1): create_regions(ret, player) diff --git a/Rom.py b/Rom.py index da431388..aea11ce7 100644 --- a/Rom.py +++ b/Rom.py @@ -1066,9 +1066,13 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x178000 + i, random.randint(0, 255)) # 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, - 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] + pack_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] dig_prizes = [0xB2, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 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): 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 - packs = chunk(prizes[:56], 8) + packs = chunk(pack_prizes, 8) 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']: prize_replacements = {0xE0: 0xDF, # Fairy -> heart 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] if world.retro[player]: prize_replacements = {0xE1: 0xDA, #5 Arrows -> Blue 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] rom.write_bytes(0x180100, dig_prizes) # write tree pull prizes - rom.write_byte(0xEFBD4, prizes.pop()) - rom.write_byte(0xEFBD5, prizes.pop()) - rom.write_byte(0xEFBD6, prizes.pop()) + rom.write_byte(0xEFBD4, world.prizes[player]['pull'][0]) + rom.write_byte(0xEFBD5, world.prizes[player]['pull'][1]) + rom.write_byte(0xEFBD6, world.prizes[player]['pull'][2]) # rupee crab prizes - rom.write_byte(0x329C8, prizes.pop()) # first prize - rom.write_byte(0x329C4, prizes.pop()) # final prize + rom.write_byte(0x329C8, world.prizes[player]['crab'][0]) # first prize + rom.write_byte(0x329C4, world.prizes[player]['crab'][1]) # final prize # stunned enemy prize - rom.write_byte(0x37993, prizes.pop()) + rom.write_byte(0x37993, world.prizes[player]['stun']) # saved fish prize - rom.write_byte(0xE82CC, prizes.pop()) + rom.write_byte(0xE82CC, world.prizes[player]['fish']) # fill enemy prize packs - rom.write_bytes(0x37A78, prizes) + rom.write_bytes(0x37A78, pack_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, diff --git a/Tables.py b/Tables.py index 5365e69c..64464b9f 100644 --- a/Tables.py +++ b/Tables.py @@ -124,3 +124,18 @@ divisor_lookup = { # 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 # } + +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' +}