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 52a73ada..ff5041eb 100644 Binary files a/data/base2current.bps and b/data/base2current.bps differ diff --git a/docs/SuperTrueIceRodHunt.yaml b/docs/SuperTrueIceRodHunt.yaml index 3b644bf9..d44e94c5 100644 --- a/docs/SuperTrueIceRodHunt.yaml +++ b/docs/SuperTrueIceRodHunt.yaml @@ -4,7 +4,7 @@ meta: settings: 1: door_shuffle: vanilla - dropshuffle: true + dropshuffle: keys experimental: true goal: ganon hints: false diff --git a/docs/customizer_example.yaml b/docs/customizer_example.yaml index 3d0c7624..a8ba4de5 100644 --- a/docs/customizer_example.yaml +++ b/docs/customizer_example.yaml @@ -6,7 +6,7 @@ meta: settings: 1: door_shuffle: basic - dropshuffle: true + dropshuffle: keys experimental: true goal: ganon hints: true diff --git a/mystery_example.yml b/mystery_example.yml index 26fb559f..0776bba8 100644 --- a/mystery_example.yml +++ b/mystery_example.yml @@ -24,8 +24,9 @@ chaos: 1 decoupledoors: off dropshuffle: - on: 1 - off: 1 + none: 4 + keys: 1 + underworld: 1 pottery: none: 8 keys: 1 diff --git a/mystery_testsuite.yml b/mystery_testsuite.yml index aca535c3..7ccec6a1 100644 --- a/mystery_testsuite.yml +++ b/mystery_testsuite.yml @@ -14,8 +14,9 @@ intensity: 2: 1 3: 2 # intensity 3 usually yield more errors dropshuffle: - on: 1 - off: 1 + none: 10 # fewer locations + keys: 1 + underworld: 1 pottery: none: 10 # fewer locations keys: 1 diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index 0074143d..8988a64b 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -89,8 +89,11 @@ "type": "bool" }, "dropshuffle" : { - "action": "store_true", - "type": "bool" + "choices" : [ + "none", + "keys", + "underworld" + ] }, "pottery" : { "choices" : [ diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index a6dcc3ee..6a013167 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -277,7 +277,11 @@ "keyshuffle": [ "Small Keys are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ], "bigkeyshuffle": [ "Big Keys are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ], "shopsanity": ["Shop contents are shuffle in the main item pool and other items can take their place. (default: %(default)s)"], - "dropshuffle": [ "Keys dropped by enemies are shuffled and other items can take their place. (default: %(default)s)"], + "dropshuffle": [ "Controls how enemies drop items (default: %(default)s)", + "None: Enemies drops prize packs or keys as normal", + "Keys: Enemies that drop keys can drop other items and the keys are shuffled into the pool", + "Underworld: All killable enemies in the underworld can drop items" + ], "pottery": [ "Controls how items under pots are shuffled and if other items can take their place:", "None: No pots are changed", "Keys: Key pots are included in the location pool and other items can take their place", diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index 9a55893a..c182e023 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -59,7 +59,10 @@ "randomizer.dungeon.bigkeyshuffle": "Big Keys", "randomizer.dungeon.keydropshuffle": "Key Drop Shuffle (Legacy)", "randomizer.dungeon.decoupledoors": "Decouple Doors", - "randomizer.dungeon.dropshuffle": "Shuffle Enemy Key Drops", + "randomizer.dungeon.dropshuffle": "Shuffle Enemy Drops", + "randomizer.dungeon.none": "None", + "randomizer.dungeon.keys": "Small Key Enemies", + "randomizer.dungeon.underworld": "Underworld Enemies", "randomizer.dungeon.potshuffle": "Pot Shuffle (Legacy)", "randomizer.dungeon.pottery": "Pottery", "randomizer.dungeon.pottery.none": "None", diff --git a/resources/app/gui/randomize/dungeon/widgets.json b/resources/app/gui/randomize/dungeon/widgets.json index 19606b23..d0780906 100644 --- a/resources/app/gui/randomize/dungeon/widgets.json +++ b/resources/app/gui/randomize/dungeon/widgets.json @@ -65,7 +65,14 @@ } }, "colorizepots": { "type": "checkbox" }, - "dropshuffle": { "type": "checkbox" }, + "dropshuffle": { + "type": "selectbox", + "default": "none", + "options": [ + "none", + "keys", + "underworld" + ]}, "potshuffle": { "type": "checkbox" }, "experimental": { "type": "checkbox" }, "dungeon_counters": { diff --git a/source/classes/CustomSettings.py b/source/classes/CustomSettings.py index 08150a77..caef5870 100644 --- a/source/classes/CustomSettings.py +++ b/source/classes/CustomSettings.py @@ -82,7 +82,8 @@ class CustomSettings(object): args.pottery[p] = get_setting(settings['pottery'], args.pottery[p]) if get_setting(settings['keydropshuffle'], args.keydropshuffle[p]): - args.dropshuffle[p] = True + if args.dropshuffle[p] == 'none': + args.dropshuffle[p] = 'keys' if args.pottery[p] == 'none': args.pottery[p] = 'keys' diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py index 732990cd..a0944556 100644 --- a/source/dungeon/EnemyList.py +++ b/source/dungeon/EnemyList.py @@ -17,15 +17,27 @@ from source.logic.Rule import RuleFactory class EnemyStats: - def __init__(self, sprite, static, drop_flag=False, prize_pack: typing.Union[tuple, int] = 0, sub_type=0): + def __init__(self, sprite, static, drop_flag=False, prize_pack: typing.Union[tuple, int] = 0, + sub_type=0, health=None): self.sprite = sprite self.sub_type = sub_type self.static = static - # self.health = health + self.health = health # self.damage = damage self.drop_flag = drop_flag self.prize_pack = prize_pack + # health special cases: + # Octorok light/dark world + # Hardhat Beetle - starting position + # Tektike - starting position? + # RatCricket light/dark world + # Keese light/dark world + # Rope light/dark world + # Raven light/dark world + + + class EnemySprite(FastEnum): Raven = 0x00 @@ -252,34 +264,34 @@ def init_enemy_stats(): stats = { EnemySprite.CorrectPullSwitch: EnemyStats(EnemySprite.CorrectPullSwitch, True), EnemySprite.WrongPullSwitch: EnemyStats(EnemySprite.WrongPullSwitch, True), - EnemySprite.Octorok: EnemyStats(EnemySprite.Octorok, False, True, 2), + EnemySprite.Octorok: EnemyStats(EnemySprite.Octorok, False, True, 2, health=4), EnemySprite.Moldorm: EnemyStats(EnemySprite.Moldorm, True, False), EnemySprite.Cucco: EnemyStats(EnemySprite.Cucco, False, False), - EnemySprite.Octoballoon: EnemyStats(EnemySprite.Octoballoon, False, False, 0), + EnemySprite.Octoballoon: EnemyStats(EnemySprite.Octoballoon, False, False, 0, health=2), EnemySprite.OctoballoonBaby: EnemyStats(EnemySprite.OctoballoonBaby, False), - EnemySprite.Hinox: EnemyStats(EnemySprite.Hinox, False, True, 4), - EnemySprite.Moblin: EnemyStats(EnemySprite.Moblin, False, True, 1), - EnemySprite.MiniHelmasaur: EnemyStats(EnemySprite.MiniHelmasaur, False, True, 7), + EnemySprite.Hinox: EnemyStats(EnemySprite.Hinox, False, True, 4, health=20), + EnemySprite.Moblin: EnemyStats(EnemySprite.Moblin, False, True, 1, health=4), + EnemySprite.MiniHelmasaur: EnemyStats(EnemySprite.MiniHelmasaur, False, True, 7, health=4), EnemySprite.ThievesTownGrate: EnemyStats(EnemySprite.ThievesTownGrate, True), EnemySprite.AntiFairy: EnemyStats(EnemySprite.AntiFairy, False, False), EnemySprite.Wiseman: EnemyStats(EnemySprite.Wiseman, True), - EnemySprite.Hoarder: EnemyStats(EnemySprite.Hoarder, False, False), - EnemySprite.MiniMoldorm: EnemyStats(EnemySprite.MiniMoldorm, False, True, 2), - EnemySprite.Poe: EnemyStats(EnemySprite.Poe, False, True, 6), + EnemySprite.Hoarder: EnemyStats(EnemySprite.Hoarder, False, False, health=2), + EnemySprite.MiniMoldorm: EnemyStats(EnemySprite.MiniMoldorm, False, True, 2, health=3), + EnemySprite.Poe: EnemyStats(EnemySprite.Poe, False, True, 6, health=8), EnemySprite.Smithy: EnemyStats(EnemySprite.Smithy, True), EnemySprite.Arrow: EnemyStats(EnemySprite.Arrow, True), EnemySprite.Statue: EnemyStats(EnemySprite.Statue, True), EnemySprite.FluteQuest: EnemyStats(EnemySprite.FluteQuest, True), EnemySprite.CrystalSwitch: EnemyStats(EnemySprite.CrystalSwitch, True), EnemySprite.SickKid: EnemyStats(EnemySprite.SickKid, True), - EnemySprite.Sluggula: EnemyStats(EnemySprite.Sluggula, False, True, 4), + EnemySprite.Sluggula: EnemyStats(EnemySprite.Sluggula, False, True, 4, health=8), EnemySprite.WaterSwitch: EnemyStats(EnemySprite.WaterSwitch, True), - EnemySprite.Ropa: EnemyStats(EnemySprite.Ropa, False, True, 2), - EnemySprite.RedBari: EnemyStats(EnemySprite.RedBari, False, True, 6, 2), - EnemySprite.BlueBari: EnemyStats(EnemySprite.BlueBari, False, True, 6, 2), + EnemySprite.Ropa: EnemyStats(EnemySprite.Ropa, False, True, 2, health=8), + EnemySprite.RedBari: EnemyStats(EnemySprite.RedBari, False, True, 6, 2, health=2), + EnemySprite.BlueBari: EnemyStats(EnemySprite.BlueBari, False, True, 6, 2, health=2), EnemySprite.TalkingTree: EnemyStats(EnemySprite.TalkingTree, True), - EnemySprite.HardhatBeetle: EnemyStats(EnemySprite.HardhatBeetle, False, True, (2, 6)), + EnemySprite.HardhatBeetle: EnemyStats(EnemySprite.HardhatBeetle, False, True, (2, 6), health=32), EnemySprite.Deadrock: EnemyStats(EnemySprite.Deadrock, False, False), EnemySprite.DarkWorldHintNpc: EnemyStats(EnemySprite.DarkWorldHintNpc, True), EnemySprite.AdultNpc: EnemyStats(EnemySprite.AdultNpc, True), @@ -303,30 +315,30 @@ def init_enemy_stats(): EnemySprite.BonkItem: EnemyStats(EnemySprite.BonkItem, True), EnemySprite.KidInKak: EnemyStats(EnemySprite.KidInKak, True), EnemySprite.OldSnitch: EnemyStats(EnemySprite.OldSnitch, True), - EnemySprite.Hoarder2: EnemyStats(EnemySprite.Hoarder2, False, False), + EnemySprite.Hoarder2: EnemyStats(EnemySprite.Hoarder2, False, False, health=2), EnemySprite.TutorialGuard: EnemyStats(EnemySprite.TutorialGuard, True), EnemySprite.LightningGate: EnemyStats(EnemySprite.LightningGate, True), - EnemySprite.BlueGuard: EnemyStats(EnemySprite.BlueGuard, False, True, 1), - EnemySprite.GreenGuard: EnemyStats(EnemySprite.GreenGuard, False, True, 1), - EnemySprite.RedSpearGuard: EnemyStats(EnemySprite.RedSpearGuard, False, True, 1), - EnemySprite.BluesainBolt: EnemyStats(EnemySprite.BluesainBolt, False, True, 7), - EnemySprite.UsainBolt: EnemyStats(EnemySprite.UsainBolt, False, True, 1), - EnemySprite.BlueArcher: EnemyStats(EnemySprite.BlueArcher, False, True, 5), - EnemySprite.GreenBushGuard: EnemyStats(EnemySprite.GreenBushGuard, False, True, 5), - EnemySprite.RedJavelinGuard: EnemyStats(EnemySprite.RedJavelinGuard, False, True, 3), - EnemySprite.RedBushGuard: EnemyStats(EnemySprite.RedBushGuard, False, True, 7), - EnemySprite.BombGuard: EnemyStats(EnemySprite.BombGuard, False, True, 4), - EnemySprite.GreenKnifeGuard: EnemyStats(EnemySprite.GreenKnifeGuard, False, True, 1), - EnemySprite.Geldman: EnemyStats(EnemySprite.Geldman, False, True, 2), - EnemySprite.Popo: EnemyStats(EnemySprite.Popo, False, True, 2), - EnemySprite.Popo2: EnemyStats(EnemySprite.Popo2, False, True, 2), + EnemySprite.BlueGuard: EnemyStats(EnemySprite.BlueGuard, False, True, 1, health=6), + EnemySprite.GreenGuard: EnemyStats(EnemySprite.GreenGuard, False, True, 1, health=4), + EnemySprite.RedSpearGuard: EnemyStats(EnemySprite.RedSpearGuard, False, True, 1, health=8), + EnemySprite.BluesainBolt: EnemyStats(EnemySprite.BluesainBolt, False, True, 7, health=6), + EnemySprite.UsainBolt: EnemyStats(EnemySprite.UsainBolt, False, True, 1, health=8), + EnemySprite.BlueArcher: EnemyStats(EnemySprite.BlueArcher, False, True, 5, health=6), + EnemySprite.GreenBushGuard: EnemyStats(EnemySprite.GreenBushGuard, False, True, 5, health=4), + EnemySprite.RedJavelinGuard: EnemyStats(EnemySprite.RedJavelinGuard, False, True, 3, health=8), + EnemySprite.RedBushGuard: EnemyStats(EnemySprite.RedBushGuard, False, True, 7, health=8), + EnemySprite.BombGuard: EnemyStats(EnemySprite.BombGuard, False, True, 4, health=8), + EnemySprite.GreenKnifeGuard: EnemyStats(EnemySprite.GreenKnifeGuard, False, True, 1, health=4), + EnemySprite.Geldman: EnemyStats(EnemySprite.Geldman, False, True, 2, health=4), + EnemySprite.Popo: EnemyStats(EnemySprite.Popo, False, True, 2, health=2), + EnemySprite.Popo2: EnemyStats(EnemySprite.Popo2, False, True, 2, health=2), EnemySprite.ArmosKnight: EnemyStats(EnemySprite.ArmosKnight, True, False), EnemySprite.Lanmolas: EnemyStats(EnemySprite.Lanmolas, True, False), EnemySprite.Zora: EnemyStats(EnemySprite.Zora, False, True, 4), EnemySprite.DesertStatue: EnemyStats(EnemySprite.DesertStatue, True), - EnemySprite.Crab: EnemyStats(EnemySprite.Crab, False, True, 1), + EnemySprite.Crab: EnemyStats(EnemySprite.Crab, False, True, 1, health=2), EnemySprite.LostWoodsBird: EnemyStats(EnemySprite.LostWoodsBird, True), EnemySprite.LostWoodsSquirrel: EnemyStats(EnemySprite.LostWoodsSquirrel, True), EnemySprite.SparkCW: EnemyStats(EnemySprite.SparkCW, False, False), @@ -337,20 +349,20 @@ def init_enemy_stats(): EnemySprite.RollerHorizontalRight: EnemyStats(EnemySprite.RollerHorizontalRight, False, False), EnemySprite.Beamos: EnemyStats(EnemySprite.Beamos, False, False), EnemySprite.MasterSword: EnemyStats(EnemySprite.MasterSword, True), - EnemySprite.DebirandoPit: EnemyStats(EnemySprite.DebirandoPit, False, True, 2), - EnemySprite.Debirando: EnemyStats(EnemySprite.Debirando, False, True, 2), + EnemySprite.DebirandoPit: EnemyStats(EnemySprite.DebirandoPit, False, True, 2, health=4), + EnemySprite.Debirando: EnemyStats(EnemySprite.Debirando, False, True, 2, health=4), EnemySprite.ArcheryNpc: EnemyStats(EnemySprite.ArcheryNpc, True), EnemySprite.WallCannonVertLeft: EnemyStats(EnemySprite.WallCannonVertLeft, True), EnemySprite.WallCannonVertRight: EnemyStats(EnemySprite.WallCannonVertRight, True), EnemySprite.WallCannonHorzTop: EnemyStats(EnemySprite.WallCannonHorzTop, True), EnemySprite.WallCannonHorzBottom: EnemyStats(EnemySprite.WallCannonHorzBottom, True), - EnemySprite.BallNChain: EnemyStats(EnemySprite.BallNChain, False, True, 2), - EnemySprite.CannonTrooper: EnemyStats(EnemySprite.CannonTrooper, False, True, 1), - EnemySprite.CricketRat: EnemyStats(EnemySprite.CricketRat, False, True, 2), - EnemySprite.Snake: EnemyStats(EnemySprite.Snake, False, True, (1, 7)), - EnemySprite.Keese: EnemyStats(EnemySprite.Keese, False, True, (0, 7)), + EnemySprite.BallNChain: EnemyStats(EnemySprite.BallNChain, False, True, 2, health=16), + EnemySprite.CannonTrooper: EnemyStats(EnemySprite.CannonTrooper, False, True, 1, health=3), + EnemySprite.CricketRat: EnemyStats(EnemySprite.CricketRat, False, True, 2, health=8), + EnemySprite.Snake: EnemyStats(EnemySprite.Snake, False, True, (1, 7), health=8), + EnemySprite.Keese: EnemyStats(EnemySprite.Keese, False, True, (0, 7), health=4), - EnemySprite.Leever: EnemyStats(EnemySprite.Leever, False, True, 1), + EnemySprite.Leever: EnemyStats(EnemySprite.Leever, False, True, 1, health=4), EnemySprite.FairyPondTrigger: EnemyStats(EnemySprite.FairyPondTrigger, True), EnemySprite.UnclePriest: EnemyStats(EnemySprite.UnclePriest, True), EnemySprite.RunningNpc: EnemyStats(EnemySprite.RunningNpc, True), @@ -358,26 +370,26 @@ def init_enemy_stats(): EnemySprite.Zelda: EnemyStats(EnemySprite.Zelda, True), EnemySprite.Grandma: EnemyStats(EnemySprite.Grandma, True), EnemySprite.Agahnim: EnemyStats(EnemySprite.Agahnim, True), - EnemySprite.FloatingSkull: EnemyStats(EnemySprite.FloatingSkull, False, True, 7), + EnemySprite.FloatingSkull: EnemyStats(EnemySprite.FloatingSkull, False, True, 7, health=24), EnemySprite.BigSpike: EnemyStats(EnemySprite.BigSpike, False, False), EnemySprite.FirebarCW: EnemyStats(EnemySprite.FirebarCW, False, False), EnemySprite.FirebarCCW: EnemyStats(EnemySprite.FirebarCCW, False, False), EnemySprite.Firesnake: EnemyStats(EnemySprite.Firesnake, False, False), - EnemySprite.Hover: EnemyStats(EnemySprite.Hover, False, True, 2), + EnemySprite.Hover: EnemyStats(EnemySprite.Hover, False, True, 2, health=4), EnemySprite.AntiFairyCircle: EnemyStats(EnemySprite.AntiFairyCircle, False, False), - EnemySprite.GreenEyegoreMimic: EnemyStats(EnemySprite.GreenEyegoreMimic, False, True, 5), - EnemySprite.RedEyegoreMimic: EnemyStats(EnemySprite.RedEyegoreMimic, False, True, 5), - EnemySprite.YellowStalfos: EnemyStats(EnemySprite.YellowStalfos, True), - EnemySprite.Kondongo: EnemyStats(EnemySprite.Kondongo, False, True, 6), + EnemySprite.GreenEyegoreMimic: EnemyStats(EnemySprite.GreenEyegoreMimic, False, True, 5, health=16), + EnemySprite.RedEyegoreMimic: EnemyStats(EnemySprite.RedEyegoreMimic, False, True, 5, health=8), + EnemySprite.YellowStalfos: EnemyStats(EnemySprite.YellowStalfos, True, health=8), + EnemySprite.Kondongo: EnemyStats(EnemySprite.Kondongo, False, True, 6, health=1), EnemySprite.Mothula: EnemyStats(EnemySprite.Mothula, True), EnemySprite.SpikeBlock: EnemyStats(EnemySprite.SpikeBlock, False, False), - EnemySprite.Gibdo: EnemyStats(EnemySprite.Gibdo, False, True, 3), + EnemySprite.Gibdo: EnemyStats(EnemySprite.Gibdo, False, True, 3, health=32), EnemySprite.Arrghus: EnemyStats(EnemySprite.Arrghus, True), EnemySprite.Arrghi: EnemyStats(EnemySprite.Arrghi, True), - EnemySprite.Terrorpin: EnemyStats(EnemySprite.Terrorpin, False, True, 2), - EnemySprite.Blob: EnemyStats(EnemySprite.Blob, False, True, 1), + EnemySprite.Terrorpin: EnemyStats(EnemySprite.Terrorpin, False, True, 2, health=8), + EnemySprite.Blob: EnemyStats(EnemySprite.Blob, False, True, 1, health=4), EnemySprite.Wallmaster: EnemyStats(EnemySprite.Wallmaster, True), - EnemySprite.StalfosKnight: EnemyStats(EnemySprite.StalfosKnight, False, True, 4), + EnemySprite.StalfosKnight: EnemyStats(EnemySprite.StalfosKnight, False, True, 4, health=64), EnemySprite.HelmasaurKing: EnemyStats(EnemySprite.HelmasaurKing, True), EnemySprite.Bumper: EnemyStats(EnemySprite.Bumper, True), EnemySprite.Pirogusu: EnemyStats(EnemySprite.Pirogusu, True), @@ -385,21 +397,21 @@ def init_enemy_stats(): EnemySprite.LaserEyeRight: EnemyStats(EnemySprite.LaserEyeRight, True), EnemySprite.LaserEyeTop: EnemyStats(EnemySprite.LaserEyeTop, True), EnemySprite.LaserEyeBottom: EnemyStats(EnemySprite.LaserEyeBottom, True), - EnemySprite.Pengator: EnemyStats(EnemySprite.Pengator, False, True, 3), - EnemySprite.Kyameron: EnemyStats(EnemySprite.Kyameron, False, False), - EnemySprite.Wizzrobe: EnemyStats(EnemySprite.Wizzrobe, False, True, 1), - EnemySprite.Zoro: EnemyStats(EnemySprite.Zoro, True), - EnemySprite.Babasu: EnemyStats(EnemySprite.Babasu, False, True, 0), + EnemySprite.Pengator: EnemyStats(EnemySprite.Pengator, False, True, 3, health=16), + EnemySprite.Kyameron: EnemyStats(EnemySprite.Kyameron, False, False, health=4), + EnemySprite.Wizzrobe: EnemyStats(EnemySprite.Wizzrobe, False, True, 1, health=2), + EnemySprite.Zoro: EnemyStats(EnemySprite.Zoro, True, health=4), + EnemySprite.Babasu: EnemyStats(EnemySprite.Babasu, False, True, 0, health=4), EnemySprite.GroveOstritch: EnemyStats(EnemySprite.GroveOstritch, True), EnemySprite.GroveRabbit: EnemyStats(EnemySprite.GroveRabbit, True), EnemySprite.GroveBird: EnemyStats(EnemySprite.GroveBird, True), - EnemySprite.Freezor: EnemyStats(EnemySprite.Freezor, True, True, 0), + EnemySprite.Freezor: EnemyStats(EnemySprite.Freezor, True, True, 0, health=16), EnemySprite.Kholdstare: EnemyStats(EnemySprite.Kholdstare, True), EnemySprite.KholdstareShell: EnemyStats(EnemySprite.KholdstareShell, True), EnemySprite.FallingIce: EnemyStats(EnemySprite.FallingIce, True), - EnemySprite.BlueZazak: EnemyStats(EnemySprite.BlueZazak, False, True, 6), - EnemySprite.RedZazak: EnemyStats(EnemySprite.RedZazak, False, True, 6), - EnemySprite.Stalfos: EnemyStats(EnemySprite.Stalfos, False, True, 6), + EnemySprite.BlueZazak: EnemyStats(EnemySprite.BlueZazak, False, True, 6, health=4), + EnemySprite.RedZazak: EnemyStats(EnemySprite.RedZazak, False, True, 6, health=8), + EnemySprite.Stalfos: EnemyStats(EnemySprite.Stalfos, False, True, 6, health=4), # ... OW EnemySprite.OldMan: EnemyStats(EnemySprite.OldMan, True), EnemySprite.PipeDown: EnemyStats(EnemySprite.PipeDown, True), @@ -418,23 +430,23 @@ def init_enemy_stats(): EnemySprite.Catfish: EnemyStats(EnemySprite.Catfish, True), EnemySprite.CutsceneAgahnim: EnemyStats(EnemySprite.CutsceneAgahnim, True), EnemySprite.Boulder: EnemyStats(EnemySprite.Boulder, True), - EnemySprite.Gibo: EnemyStats(EnemySprite.Gibo, False, True, 0), # patrick! + EnemySprite.Gibo: EnemyStats(EnemySprite.Gibo, False, True, 0, health=8), # patrick! EnemySprite.Thief: EnemyStats(EnemySprite.Thief, False, False), # could drop if killable thieves is on EnemySprite.Medusa: EnemyStats(EnemySprite.Medusa, True), EnemySprite.FourWayShooter: EnemyStats(EnemySprite.FourWayShooter, True), - EnemySprite.Pokey: EnemyStats(EnemySprite.Pokey, False, True, 7), + EnemySprite.Pokey: EnemyStats(EnemySprite.Pokey, False, True, 7, health=32), EnemySprite.BigFairy: EnemyStats(EnemySprite.BigFairy, True), - EnemySprite.Tektite: EnemyStats(EnemySprite.Tektite, False, True, 2), + EnemySprite.Tektite: EnemyStats(EnemySprite.Tektite, False, True, 2, health=12), EnemySprite.Chainchomp: EnemyStats(EnemySprite.Chainchomp, False, False), EnemySprite.TrinexxRockHead: EnemyStats(EnemySprite.TrinexxRockHead, True), EnemySprite.TrinexxFireHead: EnemyStats(EnemySprite.TrinexxFireHead, True), EnemySprite.TrinexxIceHead: EnemyStats(EnemySprite.TrinexxIceHead, True), EnemySprite.Blind: EnemyStats(EnemySprite.Blind, True), - EnemySprite.Swamola: EnemyStats(EnemySprite.Swamola, False, True, 0), - EnemySprite.Lynel: EnemyStats(EnemySprite.Lynel, False, True, 7), + EnemySprite.Swamola: EnemyStats(EnemySprite.Swamola, False, True, 0, health=16), + EnemySprite.Lynel: EnemyStats(EnemySprite.Lynel, False, True, 7, health=24), EnemySprite.BunnyBeam: EnemyStats(EnemySprite.BunnyBeam, False, False), # todo: medallions can kill bunny beams? EnemySprite.FloppingFish: EnemyStats(EnemySprite.FloppingFish, True), - EnemySprite.Stal: EnemyStats(EnemySprite.Stal, False, True, 1), + EnemySprite.Stal: EnemyStats(EnemySprite.Stal, False, True, 1, health=4), EnemySprite.Ganon: EnemyStats(EnemySprite.Ganon, True), EnemySprite.Faerie: EnemyStats(EnemySprite.Faerie, True), @@ -497,8 +509,6 @@ class Sprite(object): # map of super_tile to list of Sprite objects: vanilla_sprites = {} -enemy_stats = {} - def create_sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, region=None, drops_item=False, drop_item_kind=None): @@ -1984,8 +1994,6 @@ def init_vanilla_sprites(): create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x08, 0x16) create_sprite(0x0126, EnemySprite.HeartPiece, 0x00, 0, 0x1c, 0x14) create_sprite(0x0127, EnemySprite.HeartPiece, 0x00, 0, 0x07, 0x16) - global enemy_stats - enemy_stats = init_enemy_stats() def kill_rules(world, player, stats): @@ -2015,6 +2023,7 @@ def kill_rules(world, player, stats): EnemySprite.GreenKnifeGuard: defeat_rule(world, player, h(EnemySprite.GreenKnifeGuard)), EnemySprite.Popo: defeat_rule(world, player, h(EnemySprite.Popo), hook=True), EnemySprite.Popo2: defeat_rule(world, player, h(EnemySprite.Popo2), hook=True), + EnemySprite.DebirandoPit: defeat_rule(world, player, h(EnemySprite.Debirando), fire=1, ice=1, boomerang=True), EnemySprite.Debirando: defeat_rule(world, player, h(EnemySprite.Debirando), fire=1, ice=1, boomerang=True), EnemySprite.BallNChain: defeat_rule(world, player, h(EnemySprite.BallNChain)), EnemySprite.CannonTrooper: defeat_rule(world, player, h(EnemySprite.CannonTrooper), fire=1, ice=1), @@ -2101,8 +2110,8 @@ def valid_drop_location(sprite, world, player): if sprite.drops_item and sprite.drop_item_kind == 0xe4: # already has a location return False - else: - stat = enemy_stats[sprite.kind] + elif sprite.sub_type != SpriteType.Overlord: + stat = world.data_tables[player].enemy_stats[sprite.kind] return not stat.static and stat.drop_flag @@ -2120,6 +2129,7 @@ def create_drop_location(sprite, index, super_tile, world, player): drop_location.drop = sprite sprite.location = drop_location drop_location.type = LocationType.Drop + parent.locations.append(drop_location) # todo: placeholder address @@ -2149,12 +2159,12 @@ prize_pack_selector = { def add_drop_contents(world, player): retro_bow = world.bow_mode[player].startswith('retro') index_selector = [0]*8 - for super_tile, enemy_list in world.enemy_list[player].room_map.items(): + for super_tile, enemy_list in world.data_tables[player].uw_enemy_table.room_map.items(): for sprite in enemy_list: if sprite.drops_item and sprite.drop_item_kind == 0xe4: continue - else: - stat = enemy_stats[sprite.kind] + elif sprite.sub_type != SpriteType.Overlord: + stat = world.data_tables[player].enemy_stats[sprite.kind] if not stat.static and stat.drop_flag: pack = 0 if isinstance(stat.prize_pack, int): @@ -2283,10 +2293,10 @@ def defeat_rule(world, player, health, class1=1, else: rules.append(can_fire_rod_kill(world, player, fire_rod_damage[fire], health)) if ice is not None: - rules.append(can_ice_rod_kill(world, player, ice_rod_damage[fire], health)) + rules.append(can_ice_rod_kill(world, player, ice_rod_damage[ice], health)) if boomerang: rules.append(has_boomerang(player)) - return or_rule(rules) + return or_rule(*rules) def has_blunt_weapon(player): @@ -2298,7 +2308,7 @@ def find_shops_that_sell(item, world, player): def can_shoot_arrows(world, player): - if world.retro[player]: + if world.bow_mode[player].startswith('retro'): # todo: Non-progressive silvers grant wooden arrows, but progressive bows do not. # Always require shop arrows to be safe shops = find_shops_that_sell('Single Arrow', world, player) @@ -2309,7 +2319,7 @@ def can_shoot_arrows(world, player): def can_use_bombs(world, player): - return or_rule(RuleFactory.static_rule(not world.bombag[player]), has('Bomb Upgrade (+10)', player)) + return or_rule(RuleFactory.static_rule(not world.bombbag[player]), has('Bomb Upgrade (+10)', player)) enemy_names = { 0x00: 'Raven', diff --git a/source/gui/bottom.py b/source/gui/bottom.py index 27a22217..d8764428 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -282,7 +282,7 @@ def create_guiargs(parent): # Key drop shuffle stuff if guiargs.keydropshuffle: - guiargs.dropshuffle = 1 + guiargs.dropshuffle = 'keys' if guiargs.dropshuffle == 'none' else guiargs.dropshuffle guiargs.pottery = 'keys' if guiargs.pottery == 'none' else guiargs.pottery if guiargs.retro or guiargs.mode == 'retro': diff --git a/source/item/FillUtil.py b/source/item/FillUtil.py index 82581a76..caaebba7 100644 --- a/source/item/FillUtil.py +++ b/source/item/FillUtil.py @@ -2,7 +2,6 @@ import RaceRandom as random import logging from collections import defaultdict -from source.dungeon.EnemyList import enemy_stats from source.item.District import resolve_districts from BaseClasses import PotItem, PotFlags from DoorShuffle import validate_vanilla_reservation @@ -66,7 +65,7 @@ def create_item_pool_config(world): for player in range(1, world.players + 1): config.static_placement[player] = defaultdict(list) config.static_placement[player].update(vanilla_mapping) - if world.dropshuffle[player]: + if world.dropshuffle[player] != 'none': for item, locs in keydrop_vanilla_mapping.items(): config.static_placement[player][item].extend(locs) if world.pottery[player] not in ['none', 'cave']: @@ -92,7 +91,7 @@ def create_item_pool_config(world): for item, locs in vanilla_mapping.items(): if 'Small Key' in item: universal_key_locations.extend(locs) - if world.dropshuffle[player]: + if world.dropshuffle[player] != 'none': for item, locs in keydrop_vanilla_mapping.items(): if 'Small Key' in item: universal_key_locations.extend(locs) diff --git a/source/logic/Rule.py b/source/logic/Rule.py index 8529ce2b..c4ab4832 100644 --- a/source/logic/Rule.py +++ b/source/logic/Rule.py @@ -108,6 +108,8 @@ class RuleFactory(object): rule.sub_rules.extend(r.sub_rules) # todo: this extension for the lambda calc elif r.rule_type == RuleType.Static and r.principal: # remove static flag if unnecessary continue + elif r.rule_type == RuleType.Static and not r.principal: # always evaluates to false + return r else: rule.sub_rules.append(r) if not rule_lambda: @@ -128,6 +130,8 @@ class RuleFactory(object): rule.sub_rules.extend(r.sub_rules) # todo: this extension for the lambda calc elif r.rule_type == RuleType.Static and not r.principal: # remove static flag if unnecessary continue + elif r.rule_type == RuleType.Static and r.principal: # always evaluates to true + return r else: rule.sub_rules.append(r) if not rule_lambda: diff --git a/source/rom/DataTables.py b/source/rom/DataTables.py index ab946c00..136c9b29 100644 --- a/source/rom/DataTables.py +++ b/source/rom/DataTables.py @@ -1,6 +1,6 @@ from Utils import snes_to_pc, int24_as_bytes, int16_as_bytes -from source.dungeon.EnemyList import EnemyTable, init_vanilla_sprites, vanilla_sprites +from source.dungeon.EnemyList import EnemyTable, init_vanilla_sprites, vanilla_sprites, init_enemy_stats from source.dungeon.RoomHeader import init_room_headers from source.dungeon.RoomList import Room0127 from source.enemizer.SpriteSheets import init_sprite_sheets, init_sprite_requirements @@ -17,6 +17,7 @@ class DataTables: # associated data self.sprite_requirements = None + self.enemy_stats = None def write_to_rom(self, rom, colorize_pots=False): if self.pot_secret_table.size() > 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'