From a0b781521fd51c3ee4c9447e0ad7a72fb593fb61 Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 30 Mar 2023 16:12:46 -0600 Subject: [PATCH] Big enemizer updates Fix sheets Health + damage rando Logical kill rooms --- BaseClasses.py | 18 + Gui.py | 8 +- ItemList.py | 1 + Main.py | 9 +- PotShuffle.py | 6 +- Rom.py | 8 +- Rules.py | 259 +++--- Utils.py | 14 + data/base2current.bps | Bin 99391 -> 99399 bytes source/dungeon/EnemyList.py | 999 +++++++++++------------- source/enemizer/Bossmizer.py | 1 - source/enemizer/DamageTables.py | 8 + source/enemizer/Enemizer.py | 261 ++++--- source/enemizer/EnemizerTestHarness.py | 100 ++- source/enemizer/EnemyLogic.py | 494 ++++++++++++ source/enemizer/OwEnemyList.py | 178 +++-- source/enemizer/SpriteSheets.py | 154 ++-- source/enemizer/enemy_damage_table.yaml | 10 + source/enemizer/enemy_weight.yaml | 198 +++++ source/enemizer/uw_enemy_deny.yaml | 354 +++++++++ source/logic/Rule.py | 4 + source/rom/DataTables.py | 87 ++- 22 files changed, 2224 insertions(+), 947 deletions(-) create mode 100644 source/enemizer/EnemyLogic.py create mode 100644 source/enemizer/enemy_damage_table.yaml create mode 100644 source/enemizer/enemy_weight.yaml create mode 100644 source/enemizer/uw_enemy_deny.yaml diff --git a/BaseClasses.py b/BaseClasses.py index 1bc5ba16..3071456c 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -90,6 +90,7 @@ class World(object): self.sanc_portal = {} self.fish = BabelFish() self.data_tables = {} + self.damage_table = {} for player in range(1, players + 1): def set_player_attr(attr, val): @@ -1470,6 +1471,7 @@ class Entrance(object): self.recursion_count = 0 self.vanilla = None self.access_rule = lambda state: True + self.verbose_rule = None self.player = player self.door = None self.hide_path = False @@ -2780,6 +2782,22 @@ class Spoiler(object): outfile.write(f'\n\nBosses ({self.world.get_player_names(player)}):\n\n') outfile.write('\n'.join([f'{x}: {y}' for x, y in bossmap.items() if y not in ['Agahnim', 'Agahnim 2', 'Ganon']])) + def extras(self, filename): + # todo: conditional on enemy shuffle mode + with open(filename, 'a') as outfile: + outfile.write('\n\nOverworld Enemies:\n\n') + for player in range(1, self.world.players + 1): + player_tag = ' '+self.world.get_player_names(player) if self.world.players > 1 else '' + for area, sprite_list in self.world.data_tables[player].ow_enemy_table.items(): + for idx, sprite in enumerate(sprite_list): + outfile.write(f'{hex(area)} Enemy #{idx+1}{player_tag}: {str(sprite)}\n') + outfile.write('\n\nUnderworld Enemies:\n\n') + for player in range(1, self.world.players + 1): + player_tag = ' '+self.world.get_player_names(player) if self.world.players > 1 else '' + for area, sprite_list in self.world.data_tables[player].uw_enemy_table.room_map.items(): + for idx, sprite in enumerate(sprite_list): + outfile.write(f'{hex(area)} Enemy #{idx+1}{player_tag}: {str(sprite)}\n') + def playthrough_to_file(self, filename): with open(filename, 'a') as outfile: # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name diff --git a/Gui.py b/Gui.py index d8ebf356..f11db045 100755 --- a/Gui.py +++ b/Gui.py @@ -137,14 +137,14 @@ def guiMain(args=None): self.pages["randomizer"].pages["entrance"] = entrando_page(self.pages["randomizer"].notebook) self.pages["randomizer"].notebook.add(self.pages["randomizer"].pages["entrance"], text="Entrances") + # Dungeon Shuffle + self.pages["randomizer"].pages["dungeon"] = dungeon_page(self.pages["randomizer"].notebook) + self.pages["randomizer"].notebook.add(self.pages["randomizer"].pages["dungeon"], text="Dungeons") + # Enemizer self.pages["randomizer"].pages["enemizer"],self.settings = enemizer_page(self.pages["randomizer"].notebook,self.settings) self.pages["randomizer"].notebook.add(self.pages["randomizer"].pages["enemizer"], text="Enemizer") - # Dungeon Shuffle - self.pages["randomizer"].pages["dungeon"] = dungeon_page(self.pages["randomizer"].notebook) - self.pages["randomizer"].notebook.add(self.pages["randomizer"].pages["dungeon"], text="Dungeon Shuffle") - # Multiworld # self.pages["randomizer"].pages["multiworld"],self.settings = multiworld_page(self.pages["randomizer"].notebook,self.settings) # self.pages["randomizer"].notebook.add(self.pages["randomizer"].pages["multiworld"], text="Multiworld") diff --git a/ItemList.py b/ItemList.py index 30e2183a..f6733c4c 100644 --- a/ItemList.py +++ b/ItemList.py @@ -633,6 +633,7 @@ def set_up_shops(world, player): else: cap_shop = world.get_region('Capacity Upgrade', player).shop cap_shop.inventory[0] = cap_shop.inventory[1] # remove bomb capacity upgrades in bombbag + cap_shop.inventory[1] = None def customize_shops(world, player): diff --git a/Main.py b/Main.py index 502afa42..6678dc52 100644 --- a/Main.py +++ b/Main.py @@ -17,7 +17,7 @@ from PotShuffle import shuffle_pots, shuffle_pot_switches from Regions import create_regions, create_shops, mark_light_world_regions, create_dungeon_regions, adjust_locations from InvertedRegions import create_inverted_regions, mark_dark_world_regions from EntranceShuffle import link_entrances, link_inverted_entrances -from Rom import patch_rom, patch_race_rom, patch_enemizer, apply_rom_settings, LocalRom, JsonRom, get_hash_string +from Rom import patch_rom, patch_race_rom, apply_rom_settings, LocalRom, JsonRom, get_hash_string from Doors import create_doors from DoorShuffle import link_doors, connect_portal, link_doors_prep from RoomData import create_rooms @@ -33,6 +33,8 @@ from source.item.FillUtil import create_item_pool_config, massage_item_pool, dis from source.overworld.EntranceShuffle2 import link_entrances_new from source.tools.BPS import create_bps_from_data from source.classes.CustomSettings import CustomSettings +from source.enemizer.DamageTables import DamageTable +from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables @@ -191,7 +193,9 @@ def main(args, seed=None, fish=None): create_doors(world, player) create_rooms(world, player) create_dungeons(world, player) + world.damage_table[player] = DamageTable() world.data_tables[player] = init_data_tables(world, player) + randomize_enemies(world, player) adjust_locations(world, player) place_bosses(world, player) @@ -378,6 +382,8 @@ def main(args, seed=None, fish=None): if args.create_spoiler and not args.jsonout: logger.info(world.fish.translate("cli", "cli", "patching.spoiler")) world.spoiler.to_file(output_path(f'{outfilebase}_Spoiler.txt')) + if args.loglevel == 'debug': + world.spoiler.extras(output_path(f'{outfilebase}_Spoiler.txt')) if not args.skip_playthrough: logger.info(world.fish.translate("cli","cli","calc.playthrough")) @@ -466,6 +472,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.damage_table = world.damage_table ret.data_tables = world.data_tables # can be changed... for player in range(1, world.players + 1): diff --git a/PotShuffle.py b/PotShuffle.py index c1794e94..94b4e0ed 100644 --- a/PotShuffle.py +++ b/PotShuffle.py @@ -1016,16 +1016,16 @@ key_drop_data = { 'Swamp Palace - Trench 2 Pot Key': ['Pot', 0x35, 'in a pot in Swamp Palace', 'Small Key (Swamp Palace)'], 'Swamp Palace - Waterway Pot Key': ['Pot', 0x16, 'in a pot in Swamp Palace', 'Small Key (Swamp Palace)'], 'Skull Woods - West Lobby Pot Key': ['Pot', 0x56, 'in a pot in Skull Woods', 'Small Key (Skull Woods)'], - 'Skull Woods - Spike Corner Key Drop': ['Drop', (0x09DD74, 0x39, 1), 'dropped near Mothula', 'Small Key (Skull Woods)'], + 'Skull Woods - Spike Corner Key Drop': ['Drop', (0x09DD74, 0x39, 2), 'dropped near Mothula', 'Small Key (Skull Woods)'], "Thieves' Town - Hallway Pot Key": ['Pot', 0xBC, "in a pot in Thieves Town", 'Small Key (Thieves Town)'], "Thieves' Town - Spike Switch Pot Key": ['Pot', 0xAB, "in a pot in Thieves Town", 'Small Key (Thieves Town)'], 'Ice Palace - Jelly Key Drop': ['Drop', (0x09DA21, 0xE, 3), 'dropped in Ice Palace', 'Small Key (Ice Palace)'], - 'Ice Palace - Conveyor Key Drop': ['Drop', (0x09DE08, 0x3E, 8), 'dropped in Ice Palace', 'Small Key (Ice Palace)'], + 'Ice Palace - Conveyor Key Drop': ['Drop', (0x09DE08, 0x3E, 9), 'dropped in Ice Palace', 'Small Key (Ice Palace)'], 'Ice Palace - Hammer Block Key Drop': ['Pot', 0x3F, 'under a block in Ice Palace', 'Small Key (Ice Palace)'], 'Ice Palace - Many Pots Pot Key': ['Pot', 0x9F, 'int a pot in Ice Palace', 'Small Key (Ice Palace)'], 'Misery Mire - Spikes Pot Key': ['Pot', 0xB3, 'in a pot in Misery Mire', 'Small Key (Misery Mire)'], 'Misery Mire - Fishbone Pot Key': ['Pot', 0xA1, 'in a pot in forgotten Mire', 'Small Key (Misery Mire)'], - 'Misery Mire - Conveyor Crystal Key Drop': ['Drop', (0x09E7FB, 0xC1, 9), 'dropped in Misery Mire', 'Small Key (Misery Mire)'], + 'Misery Mire - Conveyor Crystal Key Drop': ['Drop', (0x09E7FB, 0xC1, 10), 'dropped in Misery Mire', 'Small Key (Misery Mire)'], 'Turtle Rock - Pokey 1 Key Drop': ['Drop', (0x09E70D, 0xB6, 5), 'dropped in Turtle Rock', 'Small Key (Turtle Rock)'], 'Turtle Rock - Pokey 2 Key Drop': ['Drop', (0x09DA5D, 0x13, 6), 'dropped in Turtle Rock', 'Small Key (Turtle Rock)'], 'Ganons Tower - Conveyor Cross Pot Key': ['Pot', 0x8B, "in a pot in Ganon's Tower", 'Small Key (Ganons Tower)'], diff --git a/Rom.py b/Rom.py index 306c7b24..4226509e 100644 --- a/Rom.py +++ b/Rom.py @@ -39,7 +39,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '09685acbd19f0f8a9061c1ead16747d2' +RANDOMIZERBASEHASH = 'dbb03d2c4e0cd59b3ce36a110a57fe55' class JsonRom(object): @@ -1550,6 +1550,9 @@ def patch_rom(world, rom, player, team, is_mystery=False): rom.write_byte(0xFED31, 0x0E) # preopen bombable exit rom.write_byte(0xFEE41, 0x0E) # preopen bombable exit + if world.boss_shuffle[player] != 'none' or world.doorShuffle[player] != 'vanilla': + rom.write_byte(snes_to_pc(0x30835A), 1) # fix Prize On The Eyes + if world.boss_shuffle[player] != 'none': boss_writes(world, player, rom) write_enemy_shuffle_settings(world, player, rom) @@ -1566,7 +1569,7 @@ def patch_rom(world, rom, player, team, is_mystery=False): if world.data_tables[player]: colorize_pots = (world.pottery[player] != 'vanilla', 'lottery' and (world.colorizepots[player] or world.pottery[player] in ['reduced', 'clustered'])) - world.data_tables[player].write_to_rom(rom, colorize_pots) + world.data_tables[player].write_to_rom(rom, colorize_pots, world.enemy_shuffle[player] == 'random') write_enemizer_tweaks(rom, world, player) write_strings(rom, world, player, team) @@ -1663,6 +1666,7 @@ def write_enemizer_tweaks(rom, world, player): rom.write_byte(snes_to_pc(0x1DF6D8), 0) # lets enemies walk on water instead of clipping into infinity? rom.write_byte(snes_to_pc(0x0DB6B3), 0x82) # hovers don't need water necessarily? + def hud_format_text(text): output = bytes() for char in text.lower(): diff --git a/Rules.py b/Rules.py index 0b2e0447..0a970a67 100644 --- a/Rules.py +++ b/Rules.py @@ -9,7 +9,10 @@ from Dungeons import dungeon_table from RoomData import DoorKind from OverworldGlitchRules import overworld_glitches_rules -from source.dungeon.EnemyList import kill_rules, special_rules_check, special_rules_for_region +from source.logic.Rule import RuleFactory +from source.dungeon.EnemyList import EnemySprite, Sprite +from source.enemizer.EnemyLogic import special_rules_check, special_rules_for_region, defeat_rule_single +from source.enemizer.EnemyLogic import defeat_rule_multiple, and_rule as and_rule_new, or_rule as or_rule_new def set_rules(world, player): @@ -38,6 +41,7 @@ def set_rules(world, player): bomb_rules(world, player) pot_rules(world, player) drop_rules(world, player) + challenge_room_rules(world, player) if world.logic[player] == 'noglitches': no_glitches_rules(world, player) @@ -112,9 +116,19 @@ def set_defeat_dungeon_boss_rule(location): # Lambda required to defer evaluation of dungeon.boss since it will change later if boos shuffle is used set_rule(location, lambda state: location.parent_region.dungeon.boss.can_defeat(state)) + def set_always_allow(spot, rule): spot.always_allow = rule + +def add_rule_new(spot, rule, combine='and'): + if combine == 'and': + spot.verbose_rule = and_rule_new(*[spot.verbose_rule, rule]) + else: + spot.verbose_rule = or_rule_new(*[spot.verbose_rule, rule]) + add_rule(spot, rule.rule_lambda, combine) + + def add_rule(spot, rule, combine='and'): old_rule = spot.access_rule if combine == 'or': @@ -201,8 +215,8 @@ def global_rules(world, player): # Eastern Palace # Eyegore room needs a bow - set_rule(world.get_entrance('Eastern Duo Eyegores NE', player), lambda state: state.can_shoot_arrows(player)) - set_rule(world.get_entrance('Eastern Single Eyegore NE', player), lambda state: state.can_shoot_arrows(player)) + # set_rule(world.get_entrance('Eastern Duo Eyegores NE', player), lambda state: state.can_shoot_arrows(player)) + # set_rule(world.get_entrance('Eastern Single Eyegore NE', player), lambda state: state.can_shoot_arrows(player)) set_rule(world.get_entrance('Eastern Map Balcony Hook Path', player), lambda state: state.has('Hookshot', player)) # Boss rules. Same as below but no BK or arrow requirement. @@ -222,19 +236,12 @@ def global_rules(world, player): set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Prize', player)) # Castle Tower - set_rule(world.get_entrance('Tower Gold Knights SW', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('Tower Gold Knights EN', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('Tower Dark Archers WN', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('Tower Red Spears WN', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('Tower Red Guards EN', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('Tower Red Guards SW', player), lambda state: state.can_kill_most_things(player)) set_rule(world.get_entrance('Tower Altar NW', player), lambda state: state.has_sword(player)) set_defeat_dungeon_boss_rule(world.get_location('Agahnim 1', player)) - set_rule(world.get_entrance('PoD Arena Landing Bonk Path', player), lambda state: state.has_Boots(player)) - set_rule(world.get_entrance('PoD Mimics 1 NW', player), lambda state: state.can_shoot_arrows(player)) - set_rule(world.get_entrance('PoD Mimics 2 NW', player), lambda state: state.can_shoot_arrows(player)) + # set_rule(world.get_entrance('PoD Mimics 1 NW', player), lambda state: state.can_shoot_arrows(player)) + # set_rule(world.get_entrance('PoD Mimics 2 NW', player), lambda state: state.can_shoot_arrows(player)) set_rule(world.get_entrance('PoD Bow Statue Down Ladder', player), lambda state: state.can_shoot_arrows(player)) set_rule(world.get_entrance('PoD Map Balcony Drop Down', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('PoD Dark Pegs Landing to Right', player), lambda state: state.has('Hammer', player)) @@ -413,19 +420,6 @@ def global_rules(world, player): set_rule(world.get_entrance('GT Mimics 2 NE', player), lambda state: state.can_shoot_arrows(player)) # consider access to refill room - interior doors would need a change set_rule(world.get_entrance('GT Cannonball Bridge SE', player), lambda state: state.has_Boots(player)) - set_rule(world.get_entrance('GT Gauntlet 1 WN', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Gauntlet 2 EN', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Gauntlet 2 SW', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Gauntlet 3 NW', player), lambda state: state.can_kill_most_things(player)) - if not world.get_door('GT Gauntlet 3 SW', player).entranceFlag: - set_rule(world.get_entrance('GT Gauntlet 3 SW', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Gauntlet 4 NW', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Gauntlet 4 SW', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Gauntlet 5 NW', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Gauntlet 5 WS', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Wizzrobes 1 SW', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Wizzrobes 2 SE', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Wizzrobes 2 NE', player), lambda state: state.can_kill_most_things(player)) set_rule(world.get_entrance('GT Lanmolas 2 ES', player), lambda state: world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].can_defeat(state)) set_rule(world.get_entrance('GT Lanmolas 2 NW', player), lambda state: world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].can_defeat(state)) set_rule(world.get_entrance('GT Torch Cross ES', player), lambda state: state.has_fire_source(player)) @@ -656,65 +650,12 @@ def bomb_rules(world, player): for location in bonkable_items: add_rule(world.get_location(location, player), lambda state: state.can_use_bombs(player) or state.has_Boots(player)) for location in bombable_items: - add_rule(world.get_location(location, player), lambda state: state.can_use_bombs(player)) - - cave_kill_locations = ['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Left', 'Mini Moldorm Cave - Right', 'Mini Moldorm Cave - Generous Guy', 'Spiral Cave'] - for location in cave_kill_locations: - add_rule(world.get_location(location, player), lambda state: state.can_kill_most_things(player) or state.can_use_bombs(player)) - add_rule(world.get_entrance('Spiral Cave (top to bottom)', player), lambda state: state.can_kill_most_things(player) or state.can_use_bombs(player)) + add_rule(world.get_location(location, player), lambda state: state.can_use_bombs(player)) paradox_switch_chests = ['Paradox Cave Lower - Far Left', 'Paradox Cave Lower - Left', 'Paradox Cave Lower - Right', 'Paradox Cave Lower - Far Right', 'Paradox Cave Lower - Middle'] for location in paradox_switch_chests: add_rule(world.get_location(location, player), lambda state: state.can_hit_crystal_through_barrier(player)) - # Dungeon bomb logic - easy_kill_rooms = [ # Door, bool-bombable - ('Hyrule Dungeon Armory S', True), # One green guard - ('Hyrule Dungeon Armory ES', True), # One green guard - ('Hyrule Dungeon Armory Boomerang WS', True), # One blue guard - ('Desert Compass NE', True), # Three popos - ('Desert Four Statues NW', True), # Four popos - ('Desert Four Statues ES', True), # Four popos - ('Hera Beetles WS', False), # Three blue beetles and only two pots, and bombs don't work. - ('Thieves Basement Block WN', True), # One blue and one red zazak and one Stalfos. Two pots. Need to kill the third enemy somehow. - ('Ice Pengator Trap NE', False), # Five pengators. Bomb-doable? - ('TR Twin Pokeys EN', False), # Two pokeys - ('TR Twin Pokeys SW', False), # Two pokeys - ('GT Petting Zoo SE', False), # Dont make anyone do this room with bombs and/or pots. - ('GT DMs Room SW', False) # Four red stalfos - ] - for killdoor,bombable in easy_kill_rooms: - if bombable: - add_rule(world.get_entrance(killdoor, player), lambda state: (state.can_use_bombs(player) or state.can_kill_most_things(player))) - else: - add_rule(world.get_entrance(killdoor, player), lambda state: state.can_kill_most_things(player)) - add_rule(world.get_entrance('Ice Stalfos Hint SE', player), lambda state: state.can_use_bombs(player)) # Need bombs for big stalfos knights - add_rule(world.get_entrance('Mire Cross ES', player), lambda state: state.can_kill_most_things(player)) # 4 Sluggulas. Bombs don't work // or (state.can_use_bombs(player) and state.has('Magic Powder'), player) - - enemy_kill_drops = [ # Location, bool-bombable - ('Hyrule Castle - Map Guard Key Drop', True), - ('Hyrule Castle - Boomerang Guard Key Drop', True), - ('Hyrule Castle - Key Rat Key Drop', True), -# ('Hyrule Castle - Big Key Drop', True), # Pots are available -# ('Eastern Palace - Dark Eyegore Key Drop', True), # Pots are available - ('Castle Tower - Dark Archer Key Drop', True), -# ('Castle Tower - Circle of Pots Key Drop', True), # Pots are available -# ('Skull Woods - Spike Corner Key Drop', True), # Pots are available - ('Ice Palace - Jelly Key Drop', True), - ('Ice Palace - Conveyor Key Drop', True), - ('Misery Mire - Conveyor Crystal Key Drop', True), - ('Turtle Rock - Pokey 1 Key Drop', True), - ('Turtle Rock - Pokey 2 Key Drop', True), -# ('Ganons Tower - Mini Helmasaur Key Drop', True) # Pots are available - ('Castle Tower - Room 03', True), # Two spring soliders - ('Ice Palace - Compass Chest', True) # Pengators - ] - for location,bombable in enemy_kill_drops: - if bombable: - add_rule(world.get_location(location, player), lambda state: state.can_use_bombs(player) or state.can_kill_most_things(player)) - else: - add_rule(world.get_location(location, player), lambda state: state.can_kill_most_things(player)) - add_rule(world.get_location('Attic Cracked Floor', player), lambda state: state.can_use_bombs(player)) bombable_floors = ['PoD Pit Room Bomb Hole', 'Ice Bomb Drop Hole', 'Ice Freezors Bomb Hole', 'GT Bob\'s Room Hole'] for entrance in bombable_floors: @@ -751,6 +692,45 @@ def bomb_rules(world, player): add_rule(door.entrance, lambda state: state.can_use_bombs(player)) +def challenge_room_rules(world, player): + room_map = world.data_tables[player].uw_enemy_table.room_map + stats = world.data_tables[player].enemy_stats + for region, data in std_kill_rooms.items(): + entrances, room_id, enemy_list = data + rule = get_challenge_rule(world, player, room_map, stats, room_id, enemy_list, region) + for ent in entrances: + entrance = world.get_entrance(ent, player) + if not entrance.door or not entrance.door.entranceFlag: + add_rule_new(world.get_entrance(ent, player), rule) + for region, data in kill_chests.items(): + locations, room_id, enemy_list = data + rule = get_challenge_rule(world, player, room_map, stats, room_id, enemy_list, region) + for loc in locations: + add_rule_new(world.get_location(loc, player), rule) + + +def get_challenge_rule(world, player, room_map, stats, room_id, enemy_list, region): + sprite_list = room_map[room_id] + sprite_region_pairs = [] + for idx, sprite in enumerate(sprite_list): + if idx in enemy_list: + if not stats[sprite.kind].ignore_for_kill_room: + sprite_region_pairs.append((sprite, world.get_region(sprite.region, player))) + if region == 'Eastern Stalfos Spawn': + stalfos_spawn_exception(sprite_region_pairs, stats, world, player) + if sprite_region_pairs: + return defeat_rule_multiple(world, player, sprite_region_pairs) + return RuleFactory.static_rule(True) + + +def stalfos_spawn_exception(sprite_region_pairs, stats, world, player): + if stats[EnemySprite.Stalfos].health * 4 > 40: + for x in range(0, 4): + sprite_region_pairs.append((Sprite(0x00a8, EnemySprite.Stalfos, 0, 0, 0, 0, 'Eastern Stalfos Spawn'), + world.get_region('Eastern Stalfos Spawn', player))) + return sprite_region_pairs + + def pot_rules(world, player): if world.pottery[player] != 'none': blocks = [l for l in world.get_locations() if l.type == LocationType.Pot and l.pot.flags & PotFlags.Block] @@ -798,17 +778,14 @@ def pot_rules(world, 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] + rule = defeat_rule_single(world, player, enemy, enemy.location.parent_region) if enemy.location.parent_region.name in special_rules_check: - verbose_rule = special_rules_for_region(world, player, enemy.location.parent_region.name, - enemy.location, verbose_rule, enemy) - enemy.location.verbose_rule = verbose_rule - add_rule(enemy.location, verbose_rule.rule_lambda) + rule = special_rules_for_region(world, player, enemy.location.parent_region.name, + enemy.location, rule) + add_rule_new(enemy.location, rule) def default_rules(world, player): @@ -1256,34 +1233,88 @@ def swordless_rules(world, player): std_kill_rooms = { - 'Hyrule Dungeon Armory Main': ['Hyrule Dungeon Armory S', 'Hyrule Dungeon Armory ES'], # One green guard - 'Hyrule Dungeon Armory Boomerang': ['Hyrule Dungeon Armory Boomerang WS'], # One blue guard - 'Eastern Stalfos Spawn': ['Eastern Stalfos Spawn ES', 'Eastern Stalfos Spawn NW'], # Can use pots - 'Desert Compass Room': ['Desert Compass NE'], # Three popos - 'Desert Four Statues': ['Desert Four Statues NW', 'Desert Four Statues ES'], # Four popos - 'Hera Beetles': ['Hera Beetles WS'], # Three blue beetles and only two pots, and bombs don't work. - 'Tower Gold Knights': ['Tower Gold Knights SW', 'Tower Gold Knights EN'], # Two ball and chain - 'Tower Dark Archers': ['Tower Dark Archers WN'], # Not a kill room - 'Tower Red Spears': ['Tower Red Spears WN'], # Two spear soldiers - 'Tower Red Guards': ['Tower Red Guards EN', 'Tower Red Guards SW'], # Two usain bolts - 'Tower Circle of Pots': ['Tower Circle of Pots NW'], # Two spear soldiers. Plenty of pots. - 'PoD Turtle Party': ['PoD Turtle Party ES', 'PoD Turtle Party NW'], # Lots of turtles. - 'Thieves Basement Block': ['Thieves Basement Block WN'], # One blue and one red zazak and one Stalfos. Two pots. Need weapon. - 'Ice Stalfos Hint': ['Ice Stalfos Hint SE'], # Need bombs for big stalfos knights - 'Ice Pengator Trap': ['Ice Pengator Trap NE'], # Five pengators. Bomb-doable? - 'Mire 2': ['Mire 2 NE'], # Wizzrobes. Bombs dont work. - 'Mire Cross': ['Mire Cross ES'], # 4 Sluggulas. Bombs don't work - 'TR Twin Pokeys': ['TR Twin Pokeys EN', 'TR Twin Pokeys SW'], # Two pokeys - 'GT Petting Zoo': ['GT Petting Zoo SE'], # Dont make anyone do this room with bombs. - 'GT DMs Room': ['GT DMs Room SW'], # Four red stalfos - 'GT Gauntlet 1': ['GT Gauntlet 1 WN'], # Stalfos/zazaks - 'GT Gauntlet 2': ['GT Gauntlet 2 EN', 'GT Gauntlet 2 SW'], # Red stalfos - 'GT Gauntlet 3': ['GT Gauntlet 3 NW', 'GT Gauntlet 3 SW'], # Blue zazaks - 'GT Gauntlet 4': ['GT Gauntlet 4 NW', 'GT Gauntlet 4 SW'], # Red zazaks - 'GT Gauntlet 5': ['GT Gauntlet 5 NW', 'GT Gauntlet 5 WS'], # Stalfos and zazak - 'GT Wizzrobes 1': ['GT Wizzrobes 1 SW'], # Wizzrobes. Bombs don't work - 'GT Wizzrobes 2': ['GT Wizzrobes 2 SE', 'GT Wizzrobes 2 NE'] # Wizzrobes. Bombs don't work -} # all trap rooms? + 'Hyrule Dungeon Armory Main': # One green guard + (['Hyrule Dungeon Armory S', 'Hyrule Dungeon Armory ES'], 0x71, [0]), + 'Hyrule Dungeon Armory Boomerang': # One blue guard + (['Hyrule Dungeon Armory Boomerang WS'], 0x71, [1]), + 'Eastern Stalfos Spawn': # Can use pots up to a point see stalfos_spawn_exception + (['Eastern Stalfos Spawn ES', 'Eastern Stalfos Spawn NW'], 0xa8, []), + 'Eastern Single Eyegore': + (['Eastern Single Eyegore NE'], 0xd8, [8, 9, 10]), + 'Eastern Duo Eyegores': + (['Eastern Duo Eyegores NE'], 0xd8, [0, 1, 2, 3, 4, 5, 6, 7]), + 'Desert Compass Room': # Three popos (beamos) + (['Desert Compass NE'], 0x085, [2, 3, 4, 5]), + 'Desert Four Statues': # Four popos (beamos) + (['Desert Four Statues NW', 'Desert Four Statues ES'], 0x53, [5, 6, 8, 9, 10]), + 'Hera Beetles': # Three blue beetles and only two pots, and bombs don't work. + (['Hera Beetles WS'], 0x31, [7, 8, 10]), + 'Tower Gold Knights': # Two ball and chain + (['Tower Gold Knights SW', 'Tower Gold Knights EN'], 0xe0, [0, 1]), + 'Tower Dark Archers': # Backwards kill room + (['Tower Dark Archers WN'], 0xc0, [0, 1, 3]), + 'Tower Red Spears': # Two spear soldiers + (['Tower Red Spears WN'], 0xb0, [1, 2, 3, 4]), + 'Tower Red Guards': # Two usain bolts + (['Tower Red Guards EN', 'Tower Red Guards SW'], 0xb0, [0, 5]), + 'Tower Circle of Pots': # Two spear soldiers. Plenty of pots. + (['Tower Circle of Pots NW'], 0xb0, [7, 8, 9, 10]), + 'PoD Mimics 1': + (['PoD Mimics 1 NW'], 0x4b, [0, 3, 4]), + 'PoD Mimics 2': + (['PoD Mimics 2 NW'], 0x1b, [3, 4, 5]), + 'PoD Turtle Party': # Lots of turtles. + (['PoD Turtle Party ES', 'PoD Turtle Party NW'], 0x0b, [4, 5, 6, 7, 8, 9]), + 'Thieves Basement Block': # One blue and one red zazak and one Stalfos. Two pots. Need weapon. + (['Thieves Basement Block WN'], 0x45, [1, 2, 3]), + 'Ice Jelly Key': + (['Ice Jelly Key ES'], 0x0e, [1, 2, 3]), + 'Ice Stalfos Hint': # Need bombs for big stalfos knights + (['Ice Stalfos Hint SE'], 0x3e, [1, 2]), + 'Ice Pengator Trap': # Five pengators. Bomb-doable? + (['Ice Pengator Trap NE'], 0x6e, [0, 1, 2, 3, 4]), + 'Mire 2': # Wizzrobes. Bombs dont work. + (['Mire 2 NE'], 0xd2, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), + 'Mire Cross': # 4 Sluggulas. Bombs don't work + (['Mire Cross ES'], 0xb2, [5, 6, 7, 10, 11]), + 'TR Twin Pokeys': # Two pokeys + (['TR Twin Pokeys EN', 'TR Twin Pokeys SW'], 0x24, [3, 4, 5, 6]), + 'TR Tongue Pull': # Kill zols for money + (['TR Tongue Pull NE'], 0x04, [9, 13, 14]), + 'GT Petting Zoo': # Don't make anyone do this room with bombs. + (['GT Petting Zoo SE'], 0x7d, [4, 5, 6, 7, 8, 10]), + 'GT DMs Room': # Four red stalfos + (['GT DMs Room SW'], 0x7b, [2, 3, 4, 5, 8, 9, 10]), + 'GT Gauntlet 1': # Stalfos/zazaks + (['GT Gauntlet 1 WN'], 0x5d, [3, 4, 5, 6]), + 'GT Gauntlet 2': # Red stalfos + (['GT Gauntlet 2 EN', 'GT Gauntlet 2 SW'], 0x5d, [0, 1, 2, 7]), + 'GT Gauntlet 3': # Blue zazaks + (['GT Gauntlet 3 NW', 'GT Gauntlet 3 SW'], 0x5d, [8, 9, 10, 11, 12]), + 'GT Gauntlet 4': # Red zazaks + (['GT Gauntlet 4 NW', 'GT Gauntlet 4 SW'], 0x6d, [0, 1, 2, 3]), + 'GT Gauntlet 5': # Stalfos and zazak + (['GT Gauntlet 5 NW', 'GT Gauntlet 5 WS'], 0x6d, [4, 5, 6, 7, 8]), + 'GT Wizzrobes 1': # Wizzrobes. Bombs don't work + (['GT Wizzrobes 1 SW'], 0xa5, [2, 3, 7]), + 'GT Wizzrobes 2': # Wizzrobes. Bombs don't work + (['GT Wizzrobes 2 SE', 'GT Wizzrobes 2 NE'], 0xa5, [0, 1, 4, 5, 6]), + 'Spiral Cave (Top)': # for traversal in enemizer at low health + (['Spiral Cave (top to bottom)'], 0xEE, [0, 1, 2, 3, 4]), +} # all trap rooms? (Desert Trap Room, Thieves Trap Room currently subtile only) + +kill_chests = { + 'Tower Room 03': (['Castle Tower - Room 03'], 0xe0, [2, 3]), + 'Ice Compass Room': (['Ice Palace - Compass Chest'], 0x2e, [0, 1, 2, 3, 4, 5]), + 'Swamp Entrance': (['Swamp Palace - Entrance'], 0x28, [0, 1, 2, 3, 4]), + 'GT Tile Room': (['Ganons Tower - Tile Room'], 0x8d, [1, 2, 3, 4]), + 'Mini Moldorm Cave': + (['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Left', + 'Mini Moldorm Cave - Right', 'Mini Moldorm Cave - Generous Guy'], 0x123, [0, 1, 2, 3]), + 'Mimic Cave': + (['Mimic Cave'], 0x10c, [4, 5, 6, 7]), +} + def add_connection(parent_name, target_name, entrance_name, world, player): parent = world.get_region(parent_name, player) diff --git a/Utils.py b/Utils.py index 74e79d61..d26a83e8 100644 --- a/Utils.py +++ b/Utils.py @@ -8,6 +8,11 @@ from collections import defaultdict from math import factorial import fileinput +import urllib.request +import urllib.parse +import yaml +from pathlib import Path + def int16_as_bytes(value): value = value & 0xFFFF @@ -710,6 +715,15 @@ def find_and_replace(): print(data_line.replace(two, hex(number))) +def load_yaml(path_list): + path = os.path.join(*path_list) + if os.path.exists(Path(path)): + with open(path, "r", encoding="utf-8") as f: + return yaml.load(f, Loader=yaml.SafeLoader) + elif urllib.parse.urlparse(path).scheme in ['http', 'https']: + return yaml.load(urllib.request.urlopen(path), Loader=yaml.FullLoader) + + if __name__ == '__main__': # make_new_base2current() # read_entrance_data(old_rom=sys.argv[1]) diff --git a/data/base2current.bps b/data/base2current.bps index 4bcf03eeeba1e639f3d10ebedd4b9206b69d0f01..360bf6948f43574a4c22ace03e7e2e0840c1da9b 100644 GIT binary patch delta 1320 zcmW+!ZA_C_6ut*aE%beBg|g)oNWpP^;leug&^M_<%w#m#`#M!+pCpme}&2!JW_c@Q( zES@uq$4fzK$qbQ(na8%g{02N#a9<3ktlK0H<)h|Xf5i7#l1~3@&*Usgw>+Bg2oXHC zZe%yWQ|mD{4Sw<k%nXE%e!-N-OtRPQ^S_Nt z(pJyQ`a|yBgi8Rxfl5>PHIc*!H@!~r?DDnK^ztOR7WX$;Xk7j{! zR}>Gl89uv2as}j^9DwJqurkr?U*20>qa^Y+-i31`rnA|!V* zeW-_5)^Ic%we= z>Y%@5v2?N2RUp(Bl1p;=ct${b$zI}1pypN>+PX7>!J%-iX^g~~*Ne3DX_sN8#HDv=ho!RyM(st3MZ?NS`r`4-1*cNDftkWYMWEXOSm8wgcz+@P4OueI(LKA&q9_oErfUi(_IW5?V{2 zbU+&<6uXIA6;!Wk_>yyFw zU`R09FZU0f2<@efG^Y#HuNAIW;k2M)&>aj01Hr|=Mjor5*jT+1xy0ebP@h{OKvOVi zHP|c!Nq{e-7rNk3bdoj$rxSuL!C!ayV}p_`^g*KJ&M zt|=Jh*<2GX$&5sIQR;1h#}Qgm_?dH0tJfxrU2|`yw8G>~l`7D8h+aV0zrCtCLwJtj zpTT1PO3-4N)goFJEz^($2Duk}*(WB3-1wZzKmI5{o|Avw!|j-R)0aU-98@rpE-7-% zz}ek6#nL7Yj=h?fC|q0On_a%m9|~UFpFYJcJCb7=ye?iAFRxneUFu!pqp6<*r8uHk zg}xjb(GC4kl}QZxYK1hFUL(amxzkldZ+1fri0EuLWTmwVEi6&~HGZqvu}5)-zM(#d zzJxFa9q}>3M87p^b`__f-h)(QgyO@yYh}n8to7(>zvgT%kfEbYX8&JEdUV@WPscHn zHFdiT`;w<Rw%6XlV(x#4Ryoi#$Egzvu8 Jcf_L&`ww+p2bur? delta 1327 zcmW+!3rtg27(NH;rKR_lTG|Q}ZABa)FfbY7=6nFY7;NY!&VfdhAu4u&<5fWTFSVvc zD~y|RgbTD=!-%3C6qYa*{9Kck-YAb-tTU zYQ{-TlwtaZ>$}x7YZNca+LSAL(a?35?$`36AygZtUKvaz^sZ^;6J%F z%o;q;l`?7gw?HS8yB7Z&nB0_kCuOwe5sI8pcu}~6|1fLt&d3#df7}!^>YIBwp-hXJ z)AUBn&hkA(tHs%L%FjL&HavYggC3SuA zxf#C|PZxC$gi#BcL4Wi$+!&US^Qrc>_O`H+)HmZJ>O>U{+)5e7E#z@CUbVueq``v} zNRQ~j=zQU3H*4XQa>@~=GcXfHR<&RorL*OOSU|pN!E>wkP&9m9n?K+Q?VB%~FB7)O z+Kuv19v?@`Yzx|g#CXzc!~HqyqiN_@RXIE?x{f3yX33h@$??Nj{s8;yt5{h%ki@Sr z$Q2D+E(A@aycKUXR2;b{zTKvVzITgQcD=vYrUt%%Tx!MDQEwb)pa4uHsSR(4-Ft!o zZ=3S+P3z%%le#Jf{I#U64PT&i)iOUsH0?N(QhtKLyPV{;W8+p`4Fl|2f6O_)tjAw; z#`}#o{)|VfwNzLtgvNvNo2-~wx!)V;CnToE%yL$n=Zielg(yN-Cc|J&y4`Ogd^cQ&d+Gcmgu7&Go?fJZLx&NIBKckED#4oHOzBbggc4`WPGadAo|sUo3vY-K*J zXTXKfX1->KtR}}!VaqbDamoYJFs((TQ5dkGm8bxfh*sh|g?lQt8=j0#Nr_T~v@E0z z^&g5nORSU;1Qq($B_WA?rWqwC6Sd$iPf&ojIK>Fpe4FH5-66 z51J#zafM7BlEig--3U;a|Csc2;1axoJnX;*Tuz>M;7n7)4g-Am{CQn?VPWXy-8Wxh zB*#7rn(Ys2$i_~bt8a>-;e5t@Hd%=1?K>xQvxr|!oSnF}cHc%l2&ISBz*X<`x=x|R z06LxlLZ(HwUPJC(M-ZKFg|U|8DbaR74gEV(ot)k%d*{__M=X_@15NlVWJkE=A&^id zGrlp$;oX;^1O8b0{MgdM5uV9&kUiNt7%w69fa9oSwI0MG(gS##>2PBvaFzN6Pig4& zA*CA5tyoknl8!_Y0-T7`2+iV3TG62fmhNlX1VI&PWAWa+O=cR{7kr~2W}TGXw=E;c zF8VF;G>lS<)MC`)*%xPD@UgPK@~i0N==5%~B;MmXh@?oCDm8+bOLSehH=`nn2C+(p zVCqpd^k~O~60JOZ(R;4G$TeL9Xfs-dOlTUdb9xcE)rHrlHOe{Nv7x0Q>K*zfSTK z-Ul&$2@U?ZIJu<|7yDxrt0yysB-M^fcfWH?18gT(3qsq_Sbd=e{N)`SsLw*Z1IAB|7$YS6)ST{%argCo{zJAYum4mQ+%ZzQ+uxL#=* LrLMN5?)Lo$V?Y4q diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py index 3c04b9c9..f8d55dc3 100644 --- a/source/dungeon/EnemyList.py +++ b/source/dungeon/EnemyList.py @@ -1,7 +1,9 @@ from collections import defaultdict -import math import typing +import yaml +from yaml.representer import Representer + try: from fast_enum import FastEnum except ImportError: @@ -11,26 +13,29 @@ import RaceRandom as random from BaseClasses import Location, LocationType from Items import ItemFactory from Utils import snes_to_pc, pc_to_snes, int16_as_bytes -from source.logic.Rule import RuleFactory - -# todo: bosses shifted coordinates class EnemyStats: def __init__(self, sprite, static, drop_flag=False, prize_pack: typing.Union[tuple, int] = 0, - sub_type=0, health=None): + sub_type=0, health=None, ignore=False, dmg=None, dmask=0): self.sprite = sprite self.sub_type = sub_type self.static = static self.health = health - # self.damage = damage + self.damage = dmg # this is a damage group, notably 0-9 (9 is reserved for ganon) + self.dmask = dmask # mask that is written out with the above damage class, same byte self.drop_flag = drop_flag self.prize_pack = prize_pack + # todo: write this bit for cucco (False), antifairy (True) + # rotating firebars (False) rollers (False) + self.ignore_for_kill_room = ignore + # health special cases: # Octorok light/dark world - # Hardhat Beetle - starting position - # Tektike - starting position? + # check the 0000 000x of the x coordinate + # Hardhat Beetle - (000b bit of x coordinate?) (blue if set, red otherwise) + # Tektite - starting position? - (000r bit of x coordinate) (red if set, blue otherwise) # RatCricket light/dark world # Keese light/dark world # Rope light/dark world @@ -116,6 +121,7 @@ class EnemySprite(FastEnum): Popo = 0x4e Popo2 = 0x4f + Cannonball = 0x50 ArmosStatue = 0x51 KingZora = 0x52 ArmosKnight = 0x53 @@ -167,6 +173,7 @@ class EnemySprite(FastEnum): RedEyegoreMimic = 0x84 YellowStalfos = 0x85 # falling stalfos that shoots head Kodongo = 0x86 + KodongoFire = 0x87 Mothula = 0x88 SpikeBlock = 0x8a Gibdo = 0x8b @@ -273,199 +280,216 @@ class SpriteType(FastEnum): 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, health=4), - EnemySprite.Moldorm: EnemyStats(EnemySprite.Moldorm, True, False), - EnemySprite.Cucco: EnemyStats(EnemySprite.Cucco, False, False), + EnemySprite.Raven: EnemyStats(EnemySprite.Raven, False, True, (6, 2), health=(4, 8), dmg=(1, 8), dmask=0x80), + EnemySprite.Vulture: EnemyStats(EnemySprite.Vulture, False, True, 6, health=6, dmg=3, dmask=0x80), + EnemySprite.CorrectPullSwitch: EnemyStats(EnemySprite.CorrectPullSwitch, True, ignore=True, dmg=2), + EnemySprite.WrongPullSwitch: EnemyStats(EnemySprite.WrongPullSwitch, True, ignore=True, dmg=2), + EnemySprite.Octorok: EnemyStats(EnemySprite.Octorok, False, True, 2, health=(2, 4), dmg=(3, 5)), + EnemySprite.Moldorm: EnemyStats(EnemySprite.Moldorm, True, False, dmg=3, dmask=0x10), + EnemySprite.Octorok4Way: EnemyStats(EnemySprite.Octorok4Way, False, True, 2, health=(2, 4), dmg=(3, 5)), + EnemySprite.Cucco: EnemyStats(EnemySprite.Cucco, False, False, ignore=True, dmg=1), + EnemySprite.Buzzblob: EnemyStats(EnemySprite.Buzzblob, False, True, 3, health=3, dmg=1), + EnemySprite.Snapdragon: EnemyStats(EnemySprite.Snapdragon, False, True, 7, health=12, dmg=8), - EnemySprite.Octoballoon: EnemyStats(EnemySprite.Octoballoon, False, False, 0, health=2), - EnemySprite.OctoballoonBaby: EnemyStats(EnemySprite.OctoballoonBaby, False), - 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, 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, health=8), - EnemySprite.WaterSwitch: EnemyStats(EnemySprite.WaterSwitch, True), - 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), health=32), - EnemySprite.Deadrock: EnemyStats(EnemySprite.Deadrock, False, False), - EnemySprite.DarkWorldHintNpc: EnemyStats(EnemySprite.DarkWorldHintNpc, True), - EnemySprite.AdultNpc: EnemyStats(EnemySprite.AdultNpc, True), - EnemySprite.SweepingLady: EnemyStats(EnemySprite.SweepingLady, True), - EnemySprite.Hobo: EnemyStats(EnemySprite.Hobo, True), - EnemySprite.Lumberjacks: EnemyStats(EnemySprite.Lumberjacks, True), - EnemySprite.TelepathicTile: EnemyStats(EnemySprite.TelepathicTile, True), - EnemySprite.FluteKid: EnemyStats(EnemySprite.FluteKid, True), - EnemySprite.RaceGameLady: EnemyStats(EnemySprite.RaceGameLady, True), + EnemySprite.Octoballoon: EnemyStats(EnemySprite.Octoballoon, False, False, 0, health=2, dmg=1), + EnemySprite.OctoballoonBaby: EnemyStats(EnemySprite.OctoballoonBaby, False, dmg=1), + EnemySprite.Hinox: EnemyStats(EnemySprite.Hinox, False, True, 4, health=20, dmg=8), + EnemySprite.Moblin: EnemyStats(EnemySprite.Moblin, False, True, 1, health=4, dmg=5), + EnemySprite.MiniHelmasaur: EnemyStats(EnemySprite.MiniHelmasaur, False, True, 7, health=4, dmg=3), + EnemySprite.ThievesTownGrate: EnemyStats(EnemySprite.ThievesTownGrate, True, dmg=0, dmask=0x40), + EnemySprite.AntiFairy: EnemyStats(EnemySprite.AntiFairy, False, False, dmg=4), + EnemySprite.Wiseman: EnemyStats(EnemySprite.Wiseman, True, dmg=0), + EnemySprite.Hoarder: EnemyStats(EnemySprite.Hoarder, False, False, health=2, dmg=2), + EnemySprite.MiniMoldorm: EnemyStats(EnemySprite.MiniMoldorm, False, True, 2, health=3, dmg=3), + EnemySprite.Poe: EnemyStats(EnemySprite.Poe, False, True, 6, health=8, dmg=5, dmask=0x80), + EnemySprite.Smithy: EnemyStats(EnemySprite.Smithy, True, dmg=0), + EnemySprite.Arrow: EnemyStats(EnemySprite.Arrow, True, ignore=True, dmg=1), + EnemySprite.Statue: EnemyStats(EnemySprite.Statue, True, ignore=True, dmg=0), + EnemySprite.FluteQuest: EnemyStats(EnemySprite.FluteQuest, True, dmg=0, dmask=0x40), + EnemySprite.CrystalSwitch: EnemyStats(EnemySprite.CrystalSwitch, True, ignore=True, dmg=0), + EnemySprite.SickKid: EnemyStats(EnemySprite.SickKid, True, dmg=0), + EnemySprite.Sluggula: EnemyStats(EnemySprite.Sluggula, False, True, 4, health=8, dmg=6), + EnemySprite.WaterSwitch: EnemyStats(EnemySprite.WaterSwitch, True, dmg=0), + EnemySprite.Ropa: EnemyStats(EnemySprite.Ropa, False, True, 2, health=8, dmg=5), + EnemySprite.RedBari: EnemyStats(EnemySprite.RedBari, False, True, 6, 2, health=2, dmg=3), + EnemySprite.BlueBari: EnemyStats(EnemySprite.BlueBari, False, True, 6, 2, health=2, dmg=1), + EnemySprite.TalkingTree: EnemyStats(EnemySprite.TalkingTree, True, dmg=0), + EnemySprite.HardhatBeetle: EnemyStats(EnemySprite.HardhatBeetle, False, True, (2, 6), health=32, dmg=0), + EnemySprite.Deadrock: EnemyStats(EnemySprite.Deadrock, False, True, dmg=3, health=255), + EnemySprite.DarkWorldHintNpc: EnemyStats(EnemySprite.DarkWorldHintNpc, True, dmg=0), + EnemySprite.AdultNpc: EnemyStats(EnemySprite.AdultNpc, True, dmg=0), + EnemySprite.SweepingLady: EnemyStats(EnemySprite.SweepingLady, True, dmg=0), + EnemySprite.Hobo: EnemyStats(EnemySprite.Hobo, True, dmg=0), + EnemySprite.Lumberjacks: EnemyStats(EnemySprite.Lumberjacks, True, dmg=0), + EnemySprite.TelepathicTile: EnemyStats(EnemySprite.TelepathicTile, True, dmg=0), + EnemySprite.FluteKid: EnemyStats(EnemySprite.FluteKid, True, dmg=0), + EnemySprite.RaceGameLady: EnemyStats(EnemySprite.RaceGameLady, True, dmg=0), - EnemySprite.FortuneTeller: EnemyStats(EnemySprite.FortuneTeller, True), - EnemySprite.ArgueBros: EnemyStats(EnemySprite.ArgueBros, True), - EnemySprite.RupeePull: EnemyStats(EnemySprite.RupeePull, True), - EnemySprite.YoungSnitch: EnemyStats(EnemySprite.YoungSnitch, True), - EnemySprite.Innkeeper: EnemyStats(EnemySprite.Innkeeper, True), - EnemySprite.Witch: EnemyStats(EnemySprite.Witch, True), - EnemySprite.Waterfall: EnemyStats(EnemySprite.Waterfall, True), - EnemySprite.EyeStatue: EnemyStats(EnemySprite.EyeStatue, True), - EnemySprite.Locksmith: EnemyStats(EnemySprite.Locksmith, True), - EnemySprite.MagicBat: EnemyStats(EnemySprite.MagicBat, True), - 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, health=2), - EnemySprite.TutorialGuard: EnemyStats(EnemySprite.TutorialGuard, True), + EnemySprite.FortuneTeller: EnemyStats(EnemySprite.FortuneTeller, True, dmg=0), + EnemySprite.ArgueBros: EnemyStats(EnemySprite.ArgueBros, True, dmg=0), + EnemySprite.RupeePull: EnemyStats(EnemySprite.RupeePull, True, dmg=0), + EnemySprite.YoungSnitch: EnemyStats(EnemySprite.YoungSnitch, True, dmg=0), + EnemySprite.Innkeeper: EnemyStats(EnemySprite.Innkeeper, True, dmg=0), + EnemySprite.Witch: EnemyStats(EnemySprite.Witch, True, dmg=0), + EnemySprite.Waterfall: EnemyStats(EnemySprite.Waterfall, True, ignore=True, dmg=0, dmask=0x40), + EnemySprite.EyeStatue: EnemyStats(EnemySprite.EyeStatue, True, dmg=0), + EnemySprite.Locksmith: EnemyStats(EnemySprite.Locksmith, True, dmg=0), + EnemySprite.MagicBat: EnemyStats(EnemySprite.MagicBat, True, dmg=0), + EnemySprite.BonkItem: EnemyStats(EnemySprite.BonkItem, True, dmg=0), + EnemySprite.KidInKak: EnemyStats(EnemySprite.KidInKak, True, dmg=0), + EnemySprite.OldSnitch: EnemyStats(EnemySprite.OldSnitch, True, dmg=0), + EnemySprite.Hoarder2: EnemyStats(EnemySprite.Hoarder2, False, False, health=2, dmg=2), + EnemySprite.TutorialGuard: EnemyStats(EnemySprite.TutorialGuard, True, dmg=2), - EnemySprite.LightningGate: EnemyStats(EnemySprite.LightningGate, True), - 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.LightningGate: EnemyStats(EnemySprite.LightningGate, True, dmg=0), + EnemySprite.BlueGuard: EnemyStats(EnemySprite.BlueGuard, False, True, 1, health=6, dmg=1), + EnemySprite.GreenGuard: EnemyStats(EnemySprite.GreenGuard, False, True, 1, health=4, dmg=1), + EnemySprite.RedSpearGuard: EnemyStats(EnemySprite.RedSpearGuard, False, True, 1, health=8, dmg=3), + EnemySprite.BluesainBolt: EnemyStats(EnemySprite.BluesainBolt, False, True, 7, health=6, dmg=1), + EnemySprite.UsainBolt: EnemyStats(EnemySprite.UsainBolt, False, True, 1, health=8, dmg=3), + EnemySprite.BlueArcher: EnemyStats(EnemySprite.BlueArcher, False, True, 5, health=6, dmg=1), + EnemySprite.GreenBushGuard: EnemyStats(EnemySprite.GreenBushGuard, False, True, 5, health=4, dmg=1), + EnemySprite.RedJavelinGuard: EnemyStats(EnemySprite.RedJavelinGuard, False, True, 3, health=8, dmg=3), + EnemySprite.RedBushGuard: EnemyStats(EnemySprite.RedBushGuard, False, True, 7, health=8, dmg=3), + EnemySprite.BombGuard: EnemyStats(EnemySprite.BombGuard, False, True, 4, health=8, dmg=3), + EnemySprite.GreenKnifeGuard: EnemyStats(EnemySprite.GreenKnifeGuard, False, True, 1, health=4, dmg=1), + EnemySprite.Geldman: EnemyStats(EnemySprite.Geldman, False, True, 2, health=4, dmg=3), + EnemySprite.Toppo: EnemyStats(EnemySprite.Toppo, False, False, 1, health=2, dmg=1), + EnemySprite.Popo: EnemyStats(EnemySprite.Popo, False, True, 2, health=2, dmg=1), + EnemySprite.Popo2: EnemyStats(EnemySprite.Popo2, False, True, 2, health=2, dmg=1), - 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, health=2), - EnemySprite.LostWoodsBird: EnemyStats(EnemySprite.LostWoodsBird, True), - EnemySprite.LostWoodsSquirrel: EnemyStats(EnemySprite.LostWoodsSquirrel, True), - EnemySprite.SparkCW: EnemyStats(EnemySprite.SparkCW, False, False), - EnemySprite.SparkCCW: EnemyStats(EnemySprite.SparkCCW, False, False), - EnemySprite.RollerVerticalUp: EnemyStats(EnemySprite.RollerVerticalUp, False, False), - EnemySprite.RollerVerticalDown: EnemyStats(EnemySprite.RollerVerticalDown, False, False), - EnemySprite.RollerHorizontalLeft: EnemyStats(EnemySprite.RollerHorizontalLeft, False, False), - 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, 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, 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.Cannonball: EnemyStats(EnemySprite.Cannonball, True, health=255, dmg=1), + EnemySprite.ArmosStatue: EnemyStats(EnemySprite.ArmosStatue, False, True, 5, health=8), + EnemySprite.ArmosKnight: EnemyStats(EnemySprite.ArmosKnight, True, False, dmg=1, dmask=0x10), + EnemySprite.Lanmolas: EnemyStats(EnemySprite.Lanmolas, True, False, dmg=4, dmask=0x10), + EnemySprite.FireballZora: EnemyStats(EnemySprite.Zora, False, True, 4, health=8, dmg=1), + EnemySprite.Zora: EnemyStats(EnemySprite.Zora, False, True, 4, health=8, dmg=1), + EnemySprite.DesertStatue: EnemyStats(EnemySprite.DesertStatue, True, dmg=2), + EnemySprite.Crab: EnemyStats(EnemySprite.Crab, False, True, 1, health=2, dmg=5), + EnemySprite.LostWoodsBird: EnemyStats(EnemySprite.LostWoodsBird, True, dmg=0), + EnemySprite.LostWoodsSquirrel: EnemyStats(EnemySprite.LostWoodsSquirrel, True, dmg=0), + EnemySprite.SparkCW: EnemyStats(EnemySprite.SparkCW, False, False, ignore=True, dmg=4), + EnemySprite.SparkCCW: EnemyStats(EnemySprite.SparkCCW, False, False, ignore=True, dmg=4), + EnemySprite.RollerVerticalUp: EnemyStats(EnemySprite.RollerVerticalUp, False, False, dmg=8), + EnemySprite.RollerVerticalDown: EnemyStats(EnemySprite.RollerVerticalDown, False, False, dmg=8), + EnemySprite.RollerHorizontalLeft: EnemyStats(EnemySprite.RollerHorizontalLeft, False, False, dmg=8), + EnemySprite.RollerHorizontalRight: EnemyStats(EnemySprite.RollerHorizontalRight, False, False, dmg=8), + EnemySprite.Beamos: EnemyStats(EnemySprite.Beamos, False, False, ignore=True, dmg=4), + EnemySprite.MasterSword: EnemyStats(EnemySprite.MasterSword, True, dmg=0), + EnemySprite.DebirandoPit: EnemyStats(EnemySprite.DebirandoPit, False, True, 2, health=4, ignore=True, dmg=4), + EnemySprite.Debirando: EnemyStats(EnemySprite.Debirando, False, True, 2, health=4, dmg=3), + EnemySprite.ArcheryNpc: EnemyStats(EnemySprite.ArcheryNpc, True, dmg=2), + EnemySprite.WallCannonVertLeft: EnemyStats(EnemySprite.WallCannonVertLeft, True, dmg=2), + EnemySprite.WallCannonVertRight: EnemyStats(EnemySprite.WallCannonVertRight, True, dmg=2), + EnemySprite.WallCannonHorzTop: EnemyStats(EnemySprite.WallCannonHorzTop, True, dmg=2), + EnemySprite.WallCannonHorzBottom: EnemyStats(EnemySprite.WallCannonHorzBottom, True, dmg=2), + EnemySprite.BallNChain: EnemyStats(EnemySprite.BallNChain, False, True, 2, health=16, dmg=3), + EnemySprite.CannonTrooper: EnemyStats(EnemySprite.CannonTrooper, False, True, 1, health=3, dmg=1), + EnemySprite.CricketRat: EnemyStats(EnemySprite.CricketRat, False, True, 2, health=(2, 8), dmg=(0, 5)), + EnemySprite.Snake: EnemyStats(EnemySprite.Snake, False, True, (1, 7), health=(4, 8), dmg=(1, 5)), + EnemySprite.Keese: EnemyStats(EnemySprite.Keese, False, True, (0, 7), health=(1, 4), ignore=True, + dmg=(0, 5), dmask=0x80), - 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), - EnemySprite.BottleMerchant: EnemyStats(EnemySprite.BottleMerchant, True), - 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, 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, health=4), - EnemySprite.AntiFairyCircle: EnemyStats(EnemySprite.AntiFairyCircle, False, False), - 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.Kodongo: EnemyStats(EnemySprite.Kodongo, 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, health=32), - EnemySprite.Arrghus: EnemyStats(EnemySprite.Arrghus, True), - EnemySprite.Arrghi: EnemyStats(EnemySprite.Arrghi, True), - 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, health=64), - EnemySprite.HelmasaurKing: EnemyStats(EnemySprite.HelmasaurKing, True), - EnemySprite.Bumper: EnemyStats(EnemySprite.Bumper, True), - EnemySprite.Pirogusu: EnemyStats(EnemySprite.Pirogusu, True), - EnemySprite.LaserEyeLeft: EnemyStats(EnemySprite.LaserEyeLeft, True), - 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, 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, False, 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, 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), - EnemySprite.PipeUp: EnemyStats(EnemySprite.PipeUp, True), - EnemySprite.PipeRight: EnemyStats(EnemySprite.PipeRight, True), - EnemySprite.PipeLeft: EnemyStats(EnemySprite.PipeLeft, True), - EnemySprite.GoodBee: EnemyStats(EnemySprite.GoodBee, True), - EnemySprite.PedestalPlaque: EnemyStats(EnemySprite.PedestalPlaque, True), - EnemySprite.BombShopGuy: EnemyStats(EnemySprite.BombShopGuy, True), - EnemySprite.BlindMaiden: EnemyStats(EnemySprite.BlindMaiden, True), + # skip helmafireball for damage + EnemySprite.Leever: EnemyStats(EnemySprite.Leever, False, True, 1, health=4, dmg=1), + EnemySprite.FairyPondTrigger: EnemyStats(EnemySprite.FairyPondTrigger, True, dmg=0), + EnemySprite.UnclePriest: EnemyStats(EnemySprite.UnclePriest, True, dmg=0), + EnemySprite.RunningNpc: EnemyStats(EnemySprite.RunningNpc, True, dmg=0), + EnemySprite.BottleMerchant: EnemyStats(EnemySprite.BottleMerchant, True, dmg=0, dmask=0x40), + EnemySprite.Zelda: EnemyStats(EnemySprite.Zelda, True, dmg=0), + EnemySprite.Grandma: EnemyStats(EnemySprite.Grandma, True, dmg=0), + EnemySprite.Agahnim: EnemyStats(EnemySprite.Agahnim, True, dmg=4, dmask=0x10), + EnemySprite.FloatingSkull: EnemyStats(EnemySprite.FloatingSkull, False, True, 7, health=24, dmg=6), + EnemySprite.BigSpike: EnemyStats(EnemySprite.BigSpike, False, False, ignore=True, dmg=4), + EnemySprite.FirebarCW: EnemyStats(EnemySprite.FirebarCW, False, False, ignore=True, dmg=4), + EnemySprite.FirebarCCW: EnemyStats(EnemySprite.FirebarCCW, False, False, ignore=True, dmg=4), + EnemySprite.Firesnake: EnemyStats(EnemySprite.Firesnake, False, False, ignore=True, dmg=4), + EnemySprite.Hover: EnemyStats(EnemySprite.Hover, False, True, 2, health=4, dmg=3), + EnemySprite.AntiFairyCircle: EnemyStats(EnemySprite.AntiFairyCircle, False, False, ignore=True, dmg=4), + EnemySprite.GreenEyegoreMimic: EnemyStats(EnemySprite.GreenEyegoreMimic, False, True, 5, health=16, dmg=4), + EnemySprite.RedEyegoreMimic: EnemyStats(EnemySprite.RedEyegoreMimic, False, True, 5, health=8, dmg=4), + EnemySprite.YellowStalfos: EnemyStats(EnemySprite.YellowStalfos, True, health=8, ignore=True, dmg=1), + EnemySprite.Kodongo: EnemyStats(EnemySprite.Kodongo, False, True, 6, health=1, dmg=4), + EnemySprite.KodongoFire: EnemyStats(EnemySprite.KodongoFire, True, health=255, dmg=4), + EnemySprite.Mothula: EnemyStats(EnemySprite.Mothula, True, dmg=5, dmask=0x10), + EnemySprite.SpikeBlock: EnemyStats(EnemySprite.SpikeBlock, False, False, ignore=True, dmg=4), + EnemySprite.Gibdo: EnemyStats(EnemySprite.Gibdo, False, True, 3, health=32, dmg=5), + EnemySprite.Arrghus: EnemyStats(EnemySprite.Arrghus, True, ignore=True, dmg=5, dmask=0x10), + EnemySprite.Arrghi: EnemyStats(EnemySprite.Arrghi, True, dmg=5, dmask=0x10), + EnemySprite.Terrorpin: EnemyStats(EnemySprite.Terrorpin, False, True, 2, health=8, dmg=3), + EnemySprite.Blob: EnemyStats(EnemySprite.Blob, False, True, 1, health=4, dmg=5), + EnemySprite.Wallmaster: EnemyStats(EnemySprite.Wallmaster, True, dmg=0), + EnemySprite.StalfosKnight: EnemyStats(EnemySprite.StalfosKnight, False, True, 4, health=64, dmg=5), + EnemySprite.HelmasaurKing: EnemyStats(EnemySprite.HelmasaurKing, True, dmg=5, dmask=0x10), + EnemySprite.Bumper: EnemyStats(EnemySprite.Bumper, True, ignore=True, dmg=5), + EnemySprite.Pirogusu: EnemyStats(EnemySprite.Pirogusu, True, dmg=5), + EnemySprite.LaserEyeLeft: EnemyStats(EnemySprite.LaserEyeLeft, True, ignore=True, dmg=6), + EnemySprite.LaserEyeRight: EnemyStats(EnemySprite.LaserEyeRight, True, ignore=True, dmg=6), + EnemySprite.LaserEyeTop: EnemyStats(EnemySprite.LaserEyeTop, True, ignore=True, dmg=6), + EnemySprite.LaserEyeBottom: EnemyStats(EnemySprite.LaserEyeBottom, True, ignore=True, dmg=6), + EnemySprite.Pengator: EnemyStats(EnemySprite.Pengator, False, True, 3, health=16, dmg=5), + EnemySprite.Kyameron: EnemyStats(EnemySprite.Kyameron, False, False, health=4, ignore=True, dmg=3), + EnemySprite.Wizzrobe: EnemyStats(EnemySprite.Wizzrobe, False, True, 1, health=2, dmg=6), + EnemySprite.Zoro: EnemyStats(EnemySprite.Zoro, True, health=4, dmg=5), + EnemySprite.Babasu: EnemyStats(EnemySprite.Babasu, False, True, 0, health=4, dmg=5), + EnemySprite.GroveOstritch: EnemyStats(EnemySprite.GroveOstritch, True, ignore=True, dmg=3), + EnemySprite.GroveRabbit: EnemyStats(EnemySprite.GroveRabbit, True, ignore=True, dmg=3), + EnemySprite.GroveBird: EnemyStats(EnemySprite.GroveBird, True, ignore=True, dmg=3), + EnemySprite.Freezor: EnemyStats(EnemySprite.Freezor, True, False, 0, health=16, dmg=6), + EnemySprite.Kholdstare: EnemyStats(EnemySprite.Kholdstare, True, dmg=7, dmask=0x10), + EnemySprite.KholdstareShell: EnemyStats(EnemySprite.KholdstareShell, True, dmg=5, dmask=0x10), + EnemySprite.FallingIce: EnemyStats(EnemySprite.FallingIce, True, dmg=5, dmask=0x10), + EnemySprite.BlueZazak: EnemyStats(EnemySprite.BlueZazak, False, True, 6, health=4, dmg=5), + EnemySprite.RedZazak: EnemyStats(EnemySprite.RedZazak, False, True, 6, health=8, dmg=5), + EnemySprite.Stalfos: EnemyStats(EnemySprite.Stalfos, False, True, 6, health=4, dmg=1), + EnemySprite.GreenZirro: EnemyStats(EnemySprite.GreenZirro, False, True, 1, health=4, dmg=5, dmask=0x80), + EnemySprite.BlueZirro: EnemyStats(EnemySprite.BlueZirro, False, True, 7, health=8, dmg=3, dmask=0x80), + EnemySprite.Pikit: EnemyStats(EnemySprite.Pikit, False, True, 2, health=12, dmg=5), - EnemySprite.Whirlpool: EnemyStats(EnemySprite.Whirlpool, True), - EnemySprite.Shopkeeper: EnemyStats(EnemySprite.Shopkeeper, True), - EnemySprite.Drunkard: EnemyStats(EnemySprite.Drunkard, True), - EnemySprite.Vitreous: EnemyStats(EnemySprite.Vitreous, True), - 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, 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, health=32), - EnemySprite.BigFairy: EnemyStats(EnemySprite.BigFairy, True), - 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, 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, health=4), - EnemySprite.Ganon: EnemyStats(EnemySprite.Ganon, True), + EnemySprite.OldMan: EnemyStats(EnemySprite.OldMan, True, dmg=0), + EnemySprite.PipeDown: EnemyStats(EnemySprite.PipeDown, True, dmg=0), + EnemySprite.PipeUp: EnemyStats(EnemySprite.PipeUp, True, dmg=0), + EnemySprite.PipeRight: EnemyStats(EnemySprite.PipeRight, True, dmg=0), + EnemySprite.PipeLeft: EnemyStats(EnemySprite.PipeLeft, True, dmg=0), + EnemySprite.GoodBee: EnemyStats(EnemySprite.GoodBee, True, ignore=True, dmg=0), + EnemySprite.PedestalPlaque: EnemyStats(EnemySprite.PedestalPlaque, True, dmg=0), + EnemySprite.BombShopGuy: EnemyStats(EnemySprite.BombShopGuy, True, dmg=0), + EnemySprite.BlindMaiden: EnemyStats(EnemySprite.BlindMaiden, True, dmg=0), - EnemySprite.Faerie: EnemyStats(EnemySprite.Faerie, True), - EnemySprite.SmallKey: EnemyStats(EnemySprite.SmallKey, True), - EnemySprite.MagicShopAssistant: EnemyStats(EnemySprite.MagicShopAssistant, True), - EnemySprite.HeartPiece: EnemyStats(EnemySprite.HeartPiece, True), - EnemySprite.CastleMantle: EnemyStats(EnemySprite.CastleMantle, True), + EnemySprite.Whirlpool: EnemyStats(EnemySprite.Whirlpool, True, dmg=0), + EnemySprite.Shopkeeper: EnemyStats(EnemySprite.Shopkeeper, True, dmg=0), + EnemySprite.Drunkard: EnemyStats(EnemySprite.Drunkard, True, dmg=0), + EnemySprite.Vitreous: EnemyStats(EnemySprite.Vitreous, True, dmg=7, dmask=0x10), + EnemySprite.Catfish: EnemyStats(EnemySprite.Catfish, True, dmg=5), + EnemySprite.CutsceneAgahnim: EnemyStats(EnemySprite.CutsceneAgahnim, True, dmg=5), + EnemySprite.Boulder: EnemyStats(EnemySprite.Boulder, True, dmg=4), + EnemySprite.Gibo: EnemyStats(EnemySprite.Gibo, False, True, 0, health=8, dmg=3), # patrick! + # could drop if killable thieves is on + EnemySprite.Thief: EnemyStats(EnemySprite.Thief, False, False, health=0, dmg=2), + EnemySprite.Medusa: EnemyStats(EnemySprite.Medusa, True, ignore=True, dmg=0, dmask=0x10), + EnemySprite.FourWayShooter: EnemyStats(EnemySprite.FourWayShooter, True, ignore=True, dmg=0), + EnemySprite.Pokey: EnemyStats(EnemySprite.Pokey, False, True, 7, health=32, dmg=6), + EnemySprite.BigFairy: EnemyStats(EnemySprite.BigFairy, True, dmg=0), + EnemySprite.Tektite: EnemyStats(EnemySprite.Tektite, False, True, 2, health=12, dmg=(3, 5)), + EnemySprite.Chainchomp: EnemyStats(EnemySprite.Chainchomp, False, False, dmg=7), + EnemySprite.TrinexxRockHead: EnemyStats(EnemySprite.TrinexxRockHead, True, dmg=7, dmask=0x10), + EnemySprite.TrinexxFireHead: EnemyStats(EnemySprite.TrinexxFireHead, True, dmg=7, dmask=0x10), + EnemySprite.TrinexxIceHead: EnemyStats(EnemySprite.TrinexxIceHead, True, dmg=7, dmask=0x10), + EnemySprite.Blind: EnemyStats(EnemySprite.Blind, True, dmg=5, dmask=0x10), + EnemySprite.Swamola: EnemyStats(EnemySprite.Swamola, False, True, 0, health=16, dmg=7), + EnemySprite.Lynel: EnemyStats(EnemySprite.Lynel, False, True, 7, health=24, dmg=6), + # medallions can kill bunny beams, but we don't need that in logic per se + EnemySprite.BunnyBeam: EnemyStats(EnemySprite.BunnyBeam, False, False, ignore=True, dmg=0, dmask=0x10), + EnemySprite.FloppingFish: EnemyStats(EnemySprite.FloppingFish, True, dmg=0), + EnemySprite.Stal: EnemyStats(EnemySprite.Stal, False, True, 1, health=4, dmg=3), + EnemySprite.Ganon: EnemyStats(EnemySprite.Ganon, True, dmg=9, dmask=0x10), + EnemySprite.Faerie: EnemyStats(EnemySprite.Faerie, True, ignore=True, dmg=0, dmask=0x10), + EnemySprite.SmallKey: EnemyStats(EnemySprite.SmallKey, True, ignore=True, dmg=0), + EnemySprite.FakeMasterSword: EnemyStats(EnemySprite.FakeMasterSword, False, False, ignore=True, dmg=0), + EnemySprite.MagicShopAssistant: EnemyStats(EnemySprite.MagicShopAssistant, True, ignore=True, dmg=0), + EnemySprite.HeartPiece: EnemyStats(EnemySprite.HeartPiece, True, ignore=True, dmg=0), + EnemySprite.CastleMantle: EnemyStats(EnemySprite.CastleMantle, True, dmg=0), } return stats @@ -500,6 +524,8 @@ class Sprite(object): self.location = None self.original_address = None + self.static = False # don't randomize me + self.water = False # water types can spawn here def copy(self): sprite = Sprite(self.super_tile, self.kind, self.sub_type, self.layer, self.tile_x, self.tile_y, self.region, @@ -523,16 +549,24 @@ class Sprite(object): def sprite_data_ow(self): return [self.tile_y, self.tile_x, self.kind] + def __str__(self): + return enemy_names[self.kind] if self.sub_type != 0x7 else overlord_names[self.kind] + # map of super_tile to list of Sprite objects: vanilla_sprites = {} + def create_sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, region=None, - drops_item=False, drop_item_kind=None): + drops_item=False, drop_item_kind=None, fix=False, water=False): if super_tile not in vanilla_sprites: vanilla_sprites[super_tile] = [] - vanilla_sprites[super_tile].append(Sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, - region, drops_item, drop_item_kind)) + sprite = Sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, region, drops_item, drop_item_kind) + if fix: + sprite.static = True + if water: + sprite.water = True + vanilla_sprites[super_tile].append(sprite) def init_vanilla_sprites(): @@ -656,9 +690,9 @@ def init_vanilla_sprites(): create_sprite(0x0016, EnemySprite.Blob, 0x00, 0, 0x15, 0x08, 'Swamp C') create_sprite(0x0016, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x09, 'Swamp C') create_sprite(0x0016, EnemySprite.Blob, 0x00, 0, 0x10, 0x0a, 'Swamp I') - create_sprite(0x0016, EnemySprite.Hover, 0x00, 1, 0x0c, 0x18, 'Swamp Waterway') # todo: quake only - create_sprite(0x0016, EnemySprite.Hover, 0x00, 1, 0x07, 0x1b, 'Swamp Waterway') - create_sprite(0x0016, EnemySprite.Hover, 0x00, 1, 0x14, 0x1b, 'Swamp Waterway') + create_sprite(0x0016, EnemySprite.Hover, 0x00, 1, 0x0c, 0x18, 'Swamp Waterway', fix=True) + create_sprite(0x0016, EnemySprite.Hover, 0x00, 1, 0x07, 0x1b, 'Swamp Waterway', fix=True) + create_sprite(0x0016, EnemySprite.Hover, 0x00, 1, 0x14, 0x1b, 'Swamp Waterway', fix=True) create_sprite(0x0017, EnemySprite.Bumper, 0x00, 0, 0x07, 0x0b) create_sprite(0x0017, EnemySprite.Bumper, 0x00, 0, 0x10, 0x0e) create_sprite(0x0017, EnemySprite.Bumper, 0x00, 0, 0x07, 0x16) @@ -710,7 +744,7 @@ def init_vanilla_sprites(): create_sprite(0x001f, EnemySprite.Pengator, 0x00, 0, 0x04, 0x15, 'Ice Pengator Switch') create_sprite(0x001f, EnemySprite.Pengator, 0x00, 0, 0x09, 0x15, 'Ice Pengator Switch') create_sprite(0x001f, EnemySprite.AntiFairy, 0x00, 0, 0x06, 0x16, 'Ice Pengator Switch') - create_sprite(0x001f, EnemySprite.BunnyBeam, 0x00, 0, 0x07, 0x17, 'Ice Pengator Switch') + create_sprite(0x001f, EnemySprite.BunnyBeam, 0x00, 0, 0x07, 0x17, 'Ice Pengator Switch', fix=True) create_sprite(0x001f, EnemySprite.Pengator, 0x00, 0, 0x0a, 0x17, 'Ice Pengator Switch') create_sprite(0x001f, EnemySprite.Pengator, 0x00, 0, 0x0a, 0x19, 'Ice Pengator Switch') create_sprite(0x001f, EnemySprite.Pengator, 0x00, 0, 0x04, 0x1b, 'Ice Pengator Switch') @@ -743,9 +777,9 @@ def init_vanilla_sprites(): create_sprite(0x0024, EnemySprite.Medusa, 0x00, 0, 0x1c, 0x04) create_sprite(0x0024, EnemySprite.RollerHorizontalRight, 0x00, 0, 0x1b, 0x06, 'TR Dodgers') create_sprite(0x0024, EnemySprite.Pokey, 0x00, 0, 0x05, 0x08, 'TR Twin Pokeys') - create_sprite(0x0024, EnemySprite.Medusa, 0x00, 0, 0x07, 0x08) + create_sprite(0x0024, EnemySprite.Medusa, 0x00, 0, 0x07, 0x08, 'TR Twin Pokeys') create_sprite(0x0024, EnemySprite.Pokey, 0x00, 0, 0x0a, 0x08, 'TR Twin Pokeys') - create_sprite(0x0024, EnemySprite.BunnyBeam, 0x00, 0, 0x0c, 0x0c, 'TR Twin Pokeys') + create_sprite(0x0024, EnemySprite.BunnyBeam, 0x00, 0, 0x0c, 0x0c, 'TR Twin Pokeys', fix=True) create_sprite(0x0026, EnemySprite.Medusa, 0x00, 0, 0x03, 0x04) create_sprite(0x0026, EnemySprite.RedBari, 0x00, 0, 0x1a, 0x05, 'Swamp Right Elbow') create_sprite(0x0026, EnemySprite.RedBari, 0x00, 0, 0x05, 0x06, 'Swamp Shooters') @@ -755,7 +789,7 @@ def init_vanilla_sprites(): create_sprite(0x0026, EnemySprite.Statue, 0x00, 0, 0x06, 0x17) create_sprite(0x0026, EnemySprite.FourWayShooter, 0x00, 0, 0x19, 0x17) create_sprite(0x0026, EnemySprite.RedBari, 0x00, 0, 0x07, 0x18, 'Swamp Push Statue') - create_sprite(0x0026, EnemySprite.Kyameron, 0x00, 0, 0x15, 0x18, 'Swamp Push Statue') + create_sprite(0x0026, EnemySprite.Kyameron, 0x00, 0, 0x15, 0x18, 'Swamp Push Statue', water=True) create_sprite(0x0026, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x19, 'Swamp Push Statue') create_sprite(0x0026, EnemySprite.Firesnake, 0x00, 0, 0x1c, 0x1a, 'Swamp Push Statue') create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x17, 0x09, 'Hera 4F') @@ -765,10 +799,10 @@ def init_vanilla_sprites(): create_sprite(0x0027, EnemySprite.SparkCW, 0x00, 0, 0x0f, 0x06, 'Hera Big Chest Landing') create_sprite(0x0027, EnemySprite.Kodongo, 0x00, 0, 0x05, 0x0e, 'Hera 4F') create_sprite(0x0027, EnemySprite.Kodongo, 0x00, 0, 0x04, 0x16, 'Hera 4F') - create_sprite(0x0028, EnemySprite.Kyameron, 0x00, 0, 0x0a, 0x06, 'Swamp Entrance') - create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x08, 0x08, 'Swamp Entrance') - create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x0b, 0x0a, 'Swamp Entrance') - create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x07, 0x0d, 'Swamp Entrance') + create_sprite(0x0028, EnemySprite.Kyameron, 0x00, 0, 0x0a, 0x06, 'Swamp Entrance', water=True) + create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x08, 0x08, 'Swamp Entrance', water=True) + create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x0b, 0x0a, 'Swamp Entrance', water=True) + create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x07, 0x0d, 'Swamp Entrance', water=True) create_sprite(0x0028, EnemySprite.SpikeBlock, 0x00, 0, 0x08, 0x10, 'Swamp Entrance') create_sprite(0x0029, EnemySprite.Mothula, 0x00, 0, 0x08, 0x06) create_sprite(0x0029, 0x07, SpriteType.Overlord, 0, 0x07, 0x16) @@ -819,9 +853,9 @@ def init_vanilla_sprites(): create_sprite(0x0033, EnemySprite.Lanmolas, 0x00, 0, 0x06, 0x07) create_sprite(0x0033, EnemySprite.Lanmolas, 0x00, 0, 0x09, 0x07) create_sprite(0x0033, EnemySprite.Lanmolas, 0x00, 0, 0x07, 0x09) - create_sprite(0x0034, EnemySprite.Hover, 0x00, 0, 0x0f, 0x0b, 'Swamp West Shallows') - create_sprite(0x0034, EnemySprite.Hover, 0x00, 0, 0x10, 0x12, 'Swamp West Shallows') - create_sprite(0x0034, EnemySprite.Kyameron, 0x00, 0, 0x0f, 0x15, 'Swamp West Shallows') + create_sprite(0x0034, EnemySprite.Hover, 0x00, 0, 0x0f, 0x0b, 'Swamp West Shallows', water=True) + create_sprite(0x0034, EnemySprite.Hover, 0x00, 0, 0x10, 0x12, 'Swamp West Shallows', water=True) + create_sprite(0x0034, EnemySprite.Kyameron, 0x00, 0, 0x0f, 0x15, 'Swamp West Shallows', water=True) create_sprite(0x0034, EnemySprite.Firesnake, 0x00, 0, 0x19, 0x17, 'Swamp West Shallows') create_sprite(0x0034, EnemySprite.Blob, 0x00, 0, 0x03, 0x18, 'Swamp West Block Path') create_sprite(0x0034, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x18, 'Swamp West Shallows') @@ -838,14 +872,14 @@ def init_vanilla_sprites(): create_sprite(0x0035, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x1b, 'Swamp Trench 2 Pots') create_sprite(0x0035, EnemySprite.Stalfos, 0x00, 0, 0x1b, 0x1c, 'Swamp Trench 2 Pots') create_sprite(0x0036, 0x12, SpriteType.Overlord, 0, 0x17, 0x02) - create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x0b, 0x0a, 'Swamp Hub') - create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x14, 0x0a, 'Swamp Hub') - create_sprite(0x0036, EnemySprite.Medusa, 0x00, 0, 0x15, 0x0b) + create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x0b, 0x0a, 'Swamp Hub', water=True) + create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x14, 0x0a, 'Swamp Hub', water=True) + create_sprite(0x0036, EnemySprite.Medusa, 0x00, 0, 0x15, 0x0b, 'Swamp Hub', water=True) create_sprite(0x0036, 0x10, SpriteType.Overlord, 0, 0x01, 0x0d) - create_sprite(0x0036, EnemySprite.Kyameron, 0x00, 0, 0x14, 0x13, 'Swamp Hub') + create_sprite(0x0036, EnemySprite.Kyameron, 0x00, 0, 0x14, 0x13, 'Swamp Hub', water=True) create_sprite(0x0036, 0x11, SpriteType.Overlord, 0, 0x1e, 0x13) - create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x09, 0x14, 'Swamp Hub') - create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x12, 0x17, 'Swamp Hub') + create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x09, 0x14, 'Swamp Hub', water=True) + create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x12, 0x17, 'Swamp Hub', water=True) create_sprite(0x0036, 0x13, SpriteType.Overlord, 0, 0x0a, 0x1e) create_sprite(0x0036, 0x13, SpriteType.Overlord, 0, 0x14, 0x1e) create_sprite(0x0037, EnemySprite.WaterSwitch, 0x00, 0, 0x0b, 0x04) @@ -858,13 +892,13 @@ def init_vanilla_sprites(): create_sprite(0x0037, EnemySprite.BlueBari, 0x00, 0, 0x13, 0x19, 'Swamp Trench 1 Approach') create_sprite(0x0037, EnemySprite.FourWayShooter, 0x00, 0, 0x17, 0x1a) create_sprite(0x0037, EnemySprite.RedBari, 0x00, 0, 0x15, 0x1c, 'Swamp Trench 1 Approach') - create_sprite(0x0038, EnemySprite.Hover, 0x00, 0, 0x0c, 0x06, 'Swamp Pot Row') - create_sprite(0x0038, EnemySprite.Hover, 0x00, 0, 0x07, 0x0a, 'Swamp Pot Row') - create_sprite(0x0038, EnemySprite.Kyameron, 0x00, 0, 0x0c, 0x0c, 'Swamp Pot Row') - create_sprite(0x0038, EnemySprite.Medusa, 0x00, 0, 0x0c, 0x10, 'Swamp Pot Row') - create_sprite(0x0038, EnemySprite.Kyameron, 0x00, 0, 0x06, 0x14, 'Swamp Pot Row') - create_sprite(0x0038, EnemySprite.Kyameron, 0x00, 0, 0x0c, 0x18, 'Swamp Pot Row') - create_sprite(0x0038, EnemySprite.Hover, 0x00, 0, 0x07, 0x1a, 'Swamp Pot Row') + create_sprite(0x0038, EnemySprite.Hover, 0x00, 0, 0x0c, 0x06, 'Swamp Pot Row', water=True) + create_sprite(0x0038, EnemySprite.Hover, 0x00, 0, 0x07, 0x0a, 'Swamp Pot Row', water=True) + create_sprite(0x0038, EnemySprite.Kyameron, 0x00, 0, 0x0c, 0x0c, 'Swamp Pot Row', water=True) + create_sprite(0x0038, EnemySprite.Medusa, 0x00, 0, 0x0c, 0x10, 'Swamp Pot Row', water=True) + create_sprite(0x0038, EnemySprite.Kyameron, 0x00, 0, 0x06, 0x14, 'Swamp Pot Row', water=True) + create_sprite(0x0038, EnemySprite.Kyameron, 0x00, 0, 0x0c, 0x18, 'Swamp Pot Row', water=True) + create_sprite(0x0038, EnemySprite.Hover, 0x00, 0, 0x07, 0x1a, 'Swamp Pot Row', water=True) create_sprite(0x0039, EnemySprite.MiniMoldorm, 0x00, 0, 0x04, 0x18, 'Skull Spike Corner') create_sprite(0x0039, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) create_sprite(0x0039, EnemySprite.Gibdo, 0x00, 0, 0x05, 0x15, 'Skull Spike Corner', True, 0xe4) @@ -905,11 +939,11 @@ def init_vanilla_sprites(): create_sprite(0x003e, EnemySprite.CrystalSwitch, 0x00, 0, 0x06, 0x15) create_sprite(0x003e, EnemySprite.StalfosKnight, 0x00, 0, 0x19, 0x04, 'Ice Stalfos Hint') create_sprite(0x003e, EnemySprite.StalfosKnight, 0x00, 0, 0x16, 0x0b, 'Ice Stalfos Hint') - create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x05, 0x12, 'Ice Conveyor') - create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x0e, 0x12, 'Ice Conveyor') + create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x05, 0x12, 'Ice Conveyor', fix=True) + create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x0e, 0x12, 'Ice Conveyor', fix=True) create_sprite(0x003e, 0x07, SpriteType.Overlord, 0, 0x10, 0x12) - create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x12, 0x12, 'Ice Conveyor') - create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x15, 0x12, 'Ice Conveyor') + create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x12, 0x12, 'Ice Conveyor', fix=True) + create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x15, 0x12, 'Ice Conveyor', fix=True) create_sprite(0x003e, EnemySprite.BlueBari, 0x00, 0, 0x07, 0x16, 'Ice Conveyor') create_sprite(0x003e, EnemySprite.BlueBari, 0x00, 0, 0x11, 0x18, 'Ice Conveyor', True, 0xe4) create_sprite(0x003e, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x19, 'Ice Conveyor') @@ -950,18 +984,18 @@ def init_vanilla_sprites(): create_sprite(0x0045, EnemySprite.RedZazak, 0x00, 0, 0x06, 0x06, 'Thieves Basement Block') create_sprite(0x0045, EnemySprite.BlueZazak, 0x00, 0, 0x04, 0x0b, 'Thieves Basement Block') create_sprite(0x0045, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x0b, 'Thieves Basement Block') - create_sprite(0x0045, EnemySprite.BunnyBeam, 0x00, 0, 0x17, 0x0b, "Thieves Blind's Cell Interior") + create_sprite(0x0045, EnemySprite.BunnyBeam, 0x00, 0, 0x17, 0x0b, "Thieves Blind's Cell Interior", fix=True) create_sprite(0x0045, EnemySprite.BlueZazak, 0x00, 0, 0x18, 0x0c, "Thieves Blind's Cell Interior") create_sprite(0x0045, EnemySprite.BlueZazak, 0x00, 0, 0x1a, 0x0c, "Thieves Blind's Cell Interior") create_sprite(0x0045, EnemySprite.BlueZazak, 0x00, 0, 0x18, 0x11, "Thieves Blind's Cell Interior") create_sprite(0x0045, EnemySprite.Blob, 0x00, 0, 0x16, 0x18, "Thieves Blind's Cell") create_sprite(0x0045, EnemySprite.RedZazak, 0x00, 0, 0x19, 0x1b, "Thieves Blind's Cell") create_sprite(0x0045, EnemySprite.RedZazak, 0x00, 0, 0x07, 0x1c, 'Thieves Lonely Zazak') - create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x16, 0x05, 'Swamp Donut Top') + create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x16, 0x05, 'Swamp Donut Top', water=True) create_sprite(0x0046, 0x11, SpriteType.Overlord, 0, 0x1b, 0x06) - create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x09, 0x1a, 'Swamp Donut Bottom') + create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x09, 0x1a, 'Swamp Donut Bottom', water=True) create_sprite(0x0046, 0x11, SpriteType.Overlord, 0, 0x1b, 0x1a) - create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x11, 0x1b, 'Swamp Donut Bottom') + create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x11, 0x1b, 'Swamp Donut Bottom', water=True) create_sprite(0x0049, EnemySprite.MiniMoldorm, 0x00, 0, 0x0b, 0x05, 'Skull Vines') create_sprite(0x0049, EnemySprite.MiniMoldorm, 0x00, 0, 0x04, 0x0b, 'Skull Vines') create_sprite(0x0049, EnemySprite.MiniMoldorm, 0x00, 0, 0x09, 0x0c, 'Skull Vines') @@ -1024,14 +1058,14 @@ def init_vanilla_sprites(): create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x0b, 0x1a, 'Desert Four Statues') create_sprite(0x0053, EnemySprite.Beamos, 0x00, 0, 0x1b, 0x1a, 'Desert Beamos Hall') create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x1a, 0x1b, 'Desert Beamos Hall') - create_sprite(0x0054, EnemySprite.Kyameron, 0x00, 0, 0x0e, 0x05, 'Swamp Attic') - create_sprite(0x0054, EnemySprite.Hover, 0x00, 0, 0x0c, 0x0b, 'Swamp Attic') - create_sprite(0x0054, EnemySprite.Medusa, 0x00, 0, 0x0b, 0x0e, 'Swamp Attic') + create_sprite(0x0054, EnemySprite.Kyameron, 0x00, 0, 0x0e, 0x05, 'Swamp Attic', water=True) + create_sprite(0x0054, EnemySprite.Hover, 0x00, 0, 0x0c, 0x0b, 'Swamp Attic', water=True) + create_sprite(0x0054, EnemySprite.Medusa, 0x00, 0, 0x0b, 0x0e, 'Swamp Attic', water=True) create_sprite(0x0054, EnemySprite.FirebarCW, 0x00, 0, 0x0f, 0x0e, 'Swamp Attic') - create_sprite(0x0054, EnemySprite.Hover, 0x00, 0, 0x10, 0x0f, 'Swamp Attic') - create_sprite(0x0054, EnemySprite.Kyameron, 0x00, 0, 0x12, 0x14, 'Swamp Attic') - create_sprite(0x0054, EnemySprite.Hover, 0x00, 0, 0x0f, 0x15, 'Swamp Attic') - create_sprite(0x0054, EnemySprite.Kyameron, 0x00, 0, 0x0c, 0x17, 'Swamp Attic') + create_sprite(0x0054, EnemySprite.Hover, 0x00, 0, 0x10, 0x0f, 'Swamp Attic', water=True) + create_sprite(0x0054, EnemySprite.Kyameron, 0x00, 0, 0x12, 0x14, 'Swamp Attic', water=True) + create_sprite(0x0054, EnemySprite.Hover, 0x00, 0, 0x0f, 0x15, 'Swamp Attic', water=True) + create_sprite(0x0054, EnemySprite.Kyameron, 0x00, 0, 0x0c, 0x17, 'Swamp Attic', water=True) create_sprite(0x0055, EnemySprite.UnclePriest, 0x00, 0, 0x0e, 0x08, 'Hyrule Castle Secret Entrance') create_sprite(0x0055, EnemySprite.GreenKnifeGuard, 0x00, 0, 0x14, 0x15, 'Hyrule Castle Secret Entrance') create_sprite(0x0055, EnemySprite.GreenKnifeGuard, 0x00, 0, 0x0d, 0x16, 'Hyrule Castle Secret Entrance') @@ -1048,7 +1082,7 @@ def init_vanilla_sprites(): create_sprite(0x0056, EnemySprite.HardhatBeetle, 0x00, 0, 0x03, 0x1b, 'Skull 2 West Lobby') create_sprite(0x0056, EnemySprite.Firesnake, 0x00, 0, 0x13, 0x1c, 'Skull Small Hall') create_sprite(0x0056, EnemySprite.HardhatBeetle, 0x00, 0, 0x19, 0x1c, 'Skull Small Hall') - create_sprite(0x0057, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x04, 'Skull Big Key') + create_sprite(0x0057, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x04, 'Skull Big Key', fix=True) create_sprite(0x0057, EnemySprite.RedBari, 0x00, 0, 0x0c, 0x04, 'Skull Big Key') create_sprite(0x0057, EnemySprite.SpikeBlock, 0x00, 0, 0x08, 0x05, 'Skull Big Key') create_sprite(0x0057, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x07, 'Skull Big Key') @@ -1133,7 +1167,7 @@ def init_vanilla_sprites(): create_sprite(0x0064, EnemySprite.Keese, 0x00, 0, 0x05, 0x12, 'Thieves Attic Hint') create_sprite(0x0064, EnemySprite.WrongPullSwitch, 0x00, 0, 0x0b, 0x13) create_sprite(0x0064, EnemySprite.Keese, 0x00, 0, 0x05, 0x13, 'Thieves Attic Hint') - create_sprite(0x0064, EnemySprite.BunnyBeam, 0x00, 0, 0x03, 0x16, 'Thieves Attic Hint') + create_sprite(0x0064, EnemySprite.BunnyBeam, 0x00, 0, 0x03, 0x16, 'Thieves Attic Hint', fix=True) create_sprite(0x0064, EnemySprite.CricketRat, 0x00, 0, 0x17, 0x17, 'Thieves Cricket Hall Left') create_sprite(0x0064, EnemySprite.CricketRat, 0x00, 0, 0x19, 0x19, 'Thieves Cricket Hall Left') create_sprite(0x0064, EnemySprite.CricketRat, 0x00, 0, 0x05, 0x1a, 'Thieves Attic') @@ -1154,12 +1188,12 @@ def init_vanilla_sprites(): create_sprite(0x0066, EnemySprite.BlueBari, 0x00, 0, 0x1a, 0x07, 'Swamp Behind Waterfall') create_sprite(0x0066, EnemySprite.Waterfall, 0x00, 1, 0x17, 0x14, 'Swamp Waterfall Room') create_sprite(0x0066, 0x10, SpriteType.Overlord, 1, 0x01, 0x16) - create_sprite(0x0066, EnemySprite.Kyameron, 0x00, 1, 0x0f, 0x16, 'Swamp Waterfall Room') - create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x13, 0x16, 'Swamp Waterfall Room') - create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x0b, 0x18, 'Swamp Waterfall Room') - create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x0d, 0x19, 'Swamp Waterfall Room') + create_sprite(0x0066, EnemySprite.Kyameron, 0x00, 1, 0x0f, 0x16, 'Swamp Waterfall Room', water=True) + create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x13, 0x16, 'Swamp Waterfall Room', water=True) + create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x0b, 0x18, 'Swamp Waterfall Room', water=True) + create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x0d, 0x19, 'Swamp Waterfall Room', water=True) create_sprite(0x0066, 0x11, SpriteType.Overlord, 1, 0x1e, 0x19) - create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x17, 0x1b, 'Swamp Waterfall Room') + create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x17, 0x1b, 'Swamp Waterfall Room', water=True) create_sprite(0x0067, EnemySprite.Bumper, 0x00, 0, 0x07, 0x0c, 'Skull Left Drop') create_sprite(0x0067, EnemySprite.BlueBari, 0x00, 0, 0x04, 0x06, 'Skull Left Drop') create_sprite(0x0067, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x06, 'Skull Left Drop') @@ -1174,10 +1208,10 @@ def init_vanilla_sprites(): create_sprite(0x0068, EnemySprite.Bumper, 0x00, 0, 0x11, 0x07) create_sprite(0x0068, EnemySprite.Bumper, 0x00, 0, 0x0c, 0x0b) create_sprite(0x0068, EnemySprite.Bumper, 0x00, 0, 0x13, 0x0b) - create_sprite(0x0068, EnemySprite.Gibdo, 0x00, 0, 0x14, 0x08, 'Skull Compass Room') + create_sprite(0x0068, EnemySprite.Gibdo, 0x00, 0, 0x14, 0x08, 'Skull Pinball') create_sprite(0x0068, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) - create_sprite(0x0068, EnemySprite.Gibdo, 0x00, 0, 0x0e, 0x12, 'Skull Compass Room') - create_sprite(0x0068, EnemySprite.Gibdo, 0x00, 0, 0x12, 0x12, 'Skull Compass Room') + create_sprite(0x0068, EnemySprite.Gibdo, 0x00, 0, 0x0e, 0x12, 'Skull Pinball') + create_sprite(0x0068, EnemySprite.Gibdo, 0x00, 0, 0x12, 0x12, 'Skull Pinball') create_sprite(0x006a, EnemySprite.Terrorpin, 0x00, 0, 0x17, 0x0a, 'PoD Dark Alley') create_sprite(0x006a, EnemySprite.Terrorpin, 0x00, 0, 0x18, 0x0a, 'PoD Dark Alley') create_sprite(0x006a, EnemySprite.AntiFairy, 0x00, 0, 0x14, 0x0b, 'PoD Dark Basement') @@ -1247,9 +1281,9 @@ def init_vanilla_sprites(): create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x07, 0x19, 'Desert Arrow Pot Corner') create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x09, 0x19, 'Desert Arrow Pot Corner') create_sprite(0x0076, EnemySprite.WaterSwitch, 0x00, 0, 0x19, 0x03) - create_sprite(0x0076, EnemySprite.Hover, 0x00, 0, 0x07, 0x0a, 'Swamp Basement Shallows') - create_sprite(0x0076, EnemySprite.Kyameron, 0x00, 0, 0x07, 0x0f, 'Swamp Basement Shallows') - create_sprite(0x0076, EnemySprite.Hover, 0x00, 0, 0x08, 0x11, 'Swamp Basement Shallows') + create_sprite(0x0076, EnemySprite.Hover, 0x00, 0, 0x07, 0x0a, 'Swamp Basement Shallows', water=True) + create_sprite(0x0076, EnemySprite.Kyameron, 0x00, 0, 0x07, 0x0f, 'Swamp Basement Shallows', water=True) + create_sprite(0x0076, EnemySprite.Hover, 0x00, 0, 0x08, 0x11, 'Swamp Basement Shallows', water=True) create_sprite(0x0076, EnemySprite.Blob, 0x00, 0, 0x1b, 0x19, 'Swamp Flooded Room') create_sprite(0x0076, 0x13, SpriteType.Overlord, 0, 0x08, 0x1c) create_sprite(0x0076, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x1c, 'Swamp Flooded Room') @@ -1392,7 +1426,7 @@ def init_vanilla_sprites(): create_sprite(0x008d, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x1c, 'GT Speed Torch') create_sprite(0x008e, EnemySprite.Freezor, 0x00, 0, 0x1b, 0x02, 'Ice Lonely Freezor') create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x18, 0x05, 'Ice Lonely Freezor') - create_sprite(0x008e, EnemySprite.BunnyBeam, 0x00, 0, 0x14, 0x06, 'Ice Lonely Freezor') + create_sprite(0x008e, EnemySprite.BunnyBeam, 0x00, 0, 0x14, 0x06, 'Ice Lonely Freezor', fix=True) create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x1b, 0x08, 'Ice Lonely Freezor') create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x14, 0x09, 'Ice Lonely Freezor') create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x16, 0x0a, 'Ice Lonely Freezor') @@ -1486,10 +1520,10 @@ def init_vanilla_sprites(): create_sprite(0x009e, EnemySprite.StalfosKnight, 0x00, 0, 0x18, 0x08, 'Ice Backwards Room') create_sprite(0x009e, EnemySprite.RedBari, 0x00, 0, 0x19, 0x08, 'Ice Backwards Room') create_sprite(0x009e, EnemySprite.Freezor, 0x00, 0, 0x14, 0x12, 'Ice Crystal Left') - create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x04, 0x12, 'Ice Many Pots') - create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x06, 0x12, 'Ice Many Pots') - create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x09, 0x12, 'Ice Many Pots') - create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x0b, 0x12, 'Ice Many Pots') + create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x04, 0x12, 'Ice Many Pots', fix=True) + create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x06, 0x12, 'Ice Many Pots', fix=True) + create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x09, 0x12, 'Ice Many Pots', fix=True) + create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x0b, 0x12, 'Ice Many Pots', fix=True) create_sprite(0x009f, EnemySprite.AntiFairy, 0x00, 0, 0x07, 0x17, 'Ice Many Pots') create_sprite(0x009f, EnemySprite.FirebarCW, 0x00, 0, 0x08, 0x18, 'Ice Many Pots') create_sprite(0x00a0, EnemySprite.Medusa, 0x00, 0, 0x03, 0x08, 'Mire Antechamber') @@ -1502,7 +1536,7 @@ def init_vanilla_sprites(): create_sprite(0x00a1, EnemySprite.Medusa, 0x00, 0, 0x15, 0x15, 'Mire South Fish') create_sprite(0x00a1, EnemySprite.Medusa, 0x00, 0, 0x1a, 0x15, 'Mire South Fish') create_sprite(0x00a1, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x19, 'Mire South Fish') - create_sprite(0x00a1, EnemySprite.BunnyBeam, 0x00, 0, 0x17, 0x19, 'Mire South Fish') + create_sprite(0x00a1, EnemySprite.BunnyBeam, 0x00, 0, 0x17, 0x19, 'Mire South Fish', fix=True) create_sprite(0x00a1, EnemySprite.Stalfos, 0x00, 0, 0x1b, 0x19, 'Mire South Fish') create_sprite(0x00a4, EnemySprite.TrinexxRockHead, 0x00, 0, 0x07, 0x05) create_sprite(0x00a4, EnemySprite.TrinexxFireHead, 0x00, 0, 0x07, 0x05) @@ -1578,9 +1612,9 @@ def init_vanilla_sprites(): create_sprite(0x00b1, EnemySprite.AntiFairy, 0x00, 0, 0x15, 0x1a, 'Mire Spike Barrier') create_sprite(0x00b1, EnemySprite.Wizzrobe, 0x00, 0, 0x08, 0x1c, 'Mire Square Rail') create_sprite(0x00b2, EnemySprite.Wizzrobe, 0x00, 1, 0x14, 0x08, 'Mire BK Door Room') - create_sprite(0x00b2, EnemySprite.BunnyBeam, 0x00, 1, 0x0c, 0x0a, 'Mire BK Door Room') + create_sprite(0x00b2, EnemySprite.BunnyBeam, 0x00, 1, 0x0c, 0x0a, 'Mire BK Door Room', fix=True) create_sprite(0x00b2, EnemySprite.AntiFairy, 0x00, 1, 0x12, 0x0a, 'Mire BK Door Room') - create_sprite(0x00b2, EnemySprite.BunnyBeam, 0x00, 1, 0x13, 0x0a, 'Mire BK Door Room') + create_sprite(0x00b2, EnemySprite.BunnyBeam, 0x00, 1, 0x13, 0x0a, 'Mire BK Door Room', fix=True) create_sprite(0x00b2, EnemySprite.AntiFairy, 0x00, 1, 0x07, 0x0b, 'Mire BK Door Room') create_sprite(0x00b2, EnemySprite.Sluggula, 0x00, 0, 0x04, 0x15, 'Mire Cross') create_sprite(0x00b2, EnemySprite.Sluggula, 0x00, 0, 0x0b, 0x15, 'Mire Cross') @@ -1681,7 +1715,7 @@ def init_vanilla_sprites(): create_sprite(0x00c2, EnemySprite.Medusa, 0x00, 0, 0x08, 0x10, 'Mire Hub') create_sprite(0x00c2, EnemySprite.SparkCW, 0x00, 1, 0x10, 0x12, 'Mire Hub') create_sprite(0x00c2, EnemySprite.SparkCW, 0x00, 1, 0x19, 0x12, 'Mire Hub') - create_sprite(0x00c2, EnemySprite.BunnyBeam, 0x00, 1, 0x10, 0x14, 'Mire Hub') + create_sprite(0x00c2, EnemySprite.BunnyBeam, 0x00, 1, 0x10, 0x14, 'Mire Hub', fix=True) create_sprite(0x00c2, EnemySprite.Firesnake, 0x00, 1, 0x08, 0x16, 'Mire Hub') create_sprite(0x00c2, EnemySprite.SparkCW, 0x00, 1, 0x16, 0x16, 'Mire Hub') create_sprite(0x00c3, EnemySprite.Medusa, 0x00, 0, 0x05, 0x06) @@ -1727,7 +1761,7 @@ def init_vanilla_sprites(): create_sprite(0x00c9, EnemySprite.Popo2, 0x00, 0, 0x10, 0x05, 'Eastern Lobby Bridge') create_sprite(0x00c9, EnemySprite.Popo2, 0x00, 0, 0x0f, 0x06, 'Eastern Lobby Bridge') create_sprite(0x00c9, EnemySprite.Popo2, 0x00, 0, 0x10, 0x07, 'Eastern Lobby Bridge') - create_sprite(0x00cb, EnemySprite.BunnyBeam, 0x00, 0, 0x14, 0x04, 'Thieves Ambush') + create_sprite(0x00cb, EnemySprite.BunnyBeam, 0x00, 0, 0x14, 0x04, 'Thieves Ambush', fix=True) create_sprite(0x00cb, EnemySprite.Firesnake, 0x00, 1, 0x08, 0x09, 'Thieves Ambush') create_sprite(0x00cb, EnemySprite.BlueZazak, 0x00, 1, 0x10, 0x0a, 'Thieves Ambush') create_sprite(0x00cb, EnemySprite.Blob, 0x00, 0, 0x13, 0x0a, 'Thieves Ambush') @@ -1738,7 +1772,7 @@ def init_vanilla_sprites(): create_sprite(0x00cb, EnemySprite.RedZazak, 0x00, 1, 0x08, 0x17, 'Thieves Ambush') create_sprite(0x00cb, EnemySprite.Blob, 0x00, 0, 0x0b, 0x17, 'Thieves Ambush') create_sprite(0x00cb, EnemySprite.Blob, 0x00, 0, 0x0c, 0x18, 'Thieves Ambush') - create_sprite(0x00cb, EnemySprite.BunnyBeam, 0x00, 0, 0x14, 0x1c, 'Thieves Ambush') + create_sprite(0x00cb, EnemySprite.BunnyBeam, 0x00, 0, 0x14, 0x1c, 'Thieves Ambush', fix=True) create_sprite(0x00cc, EnemySprite.Firesnake, 0x00, 0, 0x13, 0x04, 'Thieves BK Corner') create_sprite(0x00cc, EnemySprite.BunnyBeam, 0x00, 1, 0x0b, 0x09, 'Thieves BK Corner') create_sprite(0x00cc, EnemySprite.BlueZazak, 0x00, 1, 0x08, 0x0a, 'Thieves BK Corner') @@ -1815,7 +1849,7 @@ def init_vanilla_sprites(): create_sprite(0x00d9, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x18, 0x1b, 'Eastern False Switches') create_sprite(0x00da, EnemySprite.AntiFairy, 0x00, 0, 0x07, 0x18, 'Eastern Attic Start') create_sprite(0x00da, EnemySprite.AntiFairy, 0x00, 0, 0x08, 0x18, 'Eastern Attic Start') - create_sprite(0x00db, EnemySprite.BunnyBeam, 0x00, 0, 0x03, 0x04, 'Thieves Lobby') + create_sprite(0x00db, EnemySprite.BunnyBeam, 0x00, 0, 0x03, 0x04, 'Thieves Lobby', fix=True) create_sprite(0x00db, EnemySprite.SparkCW, 0x00, 1, 0x0e, 0x0a, 'Thieves Lobby') create_sprite(0x00db, EnemySprite.RedZazak, 0x00, 1, 0x17, 0x0b, 'Thieves Lobby') create_sprite(0x00db, EnemySprite.BlueZazak, 0x00, 1, 0x0f, 0x0c, 'Thieves Lobby') @@ -1917,9 +1951,9 @@ def init_vanilla_sprites(): create_sprite(0x00fa, EnemySprite.Faerie, 0x00, 0, 0x17, 0x0e) create_sprite(0x00fa, EnemySprite.Faerie, 0x00, 0, 0x18, 0x10) create_sprite(0x00fa, EnemySprite.Faerie, 0x00, 0, 0x15, 0x11) - create_sprite(0x00fb, EnemySprite.Bumper, 0x00, 0, 0x17, 0x0d) - create_sprite(0x00fb, EnemySprite.HardhatBeetle, 0x00, 0, 0x19, 0x0a, 'Bumper Cave') - create_sprite(0x00fb, EnemySprite.HardhatBeetle, 0x00, 0, 0x15, 0x12, 'Bumper Cave') + create_sprite(0x00fb, EnemySprite.Bumper, 0x00, 0, 0x17, 0x0d, 'Bumper Cave (top)') + create_sprite(0x00fb, EnemySprite.HardhatBeetle, 0x00, 0, 0x19, 0x0a, 'Bumper Cave (bottom)') + create_sprite(0x00fb, EnemySprite.HardhatBeetle, 0x00, 0, 0x15, 0x12, 'Bumper Cave (bottom)') create_sprite(0x00fd, EnemySprite.MiniMoldorm, 0x00, 0, 0x09, 0x0e, 'Fairy Ascension Cave (Bottom)') create_sprite(0x00fd, EnemySprite.BlueBari, 0x00, 0, 0x05, 0x08, 'Fairy Ascension Cave (Bottom)') create_sprite(0x00fd, EnemySprite.Faerie, 0x00, 0, 0x16, 0x08) @@ -1980,7 +2014,7 @@ def init_vanilla_sprites(): create_sprite(0x0115, EnemySprite.Faerie, 0x00, 0, 0x18, 0x07) create_sprite(0x0115, EnemySprite.Faerie, 0x00, 0, 0x17, 0x08) create_sprite(0x0115, EnemySprite.Faerie, 0x00, 0, 0x18, 0x08) - # create_sprite(0x0115, EnemySprite.FairyPondTrigger, 0x00, 0, 0x07, 0x09) # todo: I think this is gone + create_sprite(0x0115, EnemySprite.FairyPondTrigger, 0x00, 0, 0x07, 0x09) create_sprite(0x0116, EnemySprite.FairyPondTrigger, 0x00, 0, 0x17, 0x18) create_sprite(0x0118, EnemySprite.Shopkeeper, 0x00, 0, 0x19, 0x1b) create_sprite(0x0119, EnemySprite.AdultNpc, 0x00, 0, 0x0e, 0x18) @@ -2014,67 +2048,35 @@ def init_vanilla_sprites(): create_sprite(0x0126, EnemySprite.HeartPiece, 0x00, 0, 0x1c, 0x14) create_sprite(0x0127, EnemySprite.HeartPiece, 0x00, 0, 0x07, 0x16) - -def kill_rules(world, player, stats): - - def h(enemy): - return stats[enemy].health - defeat_rules = { - EnemySprite.MiniHelmasaur: defeat_rule(world, player, h(EnemySprite.MiniHelmasaur), - bomb=2, silver=3, fire=None), - EnemySprite.MiniMoldorm: defeat_rule(world, player, h(EnemySprite.MiniMoldorm), ice=1, hook=True), - EnemySprite.Sluggula: defeat_rule(world, player, h(EnemySprite.Sluggula), bomb=None), - EnemySprite.RedBari: or_rule(has('Fire Rod', player), and_rule(has_sword(player), has('Bombos', player))), - EnemySprite.BlueBari: defeat_rule(world, player, h(EnemySprite.BlueBari), ice=2, hook=True), - EnemySprite.HardhatBeetle: defeat_rule(world, player, h(EnemySprite.HardhatBeetle), - arrow=None, bomb=None, fire=None), - EnemySprite.BlueGuard: defeat_rule(world, player, h(EnemySprite.BlueGuard), ice=1), - EnemySprite.GreenGuard: defeat_rule(world, player, h(EnemySprite.GreenGuard)), - EnemySprite.RedSpearGuard: defeat_rule(world, player, h(EnemySprite.RedSpearGuard)), - EnemySprite.BluesainBolt: defeat_rule(world, player, h(EnemySprite.BluesainBolt)), - EnemySprite.UsainBolt: defeat_rule(world, player, h(EnemySprite.UsainBolt)), - EnemySprite.BlueArcher: defeat_rule(world, player, h(EnemySprite.BlueArcher)), - EnemySprite.GreenBushGuard: defeat_rule(world, player, h(EnemySprite.GreenBushGuard), ice=1), - EnemySprite.RedJavelinGuard: defeat_rule(world, player, h(EnemySprite.RedJavelinGuard)), - EnemySprite.RedBushGuard: defeat_rule(world, player, h(EnemySprite.RedBushGuard)), - EnemySprite.BombGuard: defeat_rule(world, player, h(EnemySprite.BombGuard)), - 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), - EnemySprite.CricketRat: defeat_rule(world, player, h(EnemySprite.CricketRat), hook=True), - EnemySprite.Snake: defeat_rule(world, player, h(EnemySprite.Snake), hook=True), - EnemySprite.Keese: defeat_rule(world, player, h(EnemySprite.Keese), hook=True, boomerang=True), - EnemySprite.Leever: defeat_rule(world, player, h(EnemySprite.Leever)), - EnemySprite.FloatingSkull: defeat_rule(world, player, h(EnemySprite.FloatingSkull), bomb=2), - EnemySprite.Hover: defeat_rule(world, player, h(EnemySprite.Hover), hook=True), - EnemySprite.GreenEyegoreMimic: defeat_rule(world, player, h(EnemySprite.GreenEyegoreMimic), - arrow=2, silver=2, fire=None), - EnemySprite.RedEyegoreMimic: can_bow_kill(world, player, arrow_damage[1], silver_damage[1], - h(EnemySprite.RedEyegoreMimic)), - EnemySprite.Kodongo: defeat_rule(world, player, h(EnemySprite.Kodongo), bomb=None, fire=1), - EnemySprite.Gibdo: defeat_rule(world, player, h(EnemySprite.Gibdo), arrow=0), - EnemySprite.Terrorpin: has('Hammer', player), - EnemySprite.Blob: defeat_rule(world, player, h(EnemySprite.Blob), hook=True, bomb=2), - # bombs are required for quick kills, but cannot collapse him - EnemySprite.StalfosKnight: and_rule(can_use_bombs(world, player), - defeat_rule(world, player, h(EnemySprite.StalfosKnight), - bomb=None, arrow=None, fire=None, boomerang=True)), - EnemySprite.Pengator: defeat_rule(world, player, h(EnemySprite.Pengator), - fire=1, bomb=2, hook=True, boomerang=True), - EnemySprite.Wizzrobe: defeat_rule(world, player, h(EnemySprite.Wizzrobe), fire=1, ice=1), - EnemySprite.Babasu: defeat_rule(world, player, h(EnemySprite.Babasu), ice=2, hook=True), - EnemySprite.Freezor: or_rule(has('Fire Rod', player), and_rule(has_sword(player), has('Bombos', player))), - EnemySprite.BlueZazak: defeat_rule(world, player, h(EnemySprite.BlueZazak), bomb=2), - EnemySprite.RedZazak: defeat_rule(world, player, h(EnemySprite.RedZazak), bomb=2), - EnemySprite.Stalfos: defeat_rule(world, player, h(EnemySprite.Stalfos), fire=1, ice=2, boomerang=True), - EnemySprite.Gibo: defeat_rule(world, player, h(EnemySprite.Gibo), arrow=3, fire=None), - EnemySprite.Pokey: defeat_rule(world, player, h(EnemySprite.Pokey), silver=2, ice=1), - } - return defeat_rules + # Dump above data into yaml + # class HexInt(int): pass + # + # def representer(dumper, data: HexInt): + # return yaml.ScalarNode('tag:yaml.org,2002:int', hex(data)) + # + # def build_dict(z): + # spr = { + # 'super_tile': HexInt(z.super_tile), + # 'kind': enemy_names[z.kind] if z.sub_type != 0x07 else HexInt(z.kind), + # 'sub_type': HexInt(z.sub_type), + # 'layer': z.layer, + # 'tile_x': HexInt(z.tile_x), + # 'tile_y': HexInt(z.tile_y) + # } + # if z.region: + # spr['region'] = z.region + # if z.drops_item: + # spr['drops_item'] = z.drops_item + # if z.drop_item_kind: + # spr['drop_item_kind'] = HexInt(z.drop_item_kind) + # return spr + # + # data_dump = {HexInt(x): [build_dict(z) for z in y] for x, y in vanilla_sprites.items()} + # + # yaml.add_representer(HexInt, representer) + # yaml.add_representer(defaultdict, Representer.represent_dict) + # with open('uw_enemy_list.yaml', 'w') as file: + # yaml.dump(data_dump, file, sort_keys=False) layered_oam_rooms = { @@ -2127,7 +2129,7 @@ exceptions = {0xf1: [4, 5]} # these keese cannot be lured away def valid_drop_location(sprite, index, world, player): if world.dropshuffle[player] == 'underworld': - if sprite.drops_item and sprite.drop_item_kind == 0xe4: + if sprite.drops_item and sprite.drop_item_kind in [0xe4, 0xe5]: # already has a location return False elif sprite.sub_type != SpriteType.Overlord: @@ -2201,212 +2203,6 @@ def add_drop_contents(world, player): world.itempool.append(ItemFactory(item_name, player)) -def or_rule(*rules): - return RuleFactory.disj(rules) - - -def and_rule(*rules): - return RuleFactory.conj(rules) - - -def has(item, player, count=1): - return RuleFactory.item(item, player, count) - - -def has_sword(player): - return or_rule( - has('Fighter Sword', player), has('Master Sword', player), - has('Tempered Sword', player), has('Golden Sword', player) - ) - - -def can_extend_magic(world, player, magic, flag_t=False): - potion_shops = (find_shops_that_sell('Blue Potion', world, player) | - find_shops_that_sell('Green Potion', world, player)) - return RuleFactory.extend_magic(player, magic, world.difficulty_adjustments[player], potion_shops, flag_t) - - -# class 0 damage (subtypes 1 and 2) -def has_boomerang(player): - return or_rule(has('Blue Boomerang', player), has('Red_Boomerang', player)) - - -class_1_damage = {1: 2, 2: 64, 3: 4} # somaria, byrna -arrow_damage = {0: 0, 1: 2, 2: 64, 3: 16} # normal arrows (0 is for when silvers work, but wooden do not) -silver_damage = {1: 100, 2: 24, 3: 100} # silvers -bomb_damage = {1: 4, 2: 64, 6: 32} # bombs -ice_rod_damage = {1: 8, 2: 64, 4: 4} # ice rod -fire_rod_damage = {1: 8, 2: 64, 4: 4, 5: 16} # fire rod - - -# assumes 8 hits per magic bar - a little generous -def can_byrna_kill(world, player, damage, health): - magic_needed = math.ceil(health / damage) - if magic_needed > 8: - return and_rule(has('Cane of Byrna', player), can_extend_magic(world, player, magic_needed)) - else: - return has('Cane of Byrna', player) - - -# assumes 64 hits per somaria magic bar (max is like 80) -def can_somaria_kill(world, player, damage, health): - magic_needed = hits = math.ceil(health / (damage * 64)) - if magic_needed > 8: - return and_rule(has('Cane of Somaria', player), can_extend_magic(world, player, magic_needed)) - else: - return has('Cane of Somaria', player) - - -# assume hitting 8 of 10 bombs? -def can_bombs_kill(world, player, damage, health): - bombs_needed = math.ceil(health / damage) - if bombs_needed > 8: - return RuleFactory.static_rule(False) - return can_use_bombs(world, player) - - -# assume all 8 hit -def can_ice_rod_kill(world, player, damage, health): - magic_needed = math.ceil(health / damage) - if magic_needed > 8: - return and_rule(has('Ice Rod', player), can_extend_magic(world, player, magic_needed)) - else: - return has('Ice Rod', player) - - -# assume all 8 hit -def can_fire_rod_kill(world, player, damage, health): - magic_needed = math.ceil(health / damage) - if magic_needed > 8: - return and_rule(has('Fire Rod', player), can_extend_magic(world, player, magic_needed)) - else: - return has('Fire Rod', player) - - -# 20/30 arrows hit -def can_bow_kill(world, player, damage, silver_damage, health): - wood_arrows_needed = math.ceil(health / damage) if damage != 0 else 999 - if wood_arrows_needed > 20: - silvers_arrows_needed = math.ceil(health / silver_damage) - if silvers_arrows_needed > 20: - return RuleFactory.static_rule(False) - return and_rule(can_shoot_arrows(world, player), has('Silver Arrows', player)) - return can_shoot_arrows(world, player) - - -def can_quake_kill(world, player, damage, health): - magic_needed = math.ceil(health / damage) * 2 - if magic_needed > 8: - return and_rule(has('Quake', player), has_sword(player), can_extend_magic(world, player, magic_needed)) - else: - return and_rule(has('Quake', player), has_sword(player)) - - -def can_ether_kill(world, player, damage, health): - magic_needed = math.ceil(health / damage) * 2 - if magic_needed > 8: - return and_rule(has('Ether', player), has_sword(player), can_extend_magic(world, player, magic_needed)) - else: - return and_rule(has('Ether', player), has_sword(player)) - - -def can_bombos_kill(world, player, damage, health): - magic_needed = math.ceil(health / damage) * 2 - if magic_needed > 8: - return and_rule(has('Bombos', player), has_sword(player), can_extend_magic(world, player, magic_needed)) - else: - return and_rule(has('Bombos', player), has_sword(player)) - - -# main enemy types -def defeat_rule(world, player, health, class1=1, - arrow: typing.Optional[int] = 1, silver=1, - bomb: typing.Optional[int] = 1, - fire: typing.Union[str, int, None] = 'Burn', - ice=None, hook=False, boomerang=False): - rules = [has_blunt_weapon(player), - can_somaria_kill(world, player, class_1_damage[class1], health), - can_byrna_kill(world, player, class_1_damage[class1], health)] - if arrow is not None: - rules.append(can_bow_kill(world, player, arrow_damage[arrow], silver_damage[silver], health)) - if bomb is not None: - rules.append(can_bombs_kill(world, player, bomb_damage[bomb], health)) - if hook: - rules.append(has('Hookshot', player)) - if fire is not None: - if fire == 'Burn': - rules.append(has('Fire Rod', player)) - 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[ice], health)) - if boomerang: - rules.append(has_boomerang(player)) - return or_rule(*rules) - - -def has_blunt_weapon(player): - return or_rule(has_sword(player), has('Hammer', player)) - - -def find_shops_that_sell(item, world, player): - return {shop.region for shop in world.shops[player] if shop.has_unlimited(item) and shop.region.player == player} - - -def can_shoot_arrows(world, 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) - # retro+shopsanity, shops may not sell the Single Arrow - return and_rule(has('Bow', player), or_rule(RuleFactory.unlimited('Single Arrow', player, shops), - has('Single Arrow', player))) - return has('Bow', player) - - -def can_use_bombs(world, player): - return or_rule(RuleFactory.static_rule(not world.bombbag[player]), has('Bomb Upgrade (+10)', player)) - - -special_rules_check = { - 'Swamp Waterway': None, - 'Hera Back': [5, 6], - 'GT Petting Zoo': [1, 4, 5, 7], - 'Mimic Cave': [3, 4], - 'Ice Hookshot Ledge': None, - 'TR Hub Ledges': [3, 4, 5, 6, 7], - 'Old Man Cave': None, - 'Old Man House Back': [4, 5, 6], - 'Death Mountain Return Cave (left)': None, - 'Death Mountain Return Cave (right)': [1, 2, 3, 6, 7] - -} - - -def special_rules_for_region(world, player, region_name, location, original_rule, enemy): - if region_name == 'Swamp Waterway': - stats = world.data_tables[player].enemy_stats[enemy.kind] - return or_rule(can_quake_kill(world, player, 64, stats.health), can_ether_kill(world, player, 16, stats.health), - can_bombos_kill(world, player, 64, stats.health)) - elif region_name in ['Hera Back', 'GT Petting Zoo', 'Mimic Cave']: - enemy_number = int(location.name.split('#')[1]) - if enemy_number in special_rules_check[region_name]: - return and_rule(original_rule, has_boomerang(player)) - else: - return original_rule - elif region_name == 'Ice Hookshot Ledge': # enemizer has these hardcoded to red baris for now - return and_rule(or_rule(has('Fire Rod', player), and_rule(has_sword(player), has('Bombos', player))), - or_rule(has_boomerang(player), has('Hookshot', player))) - elif region_name in ['TR Hub Ledges', 'Old Man Cave', 'Old Man House Back', - 'Death Mountain Return Cave (left)', 'Death Mountain Return Cave (right)']: - enemy_number = int(location.name.split('#')[1]) - if special_rules_check[region_name] is None or enemy_number in special_rules_check[region_name]: - return and_rule(original_rule, or_rule(has_boomerang(player), has('Hookshot', player))) - else: - return original_rule - return original_rule - - enemy_names = { 0x00: 'Raven', 0x01: 'Vulture', @@ -2453,6 +2249,7 @@ enemy_names = { 0x2e: 'FluteKid', 0x2f: 'RaceGameLady', + 0x30: 'RaceGameGuy', 0x31: 'FortuneTeller', 0x32: 'ArgueBros', 0x33: 'RupeePull', @@ -2524,6 +2321,7 @@ enemy_names = { 0x75: 'BottleMerchant', 0x76: 'Zelda', 0x78: 'Grandma', + 0x79: 'Bee', 0x7a: 'Agahnim', 0x7c: 'FloatingSkull', 0x7d: 'BigSpike', @@ -2535,7 +2333,7 @@ enemy_names = { 0x83: 'GreenEyegoreMimic', 0x84: 'RedEyegoreMimic', 0x85: 'YellowStalfos', # falling stalfos that shoots head - 0x86: 'Kondongo', + 0x86: 'Kodongo', 0x88: 'Mothula', 0x8a: 'SpikeBlock', 0x8b: 'Gibdo', @@ -2572,6 +2370,7 @@ enemy_names = { 0xaa: 'Pikit', 0xab: 'CrystalMaiden', # ... OW + 0xac: 'Apple', 0xad: 'OldMan', 0xae: 'PipeDown', 0xaf: 'PipeUp', @@ -2610,15 +2409,141 @@ enemy_names = { 0xd1: 'BunnyBeam', 0xd2: 'FloppingFish', 0xd3: 'Stal', # alive skull rock? + 0xd4: 'Landmine', 0xd5: 'DiggingGameNPC', 0xd6: 'Ganon', + 0xd8: 'SmallHeart', + 0xda: 'BlueRupee', + 0xdb: 'RedRupee', + 0xdc: 'BombRefill1', + 0xdd: 'BombRefill4', + 0xde: 'BombRefill8', + 0xe0: 'LargeMagic', 0xe3: 'Faerie', 0xe4: 'SmallKey', + 0xe7: 'Mushroom', 0xe8: 'FakeMasterSword', 0xe9: 'MagicShopAssistant', 0xeb: 'HeartPiece', 0xed: 'SomariaPlatform', 0xee: 'CastleMantle', 0xf2: 'MedallionTablet', -} \ No newline at end of file + 0xf3: 'PositionTarget', + 0xf4: 'Boulders' +} + +overlord_names = { + 0x01: 'PositionTarget', 0x02: 'FullRoomCannons', 0x03: 'VerticalCanon', + 0x05: 'FallingStalfos', 0x06: 'SnakeTrap', + 0x07: 'MovingFloor', 0x08: 'BlobSpawner', 0x09: 'Wallmaster', + 0x0A: 'FallingSquare', 0x0B: 'FallingBridge', + 0x10: 'Pirogusu_Left', 0x11: 'Pirogusu_Right', 0x12: 'Pirogusu_Top', 0x13: 'Pirogusu_Bottom', + 0x14: 'TileRoom', + 0x15: 'WizzrobeSpawner', 0x16: 'ZoroSpawner', 0x17: 'PotTrap', 0x18: 'InvisibleStalfos', + 0x19: 'ArmosCoordinator', 0x1A: 'BombTrap', +} + +sprite_translation = { + 'RollerVerticalDown': EnemySprite.RollerVerticalDown, + 'RollerVerticalUp': EnemySprite.RollerVerticalUp, + 'RollerHorizontalRight': EnemySprite.RollerHorizontalRight, + 'RollerHorizontalLeft': EnemySprite.RollerHorizontalLeft, + 'AntiFairyCircle': EnemySprite.AntiFairyCircle, + 'Beamos': EnemySprite.Beamos, + 'BigSpike': EnemySprite.BigSpike, + 'SpikeBlock': EnemySprite.SpikeBlock, + 'Bumper': EnemySprite.Bumper, + 'Statue': EnemySprite.Statue, + 'FirebarCW': EnemySprite.FirebarCW, + 'FirebarCCW': EnemySprite.FirebarCCW, + 'SparkCW': EnemySprite.SparkCW, + 'SparkCCW': EnemySprite.SparkCCW, + 'Kodongo': EnemySprite.Kodongo, + 'Antifairy': EnemySprite.AntiFairy, + 'AntiFairy': EnemySprite.AntiFairy, + 'ArmosStatue': EnemySprite.ArmosStatue, + 'Babasu': EnemySprite.Babasu, + 'BallNChain': EnemySprite.BallNChain, + 'Blob': EnemySprite.Blob, + 'BlueArcher': EnemySprite.BlueArcher, + 'BlueBari': EnemySprite.BlueBari, + 'BlueGuard': EnemySprite.BlueGuard, + 'BluesainBolt': EnemySprite.BluesainBolt, + 'BlueZazak': EnemySprite.BlueZazak, + 'BlueZirro': EnemySprite.BlueZirro, + 'BombGuard': EnemySprite.BombGuard, + 'BunnyBeam': EnemySprite.BunnyBeam, + 'Buzzblob': EnemySprite.Buzzblob, + 'CannonTrooper': EnemySprite.CannonTrooper, + 'Chainchomp': EnemySprite.Chainchomp, + 'Crab': EnemySprite.Crab, + 'CricketRat': EnemySprite.CricketRat, + 'Cucco': EnemySprite.Cucco, + 'Deadrock': EnemySprite.Deadrock, + 'Debirando': EnemySprite.Debirando, + 'DebirandoPit': EnemySprite.DebirandoPit, + 'FakeMasterSword': EnemySprite.FakeMasterSword, + 'FireballZora': EnemySprite.FireballZora, + 'Firesnake': EnemySprite.Firesnake, + 'FloatingSkull': EnemySprite.FloatingSkull, + 'FloppingFish': EnemySprite.FloppingFish, + 'FourWayShooter': EnemySprite.FourWayShooter, + 'Freezor': EnemySprite.Freezor, + 'Geldman': EnemySprite.Geldman, + 'Gibdo': EnemySprite.Gibdo, + 'Gibo': EnemySprite.Gibo, + 'GreenBushGuard': EnemySprite.GreenBushGuard, + 'GreenEyegoreMimic': EnemySprite.GreenEyegoreMimic, + 'GreenGuard': EnemySprite.GreenGuard, + 'GreenKnifeGuard': EnemySprite.GreenKnifeGuard, + 'GreenZirro': EnemySprite.GreenZirro, + 'HardhatBeetle': EnemySprite.HardhatBeetle, + 'Hinox': EnemySprite.Hinox, + 'Hoarder': EnemySprite.Hoarder, + 'Hoarder2': EnemySprite.Hoarder2, + 'Hover': EnemySprite.Hover, + 'Keese': EnemySprite.Keese, + 'Kyameron': EnemySprite.Kyameron, + 'Landmine': EnemySprite.Landmine, + 'Leever': EnemySprite.Leever, + 'Lynel': EnemySprite.Lynel, + 'Medusa': EnemySprite.Medusa, + 'MiniHelmasaur': EnemySprite.MiniHelmasaur, + 'MiniMoldorm': EnemySprite.MiniMoldorm, + 'Moblin': EnemySprite.Moblin, + 'Octoballoon': EnemySprite.Octoballoon, + 'Octorok': EnemySprite.Octorok, + 'Octorok4Way': EnemySprite.Octorok4Way, + 'Pengator': EnemySprite.Pengator, + 'Pikit': EnemySprite.Pikit, + 'Poe': EnemySprite.Poe, + 'Pokey': EnemySprite.Pokey, + 'Popo': EnemySprite.Popo, + 'Popo2': EnemySprite.Popo2, + 'Raven': EnemySprite.Raven, + 'RedBari': EnemySprite.RedBari, + 'RedBushGuard': EnemySprite.RedBushGuard, + 'RedEyegoreMimic': EnemySprite.RedEyegoreMimic, + 'RedJavelinGuard': EnemySprite.RedJavelinGuard, + 'RedSpearGuard': EnemySprite.RedSpearGuard, + 'RedZazak': EnemySprite.RedZazak, + 'Ropa': EnemySprite.Ropa, + 'Sluggula': EnemySprite.Sluggula, + 'Snake': EnemySprite.Snake, + 'Snapdragon': EnemySprite.Snapdragon, + 'Stal': EnemySprite.Stal, + 'Stalfos': EnemySprite.Stalfos, + 'StalfosKnight': EnemySprite.StalfosKnight, + 'Swamola': EnemySprite.Swamola, + 'Tektite': EnemySprite.Tektite, + 'Terrorpin': EnemySprite.Terrorpin, + 'Thief': EnemySprite.Thief, + 'Toppo': EnemySprite.Toppo, + 'UsainBolt': EnemySprite.UsainBolt, + 'Vulture': EnemySprite.Vulture, + 'Wallmaster': EnemySprite.Wallmaster, + 'Wizzrobe': EnemySprite.Wizzrobe, + 'Zora': EnemySprite.Zora, + 'Zoro': EnemySprite.Zoro, +} diff --git a/source/enemizer/Bossmizer.py b/source/enemizer/Bossmizer.py index dcc34b21..935b11e3 100644 --- a/source/enemizer/Bossmizer.py +++ b/source/enemizer/Bossmizer.py @@ -124,7 +124,6 @@ def boss_writes(world, player, rom): eye_number = random.randint(0, 8) # randomize moldorm eyes (var + 1) rom.write_byte(snes_to_pc(0x368102), eye_number) # enemizer flag rom.write_byte(snes_to_pc(0x1DDBB3), eye_number) # loop variable - # todo: flag vitreous key fix (prize on the eyes) data_tables = world.data_tables[player] arrghus_can_swim = True water_tiles_on = True diff --git a/source/enemizer/DamageTables.py b/source/enemizer/DamageTables.py index e69de29b..032e9942 100644 --- a/source/enemizer/DamageTables.py +++ b/source/enemizer/DamageTables.py @@ -0,0 +1,8 @@ +from Utils import load_yaml + + +class DamageTable: + def __init__(self): + self.damage_table = load_yaml(['source', 'enemizer', 'damage_table.yaml']) + self.enemy_damage = load_yaml(['source', 'enemizer', 'enemy_damage_table.yaml']) + diff --git a/source/enemizer/Enemizer.py b/source/enemizer/Enemizer.py index 4d63bd1d..e5c6f099 100644 --- a/source/enemizer/Enemizer.py +++ b/source/enemizer/Enemizer.py @@ -1,46 +1,24 @@ import RaceRandom as random from Utils import snes_to_pc -from source.dungeon.EnemyList import SpriteType +from source.dungeon.EnemyList import SpriteType, EnemySprite from source.dungeon.RoomList import Room010C -from source.enemizer.SpriteSheets import sub_group_choices, setup_required_dungeon_groups +from source.enemizer.SpriteSheets import sub_group_choices from source.enemizer.SpriteSheets import randomize_underworld_sprite_sheets, randomize_overworld_sprite_sheets from source.enemizer.TilePattern import tile_patterns -water_rooms = { - 0x16, 0x28, 0x34, 0x36, 0x38, 0x46, 0x66 -} # these room need to be locked on the gfx ID : 17 - -# todo: task list -# anti-fairy shutter logic -# check cucco, implement flag for certain immune enemies that are okay in shutter rooms -# Room 0x16 (sprites 4,5,6 need to be water but 0-3 don't) -# - shutter_sprites = { 0xb8: {0, 1, 2, 3, 4, 5}, 0xb: {4, 5, 6, 7, 8, 9}, 0x1b: {3, 4, 5}, 0x4b: {0, 3, 4}, 0x4: {9, 13, 14}, - 0x24: {3, 5, 6}, # not sure about 6 - bunny beam under pot + 0x24: {3, 4, 5, 6}, # not sure about 6 - bunny beam under pot 0x28: {0, 1, 2, 3, 4}, 0xe: {0, 1, 2, 3}, 0x2e: {0, 1, 2, 3, 4, 5}, 0x3e: {1, 2}, 0x6e: {0, 1, 2, 3, 4}, 0x31: {7, 8, 10}, 0x44: {2, 3, 5}, 0x45: {1, 2, 3}, 0x53: {5, 6, 8, 9, 10}, 0x75: {0, 2, 3, 4, 5}, 0x85: {2, 3, 4, 5}, 0x5d: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, 0x6b: {5, 6, 7, 8, 9, 10, 11, 12, 13}, - 0x6d: {0, 1, 2, 3, 4, 5, 6, 7, 8}, 0x7b: {3, 4, 8}, 0x7d: {4, 5, 6, 7, 8}, 0x8d: {0, 1, 2, 3, 4}, + 0x6d: {0, 1, 2, 3, 4, 5, 6, 7, 8}, 0x7b: {2, 3, 4, 5, 8, 9, 10}, 0x7d: {4, 5, 6, 7, 8, 10}, 0x8d: {0, 1, 2, 3, 4}, 0xa5: {0, 1, 2, 3, 4, 5, 6, 7}, 0x71: {0, 1}, 0xd8: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 0xb0: {0, 1, 2, 3, 4, 5, 7, 8, 9, 10}, 0xc0: {0, 1, 2}, 0xe0: {0, 1, 2, 3}, 0xb2: {5, 6, 7, 10, 11}, 0xd2: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 0xef: {0, 1, 2}, 0x10c: {4, 5, 6, 7}, 0x123: {0, 1, 2, 3} } -water_sprites = { - 0x16: {4, 5, 6}, 0x28: {0, 1, 2, 3}, 0x34: {0, 1, 2}, 0x36: {1, 2, 5, 7, 8}, 0x38: {0, 1, 2, 4, 5, 6}, -} - -# not really shutters: only tiles: -# 0xb6 TR Tile, TR Pokey 1, Chain chomp? -# 0x87 hera tile room? -# 0x3d gt minihelma? -# 0x8d gt tile room? -# 0x96 gt torch cross? - - def setup_specific_requirements(data_tables): requirements = data_tables.sprite_requirements water_groups = set() @@ -78,7 +56,6 @@ def get_possible_sheets(room_id, data_tables, specific, uw_sheets): # forced_req = set() key_needed = False killable_needed = room_id in shutter_sprites - water_needed = room_id in water_rooms for sheet in uw_sheets: if room_id in sheet.room_set: @@ -116,16 +93,14 @@ def get_possible_sheets(room_id, data_tables, specific, uw_sheets): exclude_all_groups = set() exclude_all_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} - if water_needed: - if water_groups: - match_any_room_groups.update(water_groups) - for i in range(0, 4): - if water_sub_groups[i]: - match_any_sub_groups[i].update(water_sub_groups[i]) - else: # exclude water stuff - exclude_all_groups.update(water_groups) - for i in range(0, 4): - exclude_all_sub_groups[i].update(water_sub_groups[i]) + if room_id in data_tables.room_requirements: + required_groups = data_tables.room_requirements[room_id] + for idx, grp in enumerate(required_groups): + if grp is not None: + if isinstance(grp, tuple): + match_any_sub_groups[idx].update(grp) + else: + match_all_sub_groups[idx] = {grp} if key_needed: if key_groups: @@ -142,16 +117,15 @@ def get_possible_sheets(room_id, data_tables, specific, uw_sheets): possible_sheets = [] for sheet in uw_sheets: - str(sheet) - if match_all_room_groups and sheet not in match_all_room_groups: + if match_all_room_groups and sheet.id not in match_all_room_groups: continue if any(match_all_sub_groups[i] and sheet.sub_groups[i] not in match_all_sub_groups[i] for i in range(0, 4)): continue - if exclude_all_groups and sheet in exclude_all_groups: + if exclude_all_groups and sheet.id in exclude_all_groups: continue if any(exclude_all_sub_groups[i] and sheet.sub_groups[i] in exclude_all_sub_groups[i] for i in range(0, 4)): continue - if match_any_room_groups and sheet not in match_any_sub_groups: + if match_any_room_groups and sheet.id not in match_any_sub_groups: continue test_subs = [i for i in range(0, 4) if match_any_sub_groups[i]] if test_subs and all(sheet.sub_groups[i] not in match_any_sub_groups[i] for i in test_subs): @@ -160,21 +134,50 @@ def get_possible_sheets(room_id, data_tables, specific, uw_sheets): return possible_sheets -def get_possible_ow_sheets(area_id, ow_sheets): - # requirements = data_tables.sprite_requirements +def get_possible_ow_sheets(area_id, ow_sheets, data_tables): + requirements = data_tables.sprite_requirements for sheet in ow_sheets: if area_id in sheet.room_set: return [sheet] - # not sure I need to match anything else at this point - return ow_sheets + match_all_room_groups = set() + match_all_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} + + for sprite in data_tables.ow_enemy_table[area_id]: + sprite_secondary = 0 if sprite.sub_type != SpriteType.Overlord else sprite.sub_type + key = (sprite.kind, sprite_secondary) + if key not in requirements: + continue + req = requirements[key] + if isinstance(req, dict): + req = req[area_id] + if req.static or not req.can_randomize: + if req.groups: + match_all_room_groups.intersection_update(req.groups) + if not match_all_room_groups: + match_all_room_groups = set(req.groups) + for i in range(0, 4): + if req.sub_groups[i]: + match_all_sub_groups[i].intersection_update(req.sub_groups[i]) + if not match_all_sub_groups[i]: + match_all_sub_groups[i] = set(req.sub_groups[i]) + + possible_sheets = [] + for sheet in ow_sheets: + if match_all_room_groups and sheet.id not in match_all_room_groups: + continue + if any(match_all_sub_groups[i] and sheet.sub_groups[i] not in match_all_sub_groups[i] for i in range(0, 4)): + continue + possible_sheets.append(sheet) + return possible_sheets -def find_candidate_sprites(data_tables, sheet_range): +def find_candidate_sprites(data_tables, sheet_range, uw=True): requirements = data_tables.sprite_requirements - uw_sprite_candidates = [] - uw_sheet_candidates = [] + sprite_candidates = [] + sheet_candidates = [] + all_sheets = [] candidate_groups = set() candidate_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} @@ -182,22 +185,24 @@ def find_candidate_sprites(data_tables, sheet_range): for k, r in requirements.items(): if isinstance(r, dict): continue - if not r.static and r.uw_valid and not r.dont_use: + valid_flag = (uw and r.uw_valid) or (not uw and r.ow_valid) + if not r.static and valid_flag and not r.dont_use: candidate_groups.update(r.groups) for i in range(0, 4): candidate_sub_groups[i].update(r.sub_groups[i]) - uw_sprite_candidates.append(k) + sprite_candidates.append(k) for num in sheet_range: sheet = data_tables.sprite_sheets[num] + all_sheets.append(sheet) if candidate_groups and sheet not in candidate_groups: continue test_subs = [i for i in range(0, 4) if candidate_sub_groups[i]] if test_subs and all(sheet.sub_groups[i] not in candidate_sub_groups[i] for i in test_subs): continue - uw_sheet_candidates.append(sheet) + sheet_candidates.append(sheet) - return uw_sprite_candidates, uw_sheet_candidates + return sprite_candidates, sheet_candidates, all_sheets def get_possible_enemy_sprites(room_id, sheet, uw_sprites, data_tables): @@ -217,7 +222,7 @@ def get_possible_enemy_sprites_ow(sheet, sprites, data_tables): requirement = data_tables.sprite_requirements[sprite] if isinstance(requirement, dict): continue - if sheet.valid_sprite(requirement): + if sheet.valid_sprite(requirement) and requirement.ow_valid: ret.append(requirement) return ret @@ -232,7 +237,7 @@ def get_randomize_able_sprites(room_id, data_tables): req = data_tables.sprite_requirements[key] if isinstance(req, dict): continue - if not req.static and req.can_randomize: + if not req.static and req.can_randomize and not sprite.static: sprite_table[idx] = sprite return sprite_table @@ -252,61 +257,66 @@ def get_randomize_able_sprites_ow(area_id, data_tables): return sprite_table -# RandomizeRooms(optionFlags); def randomize_underworld_rooms(data_tables): - # RoomCollection.RandomizeRoomSpriteGroups - # randomize room sprite sheets specific = setup_specific_requirements(data_tables) - uw_candidates, uw_sheets = find_candidate_sprites(data_tables, range(65, 124)) + uw_candidates, uw_sheets, all_sheets = find_candidate_sprites(data_tables, range(65, 124)) for room_id in range(0, 0x128): if room_id in {0, 1, 3, 6, 7, 0xd, 0x14, 0x1c, 0x20, 0x29, 0x30, 0x33, - 0x4d, 0x5a, 0x7F, 0x90, 0xa4, 0xac, 0xc8, 0xde}: + 0x4d, 0x5a, 0x90, 0xa4, 0xac, 0xc8, 0xde}: continue if room_id not in data_tables.uw_enemy_table.room_map: continue # sprite_reqs = data_tables.sprite_requirements randomizeable_sprites = get_randomize_able_sprites(room_id, data_tables) + if not randomizeable_sprites: + candidate_sheets = get_possible_sheets(room_id, data_tables, specific, all_sheets) + chosen_sheet = random.choice(candidate_sheets) + data_tables.room_headers[room_id].sprite_sheet = chosen_sheet.id - 0x40 if randomizeable_sprites: - candidate_sheets = get_possible_sheets(room_id, data_tables, specific, uw_sheets) + candidate_sheets = get_possible_sheets(room_id, data_tables, specific, all_sheets) done = False while not done: chosen_sheet = random.choice(candidate_sheets) data_tables.room_headers[room_id].sprite_sheet = chosen_sheet.id - 0x40 candidate_sprites = get_possible_enemy_sprites(room_id, chosen_sheet, uw_candidates, data_tables) randomized = True - if room_id in water_rooms: - water_sprites = [x for x in candidate_sprites if x.water_only] - if len(water_sprites) == 0: - randomized = False + wallmaster_chosen = room_id in {0x0039, 0x0049, 0x0056, 0x0057, 0x0068, 0x008d} + for i, sprite in randomizeable_sprites.items(): + # filter out water if necessary + candidate_sprites = [x for x in candidate_sprites if not x.water_only or sprite.water] + # filter out wallmaster if already on tile + if wallmaster_chosen: + candidate_sprites = [x for x in candidate_sprites if x.sprite != EnemySprite.Wallmaster] + if sprite.drops_item: + choice_list = [x for x in candidate_sprites if x.good_for_key_drop()] + elif room_id in shutter_sprites and i in shutter_sprites[room_id]: + choice_list = [x for x in candidate_sprites if x.good_for_shutter()] else: - for i, sprite in randomizeable_sprites.items(): - chosen = random.choice(water_sprites) - sprite.kind = chosen.sprite - else: - # todo: stal sprites - for i, sprite in randomizeable_sprites.items(): - if sprite.drops_item: - choice_list = [x for x in candidate_sprites if x.good_for_key_drop() and not x.water_only] - elif room_id in shutter_sprites and i in shutter_sprites[room_id]: - choice_list = [x for x in candidate_sprites if x.good_for_shutter() and not x.water_only] - else: - choice_list = [x for x in candidate_sprites if not x.water_only] - if len(choice_list) == 0: - randomized = False - break - chosen = random.choice(choice_list) - sprite.kind = chosen.sprite + choice_list = [x for x in candidate_sprites if not x.water_only] + choice_list = filter_choices(choice_list, room_id, i, data_tables.uw_enemy_denials) + if len(choice_list) == 0: + randomized = False + break + weight = [data_tables.uw_weights[r.sprite] for r in choice_list] + chosen = random.choices(choice_list, weight, k=1)[0] + sprite.kind = chosen.sprite + if chosen.sprite == EnemySprite.Wallmaster: + wallmaster_chosen = True + sprite.kind = 0x09 + sprite.sub_type = SpriteType.Overlord done = randomized # done with sprites # done with rooms -def randomize_overworld_enemies(data_tables, randomize_bush_sprites): - # todo: decision on stump/bird - # original kodongo discovery? - # rom.write_byte(snes_to_pc(0x09CF4F), 0x10) //move bird from tree stump in lost woods - ow_candidates, ow_sheets = find_candidate_sprites(data_tables, range(1, 64)) +def filter_choices(options, room_id, sprite_idx, denials): + key = room_id, sprite_idx + return [x for x in options if key not in denials or x.sprite not in denials[key]] + + +def randomize_overworld_enemies(data_tables): + ow_candidates, ow_sheets, all_sheets = find_candidate_sprites(data_tables, range(1, 64), False) areas_to_randomize = [0, 2, 3, 5, 7, 0xA, 0xF, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1d, 0x1e, 0x22, 0x25, 0x28, 0x29, 0x2A, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x32, 0x33, 0x34, 0x35, 0x37, 0x3a, 0x3b, 0x3c, 0x3f] @@ -314,28 +324,85 @@ def randomize_overworld_enemies(data_tables, randomize_bush_sprites): area_list += [0x80, 0x81] + [x + 0x90 for x in areas_to_randomize] # specials + post aga LW for area_id in area_list: randomizeable_sprites = get_randomize_able_sprites_ow(area_id, data_tables) - if randomizeable_sprites: - candidate_sheets = get_possible_ow_sheets(area_id, ow_sheets) + if not randomizeable_sprites: + candidate_sheets = get_possible_ow_sheets(area_id, all_sheets, data_tables) + chosen_sheet = random.choice(candidate_sheets) + data_tables.overworld_sprite_sheets[area_id] = chosen_sheet + candidate_sprites = get_possible_enemy_sprites_ow(chosen_sheet, ow_candidates, data_tables) + else: + candidate_sheets = get_possible_ow_sheets(area_id, ow_sheets, data_tables) chosen_sheet = random.choice(candidate_sheets) data_tables.overworld_sprite_sheets[area_id] = chosen_sheet candidate_sprites = get_possible_enemy_sprites_ow(chosen_sheet, ow_candidates, data_tables) for i, sprite in randomizeable_sprites.items(): - chosen = random.choice(candidate_sprites) - sprite.kind = chosen - if randomize_bush_sprites: - pass - # todo: randomize the bush sprite + weight = [data_tables.ow_weights[r.sprite] for r in candidate_sprites] + chosen = random.choices(candidate_sprites, weight, k=1)[0] + sprite.kind = chosen.sprite + # randomize the bush sprite per area + weight = [data_tables.ow_weights[r.sprite] for r in candidate_sprites] + bush_sprite_choice = random.choices(candidate_sprites, weight, k=1)[0] + data_tables.bush_sprite_table[area_id] = bush_sprite_choice + + +# damage and health tables only go to F2 +skip_sprites = { + EnemySprite.ArmosKnight, EnemySprite.Lanmolas, EnemySprite.Moldorm, EnemySprite.Mothula, EnemySprite.Arrghus, + EnemySprite.HelmasaurKing, EnemySprite.Vitreous, EnemySprite.TrinexxRockHead, EnemySprite.TrinexxFireHead, + EnemySprite.TrinexxIceHead, EnemySprite.Blind, EnemySprite.Kholdstare, EnemySprite.KholdstareShell, + EnemySprite.FallingIce, EnemySprite.Arrghi, EnemySprite.Agahnim, EnemySprite.Ganon, + EnemySprite.PositionTarget, EnemySprite.Boulders +} def randomize_enemies(world, player): if world.enemy_shuffle[player] != 'none': data_tables = world.data_tables[player] - randomize_underworld_sprite_sheets(data_tables.sprite_sheets) + randomize_underworld_sprite_sheets(data_tables.sprite_sheets, data_tables) randomize_underworld_rooms(data_tables) randomize_overworld_sprite_sheets(data_tables.sprite_sheets) - randomize_overworld_enemies(data_tables, world.enemy_shuffle[player] == 'random') - # todo: health shuffle - # todo: damage shuffle + randomize_overworld_enemies(data_tables) + # fix thief stats + subclass_table = world.damage_table[player].damage_table['SubClassTable'] + subclass_table[EnemySprite.Thief] = subclass_table[EnemySprite.GreenEyegoreMimic] + data_tables.enemy_stats[EnemySprite.Thief].health = 4 + # todo: could turn droppable on here if we wanted + # health shuffle + if world.enemy_health[player] != 'default': + stats = world.data_tables[player].enemy_stats + min_health = {'easy': 1, 'normal': 2, 'hard': 2, 'expert': 4} + max_health = {'easy': 4, 'normal': 15, 'hard': 25, 'expert': 50} + min_h = min_health[world.enemy_health[player]] + max_h = max_health[world.enemy_health[player]] + for sprite, stat in stats.items(): + if sprite == EnemySprite.Octorok4Way: + stat.health = stats[EnemySprite.Octorok].health # these guys share data + elif sprite not in skip_sprites: + if isinstance(stat.health, tuple): + stat.health = random.randint(min_h, max_h), random.randint(min_h, max_h) + else: + stat.health = random.randint(min_h, max_h) + if world.enemy_damage[player] != 'default': + stats = world.data_tables[player].enemy_stats + # randomize damage groupings + for sprite, stat in stats.items(): + if sprite == EnemySprite.Octorok4Way: + stat.damage = stats[EnemySprite.Octorok].damage # these guys share data + elif sprite not in skip_sprites: + if isinstance(stat.damage, tuple): + stat.damage = random.randint(0, 8), random.randint(0, 8) + else: + stat.damage = random.randint(0, 8) + # randomize bump table + for i in range(0, 10): + max_damage = 64 if i == 9 or world.enemy_damage[player] == 'random' else 32 + green_mail = random.randint(0, max_damage) + if world.enemy_damage[player] == 'random': + blue_mail = random.randint(0, max_damage) + red_mail = random.randint(0, max_damage) + else: + blue_mail = (green_mail * 3) // 4 + red_mail = (green_mail * 3) // 8 + world.data_tables[player].enemy_damage[i] = [green_mail, blue_mail, red_mail] def write_enemy_shuffle_settings(world, player, rom): diff --git a/source/enemizer/EnemizerTestHarness.py b/source/enemizer/EnemizerTestHarness.py index 6e52933d..5013fcc0 100644 --- a/source/enemizer/EnemizerTestHarness.py +++ b/source/enemizer/EnemizerTestHarness.py @@ -3,41 +3,83 @@ from collections import Counter, defaultdict from source.dungeon.EnemyList import enemy_names, SpriteType from source.enemizer.Enemizer import randomize_underworld_rooms -from source.enemizer.SpriteSheets import randomize_underworld_sprite_sheets +from source.enemizer.SpriteSheets import randomize_underworld_sprite_sheets, randomize_overworld_sprite_sheets from source.rom.DataTables import init_data_tables +from source.enemizer.DamageTables import DamageTable import RaceRandom as random -if __name__ == '__main__': - random.seed(42) - - stats = defaultdict(Counter) - column_headers = {} +def calculate_odds(): + ctr_uw = Counter() + ctr_ow = Counter() for trial in range(0, 100): - world = SimpleNamespace(pottery={1: 'none'}) + world = SimpleNamespace(pottery={1: 'none'}, damage_table={1: DamageTable()}) data_tables = init_data_tables(world, 1) - randomize_underworld_sprite_sheets(data_tables.sprite_sheets) - randomize_underworld_rooms(data_tables) - for room_id, enemy_list in data_tables.uw_enemy_table.room_map.items(): - # print(f'Room {hex(room_id)}:') - for i, sprite in enumerate(enemy_list): - if sprite.sub_type == SpriteType.Overlord: - result = f'O{hex(sprite.kind)}' - else: - result = enemy_names[sprite.kind] - if result not in column_headers: - column_headers[result] = None - stats[(room_id, i)][result] += 1 - with open('result.csv', 'w') as result_file: - result_file.write('room_id,slot,') - result_file.write(','.join(column_headers.keys())) - result_file.write('\n') + randomize_underworld_sprite_sheets(data_tables.sprite_sheets, data_tables) + randomize_overworld_sprite_sheets(data_tables.sprite_sheets) - for key, counter in stats.items(): - rid, slot = key - result_file.write(f'{rid},{slot}') - for result_item in column_headers.keys(): - result_file.write(f',{counter[result_item]}') - result_file.write('\n') + for num in range(65, 124): + sheet = data_tables.sprite_sheets[num] + ret = [] + for req in data_tables.sprite_requirements.values(): + if not isinstance(req, dict) and sheet.valid_sprite(req) and not req.overlord and not req.static: + ret.append(enemy_names[req.sprite]) + for x in ret: + ctr_uw[x] += 1 + + for num in range(1, 64): + sheet = data_tables.sprite_sheets[num] + ret = [] + for req in data_tables.sprite_requirements.values(): + if not isinstance(req, dict) and sheet.valid_sprite(req) and not req.overlord and not req.static: + ret.append(enemy_names[req.sprite]) + for x in ret: + ctr_ow[x] += 1 + ttl = sum(ctr_uw.values()) + print(f'UW: # Total {ttl}') + for k, v in ctr_uw.items(): + weight = round(.01 * ttl * 100 / v) + print(f' {k}: {weight} # {v*100/ttl:.5f}% raw:{v}') + ttl = sum(ctr_ow.values()) + print(f'OW: # Total {ttl}') + for k, v in ctr_ow.items(): + weight = round(.01 * ttl * 100 / v) + print(f' {k}: {weight} # {v*100/ttl:.5f}% raw:{v}') + + +if __name__ == '__main__': + calculate_odds() + # random.seed(42) + # + # stats = defaultdict(Counter) + # column_headers = {} + # + # for trial in range(0, 100): + # world = SimpleNamespace(pottery={1: 'none'}) + # data_tables = init_data_tables(world, 1) + # + # randomize_underworld_sprite_sheets(data_tables.sprite_sheets) + # randomize_underworld_rooms(data_tables) + # for room_id, enemy_list in data_tables.uw_enemy_table.room_map.items(): + # # print(f'Room {hex(room_id)}:') + # for i, sprite in enumerate(enemy_list): + # if sprite.sub_type == SpriteType.Overlord: + # result = f'O{hex(sprite.kind)}' + # else: + # result = enemy_names[sprite.kind] + # if result not in column_headers: + # column_headers[result] = None + # stats[(room_id, i)][result] += 1 + # with open('result.csv', 'w') as result_file: + # result_file.write('room_id,slot,') + # result_file.write(','.join(column_headers.keys())) + # result_file.write('\n') + # + # for key, counter in stats.items(): + # rid, slot = key + # result_file.write(f'{rid},{slot}') + # for result_item in column_headers.keys(): + # result_file.write(f',{counter[result_item]}') + # result_file.write('\n') diff --git a/source/enemizer/EnemyLogic.py b/source/enemizer/EnemyLogic.py new file mode 100644 index 00000000..e036e773 --- /dev/null +++ b/source/enemizer/EnemyLogic.py @@ -0,0 +1,494 @@ +import math +from collections import defaultdict + +import RaceRandom as random + +from source.logic.Rule import RuleFactory +from source.dungeon.EnemyList import EnemySprite + + +# these are for drops only +def defeat_rule_single(world, player, enemy_sprite, region): + if enemy_sprite.kind == EnemySprite.Terrorpin: + # must be flipped + return has('Hammer', player) + elif enemy_sprite.kind == EnemySprite.RedBari: + # must be burned to drop + return or_rule(has('Fire Rod', player), and_rule(has_sword(player), has('Bombos', player))) + vln = enemy_vulnerability(world, player, enemy_sprite, region) + rules = [] + if vln['Blunt'] != 0: + rules.append(has_blunt_weapon(player)) + if vln['Stun'] != 0: + rules.append(buzzblob_rule(player)) + if vln['Somaria'] != 0: + rules.append(somaria_rule(world, player, vln['Somaria'])) + if vln['Byrna'] != 0: + rules.append(byrna_rule(world, player, vln['Byrna'])) + if vln['Master'] != 0: + rules.append(has_class_2_weapon(player)) + if vln['Bow'] != 0: + rules.append(bow_rule(world, player, vln['Bow'])) + if vln['Silvers'] != 0: + rules.append(silvers_rule(world, player, vln['Silvers'])) + if vln['Bomb'] != 0: + rules.append(bombs_rule(world, player, vln['Bomb'])) + if vln['Hookshot'] != 0: + rules.append(has('Hookshot', player)) + if vln['IceRod'] != 0: + rules.append(ice_rod_rule(world, player, vln['IceRod'])) + if vln['FireRod'] != 0: + rules.append(fire_rod_rule(world, player, vln['FireRod'])) + if vln['Boomerang'] != 0: + rules.append(has_boomerang(player)) + if vln['Powder'] != 0: + rules.append(magic_powder_rule(world, player, vln['Powder'])) + # skip medallions if vln to Blunt? + if vln['Bombos'] != 0 and vln['Blunt'] == 0: + rules.append(medallion_rule(world, player, 'Bombos', vln['Bombos'])) + if vln['Ether'] != 0 and vln['Blunt'] == 0: + rules.append(medallion_rule(world, player, 'Ether', vln['Ether'])) + if vln['Quake'] != 0 and vln['Blunt'] == 0: + rules.append(medallion_rule(world, player, 'Quake', vln['Quake'])) + if enemy_sprite.kind == EnemySprite.StalfosKnight: + # must be bombed to be made vulnerable + return and_rule(can_use_bombs(world, player), or_rule(*rules)) + return or_rule(*rules) + + +damage_cost = { + 'Bomb': 1, 'Bow': 1, 'Silvers': 1, + 'Powder': .5, 'Somaria': .5, 'Byrna': 1.125, + 'FireRod': 1, 'IceRod': 1, + 'Bombos': 2, 'Ether': 2, 'Quake': 2 +} + +# damage_set = ['Blunt', 'Stun', 'Master', 'Tempered', 'Boomerang', 'Hookshot', 'Bomb', 'Silvers', 'Bow', +# 'Somaria', 'Powder', 'FireRod', 'IceRod', 'Byrna', 'Bombos', 'Ether', 'Quake'] + + +# these are for "challenge" rooms +def defeat_rule_multiple(world, player, enemy_sprite_region_pairs): + vln_list = {} + for sprite, region in enemy_sprite_region_pairs: + vln_list[(sprite, region)] = enemy_vulnerability(world, player, sprite, region) + + # damage_accounting = {x: list(y) for x, y in damage_types.items()} + used_resources = {'Bomb': 0, 'Arrow': 0, 'Magic': 0} + required_rules = [] + picky_enemies = [] + hammer_required = False + bombs_required = False + + for key, vln in vln_list.items(): + if key[0] == EnemySprite.Terrorpin: + if not hammer_required: + required_rules.append(has('Hammer', player)) + hammer_required = True + picky_enemies.append(key) + continue + if key[0] == EnemySprite.StalfosKnight: + if not bombs_required: + required_rules.append(bombs_rule(world, player, 1)) + bombs_required = True + used_resources['Bomb'] += 1 + picky_enemies.append(key) + continue + vln_types = [k for k in vln.keys() if vln[k] != 0] + if len(vln_types) == 1: + d_type = vln_types[0] + required_rules.append(defeat_rule_single(world, player, key[0], key[1])) + picky_enemies.append(key) + if d_type in damage_cost: + cost = damage_cost[d_type] + if d_type == 'Bomb': + used_resources['Bomb'] += cost + elif d_type in ['Bow', 'Silvers']: + used_resources['Arrow'] += cost + else: + used_resources['Magic'] += cost + vln_list = {k: v for k, v in vln_list.items() if k not in picky_enemies} + + while len(vln_list) > 0: + + optional_clears = find_possible_rules(vln_list, used_resources, world, player) + if len(optional_clears) == 0: + raise Exception('Kill rules seems to be insufficient for this enemy set, please report:' + + ', '.join([str(x) for x, y in enemy_sprite_region_pairs])) + + # find rules which kill the most + # idea: this could be multiple criteria: most-constrained then which method kills the most + best_rules = {} + best_size = 0 + for vln_option in optional_clears.keys(): + if len(vln_option) > best_size: + best_size = len(vln_option) + best_rules.clear() + best_rules[vln_option] = optional_clears[vln_option] + elif len(vln_option) == best_size: # assumes vln_option is different from prior options + best_rules[vln_option] = optional_clears[vln_option] + if len(best_rules) == 1: + vln_option, rule_pair_list = next(iter(best_rules.items())) + else: + vln_option, rule_pair_list = random.choice(list(best_rules.items())) + if best_size == 0: + raise Exception('Invulnerable enemy? rules seems to be insufficient for this enemy set, please report:' + + ', '.join([str(x) for x, y in enemy_sprite_region_pairs])) + + new_vln_list = {vln_kv[0]: vln_kv[1] for idx, vln_kv in enumerate(vln_list.items()) if idx not in vln_option} + rules_to_add = [rule for rule, resources in rule_pair_list] + resources_to_use = [resources for rule, resources in rule_pair_list] + required_rules.append(or_rule(*rules_to_add)) + for r in resources_to_use: + for k, v in r.items(): + used_resources[k] += v + vln_list = new_vln_list + + return and_rule(*required_rules) + + +def find_possible_rules(vln_list, used_resources, world, player): + optional_clears = defaultdict(list) + blunt_marker = defaultdict(bool) + for damage_type in ['Blunt', 'Stun', 'Master', 'Boomerang', 'Hookshot']: + # all_vln = all(vln[damage_type] != 0 for vln in vln_list.values()) + vln_sub_list = frozenset({idx for idx, vln in enumerate(vln_list.values()) if vln[damage_type] != 0}) + if vln_sub_list: + if damage_type == 'Blunt': + optional_clears[vln_sub_list].append((has_blunt_weapon(player), {})) + blunt_marker[vln_sub_list] = True + if damage_type == 'Stun': + optional_clears[vln_sub_list].append((buzzblob_rule(player), {})) + if damage_type == 'Master' and not blunt_marker[vln_sub_list]: + optional_clears[vln_sub_list].append((has_class_2_weapon(player), {})) + if damage_type == 'Boomerang': + optional_clears[vln_sub_list].append((has('Hookshot', player), {})) + elif damage_type == 'Hookshot': + optional_clears[vln_sub_list].append((has_boomerang(player), {})) + damage_type = 'Bomb' + vln_sub_list = frozenset({idx for idx, vln in enumerate(vln_list.values()) if vln[damage_type] != 0}) + if vln_sub_list: + hits = needed_resources(damage_type, vln_list) + if hits + used_resources['Bomb'] <= 8: + optional_clears[vln_sub_list].append( + (bombs_rule(world, player, hits + used_resources['Bomb']), {'Bomb': hits})) + for damage_type in ['Bow', 'Silvers']: + vln_sub_list = frozenset({idx for idx, vln in enumerate(vln_list.values()) if vln[damage_type] != 0}) + if vln_sub_list: + hits = needed_resources(damage_type, vln_list) + resources = {'Arrow': hits} + if damage_type == 'Bow' and hits + used_resources['Arrow'] <= 25: + optional_clears[vln_sub_list].append( + (bow_rule(world, player, hits + used_resources['Arrow']), resources)) + if damage_type == 'Silvers' and hits + used_resources['Arrow'] <= 25: + optional_clears[vln_sub_list].append( + (silvers_rule(world, player, hits + used_resources['Arrow']), resources)) + for damage_type in ['Powder', 'Somaria', 'Byrna', 'FireRod', 'IceRod', 'Bombos', 'Ether', 'Quake']: + vln_sub_list = frozenset({idx for idx, vln in enumerate(vln_list.values()) if vln[damage_type] != 0}) + if vln_sub_list: + hits = needed_resources(damage_type, vln_list) + resources = {'Magic': damage_cost[damage_type] * hits} + if damage_type == 'Powder' and math.ceil(hits / 16) * 8 + used_resources['Magic'] <= 160: + flag = min(vln[damage_type] for vln in vln_list.values()) + flag = flag if flag < 0 else (hits + used_resources['Magic'] * 2) + optional_clears[vln_sub_list].append((magic_powder_rule(world, player, flag), resources)) + elif damage_type == 'Somaria' and math.ceil(hits / 64) * 8 + used_resources['Magic'] <= 160: + flag = min(vln[damage_type] for vln in vln_list.values()) + flag = flag if flag < 0 else (hits + used_resources['Magic'] * 8) + optional_clears[vln_sub_list].append((somaria_rule(world, player, flag), resources)) + elif damage_type == 'Byrna' and math.ceil(hits / 7) * 8 + used_resources['Magic'] <= 160: + flag = min(vln[damage_type] for vln in vln_list.values()) + flag = flag if flag < 0 else (hits + used_resources['Magic'] * 7 / 8) + optional_clears[vln_sub_list].append((byrna_rule(world, player, flag), resources)) + elif damage_type == 'FireRod' and hits + used_resources['Magic'] <= 160: + flag = min(vln[damage_type] for vln in vln_list.values()) + flag = flag if flag < 0 else (hits + used_resources['Magic']) + optional_clears[vln_sub_list].append((fire_rod_rule(world, player, flag), resources)) + elif damage_type == 'IceRod' and hits + used_resources['Magic'] <= 160: + flag = min(vln[damage_type] for vln in vln_list.values()) + flag = flag if flag < 0 else (hits + used_resources['Magic']) + optional_clears[vln_sub_list].append((ice_rod_rule(world, player, flag), resources)) + elif hits * 2 + used_resources['Magic'] <= 160 and not blunt_marker[vln_sub_list]: + flag = min(vln[damage_type] for vln in vln_list.values()) + flag = flag if flag < 0 else (hits + used_resources['Magic'] / 2) + optional_clears[vln_sub_list].append((medallion_rule(world, player, damage_type, flag), resources)) + return optional_clears + + +def needed_resources(damage_type, vln_list): + return sum(vln[damage_type] if vln[damage_type] >= 0 else 1 for vln in vln_list.values() if vln[damage_type] != 0) + + +special_rules_check = { + 'Swamp Waterway': None, + 'Hera Back': [5, 6], + 'GT Petting Zoo': [1, 4, 5, 7], + 'Mimic Cave': [3, 4], + 'Ice Hookshot Ledge': None, + 'TR Hub Ledges': [3, 4, 5, 6, 7], + 'Old Man Cave': None, + 'Old Man House Back': [4, 5, 6], + 'Death Mountain Return Cave (left)': None, + 'Death Mountain Return Cave (right)': [1, 2, 3, 6, 7] +} + + +def special_rules_for_region(world, player, region_name, location, original_rule): + if region_name == 'Swamp Waterway': # todo: check on enemizer interaction + return or_rule(medallion_rule(world, player, 'Quake', 1), + medallion_rule(world, player, 'Ether', 1), + medallion_rule(world, player, 'Bombos', 1)) + elif region_name in ['Hera Back', 'GT Petting Zoo', 'Mimic Cave']: + enemy_number = int(location.name.split('#')[1]) + if enemy_number in special_rules_check[region_name]: + return and_rule(original_rule, has_boomerang(player)) + else: + return original_rule + elif region_name in ['TR Hub Ledges', 'Ice Hookshot Ledge', 'Old Man Cave', 'Old Man House Back', + 'Death Mountain Return Cave (left)', 'Death Mountain Return Cave (right)']: + enemy_number = int(location.name.split('#')[1]) + if special_rules_check[region_name] is None or enemy_number in special_rules_check[region_name]: + return and_rule(original_rule, or_rule(has_boomerang(player), has('Hookshot', player))) + else: + return original_rule + return original_rule + + +def has_blunt_weapon(player): + return or_rule(has_sword(player), has('Hammer', player)) + + +def buzzblob_rule(player): + return or_rule(has('Golden Sword', player), + and_rule(has_blunt_weapon(player), + or_rule(has_boomerang(player), has('Hookshot', player)))) # freeze it? + + +def has_class_2_weapon(player): + return or_rule(has_beam_sword(player), has('Hammer', player)) + + +def somaria_rule(world, player, somaria_hits): + if somaria_hits == -1: + return has('Cane of Somaria', player) # insta-kill somaria? - not in vanilla + else: + magic_needed = math.ceil(somaria_hits / 64) * 8 # 64 hits per magic bar - 80 max? + if magic_needed > 8: + return and_rule(has('Cane of Somaria', player), can_extend_magic(world, player, magic_needed)) + else: + return has('Cane of Somaria', player) + + +def byrna_rule(world, player, byrna_hits): + if byrna_hits == -1: + return has('Cane of Byrna', player) # insta-kill byrna? - not in vanilla + else: + magic_needed = math.ceil(byrna_hits / 7) * 8 # 7 hits per magic bar - generous? + if magic_needed > 8: + return and_rule(has('Cane of Byrna', player), can_extend_magic(world, player, magic_needed)) + else: + return has('Cane of Byrna', player) + + +def bow_rule(world, player, arrows): + if arrows == -1 or 0 < arrows <= 25: + return can_shoot_normal_arrows(world, player) + return RuleFactory.static_rule(False) + + +def silvers_rule(world, player, arrows): + if arrows == -1 or 0 < arrows <= 25: + return can_shoot_silver_arrows(world, player) + return RuleFactory.static_rule(False) + + +def bombs_rule(world, player, bombs): + if bombs == -1 or 0 < bombs <= 8: + return can_use_bombs(world, player) + return RuleFactory.static_rule(False) + + +def ice_rod_rule(world, player, shots): + if shots == -1: + return has('Ice Rod', player) + if shots > 8: + return and_rule(has('Ice Rod', player), can_extend_magic(world, player, shots)) + else: + return has('Ice Rod', player) + + +def fire_rod_rule(world, player, shots): + if shots == -1: + return has('Fire Rod', player) + if shots > 8: + return and_rule(has('Fire Rod', player), can_extend_magic(world, player, shots)) + else: + return has('Fire Rod', player) + + +def magic_powder_rule(world, player, shots): + if shots == -1: + return has('Magic Powder', player) + if shots == -2: + # todo: other resources possible I guess - harder to keep track of though + return and_rule(has('Magic Powder', player), or_rule(has_blunt_weapon(player), has('Hookshot', player))) + magic_needed = math.ceil(shots / 16) * 8 # 16 tries per magic bar, that could be tight... + if magic_needed > 8: + return and_rule(has('Magic Powder', player), can_extend_magic(world, player, shots)) + else: + return has('Magic Powder', player) + + +def medallion_rule(world, player, medallion, shots): + if shots == -1: + return and_rule(has(medallion, player), has_sword(player)) + if shots == -2: + return and_rule(has(medallion, player), has_sword(player)) + magic_needed = shots * 2 + if magic_needed > 8: + return and_rule(has(medallion, player), has_sword(player), can_extend_magic(world, player, shots)) + else: + return and_rule(has(medallion, player), has_sword(player)) + + +def or_rule(*rules): + return RuleFactory.disj(rules) + + +def and_rule(*rules): + return RuleFactory.conj(rules) + + +def has(item, player, count=1): + return RuleFactory.item(item, player, count) + + +def has_sword(player): + return or_rule( + has('Fighter Sword', player), has('Master Sword', player), + has('Tempered Sword', player), has('Golden Sword', player) + ) + + +def has_beam_sword(player): + return or_rule( + has('Master Sword', player), has('Tempered Sword', player), has('Golden Sword', player) + ) + + +def has_class_3_sword(player): + return or_rule( + has('Tempered Sword', player), has('Golden Sword', player) + ) + + +def can_extend_magic(world, player, magic, flag_t=False): + potion_shops = (find_shops_that_sell('Blue Potion', world, player) | + find_shops_that_sell('Green Potion', world, player)) + return RuleFactory.extend_magic(player, magic, world.difficulty_adjustments[player], potion_shops, flag_t) + + +# class 0 damage (subtypes 1 and 2) +def has_boomerang(player): + return or_rule(has('Blue Boomerang', player), has('Red_Boomerang', player)) + + +def find_shops_that_sell(item, world, player): + return {shop.region for shop in world.shops[player] if shop.has_unlimited(item) and shop.region.player == player} + + +def can_shoot_normal_arrows(world, player): + if world.bow_mode[player].startswith('retro'): + shops = find_shops_that_sell('Single Arrow', world, player) + # retro+shopsanity, shops may not sell the Single Arrow at all + if world.bow_mode[player] == 'retro_silvers': + # non-progressive silvers grant wooden arrows, so shop may not be needed + return and_rule(has('Bow', player), or_rule(RuleFactory.unlimited('Single Arrow', player, shops), + has('Single Arrow', player), has('Silver Arrows', player))) + else: + return and_rule(has('Bow', player), or_rule(RuleFactory.unlimited('Single Arrow', player, shops), + has('Single Arrow', player))) + return has('Bow', player) + + +def can_shoot_silver_arrows(world, player): + # retro_silver requires the silver arrows item which is sufficient for the quiver + if world.bow_mode[player] == 'retro': + shops = find_shops_that_sell('Single Arrow', world, player) + # retro+shopsanity, shops may not sell the Single Arrow at all + return and_rule(has('Silver Arrows', player), or_rule(RuleFactory.unlimited('Single Arrow', player, shops), + has('Single Arrow', player))) + return and_rule(has('Bow', player), has('Silver Arrows', player)) + + +def can_use_bombs(world, player): + return or_rule(RuleFactory.static_rule(not world.bombbag[player]), has('Bomb Upgrade (+10)', player)) + + +def enemy_vulnerability(world, player, enemy_sprite, region): + damage_table = world.damage_table[player].damage_table + stats = world.data_tables[player].enemy_stats + damage_src = damage_table['DamageSource'] + sub_class_table = damage_table['SubClassTable'] + + enemy_sub_class = sub_class_table[enemy_sprite.kind] + + vulnerability = defaultdict(int) + + c1 = number_of_hits('Sword1', damage_src, enemy_sub_class, stats, enemy_sprite, region) + if c1 != 0: + if enemy_sprite.kind == EnemySprite.Buzzblob: + vulnerability['Stun'] = -1 + else: + vulnerability['Blunt'] = -1 + vulnerability['Master'] = -1 + vulnerability['Somaria'] = c1 + vulnerability['Byrna'] = c1 + else: + c2 = number_of_hits('Sword3', damage_src, enemy_sub_class, stats, enemy_sprite, region) + if c2 != 0: + vulnerability['Master'] = -1 # currently Lynels are only vulnerable to only master spins or above + hits = number_of_hits('Arrow', damage_src, enemy_sub_class, stats, enemy_sprite, region) + if hits != 0: + vulnerability['Bow'] = hits + hits = number_of_hits('SilverArrow', damage_src, enemy_sub_class, stats, enemy_sprite, region) + if hits != 0: + vulnerability['Silvers'] = hits + for method in ['Bomb', 'Hookshot', 'FireRod', 'IceRod', 'Boomerang', 'Powder', 'Bombos', 'Ether', 'Quake']: + hits = number_of_hits(method, damage_src, enemy_sub_class, stats, enemy_sprite, region) + if hits != 0: + vulnerability[method] = hits + return vulnerability + + +def number_of_hits(source_name, damage_src, enemy_sub_class, stats, enemy_sprite, region): + damage_class = damage_src[source_name]['class'] + sub_class = enemy_sub_class[damage_class] + damage_amount = damage_src[source_name]['subclass'][sub_class] + if damage_amount == 0: + return 0 + elif damage_amount <= 0x64: + health = stats[enemy_sprite.kind].health + if isinstance(health, tuple): + if enemy_sprite.kind in [EnemySprite.Tektite, EnemySprite.HardhatBeetle]: + idx = enemy_sprite.tile_x & 0x1 + health = health[idx] + elif region.is_light_world and region.is_dark_world: + health = min(health) + elif region.is_light_world: + health = health[0] + elif region.is_dark_world: + health = health[1] + else: + health = max(health) + return math.ceil(health / damage_amount) + elif damage_amount in [0xF9, 0xFA, 0xFD]: + # -1 faired or incinerated; -2 blobbed + # F9: fairy, defeated, but doesn't drop anything + # FA: blobbed - can you kill a blob? = -2 + # FD: incinerated + return -1 if damage_amount != 0xFA else -2 + else: + return 0 + + diff --git a/source/enemizer/OwEnemyList.py b/source/enemizer/OwEnemyList.py index 03c6b2b3..90882a4e 100644 --- a/source/enemizer/OwEnemyList.py +++ b/source/enemizer/OwEnemyList.py @@ -2,10 +2,14 @@ from source.dungeon.EnemyList import Sprite, EnemySprite vanilla_sprites_ow = {} -def create_sprite(area_id, kind, tile_x, tile_y, region=None, address=None): +def create_sprite(area_id, kind, tile_x, tile_y, region=None, address=None, fix=True, water=False): if area_id not in vanilla_sprites_ow: vanilla_sprites_ow[area_id] = [] sprite = Sprite(area_id, kind, 0, 0, tile_x, tile_y, region, False, None) + if water: + sprite.water = True + if fix: + sprite.static = True sprite.original_address = address vanilla_sprites_ow[area_id].append(sprite) @@ -76,7 +80,7 @@ def init_vanilla_sprites_ow(): create_sprite(0x4a, EnemySprite.Ropa, 0x0E, 0x18, '', 0x09CBEE) create_sprite(0x4a, EnemySprite.Stal, 0x14, 0x1A, '', 0x09CBF1) # Screen4F: - create_sprite(0x4f, EnemySprite.FireballZora, 0x19, 0x08, '', 0x09CBF5) + create_sprite(0x4f, EnemySprite.FireballZora, 0x19, 0x08, '', 0x09CBF5, water=True) create_sprite(0x4f, EnemySprite.Catfish, 0x04, 0x0B, '', 0x09CBF8) create_sprite(0x4f, EnemySprite.Stal, 0x18, 0x0D, '', 0x09CBFB) create_sprite(0x4f, EnemySprite.Ropa, 0x1A, 0x11, '', 0x09CBFE) @@ -123,13 +127,13 @@ def init_vanilla_sprites_ow(): create_sprite(0x55, EnemySprite.Bee, 0x0A, 0x1A, '', 0x09CC6D) create_sprite(0x55, EnemySprite.Ropa, 0x1A, 0x1B, '', 0x09CC70) # Screen56: - create_sprite(0x56, EnemySprite.FireballZora, 0x0A, 0x06, '', 0x09CC74) - create_sprite(0x56, EnemySprite.FireballZora, 0x13, 0x0A, '', 0x09CC77) + create_sprite(0x56, EnemySprite.FireballZora, 0x0A, 0x06, '', 0x09CC74, water=True) + create_sprite(0x56, EnemySprite.FireballZora, 0x13, 0x0A, '', 0x09CC77, water=True) create_sprite(0x56, EnemySprite.Bee, 0x04, 0x0E, '', 0x09CC7A) create_sprite(0x56, EnemySprite.Ropa, 0x11, 0x17, '', 0x09CC7D) create_sprite(0x56, EnemySprite.Ropa, 0x05, 0x1A, '', 0x09CC80) # Screen57: - create_sprite(0x57, EnemySprite.FireballZora, 0x0C, 0x04, '', 0x09CC84) + create_sprite(0x57, EnemySprite.FireballZora, 0x0C, 0x04, '', 0x09CC84, water=True) create_sprite(0x57, EnemySprite.Octorok, 0x16, 0x08, '', 0x09CC87) create_sprite(0x57, EnemySprite.Octorok, 0x18, 0x0A, '', 0x09CC8A) create_sprite(0x57, EnemySprite.Octorok, 0x0E, 0x0E, '', 0x09CC8D) @@ -257,17 +261,17 @@ def init_vanilla_sprites_ow(): create_sprite(0x6f, EnemySprite.Stal, 0x09, 0x17, '', 0x09CDDD) # Screen70: create_sprite(0x70, EnemySprite.Raven, 0x21, 0x1B, '', 0x09CDE1) - create_sprite(0x70, EnemySprite.FireballZora, 0x2B, 0x1C, '', 0x09CDE4) - create_sprite(0x70, EnemySprite.FireballZora, 0x12, 0x21, '', 0x09CDE7) - create_sprite(0x70, EnemySprite.Swamola, 0x1B, 0x24, '', 0x09CDEA) - create_sprite(0x70, EnemySprite.Swamola, 0x10, 0x27, '', 0x09CDED) + create_sprite(0x70, EnemySprite.FireballZora, 0x2B, 0x1C, '', 0x09CDE4, water=True) + create_sprite(0x70, EnemySprite.FireballZora, 0x12, 0x21, '', 0x09CDE7, water=True) + create_sprite(0x70, EnemySprite.Swamola, 0x1B, 0x24, '', 0x09CDEA, water=True) + create_sprite(0x70, EnemySprite.Swamola, 0x10, 0x27, '', 0x09CDED, water=True) create_sprite(0x70, EnemySprite.Raven, 0x07, 0x28, '', 0x09CDF0) - create_sprite(0x70, EnemySprite.FireballZora, 0x16, 0x2B, '', 0x09CDF3) - create_sprite(0x70, EnemySprite.FireballZora, 0x1E, 0x2E, '', 0x09CDF6) - create_sprite(0x70, EnemySprite.Swamola, 0x17, 0x33, '', 0x09CDF9) - create_sprite(0x70, EnemySprite.FireballZora, 0x11, 0x38, '', 0x09CDFC) - create_sprite(0x70, EnemySprite.FireballZora, 0x23, 0x2B, '', 0x09CDFF) - create_sprite(0x70, EnemySprite.Swamola, 0x27, 0x2C, '', 0x09CE02) + create_sprite(0x70, EnemySprite.FireballZora, 0x16, 0x2B, '', 0x09CDF3, water=True) + create_sprite(0x70, EnemySprite.FireballZora, 0x1E, 0x2E, '', 0x09CDF6, water=True) + create_sprite(0x70, EnemySprite.Swamola, 0x17, 0x33, '', 0x09CDF9, water=True) + create_sprite(0x70, EnemySprite.FireballZora, 0x11, 0x38, '', 0x09CDFC, water=True) + create_sprite(0x70, EnemySprite.FireballZora, 0x23, 0x2B, '', 0x09CDFF, water=True) + create_sprite(0x70, EnemySprite.Swamola, 0x27, 0x2C, '', 0x09CE02, water=True) # Screen72: create_sprite(0x72, EnemySprite.TalkingTree, 0x1B, 0x0B, '', 0x09CE06) create_sprite(0x72, EnemySprite.BlueGuard, 0x10, 0x0D, '', 0x09CE09) @@ -293,31 +297,31 @@ def init_vanilla_sprites_ow(): create_sprite(0x75, EnemySprite.BlueGuard, 0x09, 0x07, '', 0x09CE3F) create_sprite(0x75, EnemySprite.BlueGuard, 0x0B, 0x09, '', 0x09CE42) create_sprite(0x75, EnemySprite.Octorok, 0x07, 0x13, '', 0x09CE45) - create_sprite(0x75, EnemySprite.GreenZirro, 0x18, 0x16, '', 0x09CE48) + create_sprite(0x75, EnemySprite.GreenZirro, 0x18, 0x16, '', 0x09CE48, water=True) create_sprite(0x75, EnemySprite.Pikit, 0x09, 0x17, '', 0x09CE4B) - create_sprite(0x75, EnemySprite.FireballZora, 0x30, 0x0C, '', 0x09CE4E) + create_sprite(0x75, EnemySprite.FireballZora, 0x30, 0x0C, '', 0x09CE4E, water=True) create_sprite(0x75, EnemySprite.BlueZirro, 0x29, 0x11, '', 0x09CE51) create_sprite(0x75, EnemySprite.GreenZirro, 0x36, 0x15, '', 0x09CE54) create_sprite(0x75, EnemySprite.Pikit, 0x31, 0x1F, '', 0x09CE57) - create_sprite(0x75, EnemySprite.FireballZora, 0x1B, 0x22, '', 0x09CE5A) - create_sprite(0x75, EnemySprite.GreenZirro, 0x14, 0x28, '', 0x09CE5D) + create_sprite(0x75, EnemySprite.FireballZora, 0x1B, 0x22, '', 0x09CE5A, water=True) + create_sprite(0x75, EnemySprite.GreenZirro, 0x14, 0x28, '', 0x09CE5D, water=True) create_sprite(0x75, EnemySprite.Pikit, 0x16, 0x2E, '', 0x09CE60) create_sprite(0x75, EnemySprite.GreenZirro, 0x19, 0x32, '', 0x09CE63) create_sprite(0x75, EnemySprite.BlueZirro, 0x0A, 0x35, '', 0x09CE66) create_sprite(0x75, EnemySprite.Ropa, 0x08, 0x39, '', 0x09CE69) - create_sprite(0x75, EnemySprite.FireballZora, 0x1B, 0x39, '', 0x09CE6C) + create_sprite(0x75, EnemySprite.FireballZora, 0x1B, 0x39, '', 0x09CE6C, water=True) create_sprite(0x75, EnemySprite.Pikit, 0x2A, 0x26, '', 0x09CE6F) create_sprite(0x75, EnemySprite.GreenZirro, 0x32, 0x28, '', 0x09CE72) - create_sprite(0x75, EnemySprite.FireballZora, 0x2A, 0x2C, '', 0x09CE75) - create_sprite(0x75, EnemySprite.FireballZora, 0x32, 0x35, '', 0x09CE78) + create_sprite(0x75, EnemySprite.FireballZora, 0x2A, 0x2C, '', 0x09CE75, water=True) + create_sprite(0x75, EnemySprite.FireballZora, 0x32, 0x35, '', 0x09CE78, water=True) create_sprite(0x75, EnemySprite.Octorok, 0x37, 0x39, '', 0x09CE7B) # Screen77: create_sprite(0x77, EnemySprite.Octorok, 0x11, 0x08, '', 0x09CE7F) create_sprite(0x77, EnemySprite.Stal, 0x09, 0x0A, '', 0x09CE82) create_sprite(0x77, EnemySprite.BlueZirro, 0x0D, 0x0B, '', 0x09CE85) create_sprite(0x77, EnemySprite.Octorok, 0x18, 0x11, '', 0x09CE88) - create_sprite(0x77, EnemySprite.FireballZora, 0x07, 0x12, '', 0x09CE8B) - create_sprite(0x77, EnemySprite.FireballZora, 0x12, 0x19, '', 0x09CE8E) + create_sprite(0x77, EnemySprite.FireballZora, 0x07, 0x12, '', 0x09CE8B, water=True) + create_sprite(0x77, EnemySprite.FireballZora, 0x12, 0x19, '', 0x09CE8E, water=True) # Screen7A: create_sprite(0x7a, EnemySprite.Hinox, 0x06, 0x07, '', 0x09CE92) create_sprite(0x7a, EnemySprite.Ropa, 0x16, 0x09, '', 0x09CE95) @@ -341,7 +345,7 @@ def init_vanilla_sprites_ow(): create_sprite(0x7f, EnemySprite.GreenZirro, 0x10, 0x06, '', 0x09CEC5) create_sprite(0x7f, EnemySprite.Octorok, 0x16, 0x06, '', 0x09CEC8) create_sprite(0x7f, EnemySprite.Whirlpool, 0x07, 0x0C, '', 0x09CECB) - create_sprite(0x7f, EnemySprite.FireballZora, 0x07, 0x0E, '', 0x09CECE) + create_sprite(0x7f, EnemySprite.FireballZora, 0x07, 0x0E, '', 0x09CECE, water=True) create_sprite(0x7f, EnemySprite.GreenZirro, 0x0D, 0x13, '', 0x09CED1) create_sprite(0x7f, EnemySprite.Pikit, 0x16, 0x14, '', 0x09CED4) create_sprite(0x7f, EnemySprite.Octorok, 0x0F, 0x17, '', 0x09CED7) @@ -356,34 +360,34 @@ def init_vanilla_sprites_ow(): create_sprite(0x80, EnemySprite.Hobo, 0x16, 0x04, '', 0x09CEF0) # Screen81: create_sprite(0x81, EnemySprite.HeartPiece, 0x1B, 0x26, '', 0x09CEF4) - create_sprite(0x81, EnemySprite.Zora, 0x0A, 0x06, '', 0x09CEF7) - create_sprite(0x81, EnemySprite.Zora, 0x1C, 0x06, '', 0x09CEFA) - create_sprite(0x81, EnemySprite.FireballZora, 0x11, 0x07, '', 0x09CEFD) - create_sprite(0x81, EnemySprite.Zora, 0x16, 0x0A, '', 0x09CF00) - create_sprite(0x81, EnemySprite.FireballZora, 0x1A, 0x0A, '', 0x09CF03) - create_sprite(0x81, EnemySprite.FireballZora, 0x09, 0x0C, '', 0x09CF06) - create_sprite(0x81, EnemySprite.FireballZora, 0x12, 0x0D, '', 0x09CF09) - create_sprite(0x81, EnemySprite.Zora, 0x1A, 0x12, '', 0x09CF0C) - create_sprite(0x81, EnemySprite.Zora, 0x07, 0x13, '', 0x09CF0F) - create_sprite(0x81, EnemySprite.Zora, 0x14, 0x13, '', 0x09CF12) - create_sprite(0x81, EnemySprite.Zora, 0x08, 0x18, '', 0x09CF15) - create_sprite(0x81, EnemySprite.Zora, 0x04, 0x1C, '', 0x09CF18) + create_sprite(0x81, EnemySprite.Zora, 0x0A, 0x06, '', 0x09CEF7, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x1C, 0x06, '', 0x09CEFA, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x11, 0x07, '', 0x09CEFD, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x16, 0x0A, '', 0x09CF00, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x1A, 0x0A, '', 0x09CF03, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x09, 0x0C, '', 0x09CF06, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x12, 0x0D, '', 0x09CF09, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x1A, 0x12, '', 0x09CF0C, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x07, 0x13, '', 0x09CF0F, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x14, 0x13, '', 0x09CF12, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x08, 0x18, '', 0x09CF15, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x04, 0x1C, '', 0x09CF18, water=True) create_sprite(0x81, EnemySprite.KingZora, 0x3B, 0x04, '', 0x09CF1B) - create_sprite(0x81, EnemySprite.FireballZora, 0x27, 0x08, '', 0x09CF1E) - create_sprite(0x81, EnemySprite.FireballZora, 0x2D, 0x08, '', 0x09CF21) - create_sprite(0x81, EnemySprite.Zora, 0x22, 0x0E, '', 0x09CF24) - create_sprite(0x81, EnemySprite.FireballZora, 0x2D, 0x0E, '', 0x09CF27) - create_sprite(0x81, EnemySprite.FireballZora, 0x21, 0x14, '', 0x09CF2A) - create_sprite(0x81, EnemySprite.Zora, 0x0D, 0x20, '', 0x09CF2D) - create_sprite(0x81, EnemySprite.Zora, 0x08, 0x31, '', 0x09CF30) - create_sprite(0x81, EnemySprite.FireballZora, 0x14, 0x31, '', 0x09CF33) - create_sprite(0x81, EnemySprite.Zora, 0x0C, 0x33, '', 0x09CF36) - create_sprite(0x81, EnemySprite.FireballZora, 0x0E, 0x35, '', 0x09CF39) - create_sprite(0x81, EnemySprite.Zora, 0x08, 0x38, '', 0x09CF3C) - create_sprite(0x81, EnemySprite.Zora, 0x3B, 0x28, '', 0x09CF3F) - create_sprite(0x81, EnemySprite.Zora, 0x3A, 0x2B, '', 0x09CF42) - create_sprite(0x81, EnemySprite.Zora, 0x2D, 0x35, '', 0x09CF45) - create_sprite(0x81, EnemySprite.Zora, 0x37, 0x36, '', 0x09CF48) + create_sprite(0x81, EnemySprite.FireballZora, 0x27, 0x08, '', 0x09CF1E, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x2D, 0x08, '', 0x09CF21, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x22, 0x0E, '', 0x09CF24, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x2D, 0x0E, '', 0x09CF27, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x21, 0x14, '', 0x09CF2A, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x0D, 0x20, '', 0x09CF2D, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x08, 0x31, '', 0x09CF30, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x14, 0x31, '', 0x09CF33, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x0C, 0x33, '', 0x09CF36, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x0E, 0x35, '', 0x09CF39, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x08, 0x38, '', 0x09CF3C, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x3B, 0x28, '', 0x09CF3F, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x3A, 0x2B, '', 0x09CF42, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x2D, 0x35, '', 0x09CF45, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x37, 0x36, '', 0x09CF48, water=True) # Screen00_1: create_sprite(0x0, EnemySprite.FakeMasterSword, 0x07, 0x12, '', 0x09CF4C) create_sprite(0x0, EnemySprite.Raven, 0x12, 0x0B, '', 0x09CF4F) @@ -450,7 +454,7 @@ def init_vanilla_sprites_ow(): # Screen0F_1: create_sprite(0xf, EnemySprite.Waterfall, 0x06, 0x02, '', 0x09CFFD) create_sprite(0xf, EnemySprite.Crab, 0x0D, 0x0D, '', 0x09D000) - create_sprite(0xf, EnemySprite.FireballZora, 0x05, 0x10, '', 0x09D003) + create_sprite(0xf, EnemySprite.FireballZora, 0x05, 0x10, '', 0x09D003, water=True) create_sprite(0xf, EnemySprite.Crab, 0x11, 0x12, '', 0x09D006) create_sprite(0xf, EnemySprite.Whirlpool, 0x08, 0x13, '', 0x09D009) create_sprite(0xf, EnemySprite.Raven, 0x1C, 0x15, '', 0x09D00C) @@ -513,9 +517,10 @@ def init_vanilla_sprites_ow(): create_sprite(0x1a, EnemySprite.GreenGuard, 0x0C, 0x0E, '', 0x09D0A3) create_sprite(0x1a, EnemySprite.Faerie, 0x0D, 0x11, '', 0x09D0A6) create_sprite(0x1a, EnemySprite.BlueRupee, 0x17, 0x17, '', 0x09D0A9) - create_sprite(0x1a, EnemySprite.SmallHeart, 0x0A, 0x18, '', 0x09D0AC) - create_sprite(0x1a, EnemySprite.RedSpearGuard, 0x0F, 0x18, '', 0x09D0AF) + # create_sprite(0x1a, EnemySprite.SmallHeart, 0x0A, 0x18, '', 0x09D0AC) + create_sprite(0x1a, EnemySprite.RedSpearGuard, 0x0F, 0x18, '', 0x09D0AC) # was 0x09D0AF # Screen1B_1: + create_sprite(0x1b, EnemySprite.Wiseman, 0x19, 0x12, '', 0x09D0B0) create_sprite(0x1b, EnemySprite.LightningGate, 0x1F, 0x06, '', 0x09D0B3) create_sprite(0x1b, EnemySprite.RedBushGuard, 0x09, 0x11, '', 0x09D0B6) create_sprite(0x1b, EnemySprite.RedBushGuard, 0x0A, 0x13, '', 0x09D0B9) @@ -556,7 +561,7 @@ def init_vanilla_sprites_ow(): create_sprite(0x1e, EnemySprite.ArmosStatue, 0x3D, 0x29, '', 0x09D11E) create_sprite(0x1e, EnemySprite.Octorok, 0x2E, 0x3B, '', 0x09D121) # Screen22_1: - create_sprite(0x22, EnemySprite.BunnyBeam, 0x0C, 0x04, '', 0x09D125) + create_sprite(0x22, EnemySprite.BunnyBeam, 0x0C, 0x04, '', 0x09D125, fix=True) # smithy smoke create_sprite(0x22, EnemySprite.GreenGuard, 0x17, 0x12, '', 0x09D128) create_sprite(0x22, EnemySprite.Cucco, 0x12, 0x14, '', 0x09D12B) # Screen25_1: @@ -595,7 +600,7 @@ def init_vanilla_sprites_ow(): # Screen2E_1: create_sprite(0x2e, EnemySprite.BlueGuard, 0x0E, 0x0C, '', 0x09D186) create_sprite(0x2e, EnemySprite.BlueGuard, 0x17, 0x0E, '', 0x09D189) - create_sprite(0x2e, EnemySprite.FireballZora, 0x05, 0x12, '', 0x09D18C) + create_sprite(0x2e, EnemySprite.FireballZora, 0x05, 0x12, '', 0x09D18C, water=True) create_sprite(0x2e, EnemySprite.Octorok, 0x19, 0x17, '', 0x09D18F) # Screen2F_1: create_sprite(0x2f, EnemySprite.BlueGuard, 0x0F, 0x0C, '', 0x09D193) @@ -650,30 +655,30 @@ def init_vanilla_sprites_ow(): create_sprite(0x35, EnemySprite.HeartPiece, 0x19, 0x13, '', 0x09D21C) create_sprite(0x35, EnemySprite.Buzzblob, 0x19, 0x14, '', 0x09D21F) create_sprite(0x35, EnemySprite.Crab, 0x07, 0x17, '', 0x09D222) - create_sprite(0x35, EnemySprite.FireballZora, 0x11, 0x17, '', 0x09D225) - create_sprite(0x35, EnemySprite.FireballZora, 0x25, 0x0D, '', 0x09D228) + create_sprite(0x35, EnemySprite.FireballZora, 0x11, 0x17, '', 0x09D225, water=True) + create_sprite(0x35, EnemySprite.FireballZora, 0x25, 0x0D, '', 0x09D228, water=True) create_sprite(0x35, EnemySprite.Buzzblob, 0x27, 0x1F, '', 0x09D22B) create_sprite(0x35, EnemySprite.Buzzblob, 0x2F, 0x1F, '', 0x09D22E) create_sprite(0x35, EnemySprite.Octorok, 0x0A, 0x35, '', 0x09D231) - create_sprite(0x35, EnemySprite.FireballZora, 0x14, 0x35, '', 0x09D234) + create_sprite(0x35, EnemySprite.FireballZora, 0x14, 0x35, '', 0x09D234, water=True) create_sprite(0x35, EnemySprite.Raven, 0x0F, 0x35, '', 0x09D237) create_sprite(0x35, EnemySprite.Octorok, 0x0B, 0x39, '', 0x09D23A) create_sprite(0x35, EnemySprite.Buzzblob, 0x19, 0x3A, '', 0x09D23D) create_sprite(0x35, EnemySprite.Crab, 0x11, 0x3B, '', 0x09D240) - create_sprite(0x35, EnemySprite.FireballZora, 0x24, 0x2B, '', 0x09D243) + create_sprite(0x35, EnemySprite.FireballZora, 0x24, 0x2B, '', 0x09D243, water=True) create_sprite(0x35, EnemySprite.Whirlpool, 0x29, 0x2B, '', 0x09D246) - create_sprite(0x35, EnemySprite.FireballZora, 0x39, 0x31, '', 0x09D249) - create_sprite(0x35, EnemySprite.FireballZora, 0x21, 0x36, '', 0x09D24C) + create_sprite(0x35, EnemySprite.FireballZora, 0x39, 0x31, '', 0x09D249, water=True) + create_sprite(0x35, EnemySprite.FireballZora, 0x21, 0x36, '', 0x09D24C, water=True) create_sprite(0x35, EnemySprite.Buzzblob, 0x32, 0x37, '', 0x09D24F) create_sprite(0x35, EnemySprite.Buzzblob, 0x34, 0x39, '', 0x09D252) create_sprite(0x35, EnemySprite.Crab, 0x2E, 0x3A, '', 0x09D255) # Screen37_1: - create_sprite(0x35, EnemySprite.Crab, 0x08, 0x08, '', 0x09D259) - create_sprite(0x35, EnemySprite.Crab, 0x10, 0x08, '', 0x09D25C) + create_sprite(0x37, EnemySprite.Crab, 0x08, 0x08, '', 0x09D259) + create_sprite(0x37, EnemySprite.Crab, 0x10, 0x08, '', 0x09D25C) create_sprite(0x37, EnemySprite.Crab, 0x0F, 0x0B, '', 0x09D25F) create_sprite(0x37, EnemySprite.Crab, 0x16, 0x11, '', 0x09D262) create_sprite(0x37, EnemySprite.Raven, 0x0C, 0x15, '', 0x09D265) - create_sprite(0x37, EnemySprite.FireballZora, 0x12, 0x19, '', 0x09D268) + create_sprite(0x37, EnemySprite.FireballZora, 0x12, 0x19, '', 0x09D268, water=True) # Screen3A_1: create_sprite(0x3a, EnemySprite.Locksmith, 0x17, 0x05, '', 0x09D26C) create_sprite(0x3a, EnemySprite.Raven, 0x0E, 0x09, '', 0x09D26F) @@ -682,10 +687,10 @@ def init_vanilla_sprites_ow(): # Screen3B_1: create_sprite(0x3b, EnemySprite.GreenBushGuard, 0x13, 0x06, '', 0x09D279) create_sprite(0x3b, EnemySprite.BlueArcher, 0x0C, 0x0A, '', 0x09D27C) - create_sprite(0x3b, EnemySprite.FloppingFish, 0x13, 0x0D, '', 0x09D27F) + create_sprite(0x3b, EnemySprite.FloppingFish, 0x13, 0x0D, '', 0x09D27F, water=True) create_sprite(0x3b, EnemySprite.Raven, 0x08, 0x0B, '', 0x09D282) create_sprite(0x3b, EnemySprite.HeartPiece, 0x14, 0x0E, '', 0x09D285) - create_sprite(0x3b, EnemySprite.FloppingFish, 0x1B, 0x10, '', 0x09D288) + create_sprite(0x3b, EnemySprite.FloppingFish, 0x1B, 0x10, '', 0x09D288, water=True) create_sprite(0x3b, EnemySprite.Toppo, 0x0F, 0x14, '', 0x09D28B) create_sprite(0x3b, EnemySprite.Raven, 0x14, 0x1B, '', 0x09D28E) # Screen3C_1: @@ -699,7 +704,7 @@ def init_vanilla_sprites_ow(): # Screen3F_1: create_sprite(0x3f, EnemySprite.Octorok, 0x11, 0x04, '', 0x09D2A8) create_sprite(0x3f, EnemySprite.Octorok, 0x16, 0x05, '', 0x09D2AB) - create_sprite(0x3f, EnemySprite.FireballZora, 0x08, 0x0B, '', 0x09D2AE) + create_sprite(0x3f, EnemySprite.FireballZora, 0x08, 0x0B, '', 0x09D2AE, water=True) create_sprite(0x3f, EnemySprite.Whirlpool, 0x07, 0x0C, '', 0x09D2B1) create_sprite(0x3f, EnemySprite.Octoballoon, 0x10, 0x16, '', 0x09D2B4) # Screen00_2: @@ -768,8 +773,8 @@ def init_vanilla_sprites_ow(): # Screen0F_2: create_sprite(0x9f, EnemySprite.Waterfall, 0x06, 0x02, '', 0x09D369) create_sprite(0x9f, EnemySprite.Crab, 0x0D, 0x0D, '', 0x09D36C) - create_sprite(0x9f, EnemySprite.FireballZora, 0x05, 0x10, '', 0x09D36F) - create_sprite(0x9f, EnemySprite.FireballZora, 0x0A, 0x11, '', 0x09D372) + create_sprite(0x9f, EnemySprite.FireballZora, 0x05, 0x10, '', 0x09D36F, water=True) + create_sprite(0x9f, EnemySprite.FireballZora, 0x0A, 0x11, '', 0x09D372, water=True) create_sprite(0x9f, EnemySprite.Crab, 0x11, 0x12, '', 0x09D375) create_sprite(0x9f, EnemySprite.Whirlpool, 0x08, 0x13, '', 0x09D378) create_sprite(0x9f, EnemySprite.Octorok4Way, 0x0E, 0x17, '', 0x09D37B) @@ -834,9 +839,10 @@ def init_vanilla_sprites_ow(): create_sprite(0xaa, EnemySprite.BlueGuard, 0x0F, 0x08, '', 0x09D418) create_sprite(0xaa, EnemySprite.BlueGuard, 0x0C, 0x0E, '', 0x09D41B) create_sprite(0xaa, EnemySprite.Faerie, 0x0D, 0x11, '', 0x09D41E) - create_sprite(0xaa, EnemySprite.SmallHeart, 0x0A, 0x18, '', 0x09D421) - create_sprite(0xaa, EnemySprite.UsainBolt, 0x0F, 0x18, '', 0x09D424) + # create_sprite(0xaa, EnemySprite.SmallHeart, 0x0A, 0x18, '', 0x09D421) + create_sprite(0xaa, EnemySprite.UsainBolt, 0x0F, 0x18, '', 0x09D421) # was 0x09D424 # Screen1B_2: + create_sprite(0x1b, EnemySprite.Wiseman, 0x19, 0x12, '', 0x09D425) create_sprite(0xab, EnemySprite.UsainBolt, 0x06, 0x0D, '', 0x09D428) create_sprite(0xab, EnemySprite.Apple, 0x16, 0x14, '', 0x09D42B) create_sprite(0xab, EnemySprite.UsainBolt, 0x1F, 0x1A, '', 0x09D42E) @@ -874,7 +880,7 @@ def init_vanilla_sprites_ow(): create_sprite(0xae, EnemySprite.Faerie, 0x22, 0x37, '', 0x09D48A) create_sprite(0xae, EnemySprite.UsainBolt, 0x2D, 0x3A, '', 0x09D48D) # Screen22_2: - create_sprite(0xb2, EnemySprite.BunnyBeam, 0x0C, 0x04, '', 0x09D491) + create_sprite(0xb2, EnemySprite.BunnyBeam, 0x0C, 0x04, '', 0x09D491, fix=True) # smithy smoke create_sprite(0xb2, EnemySprite.Cucco, 0x0C, 0x14, '', 0x09D494) create_sprite(0xb2, EnemySprite.Cucco, 0x12, 0x14, '', 0x09D497) # Screen25_2: @@ -915,13 +921,13 @@ def init_vanilla_sprites_ow(): create_sprite(0xbd, EnemySprite.Octorok4Way, 0x0F, 0x08, '', 0x09D4F5) create_sprite(0xbd, EnemySprite.BlueGuard, 0x12, 0x0B, '', 0x09D4F8) create_sprite(0xbd, EnemySprite.UsainBolt, 0x12, 0x16, '', 0x09D4FB) - create_sprite(0xbd, EnemySprite.FireballZora, 0x1C, 0x17, '', 0x09D4FE) + create_sprite(0xbd, EnemySprite.FireballZora, 0x1C, 0x17, '', 0x09D4FE, water=True) # Screen2E_2: create_sprite(0xbe, EnemySprite.Faerie, 0x0C, 0x09, '', 0x09D502) create_sprite(0xbe, EnemySprite.Bee, 0x14, 0x0B, '', 0x09D505) create_sprite(0xbe, EnemySprite.UsainBolt, 0x0E, 0x0C, '', 0x09D508) create_sprite(0xbe, EnemySprite.BlueGuard, 0x17, 0x0E, '', 0x09D50B) - create_sprite(0xbe, EnemySprite.FireballZora, 0x05, 0x12, '', 0x09D50E) + create_sprite(0xbe, EnemySprite.FireballZora, 0x05, 0x12, '', 0x09D50E, water=True) create_sprite(0xbe, EnemySprite.Octorok, 0x19, 0x17, '', 0x09D511) # Screen2F_2: create_sprite(0xbf, EnemySprite.UsainBolt, 0x0F, 0x0C, '', 0x09D515) @@ -973,22 +979,22 @@ def init_vanilla_sprites_ow(): create_sprite(0xc5, EnemySprite.UsainBolt, 0x0A, 0x0C, '', 0x09D595) create_sprite(0xc5, EnemySprite.HeartPiece, 0x19, 0x13, '', 0x09D598) create_sprite(0xc5, EnemySprite.Buzzblob, 0x19, 0x14, '', 0x09D59B) - create_sprite(0xc5, EnemySprite.FireballZora, 0x11, 0x17, '', 0x09D59E) + create_sprite(0xc5, EnemySprite.FireballZora, 0x11, 0x17, '', 0x09D59E, water=True) create_sprite(0xc5, EnemySprite.Octorok4Way, 0x38, 0x0A, '', 0x09D5A1) - create_sprite(0xc5, EnemySprite.FireballZora, 0x25, 0x0D, '', 0x09D5A4) - create_sprite(0xc5, EnemySprite.FireballZora, 0x37, 0x19, '', 0x09D5A7) + create_sprite(0xc5, EnemySprite.FireballZora, 0x25, 0x0D, '', 0x09D5A4, water=True) + create_sprite(0xc5, EnemySprite.FireballZora, 0x37, 0x19, '', 0x09D5A7, water=True) create_sprite(0xc5, EnemySprite.Buzzblob, 0x27, 0x1F, '', 0x09D5AA) create_sprite(0xc5, EnemySprite.Buzzblob, 0x2F, 0x1F, '', 0x09D5AD) - create_sprite(0xc5, EnemySprite.FireballZora, 0x1B, 0x26, '', 0x09D5B0) + create_sprite(0xc5, EnemySprite.FireballZora, 0x1B, 0x26, '', 0x09D5B0, water=True) create_sprite(0xc5, EnemySprite.Raven, 0x0D, 0x2F, '', 0x09D5B3) create_sprite(0xc5, EnemySprite.Octorok, 0x06, 0x34, '', 0x09D5B6) create_sprite(0xc5, EnemySprite.Octorok, 0x0A, 0x35, '', 0x09D5B9) - create_sprite(0xc5, EnemySprite.FireballZora, 0x14, 0x35, '', 0x09D5BC) + create_sprite(0xc5, EnemySprite.FireballZora, 0x14, 0x35, '', 0x09D5BC, water=True) create_sprite(0xc5, EnemySprite.Octorok, 0x0B, 0x39, '', 0x09D5BF) create_sprite(0xc5, EnemySprite.Buzzblob, 0x19, 0x3A, '', 0x09D5C2) create_sprite(0xc5, EnemySprite.Whirlpool, 0x29, 0x2B, '', 0x09D5C5) - create_sprite(0xc5, EnemySprite.FireballZora, 0x39, 0x31, '', 0x09D5C8) - create_sprite(0xc5, EnemySprite.FireballZora, 0x21, 0x36, '', 0x09D5CB) + create_sprite(0xc5, EnemySprite.FireballZora, 0x39, 0x31, '', 0x09D5C8, water=True) + create_sprite(0xc5, EnemySprite.FireballZora, 0x21, 0x36, '', 0x09D5CB, water=True) create_sprite(0xc5, EnemySprite.Buzzblob, 0x32, 0x37, '', 0x09D5CE) create_sprite(0xc5, EnemySprite.Buzzblob, 0x34, 0x39, '', 0x09D5D1) # Screen37_2: @@ -996,7 +1002,7 @@ def init_vanilla_sprites_ow(): create_sprite(0xc7, EnemySprite.Crab, 0x10, 0x08, '', 0x09D5D8) create_sprite(0xc7, EnemySprite.Crab, 0x0F, 0x0B, '', 0x09D5DB) create_sprite(0xc7, EnemySprite.Crab, 0x16, 0x11, '', 0x09D5DE) - create_sprite(0xc7, EnemySprite.FireballZora, 0x12, 0x19, '', 0x09D5E1) + create_sprite(0xc7, EnemySprite.FireballZora, 0x12, 0x19, '', 0x09D5E1, water=True) # Screen3A_2: create_sprite(0xca, EnemySprite.Locksmith, 0x17, 0x05, '', 0x09D5E5) create_sprite(0xca, EnemySprite.Hoarder2, 0x0B, 0x0A, '', 0x09D5E8) @@ -1007,7 +1013,7 @@ def init_vanilla_sprites_ow(): create_sprite(0xca, EnemySprite.UsainBolt, 0x11, 0x0F, '', 0x09D5F7) create_sprite(0xca, EnemySprite.Hoarder2, 0x17, 0x17, '', 0x09D5FA) # Screen3B_2: - create_sprite(0xcb, EnemySprite.FloppingFish, 0x13, 0x0D, '', 0x09D5FE) + create_sprite(0xcb, EnemySprite.FloppingFish, 0x13, 0x0D, '', 0x09D5FE, water=True) create_sprite(0xcb, EnemySprite.Octorok, 0x0C, 0x0F, '', 0x09D601) create_sprite(0xcb, EnemySprite.HeartPiece, 0x14, 0x0E, '', 0x09D604) create_sprite(0xcb, EnemySprite.Octorok4Way, 0x0F, 0x14, '', 0x09D607) @@ -1022,5 +1028,5 @@ def init_vanilla_sprites_ow(): # Screen3F_2: create_sprite(0xcf, EnemySprite.Octorok4Way, 0x16, 0x05, '', 0x09D621) create_sprite(0xcf, EnemySprite.Whirlpool, 0x07, 0x0C, '', 0x09D624) - create_sprite(0xcf, EnemySprite.FireballZora, 0x06, 0x13, '', 0x09D627) + create_sprite(0xcf, EnemySprite.FireballZora, 0x06, 0x13, '', 0x09D627, water=True) create_sprite(0xcf, EnemySprite.Octoballoon, 0x11, 0x16, '', 0x09D62A) diff --git a/source/enemizer/SpriteSheets.py b/source/enemizer/SpriteSheets.py index eae51f2d..8799fb73 100644 --- a/source/enemizer/SpriteSheets.py +++ b/source/enemizer/SpriteSheets.py @@ -165,14 +165,12 @@ def init_sprite_requirements(): SpriteRequirement(EnemySprite.Moldorm).exalt().sub_group(2, 0x30), SpriteRequirement(EnemySprite.Octorok4Way).sub_group(2, 0xc), SpriteRequirement(EnemySprite.Cucco).immune().sub_group(3, [0x15, 0x50]).exclude(NoFlyingRooms), - # todo: Buzzblob kill rule for mimics SpriteRequirement(EnemySprite.Buzzblob).sub_group(3, 0x11), SpriteRequirement(EnemySprite.Snapdragon).sub_group(0, 0x16).sub_group(2, 0x17), SpriteRequirement(EnemySprite.Octoballoon).no_drop().sub_group(2, 0xc).exclude(NoFlyingRooms), SpriteRequirement(EnemySprite.Hinox).sub_group(0, 0x16), SpriteRequirement(EnemySprite.Moblin).sub_group(2, 0x17), SpriteRequirement(EnemySprite.MiniHelmasaur).sub_group(1, 0x1e), - # todo: antifairy kill rule SpriteRequirement(EnemySprite.AntiFairy).no_drop().sub_group(3, [0x52, 0x53]) .exclude(NoFlyingRooms).exclude({0x40}), # no anti-fairies in aga tower bridge room SpriteRequirement(EnemySprite.Wiseman).affix().sub_group(2, 0x4c), @@ -180,7 +178,7 @@ def init_sprite_requirements(): SpriteRequirement(EnemySprite.MiniMoldorm).sub_group(1, 0x1e), SpriteRequirement(EnemySprite.Poe).no_drop().sub_group(3, 0x15).exclude(NoFlyingRooms), SpriteRequirement(EnemySprite.Smithy).affix().sub_group(1, 0x1d).sub_group(3, 0x15), - SpriteRequirement(EnemySprite.Statue).affix().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.Statue).stasis().immune().sub_group(3, [0x52, 0x53]), SpriteRequirement(EnemySprite.CrystalSwitch).affix().sub_group(3, [0x52, 0x53]), SpriteRequirement(EnemySprite.SickKid).affix().sub_group(0, 0x51), SpriteRequirement(EnemySprite.Sluggula).sub_group(2, 0x25), @@ -188,10 +186,8 @@ def init_sprite_requirements(): SpriteRequirement(EnemySprite.Ropa).sub_group(0, 0x16), SpriteRequirement(EnemySprite.RedBari).no_drop().sub_group(0, 0x1f), SpriteRequirement(EnemySprite.BlueBari).sub_group(0, 0x1f), - # todo: don't randomize red/blue bari in room 0x7F SpriteRequirement(EnemySprite.TalkingTree).affix().sub_group(0, 0x15), SpriteRequirement(EnemySprite.HardhatBeetle).sub_group(1, 0x1e), - # todo: deadrock kill rule for mimics (not sure why ice spike room...) SpriteRequirement(EnemySprite.Deadrock).sub_group(3, 0x10).exclude({0x7f, 0x10c}), SpriteRequirement(EnemySprite.DarkWorldHintNpc).affix(), # no groups? SpriteRequirement(EnemySprite.AdultNpc).affix().sub_group(0, [0xe, 0x4f]), @@ -233,8 +229,8 @@ def init_sprite_requirements(): SpriteRequirement(EnemySprite.KingZora).affix().sub_group(3, 0x44), SpriteRequirement(EnemySprite.ArmosKnight).exalt().sub_group(3, 0x1d), SpriteRequirement(EnemySprite.Lanmolas).exalt().sub_group(3, 0x31), - SpriteRequirement(EnemySprite.FireballZora).immerse().uw_skip().sub_group(2, [0xc, 0x18]), - SpriteRequirement(EnemySprite.Zora).immerse().no_drop().uw_skip().sub_group(2, 0xc).sub_group(3, 0x44), + SpriteRequirement(EnemySprite.FireballZora).immerse().no_drop().sub_group(2, [0xc, 0x18]), # .uw_skip() test + SpriteRequirement(EnemySprite.Zora).no_drop().sub_group(2, 0xc).sub_group(3, 0x44), # .uw_skip() test SpriteRequirement(EnemySprite.DesertStatue).affix().sub_group(2, 0x12), SpriteRequirement(EnemySprite.Crab).sub_group(2, 0xc), SpriteRequirement(EnemySprite.LostWoodsBird).affix().sub_group(2, 0x37).sub_group(3, 0x36), @@ -245,11 +241,11 @@ def init_sprite_requirements(): SpriteRequirement(EnemySprite.RollerVerticalDown).immune().sub_group(2, 0x27).exclude(NoBeamosOrTrapRooms), SpriteRequirement(EnemySprite.RollerHorizontalLeft).immune().sub_group(2, 0x27).exclude(NoBeamosOrTrapRooms), SpriteRequirement(EnemySprite.RollerHorizontalRight).immune().sub_group(2, 0x27).exclude(NoBeamosOrTrapRooms), - SpriteRequirement(EnemySprite.Beamos).immune().sub_group(1, 0x2c).exclude(NoBeamosOrTrapRooms), + SpriteRequirement(EnemySprite.Beamos).no_drop().sub_group(1, 0x2c).exclude(NoBeamosOrTrapRooms), SpriteRequirement(EnemySprite.MasterSword).affix().sub_group(2, 0x37).sub_group(3, 0x36), - # these are excluded for now - SpriteRequirement(EnemySprite.DebirandoPit).skip().sub_group(0, 0x2f), - SpriteRequirement(EnemySprite.Debirando).skip().sub_group(0, 0x2f), + + SpriteRequirement(EnemySprite.DebirandoPit).sub_group(0, 0x2f), # skip + SpriteRequirement(EnemySprite.Debirando).sub_group(0, 0x2f), # skip SpriteRequirement(EnemySprite.ArcheryNpc).affix().sub_group(0, 0x4b), SpriteRequirement(EnemySprite.WallCannonVertLeft).affix().sub_group(0, 0x2f), SpriteRequirement(EnemySprite.WallCannonVertRight).affix().sub_group(0, 0x2f), @@ -270,17 +266,16 @@ def init_sprite_requirements(): SpriteRequirement(EnemySprite.Agahnim).exalt().sub_group(0, 0x55).sub_group(1, [0x1a, 0x3d]).sub_group(2, 0x42) .sub_group(3, 0x43), SpriteRequirement(EnemySprite.FloatingSkull).no_drop().sub_group(0, 0x1f).exclude(NoFlyingRooms), + SpriteRequirement(EnemySprite.BigSpike).sub_group(3, [0x52, 0x53]).no_drop(), SpriteRequirement(EnemySprite.FirebarCW).immune().sub_group(0, 0x1f), SpriteRequirement(EnemySprite.FirebarCCW).immune().sub_group(0, 0x1f), - # todo: don't randomize these in GT Torch Cross and TR Dark Ride? was that ever implemented? - SpriteRequirement(EnemySprite.Firesnake).immune().sub_group(0, 0x1f), - SpriteRequirement(EnemySprite.Hover).immerse().sub_group(2, 0x22).exclude(NoFlyingRooms), - # todo: leave them in swamp palace entrance... - SpriteRequirement(EnemySprite.AntiFairyCircle).skip().no_drop().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.Firesnake).no_drop().sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.Hover).sub_group(2, 0x22), # .exclude(NoFlyingRooms), might be okay now + SpriteRequirement(EnemySprite.AntiFairyCircle).no_drop().sub_group(3, [0x52, 0x53]), SpriteRequirement(EnemySprite.GreenEyegoreMimic).sub_group(2, 0x2e), SpriteRequirement(EnemySprite.RedEyegoreMimic).sub_group(2, 0x2e), - # kodongos apparently broken? - SpriteRequirement(EnemySprite.Kodongo).skip().sub_group(2, 0x2a), + + SpriteRequirement(EnemySprite.Kodongo).sub_group(2, 0x2a), SpriteRequirement(EnemySprite.Mothula).exalt().sub_group(2, 0x38).sub_group(3, 0x52), SpriteRequirement(EnemySprite.SpikeBlock).immune().sub_group(3, [0x52, 0x53]).exclude(NoBeamosOrTrapRooms) .exclude({0x28}), # why exclude sp entrance? @@ -289,19 +284,17 @@ def init_sprite_requirements(): SpriteRequirement(EnemySprite.Arrghi).exalt().sub_group(2, 0x39), SpriteRequirement(EnemySprite.Terrorpin).sub_group(2, 0x2a).exclude({0x10c}), # probably fine in mimic now SpriteRequirement(EnemySprite.Blob).sub_group(1, 0x20), - # todo: wallmaster overlords SpriteRequirement(EnemySprite.Wallmaster).immune().ow_skip().sub_group(2, 0x23) .allow(WallmasterValidRooms), - SpriteRequirement(EnemySprite.StalfosKnight).sub_group(1, 0x10).exclude({0x10c}), + SpriteRequirement(EnemySprite.StalfosKnight).sub_group(1, 0x20).exclude({0x10c}), SpriteRequirement(EnemySprite.HelmasaurKing).exalt().sub_group(2, 0x3a).sub_group(3, 0x3e), - SpriteRequirement(EnemySprite.Bumper).affix().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.Bumper).immune().sub_group(3, [0x52, 0x53]), SpriteRequirement(EnemySprite.LaserEyeLeft).affix().sub_group(3, [0x52, 0x53]), SpriteRequirement(EnemySprite.LaserEyeRight).affix().sub_group(3, [0x52, 0x53]), SpriteRequirement(EnemySprite.LaserEyeTop).affix().sub_group(3, [0x52, 0x53]), SpriteRequirement(EnemySprite.LaserEyeBottom).affix().sub_group(3, [0x52, 0x53]), SpriteRequirement(EnemySprite.Pengator).sub_group(2, 0x26), SpriteRequirement(EnemySprite.Kyameron).no_drop().immerse().sub_group(2, 0x22), - # todo: leave them in swamp palace entrance... SpriteRequirement(EnemySprite.Wizzrobe).sub_group(2, [0x25, 0x29]), SpriteRequirement(EnemySprite.Zoro).no_drop().sub_group(1, 0x20), SpriteRequirement(EnemySprite.Babasu).no_drop().sub_group(1, 0x20), @@ -351,11 +344,12 @@ def init_sprite_requirements(): SpriteRequirement(EnemySprite.TrinexxFireHead).exalt().sub_group(0, 0x40).sub_group(3, 0x3f), SpriteRequirement(EnemySprite.TrinexxIceHead).exalt().sub_group(0, 0x40).sub_group(3, 0x3f), SpriteRequirement(EnemySprite.Blind).exalt().sub_group(1, 0x2c).sub_group(2, 0x3b), - SpriteRequirement(EnemySprite.Swamola).immerse().no_drop().sub_group(3, 0x19), + SpriteRequirement(EnemySprite.Swamola).no_drop().sub_group(3, 0x19), SpriteRequirement(EnemySprite.Lynel).sub_group(3, 0x14), - SpriteRequirement(EnemySprite.BunnyBeam).affix(), + SpriteRequirement(EnemySprite.BunnyBeam).no_drop().ow_skip(), SpriteRequirement(EnemySprite.FloppingFish).uw_skip().immune(), - SpriteRequirement(EnemySprite.Stal).skip(), + SpriteRequirement(EnemySprite.Stal), + SpriteRequirement(EnemySprite.Landmine).skip(), SpriteRequirement(EnemySprite.DiggingGameNPC).affix().sub_group(1, 0x2a), SpriteRequirement(EnemySprite.Ganon).exalt().sub_group(0, 0x21).sub_group(1, 0x41) .sub_group(2, 0x45).sub_group(3, 0x33), @@ -366,7 +360,7 @@ def init_sprite_requirements(): SpriteRequirement(EnemySprite.CastleMantle).affix().sub_group(0, 0x5d), SpriteRequirement(EnemySprite.MedallionTablet).affix().sub_group(2, 0x12), - # todo: overlord requirements + # overlord requirements - encapsulated mostly in the required sheets SpriteRequirement(2, 7).affix().sub_group(2, 46), SpriteRequirement(3, 7).affix().sub_group(2, 46), SpriteRequirement(5, 7).affix().sub_group(0, 31), @@ -399,7 +393,7 @@ def init_sprite_requirements(): SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 14).sub_group(2, 74).sub_group(3, 90) .allow({0x123, 0x124}), SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 14).sub_group(2, 74).sub_group(3, 80) - .allow({0x125}), + .allow({0x125, 0x100}), SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 21).allow({0x11e}), ] complex_r = {} @@ -513,7 +507,7 @@ def init_sprite_sheets(requirements): return sheets -def setup_required_dungeon_groups(sheets): +def setup_required_dungeon_groups(sheets, data_tables): sheets[did(1)].add_sprite_to_sheet([70, 73, 28, 82], {0xe4, 0xf0}) # old man # various npcs @@ -540,53 +534,47 @@ def setup_required_dungeon_groups(sheets): ([None, None, None, 80], [0x108]), # chicken house ([14, 30, None, None], [0x123]), # mini moldorm (shutter door) ([None, None, 34, None], [0x36, 0x46, 0x66, 0x76]), # pirogusu spawners - ([None, 32, None, None], [0x3e, 0x9f]), # babasu spawners + ([None, 32, None, None], [0x9f]), # babasu spawners ([31, None, None, None], [0x7f]), # force baris - ([None, None, 35, None], [0x39, 0x49, 0x56, 0x57, 0x68, 0x8d]), # wallmasters + ([None, None, 35, None], [0x39, 0x49, 0x8d]), # wallmasters # bumpers - why the split - because of other requirements - - ([None, None, None, (82, 83)], [0x17, 0x2a, 0x4c, 0x59, 0x67, 0x68, 0x7e, 0x8b, 0xeb, 0xfb]), + ([None, None, None, (82, 83)], [0x17, 0x2a, 0x4c, 0x59, 0x67, 0x7e, 0x8b, 0xeb, 0xfb]), # crystal switches - split for some reason - ([None, None, None, (82, 83)], [0xb, 0x13, 0x1b, 0x1e, 0x2a, 0x2b, 0x31, 0x3e, 0x5b, 0x6b, 0x77, 0xb7, 0x8b, 0x91, 0x92, 0x9b, 0x9d, 0xa1, 0xab, 0xb6, 0xbf, 0xc1, 0xc4, 0xef]), + ([None, None, None, (82, 83)], [0xb, 0x13, 0x1b, 0x1e, 0x2a, 0x2b, 0x31, 0x5b, 0x6b, 0x77, 0x8b, + 0x91, 0x92, 0x9b, 0x9d, 0xa1, 0xab, 0xb6, 0xbf, 0xc1, 0xc4, 0xef]), # laser eyes - split for some reason ([None, None, None, (82, 83)], [0x13, 0x23, 0x96, 0xa5, 0xc5, 0xd5]), # statues - split for some reason - ([None, None, None, (82, 83)], [0x26, 0x2b, 0x40, 0x4a, 0x57, 0x6b, 0x7b]), + ([None, None, None, (82, 83)], [0x26, 0x2b, 0x40, 0x4a, 0x6b, 0x7b]), # non-optional - ([None, None, None, 82], [0x2, 0x58, 0x64, 0x8c, 0x10b]), # pull switches - ([None, None, None, 82], [0x1a, 0x3d, 0x44, 0x56, 0x5e, 0x7c, 0x95, 0xc3]), # collapsing bridges + ([None, None, None, 82], [0x2, 0x58, 0x64, 0x8c, 0x10b]), # pull switches + ([None, None, None, 82], [0x1a, 0x3d, 0x44, 0x5e, 0x7c, 0x95, 0xc3]), # collapsing bridges ([None, None, None, 83], [0x4, 0x3f, 0xce]), # pull tongue ([None, None, None, 83], [0x35, 0x37, 0x76]), # swamp drains ([None, None, 34, None], [0x28]), # tektike forced? - spawn chest ([None, None, 37, None], [0x97]), # wizzrobe spawner - in middle of room... + + # combined + ([None, 32, None, (82, 83)], [0x3e]), # babasu spawners + crystal switch + ([None, None, 35, 82], [0x56]), # wallmaster + collasping bridge + ([None, None, 35, (82, 83)], [0x57, 0x68]), # wallmaster + statue and wallmaster + bumpers + ([None, None, 34, 83], [0x76]), # swamp drain + pirogusu spawners + + # allow some sprites / increase odds: + ([72, 73, None, None], []), # allow for blue archer + greenbush + ([None, 73, 19, None], []), # allow for green knife guard + ([None, None, 12, 68], []), # increase odds for zora + ([22, None, 23, None], []), # increase odds for snapdragon ] + data_tables.room_requirements = {} # find home for the free_sheet_reqs for pair in free_sheet_reqs: groups, room_list = pair - possible_sheets = [] - found_match = False - for num in range(65, 124): - sheet = sheets[num] - valid = True - match = True - for idx, value in enumerate(groups): - if value is not None and sheet.locked[idx]: - valid = False - if (sheet.sub_groups[idx] not in value if isinstance(value, tuple) - else value != sheet.sub_groups[idx]): - match = False - elif value is not None: - match = False - if match: - found_match = True - break - if valid: - possible_sheets.append(sheet) - if not found_match: - chosen_sheet = random.choice(possible_sheets) - chosen_groups = [(random.choice(g) if isinstance(g, tuple) else g) for g in groups] - chosen_sheet.add_sprite_to_sheet(chosen_groups, room_list) + for room in room_list: + data_tables.room_requirements[room] = groups + find_matching_sheet(groups, sheets, range(65, 124), room_list) # RandomizeRooms(optionFlags); @@ -594,15 +582,15 @@ def setup_required_dungeon_groups(sheets): # roomCollection.RandomizeRoomSpriteGroups(spriteGroupCollection, optionFlags); # more stuff sub_group_choices = { - 0: [22, 31, 47, 14], # 70, 72 for guards + 0: [22, 31, 47, 14, 72, 70], # 70, 72 for guards #72 specifically for BlueArcher/GreenBush 1: [44, 30, 32], # 73, 13 - 2: [12, 18, 23, 24, 28, 46, 34, 35, 39, 40, 38, 41, 36, 37, 42], - 3: [17, 16, 27, 20, 82, 83] + 2: [12, 18, 23, 24, 28, 46, 34, 35, 39, 40, 38, 41, 36, 37, 42, 19], # 19 for GreenKnifeGuard + 3: [17, 16, 27, 20, 82, 83, 25, 68] # 25 for Swamola, 68 for Zora (walking?) } -def randomize_underworld_sprite_sheets(sheets): - setup_required_dungeon_groups(sheets) +def randomize_underworld_sprite_sheets(sheets, data_tables): + setup_required_dungeon_groups(sheets, data_tables) for num in range(65, 124): # sheets 0x41 to 0x7B inclusive sheet = sheets[num] @@ -624,7 +612,7 @@ def setup_required_overworld_groups(sheets): sheets[1].add_sprite_to_sheet([None, None, 76, 63], {0x1B, 0xAB}) # Hyrule Castle (pre/post-Aga) sheets[6].add_sprite_to_sheet([None, None, None, None], {0x22, 0x28, 0xB2, 0xB8}) # Smithy/Race (pre/post-Aga) sheets[8].add_sprite_to_sheet([None, None, 18, None], {0x30, 0xC0}) # Desert (pre/post-Aga) - sheets[10].add_sprite_to_sheet([None, None, None, None], {0x3A, 0xCA}) # M-rock (pre/post-Aga) + sheets[10].add_sprite_to_sheet([None, None, None, 17], {0x3A, 0xCA}) # M-rock (pre/post-Aga) sheets[22].add_sprite_to_sheet([None, None, 24, None], {0x4F, 0xDF}) # Catfish (pre/post-Aga) sheets[21].add_sprite_to_sheet([21, None, None, 21], {0x62, 0xF2}) # Smith DW (pre/post-Aga) sheets[27].add_sprite_to_sheet([None, 42, None, None], {0x68, 0xF8}) # Dig Game (pre/post-Aga) @@ -637,12 +625,50 @@ def setup_required_overworld_groups(sheets): sheets[26].add_sprite_to_sheet([15, None, None, None], {0x92}) # Lumberjacks post-Aga sheets[23].add_sprite_to_sheet([None, None, None, 25], {0x5E, 0xEE}) # PoD pre/post-Aga + free_sheet_reqs = [ + [72, 73, None, None], # allow for blue archer + green bush + [None, 73, 19, None], # allow for green knife guard + [22, None, 23, None], # increase odds for snapdragon + [70, 73, None, None], # guards group (ballnchain, redbush, redjav, cannon, bomb, bluesain + ] + + for group in free_sheet_reqs: + find_matching_sheet(group, sheets, range(1, 64)) + + +def find_matching_sheet(groups, sheets, search_sheets, room_list=None): + possible_sheets = [] + found_match = False + for num in search_sheets: + sheet = sheets[num] + valid = True + match = True + for idx, value in enumerate(groups): + if value is not None and sheet.locked[idx]: + valid = False + if (sheet.sub_groups[idx] not in value if isinstance(value, tuple) + else value != sheet.sub_groups[idx]): + match = False + elif value is not None: + match = False + if match: + found_match = True + break + if valid: + possible_sheets.append(sheet) + if not found_match: + chosen_sheet = random.choice(possible_sheets) + chosen_groups = [(random.choice(g) if isinstance(g, tuple) else g) for g in groups] + chosen_sheet.add_sprite_to_sheet(chosen_groups, room_list) + def randomize_overworld_sprite_sheets(sheets): setup_required_overworld_groups(sheets) for num in range(1, 64): # sheets 0x1 to 0x3F inclusive sheet = sheets[num] + if num == 6: # skip this group, would like to know why though + continue for idx in range(0, 4): if not sheet.locked[idx]: sheet.sub_groups[idx] = random.choice(sub_group_choices[idx]) diff --git a/source/enemizer/enemy_damage_table.yaml b/source/enemizer/enemy_damage_table.yaml new file mode 100644 index 00000000..f7ed65b5 --- /dev/null +++ b/source/enemizer/enemy_damage_table.yaml @@ -0,0 +1,10 @@ +0x00: [0x02, 0x01, 0x01] +0x01: [0x04, 0x04, 0x04] +0x02: [0x00, 0x00, 0x00] +0x03: [0x08, 0x04, 0x02] +0x04: [0x08, 0x08, 0x08] +0x05: [0x10, 0x08, 0x04] +0x06: [0x20, 0x10, 0x08] +0x07: [0x20, 0x18, 0x10] +0x08: [0x18, 0x10, 0x08] # Roller class damage +0x09: [0x40, 0x30, 0x18] # Ganon class damage - max 8 hearts diff --git a/source/enemizer/enemy_weight.yaml b/source/enemizer/enemy_weight.yaml new file mode 100644 index 00000000..99d86f73 --- /dev/null +++ b/source/enemizer/enemy_weight.yaml @@ -0,0 +1,198 @@ +UW: # Total 82753 + AntiFairy: 50 # f1.99026% raw:1647 + Statue: 50 # f1.99026% raw:1647 + BlueGuard: 124 # f0.80480% raw:666 + GreenGuard: 157 # f0.63563% raw:526 + RedSpearGuard: 124 # f0.80480% raw:666 + BluesainBolt: 567 # f0.17643% raw:146 + UsainBolt: 124 # f0.80480% raw:666 + RedJavelinGuard: 662 # f0.15105% raw:125 + RedBushGuard: 662 # f0.15105% raw:125 + BombGuard: 662 # f0.15105% raw:125 + BallNChain: 662 # f0.15105% raw:125 + CannonTrooper: 662 # f0.15105% raw:125 + CricketRat: 169 # f0.59212% raw:490 + Snake: 169 # f0.59212% raw:490 + Keese: 169 # f0.59212% raw:490 + BigSpike: 50 # f1.99026% raw:1647 + AntiFairyCircle: 50 # f1.99026% raw:1647 + SpikeBlock: 50 # f1.99026% raw:1647 + Bumper: 50 # f1.99026% raw:1647 + BunnyBeam: 14 # f7.12965% raw:5900 + FloppingFish: 14 # f7.12965% raw:5900 + Stal: 14 # f7.12965% raw:5900 + Landmine: 14 # f7.12965% raw:5900 + Octorok: 159 # f0.62717% raw:519 + RedBari: 99 # f1.01386% raw:839 + BlueBari: 99 # f1.01386% raw:839 + FireballZora: 159 # f0.62717% raw:519 + SparkCW: 99 # f1.01386% raw:839 + SparkCCW: 99 # f1.01386% raw:839 + FloatingSkull: 99 # f1.01386% raw:839 + FirebarCW: 99 # f1.01386% raw:839 + FirebarCCW: 99 # f1.01386% raw:839 + Firesnake: 99 # f1.01386% raw:839 + Stalfos: 99 # f1.01386% raw:839 + Raven: 92 # f1.09120% raw:903 + Vulture: 400 # f0.25014% raw:207 + Buzzblob: 196 # f0.51116% raw:423 + Hoarder: 196 # f0.51116% raw:423 + Hoarder2: 196 # f0.51116% raw:423 + Geldman: 400 # f0.25014% raw:207 + Toppo: 196 # f0.51116% raw:423 + FakeMasterSword: 196 # f0.51116% raw:423 + MiniHelmasaur: 56 # f1.79450% raw:1485 + MiniMoldorm: 56 # f1.79450% raw:1485 + HardhatBeetle: 56 # f1.79450% raw:1485 + Lynel: 172 # f0.58246% raw:482 + Deadrock: 176 # f0.56796% raw:470 + ArmosStatue: 176 # f0.56796% raw:470 + Hover: 211 # f0.47370% raw:392 + Blob: 52 # f1.90930% raw:1580 + StalfosKnight: 52 # f1.90930% raw:1580 + Kyameron: 211 # f0.47370% raw:392 + Zoro: 52 # f1.90930% raw:1580 + Babasu: 52 # f1.90930% raw:1580 + Tektite: 176 # f0.56796% raw:470 + Popo: 56 # f1.77516% raw:1469 + Popo2: 56 # f1.77516% raw:1469 + Beamos: 56 # f1.77516% raw:1469 + DebirandoPit: 110 # f0.90510% raw:749 + Debirando: 110 # f0.90510% raw:749 + Leever: 110 # f0.90510% raw:749 + GreenEyegoreMimic: 270 # f0.36978% raw:306 + RedEyegoreMimic: 270 # f0.36978% raw:306 + Swamola: 172 # f0.58004% raw:480 + GreenZirro: 191 # f0.52324% raw:433 + BlueZirro: 191 # f0.52324% raw:433 + Pikit: 191 # f0.52324% raw:433 + Cucco: 207 # f0.48337% raw:400 + Moblin: 281 # f0.35648% raw:295 + Octorok4Way: 273 # f0.36615% raw:303 + Octoballoon: 273 # f0.36615% raw:303 + Zora: 690 # f0.14501% raw:120 + Crab: 273 # f0.36615% raw:303 + Thief: 81 # f1.22896% raw:1017 + Wizzrobe: 155 # f0.64529% raw:534 + Poe: 828 # f0.12084% raw:100 + Hinox: 109 # f0.92081% raw:762 + Ropa: 109 # f0.92081% raw:762 + Pengator: 294 # f0.33956% raw:281 + Freezor: 294 # f0.33956% raw:281 + Sluggula: 239 # f0.41811% raw:346 + RollerVerticalUp: 280 # f0.35769% raw:296 + RollerVerticalDown: 280 # f0.35769% raw:296 + RollerHorizontalLeft: 280 # f0.35769% raw:296 + RollerHorizontalRight: 280 # f0.35769% raw:296 + Pokey: 280 # f0.35769% raw:296 + Chainchomp: 280 # f0.35769% raw:296 + Gibdo: 196 # f0.51116% raw:423 + Wallmaster: 196 # f0.51116% raw:423 + GreenKnifeGuard: 773 # f0.12930% raw:107 + BlueArcher: 632 # f0.15830% raw:131 + GreenBushGuard: 632 # f0.15830% raw:131 + Snapdragon: 690 # f0.14501% raw:120 + Kodongo: 368 # f0.27189% raw:225 + Terrorpin: 368 # f0.27189% raw:225 + BlueZazak: 400 # f0.25014% raw:207 + RedZazak: 400 # f0.25014% raw:207 + Gibo: 400 # f0.25014% raw:207 +OW: # Total 94707 + Popo: 50 # f2.00513% raw:1899 + Popo2: 50 # f2.00513% raw:1899 + Beamos: 50 # f2.00513% raw:1899 + DebirandoPit: 100 # f1.00415% raw:951 + Debirando: 100 # f1.00415% raw:951 + Leever: 100 # f1.00415% raw:951 + BunnyBeam: 15 # f6.65210% raw:6300 + FloppingFish: 15 # f6.65210% raw:6300 + Stal: 15 # f6.65210% raw:6300 + Landmine: 15 # f6.65210% raw:6300 + MiniHelmasaur: 49 # f2.03364% raw:1926 + AntiFairy: 73 # f1.36738% raw:1295 + MiniMoldorm: 49 # f2.03364% raw:1926 + Statue: 73 # f1.36738% raw:1295 + HardhatBeetle: 49 # f2.03364% raw:1926 + BigSpike: 73 # f1.36738% raw:1295 + AntiFairyCircle: 73 # f1.36738% raw:1295 + GreenEyegoreMimic: 316 # f0.31677% raw:300 + RedEyegoreMimic: 316 # f0.31677% raw:300 + SpikeBlock: 73 # f1.36738% raw:1295 + Bumper: 73 # f1.36738% raw:1295 + Hinox: 87 # f1.14353% raw:1083 + Ropa: 87 # f1.14353% raw:1083 + CricketRat: 160 # f0.62509% raw:592 + Snake: 160 # f0.62509% raw:592 + Keese: 160 # f0.62509% raw:592 + GreenZirro: 151 # f0.66415% raw:629 + BlueZirro: 151 # f0.66415% raw:629 + Pikit: 151 # f0.66415% raw:629 + Raven: 61 # f1.64719% raw:1560 + RollerVerticalUp: 272 # f0.36745% raw:348 + RollerVerticalDown: 272 # f0.36745% raw:348 + RollerHorizontalLeft: 272 # f0.36745% raw:348 + RollerHorizontalRight: 272 # f0.36745% raw:348 + Pokey: 272 # f0.36745% raw:348 + Chainchomp: 272 # f0.36745% raw:348 + Swamola: 128 # f0.78030% raw:739 + Cucco: 316 # f0.31677% raw:300 + BlueGuard: 239 # f0.41813% raw:396 + GreenGuard: 239 # f0.41813% raw:396 + RedSpearGuard: 239 # f0.41813% raw:396 + UsainBolt: 239 # f0.41813% raw:396 + Buzzblob: 115 # f0.86688% raw:821 + Hoarder: 115 # f0.86688% raw:821 + RedBari: 98 # f1.01999% raw:966 + BlueBari: 98 # f1.01999% raw:966 + Hoarder2: 115 # f0.86688% raw:821 + Toppo: 115 # f0.86688% raw:821 + SparkCW: 98 # f1.01999% raw:966 + SparkCCW: 98 # f1.01999% raw:966 + FloatingSkull: 98 # f1.01999% raw:966 + FirebarCW: 98 # f1.01999% raw:966 + FirebarCCW: 98 # f1.01999% raw:966 + Firesnake: 98 # f1.01999% raw:966 + Stalfos: 98 # f1.01999% raw:966 + FakeMasterSword: 115 # f0.86688% raw:821 + Vulture: 184 # f0.54484% raw:516 + Geldman: 184 # f0.54484% raw:516 + Blob: 50 # f1.98401% raw:1879 + StalfosKnight: 50 # f1.98401% raw:1879 + Zoro: 50 # f1.98401% raw:1879 + Babasu: 50 # f1.98401% raw:1879 + Octorok: 112 # f0.89011% raw:843 + Octorok4Way: 226 # f0.44242% raw:419 + Octoballoon: 226 # f0.44242% raw:419 + FireballZora: 112 # f0.89011% raw:843 + Crab: 226 # f0.44242% raw:419 + Thief: 90 # f1.11713% raw:1058 + Zora: 686 # f0.14571% raw:138 + Deadrock: 127 # f0.78769% raw:746 + ArmosStatue: 127 # f0.78769% raw:746 + Tektite: 127 # f0.78769% raw:746 + Hover: 301 # f0.33260% raw:315 + Kyameron: 301 # f0.33260% raw:315 + Pengator: 274 # f0.36534% raw:346 + Freezor: 274 # f0.36534% raw:346 + BlueArcher: 824 # f0.12143% raw:115 + GreenBushGuard: 824 # f0.12143% raw:115 + Kodongo: 287 # f0.34844% raw:330 + Terrorpin: 287 # f0.34844% raw:330 + Wizzrobe: 152 # f0.65676% raw:622 + Poe: 474 # f0.21118% raw:200 + Snapdragon: 596 # f0.16789% raw:159 + Moblin: 221 # f0.45298% raw:429 + Lynel: 145 # f0.69161% raw:655 + BlueZazak: 308 # f0.32416% raw:307 + RedZazak: 308 # f0.32416% raw:307 + Gibo: 308 # f0.32416% raw:307 + Gibdo: 290 # f0.34528% raw:327 + Wallmaster: 290 # f0.34528% raw:327 + GreenKnifeGuard: 853 # f0.11720% raw:111 + BluesainBolt: 803 # f0.12459% raw:118 + RedJavelinGuard: 803 # f0.12459% raw:118 + RedBushGuard: 803 # f0.12459% raw:118 + BombGuard: 803 # f0.12459% raw:118 + BallNChain: 803 # f0.12459% raw:118 + CannonTrooper: 803 # f0.12459% raw:118 + Sluggula: 302 # f0.33155% raw:314 \ No newline at end of file diff --git a/source/enemizer/uw_enemy_deny.yaml b/source/enemizer/uw_enemy_deny.yaml new file mode 100644 index 00000000..7aaa41c0 --- /dev/null +++ b/source/enemizer/uw_enemy_deny.yaml @@ -0,0 +1,354 @@ +- [0x0002, 0, ["RollerVerticalDown"]] #"Sewers - Rat Pots - Rat 1" +- [0x0002, 1, ["RollerVerticalDown"]] #"Sewers - Rat Pots - Rat 2" +- [0x0002, 2, ["RollerVerticalUp"]] #"Sewers - Rat Pots - Rat 3" +- [0x0002, 3, ["RollerVerticalUp"]] #"Sewers - Rat Pots - Rat 4" +- [0x0002, 15, ["RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft"]] #"Sewers - Rat Pots - Rat 6" +- [0x0002, 16, ["RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft"]] #"Sewers - Rat Pots - Rat 7" +- [0x000a, 0, ["RollerVerticalDown", "RollerVerticalUp"]] #"Palace of Darkness - Basement Ledge - Terrorpin 1" +- [0x000a, 1, ["RollerHorizontalRight", "RollerHorizontalLeft"]] #"Palace of Darkness - Basement Ledge - Terrorpin 2" +- [0x000b, 1, ["RollerVerticalDown", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Palace of Darkness - Callback - Terrorpin 1" +- [0x000e, 0, ["RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft"]] #"Ice Palace - Entrance - Freezor" +- [0x000e, 1, ["RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft"]] #"Ice Palace - Bari Key - Top Bari" +- [0x000e, 2, ["RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft"]] #"Ice Palace - Bari Key - Middle Bari" +- [0x0016, 0, ["RollerVerticalDown", "RollerVerticalUp", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Swamp Palace - Pool - Zol 1" +- [0x0016, 1, ["RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Swamp Palace - Pool - Zol 2" +- [0x0016, 2, ["SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Swamp Palace - Pool - Blue Bari" +- [0x0016, 3, ["RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Swamp Palace - Pool - Zol 3" +- [0x0017, 5, ["Beamos", "AntiFairyCircle", "SpikeBlock", "Bumper"]] #"Tower Of Hera - Bumper Room - Fire Bar (Clockwise)" +- [0x0019, 0, ["SparkCW", "SparkCCW", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Palace of Darkness - Dark Maze - Kodongo 1" +- [0x0019, 1, ["RollerVerticalUp"]] #"Palace of Darkness - Dark Maze - Kodongo 2" +- [0x0019, 2, ["RollerVerticalDown", "RollerVerticalUp"]] #"Palace of Darkness - Dark Maze - Kodongo 3" +- [0x0019, 3, ["RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft"]] #"Palace of Darkness - Dark Maze - Kodongo 4" +- [0x001a, 0, ["SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper"]] #"Palace of Darkness - Compass Room - Mini Helmasaur 1" +- [0x001a, 5, ["SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper"]] #"Palace of Darkness - Compass Room - Mini Helmasaur 2" +- [0x001b, 3, ["Beamos", "AntiFairyCircle", "Bumper"]] #"Palace of Darkness - Mimics 2 - Red Eyegore" +- [0x001b, 4, ["RollerVerticalUp"]] #"Palace of Darkness - Mimics 2 - Green Eyegore L" +- [0x001e, 3, ["RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "BigSpike", "Bumper"]] #"Ice Palace - Blob Ambush - Red Bari 3" +- [0x001e, 4, ["RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "BigSpike", "Bumper"]] #"Ice Palace - Blob Ambush - Red Bari 4" +- [0x001e, 5, ["SparkCW", "SparkCCW", "RollerVerticalDown", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ice Palace - Blob Ambush - Zol 1" +- [0x001e, 6, ["SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ice Palace - Blob Ambush - Zol 2" +- [0x001f, 0, ["RollerHorizontalRight"]] #"Ice Palace - Big Key View - Pengator 1" +- [0x0021, 3, ["RollerVerticalDown", "RollerVerticalUp"]] #"Sewers - Dark U - Rat 2" +- [0x0021, 4, ["RollerVerticalDown", "RollerVerticalUp"]] #"Sewers - Dark U - Rat 3" +- [0x0026, 1, ["SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Swamp Palace - Big Spoon - Red Bari 1" +- [0x0026, 8, ["AntiFairyCircle", "Bumper"]] #"Swamp Palace - Big Spoon - Red Bari 3" +- [0x0026, 9, ["RollerHorizontalRight"]] #"Swamp Palace - Big Spoon - Kyameron" +- [0x0027, 0, ["SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalLeft", "FirebarCW"]] #"Tower Of Hera - Petting Zoo - Mini Moldorm 1" +- [0x0027, 1, ["RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "SpikeBlock", "Bumper"]] #"Tower Of Hera - Petting Zoo - Mini Moldorm 2" +- [0x0027, 2, ["RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "SpikeBlock", "Bumper"]] #"Tower Of Hera - Petting Zoo - Mini Moldorm 3" +- [0x0027, 5, ["SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Tower Of Hera - Petting Zoo - Kodongo 1" +- [0x0028, 4, ["RollerVerticalUp"]] #"Swamp Palace - Entrance Ledge - Spike Trap" +- [0x002a, 2, ["SparkCW", "SparkCCW", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "SpikeBlock"]] #"Palace of Darkness - Arena Main - Hardhat Beetle 1" +- [0x002a, 3, ["Statue", "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper"]] #"Palace of Darkness - Arena Main - Hardhat Beetle 2" +- [0x002a, 6, ["RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Palace of Darkness - Arena Main - Hardhat Beetle 5" +- [0x002b, 5, ["RollerHorizontalRight"]] #"Palace of Darkness - Fairies - Red Bari 2" +- [0x002e, 0, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW"]] #"Ice Palace - Penguin Chest - Pengator 1" +- [0x002e, 1, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW"]] #"Ice Palace - Penguin Chest - Pengator 2" +- [0x002e, 2, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW"]] #"Ice Palace - Penguin Chest - Pengator 3" +- [0x002e, 3, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW"]] #"Ice Palace - Penguin Chest - Pengator 4" +- [0x002e, 4, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW"]] #"Ice Palace - Penguin Chest - Pengator 5" +- [0x002e, 5, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW"]] #"Ice Palace - Penguin Chest - Pengator 6" +- [0x0034, 0, ["SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Swamp Palace - West Wing - Hover 1" +- [0x0034, 1, ["SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Swamp Palace - West Wing - Hover 2" +- [0x0034, 2, ["SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Swamp Palace - West Wing - Kyameron" +- [0x0034, 4, ["Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Swamp Palace - West Wing - Zol" +- [0x0035, 6, ["RollerHorizontalRight"]] #"Swamp Palace - West Lever - Stalfos 2" +- [0x0035, 9, ["RollerHorizontalRight", "Beamos", "AntiFairyCircle", "Bumper"]] #"Swamp Palace - West Lever - Blue Bari" +- [0x0036, 7, ["RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper"]] #"Swamp Palace - Lobby - Hover 3" +- [0x0037, 7, ["RollerHorizontalRight"]] #"Swamp Palace - Water 1 - Blue Bari" +- [0x0038, 4, ["RollerHorizontalRight"]] #"Swamp Palace - Long Hall - Kyameron 2" +- [0x0039, 3, ["RollerVerticalUp", "RollerHorizontalLeft"]] #"Skull Woods - Play Pen - Mini Helmasaur" +- [0x0039, 4, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "FirebarCW", "FirebarCCW"]] #"Skull Woods - Play Pen - Spike Trap 1" +- [0x0039, 5, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft"]] #"Skull Woods - Play Pen - Hardhat Beetle" +- [0x0039, 6, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "FirebarCW", "FirebarCCW"]] #"Skull Woods - Play Pen - Spike Trap 2" +- [0x003c, 1, ["SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Hookshot Cave - Blue Bari 1" +- [0x003c, 2, ["SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Hookshot Cave - Blue Bari 2" +- [0x003d, 9, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ganon's Tower - Torches 2 - Spark (Counterclockwise)" +- [0x003d, 10, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ganon's Tower - Torches 2 - Spark (Clockwise) 1" +- [0x003d, 12, ["AntiFairyCircle", "Bumper"]] #"Ganon's Tower - Torches 2 - Bunny Beam" +- [0x003d, 13, ["AntiFairyCircle", "Bumper"]] #"Ganon's Tower - Torches 2 - Antifairy" +#- [0x003e, 3, ["Kodongo"]] #"Ice Palace - Conveyor - Babasu 1" +#- [0x003e, 4, ["Kodongo"]] #"Ice Palace - Conveyor - Babasu 2" +#- [0x003e, 6, ["Kodongo"]] #"Ice Palace - Conveyor - Babasu 3" +#- [0x003e, 7, ["Kodongo"]] #"Ice Palace - Conveyor - Babasu 4" +- [0x003f, 1, ["RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper"]] #"Ice Palace - P Room - Stalfos Knight 1" +- [0x003f, 3, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ice Palace - P Room - Stalfos Knight 2" +- [0x0040, 0, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] # Agahnims Tower - Bridge - Blue Guard 1 +- [0x0040, 1, ["Statue", "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] # Agahnims Tower - Bridge - Blue Guard 2 +- [0x0041, 0, ["RollerHorizontalLeft"]] #"Sewers - Dark Cactus - Rat 1" +- [0x0041, 1, ["RollerVerticalDown", "RollerHorizontalRight"]] #"Sewers - Dark Cactus - Rat 2" +- [0x0041, 2, ["RollerVerticalUp"]] #"Sewers - Dark Cactus - Rat 3" +- [0x0042, 0, ["SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Sewers - Dark Rope Corridor - Rope 1" +- [0x0042, 1, ["SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Sewers - Dark Rope Corridor - Rope 2" +- [0x0042, 2, ["SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Sewers - Dark Rope Corridor - Rope 3" +- [0x0042, 3, ["SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Sewers - Dark Rope Corridor - Rope 4" +- [0x0042, 4, ["SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Sewers - Dark Rope Corridor - Rope 5" +- [0x0042, 5, ["SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Sewers - Dark Rope Corridor - Rope 6" +- [0x0044, 4, ["RollerVerticalUp", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Thieves' Town - Joke Room - Zol" +- [0x0044, 6, ["RollerVerticalUp", "RollerVerticalDown", "Beamos", "BigSpike"]] #"Thieves' Town - Joke Room - Red Bari" +- [0x0044, 8, ["Statue", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "SpikeBlock", "Bumper"]] #"Thieves' Town - Joke Room - Blue Bari 4" +- [0x0045, 1, ["RollerVerticalUp", "RollerVerticalDown"]] #"Thieves' Town - Basement Block Totems - Red Zazak" +- [0x0045, 7, ["AntiFairyCircle", "Bumper"]] #"Thieves' Town - Cells - Blue Zazak 4" +- [0x0045, 8, ["RollerHorizontalRight"]] #"Thieves' Town - Cells - Zol" +- [0x0046, 0, ["RollerVerticalUp", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Swamp Palace - Big O Top - Hover 1" +- [0x0046, 2, ["RollerVerticalDown", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Swamp Palace - Big O Top - Hover 2" +- [0x0046, 4, ["RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Swamp Palace - Big O Top - Hover 3" +- [0x0049, 5, ["RollerVerticalUp", "RollerVerticalDown"]] #"Skull Woods - Bari Pits - Gibdo 2" +- [0x0049, 7, ["RollerVerticalUp", "RollerVerticalDown"]] #"Skull Woods - Bari Pits - Gibdo 4" +- [0x0049, 8, ["Beamos", "AntiFairyCircle", "Bumper"]] #"Skull Woods - Bari Pits - Gibdo 5" +- [0x004b, 0, ["Beamos", "AntiFairyCircle", "Bumper"]] #"Palace of Darkness - Mimics 1 - Red Eyegore" +- [0x004b, 1, ["RollerHorizontalRight"]] #"Palace of Darkness - Warp Hint - Antifairy 1" +- [0x004b, 5, ["RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Palace of Darkness - Jelly Hall - Blue Bari 1" +- [0x004b, 6, ["AntiFairyCircle", "BigSpike"]] #"Palace of Darkness - Jelly Hall - Blue Bari 2" +- [0x004b, 7, ["AntiFairyCircle", "BigSpike"]] #"Palace of Darkness - Jelly Hall - Blue Bari 3" +- [0x004e, 0, ["RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ice Palace - Blob Alley - Zol 1" +- [0x004e, 1, ["RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ice Palace - Blob Alley - Zol 2" +- [0x004e, 2, ["RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ice Palace - Blob Alley - Zol 3" +- [0x0050, 0, ["RollerVerticalUp", "RollerVerticalDown"]] #"Hyrule Castle - North West Passage - Green Guard" +- [0x0050, 1, ["RollerVerticalUp", "RollerVerticalDown"]] #"Hyrule Castle - North West Passage - Green Knife Guard 1" +- [0x0050, 2, ["RollerVerticalUp", "RollerVerticalDown"]] #"Hyrule Castle - North West Passage - Green Knife Guard 2" +- [0x0052, 0, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "Bumper"]] #"Hyrule Castle - North East Passage - Green Guard" +- [0x0052, 1, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "Bumper"]] #"Hyrule Castle - North East Passage - Green Knife Guard 1" +- [0x0052, 2, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "Bumper"]] #"Hyrule Castle - North East Passage - Green Knife Guard 2" +- [0x0053, 1, ["AntiFairyCircle", "Bumper"]] #"Desert Palace - Bridge - Beamos 1" +- [0x0053, 5, ["RollerVerticalDown"]] #"Desert Palace - Popo Genocide - Popo TL" +- [0x0053, 7, ["Beamos", "AntiFairyCircle", "Bumper"]] #"Desert Palace - Bridge - Popo 5" +- [0x0055, 1, ["RollerVerticalUp", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Secret Passage Exit - Green Knife Guard 1" +- [0x0057, 2, ["RollerVerticalUp", "AntiFairyCircle", "Bumper"]] #"Skull Woods - Big Key Room - Spike Trap" +- [0x0057, 7, ["RollerVerticalUp", "RollerVerticalDown"]] #"Skull Woods - Big Key Room - Gibdo 2" +- [0x0057, 12, ["RollerVerticalUp", "RollerVerticalDown"]] #"Skull Woods - Big Key Room - Gibdo 6" +- [0x0057, 13, ["RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper"]] #"Skull Woods - Big Key Room - Blue Bari 1" +- [0x0057, 14, ["RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper"]] #"Skull Woods - Big Key Room - Blue Bari 2" +- [0x0058, 7, ["RollerHorizontalLeft"]] #"Skull Woods - Lever Room - Hardhat Beetle 2" +- [0x0059, 0, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Skull Woods - Bridge Room - Mini Moldorm 1" +- [0x0059, 1, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Skull Woods - Bridge Room - Mini Moldorm 2" +- [0x0059, 9, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Skull Woods - Bridge Room - Gibdo 1" +- [0x005e, 3, ["RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "SpikeBlock", "Bumper"]] #"Ice Palace - Pit Trap - Big Spike Trap" +- [0x005e, 4, ["RollerVerticalUp", "RollerVerticalDown"]] #"Ice Palace - Pit Trap - Fire Bar (Clockwise)" +- [0x005f, 0, ["RollerVerticalDown", "RollerHorizontalLeft"]] #"Ice Palace - Bari University - Blue Bari 1" +- [0x005f, 1, ["RollerVerticalDown", "RollerHorizontalRight"]] #"Ice Palace - Bari University - Blue Bari 2" +- [0x0060, 0, ["RollerVerticalUp", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Hyrule Castle - West - Blue Guard" +- [0x0062, 0, ["RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Hyrule Castle - East - Blue Guard" +- [0x0064, 0, ["Kodongo"]] #"Thieves' Town - Attic Hall Left - Keese 1" +- [0x0064, 2, ["Kodongo"]] #"Thieves' Town - Attic Hall Left - Keese 2" +- [0x0064, 4, ["RollerHorizontalLeft", "RollerHorizontalRight"]] #"Thieves' Town - Attic Hall Left - Rat 1" +- [0x0065, 0, ["RollerVerticalUp", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Thieves' Town - Attic Window - Rat 1" +- [0x0065, 1, ["RollerHorizontalLeft", "RollerHorizontalRight"]] #"Thieves' Town - Attic Window - Rat 2" +- [0x0065, 2, ["Beamos", "AntiFairyCircle", "Bumper"]] #"Thieves' Town - Attic Window - Rat 3" +- [0x0066, 0, ["Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Swamp Palace - Waterfall Room - Hover 1" +- [0x0067, 1, ["RollerVerticalUp", "RollerVerticalDown"]] #"Skull Woods - Firebar Pits - Blue Bari 1" +- [0x0067, 3, ["RollerVerticalUp", "RollerVerticalDown"]] #"Skull Woods - Firebar Pits - Hardhat Beetle 1" +- [0x0067, 5, ["RollerVerticalDown"]] #"Skull Woods - Firebar Pits - Hardhat Beetle 3" +- [0x0067, 6, ["RollerVerticalDown"]] #"Skull Woods - Firebar Pits - Hardhat Beetle 4" +- [0x0067, 7, ["Beamos", "AntiFairyCircle", "Bumper"]] #"Skull Woods - Firebar Pits - Fire Bar (Clockwise)" +- [0x006a, 0, ["RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Palace of Darkness - Dark Alley - Terrorpin 1" +- [0x006a, 1, ["RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Palace of Darkness - Dark Alley - Terrorpin 2" +- [0x006a, 2, ["RollerVerticalUp", "RollerVerticalDown"]] #"Palace of Darkness - Dark Alley - Antifairy 1" +- [0x006a, 4, ["RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Palace of Darkness - Dark Alley - Terrorpin 3" +- [0x006a, 5, ["RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Palace of Darkness - Dark Alley - Terrorpin 4" +- [0x006b, 7, ["RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "Bumper"]] #"Ganon's Tower - Mimics 1 - Spike Trap 1" +- [0x0071, 0, ["RollerHorizontalLeft"]] #"Hyrule Castle - Basement Trap - Green Guard" +- [0x0074, 0, ["AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Desert Palace - North Hallway - Red Devalant 1" +- [0x0074, 1, ["AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Desert Palace - North Hallway - Red Devalant 2" +- [0x0074, 4, ["AntiFairyCircle", "Bumper"]] #"Desert Palace - North Hallway - Leever 1" +- [0x0074, 5, ["AntiFairyCircle", "Bumper"]] #"Desert Palace - North Hallway - Leever 2" +- [0x0075, 6, ["Kodongo"]] #"Desert Palace - Trap Room - Moving Cannon (Right)" +- [0x0075, 7, ["Kodongo"]] #"Desert Palace - Trap Room - Moving Cannon (Left)" +- [0x0076, 1, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Swamp Palace - Toilet Left - Hover 1" +- [0x0076, 2, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Swamp Palace - Toilet Left - Kyameron" +- [0x0076, 3, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Swamp Palace - Toilet Left - Hover 2" +- [0x0076, 4, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Swamp Palace - Toilet Left - Zol" +- [0x0076, 6, ["RollerVerticalDown", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Swamp Palace - Toilet Left - Blue Bari" +- [0x007b, 0, ["RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - DMs Room - Blue Bari 1" +- [0x007b, 1, ["AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - DMs Room - Blue Bari 2" +- [0x007b, 6, ["Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ganon's Tower - DMs Room - Statue" +- [0x007b, 7, ["Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ganon's Tower - DMs Room - Hardhat Beetle" +- [0x007c, 1, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ganon's Tower - Randomizer Room - Fire Bar (Counterclockwise)" +- [0x007c, 2, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - Randomizer Room - Spike Trap" +- [0x007c, 3, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - Randomizer Room - Fire Bar (Clockwise)" +- [0x007c, 4, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ganon's Tower - Randomizer Room - Hardhat Beetle" +- [0x007d, 0, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - The Zoo - Fire Snake 1" +- [0x007d, 1, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - The Zoo - Fire Snake 2" +- [0x007d, 2, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - The Zoo - Fire Snake 3" +- [0x007d, 3, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - The Zoo - Fire Snake 4" +- [0x007d, 7, ["RollerVerticalUp", "RollerHorizontalLeft"]] #"Ganon's Tower - The Zoo - Mini Helmasaur" +- [0x007d, 8, ["RollerVerticalUp", "RollerHorizontalLeft", "RollerHorizontalRight"]] #"Ganon's Tower - The Zoo - Red Bari" +- [0x007f, 0, ["Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock"]] #"Ice Palace - Big Spikes - Red Bari 1" +- [0x007f, 1, ["Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock"]] #"Ice Palace - Big Spikes - Red Bari 2" +- [0x007f, 2, ["Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock"]] #"Ice Palace - Big Spikes - Red Bari 3" +- [0x007f, 3, ["Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock"]] #"Ice Palace - Big Spikes - Red Bari 4" +- [0x007f, 4, ["RollerVerticalDown"]] #"Ice Palace - Big Spikes - Big Spike Trap 1" +- [0x007f, 5, ["RollerVerticalDown"]] #"Ice Palace - Big Spikes - Big Spike Trap 2" +- [0x0082, 0, ["RollerVerticalDown"]] #"Hyrule Castle - Basement Playpit - Blue Guard 1" +- [0x0082, 2, ["RollerVerticalUp"]] #"Hyrule Castle - Basement Playpit - Blue Guard 3" +- [0x0083, 0, ["RollerVerticalUp", "RollerVerticalDown"]] #"Desert Palace - Left Hallway - Blue Devalant 1" +- [0x0084, 0, ["RollerVerticalDown", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Desert Palace - Main Room - Left - Leever 1" +- [0x0084, 1, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Desert Palace - Main Room - Left - Leever 2" +- [0x0085, 2, ["RollerHorizontalRight"]] #"Desert Palace - Compass Room - Popo TL" +- [0x0085, 7, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Desert Palace - Right Hallway - Leever 2" +- [0x008b, 4, ["Statue", "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper"]] #"Ganon's Tower - Map Room - Spike Trap" +- [0x008b, 6, ["Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - Map Room - Fire Bar (Clockwise)" +- [0x008b, 7, ["Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - Map Room - Fire Bar (Counterclockwise)" +- [0x008d, 1, ["AntiFairyCircle", "Bumper"]] #"Ganon's Tower - Tile Room - Yomo Medusa T" +- [0x008d, 7, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - Tile Room - Spike Trap" +- [0x008d, 8, ["RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ganon's Tower - Tile Room - Stalfos" +- [0x008d, 9, ["Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ganon's Tower - Tile Room - Fire Bar (Clockwise)" +- [0x008d, 10, ["RollerVerticalUp", "RollerVerticalDown"]] #"Ganon's Tower - Tile Room - Blue Bari 1" +- [0x008d, 12, ["RollerVerticalUp", "RollerVerticalDown"]] #"Ganon's Tower - Tile Room - Blue Bari 2" +- [0x0092, 8, ["RollerVerticalUp", "Beamos", "AntiFairyCircle", "Bumper"]] #"Misery Mire - Dark Weave - Spike Trap" +- [0x0092, 9, ["RollerHorizontalRight"]] #"Misery Mire - Dark Weave - Antifairy 3" +- [0x0092, 10, ["RollerHorizontalLeft"]] #"Misery Mire - Dark Weave - Stalfos" +- [0x0095, 0, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock"]] #"Ganon's Tower - Conveyer Falling Bridge - Red Spear Guard 1" +- [0x0095, 1, ["Statue", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ganon's Tower - Conveyer Falling Bridge - Red Spear Guard 2" +- [0x0095, 2, ["Statue", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ganon's Tower - Conveyer Falling Bridge - Red Spear Guard 3" +- [0x0095, 3, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "SpikeBlock"]] #"Ganon's Tower - Conveyer Falling Bridge - Red Spear Guard 4" +- [0x0096, 0, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ganon's Tower - Torches 1 - Fire Bar (Clockwise)" +- [0x0096, 1, ["Kodongo"]] #"Ganon's Tower - Torches 1 - Laser Eye (Left) 1" +- [0x0096, 2, ["Kodongo"]] #"Ganon's Tower - Torches 1 - Laser Eye (Left) 2" +- [0x0096, 3, ["Kodongo"]] #"Ganon's Tower - Torches 1 - Laser Eye (Left) 3" +- [0x0096, 4, ["Kodongo"]] #"Ganon's Tower - Torches 1 - Laser Eye (Left) 4" +- [0x0098, 0, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Misery Mire - Entrance - Zol 1" +- [0x0098, 1, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Misery Mire - Entrance - Zol 2" +- [0x0098, 2, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Misery Mire - Entrance - Zol 3" +- [0x0098, 3, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Misery Mire - Entrance - Zol 4" +- [0x0098, 4, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Misery Mire - Entrance - Zol 5" +- [0x009b, 3, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - Spike Switch - Spike Trap 1" +- [0x009b, 4, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - Spike Switch - Spike Trap 2" +- [0x009b, 6, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - Spike Switch - Yomo Medusa" +- [0x009b, 7, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - Spike Switch - Spike Trap 4" +- [0x009b, 8, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - Spike Switch - Spike Trap 5" +- [0x009b, 9, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - Spike Switch - Spike Trap 6" +- [0x009b, 10, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - Spike Switch - Spike Trap 7" +- [0x009c, 1, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ganon's Tower - Invisible Floor Maze - Mini Helmasaur" +- [0x009c, 2, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ganon's Tower - Invisible Floor Maze - Hardhat Beetle 2" +- [0x009c, 3, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ganon's Tower - Invisible Floor Maze - Hardhat Beetle 3" +- [0x009c, 4, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - Invisible Floor Maze - Hardhat Beetle 4" +- [0x009c, 5, ["RollerVerticalUp", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - Invisible Floor Maze - Hardhat Beetle 5" +- [0x009d, 3, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ganon's Tower - Compass Room - Gibdo 2" +- [0x009d, 6, ["RollerHorizontalLeft", "RollerHorizontalRight"]] #"Ganon's Tower - Compass Room - Blue Bari 1" +- [0x009d, 7, ["RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - Compass Room - Blue Bari 2" +- [0x009d, 8, ["RollerHorizontalLeft", "RollerHorizontalRight"]] #"Ganon's Tower - Compass Room - Blue Bari 3" +- [0x009e, 0, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ice Palace - Fairy Drop - blue - Red Bari 1" +- [0x009e, 1, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ice Palace - Fairy Drop - blue - Red Bari 2" +- [0x009e, 2, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ice Palace - Fairy Drop - blue - Stalfos Knight" +- [0x009e, 3, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ice Palace - Fairy Drop - blue - Red Bari 3" +- [0x009f, 0, ["Kodongo"]] #"Ice Palace - Pottery Barn - Babasu 1" +- [0x009f, 1, ["Kodongo"]] #"Ice Palace - Pottery Barn - Babasu 2" +- [0x009f, 2, ["Kodongo"]] #"Ice Palace - Pottery Barn - Babasu 3" +- [0x009f, 3, ["Kodongo"]] #"Ice Palace - Pottery Barn - Babasu 4" +- [0x00a0, 1, ["RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Misery Mire - Boss Antichamber - Antifairy" +- [0x00a1, 2, ["Statue", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Misery Mire - Fish Room - Spark (Clockwise) 2" +- [0x00a5, 8, ["Kodongo"]] #"Ganon's Tower - Laser Bridge - Laser Eye (Down) L" +- [0x00a5, 9, ["Kodongo"]] #"Ganon's Tower - Laser Bridge - Laser Eye (Down) R" +- [0x00a5, 10, ["RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ganon's Tower - Laser Bridge - Red Spear Guard" +- [0x00a8, 1, ["RollerVerticalUp", "RollerHorizontalLeft"]] #"Eastern Palace - West Wing - Top - Stalfos 2" +- [0x00a8, 3, ["RollerVerticalDown", "RollerHorizontalLeft"]] #"Eastern Palace - West Wing - Top - Stalfos 4" +- [0x00aa, 4, ["AntiFairyCircle", "BigSpike", "Bumper"]] #"Eastern Palace - East Wing - Top - Stalfos 3" +- [0x00aa, 5, ["Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Eastern Palace - East Wing - Top - Popo B 2" +- [0x00ab, 7, ["RollerVerticalUp", "RollerHorizontalLeft"]] #"Thieves' Town - Spike Dodge - Spike Trap 6" +- [0x00ae, 0, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ice Palace - Ice T - Blue Bari 1" +- [0x00ae, 1, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Ice Palace - Ice T - Blue Bari 2" +- [0x00af, 0, ["RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Ice Palace - Ice Clock - Fire Bar (Clockwise)" +- [0x00b1, 2, ["RollerVerticalUp", "RollerVerticalDown"]] #"Misery Mire - Hourglass - Spike Trap 1" +- [0x00b1, 3, ["RollerVerticalUp", "RollerVerticalDown"]] #"Misery Mire - Hourglass - Spike Trap 2" +- [0x00b2, 6, ["RollerVerticalUp", "RollerHorizontalLeft"]] #"Misery Mire - Sluggula Cross - Sluggula TR" +- [0x00b2, 8, ["RollerVerticalDown"]] #"Misery Mire - Popo Push - Medusa 1" +- [0x00b2, 9, ["RollerVerticalUp"]] #"Misery Mire - Sluggula Cross - Sluggula BL" +- [0x00b3, 0, ["RollerVerticalUp", "RollerHorizontalRight", "BigSpike", "SpikeBlock"]] #"Misery Mire - Spike Room - Stalfos 1" +- [0x00b3, 2, ["Statue", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "Bumper"]] #"Misery Mire - Spike Room - Beamos" +- [0x00b3, 3, ["AntiFairyCircle", "Bumper"]] #"Misery Mire - Spike Room - Yomo Medusa" +- [0x00b6, 7, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Turtle Rock - Tile Room - Zol 1" +- [0x00b6, 8, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Turtle Rock - Tile Room - Zol 2" +- [0x00ba, 1, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Eastern Palace - Dark Stalfos - Antifairy 1" +- [0x00ba, 3, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Eastern Palace - Dark Stalfos - Antifairy 2" +- [0x00ba, 4, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Eastern Palace - Dark Stalfos - Popo B 1" +- [0x00ba, 6, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Eastern Palace - Dark Stalfos - Popo B 2" +- [0x00bb, 1, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight"]] #"Thieves' Town - Spikeveyer - Gibo 1" +- [0x00bb, 4, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "Bumper"]] #"Thieves' Town - Spikeveyer - Antifairy 1" +- [0x00bb, 5, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Thieves' Town - Spikeveyer - Gibo 3" +- [0x00bb, 6, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight"]] #"Thieves' Town - Spikeveyer - Fire Snake" +- [0x00bb, 7, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "SpikeBlock", "Bumper"]] #"Thieves' Town - Spikeveyer - Gibo 4" +- [0x00bb, 8, ["RollerHorizontalLeft", "RollerHorizontalRight"]] #"Thieves' Town - Spikeveyer - Gibo 5" +- [0x00bb, 9, ["RollerHorizontalLeft", "RollerHorizontalRight"]] #"Thieves' Town - Spikeveyer - Antifairy 2" +- [0x00bc, 6, ["AntiFairyCircle", "SpikeBlock", "Bumper"]] #"Thieves' Town - Toilet - Stalfos 2" +- [0x00bc, 7, ["AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Thieves' Town - Toilet - Stalfos 3" +- [0x00bc, 8, ["RollerVerticalUp", "RollerVerticalDown"]] #"Thieves' Town - Toilet - Stalfos 4" +- [0x00c1, 3, ["RollerVerticalUp", "RollerHorizontalLeft"]] #"Misery Mire - 4 Rails - Stalfos 1" +- [0x00c2, 0, ["RollerHorizontalLeft", "RollerHorizontalRight"]] #"Misery Mire - Main Lobby - blue - Fire Snake 1" +- [0x00c3, 1, ["Kodongo"]] #"Misery Mire - Falling Bridge - Laser Eye (Left) 1" +- [0x00c3, 2, ["Kodongo"]] #"Misery Mire - Falling Bridge - Laser Eye (Right) 1" +- [0x00c3, 3, ["Kodongo"]] #"Misery Mire - Falling Bridge - Laser Eye (Left) 2" +- [0x00c3, 4, ["Kodongo"]] #"Misery Mire - Falling Bridge - Laser Eye (Right) 2" +- [0x00c5, 0, ["Kodongo"]] #"Turtle Rock - Catwalk - Laser Eye (Left) 1" +- [0x00c5, 1, ["Kodongo"]] #"Turtle Rock - Catwalk - Laser Eye (Right) 1" +- [0x00c5, 2, ["Kodongo"]] #"Turtle Rock - Catwalk - Laser Eye (Left) 2" +- [0x00c5, 3, ["Kodongo"]] #"Turtle Rock - Catwalk - Laser Eye (Right) 2" +- [0x00c5, 4, ["Kodongo"]] #"Turtle Rock - Catwalk - Laser Eye (Left) 3" +- [0x00c5, 5, ["Kodongo"]] #"Turtle Rock - Catwalk - Laser Eye (Right) 3" +- [0x00c5, 6, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Turtle Rock - Catwalk - Mini Helmasaur" +- [0x00c5, 7, ["Statue"]] #"Turtle Rock - Catwalk - Laser Eye (Left) 4" +- [0x00cb, 3, ["RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Thieves' Town - Grand Room NW - Zol 1" +- [0x00cb, 5, ["RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Thieves' Town - Grand Room NW - Zol 2" +- [0x00ce, 0, ["RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCCW", "Bumper"]] #"Ice Palace - Over Boss - top - Red Bari 1" +- [0x00ce, 1, ["RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCW", "Bumper"]] #"Ice Palace - Over Boss - top - Red Bari 2" +- [0x00ce, 3, ["SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper"]] #"Ice Palace - Over Boss - top - Statue" +- [0x00d0, 6, ["Statue", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] # Agahnims Tower - Dark Maze - Blue Guard 2 +- [0x00d2, 8, ["RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Misery Mire - Mire 2 - Popo BL" +- [0x00d5, 0, ["Kodongo"]] #"Turtle Rock - Eye Bridge - Laser Eye (Left) 1" +- [0x00d5, 1, ["Kodongo"]] #"Turtle Rock - Eye Bridge - Laser Eye (Right) 1" +- [0x00d5, 2, ["Kodongo"]] #"Turtle Rock - Eye Bridge - Laser Eye (Left) 2" +- [0x00d5, 3, ["Kodongo"]] #"Turtle Rock - Eye Bridge - Laser Eye (Right) 2" +- [0x00d5, 4, ["Statue", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper"]] #"Turtle Rock - Eye Bridge - Hardhat Beetle" +- [0x00d8, 0, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Eastern Palace - Kill Room 2 - Red Eyegore L" +- [0x00d8, 1, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Eastern Palace - Kill Room 2 - Red Eyegore R" +- [0x00d8, 2, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Eastern Palace - Kill Room 2 - Popo B TL" +- [0x00d8, 3, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Eastern Palace - Kill Room 2 - Popo B TR" +- [0x00d8, 4, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Eastern Palace - Kill Room 2 - Popo B LT" +- [0x00d8, 5, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Eastern Palace - Kill Room 2 - Popo B RT" +- [0x00d8, 6, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Eastern Palace - Kill Room 2 - Popo LB" +- [0x00d8, 7, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Eastern Palace - Kill Room 2 - Popo RB" +- [0x00d8, 8, ["Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Eastern Palace - Kill Room 1 - Red Eyegore" +- [0x00d9, 1, ["RollerHorizontalRight"]] #"Eastern Palace - Dodgeball - Green Eyegore 1" +- [0x00dc, 9, ["RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Thieves' Town - Grand Room SE - Fire Snake 2" +- [0x00df, 0, ["RollerVerticalDown", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Paradox Cave - Top - Mini Moldorm 1" +- [0x00df, 1, ["RollerVerticalDown", "RollerHorizontalRight", "AntiFairyCircle"]] #"Paradox Cave - Top - Mini Moldorm 2" +- [0x00e4, 0, ["RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Old Man Home - Keese 1" +- [0x00e4, 1, ["RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Old Man Home - Keese 2" +- [0x00e4, 2, ["RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Old Man Home - Keese 3" +#- [0x00e5, 3, ["Kodongo"]] #"Old Man Home Circle - Keese 4" +- [0x00e5, 4, ["RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Kodongo", "Bumper"]] #"Old Man Home Circle - Keese 5" +- [0x00e5, 5, ["RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Kodongo", "Bumper"]] #"Old Man Home Circle - Keese 6" +- [0x00e7, 0, [ "RollerVerticalUp", "Kodongo", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Death Mountain Descent Cave Right - Keese 1" +- [0x00e7, 1, [ "RollerVerticalUp", "Kodongo", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Death Mountain Descent Cave Right - Keese 2" +- [0x00e7, 2, [ "Kodongo", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Death Mountain Descent Cave Right - Keese 3" +- [0x00e7, 3, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Death Mountain Descent Cave Right - Keese 4" +- [0x00e7, 4, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Death Mountain Descent Cave Right - Keese 5" +- [0x00e7, 5, [ "Kodongo", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Death Mountain Descent Cave Right - Keese 6" +- [0x00e7, 6, [ "Kodongo", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Death Mountain Descent Cave Right - Keese 7" +- [0x00e8, 0, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "SpikeBlock", "Bumper"]] #"Super Bunny Exit - Hardhat Beetle 1" +- [0x00e8, 1, ["RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Super Bunny Exit - Hardhat Beetle 2" +- [0x00ee, 0, ["RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper"]] #"Sprial Cave Top - Mini Moldorm 1" +- [0x00ee, 1, ["RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Sprial Cave Top - Mini Moldorm 2" +- [0x00ee, 2, ["RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Sprial Cave Top - Mini Moldorm 3" +- [0x00ee, 3, ["RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Sprial Cave Top - Blue Bari 1" +- [0x00ee, 4, ["RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Sprial Cave Top - Blue Bari 2" +- [0x00ef, 1, ["RollerVerticalUp", "RollerVerticalDown"]] #"Paradox Cave - Middle - Mini Moldorm 2" +#- [0x00f0, 0, ["Kodongo"]] #"Old Man Cave Ledge - Keese 1" +#- [0x00f0, 1, ["Kodongo"]] #"Old Man Cave Ledge - Keese 2" +#- [0x00f0, 2, ["Kodongo"]] #"Old Man Cave Ledge - Keese 3" +#- [0x00f0, 3, ["Kodongo"]] #"Old Man Cave Ledge - Keese 4" +#- [0x00f0, 4, ["Kodongo"]] #"Old Man Cave Ledge - Keese 5" +#- [0x00f0, 5, ["Kodongo"]] #"Old Man Cave Ledge - Keese 6" +#- [0x00f0, 6, ["Kodongo"]] #"Old Man Cave Ledge - Keese 7" +#- [0x00f0, 7, ["Kodongo"]] #"Old Man Cave Ledge - Keese 8" +#- [0x00f0, 9, ["Kodongo"]] #"Old Man Cave Ledge - Keese 9" +- [0x00f1, 0, ["Kodongo", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight"]] #"Old Man Maze - Keese 1" +- [0x00f1, 1, ["Kodongo", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight"]] #"Old Man Maze - Keese 2" +- [0x00f1, 2, ["Kodongo", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight"]] #"Old Man Maze - Keese 3" +- [0x00f1, 3, ["Kodongo", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight"]] #"Old Man Maze - Keese 4" +- [0x00f1, 4, ["Kodongo", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight"]] #"Old Man Maze - Keese 5" +- [0x00f1, 5, ["Kodongo", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight"]] #"Old Man Maze - Keese 6" +#- [0x00f1, 6, ["Kodongo"]] #"Old Man Maze - Keese 7" +#- [0x00f1, 7, ["Kodongo"]] #"Old Man Maze - Keese 8" +#- [0x00f1, 8, ["Kodongo"]] #"Old Man Maze - Keese 9" +#- [0x00f1, 9, ["Kodongo"]] #"Old Man Maze - Keese 10" \ No newline at end of file diff --git a/source/logic/Rule.py b/source/logic/Rule.py index c4ab4832..ff923b8d 100644 --- a/source/logic/Rule.py +++ b/source/logic/Rule.py @@ -104,6 +104,8 @@ class RuleFactory(object): rule = Rule(RuleType.Conjunction) rule_lambda = None for r in rules: + if r is None: + continue if r.rule_type == RuleType.Conjunction: 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 @@ -126,6 +128,8 @@ class RuleFactory(object): rule = Rule(RuleType.Disjunction) rule_lambda = None for r in rules: + if r is None: + continue if r.rule_type == RuleType.Disjunction: 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 diff --git a/source/rom/DataTables.py b/source/rom/DataTables.py index 236f2bea..2d891c20 100644 --- a/source/rom/DataTables.py +++ b/source/rom/DataTables.py @@ -1,8 +1,9 @@ from collections import defaultdict -from Utils import snes_to_pc, int24_as_bytes, int16_as_bytes +from Utils import snes_to_pc, int24_as_bytes, int16_as_bytes, load_yaml -from source.dungeon.EnemyList import EnemyTable, init_vanilla_sprites, vanilla_sprites, init_enemy_stats +from source.dungeon.EnemyList import EnemyTable, init_vanilla_sprites, vanilla_sprites, init_enemy_stats, EnemySprite +from source.dungeon.EnemyList import sprite_translation from source.dungeon.RoomHeader import init_room_headers from source.dungeon.RoomList import Room0127 from source.enemizer.OwEnemyList import init_vanilla_sprites_ow, vanilla_sprites_ow @@ -14,7 +15,7 @@ def convert_area_id_to_offset(area_id): return area_id if 0x40 <= area_id < 0x80: return area_id + 0x40 - if 0x90 <= area_id < 0xCF: + if 0x90 <= area_id <= 0xCF: return area_id - 0x50 raise Exception(f'{hex(area_id)} is not a valid area id for offset math') @@ -31,12 +32,24 @@ class DataTables: # associated data self.sprite_requirements = None + self.room_requirements = None self.enemy_stats = None + self.enemy_damage = None + self.bush_sprite_table = {} - def write_to_rom(self, rom, colorize_pots=False): + # enemizer conditions + self.uw_enemy_denials = {} + for denial in load_yaml(['source', 'enemizer', 'uw_enemy_deny.yaml']): + self.uw_enemy_denials[denial[0], denial[1]] = {sprite_translation[x] for x in denial[2]} + # todo: ow_denials + weights = load_yaml(['source', 'enemizer', 'enemy_weight.yaml']) + self.uw_weights = {sprite_translation[k]: v for k, v in weights['UW'].items()} + self.ow_weights = {sprite_translation[k]: v for k, v in weights['OW'].items()} + + def write_to_rom(self, rom, colorize_pots=False, increase_bush_sprite_chance=False): if self.pot_secret_table.size() > 0x11c0: raise Exception('Pot table is too big for current area') - self.pot_secret_table.write_pot_data_to_rom(rom, colorize_pots) + self.pot_secret_table.write_pot_data_to_rom(rom, colorize_pots, self) for room_id, header in self.room_headers.items(): data_location = (0x30DA00 + room_id * 14) & 0xFFFF rom.write_bytes(snes_to_pc(0x04F1E2) + room_id * 2, int16_as_bytes(data_location)) @@ -58,19 +71,73 @@ class DataTables: if self.uw_enemy_table.size() > 0x2800: raise Exception('Sprite table is too big for current area') self.uw_enemy_table.write_sprite_data_to_rom(rom) - for area_id, sheet_number in self.overworld_sprite_sheets.items(): + for area_id, sheet in self.overworld_sprite_sheets.items(): if area_id in [0x80, 0x81]: offset = area_id - 0x80 # 02E575 for special areas? - rom.write_byte(snes_to_pc(0x02E576+offset), sheet_number) + rom.write_byte(snes_to_pc(0x02E576+offset), sheet.id) else: offset = convert_area_id_to_offset(area_id) - rom.write_byte(snes_to_pc(0x00FA81+offset), sheet_number) + rom.write_byte(snes_to_pc(0x00FA81+offset), sheet.id) # _00FA81 is LW normal # _00FAC1 is LW post-aga # _00FB01 is DW - for area, sprite_list in vanilla_sprites_ow.items(): + for area, sprite_list in self.ow_enemy_table.items(): for sprite in sprite_list: rom.write_bytes(snes_to_pc(sprite.original_address), sprite.sprite_data_ow()) + for sprite, stats in self.enemy_stats.items(): + # write health to rom + if stats.health is not None: + if isinstance(stats.health, tuple): + if sprite == EnemySprite.Octorok4Way: # skip this one + continue + if sprite in special_health_table: + a1, a2 = special_health_table[sprite] + rom.write_byte(snes_to_pc(a1), stats.health[0]) + rom.write_byte(snes_to_pc(a2), stats.health[1]) + else: + rom.write_byte(snes_to_pc(0x0DB173+int(sprite)), stats.health) + # write damage class to rom + if stats.damage is not None: + if isinstance(stats.damage, tuple): + if sprite == EnemySprite.Octorok4Way: # skip this one + continue + if sprite in special_damage_table: + a1, a2 = special_damage_table[sprite] + rom.write_byte(snes_to_pc(a1), stats.dmask | stats.damage[0]) + rom.write_byte(snes_to_pc(a2), stats.dmask | stats.damage[1]) + else: + rom.write_byte(snes_to_pc(0x0DB266+int(sprite)), stats.dmask | stats.damage) + # write damage table to rom + for idx, damage_list in self.enemy_damage.items(): + rom.write_bytes(snes_to_pc(0x06F42D + idx * 3), damage_list) + # write bush spawns to rom: + for area_id, bush_sprite in self.bush_sprite_table.items(): + rom.write_byte(snes_to_pc(0x368120 + area_id), bush_sprite.sprite) + if increase_bush_sprite_chance: + rom.write_bytes(snes_to_pc(0x1AFBBB), [ + 0x01, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x12, + 0x0F, 0x01, 0x0F, 0x0F, 0x11, 0x0F, 0x0F, 0x03 + ]) + + +special_health_table = { + EnemySprite.Octorok: (0x068F76, 0x068F77), + EnemySprite.HardhatBeetle: (0x06911F, 0x069120), + EnemySprite.Tektite: (0x068D97, 0x068D98), + EnemySprite.CricketRat: (0x068876, 0x068877), + EnemySprite.Keese: (0x06888A, 0x06888B), + EnemySprite.Snake: (0x0688A6, 0x0688A7), + EnemySprite.Raven: (0x068965, 0x068966) +} + +special_damage_table = { + EnemySprite.Octorok: (0x068F74, 0x068F75), + EnemySprite.Tektite: (0x068D99, 0x068D9A), + EnemySprite.CricketRat: (0x068874, 0x068875), + EnemySprite.Keese: (0x068888, 0x068889), + EnemySprite.Snake: (0x0688A4, 0x0688A5), + EnemySprite.Raven: (0x068963, 0x068964) +} def init_data_tables(world, player): @@ -93,4 +160,6 @@ def init_data_tables(world, player): for area, sprite_list in vanilla_sprites_ow.items(): for sprite in sprite_list: data_tables.ow_enemy_table[area].append(sprite.copy()) + data_tables.enemy_damage = {k: list(v) for k, v in world.damage_table[player].enemy_damage.items()} + # todo: more denials based on enemy drops return data_tables