From b71c7aa2b43463e48d28bd44680e4b574b28919c Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 30 Sep 2022 15:38:35 -0600 Subject: [PATCH] Underworld dropshuffle --- BaseClasses.py | 8 +- CLI.py | 4 +- DoorShuffle.py | 10 +- ItemList.py | 2 +- Items.py | 6 +- KeyDoorShuffle.py | 2 +- Main.py | 3 + Regions.py | 4 +- Rom.py | 39 ++--- Rules.py | 14 ++ data/base2current.bps | Bin 98731 -> 98983 bytes docs/SuperTrueIceRodHunt.yaml | 2 +- docs/customizer_example.yaml | 2 +- mystery_example.yml | 5 +- mystery_testsuite.yml | 5 +- resources/app/cli/args.json | 7 +- resources/app/cli/lang/en.json | 6 +- resources/app/gui/lang/en.json | 5 +- .../app/gui/randomize/dungeon/widgets.json | 9 +- source/classes/CustomSettings.py | 3 +- source/dungeon/EnemyList.py | 164 ++++++++++-------- source/gui/bottom.py | 2 +- source/item/FillUtil.py | 5 +- source/logic/Rule.py | 4 + source/rom/DataTables.py | 4 +- source/tools/MysteryUtils.py | 3 +- 26 files changed, 182 insertions(+), 136 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index c361ef82..a296dce2 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2132,6 +2132,7 @@ class Location(object): self.real = not crystal self.always_allow = lambda item, state: False self.access_rule = lambda state: True + self.verbose_rule = None self.item_rule = lambda item: True self.player = player self.skip = False @@ -2601,7 +2602,7 @@ class Spoiler(object): outfile.write(f"Decouple Doors: {yn(self.metadata['decoupledoors'][player])}\n") outfile.write(f"Experimental: {yn(self.metadata['experimental'][player])}\n") outfile.write(f"Dungeon Counters: {self.metadata['dungeon_counters'][player]}\n") - outfile.write(f"Drop Shuffle: {yn(self.metadata['dropshuffle'][player])}\n") + outfile.write(f"Drop Shuffle: {self.metadata['dropshuffle'][player]}\n") outfile.write(f"Pottery Mode: {self.metadata['pottery'][player]}\n") outfile.write(f"Pot Shuffle (Legacy): {yn(self.metadata['potshuffle'][player])}\n") outfile.write('Map shuffle: %s\n' % ('Yes' if self.metadata['mapshuffle'][player] else 'No')) @@ -2861,6 +2862,7 @@ mixed_travel_mode = {"prevent": 0, "allow": 1, "force": 2} # new byte 4: TDDD PPPP (tavern shuffle, drop, pottery) # dropshuffle reserves 2 bits, pottery needs 4) +drop_shuffle_mode = {'none': 0, 'keys': 1, 'underworld': 2} pottery_mode = {'none': 0, 'keys': 2, 'lottery': 3, 'dungeon': 4, 'cave': 5, 'cavekeys': 6, 'reduced': 7, 'clustered': 8, 'nonempty': 9} @@ -2919,7 +2921,7 @@ class Settings(object): | (0x8 if w.standardize_palettes[p] == "original" else 0) | (0 if w.intensity[p] == "random" else w.intensity[p]), - (0x80 if w.shuffletavern[p] else 0) | (0x10 if w.dropshuffle[p] else 0) | (pottery_mode[w.pottery[p]]), + (0x80 if w.shuffletavern[p] else 0) | (drop_shuffle_mode[w.dropshuffle[p]] << 4) | (pottery_mode[w.pottery[p]]), ((8 if w.crystals_gt_orig[p] == "random" else int(w.crystals_gt_orig[p])) << 3) | (counter_mode[w.dungeon_counters[p]] << 1) | (1 if w.experimental[p] else 0), @@ -2974,7 +2976,7 @@ class Settings(object): args.intensity[p] = "random" if intensity == 0 else intensity args.shuffletavern[p] = True if settings[4] & 0x80 else False - args.dropshuffle[p] = True if settings[4] & 0x10 else False + args.dropshuffle[p] = r(drop_shuffle_mode)[settings[4] & 0x70] args.pottery[p] = r(pottery_mode)[settings[4] & 0x0F] args.dungeon_counters[p] = r(counter_mode)[(settings[5] & 0x6) >> 1] diff --git a/CLI.py b/CLI.py index fc70f8e9..272d0bca 100644 --- a/CLI.py +++ b/CLI.py @@ -107,7 +107,7 @@ def parse_cli(argv, no_defaults=False): ret.keyshuffle = 'wild' if ret.keydropshuffle: - ret.dropshuffle = True + ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' else ret.dropshuffle ret.pottery = 'keys' if ret.pottery == 'none' else ret.pottery if ret.retro or ret.mode == 'retro': @@ -196,7 +196,7 @@ def parse_settings(): "shopsanity": False, 'keydropshuffle': False, - 'dropshuffle': False, + 'dropshuffle': 'none', 'pottery': 'none', 'colorizepots': False, 'shufflepots': False, diff --git a/DoorShuffle.py b/DoorShuffle.py index 46e6b50b..a025349b 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -841,10 +841,10 @@ def main_dungeon_pool(dungeon_pool, world, player): all_dungeon_items_cnt = len(list(y for x in world.dungeons if x.player == player for y in x.all_items)) target_items = 34 if world.keyshuffle[player] == 'universal': - target_items += 1 if world.dropshuffle[player] else 0 # the hc big key + target_items += 1 if world.dropshuffle[player] != 'none' else 0 # the hc big key else: target_items += 29 # small keys in chests - if world.dropshuffle[player]: + if world.dropshuffle[player] != 'none': target_items += 14 # 13 dropped smalls + 1 big if world.pottery[player] not in ['none', 'cave']: target_items += 19 # 19 pot keys @@ -1246,10 +1246,10 @@ def cross_dungeon(world, player): all_dungeon_items_cnt = len(list(y for x in world.dungeons if x.player == player for y in x.all_items)) target_items = 34 if world.keyshuffle[player] == 'universal': - target_items += 1 if world.dropshuffle[player] else 0 # the hc big key + target_items += 1 if world.dropshuffle[player] != 'none' else 0 # the hc big key else: target_items += 29 # small keys in chests - if world.dropshuffle[player]: + if world.dropshuffle[player] != 'none': target_items += 14 # 13 dropped smalls + 1 big if world.pottery[player] not in ['none', 'cave']: target_items += 19 # 19 pot keys @@ -1335,7 +1335,7 @@ def assign_cross_keys(dungeon_builders, world, player): start = time.process_time() if world.keyshuffle[player] == 'universal': remaining = 29 - if world.dropshuffle[player]: + if world.dropshuffle[player] != 'none': remaining += 13 if world.pottery[player] not in ['none', 'cave']: remaining += 19 diff --git a/ItemList.py b/ItemList.py index 67e06644..ebf2c55d 100644 --- a/ItemList.py +++ b/ItemList.py @@ -414,7 +414,7 @@ def generate_itempool(world, player): if world.take_any[player] != 'none': set_up_take_anys(world, player, skip_pool_adjustments) if world.keyshuffle[player] == 'universal': - if world.dropshuffle[player] and not skip_pool_adjustments: + if world.dropshuffle[player] != 'none' and not skip_pool_adjustments: world.itempool += [ItemFactory('Small Key (Universal)', player)] * 13 if world.pottery[player] not in ['none', 'cave'] and not skip_pool_adjustments: world.itempool += [ItemFactory('Small Key (Universal)', player)] * 19 diff --git a/Items.py b/Items.py index 8c27bf88..7121c603 100644 --- a/Items.py +++ b/Items.py @@ -80,8 +80,8 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Single Bomb': (False, False, None, 0x27, 5, 'I make things\ngo BOOM! But\njust once.', 'and the explosion', 'the bomb-holding kid', 'firecracker for sale', 'blend fungus into bomb', '\'splosion boy explodes again', 'a bomb'), 'Arrows (5)': (False, False, None, 0x5A, 15, 'This will give\nyou five shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'five arrows'), 'Small Magic': (False, False, None, 0x45, 5, 'A bit of magic', 'and the bit of magic', 'bit-o-magic kid', 'magic bit for sale', 'fungus for magic', 'magic boy conjures again', 'a bit of magic'), - 'Big Magic': (False, False, None, 0x5A, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'), - 'Chicken': (False, False, None, 0x5A, 999, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'), + 'Big Magic': (False, False, None, 0xB4, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'), + 'Chicken': (False, False, None, 0xB3, 999, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'), 'Bombs (3)': (False, False, None, 0x28, 15, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'), 'Bombs (10)': (False, False, None, 0x31, 50, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'), 'Bomb Upgrade (+10)': (False, False, None, 0x52, 100, 'Increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), @@ -174,7 +174,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Blue Potion': (False, False, None, 0x30, 160, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a blue potion'), 'Bee': (False, False, None, 0x0E, 10, 'I will sting your foes a few times', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has mad bee again', 'a bee'), 'Small Heart': (False, False, None, 0x42, 10, 'Just a little\npiece of love!', 'and the heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart'), - 'Fairy': (False, False, None, 0x5A, 50, 'Just a pixie!', 'and the pixie', 'the pixie kid', 'pixie for sale', 'pixie fungus', 'bottle boy has pixie again', 'a pixie'), + 'Fairy': (False, False, None, 0xB2, 50, 'Just a pixie!', 'and the pixie', 'the pixie kid', 'pixie for sale', 'pixie fungus', 'bottle boy has pixie again', 'a pixie'), 'Beat Agahnim 1': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Beat Agahnim 2': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Get Frog': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), diff --git a/KeyDoorShuffle.py b/KeyDoorShuffle.py index 6d495aed..7d9aadd2 100644 --- a/KeyDoorShuffle.py +++ b/KeyDoorShuffle.py @@ -1422,7 +1422,7 @@ def prize_relevance_sig2(start_regions, d_name, dungeon_entrance): def validate_bk_layout(proposal, builder, start_regions, world, player): bk_special = check_bk_special(builder.master_sector.regions, world, player) - if world.bigkeyshuffle[player] and (world.dropshuffle[player] or not bk_special): + if world.bigkeyshuffle[player] and (world.dropshuffle[player] != 'none' or not bk_special): return True flat_proposal = flatten_pair_list(proposal) state = ExplorationState(dungeon=builder.name) diff --git a/Main.py b/Main.py index 7ed757cc..b5466a4f 100644 --- a/Main.py +++ b/Main.py @@ -461,11 +461,13 @@ def copy_world(world): ret.bigkeyshuffle = world.bigkeyshuffle.copy() ret.bombbag = world.bombbag.copy() ret.flute_mode = world.flute_mode.copy() + ret.bow_mode = world.bow_mode.copy() ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy() ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy() ret.crystals_ganon_orig = world.crystals_ganon_orig.copy() ret.crystals_gt_orig = world.crystals_gt_orig.copy() ret.open_pyramid = world.open_pyramid.copy() + ret.take_any = world.take_any.copy() ret.boss_shuffle = world.boss_shuffle.copy() ret.enemy_shuffle = world.enemy_shuffle.copy() ret.enemy_health = world.enemy_health.copy() @@ -481,6 +483,7 @@ def copy_world(world): ret.mixed_travel = world.mixed_travel.copy() ret.standardize_palettes = world.standardize_palettes.copy() ret.restrict_boss_items = world.restrict_boss_items.copy() + ret.data_tables = world.data_tables # can be changed... for player in range(1, world.players + 1): if world.mode[player] != 'inverted': diff --git a/Regions.py b/Regions.py index 93a88093..10669bc2 100644 --- a/Regions.py +++ b/Regions.py @@ -1023,8 +1023,8 @@ def adjust_locations(world, player): loc.address = pot_address(pot_index, datum[1]) loc.pot = pot pot.location = loc - if (not world.dropshuffle[player] and drop_location)\ - or (not drop_location and world.pottery[player] in ['none', 'cave']): + if ((world.dropshuffle[player] == 'none' and drop_location) + or (not drop_location and world.pottery[player] in ['none', 'cave'])): loc.skip = True else: key_item = loc.item diff --git a/Rom.py b/Rom.py index 22b534bd..27fc8f24 100644 --- a/Rom.py +++ b/Rom.py @@ -33,11 +33,11 @@ from InitialSram import InitialSram from source.classes.SFX import randomize_sfx from source.item.FillUtil import valid_pot_items -from source.dungeon.RoomList import Room0127 +from source.dungeon.EnemyList import EnemySprite JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '0587709ac8c5f2abf95b14d1e1264945' +RANDOMIZERBASEHASH = '81d7cf07a34d06ec875074296c39cd97' class JsonRom(object): @@ -616,18 +616,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): location.pot.indicator = standing_item_flag location.pot.standing_item_code = code continue - elif location.type == LocationType.Drop: - if location.item.player != player: - code = 0xF9 - else: - code = 0xF8 - sprite_pointer = snes_to_pc(location.address) - rom.write_byte(sprite_pointer, handle_native_dungeon(location, itemid)) - if code == 0xF9: - rom.write_byte(sprite_pointer+1, location.item.player) - else: - rom.write_byte(sprite_pointer+1, 0) - rom.write_byte(sprite_pointer+2, code) + elif location.type == LocationType.Drop: # handled in the sprite table routine continue if location.address is None or (type(location.address) is int and location.address >= 0x400000): continue @@ -764,7 +753,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): valid_loc_by_dungeon = valid_dungeon_locations(valid_locations) # fix hc big key problems (map and compass too) - if (world.doorShuffle[player] not in ['vanilla', 'basic'] or world.dropshuffle[player] + if (world.doorShuffle[player] not in ['vanilla', 'basic'] or world.dropshuffle[player] != 'none' or world.pottery[player] not in ['none', 'cave']): rom.write_byte(0x151f1, 2) rom.write_byte(0x15270, 2) @@ -894,9 +883,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): credits_total = len(valid_locations) - if world.dropshuffle[player] or world.pottery[player] != 'none': + if world.dropshuffle[player] != 'none' or world.pottery[player] != 'none': rom.write_byte(0x142A50, 1) # StandingItemsOn - multiClientFlags = ((0x1 if world.dropshuffle[player] else 0) + multiClientFlags = ((0x1 if world.dropshuffle[player] != 'none' else 0) | (0x2 if world.shopsanity[player] else 0) | (0x4 if world.take_any[player] != 'none' else 0) | (0x8 if world.pottery[player] != 'none' else 0) @@ -904,7 +893,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x142A51, multiClientFlags) # StandingItemCounterMask rom.write_byte(0x142A55, ((0x1 if world.pottery[player] not in ['none', 'cave'] else 0) - | (0x2 if world.dropshuffle[player] else 0))) + | (0x2 if world.dropshuffle[player] != 'none' else 0))) if world.pottery[player] not in ['none', 'keys']: # Cuccos should not prevent kill rooms from opening rom.write_byte(snes_to_pc(0x0DB457), 0x40) @@ -1249,8 +1238,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed gametype = 0x04 # item - if (world.shuffle[player] != 'vanilla' or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] - or world.pottery[player] != 'none'): + if (world.shuffle[player] != 'vanilla' or world.doorShuffle[player] != 'vanilla' + or world.dropshuffle[player] != 'none' or world.pottery[player] != 'none'): gametype |= 0x02 # entrance/door if enemized: gametype |= 0x01 # enemizer @@ -1350,7 +1339,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x18003C, 0x00) elif world.dungeon_counters[player] == 'on': compass_mode = 0x02 # always on - elif (world.compassshuffle[player] or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] + elif (world.compassshuffle[player] or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none' or world.dungeon_counters[player] == 'pickup' or world.pottery[player] not in ['none', 'cave']): compass_mode = 0x01 # show on pickup if world.shuffle[player] != 'vanilla' and world.overworld_map[player] != 'default': @@ -1518,10 +1507,11 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): if item_dungeon == 'Escape': item_dungeon = 'Hyrule Castle' is_small_key_this_dungeon = hera_basement.parent_region.dungeon.name == item_dungeon + # hera small key is 11th in list, 10th sprite because of overlord if is_small_key_this_dungeon: - rom.write_byte(0x4E3BB, 0xE4) + world.data_tables[player].uw_enemy_table.room_map[0x87][11].kind = EnemySprite.SmallKey else: - rom.write_byte(0x4E3BB, 0xEB) + world.data_tables[player].uw_enemy_table.room_map[0x87][11].kind = EnemySprite.HeartPiece # fix trock doors for reverse entrances if world.fix_trock_doors[player]: @@ -1533,7 +1523,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0xFED31, 0x0E) # preopen bombable exit rom.write_byte(0xFEE41, 0x0E) # preopen bombable exit - if (world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] + # todo: combine this with data_tables for in place edits + if (world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none' or world.pottery[player] != 'none'): for room in world.rooms: if room.player == player and room.modified: diff --git a/Rules.py b/Rules.py index 1355b7e7..83512aaf 100644 --- a/Rules.py +++ b/Rules.py @@ -9,6 +9,8 @@ from Dungeons import dungeon_table from RoomData import DoorKind from OverworldGlitchRules import overworld_glitches_rules +from source.dungeon.EnemyList import kill_rules + def set_rules(world, player): @@ -35,6 +37,7 @@ def set_rules(world, player): bomb_rules(world, player) pot_rules(world, player) + drop_rules(world, player) if world.logic[player] == 'noglitches': no_glitches_rules(world, player) @@ -770,6 +773,17 @@ def pot_rules(world, player): add_rule(l, lambda state: state.can_hit_crystal(player)) +def drop_rules(world, player): + data_tables = world.data_tables[player] + defeat_rules = kill_rules(world, player, data_tables.enemy_stats) + for super_tile, enemy_list in data_tables.uw_enemy_table.room_map.items(): + for enemy in enemy_list: + if enemy.location: + # could handle odd health rules here? assume harder variant for now + verbose_rule = defeat_rules[enemy.kind] + enemy.location.verbose_rule = verbose_rule + add_rule(enemy.location, verbose_rule.rule_lambda) + def default_rules(world, player): # overworld requirements diff --git a/data/base2current.bps b/data/base2current.bps index 52a73adadce6333bac1f62eeae77f1fc00815ef2..ff5041eb85276be001a16d3e49639471465916b9 100644 GIT binary patch delta 14245 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 0x11c0: @@ -56,6 +57,7 @@ def init_data_tables(world, player): data_tables.sprite_requirements = init_sprite_requirements() data_tables.sprite_sheets = init_sprite_sheets(data_tables.sprite_requirements) init_vanilla_sprites() + data_tables.enemy_stats = init_enemy_stats() uw_table = data_tables.uw_enemy_table = EnemyTable() for room, sprite_list in vanilla_sprites.items(): for sprite in sprite_list: diff --git a/source/tools/MysteryUtils.py b/source/tools/MysteryUtils.py index 38ee8c1a..5212aa36 100644 --- a/source/tools/MysteryUtils.py +++ b/source/tools/MysteryUtils.py @@ -97,7 +97,8 @@ def roll_settings(weights): ret.pseudoboots = get_choice('pseudoboots') == 'on' ret.shopsanity = get_choice('shopsanity') == 'on' keydropshuffle = get_choice('keydropshuffle') == 'on' - ret.dropshuffle = get_choice('dropshuffle') == 'on' or keydropshuffle + ret.dropshuffle = get_choice('dropshuffle') if 'dropshuffle' in weights else 'none' + ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' and keydropshuffle else ret.dropshuffle ret.pottery = get_choice('pottery') if 'pottery' in weights else 'none' ret.pottery = 'keys' if ret.pottery == 'none' and keydropshuffle else ret.pottery ret.colorizepots = get_choice('colorizepots') == 'on'